Script: Run in LXD

I needed a quick and dirty way to run things in container instead of polluting my system with random dependencies. Vagrant, virtualenv and friends require too much boilerplate to be quick, so here it is:

~/p/gitless$ ~/lxd-runin.sh 
Creating gitless
Starting gitless
Device gitless-shared added to gitless
root@gitless:~/gitless# exit
techtonik@SONiC:~/p/gitless$ ~/lxd-runin.sh 
Container gitless already exists
root@gitless:~/gitless#

Save as lxd-runin.sh and enjoy:

$ cat lxd-runin.sh 
#!/usr/bin/env bash

# name of current dir
NAME=$(basename $PWD)

# https://unix.stackexchange.com/questions/432816/grab-id-of-os-from-etc-os-release/498788#498788
ID=$(grep -oP '(?<=^ID=).+' /etc/os-release | tr -d '"')
VERSION=$(grep -oP '(?<=^VERSION_ID=).+' /etc/os-release | tr -d '"')

OSTORUN="$ID:$VERSION"

if lxc info $NAME &> /dev/null
then
  echo "Container $NAME already exists"
else
  lxc launch $OSTORUN $NAME
  lxc config device add $NAME $NAME-shared disk source=$PWD path=/root/$NAME
fi

# https://stackoverflow.com/questions/7120426/how-to-invoke-bash-run-commands-inside-the-new-shell
lxc exec $NAME -- /bin/sh -c "cd $NAME; bash"

There is one problem though:

root@gitless:~/gitless# ls -la
total 78
drwxr-xr-x 5 nobody nogroup 4096 Feb  5 04:53 .
drwx------ 5 root   root       7 Feb  5 15:15 ..
-rw-r--r-- 1 nobody nogroup 1032 Dec  2 05:49 .appveyor.yml
drwxr-xr-x 8 nobody nogroup 4096 Feb  5 04:34 .git
-rw-r--r-- 1 nobody nogroup  325 Dec  2 05:49 .gitignore
-rw-r--r-- 1 nobody nogroup  205 Dec  2 05:49 .pylint.rc
-rwxr-xr-x 1 nobody nogroup  264 Dec  2 05:49 .travis.sh
-rw-r--r-- 1 nobody nogroup  782 Feb  5 04:34 .travis.yml
-rw-r--r-- 1 nobody nogroup 1068 Dec  2 05:49 LICENSE.md
-rw-r--r-- 1 nobody nogroup   46 Dec  2 05:49 MANIFEST.in
-rw-r--r-- 1 nobody nogroup 4590 Dec  2 05:49 README.md
drwxr-xr-x 4 nobody nogroup 4096 Dec  2 05:49 gitless
-rwxr-xr-x 1 nobody nogroup  247 Dec  2 05:49 gl.py
-rw-r--r-- 1 nobody nogroup  385 Dec  2 05:49 gl.spec
-rw-r--r-- 1 nobody nogroup  120 Dec  2 05:49 requirements.txt
-rwxr-xr-x 1 nobody nogroup 2613 Dec  2 05:49 setup.py
drwxr-xr-x 2 nobody nogroup 4096 Dec  2 05:49 snap
-rw-r--r-- 1 nobody nogroup  107 Dec  2 05:49 tox.ini
-rw-r--r-- 1 nobody nogroup   60 Feb  5 04:53 xxx.py
root@gitless:~/gitless# echo 1 > xxx
bash: xxx: Permission denied

If you explain how to fix that without sacrificing on security - it will be appreciated. In references that I read about mappings there is always this hanging tail of “security implications” that follows. If you can explain how to make proper mapping and why it is secure - that will be the most helpful.

Hi Anatoly!

Here is how to use ID mapping in an example. You essentially add the raw LXC instruction idmap: both 1000 1000. This 1000 is your UID and GID on the host. By default, in Ubuntu the first user account has UID/GID 1000, so adapt accordingly.

$ ./runin.sh 
Creating runin
Starting runin
Device runin-shared added to runin
root@runin:~/runin# ls -l
total 4
-rwxr-xr-x 1 nobody nogroup 651 Feb  5 19:07 runin.sh
root@runin:~/runin# exit
$ echo -e "both 1000 1000" | lxc config set runin raw.idmap -
$ lxc exec runin bash
root@runin:~# ls -l
total 4
drwxr-xr-x 2 nobody nogroup 4096 Feb  5 19:07 runin
root@runin:~# ls -l runin/
total 4
-rwxr-xr-x 1 nobody nogroup 651 Feb  5 19:07 runin.sh
root@runin:~# exit
$ lxc restart runin
Remapping container filesystem
$ lxc exec runin bash
root@runin:~# ls -l
total 4
drwxr-xr-x 2 ubuntu ubuntu 4096 Feb  5 19:07 runin
root@runin:~# ls -l runin/
total 4
-rwxr-xr-x 1 ubuntu ubuntu 651 Feb  5 19:07 runin.sh
root@runin:~# exit
$ 

You can adapt the script, though, so that you add the IDMAP in the beginning and avoid having to restart the container for the idmapping to get enabled.

You can do that in three ways,

  1. First lxc init ubuntu:18.04 mycontainer (init but do not start yet), then apply with lxc config ..., and finally lxc start.
  2. Use lxc launch ubuntu: mycontainer --config .... The raw.idmap configuration is a bit peculiar and I do not have handy the example on how to set it in one go.
  3. Create a LXD profile and add raw.idmap=... into that profile. Then, launch a container with lxc launch ubuntu: mycontainer --profile withmyidmap. See here an example with the profile.

Regarding security, I suppose you have already seen the documentation on idmaps.

I think the gist is that the subdirectory (and only that subdirectory) that is exposed in this way into a container should be considered as tainted. Could be filenames with weird backticks that your shell prompt would interpret as a command to execute. Could be a crafted file that your desktop’s file indexing service (tracker) would crash and run a malicious payload in that file.

2 Likes