Chapter 5: Creating and Editing Services

We've just seen what systemd services are and how to control them. Sometimes though, you might need to either alter the behavior of a service or create a completely new one. In this chapter, we'll look at the proper way to edit services. Then, we'll look at how to create a new one. The specific topics of this chapter are as follows:

  • Editing an existing service
  • Creating a new service
  • Changing the default systemd editor
  • Creating a new container service with podman

So, if you're ready, let's jump in.

Technical requirements

As before, I'll be using an Alma Linux 8 virtual machine and an Ubuntu Server 20.04 virtual machine. To perform the Secure Shell exercise, you'll need to go into the VirtualBox network settings for both virtual machines and choose Bridged Adapter from the Attached to drop-down list. Then, expand the Advanced menu and choose Allow All from the Promiscuous Mode drop-down list. When you boot up the virtual machine, obtain its IP address by opening a terminal and typing ip a. That way, you'll be able to remotely log into your virtual machines from the command line of your host machine.

Check out the following link to see the Code in Action video: https://bit.ly/3xP0yOH

Editing an existing service

We've seen that the unit files for our services live in the /lib/systemd/system/ directory, so your first instinct might be to go there and edit files in your favorite text editor. You don't want to do that though, even though it would work. If you were to do a system update, it might overwrite the files that you edited, and you'd lose your changes.

The proper way to do this is to create edited versions of your service files in the /etc/systemd/system/ directory. You can do that with your favorite text editor, the same as you would with any other configuration file. Indeed, that's the way that you used to have to do it. When Red Hat released RHEL 7.2, they added an edit function to the systemctl command, which makes life much easier. (Of course, that edit function is now available on all Linux distros that run systemd.)

Note

It has been brought to my attention that some people prefer to add their own custom unit files to the /lib/systemd/system/ directory so that they'll be alongside the unit files that get installed by the operating system. If you're one of those people, please understand that this is not good practice. By doing this, you risk getting your custom unit files either deleted or overwritten when you do a system update. Also, keeping your custom unit files in the /etc/systemd/system/ directory will make it much easier for you to keep track of which unit files you've added, and which ones were installed by the operating system.

Now, you might be wondering how you can know what changes you can make to a service file. The most simplistic answer is to read the man pages for the various unit types and look at all the parameters and options that you can add, delete, or modify. If you're like me though, you'll start reading these man pages and soon find that they're the perfect cure for insomnia. Don't get me wrong, the man pages are definitely useful. But if you want to really learn how to make services sing and dance the way you want them to, the most painless way to do it is to look at the service files that are already on your system and see how they're set up. Then, look at the parameters that are listed in those files, and look them up in the appropriate man pages to see what they're doing for you. As we go through this chapter, I'll give you plenty of examples of what I'm talking about.

When you use the systemctl edit function, you can either partially edit the file or edit the entire file. By default, you'll do a partial edit. Let's begin with the simplest example I can think of.

Creating a partial edit to the [Install] section

Let's fire up the Ubuntu server virtual machine and add an Alias= line to the apache2.service file. Start by doing this:

sudo systemctl edit apache2

What you'll get looks something like this:

Figure 5.1 – The systemd service editor on Ubuntu

Yeah, that doesn't look like much, does it? It's just an empty file opened in the nano text editor. Don't worry, though. All we're going to do here is to add one parameter, and we don't need to see the whole service file to do that. Since we're working with Ubuntu, the name of the Apache service is apache2. Let's say that you've just come over from the Red Hat world, and you're used to always using httpd as the Apache service name. Consequently, you get frustrated when you always instinctively type the wrong service name on the Ubuntu machine. It's kind of like if you've been used to driving with a standard transmission all your life, and then you start stomping around for a clutch when you get into a car with an automatic transmission. (Well, that's what I do, anyway.) We can easily fix that, but let's first look at an example that we already have.

In another window, look at the [Install] section of the ssh.service file on the Ubuntu machine, as shown here:

. . .

. . .

[Install]

WantedBy=multi-user.target

Alias=sshd.service

That Alias= line at the end is our example. Now, over in the nano window, type this:

[Install]

Alias=httpd.service

Save the file and exit the editor by doing a Ctrl + X sequence. When it asks if you want to save the modified buffer, hit the y key. Then, just hit the Enter key to accept the default filename. Next, look inside the /etc/systemd/system/ directory. You'll see that we've just created a new apache2.service.d directory:

donnie@ubuntu20-04:/etc/systemd/system$ ls -l

total 104

drwxr-xr-x 2 root root 4096 Apr  5 16:55 apache2.service.d

