From b30d8394684a6bb73fdb6752b473d764afc4dc56 Mon Sep 17 00:00:00 2001 From: Florian Ritterhoff Date: Sat, 22 Oct 2022 07:24:44 +0200 Subject: [PATCH 01/20] fix: increase http timeout --- server/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/server.go b/server/server.go index e12c792c6..829b99337 100644 --- a/server/server.go +++ b/server/server.go @@ -42,7 +42,7 @@ func newHTTPServer(addr string, handler http.Handler, tlsConfig *tls.Config) *ht Addr: addr, Handler: handler, TLSConfig: tlsConfig, - WriteTimeout: 15 * time.Second, + WriteTimeout: 10 * time.Minute, ReadTimeout: 15 * time.Second, ReadHeaderTimeout: 15 * time.Second, IdleTimeout: 15 * time.Second, From 6fbbcb5a18e95ee9d5f59ff75d9750a9448b22ef Mon Sep 17 00:00:00 2001 From: Florian Ritterhoff Date: Sun, 2 Jul 2023 10:39:29 +0200 Subject: [PATCH 02/20] feat: add health endpoint --- api/api.go | 11 +++++++++-- api/api_test.go | 5 +++++ authority/authority.go | 5 +++++ db/db.go | 11 +++++++++++ db/simple.go | 5 +++++ go.mod | 2 ++ go.sum | 4 ++-- 7 files changed, 39 insertions(+), 4 deletions(-) diff --git a/api/api.go b/api/api.go index 7cf44a11c..a09422c2e 100644 --- a/api/api.go +++ b/api/api.go @@ -55,6 +55,7 @@ type Authority interface { GetFederation() ([]*x509.Certificate, error) Version() authority.Version GetCertificateRevocationList() ([]byte, error) + Health() error } // mustAuthority will be replaced on unit tests. @@ -360,8 +361,14 @@ func Version(w http.ResponseWriter, r *http.Request) { } // Health is an HTTP handler that returns the status of the server. -func Health(w http.ResponseWriter, _ *http.Request) { - render.JSON(w, HealthResponse{Status: "ok"}) +func Health(w http.ResponseWriter, r *http.Request) { + a := mustAuthority(r.Context()) + err := a.Health() + if err == nil { + render.JSON(w, HealthResponse{Status: "ok"}) + } else { + render.JSONStatus(w, HealthResponse{Status: "error"}, http.StatusServiceUnavailable) + } } // Root is an HTTP handler that using the SHA256 from the URL, returns the root diff --git a/api/api_test.go b/api/api_test.go index b3c018162..a4d25af8f 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -222,6 +222,10 @@ func (m *mockAuthority) GetCertificateRevocationList() ([]byte, error) { return m.ret1.([]byte), m.err } +func (m *mockAuthority) Health() error { + return nil +} + // TODO: remove once Authorize is deprecated. func (m *mockAuthority) Authorize(ctx context.Context, ott string) ([]provisioner.SignOption, error) { if m.authorize != nil { @@ -855,6 +859,7 @@ func Test_caHandler_Route(t *testing.T) { func Test_Health(t *testing.T) { req := httptest.NewRequest("GET", "http://example.com/health", http.NoBody) w := httptest.NewRecorder() + mockMustAuthority(t, &mockAuthority{}) Health(w, req) res := w.Result() diff --git a/authority/authority.go b/authority/authority.go index c112bc257..5fad2f87e 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -196,6 +196,11 @@ func NewEmbedded(opts ...Option) (*Authority, error) { return a, nil } +// Health checks if the authority is stil alive. +func (a *Authority) Health() error { + return a.db.Ping() +} + type authorityKey struct{} // NewContext adds the given authority to the context. diff --git a/db/db.go b/db/db.go index 394526725..2fb8fb744 100644 --- a/db/db.go +++ b/db/db.go @@ -60,6 +60,7 @@ type AuthDB interface { IsSSHHost(name string) (bool, error) GetSSHHostPrincipals() ([]string, error) Shutdown() error + Ping() error } type dbKey struct{} @@ -525,6 +526,11 @@ func (m *MockAuthDB) StoreCRL(info *CertificateRevocationListInfo) error { return m.Err } +// Ping mock +func (m *MockAuthDB) Ping() error { + return nil +} + // IsRevoked mock. func (m *MockAuthDB) IsRevoked(sn string) (bool, error) { if m.MIsRevoked != nil { @@ -643,6 +649,11 @@ type MockNoSQLDB struct { MCmpAndSwap func(bucket, key, old, newval []byte) ([]byte, bool, error) } +// Ping mock +func (m *MockNoSQLDB) Ping() error { + return nil +} + // CmpAndSwap mock func (m *MockNoSQLDB) CmpAndSwap(bucket, key, old, newval []byte) ([]byte, bool, error) { if m.MCmpAndSwap != nil { diff --git a/db/simple.go b/db/simple.go index dbef2d615..078802177 100644 --- a/db/simple.go +++ b/db/simple.go @@ -26,6 +26,11 @@ func newSimpleDB(*Config) (*SimpleDB, error) { return db, nil } +// Ping noop +func (s *SimpleDB) Ping() error { + return nil +} + // IsRevoked noop func (s *SimpleDB) IsRevoked(string) (bool, error) { return false, nil diff --git a/go.mod b/go.mod index 6d23bdac3..f460e37ed 100644 --- a/go.mod +++ b/go.mod @@ -166,3 +166,5 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +replace github.com/smallstep/nosql => github.com/hm-edu/nosql v0.4.1-0.20221021074654-ff0ebfc36cd6 diff --git a/go.sum b/go.sum index 7ba1523df..b3082a42c 100644 --- a/go.sum +++ b/go.sum @@ -261,6 +261,8 @@ github.com/hashicorp/vault/api/auth/approle v0.5.0 h1:a1TK6VGwYqSAfkmX4y4dJ4WBxM github.com/hashicorp/vault/api/auth/approle v0.5.0/go.mod h1:CHOQIA1AZACfjTzHggmyfiOZ+xCSKNRFqe48FTCzH0k= github.com/hashicorp/vault/api/auth/kubernetes v0.5.0 h1:CXO0fD7M3iCGovP/UApeHhPcH4paDFKcu7AjEXi94rI= github.com/hashicorp/vault/api/auth/kubernetes v0.5.0/go.mod h1:afrElBIO9Q4sHFVuVWgNevG4uAs1bT2AZFA9aEiI608= +github.com/hm-edu/nosql v0.4.1-0.20221021074654-ff0ebfc36cd6 h1:y2/TSGSQOdd1kK4NvTGeUmamcVbbWG/sLCTAKfpLeDM= +github.com/hm-edu/nosql v0.4.1-0.20221021074654-ff0ebfc36cd6/go.mod h1:6XhRTHHp0/maggMch4SF3vokbFzSnvp2VxBWNSyGN8A= github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= @@ -427,8 +429,6 @@ github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 h1:unQFBIznI+VYD1 github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262/go.mod h1:MyOHs9Po2fbM1LHej6sBUT8ozbxmMOFG+E+rx/GSGuc= github.com/smallstep/go-attestation v0.4.4-0.20240109183208-413678f90935 h1:kjYvkvS/Wdy0PVRDUAA0gGJIVSEZYhiAJtfwYgOYoGA= github.com/smallstep/go-attestation v0.4.4-0.20240109183208-413678f90935/go.mod h1:vNAduivU014fubg6ewygkAvQC0IQVXqdc8vaGl/0er4= -github.com/smallstep/nosql v0.6.0 h1:ur7ysI8s9st0cMXnTvB8tA3+x5Eifmkb6hl4uqNV5jc= -github.com/smallstep/nosql v0.6.0/go.mod h1:jOXwLtockXORUPPZ2MCUcIkGR6w0cN1QGZniY9DITQA= github.com/smallstep/pkcs7 v0.0.0-20231024181729-3b98ecc1ca81 h1:B6cED3iLJTgxpdh4tuqByDjRRKan2EvtnOfHr2zHJVg= github.com/smallstep/pkcs7 v0.0.0-20231024181729-3b98ecc1ca81/go.mod h1:SoUAr/4M46rZ3WaLstHxGhLEgoYIDRqxQEXLOmOEB0Y= github.com/smallstep/scep v0.0.0-20231024192529-aee96d7ad34d h1:06LUHn4Ia2X6syjIaCMNaXXDNdU+1N/oOHynJbWgpXw= From 184d5f368e0587d9297b64ace1e5f49fa3e8d506 Mon Sep 17 00:00:00 2001 From: Florian Ritterhoff Date: Sat, 22 Oct 2022 07:59:22 +0200 Subject: [PATCH 03/20] fix/extend eab functionality --- acme/api/account.go | 2 +- acme/api/eab.go | 2 +- acme/db/nosql/eab.go | 61 +++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 59 insertions(+), 6 deletions(-) diff --git a/acme/api/account.go b/acme/api/account.go index 25d923c77..cc4381afe 100644 --- a/acme/api/account.go +++ b/acme/api/account.go @@ -148,7 +148,7 @@ func NewAccount(w http.ResponseWriter, r *http.Request) { render.Error(w, err) return } - if err := db.UpdateExternalAccountKey(ctx, prov.ID, eak); err != nil { + if err := db.UpdateExternalAccountKey(ctx, prov.GetID(), eak); err != nil { render.Error(w, acme.WrapErrorISE(err, "error updating external account binding key")) return } diff --git a/acme/api/eab.go b/acme/api/eab.go index 26854595b..81af66060 100644 --- a/acme/api/eab.go +++ b/acme/api/eab.go @@ -51,7 +51,7 @@ func validateExternalAccountBinding(ctx context.Context, nar *NewAccountRequest) } db := acme.MustDatabaseFromContext(ctx) - externalAccountKey, err := db.GetExternalAccountKey(ctx, acmeProv.ID, keyID) + externalAccountKey, err := db.GetExternalAccountKey(ctx, acmeProv.GetID(), keyID) if err != nil { var ae *acme.Error if errors.As(err, &ae) { diff --git a/acme/db/nosql/eab.go b/acme/db/nosql/eab.go index e2a437ddf..1b58907e5 100644 --- a/acme/db/nosql/eab.go +++ b/acme/db/nosql/eab.go @@ -4,9 +4,12 @@ import ( "context" "crypto/rand" "encoding/json" + "fmt" "sync" "time" + "github.com/sirupsen/logrus" + "github.com/pkg/errors" "github.com/smallstep/certificates/acme" @@ -229,9 +232,52 @@ func (db *DB) GetExternalAccountKeyByReference(ctx context.Context, provisionerI return db.GetExternalAccountKey(ctx, provisionerID, dbExternalAccountKeyReference.ExternalAccountKeyID) } -func (db *DB) GetExternalAccountKeyByAccountID(context.Context, string, string) (*acme.ExternalAccountKey, error) { - //nolint:nilnil // legacy - return nil, nil +func (db *DB) GetExternalAccountKeyByAccountID(ctx context.Context, provisionerID, accountID string) (*acme.ExternalAccountKey, error) { + externalAccountKeyMutex.RLock() + defer externalAccountKeyMutex.RUnlock() + + logrus.Debug(fmt.Sprintf("searching for eak keys bount to provisioner %v", provisionerID)) + // cursor and limit are ignored in open source, at least for now. + + var eakIDs []string + r, err := db.db.Get(externalAccountKeyIDsByProvisionerIDTable, []byte(provisionerID)) + if err != nil { + if !nosqlDB.IsErrNotFound(err) { + return nil, errors.Wrapf(err, "error loading ACME EAB Key IDs for provisioner %s", provisionerID) + } + // it may happen that no record is found; we'll continue with an empty slice + } else { + if err := json.Unmarshal(r, &eakIDs); err != nil { + return nil, errors.Wrapf(err, "error unmarshaling ACME EAB Key IDs for provisioner %s", provisionerID) + } + } + logrus.Debug(fmt.Sprintf("found %v eak keys (%v)", len(eakIDs), eakIDs)) + for _, eakID := range eakIDs { + if eakID == "" { + continue // shouldn't happen; just in case + } + eak, err := db.getDBExternalAccountKey(ctx, eakID) + if err != nil { + if !nosqlDB.IsErrNotFound(err) { + return nil, errors.Wrapf(err, "error retrieving ACME EAB Key for provisioner %s and keyID %s", provisionerID, eakID) + } + } + logrus.Debug(fmt.Sprintf("loaded %v", eak)) + if eak.AccountID == accountID { + return &acme.ExternalAccountKey{ + ID: eak.ID, + HmacKey: eak.HmacKey, + ProvisionerID: eak.ProvisionerID, + Reference: eak.Reference, + AccountID: eak.AccountID, + CreatedAt: eak.CreatedAt, + BoundAt: eak.BoundAt, + }, nil + } + logrus.Debug(fmt.Sprintf("%s does not match %s", eak.AccountID, accountID)) + } + + return nil, errors.Errorf("ACME EAB Key for account id %s not found", accountID) } func (db *DB) UpdateExternalAccountKey(ctx context.Context, provisionerID string, eak *acme.ExternalAccountKey) error { @@ -313,7 +359,14 @@ func (db *DB) addEAKID(ctx context.Context, provisionerID, eakID string) error { } if err = db.save(ctx, provisionerID, _new, _old, "externalAccountKeyIDsByProvisionerID", externalAccountKeyIDsByProvisionerIDTable); err != nil { - return errors.Wrapf(err, "error saving eakIDs index for provisioner %s", provisionerID) + if len(eakIDs) == 0 { + logrus.Warnf("error replacing empty eakID list for provisioner %s", provisionerID) + if err_internal := db.save(ctx, provisionerID, _new, []string{}, "externalAccountKeyIDsByProvisionerID", externalAccountKeyIDsByProvisionerIDTable); err_internal != nil { + return errors.Wrapf(err, "error saving eakIDs index for provisioner %s", provisionerID) + } + } else { + return errors.Wrapf(err, "error saving eakIDs index for provisioner %s", provisionerID) + } } return nil From f8c1d0da2dc1f4ca201d7fb148ab7a012d96e2ba Mon Sep 17 00:00:00 2001 From: Florian Ritterhoff Date: Fri, 14 Apr 2023 10:33:07 +0200 Subject: [PATCH 04/20] Adaptions for MUAS - Add Connection to EAB Management Service - Split Endpoints - Passthrough Context for Actions - Add Sectigo/PKI Client - Add Jaeger Telemetry - Fix minor issues handling EAB --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index f460e37ed..81e81c950 100644 --- a/go.mod +++ b/go.mod @@ -167,4 +167,4 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect ) -replace github.com/smallstep/nosql => github.com/hm-edu/nosql v0.4.1-0.20221021074654-ff0ebfc36cd6 +replace github.com/smallstep/nosql => github.com/hm-edu/nosql v0.4.1-0.20230305071512-139857866201 diff --git a/go.sum b/go.sum index b3082a42c..7067a44ae 100644 --- a/go.sum +++ b/go.sum @@ -261,8 +261,8 @@ github.com/hashicorp/vault/api/auth/approle v0.5.0 h1:a1TK6VGwYqSAfkmX4y4dJ4WBxM github.com/hashicorp/vault/api/auth/approle v0.5.0/go.mod h1:CHOQIA1AZACfjTzHggmyfiOZ+xCSKNRFqe48FTCzH0k= github.com/hashicorp/vault/api/auth/kubernetes v0.5.0 h1:CXO0fD7M3iCGovP/UApeHhPcH4paDFKcu7AjEXi94rI= github.com/hashicorp/vault/api/auth/kubernetes v0.5.0/go.mod h1:afrElBIO9Q4sHFVuVWgNevG4uAs1bT2AZFA9aEiI608= -github.com/hm-edu/nosql v0.4.1-0.20221021074654-ff0ebfc36cd6 h1:y2/TSGSQOdd1kK4NvTGeUmamcVbbWG/sLCTAKfpLeDM= -github.com/hm-edu/nosql v0.4.1-0.20221021074654-ff0ebfc36cd6/go.mod h1:6XhRTHHp0/maggMch4SF3vokbFzSnvp2VxBWNSyGN8A= +github.com/hm-edu/nosql v0.4.1-0.20230305071512-139857866201 h1:KB8SVIw1MA30wUUXYziiTErSw487ahokcesqzgPlK/o= +github.com/hm-edu/nosql v0.4.1-0.20230305071512-139857866201/go.mod h1:jOXwLtockXORUPPZ2MCUcIkGR6w0cN1QGZniY9DITQA= github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= From 052202362d157a2626abd2f2e135dc28fa7c8c76 Mon Sep 17 00:00:00 2001 From: Florian Ritterhoff Date: Sat, 22 Oct 2022 08:30:13 +0200 Subject: [PATCH 05/20] Adaptions for MUAS - Add Connection to EAB Management Service - Split Endpoints - Passthrough Context for Actions - Add Sectigo/PKI Client - Add Jaeger Telemetry - Fix minor issues handling EAB --- .devcontainer/.env | 4 + .devcontainer/Dockerfile | 19 ++ .devcontainer/devcontainer.json | 36 ++++ .devcontainer/docker-compose.yml | 53 ++++++ .github/pull.yml | 7 + .github/workflows/code-scan-cron.yml | 7 - .github/workflows/dependabot-auto-merge.yml | 22 --- .github/workflows/docker.yml | 50 ++++++ .vscode/launch.json | 15 ++ acme/api/account_test.go | 30 ++-- acme/api/handler.go | 28 ++- acme/api/handler_test.go | 50 ++++-- acme/api/middleware.go | 8 +- acme/api/order.go | 22 ++- acme/api/order_test.go | 115 +++++++----- acme/api/revoke_test.go | 22 +-- acme/common.go | 2 +- acme/order.go | 104 ++++++----- acme/order_test.go | 107 +++++++---- acme/status.go | 1 + api/api.go | 6 +- api/api_test.go | 6 +- api/rekey.go | 3 +- api/renew.go | 3 +- api/sign.go | 4 +- api/ssh.go | 2 +- api/sshRenew.go | 2 +- authority/authority_test.go | 7 +- authority/authorize_test.go | 2 +- authority/config/config.go | 3 + authority/provisioners_test.go | 4 +- authority/tls.go | 72 +++++--- authority/tls_test.go | 14 +- ca/bootstrap_test.go | 2 +- ca/ca.go | 115 +++++++++++- ca/tls_test.go | 2 +- cas/apiv1/options_test.go | 6 +- cas/apiv1/services.go | 9 +- cas/cas_test.go | 6 +- cas/cloudcas/cloudcas.go | 6 +- cas/cloudcas/cloudcas_test.go | 6 +- cas/sectigocas/eab/client.go | 50 ++++++ cas/sectigocas/sectigocas.go | 187 ++++++++++++++++++++ cas/softcas/softcas.go | 6 +- cas/softcas/softcas_test.go | 10 +- cas/stepcas/stepcas.go | 6 +- cas/stepcas/stepcas_test.go | 6 +- cas/vaultcas/vaultcas.go | 6 +- cas/vaultcas/vaultcas_test.go | 6 +- cmd/step-ca/main.go | 1 + go.mod | 18 +- go.sum | 12 +- logging/logger.go | 1 + scep/authority.go | 4 +- 54 files changed, 1003 insertions(+), 292 deletions(-) create mode 100644 .devcontainer/.env create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json create mode 100644 .devcontainer/docker-compose.yml create mode 100644 .github/pull.yml delete mode 100644 .github/workflows/code-scan-cron.yml delete mode 100644 .github/workflows/dependabot-auto-merge.yml create mode 100644 .github/workflows/docker.yml create mode 100644 .vscode/launch.json create mode 100644 cas/sectigocas/eab/client.go create mode 100644 cas/sectigocas/sectigocas.go diff --git a/.devcontainer/.env b/.devcontainer/.env new file mode 100644 index 000000000..15181a657 --- /dev/null +++ b/.devcontainer/.env @@ -0,0 +1,4 @@ +POSTGRES_USER=postgres +POSTGRES_PASSWORD=postgres +POSTGRES_DB=postgres +POSTGRES_HOSTNAME=localhost diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 000000000..4feec8622 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,19 @@ +# [Choice] Go version (use -bullseye variants on local arm64/Apple Silicon): 1, 1.18, 1.17, 1-bullseye, 1.18-bullseye, 1.17-bullseye, 1-buster, 1.18-buster, 1.17-buster +ARG VARIANT=1-bullseye +FROM mcr.microsoft.com/vscode/devcontainers/go:0-${VARIANT} + +# [Choice] Node.js version: none, lts/*, 16, 14, 12, 10 +ARG NODE_VERSION="none" +RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi + +# [Optional] Uncomment this section to install additional OS packages. +# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ +# && apt-get -y install --no-install-recommends + +# [Optional] Uncomment the next lines to use go get to install anything else you need +# USER vscode +# RUN go get -x +# USER root + +# [Optional] Uncomment this line to install global node packages. +# RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g " 2>&1 diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000..04480f801 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,36 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: +// https://github.com/microsoft/vscode-dev-containers/tree/v0.238.0/containers/go-postgres +{ + "name": "Go & PostgreSQL", + "dockerComposeFile": "docker-compose.yml", + "service": "app", + "workspaceFolder": "/workspace", + + // Configure tool-specific properties. + "customizations": { + // Configure properties specific to VS Code. + "vscode": { + // Set *default* container specific settings.json values on container create. + "settings": { + "go.toolsManagement.checkForUpdates": "local", + "go.useLanguageServer": true, + "go.gopath": "/go", + "go.goroot": "/usr/local/go" + }, + + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "golang.Go" + ] + } + }, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [5432], + + // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "go version", + + // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. + "remoteUser": "vscode" +} diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml new file mode 100644 index 000000000..d299ccd67 --- /dev/null +++ b/.devcontainer/docker-compose.yml @@ -0,0 +1,53 @@ +version: '3.8' + +volumes: + postgres-data: + + null +services: + app: + build: + context: . + dockerfile: Dockerfile + args: + # [Choice] Go version 1, 1.18, 1.17 + # Append -bullseye or -buster to pin to an OS version. + # Use -bullseye variants on local arm64/Apple Silicon. + VARIANT: 1.18-bullseye + # Options + NODE_VERSION: "lts/*" + env_file: + # Ensure that the variables in .env match the same variables in devcontainer.json + - .env + + # Security Opt and cap_add allow for C++ based debuggers to work. + # See `runArgs`: https://github.com/Microsoft/vscode-docs/blob/main/docs/remote/devcontainerjson-reference.md + # security_opt: + # - seccomp:unconfined + # cap_add: + # - SYS_PTRACE + + volumes: + - ..:/workspace:cached + + # Overrides default command so things don't shut down after the process ends. + command: sleep infinity + + # Runs app on the same network as the database container, allows "forwardPorts" in devcontainer.json function. + network_mode: service:db + # Uncomment the next line to use a non-root user for all processes. + # user: vscode + + # Use "forwardPorts" in **devcontainer.json** to forward an app port locally. + # (Adding the "ports" property to this file will not forward from a Codespace.) + + db: + image: postgres:latest + restart: unless-stopped + volumes: + - postgres-data:/var/lib/postgresql/data + env_file: + # Ensure that the variables in .env match the same variables in devcontainer.json + - .env + # Add "forwardPorts": ["5432"] to **devcontainer.json** to forward PostgreSQL locally. + # (Adding the "ports" property to this file will not forward from a Codespace.) diff --git a/.github/pull.yml b/.github/pull.yml new file mode 100644 index 000000000..cd633663e --- /dev/null +++ b/.github/pull.yml @@ -0,0 +1,7 @@ +version: "1" +rules: + - base: master + upstream: smallstep:master + mergeMethod: merge + assignees: + - fritterhoff diff --git a/.github/workflows/code-scan-cron.yml b/.github/workflows/code-scan-cron.yml deleted file mode 100644 index 9a35b7fe6..000000000 --- a/.github/workflows/code-scan-cron.yml +++ /dev/null @@ -1,7 +0,0 @@ -on: - schedule: - - cron: '0 0 * * *' - -jobs: - code-scan: - uses: smallstep/workflows/.github/workflows/code-scan.yml@main diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml deleted file mode 100644 index 8ca265e0f..000000000 --- a/.github/workflows/dependabot-auto-merge.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Dependabot auto-merge -on: pull_request - -permissions: - contents: write - pull-requests: write - -jobs: - dependabot: - runs-on: ubuntu-latest - if: ${{ github.actor == 'dependabot[bot]' }} - steps: - - name: Dependabot metadata - id: metadata - uses: dependabot/fetch-metadata@v1.6.0 - with: - github-token: "${{ secrets.GITHUB_TOKEN }}" - - name: Enable auto-merge for Dependabot PRs - run: gh pr merge --auto --merge "$PR_URL" - env: - PR_URL: ${{github.event.pull_request.html_url}} - GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 000000000..4aee10c1f --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,50 @@ +name: Docker Images + +on: + push: + pull_request: + branches: + - 'main' + +jobs: + build: + runs-on: ubuntu-latest + env: + # Use docker.io for Docker Hub if empty + REGISTRY: ghcr.io + # github.repository as / + IMAGE_NAME: ${{ github.repository }} + permissions: + contents: read + packages: write + steps: + - uses: actions/checkout@v2 + - name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@v1 + - name: Log into registry ${{ env.REGISTRY }} + if: github.event_name != 'pull_request' + uses: docker/login-action@v1 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@v3 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=schedule + type=ref,event=branch + type=ref,event=tag + type=ref,event=pr + type=raw,value={{branch}}-{{sha}}-{{date 'X'}},enable=${{ github.event_name != 'pull_request' }} + - name: Build and push + uses: docker/build-push-action@v2 + with: + context: . + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + file: docker/Dockerfile \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..c6db162df --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Package", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${workspaceFolder}/cmd/step-ca", + } + ] +} \ No newline at end of file diff --git a/acme/api/account_test.go b/acme/api/account_test.go index 7d799c883..8febbf5d4 100644 --- a/acme/api/account_test.go +++ b/acme/api/account_test.go @@ -314,14 +314,14 @@ func TestHandler_GetOrdersByAccountID(t *testing.T) { "fail/nil-account": func(t *testing.T) test { return test{ db: &acme.MockDB{}, - ctx: context.WithValue(context.Background(), accContextKey, http.NoBody), + ctx: context.WithValue(context.Background(), AccContextKey, http.NoBody), statusCode: 400, err: acme.NewError(acme.ErrorAccountDoesNotExistType, "account does not exist"), } }, "fail/account-id-mismatch": func(t *testing.T) test { acc := &acme.Account{ID: "foo"} - ctx := context.WithValue(context.Background(), accContextKey, acc) + ctx := context.WithValue(context.Background(), AccContextKey, acc) ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx) return test{ db: &acme.MockDB{}, @@ -332,7 +332,7 @@ func TestHandler_GetOrdersByAccountID(t *testing.T) { }, "fail/db.GetOrdersByAccountID-error": func(t *testing.T) test { acc := &acme.Account{ID: accID} - ctx := context.WithValue(context.Background(), accContextKey, acc) + ctx := context.WithValue(context.Background(), AccContextKey, acc) ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx) return test{ db: &acme.MockDB{ @@ -347,7 +347,7 @@ func TestHandler_GetOrdersByAccountID(t *testing.T) { acc := &acme.Account{ID: accID} ctx := context.WithValue(context.Background(), chi.RouteCtxKey, chiCtx) ctx = acme.NewProvisionerContext(ctx, prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) return test{ db: &acme.MockDB{ MockGetOrdersByAccountID: func(ctx context.Context, id string) ([]string, error) { @@ -682,7 +682,7 @@ func TestHandler_NewAccount(t *testing.T) { } ctx := acme.NewProvisionerContext(context.Background(), prov) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: b}) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) return test{ db: &acme.MockDB{}, ctx: ctx, @@ -863,7 +863,7 @@ func TestHandler_GetOrUpdateAccount(t *testing.T) { } }, "fail/nil-account": func(t *testing.T) test { - ctx := context.WithValue(context.Background(), accContextKey, nil) + ctx := context.WithValue(context.Background(), AccContextKey, nil) return test{ db: &acme.MockDB{}, ctx: ctx, @@ -872,7 +872,7 @@ func TestHandler_GetOrUpdateAccount(t *testing.T) { } }, "fail/no-payload": func(t *testing.T) test { - ctx := context.WithValue(context.Background(), accContextKey, &acc) + ctx := context.WithValue(context.Background(), AccContextKey, &acc) return test{ db: &acme.MockDB{}, ctx: ctx, @@ -881,7 +881,7 @@ func TestHandler_GetOrUpdateAccount(t *testing.T) { } }, "fail/nil-payload": func(t *testing.T) test { - ctx := context.WithValue(context.Background(), accContextKey, &acc) + ctx := context.WithValue(context.Background(), AccContextKey, &acc) ctx = context.WithValue(ctx, payloadContextKey, nil) return test{ db: &acme.MockDB{}, @@ -891,7 +891,7 @@ func TestHandler_GetOrUpdateAccount(t *testing.T) { } }, "fail/unmarshal-payload-error": func(t *testing.T) test { - ctx := context.WithValue(context.Background(), accContextKey, &acc) + ctx := context.WithValue(context.Background(), AccContextKey, &acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{}) return test{ db: &acme.MockDB{}, @@ -906,7 +906,7 @@ func TestHandler_GetOrUpdateAccount(t *testing.T) { } b, err := json.Marshal(uar) assert.FatalError(t, err) - ctx := context.WithValue(context.Background(), accContextKey, &acc) + ctx := context.WithValue(context.Background(), AccContextKey, &acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: b}) return test{ db: &acme.MockDB{}, @@ -921,7 +921,7 @@ func TestHandler_GetOrUpdateAccount(t *testing.T) { } b, err := json.Marshal(uar) assert.FatalError(t, err) - ctx := context.WithValue(context.Background(), accContextKey, &acc) + ctx := context.WithValue(context.Background(), AccContextKey, &acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: b}) return test{ db: &acme.MockDB{ @@ -943,7 +943,7 @@ func TestHandler_GetOrUpdateAccount(t *testing.T) { b, err := json.Marshal(uar) assert.FatalError(t, err) ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, &acc) + ctx = context.WithValue(ctx, AccContextKey, &acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: b}) return test{ db: &acme.MockDB{ @@ -962,7 +962,7 @@ func TestHandler_GetOrUpdateAccount(t *testing.T) { b, err := json.Marshal(uar) assert.FatalError(t, err) ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, &acc) + ctx = context.WithValue(ctx, AccContextKey, &acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: b}) return test{ db: &acme.MockDB{}, @@ -977,7 +977,7 @@ func TestHandler_GetOrUpdateAccount(t *testing.T) { b, err := json.Marshal(uar) assert.FatalError(t, err) ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, &acc) + ctx = context.WithValue(ctx, AccContextKey, &acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: b}) return test{ db: &acme.MockDB{ @@ -993,7 +993,7 @@ func TestHandler_GetOrUpdateAccount(t *testing.T) { }, "ok/post-as-get": func(t *testing.T) test { ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, &acc) + ctx = context.WithValue(ctx, AccContextKey, &acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{isPostAsGet: true}) return test{ db: &acme.MockDB{}, diff --git a/acme/api/handler.go b/acme/api/handler.go index d2940f49d..c32fd88c7 100644 --- a/acme/api/handler.go +++ b/acme/api/handler.go @@ -5,16 +5,22 @@ import ( "crypto/x509" "encoding/json" "encoding/pem" + "errors" "fmt" "net/http" "time" "github.com/go-chi/chi/v5" + "github.com/sirupsen/logrus" + + pb "github.com/hm-edu/portal-apis" + "github.com/smallstep/certificates/cas/sectigocas/eab" "github.com/smallstep/certificates/acme" "github.com/smallstep/certificates/api" "github.com/smallstep/certificates/api/render" "github.com/smallstep/certificates/authority" + "github.com/smallstep/certificates/authority/config" "github.com/smallstep/certificates/authority/provisioner" ) @@ -68,6 +74,7 @@ type HandlerOptions struct { // PrerequisitesChecker checks if all prerequisites for serving ACME are // met by the CA configuration. PrerequisitesChecker func(ctx context.Context) (bool, error) + Cfg *config.Config } var mustAuthority = func(ctx context.Context) acme.CertificateAuthority { @@ -102,7 +109,6 @@ func (h *handler) Route(r api.Router) { } // NewHandler returns a new ACME API handler. -// // Note: this method is deprecated in step-ca, other applications can still use // this to support ACME, but the recommendation is to use use // api.Route(api.Router) and acme.NewContext() instead. @@ -397,3 +403,23 @@ func GetCertificate(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/pem-certificate-chain") w.Write(certBytes) } + +func checkPermission(ctx context.Context, identifiers []acme.Identifier, eak *acme.ExternalAccountKey) ([]string, error) { + if eak == nil { + logrus.Warn("No external account key given. Cannot check permissions") + return nil, nil + } + var domains []string + for _, x := range identifiers { + domains = append(domains, x.Value) + } + client, ok := eab.FromContext(ctx) + if !ok { + return nil, errors.New("no external account client available") + } + result, err := client.CheckEABPermissions(ctx, &pb.CheckEABPermissionRequest{Domains: domains, EabKey: eak.ID}) + if err != nil { + return nil, err + } + return result.Missing, nil +} diff --git a/acme/api/handler_test.go b/acme/api/handler_test.go index bd7bb50e3..7fb2d2c3b 100644 --- a/acme/api/handler_test.go +++ b/acme/api/handler_test.go @@ -17,6 +17,7 @@ import ( "github.com/go-chi/chi/v5" "github.com/google/go-cmp/cmp" + pb "github.com/hm-edu/portal-apis" "github.com/pkg/errors" "go.step.sm/crypto/jose" @@ -25,8 +26,21 @@ import ( "github.com/smallstep/assert" "github.com/smallstep/certificates/acme" "github.com/smallstep/certificates/authority/provisioner" + "google.golang.org/grpc" ) +type MockClient struct { + Missing []string +} + +func (c *MockClient) CheckEABPermissions(ctx context.Context, in *pb.CheckEABPermissionRequest, opts ...grpc.CallOption) (*pb.CheckEABPermissionResponse, error) { + return &pb.CheckEABPermissionResponse{Missing: c.Missing}, nil +} + +func (c *MockClient) ResolveAccountId(ctx context.Context, in *pb.ResolveAccountIdRequest, opts ...grpc.CallOption) (*pb.ResolveAccountIdResponse, error) { //nolint + return nil, nil +} + type mockClient struct { get func(url string) (*http.Response, error) lookupTxt func(name string) ([]string, error) @@ -263,7 +277,7 @@ func TestHandler_GetAuthorization(t *testing.T) { }, "fail/nil-account": func(t *testing.T) test { ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, nil) + ctx = context.WithValue(ctx, AccContextKey, nil) return test{ db: &acme.MockDB{}, ctx: ctx, @@ -273,7 +287,7 @@ func TestHandler_GetAuthorization(t *testing.T) { }, "fail/db.GetAuthorization-error": func(t *testing.T) test { acc := &acme.Account{ID: "accID"} - ctx := context.WithValue(context.Background(), accContextKey, acc) + ctx := context.WithValue(context.Background(), AccContextKey, acc) ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx) return test{ db: &acme.MockDB{ @@ -286,7 +300,7 @@ func TestHandler_GetAuthorization(t *testing.T) { }, "fail/account-id-mismatch": func(t *testing.T) test { acc := &acme.Account{ID: "accID"} - ctx := context.WithValue(context.Background(), accContextKey, acc) + ctx := context.WithValue(context.Background(), AccContextKey, acc) ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx) return test{ db: &acme.MockDB{ @@ -304,7 +318,7 @@ func TestHandler_GetAuthorization(t *testing.T) { }, "fail/db.UpdateAuthorization-error": func(t *testing.T) test { acc := &acme.Account{ID: "accID"} - ctx := context.WithValue(context.Background(), accContextKey, acc) + ctx := context.WithValue(context.Background(), AccContextKey, acc) ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx) return test{ db: &acme.MockDB{ @@ -329,7 +343,7 @@ func TestHandler_GetAuthorization(t *testing.T) { "ok": func(t *testing.T) test { acc := &acme.Account{ID: "accID"} ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx) return test{ db: &acme.MockDB{ @@ -426,7 +440,7 @@ func TestHandler_GetCertificate(t *testing.T) { } }, "fail/nil-account": func(t *testing.T) test { - ctx := context.WithValue(context.Background(), accContextKey, nil) + ctx := context.WithValue(context.Background(), AccContextKey, nil) return test{ db: &acme.MockDB{}, ctx: ctx, @@ -436,7 +450,7 @@ func TestHandler_GetCertificate(t *testing.T) { }, "fail/db.GetCertificate-error": func(t *testing.T) test { acc := &acme.Account{ID: "accID"} - ctx := context.WithValue(context.Background(), accContextKey, acc) + ctx := context.WithValue(context.Background(), AccContextKey, acc) ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx) return test{ db: &acme.MockDB{ @@ -449,7 +463,7 @@ func TestHandler_GetCertificate(t *testing.T) { }, "fail/account-id-mismatch": func(t *testing.T) test { acc := &acme.Account{ID: "accID"} - ctx := context.WithValue(context.Background(), accContextKey, acc) + ctx := context.WithValue(context.Background(), AccContextKey, acc) ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx) return test{ db: &acme.MockDB{ @@ -465,7 +479,7 @@ func TestHandler_GetCertificate(t *testing.T) { }, "ok": func(t *testing.T) test { acc := &acme.Account{ID: "accID"} - ctx := context.WithValue(context.Background(), accContextKey, acc) + ctx := context.WithValue(context.Background(), AccContextKey, acc) ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx) return test{ db: &acme.MockDB{ @@ -549,14 +563,14 @@ func TestHandler_GetChallenge(t *testing.T) { "fail/nil-account": func(t *testing.T) test { return test{ db: &acme.MockDB{}, - ctx: context.WithValue(context.Background(), accContextKey, nil), + ctx: context.WithValue(context.Background(), AccContextKey, nil), statusCode: 400, err: acme.NewError(acme.ErrorAccountDoesNotExistType, "account does not exist"), } }, "fail/no-payload": func(t *testing.T) test { acc := &acme.Account{ID: "accID"} - ctx := context.WithValue(context.Background(), accContextKey, acc) + ctx := context.WithValue(context.Background(), AccContextKey, acc) return test{ db: &acme.MockDB{}, ctx: ctx, @@ -567,7 +581,7 @@ func TestHandler_GetChallenge(t *testing.T) { "fail/nil-payload": func(t *testing.T) test { acc := &acme.Account{ID: "accID"} ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, payloadContextKey, nil) return test{ db: &acme.MockDB{}, @@ -579,7 +593,7 @@ func TestHandler_GetChallenge(t *testing.T) { "fail/db.GetChallenge-error": func(t *testing.T) test { acc := &acme.Account{ID: "accID"} ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{isEmptyJSON: true}) ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx) return test{ @@ -598,7 +612,7 @@ func TestHandler_GetChallenge(t *testing.T) { "fail/account-id-mismatch": func(t *testing.T) test { acc := &acme.Account{ID: "accID"} ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{isEmptyJSON: true}) ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx) return test{ @@ -617,7 +631,7 @@ func TestHandler_GetChallenge(t *testing.T) { "fail/no-jwk": func(t *testing.T) test { acc := &acme.Account{ID: "accID"} ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{isEmptyJSON: true}) ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx) return test{ @@ -636,7 +650,7 @@ func TestHandler_GetChallenge(t *testing.T) { "fail/nil-jwk": func(t *testing.T) test { acc := &acme.Account{ID: "accID"} ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{isEmptyJSON: true}) ctx = context.WithValue(ctx, jwkContextKey, nil) ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx) @@ -656,7 +670,7 @@ func TestHandler_GetChallenge(t *testing.T) { "fail/validate-challenge-error": func(t *testing.T) test { acc := &acme.Account{ID: "accID"} ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{isEmptyJSON: true}) _jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) assert.FatalError(t, err) @@ -696,7 +710,7 @@ func TestHandler_GetChallenge(t *testing.T) { "ok": func(t *testing.T) test { acc := &acme.Account{ID: "accID"} ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{isEmptyJSON: true}) _jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) assert.FatalError(t, err) diff --git a/acme/api/middleware.go b/acme/api/middleware.go index c3e1458e2..82fdb500a 100644 --- a/acme/api/middleware.go +++ b/acme/api/middleware.go @@ -258,7 +258,7 @@ func extractJWK(next nextHTTP) nextHTTP { render.Error(w, acme.NewError(acme.ErrorUnauthorizedType, "account is not active")) return } - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) } next(w, r.WithContext(ctx)) } @@ -359,7 +359,7 @@ func lookupJWK(next nextHTTP) nextHTTP { return } } - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, jwkContextKey, acc.Key) next(w, r.WithContext(ctx)) return @@ -566,7 +566,7 @@ type ContextKey string const ( // accContextKey account key - accContextKey = ContextKey("acc") + AccContextKey = ContextKey("acc") // jwsContextKey jws key jwsContextKey = ContextKey("jws") // jwkContextKey jwk key @@ -578,7 +578,7 @@ const ( // accountFromContext searches the context for an ACME account. Returns the // account or an error. func accountFromContext(ctx context.Context) (*acme.Account, error) { - val, ok := ctx.Value(accContextKey).(*acme.Account) + val, ok := ctx.Value(AccContextKey).(*acme.Account) if !ok || val == nil { return nil, acme.NewError(acme.ErrorAccountDoesNotExistType, "account not in context") } diff --git a/acme/api/order.go b/acme/api/order.go index b207f87ce..43b3354ce 100644 --- a/acme/api/order.go +++ b/acme/api/order.go @@ -180,6 +180,15 @@ func NewOrder(w http.ResponseWriter, r *http.Request) { NotAfter: nor.NotAfter, } + if missing, err := checkPermission(ctx, o.Identifiers, eak); len(missing) != 0 || err != nil { + if err != nil { + render.Error(w, acme.NewError(acme.ErrorServerInternalType, "Internal server error")) + return + } + render.Error(w, acme.NewError(acme.ErrorRejectedIdentifierType, "Missing registration for domain(s) %v", missing)) + return + } + for i, identifier := range o.Identifiers { az := &acme.Authorization{ AccountID: acc.ID, @@ -320,6 +329,15 @@ func GetOrder(w http.ResponseWriter, r *http.Request) { linker.LinkOrder(ctx, o) w.Header().Set("Location", linker.GetLink(ctx, acme.OrderLinkType, o.ID)) + if o.Status == acme.StatusProcessing { + // Due to the bad behavior of the k8s cert-manager we must catch this client using the User-Agent and handle it in a special way. + // The cert-manager does not repect the retry after flags and will retry too fast. + if strings.Contains(r.UserAgent(), "cert-manager") { + render.Error(w, acme.NewErrorISE("Request is processing")) + return + } + w.Header().Set("Retry-After", "10") + } render.JSON(w, o) } @@ -372,14 +390,14 @@ func FinalizeOrder(w http.ResponseWriter, r *http.Request) { } ca := mustAuthority(ctx) - if err = o.Finalize(ctx, db, fr.csr, ca, prov); err != nil { + if _, err = o.Finalize(ctx, db, fr.csr, ca, prov); err != nil { render.Error(w, acme.WrapErrorISE(err, "error finalizing order")) return } linker.LinkOrder(ctx, o) - w.Header().Set("Location", linker.GetLink(ctx, acme.OrderLinkType, o.ID)) + w.Header().Set("Retry-After", "20") render.JSON(w, o) } diff --git a/acme/api/order_test.go b/acme/api/order_test.go index 36de975a3..55de4b79b 100644 --- a/acme/api/order_test.go +++ b/acme/api/order_test.go @@ -7,6 +7,7 @@ import ( "encoding/base64" "encoding/json" "fmt" + "github.com/smallstep/certificates/cas/sectigocas/eab" "io" "net/http" "net/http/httptest" @@ -328,7 +329,7 @@ func TestHandler_GetOrder(t *testing.T) { }, "fail/nil-account": func(t *testing.T) test { ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, nil) + ctx = context.WithValue(ctx, AccContextKey, nil) return test{ db: &acme.MockDB{}, ctx: ctx, @@ -338,7 +339,7 @@ func TestHandler_GetOrder(t *testing.T) { }, "fail/no-provisioner": func(t *testing.T) test { acc := &acme.Account{ID: "accountID"} - ctx := context.WithValue(context.Background(), accContextKey, acc) + ctx := context.WithValue(context.Background(), AccContextKey, acc) return test{ db: &acme.MockDB{}, ctx: ctx, @@ -349,7 +350,7 @@ func TestHandler_GetOrder(t *testing.T) { "fail/nil-provisioner": func(t *testing.T) test { acc := &acme.Account{ID: "accountID"} ctx := acme.NewProvisionerContext(context.Background(), nil) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) return test{ db: &acme.MockDB{}, ctx: ctx, @@ -360,7 +361,7 @@ func TestHandler_GetOrder(t *testing.T) { "fail/db.GetOrder-error": func(t *testing.T) test { acc := &acme.Account{ID: "accountID"} ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx) return test{ db: &acme.MockDB{ @@ -374,7 +375,7 @@ func TestHandler_GetOrder(t *testing.T) { "fail/account-id-mismatch": func(t *testing.T) test { acc := &acme.Account{ID: "accountID"} ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx) return test{ db: &acme.MockDB{ @@ -390,7 +391,7 @@ func TestHandler_GetOrder(t *testing.T) { "fail/provisioner-id-mismatch": func(t *testing.T) test { acc := &acme.Account{ID: "accountID"} ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx) return test{ db: &acme.MockDB{ @@ -406,7 +407,7 @@ func TestHandler_GetOrder(t *testing.T) { "fail/order-update-error": func(t *testing.T) test { acc := &acme.Account{ID: "accountID"} ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx) return test{ db: &acme.MockDB{ @@ -430,7 +431,7 @@ func TestHandler_GetOrder(t *testing.T) { "ok": func(t *testing.T) test { acc := &acme.Account{ID: "accountID"} ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx) return test{ db: &acme.MockDB{ @@ -799,6 +800,7 @@ func TestHandler_NewOrder(t *testing.T) { ctx context.Context nor *NewOrderRequest statusCode int + missing []string vr func(t *testing.T, o *acme.Order) err *acme.Error } @@ -813,7 +815,7 @@ func TestHandler_NewOrder(t *testing.T) { }, "fail/nil-account": func(t *testing.T) test { ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, nil) + ctx = context.WithValue(ctx, AccContextKey, nil) return test{ db: &acme.MockDB{}, ctx: ctx, @@ -823,7 +825,7 @@ func TestHandler_NewOrder(t *testing.T) { }, "fail/no-provisioner": func(t *testing.T) test { acc := &acme.Account{ID: "accountID"} - ctx := context.WithValue(context.Background(), accContextKey, acc) + ctx := context.WithValue(context.Background(), AccContextKey, acc) return test{ db: &acme.MockDB{}, ctx: ctx, @@ -834,7 +836,7 @@ func TestHandler_NewOrder(t *testing.T) { "fail/nil-provisioner": func(t *testing.T) test { acc := &acme.Account{ID: "accountID"} ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) return test{ db: &acme.MockDB{}, ctx: ctx, @@ -844,7 +846,7 @@ func TestHandler_NewOrder(t *testing.T) { }, "fail/no-payload": func(t *testing.T) test { acc := &acme.Account{ID: "accountID"} - ctx := context.WithValue(context.Background(), accContextKey, acc) + ctx := context.WithValue(context.Background(), AccContextKey, acc) ctx = acme.NewProvisionerContext(ctx, prov) return test{ db: &acme.MockDB{}, @@ -856,7 +858,7 @@ func TestHandler_NewOrder(t *testing.T) { "fail/nil-payload": func(t *testing.T) test { acc := &acme.Account{ID: "accountID"} ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, payloadContextKey, nil) return test{ db: &acme.MockDB{}, @@ -868,7 +870,7 @@ func TestHandler_NewOrder(t *testing.T) { "fail/unmarshal-payload-error": func(t *testing.T) test { acc := &acme.Account{ID: "accID"} ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{}) return test{ db: &acme.MockDB{}, @@ -883,7 +885,7 @@ func TestHandler_NewOrder(t *testing.T) { b, err := json.Marshal(fr) assert.FatalError(t, err) ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: b}) return test{ db: &acme.MockDB{}, @@ -902,7 +904,7 @@ func TestHandler_NewOrder(t *testing.T) { b, err := json.Marshal(fr) assert.FatalError(t, err) ctx := acme.NewProvisionerContext(context.Background(), &acme.MockProvisioner{}) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: b}) return test{ ctx: ctx, @@ -930,7 +932,7 @@ func TestHandler_NewOrder(t *testing.T) { b, err := json.Marshal(fr) assert.FatalError(t, err) ctx := acme.NewProvisionerContext(context.Background(), acmeProv) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: b}) return test{ ctx: ctx, @@ -958,7 +960,7 @@ func TestHandler_NewOrder(t *testing.T) { b, err := json.Marshal(fr) assert.FatalError(t, err) ctx := acme.NewProvisionerContext(context.Background(), acmeProv) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: b}) return test{ ctx: ctx, @@ -994,7 +996,7 @@ func TestHandler_NewOrder(t *testing.T) { b, err := json.Marshal(fr) assert.FatalError(t, err) ctx := acme.NewProvisionerContext(context.Background(), acmeProv) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: b}) return test{ ctx: ctx, @@ -1037,7 +1039,7 @@ func TestHandler_NewOrder(t *testing.T) { b, err := json.Marshal(fr) assert.FatalError(t, err) ctx := acme.NewProvisionerContext(context.Background(), provWithPolicy) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: b}) return test{ ctx: ctx, @@ -1080,7 +1082,7 @@ func TestHandler_NewOrder(t *testing.T) { b, err := json.Marshal(fr) assert.FatalError(t, err) ctx := acme.NewProvisionerContext(context.Background(), provWithPolicy) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: b}) return test{ ctx: ctx, @@ -1118,7 +1120,7 @@ func TestHandler_NewOrder(t *testing.T) { b, err := json.Marshal(fr) assert.FatalError(t, err) ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: b}) return test{ ctx: ctx, @@ -1152,7 +1154,7 @@ func TestHandler_NewOrder(t *testing.T) { b, err := json.Marshal(fr) assert.FatalError(t, err) ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: b}) var ( ch1, ch2, ch3 **acme.Challenge @@ -1217,6 +1219,36 @@ func TestHandler_NewOrder(t *testing.T) { err: acme.NewErrorISE("error creating order: force"), } }, + "fail/missingDomain": func(t *testing.T) test { + acc := &acme.Account{ID: "accID"} + nor := &NewOrderRequest{ + Identifiers: []acme.Identifier{ + {Type: "dns", Value: "zap.internal"}, + {Type: "dns", Value: "*.zar.internal"}, + }, + } + b, err := json.Marshal(nor) + assert.FatalError(t, err) + p := newACMEProv(t) + p.RequireEAB = true + ctx := acme.NewProvisionerContext(context.Background(), p) + ctx = context.WithValue(ctx, AccContextKey, acc) + ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: b}) + return test{ + ctx: ctx, + statusCode: 400, + ca: &mockCA{}, + missing: []string{"zap.internal"}, + db: &acme.MockDB{MockGetExternalAccountKeyByAccountID: func(ctx context.Context, provisionerID, accountID string) (*acme.ExternalAccountKey, error) { + assert.Equals(t, prov.GetID(), provisionerID) + assert.Equals(t, "accID", accountID) + return &acme.ExternalAccountKey{ + ID: "test", + }, nil + }}, + err: acme.NewError(acme.ErrorRejectedIdentifierType, "account does not exist"), + } + }, "ok/multiple-authz": func(t *testing.T) test { acc := &acme.Account{ID: "accID"} nor := &NewOrderRequest{ @@ -1228,7 +1260,7 @@ func TestHandler_NewOrder(t *testing.T) { b, err := json.Marshal(nor) assert.FatalError(t, err) ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: b}) var ( ch1, ch2, ch3, ch4 **acme.Challenge @@ -1348,7 +1380,7 @@ func TestHandler_NewOrder(t *testing.T) { b, err := json.Marshal(nor) assert.FatalError(t, err) ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: b}) var ( ch1, ch2, ch3 **acme.Challenge @@ -1445,7 +1477,7 @@ func TestHandler_NewOrder(t *testing.T) { b, err := json.Marshal(nor) assert.FatalError(t, err) ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: b}) var ( ch1, ch2, ch3 **acme.Challenge @@ -1541,7 +1573,7 @@ func TestHandler_NewOrder(t *testing.T) { b, err := json.Marshal(nor) assert.FatalError(t, err) ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: b}) var ( ch1, ch2, ch3 **acme.Challenge @@ -1638,7 +1670,7 @@ func TestHandler_NewOrder(t *testing.T) { b, err := json.Marshal(nor) assert.FatalError(t, err) ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: b}) var ( ch1, ch2, ch3 **acme.Challenge @@ -1738,7 +1770,7 @@ func TestHandler_NewOrder(t *testing.T) { b, err := json.Marshal(nor) assert.FatalError(t, err) ctx := acme.NewProvisionerContext(context.Background(), provWithPolicy) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: b}) var ( ch1, ch2, ch3 **acme.Challenge @@ -1828,6 +1860,7 @@ func TestHandler_NewOrder(t *testing.T) { t.Run(name, func(t *testing.T) { mockMustAuthority(t, tc.ca) ctx := newBaseContext(tc.ctx, tc.db, acme.NewLinker("test.ca.smallstep.com", "acme")) + ctx = eab.NewContext(ctx, &MockClient{Missing: tc.missing}) req := httptest.NewRequest("GET", u, http.NoBody) req = req.WithContext(ctx) w := httptest.NewRecorder() @@ -1930,7 +1963,7 @@ func TestHandler_FinalizeOrder(t *testing.T) { }, "fail/nil-account": func(t *testing.T) test { ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, nil) + ctx = context.WithValue(ctx, AccContextKey, nil) return test{ db: &acme.MockDB{}, ctx: ctx, @@ -1940,7 +1973,7 @@ func TestHandler_FinalizeOrder(t *testing.T) { }, "fail/no-provisioner": func(t *testing.T) test { acc := &acme.Account{ID: "accountID"} - ctx := context.WithValue(context.Background(), accContextKey, acc) + ctx := context.WithValue(context.Background(), AccContextKey, acc) return test{ db: &acme.MockDB{}, ctx: ctx, @@ -1951,7 +1984,7 @@ func TestHandler_FinalizeOrder(t *testing.T) { "fail/nil-provisioner": func(t *testing.T) test { acc := &acme.Account{ID: "accountID"} ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) return test{ db: &acme.MockDB{}, ctx: ctx, @@ -1961,7 +1994,7 @@ func TestHandler_FinalizeOrder(t *testing.T) { }, "fail/no-payload": func(t *testing.T) test { acc := &acme.Account{ID: "accountID"} - ctx := context.WithValue(context.Background(), accContextKey, acc) + ctx := context.WithValue(context.Background(), AccContextKey, acc) ctx = acme.NewProvisionerContext(ctx, prov) return test{ db: &acme.MockDB{}, @@ -1973,7 +2006,7 @@ func TestHandler_FinalizeOrder(t *testing.T) { "fail/nil-payload": func(t *testing.T) test { acc := &acme.Account{ID: "accountID"} ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, payloadContextKey, nil) return test{ db: &acme.MockDB{}, @@ -1985,7 +2018,7 @@ func TestHandler_FinalizeOrder(t *testing.T) { "fail/unmarshal-payload-error": func(t *testing.T) test { acc := &acme.Account{ID: "accID"} ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{}) return test{ db: &acme.MockDB{}, @@ -2000,7 +2033,7 @@ func TestHandler_FinalizeOrder(t *testing.T) { b, err := json.Marshal(fr) assert.FatalError(t, err) ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: b}) return test{ db: &acme.MockDB{}, @@ -2013,7 +2046,7 @@ func TestHandler_FinalizeOrder(t *testing.T) { acc := &acme.Account{ID: "accountID"} ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: payloadBytes}) ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx) return test{ @@ -2028,7 +2061,7 @@ func TestHandler_FinalizeOrder(t *testing.T) { "fail/account-id-mismatch": func(t *testing.T) test { acc := &acme.Account{ID: "accountID"} ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: payloadBytes}) ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx) return test{ @@ -2045,7 +2078,7 @@ func TestHandler_FinalizeOrder(t *testing.T) { "fail/provisioner-id-mismatch": func(t *testing.T) test { acc := &acme.Account{ID: "accountID"} ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: payloadBytes}) ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx) return test{ @@ -2062,7 +2095,7 @@ func TestHandler_FinalizeOrder(t *testing.T) { "fail/order-finalize-error": func(t *testing.T) test { acc := &acme.Account{ID: "accountID"} ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: payloadBytes}) ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx) return test{ @@ -2087,7 +2120,7 @@ func TestHandler_FinalizeOrder(t *testing.T) { "ok": func(t *testing.T) test { acc := &acme.Account{ID: "accountID"} ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: payloadBytes}) ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx) return test{ diff --git a/acme/api/revoke_test.go b/acme/api/revoke_test.go index b01aff573..c8da0270a 100644 --- a/acme/api/revoke_test.go +++ b/acme/api/revoke_test.go @@ -281,7 +281,7 @@ type mockCA struct { MockAreSANsallowed func(ctx context.Context, sans []string) error } -func (m *mockCA) Sign(*x509.CertificateRequest, provisioner.SignOptions, ...provisioner.SignOption) ([]*x509.Certificate, error) { +func (m *mockCA) Sign(ctx context.Context, cr *x509.CertificateRequest, opts provisioner.SignOptions, signOpts ...provisioner.SignOption) ([]*x509.Certificate, error) { return nil, nil } @@ -688,7 +688,7 @@ func TestHandler_RevokeCert(t *testing.T) { ctx := acme.NewProvisionerContext(context.Background(), prov) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: payloadBytes}) ctx = context.WithValue(ctx, jwsContextKey, jws) - ctx = context.WithValue(ctx, accContextKey, nil) + ctx = context.WithValue(ctx, AccContextKey, nil) db := &acme.MockDB{ MockGetCertificateBySerial: func(ctx context.Context, serial string) (*acme.Certificate, error) { assert.Equals(t, cert.SerialNumber.String(), serial) @@ -707,7 +707,7 @@ func TestHandler_RevokeCert(t *testing.T) { "fail/account-not-valid": func(t *testing.T) test { acc := &acme.Account{ID: "accountID", Status: acme.StatusInvalid} ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: payloadBytes}) ctx = context.WithValue(ctx, jwsContextKey, jws) ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx) @@ -736,7 +736,7 @@ func TestHandler_RevokeCert(t *testing.T) { "fail/account-not-authorized": func(t *testing.T) test { acc := &acme.Account{ID: "accountID", Status: acme.StatusValid} ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: payloadBytes}) ctx = context.WithValue(ctx, jwsContextKey, jws) ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx) @@ -815,7 +815,7 @@ func TestHandler_RevokeCert(t *testing.T) { "fail/certificate-revoked-check-fails": func(t *testing.T) test { acc := &acme.Account{ID: "accountID", Status: acme.StatusValid} ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: payloadBytes}) ctx = context.WithValue(ctx, jwsContextKey, jws) ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx) @@ -848,7 +848,7 @@ func TestHandler_RevokeCert(t *testing.T) { "fail/certificate-already-revoked": func(t *testing.T) test { acc := &acme.Account{ID: "accountID", Status: acme.StatusValid} ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: payloadBytes}) ctx = context.WithValue(ctx, jwsContextKey, jws) db := &acme.MockDB{ @@ -886,7 +886,7 @@ func TestHandler_RevokeCert(t *testing.T) { assert.FatalError(t, err) acc := &acme.Account{ID: "accountID", Status: acme.StatusValid} ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: invalidReasonCodePayloadBytes}) ctx = context.WithValue(ctx, jwsContextKey, jws) db := &acme.MockDB{ @@ -924,7 +924,7 @@ func TestHandler_RevokeCert(t *testing.T) { } acc := &acme.Account{ID: "accountID", Status: acme.StatusValid} ctx := acme.NewProvisionerContext(context.Background(), mockACMEProv) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: payloadBytes}) ctx = context.WithValue(ctx, jwsContextKey, jws) db := &acme.MockDB{ @@ -956,7 +956,7 @@ func TestHandler_RevokeCert(t *testing.T) { "fail/ca.Revoke": func(t *testing.T) test { acc := &acme.Account{ID: "accountID", Status: acme.StatusValid} ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: payloadBytes}) ctx = context.WithValue(ctx, jwsContextKey, jws) db := &acme.MockDB{ @@ -988,7 +988,7 @@ func TestHandler_RevokeCert(t *testing.T) { "fail/ca.Revoke-already-revoked": func(t *testing.T) test { acc := &acme.Account{ID: "accountID", Status: acme.StatusValid} ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: payloadBytes}) ctx = context.WithValue(ctx, jwsContextKey, jws) db := &acme.MockDB{ @@ -1019,7 +1019,7 @@ func TestHandler_RevokeCert(t *testing.T) { "ok/using-account-key": func(t *testing.T) test { acc := &acme.Account{ID: "accountID", Status: acme.StatusValid} ctx := acme.NewProvisionerContext(context.Background(), prov) - ctx = context.WithValue(ctx, accContextKey, acc) + ctx = context.WithValue(ctx, AccContextKey, acc) ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: payloadBytes}) ctx = context.WithValue(ctx, jwsContextKey, jws) ctx = context.WithValue(ctx, chi.RouteCtxKey, chiCtx) diff --git a/acme/common.go b/acme/common.go index 7cce25fd1..8854ea221 100644 --- a/acme/common.go +++ b/acme/common.go @@ -21,7 +21,7 @@ var clock Clock // CertificateAuthority is the interface implemented by a CA authority. type CertificateAuthority interface { - Sign(cr *x509.CertificateRequest, opts provisioner.SignOptions, signOpts ...provisioner.SignOption) ([]*x509.Certificate, error) + Sign(ctx context.Context, cr *x509.CertificateRequest, opts provisioner.SignOptions, signOpts ...provisioner.SignOption) ([]*x509.Certificate, error) AreSANsAllowed(ctx context.Context, sans []string) error IsRevoked(sn string) (bool, error) Revoke(context.Context, *authority.RevokeOptions) error diff --git a/acme/order.go b/acme/order.go index 8dfcf97a6..271bc3694 100644 --- a/acme/order.go +++ b/acme/order.go @@ -11,6 +11,7 @@ import ( "strings" "time" + "github.com/sirupsen/logrus" "github.com/smallstep/certificates/authority/provisioner" "go.step.sm/crypto/keyutil" "go.step.sm/crypto/x509util" @@ -67,6 +68,8 @@ func (o *Order) UpdateStatus(ctx context.Context, db DB) error { now := clock.Now() switch o.Status { + case StatusProcessing: + return nil case StatusInvalid: return nil case StatusValid: @@ -150,27 +153,29 @@ func (o *Order) getAuthorizationFingerprint(ctx context.Context, db DB) (string, // Finalize signs a certificate if the necessary conditions for Order completion // have been met. -// -// TODO(mariano): Here or in the challenge validation we should perform some -// external validation using the identifier value and the attestation data. From -// a validation service we can get the list of SANs to set in the final -// certificate. -func (o *Order) Finalize(ctx context.Context, db DB, csr *x509.CertificateRequest, auth CertificateAuthority, p Provisioner) error { +func (o *Order) Finalize(ctx context.Context, db DB, csr *x509.CertificateRequest, auth CertificateAuthority, p Provisioner) (chan error, error) { if err := o.UpdateStatus(ctx, db); err != nil { - return err + return nil, err } switch o.Status { case StatusInvalid: - return NewError(ErrorOrderNotReadyType, "order %s has been abandoned", o.ID) + return nil, NewError(ErrorOrderNotReadyType, "order %s has been abandoned", o.ID) case StatusValid: - return nil + return nil, nil case StatusPending: - return NewError(ErrorOrderNotReadyType, "order %s is not ready", o.ID) + return nil, NewError(ErrorOrderNotReadyType, "order %s is not ready", o.ID) case StatusReady: break + case StatusProcessing: + return nil, NewErrorISE("order %s is already processing", o.ID) default: - return NewErrorISE("unexpected status %s for order %s", o.Status, o.ID) + return nil, NewErrorISE("unexpected status %s for order %s", o.Status, o.ID) + } + + o.Status = StatusProcessing + if err := db.UpdateOrder(ctx, o); err != nil { + return nil, WrapErrorISE(err, "error updating order %s", o.ID) } // Get key fingerprint if any. And then compare it with the CSR fingerprint. @@ -179,15 +184,15 @@ func (o *Order) Finalize(ctx context.Context, db DB, csr *x509.CertificateReques // and the attestation certificate are the same. fingerprint, err := o.getAuthorizationFingerprint(ctx, db) if err != nil { - return err + return nil, err } if fingerprint != "" { fp, err := keyutil.Fingerprint(csr.PublicKey) if err != nil { - return WrapErrorISE(err, "error calculating key fingerprint") + return nil, WrapErrorISE(err, "error calculating key fingerprint") } if subtle.ConstantTimeCompare([]byte(fingerprint), []byte(fp)) == 0 { - return NewError(ErrorUnauthorizedType, "order %s csr does not match the attested key", o.ID) + return nil, NewError(ErrorUnauthorizedType, "order %s csr does not match the attested key", o.ID) } } @@ -212,7 +217,7 @@ func (o *Order) Finalize(ctx context.Context, db DB, csr *x509.CertificateReques // could result in unauthorized access if a relying system relies on the Common // Name in its authorization logic. if csr.Subject.CommonName != "" && csr.Subject.CommonName != permanentIdentifier { - return NewError(ErrorBadCSRType, "CSR Subject Common Name does not match identifiers exactly: "+ + return nil, NewError(ErrorBadCSRType, "CSR Subject Common Name does not match identifiers exactly: "+ "CSR Subject Common Name = %s, Order Permanent Identifier = %s", csr.Subject.CommonName, permanentIdentifier) } break @@ -233,7 +238,7 @@ func (o *Order) Finalize(ctx context.Context, db DB, csr *x509.CertificateReques defaultTemplate = x509util.DefaultLeafTemplate sans, err := o.sans(csr) if err != nil { - return err + return nil, err } data.SetSubjectAlternativeNames(sans...) } @@ -242,7 +247,7 @@ func (o *Order) Finalize(ctx context.Context, db DB, csr *x509.CertificateReques ctx = provisioner.NewContextWithMethod(ctx, provisioner.SignMethod) signOps, err := p.AuthorizeSign(ctx, "") if err != nil { - return WrapErrorISE(err, "error retrieving authorization options from ACME provisioner") + return nil, WrapErrorISE(err, "error retrieving authorization options from ACME provisioner") } // Unlike most of the provisioners, ACME's AuthorizeSign method doesn't // define the templates, and the template data used in WebHooks is not @@ -255,38 +260,53 @@ func (o *Order) Finalize(ctx context.Context, db DB, csr *x509.CertificateReques templateOptions, err := provisioner.CustomTemplateOptions(p.GetOptions(), data, defaultTemplate) if err != nil { - return WrapErrorISE(err, "error creating template options from ACME provisioner") + return nil, WrapErrorISE(err, "error creating template options from ACME provisioner") } // Build extra signing options. signOps = append(signOps, templateOptions) signOps = append(signOps, extraOptions...) + ch := make(chan error) + go func() { + // Sign a new certificate. + certChain, err := auth.Sign(ctx, csr, provisioner.SignOptions{ + NotBefore: provisioner.NewTimeDuration(o.NotBefore), + NotAfter: provisioner.NewTimeDuration(o.NotAfter), + }, signOps...) + if err != nil { + logrus.WithError(err).Error("error signing certificate") + o.Status = StatusInvalid + ch <- WrapErrorISE(err, "error signing certificate for order %s", o.ID) + if err = db.UpdateOrder(ctx, o); err != nil { + logrus.WithError(err).Error("error updating order") + } + return + } - // Sign a new certificate. - certChain, err := auth.Sign(csr, provisioner.SignOptions{ - NotBefore: provisioner.NewTimeDuration(o.NotBefore), - NotAfter: provisioner.NewTimeDuration(o.NotAfter), - }, signOps...) - if err != nil { - return WrapErrorISE(err, "error signing certificate for order %s", o.ID) - } - - cert := &Certificate{ - AccountID: o.AccountID, - OrderID: o.ID, - Leaf: certChain[0], - Intermediates: certChain[1:], - } - if err := db.CreateCertificate(ctx, cert); err != nil { - return WrapErrorISE(err, "error creating certificate for order %s", o.ID) - } + cert := &Certificate{ + AccountID: o.AccountID, + OrderID: o.ID, + Leaf: certChain[0], + Intermediates: certChain[1:], + } + if err := db.CreateCertificate(ctx, cert); err != nil { + logrus.WithError(err).Error("error creating certificate") + o.Status = StatusInvalid + ch <- WrapErrorISE(err, "error creating certificate for order %s", o.ID) + if err = db.UpdateOrder(ctx, o); err != nil { + logrus.WithError(err).Error("error updating order") + } + return + } - o.CertificateID = cert.ID - o.Status = StatusValid - if err = db.UpdateOrder(ctx, o); err != nil { - return WrapErrorISE(err, "error updating order %s", o.ID) - } - return nil + o.CertificateID = cert.ID + o.Status = StatusValid + if err = db.UpdateOrder(ctx, o); err != nil { + logrus.WithError(err).Error("error updating order") + ch <- WrapErrorISE(err, "error updating order %s", o.ID) + } + }() + return ch, nil } func (o *Order) sans(csr *x509.CertificateRequest) ([]x509util.SubjectAlternativeName, error) { diff --git a/acme/order_test.go b/acme/order_test.go index 2851bb190..69edcb0ef 100644 --- a/acme/order_test.go +++ b/acme/order_test.go @@ -271,16 +271,16 @@ func TestOrder_UpdateStatus(t *testing.T) { } type mockSignAuth struct { - sign func(csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) + sign func(ctx context.Context, csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) areSANsAllowed func(ctx context.Context, sans []string) error loadProvisionerByName func(string) (provisioner.Interface, error) ret1, ret2 interface{} err error } -func (m *mockSignAuth) Sign(csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) { +func (m *mockSignAuth) Sign(ctx context.Context, csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) { if m.sign != nil { - return m.sign(csr, signOpts, extraOpts...) + return m.sign(ctx, csr, signOpts, extraOpts...) } else if m.err != nil { return nil, m.err } @@ -497,7 +497,16 @@ func TestOrder_Finalize(t *testing.T) { MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) { return &Authorization{ID: id, Status: StatusValid}, nil }, - }, + MockUpdateOrder: func(ctx context.Context, updo *Order) error { + assert.Equals(t, updo.CertificateID, "") + assert.Equals(t, updo.Status, StatusProcessing) + assert.Equals(t, updo.ID, o.ID) + assert.Equals(t, updo.AccountID, o.AccountID) + assert.Equals(t, updo.ExpiresAt, o.ExpiresAt) + assert.Equals(t, updo.AuthorizationIDs, o.AuthorizationIDs) + assert.Equals(t, updo.Identifiers, o.Identifiers) + return nil + }}, err: NewErrorISE("error retrieving authorization options from ACME provisioner: force"), } }, @@ -541,6 +550,16 @@ func TestOrder_Finalize(t *testing.T) { MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) { return &Authorization{ID: id, Status: StatusValid}, nil }, + MockUpdateOrder: func(ctx context.Context, updo *Order) error { + assert.Equals(t, updo.CertificateID, "") + assert.Equals(t, updo.Status, StatusProcessing) + assert.Equals(t, updo.ID, o.ID) + assert.Equals(t, updo.AccountID, o.AccountID) + assert.Equals(t, updo.ExpiresAt, o.ExpiresAt) + assert.Equals(t, updo.AuthorizationIDs, o.AuthorizationIDs) + assert.Equals(t, updo.Identifiers, o.Identifiers) + return nil + }, }, err: NewErrorISE("error creating template options from ACME provisioner: error unmarshaling template data: invalid character 'o' in literal false (expecting 'a')"), } @@ -578,7 +597,7 @@ func TestOrder_Finalize(t *testing.T) { }, }, ca: &mockSignAuth{ - sign: func(_csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) { + sign: func(ctx context.Context, _csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) { assert.Equals(t, _csr, csr) return nil, errors.New("force") }, @@ -587,7 +606,15 @@ func TestOrder_Finalize(t *testing.T) { MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) { return &Authorization{ID: id, Status: StatusValid}, nil }, - }, + MockUpdateOrder: func(ctx context.Context, updo *Order) error { + assert.Equals(t, updo.CertificateID, "") + assert.Equals(t, updo.ID, o.ID) + assert.Equals(t, updo.AccountID, o.AccountID) + assert.Equals(t, updo.ExpiresAt, o.ExpiresAt) + assert.Equals(t, updo.AuthorizationIDs, o.AuthorizationIDs) + assert.Equals(t, updo.Identifiers, o.Identifiers) + return nil + }}, err: NewErrorISE("error signing certificate for order oID: force"), } }, @@ -628,7 +655,7 @@ func TestOrder_Finalize(t *testing.T) { }, }, ca: &mockSignAuth{ - sign: func(_csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) { + sign: func(ctx context.Context, _csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) { assert.Equals(t, _csr, csr) return []*x509.Certificate{foo, bar, baz}, nil }, @@ -644,6 +671,14 @@ func TestOrder_Finalize(t *testing.T) { assert.Equals(t, cert.Intermediates, []*x509.Certificate{bar, baz}) return errors.New("force") }, + MockUpdateOrder: func(ctx context.Context, updo *Order) error { + assert.Equals(t, updo.ID, o.ID) + assert.Equals(t, updo.AccountID, o.AccountID) + assert.Equals(t, updo.ExpiresAt, o.ExpiresAt) + assert.Equals(t, updo.AuthorizationIDs, o.AuthorizationIDs) + assert.Equals(t, updo.Identifiers, o.Identifiers) + return nil + }, }, err: NewErrorISE("error creating certificate for order oID: force"), } @@ -685,7 +720,7 @@ func TestOrder_Finalize(t *testing.T) { }, }, ca: &mockSignAuth{ - sign: func(_csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) { + sign: func(ctx context.Context, _csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) { assert.Equals(t, _csr, csr) return []*x509.Certificate{foo, bar, baz}, nil }, @@ -703,8 +738,8 @@ func TestOrder_Finalize(t *testing.T) { return nil }, MockUpdateOrder: func(ctx context.Context, updo *Order) error { - assert.Equals(t, updo.CertificateID, "certID") - assert.Equals(t, updo.Status, StatusValid) + assert.Equals(t, updo.CertificateID, "") + assert.Equals(t, updo.Status, StatusProcessing) assert.Equals(t, updo.ID, o.ID) assert.Equals(t, updo.AccountID, o.AccountID) assert.Equals(t, updo.ExpiresAt, o.ExpiresAt) @@ -770,7 +805,7 @@ func TestOrder_Finalize(t *testing.T) { }, }, ca: &mockSignAuth{ - sign: func(_csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) { + sign: func(_ctx context.Context, _csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) { assert.Equals(t, _csr, csr) return []*x509.Certificate{leaf, inter, root}, nil }, @@ -792,8 +827,8 @@ func TestOrder_Finalize(t *testing.T) { return nil }, MockUpdateOrder: func(ctx context.Context, updo *Order) error { - assert.Equals(t, updo.CertificateID, "certID") - assert.Equals(t, updo.Status, StatusValid) + // assert.Equals(t, updo.CertificateID, "certID") + // assert.Equals(t, updo.Status, StatusValid) assert.Equals(t, updo.ID, o.ID) assert.Equals(t, updo.AccountID, o.AccountID) assert.Equals(t, updo.ExpiresAt, o.ExpiresAt) @@ -863,7 +898,7 @@ func TestOrder_Finalize(t *testing.T) { }, }, ca: &mockSignAuth{ - sign: func(_csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) { + sign: func(_ctx context.Context, _csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) { assert.Equals(t, _csr, csr) return []*x509.Certificate{leaf, inter, root}, nil }, @@ -896,8 +931,8 @@ func TestOrder_Finalize(t *testing.T) { return nil }, MockUpdateOrder: func(ctx context.Context, updo *Order) error { - assert.Equals(t, updo.CertificateID, "certID") - assert.Equals(t, updo.Status, StatusValid) + // assert.Equals(t, updo.CertificateID, "certID") + // assert.Equals(t, updo.Status, StatusValid) assert.Equals(t, updo.ID, o.ID) assert.Equals(t, updo.AccountID, o.AccountID) assert.Equals(t, updo.ExpiresAt, o.ExpiresAt) @@ -973,7 +1008,7 @@ func TestOrder_Finalize(t *testing.T) { // using the mocking functions as a wrapper for actual test helpers generated per test case or per // function that's tested. ca: &mockSignAuth{ - sign: func(_csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) { + sign: func(_ctx context.Context, _csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) { assert.Equals(t, _csr, csr) return []*x509.Certificate{leaf, inter, root}, nil }, @@ -995,8 +1030,8 @@ func TestOrder_Finalize(t *testing.T) { return nil }, MockUpdateOrder: func(ctx context.Context, updo *Order) error { - assert.Equals(t, updo.CertificateID, "certID") - assert.Equals(t, updo.Status, StatusValid) + // assert.Equals(t, updo.CertificateID, "certID") + // assert.Equals(t, updo.Status, StatusValid) assert.Equals(t, updo.ID, o.ID) assert.Equals(t, updo.AccountID, o.AccountID) assert.Equals(t, updo.ExpiresAt, o.ExpiresAt) @@ -1044,7 +1079,7 @@ func TestOrder_Finalize(t *testing.T) { }, }, ca: &mockSignAuth{ - sign: func(_csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) { + sign: func(ctx context.Context, _csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) { assert.Equals(t, _csr, csr) return []*x509.Certificate{foo, bar, baz}, nil }, @@ -1062,8 +1097,6 @@ func TestOrder_Finalize(t *testing.T) { return nil }, MockUpdateOrder: func(ctx context.Context, updo *Order) error { - assert.Equals(t, updo.CertificateID, "certID") - assert.Equals(t, updo.Status, StatusValid) assert.Equals(t, updo.ID, o.ID) assert.Equals(t, updo.AccountID, o.AccountID) assert.Equals(t, updo.ExpiresAt, o.ExpiresAt) @@ -1108,7 +1141,7 @@ func TestOrder_Finalize(t *testing.T) { }, }, ca: &mockSignAuth{ - sign: func(_csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) { + sign: func(ctx context.Context, _csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) { assert.Equals(t, _csr, csr) return []*x509.Certificate{foo, bar, baz}, nil }, @@ -1126,8 +1159,6 @@ func TestOrder_Finalize(t *testing.T) { return nil }, MockUpdateOrder: func(ctx context.Context, updo *Order) error { - assert.Equals(t, updo.CertificateID, "certID") - assert.Equals(t, updo.Status, StatusValid) assert.Equals(t, updo.ID, o.ID) assert.Equals(t, updo.AccountID, o.AccountID) assert.Equals(t, updo.ExpiresAt, o.ExpiresAt) @@ -1175,7 +1206,7 @@ func TestOrder_Finalize(t *testing.T) { }, }, ca: &mockSignAuth{ - sign: func(_csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) { + sign: func(ctx context.Context, _csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) { assert.Equals(t, _csr, csr) return []*x509.Certificate{foo, bar, baz}, nil }, @@ -1193,8 +1224,6 @@ func TestOrder_Finalize(t *testing.T) { return nil }, MockUpdateOrder: func(ctx context.Context, updo *Order) error { - assert.Equals(t, updo.CertificateID, "certID") - assert.Equals(t, updo.Status, StatusValid) assert.Equals(t, updo.ID, o.ID) assert.Equals(t, updo.AccountID, o.AccountID) assert.Equals(t, updo.ExpiresAt, o.ExpiresAt) @@ -1209,7 +1238,9 @@ func TestOrder_Finalize(t *testing.T) { for name, run := range tests { t.Run(name, func(t *testing.T) { tc := run(t) - if err := tc.o.Finalize(context.Background(), tc.db, tc.csr, tc.ca, tc.prov); err != nil { + ch, err := tc.o.Finalize(context.Background(), tc.db, tc.csr, tc.ca, tc.prov) + + if err != nil { if assert.NotNil(t, tc.err) { var k *Error if errors.As(err, &k) { @@ -1223,7 +1254,23 @@ func TestOrder_Finalize(t *testing.T) { } } } else { - assert.Nil(t, tc.err) + select { + case e := <-ch: + if assert.NotNil(t, tc.err) { + switch k := e.(type) { + case *Error: + assert.Equals(t, k.Type, tc.err.Type) + assert.Equals(t, k.Detail, tc.err.Detail) + assert.Equals(t, k.Status, tc.err.Status) + assert.Equals(t, k.Err.Error(), tc.err.Err.Error()) + assert.Equals(t, k.Detail, tc.err.Detail) + default: + assert.FatalError(t, errors.New("unexpected error type")) + } + } + case <-time.After(1 * time.Second): + assert.Nil(t, tc.err) + } } }) } diff --git a/acme/status.go b/acme/status.go index d9aae82dc..c4fd8e0b4 100644 --- a/acme/status.go +++ b/acme/status.go @@ -16,5 +16,6 @@ var ( StatusReady = Status("ready") //statusExpired = "expired" //statusActive = "active" + StatusProcessing = Status("processing") //statusProcessing = "processing" ) diff --git a/api/api.go b/api/api.go index a09422c2e..ea9f55fb6 100644 --- a/api/api.go +++ b/api/api.go @@ -42,10 +42,10 @@ type Authority interface { AuthorizeRenewToken(ctx context.Context, ott string) (*x509.Certificate, error) GetTLSOptions() *config.TLSOptions Root(shasum string) (*x509.Certificate, error) - Sign(cr *x509.CertificateRequest, opts provisioner.SignOptions, signOpts ...provisioner.SignOption) ([]*x509.Certificate, error) - Renew(peer *x509.Certificate) ([]*x509.Certificate, error) + Sign(ctx context.Context, cr *x509.CertificateRequest, opts provisioner.SignOptions, signOpts ...provisioner.SignOption) ([]*x509.Certificate, error) + Renew(ctx context.Context, peer *x509.Certificate) ([]*x509.Certificate, error) RenewContext(ctx context.Context, peer *x509.Certificate, pk crypto.PublicKey) ([]*x509.Certificate, error) - Rekey(peer *x509.Certificate, pk crypto.PublicKey) ([]*x509.Certificate, error) + Rekey(ctx context.Context, peer *x509.Certificate, pk crypto.PublicKey) ([]*x509.Certificate, error) LoadProvisionerByCertificate(*x509.Certificate) (provisioner.Interface, error) LoadProvisionerByName(string) (provisioner.Interface, error) GetProvisioners(cursor string, limit int) (provisioner.List, string, error) diff --git a/api/api_test.go b/api/api_test.go index a4d25af8f..9164b6a23 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -255,14 +255,14 @@ func (m *mockAuthority) Root(shasum string) (*x509.Certificate, error) { return m.ret1.(*x509.Certificate), m.err } -func (m *mockAuthority) Sign(cr *x509.CertificateRequest, opts provisioner.SignOptions, signOpts ...provisioner.SignOption) ([]*x509.Certificate, error) { +func (m *mockAuthority) Sign(_ context.Context, cr *x509.CertificateRequest, opts provisioner.SignOptions, signOpts ...provisioner.SignOption) ([]*x509.Certificate, error) { if m.sign != nil { return m.sign(cr, opts, signOpts...) } return []*x509.Certificate{m.ret1.(*x509.Certificate), m.ret2.(*x509.Certificate)}, m.err } -func (m *mockAuthority) Renew(cert *x509.Certificate) ([]*x509.Certificate, error) { +func (m *mockAuthority) Renew(_ context.Context, cert *x509.Certificate) ([]*x509.Certificate, error) { if m.renew != nil { return m.renew(cert) } @@ -276,7 +276,7 @@ func (m *mockAuthority) RenewContext(ctx context.Context, oldcert *x509.Certific return []*x509.Certificate{m.ret1.(*x509.Certificate), m.ret2.(*x509.Certificate)}, m.err } -func (m *mockAuthority) Rekey(oldcert *x509.Certificate, pk crypto.PublicKey) ([]*x509.Certificate, error) { +func (m *mockAuthority) Rekey(_ context.Context, oldcert *x509.Certificate, pk crypto.PublicKey) ([]*x509.Certificate, error) { if m.rekey != nil { return m.rekey(oldcert, pk) } diff --git a/api/rekey.go b/api/rekey.go index cda843a3d..9384a02e6 100644 --- a/api/rekey.go +++ b/api/rekey.go @@ -1,6 +1,7 @@ package api import ( + "context" "net/http" "github.com/smallstep/certificates/api/read" @@ -45,7 +46,7 @@ func Rekey(w http.ResponseWriter, r *http.Request) { } a := mustAuthority(r.Context()) - certChain, err := a.Rekey(r.TLS.PeerCertificates[0], body.CsrPEM.CertificateRequest.PublicKey) + certChain, err := a.Rekey(context.Background(), r.TLS.PeerCertificates[0], body.CsrPEM.CertificateRequest.PublicKey) if err != nil { render.Error(w, errs.Wrap(http.StatusInternalServerError, err, "cahandler.Rekey")) return diff --git a/api/renew.go b/api/renew.go index 1b9ed95fa..fae219043 100644 --- a/api/renew.go +++ b/api/renew.go @@ -1,6 +1,7 @@ package api import ( + "context" "crypto/x509" "net/http" "strings" @@ -33,7 +34,7 @@ func Renew(w http.ResponseWriter, r *http.Request) { } a := mustAuthority(ctx) - certChain, err := a.RenewContext(ctx, cert, nil) + certChain, err := a.Renew(context.Background(), cert) if err != nil { render.Error(w, errs.Wrap(http.StatusInternalServerError, err, "cahandler.Renew")) return diff --git a/api/sign.go b/api/sign.go index c0c83ce21..3fb3bd5c2 100644 --- a/api/sign.go +++ b/api/sign.go @@ -1,6 +1,7 @@ package api import ( + "context" "crypto/tls" "encoding/json" "net/http" @@ -78,7 +79,8 @@ func Sign(w http.ResponseWriter, r *http.Request) { return } - certChain, err := a.Sign(body.CsrPEM.CertificateRequest, opts, signOpts...) + certChain, err := a.Sign(context.Background(), body.CsrPEM.CertificateRequest, opts, signOpts...) + if err != nil { render.Error(w, errs.ForbiddenErr(err, "error signing certificate")) return diff --git a/api/ssh.go b/api/ssh.go index 9d0bbc14b..1c0948586 100644 --- a/api/ssh.go +++ b/api/ssh.go @@ -330,7 +330,7 @@ func SSHSign(w http.ResponseWriter, r *http.Request) { NotAfter: time.Unix(int64(cert.ValidBefore), 0), }) - certChain, err := a.Sign(cr, provisioner.SignOptions{}, signOpts...) + certChain, err := a.Sign(context.Background(), cr, provisioner.SignOptions{}, signOpts...) if err != nil { render.Error(w, errs.ForbiddenErr(err, "error signing identity certificate")) return diff --git a/api/sshRenew.go b/api/sshRenew.go index cd6d9bde6..b7ad63adc 100644 --- a/api/sshRenew.go +++ b/api/sshRenew.go @@ -109,7 +109,7 @@ func renewIdentityCertificate(r *http.Request, notBefore, notAfter time.Time) ([ cert.NotAfter = notAfter } - certChain, err := mustAuthority(r.Context()).Renew(cert) + certChain, err := mustAuthority(r.Context()).Renew(r.Context(), cert) if err != nil { return nil, err } diff --git a/authority/authority_test.go b/authority/authority_test.go index 45c7cd861..b058ca97b 100644 --- a/authority/authority_test.go +++ b/authority/authority_test.go @@ -1,6 +1,7 @@ package authority import ( + "context" "crypto" "crypto/rand" "crypto/sha256" @@ -414,7 +415,7 @@ func TestNewEmbedded_Sign(t *testing.T) { csr, err := x509.ParseCertificateRequest(cr) assert.FatalError(t, err) - cert, err := a.Sign(csr, provisioner.SignOptions{}) + cert, err := a.Sign(context.TODO(), csr, provisioner.SignOptions{}) assert.FatalError(t, err) assert.Equals(t, []string{"foo.bar.zar"}, cert[0].DNSNames) assert.Equals(t, crt, cert[1]) @@ -431,9 +432,9 @@ func TestNewEmbedded_GetTLSCertificate(t *testing.T) { a, err := NewEmbedded(WithX509RootBundle(caPEM), WithX509Signer(crt, key.(crypto.Signer))) assert.FatalError(t, err) - + name, _ := os.MkdirTemp("", "") // GetTLSCertificate - cert, err := a.GetTLSCertificate() + cert, err := a.GetTLSCertificate(name, false) assert.FatalError(t, err) assert.Equals(t, []string{"localhost"}, cert.Leaf.DNSNames) assert.True(t, cert.Leaf.IPAddresses[0].Equal(net.ParseIP("127.0.0.1"))) diff --git a/authority/authorize_test.go b/authority/authorize_test.go index 3d748f69a..542d0f0ba 100644 --- a/authority/authorize_test.go +++ b/authority/authorize_test.go @@ -1375,7 +1375,7 @@ func TestAuthority_AuthorizeRenewToken(t *testing.T) { } generateX5cToken := func(a *Authority, key crypto.Signer, claims jose.Claims, opts ...provisioner.SignOption) (string, *x509.Certificate) { - chain, err := a.Sign(csr, provisioner.SignOptions{}, opts...) + chain, err := a.Sign(context.TODO(), csr, provisioner.SignOptions{}, opts...) if err != nil { t.Fatal(err) } diff --git a/authority/config/config.go b/authority/config/config.go index ea7ce35da..4b23f4764 100644 --- a/authority/config/config.go +++ b/authority/config/config.go @@ -70,6 +70,7 @@ type Config struct { IntermediateCert string `json:"crt"` IntermediateKey string `json:"key"` Address string `json:"address"` + PublicAddress string `json:"publicAddress"` InsecureAddress string `json:"insecureAddress"` DNSNames []string `json:"dnsNames"` KMS *kms.Options `json:"kms,omitempty"` @@ -85,6 +86,8 @@ type Config struct { CRL *CRLConfig `json:"crl,omitempty"` MetricsAddress string `json:"metricsAddress,omitempty"` SkipValidation bool `json:"-"` + Storage string `json:"storage,omitempty"` + ManagementHost string `json:"managementHost"` // Keeps record of the filename the Config is read from loadedFromFilepath string diff --git a/authority/provisioners_test.go b/authority/provisioners_test.go index f6af6f548..16aac43c9 100644 --- a/authority/provisioners_test.go +++ b/authority/provisioners_test.go @@ -149,8 +149,8 @@ func TestAuthority_LoadProvisionerByCertificate(t *testing.T) { opts, err := a.Authorize(ctx, token) require.NoError(t, err) opts = append(opts, extraOpts...) - certs, err := a.Sign(csr, provisioner.SignOptions{}, opts...) - require.NoError(t, err) + certs, err := a.Sign(context.TODO(), csr, provisioner.SignOptions{}, opts...) + assert.FatalError(t, err) return certs[0] } getProvisioner := func(a *Authority, name string) provisioner.Interface { diff --git a/authority/tls.go b/authority/tls.go index 0dd6eb54d..26ce13052 100644 --- a/authority/tls.go +++ b/authority/tls.go @@ -14,6 +14,7 @@ import ( "math/big" "net" "net/http" + "os" "strings" "time" @@ -92,13 +93,13 @@ func withDefaultASN1DN(def *config.ASN1DN) provisioner.CertificateModifierFunc { } // Sign creates a signed certificate from a certificate signing request. -func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) { - chain, prov, err := a.signX509(csr, signOpts, extraOpts...) +func (a *Authority) Sign(ctx context.Context, csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) { + chain, prov, err := a.signX509(ctx, csr, signOpts, extraOpts...) a.meter.X509Signed(prov, err) return chain, err } -func (a *Authority) signX509(csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, provisioner.Interface, error) { +func (a *Authority) signX509(ctx context.Context, csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, provisioner.Interface, error) { var ( certOptions []x509util.Option certValidators []provisioner.CertificateValidator @@ -275,7 +276,7 @@ func (a *Authority) signX509(csr *x509.CertificateRequest, signOpts provisioner. // Sign certificate lifetime := leaf.NotAfter.Sub(leaf.NotBefore.Add(signOpts.Backdate)) - resp, err := a.x509CAService.CreateCertificate(&casapi.CreateCertificateRequest{ + resp, err := a.x509CAService.CreateCertificate(ctx, &casapi.CreateCertificateRequest{ Template: leaf, CSR: csr, Lifetime: lifetime, @@ -316,10 +317,10 @@ func (a *Authority) AreSANsAllowed(_ context.Context, sans []string) error { return a.policyEngine.AreSANsAllowed(sans) } -// Renew creates a new Certificate identical to the old certificate, except with -// a validity window that begins 'now'. -func (a *Authority) Renew(oldCert *x509.Certificate) ([]*x509.Certificate, error) { - return a.RenewContext(context.Background(), oldCert, nil) +// Renew creates a new Certificate identical to the old certificate, except +// with a validity window that begins 'now'. +func (a *Authority) Renew(ctx context.Context, oldCert *x509.Certificate) ([]*x509.Certificate, error) { + return a.RenewContext(ctx, oldCert, nil) } // Rekey is used for rekeying and renewing based on the public key. If the @@ -332,8 +333,8 @@ func (a *Authority) Renew(oldCert *x509.Certificate) ([]*x509.Certificate, error // have changed), 'SubjectKeyId' (different in case of rekey), and // 'NotBefore/NotAfter' (the validity duration of the new certificate should be // equal to the old one, but starting 'now'). -func (a *Authority) Rekey(oldCert *x509.Certificate, pk crypto.PublicKey) ([]*x509.Certificate, error) { - return a.RenewContext(context.Background(), oldCert, pk) +func (a *Authority) Rekey(ctx context.Context, oldCert *x509.Certificate, pk crypto.PublicKey) ([]*x509.Certificate, error) { + return a.RenewContext(ctx, oldCert, pk) } // RenewContext creates a new certificate identical to the old one, but it can @@ -451,7 +452,7 @@ func (a *Authority) renewContext(ctx context.Context, oldCert *x509.Certificate, // mode, this can be used to renew a certificate. token, _ := TokenFromContext(ctx) - resp, err := a.x509CAService.RenewCertificate(&casapi.RenewCertificateRequest{ + resp, err := a.x509CAService.RenewCertificate(ctx, &casapi.RenewCertificateRequest{ Template: newCert, Lifetime: lifetime, Backdate: backdate, @@ -650,7 +651,7 @@ func (a *Authority) Revoke(ctx context.Context, revokeOpts *RevokeOptions) error // CAS operation, note that SoftCAS (default) is a noop. // The revoke happens when this is stored in the db. - _, err := a.x509CAService.RevokeCertificate(&casapi.RevokeCertificateRequest{ + _, err := a.x509CAService.RevokeCertificate(ctx, &casapi.RevokeCertificateRequest{ Certificate: revokedCert, SerialNumber: rci.Serial, Reason: rci.Reason, @@ -835,16 +836,49 @@ func (a *Authority) GenerateCertificateRevocationList() error { } // GetTLSCertificate creates a new leaf certificate to be used by the CA HTTPS server. -func (a *Authority) GetTLSCertificate() (*tls.Certificate, error) { +func (a *Authority) GetTLSCertificate(storage string, renew bool) (*tls.Certificate, error) { fatal := func(err error) (*tls.Certificate, error) { return nil, errs.Wrap(http.StatusInternalServerError, err, "authority.GetTLSCertificate") } + var priv crypto.PrivateKey + data, err := os.ReadFile(fmt.Sprintf("%s/%s", storage, "ca.key")) + switch { + case err != nil && os.IsNotExist(err): + // Generate default key. + priv, err = keyutil.GenerateDefaultKey() + if err != nil { + return fatal(err) + } + pemutil.Serialize(priv, pemutil.ToFile(fmt.Sprintf("%s/%s", storage, "ca.key"), 0600)) + case err != nil: + return fatal(err) + default: + priv, err = pemutil.ParseKey(data) + if err != nil { + return fatal(err) + } + } - // Generate default key. - priv, err := keyutil.GenerateDefaultKey() + keyPEM, err := pemutil.Serialize(priv) if err != nil { return fatal(err) } + data, err = os.ReadFile(fmt.Sprintf("%s/%s", storage, "ca.crt")) + + if !renew && err == nil { + cert, err := pemutil.ParseCertificateBundle(data) + if err != nil { + return fatal(err) + } else if cert[0].NotAfter.After(time.Now().Add(7 * 24 * time.Hour)) { + tlsCrt, err := tls.X509KeyPair(data, pem.EncodeToMemory(keyPEM)) + if err != nil { + return fatal(err) + } + tlsCrt.Leaf = cert[0] + return &tlsCrt, nil + } + } + signer, ok := priv.(crypto.Signer) if !ok { return fatal(errors.New("private key is not a crypto.Signer")) @@ -891,7 +925,7 @@ func (a *Authority) GetTLSCertificate() (*tls.Certificate, error) { return fatal(err) } - resp, err := a.x509CAService.CreateCertificate(&casapi.CreateCertificateRequest{ + resp, err := a.x509CAService.CreateCertificate(context.Background(), &casapi.CreateCertificateRequest{ Template: certTpl, CSR: cr, Lifetime: 24 * time.Hour, @@ -913,10 +947,6 @@ func (a *Authority) GetTLSCertificate() (*tls.Certificate, error) { Bytes: crt.Raw, })...) } - keyPEM, err := pemutil.Serialize(priv) - if err != nil { - return fatal(err) - } tlsCrt, err := tls.X509KeyPair(pemBlocks, pem.EncodeToMemory(keyPEM)) if err != nil { @@ -924,7 +954,9 @@ func (a *Authority) GetTLSCertificate() (*tls.Certificate, error) { } // Set leaf certificate tlsCrt.Leaf = resp.Certificate + os.WriteFile(fmt.Sprintf("%s/%s", storage, "ca.crt"), pemBlocks, 0600) return &tlsCrt, nil + } // RFC 5280, 5.2.5 diff --git a/authority/tls_test.go b/authority/tls_test.go index efcb78f83..ad029e9cb 100644 --- a/authority/tls_test.go +++ b/authority/tls_test.go @@ -846,7 +846,7 @@ ZYtQ9Ot36qc= t.Run(name, func(t *testing.T) { tc := genTestCase(t) - certChain, err := tc.auth.Sign(tc.csr, tc.signOpts, tc.extraOpts...) + certChain, err := tc.auth.Sign(context.TODO(), tc.csr, tc.signOpts, tc.extraOpts...) if err != nil { if assert.NotNil(t, tc.err, fmt.Sprintf("unexpected error: %s", err)) { assert.Nil(t, certChain) @@ -1060,9 +1060,9 @@ func TestAuthority_Renew(t *testing.T) { var certChain []*x509.Certificate if tc.auth != nil { - certChain, err = tc.auth.Renew(tc.cert) + certChain, err = tc.auth.Renew(context.Background(), tc.cert) } else { - certChain, err = a.Renew(tc.cert) + certChain, err = a.Renew(context.Background(), tc.cert) } if err != nil { if assert.NotNil(t, tc.err, fmt.Sprintf("unexpected error: %s", err)) { @@ -1265,9 +1265,9 @@ func TestAuthority_Rekey(t *testing.T) { var certChain []*x509.Certificate if tc.auth != nil { - certChain, err = tc.auth.Rekey(tc.cert, tc.pk) + certChain, err = tc.auth.Rekey(context.Background(), tc.cert, tc.pk) } else { - certChain, err = a.Rekey(tc.cert, tc.pk) + certChain, err = a.Rekey(context.Background(), tc.cert, tc.pk) } if err != nil { if assert.NotNil(t, tc.err, fmt.Sprintf("unexpected error: %s", err)) { @@ -1795,12 +1795,12 @@ func TestAuthority_constraints(t *testing.T) { t.Fatal(err) } - _, err = auth.Sign(csr, provisioner.SignOptions{}, templateOption) + _, err = auth.Sign(context.Background(), csr, provisioner.SignOptions{}, templateOption) if (err != nil) != tt.wantErr { t.Errorf("Authority.Sign() error = %v, wantErr %v", err, tt.wantErr) } - _, err = auth.Renew(cert) + _, err = auth.Renew(context.Background(), cert) if (err != nil) != tt.wantErr { t.Errorf("Authority.Renew() error = %v, wantErr %v", err, tt.wantErr) } diff --git a/ca/bootstrap_test.go b/ca/bootstrap_test.go index 62c422d43..f775fd0ff 100644 --- a/ca/bootstrap_test.go +++ b/ca/bootstrap_test.go @@ -54,7 +54,7 @@ func startCABootstrapServer() *httptest.Server { if err != nil { panic(err) } - baseContext := buildContext(ca.auth, nil, nil, nil) + baseContext := buildContext(ca.auth, nil, nil, nil, nil) srv.Config.Handler = ca.srv.Handler srv.Config.BaseContext = func(net.Listener) context.Context { return baseContext diff --git a/ca/ca.go b/ca/ca.go index 0059a5d06..05369eed2 100644 --- a/ca/ca.go +++ b/ca/ca.go @@ -10,6 +10,7 @@ import ( "net" "net/http" "net/url" + "os" "reflect" "strings" "sync" @@ -17,6 +18,13 @@ import ( "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" + "go.opentelemetry.io/contrib/propagators/b3" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/exporters/jaeger" + "go.opentelemetry.io/otel/sdk/resource" + sdktrace "go.opentelemetry.io/otel/sdk/trace" + semconv "go.opentelemetry.io/otel/semconv/v1.4.0" + "github.com/pkg/errors" "github.com/smallstep/certificates/acme" acmeAPI "github.com/smallstep/certificates/acme/api" @@ -36,6 +44,10 @@ import ( "github.com/smallstep/nosql" "go.step.sm/cli-utils/step" "go.step.sm/crypto/x509util" + + pb "github.com/hm-edu/portal-apis" + + "github.com/smallstep/certificates/cas/sectigocas/eab" ) type options struct { @@ -125,11 +137,13 @@ type CA struct { auth *authority.Authority config *config.Config srv *server.Server + public *server.Server insecureSrv *server.Server metricsSrv *server.Server opts *options renewer *TLSRenewer compactStop chan struct{} + tp *sdktrace.TracerProvider } // New creates and initializes the CA with the given configuration and options. @@ -145,6 +159,23 @@ func New(cfg *config.Config, opts ...Option) (*CA, error) { // Init initializes the CA with the given configuration. func (ca *CA) Init(cfg *config.Config) (*CA, error) { + exporter, err := jaeger.New(jaeger.WithCollectorEndpoint()) + if err != nil { + return nil, err + } + ca.tp = sdktrace.NewTracerProvider( + sdktrace.WithSampler(sdktrace.AlwaysSample()), + sdktrace.WithBatcher(exporter), + sdktrace.WithResource( + resource.NewWithAttributes( + semconv.SchemaURL, + semconv.ServiceNameKey.String("certificates"), + )), + ) + otel.SetTracerProvider(ca.tp) + + otel.SetTextMapPropagator(b3.New()) + // Set password, it's ok to set nil password, the ca will prompt for them if // they are required. opts := []authority.Option{ @@ -181,7 +212,7 @@ func (ca *CA) Init(cfg *config.Config) (*CA, error) { } ca.auth = auth - tlsConfig, clientTLSConfig, err := ca.getTLSConfig(auth) + tlsConfig, clientTLSConfig, err := ca.getTLSConfig(auth, cfg) if err != nil { return nil, err } @@ -191,6 +222,12 @@ func (ca *CA) Init(cfg *config.Config) (*CA, error) { // Using chi as the main router mux := chi.NewRouter() handler := http.Handler(mux) + var publicHandler http.Handler + var publicMux *chi.Mux + if cfg.PublicAddress != "" { + publicMux = chi.NewRouter() + publicHandler = http.Handler(publicMux) + } insecureMux := chi.NewRouter() insecureHandler := http.Handler(insecureMux) @@ -237,8 +274,18 @@ func (ca *CA) Init(cfg *config.Config) (*CA, error) { mux.Route("/2.0/acme", func(r chi.Router) { acmeAPI.Route(r) }) - } + if cfg.PublicAddress != "" { + publicMux.Route("/acme", func(r chi.Router) { + acmeAPI.Route(r) + }) + // Use 2.0 because, at the moment, our ACME api is only compatible with v2.0 + // of the ACME spec. + publicMux.Route("/2.0/acme", func(r chi.Router) { + acmeAPI.Route(r) + }) + } + } // Admin API Router if cfg.AuthorityConfig.EnableAdmin { adminDB := auth.GetAdminDatabase() @@ -294,6 +341,9 @@ func (ca *CA) Init(cfg *config.Config) (*CA, error) { } handler = m.Middleware(handler) insecureHandler = m.Middleware(insecureHandler) + if cfg.PublicAddress != "" { + publicHandler = m.Middleware(publicHandler) + } } // Add logger if configured @@ -304,15 +354,28 @@ func (ca *CA) Init(cfg *config.Config) (*CA, error) { } handler = logger.Middleware(handler) insecureHandler = logger.Middleware(insecureHandler) + if cfg.PublicAddress != "" { + publicHandler = logger.Middleware(publicHandler) + } } // Create context with all the necessary values. - baseContext := buildContext(auth, scepAuthority, acmeDB, acmeLinker) + client, err := eab.Connect(cfg.ManagementHost) + if err != nil { + return nil, errors.Wrap(err, "error connecting to EAB") + } + baseContext := buildContext(auth, scepAuthority, acmeDB, acmeLinker, client) ca.srv = server.New(cfg.Address, handler, tlsConfig) ca.srv.BaseContext = func(net.Listener) context.Context { return baseContext } + if cfg.PublicAddress != "" { + ca.public = server.New(cfg.PublicAddress, publicHandler, tlsConfig) + ca.public.BaseContext = func(net.Listener) context.Context { + return baseContext + } + } // only start the insecure server if the insecure address is configured // and, currently, also only when it should serve SCEP endpoints. @@ -355,7 +418,7 @@ func (ca *CA) shouldServeInsecureServer() bool { } // buildContext builds the server base context. -func buildContext(a *authority.Authority, scepAuthority *scep.Authority, acmeDB acme.DB, acmeLinker acme.Linker) context.Context { +func buildContext(a *authority.Authority, scepAuthority *scep.Authority, acmeDB acme.DB, acmeLinker acme.Linker, eabClient pb.EABServiceClient) context.Context { ctx := authority.NewContext(context.Background(), a) if authDB := a.GetDatabase(); authDB != nil { ctx = db.NewContext(ctx, authDB) @@ -369,6 +432,9 @@ func buildContext(a *authority.Authority, scepAuthority *scep.Authority, acmeDB if acmeDB != nil { ctx = acme.NewContext(ctx, acmeDB, acme.NewClient(), acmeLinker, nil) } + if eabClient != nil { + ctx = eab.NewContext(ctx, eabClient) + } return ctx } @@ -433,6 +499,13 @@ func (ca *CA) Run() error { defer wg.Done() errs <- ca.srv.ListenAndServe() }() + wg.Add(1) + if ca.public != nil { + go func() { + defer wg.Done() + errs <- ca.public.ListenAndServe() + }() + } // wait till error occurs; ensures the servers keep listening err := <-errs @@ -450,15 +523,25 @@ func (ca *CA) Stop() error { log.Printf("error stopping ca.Authority: %+v\n", err) } var insecureShutdownErr error + var publicErr error if ca.insecureSrv != nil { insecureShutdownErr = ca.insecureSrv.Shutdown() } + if ca.public != nil { + publicErr = ca.public.Shutdown() + } secureErr := ca.srv.Shutdown() - + err := ca.tp.Shutdown(context.Background()) + if err != nil { + return err + } if insecureShutdownErr != nil { return insecureShutdownErr } + if publicErr != nil { + return publicErr + } return secureErr } @@ -515,6 +598,12 @@ func (ca *CA) Reload() error { logContinue("Reload failed because server could not be replaced.") return errors.Wrap(err, "error reloading server") } + if ca.public != nil { + if err = ca.public.Reload(newCA.public); err != nil { + logContinue("Reload failed because server could not be replaced.") + return errors.Wrap(err, "error reloading server") + } + } // 1. Stop previous renewer // 2. Safely shutdown any internal resources (e.g. key manager) @@ -531,9 +620,17 @@ func (ca *CA) Reload() error { // get TLSConfig returns separate TLSConfigs for server and client with the // same self-renewing certificate. -func (ca *CA) getTLSConfig(auth *authority.Authority) (*tls.Config, *tls.Config, error) { +func (ca *CA) getTLSConfig(auth *authority.Authority, cfg *config.Config) (*tls.Config, *tls.Config, error) { + + if cfg.Storage != "" { + err := os.Mkdir(cfg.Storage, 0600) + if err != nil && !os.IsExist(err) { + return nil, nil, errors.Wrap(err, "error creating storage directory") + } + } + // Create initial TLS certificate - tlsCrt, err := auth.GetTLSCertificate() + tlsCrt, err := auth.GetTLSCertificate(cfg.Storage, false) if err != nil { return nil, nil, err } @@ -544,7 +641,9 @@ func (ca *CA) getTLSConfig(auth *authority.Authority) (*tls.Config, *tls.Config, ca.renewer.Stop() } - ca.renewer, err = NewTLSRenewer(tlsCrt, auth.GetTLSCertificate) + ca.renewer, err = NewTLSRenewer(tlsCrt, func() (*tls.Certificate, error) { + return auth.GetTLSCertificate(cfg.Storage, true) + }) if err != nil { return nil, nil, err } diff --git a/ca/tls_test.go b/ca/tls_test.go index dbcc6023d..bde8b20bc 100644 --- a/ca/tls_test.go +++ b/ca/tls_test.go @@ -82,7 +82,7 @@ func startCATestServer() *httptest.Server { panic(err) } // Use a httptest.Server instead - baseContext := buildContext(ca.auth, nil, nil, nil) + baseContext := buildContext(ca.auth, nil, nil, nil, nil) srv := startTestServer(baseContext, ca.srv.TLSConfig, ca.srv.Handler) return srv } diff --git a/cas/apiv1/options_test.go b/cas/apiv1/options_test.go index d48b63df8..891779938 100644 --- a/cas/apiv1/options_test.go +++ b/cas/apiv1/options_test.go @@ -12,15 +12,15 @@ type testCAS struct { name string } -func (t *testCAS) CreateCertificate(*CreateCertificateRequest) (*CreateCertificateResponse, error) { +func (t *testCAS) CreateCertificate(_ context.Context, req *CreateCertificateRequest) (*CreateCertificateResponse, error) { return nil, nil } -func (t *testCAS) RenewCertificate(*RenewCertificateRequest) (*RenewCertificateResponse, error) { +func (t *testCAS) RenewCertificate(_ context.Context, req *RenewCertificateRequest) (*RenewCertificateResponse, error) { return nil, nil } -func (t *testCAS) RevokeCertificate(*RevokeCertificateRequest) (*RevokeCertificateResponse, error) { +func (t *testCAS) RevokeCertificate(_ context.Context, req *RevokeCertificateRequest) (*RevokeCertificateResponse, error) { return nil, nil } diff --git a/cas/apiv1/services.go b/cas/apiv1/services.go index bca24d96f..1eff1cc8d 100644 --- a/cas/apiv1/services.go +++ b/cas/apiv1/services.go @@ -1,6 +1,7 @@ package apiv1 import ( + "context" "crypto/x509" "net/http" "strings" @@ -9,9 +10,9 @@ import ( // CertificateAuthorityService is the interface implemented to support external // certificate authorities. type CertificateAuthorityService interface { - CreateCertificate(req *CreateCertificateRequest) (*CreateCertificateResponse, error) - RenewCertificate(req *RenewCertificateRequest) (*RenewCertificateResponse, error) - RevokeCertificate(req *RevokeCertificateRequest) (*RevokeCertificateResponse, error) + CreateCertificate(context context.Context, req *CreateCertificateRequest) (*CreateCertificateResponse, error) + RenewCertificate(context context.Context, req *RenewCertificateRequest) (*RenewCertificateResponse, error) + RevokeCertificate(context context.Context, req *RevokeCertificateRequest) (*RevokeCertificateResponse, error) } // CertificateAuthorityCRLGenerator is an optional interface implemented by CertificateAuthorityService @@ -53,6 +54,8 @@ const ( StepCAS = "stepcas" // VaultCAS is a CertificateAuthorityService using Hasicorp Vault PKI. VaultCAS = "vaultcas" + // SectigoCAS is a CertificateAuthorityService using sectigocas. + SectigoCAS = "sectigocas" ) // String returns a string from the type. It will always return the lower case diff --git a/cas/cas_test.go b/cas/cas_test.go index 9fc06567a..7aa981e90 100644 --- a/cas/cas_test.go +++ b/cas/cas_test.go @@ -18,15 +18,15 @@ import ( type mockCAS struct{} -func (m *mockCAS) CreateCertificate(*apiv1.CreateCertificateRequest) (*apiv1.CreateCertificateResponse, error) { +func (m *mockCAS) CreateCertificate(ctx context.Context, req *apiv1.CreateCertificateRequest) (*apiv1.CreateCertificateResponse, error) { panic("not implemented") } -func (m *mockCAS) RenewCertificate(*apiv1.RenewCertificateRequest) (*apiv1.RenewCertificateResponse, error) { +func (m *mockCAS) RenewCertificate(ctx context.Context, req *apiv1.RenewCertificateRequest) (*apiv1.RenewCertificateResponse, error) { panic("not implemented") } -func (m *mockCAS) RevokeCertificate(*apiv1.RevokeCertificateRequest) (*apiv1.RevokeCertificateResponse, error) { +func (m *mockCAS) RevokeCertificate(ctx context.Context, req *apiv1.RevokeCertificateRequest) (*apiv1.RevokeCertificateResponse, error) { panic("not implemented") } diff --git a/cas/cloudcas/cloudcas.go b/cas/cloudcas/cloudcas.go index c9c8364f2..a8d76610c 100644 --- a/cas/cloudcas/cloudcas.go +++ b/cas/cloudcas/cloudcas.go @@ -188,7 +188,7 @@ func (c *CloudCAS) GetCertificateAuthority(req *apiv1.GetCertificateAuthorityReq } // CreateCertificate signs a new certificate using Google Cloud CAS. -func (c *CloudCAS) CreateCertificate(req *apiv1.CreateCertificateRequest) (*apiv1.CreateCertificateResponse, error) { +func (c *CloudCAS) CreateCertificate(_ context.Context, req *apiv1.CreateCertificateRequest) (*apiv1.CreateCertificateResponse, error) { switch { case req.Template == nil: return nil, errors.New("createCertificateRequest `template` cannot be nil") @@ -210,7 +210,7 @@ func (c *CloudCAS) CreateCertificate(req *apiv1.CreateCertificateRequest) (*apiv // RenewCertificate renews the given certificate using Google Cloud CAS. // Google's CAS does not support the renew operation, so this method uses // CreateCertificate. -func (c *CloudCAS) RenewCertificate(req *apiv1.RenewCertificateRequest) (*apiv1.RenewCertificateResponse, error) { +func (c *CloudCAS) RenewCertificate(_ context.Context, req *apiv1.RenewCertificateRequest) (*apiv1.RenewCertificateResponse, error) { switch { case req.Template == nil: return nil, errors.New("renewCertificateRequest `template` cannot be nil") @@ -230,7 +230,7 @@ func (c *CloudCAS) RenewCertificate(req *apiv1.RenewCertificateRequest) (*apiv1. } // RevokeCertificate revokes a certificate using Google Cloud CAS. -func (c *CloudCAS) RevokeCertificate(req *apiv1.RevokeCertificateRequest) (*apiv1.RevokeCertificateResponse, error) { +func (c *CloudCAS) RevokeCertificate(_ context.Context, req *apiv1.RevokeCertificateRequest) (*apiv1.RevokeCertificateResponse, error) { reason, ok := revocationCodeMap[req.ReasonCode] switch { case !ok: diff --git a/cas/cloudcas/cloudcas_test.go b/cas/cloudcas/cloudcas_test.go index 95446ee65..8eab42cc0 100644 --- a/cas/cloudcas/cloudcas_test.go +++ b/cas/cloudcas/cloudcas_test.go @@ -532,7 +532,7 @@ func TestCloudCAS_CreateCertificate(t *testing.T) { client: tt.fields.client, certificateAuthority: tt.fields.certificateAuthority, } - got, err := c.CreateCertificate(tt.args.req) + got, err := c.CreateCertificate(context.TODO(), tt.args.req) if (err != nil) != tt.wantErr { t.Errorf("CloudCAS.CreateCertificate() error = %v, wantErr %v", err, tt.wantErr) return @@ -648,7 +648,7 @@ func TestCloudCAS_RenewCertificate(t *testing.T) { client: tt.fields.client, certificateAuthority: tt.fields.certificateAuthority, } - got, err := c.RenewCertificate(tt.args.req) + got, err := c.RenewCertificate(context.TODO(), tt.args.req) if (err != nil) != tt.wantErr { t.Errorf("CloudCAS.RenewCertificate() error = %v, wantErr %v", err, tt.wantErr) return @@ -727,7 +727,7 @@ func TestCloudCAS_RevokeCertificate(t *testing.T) { client: tt.fields.client, certificateAuthority: tt.fields.certificateAuthority, } - got, err := c.RevokeCertificate(tt.args.req) + got, err := c.RevokeCertificate(context.TODO(), tt.args.req) if (err != nil) != tt.wantErr { t.Errorf("CloudCAS.RevokeCertificate() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/cas/sectigocas/eab/client.go b/cas/sectigocas/eab/client.go new file mode 100644 index 000000000..d93e04603 --- /dev/null +++ b/cas/sectigocas/eab/client.go @@ -0,0 +1,50 @@ +package eab + +import ( + "context" + + pb "github.com/hm-edu/portal-apis" + "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +type eabKey struct{} + +// NewContext adds the given eab client to the context. +func NewContext(ctx context.Context, a pb.EABServiceClient) context.Context { + return context.WithValue(ctx, eabKey{}, a) +} + +// FromContext returns the eab client from the given context. +func FromContext(ctx context.Context) (a pb.EABServiceClient, ok bool) { + a, ok = ctx.Value(eabKey{}).(pb.EABServiceClient) + return +} + +// MustFromContext returns the eab client from the given context. It will +// panic if no eab client is not in the context. +func MustFromContext(ctx context.Context) pb.EABServiceClient { + if a, ok := FromContext(ctx); !ok { + panic("eab client is not in the context") + } else { + return a + } +} + +func Connect(host string) (pb.EABServiceClient, error) { + + conn, err := grpc.DialContext( + context.Background(), + host, + grpc.WithTransportCredentials(insecure.NewCredentials()), + grpc.WithUnaryInterceptor(otelgrpc.UnaryClientInterceptor()), + ) + if err != nil { + return nil, err + } + + apiClient := pb.NewEABServiceClient(conn) + return apiClient, nil + +} diff --git a/cas/sectigocas/sectigocas.go b/cas/sectigocas/sectigocas.go new file mode 100644 index 000000000..e7530e376 --- /dev/null +++ b/cas/sectigocas/sectigocas.go @@ -0,0 +1,187 @@ +package sectigocas + +import ( + "context" + "crypto/x509" + "encoding/json" + "encoding/pem" + "fmt" + "time" + + "github.com/smallstep/certificates/acme" + "github.com/smallstep/certificates/acme/api" + "github.com/smallstep/certificates/authority/provisioner" + "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" + + pb "github.com/hm-edu/portal-apis" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/smallstep/certificates/cas/apiv1" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +type Options struct { + PKIBackend string `json:"pkiBackend"` + EABBackend string `json:"eabBackend"` +} + +func init() { + apiv1.Register(apiv1.SectigoCAS, func(ctx context.Context, opts apiv1.Options) (apiv1.CertificateAuthorityService, error) { + return New(ctx, opts) + }) +} + +func New(ctx context.Context, opts apiv1.Options) (*SectigoCAS, error) { + var config Options + err := json.Unmarshal(opts.Config, &config) + if err != nil { + return nil, err + } + ctx, cancel := context.WithTimeout(ctx, 3*time.Second) + defer cancel() + + conn, err := grpc.DialContext( + ctx, + config.PKIBackend, + grpc.WithBlock(), + grpc.WithTransportCredentials(insecure.NewCredentials()), + grpc.WithUnaryInterceptor(otelgrpc.UnaryClientInterceptor()), + ) + if err != nil { + return nil, err + } + sslServiceClient := pb.NewSSLServiceClient(conn) + conn, err = grpc.DialContext( + ctx, + config.EABBackend, + grpc.WithBlock(), + grpc.WithTransportCredentials(insecure.NewCredentials()), + grpc.WithUnaryInterceptor(otelgrpc.UnaryClientInterceptor()), + ) + if err != nil { + return nil, err + } + eabClient := pb.NewEABServiceClient(conn) + + return &SectigoCAS{sslServiceClient: sslServiceClient, eabClient: eabClient, logger: logrus.StandardLogger()}, nil +} + +type SectigoCAS struct { + sslServiceClient pb.SSLServiceClient + eabClient pb.EABServiceClient + logger *logrus.Logger +} + +func parseCertificates(cert []byte) ([]*x509.Certificate, error) { + var certs []*x509.Certificate + for block, rest := pem.Decode(cert); block != nil; block, rest = pem.Decode(rest) { + switch block.Type { + case "CERTIFICATE": + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return nil, err + } + certs = append(certs, cert) + default: + return nil, errors.New("Unknown entry in cert chain") + } + } + return certs, nil +} +func accountFromContext(ctx context.Context) *acme.Account { + val, ok := ctx.Value(api.AccContextKey).(*acme.Account) + if !ok || val == nil { + return nil + } + return val +} + +func (s *SectigoCAS) signCertificate(ctx context.Context, cr *x509.CertificateRequest) (*x509.Certificate, []*x509.Certificate, error) { + sans := make([]string, 0, len(cr.DNSNames)+len(cr.EmailAddresses)+len(cr.IPAddresses)+len(cr.URIs)) + sans = append(sans, cr.DNSNames...) + for _, ip := range cr.IPAddresses { + sans = append(sans, ip.String()) + } + for _, u := range cr.URIs { + sans = append(sans, u.String()) + } + + issuer := "" + prov, ok := acme.ProvisionerFromContext(ctx) + if !ok || prov == nil { + issuer = "Internal" + } else { + acmeProv, ok := prov.(*provisioner.ACME) + if !ok || acmeProv == nil { + return nil, nil, errors.New("No ACME provisioner passed!") + } + if acmeProv.RequireEAB { + acc := accountFromContext(ctx) + if acc == nil { + return nil, nil, errors.New("No account passed!") + } + user, err := s.eabClient.ResolveAccountId(context.Background(), &pb.ResolveAccountIdRequest{AccountId: acc.ID}) + if err != nil { + return nil, nil, errors.WithMessage(err, "Error resolving user account!") + } + issuer = fmt.Sprintf("%v (EAB: %v)", user.User, user.EabKey) + } + + } + + certificates, err := s.sslServiceClient.IssueCertificate(context.Background(), &pb.IssueSslRequest{ + Issuer: issuer, + SubjectAlternativeNames: sans, + Source: "ACME", + Csr: string(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE REQUEST", Bytes: cr.Raw})), + }) + if err != nil { + s.logger.WithField("error", err).Error("Failed to sign certificate") + return nil, nil, err + } + certs, err := parseCertificates([]byte(certificates.Certificate)) + if err != nil { + s.logger.WithField("error", err).Error("Failed to parse certificate") + return nil, nil, err + } + return certs[0], certs[1:], nil +} + +func (s *SectigoCAS) CreateCertificate(ctx context.Context, req *apiv1.CreateCertificateRequest) (*apiv1.CreateCertificateResponse, error) { + cert, chain, err := s.signCertificate(ctx, req.CSR) + if err != nil { + return nil, err + } + return &apiv1.CreateCertificateResponse{ + Certificate: cert, + CertificateChain: chain, + }, nil +} + +func (s *SectigoCAS) RenewCertificate(ctx context.Context, req *apiv1.RenewCertificateRequest) (*apiv1.RenewCertificateResponse, error) { + cert, chain, err := s.signCertificate(ctx, req.CSR) + if err != nil { + return nil, err + } + return &apiv1.RenewCertificateResponse{ + Certificate: cert, + CertificateChain: chain, + }, nil +} + +func (s *SectigoCAS) RevokeCertificate(ctx context.Context, req *apiv1.RevokeCertificateRequest) (*apiv1.RevokeCertificateResponse, error) { + _, err := s.sslServiceClient.RevokeCertificate(context.Background(), &pb.RevokeSslRequest{ + Identifier: &pb.RevokeSslRequest_Serial{Serial: req.SerialNumber}, + Reason: req.Reason, + }) + if err != nil { + s.logger.WithField("error", err).Error("Failed to revoke certificate") + return nil, err + } + + return &apiv1.RevokeCertificateResponse{ + Certificate: req.Certificate, + CertificateChain: nil, + }, nil +} diff --git a/cas/softcas/softcas.go b/cas/softcas/softcas.go index 58be8aab8..0c4f8fc0b 100644 --- a/cas/softcas/softcas.go +++ b/cas/softcas/softcas.go @@ -54,7 +54,7 @@ func New(_ context.Context, opts apiv1.Options) (*SoftCAS, error) { } // CreateCertificate signs a new certificate using Golang or KMS crypto. -func (c *SoftCAS) CreateCertificate(req *apiv1.CreateCertificateRequest) (*apiv1.CreateCertificateResponse, error) { +func (c *SoftCAS) CreateCertificate(_ context.Context, req *apiv1.CreateCertificateRequest) (*apiv1.CreateCertificateResponse, error) { switch { case req.Template == nil: return nil, errors.New("createCertificateRequest `template` cannot be nil") @@ -90,7 +90,7 @@ func (c *SoftCAS) CreateCertificate(req *apiv1.CreateCertificateRequest) (*apiv1 } // RenewCertificate signs the given certificate template using Golang or KMS crypto. -func (c *SoftCAS) RenewCertificate(req *apiv1.RenewCertificateRequest) (*apiv1.RenewCertificateResponse, error) { +func (c *SoftCAS) RenewCertificate(_ context.Context, req *apiv1.RenewCertificateRequest) (*apiv1.RenewCertificateResponse, error) { switch { case req.Template == nil: return nil, errors.New("createCertificateRequest `template` cannot be nil") @@ -122,7 +122,7 @@ func (c *SoftCAS) RenewCertificate(req *apiv1.RenewCertificateRequest) (*apiv1.R // RevokeCertificate revokes the given certificate in step-ca. In SoftCAS this // operation is a no-op as the actual revoke will happen when we store the entry // in the db. -func (c *SoftCAS) RevokeCertificate(req *apiv1.RevokeCertificateRequest) (*apiv1.RevokeCertificateResponse, error) { +func (c *SoftCAS) RevokeCertificate(_ context.Context, req *apiv1.RevokeCertificateRequest) (*apiv1.RevokeCertificateResponse, error) { chain, _, err := c.getCertSigner() if err != nil { return nil, err diff --git a/cas/softcas/softcas_test.go b/cas/softcas/softcas_test.go index 11bf217aa..ed96dca6c 100644 --- a/cas/softcas/softcas_test.go +++ b/cas/softcas/softcas_test.go @@ -339,7 +339,7 @@ func TestSoftCAS_CreateCertificate(t *testing.T) { Signer: tt.fields.Signer, CertificateSigner: tt.fields.CertificateSigner, } - got, err := c.CreateCertificate(tt.args.req) + got, err := c.CreateCertificate(context.TODO(), tt.args.req) if (err != nil) != tt.wantErr { t.Errorf("SoftCAS.CreateCertificate() error = %v, wantErr %v", err, tt.wantErr) return @@ -383,7 +383,7 @@ func TestSoftCAS_CreateCertificate_pss(t *testing.T) { CertificateChain: []*x509.Certificate{iss}, Signer: signer, } - cert, err := c.CreateCertificate(&apiv1.CreateCertificateRequest{ + cert, err := c.CreateCertificate(context.Background(), &apiv1.CreateCertificateRequest{ Template: &x509.Certificate{ Subject: pkix.Name{CommonName: "test.smallstep.com"}, DNSNames: []string{"test.smallstep.com"}, @@ -469,7 +469,7 @@ func TestSoftCAS_CreateCertificate_ec_rsa(t *testing.T) { CertificateChain: []*x509.Certificate{iss}, Signer: intSigner, } - cert, err := c.CreateCertificate(&apiv1.CreateCertificateRequest{ + cert, err := c.CreateCertificate(context.Background(), &apiv1.CreateCertificateRequest{ Template: &x509.Certificate{ Subject: pkix.Name{CommonName: "test.smallstep.com"}, DNSNames: []string{"test.smallstep.com"}, @@ -567,7 +567,7 @@ func TestSoftCAS_RenewCertificate(t *testing.T) { Signer: tt.fields.Signer, CertificateSigner: tt.fields.CertificateSigner, } - got, err := c.RenewCertificate(tt.args.req) + got, err := c.RenewCertificate(context.TODO(), tt.args.req) if (err != nil) != tt.wantErr { t.Errorf("SoftCAS.RenewCertificate() error = %v, wantErr %v", err, tt.wantErr) return @@ -635,7 +635,7 @@ func TestSoftCAS_RevokeCertificate(t *testing.T) { Signer: tt.fields.Signer, CertificateSigner: tt.fields.CertificateSigner, } - got, err := c.RevokeCertificate(tt.args.req) + got, err := c.RevokeCertificate(context.TODO(), tt.args.req) if (err != nil) != tt.wantErr { t.Errorf("SoftCAS.RevokeCertificate() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/cas/stepcas/stepcas.go b/cas/stepcas/stepcas.go index 51c5f687e..52b09e4db 100644 --- a/cas/stepcas/stepcas.go +++ b/cas/stepcas/stepcas.go @@ -67,7 +67,7 @@ func New(ctx context.Context, opts apiv1.Options) (*StepCAS, error) { // CreateCertificate uses the step-ca sign request with the configured // provisioner to get a new certificate from the certificate authority. -func (s *StepCAS) CreateCertificate(req *apiv1.CreateCertificateRequest) (*apiv1.CreateCertificateResponse, error) { +func (s *StepCAS) CreateCertificate(ctx context.Context, req *apiv1.CreateCertificateRequest) (*apiv1.CreateCertificateResponse, error) { switch { case req.CSR == nil: return nil, errors.New("createCertificateRequest `csr` cannot be nil") @@ -102,7 +102,7 @@ func (s *StepCAS) CreateCertificate(req *apiv1.CreateCertificateRequest) (*apiv1 // RenewCertificate will always return a non-implemented error as mTLS renewals // are not supported yet. -func (s *StepCAS) RenewCertificate(req *apiv1.RenewCertificateRequest) (*apiv1.RenewCertificateResponse, error) { +func (s *StepCAS) RenewCertificate(ctx context.Context, req *apiv1.RenewCertificateRequest) (*apiv1.RenewCertificateResponse, error) { if req.Token == "" { return nil, apiv1.ValidationError{Message: "renewCertificateRequest `token` cannot be empty"} } @@ -125,7 +125,7 @@ func (s *StepCAS) RenewCertificate(req *apiv1.RenewCertificateRequest) (*apiv1.R } // RevokeCertificate revokes a certificate. -func (s *StepCAS) RevokeCertificate(req *apiv1.RevokeCertificateRequest) (*apiv1.RevokeCertificateResponse, error) { +func (s *StepCAS) RevokeCertificate(ctx context.Context, req *apiv1.RevokeCertificateRequest) (*apiv1.RevokeCertificateResponse, error) { if req.SerialNumber == "" && req.Certificate == nil { return nil, errors.New("revokeCertificateRequest `serialNumber` or `certificate` are required") } diff --git a/cas/stepcas/stepcas_test.go b/cas/stepcas/stepcas_test.go index f7746da05..7fbf50e20 100644 --- a/cas/stepcas/stepcas_test.go +++ b/cas/stepcas/stepcas_test.go @@ -751,7 +751,7 @@ func TestStepCAS_CreateCertificate(t *testing.T) { authorityID: "authority-id", fingerprint: tt.fields.fingerprint, } - got, err := s.CreateCertificate(tt.args.req) + got, err := s.CreateCertificate(context.TODO(), tt.args.req) if (err != nil) != tt.wantErr { t.Errorf("StepCAS.CreateCertificate() error = %v, wantErr %v", err, tt.wantErr) return @@ -816,7 +816,7 @@ func TestStepCAS_RenewCertificate(t *testing.T) { client: tt.fields.client, fingerprint: tt.fields.fingerprint, } - got, err := s.RenewCertificate(tt.args.req) + got, err := s.RenewCertificate(context.TODO(), tt.args.req) if (err != nil) != tt.wantErr { t.Errorf("StepCAS.RenewCertificate() error = %v, wantErr %v", err, tt.wantErr) return @@ -916,7 +916,7 @@ func TestStepCAS_RevokeCertificate(t *testing.T) { client: tt.fields.client, fingerprint: tt.fields.fingerprint, } - got, err := s.RevokeCertificate(tt.args.req) + got, err := s.RevokeCertificate(context.TODO(), tt.args.req) if (err != nil) != tt.wantErr { t.Errorf("StepCAS.RevokeCertificate() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/cas/vaultcas/vaultcas.go b/cas/vaultcas/vaultcas.go index 5908cb7df..5fb448f53 100644 --- a/cas/vaultcas/vaultcas.go +++ b/cas/vaultcas/vaultcas.go @@ -111,7 +111,7 @@ func New(ctx context.Context, opts apiv1.Options) (*VaultCAS, error) { } // CreateCertificate signs a new certificate using Hashicorp Vault. -func (v *VaultCAS) CreateCertificate(req *apiv1.CreateCertificateRequest) (*apiv1.CreateCertificateResponse, error) { +func (v *VaultCAS) CreateCertificate(_ context.Context, req *apiv1.CreateCertificateRequest) (*apiv1.CreateCertificateResponse, error) { switch { case req.CSR == nil: return nil, errors.New("createCertificate `csr` cannot be nil") @@ -166,12 +166,12 @@ func (v *VaultCAS) GetCertificateAuthority(*apiv1.GetCertificateAuthorityRequest // RenewCertificate will always return a non-implemented error as renewals // are not supported yet. -func (v *VaultCAS) RenewCertificate(*apiv1.RenewCertificateRequest) (*apiv1.RenewCertificateResponse, error) { +func (v *VaultCAS) RenewCertificate(_ context.Context, req *apiv1.RenewCertificateRequest) (*apiv1.RenewCertificateResponse, error) { return nil, apiv1.NotImplementedError{Message: "vaultCAS does not support renewals"} } // RevokeCertificate revokes a certificate by serial number. -func (v *VaultCAS) RevokeCertificate(req *apiv1.RevokeCertificateRequest) (*apiv1.RevokeCertificateResponse, error) { +func (v *VaultCAS) RevokeCertificate(_ context.Context, req *apiv1.RevokeCertificateRequest) (*apiv1.RevokeCertificateResponse, error) { if req.SerialNumber == "" && req.Certificate == nil { return nil, errors.New("revokeCertificate `serialNumber` or `certificate` are required") } diff --git a/cas/vaultcas/vaultcas_test.go b/cas/vaultcas/vaultcas_test.go index 0ea0c4b1f..4d1ad40ae 100644 --- a/cas/vaultcas/vaultcas_test.go +++ b/cas/vaultcas/vaultcas_test.go @@ -256,7 +256,7 @@ func TestVaultCAS_CreateCertificate(t *testing.T) { client: tt.fields.client, config: tt.fields.options, } - got, err := c.CreateCertificate(tt.args.req) + got, err := c.CreateCertificate(context.TODO(), tt.args.req) if (err != nil) != tt.wantErr { t.Errorf("VaultCAS.CreateCertificate() error = %v, wantErr %v", err, tt.wantErr) return @@ -379,7 +379,7 @@ func TestVaultCAS_RevokeCertificate(t *testing.T) { client: tt.fields.client, config: tt.fields.options, } - got, err := s.RevokeCertificate(tt.args.req) + got, err := s.RevokeCertificate(context.TODO(), tt.args.req) if (err != nil) != tt.wantErr { t.Errorf("VaultCAS.RevokeCertificate() error = %v, wantErr %v", err, tt.wantErr) return @@ -429,7 +429,7 @@ func TestVaultCAS_RenewCertificate(t *testing.T) { client: tt.fields.client, config: tt.fields.options, } - got, err := s.RenewCertificate(tt.args.req) + got, err := s.RenewCertificate(context.TODO(), tt.args.req) if (err != nil) != tt.wantErr { t.Errorf("VaultCAS.RenewCertificate() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/cmd/step-ca/main.go b/cmd/step-ca/main.go index 289815efd..b68bebba8 100644 --- a/cmd/step-ca/main.go +++ b/cmd/step-ca/main.go @@ -37,6 +37,7 @@ import ( // Enabled cas interfaces. _ "github.com/smallstep/certificates/cas/cloudcas" + _ "github.com/smallstep/certificates/cas/sectigocas" _ "github.com/smallstep/certificates/cas/softcas" _ "github.com/smallstep/certificates/cas/stepcas" _ "github.com/smallstep/certificates/cas/vaultcas" diff --git a/go.mod b/go.mod index 81e81c950..c20c176ee 100644 --- a/go.mod +++ b/go.mod @@ -6,8 +6,10 @@ require ( cloud.google.com/go/longrunning v0.5.4 cloud.google.com/go/security v1.15.4 github.com/Masterminds/sprig/v3 v3.2.3 + github.com/ThalesIgnite/crypto11 v1.2.5 // indirect github.com/dgraph-io/badger v1.6.2 github.com/dgraph-io/badger/v2 v2.2007.4 + github.com/dgraph-io/ristretto v0.1.0 // indirect github.com/fxamacker/cbor/v2 v2.5.0 github.com/go-chi/chi/v5 v5.0.11 github.com/go-jose/go-jose/v3 v3.0.1 @@ -50,6 +52,14 @@ require ( cloud.google.com/go/iam v1.1.5 // indirect cloud.google.com/go/kms v1.15.5 // indirect filippo.io/edwards25519 v1.1.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 + go.opentelemetry.io/contrib/propagators/b3 v1.15.0 + go.opentelemetry.io/otel v1.21.0 + go.opentelemetry.io/otel/exporters/jaeger v1.16.0 + go.opentelemetry.io/otel/sdk v1.21.0 +) + +require ( github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0 // indirect @@ -59,7 +69,6 @@ require ( github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.2.0 // indirect - github.com/ThalesIgnite/crypto11 v1.2.5 // indirect github.com/aws/aws-sdk-go-v2 v1.24.1 // indirect github.com/aws/aws-sdk-go-v2/config v1.26.5 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.16.16 // indirect @@ -81,7 +90,6 @@ require ( github.com/chzyer/readline v1.5.1 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/dgraph-io/ristretto v0.1.0 // indirect github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect @@ -113,7 +121,7 @@ require ( github.com/hashicorp/go-sockaddr v1.0.2 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/huandu/xstrings v1.3.3 // indirect - github.com/imdario/mergo v0.3.12 // indirect + github.com/imdario/mergo v0.3.15 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect github.com/jackc/pgconn v1.14.0 // indirect github.com/jackc/pgio v1.0.0 // indirect @@ -150,9 +158,7 @@ require ( github.com/x448/float16 v0.8.4 // indirect go.etcd.io/bbolt v1.3.7 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 // indirect - go.opentelemetry.io/otel v1.21.0 // indirect go.opentelemetry.io/otel/metric v1.21.0 // indirect go.opentelemetry.io/otel/trace v1.21.0 // indirect golang.org/x/oauth2 v0.16.0 // indirect @@ -167,4 +173,6 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect ) +require github.com/hm-edu/portal-apis v0.0.0-20230929065638-ad1f7e7c7ab3 + replace github.com/smallstep/nosql => github.com/hm-edu/nosql v0.4.1-0.20230305071512-139857866201 diff --git a/go.sum b/go.sum index 7067a44ae..22c91867a 100644 --- a/go.sum +++ b/go.sum @@ -263,11 +263,13 @@ github.com/hashicorp/vault/api/auth/kubernetes v0.5.0 h1:CXO0fD7M3iCGovP/UApeHhP github.com/hashicorp/vault/api/auth/kubernetes v0.5.0/go.mod h1:afrElBIO9Q4sHFVuVWgNevG4uAs1bT2AZFA9aEiI608= github.com/hm-edu/nosql v0.4.1-0.20230305071512-139857866201 h1:KB8SVIw1MA30wUUXYziiTErSw487ahokcesqzgPlK/o= github.com/hm-edu/nosql v0.4.1-0.20230305071512-139857866201/go.mod h1:jOXwLtockXORUPPZ2MCUcIkGR6w0cN1QGZniY9DITQA= +github.com/hm-edu/portal-apis v0.0.0-20230929065638-ad1f7e7c7ab3 h1:XrnVZ89D8r7kXCmPsPCa+MtjZJvhhqT1Bkix5iH0sRQ= +github.com/hm-edu/portal-apis v0.0.0-20230929065638-ad1f7e7c7ab3/go.mod h1:o2FYTwt6w4uXfIoPAFRQpxbOOhGjde0PiGZlErVVyZk= github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= -github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM= +github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= @@ -449,6 +451,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -480,11 +483,16 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.4 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJMNjENlcleuuOkGAPH82y0yULBScfXcIEdS24= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo= +go.opentelemetry.io/contrib/propagators/b3 v1.15.0 h1:bMaonPyFcAvZ4EVzkUNkfnUHP5Zi63CIDlA3dRsEg8Q= +go.opentelemetry.io/contrib/propagators/b3 v1.15.0/go.mod h1:VjU0g2v6HSQ+NwfifambSLAeBgevjIcqmceaKWEzl0c= go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc= go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= +go.opentelemetry.io/otel/exporters/jaeger v1.16.0 h1:YhxxmXZ011C0aDZKoNw+juVWAmEfv/0W2XBOv9aHTaA= +go.opentelemetry.io/otel/exporters/jaeger v1.16.0/go.mod h1:grYbBo/5afWlPpdPZYhyn78Bk04hnvxn2+hvxQhKIQM= go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4= go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8= +go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc= go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= go.step.sm/cli-utils v0.8.0 h1:b/Tc1/m3YuQq+u3ghTFP7Dz5zUekZj6GUmd5pCvkEXQ= diff --git a/logging/logger.go b/logging/logger.go index 1716a7f4c..30bb504e5 100644 --- a/logging/logger.go +++ b/logging/logger.go @@ -61,6 +61,7 @@ func New(name string, raw json.RawMessage) (*Logger, error) { } if formatter != nil { logger.Formatter = formatter + logrus.SetFormatter(formatter) } return logger, nil } diff --git a/scep/authority.go b/scep/authority.go index 1d156752a..80baa66fa 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -60,7 +60,7 @@ func MustFromContext(ctx context.Context) *Authority { // SignAuthority is the interface for a signing authority type SignAuthority interface { - Sign(cr *x509.CertificateRequest, opts provisioner.SignOptions, signOpts ...provisioner.SignOption) ([]*x509.Certificate, error) + Sign(ctx context.Context, cr *x509.CertificateRequest, opts provisioner.SignOptions, signOpts ...provisioner.SignOption) ([]*x509.Certificate, error) LoadProvisionerByName(string) (provisioner.Interface, error) } @@ -306,7 +306,7 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m } signOps = append(signOps, templateOptions) - certChain, err := a.signAuth.Sign(csr, opts, signOps...) + certChain, err := a.signAuth.Sign(ctx, csr, opts, signOps...) if err != nil { return nil, fmt.Errorf("error generating certificate for order: %w", err) } From 3905b51fac86102b8eca2a51ff9139b5ac22dd01 Mon Sep 17 00:00:00 2001 From: Florian Ritterhoff Date: Mon, 10 Jul 2023 07:52:20 +0200 Subject: [PATCH 06/20] feat: add validation agent functionality --- .github/workflows/docker.yml | 43 ++++++++- .gitignore | 1 + acme/challenge.go | 37 +++++++- acme/mqtt/client.go | 100 ++++++++++++++++++++ acme/validation/client.go | 60 ++++++++++++ authority/config/config.go | 8 ++ ca/bootstrap_test.go | 2 +- ca/ca.go | 39 +++++++- ca/tls_test.go | 2 +- cmd/step-agent/main.go | 173 +++++++++++++++++++++++++++++++++++ docker/Dockerfile.agent | 18 ++++ go.mod | 17 +++- go.sum | 26 ++++-- 13 files changed, 507 insertions(+), 19 deletions(-) create mode 100644 acme/mqtt/client.go create mode 100644 acme/validation/client.go create mode 100644 cmd/step-agent/main.go create mode 100644 docker/Dockerfile.agent diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 4aee10c1f..f3882f5d7 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -47,4 +47,45 @@ jobs: push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} - file: docker/Dockerfile \ No newline at end of file + file: docker/Dockerfile + build-agent: + runs-on: ubuntu-latest + env: + # Use docker.io for Docker Hub if empty + REGISTRY: ghcr.io + # github.repository as / + IMAGE_NAME: ${{ github.repository }} + permissions: + contents: read + packages: write + steps: + - uses: actions/checkout@v2 + - name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@v1 + - name: Log into registry ${{ env.REGISTRY }} + if: github.event_name != 'pull_request' + uses: docker/login-action@v1 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@v3 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-agent + tags: | + type=schedule + type=ref,event=branch + type=ref,event=tag + type=ref,event=pr + type=raw,value={{branch}}-{{sha}}-{{date 'X'}},enable=${{ github.event_name != 'pull_request' }} + - name: Build and push + uses: docker/build-push-action@v2 + with: + context: . + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + file: docker/Dockerfile.agent diff --git a/.gitignore b/.gitignore index 42e960498..543a0521f 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ output vendor .idea .envrc +step-ca diff --git a/acme/challenge.go b/acme/challenge.go index 995981abc..0654d6ac4 100644 --- a/acme/challenge.go +++ b/acme/challenge.go @@ -27,6 +27,7 @@ import ( "github.com/fxamacker/cbor/v2" "github.com/google/go-tpm/legacy/tpm2" + "github.com/sirupsen/logrus" "golang.org/x/exp/slices" "github.com/smallstep/go-attestation/attest" @@ -36,6 +37,7 @@ import ( "go.step.sm/crypto/pemutil" "go.step.sm/crypto/x509util" + "github.com/smallstep/certificates/acme/validation" "github.com/smallstep/certificates/authority/provisioner" ) @@ -117,11 +119,38 @@ func http01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWeb if InsecurePortHTTP01 != 0 { u.Host += ":" + strconv.Itoa(InsecurePortHTTP01) } - + go func() { + mqtt, ok := validation.FromContext(ctx) + if !ok { + return + } + req := validation.ValidationRequest{ + Authz: ch.AuthorizationID, + Challenge: ch.ID, + Target: u.String(), + } + data, err := json.Marshal(req) + if err != nil { + return + } + if token := mqtt.GetClient().Publish(fmt.Sprintf("%s/jobs", mqtt.GetOrganization()), 1, false, data); token.Wait() && token.Error() != nil { + logrus.Warn(token.Error()) + } + }() vc := MustClientFromContext(ctx) - resp, err := vc.Get(u.String()) - if err != nil { - return storeError(ctx, db, ch, false, WrapError(ErrorConnectionType, err, + resp, errHttp := vc.Get(u.String()) + // get challenge again and check if it was already validated + chDb, errDb := db.GetChallenge(ctx, ch.ID, ch.AuthorizationID) + if errDb == nil { + logrus.WithField("challenge", chDb.ID).WithField("authz", chDb.AuthorizationID).Infof("challenge has status %s", chDb.Status) + if chDb.Status == StatusValid { + return nil + } + } else { + logrus.WithError(errDb).WithField("challenge", ch.ID).WithField("authz", ch.AuthorizationID).Warn("error getting challenge from db") + } + if errHttp != nil { + return storeError(ctx, db, ch, false, WrapError(ErrorConnectionType, errHttp, "error doing http GET for url %s", u)) } defer resp.Body.Close() diff --git a/acme/mqtt/client.go b/acme/mqtt/client.go new file mode 100644 index 000000000..f062c7b19 --- /dev/null +++ b/acme/mqtt/client.go @@ -0,0 +1,100 @@ +package mqtt + +import ( + "context" + "encoding/json" + "fmt" + "net/url" + "time" + + mqtt "github.com/eclipse/paho.mqtt.golang" + "github.com/sirupsen/logrus" + "github.com/smallstep/certificates/acme" + "github.com/smallstep/certificates/acme/validation" +) + +var clock acme.Clock + +func Connect(acmeDB acme.DB, host, user, password, organization string) (validation.MqttClient, error) { + opts := mqtt.NewClientOptions() + opts.SetOrderMatters(false) // Allow out of order messages (use this option unless in order delivery is essential) + opts.ConnectTimeout = time.Second // Minimal delays on connect + opts.WriteTimeout = time.Second // Minimal delays on writes + opts.KeepAlive = 10 // Keepalive every 10 seconds so we quickly detect network outages + opts.PingTimeout = time.Second // local broker so response should be quick + opts.ConnectRetry = true + opts.AutoReconnect = true + opts.ClientID = "acme" + opts.Username = user + opts.Password = password + opts.AddBroker(fmt.Sprintf("ssl://%s:8883", host)) + logrus.Infof("connecting to mqtt broker") + // Log events + opts.OnConnectionLost = func(cl mqtt.Client, err error) { + logrus.Println("mqtt connection lost") + } + opts.OnConnect = func(mqtt.Client) { + logrus.Println("mqtt connection established") + } + opts.OnReconnecting = func(mqtt.Client, *mqtt.ClientOptions) { + logrus.Println("mqtt attempting to reconnect") + } + + client := mqtt.NewClient(opts) + + if token := client.Connect(); token.WaitTimeout(30*time.Second) && token.Error() != nil { + logrus.Warn(token.Error()) + return nil, token.Error() + } + + go func() { + client.Subscribe(fmt.Sprintf("%s/data", organization), 1, func(client mqtt.Client, msg mqtt.Message) { + logrus.Printf("Received message on topic: %s\nMessage: %s\n", msg.Topic(), msg.Payload()) + ctx := context.Background() + data := msg.Payload() + var payload validation.ValidationResponse + err := json.Unmarshal(data, &payload) + if err != nil { + logrus.Errorf("error unmarshalling payload: %v", err) + return + } + + ch, err := acmeDB.GetChallenge(ctx, payload.Challenge, payload.Authz) + if err != nil { + logrus.Errorf("error getting challenge: %v", err) + return + } + + acc, err := acmeDB.GetAccount(ctx, ch.AccountID) + if err != nil { + logrus.Errorf("error getting account: %v", err) + return + } + expected, err := acme.KeyAuthorization(ch.Token, acc.Key) + + if payload.Content != expected || err != nil { + logrus.Errorf("invalid key authorization: %v", err) + return + } + u := &url.URL{Scheme: "http", Host: ch.Value, Path: fmt.Sprintf("/.well-known/acme-challenge/%s", ch.Token)} + logrus.Infof("challenge %s validated using mqtt", u.String()) + + if ch.Status != acme.StatusPending && ch.Status != acme.StatusValid { + return + } + + ch.Status = acme.StatusValid + ch.Error = nil + ch.ValidatedAt = clock.Now().Format(time.RFC3339) + + if err = acmeDB.UpdateChallenge(ctx, ch); err != nil { + logrus.Errorf("error updating challenge: %v", err) + } else { + logrus.Infof("challenge %s updated to valid", u.String()) + } + + }) + }() + connection := validation.BrokerConnection{Client: client, Organization: organization} + return connection, nil +} diff --git a/acme/validation/client.go b/acme/validation/client.go new file mode 100644 index 000000000..637d356a7 --- /dev/null +++ b/acme/validation/client.go @@ -0,0 +1,60 @@ +package validation + +import ( + "context" + + mqtt "github.com/eclipse/paho.mqtt.golang" +) + +type ValidationResponse struct { + Authz string `json:"authz"` + Challenge string `json:"challenge"` + Content string `json:"content"` +} + +type ValidationRequest struct { + Authz string `json:"authz"` + Challenge string `json:"challenge"` + Target string `json:"target"` +} + +type validationKey struct{} + +type MqttClient interface { + GetClient() mqtt.Client + GetOrganization() string +} + +type BrokerConnection struct { + Client mqtt.Client + Organization string +} + +func (b BrokerConnection) GetClient() mqtt.Client { + return b.Client +} + +func (b BrokerConnection) GetOrganization() string { + return b.Organization +} + +// NewContext adds the given validation client to the context. +func NewContext(ctx context.Context, a MqttClient) context.Context { + return context.WithValue(ctx, validationKey{}, a) +} + +// FromContext returns the validation client from the given context. +func FromContext(ctx context.Context) (a MqttClient, ok bool) { + a, ok = ctx.Value(validationKey{}).(MqttClient) + return +} + +// MustFromContext returns the validation client from the given context. It will +// panic if no validation client is not in the context. +func MustFromContext(ctx context.Context) MqttClient { + if a, ok := FromContext(ctx); !ok { + panic("validation client is not in the context") + } else { + return a + } +} diff --git a/authority/config/config.go b/authority/config/config.go index 4b23f4764..5018497ea 100644 --- a/authority/config/config.go +++ b/authority/config/config.go @@ -63,6 +63,13 @@ var ( } ) +type MqttConfig struct { + Host string `json:"host"` + Username string `json:"username"` + Password string `json:"password"` + Organization string `json:"organization"` +} + // Config represents the CA configuration and it's mapped to a JSON object. type Config struct { Root multiString `json:"root"` @@ -88,6 +95,7 @@ type Config struct { SkipValidation bool `json:"-"` Storage string `json:"storage,omitempty"` ManagementHost string `json:"managementHost"` + ValidationBroker *MqttConfig `json:"validationBroker,omitempty"` // Keeps record of the filename the Config is read from loadedFromFilepath string diff --git a/ca/bootstrap_test.go b/ca/bootstrap_test.go index f775fd0ff..3fde61995 100644 --- a/ca/bootstrap_test.go +++ b/ca/bootstrap_test.go @@ -54,7 +54,7 @@ func startCABootstrapServer() *httptest.Server { if err != nil { panic(err) } - baseContext := buildContext(ca.auth, nil, nil, nil, nil) + baseContext := buildContext(ca.auth, nil, nil, nil, nil, nil) srv.Config.Handler = ca.srv.Handler srv.Config.BaseContext = func(net.Listener) context.Context { return baseContext diff --git a/ca/ca.go b/ca/ca.go index 05369eed2..2cbc5e702 100644 --- a/ca/ca.go +++ b/ca/ca.go @@ -6,6 +6,7 @@ import ( "crypto/tls" "crypto/x509" "fmt" + "io" "log" "net" "net/http" @@ -20,15 +21,21 @@ import ( "github.com/go-chi/chi/v5/middleware" "go.opentelemetry.io/contrib/propagators/b3" "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/exporters/jaeger" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" + stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" "go.opentelemetry.io/otel/sdk/resource" sdktrace "go.opentelemetry.io/otel/sdk/trace" semconv "go.opentelemetry.io/otel/semconv/v1.4.0" + "github.com/sirupsen/logrus" + "github.com/pkg/errors" "github.com/smallstep/certificates/acme" acmeAPI "github.com/smallstep/certificates/acme/api" acmeNoSQL "github.com/smallstep/certificates/acme/db/nosql" + acmeMqtt "github.com/smallstep/certificates/acme/mqtt" + "github.com/smallstep/certificates/acme/validation" "github.com/smallstep/certificates/api" "github.com/smallstep/certificates/authority" "github.com/smallstep/certificates/authority/admin" @@ -159,7 +166,15 @@ func New(cfg *config.Config, opts ...Option) (*CA, error) { // Init initializes the CA with the given configuration. func (ca *CA) Init(cfg *config.Config) (*CA, error) { - exporter, err := jaeger.New(jaeger.WithCollectorEndpoint()) + var exporter sdktrace.SpanExporter + var err error + if os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT") == "" { + exporter, err = stdout.New(stdout.WithWriter(io.Discard)) + } else { + opts := []otlptracegrpc.Option{otlptracegrpc.WithInsecure(), otlptracegrpc.WithEndpoint(os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT"))} + client := otlptracegrpc.NewClient(opts...) + exporter, err = otlptrace.New(context.Background(), client) + } if err != nil { return nil, err } @@ -364,7 +379,20 @@ func (ca *CA) Init(cfg *config.Config) (*CA, error) { if err != nil { return nil, errors.Wrap(err, "error connecting to EAB") } - baseContext := buildContext(auth, scepAuthority, acmeDB, acmeLinker, client) + var validationBroker validation.MqttClient + if cfg.ValidationBroker != nil { + if cfg.ValidationBroker.Password == "" { + // pick password from env + cfg.ValidationBroker.Password = os.Getenv("MQTT_PASSWORD") + } + + validationBroker, err = acmeMqtt.Connect(acmeDB, cfg.ValidationBroker.Host, cfg.ValidationBroker.Username, cfg.ValidationBroker.Password, cfg.ValidationBroker.Organization) + if err != nil { + logrus.Warn("error connecting to validation broker. Only local validation will be available!") + } + } + + baseContext := buildContext(auth, scepAuthority, acmeDB, acmeLinker, client, validationBroker) ca.srv = server.New(cfg.Address, handler, tlsConfig) ca.srv.BaseContext = func(net.Listener) context.Context { @@ -418,7 +446,7 @@ func (ca *CA) shouldServeInsecureServer() bool { } // buildContext builds the server base context. -func buildContext(a *authority.Authority, scepAuthority *scep.Authority, acmeDB acme.DB, acmeLinker acme.Linker, eabClient pb.EABServiceClient) context.Context { +func buildContext(a *authority.Authority, scepAuthority *scep.Authority, acmeDB acme.DB, acmeLinker acme.Linker, eabClient pb.EABServiceClient, validationBroker validation.MqttClient) context.Context { ctx := authority.NewContext(context.Background(), a) if authDB := a.GetDatabase(); authDB != nil { ctx = db.NewContext(ctx, authDB) @@ -435,6 +463,9 @@ func buildContext(a *authority.Authority, scepAuthority *scep.Authority, acmeDB if eabClient != nil { ctx = eab.NewContext(ctx, eabClient) } + if validationBroker != nil { + ctx = validation.NewContext(ctx, validationBroker) + } return ctx } diff --git a/ca/tls_test.go b/ca/tls_test.go index bde8b20bc..1ee06ae23 100644 --- a/ca/tls_test.go +++ b/ca/tls_test.go @@ -82,7 +82,7 @@ func startCATestServer() *httptest.Server { panic(err) } // Use a httptest.Server instead - baseContext := buildContext(ca.auth, nil, nil, nil, nil) + baseContext := buildContext(ca.auth, nil, nil, nil, nil, nil) srv := startTestServer(baseContext, ca.srv.TLSConfig, ca.srv.Handler) return srv } diff --git a/cmd/step-agent/main.go b/cmd/step-agent/main.go new file mode 100644 index 000000000..147c93f44 --- /dev/null +++ b/cmd/step-agent/main.go @@ -0,0 +1,173 @@ +package main + +import ( + "encoding/json" + "fmt" + "io" + "os" + "os/signal" + "strings" + "syscall" + "time" + + mqtt "github.com/eclipse/paho.mqtt.golang" + "github.com/sirupsen/logrus" + "github.com/smallstep/certificates/acme" + "github.com/smallstep/certificates/acme/validation" + "github.com/urfave/cli" + "go.step.sm/cli-utils/step" + "go.step.sm/cli-utils/ui" +) + +var agent = cli.Command{ + Name: "agent", + Usage: "start the step-ca agent", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "host", + Usage: "the host of the mqtt broker", + }, + cli.StringFlag{ + Name: "user", + Usage: "the user for the mqtt broker", + }, + cli.StringFlag{ + Name: "password", + Usage: "the password for the mqtt broker", + }, + cli.StringFlag{ + Name: "organization", + Usage: "the organization for the mqtt broker connection", + }, + }, + + Action: func(c *cli.Context) error { + options := mqtt.NewClientOptions() + options.SetOrderMatters(false) + options.ConnectTimeout = time.Second + options.WriteTimeout = time.Second + options.KeepAlive = 10 + options.PingTimeout = time.Second + options.ConnectRetry = true + options.AutoReconnect = true + options.ClientID = fmt.Sprintf("acme-agent-%s-%d", c.String("organization"), time.Now().UnixNano()) + options.Username = c.String("user") + options.Password = c.String("password") + options.AddBroker(fmt.Sprintf("ssl://%s:8883", c.String("host"))) + logrus.Infof("connecting to mqtt broker") + + // Establish connection to MQTT broker + options.OnConnectionLost = func(cl mqtt.Client, err error) { + logrus.Println("mqtt connection lost") + } + options.OnConnect = func(mqtt.Client) { + logrus.Println("mqtt connection established") + } + options.OnReconnecting = func(mqtt.Client, *mqtt.ClientOptions) { + logrus.Println("mqtt reconnecting") + } + client := mqtt.NewClient(options) + if token := client.Connect(); token.WaitTimeout(30*time.Second) && token.Error() != nil { + logrus.Warn(token.Error()) + } + + // Subscribe to topic + client.Subscribe(fmt.Sprintf("%s/jobs", c.String("organization")), 0, func(client mqtt.Client, msg mqtt.Message) { + logrus.Infof("received message on topic %s", msg.Topic()) + logrus.Infof("message: %s", msg.Payload()) + + var data validation.ValidationRequest + + req := msg.Payload() + json.Unmarshal(req, &data) + + logrus.Infof("authz: %s", data.Authz) + logrus.Infof("target: %s", data.Target) + logrus.Infof("account: %s", data.Challenge) + logger := logrus.WithField("authz", data.Authz).WithField("target", data.Target).WithField("account", data.Challenge) + + http := acme.NewClient() + resp, err := http.Get(data.Target) + if err != nil { + logger.WithError(err).Warn("validating failed") + return + } + + defer resp.Body.Close() + if resp.StatusCode >= 400 { + logger.Warnf("validation for %s failed with error: %s", data.Target, resp.Status) + return + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + logger.WithError(err).Warn("parsing body failed") + return + } + + keyAuth := strings.TrimSpace(string(body)) + logger.Infof("keyAuth: %s", keyAuth) + + json, err := json.Marshal(&validation.ValidationResponse{ + Authz: data.Authz, + Challenge: data.Challenge, + Content: keyAuth, + }) + if err != nil { + logger.WithError(err).Warn("marshalling failed") + return + } + // Publish to topic + token := client.Publish(fmt.Sprintf("%s/data", c.String("organization")), 0, false, json) + if token.WaitTimeout(30*time.Second) && token.Error() != nil { + logrus.WithError(token.Error()).Warn("publishing failed") + } + + }) + + return nil + }, +} + +// commit and buildTime are filled in during build by the Makefile +var ( + BuildTime = "N/A" + Version = "N/A" +) + +func init() { + step.Set("Smallstep Agent", Version, BuildTime) +} + +func exit(code int) { + ui.Reset() + os.Exit(code) +} + +func main() { + ui.Init() + app := cli.NewApp() + app.Name = "step-agent" + app.Usage = "step-agent" + app.Version = step.Version() + app.Action = func(c *cli.Context) error { + return agent.Run(c) + } + + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) + done := make(chan bool, 1) + + go func() { + <-sigs + done <- true + }() + + if err := app.Run(os.Args); err != nil { + logrus.Warn(err) + exit(1) + } + + <-done + exit(0) +} diff --git a/docker/Dockerfile.agent b/docker/Dockerfile.agent new file mode 100644 index 000000000..3d9ebe4b0 --- /dev/null +++ b/docker/Dockerfile.agent @@ -0,0 +1,18 @@ +FROM golang:alpine AS builder + +WORKDIR /src +COPY . . + +RUN apk add --no-cache curl git make libcap +RUN PKG=github.com/smallstep/certificates/cmd/step-agent BINNAME=step-agent make V=1 bin/step-agent +RUN setcap CAP_NET_BIND_SERVICE=+eip bin/step-agent + +FROM smallstep/step-cli:latest + +COPY --from=builder /src/bin/step-agent /usr/local/bin/step-agent + +USER step + +VOLUME ["/home/step"] +STOPSIGNAL SIGTERM +CMD exec /usr/local/bin/step-agent diff --git a/go.mod b/go.mod index c20c176ee..02f6755ad 100644 --- a/go.mod +++ b/go.mod @@ -53,12 +53,17 @@ require ( cloud.google.com/go/kms v1.15.5 // indirect filippo.io/edwards25519 v1.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 - go.opentelemetry.io/contrib/propagators/b3 v1.15.0 + go.opentelemetry.io/contrib/propagators/b3 v1.19.0 go.opentelemetry.io/otel v1.21.0 - go.opentelemetry.io/otel/exporters/jaeger v1.16.0 go.opentelemetry.io/otel/sdk v1.21.0 ) +require ( + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.18.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.18.0 + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.18.0 +) + require ( github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1 // indirect @@ -85,6 +90,7 @@ require ( github.com/aws/smithy-go v1.19.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v3 v3.0.0 // indirect + github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chzyer/readline v1.5.1 // indirect @@ -111,6 +117,7 @@ require ( github.com/google/go-tspi v0.3.0 // indirect github.com/google/s2a-go v0.1.7 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect @@ -161,6 +168,7 @@ require ( go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 // indirect go.opentelemetry.io/otel/metric v1.21.0 // indirect go.opentelemetry.io/otel/trace v1.21.0 // indirect + go.opentelemetry.io/proto/otlp v1.0.0 // indirect golang.org/x/oauth2 v0.16.0 // indirect golang.org/x/sync v0.6.0 // indirect golang.org/x/sys v0.16.0 // indirect @@ -175,4 +183,9 @@ require ( require github.com/hm-edu/portal-apis v0.0.0-20230929065638-ad1f7e7c7ab3 +require ( + github.com/eclipse/paho.mqtt.golang v1.4.3 + github.com/gorilla/websocket v1.5.0 // indirect +) + replace github.com/smallstep/nosql => github.com/hm-edu/nosql v0.4.1-0.20230305071512-139857866201 diff --git a/go.sum b/go.sum index 22c91867a..743c58850 100644 --- a/go.sum +++ b/go.sum @@ -77,6 +77,8 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c= github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= +github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= +github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= @@ -124,6 +126,8 @@ github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= 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= +github.com/eclipse/paho.mqtt.golang v1.4.3 h1:2kwcUGn8seMUfWndX0hGbvH8r7crgcJguQNCyp70xik= +github.com/eclipse/paho.mqtt.golang v1.4.3/go.mod h1:CSYvoAlsMkhYOXh/oKyxa8EcBci6dVkLCbo5tTC1RIE= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -230,6 +234,10 @@ github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfF github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -401,7 +409,7 @@ github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= @@ -451,7 +459,6 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -483,18 +490,24 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.4 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJMNjENlcleuuOkGAPH82y0yULBScfXcIEdS24= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo= -go.opentelemetry.io/contrib/propagators/b3 v1.15.0 h1:bMaonPyFcAvZ4EVzkUNkfnUHP5Zi63CIDlA3dRsEg8Q= -go.opentelemetry.io/contrib/propagators/b3 v1.15.0/go.mod h1:VjU0g2v6HSQ+NwfifambSLAeBgevjIcqmceaKWEzl0c= +go.opentelemetry.io/contrib/propagators/b3 v1.19.0 h1:ulz44cpm6V5oAeg5Aw9HyqGFMS6XM7untlMEhD7YzzA= +go.opentelemetry.io/contrib/propagators/b3 v1.19.0/go.mod h1:OzCmE2IVS+asTI+odXQstRGVfXQ4bXv9nMBRK0nNyqQ= go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc= go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= -go.opentelemetry.io/otel/exporters/jaeger v1.16.0 h1:YhxxmXZ011C0aDZKoNw+juVWAmEfv/0W2XBOv9aHTaA= -go.opentelemetry.io/otel/exporters/jaeger v1.16.0/go.mod h1:grYbBo/5afWlPpdPZYhyn78Bk04hnvxn2+hvxQhKIQM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.18.0 h1:IAtl+7gua134xcV3NieDhJHjjOVeJhXAnYf/0hswjUY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.18.0/go.mod h1:w+pXobnBzh95MNIkeIuAKcHe/Uu/CX2PKIvBP6ipKRA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.18.0 h1:yE32ay7mJG2leczfREEhoW3VfSZIvHaB+gvVo1o8DQ8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.18.0/go.mod h1:G17FHPDLt74bCI7tJ4CMitEk4BXTYG4FW6XUpkPBXa4= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.18.0 h1:hSWWvDjXHVLq9DkmB+77fl8v7+t+yYiS+eNkiplDK54= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.18.0/go.mod h1:zG7KQql1WjZCaUJd+L/ReSYx4bjbYJxg5ws9ws+mYes= go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4= go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8= go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc= go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= +go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= +go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= go.step.sm/cli-utils v0.8.0 h1:b/Tc1/m3YuQq+u3ghTFP7Dz5zUekZj6GUmd5pCvkEXQ= go.step.sm/cli-utils v0.8.0/go.mod h1:S77aISrC0pKuflqiDfxxJlUbiXcAanyJ4POOnzFSxD4= go.step.sm/crypto v0.42.0 h1:1yPpg+v2c+fqKTLb5mTl45xdJ4gh1MXF0/X3dar71aU= @@ -505,6 +518,7 @@ go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= From f46f6fa93f39402e0d1122a7d2e5975c180fde65 Mon Sep 17 00:00:00 2001 From: Florian Ritterhoff Date: Fri, 22 Sep 2023 10:42:54 +0200 Subject: [PATCH 07/20] feat: permit insecure endpoints for reverse proxying --- authority/config/config.go | 1 + ca/ca.go | 27 +++++++++++++++++++-------- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/authority/config/config.go b/authority/config/config.go index 5018497ea..21fc8a24c 100644 --- a/authority/config/config.go +++ b/authority/config/config.go @@ -79,6 +79,7 @@ type Config struct { Address string `json:"address"` PublicAddress string `json:"publicAddress"` InsecureAddress string `json:"insecureAddress"` + AllInsecure bool `json:"allInsecure"` DNSNames []string `json:"dnsNames"` KMS *kms.Options `json:"kms,omitempty"` SSH *SSHConfig `json:"ssh,omitempty"` diff --git a/ca/ca.go b/ca/ca.go index 2cbc5e702..b7ce2701f 100644 --- a/ca/ca.go +++ b/ca/ca.go @@ -226,14 +226,16 @@ func (ca *CA) Init(cfg *config.Config) (*CA, error) { return nil, err } ca.auth = auth + var tlsConfig *tls.Config + if cfg.AllInsecure { + tls, clientTLSConfig, err := ca.getTLSConfig(auth, cfg) + tlsConfig = tls + if err != nil { + return nil, err + } - tlsConfig, clientTLSConfig, err := ca.getTLSConfig(auth, cfg) - if err != nil { - return nil, err + webhookTransport.TLSClientConfig = clientTLSConfig } - - webhookTransport.TLSClientConfig = clientTLSConfig - // Using chi as the main router mux := chi.NewRouter() handler := http.Handler(mux) @@ -394,12 +396,21 @@ func (ca *CA) Init(cfg *config.Config) (*CA, error) { baseContext := buildContext(auth, scepAuthority, acmeDB, acmeLinker, client, validationBroker) - ca.srv = server.New(cfg.Address, handler, tlsConfig) + if cfg.AllInsecure { + ca.srv = server.New(cfg.Address, handler, nil) + } else { + ca.srv = server.New(cfg.Address, handler, tlsConfig) + } ca.srv.BaseContext = func(net.Listener) context.Context { return baseContext } if cfg.PublicAddress != "" { - ca.public = server.New(cfg.PublicAddress, publicHandler, tlsConfig) + if cfg.AllInsecure { + ca.public = server.New(cfg.PublicAddress, publicHandler, nil) + } else { + ca.public = server.New(cfg.PublicAddress, publicHandler, tlsConfig) + } + ca.public.BaseContext = func(net.Listener) context.Context { return baseContext } From 9538c410e40e9774a3d403708107519debae96d6 Mon Sep 17 00:00:00 2001 From: Florian Ritterhoff Date: Fri, 22 Sep 2023 10:43:06 +0200 Subject: [PATCH 08/20] feat: try to extract reverse proxy headers --- logging/handler.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/logging/handler.go b/logging/handler.go index a8b77d603..deb157302 100644 --- a/logging/handler.go +++ b/logging/handler.go @@ -70,6 +70,13 @@ func (l *LoggerHandler) writeEntry(w ResponseLogger, r *http.Request, t time.Tim addr = r.RemoteAddr } + // Try to get X-Forwarded-For header first + if xff := r.Header.Get("X-Forwarded-For"); xff != "" { + addr = strings.Split(xff, ", ")[0] + } else if xrip := r.Header.Get("X-Real-Ip"); xrip != "" { + addr = xrip + } + // From https://github.com/gorilla/handlers uri := r.RequestURI // Requests using the CONNECT method over HTTP/2.0 must use From 4b7815ad4a1cef5b45ec5dc5b0fc155686e2198f Mon Sep 17 00:00:00 2001 From: Florian Ritterhoff Date: Fri, 22 Sep 2023 11:00:24 +0200 Subject: [PATCH 09/20] fix: better handling of insecure option --- authority/config/config.go | 1 - ca/ca.go | 13 ++++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/authority/config/config.go b/authority/config/config.go index 21fc8a24c..5018497ea 100644 --- a/authority/config/config.go +++ b/authority/config/config.go @@ -79,7 +79,6 @@ type Config struct { Address string `json:"address"` PublicAddress string `json:"publicAddress"` InsecureAddress string `json:"insecureAddress"` - AllInsecure bool `json:"allInsecure"` DNSNames []string `json:"dnsNames"` KMS *kms.Options `json:"kms,omitempty"` SSH *SSHConfig `json:"ssh,omitempty"` diff --git a/ca/ca.go b/ca/ca.go index b7ce2701f..1c5b17c95 100644 --- a/ca/ca.go +++ b/ca/ca.go @@ -227,7 +227,14 @@ func (ca *CA) Init(cfg *config.Config) (*CA, error) { } ca.auth = auth var tlsConfig *tls.Config - if cfg.AllInsecure { + + allInsecure := false + + if os.Getenv("STEP_TLS_INSECURE") == "1" { + allInsecure = true + } + + if allInsecure { tls, clientTLSConfig, err := ca.getTLSConfig(auth, cfg) tlsConfig = tls if err != nil { @@ -396,7 +403,7 @@ func (ca *CA) Init(cfg *config.Config) (*CA, error) { baseContext := buildContext(auth, scepAuthority, acmeDB, acmeLinker, client, validationBroker) - if cfg.AllInsecure { + if allInsecure { ca.srv = server.New(cfg.Address, handler, nil) } else { ca.srv = server.New(cfg.Address, handler, tlsConfig) @@ -405,7 +412,7 @@ func (ca *CA) Init(cfg *config.Config) (*CA, error) { return baseContext } if cfg.PublicAddress != "" { - if cfg.AllInsecure { + if allInsecure { ca.public = server.New(cfg.PublicAddress, publicHandler, nil) } else { ca.public = server.New(cfg.PublicAddress, publicHandler, tlsConfig) From 04cd77dafb320e2164a495ed3039694f8c0354ee Mon Sep 17 00:00:00 2001 From: Florian Ritterhoff Date: Fri, 22 Sep 2023 11:23:11 +0200 Subject: [PATCH 10/20] fix handling of insecure flag --- ca/ca.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ca/ca.go b/ca/ca.go index 1c5b17c95..5344bbd8a 100644 --- a/ca/ca.go +++ b/ca/ca.go @@ -234,7 +234,7 @@ func (ca *CA) Init(cfg *config.Config) (*CA, error) { allInsecure = true } - if allInsecure { + if !allInsecure { tls, clientTLSConfig, err := ca.getTLSConfig(auth, cfg) tlsConfig = tls if err != nil { From 906baa7e2e95d455d3a419687aee9e23fd321a58 Mon Sep 17 00:00:00 2001 From: Florian Ritterhoff Date: Fri, 29 Sep 2023 08:06:15 +0200 Subject: [PATCH 11/20] chore: upgrade packages --- go.mod | 49 +++++++++++++++++------------------ go.sum | 82 +++++++++++++++++++++++++++++++++------------------------- 2 files changed, 71 insertions(+), 60 deletions(-) diff --git a/go.mod b/go.mod index 02f6755ad..debd2e108 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/ThalesIgnite/crypto11 v1.2.5 // indirect github.com/dgraph-io/badger v1.6.2 github.com/dgraph-io/badger/v2 v2.2007.4 - github.com/dgraph-io/ristretto v0.1.0 // indirect + github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/fxamacker/cbor/v2 v2.5.0 github.com/go-chi/chi/v5 v5.0.11 github.com/go-jose/go-jose/v3 v3.0.1 @@ -21,12 +21,15 @@ require ( github.com/hashicorp/vault/api v1.10.0 github.com/hashicorp/vault/api/auth/approle v0.5.0 github.com/hashicorp/vault/api/auth/kubernetes v0.5.0 + github.com/hm-edu/portal-apis v0.0.0-20230929065638-ad1f7e7c7ab3 + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect github.com/newrelic/go-agent/v3 v3.29.0 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.15.1 github.com/rs/xid v1.5.0 github.com/sirupsen/logrus v1.9.3 - github.com/slackhq/nebula v1.6.1 + github.com/slackhq/nebula v1.7.2 github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 github.com/smallstep/go-attestation v0.4.4-0.20240109183208-413678f90935 github.com/smallstep/nosql v0.6.0 @@ -38,7 +41,7 @@ require ( go.step.sm/crypto v0.42.0 go.step.sm/linkedca v0.20.1 golang.org/x/crypto v0.18.0 - golang.org/x/exp v0.0.0-20230310171629-522b1b587ee0 + golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 golang.org/x/net v0.20.0 google.golang.org/api v0.157.0 google.golang.org/grpc v1.60.1 @@ -53,15 +56,15 @@ require ( cloud.google.com/go/kms v1.15.5 // indirect filippo.io/edwards25519 v1.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 - go.opentelemetry.io/contrib/propagators/b3 v1.19.0 + go.opentelemetry.io/contrib/propagators/b3 v1.20.0 go.opentelemetry.io/otel v1.21.0 go.opentelemetry.io/otel/sdk v1.21.0 ) require ( - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.18.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.18.0 - go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.18.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.19.0 ) require ( @@ -71,9 +74,9 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.10.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1 // indirect - github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.2.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect - github.com/Masterminds/semver/v3 v3.2.0 // indirect + github.com/Masterminds/semver/v3 v3.2.1 // indirect github.com/aws/aws-sdk-go-v2 v1.24.1 // indirect github.com/aws/aws-sdk-go-v2/config v1.26.5 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.16.16 // indirect @@ -89,7 +92,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 // indirect github.com/aws/smithy-go v1.19.0 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/cenkalti/backoff/v3 v3.0.0 // indirect + github.com/cenkalti/backoff/v3 v3.2.2 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect @@ -117,31 +120,29 @@ require ( github.com/google/go-tspi v0.3.0 // indirect github.com/google/s2a-go v0.1.7 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/go-retryablehttp v0.6.6 // indirect + github.com/hashicorp/go-retryablehttp v0.7.4 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect - github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 // indirect + github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7 // indirect github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect - github.com/hashicorp/go-sockaddr v1.0.2 // indirect + github.com/hashicorp/go-sockaddr v1.0.5 // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/huandu/xstrings v1.3.3 // indirect - github.com/imdario/mergo v0.3.15 // indirect + github.com/huandu/xstrings v1.4.0 // indirect + github.com/imdario/mergo v0.3.16 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect - github.com/jackc/pgconn v1.14.0 // indirect + github.com/jackc/pgconn v1.14.1 // indirect github.com/jackc/pgio v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgproto3/v2 v2.3.2 // indirect github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect github.com/jackc/pgtype v1.14.0 // indirect - github.com/jackc/pgx/v4 v4.18.0 // indirect - github.com/klauspost/compress v1.16.3 // indirect + github.com/jackc/pgx/v4 v4.18.1 // indirect + github.com/klauspost/compress v1.17.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/manifoldco/promptui v0.9.0 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.16 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect github.com/miekg/pkcs11 v1.1.1 // indirect @@ -158,9 +159,9 @@ require ( github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect github.com/schollz/jsonstore v1.1.0 // indirect - github.com/shopspring/decimal v1.2.0 // indirect + github.com/shopspring/decimal v1.3.1 // indirect github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect - github.com/spf13/cast v1.4.1 // indirect + github.com/spf13/cast v1.5.1 // indirect github.com/thales-e-security/pool v0.0.2 // indirect github.com/x448/float16 v0.8.4 // indirect go.etcd.io/bbolt v1.3.7 // indirect @@ -181,8 +182,6 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect ) -require github.com/hm-edu/portal-apis v0.0.0-20230929065638-ad1f7e7c7ab3 - require ( github.com/eclipse/paho.mqtt.golang v1.4.3 github.com/gorilla/websocket v1.5.0 // indirect diff --git a/go.sum b/go.sum index 743c58850..4f76ce6ee 100644 --- a/go.sum +++ b/go.sum @@ -27,15 +27,16 @@ github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.10.0 h1:m/sWOGCREuSBqg2 github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.10.0/go.mod h1:Pu5Zksi2KrU7LPbZbNINx6fuVrUp/ffvpxdDj+i8LeE= github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1 h1:FbH3BbSb4bvGluTesZZ+ttN/MDsnMmQP36OSnDuSXqw= github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1/go.mod h1:9V2j0jn9jDEkCkv8w/bKTNppX/d0FVA1ud77xCIP4KA= -github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1 h1:WpB/QDNLpMw72xHJc34BNNykqSOeEJDAWkhf0u12/Jk= -github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= +github.com/AzureAD/microsoft-authentication-library-for-go v1.2.0 h1:hVeq+yCyUi+MsoO/CU95yqCIcdzra5ovzk8Q2BBpV2M= +github.com/AzureAD/microsoft-authentication-library-for-go v1.2.0/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= +github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= @@ -75,8 +76,9 @@ github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6 github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c= github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= +github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M= +github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -117,8 +119,8 @@ github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdw github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= -github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI= -github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug= +github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= +github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= @@ -137,6 +139,7 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fxamacker/cbor/v2 v2.5.0 h1:oHsG0V/Q6E/wqTS2O1Cozzsy69nqCiguo5Q1a1ADivE= github.com/fxamacker/cbor/v2 v2.5.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= @@ -236,8 +239,8 @@ github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56 github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 h1:RtRsiaGvWxcwd8y3BiRZxsylPT8hLWZ5SPcfI+3IDNk= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0/go.mod h1:TzP6duP4Py2pHLVPPQp42aoYI92+PCrVotyR5e8Vqlk= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -250,17 +253,20 @@ github.com/hashicorp/go-hclog v1.2.2 h1:ihRI7YFwcZdiSD7SIenIhHfQH3OuDvWerAUBZbeQ github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM= github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= +github.com/hashicorp/go-retryablehttp v0.7.4 h1:ZQgVdpTdAL7WpMIwLzCfbalOcSUdkDZnpUv3/+BxzFA= +github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= -github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 h1:om4Al8Oy7kCm/B86rLCLah4Dt5Aa0Fr5rYBG60OzwHQ= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= +github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7 h1:UpiO20jno/eV1eVZcxqWnUohyKRe1g8FPV/xH1s/2qs= +github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U= github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= -github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= +github.com/hashicorp/go-sockaddr v1.0.5 h1:dvk7TIXCZpmfOlM+9mlcrWmWjw/wlKT+VDq2wMvfPJU= +github.com/hashicorp/go-sockaddr v1.0.5/go.mod h1:uoUUmtwU7n9Dv3O4SNLeFvg0SxQ3lyjsj6+CCykpaxI= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/vault/api v1.10.0 h1:/US7sIjWN6Imp4o/Rj1Ce2Nr5bki/AXi9vAW3p2tOJQ= @@ -273,11 +279,12 @@ github.com/hm-edu/nosql v0.4.1-0.20230305071512-139857866201 h1:KB8SVIw1MA30wUUX github.com/hm-edu/nosql v0.4.1-0.20230305071512-139857866201/go.mod h1:jOXwLtockXORUPPZ2MCUcIkGR6w0cN1QGZniY9DITQA= github.com/hm-edu/portal-apis v0.0.0-20230929065638-ad1f7e7c7ab3 h1:XrnVZ89D8r7kXCmPsPCa+MtjZJvhhqT1Bkix5iH0sRQ= github.com/hm-edu/portal-apis v0.0.0-20230929065638-ad1f7e7c7ab3/go.mod h1:o2FYTwt6w4uXfIoPAFRQpxbOOhGjde0PiGZlErVVyZk= -github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= +github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM= -github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= +github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= +github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= @@ -289,8 +296,9 @@ github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsU github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= -github.com/jackc/pgconn v1.14.0 h1:vrbA9Ud87g6JdFWkHTJXppVce58qPIdP7N8y0Ml/A7Q= github.com/jackc/pgconn v1.14.0/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E= +github.com/jackc/pgconn v1.14.1 h1:smbxIaZA08n6YuxEX1sDyjV/qkbtUtkH20qLkR9MUR4= +github.com/jackc/pgconn v1.14.1/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E= github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= @@ -321,16 +329,16 @@ github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08 github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= -github.com/jackc/pgx/v4 v4.18.0 h1:Ltaa1ePvc7msFGALnCrqKJVEByu/qYh5jJBYcDtAno4= -github.com/jackc/pgx/v4 v4.18.0/go.mod h1:FydWkUyadDmdNH/mHnGob881GawxeEm7TcMCzkb+qQE= +github.com/jackc/pgx/v4 v4.18.1 h1:YP7G1KABtKpB5IHrO9vYwSrCOhs7p3uqhvhhQBptya0= +github.com/jackc/pgx/v4 v4.18.1/go.mod h1:FydWkUyadDmdNH/mHnGob881GawxeEm7TcMCzkb+qQE= github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.16.3 h1:XuJt9zzcnaz6a16/OU53ZjWp/v7/42WcR5t2a0PcNQY= -github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= +github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -362,8 +370,9 @@ github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= @@ -425,16 +434,17 @@ github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdh github.com/schollz/jsonstore v1.1.0 h1:WZBDjgezFS34CHI+myb4s8GGpir3UMpy7vWoCeO0n6E= github.com/schollz/jsonstore v1.1.0/go.mod h1:15c6+9guw8vDRyozGjN3FoILt0wpruJk9Pi66vjaZfg= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= -github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= +github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/slackhq/nebula v1.6.1 h1:/OCTR3abj0Sbf2nGoLUrdDXImrCv0ZVFpVPP5qa0DsM= -github.com/slackhq/nebula v1.6.1/go.mod h1:UmkqnXe4O53QwToSl/gG7sM4BroQwAB7dd4hUaT6MlI= +github.com/slackhq/nebula v1.7.2 h1:Rko1Mlksz/nC0c919xjGpB8uOSrTJ5e6KPgZx+lVfYw= +github.com/slackhq/nebula v1.7.2/go.mod h1:cnaoahkUipDs1vrNoIszyp0QPRIQN9Pm68ppQEW1Fhg= github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 h1:unQFBIznI+VYD1/1fApl1A+9VcBk+9dcqGfnePY87LY= github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262/go.mod h1:MyOHs9Po2fbM1LHej6sBUT8ozbxmMOFG+E+rx/GSGuc= github.com/smallstep/go-attestation v0.4.4-0.20240109183208-413678f90935 h1:kjYvkvS/Wdy0PVRDUAA0gGJIVSEZYhiAJtfwYgOYoGA= @@ -449,8 +459,8 @@ github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2 github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA= -github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= +github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -490,16 +500,16 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.4 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJMNjENlcleuuOkGAPH82y0yULBScfXcIEdS24= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo= -go.opentelemetry.io/contrib/propagators/b3 v1.19.0 h1:ulz44cpm6V5oAeg5Aw9HyqGFMS6XM7untlMEhD7YzzA= -go.opentelemetry.io/contrib/propagators/b3 v1.19.0/go.mod h1:OzCmE2IVS+asTI+odXQstRGVfXQ4bXv9nMBRK0nNyqQ= +go.opentelemetry.io/contrib/propagators/b3 v1.20.0 h1:Yty9Vs4F3D6/liF1o6FNt0PvN85h/BJJ6DQKJ3nrcM0= +go.opentelemetry.io/contrib/propagators/b3 v1.20.0/go.mod h1:On4VgbkqYL18kbJlWsa18+cMNe6rYpBnPi1ARI/BrsU= go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc= go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.18.0 h1:IAtl+7gua134xcV3NieDhJHjjOVeJhXAnYf/0hswjUY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.18.0/go.mod h1:w+pXobnBzh95MNIkeIuAKcHe/Uu/CX2PKIvBP6ipKRA= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.18.0 h1:yE32ay7mJG2leczfREEhoW3VfSZIvHaB+gvVo1o8DQ8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.18.0/go.mod h1:G17FHPDLt74bCI7tJ4CMitEk4BXTYG4FW6XUpkPBXa4= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.18.0 h1:hSWWvDjXHVLq9DkmB+77fl8v7+t+yYiS+eNkiplDK54= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.18.0/go.mod h1:zG7KQql1WjZCaUJd+L/ReSYx4bjbYJxg5ws9ws+mYes= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 h1:3d+S281UTjM+AbF31XSOYn1qXn3BgIdWl8HNEpx08Jk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0/go.mod h1:0+KuTDyKL4gjKCF75pHOX4wuzYDUZYfAQdSu43o+Z2I= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.19.0 h1:Nw7Dv4lwvGrI68+wULbcq7su9K2cebeCUrDjVrUJHxM= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.19.0/go.mod h1:1MsF6Y7gTqosgoZvHlzcaaM8DIMNZgJh87ykokoNH7Y= go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4= go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8= @@ -543,8 +553,8 @@ golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58 golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20230310171629-522b1b587ee0 h1:LGJsf5LRplCck6jUCH3dBL2dmycNruWNF5xugkSlfXw= -golang.org/x/exp v0.0.0-20230310171629-522b1b587ee0/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 h1:5llv2sWeaMSnA3w2kS57ouQQ4pudlXrR0dCgw51QK9o= +golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -607,8 +617,10 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= From 4b74c0fde8540d202baf1012e6f51047141c58bb Mon Sep 17 00:00:00 2001 From: Florian Ritterhoff Date: Fri, 20 Oct 2023 13:20:49 +0200 Subject: [PATCH 12/20] feat: better errors for eab usage --- acme/api/eab.go | 10 +++++----- acme/api/eab_test.go | 8 ++++---- acme/errors.go | 18 ++++++++++++++++++ 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/acme/api/eab.go b/acme/api/eab.go index 81af66060..8026ae829 100644 --- a/acme/api/eab.go +++ b/acme/api/eab.go @@ -57,21 +57,21 @@ func validateExternalAccountBinding(ctx context.Context, nar *NewAccountRequest) if errors.As(err, &ae) { return nil, acme.WrapError(acme.ErrorUnauthorizedType, err, "the field 'kid' references an unknown key") } - return nil, acme.WrapErrorISE(err, "error retrieving external account key") + return nil, acme.NewError(acme.ErrorEabDoesNotExistType, "error retrieving external account key") } if externalAccountKey == nil { return nil, acme.NewError(acme.ErrorUnauthorizedType, "the field 'kid' references an unknown key") } - if len(externalAccountKey.HmacKey) == 0 { - return nil, acme.NewError(acme.ErrorServerInternalType, "external account binding key with id '%s' does not have secret bytes", keyID) - } - if externalAccountKey.AlreadyBound() { return nil, acme.NewError(acme.ErrorUnauthorizedType, "external account binding key with id '%s' was already bound to account '%s' on %s", keyID, externalAccountKey.AccountID, externalAccountKey.BoundAt) } + if len(externalAccountKey.HmacKey) == 0 { + return nil, acme.NewError(acme.ErrorEabAlreadyUsedType, "external account binding key with id '%s' does not have secret bytes", keyID) + } + payload, err := eabJWS.Verify(externalAccountKey.HmacKey) if err != nil { return nil, acme.WrapErrorISE(err, "error verifying externalAccountBinding signature") diff --git a/acme/api/eab_test.go b/acme/api/eab_test.go index 14dbdad1f..ec1906dcd 100644 --- a/acme/api/eab_test.go +++ b/acme/api/eab_test.go @@ -312,7 +312,7 @@ func TestHandler_validateExternalAccountBinding(t *testing.T) { ExternalAccountBinding: eab, }, eak: nil, - err: acme.NewErrorISE("error retrieving external account key"), + err: acme.NewError(acme.ErrorEabDoesNotExistType, "error retrieving external account key"), } }, "fail/db.GetExternalAccountKey-not-found": func(t *testing.T) test { @@ -361,7 +361,7 @@ func TestHandler_validateExternalAccountBinding(t *testing.T) { ExternalAccountBinding: eab, }, eak: nil, - err: acme.NewErrorISE("error retrieving external account key"), + err: acme.NewError(acme.ErrorEabDoesNotExistType, "error retrieving external account key"), } }, "fail/db.GetExternalAccountKey-error": func(t *testing.T) test { @@ -410,7 +410,7 @@ func TestHandler_validateExternalAccountBinding(t *testing.T) { ExternalAccountBinding: eab, }, eak: nil, - err: acme.NewErrorISE("error retrieving external account key"), + err: acme.NewError(acme.ErrorEabDoesNotExistType, "error retrieving external account key"), } }, "fail/db.GetExternalAccountKey-nil": func(t *testing.T) test { @@ -516,7 +516,7 @@ func TestHandler_validateExternalAccountBinding(t *testing.T) { ExternalAccountBinding: eab, }, eak: nil, - err: acme.NewError(acme.ErrorServerInternalType, "external account binding key with id 'eakID' does not have secret bytes"), + err: acme.NewError(acme.ErrorEabAlreadyUsedType, "external account binding key with id 'eakID' does not have secret bytes"), } }, "fail/db.GetExternalAccountKey-wrong-provisioner": func(t *testing.T) test { diff --git a/acme/errors.go b/acme/errors.go index 658ec6e0c..ebd71e65b 100644 --- a/acme/errors.go +++ b/acme/errors.go @@ -65,6 +65,10 @@ const ( ErrorUserActionRequiredType // ErrorNotImplementedType operation is not implemented ErrorNotImplementedType + // ErrorEabAlreadyUsedType the external account binding has already been used + ErrorEabAlreadyUsedType + // ErrorEabDoesNotExistType the external account binding does not exist + ErrorEabDoesNotExistType ) // String returns the string representation of the acme problem type, @@ -121,6 +125,10 @@ func (ap ProblemType) String() string { return "userActionRequired" case ErrorNotImplementedType: return "notImplemented" + case ErrorEabAlreadyUsedType: + return "eabAlreadyUsed" + case ErrorEabDoesNotExistType: + return "eabDoesNotExist" default: return fmt.Sprintf("unsupported type ACME error type '%d'", int(ap)) } @@ -141,6 +149,16 @@ var ( status: 500, } errorMap = map[ProblemType]errorMetadata{ + ErrorEabAlreadyUsedType: { + typ: officialACMEPrefix + ErrorExternalAccountRequiredType.String(), + details: "The external account binding has already been used", + status: 400, + }, + ErrorEabDoesNotExistType: { + typ: officialACMEPrefix + ErrorExternalAccountRequiredType.String(), + details: "The used external account binding key id does not exist", + status: 400, + }, ErrorAccountDoesNotExistType: { typ: officialACMEPrefix + ErrorAccountDoesNotExistType.String(), details: "Account does not exist", From 4741227b29f1325f8f49e0d23142d13b5ee9bf30 Mon Sep 17 00:00:00 2001 From: Florian Ritterhoff Date: Fri, 20 Oct 2023 15:37:39 +0200 Subject: [PATCH 13/20] fix: optimize error logging --- acme/api/order.go | 2 +- acme/api/order_test.go | 7 ++++--- acme/errors.go | 9 +++++++++ 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/acme/api/order.go b/acme/api/order.go index 43b3354ce..0709bf0c4 100644 --- a/acme/api/order.go +++ b/acme/api/order.go @@ -137,7 +137,7 @@ func NewOrder(w http.ResponseWriter, r *http.Request) { var eak *acme.ExternalAccountKey if acmeProv.RequireEAB { if eak, err = db.GetExternalAccountKeyByAccountID(ctx, prov.GetID(), acc.ID); err != nil { - render.Error(w, acme.WrapErrorISE(err, "error retrieving external account binding key")) + render.Error(w, acme.NewError(acme.ErrorEabAccountBindingDoesNotExistType, "error retrieving external account binding key")) return } } diff --git a/acme/api/order_test.go b/acme/api/order_test.go index 55de4b79b..f78600bd3 100644 --- a/acme/api/order_test.go +++ b/acme/api/order_test.go @@ -7,7 +7,6 @@ import ( "encoding/base64" "encoding/json" "fmt" - "github.com/smallstep/certificates/cas/sectigocas/eab" "io" "net/http" "net/http/httptest" @@ -16,6 +15,8 @@ import ( "testing" "time" + "github.com/smallstep/certificates/cas/sectigocas/eab" + "github.com/go-chi/chi/v5" "github.com/pkg/errors" @@ -936,7 +937,7 @@ func TestHandler_NewOrder(t *testing.T) { ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: b}) return test{ ctx: ctx, - statusCode: 500, + statusCode: 400, ca: &mockCA{}, db: &acme.MockDB{ MockGetExternalAccountKeyByAccountID: func(ctx context.Context, provisionerID, accountID string) (*acme.ExternalAccountKey, error) { @@ -945,7 +946,7 @@ func TestHandler_NewOrder(t *testing.T) { return nil, errors.New("force") }, }, - err: acme.NewErrorISE("error retrieving external account binding key: force"), + err: acme.NewError(acme.ErrorEabAccountBindingDoesNotExistType, "The used external account binding seems to be deleted"), } }, "fail/newACMEPolicyEngine-error": func(t *testing.T) test { diff --git a/acme/errors.go b/acme/errors.go index ebd71e65b..7797ccb1f 100644 --- a/acme/errors.go +++ b/acme/errors.go @@ -69,6 +69,8 @@ const ( ErrorEabAlreadyUsedType // ErrorEabDoesNotExistType the external account binding does not exist ErrorEabDoesNotExistType + // ErrorEabAccountBindingDoesNotExistType the external account binding does not exist + ErrorEabAccountBindingDoesNotExistType ) // String returns the string representation of the acme problem type, @@ -129,6 +131,8 @@ func (ap ProblemType) String() string { return "eabAlreadyUsed" case ErrorEabDoesNotExistType: return "eabDoesNotExist" + case ErrorEabAccountBindingDoesNotExistType: + return "eabAccountBindingDoesNotExist" default: return fmt.Sprintf("unsupported type ACME error type '%d'", int(ap)) } @@ -159,6 +163,11 @@ var ( details: "The used external account binding key id does not exist", status: 400, }, + ErrorEabAccountBindingDoesNotExistType: { + typ: officialACMEPrefix + ErrorExternalAccountRequiredType.String(), + details: "The used external account binding seems to be deleted", + status: 400, + }, ErrorAccountDoesNotExistType: { typ: officialACMEPrefix + ErrorAccountDoesNotExistType.String(), details: "Account does not exist", From 17130126f98f363b2b2af049bb111a8b987867fc Mon Sep 17 00:00:00 2001 From: Florian Ritterhoff Date: Sun, 22 Oct 2023 17:52:43 +0200 Subject: [PATCH 14/20] chore: update github actions --- .github/workflows/docker.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index f3882f5d7..227169878 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -18,20 +18,20 @@ jobs: contents: read packages: write steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Docker Buildx id: buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v3 - name: Log into registry ${{ env.REGISTRY }} if: github.event_name != 'pull_request' - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Extract Docker metadata id: meta - uses: docker/metadata-action@v3 + uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} tags: | @@ -41,7 +41,7 @@ jobs: type=ref,event=pr type=raw,value={{branch}}-{{sha}}-{{date 'X'}},enable=${{ github.event_name != 'pull_request' }} - name: Build and push - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v5 with: context: . push: ${{ github.event_name != 'pull_request' }} @@ -59,20 +59,20 @@ jobs: contents: read packages: write steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Docker Buildx id: buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v3 - name: Log into registry ${{ env.REGISTRY }} if: github.event_name != 'pull_request' - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Extract Docker metadata id: meta - uses: docker/metadata-action@v3 + uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-agent tags: | @@ -82,7 +82,7 @@ jobs: type=ref,event=pr type=raw,value={{branch}}-{{sha}}-{{date 'X'}},enable=${{ github.event_name != 'pull_request' }} - name: Build and push - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v5 with: context: . push: ${{ github.event_name != 'pull_request' }} From 7ff64825d7b6d222b1f7670c126545b301f2adea Mon Sep 17 00:00:00 2001 From: Florian Ritterhoff Date: Thu, 26 Oct 2023 08:13:49 +0200 Subject: [PATCH 15/20] feat: more mqtt logging --- acme/challenge.go | 2 ++ cmd/step-agent/main.go | 16 +++++++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/acme/challenge.go b/acme/challenge.go index 0654d6ac4..489aca527 100644 --- a/acme/challenge.go +++ b/acme/challenge.go @@ -131,11 +131,13 @@ func http01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWeb } data, err := json.Marshal(req) if err != nil { + logrus.Warn(err) return } if token := mqtt.GetClient().Publish(fmt.Sprintf("%s/jobs", mqtt.GetOrganization()), 1, false, data); token.Wait() && token.Error() != nil { logrus.Warn(token.Error()) } + logrus.Info("published validation request") }() vc := MustClientFromContext(ctx) resp, errHttp := vc.Get(u.String()) diff --git a/cmd/step-agent/main.go b/cmd/step-agent/main.go index 147c93f44..21a83ee16 100644 --- a/cmd/step-agent/main.go +++ b/cmd/step-agent/main.go @@ -66,13 +66,14 @@ var agent = cli.Command{ options.OnReconnecting = func(mqtt.Client, *mqtt.ClientOptions) { logrus.Println("mqtt reconnecting") } + client := mqtt.NewClient(options) if token := client.Connect(); token.WaitTimeout(30*time.Second) && token.Error() != nil { logrus.Warn(token.Error()) } // Subscribe to topic - client.Subscribe(fmt.Sprintf("%s/jobs", c.String("organization")), 0, func(client mqtt.Client, msg mqtt.Message) { + token := client.Subscribe(fmt.Sprintf("%s/jobs", c.String("organization")), 0, func(client mqtt.Client, msg mqtt.Message) { logrus.Infof("received message on topic %s", msg.Topic()) logrus.Infof("message: %s", msg.Payload()) @@ -81,9 +82,6 @@ var agent = cli.Command{ req := msg.Payload() json.Unmarshal(req, &data) - logrus.Infof("authz: %s", data.Authz) - logrus.Infof("target: %s", data.Target) - logrus.Infof("account: %s", data.Challenge) logger := logrus.WithField("authz", data.Authz).WithField("target", data.Target).WithField("account", data.Challenge) http := acme.NewClient() @@ -120,11 +118,19 @@ var agent = cli.Command{ // Publish to topic token := client.Publish(fmt.Sprintf("%s/data", c.String("organization")), 0, false, json) if token.WaitTimeout(30*time.Second) && token.Error() != nil { - logrus.WithError(token.Error()).Warn("publishing failed") + logger.WithError(token.Error()).Warn("publishing failed") + } else { + logger.Infof("published to topic %s", fmt.Sprintf("%s/data", c.String("organization"))) } }) + if token.WaitTimeout(30*time.Second) && token.Error() != nil { + logrus.WithError(token.Error()).Warn("subscribing failed") + } else { + logrus.Infof("subscribed to topic %s", fmt.Sprintf("%s/jobs", c.String("organization"))) + } + return nil }, } From 357737ea78a0d6567c1ecb16e84455744c901fd7 Mon Sep 17 00:00:00 2001 From: Florian Ritterhoff Date: Thu, 26 Oct 2023 08:42:20 +0200 Subject: [PATCH 16/20] chore: better handling of reconnects --- acme/mqtt/client.go | 98 ++++++++++++++++----------------- cmd/step-agent/main.go | 120 ++++++++++++++++++++--------------------- 2 files changed, 109 insertions(+), 109 deletions(-) diff --git a/acme/mqtt/client.go b/acme/mqtt/client.go index f062c7b19..655bcd3b3 100644 --- a/acme/mqtt/client.go +++ b/acme/mqtt/client.go @@ -33,8 +33,56 @@ func Connect(acmeDB acme.DB, host, user, password, organization string) (validat opts.OnConnectionLost = func(cl mqtt.Client, err error) { logrus.Println("mqtt connection lost") } - opts.OnConnect = func(mqtt.Client) { + opts.OnConnect = func(cl mqtt.Client) { logrus.Println("mqtt connection established") + go func() { + cl.Subscribe(fmt.Sprintf("%s/data", organization), 1, func(client mqtt.Client, msg mqtt.Message) { + logrus.Printf("Received message on topic: %s\nMessage: %s\n", msg.Topic(), msg.Payload()) + ctx := context.Background() + data := msg.Payload() + var payload validation.ValidationResponse + err := json.Unmarshal(data, &payload) + if err != nil { + logrus.Errorf("error unmarshalling payload: %v", err) + return + } + + ch, err := acmeDB.GetChallenge(ctx, payload.Challenge, payload.Authz) + if err != nil { + logrus.Errorf("error getting challenge: %v", err) + return + } + + acc, err := acmeDB.GetAccount(ctx, ch.AccountID) + if err != nil { + logrus.Errorf("error getting account: %v", err) + return + } + expected, err := acme.KeyAuthorization(ch.Token, acc.Key) + + if payload.Content != expected || err != nil { + logrus.Errorf("invalid key authorization: %v", err) + return + } + u := &url.URL{Scheme: "http", Host: ch.Value, Path: fmt.Sprintf("/.well-known/acme-challenge/%s", ch.Token)} + logrus.Infof("challenge %s validated using mqtt", u.String()) + + if ch.Status != acme.StatusPending && ch.Status != acme.StatusValid { + return + } + + ch.Status = acme.StatusValid + ch.Error = nil + ch.ValidatedAt = clock.Now().Format(time.RFC3339) + + if err = acmeDB.UpdateChallenge(ctx, ch); err != nil { + logrus.Errorf("error updating challenge: %v", err) + } else { + logrus.Infof("challenge %s updated to valid", u.String()) + } + + }) + }() } opts.OnReconnecting = func(mqtt.Client, *mqtt.ClientOptions) { logrus.Println("mqtt attempting to reconnect") @@ -47,54 +95,6 @@ func Connect(acmeDB acme.DB, host, user, password, organization string) (validat return nil, token.Error() } - go func() { - client.Subscribe(fmt.Sprintf("%s/data", organization), 1, func(client mqtt.Client, msg mqtt.Message) { - logrus.Printf("Received message on topic: %s\nMessage: %s\n", msg.Topic(), msg.Payload()) - ctx := context.Background() - data := msg.Payload() - var payload validation.ValidationResponse - err := json.Unmarshal(data, &payload) - if err != nil { - logrus.Errorf("error unmarshalling payload: %v", err) - return - } - - ch, err := acmeDB.GetChallenge(ctx, payload.Challenge, payload.Authz) - if err != nil { - logrus.Errorf("error getting challenge: %v", err) - return - } - - acc, err := acmeDB.GetAccount(ctx, ch.AccountID) - if err != nil { - logrus.Errorf("error getting account: %v", err) - return - } - expected, err := acme.KeyAuthorization(ch.Token, acc.Key) - - if payload.Content != expected || err != nil { - logrus.Errorf("invalid key authorization: %v", err) - return - } - u := &url.URL{Scheme: "http", Host: ch.Value, Path: fmt.Sprintf("/.well-known/acme-challenge/%s", ch.Token)} - logrus.Infof("challenge %s validated using mqtt", u.String()) - - if ch.Status != acme.StatusPending && ch.Status != acme.StatusValid { - return - } - - ch.Status = acme.StatusValid - ch.Error = nil - ch.ValidatedAt = clock.Now().Format(time.RFC3339) - - if err = acmeDB.UpdateChallenge(ctx, ch); err != nil { - logrus.Errorf("error updating challenge: %v", err) - } else { - logrus.Infof("challenge %s updated to valid", u.String()) - } - - }) - }() connection := validation.BrokerConnection{Client: client, Organization: organization} return connection, nil } diff --git a/cmd/step-agent/main.go b/cmd/step-agent/main.go index 21a83ee16..85d322a25 100644 --- a/cmd/step-agent/main.go +++ b/cmd/step-agent/main.go @@ -60,9 +60,68 @@ var agent = cli.Command{ options.OnConnectionLost = func(cl mqtt.Client, err error) { logrus.Println("mqtt connection lost") } - options.OnConnect = func(mqtt.Client) { + options.OnConnect = func(cl mqtt.Client) { logrus.Println("mqtt connection established") + // Subscribe to topic + token := cl.Subscribe(fmt.Sprintf("%s/jobs", c.String("organization")), 0, func(client mqtt.Client, msg mqtt.Message) { + logrus.Infof("received message on topic %s", msg.Topic()) + logrus.Infof("message: %s", msg.Payload()) + + var data validation.ValidationRequest + + req := msg.Payload() + json.Unmarshal(req, &data) + + logger := logrus.WithField("authz", data.Authz).WithField("target", data.Target).WithField("account", data.Challenge) + + http := acme.NewClient() + resp, err := http.Get(data.Target) + if err != nil { + logger.WithError(err).Warn("validating failed") + return + } + + defer resp.Body.Close() + if resp.StatusCode >= 400 { + logger.Warnf("validation for %s failed with error: %s", data.Target, resp.Status) + return + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + logger.WithError(err).Warn("parsing body failed") + return + } + + keyAuth := strings.TrimSpace(string(body)) + logger.Infof("keyAuth: %s", keyAuth) + + json, err := json.Marshal(&validation.ValidationResponse{ + Authz: data.Authz, + Challenge: data.Challenge, + Content: keyAuth, + }) + if err != nil { + logger.WithError(err).Warn("marshalling failed") + return + } + // Publish to topic + token := cl.Publish(fmt.Sprintf("%s/data", c.String("organization")), 0, false, json) + if token.WaitTimeout(30*time.Second) && token.Error() != nil { + logger.WithError(token.Error()).Warn("publishing failed") + } else { + logger.Infof("published to topic %s", fmt.Sprintf("%s/data", c.String("organization"))) + } + + }) + + if token.WaitTimeout(30*time.Second) && token.Error() != nil { + logrus.WithError(token.Error()).Warn("subscribing failed") + } else { + logrus.Infof("subscribed to topic %s", fmt.Sprintf("%s/jobs", c.String("organization"))) + } } + options.OnReconnecting = func(mqtt.Client, *mqtt.ClientOptions) { logrus.Println("mqtt reconnecting") } @@ -72,65 +131,6 @@ var agent = cli.Command{ logrus.Warn(token.Error()) } - // Subscribe to topic - token := client.Subscribe(fmt.Sprintf("%s/jobs", c.String("organization")), 0, func(client mqtt.Client, msg mqtt.Message) { - logrus.Infof("received message on topic %s", msg.Topic()) - logrus.Infof("message: %s", msg.Payload()) - - var data validation.ValidationRequest - - req := msg.Payload() - json.Unmarshal(req, &data) - - logger := logrus.WithField("authz", data.Authz).WithField("target", data.Target).WithField("account", data.Challenge) - - http := acme.NewClient() - resp, err := http.Get(data.Target) - if err != nil { - logger.WithError(err).Warn("validating failed") - return - } - - defer resp.Body.Close() - if resp.StatusCode >= 400 { - logger.Warnf("validation for %s failed with error: %s", data.Target, resp.Status) - return - } - - body, err := io.ReadAll(resp.Body) - if err != nil { - logger.WithError(err).Warn("parsing body failed") - return - } - - keyAuth := strings.TrimSpace(string(body)) - logger.Infof("keyAuth: %s", keyAuth) - - json, err := json.Marshal(&validation.ValidationResponse{ - Authz: data.Authz, - Challenge: data.Challenge, - Content: keyAuth, - }) - if err != nil { - logger.WithError(err).Warn("marshalling failed") - return - } - // Publish to topic - token := client.Publish(fmt.Sprintf("%s/data", c.String("organization")), 0, false, json) - if token.WaitTimeout(30*time.Second) && token.Error() != nil { - logger.WithError(token.Error()).Warn("publishing failed") - } else { - logger.Infof("published to topic %s", fmt.Sprintf("%s/data", c.String("organization"))) - } - - }) - - if token.WaitTimeout(30*time.Second) && token.Error() != nil { - logrus.WithError(token.Error()).Warn("subscribing failed") - } else { - logrus.Infof("subscribed to topic %s", fmt.Sprintf("%s/jobs", c.String("organization"))) - } - return nil }, } From 52bc47d1fbe46ac67791a8c00a3b0ef3a3d45db6 Mon Sep 17 00:00:00 2001 From: Florian Ritterhoff Date: Tue, 21 Nov 2023 08:39:34 +0100 Subject: [PATCH 17/20] upgrade packages --- go.mod | 8 ++++---- go.sum | 18 +++++++++--------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/go.mod b/go.mod index debd2e108..f194c638c 100644 --- a/go.mod +++ b/go.mod @@ -56,15 +56,15 @@ require ( cloud.google.com/go/kms v1.15.5 // indirect filippo.io/edwards25519 v1.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 - go.opentelemetry.io/contrib/propagators/b3 v1.20.0 + go.opentelemetry.io/contrib/propagators/b3 v1.21.1 go.opentelemetry.io/otel v1.21.0 go.opentelemetry.io/otel/sdk v1.21.0 ) require ( - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 - go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.19.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.21.0 ) require ( diff --git a/go.sum b/go.sum index 4f76ce6ee..a3746ac84 100644 --- a/go.sum +++ b/go.sum @@ -500,16 +500,16 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.4 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJMNjENlcleuuOkGAPH82y0yULBScfXcIEdS24= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo= -go.opentelemetry.io/contrib/propagators/b3 v1.20.0 h1:Yty9Vs4F3D6/liF1o6FNt0PvN85h/BJJ6DQKJ3nrcM0= -go.opentelemetry.io/contrib/propagators/b3 v1.20.0/go.mod h1:On4VgbkqYL18kbJlWsa18+cMNe6rYpBnPi1ARI/BrsU= +go.opentelemetry.io/contrib/propagators/b3 v1.21.1 h1:WPYiUgmw3+b7b3sQ1bFBFAf0q+Di9dvNc3AtYfnT4RQ= +go.opentelemetry.io/contrib/propagators/b3 v1.21.1/go.mod h1:EmzokPoSqsYMBVK4nRnhsfm5mbn8J1eDuz/U1UaQaWg= go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc= go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 h1:3d+S281UTjM+AbF31XSOYn1qXn3BgIdWl8HNEpx08Jk= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0/go.mod h1:0+KuTDyKL4gjKCF75pHOX4wuzYDUZYfAQdSu43o+Z2I= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.19.0 h1:Nw7Dv4lwvGrI68+wULbcq7su9K2cebeCUrDjVrUJHxM= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.19.0/go.mod h1:1MsF6Y7gTqosgoZvHlzcaaM8DIMNZgJh87ykokoNH7Y= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 h1:cl5P5/GIfFh4t6xyruOgJP5QiA1pw4fYYdv6nc6CBWw= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0/go.mod h1:zgBdWWAu7oEEMC06MMKc5NLbA/1YDXV1sMpSqEeLQLg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 h1:tIqheXEFWAZ7O8A7m+J0aPTmpJN3YQ7qetUAdkkkKpk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0/go.mod h1:nUeKExfxAQVbiVFn32YXpXZZHZ61Cc3s3Rn1pDBGAb0= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.21.0 h1:VhlEQAPp9R1ktYfrPk5SOryw1e9LDDTZCbIPFrho0ec= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.21.0/go.mod h1:kB3ufRbfU+CQ4MlUcqtW8Z7YEOBeK2DJ6CmR5rYYF3E= go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4= go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8= @@ -528,7 +528,7 @@ go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= From 53e9aa311cd40981706920fea80d23b26a2d3bc6 Mon Sep 17 00:00:00 2001 From: Florian Ritterhoff Date: Sat, 25 Nov 2023 06:07:44 +0100 Subject: [PATCH 18/20] fix: try to handle concurrent saving better --- acme/challenge.go | 14 +++++++++++--- acme/mqtt/client.go | 16 ++++++++++++---- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/acme/challenge.go b/acme/challenge.go index 489aca527..4c683c6f5 100644 --- a/acme/challenge.go +++ b/acme/challenge.go @@ -181,9 +181,17 @@ func http01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWeb ch.Status = StatusValid ch.Error = nil ch.ValidatedAt = clock.Now().Format(time.RFC3339) - - if err = db.UpdateChallenge(ctx, ch); err != nil { - return WrapErrorISE(err, "error updating challenge") + for { + if err = db.UpdateChallenge(ctx, ch); err != nil { + if strings.Contains(err.Error(), "changed since last read") { + // If the challenge has changed since we read it, then we + // don't want to overwrite the error. + logrus.Warn("challenge changed since last read -> retry saving") + continue + } + return WrapErrorISE(err, "error updating challenge") + } + break } return nil } diff --git a/acme/mqtt/client.go b/acme/mqtt/client.go index 655bcd3b3..086493ba7 100644 --- a/acme/mqtt/client.go +++ b/acme/mqtt/client.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "net/url" + "strings" "time" mqtt "github.com/eclipse/paho.mqtt.golang" @@ -74,11 +75,18 @@ func Connect(acmeDB acme.DB, host, user, password, organization string) (validat ch.Status = acme.StatusValid ch.Error = nil ch.ValidatedAt = clock.Now().Format(time.RFC3339) - - if err = acmeDB.UpdateChallenge(ctx, ch); err != nil { - logrus.Errorf("error updating challenge: %v", err) - } else { + for { + if err = acmeDB.UpdateChallenge(ctx, ch); err != nil { + if strings.Contains(err.Error(), "changed since last read") { + // If the challenge has changed since we read it, then we + // don't want to overwrite the error. + logrus.Warn("challenge changed since last read -> retry saving") + continue + } + logrus.Errorf("error updating challenge: %v", err) + } logrus.Infof("challenge %s updated to valid", u.String()) + break } }) From 2b369aa7e1a161e6f5505f930cc35f08501ce99c Mon Sep 17 00:00:00 2001 From: Florian Ritterhoff Date: Tue, 30 Jan 2024 07:53:35 +0100 Subject: [PATCH 19/20] upgrade packages --- go.mod | 40 ++++++++++++++-------------- go.sum | 83 +++++++++++++++++++++++++++++----------------------------- 2 files changed, 61 insertions(+), 62 deletions(-) diff --git a/go.mod b/go.mod index f194c638c..b5fc67b1c 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/golang/mock v1.6.0 github.com/google/go-cmp v0.6.0 github.com/google/go-tpm v0.9.0 - github.com/google/uuid v1.5.0 + github.com/google/uuid v1.6.0 github.com/googleapis/gax-go/v2 v2.12.0 github.com/hashicorp/vault/api v1.10.0 github.com/hashicorp/vault/api/auth/approle v0.5.0 @@ -24,9 +24,9 @@ require ( github.com/hm-edu/portal-apis v0.0.0-20230929065638-ad1f7e7c7ab3 github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect - github.com/newrelic/go-agent/v3 v3.29.0 + github.com/newrelic/go-agent/v3 v3.29.1 github.com/pkg/errors v0.9.1 - github.com/prometheus/client_golang v1.15.1 + github.com/prometheus/client_golang v1.18.0 github.com/rs/xid v1.5.0 github.com/sirupsen/logrus v1.9.3 github.com/slackhq/nebula v1.7.2 @@ -43,8 +43,8 @@ require ( golang.org/x/crypto v0.18.0 golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 golang.org/x/net v0.20.0 - google.golang.org/api v0.157.0 - google.golang.org/grpc v1.60.1 + google.golang.org/api v0.160.0 + google.golang.org/grpc v1.61.0 google.golang.org/protobuf v1.32.0 ) @@ -55,16 +55,16 @@ require ( cloud.google.com/go/iam v1.1.5 // indirect cloud.google.com/go/kms v1.15.5 // indirect filippo.io/edwards25519 v1.1.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 - go.opentelemetry.io/contrib/propagators/b3 v1.21.1 - go.opentelemetry.io/otel v1.21.0 - go.opentelemetry.io/otel/sdk v1.21.0 + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0 + go.opentelemetry.io/contrib/propagators/b3 v1.22.0 + go.opentelemetry.io/otel v1.22.0 + go.opentelemetry.io/otel/sdk v1.22.0 ) require ( - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 - go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.21.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0 + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.22.0 ) require ( @@ -105,7 +105,7 @@ require ( github.com/go-kit/kit v0.13.0 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect - github.com/go-logr/logr v1.3.0 // indirect + github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-piv/piv-go v1.11.0 // indirect github.com/go-sql-driver/mysql v1.7.1 // indirect @@ -143,7 +143,7 @@ require ( github.com/klauspost/compress v1.17.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/manifoldco/promptui v0.9.0 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect github.com/miekg/pkcs11 v1.1.1 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect @@ -153,9 +153,9 @@ require ( github.com/peterbourgon/diskv/v3 v3.0.1 // indirect github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_model v0.4.0 // indirect - github.com/prometheus/common v0.42.0 // indirect - github.com/prometheus/procfs v0.9.0 // indirect + github.com/prometheus/client_model v0.5.0 // indirect + github.com/prometheus/common v0.45.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect github.com/schollz/jsonstore v1.1.0 // indirect @@ -166,9 +166,9 @@ require ( github.com/x448/float16 v0.8.4 // indirect go.etcd.io/bbolt v1.3.7 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 // indirect - go.opentelemetry.io/otel/metric v1.21.0 // indirect - go.opentelemetry.io/otel/trace v1.21.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 // indirect + go.opentelemetry.io/otel/metric v1.22.0 // indirect + go.opentelemetry.io/otel/trace v1.22.0 // indirect go.opentelemetry.io/proto/otlp v1.0.0 // indirect golang.org/x/oauth2 v0.16.0 // indirect golang.org/x/sync v0.6.0 // indirect diff --git a/go.sum b/go.sum index a3746ac84..1e122edcb 100644 --- a/go.sum +++ b/go.sum @@ -98,7 +98,7 @@ github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04= github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k= +github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101 h1:7To3pQ+pZo0i3dsWEbinPNFs5gPSBOsJtx3wTT94VBY= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -159,8 +159,8 @@ github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KE github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= -github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-piv/piv-go v1.11.0 h1:5vAaCdRTFSIW4PeqMbnsDlUZ7odMYWnHBDGdmtU/Zhg= @@ -231,8 +231,8 @@ github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= -github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= @@ -373,8 +373,8 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= @@ -394,8 +394,8 @@ github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/newrelic/go-agent/v3 v3.29.0 h1:Bc1D3DoOkpJs6aIzhOjUp+yIKJ2RfZ+LMQemZOs9t9k= -github.com/newrelic/go-agent/v3 v3.29.0/go.mod h1:9utrgxlSryNqRrTvII2XBL+0lpofXbqXApvVWPpbzUg= +github.com/newrelic/go-agent/v3 v3.29.1 h1:OINNRev5ImiyRq0IUYwhfTmtqQgQFYyDNQEtbRFAi+k= +github.com/newrelic/go-agent/v3 v3.29.1/go.mod h1:9utrgxlSryNqRrTvII2XBL+0lpofXbqXApvVWPpbzUg= github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/peterbourgon/diskv/v3 v3.0.1 h1:x06SQA46+PKIUftmEujdwSEpIx8kR+M9eLYsUxeYveU= @@ -408,15 +408,15 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI= -github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= +github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk= +github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= -github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= -github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= -github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= -github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= -github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= +github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= +github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= @@ -496,26 +496,26 @@ go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 h1:SpGay3w+nEwMpfVnbqOLH5gY52/foP8RE8UzTZ1pdSE= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJMNjENlcleuuOkGAPH82y0yULBScfXcIEdS24= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo= -go.opentelemetry.io/contrib/propagators/b3 v1.21.1 h1:WPYiUgmw3+b7b3sQ1bFBFAf0q+Di9dvNc3AtYfnT4RQ= -go.opentelemetry.io/contrib/propagators/b3 v1.21.1/go.mod h1:EmzokPoSqsYMBVK4nRnhsfm5mbn8J1eDuz/U1UaQaWg= -go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc= -go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 h1:cl5P5/GIfFh4t6xyruOgJP5QiA1pw4fYYdv6nc6CBWw= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0/go.mod h1:zgBdWWAu7oEEMC06MMKc5NLbA/1YDXV1sMpSqEeLQLg= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 h1:tIqheXEFWAZ7O8A7m+J0aPTmpJN3YQ7qetUAdkkkKpk= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0/go.mod h1:nUeKExfxAQVbiVFn32YXpXZZHZ61Cc3s3Rn1pDBGAb0= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.21.0 h1:VhlEQAPp9R1ktYfrPk5SOryw1e9LDDTZCbIPFrho0ec= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.21.0/go.mod h1:kB3ufRbfU+CQ4MlUcqtW8Z7YEOBeK2DJ6CmR5rYYF3E= -go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4= -go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= -go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8= -go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= -go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc= -go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0 h1:UNQQKPfTDe1J81ViolILjTKPr9WetKW6uei2hFgJmFs= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0/go.mod h1:r9vWsPS/3AQItv3OSlEJ/E4mbrhUbbw18meOjArPtKQ= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 h1:sv9kVfal0MK0wBMCOGr+HeJm9v803BkJxGrk2au7j08= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0/go.mod h1:SK2UL73Zy1quvRPonmOmRDiWk1KBV3LyIeeIxcEApWw= +go.opentelemetry.io/contrib/propagators/b3 v1.22.0 h1:Okbgv0pWHMQq+mF7H2o1mucJ5PvxKFq2c8cyqoXfeaQ= +go.opentelemetry.io/contrib/propagators/b3 v1.22.0/go.mod h1:N3z0ycFRhsVZ+tG/uavMxHvOvFE95QM6gwW1zSqT9dQ= +go.opentelemetry.io/otel v1.22.0 h1:xS7Ku+7yTFvDfDraDIJVpw7XPyuHlB9MCiqqX5mcJ6Y= +go.opentelemetry.io/otel v1.22.0/go.mod h1:eoV4iAi3Ea8LkAEI9+GFT44O6T/D0GWAVFyZVCC6pMI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0 h1:9M3+rhx7kZCIQQhQRYaZCdNu1V73tm4TvXs2ntl98C4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0/go.mod h1:noq80iT8rrHP1SfybmPiRGc9dc5M8RPmGvtwo7Oo7tc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0 h1:H2JFgRcGiyHg7H7bwcwaQJYrNFqCqrbTQ8K4p1OvDu8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0/go.mod h1:WfCWp1bGoYK8MeULtI15MmQVczfR+bFkk0DF3h06QmQ= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.22.0 h1:zr8ymM5OWWjjiWRzwTfZ67c905+2TMHYp2lMJ52QTyM= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.22.0/go.mod h1:sQs7FT2iLVJ+67vYngGJkPe1qr39IzaBzaj9IDNNY8k= +go.opentelemetry.io/otel/metric v1.22.0 h1:lypMQnGyJYeuYPhOM/bgjbFM6WE44W1/T45er4d8Hhg= +go.opentelemetry.io/otel/metric v1.22.0/go.mod h1:evJGjVpZv0mQ5QBRJoBF64yMuOf4xCWdXjK8pzFvliY= +go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw= +go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc= +go.opentelemetry.io/otel/trace v1.22.0 h1:Hg6pPujv0XG9QaVbGOBVHunyuLcCC3jN7WEhPx83XD0= +go.opentelemetry.io/otel/trace v1.22.0/go.mod h1:RbbHXVqKES9QhzZq/fE5UnOSILqRt40a21sPw2He1xo= go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= go.step.sm/cli-utils v0.8.0 h1:b/Tc1/m3YuQq+u3ghTFP7Dz5zUekZj6GUmd5pCvkEXQ= @@ -584,7 +584,6 @@ golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -663,8 +662,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.157.0 h1:ORAeqmbrrozeyw5NjnMxh7peHO0UzV4wWYSwZeCUb20= -google.golang.org/api v0.157.0/go.mod h1:+z4v4ufbZ1WEpld6yMGHyggs+PmAHiaLNj5ytP3N01g= +google.golang.org/api v0.160.0 h1:SEspjXHVqE1m5a1fRy8JFB+5jSu+V0GEDKDghF3ttO4= +google.golang.org/api v0.160.0/go.mod h1:0mu0TpK33qnydLvWqbImq2b1eQ5FHRSDCBzAxX9ZHyw= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= @@ -683,8 +682,8 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU= -google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= +google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0= +google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= From f7edb178ca0fee4ab7d045cee10c41bce7f59f3d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Feb 2024 15:31:14 +0000 Subject: [PATCH 20/20] chore(deps): bump google.golang.org/api from 0.160.0 to 0.163.0 Bumps [google.golang.org/api](https://github.com/googleapis/google-api-go-client) from 0.160.0 to 0.163.0. - [Release notes](https://github.com/googleapis/google-api-go-client/releases) - [Changelog](https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md) - [Commits](https://github.com/googleapis/google-api-go-client/compare/v0.160.0...v0.163.0) --- updated-dependencies: - dependency-name: google.golang.org/api dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index b5fc67b1c..d087b7ccc 100644 --- a/go.mod +++ b/go.mod @@ -43,7 +43,7 @@ require ( golang.org/x/crypto v0.18.0 golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 golang.org/x/net v0.20.0 - google.golang.org/api v0.160.0 + google.golang.org/api v0.163.0 google.golang.org/grpc v1.61.0 google.golang.org/protobuf v1.32.0 ) @@ -176,9 +176,9 @@ require ( golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.5.0 // indirect google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917 // indirect + google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 1e122edcb..013c56762 100644 --- a/go.sum +++ b/go.sum @@ -662,8 +662,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.160.0 h1:SEspjXHVqE1m5a1fRy8JFB+5jSu+V0GEDKDghF3ttO4= -google.golang.org/api v0.160.0/go.mod h1:0mu0TpK33qnydLvWqbImq2b1eQ5FHRSDCBzAxX9ZHyw= +google.golang.org/api v0.163.0 h1:4BBDpPaSH+H28NhnX+WwjXxbRLQ7TWuEKp4BQyEjxvk= +google.golang.org/api v0.163.0/go.mod h1:6SulDkfoBIg4NFmCuZ39XeeAgSHCPecfSUuDyYlAHs0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= @@ -671,12 +671,12 @@ google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917 h1:nz5NESFLZbJGPFxDT/HCn+V1mZ8JGNoY4nUpmW/Y2eg= -google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917/go.mod h1:pZqR+glSb11aJ+JQcczCvgf47+duRuzNSKqE8YAQnV0= +google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac h1:ZL/Teoy/ZGnzyrqK/Optxxp2pmVh+fmJ97slxSRyzUg= +google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:+Rvu7ElI+aLzyDQhpHMFMMltsD6m7nqpuWDd2CwJw3k= google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 h1:rcS6EyEaoCO52hQDupoSfrxI3R6C2Tq741is7X8OvnM= google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917/go.mod h1:CmlNWB9lSezaYELKS5Ym1r44VrrbPUa7JTvw+6MbpJ0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac h1:nUQEQmH/csSvFECKYRv6HWEyypysidKl2I6Qpsglq/0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe h1:bQnxqljG/wqi4NTXu2+DJ3n7APcEA882QZ1JvhQAq9o= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=