Tools to build a software supply chain on your own
Go to file
2023-03-15 10:16:49 +01:00
example Dispatch passed arguments 2023-03-15 08:51:13 +01:00
hcl respec 2023-03-15 10:16:49 +01:00
.albatros respec 2023-03-15 10:16:49 +01:00
.gitignore Better doc 2023-03-15 10:12:30 +01:00
go.mod parse gitea webhooks 2023-03-14 18:51:31 +01:00
go.sum parse gitea webhooks 2023-03-14 18:51:31 +01:00
main.go More doc 2023-03-15 10:08:18 +01:00
README.md respec 2023-03-15 10:16:49 +01:00

Albatros

A lightweight and (quasi-)stateless CI built on top of Nomad. Our main principle: offload as much work to Nomad as possible. We don't want to build an abstraction on top of it and hide Nomad internals, but instead expose them as directly and transparently as possible, so that you can benefit from all the features of this software. Albatros is a specialized CI for Nomad

When we can't offload our work directly to Nomad, we should consider offloading it to Gitea, Consul, and the others. At a last resort, we might do it in Albatros...

Deploy

Requirements: Nomad, Consul Optional: Gitea

nomad namespace apply -description "Continuous Integration" ci
nomad run hcl/builder.hcl
go run main.go

Register a build

Add to Consul a key in albatros hierarchy named after your repo URL as base64. Example:

albatros/aHR0cHM6Ly9naXQuZGV1eGZsZXVycy5mci9xdWVudGluL2FsYmF0cm9zLmdpdA==

The key must contain a JSON file with your desired token, trust conditions, and secrets:

{
  "token": "s3cr3t",
  "trusted_if": {
    "sender": [ "quentin", "lx" ]
  }
  "secrets": {
    "SECRET1": "blabla",
    "SECRET2": "hey hey"
  }
}

Your secrets will be passed as a job payload only if all trusted conditions are passing. (For now, we can only check that based on sender's login).

Then you can trigger a build as follow:

$ curl -d @example/albatros.json http://localhost:8080/hook?token=s3cr3t&flavor=default
builder/dispatch-1678866433-15aad86a

You need to pass your token, and you can optionally pass a flavor, that can be used later by your build script.

As you can see, you now have an identifier representing your job, you can use it to follow your logs (don't forget to urlencode it):

$ curl http://localhost:8080/build?job=builder%2Fdispatch-1678866433-15aad86a
<todo>

Of course, most of that will be handled by Gitea.

Writing your build script

You must create an executable .albatros file at the root of your repository. Then, you can use the interpreter you want to execute it, let use bash as an example, but please use python, javascript, or anything else that has proper error handling...

#!/usr/bin/env bash
echo "Building commit $COMMIT"
go build

During the build, the following environment variables are available:

REPO_URL=https://git.deuxfleurs.fr/quentin/albatros.git
COMMIT=3fff73597f8ca18ef04c0d9bf64132ba55aadcaa
BRANCH=main
FLAVOR=default
SECRET1=xxx
SECRET2=xxx

Gitea integration

Todo

Security model

Albatros only tries to protect your secrets. To achieve that, we only inject them in the build script if the job has been sent (triggered) by a trusted sender. This decision is taken by looking at the webhook payload content.

To protect against sender impersonification, your webhook must be called only by trusted code, and more generally, your token must remain secret.

We assume otherwise that anyone can trigger the webhook through Gitea while replacing the content of the build script by a malicious software. Signing the build script is tempting but it will not prevent someone to put its malicious code, for example in a Rust project, in the build.rs file. So you must assume that your CI will execute untrusted code.

To protect against undesired code execution, you must harden your environment, for example by using VMs instead of containers, timeouts, and restricting some IO. All of that must be handled by Nomad. Also, be careful to the local network in which your workload will be executed.

Passing secrets through environment variables has been criticized as other process inspecting the process can dump the environment variables. It is your responsability to ensure that no malicious process can read the content of your environment variable. It should not be that hard, containers use PID namespace by default, so one containerized process can not access process information of other processes in the system.

Ideas

  • Register the builder programatically
  • Allow users to define their own set of builders (ones with more CPU+RAM, etc.)