Skip to content

Set up Pagekite as a Linux service

Services are programs which will auto start at boot, and auto restart if the program stops unexpectedly. Systemd provides this function. All the config is stored in a .service file.

Pagekite is a hosting service, which can expose your localhost website to the public, like this website for example.

Making a Pagekite service unit file

Let's set up a pagekite service for example. Pagekite let's you expose your localhost website to the internet. Lets start by doing a service file.

pagekite.service

[Unit]
Description=PageKite
After=network.target

[Service]
Type=simple
ExecStart=/usr/bin/python /usr/local/bin/pagekite.py --clean --optfile=/home/samsi/.pagekite.rc --ca_certs=/etc/ssl/certs/ca-certificates.crt
TimeoutStopSec=5
KillMode=mixed

PermissionsStartOnly=true
Restart=on-abnormal
RestartSec=2s
LimitNOFILE=65536
#WorkingDirectory=/tmp

# Hardening
SystemCallFilter=~@clock @debug @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io @reboot @swap
NoNewPrivileges=yes
PrivateDevices=yes
PrivateTmp=yes
#ProtectHome=yes
ProtectSystem=strict
ProtectKernelModules=yes
ProtectKernelTunables=yes

[Install]
WantedBy=multi-user.target

Replace samsi with your username.

[Unit] Section

This section contains info and dependencies of the service.

"Description" option

A brief description of the service

"After" option

The service will start after all the services we specify in the After option. In the case of Pagekite, it will only need internet, so we specified network.target.

[Service] Section

In this section we can specify things like the command to be executed.

"Type" option

  • Type=simple (default): systemd considers the service to be started up immediately. The process must not fork. Do not use this type if other services need to be ordered on this service, unless it is socket activated.
  • Type=forking: systemd considers the service started up once the process forks and the parent has exited. For classic daemons use this type unless you know that it is not necessary. You should specify PIDFile= as well so systemd can keep track of the main process.
  • Type=oneshot: this is useful for scripts that do a single job and then exit. You may want to set RemainAfterExit=yes as well so that systemd still considers the service as active after the process has exited.
  • Type=notify: identical to Type=simple, but with the stipulation that the daemon will send a signal to systemd when it is ready. The reference implementation for this notification is provided by libsystemd-daemon.so.
  • Type=dbus: the service is considered ready when the specified BusName appears on DBus's system bus.
  • Type=idle: systemd will delay execution of the service binary until all jobs are dispatched. Other than that behavior is very similar to Type=simple.

"ExecStart" option

Defines the command to be executed when the service starts.

"TimeoutStopSec" option

This option sets 2 things. First, it configures the time to wait for each ExecStop= command. If the ExecStop= command times out, all subsequent ExecStop commands are skipped and the service will be terminated by SIGTERM. Second, it configures the timeout for the service itself. Takes a unit-less value in seconds, or a time span value such as "5min 20s". Pass "infinity" to disable the timeout logic.

"KillMode" option

Specifies how processes of this unit shall be killed. Can be control-group, process, mixed, or none.

  • If set to control-group, all remaining processes in the control group of this unit will be killed on unit stop (for services: after the stop command is executed, as configured with ExecStop=).
  • If set to process, only the main process itself is killed.
  • If set to mixed, the SIGTERM signal is sent to the main process while the subsequent SIGKILL signal is sent to all remaining processes of the unit's control group.
  • If set to none, no process is killed. In this case, only the stop command will be executed on unit stop, but no process will be killed otherwise. Processes remaining alive after stop are left in their control group and the control group continues to exist after stop unless it is empty.

Defaults to control-group.

"PermissionsStartOnly" option

Is apparently deprecated. Takes a boolean argument. If true, the permission-related execution options, as configured with User= and similar options (see systemd.exec for more information), are only applied to the process started with ExecStart=, and not to the various other ExecStartPre=, ExecStartPost=, ExecReload=, ExecStop=, and ExecStopPost= commands. If false, the setting is applied to all configured commands the same way. Defaults to false.

"Restart" option

