use asym key
This commit is contained in:
parent
48564b0271
commit
fe3349df6c
15
Readme.md
15
Readme.md
@ -1,27 +1,26 @@
|
|||||||
# Woodpecker CI Configuration Service example
|
# Woodpecker CI Configuration Service example
|
||||||
|
|
||||||
This repository provides a very simplistic example of how to set up an external configuration service for **Woodpecker CI**
|
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.
|
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
|
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
|
- Centralized configuration for multiple repositories at once
|
||||||
- Preprocessing steps in the pipeline like templating, macros or conversion from different pipeline formats to woodpeckers format
|
- 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`
|
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)
|
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
|
```shell
|
||||||
# Server
|
# Server
|
||||||
# ...
|
# ...
|
||||||
WOODPECKER_CONFIG_SERVICE_ENDPOINT=http://<service>:8000/ciconfig
|
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
7
go.mod
@ -3,8 +3,7 @@ module github.com/woodpecker-ci/example-config-service
|
|||||||
go 1.17
|
go 1.17
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/99designs/httpsignatures-go v0.0.0-20170731043157-88528bf4ca7e
|
github.com/go-ap/httpsig v0.0.0-20210714162115-62a09257db51
|
||||||
github.com/woodpecker-ci/woodpecker v0.15.0
|
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
6
go.sum
@ -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=
|
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=
|
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=
|
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/errname v0.1.5/go.mod h1:DugbBstvPFQbv/5uLcRRzfrNqKE9tVdVCqWCLp6Cifo=
|
||||||
github.com/Antonboom/nilnil v0.1.0/go.mod h1:PhHLvRPSghY5Y7mX4TW+BHZQYo1A8flE5H20D3IPZBo=
|
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=
|
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/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-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/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-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 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=
|
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/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 h1:p3svFdtoXwmsd7w0/PJwyqfGejk2HaS8f6cC69xRxUE=
|
||||||
github.com/woodpecker-ci/woodpecker v0.15.0/go.mod h1:gWRKIzkowiQmy8WC/fp5Ktn3QHZc15GtneRuoalysxg=
|
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/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-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||||
|
34
main.go
34
main.go
@ -1,7 +1,9 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/ed25519"
|
||||||
_ "embed"
|
_ "embed"
|
||||||
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
@ -9,7 +11,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
"github.com/99designs/httpsignatures-go"
|
"github.com/go-ap/httpsig"
|
||||||
"github.com/joho/godotenv"
|
"github.com/joho/godotenv"
|
||||||
"github.com/woodpecker-ci/woodpecker/server/model"
|
"github.com/woodpecker-ci/woodpecker/server/model"
|
||||||
)
|
)
|
||||||
@ -36,14 +38,24 @@ func main() {
|
|||||||
log.Fatalf("Error loading .env file: %v", err)
|
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")
|
host := os.Getenv("CONFIG_SERVICE_HOST")
|
||||||
filterRegex := os.Getenv("CONFIG_SERVICE_OVERRIDE_FILTER")
|
filterRegex := os.Getenv("CONFIG_SERVICE_OVERRIDE_FILTER")
|
||||||
|
|
||||||
if secretToken == "" && host == "" {
|
if pubKeyPath == "" && host == "" {
|
||||||
log.Fatal("Please make sure CONFIG_SERVICE_HOST and CONFIG_SERVICE_SECRET are set properly")
|
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)
|
filter := regexp.MustCompile(filterRegex)
|
||||||
|
|
||||||
http.HandleFunc("/ciconfig", func(w http.ResponseWriter, r *http.Request) {
|
http.HandleFunc("/ciconfig", func(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -52,13 +64,23 @@ func main() {
|
|||||||
return
|
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 {
|
if err != nil {
|
||||||
log.Printf("config: invalid or missing signature in http.Request")
|
log.Printf("config: invalid or missing signature in http.Request")
|
||||||
http.Error(w, "Invalid or Missing Signature", http.StatusBadRequest)
|
http.Error(w, "Invalid or Missing Signature", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !signature.IsValid(secretToken, r) {
|
|
||||||
|
if keyID != pubKeyID {
|
||||||
log.Printf("config: invalid signature in http.Request")
|
log.Printf("config: invalid signature in http.Request")
|
||||||
http.Error(w, "Invalid Signature", http.StatusBadRequest)
|
http.Error(w, "Invalid Signature", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
|
Loading…
Reference in New Issue
Block a user