Migrate to maintained httpsign library (#3839)

Co-authored-by: 6543 <6543@obermui.de>
This commit is contained in:
qwerty287 2024-07-23 20:41:37 +02:00 committed by GitHub
parent 047eb19d42
commit b330c19bf4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 99 additions and 68 deletions

View file

@ -78,7 +78,7 @@
"homelab",
"HTMLURL",
"HTTPFS",
"httpsig",
"httpsign",
"HTTPURL",
"httputil",
"ianvs",

View file

@ -3,7 +3,7 @@
To provide additional management and preprocessing capabilities for pipeline configurations Woodpecker supports an HTTP API which can be enabled to call an external config service.
Before the run or restart of any pipeline Woodpecker will make a POST request to an external HTTP API sending the current repository, build information and all current config files retrieved from the repository. The external API can then send back new pipeline configurations that will be used immediately or respond with `HTTP 204` to tell the system to use the existing configuration.
Every request sent by Woodpecker is signed using a [http-signature](https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures) by a private key (ed25519) generated on the first start of the Woodpecker server. You can get the public key for the verification of the http-signature from `http(s)://your-woodpecker-server/api/signature/public-key`.
Every request sent by Woodpecker is signed using a [http-signature](https://datatracker.ietf.org/doc/html/rfc9421) by a private key (ed25519) generated on the first start of the Woodpecker server. You can get the public key for the verification of the http-signature from `http(s)://your-woodpecker-server/api/signature/public-key`.
A simplistic example configuration service can be found here: [https://github.com/woodpecker-ci/example-config-service](https://github.com/woodpecker-ci/example-config-service)

View file

@ -22,6 +22,7 @@ Some versions need some changes to the server configuration or the pipeline conf
- Deprecated slice definition for env vars
- Deprecated `environment` filter, use `when.evaluate`
- Deprecated `WOODPECKER_WEBHOOK_HOST` in favor of `WOODPECKER_EXPERT_WEBHOOK_HOST`
- Migrated to rfc9421 for webhook signatures
## 2.0.0

13
go.mod
View file

@ -29,7 +29,6 @@ require (
github.com/getkin/kin-openapi v0.126.0
github.com/gin-gonic/gin v1.10.0
github.com/gitsight/go-vcsurl v1.0.1
github.com/go-ap/httpsig v0.0.0-20221203064646-3647b4d88fdf
github.com/go-sql-driver/mysql v1.8.1
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/google/go-github/v63 v63.0.0
@ -60,6 +59,7 @@ require (
github.com/urfave/cli/v3 v3.0.0-alpha9.0.20240711030030-937cfe918cb1
github.com/xanzy/go-gitlab v0.107.0
github.com/xeipuuv/gojsonschema v1.2.0
github.com/yaronf/httpsign v0.3.1
github.com/zalando/go-keyring v0.2.5
go.uber.org/multierr v1.11.0
golang.org/x/crypto v0.25.0
@ -107,8 +107,10 @@ require (
github.com/danieljoos/wincred v1.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/davidmz/go-pageant v1.0.2 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker-credential-helpers v0.8.0 // indirect
github.com/dunglas/httpsfv v1.0.2 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
@ -124,7 +126,7 @@ require (
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.20.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/goccy/go-json v0.10.3 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
@ -145,6 +147,12 @@ require (
github.com/julienschmidt/httprouter v1.3.0 // indirect
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/lestrrat-go/blackmagic v1.0.2 // indirect
github.com/lestrrat-go/httpcc v1.0.1 // indirect
github.com/lestrrat-go/httprc v1.0.5 // indirect
github.com/lestrrat-go/iter v1.0.2 // indirect
github.com/lestrrat-go/jwx/v2 v2.1.0 // indirect
github.com/lestrrat-go/option v1.0.1 // indirect
github.com/libdns/libdns v0.2.2 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
@ -174,6 +182,7 @@ require (
github.com/prometheus/procfs v0.12.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/segmentio/asm v1.2.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/objx v0.5.2 // indirect

30
go.sum
View file

@ -30,6 +30,8 @@ github.com/adrg/xdg v0.5.0 h1:dDaZvhMXatArP1NPHhnfaQUqWBLBsmx1h1HXQdMoFCY=
github.com/adrg/xdg v0.5.0/go.mod h1:dDdY4M4DF9Rjy4kHPeNL+ilVF+p2lK8IdM9/rTSGcI4=
github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0=
github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
@ -99,6 +101,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davidmz/go-pageant v1.0.2 h1:bPblRCh5jGU+Uptpz6LgMZGD5hJoOt7otgT454WvHn0=
github.com/davidmz/go-pageant v1.0.2/go.mod h1:P2EDDnMqIwG5Rrp05dTRITj9z2zpGcD9efWSkTNKLIE=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
github.com/denisenkom/go-mssqldb v0.12.3 h1:pBSGx9Tq67pBOTLmxNuirNTeB8Vjmf886Kx+8Y+8shw=
github.com/denisenkom/go-mssqldb v0.12.3/go.mod h1:k0mtMFOnU+AihqFxPMiF05rtiDrorD1Vrm1KEz5hxDo=
github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0=
@ -118,6 +122,8 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/drone/envsubst v1.0.3 h1:PCIBwNDYjs50AsLZPYdfhSATKaRg/FJmDc2D6+C2x8g=
github.com/drone/envsubst v1.0.3/go.mod h1:N2jZmlMufstn1KEqvbHjw40h1KyTmnVzHcSc9bFiJ2g=
github.com/dunglas/httpsfv v1.0.2 h1:iERDp/YAfnojSDJ7PW3dj1AReJz4MrwbECSSE59JWL0=
github.com/dunglas/httpsfv v1.0.2/go.mod h1:zID2mqw9mFsnt7YC3vYQ9/cjq30q41W+1AnDwH8TiMg=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
@ -147,8 +153,6 @@ github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/gitsight/go-vcsurl v1.0.1 h1:wkijKsbVg9R2IBP97U7wOANeIW9WJJKkBwS9XqllzWo=
github.com/gitsight/go-vcsurl v1.0.1/go.mod h1:qRFdKDa/0Lh9MT0xE+qQBYZ/01+mY1H40rZUHR24X9U=
github.com/go-ap/httpsig v0.0.0-20221203064646-3647b4d88fdf h1:Ab5yBsD/dXhFmgf2hX7T/YYr+VK0Df7SrIxyNztT9YE=
github.com/go-ap/httpsig v0.0.0-20221203064646-3647b4d88fdf/go.mod h1:+4SUDMvPlRMUPW5PlMTbxj3U5a4fWasBIbakUw7Kp6c=
github.com/go-fed/httpsig v1.1.0 h1:9M+hb0jkEICD8/cAiNqEB66R87tTINszBRTjwjQzWcI=
github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
@ -180,8 +184,9 @@ github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/goccy/go-json v0.8.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
@ -324,6 +329,18 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k=
github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU=
github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E=
github.com/lestrrat-go/httprc v1.0.5 h1:bsTfiH8xaKOJPrg1R+E3iE/AWZr/x0Phj9PBTG/OLUk=
github.com/lestrrat-go/httprc v1.0.5/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo=
github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI=
github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4=
github.com/lestrrat-go/jwx/v2 v2.1.0 h1:0zs7Ya6+39qoit7gwAf+cYm1zzgS3fceIdo7RmQ5lkw=
github.com/lestrrat-go/jwx/v2 v2.1.0/go.mod h1:Xpw9QIaUGiIUD1Wx0NcY1sIHwFf8lDuZn/cmxtXYRys=
github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=
github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
@ -447,6 +464,10 @@ github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWR
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
@ -467,6 +488,7 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
@ -503,6 +525,8 @@ github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
github.com/yaronf/httpsign v0.3.1 h1:A4pqjwOVCwlExyy/mPTTryCHcXn+xhq4+wxq4BKhi2k=
github.com/yaronf/httpsign v0.3.1/go.mod h1:+7d6GccMcoljvE3QtU00NCmR1iTXCVNfbMe5nqaxRG4=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=

View file

@ -27,9 +27,9 @@ import (
"testing"
"time"
"github.com/go-ap/httpsig"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/yaronf/httpsign"
"go.woodpecker-ci.org/woodpecker/v2/server/forge/mocks"
forge_types "go.woodpecker-ci.org/woodpecker/v2/server/forge/types"
@ -120,25 +120,19 @@ func TestFetchFromConfigService(t *testing.T) {
fixtureHandler := func(w http.ResponseWriter, r *http.Request) {
// check signature
pubKeyID := "woodpecker-ci-plugins"
pubKeyID := "woodpecker-ci-extensions"
keystore := httpsig.NewMemoryKeyStore()
keystore.SetKey(pubKeyID, pubEd25519Key)
verifier, err := httpsign.NewEd25519Verifier(pubEd25519Key,
httpsign.NewVerifyConfig(),
httpsign.Headers("@request-target", "content-digest")) // The Content-Digest header will be auto-generated
assert.NoError(t, err)
verifier := httpsig.NewVerifier(keystore)
verifier.SetRequiredHeaders([]string{"(request-target)", "date"})
keyID, err := verifier.Verify(r)
err = httpsign.VerifyRequest(pubKeyID, *verifier, r)
if err != nil {
http.Error(w, "Invalid signature", http.StatusBadRequest)
return
}
if keyID != pubKeyID {
http.Error(w, "Used wrong key", http.StatusBadRequest)
return
}
type config struct {
Name string `json:"name"`
Data string `json:"data"`
@ -163,12 +157,12 @@ func TestFetchFromConfigService(t *testing.T) {
}
if req.Repo.Name == "Fetch empty" {
w.WriteHeader(404)
w.WriteHeader(http.StatusNotFound)
return
}
if req.Repo.Name == "Use old config" {
w.WriteHeader(204)
w.WriteHeader(http.StatusNoContent)
return
}
@ -192,7 +186,7 @@ func TestFetchFromConfigService(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(fixtureHandler))
defer ts.Close()
httpFetcher := config.NewHTTP(ts.URL, privEd25519Key)
httpFetcher := config.NewHTTP(ts.URL+"/", privEd25519Key)
for _, tt := range testTable {
t.Run(tt.name, func(t *testing.T) {

View file

@ -16,7 +16,7 @@ package config
import (
"context"
"crypto"
"crypto/ed25519"
"fmt"
net_http "net/http"
@ -28,7 +28,7 @@ import (
type http struct {
endpoint string
privateKey crypto.PrivateKey
privateKey ed25519.PrivateKey
}
// configData same as forge.FileMeta but with json tags and string data.
@ -48,7 +48,7 @@ type responseStructure struct {
Configs []*configData `json:"configs"`
}
func NewHTTP(endpoint string, privateKey crypto.PrivateKey) Service {
func NewHTTP(endpoint string, privateKey ed25519.PrivateKey) Service {
return &http{endpoint, privateKey}
}

View file

@ -57,7 +57,7 @@ func setupSecretService(store store.Store) secret.Service {
return secret.NewDB(store)
}
func setupConfigService(c *cli.Command, privateSignatureKey crypto.PrivateKey) (config.Service, error) {
func setupConfigService(c *cli.Command, privateSignatureKey ed25519.PrivateKey) (config.Service, error) {
timeout := c.Duration("forge-timeout")
retries := c.Uint("forge-retry")
if retries == 0 {
@ -74,7 +74,7 @@ func setupConfigService(c *cli.Command, privateSignatureKey crypto.PrivateKey) (
}
// setupSignatureKeys generate or load key pair to sign webhooks requests (i.e. used for service extensions).
func setupSignatureKeys(_store store.Store) (crypto.PrivateKey, crypto.PublicKey, error) {
func setupSignatureKeys(_store store.Store) (ed25519.PrivateKey, crypto.PublicKey, error) {
privKeyID := "signature-private-key"
privKey, err := _store.ServerConfigGet(privKeyID)

View file

@ -17,19 +17,19 @@ package utils
import (
"bytes"
"context"
"crypto"
"crypto/ed25519"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"github.com/go-ap/httpsig"
"github.com/yaronf/httpsign"
)
// Send makes an http request to the given endpoint, writing the input
// to the request body and un-marshaling the output from the response body.
func Send(ctx context.Context, method, path string, privateKey crypto.PrivateKey, in, out any) (int, error) {
func Send(ctx context.Context, method, path string, privateKey ed25519.PrivateKey, in, out any) (int, error) {
uri, err := url.Parse(path)
if err != nil {
return 0, err
@ -54,12 +54,12 @@ func Send(ctx context.Context, method, path string, privateKey crypto.PrivateKey
req.Header.Set("Content-Type", "application/json")
}
err = SignHTTPRequest(privateKey, req)
client, err := signClient(privateKey)
if err != nil {
return 0, err
}
resp, err := http.DefaultClient.Do(req)
resp, err := client.Do(req)
if err != nil {
return 0, err
}
@ -79,10 +79,14 @@ func Send(ctx context.Context, method, path string, privateKey crypto.PrivateKey
return resp.StatusCode, err
}
func SignHTTPRequest(privateKey crypto.PrivateKey, req *http.Request) error {
pubKeyID := "woodpecker-ci-plugins"
func signClient(privateKey ed25519.PrivateKey) (*httpsign.Client, error) {
pubKeyID := "woodpecker-ci-extensions"
signer := httpsig.NewEd25519Signer(pubKeyID, privateKey, nil)
return signer.Sign(req)
signer, err := httpsign.NewEd25519Signer(privateKey,
httpsign.NewSignConfig(),
httpsign.Headers("@request-target", "content-digest")) // The Content-Digest header will be auto-generated
if err != nil {
return nil, err
}
return httpsign.NewDefaultClient(httpsign.NewClientConfig().SetSignatureName(pubKeyID).SetSigner(signer)), nil // sign requests, don't verify responses
}

View file

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package utils_test
package utils
import (
"bytes"
@ -21,15 +21,14 @@ import (
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/go-ap/httpsig"
"github.com/stretchr/testify/assert"
"go.woodpecker-ci.org/woodpecker/v2/server/services/utils"
"github.com/yaronf/httpsign"
)
func TestSign(t *testing.T) {
pubKeyID := "woodpecker-ci-plugins"
func TestSignClient(t *testing.T) {
pubKeyID := "woodpecker-ci-extensions"
pubEd25519Key, privEd25519Key, err := ed25519.GenerateKey(rand.Reader)
if !assert.NoError(t, err) {
@ -38,36 +37,36 @@ func TestSign(t *testing.T) {
body := []byte("{\"foo\":\"bar\"}")
req, err := http.NewRequest(http.MethodGet, "http://example.com", bytes.NewBuffer(body))
if err != nil {
t.Fatal(err)
}
req.Header.Set("Content-Type", "application/json")
err = utils.SignHTTPRequest(privEd25519Key, req)
if err != nil {
t.Fatal(err)
}
verifyHandler := func(w http.ResponseWriter, r *http.Request) {
keystore := httpsig.NewMemoryKeyStore()
keystore.SetKey(pubKeyID, pubEd25519Key)
verifier := httpsig.NewVerifier(keystore)
verifier.SetRequiredHeaders([]string{"(request-target)", "date"})
keyID, err := verifier.Verify(r)
verifier, err := httpsign.NewEd25519Verifier(pubEd25519Key,
httpsign.NewVerifyConfig(),
httpsign.Headers("@request-target", "content-digest")) // The Content-Digest header will be auto-generated
assert.NoError(t, err)
err = httpsign.VerifyRequest(pubKeyID, *verifier, r)
assert.NoError(t, err)
assert.Equal(t, pubKeyID, keyID)
w.WriteHeader(http.StatusOK)
}
rr := httptest.NewRecorder()
handler := http.HandlerFunc(verifyHandler)
server := httptest.NewServer(http.HandlerFunc(verifyHandler))
handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusOK, rr.Code)
req, err := http.NewRequest("GET", server.URL+"/", bytes.NewBuffer(body))
if !assert.NoError(t, err) {
return
}
req.Header.Set("Date", time.Now().Format(time.RFC3339))
req.Header.Set("Content-Type", "application/json")
client, err := signClient(privEd25519Key)
if !assert.NoError(t, err) {
return
}
rr, err := client.Do(req)
assert.NoError(t, err)
defer rr.Body.Close()
assert.Equal(t, http.StatusOK, rr.StatusCode)
}