Configures whether the service shall be restarted when the service process exits, is killed, or a timeout is reached. The service process may be the main service process, but it may also be one of the processes specified with ExecStartPre=, ExecStartPost=, ExecStop=, ExecStopPost=, or ExecReload=. When the death of the process is a result of systemd operation (e.g. service stop or restart), the service will not be restarted. Timeouts include missing the watchdog "keep-alive ping" deadline and a service start, reload, and stop operation timeouts.

Takes one of no, on-success, on-failure, on-abnormal, on-watchdog, on-abort, or always. If set to no (the default), the service will not be restarted. If set to on-success, it will be restarted only when the service process exits cleanly. In this context, a clean exit means an exit code of 0, or one of the signals SIGHUP, SIGINT, SIGTERM or SIGPIPE, and additionally, exit statuses and signals specified in SuccessExitStatus=. If set to on-failure, the service will be restarted when the process exits with a non-zero exit code, is terminated by a signal (including on core dump, but excluding the aforementioned four signals), when an operation (such as service reload) times out, and when the configured watchdog timeout is triggered. If set to on-abnormal, the service will be restarted when the process is terminated by a signal (including on core dump, excluding the aforementioned four signals), when an operation times out, or when the watchdog timeout is triggered. If set to on-abort, the service will be restarted only if the service process exits due to an uncaught signal not specified as a clean exit status. If set to on-watchdog, the service will be restarted only if the watchdog timeout for the service expires. If set to always, the service will be restarted regardless of whether it exited cleanly or not, got terminated abnormally by a signal, or hit a timeout.

Table 2. Exit causes and the effect of the Restart= settings on them

Restart settings/Exit causes no always on-success on-failure on-abnormal on-abort on-watchdog
Clean exit code or signal X X
Unclean exit code X X
Unclean signal X X X X
Timeout X X X
Watchdog X X X X

As exceptions to the setting above, the service will not be restarted if the exit code or signal is specified in RestartPreventExitStatus= (see below) or the service is stopped with systemctl stop or an equivalent operation. Also, the services will always be restarted if the exit code or signal is specified in RestartForceExitStatus= (see below).

Note that service restart is subject to unit start rate limiting configured with StartLimitIntervalSec= and StartLimitBurst=, see systemd.unit for details. A restarted service enters the failed state only after the start limits are reached.

Setting this to on-failure is the recommended choice for long-running services, in order to increase reliability by attempting automatic recovery from errors. For services that shall be able to terminate on their own choice (and avoid immediate restarting), on-abnormal is an alternative choice.

"RestartSec" option

Time systemd should wait before restarting a service. The default is 100ms.

"LimitNOFILE" option

Honestly, i have no idea.

Hardening

This stuff is to limit the freedom of the program, and provides security for your system.

"SystemCallFilter" option

Takes a space-separated list of system call names. If this setting is used, all system calls executed by the unit processes except for the listed ones will result in immediate process termination with the SIGSYS signal (whitelisting). (See SystemCallErrorNumber= below for changing the default action). If the first character of the list is "~", the effect is inverted: only the listed system calls will result in immediate process termination (blacklisting).

"NoNewPrivileges" option

Takes a boolean argument. If true, ensures that the service process and all its children can never gain new privileges through execve() (e.g. via setuid or setgid bits, or filesystem capabilities). This is the simplest and most effective way to ensure that a process and its children can never elevate privileges again. Defaults to false.

"PrivateDevices" option

Takes a boolean argument. If true, sets up a new /dev mount for the executed processes and only adds API pseudo devices such as /dev/null, /dev/zero or /dev/random (as well as the pseudo TTY subsystem) to it, but no physical devices such as /dev/sda, system memory /dev/mem, system ports /dev/port and others. This is useful to securely turn off physical device access by the executed process. Defaults to false.

"PrivateTmp" option

Takes a boolean argument. If true, sets up a new file system namespace for the executed processes and mounts private /tmp and /var/tmp directories inside it that is not shared by processes outside of the namespace. This is useful to secure access to temporary files of the process, but makes sharing between processes via /tmp or /var/tmp impossible. If this is enabled, all temporary files created by a service in these directories will be removed after the service is stopped. Defaults to false. It is possible to run two or more units within the same private /tmp and /var/tmp namespace by using the JoinsNamespaceOf= directive.

"ProtectHome" option

