Categories
DSSP2

Emacs major mode for editing Common Intermediate Language (CIL)

I had a cil-mode.el before but it was basically a modified lisp-mode.el. This time I created a cil-mode.el that is derived from lisp-mode instead. It makes it cleaner. Its not much though. It has custom syntax highlighting for various types of statements, keywords etc. I do need to work on my color palette though as the current scheme might not work well on light backgrounds. I also added syntax completion but only for the statements and keywords, so nothing too fancy. The indentation and positioning is derived from lisp-mode (indentation is HARD). Also added some other minor goodies. Would love to have some “abbrev” neatness but that is also non-trivial. Was looking into “statement look-up” feature but the CIL docs are on github and that does not allow you to search without subscription/login (I boycotted GitHub BTW). Anyhow, without further ado: cil-mode.el. Put it in your ~/.emacs.d and run byte-compile-file ~/.emacs.d/cil-mode.el then either load it wih m-x cil-mode or open a file with .cil extension.

;;; cil-mode.el --- major mode for editing Common Intermediate Language (CIL) -*- lexical-binding: t; -*-

;; SPDX-License-Identifier: GPL-3.0-or-later
;; Copyright (C) 2020  Dominick Grift
;; Author: Dominick Grift <dac.override@gmail.com>

;;; Code:

;;; define faces
(defface cil-access-vector-rules-face
  '((t :foreground "gold"
       ))
  "Face for access vector rules"
  :group 'cil-mode )
(defvar cil-access-vector-rules-face 'cil-access-vector-rules-face)

(defface cil-call-macro-statements-face
  '((t :foreground "royalblue"
       ))
  "Face for call/macro statements"
  :group 'cil-mode )
(defvar cil-call-macro-statements-face 'cil-call-macro-statements-face)

(defface cil-class-permission-statements-face
  '((t :foreground "magenta"
       ))
  "Face for class/permission statements"
  :group 'cil-mode )
(defvar cil-class-permission-statements-face 'cil-class-permission-statements-face)

(defface cil-conditional-statements-face
  '((t :foreground "yellow"
       ))
  "Face for conditional statements"
  :group 'cil-mode )
(defvar cil-conditional-statements-face 'cil-conditional-statements-face)

(defface cil-constraint-statements-face
  '((t :foreground "hotpink"
       ))
  "Face for constraint statements"
  :group 'cil-mode )
(defvar cil-constraint-statements-face 'cil-constraint-statements-face)

(defface cil-container-statements-face
  '((t :foreground "grey"
       ))
  "Face for container statements"
  :group 'cil-mode )
(defvar cil-container-statements-face 'cil-container-statements-face)

(defface cil-context-statement-face
  '((t :foreground "blue"
       ))
  "Face for context statement"
  :group 'cil-mode )
(defvar cil-context-statement-face 'cil-context-statement-face)

(defface cil-default-object-statements-face
  '((t :foreground "green"
       ))
  "Face for default object statements"
  :group 'cil-mode )
(defvar cil-default-object-statements-face 'cil-default-object-statements-face)

(defface cil-file-labeling-statements-face
  '((t :foreground "pink"
       ))
  "Face for file labeling statements"
  :group 'cil-mode )
(defvar cil-file-labeling-statements-face 'cil-file-labeling-statements-face)

(defface cil-infiniband-statements-face
  '((t :foreground "orange"
       ))
  "Face for infiniband statements"
  :group 'cil-mode )
(defvar cil-infiniband-statements-face 'cil-infiniband-statements-face)

(defface cil-mls-labeling-statements-face
  '((t :foreground "khaki"
       ))
  "Face for mls labeling statements"
  :group 'cil-mode )
(defvar cil-mls-labeling-statements-face 'cil-mls-labeling-statements-face)

(defface cil-network-labeling-statements-face
  '((t :foreground "teal"
       ))
  "Face for network labeling statements"
  :group 'cil-mode )
(defvar cil-network-labeling-statements-face 'cil-network-labeling-statements-face)

(defface cil-policy-config-statements-face
  '((t :foreground "aqua"
       ))
  "Face for policy config statements"
  :group 'cil-mode )
(defvar cil-policy-config-statements-face 'cil-policy-config-statements-face)

(defface cil-role-statements-face
  '((t :foreground "wheat"
       ))
  "Face for role statements"
  :group 'cil-mode )
