This commit is contained in:
Quentin 2023-03-15 10:08:18 +01:00
parent 09752bf4d6
commit 205143d7a7
Signed by: quentin
GPG key ID: E9602264D639FF68
2 changed files with 102 additions and 4 deletions

103
README.md
View file

@ -2,8 +2,14 @@
A lightweight and (quasi-)stateless CI built on top of Nomad. A lightweight and (quasi-)stateless CI built on top of Nomad.
Our main principle: offload as much work to Nomad as possible. Our main principle: offload as much work to Nomad as possible.
When we can't, offload it to Gitea, Consul, and the others. We don't want to build an abstraction on top of it and hide
At a last resort, we might do it in Albatros... 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 ## Deploy
@ -13,6 +19,99 @@ nomad run hcl/builder.hcl
go run main.go 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
<todo>
```
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 ## Ideas
- [ ] Register the builder programatically - [ ] Register the builder programatically

View file

@ -98,8 +98,7 @@ func hook(w http.ResponseWriter, r *http.Request) {
log.Printf("Query info: %+v\n", dmeta) log.Printf("Query info: %+v\n", dmeta)
log.Printf("Job info: %+v\n", dres) log.Printf("Job info: %+v\n", dres)
fmt.Println(notification.CompareUrl) io.WriteString(w, dres.DispatchedJobID)
io.WriteString(w, "ok")
} }
func build(w http.ResponseWriter, r *http.Request) { func build(w http.ResponseWriter, r *http.Request) {