How to configure SELinux to allow specific services to communicate with Avahi?

I have a service, running on a Fedora 20 machine, that when started attempts to register services with Avahi. This works perfectly if my service is started while SELinux is in permissive mode, but the service will not register when SELinux is enforcing.

I am aware of the httpd_dbus_avahi boolean in SELinux. This works perfectly to allow Apache to register services, but I have been unable to find much information about how to allow other specific services to communicate with Avahi.

More specifically I am attempting to allow tvheadend to register its HTSP service with Avahi, but I am also curious how any particular service can be allowed to communicate with Avahi without being stopped by SELinux. I am NOT interested in turning off SELinux or making the process permissive that wants to communicate with Avahi.


EDIT: added all SELinux and service unit information relating to tvheadend

---SELinux---

audit.log messages

After executing semodule -DB and restarting the tvheadend service. The following are all messages that appeared in the audit log. The last message seems like the problem, but I am not sure what to make of it...

type=SERVICE_STOP msg=audit(1393282994.012:512): pid=1 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:init_t:s0 msg=' comm="tvheadend" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'
type=SERVICE_START msg=audit(1393283083.635:513): pid=1 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:init_t:s0 msg=' comm="tvheadend" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'
type=USER_AVC msg=audit(1393283084.291:514): pid=752 uid=81 auid=4294967295 ses=4294967295 subj=system_u:system_r:system_dbusd_t:s0-s0:c0.c1023 msg='avc:  denied  { send_msg } for msgtype=method_return dest=:1.114 spid=731 tpid=14478 scontext=system_u:system_r:avahi_t:s0 tcontext=system_u:system_r:init_t:s0 tclass=dbus  exe="/usr/bin/dbus-daemon" sauid=81 hostname=? addr=? terminal=?'

Process

Output from ps -AZ | grep tvheadend

system_u:system_r:init_t:s0      2599 ?        00:00:06 tvheadend

I noticed that the init_t process type seems a little strange since all other services on my system have the initrc_t process type. I am not sure why the tvheadend service is different in this way.

User

Output from sudo -u hts id

uid=1001(hts) gid=1003(hts) groups=1003(hts),39(video) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

I use this user only for running the tvheadend service. This user has a home directory on a separate partition much larger than the system partition since DVR files generated by tvheadend can become quite large.

When I created this user I did not use the --system switch with the useradd command. Perhaps I should have?

Executable

Output from ls -Z /usr/local/bin | grep tvheadend

-rwxr-xr-x. root root system_u:object_r:bin_t:s0       tvheadend

---Service---

Unit File

[Unit]
Description=TVHeadEnd
After=syslog.target network.target avahi-daemon.service sys-devices-pci0000:00-0000:00:1c.0-0000:05:00.0-dvb-dvb0.demux0.device sys-devices-pci0000:00-0000:00:1c.0-0000:05:00.0-dvb-dvb0.dvr0.device sys-devices-pci0000:00-0000:00:1c.0-0000:05:00.0-dvb-dvb0.frontend0.device sys-devices-pci0000:00-0000:00:1c.0-0000:05:00.0-dvb-dvb0.net0.device sys-devices-pci0000:00-0000:00:1c.0-0000:05:00.0-dvb-dvb1.demux0.device sys-devices-pci0000:00-0000:00:1c.0-0000:05:00.0-dvb-dvb1.dvr0.device sys-devices-pci0000:00-0000:00:1c.0-0000:05:00.0-dvb-dvb1.frontend0.device sys-devices-pci0000:00-0000:00:1c.0-0000:05:00.0-dvb-dvb1.net0.device sys-devices-pci0000:00-0000:00:1c.0-0000:05:00.0-video4linux-vbi0.device sys-devices-pci0000:00-0000:00:1c.0-0000:05:00.0-video4linux-vbi1.device sys-devices-pci0000:00-0000:00:1c.0-0000:05:00.0-video4linux-video0.device sys-devices-pci0000:00-0000:00:1c.0-0000:05:00.0-video4linux-video1.device
Wants=sys-devices-pci0000:00-0000:00:1c.0-0000:05:00.0-dvb-dvb0.demux0.device sys-devices-pci0000:00-0000:00:1c.0-0000:05:00.0-dvb-dvb0.dvr0.device sys-devices-pci0000:00-0000:00:1c.0-0000:05:00.0-dvb-dvb0.frontend0.device sys-devices-pci0000:00-0000:00:1c.0-0000:05:00.0-dvb-dvb0.net0.device sys-devices-pci0000:00-0000:00:1c.0-0000:05:00.0-dvb-dvb1.demux0.device sys-devices-pci0000:00-0000:00:1c.0-0000:05:00.0-dvb-dvb1.dvr0.device sys-devices-pci0000:00-0000:00:1c.0-0000:05:00.0-dvb-dvb1.frontend0.device sys-devices-pci0000:00-0000:00:1c.0-0000:05:00.0-dvb-dvb1.net0.device sys-devices-pci0000:00-0000:00:1c.0-0000:05:00.0-video4linux-vbi0.device sys-devices-pci0000:00-0000:00:1c.0-0000:05:00.0-video4linux-vbi1.device sys-devices-pci0000:00-0000:00:1c.0-0000:05:00.0-video4linux-video0.device sys-devices-pci0000:00-0000:00:1c.0-0000:05:00.0-video4linux-video1.device

[Service]
Type=forking
GuessMainPID=no
EnvironmentFile=/etc/sysconfig/tvheadend
ExecStart=/usr/local/bin/tvheadend -f -u $TVH_USER -g $TVH_GROUP -p $TVH_PID -b $TVH_ADDRESS --http_port $TVH_HTTP_PORT --htsp_port $TVH_HTSP_PORT
PIDFile=$TVH_PID
ExecReload=/bin/kill -HUP $MAINPID

