Linux containers at home: why and how




    Reasoning


    When mentioning the phrase “container virtualization”, many people immediately think of Virtuozzo and OpenVZ , as well as Docker . But all this is associated, first of all, with hosting, VPS and other similar things.

    At home, on personal computers, many use virtual machines: basically, perhaps, Virtualbox. As a rule, in order to work under Linux, have Windows on hand or vice versa. However, in the presence of many related Linux operating systems, I began to notice that the use of virtual machines is, to put it mildly, irrational.

    First of all, disk space is consumed very quickly. Each virtual machine needs a place, even if several of them differ only in configs. This is especially critical on the small size of the laptop SSD. In principle, Virtualbox can work with raw devices and, theoretically, rw LVM snapshots can be assigned to machines, but here again questions arise with resizing the file system in the future, automating cloning, transferring, deleting, and the like.

    In the second - this is a greater consumption of RAM. In the third - not the most convenient tools for interaction ...

    Therefore, the idea arose to try container virtualization at home. OpenVZ dismissed immediately, due to the need to mess with the custom kernel. The choice fell on the LXC, shipped to the stable Debian repository.

    What are containers and how does it all differ from virtualization? In the case of containers, a virtual hardware environment is not created, but an isolated space of processes and network stack is used. Let's just say it turns out chroot with advanced features.

    Why this is needed:

    - To build software if you do not want to clutter up the main working system with unsuitable * -dev packages.
    - The need for another distribution to run any specific programs and, again, builds.
    - Isolation of potentially unsafe software, like the same Skype committing various obscure actions in the user's home directory and all kinds of dubious web technologies: vulnerability in flash, in java , in the pdf handler - this is just what floats on the surface.
    - Anonymity. You can corny remain logged in to your favorite social account, forget to clear cookies or be unfamiliar with yet another new web technology like this webrtc . You can, of course, keep several browser profiles, but this will not protect against the above holes and technologies.

    So, let’s take a look at the pros and cons of LXC:

    + It works on the vanilla kernel
    + Ease of forwarding devices and host directories, since it all works through cgroups
    + It’s very undemanding to resources, unlike virtual machines like Virtualbox or qemu

    - Containers will work on the same core as the host, although this is rather a feature of container virtualization as a whole.
    - Some incompleteness of the bundled utilities.

    Deploy and configure a container



    First of all, we install the lxc package and all the necessary utilities:
    sudo apt-get install lxc bridge-utils


    See the available LVM volume groups:
    $sudo vgs
      VG         #PV #LV #SN Attr   VSize   VFree
      nethack-vg   1   6   0 wz--n- 119,00g 7,36g
    


    sudo lxc-create -t debian -B lvm --vgname nethack-vg --fssize 2G -n deb_test




    We advise you to use LVM as a storage system, Volume Group (nethack-vg in my case) and a size of 2 gigabytes, otherwise a one-gig volume will be created by default. Although, if it suddenly became cramped, you can do lvresize.

    We look:



    $sudo lvs
      LV   VG         Attr       LSize   Pool Origin Data%  Meta%  Move Log Cpy%Sync Convert
      deb_test nethack-vg -wi-ao----   2,00g
      home     nethack-vg -wi-ao----  93,09g
      root     nethack-vg -wi-ao----   8,38g
      tmp      nethack-vg -wi-ao---- 380,00m
      var      nethack-vg -wi-ao----   2,79g
      vm       nethack-vg -wi-ao----   5,00g
    


    We see that we have the deb_test volume.

    A typical config created by a script:
    / var / lib / lxc / deb_test / config

    # Template used to create this container: /usr/share/lxc/templates/lxc-debian
    # Parameters passed to the template:
    # For additional config options, please look at lxc.container.conf(5)
    lxc.rootfs = /dev/nethack-vg/deb_test
    # Common configuration
    lxc.include = /usr/share/lxc/config/debian.common.conf
    # Container specific configuration
    lxc.mount = /var/lib/lxc/deb_test/fstab
    lxc.utsname = deb_test
    lxc.arch = amd64
    lxc.autodev = 1
    lxc.kmsg = 0
    




    We start:
    sudo lxc-start -n deb_test



    Log in with the specified password. To start in headless mode, use the -d switch, and the root console can be obtained using the command

    sudo lxc-attach -n deb_test


    So far we have neither the network nor the programs necessary for the work. To do this, we raise the bridge on the host, set the IP, wrap the traffic from the virtual network subnet, and when the interface is turned off, we destroy the bridge.

    On the host, register in / etc / network / interfaces
    auto lo br0
    iface br0 inet static
       address 172.20.0.1
       netmask 255.255.255.0
       pre-up  /sbin/brctl addbr br0
       post-up /sbin/brctl setfd br0 0
       post-up iptables -t nat -A POSTROUTING -s 172.20.0.0/24 -j MASQUERADE
       post-up echo 1 > /proc/sys/net/ipv4/ip_forward
       pre-down /sbin/brctl delbr br0
    


    In the container config, add:
    lxc.network.type = veth
    lxc.network.flags = up
    lxc.network.link = br0
    lxc.network.hwaddr = 00:01:02:03:04:05
    


    It’s clear that the mac address is arbitrary, for every taste.
    To immediately get a working network and the ability to install packages with apt, we’ll add
    lxc.network.ipv4 = 172.20.0.3
    lxc.network.ipv4.gateway = 172.20.0.1
    

    And do
    echo "nameserver 192.168.18.1">/etc/resolv.conf


    It is clear that 192.168.18.1 is the IP of my DNS.

    Install the necessary packages:
    #apt-get install vim openvpn zsh iftop


    Further, either on the host or on another working virtual machine, you can get a list of installed packages and install them all in our new container:
    scp user@172.20.0.2:/etc/apt/sources.list /etc/apt/
    scp -r user@172.20.0.2:/etc/apt/sources.list.d /etc/apt/
    apt-get update
    ssh user@172.20.0.2 'dpkg --get-selections|grep -v deinstall'|dpkg --set-selections
    apt-get dselect-upgrade
    


    Now you can humanly configure the network interface in the container using your favorite text editor:

    / etc / network / interfaces:
    auto lo eth0
    iface lo inet loopback
    iface eth0 inet static
    address 172.20.0.3
    netmask 255.255.255.0
    gateway 172.20.0.1
    dns-nameservers 192.168.18.1
    


    However, this could be done from the host system, for example, by mounting a logical volume. There are many ways.

    In principle, you can specify any public as the DNS, if you are not afraid for your privacy. For example, Google’s 8.8.8.8 and 8.8.4.4.

    By accessing the devices of the host system, I adhere to the policy of "everything that is not allowed is prohibited." Add the following line to the config for this:
    lxc.cgroup.devices.deny = a
    


    Delete
    lxc.include = /usr/share/lxc/config/debian.common.conf


    Let's try to connect through OpenVPN. Immediately we get the error:
    Thu Oct 15 16:39:33 2015 ERROR: Cannot open TUN/TAP dev /dev/net/tun: No such file or directory (errno=2)
    Thu Oct 15 16:39:33 2015 Exiting due to fatal error
    




    The system writes that the TUN / TAP interfaces are not available due to their absence. Obviously, you need to allow the guest system to use host devices. Open the container configuration file, / var / lib / lxc / deb_test / config and add the line there:
    lxc.cgroup.devices.allow = c 10:200 rwm 
    


    In the container we execute:
    root@deb_test:/# mkdir /dev/net; mknod /dev/net/tun c 10 200
    





    Pay attention to 10: 200 - this is the identifier of the type of device. If we execute on the host:
    $ls -l /dev/net/tun
    crw-rw-rw- 1 root root 10, 200 окт 13 10:30 /dev/net/tun
    


    Then we will see identifiers 10, 200. We will be guided by them, allowing access to the device, for example, the camera - video0.

    lxc.cgroup.devices.allow = c 81:* rwm
    


    In the same way, add the remaining necessary devices:

    # /dev/null and zero
    lxc.cgroup.devices.allow = c 1:3 rwm
    lxc.cgroup.devices.allow = c 1:5 rwm
    # consoles
    lxc.cgroup.devices.allow = c 5:1 rwm
    lxc.cgroup.devices.allow = c 5:0 rwm
    lxc.cgroup.devices.allow = c 4:0 rwm
    lxc.cgroup.devices.allow = c 4:1 rwm
    # /dev/{,u}random
    lxc.cgroup.devices.allow = c 1:9 rwm
    lxc.cgroup.devices.allow = c 1:8 rwm
    lxc.cgroup.devices.allow = c 136:* rwm
    lxc.cgroup.devices.allow = c 5:2 rwm
    # rtc
    lxc.cgroup.devices.allow = c 254:0 rm
    #usb passthrough
    lxc.cgroup.devices.allow = c 189:* rwm
    #video
    lxc.cgroup.devices.allow = c 81:* rwm
    #sound
    lxc.cgroup.devices.allow = c 116:* rwm
    lxc.cgroup.devices.allow = c 14:* rwm
    


    For the functioning of the X and the possibility of forwarding them through ssh, you need to add a mount point:
    lxc.mount.entry = /tmp/.X11-unix/X0 tmp/.X11-unix/X0 none bind,optional,create=file
    


    By analogy, you can mount other necessary directories and files:
    lxc.mount.entry = /home/user/.vim home/user/.vim none bind,optional,create=dir 0 0 
    lxc.mount.entry = /home/user/.vimrc home/user/.vimrc none bind,optional,create=file 0 0 
    


    To play sound, you can allow access to the sound device if the card is multi-threaded (with single-threaded when using dmix there are problems with blocking):
    lxc.cgroup.devices.allow = c 116:* rwm
    lxc.cgroup.devices.allow = c 14:* rwm
    lxc.mount.entry = /dev/snd dev/snd none bind,optional,create=dir 0 0 
    


    And you can configure pulseaudio to play sound over the network, as described here . Briefly:

    Edit on the host /etc/pulse/default.pa, adding there:
    load-module module-native-protocol-tcp auth-ip-acl=127.0.0.1;172.20.0.3 auth-anonymous=1
    


    As a result, we get this kind of config:
    / var / lib / lxc / deb_test / config

    lxc.rootfs = /dev/nethack-vg/deb_test
    lxc.mount = /var/lib/lxc/deb_test/fstab
    lxc.utsname = deb_test
    lxc.arch = amd64
    lxc.autodev = 1
    lxc.kmsg = 0
    lxc.network.type = veth
    lxc.network.flags = up
    lxc.network.link = br0
    lxc.network.hwaddr = 00:01:02:03:04:05
    lxc.network.ipv4 = 172.20.0.3
    lxc.network.ipv4.gateway = 172.20.0.1
    #deny acces for all devices
    lxc.cgroup.devices.deny = a
    # /dev/null and zero
    lxc.cgroup.devices.allow = c 1:3 rwm
    lxc.cgroup.devices.allow = c 1:5 rwm
    # consoles
    lxc.cgroup.devices.allow = c 5:1 rwm
    lxc.cgroup.devices.allow = c 5:0 rwm
    lxc.cgroup.devices.allow = c 4:0 rwm
    lxc.cgroup.devices.allow = c 4:1 rwm
    # /dev/{,u}random
    lxc.cgroup.devices.allow = c 1:9 rwm
    lxc.cgroup.devices.allow = c 1:8 rwm
    lxc.cgroup.devices.allow = c 136:* rwm
    lxc.cgroup.devices.allow = c 5:2 rwm
    # rtc
    lxc.cgroup.devices.allow = c 254:0 rm
    #sound
    lxc.cgroup.devices.allow = c 116:* rwm
    lxc.mount.entry = /dev/snd dev/snd none bind,optional,create=dir 0 0 
    #tun/tap adapters
    lxc.cgroup.devices.allow = c 10:200 rwm 
    #video0
    lxc.cgroup.devices.allow = c 81:* rwm
    lxc.mount.entry = /dev/video0 dev/video0 none bind,optional,create=file
    lxc.mount.entry = /dev/dri dev/dri none bind,optional,create=dir
    lxc.mount.entry = /tmp/.X11-unix/X0 tmp/.X11-unix/X0 none bind,optional,create=file
    




    The container is ready to use.

    Using



    We’ll install, for example, i2p with Tor, if you haven’t done this before, and immediately configure privoxy:
    wget -q https://geti2p.net/_static/i2p-debian-repo.key.asc -O- | sudo apt-key add -
    echo "deb http://deb.i2p2.no/ jessie main" >/etc/apt/sources.list.d/i2p.list
    echo "deb-src http://deb.i2p2.no/ jessie main" >>/etc/apt/sources.list.d/i2p.list
    apt-get update
    apt-get install privoxy i2p tor
    

    / etc / privoxy / config

    user-manual /usr/share/doc/privoxy/user-manual
    confdir /etc/privoxy
    logdir /var/log/privoxy
    actionsfile user.action      # User customizations
    filterfile default.filter
    filterfile user.filter      # User customizations
    logfile logfile
    listen-address  localhost:8118
    toggle  1
    enable-remote-toggle  1
    enable-remote-http-toggle  1
    enable-edit-actions 1
    enforce-blocks 0
    buffer-limit 4096
    enable-proxy-authentication-forwarding 0
    forwarded-connect-retries  0
    accept-intercepted-requests 0
    allow-cgi-request-crunching 0
    split-large-forms 0
    keep-alive-timeout 5
    tolerate-pipelining 1
    socket-timeout 300
    forward .i2p localhost:4444
    forward-socks5 .onion localhost:9050 .
    




    Running graphical applications like a browser is most convenient through ssh:
    ssh -Y 172.20.0.2 "PULSE_SERVER=172.20.0.1 http_proxy=127.0.0.1:8118 chromium"
    





    Also, of course, the LXC provides tools for cloning containers and removing snapshots.

    So, for example, you can clone a container whose file system will be an LVM snapshot using the command:
    sudo lxc-clone -s -H -o deb_test -L 200M --new deb_test2
    




    A deb_test2 container will be created with a file system hosted on a 200MB LVM snapshot (for storing diffs). This will be an exact copy of deb_test, on which you can conduct a couple of experiments and, for example, delete it painlessly.

    But lxc-snapshot with LVM as storage, for some reason does not work on lxc-1.0.6:
    ->sudo lxc-snapshot -n deb_test 
    	lxc_container: deb_test's backing store cannot be backed up.
    	lxc_container: Your container must use another backing store type.
    	lxc_container: Error creating a snapshot
    


    The problem is described and discussed here . Therefore, the pictures will have to be done the old fashioned way:
    sudo lvcreate -L100M -s -n deb_test_before_rm_rf -p r /dev/nethack-vg/deb_test
    


    In this case, we created a read-only snapshot with the name deb_test_before_rm_rf of size 100MB. What to do next? For example, you can dump it using dd, transfer it to another machine along with the container configs, create a volume of the required size there and spill it with the same dd (cp, cat, etc.) - a kind of “live migration”.

    As described above, there are tons of uses for containers. But the main thing for home use, in my opinion, is application isolation.

    That's all for now.

    Also popular now: