Voglio esporre su internet alcuni servizi da una board (Odroid C2).
Giocando con i vari sistemi per creare container quello piú simile ai miei desideri é `systemd-nspawn` (si, systemd é una merda ed é il male). `systemd-nspawn` é tipo una chroot ma meglio.
Uso [alpine linux](https://alpinelinux.org) come base perché la rootfs é scandalosamente piccola (1.9Mb), supporta arm64 (che é l'architettura della mia board casalinga), supporta un sistema di pacchetti degno di questo nome (apk) ed e' orientata alla sicurezza.
L'assunto di base é che i servizi che girano dentro il container sono bucabili e quindi bisogna limitare i danni di un sicuro pwn.
Per fare questo ho pensato di far girare la root di ogni container dentro una partizione read-only.
Per i dati necessari ai vari servizi, faccio un bind di una directory da una partizione no-exec in modo che
anche se il servizio viene bucato, l'attaccante puó scrivere solamente sulla partizione dei dati (da cui non puo'
Per evitare di far usare interpreti e altre utilities utili all'attaccante, il container deve contenere esclusivamente i files strettamente necessari per far girare il servizio. Per fare questo, prima della fase di deploy il servizio verrá fatto girare con strace per controllare tutte le syscall a `open`: i file aperti saranno gli unici a rimanere nel container.
Spawning container nginx on /data/dev/lhc/containers/nginx.
Press ^] three times within 1s to kill container.
Timezone Europe/Rome does not exist in container, not updating container timezone.
-sh: can't access tty; job control turned off
nginx:~#
```
e siamo dentro il container, quindi possiamo iniziare ad installare quello che dobbiamo:
```bash
apk update
apk add nginx
```
da adesso in poi, dobbiamo cercare di configurare il servizio per usare /data/ come path per tutti i file che vuole utilizzare in scrittura. Per fare ció possiamo fare in tanti modi, iniziamo a vedere cosa succede ad avviarlo manualmente:
```bash
nginx: [emerg] open() "/run/nginx/nginx.pid" failed (2: No such file or directory)
nginx3:~#
```
ok, /run é svuotata da `systemd-nspawn` di proposito e quindi la directory 'nginx' al suo interno non possiamo mettercela, inoltre per l'assunto di prima (ovvero che questo fs sará read-only) dobbiamo metterlo dentro /data, quindi modifichiamo `nginx.conf` per fare in modo che questo accada e quindi `vi /etc/nginx/nginx.conf`:
```nginx.conf
# aggiungiamo da qualche parte se non c'é giá
pid /data/nginx.pid
# e giá che ci siamo cerchiamo anche gli altri path usati da nginx in scrittura e li cambiamo
error_log /data/error.log;
access_log /data/access.log;
```
riproviamo:
```bash
nginx:/etc/nginx# nginx
nginx:/etc/nginx# ps aux
PID USER TIME COMMAND
1 root 0:00 -sh
26 root 0:00 nginx: master process nginx
27 nginx 0:00 nginx: worker process
28 nginx 0:00 nginx: worker process
29 nginx 0:00 nginx: worker process
30 nginx 0:00 nginx: worker process
31 nginx 0:00 nginx: worker process
32 nginx 0:00 nginx: worker process
33 nginx 0:00 nginx: worker process
34 nginx 0:00 nginx: worker process
35 root 0:00 ps aux
nginx:/etc/nginx#
```
direi molto meglio, ora usciamo dal container (ctrl+] per tre volte oppure `exit`).
Ora ci serve far partire il container direttamente con `nginx` e non con la shell e c'é un problema:
per come é fatto systemd-nspawn il programma che prende il comando non deve uscire (é effettivamente PID 1 dentro il container) quindi bisogna dire a nginx di rimanere in foreground (basta aggiungere `daemon off;` in `containers/nginx/etc/nginx.conf`)
per come é pacchettizzato dentro alpine linux, nginx cambia di suo l'utente con cui gira quindi per questo servizio non serve dire a systemd-nspawn di cambiare utente, negli altri casi bisogna usare il flag `-u`.
Ultima cosa, non voglio avere una shell dentro il container o altri tool (metti che appunto ci bucano, mica vogliamo fargli un favore), ma come fare? ci serve qualcosa che trovi tutti i file aperti da `nginx` e quello che mi viene in mente é `strace`, quindi da dentro il container installiamo strace (`apk add strace`) e lanciamo nginx con strace redirigendo `stderr` su un file (`strace nginx 2> strace.log`). Dobbiamo cercare di "attivare" tutte le funzionalitá del servizio in modo che becchiamo tutti i file che possono essere utilizzati da `nginx` (in questo caso quello che mi viene in mente é di lanciare una richiesta HTTP a nginx per dire, tipo dall'host faccio un `wget localhost`).
Ora killiamo lo strace `kill -9 strace` e cerchiamo le syscall a open dentro il log:
```bash
nginx5:~# grep open strace.log
open("/etc/ld-musl-x86_64.path", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/lib/libpcre.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/usr/local/lib/libpcre.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
giá guardando gli errori (ENOENT) possiamo vedere alcune cosette, in particolare che ssl non funzionerá (probabilmente manca il pacchetto, ma a noi ora non interessa) e che mi sono dimenticato di cambiare qualche path dei log. comunque, i file mostrati qui sopra sono gli unici che devono rimanere dentro il container per fare in modo che tutti continui a funzionare, quindi estrapoliamo tutti i path:
```bash
nginx:~# grep open strace.log | sed "s/.*\"\(.*\)\".*/\1/" | sort | uniq > needed_files
nginx:~# cat needed_files
/
/data/error.log
/data/nginx.pid
/etc
/etc/group
/etc/ld-musl-x86_64.path
/etc/localtime
/etc/nginx
/etc/nginx/conf.d
/etc/nginx/conf.d/default.conf
/etc/nginx/mime.types
/etc/nginx/modules
/etc/nginx/nginx.conf
/etc/passwd
/etc/ssl/openssl.cnf
/lib/libcrypto.so.38
/lib/libpcre.so.1
/lib/libssl.so.39
/lib/libz.so.1
/usr/lib/libpcre.so.1
/usr/local/lib/libpcre.so.1
/var/lib/nginx/logs/error.log
/var/log/nginx/access.log
```
ora, preparo un'altra directory con solo questi files `mkdir containers/nginx-prod` e copio dentro solo i files di cui sopra:
```bash
CONTAINER_ROOT=`pwd`/containers/nginx
for f in `cat containers/nginx/root/only_needed` ; do echo $f; if [ -f $CONTAINER_ROOT$f ]; then mkdir -p containers/nginx-prod/`dirname $f` ; cp $CONTAINER_ROOT$f containers/nginx-prod/`dirname $f`; fi; done