act icon indicating copy to clipboard operation
act copied to clipboard

Draft: LXC support for self-hosted runners

Open earl-warren opened this issue 2 years ago • 10 comments

Description

The LXC support for self-hosted runners is used to run tests that do not fit the constraints imposed by the docker backend such as having a systemd capable environment.

It creates a container from scratch on every run. If the tests accidentally damage essential services such as the ssh server, it will not have any impact on the host running the LXC container. If the same accident happens without the isolation provided by the LXC container, the host itself will be damaged. The LXC support provides a robust isolation for each job in the workflows, which the self-hosted platform does not.

Implementation details

It is roughly the equivalent of doing the following:

  • Creating a LXC container (with lxc-create)
  • Running the commands in the container (with lxc-attach)
  • Destroying the LXC container (with lxc-destroy)

This is inherently insecure, in the same way the self-hosted platform is. Hardening LXC containers is possible but it makes them no more useful than docker containers.

FAQ

  • Why not run LXC inside a Docker container? Because it does not work. Although it could probably be hacked, that would not be more than that.
  • Why not run the LXC logic from the sef-hosted platform? Because it would require significant work and LXC knowledge from each ACT user. Instead they only have to care for their own tests, not to setup and teardown well isolated LXC containers.
  • Why not run the LXC containers as unprivileged to improve security? Because it does not work with most templates. Given the restrictions it imposes, the user is better of using Docker.
  • Why is apparmor disabled? Because it cannot be configured to allow essential operations such as mounting file systems

earl-warren avatar Mar 16 '23 11:03 earl-warren

This is an early draft go gauge interest and I'm ready to do the needful if there is. It is developed and used in the context of the https://forgejo.org project.

earl-warren avatar Mar 16 '23 11:03 earl-warren

This should be separate backend from host

panekj avatar Mar 16 '23 11:03 panekj

@earl-warren This sounds very interesting. I haven't read through the code but I would like to see it integrated into act. I guess we need to discuss about the structure and interfaces a bit more. It's not always clearly separated currently.

@ChristopherHX might have some ideas and expectations here as well.

KnisterPeter avatar Mar 20 '23 08:03 KnisterPeter

Great to hear :tada:

It could also be an optional feature of the self-hosted platform. If LXC is available and the option is set, wrap it into a LXC container for better isolation. Otherwise not. It would be entirely transparent to the workflows being run.

@ChristopherHX is is something you think to be a valuable addition to the self-hosted platform you implemented? Or would you prefer to go in another direction?

earl-warren avatar Mar 23 '23 09:03 earl-warren

@ChristopherHX is is something you think to be a valuable addition to the self-hosted platform you implemented? Or would you prefer to go in another direction?

I primary made my self-hosted for platforms like freebsd, openbsd, plan9 etc. where docker is not a thing (and GitHub Actions doesn't support), so this should be a different implemention of the ExecutionsEnvironment interface.

I prefer that you create a new struct, which overrides (in golang it is probably a different wording, using interfaces it should work) some HostEnvironment functions. Then instantiate it via a custom identifier from the runcontext, maybe -lxc:<baseimage> instead of -self-hosted ( needs design consent, it took long for my PR to land ).

I don't think we need to add Root() and Name() to the interface. You can override the Close() executor inside the struct with access to custom fields. The start method also have access to the fields, if you create a local var with the specfic type and later assign it to jobcontainer.

ChristopherHX avatar Mar 23 '23 10:03 ChristopherHX

Thanks for the feedback @ChristopherHX

I prefer that you create a new struct, which overrides (in golang it is probably a different wording, using interfaces it should work) some HostEnvironment functions. Then instantiate it via a custom identifier from the runcontext, maybe -lxc: instead of -self-hosted ( needs design consent, it took long for my PR to land ).

I'm happy to go in this direction. The current implementation is more of a proof of concept / hack than anything else and reworking it entirely is what I was expecting anyways.

If anyone think it should go differently please speak up. Otherwise I'll start working as soon as time permits, which could be as soon as in two weeks from now.

earl-warren avatar Mar 23 '23 11:03 earl-warren

@earl-warren this pull request is now in conflict 😩

mergify[bot] avatar Jul 17 '23 19:07 mergify[bot]

I'm still motivated, it just takes a little longer than expected.

earl-warren avatar Jul 17 '23 20:07 earl-warren

The HostEnvironment took me ca. 1,5 years (1 year in my fork, 1/2 years in review) to get in here. You can still be faster than me 😅.

ChristopherHX avatar Jul 17 '23 21:07 ChristopherHX

PR is stale and will be closed in 14 days unless there is new activity

github-actions[bot] avatar Jan 14 '24 00:01 github-actions[bot]

PR is stale and will be closed in 14 days unless there is new activity

github-actions[bot] avatar Jul 14 '24 00:07 github-actions[bot]