SELinux – How to Unblock Execution in Systemd Units

fedoraselinuxsystemd

I use Fedora 31 and tried to set up a Teamspeak server. When I look in journalctl -u teamspeak I get the following error:

mar 09 22:22:46 melchior systemd[1]: Started Teamspeak server.
mar 09 22:22:46 melchior systemd[20187]: teamspeak.service: Failed to execute command: Permission denied
mar 09 22:22:46 melchior systemd[20187]: teamspeak.service: Failed at step EXEC spawning /srv/teamspeak/3.11.0/ts3server: Permission denied
mar 09 22:22:46 melchior systemd[1]: teamspeak.service: Main process exited, code=exited, status=203/EXEC
mar 09 22:22:46 melchior systemd[1]: teamspeak.service: Failed with result 'exit-code'.

My systemd unit looks like this:

[Unit]
Description=Teamspeak server
After=network-online.target

[Service]
User=teamspeak
Group=teamspeak
WorkingDirectory=/srv/teamspeak/data/
ExecStart=/srv/teamspeak/versions/3.11.0/ts3server dbsqlpath=/srv/teamspeak/versions/3.11.0/sql/ serverquerydocs_path=/srv/teamspeak/versions/3.11.0/serverquerydocs/ license_accepted=1 default_voice_port=9987 filetransfer_port=30033 query_port=10011
Restart=always

[Install]
WantedBy=multi-user.target

I use the following ansible playbook to set it up:

- name: create teamspeak server base folder
  file:
    path: "/srv/teamspeak"
    state: directory
    owner: root
    group: root
    mode: 0755
- name: create teamspeak user
  user:
    name: teamspeak
    comment: "Teamspeak 3 server user"
    system: true
    create_home: false
    shell: /sbin/nologin
    # NOTE: SELinux blocks systemd from starting any binary in a user's home
    #       folder which is why we need versions/ and data/
    home: /srv/teamspeak/data
- name: create teamspeak server user folder
  file:
    path: "/srv/teamspeak/data"
    state: directory
    owner: teamspeak
    group: teamspeak
    mode: 0755
- name: create teamspeak server version folder
  file:
    path: "/srv/teamspeak/versions/{{ teamspeak_version }}"
    state: directory
- name: download teamspeak server
  get_url:
    url: "https://files.teamspeak-services.com/releases/server/{{ teamspeak_version }}/teamspeak3-server_linux_amd64-{{ teamspeak_version }}.tar.bz2"
    dest: "/srv/teamspeak/versions/{{ teamspeak_version }}/server.tar.bz2"
    checksum: "sha256:18c63ed4a3dc7422e677cbbc335e8cbcbb27acd569e9f2e1ce86e09642c81aa2"
  register: tarball
- name: unpack teamspeak3 server files
  unarchive:
    src: "{{ tarball.dest }}"
    dest: "/srv/teamspeak/versions/{{ teamspeak_version }}/"
    remote_src: true
    extra_opts:
      - "--strip-components=1"
      # Prevent files from being world writable like some are in the tarball
      - "--no-same-permissions"
    creates: "/srv/teamspeak/versions/{{ teamspeak_version }}/ts3server"
- name: install service file
  template:
    src: teamspeak.service
    dest: /etc/systemd/system/teamspeak.service
  register: service
- name: reload systemd units
  when: service.changed
  command: systemctl daemon-reload
- name: "enable teamspeak service"
  systemd:
    name: teamspeak
    enabled: true
    state: started

Looking in sealert -l "*" shows:

SELinux is preventing (s3server) from execute access on the file ts3server.

*****  Plugin catchall (100. confidence) suggests   **************************

If you believe that (s3server) should be allowed execute access on the ts3server file by default.
Then you should report this as a bug.
You can generate a local policy module to allow this access.
Do
allow this access for now by executing:
# ausearch -c '(s3server)' --raw | audit2allow -M my-s3server
# semodule -X 300 -i my-s3server.pp


Additional Information:
Source Context                system_u:system_r:init_t:s0
Target Context                unconfined_u:object_r:var_t:s0
Target Objects                ts3server [ file ]
Source                        (s3server)
Source Path                   (s3server)
Port                          <Unknown>
Host                          melchior
Source RPM Packages           
Target RPM Packages           
Policy RPM                    selinux-policy-3.14.4-45.fc31.noarch
Selinux Enabled               True
Policy Type                   targeted
Enforcing Mode                Enforcing
Host Name                     melchior
Platform                      Linux melchior 5.4.17-200.fc31.x86_64 #1 SMP Sat
                              Feb 1 19:00:13 UTC 2020 x86_64 x86_64
Alert Count                   65
First Seen                    2020-03-09 22:22:45 CET
Last Seen                     2020-03-10 20:07:00 CET
Local ID                      20f823c0-8e46-46d1-a51c-659040857b34

Raw Audit Messages
type=AVC msg=audit(1583867220.254:4234): avc:  denied  { execute } for  pid=11418 comm="(s3server)" name="ts3server" dev="dm-0" ino=1032133 scontext=system_u:system_r:init_t:s0 tcontext=unconfined_u:object_r:var_t:s0 tclass=file permissive=0


Hash: (s3server),init_t,var_t,file,execute

I can run the server without issue if I just do sudo -u teamspeak /srv/teamspeak/versions/3.11.0/ts3server dbsqlpath=/srv/teamspeak/versions/3.11.0/sql/ serverquerydocs_path=/srv/teamspeak/versions/3.11.0/serverquerydocs/ license_accepted=1 default_voice_port=9987 filetransfer_port=30033 query_port=10011

I have no idea how to debug this further. How can I solve this?

Best Answer

It turns out SELinux has an idea that binaries can only be executed from certain locations and my custom directory was not explicitly marked as allowed. It inherited the type var_t from /srv/.* (I think).

To get an extensive list of current rules for all directories you can run semanage fcontext --list.

I added an exception using the following Ansible tasks:

- name: set SELinux permissions on ts3server binaries
  sefcontext:
    target: "/srv/teamspeak/versions/[^/]+/ts3server"
    setype: bin_t
- name: reload SELinux policy to ensure that ts3server is executable
  command: restorecon -irv /srv/teamspeak/
  when: tarball.changed

The same can be achieved by using the semanage fcontext command followed by restorecon -irv /srv/teamspeak/.