10

I need to run the binary that I don't fully trust.

I've created a systemd unit for it where it's locked down as much as humanly possible, it works great.

Now it's still on my system (Fedora) and I may accidentally execute it. How can I prevent this?

It still needs to be mode 700 or something to be executed by systemd which makes it executable by root and again I want to prevent this.

Now I can have three linked systemd units, where the first one makes it chmod 700, the second one actually executes it, the third one makes it chmod 644 but it's way too convoluted. I guess something simpler can be done.

    3 Answers 3

    18

    Confine the app using a custom SELinux policy module.

    Given that you're on Fedora, which runs with SELinux enabled, the obvious answer would be to give the program its own SELinux domain to which only init_t can transition.

    That can be expressed using something like this in your myapp.te file:

    allow init_t myapp_t:process { transition }; type_transition init_t myapp_exec_t:process myapp_t; 

    Then make sure to add allow myapp_t something rules listing exactly the kind of access the app needs to have but no more than that. Finally, label the binary as myapp_exec_t using chcon and you're set.

    If you have never created a SELinux module before, Quick start to write a custom SELinux policy should get you started.

    1
    • 1
      ooooh this is actually the best solution!CommentedNov 10, 2024 at 17:38
    4

    I'm not sure systemd will like this but one possible way to approach this is to:

    chmod 644 /path/to/binary 

    And then execute it as:

    /lib64/ld-linux-x86-64.so.2 /path/to/binary 

    Not the most elegant solution but it may work.

    However there's a huge issue with it. Any capabilities assigned to the app via setcap will be automatically lost and for my app at least one, cap_net_bind_service is required (it's listening on a port below 1024). This too could be worked around by using iptables/nftables/xinetd/etc. but the whole setup becomes quite untenable.

    3
    • Is it run as root (by the service)? If so, capabilities shouldn’t matter.CommentedNov 10, 2024 at 8:36
    • "it's locked down as much as humanly possible" - no, it's not running under root. That itself poses a huge security risk.CommentedNov 10, 2024 at 9:10
    • Note that the lack of file capabilities can be worked around using ambient capabilities (those work somewhat like environment variables, so you can have systemd set them and the program will automatically inherit them regardless of file capabilities). But this chmod a-x route is still more or less an ugly hack providing no actual security; if you really only want to prevent accidental execution, perhaps putting the binary into a hidden file in a directory not on PATH is all the safety you need.
      – TooTea
      CommentedNov 10, 2024 at 20:10
    4

    First option: make executable only to start, then undo

    You're using a wrapper already in your own answer – namely ld-linux.so – to invoke your program. Follow that idea through!

    Your systemd service file can ExecStartPre= to chmod u+x and ExecStartPost= to chmod u-x as soon as the service runs (nb: not only after it stops, which makes this a lot more robust against e.g. power failures).

    Another option that I like: mount only for service life

    Put that file (executable as it is, including all its setcap'ed capabilities stored in xattrs) into it's own little file system image. To start the executable, make the .service file depend on that file system being mounted in a location where nothing would look to execute files. (If something maliciously specifically looks everywhere to execute files, it'd probably use the ld-linux.so trick anyways.)

    • I'd recommend mksquashfs dubiousexecutable /var/lib/dubiousapplication/fs-image.squash
    • Create a systemd .mount unit, e.g. run-dubiousapplication.mount, with What=/var/lib/dubiousapplication/fs-image.squash and Where=/run/dubiousapplication (note: .mount file name must match Where, with / being replaced by - and the leading / omitted), and put it where systemd looks for these (probably /etc/systemd/system/). It might need Options=suid (can't remember whether capabilities set on files are respected without).
    • Create a systemd .service file for your service. It can run as system service with User= specifying the user it runs at, or as user service from your user's session. It must RequiresMountsFor=/run/dubiousapplicationThat way, the file system image gets mounted for the live time of this service only.

    The "usual" option: containers

    If you need isolation, you'd typically want to run a service in a container, so that it only sees the parts of your overall file system that you explicitly specify it to see. Yay to Linux namespaces!

    Podman makes integrating containerized services in systemd especially easy, so that's a route I'd take.

    You'd want to write a Dockerfile (there's other ways as well, but why?). I typically base minimal services simply on debian, i.e. my Dockerfiles start with FROM debian:12. You can basically pick any distro that publishes docker images (that is, all major).

    Then, you use the COPY <source file> <target path within> directive to have your executable inside, e.g. COPY dubiousexecutable /usr/bin/.

    You EXPOSE <portnumber>[/udp|/tcp] internal ports.

    You set the thing that gets started when the container gets started to ENTRYPOINT /usr/bin/dubiousexecutable. So, in total, your Dockerfile would have four lines (unless you first need to install more stuff inside, which you could do, e.g. with RUN apt-get update; apt-get install foobar; apt-get clean)

    Then, you podman build -t dubiouscontainerimage . a container image.

    Then, you podman create -p <externalport>:<internalport> --name dubiouscontainer dubiouscontainerimage a container of the name dubiouscontainer from the image dubiouscontainerimage. (you can do that as many times you like with different ports, for example).

    Now you could podman start dubiouscontainer manually – OR you could use podman generate-systemd dubiouscontainer to generate systemd service files, which you can then use with systemd.

      You must log in to answer this question.

      Start asking to get answers

      Find the answer to your question by asking.

      Ask question

      Explore related questions

      See similar questions with these tags.