DSSP3 status update

I am almost done sketching the outlines of DSSP3. I need some time to audit the things i did though. Today when perusing some of the functionality I hit an issue with name-based type transitions. Might be a bug. Sure looks like it. So yes bummer but the show must go on. So i have the basic system domain ready. Just need to start targeting more and more system services. I did a few so far just enough to get the system to boot in enforcing mode and with some basic stuff address properly. Networkd, Resolved, Logind, Login, Getty, SSH server and a few more, also chkpwd and updpwd as that is used by PAM. System logins are allowed by default, but can be disabled with the login.sys_login and ssh.daemon.sys_login booleans respectively. There is an unpriv user account, and one with sudo access to the system accound All unpriv users share the same login shell permissions with one exception: role access. The wheel.role may associate with sudo.subj, where the user.role may not. Ive revisited my RBACSEP and man it gets complicated really quick. Roles need to be allowed to associate with types, but the idea was that I did not want to give roles a carte blanche to just go and associate with all object types (I did that in DSSP2 and it makes thinks a lot easier) So now I am constantly identifying system objects that users need be able to create objects on. Examples are extended attribute file systems in general. Then theres the constraints. RBACSEP constrained roles arent allowed to write to system files unless explicity told otherwise. They can read all for a RBACSEP perspective, just no write operations. Exceptions are: /dev/tty, /dev/zero, various sock files (dbus for example), but also selinuxfs files (yes selinuxfs has files that are widely writable)

The RBACSEP implementation is very expensive in DSSP3 and now I remember why I decided to cut some corners there in DSSP2. There were quit a few deju vu moments for me so far. The next step is to address confined privileged roles, and then its just a matter of “porting” stuff. Eventually I will probably migrate to DSSP3. DSSP3 can be tuned to be pretty strict with some work but by default its is very open. Also the system domain is very broad but it makes things so much easier. I am getting used to the DSSP3 policy design and I am starting to really appreciate some of the decisions I made.

Regardless, after I took care of the basics for confined privileged roles, the focus will be on and on system services (short and long)



Okay where do I start. So I was working on DSSP3 docs. Halfway through. And then I got this idea. I dont want DSSP3 to be like DSSP2-minimal. I want it to be like DSSP2-standard. So I threw the docs out of the window. I will use what I have later.

I started working on DSSP3, and truth be told, I did not know where it would lead me. I still don’t. However I made some progress and slowly but surely some of the pieces start to fall into place. It is a weird experience as I am used to the concept of least privileged and DSSP3 is basically the opposite. Imagine one big “system” domain that is essentially unconfined but not in a traditional sense as there are automatic transitions to confined domains that AFAIK cannot be overriden. These confined domains are usually system daemon , short and long. Its not really my focus now, as I am trying to work towards the bigger picture. Which means I am moving towards confined logins and confined root shells. Once some of that is into place, the whole broad outlines are drawn. It seems I am getting there, but I drew blood. I mean it’s give and take. There are way’s for a confined system agents to get to the kernel. Not sure about user agents yet, but many user agents are hybrid and thus system agents as well. Anyway’s some of the ways are: readwrite inherited unix dgram, stream sockets, tty’s, pty’s connectto unix stream sockets. write sock files, append and read files, sending null, child terminated and generic signals. Imagine DBUS, systemd –system, roots systemd –user, RPM/DPKG and root logins ALL running in that same privileged system domain. You can actually login directly in the system domain. As a matter of fact thats the DEFAULT behavior. You can block system logins though. Advanced users block this access and instead map logins to confined users. Also unknown permissions ALLOWED by default. Its just the opposite of DSSP2, were defaulting to open instead of strict. The root account is hybrid and so that presents you with a dillema. How do you treat it. Is it a user or the system. In DSSP2 its a user in DSSP3 root its the system. You login as root? You ARE the system. Obviously things are not as simple as root logins have real user specific aspects like a runtime user dir in /run/user/0.

Thats where I am right now. the basic system outlines are almost into place. Then I can more to confined logins and confined root shells. The policy is CIL but nothing like DSSP2 and it took me quite some time to get used to it. In the mean time i am trying to maintain DSSP2 as well so its switching between languages all the time. Theres a lot to be said about DSSP3 language, both good and bad but i am slowely starting to like it more. Its just better thought out but not necessarily cleaner in every sense of the word. Its just a difference balance.