(defvar cil-role-statements-face 'cil-role-statements-face)

(defface cil-sid-statements-face
  '((t :foreground "peru"
       ))
  "Face for sid statements"
  :group 'cil-mode )
(defvar cil-sid-statements-face 'cil-sid-statements-face)

(defface cil-type-statements-face
  '((t :foreground "deepskyblue"
       ))
  "Face for type statements"
  :group 'cil-mode )
(defvar cil-type-statements-face 'cil-type-statements-face)

(defface cil-user-statements-face
  '((t :foreground "darkolivegreen"
       ))
  "Face for user statements"
  :group 'cil-mode )
(defvar cil-user-statements-face 'cil-user-statements-face)

(defface cil-xen-statements-face
  '((t :foreground "coral"
       ))
  "Face for xen statements"
  :group 'cil-mode )
(defvar cil-xen-statements-face 'cil-xen-statements-face)

(defface cil-name-string-face
  '((t :foreground "mediumseagreen"
       ))
  "Face for name string"
  :group 'cil-mode )
(defvar cil-name-string-face 'cil-name-string-face)

(defface cil-true-false-face
  '((t :foreground "lavender"
       ))
  "Face for true/false"
  :group 'cil-mode )
(defvar cil-true-false-face 'cil-true-false-face)

(defface cil-operators-face
  '((t :foreground "orangered"
       ))
  "Face for operators"
  :group 'cil-mode )
