SSH server + SFTP hardening
BOA hardens SSH and deploys supporting config from its conf-template set. This page
is the operator reference for what BOA configures and what to know before editing.
The templates live in aegir/conf/var/.
Important — SSH configs are edited in place, not template-deployed. BOA does
not copy aegir/conf/var/sshd_config or aegir/conf/var/ssh_config over
/etc/ssh/. Those template files exist in the source tree, but the live
/etc/ssh/sshd_config and /etc/ssh/ssh_config are the OS-provided files that
BOA edits in place (via sed/echo, see the reconciliation section below).
The reconciliation logic — not the template — determines what ends up in the
live files.
Files deployed by barracuda upgrade
Config files BOA copies verbatim (distinct from the in-place SSH editing above):
| Template (in BOA source) | Lands at | Purpose |
|---|---|---|
aegir/conf/var/sftp_config |
/etc/ssh/sftp_config (or MySecureShell config) |
SFTP subsystem config |
aegir/conf/var/boa.bashrc.txt |
/root/.bashrc (snippet) |
Root shell rc with BOA paths |
aegir/conf/var/rsyslog.conf |
/etc/rsyslog.conf |
syslog routing (SSH logs) |
aegir/conf/var/mysql-notices.conf |
(various) | Percona notice rules |
aegir/conf/var/galera.cnf |
/etc/mysql/conf.d/ |
Galera config (legacy cluster era) |
aegir/conf/var/named.conf.options |
/etc/bind/ (if BIND xtras installed) |
DNS server options |
aegir/conf/var/get.htaccess.txt |
(download dir) | Default .htaccess shipped to platforms |
What BOA's sshd_config enforces
Key directives BOA sets (operator can override with care):
PermitRootLogin prohibit-password— root SSH only with an ed25519 key, never a password.PasswordAuthentication yes— the global default in the conf template, for non-root accounts (tenants need it foroN.ftpSFTP). At upgradesystem.sh.incre-appends it asPasswordAuthentication noinstead when/root/.ssh.auth.keys.only.cnfexists (key-only enforcement).PubkeyAuthentication— left at the OpenSSH default (the template ships it commented as#PubkeyAuthentication yes, so pubkey auth is enabled by default but not explicitly written).PermitEmptyPasswords no.X11Forwarding no.AllowTcpForwarding local— local TCP forwarding is permitted (remote / gateway forwarding is not). This is intentional: it restores SQL-client tunnels (Sequel Ace/Pro, TablePlus) over SSH that an earlier all-forwarding-disabled hardening pass had broken.system.sh.incforcibly normalises any existingAllowTcpForwardingvalue back tolocalon every upgrade.AllowAgentForwarding no.MaxAuthTries 3— fast LFD trigger.LoginGraceTime 30— quick disconnect on stalls.KexAlgorithms+Ciphers+MACs+HostKey— a modern crypto set with legacy algorithms disabled. This is opt-in, not a default: the whole crypto block insystem.sh.inc(_sshd_armour) is gated on_SSH_ARMOUR, which ships as_SSH_ARMOUR=NO. When_SSH_ARMOURisNOor empty, BOA instead restores the vanillasshd_config/ssh_config(from its.vanilla.*backups) and applies none of these crypto directives — so on a default host they are not set. Only when_SSH_ARMOUR=YESdoes BOA rewrite Protocol 2 / KexAlgorithms / Ciphers / MACs / HostKey (and regenerate host keys + moduli). The strip-and-append directives below (MaxAuthTries,AllowTcpForwarding, etc.) are separate and are applied unconditionally at upgrade.
There is no Match User oN.ftp block in BOA's sshd_config (the only
Match example in the template is the stock commented-out #Match User anoncvs). Per-tenant SFTP chroot is not handled by OpenSSH internal-sftp —
it is provided by the MySecureShell + Pure-FTPd layer described below.
What you should NOT change
PermitRootLogin prohibit-password— switching toyesre-enables password root SSH and lets every brute-force attempt try. Keep key-only.- The tenant SFTP chroot (MySecureShell + Pure-FTPd) — tenants depend on it.
The chroot is enforced by the MySecureShell / Pure-FTPd layer (see below), not
by an
sshd_configMatchblock; do not disable or reconfigure that layer or tenants can escape their home. AllowUsersif present — a typo here locks you out.
What you can change safely
Port— change from 22 to a non-standard port. Reduces log noise from random scans (CSF still catches them; this is cosmetic noise reduction).AllowGroups/AllowUsers— restrict SSH access to specific users. Be sure your operator account is included.
Note that ClientAliveInterval / ClientAliveCountMax are not safe to
hand-tune: they are in the strip-and-append set below, so system.sh.inc forces
them back to ClientAliveInterval 300 / ClientAliveCountMax 10000 on every
upgrade (lib/functions/system.sh.inc). Any operator value for these does not
survive.
After editing /etc/ssh/sshd_config:
# Validate config (catches typos that would prevent SSH start)
sshd -t
# Reload
service ssh reload
Always validate before reloading. If sshd -t errors, fix the typo. If you
reload broken config, the running daemon keeps working but new connections fail.
How barracuda upgrade reconciles operator edits
At upgrade, system.sh.inc does not re-deploy the template file or use any
marker-comment mechanism. Instead it edits the live /etc/ssh/sshd_config in
place: it sed-strips a fixed set of directives by name (regardless of their
value) and then re-appends each one with BOA's hardened value. There is no
per-line preservation logic — whether your edit survives depends solely on
whether the directive is in that fixed strip-and-append set.
The directives BOA strips and forcibly re-appends on every upgrade include:
UseDNS, UsePAM, PrintMotd, ClientAliveInterval, ClientAliveCountMax,
IgnoreUserKnownHosts, PasswordAuthentication, TCPKeepAlive,
PermitEmptyPasswords, KbdInteractiveAuthentication, MaxAuthTries,
LoginGraceTime, MaxStartups, X11Forwarding, AllowTcpForwarding,
AllowAgentForwarding, and Subsystem. PermitRootLogin is separately forced
to prohibit-password on hosted systems or when
/root/.ssh.root.auth.keys.only.cnf is present. Any operator value for these is
overwritten.
Operator changes that survive — but only incidentally, because these directives are simply not in the strip set (BOA neither preserves nor touches them):
- Custom
Portsetting. AllowUsers/AllowGroupslines.- Custom
Match Userblocks for non-tenant accounts.
Changes that do not survive (intentional — security baseline):
- Switching
PermitRootLogintoyes(forced back toprohibit-password). - Re-enabling all-direction TCP forwarding —
AllowTcpForwardingis stripped and re-added asAllowTcpForwarding localon every upgrade. - Operator edits to any other directive in the strip-and-append set above (e.g.
MaxAuthTries,X11Forwarding,PasswordAuthentication).
The tenant SFTP chroot is unaffected by this reconciliation — it lives in the
MySecureShell / Pure-FTPd layer, not in sshd_config.
SFTP subsystem — MySecureShell + Pure-FTPd
For tenant SFTP, BOA layers MySecureShell on top of OpenSSH's sftp
subsystem (the template ships Subsystem sftp /usr/lib/openssh/sftp-server -u 0002, the external sftp-server binary — not internal-sftp). The MySecureShell
layer adds per-user bandwidth limits, stronger chroot enforcement, and per-user
connection limits.
For FTPS (FTP over SSL), BOA runs Pure-FTPd with forced FTPS plus chroot. See
aegir/conf/ftpd/.
Tenants get SFTP + FTPS access automatically as part of the oN.ftp account (see
lshell + manage_ltd_users).
The jail's platforms/ tree — how SFTP reaches site files
Each oN.ftp home carries a platforms/ tree of symlinks — the jail's
platform-side route to site files (a sibling clients symlink covers the
per-client view):
/home/oN.ftp/platforms/<tree>/<platform> -> <platform docroot>/sites
<tree> is the codebase revision directory under the account's distro/; each
tree also gets a keys symlink to the tree's own keys directory, which the
platform loop skips by name.
Historically these symlinks were created only for Drupal-7-era codebases with a
root-level sites/ dir. Composer D8+ codebases keep the docroot in a subdir
(web/, docroot/ or html/), so every such platform was silently skipped —
no symlink, no SFTP path to those sites' files — and a vestigial root-level
sites/ (e.g. left behind by drupal_cms_installer) could be mis-linked. Both
symlink writers — the install/upgrade path (lib/functions/satellite.sh.inc)
and its nightly twin (aegir/tools/system/night/10-account.sh) — now resolve
the real docroot via _detect_real_docroot(): it probes for index.php at the
platform root, then under docroot/, html/, web/, in that order, testing
with -f so a
dangling-symlink decoy (e.g. web -> /etc) is rejected. The helper is defined
in lib/functions/helper.sh.inc and deliberately duplicated into the deployed
night/night.inc.sh, so the nightly tools carry their own copy (BOA
separate-copies convention). The symlink is named after the platform dir and
points at <real docroot>/sites; D7 platform behaviour is unchanged.
A complementary permission fix in the batch cleanup
(_satellite_batch_cleanup, same file) now chowns
<codebase>/{web,docroot,html}/sites to root:users in addition to the
root-level sites — on both storage layouts — so jailed .ftp accounts
(group users) can traverse the D8+ sites tree the new symlinks point at.
Net effect: Composer D8+ platforms now appear in the jail's platforms/ tree,
where previously they were simply missing; the symlinks are written on
install/upgrade and refreshed by the nightly account pass.
SSH key management
For root operators
/root/.ssh/authorized_keys
ed25519 keys preferred. RSA acceptable. ECDSA discouraged. DSA explicitly disabled by BOA.
For tenants
/home/oN.ftp/.ssh/authorized_keys
Populated via SFTP or by editing the file directly. Aegir's UI does not expose key management; it is a tenant-shell operation.
Per-Aegir-Client subaccount keys
Extra-account subkeys go in the per-Client subaccount's
~/.ssh/authorized_keys. See shell extras.
SSH host-key changed on rebuild
If you rebuild the BOA host (fresh install on the same hostname), existing SSH clients see the host-key change and refuse to connect. For operator workstations, remove the old key:
ssh-keygen -R server.example.com
Then reconnect — your ~/.ssh/known_hosts gets the new fingerprint. The
tenant-facing recovery is in Troubleshooting.
Logging
/var/log/auth.log # SSH auth attempts + sessions
/var/log/messages # General system
/var/log/csf.log # CSF + LFD actions related to SSH
LFD watches auth.log and CSF acts on repeated SSH failures — see
CSF + LFD firewall.
AppArmor profile for sshd
The usr.local.sbin.sshd AppArmor profile confines the OpenSSH daemon — see
AppArmor confinement. Even a hypothetical sshd RCE is bounded
by what the profile allows.
Related
- Security model — protection #1 (encrypted connections only).
- lshell + manage_ltd_users — the per-tenant restricted shell that SSH enforces.
- CSF + LFD firewall — packet-level + LFD layer.
- AppArmor confinement — the sshd AppArmor profile.
- Password hashing — strong password hashing for the SSH password fallback.
- Troubleshooting — tenant-facing recovery for the changed-host-key case.