Its nice though. I basically just follow my gut. I have some broad ideas of where it should go (see many “a model” article) but as for the details its just “go with the flow”. Same with my model for user sessions. I know what I want, I just do not know how to implement it. The suspense is there to say the least. We will see. Leveraging roles more. Hopefully less policy eventually but that is not a given!

Have a look at the source and see if you can appreciate some of the design decisions. Heck I am open to suggestions and feedback. Its kind of frustrating working on it alone but I enjoy doing it. Sometimes I go through some rough patches but sometimes I really enjoy victory. Its really like playing a strategy game. Something like Riskor chess. You need to think three steps ahead of the opposition. I would probably call this game TAME OR BE TAMED. The system is out to get you!!!


Idea for alternate way of dealing with users

I am still working on documenting DSSP3. It is a struggle. I now have 8 chapters done, and I estimate another 8 to go. This post is not about that. Instead I wanted to write about this idea I had about redesigning policy for user login shells.

In DSSP2 every class of login user, actually i suppose every class of user period, has a private shell type. This complicates things but it also allows me to associate rules with individual shell types. Ive been playing with the idea of giving all logins except root the same login shell type, and then to leverage roles to govern what domains can be transitioned to using RBAC roletype rules. So user A and B can run app1 with a domain transtiion to app1_t because they both have the same shell type but user A’s role is not allowed to associate with app1_t and so user A is not able to run app1 with a domain transition because the persons role is not authorized. Thinks for example access to sudo and other setuid apps.

But root logins and root shells? You know root shouldnt be able to log in directly in the first place except for emergency shells and maybe local logins. But root shell policy certainly doesnt have to be the same as unpriv users shell policy. Some people in Fedora log in with staff_u:staff_r:staff_t and then run a root shell with staff_u:unconfined_r:unconfined_t instead of staff_u:sysadm_r:sysadm_t. I think this might be worth considering. I mean If you only log in as root from an emergency shell, and if you only access a root shell from sudo then it might not be so bad to give root shells unconfined access this way. You still log in with a confined shell. So you can just say, Joe can run sudo because joe’s role is authorized with sudo type, and joe’s role can be transitioned to some unconfined root shell role..

The root shell role does not have to be fully unconfined though. It can be some derivative that is unconfined minus somethingsomething like access to sec and auth files. Those can still only be access via vipw and visudo , semodule , auditctl etc etc. This design would greatly simplify policy and it would leverage/rely on RBAC more instead. Confined administration wouldnt be affected as those accounts still can have private types because those accounts shouldnt be used to run programs that need special access and then need to be able to access shells on behalf of calling users. They just need to be able to run systemctl, kill, maybe strace, a simple editor like vi or nano etc.

But yes root logins and shells would be unconfined or close to generic administration. So one needs to be careful not to allow root logins from the network, or rely on other means to secure that aspect. Its a bitter pill to swallow but it might be worth pursuing. I wonder how much can be saved in terms of rules,types and policy footprint in general etc. Its just a different approach. Different trade offs but its not like i am throwing everything over the fence..

I still have time to let this model sink in since it is going to take me atleast another week to complete a 0.1 version of the dssp3 docs, and as I have mentioned earlier I haven’t quite decided yet on whether I want to build a new personal policy on top of the currently iteration of DSSP3. It seems likely though because even though some aspects of DSSP3 do not quite sit well with me, I haven’t been able to come up with any better alternatives yet and I cannot dwell on it forever either so at some point I am going to have to accept a path forward.



I pushed an initial version of a next iteration of DSSP to Gitweb the other day. All things considering (especially the thing where I do this for fun) I am pretty satisfied with the result.

DSSP2 (standard) is and will in the foreseeable future be maintained as I use it on all my systems and obviously DSSP3 is not a substitute. I do plan though to create something equivalent to DSSP2 standard based on another iteration of DSSP (may well be DSSP3). I am not going to be rushed into it though. For now I will be working on DSSP2 as well as DSSP3 but I am not going to make any intrusive changes to DSSP2 (I guess I haven’t made any real intrusive changes to DSSP2 since version 0). DSSP2 will go into maintenance mode.

There is this one thing that bothers me more about DSSP3 than anything else. It is how I deal with filesystem identifiers. It is not consiistent with the remainder of the identifiers. In DSSP2 the various “partitions” are identified by the “partition name”. For example “dev” indentifiers are are in the “dev namespace”, files in “files”, fs in “fs” etc. The thing here is that filesystems are relatively unique in that you can only have one filesystem of each, where for example you usually have more than one file identifier or even for example have more than one config file identifier. Even when it comes to devices, as you have various classes of devices like terminals and storage and other nodes.

