Conectar Visual Studio Code y Remote SSH con Synology

Hacía tiempo que no utilizaba Visual Studio Code directamente sobre mi Synology mediante Remote SSH.

Durante bastante tiempo fue una forma muy cómoda de trabajar: conectarme al NAS, abrir mis proyectos y editar directamente desde Visual Studio Code como si estuvieran en local.

Aunque en este caso lo voy a enfocar a mi Synology, realmente esta solución puede venir bien en más situaciones.

Por ejemplo:

  • máquinas antiguas cuyo sistema ya no cumple los requisitos de VS Code Server;
  • NAS o servidores donde prefieres no empezar a instalar y modificar librerías del sistema base;
  • entornos restringidos donde no quieres tocar directamente el host;
  • o simplemente cuando quieres mantener separado el entorno de desarrollo de la máquina que almacena o ejecuta tus servicios.

No significa que VS Code deje de utilizar recursos en la máquina remota: VS Code Server seguirá ejecutándose allí, en este caso dentro del contenedor. La ventaja es que no tengo que adaptar el sistema operativo del host a sus requisitos.

Y aquí es precisamente donde entra mi caso práctico: un Synology algo antiguo que quiero seguir utilizando para desarrollar directamente sobre mis proyectos.

El problema apareció cuando VS Code fue aumentando los requisitos necesarios para ejecutar VS Code Server en la máquina remota.

Al intentar conectar empecé a encontrarme con este mensaje:

The remote host may not meet VS Code Server's prerequisites for glibc and libstdc++

El problema era que mi Synology utiliza una versión de DSM cuyo entorno base lleva una glibc más antigua de la que requieren las versiones actuales de VS Code Server (no significa que todos los Synology tengan necesariamente el mismo problema).

La solución terminó siendo bastante sencilla al levantar un contenedor con Debian 12 que funciona como servidor SSH.

Con Visual Studio Code conectamos a ese contenedor mediante Remote SSH y dentro del contenedor monto las carpetas reales del NAS que quiero utilizar.

El flujo queda así:

Visual Studio Code
        ↓
    Remote SSH
        ↓
 Contenedor Debian 12
        ↓
 Carpetas reales del NAS

Además, desde ese mismo entorno puedo utilizar Git, Python y el Docker que ya está funcionando en el Synology.

Estructura



ssh-server/
├── Dockerfile
├── docker-compose.yaml
├── .env.example
├── .gitignore
├── authorized_keys.example
├── docs/
│   ├── como-usarlo.md
│   ├── comprobacion.md
│   └── troubleshooting.md
└── README.md
 
 
**Dejo el enlace de la repo con todos los archivos al final**

He preferido dejar algunas cosas configurables mediante .env, para no tener nombres, UID, GID o rutas personales repartidas por el Dockerfile y el Compose.

Preparativos

Antes de levantar el contenedor merece la pena preparar bien los permisos en Synology.

Mi recomendación es no reutilizar directamente el grupo administrators dentro del contenedor y crear un grupo específico dándole acceso únicamente a la carpeta que vas a utilizar.

Os pongo ejemplo:

  • Carpeta a compartir: /volume1/Proyectos
  • Crear desde DSM el grupo de usuarios developer y darle permisos a la carpeta.
  • Agregar usuario al grupo

Si quieres utilizar el contenedor para más carpetas puedes darle acceso a todas las que necesites.

Nota: Si creas el grupo desde DSM en la ventana de permisos de aplicaciones no es necesario seleccionar ninguna aplicación para este grupo.

Una vez creado, toca comprobar los datos del usuario y de los grupos para mantener los mismos identificadores dentro del contenedor.

Qué queremos saberComando
Información completa del usuarioid Mario
UIDid -u Mario
GID principalid -g Mario
IDs de todos sus gruposid -G Mario
Nombres de todos sus gruposid -Gn Mario
Consultar el grupo de desarrollogetent group developer

Por ejemplo, el comando: id Mario puede devolver algo parecido a:

uid=1021(Mario) gid=100(users) groups=100(users),61521(developer)

Y con el comando: getent group developer algo como:

developer:x:61521:Mario

Con esos datos ya podemos preparar la configuración del contenedor.

En la repo dejo también un .env.example y un authorized_keys.example para que sea más sencillo ver qué datos hacen falta y crear los ficheros reales sin empezar desde cero.

Puedes partir de ellos así:

cp .env.example .env
cp authorized_keys.example authorized_keys

El .env queda con este tipo de variables:

USER_NAME=tu_usuario
USER_UID=tu_uid
USER_GID=tu_gid
ADMIN_GID=gid_grupo_desarrollo
WORKSPACE_PATH=/ruta/de/tus/proyectos
SSH_KEYS_PATH=/ruta/de/tus/claves/.ssh
VariablePara qué sirve
USER_NAMEUsuario que se creará dentro del devbox
USER_UIDUID del usuario del Synology
USER_GIDGID principal del usuario
ADMIN_GIDGID del grupo al que hemos dado acceso a la carpeta de trabajo
WORKSPACE_PATHRuta real que queremos editar desde VS Code
SSH_KEYS_PATHRuta donde están las claves SSH que utilizaremos para Git

