Skip to content

Skapa SElinux kontext för en ny tjänst

Säg att du har skrivit en helt egen tjänst, i Python t.ex., som du vill ska köra under ett eget SE context för ytterligare säkerhet.

Normalt sett hamnar tjänster under init_t om du startar dem med systemd, eller initrc_t på vissa distros. Det är då inte så snyggt att börja öppna portar och tillåta andra saker för den kontexten eftersom det då tillåts åt alla processer som kör under samma kontext.

Fallgropar med skriptade tjänster

Först och främst måste man se till att anropa tjänsten direkt via en exekverbar fil, man får inte anropa tjänsten så här för att SElinux ska fungera.

python service.py
~/venv/bin/python ~/src/python.py
bash /usr/local/sbin/service.sh

För det andra ska vi installera alla relevanta filer för vår tjänst på rätt plats, vi ska t.ex. inte använda /home för något som har med tjänsten att göra. Detta underlättar senare när vi använder SElinux faciliteter för att ge tjänsten ett kontext.

Så för guidens skull gör vi några antaganden om tjänsten som läsaren själv får lösa eftersom det inte omfattas av denna guide.

  • ''hlrpcfront'' är användarnamnet som tjänsten ska köras av.

  • ''/usr/local/sbin/hlrpcfront'' är den exekverbara fil som direkt anropas för att starta tjänsten, ''chmod 0755'' på den.

  • ''/etc/hlrpcfront.conf'' är tjänstens konfigurationsfil, ska kunna läsas av ''hlrpcfront''.

  • ''/var/log/hlrpcfront'' är mappen dit tjänsten kommer skriva logfiler, ska ägas av ''hlrpcfront'' användaren.

  • systemd service unit som startar tjänsten åt dig, den måste användas genom hela guiden eftersom policyn vi skriver är beroende av den.

Installera paket

Börja med att installera paketet ''selinux-policy-devel'' som behövs för att utföra exempel i guiden.

$ sudo yum install selinux-policy-devel -y

Skapa startskript

Tjänsten jag beskriver för guiden är skriptad så jag skapar ett startskript för att slippa anropa en tolk.

1
2
3
#!/bin/bash
source /etc/sysconfig/hlrpcfront
/opt/hlrpcfront/venv/bin/python /opt/hlrpcfront/hlrpcfront -c /etc/hlrpcfront.conf -l /var/log/hlrpcfront/hlrpcfront.log

Notera här att jag har allt som berör applikationen, bibliotek, virtualenv, under en och samma katalog i ''/opt''.

Spara den filen och se till att den är körbar.

$ sudo chmod 0755 /usr/local/sbin/hlrpcfront

Skapa policy

Skapa en katalog för ditt arbete först.

$ mkdir selinux
$ cd selinux

Skapa din SElinux policy genom att redigera filen ''hlrpcfront.te'', namnet bör vara ganska unikt eftersom det finns många mjukvaror som skapar egna policys.

Här är ett exempel med engelska kommentarer för en tjänst som heter hlrpcfront.

policy_module(hlrpcfront,1.0.0)

# My types, these must be unique.

type hlrpcfront_t;
type hlrpcfront_exec_t;

# Basic macro for all init daemons, started by initrc or systemd.

init_daemon_domain(hlrpcfront_t, hlrpcfront_exec_t)

# My log type, used solely for my log files.

type hlrpcfront_log_t;

# Macro that helps define my logging type.

logging_log_file(hlrpcfront_log_t)

# Use pre-defined macro to allow creation of log files

create_files_pattern(hlrpcfront_t, hlrpcfront_log_t, hlrpcfront_log_t)
setattr_files_pattern(hlrpcfront_t, hlrpcfront_log_t, hlrpcfront_log_t)

# Use interface macro to allow read/write on log files

hlrpcfront_rw_log_files(hlrpcfront_t)

# DNS queries

auth_use_nsswitch(hlrpcfront_t)

# Commonly required by python

libs_exec_ldconfig(hlrpcfront_t)

# bash is reading /proc/meminfo which is a system state

kernel_read_system_state(hlrpcfront_t)

Skapa interface API

Poängen med interface är inte bara att underlätta för sig själv utan även för andra som skriver egna policys, de kan använda våra API om de vill att deras program ska kunna manipulera vår tjänsts filer.

I policyfilen använder jag ett egetdefinierat makro som heter ''hlrpcfront_rw_log_files'', detta definieras i filen ''hlrpcfront.if'' i samma katalog.

## `<summary>`hlrpcfront log access API`</summary>`

