Secure and isolated GitHub Actions runs without a headache.
What is Flint?
Flint is an open source coordinator for GitHub Actions that is built to run isolated and secured actions on custom hosted runners.
It runs said runners within empheral VMs managed through firecracker.
Stability
Expect bugs! Flint is very early in it's development and did not have a stable release for now.
Getting Started
To get started with Flint compile it from source via:
git clone https://github.com/flint-actions/flint
cd flint
make build
In order to build a working go toolchain is required. After sucessfull compilation put the binary in a folder like for
e.g. /root/flint/
.
Filesystem and kernel
A linux kernel and rootfs is required in order to work. A sample kernel and rootfs can be built by (docker, dd, mkfs.ext4 are required):
make filesystem
make kernel
Please be aware the filesystem contains an active ssh server. Take care not to expose the vm ports by accident!
This builts a filesystem, kernel in build/rootfs/rootfs.ext4
and build/kernel/linux/vmlinux
. Additionally
firecracker and it's jailer binary are needed.
For more information have a look into scripts/filesystem.sh
and scripts/kernel.sh
.
Copy the filesystem, kernel, firecracker and jailer to a directory like for e.g. /root/flint/
. Make sure rootfs.ext4
has owner uid 123 and group id 100.
chown 123:100 /root/flint/rootfs.ext4
Additional dependencies
In addition to firecracker the following runtime dependencies are needed:
Network configuration
Flint for now requires a preconfigured bridge interface. It can be setup with the help of the following commands:
ip link add name br-flint type bridge
ip addr add 10.0.0.1/24 dev br-flint
ip addr add fd3b:5cee:6e4c:2a55:1::2/80 dev br-flint
ip link set dev br-flint up
For IP v6 use something similar to:
sysctl -w net.ipv6.conf.eth0.proxy_ndp=1
ip -6 neighbour add proxy fd3b:5cee:6e4c:2a55:1::2/80 dev eth0
ip -6 route add fd3b:5cee:6e4c:2a55:1::/80 dev br-flint
Replace fd3b:5cee:6e4c:2a55
with your public IPv6 /64 prefix.
GitHub App
Flint leverages a GitHub App in order to dynamically register spawned runners. GitHub Apps require an organization
account (this does not need to be an payed organization). As a getting started guide see the official documentation
on how to create a GitHub App. Configure a webhook URL which points to https://YOUR_DOMAIN/webhook
and configure
a secret. Additionally a GitHub private key is needed for the application to authenticate.
After sucessfully creating the app go to Permissions & events
and configure Read-only access for Actions and Metadata.
In addition Read and write permission for Organization Self-hosted runners is required. Save your changes and refresh
the page. It should now exist a configuration section for Subscribe to events. In this section Workflow job events
are needed to inform Flint about a new workflow which requires a runner.
Put the private.pem
file into for e.g. /root/flint/
Configuration
Create a yaml configuration similar to for e.g.:
logLevel: "debug"
address: example.net:443
email: flint@example.net
networks:
- name: "br-default"
v4: "10.0.0.1/24"
github:
appID: "ID"
organization: "ORGANIZATION"
webhookSecret: "WEBHOOK_SECRET"
privateKey: |
-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----
runners:
- name: "Default"
group: "Default"
labels: [ "self-hosted", "Linux", "amd64" ]
kernel: "/root/flint/kernel/build/kernel/linux/vmlinux"
filesystem: "/root/flint/fs/build/rootfs/bazel.ext4"
jailer: "/root/flint/jailer"
firecracker: "/root/flint/firecracker"
network: "br-default"
cpuCount: 8
memorySize: 16384 # size in megabytes
smt: true
diskSize: 42949672960 # size in bytes
Launching
Flint can be launched through multiple ways for example running it interactive:
./flint --interactive
This should launch flint and wait for any event which requires a self-hosted runner. Any action which should run und Flint
can be configured with:
runs-on: self-hosted
For continous running flint a simple systemd service could be created:
[Unit]
Description=flint a fircracker gihtub actions runner service
After=network.target
StartLimitIntervalSec=0
[Service]
Type=simple
Restart=always
RestartSec=1
WorkingDirectory=/root/flint
ExecStart=/root/flint/flint
[Install]
WantedBy=multi-user.target
Contributing
Contributions are welcome.
FAQ
-
Q: I tried to run on arm64 and got error XYZ.
A: Currently Flint is untested on arm64 but in theory everything should work out of the box.
-
Q: Is there an ability do debug an vm image?
A: Yes with the option --interactive
Flint spawns a single runner and connects stdout, stderr and stdout to the
current shell. This allows login into the machine with for e.g. runner:runner (user:password). This disables
the http server listening for events.