Aunque la variable se llama ADMIN_GID, en mi caso no le paso el grupo administrators de Synology, sino el GID del grupo específico que he creado para acceder a mis proyectos.

Los valores anteriores son solo un ejemplo. Utiliza los UID, GID y rutas obtenidos en tu propio sistema.

Así en GitHub puedo dejar todo preparado para copiarlo, pero sin publicar ni mis rutas, ni mis usuarios ni, evidentemente, ninguna clave.

Todo el código completo y los ficheros de ejemplo están disponibles en la repo pública, por lo que en este post me centro más en explicar las partes importantes que en copiar cada fichero entero.

Dockerfile

El contenedor parte de Debian 12 y crea dentro el mismo usuario que utilizamos en Synology, conservando su UID, GID y el grupo adicional de trabajo.

Las variables principales llegan desde el .env:

ARG USER_NAME
ARG USER_UID
ARG USER_GID
ARG ADMIN_GID

También instala openssh-server, Git, Python, algunas herramientas habituales y únicamente el cliente de Docker con el plugin de Compose.

Aquí he quitado directamente el acceso mediante contraseña porque el contenedor acepta autenticación SSH mediante clave pública:

PasswordAuthentication no
PubkeyAuthentication yes
KbdInteractiveAuthentication no

Para este tipo de entorno me parece bastante más limpio.

Hay un detalle importante con los grupos: si un GID ya existe dentro de Debian con otro nombre, intentar crearlo de nuevo hace fallar el build.

Por eso utilizo comprobaciones como:

getent group ${USER_GID} || groupadd -g ${USER_GID} ${USER_NAME}
getent group ${ADMIN_GID} || groupadd -g ${ADMIN_GID} devbox

Otro punto que me dio algo de guerra fue el home del usuario. Como lo guardo en un volumen persistente, algunos permisos pueden sobrevivir a los rebuilds. Por eso el contenedor vuelve a aplicar el chown necesario al arrancar.

Algunas herramientas extras

Ya que voy a utilizar el contenedor como entorno de trabajo, aprovecho para instalar algunas herramientas sencillas:

nano
zip
unzip
tree
htop
jq
wget
tmux
less
zoxide

De propina, zoxide para sustituir cd por z: aprende las rutas que más utilizas y puedes saltar directamente a ellas.

Por ejemplo: z proyecto-api y saltas directamente a ella sin pasar por todas sus rutas, sin escribir de más.

Docker Compose

En el docker-compose.yaml hay dos montajes especialmente importantes:

volumes:
  - ${WORKSPACE_PATH}:${WORKSPACE_PATH}
  - /var/run/docker.sock:/var/run/docker.sock

En el .env cada uno puede poner su ruta:

WORKSPACE_PATH=/ruta/de/sus/proyectos

sin tener que modificar el docker-compose.yaml.

Lo importante sigue siendo lo mismo: monto la misma ruta dentro y fuera del contenedor.

No hago algo como:

/ruta/de/tus/proyectos:/home/tu_usuario/proyectos

El motivo es Docker y porque si dentro de alguno de mis proyectos tengo un docker-compose.yml con rutas relativas o bind mounts, quien realmente los ejecuta es el Docker del Synology y si mantienes la misma ruta que tiene el contenedores del NAS evitas problemas con directorios o bind mounts que luego Docker no encuentra.

El Compose también persiste el home del usuario y /etc/ssh, monta authorized_keys en solo lectura y pasa al build las variables definidas en .env.

Preparar la clave y configurar la conexión SSH

El fichero authorized_keys contiene la clave pública desde la que vamos a conectarnos y, en esta versión, es además la única forma de autenticarnos por SSH mediante el puerto: 2222.

La clave privada se queda siempre en nuestro ordenador.

Si todavía no tenemos una clave SSH, podemos crearla con:

ssh-keygen

Después conviene configurar nuestro ~/.ssh/config para no tener que indicar todos los parámetros cada vez:

Host synology-dev
    HostName IP_DEL_NAS
    User tu_usuario
    Port 2222
    IdentityFile ~/.ssh/tu_clave

Si vamos a utilizar Visual Studio Code, instalamos la extensión Remote – SSH.

Después, desde Visual Studio Code:

Remote-SSH
  → Connect to Host
    → synology-dev

VS Code conectará al contenedor Debian en lugar de intentar instalar VS Code Server directamente sobre DSM.

Para conectar desde terminal también podemos utilizar:

ssh -i ~/.ssh/tu_clave -p 2222 tu_usuario@IP_DEL_NAS

O, si ya hemos configurado ~/.ssh/config, simplemente:

ssh synology-dev

A partir de ahí puedes abrir directamente la ruta que hayas definido en WORKSPACE_PATH, que por ejemplo en el NAS podría ser:

/volume1/Proyectos

Y digo esto porque al conectarte por SSH entrarás inicialmente en el home del usuario:

/home/tu_usuario

Así que la carpeta de proyectos no aparecerá dentro de tu home: tendrás que abrir su ruta real desde VS Code, salvo que utilices el comando directo que veremos al final.

