Conversation
c84c112 to
9b43f1a
Compare
Adds a SSH server that enables unlocking LUKS devices with a passphrase.
The SSH server is enabled only when the `network:` configuration is
defined, and when either `ssh_pass:` or `ssh_authorized_keys:` is defined
under `network:`.
Example configuration:
```yaml
network:
dhcp: true
ssh_addr: ":2222"
ssh_server_keys: |-
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS
1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQQDMfxplAcF1tRvQgpIXz3cUJ1G9L70
PJLmDx3IL1CwMWu5r1d1/XxsHA8Tau9CRGVliQvyKTBhlRrs3ViM8glbAAAAqEhXJrpIVy
a6AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBAMx/GmUBwXW1G9C
CkhfPdxQnUb0vvQ8kuYPHcgvULAxa7mvV3X9fGwcDxNq70JEZWWJC/IpMGGVGuzdWIzyCV
sAAAAhAPghE5yL0ITueX8r8vhYA+aG6F3UMGlwANugAK2poytVAAAAD2tlbkBrZW4tZGVz
a3RvcA==
-----END OPENSSH PRIVATE KEY-----
ssh_user: my_user
ssh_pass: "$1$FecCMLMd$R10.c/UKY4IaKwrLo4NaT0"
ssh_authorized_keys: |-
ssh-ed25519 AAAACFFFFFFFFBBBBTTTTFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF+ user@localhost
```
When enabled as above, the Booster `init` process will start a SSH
server at boot listening oen all interfaces on port 2222, using the
specified server private key, and only allowing logins from `my_user`
(default user is `root` when not specified).
Additionally, password authentication will be available (the password
above is hash of "password"), as well as public-key authentication for
the provided authorized key. If `ssh_pass:` is not provided, then
password authentication will not be available.
Similarly, if `ssh_authorized_keys:` is not provided, then public-key
authentication will not be available.
When both `ssh_pass:` and `ssh_authorized_keys:` are not provided, the
SSH server will not be started.
An example remote unlock session:
```sh
$ ssh -p 2222 my_user@example.com
Enter passphrase:
Attempting unlock...
device /dev/md1 slot #0 matches password
device /dev/md1 unlocked successfully
Connection to localhost closed.
```
The `ssh_pass:` in the example configuration above was generated as
follows:
```sh
echo "password" | mkpasswd -H md5 -s
```
Any standard crypt-style prefixed password hashes, as well as plaintext
passwords are supported for `ssh_pass:`.
See the [`github.com/go-crypt/crypt`](https://github.com/go-crypt/crypt)
package for more information.
If `ssh_server_keys:` is not provided, a random ECDSA key will be
generated and used instead.
Implements anatol#191.
9b43f1a to
cf80785
Compare
anatol
left a comment
There was a problem hiding this comment.
One more thought. If we enable SSH server during the boot, it means that it is a remote machine. In this case we should disable keyboard and plymouth input then. What do you think?
|
|
||
| // sshSessionHandler handles ssh sessions. | ||
| func sshSessionHandler(sess ssh.Session) { | ||
| warning("ssh: session %q [%s]: opened", sess.User(), sess.RemoteAddr()) |
There was a problem hiding this comment.
it should be either debug or info level
| github.com/anatol/tang.go v0.0.0-20250920193351-e505ad2c76db | ||
| github.com/anatol/vmtest v0.0.0-20250627153117-302402d269a6 | ||
| github.com/cavaliergopher/cpio v1.0.1 | ||
| github.com/gliderlabs/ssh v0.3.8 |
There was a problem hiding this comment.
I feel more comfortable with going with the basic golang.org/x/crypto/ssh as I trust golang.org/x/ it more.
Does github.com/gliderlabs/ssh really save that much for us?
There was a problem hiding this comment.
It provides much of the functionality to actually do/launch a SSH server. It would basically be recopying everything from that package. The vast majority of the actual implementation is within the golang.org/x package. Considering that the gliderlabs package is used elsewhere in other well known apps, I thought it prudent to use here.
There was a problem hiding this comment.
I would suggest reading through the gliderlabs ssh package. It has essentially zero imports, other than github.com/anmitsu/go-shlex, which is used only in one place: when executing a remote command. Note that functionality is not available through the implementation I wrote. I would prefer if the gliderlabs package didn't have this dependency, but, at the end of the day, it's not my package.
| SshServerKeys string `yaml:"ssh_server_keys,omitempty"` | ||
| SshUser string `yaml:"ssh_user,omitempty"` | ||
| SshPass string `yaml:"ssh_pass,omitempty"` | ||
| SshAuthorizedKeys string `yaml:"ssh_authorized_keys,omitempty"` |
There was a problem hiding this comment.
you use both Password and AuthorizedKeys. I propose to leave only AuthorizedKeys as a more secure approach.
There was a problem hiding this comment.
Ultimately, I'll take your lead here; it does reduce the complexity, but I would just suggest that it be kept in because it's kind of basic, expected SSH functionality, so implemented this as it was quick and fast and to head off future maintenance. I agree with your point, but trying to be somewhat general in purpose.
| // ensure secure key | ||
| if config.Network.SshServerKeys == "" { | ||
| var err error | ||
| if config.Network.SshServerKeys, err = sshGenEcdsaKey(rand.Reader); err != nil { |
There was a problem hiding this comment.
Generating server authentication keys at the boot time does not sound right to me. I think the key needs to be provided by the user during the build time, and then the user adds the public part to ~/.ssh/known_hosts.
There was a problem hiding this comment.
I agree with you, but I imagine most people would want to use this with the least amount of possible configuration entries. As with the password functionality, my thinking was that users would be able do:
network:
ssh_password: "password"
And it would work.
Technically, the SSH server could be launched without any credentials (not that the implementation I submitted is like that), but I thought at least a password or SSH key would be the most prudent options and head off future maintenance requests.
| } | ||
|
|
||
| // check if mapped device | ||
| mapping := matchLuksMapping(blk) |
There was a problem hiding this comment.
You reimplement unlock LUKS logic. See if you can piggy-back on requestKeyboardPassword() instead. Similar to how recent Plymouth integration done here a766edb#diff-fd49727bba9fd7c9b76f07c573dc61e84b82d2b8ba9c1cbe3fe48a3789c601e9
There was a problem hiding this comment.
I had originally looked at consolidating these code paths, but didn't want to waste too much time/effort in case the feature would be rejected. I'll rework the code here and make it more generalized.
Adds a SSH server that enables unlocking LUKS devices with a passphrase. The SSH server is enabled only when the
network:configuration is defined, and when eitherssh_pass:orssh_authorized_keys:is defined undernetwork:.Example configuration:
When enabled as above, the Booster
initprocess will start a SSH server at boot listening oen all interfaces on port 2222, using the specified server private key, and only allowing logins frommy_user(default user isrootwhen not specified).Additionally, password authentication will be available (the password above is hash of "password"), as well as public-key authentication for the provided authorized key. If
ssh_pass:is not provided, then password authentication will not be available.Similarly, if
ssh_authorized_keys:is not provided, then public-key authentication will not be available.When both
ssh_pass:andssh_authorized_keys:are not provided, the SSH server will not be started.An example remote unlock session:
$ ssh -p 2222 my_user@example.com Enter passphrase: Attempting unlock... device /dev/md1 slot #0 matches password device /dev/md1 unlocked successfully Connection to localhost closed.The
ssh_pass:in the example configuration above was generated as follows:Any standard crypt-style prefixed password hashes, as well as plaintext passwords are supported for
ssh_pass:.See the
github.com/go-crypt/cryptpackage for more information.If
ssh_server_keys:is not provided, a random ECDSA key will be generated and used instead.Implements #191.