. . .

. . .

Inside that directory, you'll see the following override.conf file:

donnie@ubuntu20-04:/etc/systemd/system/apache2.service.d$ ls -l

total 4

-rw-r--r-- 1 root root 30 Apr  5 16:55 override.conf

donnie@ubuntu20-04:/etc/systemd/system/apache2.service.d$

This file contains the parameter that we've just added, which looks like this:

[Install]

Alias=httpd.service

That's it – the entire file. When we start Apache, this parameter will get added to what's already in the original service file. The beauty of this is that if the original service file were to get replaced by a system update, you'd get the changes that were made by the update, and you'd still have this modification.

But, before you can use this modification, you'll need to load it into the system. Do that by doing:

donnie@ubuntu20-04:~$ sudo systemctl daemon-reload

[sudo] password for donnie:

donnie@ubuntu20-04:~$

Any time you modify or add a service file, you'll need to do a daemon-reload. When you add an Alias=, you'll also need to create a symbolic link for it in the /etc/systemd/system/ directory. You can create it manually with an ln -s command, but you don't have to. When you add an Alias= line to the [Install] section of a service file, the link will get created automatically when you enable the service. On the Ubuntu machine, the Apache service is already enabled and running, so we'll just disable it and enable it again. (Note that there's no need to stop the service.) So, let's first disable Apache, like this:

donnie@ubuntu20-04:/etc/systemd/system$ sudo systemctl disable apache2

Synchronizing state of apache2.service with SysV service script with /lib/systemd/systemd-sysv-install.

Executing: /lib/systemd/systemd-sysv-install disable apache2

Removed /etc/systemd/system/multi-user.target.wants/apache2.service.

donnie@ubuntu20-04:/etc/systemd/system$

Now, we'll enable it again, like this:

donnie@ubuntu20-04:/etc/systemd/system$ sudo systemctl enable apache2

Synchronizing state of apache2.service with SysV service script with /lib/systemd/systemd-sysv-install.

Executing: /lib/systemd/systemd-sysv-install enable apache2

Created symlink /etc/systemd/system/httpd.service → /lib/systemd/system/apache2.service.

Created symlink /etc/systemd/system/multi-user.target.wants/apache2.service → /lib/systemd/system/apache2.service.

donnie@ubuntu20-04:/etc/systemd/system$

You can see in the output that the enable command reads in the Alias= line that we inserted into the [Install] section, and creates an httpd.service link that points back to the original apache2.service file. We can verify that with this ls -l command as follows:

donnie@ubuntu20-04:/etc/systemd/system$ ls -l httpd.service

lrwxrwxrwx 1 root root 35 Apr  5 17:39 httpd.service -> /lib/systemd/system/apache2.service

donnie@ubuntu20-04:/etc/systemd/system$

Now comes the moment of truth. Can we now control Apache on our Ubuntu machine by invoking the httpd service name? Let's see:

donnie@ubuntu20-04:~$ systemctl status httpd

• apache2.service - The Apache HTTP Server

     Loaded: loaded (/lib/systemd/system/apache2.service; enabled; vendor preset: enabled)

    Drop-In: /etc/systemd/system/apache2.service.d

             └─override.conf

     Active: active (running) since Mon 2021-04-05 17:19:08 UTC; 34min ago

. . .

. . .

Oh, yeah. It works like a champ. (Don't you just love it when a plan comes together?) To see the service file along with your new edit, use systemctl cat, like this:

donnie@ubuntu20-04:~$ systemctl cat apache2

# /lib/systemd/system/apache2.service

[Unit]

Description=The Apache HTTP Server

. . .

. . .

[Install]

WantedBy=multi-user.target

# /etc/systemd/system/apache2.service.d/override.conf

[Install]

Alias=httpd.service

donnie@ubuntu20-04:~$

The top part of the output shows the original service file, and the bottom part shows the override.conf file that you created.

Of course, you can also go the opposite way with this. If you're used to doing things the Ubuntu way and suddenly find yourself administering Apache on a RHEL-type machine, you can add an Alias=apache2.service line to the httpd.service file, and then disable and re-enable Apache in order to create the link. The only difference in the procedure is that on the Ubuntu machine, systemctl edit invokes the nano text editor, and on RHEL-type machines, it might invoke the vi text editor. (The RHEL-type distros just recently switched from vi to nano as the default systemd editor.)

Pro tip

Remember that whatever changes you make to the [Install] section of a service file affects what happens whenever you enable or disable that service.

Okay, now that we've added a cool option to the [Install] section, let's add a few to the [Service] section.

Creating a partial edit to the [Service] section

Let's continue on with our Ubuntu Server virtual machine, and just add to what we've already done. This time, we'll add a few options to the [Service] section that will beef up security a bit. Before we do that though, let's see how secure Apache really is. We'll do that with the systemd-analyze utility.

On the systemd-analyze man page, you'll see that there are quite a few uses for this utility. For now, we'll just cover the security option. Let's start by checking the overall security profile for the services on our Ubuntu VM by doing:

donnie@ubuntu20-04:~$ systemd-analyze security

UNIT                                  EXPOSURE PREDICATE HAPPY

accounts-daemon.service                    9.6 UNSAFE  ?    

apache2.service                            9.2 UNSAFE  ?    

apport.service                             9.6 UNSAFE  ?    

. . .

. . .      ?    

systemd-udevd.service                      8.4 EXPOSED ?    

thermald.service                           9.6 UNSAFE  ?    

unattended-upgrades.service                9.6 UNSAFE  ?    

[email protected]                          9.4 UNSAFE  ?    

uuidd.service                              4.5 OK      ?    

vgauth.service                             9.5 UNSAFE  ?    

donnie@ubuntu20-04:~$

This command checks the security and sandboxing settings for each service and assigns an EXPOSURE score to each. The higher the score, the less safe the service is. So, this is like the game of golf, where you want to get the lowest score possible. The HAPPY column is supposed to show little face emoticons with varying degrees of happy or sad expressions, but the faces don't show when pasted into this book. That's okay though, because you can see them for yourself on your virtual machine.

Now, before you get too excited about seeing that a service is marked as UNSAFE, as we see here for the Apache service, you need to understand that this only examines the security settings in the service files. It doesn't account for any security settings that might be in the service's own configuration files, security options that are encoded into the service executable file, or any Mandatory Access Control (MAC) options that might be in effect. Still, though, this is a useful tool for suggesting ways to enhance your security settings.

Next, let's look at some suggestions for the Apache service:

donnie@ubuntu20-04:~$ systemd-analyze security apache2

  NAME                                                        DESCRIPTION                                                             EXPOSURE

✗ PrivateNetwork=                                             Service has access to the host's network                                     0.5

✗ User=/DynamicUser=                                          Service runs as root user                                                    0.4

✗ CapabilityBoundingSet=~CAP_SET(UID|GID|PCAP)                Service may change UID/GID identities/capabilities                           0.3

✗ CapabilityBoundingSet=~CAP_SYS_ADMIN                        Service has administrator privileges                                         0.3

✗ CapabilityBoundingSet=~CAP_SYS_PTRACE                       Service has ptrace() debugging abilities                                     0.3

. . .

. . .

There's too much output to show here in its entirety, but that's okay. Let's scroll down a bit and show some settings that are a bit more relevant to what we want to do:

. . .

. . .

✓ PrivateMounts=                                              Service cannot install system mounts                                            

✓ PrivateTmp=                                                 Service has no access to other software's temporary files                       

✗ PrivateUsers=                                               Service has access to other users                                            0.2

✗ ProtectClock=                                               Service may write to the hardware clock or system clock                      0.2

✗ ProtectControlGroups=                                       Service may modify the control group file system                             0.2

✗ ProtectHome=                                                Service has full access to home directories                                  0.2

. . .

. . .

✗ ProtectSystem=                                              Service has full access to the OS file hierarchy                             0.2

. . .

. . .

When you see an X in front of an entry, it means that an unsafe setting is in effect. Having a checkmark in front of an entry means that that parameter is configured with a safe setting. But, if you go in all willy-nilly and change the unsafe settings to safe ones, you'll break the service so that it will no longer run. Some of these supposedly unsafe settings are necessary for the service to do its job. Take the User=/DynamicUser= setting, for example. We see here that not having that parameter allows the Apache service to run with root privileges. Is that bad? Not really, because the Apache service needs root privileges to do certain things that it has to do. If you set this option to a non-root user, Apache will fail to start. And besides, the Apache developers have already accounted for this. They set it up so that only the first Apache process runs with root privileges, and all other Apache processes—the ones to which web browsers connect—run without root privileges. We've already seen that on a RHEL-type distro, such as Alma Linux, the Apache processes run under the apache user account. On Ubuntu, we see here that they run under the www-data account:

donnie@ubuntu20-04:/etc/apache2$ ps aux | grep apache

root        2290  0.0  0.2   6520  4480 ?        Ss   18:40   0:00 /usr/sbin/apache2 -k start

www-data    2291  0.0  0.2 752656  4344 ?        Sl   18:40   0:00 /usr/sbin/apache2 -k start

www-data    2292  0.0  0.2 752656  4344 ?        Sl   18:40   0:00 /usr/sbin/apache2 -k start

donnie      2554  0.0  0.0   6432   724 pts/0    S+   19:55   0:00 grep --color=auto apache

donnie@ubuntu20-04:/etc/apache2$

This non-root user is defined in the Apache configuration files. Let's see if our good friend grep can help us find where this is set:

donnie@ubuntu20-04:/etc/apache2$ grep -r 'USER' *

apache2.conf:User ${APACHE_RUN_USER}

envvars:export APACHE_RUN_USER=www-data

donnie@ubuntu20-04:/etc/apache2$

So, on the Ubuntu machine, the non-root www-data user is defined in the /etc/apache2/envvars file and invoked in the /etc/apache2/apache2.conf file. (I'll leave it to you to find where this is set on the Alma Linux machine.) Anyway, that's enough about the settings that we can't change. Let's look at some settings that we can change.

Tip:

On RHEL-type machines, everything I'm about to show you is already covered by SELinux. But, you can still use these settings if you want double protection. Ubuntu and SUSE use AppArmor instead of SELinux. Unfortunately, AppArmor provides almost no protection at all for Apache unless you jump through the hoops of creating your own custom AppArmor profile. Setting up this protection in systemd is much easier.

Let's first look at protecting users' home directories. We'll once again use the sudo systemctl edit apache2 command, which will open the override.conf file that we created previously. The [Install] section will still be there, so we'll just add a [Service] section, as follows:

[Service]

ProtectHome=yes

ProtectSystem=strict

[Install]

Alias=httpd.service

By default, the Apache service can read from or write to any place in the filesystem. The ProtectHome=yes setting prevents Apache from accessing the /root/, /home/, and the /run/user/ directories, even if they're set up with world-readable permissions. We can also set this to read-only if users want to serve web content out of their own home directories while preventing Apache from writing to them.

The ProtectSystem=strict setting causes Apache to have read-only access to the entire filesystem, except for the /dev/, /proc/, and /sys/ directories. Let's save the file and restart Apache to see what we've got:

donnie@ubuntu20-04:~$ sudo systemctl daemon-reload

donnie@ubuntu20-04:~$ sudo systemctl restart apache2

Job for apache2.service failed because the control process exited with error code.

See "systemctl status apache2.service" and "journalctl -xe" for details.

donnie@ubuntu20-04:~$

Oh, dear. This isn't good. Let's look at the status to see what the problem could be:

donnie@ubuntu20-04:~$ sudo systemctl status apache2

. . .

. . .

Apr 10 21:52:20 ubuntu20-04 apachectl[3848]: (30)Read-only file system: AH00091: apache2: could not open error log file /var/log/apache2/error.log.

Apr 10 21:52:20 ubuntu20-04 apachectl[3848]: AH00015: Unable to open logs

Apr 10 21:52:20 ubuntu20-04 apachectl[3836]: Action 'start' failed.

Apr 10 21:52:20 ubuntu20-04 apachectl[3836]: The Apache error log may have more information.

. . .

. . .

donnie@ubuntu20-04:~$

So, Apache wants to write to its log file in the /var/log/apache2/ directory, but it can't. Let's change the ProtectSystem setting to see if that helps:

[Service]

ProtectHome=yes

ProtectSystem=full  

[Install]

Alias=httpd.service

Setting ProtectSystem= to full causes Apache to have read-only access to the /boot/, /usr/, and /etc/ directories. Apache normally doesn't need to write to any of those directories, so it should now work. Let's try it and see:

donnie@ubuntu20-04:~$ sudo systemctl daemon-reload

donnie@ubuntu20-04:~$ sudo systemctl restart apache2

donnie@ubuntu20-04:~$

There are no error messages, so that's good. Let's check the status:

donnie@ubuntu20-04:~$ sudo systemctl status apache2

● apache2.service - The Apache HTTP Server

     Loaded: loaded (/lib/systemd/system/apache2.service; enabled; vendor preset: enabled)

    Drop-In: /etc/systemd/system/apache2.service.d

             └─override.conf

     Active: active (running) since Sat 2021-04-10 21:58:22 UTC; 4s ago

. . .

. . .

Yeah, that's good. So, yee-haw! We're golden, baby! But seriously, look in the systemd.exec man page, and you'll see a lot more security settings that you could possibly use.

Note

You can see here that I inadvertently used sudo to just look at a service status. Using sudo with systemctl is a force of habit with me, but fortunately, it doesn't hurt anything if I use it when it's not needed.

Just for fun, let's add in a few more security options, and then check the security status again. This time, we'll make our [Service] section look like this:

[Service]

ProtectHome=yes

ProtectSystem=full

PrivateDevices=yes

ProtectKernelTunables=yes

ProtectKernelModules=yes

ProtectControlGroups=yes

SystemCallFilter=@system-service

SystemCallErrorNumber=EPERM

NoNewPrivileges=yes

I'll leave it to you to read about these new parameters in the systemd.exec man page.

Once you've finished the edit, restart Apache to ensure that it will start properly. Then, run systemd-analyze security apache2.service again. You should see that the overall security score looks somewhat better than it did before.

There are a lot more security-related parameters that you can set in your service files. Remember though, that most services require that some settings be left in an UNSAFE mode for them to function correctly. Your best bet is to play around with these settings for various services on a virtual machine. That way, you can get a good idea of which settings work for the various services. And for goodness' sake, thoroughly test any service modifications you want to make before you put them into production.

Next, let's look at doing full edits for the Secure Shell service.

Creating a full edit

Doing partial edits works great when all you want to do is add a parameter that isn't already in the service file. But, it doesn't work if you need to delete a parameter, change the value of a parameter, or add a parameter that conflicts with some other existing parameter. To do any of these things, you'll need to do a full edit. The other reason for doing full edits is just that you might want to have the full file in front of you so that you can see what you're really doing. To do a full edit of the ssh.service file, for example, just use the --full option, like this:

donnie@ubuntu20-04:~$ sudo systemctl edit --full ssh.service

This time, you'll see the entire ssh.service file, as shown here:

Figure 5.2 – Editing a service file with the --full option

For our demo this time, let's set up access control for our Secure Shell service.

Pro tip

If you've been on the Linux scene for a while, you might be familiar with the concept of tcpwrappers. It's a strange name, but the concept is simple. You would just configure the IP addresses that you'd want to allow to access a particular network service in the /etc/hosts.allow file, and then deny all other IP addresses in the /etc/hosts.deny file. This still works for Ubuntu, but the Red Hat folk have removed tcpwrappers from RHEL 8. So, if you want to configure access control on any RHEL 8-type distro, such as the Alma Linux 8 that we're using, you'll need to do it by configuring the service files. On Ubuntu, you can use either method.

So, let's say that you want to allow SSH logins to your server from only one particular desktop machine. To do that, we'll use the IPAddressAllow= and the IPAddressDeny= parameters in the [Service] section of the ssh.service file. (Of course, that would be the sshd.service file if you want to try this on the Alma Linux machine.) Open the file for editing as I've just shown you and add two lines to the end of the [Service] section, using the IP address of your own host machine in the IPAddressAllow= line. The lines should look something like this:

IPAddressAllow=192.168.0.222

IPAddressDeny=any

The whole file should now look something like this:

Figure 5.3 – The ssh.service file after editing

If you just insert the IPAddressAllow= line and don't insert the IPAddressDeny= line, you'll find that nothing gets blocked. So, any time you want to set up an access whitelist, you'll need to use both of these lines together.

Save the file and do sudo systemctl daemon-reload. Then, restart or reload the Secure Shell service. Assuming that you used the correct IP address for your host machine, you should be able to log in via SSH. To really test this feature out, edit the file again, and use an incorrect IP address for your host. This time, you should be blocked from doing an SSH login. Note though, that you won't have to do another daemon-reload command. This new setting will take effect immediately upon saving the file. So, if you're doing this remotely, you will get locked out if you've entered an incorrect IP address for your host. In real life, you'd have to fix it by entering the server room and configuring things correctly from the server's local terminal.

When you do a full edit, a complete new modified copy of the original service file will get saved in the /etc/systemd/system/ directory, as we can see here:

donnie@ubuntu20-04:~$ cd /etc/systemd/system/

donnie@ubuntu20-04:/etc/systemd/system$ ls -l ssh.service

-rw-r--r-- 1 root root 586 Apr 11 20:07 ssh.service

donnie@ubuntu20-04:/etc/systemd/system$

As long as this file exists, it will always override the original file in the /lib/systemd/system/ directory.

Next, let's look at creating a brand new service.

Creating a new service

To create a brand new service from scratch, use the --force and --full options together, like this:

donnie@ubuntu20-04:~$ sudo systemctl edit --force --full timestamp.service

This will create a new service file in the /etc/systemd/system/ directory, just as we saw previously.

For this demo, we'll create a service that will place a periodic timestamp into our system log file. Our timestamp.service file will look like this:

[Unit]

Description=Service Creation Demo

Wants=network.target

After=syslog.target network-online.target

[Service]

ExecStart=/usr/local/bin/timestamp.sh

Restart=on-failure

RestartSec=20

KillMode=process

[Install]

WantedBy=multi-user.target

Here, we see the RestartSec= parameter, which we haven't seen before. This works with the Restart= line and just says to wait for the designated number of seconds before restarting a crashed service. Here, we're saying to wait for 20 seconds. We don't see a Type= line here, because we don't need it. Without this line, systemd will just go with the default of Type=simple, which is what we want. (I'll leave it to you to read about the simple Type in the systemd.service man page.)

Next, we'll create the timestamp.sh script, which will place a timestamp into the system log file every 60 seconds. Let's make it look like this:

#!/bin/bash

echo "Starting the timestamp service" | systemd-cat -p info

while :

do

        echo "timestamp.service: The current time is $(date '+%m-%d-%Y %H:%M:%S')" | systemd-cat -p info

        sleep 60

done

As you can see, it's just a simple while loop that pipes the current time into the systemd-cat utility every 60 seconds. In turn, systemd-cat sends the timestamp message to the system log file. The -p info option marks the message with an info level priority.

Next, make the script file executable, and copy it to the /usr/local/bin/ directory. Then, start the service:

donnie@ubuntu20-04:~$ chmod u+x timestamp.sh

donnie@ubuntu20-04:~$ sudo cp timestamp.sh /usr/local/bin

donnie@ubuntu20-04:~$ sudo systemctl start timestamp

donnie@ubuntu20-04:~$

You can enable the service if you really want to, but for now, we don't need to.

The status should look like this:

donnie@ubuntu20-04:~$ systemctl status timestamp

● timestamp.service - Service Creation Demo

     Loaded: loaded (/etc/systemd/system/timestamp.service; disabled; vendor preset: enabled)

     Active: active (running) since Sun 2021-04-11 21:57:26 UTC; 13min ago

   Main PID: 14293 (timestamp.sh)

      Tasks: 2 (limit: 2281)

     Memory: 820.0K

     CGroup: /system.slice/timestamp.service

             ├─14293 /bin/bash /usr/local/bin/timestamp.sh

             └─14411 sleep 60

Apr 11 22:00:26 ubuntu20-04 cat[14335]: timestamp.service: The current time is 04-11-2021 22:00:26

. . .

. . .

To see the timestamps in the log file, do:

donnie@ubuntu20-04:~$ journalctl -xe

Or, you can see them getting added in real time by doing sudo tail -f /var/log/syslog on the Ubuntu machine, or sudo tail -f /var/log/messages on the Alma machine. When you've seen enough, just do Ctrl + C to quit.

Changing the default systemd editor

So far, I've been showing you how to do all of this in the nano text editor, which is the default systemd editor for most modern Linux distros. But, what if you don't like nano, and would prefer to use something else? Let's say that Vim is your favorite text editor, and you want to use it instead of nano.

One way to use an alternate text editor is to specify the alternate editor each time you run a systemctl edit command, like this:

[donnie@localhost ~]$ sudo EDITOR=vim systemctl edit --full sshd

[donnie@localhost ~]$

That works, but doing it every time you want to run a systemctl edit command could get a bit tiresome. Fortunately, changing the default editor is easy, once you know how to do it.

First, edit the .bashrc file that's in your own home directory. At the very bottom of the file, add this line:

export SYSTEMD_EDITOR=vim

After saving the file, reload the new configuration:

[donnie@localhost ~]$ source .bashrc

[donnie@localhost ~]$

Next, open the sudoers file:

[donnie@localhost ~]$ sudo visudo

Scroll down to where you see the Defaults lines, and then add this line:

Defaults   env_keep += "SYSTEMD_EDITOR"

Save the file, and try running a systemctl edit command. You should now see vim instead of nano.

We've seen some cool stuff, but we're just getting started. For the ultimate in cool, let's look at using podman to automatically create container service files for us.

Creating a new container service with podman

Containers have been around for a long time, but they never became all that popular until Docker arrived on the scene with its new container management system. The original Docker system is cool, all right. But, it has some shortcomings, especially with security. For that reason, the good folk at Red Hat developed their own Docker replacement, which they call podman. podman comes with greatly enhanced security, and with cool features that aren't in Docker. The only problem is that podman is still only available on RHEL-type and Fedora distros, and everyone else still uses Docker. So, we'll perform these demos on the Alma Linux machine.

To install podman on your Alma machine, do:

[donnie@localhost ~]$ sudo dnf install podman-docker

This will install the podman package along with a shell script that invokes podman whenever you accidentally type docker. (Actually, that might not be by accident. You might have shell scripts that invoke docker commands, and installing the podman-docker package will prevent you from having to modify them to use podman commands.) To avoid confusion, I'll just be showing you podman commands in this demo.

The podman utility normally doesn't need root privileges, which is one of its advantages over Docker. But, in order to make this work, we'll need to create our Docker container under the root user account. We can do that either by going to the root user shell and doing everything there or by staying in the normal user shell and prefacing the podman commands with sudo. Since I normally like to avoid going to the root shell unless I absolutely have to, we'll do this with sudo.

Let's create a wordpress container, like this:

[donnie@localhost ~]$ sudo podman run -d -p 8080:80 --name wordpress wordpress

WordPress is a free open source blogging platform. Here, we're running a wordpress container in detached, or background, mode with the -d switch. Instead of exposing the default port 80 to the network, we're exposing port 8080. (Note that if this fails to start, you might already have something listening on port 8080. If that's the case, try again with another port.) The --name switch sets the container name that we'll soon be using in the command to create the service file.

We'll verify that it's running with sudo podman ps:

[donnie@localhost ~]$ sudo podman ps

CONTAINER ID  IMAGE                               COMMAND               CREATED        STATUS            PORTS                 NAMES

cc06c35f21ce  docker.io/library/wordpress:latest  apache2-foregroun...  2 minutes ago  Up 2 minutes ago  0.0.0.0:8080->80/tcp  wordpress

[donnie@localhost ~]$

You can also try to access WordPress from your host machine's web browser, but you'll first need to open port 8080/tcp on the Alma machine's firewall, like this:

[donnie@localhost ~]$ sudo firewall-cmd --permanent --add-port=8080/tcp

success

[donnie@localhost ~]$ sudo firewall-cmd --reload

success

[donnie@localhost ~]$

Then, go to the web browser of your host machine and navigate to port 8080 of the IP address of your Alma machine. The URL should look something like this:

http://192.168.0.9:8080/

This should pull up the opening WordPress screen, which will lead you through the setup process.

Okay, that's great, except that when you reboot the machine, the container won't start back up automatically. To fix that, we'll use the sudo podman generate systemd command to create the service file, as follows:

[donnie@localhost ~]$ sudo podman generate systemd wordpress | sudo tee /etc/systemd/system/wordpress-container.service

Note that using sudo to do a normal redirection with the > symbol doesn't work well in the /etc/ directory, but piping the output into the tee utility does. As you'll see, the tee utility sends output to both the screen and to a specified file.

Doing systemctl cat wordpress-container will show you the generated service file:

Figure 5.4 – The podman-generated service file

Now, if that isn't slick, I don't know what is. Let's enable the service and reboot, just to see what happens:

[donnie@localhost ~]$ sudo systemctl daemon-reload

[donnie@localhost ~]$ sudo systemctl enable wordpress-container

Created symlink /etc/systemd/system/multi-user.target.wants/wordpress-container.service → /etc/systemd/system/wordpress-container.service.

Created symlink /etc/systemd/system/default.target.wants/wordpress-container.service → /etc/systemd/system/wordpress-container.service.

[donnie@localhost ~]$ sudo shutdown -r now

When the reboot is complete, a sudo podman ps command should show you that the container is running:

[donnie@localhost ~]$ sudo podman ps

[sudo] password for donnie:

CONTAINER ID  IMAGE                               COMMAND               CREATED         STATUS                 PORTS                  NAMES

cc06c35f21ce  docker.io/library/wordpress:latest  apache2-foregroun...  12 minutes ago  Up About a minute ago  0.0.0.0:8080->80/tcp  wordpress

[donnie@localhost ~]$

(You'll need to use sudo here only because the container is running under the root user's account.)

And of course, systemctl status should also show you that it's running:

[donnie@localhost ~]$ systemctl status wordpress-container

● wordpress-container.service - Podman container-cc06c35f21cedd4d2384cf2c048f013748e84cabdc594b110a8c8529173f4c81.service

   Loaded: loaded (/etc/systemd/system/wordpress-container.service; enabled; vendor preset: disabled)

   Active: active (running) since Wed 2021-04-14 15:27:37 EDT; 2min 38s ago

. . .

. . .

Okay, that's all good. But, what if you need to create a container service that you want to run from your own user account without root privileges? Well, we've got you covered there, too. Just create the service file in your own home directory, and run it from there.

We'll start as we did before, except with a different container name and from our normal user shell, like so:

[donnie@localhost ~]$ podman run -d -p 9080:80 --name wordpress-noroot wordpress

We're using a different network port this time, so that it won't conflict with what we've already done. For now, let's stop the container:

[donnie@localhost ~]$ podman container stop wordpress-noroot

a6e2117dd4d5148d01a55d64ad8753c03436bfd9a573435e95d927f74 dc48f9e

[donnie@localhost ~]$

We'll next create a subdirectory within the user's own normal home directory:

[donnie@localhost ~]$ mkdir -p .config/systemd/user/

[donnie@localhost ~]$

We'll generate the service file, the same as we did before:

[donnie@localhost ~]$ podman generate systemd wordpress-noroot > .config/systemd/user/wordpress-noroot.service

[donnie@localhost ~]$

I forgot to point out before that there's a slight difference in the [Install] section of these generated service files. Instead of seeing only one target listed, you'll see two, as shown here:

. . .

. . .

[Install]

WantedBy=multi-user.target default.target

Default.target is needed whenever you want to run a service from your own user account. From here on out, the management commands are mostly the same, except that you won't need sudo and you'll need to use the --user option to tell systemd that the service unit file is in your own home directory. Let's load the new service file, and check the status:

[donnie@localhost ~]$ systemctl --user daemon-reload

[donnie@localhost ~]$ systemctl --user status wordpress-noroot

● wordpress-noroot.service - Podman container-a6e2117dd4d5148d01a55d64ad8753c03436bfd9a573435e95d927f74dc48f9e.service

   Loaded: loaded (/home/donnie/.config/systemd/user/wordpress-noroot.service; disabled; vendor preset: enabled)

   Active: inactive (dead)

. . .

. . .

Let's enable it and start it, and check the status again:

[donnie@localhost ~]$ systemctl --user enable --now wordpress-noroot

Created symlink /home/donnie/.config/systemd/user/multi-user.target.wants/wordpress-noroot.service → /home/donnie/.config/systemd/user/wordpress-noroot.service.

Created symlink /home/donnie/.config/systemd/user/default.target.wants/wordpress-noroot.service → /home/donnie/.config/systemd/user/wordpress-noroot.service.

[donnie@localhost ~]$ systemctl --user status wordpress-noroot

● wordpress-noroot.service - Podman container-a6e2117dd4d5148d01a55d64ad8753c03436bfd9a573435e95d927f74dc48f9e.service

   Loaded: loaded (/home/donnie/.config/systemd/user/wordpress-noroot.service; enabled; vendor preset: enabled)

   Active: active (running) since Wed 2021-04-14 15:44:26 EDT; 12s ago

. . .

. . .

If you have root privileges, you can open port 9080/tcp on the firewall and access WordPress from an external machine, just as we did before.

As things stand now, our rootless WordPress service won't automatically start when you boot the machine. But, it will start when you log into the machine and will stop when you log out. Fix that by doing the following for your own user account:

[donnie@localhost ~]$ loginctl enable-linger donnie

[donnie@localhost ~]$

Now, the container service will remain running when I log out and will automatically start when I reboot the machine.

Summary

In this chapter, we looked at how to edit and create service unit files. Along the way, we looked at various parameters that we can set, including several security-related ones. We also saw the importance of testing any changes that we make to the service files before putting them into production. And of course, there's that one thing that I keep pointing out, about the importance of knowing how to use the systemd man pages.

In the next chapter, we'll look at systemd targets. I'll see you there.

Questions

  1. How would you do a partial edit of the ssh.service file?

    a) sudo systemctl edit --partial ssh.service

    b) sudo systemctl edit ssh.service

    c) sudo systemedit ssh.service

    d) sudo systemedit --partial ssh.service

  2. How would you create a brand-new service?

    a) sudo systemctl edit --new newservice.service

    b) sudo systemctl edit --full newservice.service

    c) sudo systemctl edit --full --force newservice.service

    d) sudo systemctl edit newservice.service

  3. How would you create an access whitelist for a service?

    a) Just insert an IPAddressAllow= directive into the [Service] section.

    b) Insert both an IPAddressAllow= directive and an IPAddressDeny= directive in the [Service] section.

    c) Just insert an IPAddressAllow= directive into the [Unit] section.

    d) Insert both an IPAddressAllow= directive and an IPAddressDeny= directive in the [Unit] section.

Answers

b

c

b

Further reading

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.146.255.127