Como montar accesos SFTP

De GiLUG
Dreceres ràpides: navegació, cerca

Català - Castellano - English


SFTP es el sistema de transferencia de ficheros que, a diferencia del FTP tradicional, pasa por una conexión segura SSH, con la que toda la comunicación es cifrada y se comprueba la autenticidad del servidor. Montar un acceso a un directorio remoto implica poder acceder a las carpetas y ficheros de aquél ordenador remoto de forma plenamente integrada en nuestro ordenador, como si aquella carpeta estuviera en nuestro ordenador local.

Con los detalles de esta guía, también el administrador de servidores-web puede sustituir completamente el servicio FTP por SFTP en el mantenimiento de contenidos-web. El tutorial ha sido probado con distribuciones Debian, pero con poca diferencia puede servir para otras distribuciones GNU/Linux y Unix que utilicen el mismo software.

Equipo servidor

Una alternativa más fácil para el servidor es el ayudante para accesos SFTP enjaulados:
RemCage

El servidor es el sistema/ordenador que contiene las carpetas y ficheros a servir, es decir, el almacén accesible desde otros ordenadores.

  • Almenos se debe tener instalado el paquete openssh-server, y también el paquete libpam-umask o libpam-modules
  • Al fichero /etc/pam.d/sshd se le debe añadir (no hace falta reiniciar ningun servicio):
# 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

Lo cual establece una máscara en el servidor sobre la máscara del cliente a la hora de crear ficheros y directorios. Esto significa que si por ejemplo en el servidor-pam hay una umask=0077 y el cliente usa umask=0227, los elementos seran creados con umask=0277 (el servidor tapa al cliente), sin perjuicio de que el cliente cambie después los atributos (chmod). En resumen: es para que se puedan crear carpetas y documentos con permisos para nosotros y los demás.

Si en el sistema no tenemos un fichero /etc/pam.d/sshd entonces podemos escribir las opciones en el genérico /etc/pam.conf

  • Nota1: Abriendo del todo la máscara del servidor-pam (umask=0000), los elementos se crean de forma transparente con los permisos del usuario-cliente (su umask local), así que es en el equipo-cliente donde mejor se pueden configurar.
  • Nota2: Por internet encontrará documentado el truco de reemplazar en el fichero /etc/ssh/sshd_config del servidor la llamada a sftp-server o internal-sftp por un script, que primero ejecute umask y luego haga la llamada a sftp-server, pero esto no puede funcionar con SFTP enjaulado en donde no se habilita Shell que pueda ejecutar scripts.

SFTP enjaulado

Conectando por SFTP a un servidor tal cual, se pueden explorar los directorios del sistema como si estuviéramos en una sesión local o por terminal SSH. Para que esto no ocurra, debemos establecer un directorio en concreto que el cliente verá como si fuera la raíz del servidor (/), e impedir las sesiones de terminal/consola.

  • Requisito: OpenSSH server versión 4.8 o superior.
  • Establecemos un grupo de usuarios que sólo tendran acceso por SFTP (sin Shell), y el directorio para sus rutas de acceso:
