How to mount SFTP accesses
Català - Castellano - English
SFTP is the file transfer system that, differencing from traditional FTP, passes through an SSH secure connection, and then all the communication is ciphered and server authenticity is checked. Mounting an acces to a remote directory implies to allow accessing to the folders and files in that remote computer, in a fully integrated way in our computer, as if that folder was in our local computer.
With this guide's details, also the web-server administrator can completely replace FTP service by SFTP for the web content maintenance. The tutorial has been tested with Debian distributions, but with few difference can be useful for other GNU/Linux distributions and Unix with same software.
Contingut
Server computer
An easier alternative for server is the Jailed SFTP accesses helper: RemCage
The server is the system/computer that contains the folders and files to serve, that is, the accessible store from other computers.
- At least the package openssh-server must be installed, and also the package libpam-umask or libpam-modules
- In the file /etc/pam.d/sshd we need to add (it's not necessary to restart any service):
# Default umask mask for SSH/SFTP sessions # Shell sessions: Settings in /etc/profile or ~/.bashrc or ~/.profile will replace this session optional pam_umask.so umask=0002
This sets a mask on the server over the mask from the client, when creating files and directories. This means that, in a case with pam-server having umask=0077 and client uses umask=0227 , the elements will be created with umask=0277 (server shades client), without detriment for the client when changes attributes after (chmod). In short: this is to create folders and documents with permissions for us and others.
If in the system we haven't a file /etc/pam.d/sshd then we can write the options in the generic /etc/pam.conf
- Note1: Opening the mask at all in the pam-server (umask=0000), the elements are created transparently with the client-user permissions (his local umask), then is in the client-computer where better can be configured.
- Note2: In internet you can find the trick of replacing in the server's /etc/ssh/sshd_config file the call to sftp-server or internal-sftp by a script, that first executes umask and after makes the call to sftp-server, but this cannot work with caged SFTP (chrooted), where Shell is not enabled to allow script execution.
Chroot SFTP (caged)
Connecting with SFTP to a server as is, system directories can be explored as if we were in a local session or in a SSH terminal. To avoid this to happen, is needed to set up an specific directory that the client will see as if it were server's root (/), and preventing terminal/console sessions.
- Requirement: OpenSSH server version 4.8 or superior.
- Set a user group that will have only SFTP access (without Shell), and the directory for their access paths:
groupadd sftponly mkdir /etc/sftp-roots.d
- In the file /etc/ssh/sshd_config disable the existing line "Subsystem sftp ..." (inserting # as first character), and add at the end of file:
Subsystem sftp internal-sftp Match Group sftponly # Variables for ChrootDirectory: %h ($HOME) %u ($USERNAME) ChrootDirectory /etc/sftp-roots.d/%u ForceCommand internal-sftp -u 0002 AllowTcpForwarding no X11Forwarding no
- Restart SSH service to make changes work (open SSH sessions aren't interrupted):
service ssh restart
Create caged users and directories
The user accounts created with the following instructions will be the affected by the chroot system. It's not created an own directory/profile at /home for them (optional) nor are allowed to access via terminal/SSH (necessary restriction). In this example the user will be called "narcis" and when he connects will access to the folder /mnt/mydata :
NewUser="narcis" Destination="/mnt/mydata" ln -s $Destination /etc/sftp-roots.d/$NewUser adduser --gecos "" --no-create-home --home /etc/sftp-roots.d/$NewUser --ingroup sftponly $NewUser usermod -s /bin/false $NewUser chown root $Destination chmod go-w $Destination
Note that the access directory (in the example /mnt/mydata) must be protected by the superuser (root), and the external user (in the example "narcis") cannot create nor modify anything there. Write permissions can be given for a subdirectory, as for example "school":
ModifiableFolder="/mnt/mydata/school" mkdir $ModifiableFolder chown $NewUser:sftponly $ModifiableFolder chmod ug+rwX $ModifiableFolder
Create normal users and directories
It is worth showing the simple formula without caging and with own profile in /home , so certain users can access to any directory as a local user (without administration privileges), and with the possibility of entering via SSH terminal.
NewUser="narcis" adduser --gecos "" --add_extra_groups $NewUser
In this case, we create a user typical directory in /home/narcis , where will go if connects by SSH terminal. By SFTP the root (/) is the same as the server, so for mounting from the client computer is needed to specify /home/narcis
- Note: Normal users can also open a local session, that is, they can access using physically server's screen and the keyboard. If it's a desktop computer working as server, may be better to create users with the graphical environment tool.
Client computer
The client is the software/computer that will access to the folders and files in the network, that is, who doesn't have the data and wants to connect to the server. Someone could always connect in a simple way with a client FTP-software as gFTP, FileZilla or similar, but this guide is intended to have it mounted and integrated for all applications.
- At least the package sshfs must be installed.
- We need to enable the option user_allow_other in the file /etc/fuse.conf to allow self-mounting the connection by the normal user.
- Normally we will find this option disabled with a comment mark (#) before:
#user_allow_other
- that we must remove to leave as:
user_allow_other
- Create a file /usr/local/bin/sshfs-mount.sh with this content I elaborated:
#!/bin/sh # Connect an SFTP mount point # Version 2010.12.28 # Copyright (GNU GPL) Narcis Garcia LocalPath="$1" RemotePassword="$2" RestrictToUser="$3" Connect () { Result=0 AlreadyMounted="$(mount | grep " $LocalPath ")" if [ "$AlreadyMounted" = "" ] ; then if [ "$(which sshfs)" = "" ] ; then apt-get install -qq -y sshfs fi if [ "$(which mount.fuse.sshfs)" = "" ] ; then ln -s $(which mount.fuse) /sbin/mount.fuse.sshfs fi if [ ! -d "$LocalPath" ] ; then # Create the mount point with permissions mkdir -p "$LocalPath" if [ "$RestrictToUser" = "" ] ; then chmod ugo=rwX "$LocalPath" else LocalGroup="$(id -ng $RestrictToUser)" chown $RestrictToUser:$LocalGroup "$LocalPath" chmod u=rwX,g=rX,o= "$LocalPath" fi fi echo "$RemotePassword" | mount "$LocalPath" -o password_stdin,allow_other,StrictHostKeyChecking=no Result=$? if [ $Result -eq 0 ] ; then echo " Right" else echo "($Result) The command was:" echo "echo \"\$RemotePassword\" | mount \"$LocalPath\" -o password_stdin,allow_other,StrictHostKeyChecking=no" fi else echo "The directory was already mounted on $(ReturnWord () { echo $3; }; ReturnWord $AlreadyMounted)" Result=1 fi return $Result } TempLog="/tmp/sshfs_$(echo "$LocalPath" | tr -s "/" "-" | tr -s " " "_").log" FirstMessage="Connecting $LocalPath to the server$(if [ -f $TempLog ] ; then echo " (was interrupted)" ; fi)... " printf "$FirstMessage" Line1Log="$(date +'%Y-%m-%d %H:%M:%S') $FirstMessage" Connect > $TempLog 2>&1 cat $TempLog if [ $(id -g) -eq 0 ] ; then echo "$Line1Log$(cat $TempLog)" >>/var/log/sshfs.log fi rm $TempLog
- Give execution permissions to the new file:
chmod a+x /usr/local/bin/sshfs-mount.sh
- Create a file /etc/network/if-down.d/sshfs-umounts (without extension .sh) with this content I elaborated:
#!/bin/sh # Disconnect all the SFTP mount points # Version 2010.10.10 # Copyright (GNU GPL) Narcis Garcia # Note: Only works with local and remote paths without spaces Disconnect () { local LocalPath="$1" umount "$LocalPath" Result=$? if [ $Result -eq 0 ] ; then echo " Right" else echo "($Result)" fi return $Result } CurrentText="$(date +'%Y-%m-%d %H:%M:%S') The SFTP accesses will be disconnected:" echo "$CurrentText" if [ $(id -g) -eq 0 ] ; then echo "$CurrentText" >>/var/log/sshfs.log fi MountedList="$(mount | grep -e " fuse\.sshfs ")" if [ "$MountedList" != "" ] ; then IFS=$(printf "\n\b") ; for CurrentLine in $MountedList ; do unset IFS LocalPath="$(ReturnWord () { echo $3; }; ReturnWord $CurrentLine)" TempLog="/tmp/sshfs_$(echo "$LocalPath" | tr -s "/" "-" | tr -s " " "_").log" FirstMessage="Disconnecting $LocalPath from server $(if [ -f $TempLog ] ; then echo "(was interrupted)" ; fi)... " printf "$FirstMessage" Line1Log="$(date +'%Y-%m-%d %H:%M:%S') $FirstMessage" Disconnect "$LocalPath" > $TempLog 2>&1 cat $TempLog if [ $(id -g) -eq 0 ] ; then echo "$Line1Log$(cat $TempLog)" >>/var/log/sshfs.log fi rm $TempLog done else CurrentText="There was no mounted SFTP point." echo "$CurrentText" if [ $(id -g) -eq 0 ] ; then echo "$CurrentText" >>/var/log/sshfs.log fi fi
- Give execution permissions to the new file:
chmod a+x /etc/network/if-down.d/sshfs-umounts
Create accesses for user
With this recipe we prepare the mount of a unit to be used by a user in the client computer.
- Check the user number (uid) and the group number (gid) from the user who will use the access:
id usuarilocal
We can obtain for example:
- uid=1000(ngarcia) gid=1000(grupgarcia) grups=24(cdrom),46(plugdev),1000(grupgarcia)
from where to get the user number (1000) and the main group number (1000)
- The user and password we will use to connect must esist in the server. Add the appropiate entry to the file /etc/fstab , supposing at the example that the server is on 192.168.1.5 and that we will mount it on the folder /home/ngarcia/Documents/school in the client computer.
# school-ngarcia narcis@192.168.1.5:/school /home/ngarcia/Documents/school fuse.sshfs uid=1000,gid=1000,port=22,ConnectionAttempts=3,ServerAliveInterval=15,ServerAliveCountMax=5760,noauto,user,idmap=user,default_permissions,intr,transform_symlinks,follow_symlinks 0 0
- Create a file /etc/network/if-up.d/sshfs-mounts (without extension .sh) with a content as the following:
#!/bin/sh # Syntax for entries: sshfs-mount.sh "local path" "remote password" "restrict to user?" sshfs-mount.sh "/home/ngarcia/Documents/school" "1234" "ngarcia"
Note: If we want to mount more folders, we only need to add entries to the same file.
- Security measures, to hidden a little the password and to restrict the access from other users:
chown root:root /etc/network/if-up.d/sshfs-mounts /etc/network/if-down.d/sshfs-umounts chmod u=rwx,go= /etc/network/if-up.d/sshfs-mounts /etc/network/if-down.d/sshfs-umounts mkdir /home/ngarcia/Documents/school chown ngarcia:grupgarcia /home/ngarcia/Documents/school chmod u=rwX,g=rX,o= /home/ngarcia/Documents/school
Still a user in the same computer with administration permissions (or from a Live-CD) can see the content in /etc/network/if-up.d/ , with the written passwords. The way to armor this, is the client-system to be in an encrypted partition, or this data separated in an encrypted volume.
- Client-user default permission preparation
- When an SFTP access is prepared for one/some user(s), umask is set in ~/.bashrc and ~/.profile - At the end of each file simply add/replace a line with a command as:
umask 0027
- ~/.bashrc is specific to Shell scripts/terminals interpreted by Bash in a user session.
- ~/.profile is to Shell scripts/terminals in other interpreters as Dash (no-Bash), in a user session.
- If you want to know which shell uses the system as default (for example with scripts), execute in a terminal: ls -l /bin/sh
- If you want to know which shell use the terminal/console sessions, execute in a terminal: echo $SHELL
- When an STP access is prepared for everybody in the system, umask is set in /etc/profile
- The configuration in /etc/profile avails by default for the Shell sessions (in terminal/console and all the system scripts). At the end of the file simply add/replace a line with a command as:
umask 0027
- For both access types, set it at the different configuration files mentioned.
Note for navigators: In /etc/login.defs there is an UMASK parameter, but only avails for non-shell sessions (for example PPP connections from outside). The graphical sessions and SFTP are considered to be called from Shell-script.
- To apply right all the changes (basically umask), the system must be restarted.
Alternative: not automatically mount
If we don't want the connection to be mounted automatically on computer startup and network access, simply we must avoid the entry in the file /etc/network/if-up.d/sshfs-mounts and two ways for mounting will be possible:
- 1. With the command:
mount /home/ngarcia/Documents/school
- 2. Or with the folders explorer. In desktops such as Gnome or KDE often appear the units not mounted from /etc/fstab , and going to these Places, that is, opening the accesses shown by the graphic environment, it will ask for the remote password and will mount the folder.
FUSE/SSHFS options (reference annex)
Here an explanation table about some supplied options in the mount entry and execution command:
user | (for investigate) |
noauto | Specific to /etc/fstab for not to automatically mount, but it waits the user command. The alternative should be "auto", but with the startup decorations probably the password couldn't be supplied. |
ConnectionAttempts=3 | Connection tries number. For a local network (wired or wireless) it's enough with 3. Can be appropiate to increment this for links through internet or with slow computers. |
ServerAliveInterval=15 | Time for each "keep alive" signal send to the server, to not to break the connection when no activity. In seconds. |
ServerAliveCountMax=5760 | Total number of "keep alive" sends to do. After, the connection can let die if hasn't activity. |
idmap=user | Enable UID translation used between client and server: if for example your local running user ID is 1034 but remote connecting user ID is 1002, then all files owned remotely by 1002 will be seen localy owned by 1034. There is no names translation/comparison. Default is disabled (idmap=none).
|
uid=1000 | Client-system user identificator to translate for the remote user identificator With the tested version (SSHFS 2.2 FUSE 2.8.1), any remote user (owner) is seen as if was the specified user. Acts as a disguise for reading (server->client), and a translation for writing (client->server).
|
gid=1000 | Client-system group identificator to translate for the remote usergroup identificator. With the tested version (SSHFS 2.2 FUSE 2.8.1), any remote group is seen as if was the specified group. Acts as a disguise for reading (server->client), and a translation for writing (client->server).
|
umask=0027 | Permission mask for files and subdirectories. With the tested version (SSHFS 2.2 FUSE 2.8.1), it makes client to see all the elements disguised as if have this permissions (0027 -> rw-r-----). To see the permissions agreed to the server, avoid this parameter.
|
default_permissions | Enables permission control (not users-owners) from the client-system, in place of showing outdated attributes and expect error responses from server (R/W) |
intr | Allow requests to be interrupted |
transform_symlinks | Translate the absolute links in the server for relative links to work from the client |
follow_symlinks | (related to transform_symlinks) |
port=22 | TCP port to use to connext to SSH server (22 by default) |
password_stdin | Expect the password via stdin:
echo "password" | mount /mnt/point -o password_stdin Is the way to automatically send the password from a script. Is convenient to use with the command call; if is put in fstab can be problematic when mounting in a graphical environment. |
allow_other | Normal users can access to the mounted content (not root) Is convenient to use it only in the command call; putting it to fstab is incompatible with self-mounting the connection by the normal user. |
StrictHostKeyChecking=no | Assume as acceptable the first RSA key returned by the server. Is convenient to use only in the command call, to allow a better error management with different RSA. |
More information with:
man sshfs man ssh_config
General working of umask
Is a template-mask. Is as a chmod inverse, because is used for shading the permissions to be set when creating files and directories. As higher is the octal value, more restrictive (at binary level a bit 1 shades an attribute and a bit 0 allows it).
0 allows rwX 1 allows rw- 2 allows r-X 3 allows r-- 4 allows -wX 5 allows -w- 6 allows --X 7 allows ---
- Examples:
- Then with umask 0022 files are created with permissions rw-r--r-- and subdirectories with rwxr-xr-x
- with umask 0007 files are created with permissions rw-rw---- and subdirectories with rwxrwx---
- with umask 0027 files are created with permissions rw-r----- and subdirectories with rwxr-x---
- Initial zero is put to note that it's written in octal (0022 instead of 022)
- All this is without detriment for changing attributes after (chmod)
Synchronization, backups
Notes:
- The only way to preserve ownerships and at the same time match remote names with local ones, is to have the same numeric IDs in the local system as in the remote server. This is effectively done when have identical contents in these files:
- /etc/passwd
- /etc/group
See also
- SSH Filesystem (official web)
- Secure Shell Filesystem (encyclopedia article)
Pending to document
- Develop more sshfs-mount.sh to mount also folders that haven't entry in "fstab" (this should avoid disk unit showing on the folder explorers, or should make easier to have a portable script-tool)
- "transform_symlinks" does not seem to work, and then cannot be tested the effectiveness of "follow_symlinks"
- Utility of "user" option is pending to see.
- Take care that scripts in /etc/network/if-up.d and /etc/network/if-down.d are executed for each network interface.