Takes a boolean argument or the special values "read-only" or "tmpfs". If true, the directories /home, /root, and /run/user are made inaccessible and empty for processes invoked by this unit. If set to "read-only", the three directories are made read-only instead. If set to "tmpfs", temporary file systems are mounted on the three directories in read-only mode. The value "tmpfs" is useful to hide home directories not relevant to the processes invoked by the unit, while still allowing necessary directories to be made visible when listed in BindPaths= or BindReadOnlyPaths=.

Setting this to "yes" is mostly equivalent to set the three directories in InaccessiblePaths=. Similarly, "read-only" is mostly equivalent to ReadOnlyPaths=, and "tmpfs" is mostly equivalent to TemporaryFileSystem= with ":ro".

It is recommended to enable this setting for all long-running services (in particular network-facing ones), to ensure they cannot get access to private user data, unless the services actually require access to the user's private data. This setting is implied if DynamicUser= is set. This setting cannot ensure protection in all cases. In general it has the same limitations as ReadOnlyPaths=, see below.

This option is only available for system services and is not supported for services running in per-user instances of the service manager.

"ProtectSystem" option

Takes a boolean argument or the special values "full" or "strict". If true, mounts the /usr and /boot directories read-only for processes invoked by this unit. If set to "full", the /etc directory is mounted read-only, too. If set to "strict" the entire file system hierarchy is mounted read-only, except for the API file system subtrees /dev, /proc and /sys (protect these directories using PrivateDevices=, ProtectKernelTunables=, ProtectControlGroups=). This setting ensures that any modification of the vendor-supplied operating system (and optionally its configuration, and local mounts) is prohibited for the service. It is recommended to enable this setting for all long-running services, unless they are involved with system updates or need to modify the operating system in other ways. If this option is used, ReadWritePaths= may be used to exclude specific directories from being made read-only. This setting is implied if DynamicUser= is set. This setting cannot ensure protection in all cases. In general it has the same limitations as ReadOnlyPaths=, see below. Defaults to off.

"ProtectKernelModules" option

Takes a boolean argument. If true, explicit module loading will be denied. This allows module load and unload operations to be turned off on modular kernels. It is recommended to turn this on for most services that do not need special file systems or extra kernel modules to work. Defaults to off. Enabling this option removes CAP_SYS_MODULE from the capability bounding set for the unit, and installs a system call filter to block module system calls, also /usr/lib/modules is made inaccessible. For this setting the same restrictions regarding mount propagation and privileges apply as for ReadOnlyPaths= and related calls, see above. Note that limited automatic module loading due to user configuration or kernel mapping tables might still happen as side effect of requested user operations, both privileged and unprivileged. To disable module auto-load feature please see sysctl.d(5) kernel.modules_disabled mechanism and /proc/sys/kernel/modules_disabled documentation. If turned on and if running in user mode, or in system mode, but without the CAP_SYS_ADMIN capability (e.g. setting User=), NoNewPrivileges=yes is implied.

This option is only available for system services and is not supported for services running in per-user instances of the service manager.

"ProtectKernelTunables" option

Takes a boolean argument. If true, kernel variables accessible through /proc/sys, /sys, /proc/sysrq-trigger, /proc/latency_stats, /proc/acpi, /proc/timer_stats, /proc/fs and /proc/irq will be made read-only to all processes of the unit. Usually, tunable kernel variables should be initialized only at boot-time, for example with the sysctl.d(5) mechanism. Few services need to write to these at runtime; it is hence recommended to turn this on for most services. For this setting the same restrictions regarding mount propagation and privileges apply as for ReadOnlyPaths= and related calls, see above. Defaults to off. If turned on and if running in user mode, or in system mode, but without the CAP_SYS_ADMIN capability (e.g. services for which User= is set), NoNewPrivileges=yes is implied. Note that this option does not prevent indirect changes to kernel tunables effected by IPC calls to other processes. However, InaccessiblePaths= may be used to make relevant IPC file system objects inaccessible. If ProtectKernelTunables= is set, MountAPIVFS=yes is implied.

This option is only available for system services and is not supported for services running in per-user instances of the service manager.

[Install] Section

In this section we can specify what services depend on this service.

Enabling service

sudo systemctl daemon-reload
sudo systemctl enable gitea
sudo systemctl start gitea