Can I work with a systemd service without adding it the global system configuration?

Salam everybody, As seen in this post, Can I use and make a service file for systemd to run my python script with its dependencies without let NixOS configure the service.
For more context to clarify, please read: How to Make a Python Script Unkillable on NixOS | Tried Running as Root but Failed - #2 by bme

A side note: I want the solution to be as strong enough as possible to prevent me from killing or disabling the service at all with no to low hassle(because configuring and choosing which sudo command my account can run , It was a big pain for me.)
I read this blog post but as beginner 90% of the content I can’t apply And don’t know how to apply it.

Thanks in advance.

Can you clarify what you mean by this? Are you running NixOS or nix on some other distribution? What is the goal of not having nixos configure the service? Is it to prevent you from reconfiguring nixos to not run the service?

Please read this , You will understand what I mean : How to Make a Python Script Unkillable on NixOS | Tried Running as Root but Failed - #2 by bme

You have linked to a post that I wrote :slight_smile:

The part which is confusing for me is that using a typical setup, root is required to configure a nixos service. You have an accountability buddy with the root password, therefore you do not have root, therefore you can’t kill systemd services, therefore it is fine to have nixos configure the service. However this post says that you do not want nixos to configure the service. I want to understanand why.

My bad, I’m sorry, What I mean: I want to use this method for these reasons :
1- I can’t use Nixos without the configuration.nix. So I can’t lower my sudo privileges for this especially.
2- Also when I implement the service in the configuration file, I can easily delete the snippet of it, And this needn’t happen.
So I want an approach as far as possible from my manipulations.

So the problem is that nix is the configuration.nix so as long as you can change that, you don’t really have a way to “lock yourself out”.

Idle thought: change configuration.nix to look something like

import ./can-edit-this-file.nix

systemd.timers.screenshots = {
};

systemd.services.screenshots = {
};

Then only grant yourself access to ./can-edit-this-file.nix, but this itself still has problems: you still via config in your module can also mess with systemd.services. You could try to stuff your service into a snippet in activationScripts using systemd-run, but activationScripts is an attr set, so you can still null the script out if you want via config. Unfortunately the fix-point nature of the module system means you always via any file have a handle on the final global configuration.

The only way I can really see you making this work is if you basically migrate almost everything to home manager, then you really probably could lock yourself out of configuration.nix for quite extended periods of time without much of a productivity hit.

Alternatively, I have no idea what happens if you just write a unit directly into /etc/systemd/system. Maybe that “just works”.

And this is what just I need, Like in any other normal distro like Debian for example.

Looks like that folder gets marked as read only :upside_down_face:

You’re right and this is what happened when I tried but I thought this was a problem with my machine only, But seems not :slight_smile: .

I haven’t used this but there is GitHub - numtide/system-manager: Manage system config using nix on any distro which claims to run systemd services in non-nixos systems (even on nixos)

Also home-manager creates user level systemd services, by defining systemd.user.services maybe if root is not a requirement. related What is the difference between `systemd.services` and `systemd.user.services`?

1 Like

You can run SystemD in Home Manager, as stated by Phanirithvij.

The syntax is slightly different though. E.G.

systemd.user.services.system_update = {
  Unit = {
    Description = "System update on restart";};
      Service = {
        Type = "oneshot";
        ExecStart = "/home/me/Scripts/startup.sh";
      };
  Install = {
    WantedBy = [ "default.target" ];
  };
    };

How I can implement this I’m my Python script or How to make use of it, If I succeed in implementing the approach illustrated in this blog post I will save a lot of brain pain :confused: ? So if there is anyone who can help me or at least forward me to other sources or discussions to help me at this point.
Thanks in advance.


Edit : This answer of ChatGPT, so my question is this gonna work :

The blog explains how to create an unkillable process on Linux by setting a special flag in the kernel's process structure using a kernel module. This involves:

1. **Creating a Kernel Module**: A module that loads into the kernel.
2. **Accessing the Process Structure**: Finding the target process's `task_struct`.
3. **Setting the Unkillable Flag**: Modifying the `SIGNAL_UNKILLABLE` flag to prevent the process from being killed.

### Implementation in Python

Since Python cannot directly manipulate kernel structures, you'll need to:

1. **Write the Kernel Module in C**: Use the example in the blog as a guide.
2. **Communicate from Python**: Use Python to load and unload the kernel module, and to send commands to it via a character device or other interface.

### Example Outline

1. **Kernel Module (C)**:
    ```c
    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/sched.h>

    static int pid = 1;
    module_param(pid, int, 0644);

    static int __init make_unkillable_init(void) {
        struct task_struct *task = pid_task(find_vpid(pid), PIDTYPE_PID);
        if (task) {
            task->signal->flags |= SIGNAL_UNKILLABLE;
            printk(KERN_INFO "Process %d is now unkillable.\n", pid);
        }
        return 0;
    }

    static void __exit make_unkillable_exit(void) {
        struct task_struct *task = pid_task(find_vpid(pid), PIDTYPE_PID);
        if (task) {
            task->signal->flags &= ~SIGNAL_UNKILLABLE;
            printk(KERN_INFO "Process %d is now killable.\n", pid);
        }
    }

    module_init(make_unkillable_init);
    module_exit(make_unkillable_exit);
    MODULE_LICENSE("GPL");
    ```

2. **Python Script**:
    ```python
    import os
    import subprocess

    def make_process_unkillable(pid):
        # Compile the kernel module if necessary
        if not os.path.exists('make_unkillable.ko'):
            subprocess.run(['make', '-C', '/lib/modules/$(uname -r)/build', 'M=$(pwd)', 'modules'])

        # Insert the kernel module with the specified PID
        subprocess.run(['insmod', 'make_unkillable.ko', f'pid={pid}'])

    def make_process_killable():
        # Remove the kernel module
        subprocess.run(['rmmod', 'make_unkillable'])

    # Example usage
    pid = 1234  # Replace with the actual PID of the process you want to protect
    make_process_unkillable(pid)
    ```

### Note:
- **Kernel Programming Knowledge**: This requires knowledge of kernel programming and module compilation.
- **Privileges**: Root privileges are needed to load/unload kernel modules.
- **Safety**: Modifying kernel structures can make your system unstable; proceed with caution.

For more details, refer to the original [blog post](https://ortiz.sh/linux/2020/07/05/UNKILLABLE.html).

Maybe I misunderstood something, would running a systemd service inside a NixOS container achieve what you wanted?

Let me restate what I think the OP wants:

  1. They wish to retain sudo via their own user account edit permissions on /etc/nixos/configuration.nix, presumably so they can be productive of the computer.
  2. They want some supervised service running on NixOS that only the root account can disable (which they do not have access to).

My feeling is that this is impossible on NixOS. I think you’d be making enough changes to either how modules work or how the boot process worked that it would be a fork.

If you want the service to be unkillable by the current user who doesn’t have root privileges, you can make a user-level systemd service for another user.

There are few ways to do that (while acting from another user):

  • via home-manager (as already shown in this thread)
  • by placing or symlinking a regular systemd unit file into ~/.config/systemd/user/ (as in any other systemd-based linux distro)

Ok, so now that other user has gain a session automatically on boot. How are you doing that without touching configuration.nix? I’d love a solution, this problem is very interesting, because it basically is trying to undermine NixOS.

1 Like