devmachine

How it works

Password login, account by account

setup and machines add turn SSH password login off once the key is proved. On a server that only you reach, that is the whole story: the key is the only way in, and a password nobody can guess is better than one somebody might.

A machine people use is different. A Mac has the accounts of the people who sit at it, and some of them may SSH in with their password. Turning password login off reaches every account, not only the admin login the CLI uses: SSH has one setting for the whole machine. So before it changes anything, the wizard asks:

SSH password login, now that the key is proved:
On a Mac this is every account on it. Whoever logs in over SSH with a password needs a key afterwards; the login window, sudo and Screen Sharing keep their passwords.
Other accounts on this machine: alice, bob.
  1) off for every account
  2) off, except for accounts you name
  3) leave it as it is
choice [1]:

Enter keeps the default, off for everybody. --keep-password-login a,b answers it without a terminal, and --no-harden is the third choice. Only SSH changes: the login window, sudo, Screen Sharing and FileVault keep their passwords.

How an account keeps its password

The file the CLI writes, /etc/ssh/sshd_config.d/00-devmachine-hardening.conf, turns password login off for everybody, then ends with a Match User block that turns it back on for the accounts you named:

PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin prohibit-password
Match User alice,bob
	PasswordAuthentication yes
	KbdInteractiveAuthentication yes

A Match block runs to the end of the file it is in. It does not run on into the main sshd_config past the Include line, which would make every later line in that file apply to those accounts only. This was checked, not assumed: with a Subsystem line after the Include — which sshd -t refuses inside a Match — sshd -t accepts the configuration, and sshd -T -C user=… gives yes for a kept account and no for the rest, on OpenSSH 9.6 (Ubuntu 24.04) and 9.9 (macOS). The CLI’s own tests run the same check against the sshd they find. Still, the block stays last in the file, so a different reading of the rule could only affect lines the CLI does not write.

After the change the CLI asks sshd -T twice: once for an account no exception names, which must say no, and once for each kept account, which must say yes. A file that does not do what it says is taken back.

One place for the exceptions

The accounts are written on the machine in config.yml:

machines:
  - name: studio
    hosts: [203.0.113.20]
    password_login_keep: [alice, bob]

They live on the machine, not in a package setting, because setup turns password login off before any package exists. The ssh_hardening package writes the same file on every sync, and it reads this list, so a sync keeps the same exceptions instead of wiping them.

Changing it later

devmachine machines password-login studio                 # who can log in with a password
devmachine machines password-login studio --keep alice    # off, except alice
devmachine machines password-login studio --off           # off for every account
devmachine machines password-login studio --on            # the system's own setting

The change goes over the admin login’s key, which is the proof that a way in stays open. --on takes the CLI’s file away, so the system’s own setting applies again: on macOS, Debian and Ubuntu that is password login on. It is refused while the machine has ssh_hardening, which turns password login off again on every sync.

Each change keeps a copy of the file it replaced in /etc/ssh/.devmachine-hardening.conf.previous, outside sshd_config.d: macOS reads every file in that folder, not only *.conf, so a copy kept there would be read as configuration.