diff --git a/README.md b/README.md index 8ac9ea6..4f86a66 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,14 @@ A lightweight and (quasi-)stateless CI built on top of Nomad. Our main principle: offload as much work to Nomad as possible. -When we can't, offload it to Gitea, Consul, and the others. -At a last resort, we might do it in Albatros... +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 @@ -13,6 +19,99 @@ 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: + +```json +{ + "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 + +``` + +Of course, most of that will be handled by Gitea. + +## 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. + +## Writing your build script + +The following variables will be + +```bash +REPO_URL=https://git.deuxfleurs.fr/quentin/albatros.git +COMMIT=3fff73597f8ca18ef04c0d9bf64132ba55aadcaa +BRANCH=main +FLAVOR=default +SECRET1=xxx +SECRET2=xxx +``` + ## Ideas - [ ] Register the builder programatically diff --git a/main.go b/main.go index f6e31df..4323775 100644 --- a/main.go +++ b/main.go @@ -98,8 +98,7 @@ func hook(w http.ResponseWriter, r *http.Request) { log.Printf("Query info: %+v\n", dmeta) log.Printf("Job info: %+v\n", dres) - fmt.Println(notification.CompareUrl) - io.WriteString(w, "ok") + io.WriteString(w, dres.DispatchedJobID) } func build(w http.ResponseWriter, r *http.Request) {