(defvar cil-operators-face 'cil-operators-face)

;;; syntax highlighting
(setq cil-highlights
      (let* (
	     ;; define several category of keywords
	     (cil-access-vector-rules '("allow" "auditallow"
					"dontaudit" "neverallow"
					"allowx" "auditallowx"
					"dontauditx" "neverallowx"))
	     (cil-call-macro-statements '("call" "macro"))
	     (cil-class-permission-statements '("common"
						"classcommon"
						"class" "classorder"
						"classpermission"
						"classpermissionset"
						"classmap"
						"classmapping"
						"permissionx"))
	     (cil-conditional-statements '("boolean" "booleanif"
					   "tunable" "tunableif"))
	     (cil-constraint-statements '("constrain"
					  "validatetrans"
					  "mlsconstrain"
					  "mlsvalidatetrans"))
	     (cil-container-statements '("block" "blockabstract"
					 "blockinherit" "optional"
					 "in"))
	     (cil-context-statement '("context"))
	     (cil-default-object-statements '("defaultuser"
					      "defaultrole"
					      "defaulttype"
					      "defaultrange"))
	     (cil-file-labeling-statements '("filecon" "fsuse"
					     "genfscon"))
	     (cil-infiniband-statements '("ibpkeycon"
					  "ibendportcon"))
	     (cil-mls-labeling-statements '("sensitivity"
					    "sensitivityalias"
					    "sensitivityaliasactual"
					    "sensitivityorder"
					    "category"
					    "categoryalias"
					    "categoryaliasactual"
					    "categoryorder"
					    "categoryset"
					    "sensitivitycategory"
					    "level" "levelrange"
					    "rangetransition"))
	     (cil-network-labeling-statements '("ipaddr" "netifcon"
						"nodecon"
						"portcon"))
	     (cil-policy-config-statements '("mls" "handleunknown"
					     "policycap"))
	     (cil-role-statements '("role" "roletype"
				    "roleattribute"
				    "roleattributeset" "roleallow"
				    "roletransition" "rolebounds"))
	     (cil-sid-statements '("sid" "sidorder" "sidcontext"))
	     (cil-type-statements '("type" "typealias"
				    "typealiasactual"
				    "typeattribute"
				    "typeattributeset" "typebounds"
				    "typechange" "typemember"
				    "typetransition"
				    "typepermissive"))
	     (cil-user-statements '("user" "userrole"
				    "userattribute"
				    "userattributeset" "userlevel"
				    "userrange" "userbounds"
				    "userprefix" "selinuxuser"
				    "selinuxuserdefault"))
	     (cil-xen-statements '("iomemcon" "ioportcon"
				   "pcidevicecon" "pirqcon"
				   "devicetreecon"))
	     (cil-name-string '("name"))
	     (cil-true-false '("true" "false"))
	     (cil-operators '("and" "or" "xor" "eq" "neq" "not"
			      "all" "dom" "domby" "incomp" "range"))

	     ;; generate regex string for each category of keywords
	     (cil-access-vector-rules-regexp (regexp-opt cil-access-vector-rules 'words))
	     (cil-call-macro-statements-regexp (regexp-opt cil-call-macro-statements 'words))
	     (cil-class-permission-statements-regexp (regexp-opt cil-class-permission-statements 'words))
	     (cil-conditional-statements-regexp (regexp-opt cil-conditional-statements 'words))
	     (cil-constraint-statements-regexp (regexp-opt cil-constraint-statements 'words))
	     (cil-container-statements-regexp (regexp-opt cil-container-statements 'words))
	     (cil-context-statement-regexp (regexp-opt cil-context-statement 'words))
	     (cil-default-object-statements-regexp (regexp-opt cil-default-object-statements 'words))
	     (cil-file-labeling-statements-regexp (regexp-opt cil-file-labeling-statements 'words))
	     (cil-infiniband-statements-regexp (regexp-opt cil-infiniband-statements 'words))
	     (cil-mls-labeling-statements-regexp (regexp-opt cil-mls-labeling-statements 'words))
	     (cil-network-labeling-statements-regexp (regexp-opt cil-network-labeling-statements 'words))
	     (cil-policy-config-statements-regexp (regexp-opt cil-policy-config-statements 'words))
	     (cil-role-statements-regexp (regexp-opt cil-role-statements 'words))
	     (cil-sid-statements-regexp (regexp-opt cil-sid-statements 'words))
	     (cil-type-statements-regexp (regexp-opt cil-type-statements 'words))
	     (cil-user-statements-regexp (regexp-opt cil-user-statements 'words))
	     (cil-xen-statements-regexp (regexp-opt cil-xen-statements 'words))
	     (cil-name-string-regexp (regexp-opt cil-name-string 'words))
	     (cil-true-false-regexp (regexp-opt cil-true-false 'words))
	     (cil-operators-regexp (regexp-opt cil-operators 'words)))
	`(

          (,cil-access-vector-rules-regexp . cil-access-vector-rules-face)
          (,cil-call-macro-statements-regexp . cil-call-macro-statements-face)
          (,cil-class-permission-statements-regexp . cil-class-permission-statements-face)
          (,cil-conditional-statements-regexp . cil-conditional-statements-face)
	  (,cil-constraint-statements-regexp . cil-constraint-statements-face)
	  (,cil-container-statements-regexp . cil-container-statements-face)
	  (,cil-context-statement-regexp . cil-context-statement-face)
	  (,cil-default-object-statements-regexp . cil-default-object-statements-face)
	  (,cil-file-labeling-statements-regexp . cil-file-labeling-statements-face)
	  (,cil-infiniband-statements-regexp . cil-infiniband-statements-face)
	  (,cil-mls-labeling-statements-regexp . cil-mls-labeling-statements-face)
	  (,cil-network-labeling-statements-regexp . cil-network-labeling-statements-face)
	  (,cil-policy-config-statements-regexp . cil-policy-config-statements-face)
	  (,cil-role-statements-regexp . cil-role-statements-face)
	  (,cil-sid-statements-regexp . cil-sid-statements-face)
	  (,cil-type-statements-regexp . cil-type-statements-face)
	  (,cil-user-statements-regexp . cil-user-statements-face)
	  (,cil-xen-statements-regexp . cil-xen-statements-face)
	  (,cil-name-string-regexp . cil-name-string-face)
	  (,cil-true-false-regexp . cil-true-false-face)
	  (,cil-operators-regexp . cil-operators-face)
          )))

;;; syntax table
(defvar cil-syntax-table nil "Syntax table for `cil-mode'")

(setq cil-syntax-table
      (let ( (synTable (make-syntax-table)))
        ;; set/modify each char's class

	;; do not highlight syntax with . and _
	(modify-syntax-entry ?_ "W" synTable)
        (modify-syntax-entry ?. "W" synTable)

        ;; lisp style comment “;; …”
        (modify-syntax-entry ?\; ". 12b" synTable)
        (modify-syntax-entry ?\n "> b" synTable)
	synTable))

;;; command completion with ido
(require 'ido)
(setq cil-keywords
      '("allow" "auditallow" "dontaudit" "neverallow" "allowx"
	"auditallowx" "dontauditx" "neverallowx" "call" "macro"
	"common" "classcommon" "class" "classorder" "classpermission"
	"classpermissionset" "classmap" "classmapping" "permissionx"
	"boolean" "booleanif" "tunable" "tunableif" "constrain"
	"validatetrans" "mlsconstrain" "mlsvalidatetrans" "block"
	"blockabstract" "blockinherit" "optional" "in" "context"
	"defaultuser" "defaultrole" "defaulttype" "defaultrange"
	"filecon" "fsuse" "genfscon" "ibpkeycon" "ibendportcon"
	"sensitivity" "sensitivityalias" "sensitivityaliasactual"
	"sensitivityorder" "category" "categoryalias"
	"categoryaliasactual" "categoryorder" "categoryset"
	"sensitivitycategory" "level" "levelrange" "rangetransition"
	"ipaddr" "nefifcon" "nodecon" "portcon" "mls" "handleunknown"
	"policycap" "role" "roletype" "roleattribute"
	"roleattributeset" "roleallow" "roletransition" "rolebounds"
	"sid" "sidorder" "sidcontext" "type" "typealias"
	"typealiasactual" "typeattribute" "typeattributeset"
	"typebounds" "typechange" "typemember" "typetransition"
	"typepermissive" "user" "userrole" "userattribute"
	"userattributeset" "userlevel" "userrange" "userbounds"
	"userprefix" "selinuxuser" "selinuxuserdefault" "iomemcon"
	"ioportcon" "pcidevicecon" "pirqcon" "devicetreecon" "name"
	"true" "false" "and" "or" "xor" "eq" "neq" "not" "all"
	"dom" "domby" "incomp" "range"))

(defun cil-complete-symbol ()
  "Perform keyword completion on current symbol.
This uses `ido-mode' user interface for completion"
  (interactive)
  (let* (
         ($bds (bounds-of-thing-at-point 'symbol))
         ($p1 (car $bds))
         ($p2 (cdr $bds))
         ($current-sym
          (if  (or (null $p1) (null $p2) (equal $p1 $p2))
              ""
            (buffer-substring-no-properties $p1 $p2)))
         $result-sym)
    (when (not $current-sym) (setq $current-sym ""))
    (setq $result-sym
          (ido-completing-read "" cil-keywords nil nil $current-sym ))
    (delete-region $p1 $p2)
    (insert $result-sym)))

;;; keymap commands
(defvar cil-map nil "Keymap for `cil-mode'")
(progn
  (setq cil-map (make-sparse-keymap))

  ;; ido command completion
  (define-key cil-map (kbd "TAB") 'cil-complete-symbol))

;;;###autoload
(define-derived-mode cil-mode lisp-mode "cil"
  "major mode for editing Common Intermediate Language (CIL)"

  (add-to-list 'auto-mode-alist '("\\.cil\\'" . cil-mode))

  ;; keymap
  (use-local-map cil-map) 

  ;; comment-dwim support
  (setq-local comment-start "; ")
  (setq-local comment-end "")

  ;; syntax table
  (set-syntax-table cil-syntax-table)

  ;; syntax highlighting
  (setq font-lock-defaults '((cil-highlights))))

(provide 'cil-mode)
;;; cil-mode.el ends here
Categories
DSSP2

Report 21 to 28 Dec. 2019

  • Added an FC spec for some emacs elpa helper scripts. Debian apt post installation scriptlet prompted this. Not sure if eventually i want to label that this way but for now this will keep apt happy.
  • Removed some macro access to capabilities for “common_user”. These macros are also called for prefixed domains and not only for “subj.common_subj” access. Therefore I shouldnt imply access to caps here.
  • I reverted a commit that actually opened up a LOT of bad access. These “subj.common_subj.all_macro_templates” reference “subj_type_attribute” from the calling namespace. If that type attribute does not exist there then it will use the name space of the macro. In this care it allows all callers for “chfn.run” access to various process perms for “subj.common_subj.subj_type_attribute”
  • One one of my systems fstrim does a “write” access check on mounpoints suchs as /boot and others (I guess the filesystems mounted on there don’t support “discared” even though the are storage) Anyhow, that is speculation and i dontaudited audit_access there.
  • Googler wants to execute uname.
  • Some changes to screen. Initially i gave “screen home” a private type for “.screenrc”, and screen read access. I opened this up for also make it apply to “screenlog” and “hardcopy”. So I ended giving “manage” access to screen for “screen home” files.. People might want screen to “write” to .screenrc so this is a little bit more tolerant/liberal.
  • Added a basic policy to “radeontop”. This is probably one of the simplest policy modules in dssp2 standard.

… And that wraps it up for this week.

Categories
DSSP2

DSSP2 Trivia

What happened here?

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.