SSHFS File Systems
SSHFS is
a
FUSE
filesystem that allows easy access to directories on a remote
machine using the SSH File Transfer Protocol (SFTP). It means that
if you have SSH access to a machine, no additional setup is needed
to mount a directory.
Interactive mounting
In NixOS, SSHFS is packaged as sshfs. Once
installed, mounting a directory interactively is simple as
running:
$ sshfs my-user@example.com:/my-dir /mnt/my-dir
Like any other FUSE file system, the directory is unmounted using:
$ fusermount -u /mnt/my-dir
Non-interactive mounting
Mounting non-interactively requires some precautions because
sshfs will run at boot and under a different
user (root). For obvious reason, you can’t input a password, so
public key authentication using an unencrypted key is needed. To
create a new key without a passphrase you can do:
$ ssh-keygen -t ed25519 -P '' -f example-key
Generating public/private ed25519 key pair.
Your identification has been saved in test-key
Your public key has been saved in test-key.pub
The key fingerprint is:
SHA256:yjxl3UbTn31fLWeyLYTAKYJPRmzknjQZoyG8gSNEoIE my-user@workstation
To keep the key safe, change the ownership to
root:root and make sure the permissions are
600: OpenSSH normally refuses to use the key if
it’s not well-protected.
The file system can be configured in NixOS via the usual
fileSystems
option. Here’s a typical setup:
{
system.fsPackages = [ pkgs.sshfs ];
fileSystems."/mnt/my-dir" = {
device = "my-user@example.com:/my-dir/";
fsType = "sshfs";
options =
[ # Filesystem options
"allow_other" # for non-root access
"_netdev" # this is a network fs
"x-systemd.automount" # mount on demand
# SSH options
"reconnect" # handle connection drops
"ServerAliveInterval=15" # keep connections alive
"IdentityFile=/var/secrets/example-key"
];
};
}
More options from ssh_config(5) can be given as
well, for example you can change the default SSH port or specify a
jump proxy:
{
options =
[ "ProxyJump=bastion@example.com"
"Port=22"
];
}
It’s also possible to change the ssh command
used by SSHFS to connect to the server. For example:
{
options =
[ (builtins.replaceStrings [" "] ["\\040"]
"ssh_command=${pkgs.openssh}/bin/ssh -v -L 8080:localhost:80")
];
}
The escaping of spaces is needed because every option is written
to the /etc/fstab file, which is a
space-separated table.
Troubleshooting
If you’re having a hard time figuring out why mounting is
failing, you can add the option
"debug". This enables a verbose log
in SSHFS that you can access via:
$ journalctl -u $(systemd-escape -p /mnt/my-dir/).mount
Jun 22 11:41:18 workstation mount[87790]: SSHFS version 3.7.1
Jun 22 11:41:18 workstation mount[87793]: executing <ssh> <-x> <-a> <-oClearAllForwardings=yes> <-oServerAliveInterval=15> <-oIdentityFile=/var/secrets/wrong-key> <-2> <my-user@example.com> <-s> <sftp>
Jun 22 11:41:19 workstation mount[87793]: my-user@example.com: Permission denied (publickey).
Jun 22 11:41:19 workstation mount[87790]: read: Connection reset by peer
Jun 22 11:41:19 workstation systemd[1]: mnt-my\x2ddir.mount: Mount process exited, code=exited, status=1/FAILURE
Jun 22 11:41:19 workstation systemd[1]: mnt-my\x2ddir.mount: Failed with result 'exit-code'.
Jun 22 11:41:19 workstation systemd[1]: Failed to mount /mnt/my-dir.
Jun 22 11:41:19 workstation systemd[1]: mnt-my\x2ddir.mount: Consumed 54ms CPU time, received 2.3K IP traffic, sent 2.7K IP traffic.
If the mount point contains special characters it needs to be
escaped using systemd-escape. This is due
to the way systemd converts paths into unit names.