Salta ai contenuti
Sirio è in beta privata: sorgenti e installer vengono inviati via email a chi la prova. Chiedi di partecipare.

Accesso SFTP

Ogni sito può avere un proprio server SFTP, così chi ci lavora ne raggiunge i file con qualsiasi client SFTP — FileZilla, WinSCP, Cyberduck, VS Code, sftp, rsync — senza un account sul server e senza vedere altro che ciò che gli viene concesso.

Il server è SFTP (trasferimento file su SSH), non FTP né FTPS: una sola porta TCP per sito, nessun intervallo di porte passive da aprire, accesso con password e/o chiave pubblica, e supporto in tutti i client.

Il server di ogni sito gira in un container dedicato, sirio-sftp-<id del sito>, costruito dal binario di Sirio stesso: nessuna immagine di terzi e nessuna modifica all’SSH dell’host. I suoi account sono utenti del sito, non di Sirio: non accedono mai al pannello, e il pannello non mostra mai le loro password.

SFTP è spento di default, perché apre porte verso l’esterno. Nella unit systemd:

Variabile Default Descrizione
SIRIO_SFTP_ENABLED false server SFTP per sito
SIRIO_SFTP_PORTS 2222-2299 le porte dell’host che i server possono prendere, una per sito
SIRIO_SFTP_BIND 0.0.0.0 l’indirizzo dell’host su cui sono pubblicate le loro porte (:: per includere IPv6)

L’intervallo non può contenere la porta di Sirio (SIRIO_LISTEN), né la 80, la 443 e la porta del pannello quando le pubblica il proxy: altrimenti Sirio non parte. Chi gestisce l’SFTP di un sito sceglie una porta dentro l’intervallo; l’intervallo lo decide l’amministratore.

Docker pubblica le porte scavalcando ufw e firewalld, quindi sono raggiungibili appena un server è attivo. La pagina Porte le elenca come ogni altra porta pubblicata. Legale a un solo indirizzo con SIRIO_SFTP_BIND se non devono essere esposte su tutte le interfacce.

Il binario di Sirio deve essere una build Linux statica, come le release: l’immagine dei server contiene solo quel binario. Una build con cgo (make build su una macchina Linux con un compilatore C) viene rifiutata con una spiegazione; compilala con CGO_ENABLED=0.

Nella pagina del sito, Accesso SFTP → Accendi crea il server: prende la prima porta libera dell’intervallo (o quella indicata in Impostazioni del server) e una nuova chiave host ed25519. La card mostra poi come collegarsi:

sftp -P 2222 UTENTE@il-tuo-server

e l’impronta della chiave host. Al primo collegamento i client la mostrano e chiedono se fidarsi: deve coincidere con quella del pannello. La chiave appartiene al server del sito e resta la stessa tra riavvii e aggiornamenti; anche spegnere il server la conserva.

Spegni rimuove il container del server. Gli account e i loro accessi restano, pronti per quando lo riaccendi.

Lo stato accanto al titolo dice se il server è attivo o perché non lo è — tipicamente una porta che qualcos’altro sull’host usa già. Sirio riprova ogni minuto e dopo ogni modifica.

Un account ha un nome utente, una password, chiavi pubbliche (ed25519, ECDSA, RSA da 2048 bit) o entrambe, e i mount che gli sono concessi, ciascuno in lettura e scrittura o in sola lettura. Un account conserva sempre almeno una password o una chiave. Un account disattivato non può accedere e mantiene le sue impostazioni.

Ogni modifica ha effetto subito: il server ricarica la configurazione e chiude le sessioni aperte degli account modificati (una chiave rimossa, un mount revocato), lasciando stare gli altri.

Ciò che si può concedere a un account sono i mount del sito: i volumi Docker e le directory dell’host che i suoi container montano. La card li elenca. Un mount viene esposto solo se è concesso a un account: nient’altro viene montato nel container del server.

