1<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-sshfs-file-systems">
2 <title>SSHFS File Systems</title>
3 <para>
4 <link xlink:href="https://github.com/libfuse/sshfs">SSHFS</link> is
5 a
6 <link xlink:href="https://en.wikipedia.org/wiki/Filesystem_in_Userspace">FUSE</link>
7 filesystem that allows easy access to directories on a remote
8 machine using the SSH File Transfer Protocol (SFTP). It means that
9 if you have SSH access to a machine, no additional setup is needed
10 to mount a directory.
11 </para>
12 <section xml:id="sec-sshfs-interactive">
13 <title>Interactive mounting</title>
14 <para>
15 In NixOS, SSHFS is packaged as <package>sshfs</package>. Once
16 installed, mounting a directory interactively is simple as
17 running:
18 </para>
19 <programlisting>
20$ sshfs my-user@example.com:/my-dir /mnt/my-dir
21</programlisting>
22 <para>
23 Like any other FUSE file system, the directory is unmounted using:
24 </para>
25 <programlisting>
26$ fusermount -u /mnt/my-dir
27</programlisting>
28 </section>
29 <section xml:id="sec-sshfs-non-interactive">
30 <title>Non-interactive mounting</title>
31 <para>
32 Mounting non-interactively requires some precautions because
33 <literal>sshfs</literal> will run at boot and under a different
34 user (root). For obvious reason, you can’t input a password, so
35 public key authentication using an unencrypted key is needed. To
36 create a new key without a passphrase you can do:
37 </para>
38 <programlisting>
39$ ssh-keygen -t ed25519 -P '' -f example-key
40Generating public/private ed25519 key pair.
41Your identification has been saved in test-key
42Your public key has been saved in test-key.pub
43The key fingerprint is:
44SHA256:yjxl3UbTn31fLWeyLYTAKYJPRmzknjQZoyG8gSNEoIE my-user@workstation
45</programlisting>
46 <para>
47 To keep the key safe, change the ownership to
48 <literal>root:root</literal> and make sure the permissions are
49 <literal>600</literal>: OpenSSH normally refuses to use the key if
50 it’s not well-protected.
51 </para>
52 <para>
53 The file system can be configured in NixOS via the usual
54 <link linkend="opt-fileSystems">fileSystems</link> option. Here’s
55 a typical setup:
56 </para>
57 <programlisting language="bash">
58{
59 system.fsPackages = [ pkgs.sshfs ];
60
61 fileSystems."/mnt/my-dir" = {
62 device = "my-user@example.com:/my-dir/";
63 fsType = "sshfs";
64 options =
65 [ # Filesystem options
66 "allow_other" # for non-root access
67 "_netdev" # this is a network fs
68 "x-systemd.automount" # mount on demand
69
70 # SSH options
71 "reconnect" # handle connection drops
72 "ServerAliveInterval=15" # keep connections alive
73 "IdentityFile=/var/secrets/example-key"
74 ];
75 };
76}
77</programlisting>
78 <para>
79 More options from <literal>ssh_config(5)</literal> can be given as
80 well, for example you can change the default SSH port or specify a
81 jump proxy:
82 </para>
83 <programlisting language="bash">
84{
85 options =
86 [ "ProxyJump=bastion@example.com"
87 "Port=22"
88 ];
89}
90</programlisting>
91 <para>
92 It’s also possible to change the <literal>ssh</literal> command
93 used by SSHFS to connect to the server. For example:
94 </para>
95 <programlisting language="bash">
96{
97 options =
98 [ (builtins.replaceStrings [" "] ["\\040"]
99 "ssh_command=${pkgs.openssh}/bin/ssh -v -L 8080:localhost:80")
100 ];
101
102}
103</programlisting>
104 <note>
105 <para>
106 The escaping of spaces is needed because every option is written
107 to the <literal>/etc/fstab</literal> file, which is a
108 space-separated table.
109 </para>
110 </note>
111 <section xml:id="sec-sshfs-troubleshooting">
112 <title>Troubleshooting</title>
113 <para>
114 If you’re having a hard time figuring out why mounting is
115 failing, you can add the option
116 <literal>"debug"</literal>. This enables a verbose log
117 in SSHFS that you can access via:
118 </para>
119 <programlisting>
120$ journalctl -u $(systemd-escape -p /mnt/my-dir/).mount
121Jun 22 11:41:18 workstation mount[87790]: SSHFS version 3.7.1
122Jun 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>
123Jun 22 11:41:19 workstation mount[87793]: my-user@example.com: Permission denied (publickey).
124Jun 22 11:41:19 workstation mount[87790]: read: Connection reset by peer
125Jun 22 11:41:19 workstation systemd[1]: mnt-my\x2ddir.mount: Mount process exited, code=exited, status=1/FAILURE
126Jun 22 11:41:19 workstation systemd[1]: mnt-my\x2ddir.mount: Failed with result 'exit-code'.
127Jun 22 11:41:19 workstation systemd[1]: Failed to mount /mnt/my-dir.
128Jun 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.
129</programlisting>
130 <note>
131 <para>
132 If the mount point contains special characters it needs to be
133 escaped using <literal>systemd-escape</literal>. This is due
134 to the way systemd converts paths into unit names.
135 </para>
136 </note>
137 </section>
138 </section>
139</section>