The way i designed it currently is that when you operate on an filesystem you end up calling a macro that is not prefixed with something that identifies filesystems. Instead the suffix identifies the identifiers as being a filesystem. Its a weird solution but I feel that its pretty elegant considering the circumstances. It should be obvious that I already decided to accept this implementation with DSSP3. Nonetheless it feels weird.

Also when you design a new policy from scratch it is kind of hard to anticipate how things are going to play out eventually. Until you fully leverage everything there can always surface aspects that you might not have foreseen. Anyway I guess I just wanted to vent my unease with the filesystem situation.

I am currently in the process of producing decent documentation for DSSP3. Writing is not one of my stronger skills and I still have some way’s to go but I do have three chapters that I am pretty happy with. Only about 13 or more to go.

I haven’t commited to DSSP2 for ten day’s but that is not only because of my work on DSSP3. Fedora has been pretty chaotic in the last few weeks as well and I decided to let this storm pass in hopes that things looked worse than they are. It looks like it my be prudent to wait some more as a recent mass-rebuilt still hasnt trickled down.

So for now its a focus on DSSP3 docs, then update Fedora/Debian and support any changes in DSSP2 for that, and then eventually decide on a way forward either with DSSP3 or maybe some other iteration. Ive also been contemplating my options for building on top a base. One of the other pain points of DSSP2 standard has also been that of “derived” domains. Things would be so much simpler if I could somehow do away with those. Seems as if this is easier said than done. I do have some ideas but I would have to sacrifice a lot in terms of niceness.

Then again, I do this for fun first and foremost. I like exploring. We’ll see. Just wanted to dump a blog about the current status.


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 <>

;;; 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"
						"class" "classorder"
	     (cil-conditional-statements '("boolean" "booleanif"
					   "tunable" "tunableif"))
	     (cil-constraint-statements '("constrain"
	     (cil-container-statements '("block" "blockabstract"
					 "blockinherit" "optional"
	     (cil-context-statement '("context"))
	     (cil-default-object-statements '("defaultuser"
	     (cil-file-labeling-statements '("filecon" "fsuse"
	     (cil-infiniband-statements '("ibpkeycon"
	     (cil-mls-labeling-statements '("sensitivity"
					    "level" "levelrange"
	     (cil-network-labeling-statements '("ipaddr" "netifcon"
	     (cil-policy-config-statements '("mls" "handleunknown"
	     (cil-role-statements '("role" "roletype"
				    "roleattributeset" "roleallow"
				    "roletransition" "rolebounds"))
	     (cil-sid-statements '("sid" "sidorder" "sidcontext"))
	     (cil-type-statements '("type" "typealias"
				    "typeattributeset" "typebounds"
				    "typechange" "typemember"
	     (cil-user-statements '("user" "userrole"
				    "userattributeset" "userlevel"
				    "userrange" "userbounds"
				    "userprefix" "selinuxuser"
	     (cil-xen-statements '("iomemcon" "ioportcon"
				   "pcidevicecon" "pirqcon"
	     (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)

;;; 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"
  (let* (
         ($bds (bounds-of-thing-at-point 'symbol))
         ($p1 (car $bds))
         ($p2 (cdr $bds))
          (if  (or (null $p1) (null $p2) (equal $p1 $p2))
            (buffer-substring-no-properties $p1 $p2)))
    (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'")
  (setq cil-map (make-sparse-keymap))

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

(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

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 “” 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.


DSSP2 Trivia

What happened here?


Leveraging Secmark on Agnus for

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”, 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 {

    secmark allowed_git {

    secmark allowed_web {

    map secmapping4_out {
        type ipv4_addr . inet_service : secmark
        elements = { . 53 : "allowed_dns", . 9418 : "allowed_git", . 80 : "allowed_web", . 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
Description=Netfilter Tables

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


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 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 {
    secmark rt_client {

    map secmapping_4_out {
        type ipv4_addr . inet_service : secmark
        elements = { . 53 : "dns_client", . 80 : "rt_client", . 443 : "rt_client", . 80 : "rt_client", . 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:


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)))" >>
# 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 {

    secmark icmp_server {

    secmark irc_client {

    secmark irc_server {

    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