[Install]
WantedBy=multi-user.target

Unit Environment File

TVH_USER=hts

TVH_GROUP=hts

TVH_PID=/var/run/tvheadend.pid

TVH_ADDRESS=0.0.0.0

TVH_HTTP_PORT=9981

TVH_HTSP_PORT=9982

Solution 1:

Running this service as init_t is probably not a great idea.

The reason you get this behaviour is because tvheadend is probably labelled bin_t, and no transition rule exists to move a file of this type out of the init_t context.

You can search this to know for sure..

$ sesearch -s init_t --type -c process | grep bin_t

This command returns no results. No transitions out of init_t for a bin_t process.

You should also avoid running this type in initrc_t too since its not appropriate for the service. As a general matter of fact -- the best solution would be to properly write a confined policy for you're service, however this is beyond the scope of this answer.

Instead, you need to get your process out of init_t to another type. Because no policy exists for this application it is probably best to move this into unconfined_t, for which a type transition does exist.

$ sesearch -s init_t --type -c process -t unconfined_exec_t
Found 1 semantic te rules:
   type_transition init_t unconfined_exec_t : process unconfined_t;

We can also check that the rule that is being hit in policy will not get hit in unconfined_t (which it shouldn't).

$ sesearch -s avahi_t -p send_msg -c dbus -t unconfined_t --allow
Found 2 semantic av rules:
   allow avahi_t unconfined_t : dbus send_msg ; 
   allow system_bus_type unconfined_t : dbus send_msg ;

To do this, simply relabel your tvheadend program to unconfined_exec_t.

semanage fcontext -a -t unconfined_exec_t -f f /usr/bin/tvheadend

Then restore.

restorecon /usr/bin/tvheadend

Now, re-running your service should work. If you re-run ps -AZ | grep tvheadend you should see your process running in unconfined_t.

Ideally a new policy should be used for this program, but if no policy exists its best to run it without policy at all in the unconfined_t domain. initrc_t and init_t are not really meant to be used to run services (although some do incorrectly go in there), initrc_t was originally conceived to run SYSV shell scripts and init_t is meant for running systemd/init itself.

Solution 2:

The manpage for apache_selinux mentions that # semanage permissive can be used to allow specific processes to run in permissive mode.

# semanage permissive -a avahi_t

I suggest finding some more detailed info on its usage

Though, it might be wise to find a better solution than simply "allowing a service permissive" access controls;

especially when SELinux provides a vast number of config options to allow a given service permissions to do only the things you expect it to do, and nothing more! Because of this vast number of control options, however, admins have to learn what options exist, and decide how/when/where to implement them efficaciously. As I mentioned, the first place to look is within the manpages on your system, as well as your audit log in /var/log/audit/audit.log. To search for messages about avahi, for example, within your log files, execute:

# grep /usr/sbin/avahi /var/log/audit/audit.log

EDIT: to reflect updated question

msg=avc: denied { send_msg } for msgtype=method_return dest=:1.114
spid=731 tpid=14478 scontext=system_u:system_r:avahi_t:s0 
tcontext=system_u:system_r:init_t:s0

Take a look at the Manpages

The following items in particular should be of interest to you:

  • system_dbusd_t SELinux Type:

    $ man system_dbusd_selinux
    
  • avahi_t SELinux Type:

    $ man avahi_selinux
    
    • take a look at the avahi_exec_t
    • avahi_initrc_exec_t file contexts
    • avahi_var_run_t
    • avahi_unit_file_t
    • as well as the list of related booleans therein.

Allright, as I'm not sure that you've noticed that last bit on avahi_exec_t; and since I can't post comments :/ yet; here's the pertinent info from the manpage itself:

SELinux defines the file context types for the avahi, if you wanted to store files with these types in a diffent paths, you need to execute the semanage command to sepecify alternate labeling and then use restorecon to put the labels on disk.

   semanage fcontext -a -t avahi_exec_t '/srv/avahi/content(/.*)?'
   restorecon -R -v /srv/myavahi_content

   Note: SELinux often uses regular expressions to specify labels that match multiple files.

   The following file types are defined for avahi:

   avahi_exec_t

   - Set files with the avahi_exec_t type, if you want to transition an executable to the avahi_t domain.

   Paths:
        /usr/sbin/avahi-daemon, /usr/sbin/avahi-autoipd, /usr/sbin/avahi-dnsconfd

   avahi_initrc_exec_t

   - Set files with the avahi_initrc_exec_t type, if you want to transition an executable to the avahi_initrc_t domain.

   avahi_unit_file_t

   - Set files with the avahi_unit_file_t type, if you want to treat the files as avahi unit content.

SELinux and Process Transitions

Head over to Dan Walsh's blog for some detailed discussion/information on SELinux, this post in particular mentions

A process transition says when process running as label a_t executes a file labeled b_exec_t it should execute the process as b_t An example of this would be service httpd start. In this case we have unconfined_t running an init script labeled initrc_exec_t and SELinux starts the process as initrc_t.

Aside from that, your best bet is to keep researching the tvheadend program, as well as SELinux management. You can create a custom policy as suggested by the audit log messages; something like:

# grep tvheadend /var/log/audit/audit.log | audit2allow -M mypol
# semodule -i mypol.pp

At least until a better solutions comes across, and/or tvheadend makes its way to the fedora repos. Lastly, take a better look at the unit file (service file) you created for systemd; check for errors, potential additions and changes.

Solution 3:

You could fix this using audit2allow to create a custom policy module

audit2allow -M custom_avhi <file_containing_AVC-denied messages
******************** IMPORTANT ***********************
To make this policy package active, execute:

semodule -i custom_avhi.pp