[Tails-dev] Fwd: Per-process netfilter rules

Delete this message

Reply to this message
Author: intrigeri
Date:  
To: tails-dev
Subject: [Tails-dev] Fwd: Per-process netfilter rules
Hi,

FWIW. Not sure what exactly we can use this for, but I would not be
surprised if this could help make some parts of Tails safer or the
implementation more elegant :)

This article documents how the traffic of specific Linux processes can be
subjected to a custom firewall or routing configuration, thanks to the magic
of cgroups. We will use the [Network classifier
cgroup](https://www.kernel.org/doc/Documentation/cgroups/net_cls.txt), which
allows tagging the packets sent by specific processes.

To create the cgroup which will be used to identify the processes I added
something like this to `/etc/rc.local`:



    mkdir /sys/fs/cgroup/net_cls/unlocator
    /bin/echo 42 > /sys/fs/cgroup/net_cls/unlocator/net_cls.classid
    chown md: /sys/fs/cgroup/net_cls/unlocator/tasks



The `tasks` file, which controls the membership of processes in a cgroup, is
made writeable by my user: this way I can add new processes without becoming
root. 42 is the arbitrary class identifier that the kernel will associate with
the packets generated by the member processes.

A command like `systemd-cgls /sys/fs/cgroup/net_cls/` can be used to explore
which processes are in which cgroup.

I use a simple shell wrapper to start a shell or a new program as members of
this cgroup:



    #!/bin/sh -e
    CGROUP_NAME=unlocator


    if [ ! -d /sys/fs/cgroup/net_cls/$CGROUP_NAME/ ]; then
      echo "The $CGROUP_NAME net_cls cgroup does not exist!" >&2
      exit 1
    fi


    /bin/echo $$ > /sys/fs/cgroup/net_cls/$CGROUP_NAME/tasks


    if [ $# = 0 ]; then
      exec ${SHELL:-/bin/sh}
    fi


    exec "$@"


My first goal is to use a _special_ name server for the DNS queries of some
processes, thanks to a second dnsmasq process which acts as a caching
forwarder.

`/etc/dnsmasq2.conf`:



    port=5354
    listen-address=127.0.0.1
    bind-interfaces
    no-dhcp-interface=*


    no-hosts
    no-resolv
    server=185.37.37.37
    server=185.37.37.185


`/etc/systemd/system/dnsmasq2.service`:



    [Unit]
    Description=dnsmasq - Second instance
    Requires=network.target


    [Service]
    ExecStartPre=/usr/sbin/dnsmasq --test
    ExecStart=/usr/sbin/dnsmasq --keep-in-foreground --conf-file=/etc/dnsmasq2.conf
    ExecReload=/bin/kill -HUP $MAINPID
    PIDFile=/run/dnsmasq/dnsmasq.pid


    [Install]
    WantedBy=multi-user.target



Do not forget to enable the new service:



    systemctl enable dnsmasq2
    systemctl start dnsmasq2


Since the [cgroup match extension](https://git.netfilter.org/iptables/plain/ex
tensions/libxt_cgroup.man) is not yet available in a released version of
iptables, you will first need to build and install it manually:



    git clone git://git.netfilter.org/iptables.git
    cd iptables
    ./autogen.sh
    ./configure
    make -k
    sudo cp extensions/libxt_cgroup.so /lib/xtables/
    sudo chmod -x /lib/xtables/libxt_cgroup.so


The netfilter configuration required is very simple: all DNS traffic from the
marked processes is redirected to the port of the local dnsmasq2:



    iptables -t nat -A OUTPUT -m cgroup --cgroup 42 -p udp --dport 53 -j REDIRECT --to-ports 5354
    iptables -t nat -A OUTPUT -m cgroup --cgroup 42 -p tcp --dport 53 -j REDIRECT --to-ports 5354


For related reasons, I also need to disable IPv6 for these processes:



    ip6tables -A OUTPUT -m cgroup --cgroup 42 -j REJECT


I use a different cgroup to force some programs to use my office VPN by first
setting a netfilter packet mark on their traffic:



    iptables -t mangle -A OUTPUT -m cgroup --cgroup 43 -j MARK --set-mark 43


The packet mark is then used to policy-route this traffic using a dedicate
VRF, i.e. routing table 43:



    ip rule add fwmark 43 table 43


This VPN VRF just contains a default route for the VPN interface:



    ip route add default dev tun0 table 43


Depending on your local configuration it may be a good idea to also add to the
VPN VRF the routes of your local interfaces:



    ip route show scope link proto kernel \
      | xargs -I ROUTE ip route add ROUTE table 43


Since the source address selection happens before the traffic is diverted to
the VPN, we also need to source-NAT to the VPN address the marked packets:



    iptables -t nat -A POSTROUTING -m mark --mark 43 --out-interface tun0 -j MASQUERADE




URL: http://blog.bofh.it/debian/id_457


--
intrigeri