Never run a service as root or a standard login user. Create a dedicated system user.
# -r: System account (no aging, UID in system range)
# -s /sbin/nologin: No interactive login shell for security
# -U: Create a group with the same name as the user
# -m -d: Create and set the home directory
sudo useradd -rs /sbin/nologin -Umd /var/lib/myservice myserviceFedora follows the Filesystem Hierarchy Standard (FHS). SELinux expects files in specific places:
- Binaries:
/usr/local/bin/or/usr/bin/ - Persistent Data/State:
/var/lib/myservice/(databases, models, runtime state) - Logs:
/var/log/myservice/ - Configuration:
/etc/myservice/
Avoid /usr/share/ for mutable data; SELinux labels it as usr_t (read-only).
SELinux uses "labels" to decide what a process can touch. Even if chown is correct, a label mismatch causes "Permission Denied."
# 1. Tell SELinux /var/lib/myservice is for service data (var_lib_t)
sudo semanage fcontext -a -t var_lib_t "/var/lib/myservice(/.*)?"
# 2. Apply the labels to the existing files
sudo restorecon -Rv /var/lib/myservice
# 3. If your binary is in a custom path, ensure it has the correct executable label
sudo restorecon -v /usr/local/bin/myservice-binaryCreate the file at /etc/systemd/system/myservice.service.
[Unit]
Description=My Custom Service
Wants=network-online.target
After=network-online.target
[Service]
Environment="DATA_PATH=/var/lib/myservice"
ExecStart=/usr/bin/myservice-binary
User=myservice
Group=myservice
Restart=always
# Security Hardening (optional but worth)
ProtectSystem=strict # Makes entire filesystem read-only except /dev, /proc, /sys
PrivateTmp=true # Isolated /tmp and /var/tmp
NoNewPrivileges=true # Prevents privilege escalation via setuid/capabilities
[Install]
WantedBy=multi-user.targetAfter creating or modifying the file, reload systemd:
sudo systemctl daemon-reload
sudo systemctl enable --now myserviceBy default, a custom service without its own SELinux policy runs in the unconfined_service_t domain, which generally permits outbound connections. However, if you write a custom SELinux policy for tighter confinement, network access must be explicitly granted.
If SELinux is blocking a connection (you will see AVC denials in the audit log):
- Run the service and let it fail
sudo systemctl start myservice- Extract the specific AVC denial and generate a policy module
sudo ausearch -m AVC -c myservice | audit2allow -M myservice_net
sudo semodule -i myservice_net.ppNote:
audit2allowgrants the minimum permissions needed for the observed denial. Review the generated.tefile before loading it in production.
If your service starts but "cannot write" or "cannot connect":
- Check standard permissions:
ls -l /var/lib/myservice - Check SELinux labels:
ls -Z /var/lib/myservice - Search for AVC denials:
sudo ausearch -m AVC -c myservice- Check the service journal for application-level errors:
sudo journalctl -u myservice -xe