use asym key

This commit is contained in:
Anbraten 2022-06-01 20:38:04 +02:00
parent 48564b0271
commit fe3349df6c
4 changed files with 42 additions and 20 deletions

View File

@ -1,27 +1,26 @@
# Woodpecker CI Configuration Service example
This repository provides a very simplistic example of how to set up an external configuration service for **Woodpecker CI**
The external service gets a **HTTP POST** request with information about the repo, current build, and the configs that would normally be used.
It can then decide to acnowledge the current configs (By returning **HTTP 204**), or overriding the configurations and returning new ones in the response
The external service gets a **HTTP POST** request with information about the repo, current build, and the configs that would normally be used.
It can then decide to acknowledge the current configs (By returning **HTTP 204**), or overriding the configurations and returning new ones in the response
Usecases for this system are:
Use cases for this system are:
- Centralized configuration for multiple repositories at once
- Preprocessing steps in the pipeline like templating, macros or conversion from different pipeline formats to woodpeckers format
This service is written in go, to run it first copy the config example: `cp .env.example .env`
Adjust the secret and add a filtering regex. The repositories that have a name match the filtering regex will receive the config from `central-pipeline-config.yaml`, while all other repositories will continue using their original configuration.
Download the public key from your woodpecker instance from `http(s)://your-woodpecker-server/api/signature/public-key` and save it to file. Set `WOODPECKER_CONFIG_SERVICE_PUBLIC_KEY_FILE` to the path to that file and add a filtering regex. The repositories that have a name match the filtering regex will receive the config from `central-pipeline-config.yaml`, while all other repositories will continue using their original configuration.
Then run using `go run .`.
Then run using `go run .`.
Make sure to configure your woodpecker instance with the correct **endpoint** and configure the same **secret**. See [Woodpeckers documentation here](https://woodpecker-ci.org/docs/administration/external-configuration-api)
eg:
eg:
```shell
# Server
# ...
WOODPECKER_CONFIG_SERVICE_ENDPOINT=http://<service>:8000/ciconfig
WOODPECKER_CONFIG_SERVICE_SECRET=mysecretsigningkey
WOODPECKER_CONFIG_SERVICE_PUBLIC_KEY_FILE=public-key.pem
```

7
go.mod
View File

@ -3,8 +3,7 @@ module github.com/woodpecker-ci/example-config-service
go 1.17
require (
github.com/99designs/httpsignatures-go v0.0.0-20170731043157-88528bf4ca7e
github.com/woodpecker-ci/woodpecker v0.15.0
github.com/go-ap/httpsig v0.0.0-20210714162115-62a09257db51
github.com/joho/godotenv v1.4.0
github.com/woodpecker-ci/woodpecker v0.15.1
)
require github.com/joho/godotenv v1.4.0

6
go.sum
View File

@ -57,8 +57,6 @@ code.gitea.io/sdk/gitea v0.15.0/go.mod h1:klY2LVI3s3NChzIk/MzMn7G1FHrfU7qd63iSMV
contrib.go.opencensus.io/exporter/stackdriver v0.13.4/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU=
github.com/99designs/httpsignatures-go v0.0.0-20170731043157-88528bf4ca7e h1:rl2Aq4ZODqTDkeSqQBy+fzpZPamacO1Srp8zq7jf2Sc=
github.com/99designs/httpsignatures-go v0.0.0-20170731043157-88528bf4ca7e/go.mod h1:Xa6lInWHNQnuWoF0YPSsx+INFA9qk7/7pTjwb3PInkY=
github.com/Antonboom/errname v0.1.5/go.mod h1:DugbBstvPFQbv/5uLcRRzfrNqKE9tVdVCqWCLp6Cifo=
github.com/Antonboom/nilnil v0.1.0/go.mod h1:PhHLvRPSghY5Y7mX4TW+BHZQYo1A8flE5H20D3IPZBo=
github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
@ -397,6 +395,8 @@ github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2H
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U=
github.com/go-ap/httpsig v0.0.0-20210714162115-62a09257db51 h1:cytjZGyqtAu9JspfDt9ThCJ2KKCT/kPGDPCKWIZv8dw=
github.com/go-ap/httpsig v0.0.0-20210714162115-62a09257db51/go.mod h1:+4SUDMvPlRMUPW5PlMTbxj3U5a4fWasBIbakUw7Kp6c=
github.com/go-critic/go-critic v0.6.2/go.mod h1:td1s27kfmLpe5G/DPjlnFI7o1UCzePptwU7Az0V5iCM=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@ -1196,6 +1196,8 @@ github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr
github.com/woodpecker-ci/expr v0.0.0-20210628233344-164b8b3d0915/go.mod h1:PbzlZ93HrA1cf16OUP1vckAPq57gtF+ccnwZeDkmC9s=
github.com/woodpecker-ci/woodpecker v0.15.0 h1:p3svFdtoXwmsd7w0/PJwyqfGejk2HaS8f6cC69xRxUE=
github.com/woodpecker-ci/woodpecker v0.15.0/go.mod h1:gWRKIzkowiQmy8WC/fp5Ktn3QHZc15GtneRuoalysxg=
github.com/woodpecker-ci/woodpecker v0.15.1 h1:S9RgkDoUQ0tvOL8/yJDDvDxdXUdE+XOdlZf/I6D3SRg=
github.com/woodpecker-ci/woodpecker v0.15.1/go.mod h1:gWRKIzkowiQmy8WC/fp5Ktn3QHZc15GtneRuoalysxg=
github.com/xanzy/go-gitlab v0.55.1/go.mod h1:F0QEXwmqiBUxCgJm8fE9S+1veX4XC9Z4cfaAbqwk4YM=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=

34
main.go
View File

@ -1,7 +1,9 @@
package main
import (
"crypto/ed25519"
_ "embed"
"encoding/hex"
"encoding/json"
"io/ioutil"
"log"
@ -9,7 +11,7 @@ import (
"os"
"regexp"
"github.com/99designs/httpsignatures-go"
"github.com/go-ap/httpsig"
"github.com/joho/godotenv"
"github.com/woodpecker-ci/woodpecker/server/model"
)
@ -36,14 +38,24 @@ func main() {
log.Fatalf("Error loading .env file: %v", err)
}
secretToken := os.Getenv("CONFIG_SERVICE_SECRET")
pubKeyPath := os.Getenv("CONFIG_SERVICE_PUBLIC_KEY_FILE")
host := os.Getenv("CONFIG_SERVICE_HOST")
filterRegex := os.Getenv("CONFIG_SERVICE_OVERRIDE_FILTER")
if secretToken == "" && host == "" {
log.Fatal("Please make sure CONFIG_SERVICE_HOST and CONFIG_SERVICE_SECRET are set properly")
if pubKeyPath == "" && host == "" {
log.Fatal("Please make sure CONFIG_SERVICE_HOST and CONFIG_SERVICE_PUBLIC_KEY_FILE are set properly")
}
pubKeyRaw, err := ioutil.ReadFile(pubKeyPath)
if err != nil {
log.Fatal("Failed to read public key file")
}
pubKeyStr, err := hex.DecodeString(string(pubKeyRaw))
if err != nil {
log.Fatal("Failed to decode public key")
}
pubKey := ed25519.PublicKey(pubKeyStr)
filter := regexp.MustCompile(filterRegex)
http.HandleFunc("/ciconfig", func(w http.ResponseWriter, r *http.Request) {
@ -52,13 +64,23 @@ func main() {
return
}
signature, err := httpsignatures.FromRequest(r)
// check signature
pubKeyID := "woodpecker-ci-plugins"
keystore := httpsig.NewMemoryKeyStore()
keystore.SetKey(pubKeyID, pubKey)
verifier := httpsig.NewVerifier(keystore)
verifier.SetRequiredHeaders([]string{"(request-target)", "date"})
keyID, err := verifier.Verify(r)
if err != nil {
log.Printf("config: invalid or missing signature in http.Request")
http.Error(w, "Invalid or Missing Signature", http.StatusBadRequest)
return
}
if !signature.IsValid(secretToken, r) {
if keyID != pubKeyID {
log.Printf("config: invalid signature in http.Request")
http.Error(w, "Invalid Signature", http.StatusBadRequest)
return