Skip to content

Instantly share code, notes, and snippets.

@jmfernandez
Last active December 3, 2020 20:50
Show Gist options
  • Save jmfernandez/ed71aeae863f4b31a9dc to your computer and use it in GitHub Desktop.
Save jmfernandez/ed71aeae863f4b31a9dc to your computer and use it in GitHub Desktop.
Unprivileged lxc containers with phyisical network (and boot with the host!)

Unprivilegedl lxc containers potentially provide higher security levels than privileged ones. But they also have some limitations, like it is not easy to start them on boot, or give them a public IP address. These instructions teach how to achieve these goals.

These instructions have been developed taking as base Ubuntu 14.04 and its packages. They can be adapted to other recent Linux distributions.

  1. Be sure you have installed lxc, bridge-utils, cgmanager-utils and cgroup-bin packages:

    apt-get install apt-get install lxc bridge-utils cgmanager-utils cgroup-bin
  2. Protect access to host dmesg, so in case of a compromised container it does not give clues about the host.

    sysctl -w kernel.dmesg_restrict=1
    grep -qF kernel.dmesg_restrict /etc/systctl.conf || echo "kernel.dmesg_restrict=1" >> /etc/sysctl.conf
  3. Create one or more users which will manage the unprivileged containers:

    useradd -b /vserver -c 'Unprivileged LXC user' -s /bin/bash -m -U lxc-unpriv
  4. Change host network configuration in order to use a bridge. For instance, if the original network setup in /etc/network/interfaces contained:

    auto eth0
    iface eth0 inet dhcp
    

    then it has to be translated into

    auto eth0
    iface eth0 inet manual
    
    auto br0
    iface br0 inet dhcp
    	bridge_ports eth0
    	bridge_stp off
    	bridge_fd 0
    	bridge_maxwait 0
    
  5. At this point, you should reboot the host, in order to be sure the network setup is still working.

  6. Enable for the created users the bridge usage from lxc:

    echo "lxc-unpriv veth br0 2" >> /etc/lxc/lxc-usernet
  7. Copy files lxc-unprivileged.conf and lxc-unprivileged-instance.conf (included below) into /etc/init.

  8. Declare your unprivileged users in /etc/default/lxc-unprivileged, with a line like this:

    USERS="lxc-unpriv"
  9. For the next steps, become each one of the unprivileged users:

    sudo -iH -u lxc-unpriv /bin/bash
  10. Setup the default lxc containers profile:

    mkdir -p ~/.config/lxc/
    echo "lxc.id_map = u 0 $(grep "^$USER:" /etc/subuid | cut -d ':' -f 2,3 --output-delimiter=' ')" > ~/.config/lxc/default.conf
    echo "lxc.id_map = g 0 $(grep "^$USER:" /etc/subgid | cut -d ':' -f 2,3 --output-delimiter=' ')" >> ~/.config/lxc/default.conf
    echo "lxc.network.type = veth" >> ~/.config/lxc/default.conf
    echo "lxc.network.link = br0" >> ~/.config/lxc/default.conf
  11. Create one or more containers, for instance based on the same ubuntu release installed in the host:

    lxc-create -t download -n u1 -B dir --dir ~/containers/u1 -- -d ubuntu -r $(lsb_release -c | cut -f 2) -a amd64

    If the host is behind a firewall, you could have to add --keyserver hkp://pool.sks-keyservers.net:443 (or --keyserver hkp://pool.sks-keyservers.net:80) parameter to the previous line

  12. Label created containers as auto-startable:

    echo "lxc.start.auto = 1" >> ~/.local/share/lxc/u1/config
  13. Start the containers for the first time, and set them up as you need. Remember to setup network properly, as the containers are reachable at the same level as the host. Next time you reboot the host they will be started.

(PS: The idea of using a bridge was taken from (http://www.flockport.com/lxc-networking-guide/))

description "lxc unprivileged instance"
author "Christian Kampka <chris@emerge-life.de>"
author "Jose M. Fernandez <jmfernandez@cnio.es>"
stop on stopping lxc-unprivileged
# wait for 120 seconds for container to shutdown before killing it
kill timeout 120
# send SIGPWR to container to trigger a shutdown (see lxc-shutdown(1))
kill signal SIGPWR
instance "$LXCUSER:$NAME"
usage "LXCUSER=lxcuser NAME=name of LXC instance"
pre-start script
sudo -iH -u "$LXCUSER" -- lxc-wait -s RUNNING -n "$NAME" -t 0 && { stop; exit 0; } || true
end script
script
cgm movepid all "$LXCCGROUP" $$
exec sudo -iH -u "$LXCUSER" -- lxc-start -n "$NAME" -o "$(getent passwd "$LXCUSER" | cut -f 6 -d :)"/log-"$LXCUSER".txt
end script
description "LXC Unprivileged Containers"
author "Mike Bernson <mike@mlb.org>"
author "Jose M. Fernandez <jmfernandez@cnio.es>"
# http://blog.lifebloodnetworks.com/?p=2118
start on started lxc
stop on starting rc RUNLEVEL=[016]
# This can be overriden in /etc/default/lxc-unprivileged
env USERS="lxc-unpriv"
# BOOTGROUPS - What groups should start on bootup?
# Comma separated list of groups.
# Leading comma, trailing comma or embedded double
# comma indicates when the NULL group should be run.
# Example (default): boot the onboot group first then the NULL group
env BOOTGROUPS="onboot,"
pre-start script
[ -f /etc/default/lxc-unprivileged ] && . /etc/default/lxc-unprivileged
if [ -n "$BOOTGROUPS" ] ; then
BOOTGROUPS="-g $BOOTGROUPS"
fi
for u in $USERS; do
LXC_USERDIR="$(getent passwd "$u" | cut -f 6 -d :)"/.local/share/lxc
if [ -d "$LXC_USERDIR" ] ; then
initcgm=
lxccgroup="lxc$u"
lxc-autostart -L -P "$LXC_USERDIR" $BOOTGROUPS | while read line; do
# Init the cgroup only once
if [ -z "$initcgm" ] ; then
cgm create all "$lxccgroup"
cgm chown all "$lxccgroup" "$(id -u "$u")" "$(id -g "$u")"
initcgm=1
fi
set -- $line
(start lxc-unprivileged-instance LXCUSER="$u" NAME=$1 LXCCGROUP="$lxccgroup" && sleep $2 ) || true
done
fi
done
end script
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment