This guide covers security best practices for deploying and configuring bssh-server.
- Overview
- Authentication Security
- Network Security
- Session Security
- File Transfer Security
- Host Key Management
- Audit and Compliance
- Hardening Checklist
bssh-server is designed with security as a primary concern. It implements multiple layers of protection:
- Authentication: Public key and password authentication with rate limiting
- Network: IP allowlists/blocklists with CIDR support
- Sessions: Per-user session limits, idle timeouts, and maximum duration
- File Transfers: Path traversal prevention and file filtering
- Audit: Comprehensive logging with multiple export options
Public key authentication is more secure than passwords because:
- No secrets are transmitted over the network
- Keys are computationally infeasible to brute-force
- Keys can be protected with passphrases locally
Configuration:
auth:
methods:
- publickey
publickey:
# Per-user directory structure
authorized_keys_dir: /etc/bssh/authorized_keys
# Structure: /etc/bssh/authorized_keys/{username}/authorized_keys
# OR pattern-based (for existing systems)
authorized_keys_pattern: "/home/{user}/.ssh/authorized_keys"Best Practices:
- Use Ed25519 keys (
bssh-keygen -t ed25519) - Protect private keys with passphrases
- Restrict authorized_keys file permissions to 0600
- Regularly rotate keys (annually or when personnel changes)
If password authentication is required, use strong passwords with Argon2id hashing:
auth:
methods:
- password
password:
users:
- name: username
password_hash: "$argon2id$v=19$m=19456,t=2,p=1$..."Generating Password Hashes:
bssh-server hash-password
# Uses Argon2id with:
# - 19 MiB memory cost (resistant to GPU attacks)
# - 2 iterations
# - 1 degree of parallelismBest Practices:
- Require minimum 12-character passwords
- Use unique passwords per user
- Consider disabling password auth in favor of keys
- Never store plaintext passwords
Protect against brute-force attacks:
security:
# Ban IP after 5 failed attempts
max_auth_attempts: 5
# Time window for counting failures (5 minutes)
auth_window: 300
# Ban duration (5 minutes)
ban_time: 300
# IPs exempt from rate limiting
whitelist_ips:
- "127.0.0.1"
- "::1"
- "10.0.0.0/8" # Internal networkRestrict access to trusted networks:
security:
# Allow only these IP ranges (whitelist mode)
allowed_ips:
- "192.168.1.0/24" # Office network
- "10.0.0.0/8" # Internal network
- "2001:db8::/32" # IPv6 range
# Always block these IPs (takes priority)
blocked_ips:
- "192.168.1.100/32" # Compromised host
- "203.0.113.0/24" # Known bad actorsPriority Rules:
- Blocked IPs are always denied (highest priority)
- If allowed_ips is configured, only those IPs are permitted
- If allowed_ips is empty, all IPs (except blocked) are permitted
Best Practices:
- Use CIDR notation for precise control
- Block entire ranges known for abuse
- Use allowlists in high-security environments
- Monitor blocked connection attempts
Bind to specific interfaces when possible:
server:
# Bind to specific internal interface
bind_address: "10.0.0.1"
# Or all interfaces (for containers)
bind_address: "0.0.0.0"server:
# Use non-standard port to reduce automated scans
port: 2222
# Or standard port 22 (requires root or capabilities)
port: 22Prevent resource exhaustion and detect compromised accounts:
security:
# Maximum sessions per user
max_sessions_per_user: 10
# Idle session timeout (1 hour)
idle_timeout: 3600
# Maximum session duration (24 hours, 0 = disabled)
session_timeout: 86400
server:
# Maximum total connections
max_connections: 100
# Connection timeout (5 minutes)
timeout: 300
# SSH keepalive interval
keepalive_interval: 60shell:
# Use restricted shell if available
default: /bin/rbash
# Command execution timeout
command_timeout: 3600
# Minimal environment
env:
PATH: /usr/bin:/bin
LANG: C.UTF-8Per-User Shell Restrictions:
auth:
password:
users:
- name: sftp-only
password_hash: "..."
shell: /usr/sbin/nologin # No shell access# Disable if not needed
sftp:
enabled: false
scp:
enabled: false
# Or enable with chroot. SCP falls back to sftp.root when scp.root is unset,
# so a single setting governs both subsystems.
sftp:
enabled: true
root: /data/sftp # Confine SFTP and SCP to this directory.
# Use scp.root only when SCP and SFTP need separate chroots:
scp:
enabled: true
root: /data/scpChroot semantics. When root is set, the client's / is the chroot root
(OpenSSH ChrootDirectory re-rooting semantics). SFTP and SCP behave the same
way:
- Both absolute and relative client paths are re-anchored under
root, so/subdirresolves to<root>/subdirand plain/maps to the chroot directory (interactive clients'cd /,pwdkeep working). - A host-looking path such as
/etc/passwdis confined to<root>/etc/passwd, never the host file. ..traversal is clamped at the chroot boundary and can never escape.- Path-traversal and symlink-escape protections apply, including for paths
whose final component does not exist yet: the closest existing ancestor is
canonicalized and verified to stay inside
root, which blocks intermediate-directory symlinks pointing outside the chroot. An absolute SFTP symlink target is itself re-anchored underroot, so a created link can never point at the host filesystem.
When root is unset (the default), the handler runs
without chroot. Absolute paths are honored verbatim and relative paths
resolve from the user's home directory, matching OpenSSH sftp-server.
This is the recommended default for Backend.AI session containers and any
deployment whose clients submit absolute filesystem paths (such as the
WebUI's "Download SSH key / SCP example" snippet, or bssh upload /abs/remote.bin).
Block dangerous file types:
filter:
enabled: true
rules:
# Block executable uploads
- pattern: "*.exe"
action: deny
- pattern: "*.sh"
action: deny
- pattern: "*.py"
action: deny
# Block uploads to sensitive directories
- path_prefix: "/etc/"
action: deny
- path_prefix: "/usr/"
action: deny
# Log all transfers to temp
- path_prefix: "/tmp/"
action: logFilter Actions:
deny: Block the transferallow: Explicitly allow (overrides other rules)log: Allow but log the transfer
bssh-server automatically prevents path traversal attacks:
- All paths are normalized before processing
..components cannot escape the root directory- Symlinks are resolved and validated
- Absolute paths are stripped and joined with the user's root
# Generate Ed25519 key (recommended)
bssh-server gen-host-key -t ed25519 -o /etc/bssh/ssh_host_ed25519_key
# Generate RSA key (for compatibility)
bssh-server gen-host-key -t rsa -o /etc/bssh/ssh_host_rsa_key --bits 4096# Ensure secure permissions
chmod 600 /etc/bssh/ssh_host_*_key
chown root:root /etc/bssh/ssh_host_*_keyRotate host keys periodically (annually) or when:
- Key may have been compromised
- Significant personnel changes occur
- Migrating to new infrastructure
Rotation Process:
- Generate new host key
- Update configuration to include both old and new keys
- Notify users of host key change
- After transition period, remove old key
audit:
enabled: true
exporters:
# Local file (JSON Lines format)
- type: file
path: /var/log/bssh/audit.log
# Send to SIEM via OpenTelemetry
- type: otel
endpoint: http://otel-collector:4317
# Send to Logstash
- type: logstash
host: logstash.example.com
port: 5044The following events are logged:
- Authentication: Success, failure, rate limiting
- Sessions: Start, end, duration
- Commands: Executed commands (can contain sensitive data)
- File Operations: Read, write, delete, rename
- Security: IP blocks, suspicious activity
# Protect audit logs
chmod 600 /var/log/bssh/audit.log
chown root:root /var/log/bssh/audit.log
# Configure log rotation
cat > /etc/logrotate.d/bssh << EOF
/var/log/bssh/*.log {
daily
rotate 90
compress
delaycompress
missingok
notifempty
create 0600 root root
}
EOF- Use public key authentication only
- Disable password authentication
- Disable unused subsystems (SFTP/SCP)
- Set appropriate timeouts
- Configure session limits
- Configure IP allowlists for production
- Use non-standard port if appropriate
- Bind to specific interface if possible
- Deploy behind firewall/load balancer
- Use Ed25519 keys
- Set correct file permissions (0600)
- Rotate keys periodically
- Store keys securely (HSM for high security)
- Enable audit logging
- Configure log shipping to SIEM
- Set up alerts for security events
- Regularly review access logs
- Enable file transfer filtering
- Block dangerous file types
- Restrict upload directories
- Use SFTP chroot when possible
- Use minimal base image
- Run as non-root if possible
- Set resource limits
- Use read-only filesystem
- Generate unique host keys per instance
Complete security-focused configuration:
# /etc/bssh/server.yaml - High Security Configuration
server:
bind_address: "10.0.0.1"
port: 2222
host_keys:
- /etc/bssh/ssh_host_ed25519_key
max_connections: 50
timeout: 120
keepalive_interval: 30
auth:
methods:
- publickey
publickey:
authorized_keys_dir: /etc/bssh/authorized_keys
shell:
default: /bin/rbash
command_timeout: 1800
env:
PATH: /usr/bin:/bin
sftp:
enabled: true
root: /data/sftp
scp:
enabled: false
filter:
enabled: true
rules:
- pattern: "*.exe"
action: deny
- pattern: "*.dll"
action: deny
- path_prefix: "/etc/"
action: deny
audit:
enabled: true
exporters:
- type: file
path: /var/log/bssh/audit.log
- type: otel
endpoint: http://siem:4317
security:
max_auth_attempts: 3
auth_window: 300
ban_time: 900
whitelist_ips:
- "127.0.0.1"
max_sessions_per_user: 5
idle_timeout: 1800
session_timeout: 28800
allowed_ips:
- "10.0.0.0/8"
- "192.168.0.0/16"
blocked_ips:
- "0.0.0.0/8"