Un apunte sobre los permisos:
Si después de montar tu WORKSPACE_PATH ves carpetas vacías aunque UID y GID sean correctos, revisa los permisos del grupo sobre esa carpeta en DSM como vimos en preparatorios al principio.


Usar las claves Git del propio NAS

Con authorized_keys hemos solucionado una cosa:

ordenador → devbox

Pero todavía queda otra:

devbox → GitLab / GitHub

Para hacer un git push desde dentro del contenedor necesitamos también una identidad SSH.

Podría utilizar SSH agent forwarding, pero en mi caso prefiero otra opción porque tengo claves propias asociadas al NAS para poder identificar desde qué máquina se realiza cada acceso a Git.

Por eso monto esas claves directamente en el contenedor como solo lectura.

Igual que con los proyectos, tampoco dejo escrita en el Compose la ruta real de mis claves. La saco al .env:

SSH_KEYS_PATH=/ruta/de/mi/.ssh

Y el Compose monta únicamente las claves necesarias:

volumes:
  - ${SSH_KEYS_PATH}/gitlab:/home/${USER_NAME}/.ssh/gitlab:ro
  - ${SSH_KEYS_PATH}/gitlab.pub:/home/${USER_NAME}/.ssh/gitlab.pub:ro
  - ${SSH_KEYS_PATH}/github:/home/${USER_NAME}/.ssh/github:ro
  - ${SSH_KEYS_PATH}/github.pub:/home/${USER_NAME}/.ssh/github.pub:ro

No estoy copiando las claves.

Son los mismos archivos del NAS, simplemente visibles también desde dentro del contenedor.

Como el usuario dentro del contenedor utiliza el mismo UID que en Synology, los permisos originales de las claves se mantienen.

Después podemos recrear el contenedor:

docker compose up -d --force-recreate

y probar:

ssh -T git@gitlab.com

o directamente:

git push

Esta separación entre la clave utilizada para entrar al devbox y las claves utilizadas para salir hacia Git fue otra de las partes importantes de la configuración.

Utilizar Docker desde dentro de VS Code

Aquí tampoco quería montar otro Docker dentro del contenedor ni nada de Docker-in-Docker.

Instalo únicamente:

docker-ce-cli
docker-compose-plugin

y monto:

/var/run/docker.sock

Así cuando ejecuto desde el terminal de VS Code:

sudo docker compose ps

realmente estoy hablando con el Docker del Synology.

Es decir, puedo gestionar desde el mismo entorno los contenedores que ya están ejecutándose en el NAS.

Eso sí: montar docker.sock da muchísimo poder al contenedor sobre el host.

Por eso este devbox debe considerarse un entorno de confianza, no un contenedor cualquiera expuesto a usuarios externos.

Algunos errores que me encontré con las pruebas

ProblemaCausa
chown: invalid groupEl GID ya existía dentro de Debian con otro nombre
Permission denied (publickey)La clave no está autorizada o .ssh / authorized_keys tienen propietario o permisos incorrectos
VS Code no puede crear su directorioUn RemoteCommand fijo en SSH puede interferir con Remote-SSH
La clave del host cambia después de reconstruirNo se estaban persistiendo las claves de /etc/ssh
Las carpetas aparecen vacíasUID/GID o ACL de Synology no permiten el acceso
git push no tiene identidadauthorized_keys solo sirve para entrar, no para salir
Cambios del Dockerfile sobre /home no aparecenEl home está almacenado en un volumen persistente

Persistir:

- etc-ssh:/etc/ssh

evita que las claves SSH del contenedor cambien en cada rebuild y así no tienes que limpiar continuamente known_hosts cada vez que reconstruyes la imagen.

Abrir directamente el proyecto

Una vez estaba todo funcionando quería eliminar también el último paso repetitivo.

En lugar de:

Abrir VS Code
→ Remote SSH
→ seleccionar servidor
→ abrir carpeta

puedes abrir directamente la conexión y la carpeta desde terminal:

code --remote ssh-remote+synology-dev /ruta/de/tus/proyectos

En mi caso sería la misma ruta que tengo definida en WORKSPACE_PATH.

Incluso puedes crear un alias:

alias synology-code="code --remote ssh-remote+synology-dev /ruta/de/tus/proyectos"

Y a partir de ahí:

synology-code

abre Visual Studio Code directamente conectado al contenedor y situado en la carpeta de proyectos.


Si tienes el mismo problema con tu NAS Synology, con esto puedes dejar el entorno listo en unos minutos.

Sinceramente, si una actualización de Visual Studio Code puede volver a romper la compatibilidad con las librerías de DSM, prefiero separar ambos mundos.

DSM sigue haciendo lo que tiene que hacer: ser mi NAS, mientras Visual Studio Code se conecta con normalidad a un Debian actualizado y yo sigo trabajando directamente sobre los mismos proyectos.

Sin modificar librerías de DSM, sin parchear el sistema y con todo el entorno reproducible dentro de Docker.

Como comenté al principio, todo el código y los ficheros de ejemplo están disponibles en la repo de GitHub:

https://github.com/Pistatxos/ssh-server-docker

Espero que os resulte útil.

¡Salu2!