interface(`hlrpcfront_rw_log_files',`
 gen_require(`
    type hlrpcfront_log_t;
 ')

 logging_search_logs($1)
 rw_files_pattern($1, hlrpcfront_log_t, hlrpcfront_log_t)
')

På CentOS kan du se fler sådana makron i ''/usr/share/selinux/devel/include/support/file_patterns.spt'', dessa ger en ganska bra förståelse för SElinux makron.

Definiera filkontext för policy

En policy är ett komplett paket som även inkluderar filkontext.

Till sist definieras en fil som heter ''hlrpcfront.fc''.

 /usr/local/sbin/hlrpcfront -- gen_context(system_u:object_r:hlrpcfront_exec_t,s0)
 /var/log/hlrpcfront(/.*)? -- gen_context(system_u:object_r:hlrpcfront_log_t,s0)

I den kan man definiera sökvägar som ska falla under en viss filkontext när policyn laddas, så om denna policy laddas in och man kör ''restorecon -Rv /var/log'' så kommer katalogen för loggarna, och eventuella filer där under, märkas om till ''hlrpcfront_log_t'' kontext.

Detta är precis som att använda semanage fcontext för att skapa filkontext manuellt.

Så vill man definiera ännu en typ för t.ex. konfigurationsfiler gör man det i ''.fc'' filen så här.

/etc/hlrpcfront.conf -- gen_context(system_u:object_r:hlrpcfront_conf_t,s0)

Och glöm inte att lägga till den nya typen i ''hlrpcfront.te'' så här.

# Make it into a conf file context
files_config_file(hlrpcfront_conf_t)

# Again use my own macro defined in interfaces to ensure configuration files

# are writable and readable.
hlrpcfront_rw_conf_files(hlrpcfront_t)

Och precis som med logfilerna har jag även ett makro definierat i ''hlrpcfront.if''.

## `<summary>`hlrpcfront conf file access API`</summary>`

interface(`hlrpcfront_rw_conf_files',`
 gen_require(`
    type hlrpcfront_conf_t;
 ')

 files_search_etc($1)
 manage_files_pattern($1, hlrpcfront_conf_t, hlrpcfront_conf_t)
 read_lnk_files_pattern($1, hlrpcfront_conf_t, hlrpcfront_conf_t)
')

Kompilera policy

$ make -f /usr/share/selinux/devel/Makefile hlrpcfront.pp

Installera policy

$ sudo semodule -i hlrpcfront.pp

Virtualenv

En knepig sak är att köra tjänster som använder Pythons virtualenv med en SElinux policy, samma gäller nog Rubys rbenv och liknande tekniker för andra språk.

I detta fallet har jag varit noga med att samla hela virtualenv miljön och alla bibliotek som tjänsten är beroende av under ''/opt'' vilket förenklar allt.

Då kan man i filen för filkontext, ''hlrpcfront.fc'', definiera den sökvägen och ange vilken typ den ska ha. Ett exempel.

/opt/hlrpcfront(/.*)? -- gen_context(system_u:object_r:hlrpcfront_opt_lib_t,s0) /opt/hlrpcfront/venv/bin/python -- gen_context(system_u:object_r:bin_t,s0)

Här säger jag alltså att allt under /opt/hlrpcfront ska behandlas med en egen typ som jag måste definiera i policymodulen. Att Pythontolken själv ska behandlas som bin_t vilket gör att den går att exekvera.

FIXME: Fortsätt här.

Nätverk

Hur tillåter du nätverkstjänsten att använda vissa nätverksportar?

Antagande att nätverksporten inte redan har en typ definierad om du kollar med ''semanage port -l''.

Först definiera upp en ny typ för nätverksporten i din ''.te'' fil.

type hlrpcfront_port_t;
corenet_port(hlrpcfront_port_t)

Ge processtypen tillåtelse att starta servrar på den porttypen, i samma fil.

allow hlrpcfront_t:tcp_socket name_bind;

FIXME mer behövs

Definiera upp portnummer för typen.

$ sudo semanage port -a -t hlrpcfront_port_t -p tcp 9002-9005

Bonustips

Filerna i ''/usr/share/selinux/devel'' definierar mycket av vad vi använder för att skapa policy.

T.ex. kan vi hitta i ''/usr/share/selinux/devel/include/contrib/apache.if'' ett bra exempel på ett makro som hanterar åtkomst till konfigurationsfiler, ''apache_manage_config''.

I ''system/systemd.if'' hittar vi även en ersättare för ''init_daemon_domain'' just för systemd, som heter ''systemd_domain_template''. Den tar bara ett argument och hanterar även ''kernel_read_system_state'' så om vi byter ut ''init_daemon_domain'' mot ''systemd_domain_template'' kan vi anropa den så här.

systemd_domain_template(hlrpcfront_t)

Eftersom den själv lägger till andra argumentet till ''init_daemon_domain'', plus att vi kan ta bort anropet till ''kernel_read_system_state'' som är längst ner i policyfilen.


Last update: April 24, 2020