Categories
DSSP2

Leveraging Secmark on Agnus for termy.id

I have a login shell account on my Agnus host that I basically use for shared confined shell access. It is not quite a minimal login shell but not far from it. The user can connect with Termy as well as SSH with SFTP access, it has a systemd –user instance, and it has access to emacs, git client, screen.

Emacs and git can optionally be used to connect to the network, but I did not want that to be an all-or-nothing proposition. So I decided to leverage Secmark so that Emacs Termy instance (Eww) can be used to browse “allowed_web” and so that Git client Termy instance can be used to connect to “allowed_git” (this only includes Git access via git:// an https:// — so no ssh:// access). Also DNS access is restricted to “allowed_dns”. Local DNS is currently “allowed_dns”, defensec.nl is currently “allowed_web” and “allowed_git”. So Termy can use Emacs Eww to browse this blog , and git can be used to access the dssp2.git repositories. I can easily manage the “allowed_web”, “allowed_git”, and “allowed_dns” hosts. And I might extend Emacs access to for example mail with “allowed_mail”, IRC “allowed_irc”, etc.

The mysecmark.cil policy module that I use for this:

(block allowed_dns (blockinherit net.packet.obj_template))
(block allowed_git (blockinherit net.packet.obj_template))
(block allowed_web (blockinherit net.packet.obj_template))

(typeattribute secmark_constrained_subj_type_attribute)
(typeattributeset secmark_constrained_subj_type_attribute (emacs.termy.subj git.client.termy.subj screen.termy.subj termy_server.termy.subj systemd.termy.subj emacs.client.termy.subj passwd.termy.subj))

(typeattribute dns_client_minus_secmark_constrained_type_attribute)
(typeattributeset dns_client_minus_secmark_constrained_type_attribute (and dns.client_subj_type_attribute (not secmark_constrained_subj_type_attribute)))

(call sys.invalid.packet_send (dns_client_minus_secmark_constrained_type_attribute))
(call sys.invalid.packet_recv (dns_client_minus_secmark_constrained_type_attribute))

(call allowed_dns.packet_recv (dns.client_subj_type_attribute))
(call allowed_dns.packet_send (dns.client_subj_type_attribute))

(call allowed_git.packet_recv (dns.client_subj_type_attribute))
(call allowed_git.packet_send (dns.client_subj_type_attribute))

(call allowed_web.packet_recv (dns.client_subj_type_attribute))
(call allowed_web.packet_send (dns.client_subj_type_attribute))

(call sys.invalid.dontaudit_packet_recv (secmark_constrained_subj_type_attribute))
(call sys.invalid.dontaudit_packet_send (secmark_constrained_subj_type_attribute))

The /etc/sysconfig/nftables.secmark configuration I use for this:

table inet mysecmark {

    secmark allowed_dns {
        "sys.id:sys.role:allowed_dns.packet:s0"
    }

    secmark allowed_git {
        "sys.id:sys.role:allowed_git.packet:s0"
    }

    secmark allowed_web {
        "sys.id:sys.role:allowed_web.packet:s0"
    }

    map secmapping4_out {
        type ipv4_addr . inet_service : secmark
        elements = { 192.168.1.1 . 53 : "allowed_dns", 80.100.19.56 . 9418 : "allowed_git", 80.100.19.56 . 80 : "allowed_web", 80.100.19.56 . 443 : "allowed_web" }
    }

    map secmapping6_out {
        type ipv6_addr . inet_service : secmark
        elements = { fd18:5480:168d::1 . 53 : "allowed_dns", 2001:985:d55d::711 . 9418 : "allowed_git", 2001:985:d55d::711 . 80 : "allowed_web", 2001:985:d55d::711 . 443 : "allowed_web" }
    }

    chain input {
        type filter hook input priority -225;

        # label new incoming packets and add to connection
        ct state new ct secmark set meta secmark

        # set label for est/rel packets from connection
        ct state established,related meta secmark set ct secmark
    }

    chain output {
        type filter hook output priority 225;

        # label new outgoing packets and add to connection
        ct state new meta secmark set ip daddr . tcp dport map @secmapping4_out
        ct state new meta secmark set ip daddr . udp dport map @secmapping4_out
        ct state new meta secmark set ip6 daddr . tcp dport map @secmapping6_out
        ct state new meta secmark set ip6 daddr . udp dport map @secmapping6_out
        ct state new ct secmark set meta secmark

        # set label for est/rel packets from connection
        ct state established,related meta secmark set ct secmark
    }
}

The /etc/systemd/system/nftables.secmark.service I use for this:

# /etc/systemd/system/nftables.secmark.service
[Unit]
Description=Netfilter Tables
Documentation=man:nft(8)
Wants=network-pre.target
Before=network-pre.target

[Service]
Type=oneshot
ProtectSystem=full
ProtectHome=true
ExecStart=/sbin/nft -f /etc/sysconfig/nftables.secmark
ExecReload=/sbin/nft 'flush table inet mysecmark; include "/etc/sysconfig/nftables.secmark";'
ExecStop=/sbin/nft flush table inet mysecmark
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
Categories
DSSP2

Another pointless example of NFT, Secmark and DSSP2-Standard

I was just playing with things to get a little bit more used to the NFT syntax. In the example below I only allow the firefox instances of users in the wheel group to access the local DNS server and the rt.com website. Everything else is allowed.

#!/usr/sbin/nft -f

# nft -f <file>

#(in dns (block client (blockinherit net.packet.obj_template)))
#(block rt (block client (blockinherit net.packet.obj_template)))
#(typeattribute dns_clients_minus_wheel_seamonkey)
#(typeattributeset dns_clients_minus_wheel_seamonkey (and dns.client_subj_type_attribute (not seamonkey.wheel.subj)))
#(call sys.invalid.packet_send (dns_clients_minus_wheel_seamonkey))
#(call sys.invalid.packet_recv (dns_clients_minus_wheel_seamonkey))
#(call dns.client.packet_recv (dns.client_subj_type_attribute))
#(call dns.client.packet_send (dns.client_subj_type_attribute))
#(call rt.client.packet_recv (dns.client_subj_type_attribute))
#(call rt.client.packet_send (dns.client_subj_type_attribute))

# setsebool sys.recv_and_send_invalid_packets off

table inet mysecmark {

    secmark dns_client {
        "sys.id:sys.role:dns.client.packet:s0"
    }
    secmark rt_client {
        "sys.id:sys.role:rt.client.packet:s0"
    }

    map secmapping_4_out {
        type ipv4_addr . inet_service : secmark
        elements = { 192.168.1.1 . 53 : "dns_client", 207.244.80.166 . 80 : "rt_client", 207.244.80.166 . 443 : "rt_client", 92.223.126.251 . 80 : "rt_client", 92.223.126.251 . 443 : "rt_client" }
    }

    map secmapping_6_out {
        type ipv6_addr . inet_service : secmark
        elements = { fd18:5480:168d::1 . 53 : "dns_client", 2001:1af8:4700:b220::112 . 80 : "rt_client", 2001:1af8:4700:b220::112 . 443 : "rt_client", 2a03:90c0:9997::9997 . 80 : "rt_client", 2a03:90c0:9997::9997 . 443 : "rt_client" }
    }

    chain input {
        type filter hook input priority -225;

        # label new incoming packets and add to connection
        ct state new ct secmark set meta secmark

        # set label for est/rel packets from connection
        ct state established,related meta secmark set ct secmark
    }

    chain output {
        type filter hook output priority 225;

        # label new outgoing packets and add to connection
        ct state new meta secmark set ip daddr . tcp dport map @secmapping_4_out
        ct state new meta secmark set ip daddr . udp dport map @secmapping_4_out
        ct state new meta secmark set ip6 daddr . tcp dport map @secmapping_6_out
        ct state new meta secmark set ip6 daddr . udp dport map @secmapping_6_out
        ct state new ct secmark set meta secmark

        # set label for est/rel packets from connection
        ct state established,related meta secmark set ct secmark
    }
}

This stuff is more powerful than systemd IPAddressAllow= and IPaddressDeny= though (If BPF is not disabled due to Kernel Lock-down in the first place), as you can also leverage other attributes like protocol, source and destination port and address.

Demo time:

Categories
DSSP2

Per-process firewalling with DSSP2-standard and NFTables using Secmark filtering

NFTables 0.9.3 was recently released with Secmark filtering support (Thanks!). Secmark with DSSP2-standard allows for per-process network filtering.

DSSP2-standard by default allows processes to send and receive “invalid” packets with the sys.recv_and_send_invalid_packets boolean. This means that processes can send and receive packets that are not associated with a valid label.

This makes it easier to configure your firewall because you will not lose connectivity in enforcing mode while you label your packets and associate your packets with nodes and ports. Access to valid packet labels is ofcourse denied by default. Associate your packet types with allow rules.

Here is a simple example that creates four packet types: two client packet types and two server packet types. One server and client packet type for IRC and one server and client packet type for ICMP. The IRC client and server packet types are associated with port 4567 but in this example they are not associated with any specific nodes (IP addresses).

In the example there are no rules associated with the packet types yet and so access to these packets is blocked by default.

!/usr/sbin/nft -f

# nft -f <file>

# echo "(block icmp (block client (blockinherit net.packet.obj_template)) (block server (blockinherit net.packet.obj_template)))" > icmp_packet.ci>
# echo "(in irc (block client (blockinherit net.packet.obj_template)) (block server (blockinherit net.packet.obj_template)))" > irc_packet.cil && >

table inet mysecmark {

    secmark icmp_client {
        "sys.id:sys.role:icmp.client.packet:s0"
    }

    secmark icmp_server {
        "sys.id:sys.role:icmp.server.packet:s0"
    }

    secmark irc_client {
        "sys.id:sys.role:irc.client.packet:s0"
    }

    secmark irc_server {
        "sys.id:sys.role:irc.server.packet:s0"
    }

    map secmapping_in {
        type inet_service : secmark
        elements = { 4567 : "irc_server" }

    }

    map secmapping_out {
        type inet_service : secmark
        elements = { 4567 : "irc_client" }
    }

    chain input {
        type filter hook input priority -225;

        # label new incoming packets and add to connection
        ct state new meta secmark set tcp dport map @secmapping_in
        ct state new meta secmark set udp dport map @secmapping_in
        ct state new ip protocol icmp meta secmark set "icmp_server"
        ct state new ip6 nexthdr icmpv6 meta secmark set "icmp_server"
        ct state new ct secmark set meta secmark

        # set label for est/rel packets from connection
        ct state established,related meta secmark set ct secmark
    }

    chain output {
        type filter hook output priority 225;

        # label new outgoing packets and add to connection
        ct state new meta secmark set tcp dport map @secmapping_out
        ct state new meta secmark set udp dport map @secmapping_out
        ct state new ip protocol icmp meta secmark set "icmp_client"
        ct state new ip6 nexthdr icmpv6 meta secmark set "icmp_client"
        ct state new ct secmark set meta secmark

        # set label for est/rel packets from connection
        ct state established,related meta secmark set ct secmark
    }
}

You can simply load the above table with sudo nft -f <file>. This should not interfere with existing tables. So you can for example use this alongside Firewalld as that maintains its own “Firewalld” table. Make sure you declare the packet types referenced in the table:

echo "(block icmp (block client (blockinherit net.packet.obj_template)) (block server (blockinherit net.packet.obj_template)))" > icmp_packet.cil && sudo semodule -i icmp_packet.cil

echo "(in irc (block client (blockinherit net.packet.obj_template)) (block server (blockinherit net.packet.obj_template)))" > irc_packet.cil && sudo semodule -i irc_packet.cil

Once you have created packet types for all your network requirements and you have associated these packet types with the appropriate rules, You can disable the sys.recv_and_send_invalid_packets boolean so that all traffic that is not explicitly allowed will be denied.

If you want to undo your Secmark configuration then simply delete the mysecmark table:

nft delete table inet mysecmark

And remove the custom modules:

sudo semodule -r icmp_packet irc_packet

Categories
DSSP2

DSSP2 v2.1

I just tagged DSSP2 v2.1. It has been just over three months since I moved the policy from Github and imported DSSP2 as v2.0. Some of the more notable changes since then are:

  • Add policy needed to host DSSP2: Gitweb, git-http-backend, Confined Git shell, Git daemon.
  • The RPM domain has been reworked. Is now more strict in particular when the unconfined module is disabled.
  • Reworked sec_file and auth_file: Added support for conditional access, and removed direct access with shell. You can now only access these files with the appropriate API.
  • Added support for fsnotify (Linux 5.4)
  • Added initial support for audit_enable
  • Did a lot of re-ordering of access vectors to deal with compatibility with legacy interfaces (Debian)
  • DSSP2 works with Debian Buster/Sid (Minimal installation w/o printing server)
  • Some initial work has been done to support “Split” libvirt. Libvirt is splitting into individual socket activatable components and that allows for privilege separation.
  • DSSP2 works with Centos 8.0 (Minimal installation w/o printing server)
  • Some work on making Fedora 31/Centos OS Workstation (Gnome) work. Currently should work for unconfined users, confined users are currently unsupported in the scenario.
  • Support /var/run: After almost 10 years there is still just too much legacy referencing /var/run.
  • Added various new modules

… And much more

Categories
DSSP2

DSSP2 and SELinux 3.0

SELinux 3.0 shaves off 200KiB of the size of the DSSP2 standard kernel policy. I just updated my Fedora buildrpms script.