groupadd sftponly
mkdir /etc/sftp-roots.d
  • En el fichero /etc/ssh/sshd_config deshabilitar la línea "Subsystem sftp ..." existente (insertando # como primer carácter), y añadir al final del fichero:
Subsystem       sftp    internal-sftp
Match Group sftponly
	# Variables para ChrootDirectory: %h ($HOME) %u ($USERNAME)
	ChrootDirectory /etc/sftp-roots.d/%u
	ForceCommand internal-sftp -u 0002
	AllowTcpForwarding no
	X11Forwarding no
  • Reiniciamos el servicio SSH para que funcionen los cambios (no se cortan las sesiones SSH abiertas):
service ssh restart

Crear usuarios y directorios enjaulados

Las cuentas de usuario creadas con las siguientes instrucciones seran las afectadas por el sistema de enjaulado. No se les crea un perfil/directorio propio en /home (opcional) ni se les permite acceder via terminal/SSH (restricción necesaria). En este ejemplo el usuario se llamará "narcis" y cuando conecte accederá a la carpeta /mnt/misdatos :

NuevoUsuario="narcis"
Destino="/mnt/misdatos"
ln -s $Destino /etc/sftp-roots.d/$NuevoUsuario
adduser  --gecos "" --no-create-home --home /etc/sftp-roots.d/$NuevoUsuario --ingroup sftponly $NuevoUsuario
usermod -s /bin/false $NuevoUsuario
chown root $Destino
chmod go-w $Destino

Véase que el directorio de acceso (en el ejemplo /mnt/misdatos) debe estar protegido por el superusuario (root), y ahí mismo el usuario externo (en el ejemplo "narcis") no podrá crear ni modificar nada. Se le pueden dar permisos de escritura en un subdirectorio, como por ejemplo "escuela":

CarpetaModificable="/mnt/misdatos/escuela"
mkdir $CarpetaModificable
chown $NuevoUsuario:sftponly $CarpetaModificable
chmod ug+rwX $CarpetaModificable

Crear usuarios y directorios normales

No está de más enseñar la fórmula simple sin enjaular y con perfil propio en /home , de manera que determinados usuarios puedan acceder a cualquier directorio como un usuario local (sin privilegios de administración), y con la posibilidad de entrar via terminal SSH.

NuevoUsuario="narcis"
adduser  --gecos "" --add_extra_groups $NuevoUsuario

En este caso se le crea un típico directorio de usuario en /home/narcis , a donde irá a parar si se conecta por terminal SSH. Por SFTP la raíz (/) es la misma que el servidor, así que para montar desde el equipo cliente se debería especificar /home/narcis

  • Nota: Los usuarios normales también pueden iniciar sesión local, es decir, que pueden acceder utilizando físicamente la pantalla y el teclado del servidor. Si es un ordenador de escritorio el que hace de servidor, puede que sea mejor crear los usuarios con la herramienta del entorno gráfico.

Equipo cliente

El cliente es el software/ordenador que accederá a las carpetas y ficheros de la red, es decir, el que no tiene los datos y se quiere conectar al servidor. Siempre se podría conectar de forma sencilla con un programa-cliente de FTP como gFTP, FileZilla o similar, pero esta guía es para tenerlo montado e integrado para todas las aplicaciones.

  • Almenos se debe tener instalado el paquete sshfs
  • Hay que habilitar la opción user_allow_other en el fichero /etc/fuse.conf para permitir que el usuario normal monte por sí mismo la conexión.
Normalmente encontraremos esta opción deshabilitada con una marca de comentario (#) por delante:
#user_allow_other
que debemos quitar para dejar:
user_allow_other
  • Crear un fichero /usr/local/bin/sshfs-mount.sh con este contenido que he elaborado:
#!/bin/sh
# Conectar un punto de montaje SFTP
# Version 2010.12.28
# Copyright (GNU GPL) Narcis Garcia

RutaLocal="$1"
ContrasenyaRemota="$2"
RestringirAUsuario="$3"

Conectar ()
{
	Result=0
	YaMontado="$(mount | grep " $RutaLocal ")"
	if [ "$YaMontado" = "" ] ; 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 "$RutaLocal" ] ; then  # Crear el punto de montaje con permisos
			mkdir -p "$RutaLocal"
			if [ "$RestringirAUsuario" = "" ] ; then
				chmod ugo=rwX "$RutaLocal"
			else
				GrupoLocal="$(id -ng $RestringirAUsuario)"
				chown $RestringirAUsuario:$GrupoLocal "$RutaLocal"
				chmod u=rwX,g=rX,o= "$RutaLocal"
			fi
		fi
		echo "$ContrasenyaRemota" | mount "$RutaLocal" -o password_stdin,allow_other,StrictHostKeyChecking=no
		Result=$?
		if [ $Result -eq 0 ] ; then
			echo "	Correcto"
		else
			echo "($Result) La instruccion era:"
			echo "echo \"\$ContrasenyaRemota\" | mount \"$RutaLocal\" -o password_stdin,allow_other,StrictHostKeyChecking=no"
		fi
	else
		echo "El directorio ya estaba montado en $(DevuelvePalabra () { echo $3; }; DevuelvePalabra $YaMontado)"
		Result=1
	fi
	return $Result
}
BitacoraTemp="/tmp/sshfs_$(echo "$RutaLocal" | tr -s "/" "-" | tr -s " " "_").log"
MensajeInicial="Conectando $RutaLocal al servidor$(if [ -f $BitacoraTemp ] ; then echo " (se habia interrumpido)" ; fi)... "
printf "$MensajeInicial"
Linea1Log="$(date +'%Y-%m-%d %H:%M:%S') $MensajeInicial"
Conectar > $BitacoraTemp 2>&1
cat $BitacoraTemp
if [ $(id -g) -eq 0 ] ; then
	echo "$Linea1Log$(cat $BitacoraTemp)" >>/var/log/sshfs.log
fi
rm $BitacoraTemp
  • Dar permisos de ejecución al nuevo fichero:
chmod a+x /usr/local/bin/sshfs-mount.sh
  • Crear un fichero /etc/network/if-down.d/sshfs-umounts (sin extensión .sh) con este contenido que he elaborado:
#!/bin/sh
# Desconectar todos los puntos de montaje SFTP
# Version 2010.10.10
# Copyright (GNU GPL) Narcis Garcia
# Nota: Solamente funciona con rutas remotas y locales sin espacios

Desconectar ()
{
	local RutaLocal="$1"
	umount "$RutaLocal"
	Result=$?
	if [ $Result -eq 0 ] ; then
		echo "	Correcto"
	else
		echo "($Result)"
	fi
	return $Result
}

TextActual="$(date +'%Y-%m-%d %H:%M:%S') Se desconectaran los accesos SFTP:"
echo "$TextActual"
if [ $(id -g) -eq 0 ] ; then
	echo "$TextActual" >>/var/log/sshfs.log
fi
ListaMontados="$(mount | grep -e " fuse\.sshfs ")"
if [ "$ListaMontados" != "" ] ; then
	IFS=$(printf "\n\b") ; for LineaActual in $ListaMontados ; do unset IFS
		RutaLocal="$(DevuelvePalabra () { echo $3; }; DevuelvePalabra $LineaActual)"
		BitacoraTemp="/tmp/sshfs_$(echo "$RutaLocal" | tr -s "/" "-" | tr -s " " "_").log"
		MensajeInicial="Desconectando $RutaLocal del servidor $(if [ -f $BitacoraTemp ] ; then echo "(se habia interrumpido)" ; fi)... "
		printf "$MensajeInicial"
		Linea1Log="$(date +'%Y-%m-%d %H:%M:%S') $MensajeInicial"
		Desconectar "$RutaLocal" > $BitacoraTemp 2>&1
		cat $BitacoraTemp
	if [ $(id -g) -eq 0 ] ; then
		echo "$Linea1Log$(cat $BitacoraTemp)" >>/var/log/sshfs.log
		rm $BitacoraTemp
	done
else
	TextActual="No habia puntos SFTP montados."
	echo "$TextActual"
	if [ $(id -g) -eq 0 ] ; then
		echo "$TextActual" >>/var/log/sshfs.log
	fi
fi
  • Dar permisos de ejecución al nuevo fichero:
chmod a+x /etc/network/if-down.d/sshfs-umounts

Crear accesos para usuario

Con esta receta preparamos el montaje de una unidad para que la utilice un usuario del equipo cliente.

  • Comprobamos el número de usuario (uid) y el número de grupo (gid) del usuario que utilizará el acceso:
id usuariolocal

Podemos obtener por ejemplo:

uid=1000(ngarcia) gid=1000(grupogarcia) grups=24(cdrom),46(plugdev),1000(grupogarcia)

de donde obtenemos el número de usuario (1000) y el número de grupo principal (1000)

  • El usuario y contraseña que utilizaremos para conectar deben existir en el servidor. Añadimos la entrada correspondiente en el fichero /etc/fstab , suponiendo en el ejemplo que el servidor se encuentra en 192.168.1.5 y que lo montaremos en la carpeta /home/ngarcia/Documentos/escuela del ordenador-cliente.
# escuela-ngarcia
narcis@192.168.1.5:/escuela /home/ngarcia/Documentos/escuela 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
  • Crear un fichero /etc/network/if-up.d/sshfs-mounts (sin extensión .sh) con un contenido como este:
#!/bin/sh
# Sintaxis de las entrades: sshfs-mount.sh "ruta local" "contraseñaremota" "restringir a usuario?"

sshfs-mount.sh "/home/ngarcia/Documentos/escuela" "1234" "ngarcia"

Nota: Si queremos montar más carpetas, sólo necesitamos añadir entradas a este mismo fichero.

  • Medidas de seguridad, para esconder un poco la contraseña y para restringir el acceso desde otros usuarios:
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/Documentos/escuela
chown ngarcia:grupogarcia /home/ngarcia/Documentos/escuela
chmod u=rwX,g=rX,o= /home/ngarcia/Documentos/escuela

Aun así, un usuario del ordenador con permisos de administración (o desde un Live-CD) puede ver el contenido de /etc/network/if-up.d/ , con las contraseñas que haya escritas. La manera de blindar esto es que el sistema-cliente esté en una partición cifrada, o separar ese dato en un volumen cifrado.

  • Preparación de permisos predeterminados del usuario-cliente
    • Cuando se prepara un acceso SFTP para un(os) usuario(s), se establece umask en ~/.bashrc y ~/.profile - Sencillamente al final de cada fichero se añade/reemplaza una línea con una instrucción tal como:
umask 0027
~/.bashrc es específico para los terminales/scripts Shell interpretados por Bash en una sesión de usuario.
~/.profile es para los terminales/scripts de otros intérpretes como Dash (no-Bash), en una sesión de usuario.
      • Si quieres saber qué intérprete usa el sistema de forma predeterminada (por ejemplo con scripts), ejecuta en un terminal: ls -l /bin/sh
      • Si quieres saber qué intérprete utilizan las sesiones de terminal/consola, ejecuta en un terminal: echo $SHELL
    • Cuando se prepara un acceso SFTP para todo el mundo en el sistema, se establece umask en /etc/profile
La configuración de /etc/profile vale de forma predeterminada para las sesiones Shell (de terminal/consola y de todos los scripts del sistema). Sencillamente al final del fichero se añade/reemplaza una línea con una instrucción tal como:
umask 0027
    • Para ambos tipos de acceso, establecerlo en los diferentes ficheros de configuracion mencionados.

Nota para navegantes: En /etc/login.defs hay un parámetro UMASK, pero sólo vale para las sesiones no-shell (por ejemplo conexiones PPP desde fuera). Las sesiones gráficas y SFTP se consideran llamadas desde Shell-script.

  • Para que todos los cambios se apliquen correctamente (básicamente umask) hay que reiniciar el sistema.
Alternativa: no montar automáticamente

Si no queremos que la conexión se monte automáticamente al arrancar el ordenador y tener red, símplemente debemos omitir la entrada en el fichero /etc/network/if-up.d/sshfs-mounts y podremos montar de dos maneras:

1. Con la instrucción:
mount /home/ngarcia/Documentos/escuela
2. O bién mediante el explorador de carpetas. En escritorios como Gnome o KDE suelen aparecer las unidades sin montar de /etc/fstab , y accediendo a estos Lugares, es decir, abriendo los accesos que nos muestra el entorno gráfico, nos pedirá la contraseña remota y nos montará la carpeta.

Opciones de FUSE/SSHFS (anexo de referencia)

Aquí una tabla explicativa sobre algunas opciones suministradas a la entrada de montaje e instrucción de conexión:

user (por investigar)
noauto Específica de /etc/fstab para que mount no monte automáticamente, sinó que espere la instrucción del usuario.
La alternativa sería "auto", pero con las decoraciones de inicio seguramente no se podría suministrar la contraseña.
ConnectionAttempts=3 Número de intentos de conexión. Para una red local (cableada o inalambrica) es suficiente con 3. Puede convenir incrementarlo para enlaces a través de internet o con equipos lentos.
ServerAliveInterval=15 Intervalo de envío de la señal "keep alive" al servidor para que no corte la conexión durante la falta de actividad. En segundos.
ServerAliveCountMax=5760 Número total de envíos de "keep alive" que se haran. Después se dejaría morir la conexión si no hay actividad.
idmap=user Habilitar la traducción del UID usado entre cliente y servidor: si por ejemplo su ID de usuario local en uso es 1034 pero el ID de usuario remoto de la conexión es 1002, entonces todos los ficheros pertenecientes remotamente a 1002 seran vistos localmente pertenecientes a 1034. No hay traducción/comparación de nombres. Por defecto es deshabilitado (idmap=none).
  • No use idmap=user si quiere ver u obtener ficheros remotos estrictamente con el número de ID propietario:grupo original.
uid=1000 Identificador de usuario del sistema-cliente a traducir por el identificador del usuario remoto
Con la verión probada (SSHFS 2.2 FUSE 2.8.1), cualquier usuario remoto (propietario) se ve como si fuera el usuario especificado.
Actúa como un disfraz para la lectura (servidor->cliente), y una traducción para la escritura (cliente->servidor).
  • Aún con el disfraz, el servidor aplica sus permisos de forma realista; el pequeño inconveniente desde el cliente puede ser que no parezca coherente una restricción si se ve todo de su propiedad.
  • No use esta opción si quiere ver o obtener ficheros remotos con el propietario original. De todas formas no hay traducción/comparación de nombres, pues un propietario remoto 1234 (UID remoto para jorge) puede ser visto aqui como 1234 (UID local para miguel).
gid=1000 Identificador de grupo del sistema-cliente a traducir por el identificador de grupo de usuario remoto.
Con la verión probada (SSHFS 2.2 FUSE 2.8.1), cualquier grupo remoto se ve como si fuera el grupo especificado.
Actúa como un disfraz para la lectura (servidor->cliente), y como un traducción para la escritura (cliente->servidor).
  • Aún con el disfraz, el servidor aplica sus permisos de forma realista; el pequeño inconveniente desde el cliente puede ser que no parezca coherente una restricción si se ve todo de su mismo grupo.
  • No use esta opción si quiere ver o obtener ficheros remotos con el grupo original. De todas formas no hay traducción/comparación de nombres, igual que pasa con UID.
umask=0027 Máscara de permisos para ficheros y subdirectorios
Con la verión probada (SSHFS 2.2 FUSE 2.8.1), el cliente ve disfrazados todos los elementos como si llevaran estos permisos (0027 -> rw-r-----).
Para ver los permisos tal como son en el servidor, hay que omitir este parámetro.
Nota: aún con el disfraz, el servidor aplica sus permisos de forma realista; el inconveniente puede ser que desde el cliente no parezca coherente una restricción si se ven permisos favorables.
default_permissions Habilita el control de permisos (no de usuarios-propietarios) desde el sistema-cliente, en lugar de mostrar atributos desactualizados y esperar respuestas de error del servidor (R/W)
intr Permite que las peticiones sean interrumpidas
transform_symlinks Traducir los enlaces absolutos en el servidor por enlaces relativos que funcionen desde el cliente
follow_symlinks (relacionado con transform_symlinks)
port=22 Puerto TCP de conexión al servidor SSH (por defecto 22)
password_stdin Esperar la contraseña vía stdin:
echo "micontraseña" | mount /mnt/punto -o password_stdin

Es la manera de enviar automáticamente la contraseña desde un programillo-script. Conviene utilizarla sólo con la llamada de instrucción; si se pone en fstab puede ser problemático montando mediante entorno gráfico.

allow_other Permitir el acceso al contenido montado a usuarios normales (no root)
Conviene utilizarla sólo en la llamada de instrucción; ponerlo en fstab es incompatible con que lo monte el usuario normal.
StrictHostKeyChecking=no Asumir como buena la primera clave RSA que devuelva el servidor.
Conviene utilizarlo sólo en la llamada de instrucción, para poder gestionar mejor un error de RSA diferente.

Más información con:

man sshfs
man ssh_config

Funcionamento general de umask

Es una máscara-plantilla. Es como el inverso de chmod, ya que sirve para tapar los permisos a establecer en la creación de ficheros y subdirectorios. Cuanto más alto es el valor octal, más restrictivo (a nivel binario, un bit 1 tapa un atributo y un bit 0 lo permite).

0 permite rwX
1 permite rw-
2 permite r-X
3 permite r--
4 permite -wX
5 permite -w-
6 permite --X
7 permite ---
  • Ejemplos:
Entonces con umask umask 0022 se crean ficheros con permisos rw-r--r-- y subdirectorios con rwxr-xr-x
con umask 0007 se crean ficheros con permisos rw-rw---- y subdirectorios con rwxrwx---
con umask 0027 se crean ficheros con permisos rw-r----- y subdirectorios con rwxr-x---
  • El cero inicial se pone para indicar que está escrito en octal (0022 en lugar de 022)
  • Todo esto sin perjuicio de que se puedan cambiar luego los atributos (chmod)

Sincronización, copias de seguridad

Notas:

  • La única forma de preservar las pertenencias de propietario y al mismo tiempo que coincidan los nombres remotos con los locales, es tener los mismos IDs numéricos en el sistema local que en el servidor remoto. Esto se consigue efectivamente cuando se tiene idéntico contenido en estos ficheros:
/etc/passwd
/etc/group

Ver también

Pendiente de documentar

  • Desarrollar más sshfs-mount.sh para que también monte carpetas que no tienen entrada en "fstab" (esto evitaría la aparición de unidades de disco en los exploradores de carpetas, o facilitaría tener una herramienta-script portable)
  • "transform_symlinks" no parece funcionar, y entonces no se puede comprobar la eficacia de "follow_symlinks"
  • Está por ver la utilidad de la opción "user"
  • Tener en cuenta que los programillos en /etc/network/if-up.d y /etc/network/if-down.d son ejecutados para cada interfaz de red.