Alcuni mount non vengono mai esposti, qualunque container li monti, e la card dice perché:

  • Le directory di sistema dell’host, e ogni directory che ne contiene una: /, /etc, /usr, /root, /boot, /proc, /sys, /dev, /run, /tmp, /var/lib, /var/log, /var/spool e simili. Scriverci è un modo per eseguire codice come root.
  • Il socket di Docker, la directory dei dati di Docker e la directory dei dati di Sirio.
  • Le home degli utenti, e i file e le directory nascosti al loro interno (.ssh, .bashrc, .config…). Una cartella dentro una home, come /home/marco/www, è un dato qualsiasi.
  • I dati di un database: i volumi e le directory di un container database riconosciuto (MySQL, MariaDB, Percona, PostgreSQL e varianti), e ogni directory che li contiene o che sta al loro interno. Modificare i file di un database in esecuzione lo corrompe; per il contenuto ci sono i backup.
  • Ciò che monta anche un altro sito, o un container che non appartiene a nessun sito: lo stesso volume, o una directory dell’host che si sovrappone a una delle sue, in un verso o nell’altro.
  • Una directory dentro un’altra già esposta: quella esterna la raggiunge già.
  • Singoli file (un bind di wp-config.php): si possono esporre solo directory.
  • Volumi con un driver diverso da local.
  • Percorsi che Sirio non riesce a seguire fin dove portano davvero. La unit systemd di Sirio gli nasconde /home (ProtectHome=true), quindi le directory dell’host sotto /home compaiono così; sposta i dati altrove (/srv, /var/www) per esporli.

I percorsi dell’host vengono controllati così come sono scritti e dopo averne seguito i symlink, e il container del server viene creato con la destinazione reale: un symlink non può dirottare una directory già controllata. Tutto questo viene deciso di nuovo da ciò che i container montano in quel momento, a ogni modifica e ogni minuto. Un deploy che smette di montare una directory, o un container di un altro sito che inizia a montarla, la toglie dal server; la concessione dell’account resta salvata e torna valida se il mount ricompare.

Ciò che gli utenti caricano deve appartenere all’utente con cui gira l’applicazione, altrimenti l’applicazione non potrà leggerlo né modificarlo. Di default un nuovo file o una nuova directory prende il proprietario della directory in cui viene creato: in un volume WordPress, dove tutto appartiene a www-data, anche i file caricati appartengono a www-data. Proprietario imposta invece un UID e un GID fissi per un mount. Il proprietario e i bit setuid, setgid e sticky non si possono cambiare da un client.

  • Il container conserva solo le capability CHOWN, DAC_OVERRIDE e FOWNER (per assegnare i file al loro proprietario e raggiungere quelli di qualsiasi proprietario dentro i mount), gira con no-new-privileges, il filesystem radice in sola lettura e limiti su processi e memoria, su una rete dedicata in cui i container non possono raggiungersi tra loro. Monta la sua configurazione in sola lettura e i mount concessi; un mount concesso in sola lettura a tutti è in sola lettura anche per Docker.
  • Il server parla solo il sottosistema SFTP: non esistono shell, esecuzione di comandi o port forwarding da rifiutare. Non si possono creare symlink né hard link, e le rinomine non possono attraversare mount diversi.
  • Ogni mount è aperto come os.Root: né .. né un symlink piazzato in un volume portano fuori.
  • Dopo 10 password sbagliate un IP viene respinto per 15 minuti; le connessioni sono limitate in totale e per IP, e le sessioni inattive si chiudono dopo 15 minuti.
  • L’IP del client è quello che vede il container. Con la normale pubblicazione delle porte di Docker via iptables è quello reale; con il proxy userland ("userland-proxy": true e client IPv6 verso un container solo IPv4, o Docker Desktop) ogni client sembra il gateway di Docker, e i limiti per IP valgono per tutti insieme.

Accessi, upload, download, cancellazioni e rinomine finiscono come righe JSON nel log del container (docker logs sirio-sftp-<id del sito>, o la pagina del container). Le modifiche a server, account, chiavi e proprietari finiscono nel registro attività.

L’immagine dei server viene costruita dal binario di Sirio e prende il nome dal suo hash: dopo un aggiornamento di Sirio i suoi server vengono ricreati sulla nuova, e le sessioni aperte si interrompono. Le immagini vecchie vengono rimosse.

L’area di sito SFTP permette di vedere server, mount e account del sito; l’azione Modificare accende e spegne il server e gestisce account, credenziali, accessi e proprietari; Eliminare rimuove gli account. Come per ogni area, il permesso globale Siti vale per tutti i siti.