chroot during setup, then container ...
This commit is contained in:
parent
073a4b3704
commit
5c03ed0634
7 changed files with 233 additions and 206 deletions
172
README.md
172
README.md
|
@ -1,161 +1,43 @@
|
||||||
## Light Hardened Container
|
## Light Hardened Container
|
||||||
|
|
||||||
#### Perché
|
#### Perché
|
||||||
Voglio esporre su internet alcuni servizi da una board (Odroid C2) e farlo a modino, ogni servizio dentro un container.
|
Voglio esporre su internet alcuni servizi da una single board (Odroid C2) e farlo a modino, ogni servizio dentro un container.
|
||||||
Giocando con i vari sistemi per creare container quello piú simile ai miei desideri é `runc`.
|
Giocando con i vari sistemi per creare container quello piú simile ai miei desideri é `runc`.
|
||||||
|
|
||||||
#### Come
|
#### Come
|
||||||
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.
|
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.
|
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 fare questo ho pensato di far girare la root (/) di ogni container dentro una partizione read-only con un utente specifico per ogni servizio.
|
||||||
Per __i dati__ necessari ai vari servizi, faccio un bind di una directory da una partizione no-exec in modo che
|
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'
|
anche se il servizio viene bucato, l'attaccante puó scrivere solamente sulla partizione dei dati (da cui non puo'
|
||||||
peró avviare niente).
|
peró avviare niente).
|
||||||
|
|
||||||
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. Come fare? Partendo dal binario che vogliamo lanciare (sia esso apache, nginx, dovecot o tor) prendiamo innanzitutto le librerie da cui dipende con un `ldd` (anche se sarebbe stato figo usare una distro [tutta statica](http://sta.li)) e poi scopriamo tutti i file da cui dipende cercando le syscall a `open` mentre gira (con `strace`).
|
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, mi buchi ma vuoi usare `ls`? non puoi. vuoi usare `netcat`? non c'é. ok puoi compilarlo statico per `arm64`, auguri, dove lo uploadi? il fs e' read-ony, e in /data e' noexec. Insomma diventa fastidioso.
|
||||||
|
Come fare? ~~Partendo dal binario che vogliamo lanciare (sia esso apache, nginx, dovecot o tor) prendiamo innanzitutto le librerie da cui dipende con un `ldd` (anche se sarebbe stato figo usare una distro [tutta statica](http://sta.li)) e poi scopriamo tutti i file da cui dipende cercando le syscall a `open` mentre gira (con `strace`).~~ Ci serve una lista dei file a cui accede il nostro servizio, servirebbe un proxy fs, quale utilizzo migliore per un filesystem fuse?
|
||||||
|
Cercando in giro trovo ben due implementazioni di proxy fs, [BigBrotherFS](https://www.cs.nmsu.edu/~pfeiffer/fuse-tutorial/) e [https://rflament.github.io/loggedfs/](Loggedfs) che supporta anche i filtri delle syscall quindi piú adatto a questo uso.
|
||||||
|
Le syscall che ci interessano sono le `open` e le `readlink`, quindi il [file di configurazione di loggedfs](config.xml) filtrerá solo queste call, mostrandoci nel file di log solamente i file e i symlink a cui il nostro servizio ha avuto accesso.
|
||||||
|
|
||||||
Nella pratica, immaginiamo di voler far girare [caddy](https://caddyserver.com/) in questa maniera, ecco come sarebbe il setup a mano:
|
Per fare tutte queste cose ho scritto uno script bash per evitare di rifare queste cose tutte le volte, vediamo come usarlo:
|
||||||
|
|
||||||
|
## Uso
|
||||||
|
|
||||||
|
Immaginiamo di voler far girare [caddy](https://caddyserver.com/) in questa maniera, diciamo che abbiamo una partizione cifrata dove vogliamo
|
||||||
|
mettere i nostri servizi in `/var/lhc/` con dentro due directory, `containers` e `data`, possiamo lanciare `lhc caddy /var/lhc/containers/ /var/lhc/data`.
|
||||||
|
lhc fará le seguenti operazioni.
|
||||||
|
|
||||||
|
1. crea le dir `/var/lhc/containers/caddy` e `/var/lhc/data/caddy`
|
||||||
|
2. in `/var/lhc/containers/caddy` mette la rootfs di alpine
|
||||||
|
3. crea un utente `caddy` nell'host
|
||||||
|
4. prepara un file di configurazione per lanciare `runc` con i dati di cui sopra
|
||||||
|
5. monta `/var/lhc/containers/caddy` con loggedfs filtrando le chiamate `open` e `readlink`
|
||||||
|
6. lancia una `chroot` dentro `/var/lhc/containers/caddy`
|
||||||
|
6a. qui si aspetta che noi installiamo e configuriamo il servizio
|
||||||
|
7. quando la chroot esce, prende tutti i file aperti, sposta il container originale in `/var/lhc/containers/caddy.orig` e dentro `/var/lhc/containers/caddy` mette solo i file che sono stati aperti.
|
||||||
|
|
||||||
|
il grosso del lavoro che rimane é il punto __6a__, ovvero l'installazione e la configurazione del servizio:
|
||||||
|
|
||||||
|
### Apache setup
|
||||||
|
|
||||||
|
|
||||||
Creo una partizione per __i dati__ di 30Mb (a titolo di esempio lo facciamo in loop) e ne faccio il mount (per ora senza noexec)
|
### Caddy setup
|
||||||
|
|
||||||
```bash
|
|
||||||
dd if=/dev/zero of=./testfs/datafs bs=1024 count=30720
|
|
||||||
losetup /dev/loop0 ./testfs/datafs
|
|
||||||
mkfs.ext4 /dev/loop0
|
|
||||||
mkdir data
|
|
||||||
mount /dev/loop0 data
|
|
||||||
```
|
|
||||||
|
|
||||||
stessa cosa per la partizione dei container (per ora non la monto in read-only)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
dd if=/dev/zero of=./testfs/containerfs bs=1024 count=30720
|
|
||||||
losetup /dev/loop1 ./testfs/containerfs
|
|
||||||
mkfs.ext4 /dev/loop1
|
|
||||||
mkdir containers
|
|
||||||
mount /dev/loop1 containers
|
|
||||||
```
|
|
||||||
|
|
||||||
creo le dir per il container
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mkdir containers/caddy
|
|
||||||
mkdir data/caddy
|
|
||||||
```
|
|
||||||
|
|
||||||
e ci scompatto dentro la rootfs di alpine linux: `tar xvf rootfs/alpine-minirootfs-3.5.1-aarch64.tar.gz -C containers/caddy`
|
|
||||||
ora sará sufficiente `run spec` per creare un file di configurazione di runc e modificare i parametri del file che ci interessano:
|
|
||||||
|
|
||||||
|
|
||||||
```js
|
|
||||||
// prima di tutto il path della rootfs (quindi cercando 'root')
|
|
||||||
"root": {
|
|
||||||
"path": "./containers/caddy",
|
|
||||||
// per ora non lo mettiamo read-only perché dobbiamo prima installare
|
|
||||||
// quello che ci interessa, per il deploy questo bisogna metterlo a true
|
|
||||||
"readonly": false
|
|
||||||
}
|
|
||||||
|
|
||||||
// poi dentro la voce "mounts" aggiungiamo la nostra partizione per i dati
|
|
||||||
"mounts": [
|
|
||||||
{
|
|
||||||
"type": "bind",
|
|
||||||
"destination": "/data",
|
|
||||||
"source": "./data/caddy",
|
|
||||||
"options": ["rbind", "rw", "noexec"]
|
|
||||||
},
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
a questo punto possiamo far partire il container con `runc run caddy` e siamo dentro,
|
|
||||||
non rimane che configurare il servizio che ci serve, in questo caso caddy.
|
|
||||||
Potrei fare velocemente con un `apk update` e `apk add caddy` ma il pacchettizzato
|
|
||||||
manca di alcune funzionalitá quindi scelgo di scaricarlo direttamente dal sito:
|
|
||||||
|
|
||||||
```
|
|
||||||
apk update
|
|
||||||
apk add wget
|
|
||||||
wget 'https://caddyserver.com/download/build?os=linux&arch=arm64&features=git,filemanager,cors,expires,minify' -O caddy.tar.gz
|
|
||||||
|
|
||||||
```
|
|
||||||
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
|
|
||||||
```
|
|
||||||
|
|
||||||
Ora ci serve far partire il container direttamente con `caddy` e non con la shell
|
|
||||||
|
|
||||||
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 `caddy` 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 syscallscall 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)
|
|
||||||
open("/usr/lib/libpcre.so.1", O_RDONLY|O_CLOEXEC) = 3
|
|
||||||
open("/lib/libssl.so.39", O_RDONLY|O_CLOEXEC) = 3
|
|
||||||
open("/lib/libcrypto.so.38", O_RDONLY|O_CLOEXEC) = 3
|
|
||||||
open("/lib/libz.so.1", O_RDONLY|O_CLOEXEC) = 3
|
|
||||||
open("/etc/localtime", O_RDONLY|O_NONBLOCK|O_CLOEXEC) = -1 ENOENT (No such file or directory)
|
|
||||||
open("/var/lib/nginx/logs/error.log", O_WRONLY|O_CREAT|O_APPEND, 0644) = 3
|
|
||||||
open("/etc/ssl/openssl.cnf", O_RDONLY) = -1 ENOENT (No such file or directory)
|
|
||||||
open("/etc/nginx/nginx.conf", O_RDONLY) = 4
|
|
||||||
open("/etc/passwd", O_RDONLY|O_CLOEXEC) = 5
|
|
||||||
open("/etc/group", O_RDONLY|O_CLOEXEC) = 5
|
|
||||||
open("/", O_RDONLY|O_DIRECTORY|O_CLOEXEC) = 5
|
|
||||||
open("/etc", O_RDONLY|O_DIRECTORY|O_CLOEXEC) = 6
|
|
||||||
open("/etc/nginx", O_RDONLY|O_DIRECTORY|O_CLOEXEC) = 7
|
|
||||||
open("/etc/nginx/modules", O_RDONLY|O_DIRECTORY|O_CLOEXEC) = 8
|
|
||||||
open("/etc/nginx/mime.types", O_RDONLY) = 5
|
|
||||||
open("/", O_RDONLY|O_DIRECTORY|O_CLOEXEC) = 5
|
|
||||||
open("/etc", O_RDONLY|O_DIRECTORY|O_CLOEXEC) = 6
|
|
||||||
open("/etc/nginx", O_RDONLY|O_DIRECTORY|O_CLOEXEC) = 7
|
|
||||||
open("/etc/nginx/conf.d", O_RDONLY|O_DIRECTORY|O_CLOEXEC) = 8
|
|
||||||
open("/etc/nginx/conf.d/default.conf", O_RDONLY) = 5
|
|
||||||
open("/data/error.log", O_WRONLY|O_CREAT|O_APPEND, 0644) = 4
|
|
||||||
open("/var/log/nginx/access.log", O_WRONLY|O_CREAT|O_APPEND, 0644) = 5
|
|
||||||
open("/data/nginx.pid", O_RDWR|O_CREAT|O_TRUNC, 0644) = 8
|
|
||||||
nginx5:~#
|
|
||||||
```
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
```
|
|
160
config.json
Normal file
160
config.json
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
{
|
||||||
|
"ociVersion": "1.0.0-rc1",
|
||||||
|
"platform": {
|
||||||
|
"os": "linux",
|
||||||
|
"arch": "x86_64"
|
||||||
|
},
|
||||||
|
"process": {
|
||||||
|
"args": ["sh"],
|
||||||
|
"terminal": false,
|
||||||
|
"tty": false,
|
||||||
|
"user": {
|
||||||
|
"uid": 1004,
|
||||||
|
"gid": 1004
|
||||||
|
},
|
||||||
|
"env": [
|
||||||
|
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||||
|
"TERM=xterm"
|
||||||
|
],
|
||||||
|
"cwd": "/",
|
||||||
|
"capabilities": [
|
||||||
|
"CAP_AUDIT_WRITE",
|
||||||
|
"CAP_KILL",
|
||||||
|
"CAP_NET_BIND_SERVICE"
|
||||||
|
],
|
||||||
|
"rlimits": [
|
||||||
|
{
|
||||||
|
"type": "RLIMIT_NOFILE",
|
||||||
|
"hard": 1024,
|
||||||
|
"soft": 1024
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"noNewPrivileges": true
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"path": "/tmp/agent/agent",
|
||||||
|
"readonly": true
|
||||||
|
},
|
||||||
|
"mounts": [
|
||||||
|
{
|
||||||
|
"type": "bind",
|
||||||
|
"source": "/tmp/data/agent",
|
||||||
|
"destination": "/data",
|
||||||
|
"options": [ "rbind", "rw", "noexec" ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"destination": "/proc",
|
||||||
|
"type": "proc",
|
||||||
|
"source": "proc"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"destination": "/dev",
|
||||||
|
"type": "tmpfs",
|
||||||
|
"source": "tmpfs",
|
||||||
|
"options": [
|
||||||
|
"nosuid",
|
||||||
|
"strictatime",
|
||||||
|
"mode=755",
|
||||||
|
"size=65536k"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"destination": "/dev/pts",
|
||||||
|
"type": "devpts",
|
||||||
|
"source": "devpts",
|
||||||
|
"options": [
|
||||||
|
"nosuid",
|
||||||
|
"noexec",
|
||||||
|
"newinstance",
|
||||||
|
"ptmxmode=0666",
|
||||||
|
"mode=0620",
|
||||||
|
"gid=5"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"destination": "/dev/shm",
|
||||||
|
"type": "tmpfs",
|
||||||
|
"source": "shm",
|
||||||
|
"options": [
|
||||||
|
"nosuid",
|
||||||
|
"noexec",
|
||||||
|
"nodev",
|
||||||
|
"mode=1777",
|
||||||
|
"size=65536k"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"destination": "/dev/mqueue",
|
||||||
|
"type": "mqueue",
|
||||||
|
"source": "mqueue",
|
||||||
|
"options": [
|
||||||
|
"nosuid",
|
||||||
|
"noexec",
|
||||||
|
"nodev"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"destination": "/sys",
|
||||||
|
"type": "sysfs",
|
||||||
|
"source": "sysfs",
|
||||||
|
"options": [
|
||||||
|
"nosuid",
|
||||||
|
"noexec",
|
||||||
|
"nodev",
|
||||||
|
"ro"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"destination": "/sys/fs/cgroup",
|
||||||
|
"type": "cgroup",
|
||||||
|
"source": "cgroup",
|
||||||
|
"options": [
|
||||||
|
"nosuid",
|
||||||
|
"noexec",
|
||||||
|
"nodev",
|
||||||
|
"relatime",
|
||||||
|
"ro"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"hooks": {},
|
||||||
|
"linux": {
|
||||||
|
"resources": {
|
||||||
|
"devices": [
|
||||||
|
{
|
||||||
|
"allow": false,
|
||||||
|
"access": "rwm"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"namespaces": [
|
||||||
|
{
|
||||||
|
"type": "pid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "ipc"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "mount"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"maskedPaths": [
|
||||||
|
"/proc/kcore",
|
||||||
|
"/proc/latency_stats",
|
||||||
|
"/proc/timer_stats",
|
||||||
|
"/proc/sched_debug"
|
||||||
|
],
|
||||||
|
"readonlyPaths": [
|
||||||
|
"/proc/asound",
|
||||||
|
"/proc/bus",
|
||||||
|
"/proc/fs",
|
||||||
|
"/proc/irq",
|
||||||
|
"/proc/sys",
|
||||||
|
"/proc/sysrq-trigger"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"solaris": {
|
||||||
|
"cappedCPU": {},
|
||||||
|
"cappedMemory": {}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
<loggedFS logEnabled="true" printProcessName="true">
|
<loggedFS logEnabled="true" printProcessName="true">
|
||||||
<includes>
|
<includes>
|
||||||
<include extension=".*" uid="*" action="open" retname=".*"/>
|
<include extension=".*" uid="*" action="open" retname="SUCCESS"/>
|
||||||
<include extension=".*" uid="*" action="readlink" retname="SUCCESS"/>
|
<include extension=".*" uid="*" action="readlink" retname="SUCCESS"/>
|
||||||
</includes>
|
</includes>
|
||||||
<excludes>
|
<excludes>
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
function print_help {
|
function print_help {
|
||||||
echo '''
|
echo '''
|
||||||
Super Mini hardened container manager using alpine and runc
|
Light Hardened Container / a super light hardened container using alpine and runc
|
||||||
v1.0
|
v1.0
|
||||||
Usage: lhc-create <containername>
|
Usage: lhc <containername> <containerpath> <datapath>
|
||||||
'''
|
'''
|
||||||
exit -1
|
exit -1
|
||||||
}
|
}
|
||||||
|
@ -29,28 +29,31 @@ print_msg() {
|
||||||
TYPE=${msgType:-"[\e[92mInfo\e[0m]"}
|
TYPE=${msgType:-"[\e[92mInfo\e[0m]"}
|
||||||
echo -e $TYPE $CONTENT
|
echo -e $TYPE $CONTENT
|
||||||
}
|
}
|
||||||
|
|
||||||
export ARCH=$(get_arch)
|
export ARCH=$(get_arch)
|
||||||
print_msg "Arch: $ARCH"
|
print_msg "Arch: $ARCH"
|
||||||
|
|
||||||
## check if container's name is passed
|
## check if container's name is passed
|
||||||
## TODO, has to check if is not '--help' or '-h'
|
## TODO, has to check if is not '--help' or '-h'
|
||||||
if [ $# -lt 1 ]
|
if [ $# -lt 3 ]
|
||||||
then
|
then
|
||||||
print_help
|
print_help
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
export CONTAINER_NAME=$1
|
export CONTAINER_NAME=$1
|
||||||
export FULL_CONTAINER_PATH="`pwd`/containers/$CONTAINER_NAME/"
|
export FULL_CONTAINER_PATH=`readlink -f $2`/$CONTAINER_NAME
|
||||||
|
export FULL_DATA_PATH=`readlink -f $3`/$CONTAINER_NAME
|
||||||
|
|
||||||
print_msg "Container Name: '$CONTAINER_NAME'"
|
print_msg "Container Name: '$CONTAINER_NAME'"
|
||||||
print_msg "Creating directory '$FULL_CONTAINER_PATH'"
|
print_msg "Creating directory '$FULL_CONTAINER_PATH'"
|
||||||
|
|
||||||
mkdir `pwd`/data/$CONTAINER_NAME
|
mkdir $FULL_DATA_PATH
|
||||||
mkdir `pwd`/containers/$CONTAINER_NAME
|
mkdir $FULL_CONTAINER_PATH
|
||||||
|
|
||||||
print_msg "Decompress alpine rootfs into '$FULL_CONTAINER_PATH'"
|
print_msg "Decompress alpine rootfs into '$FULL_CONTAINER_PATH'"
|
||||||
sudo tar xf rootfs/alpine-minirootfs-3.5.1-$ARCH.tar.gz -C $FULL_CONTAINER_PATH
|
sudo tar xf rootfs/alpine-minirootfs-3.5.1-$ARCH.tar.gz -C $FULL_CONTAINER_PATH
|
||||||
sudo chmod 0755 $FULL_CONTAINER_PATH
|
chmod 0755 $FULL_CONTAINER_PATH
|
||||||
|
|
||||||
## set dns
|
## set dns
|
||||||
echo "nameserver 84.200.70.40" >> $FULL_CONTAINER_PATH/etc/resolv.conf
|
echo "nameserver 84.200.70.40" >> $FULL_CONTAINER_PATH/etc/resolv.conf
|
||||||
|
@ -61,12 +64,13 @@ print_msg "Create user $CONTAINER_NAME"
|
||||||
useradd $CONTAINER_NAME --no-create-home -p=''
|
useradd $CONTAINER_NAME --no-create-home -p=''
|
||||||
export CONTAINER_UID=`id $CONTAINER_NAME -u`
|
export CONTAINER_UID=`id $CONTAINER_NAME -u`
|
||||||
export CONTAINER_GID=`id $CONTAINER_NAME -g`
|
export CONTAINER_GID=`id $CONTAINER_NAME -g`
|
||||||
print_msg "Ok uid: $CONTAINER_UID gid: $CONTAINER_GID"
|
print_msg "uid: $CONTAINER_UID gid: $CONTAINER_GID"
|
||||||
|
|
||||||
print_msg "Create container $CONTAINER_NAME"
|
print_msg "Create container $CONTAINER_NAME"
|
||||||
export TERMINAL=false
|
|
||||||
export DEPLOY=true
|
#export CAPABILITIES=', "CAP_SYS_ADMIN", "CAP_CHOWN", "CAP_FOWNER", "CAP_NET_RAW", "CAP_SETGID", "CAP_SETUID", "CAP_SYS_CHROOT"'
|
||||||
export CAPABILITIES=""
|
export CAPABILITIES=""
|
||||||
|
|
||||||
./runc.template > config.json
|
./runc.template > config.json
|
||||||
|
|
||||||
## mount with loggedfs container root
|
## mount with loggedfs container root
|
||||||
|
@ -74,13 +78,13 @@ loggedfs -l files_$CONTAINER_NAME.log -c config.xml -p $FULL_CONTAINER_PATH
|
||||||
|
|
||||||
## run chroot
|
## run chroot
|
||||||
print_msg "
|
print_msg "
|
||||||
\n
|
\n\n
|
||||||
I'm running chroot now, all opened files will be logged in $CONTAINER_NAME.log\n
|
I'm running chroot now, all opened files will be logged in $CONTAINER_NAME.log\n
|
||||||
\n
|
\n
|
||||||
- Install and setup your stuff, if you need some package use 'apk update' and 'apk search'\n
|
- Install and setup your stuff, if you need some package use 'apk update' and 'apk search'\n
|
||||||
- Configure your process to use /data as storage point (/ will be read-only)\n
|
- Configure your process to use /data as storage path (/ will be read-only)\n
|
||||||
- Clean $CONTAINER_NAME.log 'echo "" > $CONTAINER_NAME.log'\n
|
- Clean $CONTAINER_NAME.log from host machine: 'echo "" > $CONTAINER_NAME.log'\n
|
||||||
- Start your process, exit on done!\n\n
|
- Start your process and try to activate funtionality, exit on done!\n\n
|
||||||
|
|
||||||
"
|
"
|
||||||
|
|
||||||
|
@ -89,61 +93,45 @@ mount -t sysfs sys $FULL_CONTAINER_PATH/sys/
|
||||||
mount -o bind /dev $FULL_CONTAINER_PATH/dev/
|
mount -o bind /dev $FULL_CONTAINER_PATH/dev/
|
||||||
|
|
||||||
chroot $FULL_CONTAINER_PATH sh
|
chroot $FULL_CONTAINER_PATH sh
|
||||||
|
|
||||||
|
## let's copy all used files/symlink in a new shiny dir
|
||||||
|
|
||||||
escaped_path=$(echo $FULL_CONTAINER_PATH | sed -e 's/\//\\\//g')
|
escaped_path=$(echo $FULL_CONTAINER_PATH | sed -e 's/\//\\\//g')
|
||||||
echo "ESCAPED_PATH: $escaped_path"
|
mkdir $FULL_CONTAINER_PATH.tmp
|
||||||
mkdir `pwd`/containers/$CONTAINER_NAME.tmp
|
|
||||||
|
|
||||||
files=`sed -rn "s/.* open (readwrite |writeonly )?$escaped_path(.*) \{.*/\2/p" < files_$CONTAINER_NAME.log | sort | uniq`
|
files=`sed -rn "s/.* open (readwrite |writeonly )?$escaped_path\/?(.*) \{.*/\2/p" < files_$CONTAINER_NAME.log | sort | uniq`
|
||||||
links=`sed -rn "s/.* readlink $escaped_path(.*) \{.*/\1/p" < files_$CONTAINER_NAME.log | sort | uniq`
|
links=`sed -rn "s/.* readlink $escaped_path\/?(.*) \{.*/\1/p" < files_$CONTAINER_NAME.log | sort | uniq`
|
||||||
|
|
||||||
## ok, removing all file but ones in $CONTAINER_NAME.log
|
|
||||||
cd $FULL_CONTAINER_PATH
|
cd $FULL_CONTAINER_PATH
|
||||||
for f in $files; do
|
for f in $files; do
|
||||||
echo $f
|
echo $f
|
||||||
cp --parents $f ../$CONTAINER_NAME.tmp/
|
cp --parents $f $FULL_CONTAINER_PATH.tmp/
|
||||||
done
|
done
|
||||||
|
|
||||||
for l in $links; do
|
for l in $links; do
|
||||||
to=$(ls -la $l | sed -rn "s/.*-> (.*)/\1/p")
|
to=$(ls -la $l | sed -rn "s/.*-> (.*)/\1/p")
|
||||||
echo "$l -> $to"
|
echo "$l -> $to"
|
||||||
ln -s $to ../$CONTAINER_NAME.tmp/$l
|
ln -s $to $FULL_CONTAINER_PATH.tmp/$l
|
||||||
done
|
done
|
||||||
|
|
||||||
|
cd -
|
||||||
|
|
||||||
cd ..
|
|
||||||
umount $FULL_CONTAINER_PATH/proc
|
umount $FULL_CONTAINER_PATH/proc
|
||||||
umount $FULL_CONTAINER_PATH/dev
|
umount $FULL_CONTAINER_PATH/dev
|
||||||
umount $FULL_CONTAINER_PATH/sys
|
umount $FULL_CONTAINER_PATH/sys
|
||||||
umount $FULL_CONTAINER_PATH
|
umount $FULL_CONTAINER_PATH
|
||||||
|
|
||||||
#export TERMINAL=true
|
ORIG_SIZE=`du -hs $FULL_CONTAINER_PATH`
|
||||||
#export DEPLOY=false
|
LHC_SIZE=`du -hs $FULL_CONTAINER_PATH.tmp`
|
||||||
#export CAPABILITIES=', "CAP_SYS_ADMIN", "CAP_CHOWN", "CAP_FOWNER", "CAP_NET_RAW", "CAP_SETGID", "CAP_SETUID", "CAP_SYS_CHROOT"'
|
|
||||||
#CONTAINER_UID=0
|
print_msg "ORIGINAL CONTAINER SIZE: `du -hs $FULL_CONTAINER_PATH` // N_FILES: `find $FULL_CONTAINER_PATH | wc -l`"
|
||||||
#CONTAINER_GID=0
|
print_msg "LHC CONTAINER SIZE: `du -hs $FULL_CONTAINER_PATH.tmp` // N_FILES: `find $FULL_CONTAINER_PATH.tmp | wc -l`"
|
||||||
#./runc.template > config.dev.json
|
|
||||||
|
mv $FULL_CONTAINER_PATH $FULL_CONTAINER_PATH.orig
|
||||||
|
mv $FULL_CONTAINER_PATH.tmp $FULL_CONTAINER_PATH
|
||||||
|
|
||||||
|
print_msg "Place specify your init command in config.json and try to run 'runc run $CONTAINER_NAME'$"
|
||||||
|
cp config.json runc.config/$CONTAINER_NAME.json
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#print_msg "Patch inittab"
|
###### CHECK #####
|
||||||
## modify inittab to fix alpine tty/console issue!
|
|
||||||
## comment all ttyN respawn lines
|
|
||||||
#sudo sed -i "s/^.*respawn:\/sbin\/getty.*/#&/" $fullContainerPath/etc/inittab
|
|
||||||
|
|
||||||
## and add a line for a console
|
|
||||||
#sudo bash -c 'echo "console::respawn:/sbin/getty 38400 /dev/console" >> $fullContainerPath/etc/inittab'
|
|
||||||
|
|
||||||
#print_msg "Update package"
|
|
||||||
## update package
|
|
||||||
#sudo systemd-nspawn -D $fullContainerPath -M $containerName apk update
|
|
||||||
#print_msg "Install vim / git"
|
|
||||||
#sudo systemd-nspawn -D $fullContainerPath -M $containerName apk add vim git openrc
|
|
||||||
#print_msg "Ready"
|
|
||||||
#sudo systemd-nspawn -bD $fullContainerPath -M $containerName
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
##
|
|
||||||
|
|
||||||
#sed "s/.*\"\(.*\)\".*/\1/" file
|
|
BIN
rootfs/alpine-minirootfs-3.5.1-aarch64.tar.gz
Normal file
BIN
rootfs/alpine-minirootfs-3.5.1-aarch64.tar.gz
Normal file
Binary file not shown.
BIN
rootfs/alpine-minirootfs-3.5.1-x86_64.tar.gz
Normal file
BIN
rootfs/alpine-minirootfs-3.5.1-x86_64.tar.gz
Normal file
Binary file not shown.
|
@ -7,15 +7,13 @@ cat <<EOF
|
||||||
"arch": "$ARCH"
|
"arch": "$ARCH"
|
||||||
},
|
},
|
||||||
"process": {
|
"process": {
|
||||||
"terminal": $TERMINAL,
|
"args": ["sh"],
|
||||||
"tty": true,
|
"terminal": false,
|
||||||
|
"tty": false,
|
||||||
"user": {
|
"user": {
|
||||||
"uid": $CONTAINER_UID,
|
"uid": $CONTAINER_UID,
|
||||||
"gid": $CONTAINER_GID
|
"gid": $CONTAINER_GID
|
||||||
},
|
},
|
||||||
"args": [
|
|
||||||
"sh"
|
|
||||||
],
|
|
||||||
"env": [
|
"env": [
|
||||||
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||||
"TERM=xterm"
|
"TERM=xterm"
|
||||||
|
@ -25,7 +23,6 @@ cat <<EOF
|
||||||
"CAP_AUDIT_WRITE",
|
"CAP_AUDIT_WRITE",
|
||||||
"CAP_KILL",
|
"CAP_KILL",
|
||||||
"CAP_NET_BIND_SERVICE"
|
"CAP_NET_BIND_SERVICE"
|
||||||
$CAPABILITIES
|
|
||||||
],
|
],
|
||||||
"rlimits": [
|
"rlimits": [
|
||||||
{
|
{
|
||||||
|
@ -37,13 +34,13 @@ cat <<EOF
|
||||||
"noNewPrivileges": true
|
"noNewPrivileges": true
|
||||||
},
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"path": "./containers/$CONTAINER_NAME",
|
"path": "$FULL_CONTAINER_PATH",
|
||||||
"readonly": $DEPLOY
|
"readonly": true
|
||||||
},
|
},
|
||||||
"mounts": [
|
"mounts": [
|
||||||
{
|
{
|
||||||
"type": "bind",
|
"type": "bind",
|
||||||
"source": "./data/$CONTAINER_NAME",
|
"source": "$FULL_DATA_PATH",
|
||||||
"destination": "/data",
|
"destination": "/data",
|
||||||
"options": [ "rbind", "rw", "noexec" ]
|
"options": [ "rbind", "rw", "noexec" ]
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue