policy to systemd

Russell Coker <[email protected]> Sat, 21 Dec 2024 13:23:44 +1100
Newsgroups org.kernel.vger.selinux-refpolicy
Message-ID <9420284.CDJkKcVGEf@cupcakke>
I've attached an early version of a program to generate systemd security 
settings from SE Linux policy.  The aim of this is to lock down daemons on 
non-SE systems while using tested policy and to also act as a second level of 
security on SE Linux systems while giving a lower score for "systemd-analyze 
security".

Anyone have any ideas for how to automatically determine SystemCallFilter 
values from policy?

-- 
My Main Blog         http://etbe.coker.com.au/
My Documents Blog    http://doc.coker.com.au/
pol2systemd.pl (application/x-perl, 4.1 KB)
#!/usr/bin/perl
use strict;

# takes a single parameter of a domain for a daemon and generates systemd
# security rules based on the SE Linux policy

my @RestrictAddressFamilies;
push @RestrictAddressFamilies, "AF_NETLINK";
my @SystemCallFilter;
my %cfg;

foreach ('PrivateDevices', 'RestrictSUIDSGID', 'ProtectHome', 'ProtectClock', 'MemoryDenyWriteExecute', 'RestrictNamespaces', 'ProtectKernelLogs', 'ProtectControlGroups', 'ProtectKernelModules', 'ProtectHostname', 'ProtectKernelTunables', 'PrivateTmp', 'NoNewPrivileges')
{
  $cfg{$_} = "true";
}
my $CapabilityBoundingSet = "";

open(TRANS, "sesearch -T -s $ARGV[0] -c process|") or die "Can't run sesearch";
while(<TRANS>)
{
  $cfg{NoNewPrivileges} = "false";
}
close(TRANS);

open(ALLOW, "sesearch -A -s $ARGV[0]|") or die "Can't run sesearch";
while(<ALLOW>)
{
  chomp;
  # remove boolean checks, anything allowed with any boolean is added
  $_ =~ s/ \[.*$//;
  # remove trailing ;
  $_ =~ s/;//;
  if($_ =~ ":capability ")
  {
    $_ =~ s/^.*:capability //;
    $CapabilityBoundingSet .= $_;
    if($CapabilityBoundingSet =~ "CAP_MKNOD")
    {
      $cfg{PrivateDevices} = "false";
    }
    next;
  }
  if($_ =~ ":chr_file ")
  {
    if($_ =~ "console_device_t" or $_ =~ "_devpts_t" or $_ =~ "ptynode" or $_ =~ "tty_device_t" or $_ =~ "ttynode" or $_ =~ "devtty_t" or $_ =~ "null_device_t" or $_ =~ "urandom_device_t" or $_ =~ "zero_device_t")
    {
      next;
    }
    $cfg{PrivateDevices} = "false";
    next;
  }
  if($_ =~ ":blk_file ")
  {
    $cfg{PrivateDevices} = "false";
    next;
  }
  if($_ =~ /exec_t.*relabel/)
  {
    $cfg{RestrictSUIDSGID} = "false";
  }
  if($_ =~ /:tmp_t/)
  {
    $cfg{PrivateTmp} = "false";
  }
  if($_ =~ "home_root_t" or $_ =~ "user_home_dir_t")
  {
    if(not $_ =~ "nsswitch_domain")
    {
      $cfg{ProtectHome} = "false";
    }
    next;
  }
  if($_ =~ /process.*execmem/)
  {
    $cfg{MemoryDenyWriteExecute} = "false";
    next;
  }
  if($_ =~ /cap_userns/ or $_ =~ /cap2_userns/)
  {
    $cfg{RestrictNamespaces} = "false";
    next;
  }
  if($_ =~ /cgroup.*write/)
  {
    $cfg{ProtectControlGroups} = "false";
    next;
  }
  if($_ =~ /kernel_t:system module_request/)
  {
    $cfg{ProtectKernelModules} = "false";
    next;
  }
  if($_ =~ /sysctl_.*write/)
  {
    $cfg{ProtectKernelTunables} = "false";
    next;
  }
  if($_ =~ /udp_socket/ or $_ =~ /tcp_socket/)
  {
    push @RestrictAddressFamilies, "AF_INET AF_INET6";
    next;
  }
  if($_ =~ /unix_.*_socket/)
  {
    push @RestrictAddressFamilies, "AF_UNIX";
    next;
  }
  if($_ =~ /packet_socket/)
  {
    push @RestrictAddressFamilies, "AF_PACKET";
    next;
  }
}
close(ALLOW);


my $ProtectSystem = "strict";
open(STRICTWRITE, "(sesearch -A -s $ARGV[0] -c dir -p write ; sesearch -A -s $ARGV[0] -c file -p write)|") or die "Can't run sesearch";
while(<STRICTWRITE>)
{
  $ProtectSystem = "full";
  open(ETC, "(sesearch -A -s $ARGV[0] -t configfile -c dir -p write ; sesearch -A -s $ARGV[0] -t configfile -c file -p write)|") or die "Can't run sesearch";
  while(<ETC>)
  {
    $ProtectSystem = "true";
    last;
  } 
  close(ETC);
  last;
}
close(STRICTWRITE);

$CapabilityBoundingSet =~ s/[\{\}]/ /g;
$CapabilityBoundingSet =~ s/  / /g;
$CapabilityBoundingSet = uc($CapabilityBoundingSet);
$CapabilityBoundingSet = join(" CAP_", split(/ /, $CapabilityBoundingSet));
if (length($CapabilityBoundingSet) > 0)
{
  $CapabilityBoundingSet = "CAP_" . $CapabilityBoundingSet;
}

if($CapabilityBoundingSet =~ "CAP_SETGID" or $CapabilityBoundingSet =~ "CAP_SETUID")
{
  $cfg{RestrictSUIDSGID} = "false";
}
if($CapabilityBoundingSet =~ "CAP_SYS_TIME")
{
  $cfg{ProtectClock} = "false";
}
if($CapabilityBoundingSet =~ "CAP_SYSLOG")
{
  $cfg{ProtectKernelLogs} = "false";
}
if($CapabilityBoundingSet =~ "CAP_SYS_ADMIN")
{
  $cfg{ProtectHostname} = "false";
}

print "CapabilityBoundingSet=$CapabilityBoundingSet\n";
print "ProtectSystem=$ProtectSystem\n";
foreach (keys(%cfg))
{
  print "$_=$cfg{$_}\n";
}

# from https://perlmaven.com/unique-values-in-an-array-in-perl
sub uniq
{
  my %h = map { $_ => 1 } @_;
  keys %h
};

my $res = join(" ", uniq(@RestrictAddressFamilies));
print "RestrictAddressFamilies=$res\n";