mirror of
https://github.com/outbackdingo/certificates.git
synced 2026-01-27 10:18:34 +00:00
Merge branch 'master' into fix-1637
This commit is contained in:
17
.github/workflows/actionlint.yml
vendored
Normal file
17
.github/workflows/actionlint.yml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
name: Lint GitHub Actions workflows
|
||||
on:
|
||||
push:
|
||||
workflow_call:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
actionlint:
|
||||
uses: smallstep/workflows/.github/workflows/actionlint.yml@main
|
||||
secrets: inherit
|
||||
17
.github/workflows/dependabot-auto-merge.yml
vendored
17
.github/workflows/dependabot-auto-merge.yml
vendored
@@ -6,17 +6,6 @@ permissions:
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
dependabot:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.actor == 'dependabot[bot]' }}
|
||||
steps:
|
||||
- name: Dependabot metadata
|
||||
id: metadata
|
||||
uses: dependabot/fetch-metadata@v2.0.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}}
|
||||
dependabot-auto-merge:
|
||||
uses: smallstep/workflows/.github/workflows/dependabot-auto-merge.yml@main
|
||||
secrets: inherit
|
||||
|
||||
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
@@ -45,12 +45,12 @@ jobs:
|
||||
echo "DOCKER_TAGS_HSM=${{ env.DOCKER_TAGS_HSM }},${{ env.DOCKER_IMAGE }}:hsm" >> "${GITHUB_ENV}"
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191 # v2.0.8
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: Release ${{ github.ref }}
|
||||
tag_name: ${{ github.ref_name }}
|
||||
name: Release ${{ github.ref_name }}
|
||||
draft: false
|
||||
prerelease: ${{ steps.is_prerelease.outputs.IS_PRERELEASE }}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# This is an example .goreleaser.yml file with some sane defaults.
|
||||
# Make sure to check the documentation at http://goreleaser.com
|
||||
# Documentation: https://goreleaser.com/customization/
|
||||
project_name: step-ca
|
||||
version: 2
|
||||
|
||||
before:
|
||||
hooks:
|
||||
@@ -268,7 +268,7 @@ winget:
|
||||
# Release notes URL.
|
||||
#
|
||||
# Templates: allowed
|
||||
release_notes_url: "https://github.com/smallstep/certificates/releases/tag/{{.Version}}"
|
||||
release_notes_url: "https://github.com/smallstep/certificates/releases/tag/{{ .Tag }}"
|
||||
|
||||
# Create the PR - for testing
|
||||
skip_upload: auto
|
||||
@@ -283,7 +283,7 @@ winget:
|
||||
repository:
|
||||
owner: smallstep
|
||||
name: winget-pkgs
|
||||
branch: step
|
||||
branch: "step-ca-{{.Version}}"
|
||||
|
||||
# Optionally a token can be provided, if it differs from the token
|
||||
# provided to GoReleaser
|
||||
|
||||
88
CHANGELOG.md
88
CHANGELOG.md
@@ -25,6 +25,94 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||
|
||||
---
|
||||
|
||||
## [0.27.2] - 2024-07-18
|
||||
|
||||
### Added
|
||||
|
||||
- `--console` option to default step-ssh config (smallstep/certificates#1931)
|
||||
|
||||
|
||||
## [0.27.1] - 2024-07-12
|
||||
|
||||
### Changed
|
||||
|
||||
- Enable use of strict FQDN with a flag (smallstep/certificates#1926)
|
||||
- This reverses a change in 0.27.0 that required the use of strict FQDNs (smallstep/certificate#1910)
|
||||
|
||||
|
||||
## [0.27.0] - 2024-07-11
|
||||
|
||||
### Added
|
||||
|
||||
- Support for validity windows in templates (smallstep/certificates#1903)
|
||||
- Create identity certificate with host URI when using any provisioner (smallstep/certificates#1922)
|
||||
|
||||
### Changed
|
||||
|
||||
- Do strict DNS lookup on ACME (smallstep/certificates#1910)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Handle bad attestation object in deviceAttest01 validation (smallstep/certificates#1913)
|
||||
|
||||
|
||||
## [0.26.2] - 2024-06-13
|
||||
|
||||
### Added
|
||||
|
||||
- Add provisionerID to ACME accounts (smallstep/certificates#1830)
|
||||
- Enable verifying ACME provisioner using provisionerID if available (smallstep/certificates#1844)
|
||||
- Add methods to Authority to get intermediate certificates (smallstep/certificates#1848)
|
||||
- Add GetX509Signer method (smallstep/certificates#1850)
|
||||
|
||||
### Changed
|
||||
|
||||
- Make ISErrNotFound more flexible (smallstep/certificates#1819)
|
||||
- Log errors using slog.Logger (smallstep/certificates#1849)
|
||||
- Update hardcoded AWS certificates (smallstep/certificates#1881)
|
||||
|
||||
|
||||
## [0.26.1] - 2024-04-22
|
||||
|
||||
### Added
|
||||
|
||||
- Allow configuration of a custom SCEP key manager (smallstep/certificates#1797)
|
||||
|
||||
### Fixed
|
||||
|
||||
- id-scep-failInfoText OID (smallstep/certificates#1794)
|
||||
- CA startup with Vault RA configuration (smallstep/certificates#1803)
|
||||
|
||||
|
||||
## [0.26.0] - 2024-03-28
|
||||
|
||||
### Added
|
||||
|
||||
- [TPM KMS](https://github.com/smallstep/crypto/tree/master/kms/tpmkms) support for CA keys (smallstep/certificates#1772)
|
||||
- Propagation of HTTP request identifier using X-Request-Id header (smallstep/certificates#1743, smallstep/certificates#1542)
|
||||
- Expires header in CRL response (smallstep/certificates#1708)
|
||||
- Support for providing TLS configuration programmatically (smallstep/certificates#1685)
|
||||
- Support for providing external CAS implementation (smallstep/certificates#1684)
|
||||
- AWS `ca-west-1` identity document root certificate (smallstep/certificates#1715)
|
||||
- [COSE RS1](https://www.rfc-editor.org/rfc/rfc8812.html#section-2) as a supported algorithm with ACME `device-attest-01` challenge (smallstep/certificates#1663)
|
||||
|
||||
### Changed
|
||||
|
||||
- In an RA setup, let the CA decide the RA certificate lifetime (smallstep/certificates#1764)
|
||||
- Use Debian Bookworm in Docker containers (smallstep/certificates#1615)
|
||||
- Error message for CSR validation (smallstep/certificates#1665)
|
||||
- Updated dependencies
|
||||
|
||||
### Fixed
|
||||
|
||||
- Stop CA when any of the required servers fails to start (smallstep/certificates#1751). Before the fix, the CA would continue running and only log the server failure when stopped.
|
||||
- Configuration loading errors when not using context were not returned. Fixed in [cli-utils/109](https://github.com/smallstep/cli-utils/pull/109).
|
||||
- HTTP_PROXY and HTTPS_PROXY support for ACME validation client (smallstep/certificates#1658).
|
||||
|
||||
### Security
|
||||
|
||||
- Upgrade to using cosign v2 for signing artifacts
|
||||
|
||||
## [0.25.1] - 2023-11-28
|
||||
|
||||
### Added
|
||||
|
||||
2
Makefile
2
Makefile
@@ -147,7 +147,7 @@ lint:
|
||||
# Install
|
||||
#########################################
|
||||
|
||||
INSTALL_PREFIX?=/usr/
|
||||
INSTALL_PREFIX?=/usr/local/
|
||||
|
||||
install: $(PREFIX)bin/$(BINNAME)
|
||||
$Q install -D $(PREFIX)bin/$(BINNAME) $(DESTDIR)$(INSTALL_PREFIX)bin/$(BINNAME)
|
||||
|
||||
@@ -21,6 +21,7 @@ type Account struct {
|
||||
OrdersURL string `json:"orders"`
|
||||
ExternalAccountBinding interface{} `json:"externalAccountBinding,omitempty"`
|
||||
LocationPrefix string `json:"-"`
|
||||
ProvisionerID string `json:"-"`
|
||||
ProvisionerName string `json:"-"`
|
||||
}
|
||||
|
||||
|
||||
@@ -82,23 +82,23 @@ func NewAccount(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
payload, err := payloadFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
var nar NewAccountRequest
|
||||
if err := json.Unmarshal(payload.value, &nar); err != nil {
|
||||
render.Error(w, acme.WrapError(acme.ErrorMalformedType, err,
|
||||
render.Error(w, r, acme.WrapError(acme.ErrorMalformedType, err,
|
||||
"failed to unmarshal new-account request payload"))
|
||||
return
|
||||
}
|
||||
if err := nar.Validate(); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
prov, err := acmeProvisionerFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -108,26 +108,26 @@ func NewAccount(w http.ResponseWriter, r *http.Request) {
|
||||
var acmeErr *acme.Error
|
||||
if !errors.As(err, &acmeErr) || acmeErr.Status != http.StatusBadRequest {
|
||||
// Something went wrong ...
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Account does not exist //
|
||||
if nar.OnlyReturnExisting {
|
||||
render.Error(w, acme.NewError(acme.ErrorAccountDoesNotExistType,
|
||||
render.Error(w, r, acme.NewError(acme.ErrorAccountDoesNotExistType,
|
||||
"account does not exist"))
|
||||
return
|
||||
}
|
||||
|
||||
jwk, err := jwkFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
eak, err := validateExternalAccountBinding(ctx, &nar)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -136,20 +136,21 @@ func NewAccount(w http.ResponseWriter, r *http.Request) {
|
||||
Contact: nar.Contact,
|
||||
Status: acme.StatusValid,
|
||||
LocationPrefix: getAccountLocationPath(ctx, linker, ""),
|
||||
ProvisionerName: prov.GetName(),
|
||||
ProvisionerID: prov.ID,
|
||||
ProvisionerName: prov.Name,
|
||||
}
|
||||
if err := db.CreateAccount(ctx, acc); err != nil {
|
||||
render.Error(w, acme.WrapErrorISE(err, "error creating account"))
|
||||
render.Error(w, r, acme.WrapErrorISE(err, "error creating account"))
|
||||
return
|
||||
}
|
||||
|
||||
if eak != nil { // means that we have a (valid) External Account Binding key that should be bound, updated and sent in the response
|
||||
if err := eak.BindTo(acc); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
if err := db.UpdateExternalAccountKey(ctx, prov.ID, eak); err != nil {
|
||||
render.Error(w, acme.WrapErrorISE(err, "error updating external account binding key"))
|
||||
render.Error(w, r, acme.WrapErrorISE(err, "error updating external account binding key"))
|
||||
return
|
||||
}
|
||||
acc.ExternalAccountBinding = nar.ExternalAccountBinding
|
||||
@@ -162,7 +163,7 @@ func NewAccount(w http.ResponseWriter, r *http.Request) {
|
||||
linker.LinkAccount(ctx, acc)
|
||||
|
||||
w.Header().Set("Location", getAccountLocationPath(ctx, linker, acc.ID))
|
||||
render.JSONStatus(w, acc, httpStatus)
|
||||
render.JSONStatus(w, r, acc, httpStatus)
|
||||
}
|
||||
|
||||
// GetOrUpdateAccount is the api for updating an ACME account.
|
||||
@@ -173,12 +174,12 @@ func GetOrUpdateAccount(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
acc, err := accountFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
payload, err := payloadFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -187,12 +188,12 @@ func GetOrUpdateAccount(w http.ResponseWriter, r *http.Request) {
|
||||
if !payload.isPostAsGet {
|
||||
var uar UpdateAccountRequest
|
||||
if err := json.Unmarshal(payload.value, &uar); err != nil {
|
||||
render.Error(w, acme.WrapError(acme.ErrorMalformedType, err,
|
||||
render.Error(w, r, acme.WrapError(acme.ErrorMalformedType, err,
|
||||
"failed to unmarshal new-account request payload"))
|
||||
return
|
||||
}
|
||||
if err := uar.Validate(); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
if len(uar.Status) > 0 || len(uar.Contact) > 0 {
|
||||
@@ -203,7 +204,7 @@ func GetOrUpdateAccount(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if err := db.UpdateAccount(ctx, acc); err != nil {
|
||||
render.Error(w, acme.WrapErrorISE(err, "error updating account"))
|
||||
render.Error(w, r, acme.WrapErrorISE(err, "error updating account"))
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -212,7 +213,7 @@ func GetOrUpdateAccount(w http.ResponseWriter, r *http.Request) {
|
||||
linker.LinkAccount(ctx, acc)
|
||||
|
||||
w.Header().Set("Location", linker.GetLink(ctx, acme.AccountLinkType, acc.ID))
|
||||
render.JSON(w, acc)
|
||||
render.JSON(w, r, acc)
|
||||
}
|
||||
|
||||
func logOrdersByAccount(w http.ResponseWriter, oids []string) {
|
||||
@@ -232,23 +233,23 @@ func GetOrdersByAccountID(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
acc, err := accountFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
accID := chi.URLParam(r, "accID")
|
||||
if acc.ID != accID {
|
||||
render.Error(w, acme.NewError(acme.ErrorUnauthorizedType, "account ID '%s' does not match url param '%s'", acc.ID, accID))
|
||||
render.Error(w, r, acme.NewError(acme.ErrorUnauthorizedType, "account ID '%s' does not match url param '%s'", acc.ID, accID))
|
||||
return
|
||||
}
|
||||
|
||||
orders, err := db.GetOrdersByAccountID(ctx, acc.ID)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
linker.LinkOrdersByAccountID(ctx, orders)
|
||||
|
||||
render.JSON(w, orders)
|
||||
render.JSON(w, r, orders)
|
||||
logOrdersByAccount(w, orders)
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/google/uuid"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"go.step.sm/crypto/jose"
|
||||
@@ -66,6 +67,19 @@ func newProv() acme.Provisioner {
|
||||
return p
|
||||
}
|
||||
|
||||
func newProvWithID() acme.Provisioner {
|
||||
// Initialize provisioners
|
||||
p := &provisioner.ACME{
|
||||
ID: uuid.NewString(),
|
||||
Type: "ACME",
|
||||
Name: "test@acme-<test>provisioner.com",
|
||||
}
|
||||
if err := p.Init(provisioner.Config{Claims: globalProvisionerClaims}); err != nil {
|
||||
fmt.Printf("%v", err)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func newProvWithOptions(options *provisioner.Options) acme.Provisioner {
|
||||
// Initialize provisioners
|
||||
p := &provisioner.ACME{
|
||||
|
||||
@@ -223,13 +223,13 @@ func GetDirectory(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
acmeProv, err := acmeProvisionerFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
linker := acme.MustLinkerFromContext(ctx)
|
||||
|
||||
render.JSON(w, &Directory{
|
||||
render.JSON(w, r, &Directory{
|
||||
NewNonce: linker.GetLink(ctx, acme.NewNonceLinkType),
|
||||
NewAccount: linker.GetLink(ctx, acme.NewAccountLinkType),
|
||||
NewOrder: linker.GetLink(ctx, acme.NewOrderLinkType),
|
||||
@@ -273,8 +273,8 @@ func shouldAddMetaObject(p *provisioner.ACME) bool {
|
||||
|
||||
// NotImplemented returns a 501 and is generally a placeholder for functionality which
|
||||
// MAY be added at some point in the future but is not in any way a guarantee of such.
|
||||
func NotImplemented(w http.ResponseWriter, _ *http.Request) {
|
||||
render.Error(w, acme.NewError(acme.ErrorNotImplementedType, "this API is not implemented"))
|
||||
func NotImplemented(w http.ResponseWriter, r *http.Request) {
|
||||
render.Error(w, r, acme.NewError(acme.ErrorNotImplementedType, "this API is not implemented"))
|
||||
}
|
||||
|
||||
// GetAuthorization ACME api for retrieving an Authz.
|
||||
@@ -285,28 +285,28 @@ func GetAuthorization(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
acc, err := accountFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
az, err := db.GetAuthorization(ctx, chi.URLParam(r, "authzID"))
|
||||
if err != nil {
|
||||
render.Error(w, acme.WrapErrorISE(err, "error retrieving authorization"))
|
||||
render.Error(w, r, acme.WrapErrorISE(err, "error retrieving authorization"))
|
||||
return
|
||||
}
|
||||
if acc.ID != az.AccountID {
|
||||
render.Error(w, acme.NewError(acme.ErrorUnauthorizedType,
|
||||
render.Error(w, r, acme.NewError(acme.ErrorUnauthorizedType,
|
||||
"account '%s' does not own authorization '%s'", acc.ID, az.ID))
|
||||
return
|
||||
}
|
||||
if err = az.UpdateStatus(ctx, db); err != nil {
|
||||
render.Error(w, acme.WrapErrorISE(err, "error updating authorization status"))
|
||||
render.Error(w, r, acme.WrapErrorISE(err, "error updating authorization status"))
|
||||
return
|
||||
}
|
||||
|
||||
linker.LinkAuthorization(ctx, az)
|
||||
|
||||
w.Header().Set("Location", linker.GetLink(ctx, acme.AuthzLinkType, az.ID))
|
||||
render.JSON(w, az)
|
||||
render.JSON(w, r, az)
|
||||
}
|
||||
|
||||
// GetChallenge ACME api for retrieving a Challenge.
|
||||
@@ -317,13 +317,13 @@ func GetChallenge(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
acc, err := accountFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
payload, err := payloadFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -336,22 +336,22 @@ func GetChallenge(w http.ResponseWriter, r *http.Request) {
|
||||
azID := chi.URLParam(r, "authzID")
|
||||
ch, err := db.GetChallenge(ctx, chi.URLParam(r, "chID"), azID)
|
||||
if err != nil {
|
||||
render.Error(w, acme.WrapErrorISE(err, "error retrieving challenge"))
|
||||
render.Error(w, r, acme.WrapErrorISE(err, "error retrieving challenge"))
|
||||
return
|
||||
}
|
||||
ch.AuthorizationID = azID
|
||||
if acc.ID != ch.AccountID {
|
||||
render.Error(w, acme.NewError(acme.ErrorUnauthorizedType,
|
||||
render.Error(w, r, acme.NewError(acme.ErrorUnauthorizedType,
|
||||
"account '%s' does not own challenge '%s'", acc.ID, ch.ID))
|
||||
return
|
||||
}
|
||||
jwk, err := jwkFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
if err = ch.Validate(ctx, db, jwk, payload.value); err != nil {
|
||||
render.Error(w, acme.WrapErrorISE(err, "error validating challenge"))
|
||||
render.Error(w, r, acme.WrapErrorISE(err, "error validating challenge"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -359,7 +359,7 @@ func GetChallenge(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
w.Header().Add("Link", link(linker.GetLink(ctx, acme.AuthzLinkType, azID), "up"))
|
||||
w.Header().Set("Location", linker.GetLink(ctx, acme.ChallengeLinkType, azID, ch.ID))
|
||||
render.JSON(w, ch)
|
||||
render.JSON(w, r, ch)
|
||||
}
|
||||
|
||||
// GetCertificate ACME api for retrieving a Certificate.
|
||||
@@ -369,18 +369,18 @@ func GetCertificate(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
acc, err := accountFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
certID := chi.URLParam(r, "certID")
|
||||
cert, err := db.GetCertificate(ctx, certID)
|
||||
if err != nil {
|
||||
render.Error(w, acme.WrapErrorISE(err, "error retrieving certificate"))
|
||||
render.Error(w, r, acme.WrapErrorISE(err, "error retrieving certificate"))
|
||||
return
|
||||
}
|
||||
if cert.AccountID != acc.ID {
|
||||
render.Error(w, acme.NewError(acme.ErrorUnauthorizedType,
|
||||
render.Error(w, r, acme.NewError(acme.ErrorUnauthorizedType,
|
||||
"account '%s' does not own certificate '%s'", acc.ID, certID))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ func addNonce(next nextHTTP) nextHTTP {
|
||||
db := acme.MustDatabaseFromContext(r.Context())
|
||||
nonce, err := db.CreateNonce(r.Context())
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Replay-Nonce", string(nonce))
|
||||
@@ -64,7 +64,7 @@ func verifyContentType(next nextHTTP) nextHTTP {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
p, err := provisionerFromContext(r.Context())
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ func verifyContentType(next nextHTTP) nextHTTP {
|
||||
return
|
||||
}
|
||||
}
|
||||
render.Error(w, acme.NewError(acme.ErrorMalformedType,
|
||||
render.Error(w, r, acme.NewError(acme.ErrorMalformedType,
|
||||
"expected content-type to be in %s, but got %s", expected, ct))
|
||||
}
|
||||
}
|
||||
@@ -98,12 +98,12 @@ func parseJWS(next nextHTTP) nextHTTP {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
render.Error(w, acme.WrapErrorISE(err, "failed to read request body"))
|
||||
render.Error(w, r, acme.WrapErrorISE(err, "failed to read request body"))
|
||||
return
|
||||
}
|
||||
jws, err := jose.ParseJWS(string(body))
|
||||
if err != nil {
|
||||
render.Error(w, acme.WrapError(acme.ErrorMalformedType, err, "failed to parse JWS from request body"))
|
||||
render.Error(w, r, acme.WrapError(acme.ErrorMalformedType, err, "failed to parse JWS from request body"))
|
||||
return
|
||||
}
|
||||
ctx := context.WithValue(r.Context(), jwsContextKey, jws)
|
||||
@@ -133,15 +133,15 @@ func validateJWS(next nextHTTP) nextHTTP {
|
||||
|
||||
jws, err := jwsFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
if len(jws.Signatures) == 0 {
|
||||
render.Error(w, acme.NewError(acme.ErrorMalformedType, "request body does not contain a signature"))
|
||||
render.Error(w, r, acme.NewError(acme.ErrorMalformedType, "request body does not contain a signature"))
|
||||
return
|
||||
}
|
||||
if len(jws.Signatures) > 1 {
|
||||
render.Error(w, acme.NewError(acme.ErrorMalformedType, "request body contains more than one signature"))
|
||||
render.Error(w, r, acme.NewError(acme.ErrorMalformedType, "request body contains more than one signature"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ func validateJWS(next nextHTTP) nextHTTP {
|
||||
uh.Algorithm != "" ||
|
||||
uh.Nonce != "" ||
|
||||
len(uh.ExtraHeaders) > 0 {
|
||||
render.Error(w, acme.NewError(acme.ErrorMalformedType, "unprotected header must not be used"))
|
||||
render.Error(w, r, acme.NewError(acme.ErrorMalformedType, "unprotected header must not be used"))
|
||||
return
|
||||
}
|
||||
hdr := sig.Protected
|
||||
@@ -162,13 +162,13 @@ func validateJWS(next nextHTTP) nextHTTP {
|
||||
switch k := hdr.JSONWebKey.Key.(type) {
|
||||
case *rsa.PublicKey:
|
||||
if k.Size() < keyutil.MinRSAKeyBytes {
|
||||
render.Error(w, acme.NewError(acme.ErrorMalformedType,
|
||||
render.Error(w, r, acme.NewError(acme.ErrorMalformedType,
|
||||
"rsa keys must be at least %d bits (%d bytes) in size",
|
||||
8*keyutil.MinRSAKeyBytes, keyutil.MinRSAKeyBytes))
|
||||
return
|
||||
}
|
||||
default:
|
||||
render.Error(w, acme.NewError(acme.ErrorMalformedType,
|
||||
render.Error(w, r, acme.NewError(acme.ErrorMalformedType,
|
||||
"jws key type and algorithm do not match"))
|
||||
return
|
||||
}
|
||||
@@ -176,35 +176,35 @@ func validateJWS(next nextHTTP) nextHTTP {
|
||||
case jose.ES256, jose.ES384, jose.ES512, jose.EdDSA:
|
||||
// we good
|
||||
default:
|
||||
render.Error(w, acme.NewError(acme.ErrorBadSignatureAlgorithmType, "unsuitable algorithm: %s", hdr.Algorithm))
|
||||
render.Error(w, r, acme.NewError(acme.ErrorBadSignatureAlgorithmType, "unsuitable algorithm: %s", hdr.Algorithm))
|
||||
return
|
||||
}
|
||||
|
||||
// Check the validity/freshness of the Nonce.
|
||||
if err := db.DeleteNonce(ctx, acme.Nonce(hdr.Nonce)); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Check that the JWS url matches the requested url.
|
||||
jwsURL, ok := hdr.ExtraHeaders["url"].(string)
|
||||
if !ok {
|
||||
render.Error(w, acme.NewError(acme.ErrorMalformedType, "jws missing url protected header"))
|
||||
render.Error(w, r, acme.NewError(acme.ErrorMalformedType, "jws missing url protected header"))
|
||||
return
|
||||
}
|
||||
reqURL := &url.URL{Scheme: "https", Host: r.Host, Path: r.URL.Path}
|
||||
if jwsURL != reqURL.String() {
|
||||
render.Error(w, acme.NewError(acme.ErrorMalformedType,
|
||||
render.Error(w, r, acme.NewError(acme.ErrorMalformedType,
|
||||
"url header in JWS (%s) does not match request url (%s)", jwsURL, reqURL))
|
||||
return
|
||||
}
|
||||
|
||||
if hdr.JSONWebKey != nil && hdr.KeyID != "" {
|
||||
render.Error(w, acme.NewError(acme.ErrorMalformedType, "jwk and kid are mutually exclusive"))
|
||||
render.Error(w, r, acme.NewError(acme.ErrorMalformedType, "jwk and kid are mutually exclusive"))
|
||||
return
|
||||
}
|
||||
if hdr.JSONWebKey == nil && hdr.KeyID == "" {
|
||||
render.Error(w, acme.NewError(acme.ErrorMalformedType, "either jwk or kid must be defined in jws protected header"))
|
||||
render.Error(w, r, acme.NewError(acme.ErrorMalformedType, "either jwk or kid must be defined in jws protected header"))
|
||||
return
|
||||
}
|
||||
next(w, r)
|
||||
@@ -221,23 +221,23 @@ func extractJWK(next nextHTTP) nextHTTP {
|
||||
|
||||
jws, err := jwsFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
jwk := jws.Signatures[0].Protected.JSONWebKey
|
||||
if jwk == nil {
|
||||
render.Error(w, acme.NewError(acme.ErrorMalformedType, "jwk expected in protected header"))
|
||||
render.Error(w, r, acme.NewError(acme.ErrorMalformedType, "jwk expected in protected header"))
|
||||
return
|
||||
}
|
||||
if !jwk.Valid() {
|
||||
render.Error(w, acme.NewError(acme.ErrorMalformedType, "invalid jwk in protected header"))
|
||||
render.Error(w, r, acme.NewError(acme.ErrorMalformedType, "invalid jwk in protected header"))
|
||||
return
|
||||
}
|
||||
|
||||
// Overwrite KeyID with the JWK thumbprint.
|
||||
jwk.KeyID, err = acme.KeyToID(jwk)
|
||||
if err != nil {
|
||||
render.Error(w, acme.WrapErrorISE(err, "error getting KeyID from JWK"))
|
||||
render.Error(w, r, acme.WrapErrorISE(err, "error getting KeyID from JWK"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -247,15 +247,15 @@ func extractJWK(next nextHTTP) nextHTTP {
|
||||
// Get Account OR continue to generate a new one OR continue Revoke with certificate private key
|
||||
acc, err := db.GetAccountByKeyID(ctx, jwk.KeyID)
|
||||
switch {
|
||||
case errors.Is(err, acme.ErrNotFound):
|
||||
case acme.IsErrNotFound(err):
|
||||
// For NewAccount and Revoke requests ...
|
||||
break
|
||||
case err != nil:
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
default:
|
||||
if !acc.IsValid() {
|
||||
render.Error(w, acme.NewError(acme.ErrorUnauthorizedType, "account is not active"))
|
||||
render.Error(w, r, acme.NewError(acme.ErrorUnauthorizedType, "account is not active"))
|
||||
return
|
||||
}
|
||||
ctx = context.WithValue(ctx, accContextKey, acc)
|
||||
@@ -274,11 +274,11 @@ func checkPrerequisites(next nextHTTP) nextHTTP {
|
||||
if ok {
|
||||
ok, err := checkFunc(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, acme.WrapErrorISE(err, "error checking acme provisioner prerequisites"))
|
||||
render.Error(w, r, acme.WrapErrorISE(err, "error checking acme provisioner prerequisites"))
|
||||
return
|
||||
}
|
||||
if !ok {
|
||||
render.Error(w, acme.NewError(acme.ErrorNotImplementedType, "acme provisioner configuration lacks prerequisites"))
|
||||
render.Error(w, r, acme.NewError(acme.ErrorNotImplementedType, "acme provisioner configuration lacks prerequisites"))
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -296,13 +296,13 @@ func lookupJWK(next nextHTTP) nextHTTP {
|
||||
|
||||
jws, err := jwsFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
kid := jws.Signatures[0].Protected.KeyID
|
||||
if kid == "" {
|
||||
render.Error(w, acme.NewError(acme.ErrorMalformedType, "signature missing 'kid'"))
|
||||
render.Error(w, r, acme.NewError(acme.ErrorMalformedType, "signature missing 'kid'"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -310,14 +310,14 @@ func lookupJWK(next nextHTTP) nextHTTP {
|
||||
acc, err := db.GetAccount(ctx, accID)
|
||||
switch {
|
||||
case acme.IsErrNotFound(err):
|
||||
render.Error(w, acme.NewError(acme.ErrorAccountDoesNotExistType, "account with ID '%s' not found", accID))
|
||||
render.Error(w, r, acme.NewError(acme.ErrorAccountDoesNotExistType, "account with ID '%s' not found", accID))
|
||||
return
|
||||
case err != nil:
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
default:
|
||||
if !acc.IsValid() {
|
||||
render.Error(w, acme.NewError(acme.ErrorUnauthorizedType, "account is not active"))
|
||||
render.Error(w, r, acme.NewError(acme.ErrorUnauthorizedType, "account is not active"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -325,7 +325,7 @@ func lookupJWK(next nextHTTP) nextHTTP {
|
||||
if kid != storedLocation {
|
||||
// ACME accounts should have a stored location equivalent to the
|
||||
// kid in the ACME request.
|
||||
render.Error(w, acme.NewError(acme.ErrorUnauthorizedType,
|
||||
render.Error(w, r, acme.NewError(acme.ErrorUnauthorizedType,
|
||||
"kid does not match stored account location; expected %s, but got %s",
|
||||
storedLocation, kid))
|
||||
return
|
||||
@@ -334,14 +334,16 @@ func lookupJWK(next nextHTTP) nextHTTP {
|
||||
// Verify that the provisioner with which the account was created
|
||||
// matches the provisioner in the request URL.
|
||||
reqProv := acme.MustProvisionerFromContext(ctx)
|
||||
reqProvName := reqProv.GetName()
|
||||
accProvName := acc.ProvisionerName
|
||||
if reqProvName != accProvName {
|
||||
// Provisioner in the URL must match the provisioner with
|
||||
// which the account was created.
|
||||
render.Error(w, acme.NewError(acme.ErrorUnauthorizedType,
|
||||
switch {
|
||||
case acc.ProvisionerID == "" && acc.ProvisionerName != reqProv.GetName():
|
||||
render.Error(w, r, acme.NewError(acme.ErrorUnauthorizedType,
|
||||
"account provisioner does not match requested provisioner; account provisioner = %s, requested provisioner = %s",
|
||||
accProvName, reqProvName))
|
||||
acc.ProvisionerName, reqProv.GetName()))
|
||||
return
|
||||
case acc.ProvisionerID != "" && acc.ProvisionerID != reqProv.GetID():
|
||||
render.Error(w, r, acme.NewError(acme.ErrorUnauthorizedType,
|
||||
"account provisioner does not match requested provisioner; account provisioner = %s, requested provisioner = %s",
|
||||
acc.ProvisionerID, reqProv.GetID()))
|
||||
return
|
||||
}
|
||||
} else {
|
||||
@@ -353,7 +355,7 @@ func lookupJWK(next nextHTTP) nextHTTP {
|
||||
linker := acme.MustLinkerFromContext(ctx)
|
||||
kidPrefix := linker.GetLink(ctx, acme.AccountLinkType, "")
|
||||
if !strings.HasPrefix(kid, kidPrefix) {
|
||||
render.Error(w, acme.NewError(acme.ErrorMalformedType,
|
||||
render.Error(w, r, acme.NewError(acme.ErrorMalformedType,
|
||||
"kid does not have required prefix; expected %s, but got %s",
|
||||
kidPrefix, kid))
|
||||
return
|
||||
@@ -374,7 +376,7 @@ func extractOrLookupJWK(next nextHTTP) nextHTTP {
|
||||
ctx := r.Context()
|
||||
jws, err := jwsFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -410,16 +412,16 @@ func verifyAndExtractJWSPayload(next nextHTTP) nextHTTP {
|
||||
ctx := r.Context()
|
||||
jws, err := jwsFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
jwk, err := jwkFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
if jwk.Algorithm != "" && jwk.Algorithm != jws.Signatures[0].Protected.Algorithm {
|
||||
render.Error(w, acme.NewError(acme.ErrorMalformedType, "verifier and signature algorithm do not match"))
|
||||
render.Error(w, r, acme.NewError(acme.ErrorMalformedType, "verifier and signature algorithm do not match"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -428,11 +430,11 @@ func verifyAndExtractJWSPayload(next nextHTTP) nextHTTP {
|
||||
case errors.Is(err, jose.ErrCryptoFailure):
|
||||
payload, err = retryVerificationWithPatchedSignatures(jws, jwk)
|
||||
if err != nil {
|
||||
render.Error(w, acme.WrapError(acme.ErrorMalformedType, err, "error verifying jws with patched signature(s)"))
|
||||
render.Error(w, r, acme.WrapError(acme.ErrorMalformedType, err, "error verifying jws with patched signature(s)"))
|
||||
return
|
||||
}
|
||||
case err != nil:
|
||||
render.Error(w, acme.WrapError(acme.ErrorMalformedType, err, "error verifying jws"))
|
||||
render.Error(w, r, acme.WrapError(acme.ErrorMalformedType, err, "error verifying jws"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -549,11 +551,11 @@ func isPostAsGet(next nextHTTP) nextHTTP {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
payload, err := payloadFromContext(r.Context())
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
if !payload.isPostAsGet {
|
||||
render.Error(w, acme.NewError(acme.ErrorMalformedType, "expected POST-as-GET"))
|
||||
render.Error(w, r, acme.NewError(acme.ErrorMalformedType, "expected POST-as-GET"))
|
||||
return
|
||||
}
|
||||
next(w, r)
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/smallstep/assert"
|
||||
"github.com/smallstep/certificates/acme"
|
||||
tassert "github.com/stretchr/testify/assert"
|
||||
@@ -831,8 +832,37 @@ func TestHandler_lookupJWK(t *testing.T) {
|
||||
},
|
||||
statusCode: http.StatusUnauthorized,
|
||||
err: acme.NewError(acme.ErrorUnauthorizedType,
|
||||
"account provisioner does not match requested provisioner; account provisioner = %s, reqested provisioner = %s",
|
||||
prov.GetName(), "other"),
|
||||
"account provisioner does not match requested provisioner; account provisioner = %s, requested provisioner = %s",
|
||||
"other", prov.GetName()),
|
||||
}
|
||||
},
|
||||
"fail/account-with-location-prefix/bad-provisioner-id": func(t *testing.T) test {
|
||||
p := newProvWithID()
|
||||
acc := &acme.Account{LocationPrefix: prefix + accID, Status: "valid", Key: jwk, ProvisionerID: uuid.NewString()}
|
||||
ctx := acme.NewProvisionerContext(context.Background(), p)
|
||||
ctx = context.WithValue(ctx, jwsContextKey, parsedJWS)
|
||||
return test{
|
||||
linker: acme.NewLinker("test.ca.smallstep.com", "acme"),
|
||||
db: &acme.MockDB{
|
||||
MockGetAccount: func(ctx context.Context, id string) (*acme.Account, error) {
|
||||
assert.Equals(t, id, accID)
|
||||
return acc, nil
|
||||
},
|
||||
},
|
||||
ctx: ctx,
|
||||
next: func(w http.ResponseWriter, r *http.Request) {
|
||||
_acc, err := accountFromContext(r.Context())
|
||||
assert.FatalError(t, err)
|
||||
assert.Equals(t, _acc, acc)
|
||||
_jwk, err := jwkFromContext(r.Context())
|
||||
assert.FatalError(t, err)
|
||||
assert.Equals(t, _jwk, jwk)
|
||||
w.Write(testBody)
|
||||
},
|
||||
statusCode: http.StatusUnauthorized,
|
||||
err: acme.NewError(acme.ErrorUnauthorizedType,
|
||||
"account provisioner does not match requested provisioner; account provisioner = %s, requested provisioner = %s",
|
||||
acc.ProvisionerID, p.GetID()),
|
||||
}
|
||||
},
|
||||
"ok/account-with-location-prefix": func(t *testing.T) test {
|
||||
@@ -885,6 +915,32 @@ func TestHandler_lookupJWK(t *testing.T) {
|
||||
statusCode: 200,
|
||||
}
|
||||
},
|
||||
"ok/account-with-provisioner-id": func(t *testing.T) test {
|
||||
p := newProvWithID()
|
||||
acc := &acme.Account{LocationPrefix: prefix + accID, Status: "valid", Key: jwk, ProvisionerID: p.GetID()}
|
||||
ctx := acme.NewProvisionerContext(context.Background(), p)
|
||||
ctx = context.WithValue(ctx, jwsContextKey, parsedJWS)
|
||||
return test{
|
||||
linker: acme.NewLinker("test.ca.smallstep.com", "acme"),
|
||||
db: &acme.MockDB{
|
||||
MockGetAccount: func(ctx context.Context, id string) (*acme.Account, error) {
|
||||
assert.Equals(t, id, accID)
|
||||
return acc, nil
|
||||
},
|
||||
},
|
||||
ctx: ctx,
|
||||
next: func(w http.ResponseWriter, r *http.Request) {
|
||||
_acc, err := accountFromContext(r.Context())
|
||||
assert.FatalError(t, err)
|
||||
assert.Equals(t, _acc, acc)
|
||||
_jwk, err := jwkFromContext(r.Context())
|
||||
assert.FatalError(t, err)
|
||||
assert.Equals(t, _jwk, jwk)
|
||||
w.Write(testBody)
|
||||
},
|
||||
statusCode: 200,
|
||||
}
|
||||
},
|
||||
}
|
||||
for name, run := range tests {
|
||||
tc := run(t)
|
||||
|
||||
@@ -99,29 +99,29 @@ func NewOrder(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
acc, err := accountFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
prov, err := provisionerFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
payload, err := payloadFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
var nor NewOrderRequest
|
||||
if err := json.Unmarshal(payload.value, &nor); err != nil {
|
||||
render.Error(w, acme.WrapError(acme.ErrorMalformedType, err,
|
||||
render.Error(w, r, acme.WrapError(acme.ErrorMalformedType, err,
|
||||
"failed to unmarshal new-order request payload"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := nor.Validate(); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -130,39 +130,39 @@ func NewOrder(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
acmeProv, err := acmeProvisionerFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
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, r, acme.WrapErrorISE(err, "error retrieving external account binding key"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
acmePolicy, err := newACMEPolicyEngine(eak)
|
||||
if err != nil {
|
||||
render.Error(w, acme.WrapErrorISE(err, "error creating ACME policy engine"))
|
||||
render.Error(w, r, acme.WrapErrorISE(err, "error creating ACME policy engine"))
|
||||
return
|
||||
}
|
||||
|
||||
for _, identifier := range nor.Identifiers {
|
||||
// evaluate the ACME account level policy
|
||||
if err = isIdentifierAllowed(acmePolicy, identifier); err != nil {
|
||||
render.Error(w, acme.WrapError(acme.ErrorRejectedIdentifierType, err, "not authorized"))
|
||||
render.Error(w, r, acme.WrapError(acme.ErrorRejectedIdentifierType, err, "not authorized"))
|
||||
return
|
||||
}
|
||||
// evaluate the provisioner level policy
|
||||
orderIdentifier := provisioner.ACMEIdentifier{Type: provisioner.ACMEIdentifierType(identifier.Type), Value: identifier.Value}
|
||||
if err = prov.AuthorizeOrderIdentifier(ctx, orderIdentifier); err != nil {
|
||||
render.Error(w, acme.WrapError(acme.ErrorRejectedIdentifierType, err, "not authorized"))
|
||||
render.Error(w, r, acme.WrapError(acme.ErrorRejectedIdentifierType, err, "not authorized"))
|
||||
return
|
||||
}
|
||||
// evaluate the authority level policy
|
||||
if err = ca.AreSANsAllowed(ctx, []string{identifier.Value}); err != nil {
|
||||
render.Error(w, acme.WrapError(acme.ErrorRejectedIdentifierType, err, "not authorized"))
|
||||
render.Error(w, r, acme.WrapError(acme.ErrorRejectedIdentifierType, err, "not authorized"))
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -188,7 +188,7 @@ func NewOrder(w http.ResponseWriter, r *http.Request) {
|
||||
Status: acme.StatusPending,
|
||||
}
|
||||
if err := newAuthorization(ctx, az); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
o.AuthorizationIDs[i] = az.ID
|
||||
@@ -207,14 +207,14 @@ func NewOrder(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if err := db.CreateOrder(ctx, o); err != nil {
|
||||
render.Error(w, acme.WrapErrorISE(err, "error creating order"))
|
||||
render.Error(w, r, acme.WrapErrorISE(err, "error creating order"))
|
||||
return
|
||||
}
|
||||
|
||||
linker.LinkOrder(ctx, o)
|
||||
|
||||
w.Header().Set("Location", linker.GetLink(ctx, acme.OrderLinkType, o.ID))
|
||||
render.JSONStatus(w, o, http.StatusCreated)
|
||||
render.JSONStatus(w, r, o, http.StatusCreated)
|
||||
}
|
||||
|
||||
func isIdentifierAllowed(acmePolicy policy.X509Policy, identifier acme.Identifier) error {
|
||||
@@ -226,6 +226,7 @@ func isIdentifierAllowed(acmePolicy policy.X509Policy, identifier acme.Identifie
|
||||
|
||||
func newACMEPolicyEngine(eak *acme.ExternalAccountKey) (policy.X509Policy, error) {
|
||||
if eak == nil {
|
||||
//nolint:nilnil,nolintlint // expected values
|
||||
return nil, nil
|
||||
}
|
||||
return policy.NewX509PolicyEngine(eak.Policy)
|
||||
@@ -288,39 +289,39 @@ func GetOrder(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
acc, err := accountFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
prov, err := provisionerFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
o, err := db.GetOrder(ctx, chi.URLParam(r, "ordID"))
|
||||
if err != nil {
|
||||
render.Error(w, acme.WrapErrorISE(err, "error retrieving order"))
|
||||
render.Error(w, r, acme.WrapErrorISE(err, "error retrieving order"))
|
||||
return
|
||||
}
|
||||
if acc.ID != o.AccountID {
|
||||
render.Error(w, acme.NewError(acme.ErrorUnauthorizedType,
|
||||
render.Error(w, r, acme.NewError(acme.ErrorUnauthorizedType,
|
||||
"account '%s' does not own order '%s'", acc.ID, o.ID))
|
||||
return
|
||||
}
|
||||
if prov.GetID() != o.ProvisionerID {
|
||||
render.Error(w, acme.NewError(acme.ErrorUnauthorizedType,
|
||||
render.Error(w, r, acme.NewError(acme.ErrorUnauthorizedType,
|
||||
"provisioner '%s' does not own order '%s'", prov.GetID(), o.ID))
|
||||
return
|
||||
}
|
||||
if err = o.UpdateStatus(ctx, db); err != nil {
|
||||
render.Error(w, acme.WrapErrorISE(err, "error updating order status"))
|
||||
render.Error(w, r, acme.WrapErrorISE(err, "error updating order status"))
|
||||
return
|
||||
}
|
||||
|
||||
linker.LinkOrder(ctx, o)
|
||||
|
||||
w.Header().Set("Location", linker.GetLink(ctx, acme.OrderLinkType, o.ID))
|
||||
render.JSON(w, o)
|
||||
render.JSON(w, r, o)
|
||||
}
|
||||
|
||||
// FinalizeOrder attempts to finalize an order and create a certificate.
|
||||
@@ -331,56 +332,56 @@ func FinalizeOrder(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
acc, err := accountFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
prov, err := provisionerFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
payload, err := payloadFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
var fr FinalizeRequest
|
||||
if err := json.Unmarshal(payload.value, &fr); err != nil {
|
||||
render.Error(w, acme.WrapError(acme.ErrorMalformedType, err,
|
||||
render.Error(w, r, acme.WrapError(acme.ErrorMalformedType, err,
|
||||
"failed to unmarshal finalize-order request payload"))
|
||||
return
|
||||
}
|
||||
if err := fr.Validate(); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
o, err := db.GetOrder(ctx, chi.URLParam(r, "ordID"))
|
||||
if err != nil {
|
||||
render.Error(w, acme.WrapErrorISE(err, "error retrieving order"))
|
||||
render.Error(w, r, acme.WrapErrorISE(err, "error retrieving order"))
|
||||
return
|
||||
}
|
||||
if acc.ID != o.AccountID {
|
||||
render.Error(w, acme.NewError(acme.ErrorUnauthorizedType,
|
||||
render.Error(w, r, acme.NewError(acme.ErrorUnauthorizedType,
|
||||
"account '%s' does not own order '%s'", acc.ID, o.ID))
|
||||
return
|
||||
}
|
||||
if prov.GetID() != o.ProvisionerID {
|
||||
render.Error(w, acme.NewError(acme.ErrorUnauthorizedType,
|
||||
render.Error(w, r, acme.NewError(acme.ErrorUnauthorizedType,
|
||||
"provisioner '%s' does not own order '%s'", prov.GetID(), o.ID))
|
||||
return
|
||||
}
|
||||
|
||||
ca := mustAuthority(ctx)
|
||||
if err = o.Finalize(ctx, db, fr.csr, ca, prov); err != nil {
|
||||
render.Error(w, acme.WrapErrorISE(err, "error finalizing order"))
|
||||
render.Error(w, r, acme.WrapErrorISE(err, "error finalizing order"))
|
||||
return
|
||||
}
|
||||
|
||||
linker.LinkOrder(ctx, o)
|
||||
|
||||
w.Header().Set("Location", linker.GetLink(ctx, acme.OrderLinkType, o.ID))
|
||||
render.JSON(w, o)
|
||||
render.JSON(w, r, o)
|
||||
}
|
||||
|
||||
// challengeTypes determines the types of challenges that should be used
|
||||
|
||||
@@ -33,65 +33,65 @@ func RevokeCert(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
jws, err := jwsFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
prov, err := provisionerFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
payload, err := payloadFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
var p revokePayload
|
||||
err = json.Unmarshal(payload.value, &p)
|
||||
if err != nil {
|
||||
render.Error(w, acme.WrapErrorISE(err, "error unmarshaling payload"))
|
||||
render.Error(w, r, acme.WrapErrorISE(err, "error unmarshaling payload"))
|
||||
return
|
||||
}
|
||||
|
||||
certBytes, err := base64.RawURLEncoding.DecodeString(p.Certificate)
|
||||
if err != nil {
|
||||
// in this case the most likely cause is a client that didn't properly encode the certificate
|
||||
render.Error(w, acme.WrapError(acme.ErrorMalformedType, err, "error base64url decoding payload certificate property"))
|
||||
render.Error(w, r, acme.WrapError(acme.ErrorMalformedType, err, "error base64url decoding payload certificate property"))
|
||||
return
|
||||
}
|
||||
|
||||
certToBeRevoked, err := x509.ParseCertificate(certBytes)
|
||||
if err != nil {
|
||||
// in this case a client may have encoded something different than a certificate
|
||||
render.Error(w, acme.WrapError(acme.ErrorMalformedType, err, "error parsing certificate"))
|
||||
render.Error(w, r, acme.WrapError(acme.ErrorMalformedType, err, "error parsing certificate"))
|
||||
return
|
||||
}
|
||||
|
||||
serial := certToBeRevoked.SerialNumber.String()
|
||||
dbCert, err := db.GetCertificateBySerial(ctx, serial)
|
||||
if err != nil {
|
||||
render.Error(w, acme.WrapErrorISE(err, "error retrieving certificate by serial"))
|
||||
render.Error(w, r, acme.WrapErrorISE(err, "error retrieving certificate by serial"))
|
||||
return
|
||||
}
|
||||
|
||||
if !bytes.Equal(dbCert.Leaf.Raw, certToBeRevoked.Raw) {
|
||||
// this should never happen
|
||||
render.Error(w, acme.NewErrorISE("certificate raw bytes are not equal"))
|
||||
render.Error(w, r, acme.NewErrorISE("certificate raw bytes are not equal"))
|
||||
return
|
||||
}
|
||||
|
||||
if shouldCheckAccountFrom(jws) {
|
||||
account, err := accountFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
acmeErr := isAccountAuthorized(ctx, dbCert, certToBeRevoked, account)
|
||||
if acmeErr != nil {
|
||||
render.Error(w, acmeErr)
|
||||
render.Error(w, r, acmeErr)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
@@ -100,7 +100,7 @@ func RevokeCert(w http.ResponseWriter, r *http.Request) {
|
||||
_, err := jws.Verify(certToBeRevoked.PublicKey)
|
||||
if err != nil {
|
||||
// TODO(hs): possible to determine an error vs. unauthorized and thus provide an ISE vs. Unauthorized?
|
||||
render.Error(w, wrapUnauthorizedError(certToBeRevoked, nil, "verification of jws using certificate public key failed", err))
|
||||
render.Error(w, r, wrapUnauthorizedError(certToBeRevoked, nil, "verification of jws using certificate public key failed", err))
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -108,19 +108,19 @@ func RevokeCert(w http.ResponseWriter, r *http.Request) {
|
||||
ca := mustAuthority(ctx)
|
||||
hasBeenRevokedBefore, err := ca.IsRevoked(serial)
|
||||
if err != nil {
|
||||
render.Error(w, acme.WrapErrorISE(err, "error retrieving revocation status of certificate"))
|
||||
render.Error(w, r, acme.WrapErrorISE(err, "error retrieving revocation status of certificate"))
|
||||
return
|
||||
}
|
||||
|
||||
if hasBeenRevokedBefore {
|
||||
render.Error(w, acme.NewError(acme.ErrorAlreadyRevokedType, "certificate was already revoked"))
|
||||
render.Error(w, r, acme.NewError(acme.ErrorAlreadyRevokedType, "certificate was already revoked"))
|
||||
return
|
||||
}
|
||||
|
||||
reasonCode := p.ReasonCode
|
||||
acmeErr := validateReasonCode(reasonCode)
|
||||
if acmeErr != nil {
|
||||
render.Error(w, acmeErr)
|
||||
render.Error(w, r, acmeErr)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -128,14 +128,14 @@ func RevokeCert(w http.ResponseWriter, r *http.Request) {
|
||||
ctx = provisioner.NewContextWithMethod(ctx, provisioner.RevokeMethod)
|
||||
err = prov.AuthorizeRevoke(ctx, "")
|
||||
if err != nil {
|
||||
render.Error(w, acme.WrapErrorISE(err, "error authorizing revocation on provisioner"))
|
||||
render.Error(w, r, acme.WrapErrorISE(err, "error authorizing revocation on provisioner"))
|
||||
return
|
||||
}
|
||||
|
||||
options := revokeOptions(serial, certToBeRevoked, reasonCode)
|
||||
err = ca.Revoke(ctx, options)
|
||||
if err != nil {
|
||||
render.Error(w, wrapRevokeErr(err))
|
||||
render.Error(w, r, wrapRevokeErr(err))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package acme
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
@@ -62,6 +63,11 @@ var (
|
||||
//
|
||||
// This variable can be used for testing purposes.
|
||||
InsecurePortTLSALPN01 int
|
||||
|
||||
// StrictFQDN allows to enforce a fully qualified domain name in the DNS
|
||||
// resolution. By default it allows domain resolution using a search list
|
||||
// defined in the resolv.conf or similar configuration.
|
||||
StrictFQDN bool
|
||||
)
|
||||
|
||||
// Challenge represents an ACME response Challenge type.
|
||||
@@ -110,16 +116,19 @@ func (ch *Challenge) Validate(ctx context.Context, db DB, jwk *jose.JSONWebKey,
|
||||
}
|
||||
|
||||
func http01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebKey) error {
|
||||
u := &url.URL{Scheme: "http", Host: http01ChallengeHost(ch.Value), Path: fmt.Sprintf("/.well-known/acme-challenge/%s", ch.Token)}
|
||||
u := &url.URL{Scheme: "http", Host: ch.Value, Path: fmt.Sprintf("/.well-known/acme-challenge/%s", ch.Token)}
|
||||
challengeURL := &url.URL{Scheme: "http", Host: http01ChallengeHost(ch.Value), Path: fmt.Sprintf("/.well-known/acme-challenge/%s", ch.Token)}
|
||||
|
||||
// Append insecure port if set.
|
||||
// Only used for testing purposes.
|
||||
if InsecurePortHTTP01 != 0 {
|
||||
u.Host += ":" + strconv.Itoa(InsecurePortHTTP01)
|
||||
insecurePort := strconv.Itoa(InsecurePortHTTP01)
|
||||
u.Host += ":" + insecurePort
|
||||
challengeURL.Host += ":" + insecurePort
|
||||
}
|
||||
|
||||
vc := MustClientFromContext(ctx)
|
||||
resp, err := vc.Get(u.String())
|
||||
resp, err := vc.Get(challengeURL.String())
|
||||
if err != nil {
|
||||
return storeError(ctx, db, ch, false, WrapError(ErrorConnectionType, err,
|
||||
"error doing http GET for url %s", u))
|
||||
@@ -157,15 +166,42 @@ func http01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWeb
|
||||
return nil
|
||||
}
|
||||
|
||||
// rootedName adds a trailing "." to a given domain name.
|
||||
func rootedName(name string) string {
|
||||
if StrictFQDN {
|
||||
if name == "" || name[len(name)-1] != '.' {
|
||||
return name + "."
|
||||
}
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
// http01ChallengeHost checks if a Challenge value is an IPv6 address
|
||||
// and adds square brackets if that's the case, so that it can be used
|
||||
// as a hostname. Returns the original Challenge value as the host to
|
||||
// use in other cases.
|
||||
func http01ChallengeHost(value string) string {
|
||||
if ip := net.ParseIP(value); ip != nil && ip.To4() == nil {
|
||||
value = "[" + value + "]"
|
||||
if ip := net.ParseIP(value); ip != nil {
|
||||
if ip.To4() == nil {
|
||||
value = "[" + value + "]"
|
||||
}
|
||||
return value
|
||||
}
|
||||
return value
|
||||
return rootedName(value)
|
||||
}
|
||||
|
||||
// tlsAlpn01ChallengeHost returns the rooted DNS used on TLS-ALPN-01
|
||||
// validations.
|
||||
func tlsAlpn01ChallengeHost(name string) string {
|
||||
if ip := net.ParseIP(name); ip != nil {
|
||||
return name
|
||||
}
|
||||
return rootedName(name)
|
||||
}
|
||||
|
||||
// dns01ChallengeHost returns the TXT record used in DNS-01 validations.
|
||||
func dns01ChallengeHost(domain string) string {
|
||||
return "_acme-challenge." + rootedName(domain)
|
||||
}
|
||||
|
||||
func tlsAlert(err error) uint8 {
|
||||
@@ -190,13 +226,12 @@ func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSON
|
||||
InsecureSkipVerify: true, //nolint:gosec // we expect a self-signed challenge certificate
|
||||
}
|
||||
|
||||
var hostPort string
|
||||
|
||||
// Allow to change TLS port for testing purposes.
|
||||
hostPort := tlsAlpn01ChallengeHost(ch.Value)
|
||||
if port := InsecurePortTLSALPN01; port == 0 {
|
||||
hostPort = net.JoinHostPort(ch.Value, "443")
|
||||
hostPort = net.JoinHostPort(hostPort, "443")
|
||||
} else {
|
||||
hostPort = net.JoinHostPort(ch.Value, strconv.Itoa(port))
|
||||
hostPort = net.JoinHostPort(hostPort, strconv.Itoa(port))
|
||||
}
|
||||
|
||||
vc := MustClientFromContext(ctx)
|
||||
@@ -211,7 +246,7 @@ func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSON
|
||||
"cannot negotiate ALPN acme-tls/1 protocol for tls-alpn-01 challenge"))
|
||||
}
|
||||
return storeError(ctx, db, ch, false, WrapError(ErrorConnectionType, err,
|
||||
"error doing TLS dial for %s", hostPort))
|
||||
"error doing TLS dial for %s", ch.Value))
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
@@ -307,7 +342,7 @@ func dns01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebK
|
||||
domain := strings.TrimPrefix(ch.Value, "*.")
|
||||
|
||||
vc := MustClientFromContext(ctx)
|
||||
txtRecords, err := vc.LookupTxt("_acme-challenge." + domain)
|
||||
txtRecords, err := vc.LookupTxt(dns01ChallengeHost(domain))
|
||||
if err != nil {
|
||||
return storeError(ctx, db, ch, false, WrapError(ErrorDNSType, err,
|
||||
"error looking up TXT records for domain %s", domain))
|
||||
@@ -372,12 +407,26 @@ func deviceAttest01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose
|
||||
|
||||
attObj, err := base64.RawURLEncoding.DecodeString(p.AttObj)
|
||||
if err != nil {
|
||||
return WrapErrorISE(err, "error base64 decoding attObj")
|
||||
return storeError(ctx, db, ch, true, NewDetailedError(ErrorBadAttestationStatementType, "failed base64 decoding attObj %q", p.AttObj))
|
||||
}
|
||||
|
||||
if len(attObj) == 0 || bytes.Equal(attObj, []byte("{}")) {
|
||||
return storeError(ctx, db, ch, true, NewDetailedError(ErrorBadAttestationStatementType, "attObj must not be empty"))
|
||||
}
|
||||
|
||||
cborDecoderOptions := cbor.DecOptions{}
|
||||
cborDecoder, err := cborDecoderOptions.DecMode()
|
||||
if err != nil {
|
||||
return WrapErrorISE(err, "failed creating CBOR decoder")
|
||||
}
|
||||
|
||||
if err := cborDecoder.Wellformed(attObj); err != nil {
|
||||
return storeError(ctx, db, ch, true, NewDetailedError(ErrorBadAttestationStatementType, "attObj is not well formed CBOR: %v", err))
|
||||
}
|
||||
|
||||
att := attestationObject{}
|
||||
if err := cbor.Unmarshal(attObj, &att); err != nil {
|
||||
return WrapErrorISE(err, "error unmarshalling CBOR")
|
||||
if err := cborDecoder.Unmarshal(attObj, &att); err != nil {
|
||||
return WrapErrorISE(err, "failed unmarshalling CBOR")
|
||||
}
|
||||
|
||||
format := att.Format
|
||||
@@ -726,7 +775,7 @@ var (
|
||||
oidTCGKpAIKCertificate = asn1.ObjectIdentifier{2, 23, 133, 8, 3}
|
||||
)
|
||||
|
||||
// validateAKCertifiate validates the X.509 AK certificate to be
|
||||
// validateAKCertificate validates the X.509 AK certificate to be
|
||||
// in accordance with the required properties. The requirements come from:
|
||||
// https://www.w3.org/TR/webauthn-2/#sctn-tpm-cert-requirements.
|
||||
//
|
||||
@@ -735,7 +784,7 @@ var (
|
||||
// - The Subject Alternative Name extension MUST be set as defined
|
||||
// in [TPMv2-EK-Profile] section 3.2.9.
|
||||
// - The Extended Key Usage extension MUST contain the OID 2.23.133.8.3
|
||||
// ("joint-iso-itu-t(2) internationalorganizations(23) 133 tcg-kp(8) tcg-kp-AIKCertificate(3)").
|
||||
// ("joint-iso-itu-t(2) international-organizations(23) 133 tcg-kp(8) tcg-kp-AIKCertificate(3)").
|
||||
// - The Basic Constraints extension MUST have the CA component set to false.
|
||||
// - An Authority Information Access (AIA) extension with entry id-ad-ocsp
|
||||
// and a CRL Distribution Point extension [RFC5280] are both OPTIONAL as
|
||||
@@ -1058,14 +1107,10 @@ func doStepAttestationFormat(_ context.Context, prov Provisioner, ch *Challenge,
|
||||
// should be the ARPA address https://datatracker.ietf.org/doc/html/rfc8738#section-6.
|
||||
// It also references TLS Extensions [RFC6066].
|
||||
func serverName(ch *Challenge) string {
|
||||
var serverName string
|
||||
ip := net.ParseIP(ch.Value)
|
||||
if ip != nil {
|
||||
serverName = reverseAddr(ip)
|
||||
} else {
|
||||
serverName = ch.Value
|
||||
if ip := net.ParseIP(ch.Value); ip != nil {
|
||||
return reverseAddr(ip)
|
||||
}
|
||||
return serverName
|
||||
return ch.Value
|
||||
}
|
||||
|
||||
// reverseaddr returns the in-addr.arpa. or ip6.arpa. hostname of the IP
|
||||
|
||||
@@ -646,7 +646,7 @@ func TestChallenge_Validate(t *testing.T) {
|
||||
assert.Equal(t, "zap.internal", updch.Value)
|
||||
assert.Equal(t, StatusPending, updch.Status)
|
||||
|
||||
err := NewError(ErrorConnectionType, "error doing TLS dial for %v:443: force", ch.Value)
|
||||
err := NewError(ErrorConnectionType, "error doing TLS dial for %v: force", ch.Value)
|
||||
|
||||
assert.EqualError(t, updch.Error.Err, err.Err.Error())
|
||||
assert.Equal(t, err.Type, updch.Error.Type)
|
||||
@@ -1723,7 +1723,7 @@ func TestTLSALPN01Validate(t *testing.T) {
|
||||
assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type)
|
||||
assert.Equal(t, "zap.internal", updch.Value)
|
||||
|
||||
err := NewError(ErrorConnectionType, "error doing TLS dial for %v:443: force", ch.Value)
|
||||
err := NewError(ErrorConnectionType, "error doing TLS dial for %v: force", ch.Value)
|
||||
|
||||
assert.EqualError(t, updch.Error.Err, err.Err.Error())
|
||||
assert.Equal(t, err.Type, updch.Error.Type)
|
||||
@@ -1754,7 +1754,7 @@ func TestTLSALPN01Validate(t *testing.T) {
|
||||
assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type)
|
||||
assert.Equal(t, "zap.internal", updch.Value)
|
||||
|
||||
err := NewError(ErrorConnectionType, "error doing TLS dial for %v:443: force", ch.Value)
|
||||
err := NewError(ErrorConnectionType, "error doing TLS dial for %v: force", ch.Value)
|
||||
|
||||
assert.EqualError(t, updch.Error.Err, err.Err.Error())
|
||||
assert.Equal(t, err.Type, updch.Error.Type)
|
||||
@@ -1786,7 +1786,7 @@ func TestTLSALPN01Validate(t *testing.T) {
|
||||
assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type)
|
||||
assert.Equal(t, "zap.internal", updch.Value)
|
||||
|
||||
err := NewError(ErrorConnectionType, "error doing TLS dial for %v:443: context deadline exceeded", ch.Value)
|
||||
err := NewError(ErrorConnectionType, "error doing TLS dial for %v: context deadline exceeded", ch.Value)
|
||||
|
||||
assert.EqualError(t, updch.Error.Err, err.Err.Error())
|
||||
assert.Equal(t, err.Type, updch.Error.Type)
|
||||
@@ -2767,14 +2767,34 @@ func Test_serverName(t *testing.T) {
|
||||
|
||||
func Test_http01ChallengeHost(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
value string
|
||||
want string
|
||||
name string
|
||||
strictFQDN bool
|
||||
value string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "dns",
|
||||
value: "www.example.com",
|
||||
want: "www.example.com",
|
||||
name: "dns",
|
||||
strictFQDN: false,
|
||||
value: "www.example.com",
|
||||
want: "www.example.com",
|
||||
},
|
||||
{
|
||||
name: "dns strict",
|
||||
strictFQDN: true,
|
||||
value: "www.example.com",
|
||||
want: "www.example.com.",
|
||||
},
|
||||
{
|
||||
name: "rooted dns",
|
||||
strictFQDN: false,
|
||||
value: "www.example.com.",
|
||||
want: "www.example.com.",
|
||||
},
|
||||
{
|
||||
name: "rooted dns strict",
|
||||
strictFQDN: true,
|
||||
value: "www.example.com.",
|
||||
want: "www.example.com.",
|
||||
},
|
||||
{
|
||||
name: "ipv4",
|
||||
@@ -2789,6 +2809,11 @@ func Test_http01ChallengeHost(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tmp := StrictFQDN
|
||||
t.Cleanup(func() {
|
||||
StrictFQDN = tmp
|
||||
})
|
||||
StrictFQDN = tt.strictFQDN
|
||||
if got := http01ChallengeHost(tt.value); got != tt.want {
|
||||
t.Errorf("http01ChallengeHost() = %v, want %v", got, tt.want)
|
||||
}
|
||||
@@ -3263,10 +3288,50 @@ func Test_deviceAttest01Validate(t *testing.T) {
|
||||
AttObj: "?!",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
errorCBORPayload, err := json.Marshal(struct {
|
||||
emptyPayload, err := json.Marshal(struct {
|
||||
AttObj string `json:"attObj"`
|
||||
}{
|
||||
AttObj: "AAAA",
|
||||
AttObj: base64.RawURLEncoding.EncodeToString([]byte("")),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
emptyObjectPayload, err := json.Marshal(struct {
|
||||
AttObj string `json:"attObj"`
|
||||
}{
|
||||
AttObj: base64.RawURLEncoding.EncodeToString([]byte("{}")),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
attObj, err := cbor.Marshal(struct {
|
||||
Format string `json:"fmt"`
|
||||
AttStatement map[string]interface{} `json:"attStmt,omitempty"`
|
||||
}{
|
||||
Format: "step",
|
||||
AttStatement: map[string]interface{}{
|
||||
"alg": -7,
|
||||
"sig": "",
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
errorNonWellformedCBORPayload, err := json.Marshal(struct {
|
||||
AttObj string `json:"attObj"`
|
||||
}{
|
||||
AttObj: base64.RawURLEncoding.EncodeToString(attObj[:len(attObj)-1]), // cut the CBOR encoded data off
|
||||
})
|
||||
require.NoError(t, err)
|
||||
unsupportedFormatAttObj, err := cbor.Marshal(struct {
|
||||
Format string `json:"fmt"`
|
||||
AttStatement map[string]interface{} `json:"attStmt,omitempty"`
|
||||
}{
|
||||
Format: "unsupported-format",
|
||||
AttStatement: map[string]interface{}{
|
||||
"alg": -7,
|
||||
"sig": "",
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
errorUnsupportedFormat, err := json.Marshal(struct {
|
||||
AttObj string `json:"attObj"`
|
||||
}{
|
||||
AttObj: base64.RawURLEncoding.EncodeToString(unsupportedFormatAttObj),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
type args struct {
|
||||
@@ -3403,7 +3468,7 @@ func Test_deviceAttest01Validate(t *testing.T) {
|
||||
wantErr: nil,
|
||||
}
|
||||
},
|
||||
"fail/base64-decode": func(t *testing.T) test {
|
||||
"ok/base64-decode": func(t *testing.T) test {
|
||||
return test{
|
||||
args: args{
|
||||
ch: &Challenge{
|
||||
@@ -3419,13 +3484,29 @@ func Test_deviceAttest01Validate(t *testing.T) {
|
||||
assert.Equal(t, "azID", id)
|
||||
return &Authorization{ID: "azID"}, nil
|
||||
},
|
||||
MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error {
|
||||
assert.Equal(t, "chID", updch.ID)
|
||||
assert.Equal(t, "token", updch.Token)
|
||||
assert.Equal(t, StatusInvalid, updch.Status)
|
||||
assert.Equal(t, ChallengeType("device-attest-01"), updch.Type)
|
||||
assert.Equal(t, "12345678", updch.Value)
|
||||
|
||||
err := NewDetailedError(ErrorBadAttestationStatementType, "failed base64 decoding attObj %q", "?!")
|
||||
|
||||
assert.EqualError(t, updch.Error.Err, err.Err.Error())
|
||||
assert.Equal(t, err.Type, updch.Error.Type)
|
||||
assert.Equal(t, err.Detail, updch.Error.Detail)
|
||||
assert.Equal(t, err.Status, updch.Error.Status)
|
||||
assert.Equal(t, err.Subproblems, updch.Error.Subproblems)
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
payload: errorBase64Payload,
|
||||
},
|
||||
wantErr: NewErrorISE("error base64 decoding attObj: illegal base64 data at input byte 0"),
|
||||
}
|
||||
},
|
||||
"fail/cbor.Unmarshal": func(t *testing.T) test {
|
||||
"ok/empty-attobj": func(t *testing.T) test {
|
||||
return test{
|
||||
args: args{
|
||||
ch: &Challenge{
|
||||
@@ -3441,10 +3522,142 @@ func Test_deviceAttest01Validate(t *testing.T) {
|
||||
assert.Equal(t, "azID", id)
|
||||
return &Authorization{ID: "azID"}, nil
|
||||
},
|
||||
MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error {
|
||||
assert.Equal(t, "chID", updch.ID)
|
||||
assert.Equal(t, "token", updch.Token)
|
||||
assert.Equal(t, StatusInvalid, updch.Status)
|
||||
assert.Equal(t, ChallengeType("device-attest-01"), updch.Type)
|
||||
assert.Equal(t, "12345678", updch.Value)
|
||||
|
||||
err := NewDetailedError(ErrorBadAttestationStatementType, "attObj must not be empty")
|
||||
|
||||
assert.EqualError(t, updch.Error.Err, err.Err.Error())
|
||||
assert.Equal(t, err.Type, updch.Error.Type)
|
||||
assert.Equal(t, err.Detail, updch.Error.Detail)
|
||||
assert.Equal(t, err.Status, updch.Error.Status)
|
||||
assert.Equal(t, err.Subproblems, updch.Error.Subproblems)
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
payload: errorCBORPayload,
|
||||
payload: emptyPayload,
|
||||
},
|
||||
}
|
||||
},
|
||||
"ok/empty-json-attobj": func(t *testing.T) test {
|
||||
return test{
|
||||
args: args{
|
||||
ch: &Challenge{
|
||||
ID: "chID",
|
||||
AuthorizationID: "azID",
|
||||
Token: "token",
|
||||
Type: "device-attest-01",
|
||||
Status: StatusPending,
|
||||
Value: "12345678",
|
||||
},
|
||||
db: &MockDB{
|
||||
MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) {
|
||||
assert.Equal(t, "azID", id)
|
||||
return &Authorization{ID: "azID"}, nil
|
||||
},
|
||||
MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error {
|
||||
assert.Equal(t, "chID", updch.ID)
|
||||
assert.Equal(t, "token", updch.Token)
|
||||
assert.Equal(t, StatusInvalid, updch.Status)
|
||||
assert.Equal(t, ChallengeType("device-attest-01"), updch.Type)
|
||||
assert.Equal(t, "12345678", updch.Value)
|
||||
|
||||
err := NewDetailedError(ErrorBadAttestationStatementType, "attObj must not be empty")
|
||||
|
||||
assert.EqualError(t, updch.Error.Err, err.Err.Error())
|
||||
assert.Equal(t, err.Type, updch.Error.Type)
|
||||
assert.Equal(t, err.Detail, updch.Error.Detail)
|
||||
assert.Equal(t, err.Status, updch.Error.Status)
|
||||
assert.Equal(t, err.Subproblems, updch.Error.Subproblems)
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
payload: emptyObjectPayload,
|
||||
},
|
||||
}
|
||||
},
|
||||
"ok/cborDecoder.Wellformed": func(t *testing.T) test {
|
||||
return test{
|
||||
args: args{
|
||||
ch: &Challenge{
|
||||
ID: "chID",
|
||||
AuthorizationID: "azID",
|
||||
Token: "token",
|
||||
Type: "device-attest-01",
|
||||
Status: StatusPending,
|
||||
Value: "12345678",
|
||||
},
|
||||
db: &MockDB{
|
||||
MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) {
|
||||
assert.Equal(t, "azID", id)
|
||||
return &Authorization{ID: "azID"}, nil
|
||||
},
|
||||
MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error {
|
||||
assert.Equal(t, "chID", updch.ID)
|
||||
assert.Equal(t, "token", updch.Token)
|
||||
assert.Equal(t, StatusInvalid, updch.Status)
|
||||
assert.Equal(t, ChallengeType("device-attest-01"), updch.Type)
|
||||
assert.Equal(t, "12345678", updch.Value)
|
||||
|
||||
err := NewDetailedError(ErrorBadAttestationStatementType, "attObj is not well formed CBOR: unexpected EOF")
|
||||
|
||||
assert.EqualError(t, updch.Error.Err, err.Err.Error())
|
||||
assert.Equal(t, err.Type, updch.Error.Type)
|
||||
assert.Equal(t, err.Detail, updch.Error.Detail)
|
||||
assert.Equal(t, err.Status, updch.Error.Status)
|
||||
assert.Equal(t, err.Subproblems, updch.Error.Subproblems)
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
payload: errorNonWellformedCBORPayload,
|
||||
},
|
||||
}
|
||||
},
|
||||
"ok/unsupported-attestation-format": func(t *testing.T) test {
|
||||
ctx := NewProvisionerContext(context.Background(), mustNonAttestationProvisioner(t))
|
||||
return test{
|
||||
args: args{
|
||||
ctx: ctx,
|
||||
ch: &Challenge{
|
||||
ID: "chID",
|
||||
AuthorizationID: "azID",
|
||||
Token: "token",
|
||||
Type: "device-attest-01",
|
||||
Status: StatusPending,
|
||||
Value: "12345678",
|
||||
},
|
||||
db: &MockDB{
|
||||
MockGetAuthorization: func(ctx context.Context, id string) (*Authorization, error) {
|
||||
assert.Equal(t, "azID", id)
|
||||
return &Authorization{ID: "azID"}, nil
|
||||
},
|
||||
MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error {
|
||||
assert.Equal(t, "chID", updch.ID)
|
||||
assert.Equal(t, "token", updch.Token)
|
||||
assert.Equal(t, StatusInvalid, updch.Status)
|
||||
assert.Equal(t, ChallengeType("device-attest-01"), updch.Type)
|
||||
assert.Equal(t, "12345678", updch.Value)
|
||||
|
||||
err := NewDetailedError(ErrorBadAttestationStatementType, "unsupported attestation object format %q", "unsupported-format")
|
||||
|
||||
assert.EqualError(t, updch.Error.Err, err.Err.Error())
|
||||
assert.Equal(t, err.Type, updch.Error.Type)
|
||||
assert.Equal(t, err.Detail, updch.Error.Detail)
|
||||
assert.Equal(t, err.Status, updch.Error.Status)
|
||||
assert.Equal(t, err.Subproblems, updch.Error.Subproblems)
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
payload: errorUnsupportedFormat,
|
||||
},
|
||||
wantErr: NewErrorISE("error unmarshalling CBOR: cbor:"),
|
||||
}
|
||||
},
|
||||
"ok/prov.IsAttestationFormatEnabled": func(t *testing.T) test {
|
||||
@@ -4301,3 +4514,59 @@ func createSubjectAltNameExtension(dnsNames, emailAddresses x509util.MultiString
|
||||
Value: rawBytes,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func Test_tlsAlpn01ChallengeHost(t *testing.T) {
|
||||
type args struct {
|
||||
name string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
strictFQDN bool
|
||||
args args
|
||||
want string
|
||||
}{
|
||||
{"dns", false, args{"smallstep.com"}, "smallstep.com"},
|
||||
{"dns strict", true, args{"smallstep.com"}, "smallstep.com."},
|
||||
{"rooted dns", false, args{"smallstep.com."}, "smallstep.com."},
|
||||
{"rooted dns strict", true, args{"smallstep.com."}, "smallstep.com."},
|
||||
{"ipv4", true, args{"1.2.3.4"}, "1.2.3.4"},
|
||||
{"ipv6", true, args{"2607:f8b0:4023:1009::71"}, "2607:f8b0:4023:1009::71"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tmp := StrictFQDN
|
||||
t.Cleanup(func() {
|
||||
StrictFQDN = tmp
|
||||
})
|
||||
StrictFQDN = tt.strictFQDN
|
||||
assert.Equal(t, tt.want, tlsAlpn01ChallengeHost(tt.args.name))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_dns01ChallengeHost(t *testing.T) {
|
||||
type args struct {
|
||||
domain string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
strictFQDN bool
|
||||
args args
|
||||
want string
|
||||
}{
|
||||
{"dns", false, args{"smallstep.com"}, "_acme-challenge.smallstep.com"},
|
||||
{"dns strict", true, args{"smallstep.com"}, "_acme-challenge.smallstep.com."},
|
||||
{"rooted dns", false, args{"smallstep.com."}, "_acme-challenge.smallstep.com."},
|
||||
{"rooted dns strict", true, args{"smallstep.com."}, "_acme-challenge.smallstep.com."},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tmp := StrictFQDN
|
||||
t.Cleanup(func() {
|
||||
StrictFQDN = tmp
|
||||
})
|
||||
StrictFQDN = tt.strictFQDN
|
||||
assert.Equal(t, tt.want, dns01ChallengeHost(tt.args.domain))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ func (m *MockProvisioner) GetName() string {
|
||||
return m.Mret1.(string)
|
||||
}
|
||||
|
||||
// AuthorizeOrderIdentifiers mock
|
||||
// AuthorizeOrderIdentifier mock
|
||||
func (m *MockProvisioner) AuthorizeOrderIdentifier(ctx context.Context, identifier provisioner.ACMEIdentifier) error {
|
||||
if m.MauthorizeOrderIdentifier != nil {
|
||||
return m.MauthorizeOrderIdentifier(ctx, identifier)
|
||||
|
||||
@@ -2,6 +2,7 @@ package acme
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@@ -15,7 +16,7 @@ var ErrNotFound = errors.New("not found")
|
||||
// IsErrNotFound returns true if the error is a "not found" error. Returns false
|
||||
// otherwise.
|
||||
func IsErrNotFound(err error) bool {
|
||||
return errors.Is(err, ErrNotFound)
|
||||
return errors.Is(err, ErrNotFound) || errors.Is(err, sql.ErrNoRows)
|
||||
}
|
||||
|
||||
// DB is the DB interface expected by the step-ca ACME API.
|
||||
|
||||
@@ -18,6 +18,7 @@ type dbAccount struct {
|
||||
Contact []string `json:"contact,omitempty"`
|
||||
Status acme.Status `json:"status"`
|
||||
LocationPrefix string `json:"locationPrefix"`
|
||||
ProvisionerID string `json:"provisionerID,omitempty"`
|
||||
ProvisionerName string `json:"provisionerName"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
DeactivatedAt time.Time `json:"deactivatedAt"`
|
||||
@@ -69,6 +70,7 @@ func (db *DB) GetAccount(ctx context.Context, id string) (*acme.Account, error)
|
||||
Key: dbacc.Key,
|
||||
ID: dbacc.ID,
|
||||
LocationPrefix: dbacc.LocationPrefix,
|
||||
ProvisionerID: dbacc.ProvisionerID,
|
||||
ProvisionerName: dbacc.ProvisionerName,
|
||||
}, nil
|
||||
}
|
||||
@@ -97,6 +99,7 @@ func (db *DB) CreateAccount(ctx context.Context, acc *acme.Account) error {
|
||||
Status: acc.Status,
|
||||
CreatedAt: clock.Now(),
|
||||
LocationPrefix: acc.LocationPrefix,
|
||||
ProvisionerID: acc.ProvisionerID,
|
||||
ProvisionerName: acc.ProvisionerName,
|
||||
}
|
||||
|
||||
|
||||
@@ -68,12 +68,14 @@ func TestDB_getDBAccount(t *testing.T) {
|
||||
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
|
||||
assert.FatalError(t, err)
|
||||
dbacc := &dbAccount{
|
||||
ID: accID,
|
||||
Status: acme.StatusDeactivated,
|
||||
CreatedAt: now,
|
||||
DeactivatedAt: now,
|
||||
Contact: []string{"foo", "bar"},
|
||||
Key: jwk,
|
||||
ID: accID,
|
||||
Status: acme.StatusDeactivated,
|
||||
CreatedAt: now,
|
||||
DeactivatedAt: now,
|
||||
Contact: []string{"foo", "bar"},
|
||||
Key: jwk,
|
||||
ProvisionerID: "73d2c0f1-9753-448b-9b48-bf00fe434681",
|
||||
ProvisionerName: "acme",
|
||||
}
|
||||
b, err := json.Marshal(dbacc)
|
||||
assert.FatalError(t, err)
|
||||
|
||||
32
acme/db_test.go
Normal file
32
acme/db_test.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package acme
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIsErrNotFound(t *testing.T) {
|
||||
type args struct {
|
||||
err error
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want bool
|
||||
}{
|
||||
{"true ErrNotFound", args{ErrNotFound}, true},
|
||||
{"true sql.ErrNoRows", args{sql.ErrNoRows}, true},
|
||||
{"true wrapped ErrNotFound", args{fmt.Errorf("something failed: %w", ErrNotFound)}, true},
|
||||
{"true wrapped sql.ErrNoRows", args{fmt.Errorf("something failed: %w", sql.ErrNoRows)}, true},
|
||||
{"false other", args{errors.New("not found")}, false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := IsErrNotFound(tt.args.err); got != tt.want {
|
||||
t.Errorf("IsErrNotFound() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -424,7 +424,7 @@ func (e *Error) ToLog() (interface{}, error) {
|
||||
}
|
||||
|
||||
// Render implements render.RenderableError for Error.
|
||||
func (e *Error) Render(w http.ResponseWriter) {
|
||||
func (e *Error) Render(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/problem+json")
|
||||
render.JSONStatus(w, e, e.StatusCode())
|
||||
render.JSONStatus(w, r, e, e.StatusCode())
|
||||
}
|
||||
|
||||
@@ -186,19 +186,19 @@ func (l *linker) Middleware(next http.Handler) http.Handler {
|
||||
nameEscaped := chi.URLParam(r, "provisionerID")
|
||||
name, err := url.PathUnescape(nameEscaped)
|
||||
if err != nil {
|
||||
render.Error(w, WrapErrorISE(err, "error url unescaping provisioner name '%s'", nameEscaped))
|
||||
render.Error(w, r, WrapErrorISE(err, "error url unescaping provisioner name '%s'", nameEscaped))
|
||||
return
|
||||
}
|
||||
|
||||
p, err := authority.MustFromContext(ctx).LoadProvisionerByName(name)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
acmeProv, ok := p.(*provisioner.ACME)
|
||||
if !ok {
|
||||
render.Error(w, NewError(ErrorAccountDoesNotExistType, "provisioner must be of type ACME"))
|
||||
render.Error(w, r, NewError(ErrorAccountDoesNotExistType, "provisioner must be of type ACME"))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ func (o *Order) UpdateStatus(ctx context.Context, db DB) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// getKeyFingerprint returns a fingerprint from the list of authorizations. This
|
||||
// getAuthorizationFingerprint returns a fingerprint from the list of authorizations. This
|
||||
// fingerprint is used on the device-attest-01 flow to verify the attestation
|
||||
// certificate public key with the CSR public key.
|
||||
//
|
||||
|
||||
32
api/api.go
32
api/api.go
@@ -353,15 +353,15 @@ func Route(r Router) {
|
||||
// Version is an HTTP handler that returns the version of the server.
|
||||
func Version(w http.ResponseWriter, r *http.Request) {
|
||||
v := mustAuthority(r.Context()).Version()
|
||||
render.JSON(w, VersionResponse{
|
||||
render.JSON(w, r, VersionResponse{
|
||||
Version: v.Version,
|
||||
RequireClientAuthentication: v.RequireClientAuthentication,
|
||||
})
|
||||
}
|
||||
|
||||
// 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) {
|
||||
render.JSON(w, r, HealthResponse{Status: "ok"})
|
||||
}
|
||||
|
||||
// Root is an HTTP handler that using the SHA256 from the URL, returns the root
|
||||
@@ -372,11 +372,11 @@ func Root(w http.ResponseWriter, r *http.Request) {
|
||||
// Load root certificate with the
|
||||
cert, err := mustAuthority(r.Context()).Root(sum)
|
||||
if err != nil {
|
||||
render.Error(w, errs.Wrapf(http.StatusNotFound, err, "%s was not found", r.RequestURI))
|
||||
render.Error(w, r, errs.Wrapf(http.StatusNotFound, err, "%s was not found", r.RequestURI))
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, &RootResponse{RootPEM: Certificate{cert}})
|
||||
render.JSON(w, r, &RootResponse{RootPEM: Certificate{cert}})
|
||||
}
|
||||
|
||||
func certChainToPEM(certChain []*x509.Certificate) []Certificate {
|
||||
@@ -391,17 +391,17 @@ func certChainToPEM(certChain []*x509.Certificate) []Certificate {
|
||||
func Provisioners(w http.ResponseWriter, r *http.Request) {
|
||||
cursor, limit, err := ParseCursor(r)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
p, next, err := mustAuthority(r.Context()).GetProvisioners(cursor, limit)
|
||||
if err != nil {
|
||||
render.Error(w, errs.InternalServerErr(err))
|
||||
render.Error(w, r, errs.InternalServerErr(err))
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, &ProvisionersResponse{
|
||||
render.JSON(w, r, &ProvisionersResponse{
|
||||
Provisioners: p,
|
||||
NextCursor: next,
|
||||
})
|
||||
@@ -412,18 +412,18 @@ func ProvisionerKey(w http.ResponseWriter, r *http.Request) {
|
||||
kid := chi.URLParam(r, "kid")
|
||||
key, err := mustAuthority(r.Context()).GetEncryptedKey(kid)
|
||||
if err != nil {
|
||||
render.Error(w, errs.NotFoundErr(err))
|
||||
render.Error(w, r, errs.NotFoundErr(err))
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, &ProvisionerKeyResponse{key})
|
||||
render.JSON(w, r, &ProvisionerKeyResponse{key})
|
||||
}
|
||||
|
||||
// Roots returns all the root certificates for the CA.
|
||||
func Roots(w http.ResponseWriter, r *http.Request) {
|
||||
roots, err := mustAuthority(r.Context()).GetRoots()
|
||||
if err != nil {
|
||||
render.Error(w, errs.ForbiddenErr(err, "error getting roots"))
|
||||
render.Error(w, r, errs.ForbiddenErr(err, "error getting roots"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -432,7 +432,7 @@ func Roots(w http.ResponseWriter, r *http.Request) {
|
||||
certs[i] = Certificate{roots[i]}
|
||||
}
|
||||
|
||||
render.JSONStatus(w, &RootsResponse{
|
||||
render.JSONStatus(w, r, &RootsResponse{
|
||||
Certificates: certs,
|
||||
}, http.StatusCreated)
|
||||
}
|
||||
@@ -441,7 +441,7 @@ func Roots(w http.ResponseWriter, r *http.Request) {
|
||||
func RootsPEM(w http.ResponseWriter, r *http.Request) {
|
||||
roots, err := mustAuthority(r.Context()).GetRoots()
|
||||
if err != nil {
|
||||
render.Error(w, errs.InternalServerErr(err))
|
||||
render.Error(w, r, errs.InternalServerErr(err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -454,7 +454,7 @@ func RootsPEM(w http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
|
||||
if _, err := w.Write(block); err != nil {
|
||||
log.Error(w, err)
|
||||
log.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -464,7 +464,7 @@ func RootsPEM(w http.ResponseWriter, r *http.Request) {
|
||||
func Federation(w http.ResponseWriter, r *http.Request) {
|
||||
federated, err := mustAuthority(r.Context()).GetFederation()
|
||||
if err != nil {
|
||||
render.Error(w, errs.ForbiddenErr(err, "error getting federated roots"))
|
||||
render.Error(w, r, errs.ForbiddenErr(err, "error getting federated roots"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -473,7 +473,7 @@ func Federation(w http.ResponseWriter, r *http.Request) {
|
||||
certs[i] = Certificate{federated[i]}
|
||||
}
|
||||
|
||||
render.JSONStatus(w, &FederationResponse{
|
||||
render.JSONStatus(w, r, &FederationResponse{
|
||||
Certificates: certs,
|
||||
}, http.StatusCreated)
|
||||
}
|
||||
|
||||
@@ -13,12 +13,12 @@ import (
|
||||
func CRL(w http.ResponseWriter, r *http.Request) {
|
||||
crlInfo, err := mustAuthority(r.Context()).GetCertificateRevocationList()
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
if crlInfo == nil {
|
||||
render.Error(w, errs.New(http.StatusNotFound, "no CRL available"))
|
||||
render.Error(w, r, errs.New(http.StatusNotFound, "no CRL available"))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -9,6 +10,29 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type errorLoggerKey struct{}
|
||||
|
||||
// ErrorLogger is the function type used to log errors.
|
||||
type ErrorLogger func(http.ResponseWriter, *http.Request, error)
|
||||
|
||||
func (fn ErrorLogger) call(w http.ResponseWriter, r *http.Request, err error) {
|
||||
if fn == nil {
|
||||
return
|
||||
}
|
||||
fn(w, r, err)
|
||||
}
|
||||
|
||||
// WithErrorLogger returns a new context with the given error logger.
|
||||
func WithErrorLogger(ctx context.Context, fn ErrorLogger) context.Context {
|
||||
return context.WithValue(ctx, errorLoggerKey{}, fn)
|
||||
}
|
||||
|
||||
// ErrorLoggerFromContext returns an error logger from the context.
|
||||
func ErrorLoggerFromContext(ctx context.Context) (fn ErrorLogger) {
|
||||
fn, _ = ctx.Value(errorLoggerKey{}).(ErrorLogger)
|
||||
return
|
||||
}
|
||||
|
||||
// StackTracedError is the set of errors implementing the StackTrace function.
|
||||
//
|
||||
// Errors implementing this interface have their stack traces logged when passed
|
||||
@@ -27,8 +51,10 @@ type fieldCarrier interface {
|
||||
// Error adds to the response writer the given error if it implements
|
||||
// logging.ResponseLogger. If it does not implement it, then writes the error
|
||||
// using the log package.
|
||||
func Error(rw http.ResponseWriter, err error) {
|
||||
fc, ok := rw.(fieldCarrier)
|
||||
func Error(w http.ResponseWriter, r *http.Request, err error) {
|
||||
ErrorLoggerFromContext(r.Context()).call(w, r, err)
|
||||
|
||||
fc, ok := w.(fieldCarrier)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
@@ -51,7 +77,7 @@ func Error(rw http.ResponseWriter, err error) {
|
||||
|
||||
// EnabledResponse log the response object if it implements the EnableLogger
|
||||
// interface.
|
||||
func EnabledResponse(rw http.ResponseWriter, v any) {
|
||||
func EnabledResponse(rw http.ResponseWriter, r *http.Request, v any) {
|
||||
type enableLogger interface {
|
||||
ToLog() (any, error)
|
||||
}
|
||||
@@ -59,7 +85,7 @@ func EnabledResponse(rw http.ResponseWriter, v any) {
|
||||
if el, ok := v.(enableLogger); ok {
|
||||
out, err := el.ToLog()
|
||||
if err != nil {
|
||||
Error(rw, err)
|
||||
Error(rw, r, err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
@@ -27,21 +30,34 @@ func (stackTracedError) StackTrace() pkgerrors.StackTrace {
|
||||
}
|
||||
|
||||
func TestError(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
logger := slog.New(slog.NewJSONHandler(&buf, &slog.HandlerOptions{}))
|
||||
req := httptest.NewRequest("GET", "/test", http.NoBody)
|
||||
reqWithLogger := req.WithContext(WithErrorLogger(req.Context(), func(w http.ResponseWriter, r *http.Request, err error) {
|
||||
if err != nil {
|
||||
logger.ErrorContext(r.Context(), "request failed", slog.Any("error", err))
|
||||
}
|
||||
}))
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
error
|
||||
rw http.ResponseWriter
|
||||
r *http.Request
|
||||
isFieldCarrier bool
|
||||
isSlogLogger bool
|
||||
stepDebug bool
|
||||
expectStackTrace bool
|
||||
}{
|
||||
{"noLogger", nil, nil, false, false, false},
|
||||
{"noError", nil, logging.NewResponseLogger(httptest.NewRecorder()), true, false, false},
|
||||
{"noErrorDebug", nil, logging.NewResponseLogger(httptest.NewRecorder()), true, true, false},
|
||||
{"anError", assert.AnError, logging.NewResponseLogger(httptest.NewRecorder()), true, false, false},
|
||||
{"anErrorDebug", assert.AnError, logging.NewResponseLogger(httptest.NewRecorder()), true, true, false},
|
||||
{"stackTracedError", new(stackTracedError), logging.NewResponseLogger(httptest.NewRecorder()), true, true, true},
|
||||
{"stackTracedErrorDebug", new(stackTracedError), logging.NewResponseLogger(httptest.NewRecorder()), true, true, true},
|
||||
{"noLogger", nil, nil, req, false, false, false, false},
|
||||
{"noError", nil, logging.NewResponseLogger(httptest.NewRecorder()), req, true, false, false, false},
|
||||
{"noErrorDebug", nil, logging.NewResponseLogger(httptest.NewRecorder()), req, true, false, true, false},
|
||||
{"anError", assert.AnError, logging.NewResponseLogger(httptest.NewRecorder()), req, true, false, false, false},
|
||||
{"anErrorDebug", assert.AnError, logging.NewResponseLogger(httptest.NewRecorder()), req, true, false, true, false},
|
||||
{"stackTracedError", new(stackTracedError), logging.NewResponseLogger(httptest.NewRecorder()), req, true, false, true, true},
|
||||
{"stackTracedErrorDebug", new(stackTracedError), logging.NewResponseLogger(httptest.NewRecorder()), req, true, false, true, true},
|
||||
{"slogWithNoError", nil, logging.NewResponseLogger(httptest.NewRecorder()), reqWithLogger, true, true, false, false},
|
||||
{"slogWithError", assert.AnError, logging.NewResponseLogger(httptest.NewRecorder()), reqWithLogger, true, true, false, false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
@@ -52,27 +68,41 @@ func TestError(t *testing.T) {
|
||||
t.Setenv("STEPDEBUG", "0")
|
||||
}
|
||||
|
||||
Error(tt.rw, tt.error)
|
||||
Error(tt.rw, tt.r, tt.error)
|
||||
|
||||
// return early if test case doesn't use logger
|
||||
if !tt.isFieldCarrier {
|
||||
if !tt.isFieldCarrier && !tt.isSlogLogger {
|
||||
return
|
||||
}
|
||||
|
||||
fields := tt.rw.(logging.ResponseLogger).Fields()
|
||||
if tt.isFieldCarrier {
|
||||
fields := tt.rw.(logging.ResponseLogger).Fields()
|
||||
|
||||
// expect the error field to be (not) set and to be the same error that was fed to Error
|
||||
if tt.error == nil {
|
||||
assert.Nil(t, fields["error"])
|
||||
} else {
|
||||
assert.Same(t, tt.error, fields["error"])
|
||||
// expect the error field to be (not) set and to be the same error that was fed to Error
|
||||
if tt.error == nil {
|
||||
assert.Nil(t, fields["error"])
|
||||
} else {
|
||||
assert.Same(t, tt.error, fields["error"])
|
||||
}
|
||||
|
||||
// check if stack-trace is set when expected
|
||||
if _, hasStackTrace := fields["stack-trace"]; tt.expectStackTrace && !hasStackTrace {
|
||||
t.Error(`ResponseLogger["stack-trace"] not set`)
|
||||
} else if !tt.expectStackTrace && hasStackTrace {
|
||||
t.Error(`ResponseLogger["stack-trace"] was set`)
|
||||
}
|
||||
}
|
||||
|
||||
// check if stack-trace is set when expected
|
||||
if _, hasStackTrace := fields["stack-trace"]; tt.expectStackTrace && !hasStackTrace {
|
||||
t.Error(`ResponseLogger["stack-trace"] not set`)
|
||||
} else if !tt.expectStackTrace && hasStackTrace {
|
||||
t.Error(`ResponseLogger["stack-trace"] was set`)
|
||||
if tt.isSlogLogger {
|
||||
b := buf.Bytes()
|
||||
if tt.error == nil {
|
||||
assert.Empty(t, b)
|
||||
} else if assert.NotEmpty(t, b) {
|
||||
var m map[string]any
|
||||
assert.NoError(t, json.Unmarshal(b, &m))
|
||||
assert.Equal(t, tt.error.Error(), m["error"])
|
||||
}
|
||||
buf.Reset()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ func (s *SCEP) AuthorizeSSHSign(context.Context, string) ([]provisioner.SignOpti
|
||||
return nil, errDummyImplementation
|
||||
}
|
||||
|
||||
// AuthorizeRevoke returns an unimplemented error. Provisioners should overwrite
|
||||
// AuthorizeSSHRevoke returns an unimplemented error. Provisioners should overwrite
|
||||
// this method if they will support authorizing tokens for revoking SSH Certificates.
|
||||
func (s *SCEP) AuthorizeSSHRevoke(context.Context, string) error {
|
||||
return errDummyImplementation
|
||||
|
||||
@@ -51,7 +51,7 @@ func (e badProtoJSONError) Error() string {
|
||||
}
|
||||
|
||||
// Render implements render.RenderableError for badProtoJSONError
|
||||
func (e badProtoJSONError) Render(w http.ResponseWriter) {
|
||||
func (e badProtoJSONError) Render(w http.ResponseWriter, r *http.Request) {
|
||||
v := struct {
|
||||
Type string `json:"type"`
|
||||
Detail string `json:"detail"`
|
||||
@@ -62,5 +62,5 @@ func (e badProtoJSONError) Render(w http.ResponseWriter) {
|
||||
// trim the proto prefix for the message
|
||||
Message: strings.TrimSpace(strings.TrimPrefix(e.Error(), "proto:")),
|
||||
}
|
||||
render.JSONStatus(w, v, http.StatusBadRequest)
|
||||
render.JSONStatus(w, r, v, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
@@ -142,7 +142,8 @@ func Test_badProtoJSONError_Render(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
tt.e.Render(w)
|
||||
r := httptest.NewRequest("POST", "/test", http.NoBody)
|
||||
tt.e.Render(w, r)
|
||||
res := w.Result()
|
||||
defer res.Body.Close()
|
||||
|
||||
|
||||
10
api/rekey.go
10
api/rekey.go
@@ -29,25 +29,25 @@ func (s *RekeyRequest) Validate() error {
|
||||
// Rekey is similar to renew except that the certificate will be renewed with new key from csr.
|
||||
func Rekey(w http.ResponseWriter, r *http.Request) {
|
||||
if r.TLS == nil || len(r.TLS.PeerCertificates) == 0 {
|
||||
render.Error(w, errs.BadRequest("missing client certificate"))
|
||||
render.Error(w, r, errs.BadRequest("missing client certificate"))
|
||||
return
|
||||
}
|
||||
|
||||
var body RekeyRequest
|
||||
if err := read.JSON(r.Body, &body); err != nil {
|
||||
render.Error(w, errs.BadRequestErr(err, "error reading request body"))
|
||||
render.Error(w, r, errs.BadRequestErr(err, "error reading request body"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := body.Validate(); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
a := mustAuthority(r.Context())
|
||||
certChain, err := a.Rekey(r.TLS.PeerCertificates[0], body.CsrPEM.CertificateRequest.PublicKey)
|
||||
if err != nil {
|
||||
render.Error(w, errs.Wrap(http.StatusInternalServerError, err, "cahandler.Rekey"))
|
||||
render.Error(w, r, errs.Wrap(http.StatusInternalServerError, err, "cahandler.Rekey"))
|
||||
return
|
||||
}
|
||||
certChainPEM := certChainToPEM(certChain)
|
||||
@@ -57,7 +57,7 @@ func Rekey(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
LogCertificate(w, certChain[0])
|
||||
render.JSONStatus(w, &SignResponse{
|
||||
render.JSONStatus(w, r, &SignResponse{
|
||||
ServerPEM: certChainPEM[0],
|
||||
CaPEM: caPEM,
|
||||
CertChainPEM: certChainPEM,
|
||||
|
||||
@@ -13,8 +13,8 @@ import (
|
||||
)
|
||||
|
||||
// JSON is shorthand for JSONStatus(w, v, http.StatusOK).
|
||||
func JSON(w http.ResponseWriter, v interface{}) {
|
||||
JSONStatus(w, v, http.StatusOK)
|
||||
func JSON(w http.ResponseWriter, r *http.Request, v interface{}) {
|
||||
JSONStatus(w, r, v, http.StatusOK)
|
||||
}
|
||||
|
||||
// JSONStatus marshals v into w. It additionally sets the status code of
|
||||
@@ -22,7 +22,7 @@ func JSON(w http.ResponseWriter, v interface{}) {
|
||||
//
|
||||
// JSONStatus sets the Content-Type of w to application/json unless one is
|
||||
// specified.
|
||||
func JSONStatus(w http.ResponseWriter, v interface{}, status int) {
|
||||
func JSONStatus(w http.ResponseWriter, r *http.Request, v interface{}, status int) {
|
||||
setContentTypeUnlessPresent(w, "application/json")
|
||||
w.WriteHeader(status)
|
||||
|
||||
@@ -43,7 +43,7 @@ func JSONStatus(w http.ResponseWriter, v interface{}, status int) {
|
||||
}
|
||||
}
|
||||
|
||||
log.EnabledResponse(w, v)
|
||||
log.EnabledResponse(w, r, v)
|
||||
}
|
||||
|
||||
// ProtoJSON is shorthand for ProtoJSONStatus(w, m, http.StatusOK).
|
||||
@@ -80,22 +80,22 @@ func setContentTypeUnlessPresent(w http.ResponseWriter, contentType string) {
|
||||
type RenderableError interface {
|
||||
error
|
||||
|
||||
Render(http.ResponseWriter)
|
||||
Render(http.ResponseWriter, *http.Request)
|
||||
}
|
||||
|
||||
// Error marshals the JSON representation of err to w. In case err implements
|
||||
// RenderableError its own Render method will be called instead.
|
||||
func Error(w http.ResponseWriter, err error) {
|
||||
log.Error(w, err)
|
||||
func Error(rw http.ResponseWriter, r *http.Request, err error) {
|
||||
log.Error(rw, r, err)
|
||||
|
||||
var r RenderableError
|
||||
if errors.As(err, &r) {
|
||||
r.Render(w)
|
||||
var re RenderableError
|
||||
if errors.As(err, &re) {
|
||||
re.Render(rw, r)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
JSONStatus(w, err, statusCodeFromError(err))
|
||||
JSONStatus(rw, r, err, statusCodeFromError(err))
|
||||
}
|
||||
|
||||
// StatusCodedError is the set of errors that implement the basic StatusCode
|
||||
|
||||
@@ -18,8 +18,8 @@ import (
|
||||
func TestJSON(t *testing.T) {
|
||||
rec := httptest.NewRecorder()
|
||||
rw := logging.NewResponseLogger(rec)
|
||||
|
||||
JSON(rw, map[string]interface{}{"foo": "bar"})
|
||||
r := httptest.NewRequest("POST", "/test", http.NoBody)
|
||||
JSON(rw, r, map[string]interface{}{"foo": "bar"})
|
||||
|
||||
assert.Equal(t, http.StatusOK, rec.Result().StatusCode)
|
||||
assert.Equal(t, "application/json", rec.Header().Get("Content-Type"))
|
||||
@@ -64,7 +64,8 @@ func jsonPanicTest[T json.UnsupportedTypeError | json.UnsupportedValueError | js
|
||||
assert.ErrorAs(t, err, &e)
|
||||
}()
|
||||
|
||||
JSON(httptest.NewRecorder(), v)
|
||||
r := httptest.NewRequest("POST", "/test", http.NoBody)
|
||||
JSON(httptest.NewRecorder(), r, v)
|
||||
}
|
||||
|
||||
type renderableError struct {
|
||||
@@ -76,10 +77,9 @@ func (err renderableError) Error() string {
|
||||
return err.Message
|
||||
}
|
||||
|
||||
func (err renderableError) Render(w http.ResponseWriter) {
|
||||
func (err renderableError) Render(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "something/custom")
|
||||
|
||||
JSONStatus(w, err, err.Code)
|
||||
JSONStatus(w, r, err, err.Code)
|
||||
}
|
||||
|
||||
type statusedError struct {
|
||||
@@ -116,8 +116,8 @@ func TestError(t *testing.T) {
|
||||
|
||||
t.Run(strconv.Itoa(caseIndex), func(t *testing.T) {
|
||||
rec := httptest.NewRecorder()
|
||||
|
||||
Error(rec, kase.err)
|
||||
r := httptest.NewRequest("POST", "/test", http.NoBody)
|
||||
Error(rec, r, kase.err)
|
||||
|
||||
assert.Equal(t, kase.code, rec.Result().StatusCode)
|
||||
assert.Equal(t, kase.body, rec.Body.String())
|
||||
|
||||
@@ -23,19 +23,20 @@ func Renew(w http.ResponseWriter, r *http.Request) {
|
||||
// Get the leaf certificate from the peer or the token.
|
||||
cert, token, err := getPeerCertificate(r)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
// The token can be used by RAs to renew a certificate.
|
||||
if token != "" {
|
||||
ctx = authority.NewTokenContext(ctx, token)
|
||||
logOtt(w, token)
|
||||
}
|
||||
|
||||
a := mustAuthority(ctx)
|
||||
certChain, err := a.RenewContext(ctx, cert, nil)
|
||||
if err != nil {
|
||||
render.Error(w, errs.Wrap(http.StatusInternalServerError, err, "cahandler.Renew"))
|
||||
render.Error(w, r, errs.Wrap(http.StatusInternalServerError, err, "cahandler.Renew"))
|
||||
return
|
||||
}
|
||||
certChainPEM := certChainToPEM(certChain)
|
||||
@@ -45,7 +46,7 @@ func Renew(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
LogCertificate(w, certChain[0])
|
||||
render.JSONStatus(w, &SignResponse{
|
||||
render.JSONStatus(w, r, &SignResponse{
|
||||
ServerPEM: certChainPEM[0],
|
||||
CaPEM: caPEM,
|
||||
CertChainPEM: certChainPEM,
|
||||
|
||||
@@ -57,12 +57,12 @@ func (r *RevokeRequest) Validate() (err error) {
|
||||
func Revoke(w http.ResponseWriter, r *http.Request) {
|
||||
var body RevokeRequest
|
||||
if err := read.JSON(r.Body, &body); err != nil {
|
||||
render.Error(w, errs.BadRequestErr(err, "error reading request body"))
|
||||
render.Error(w, r, errs.BadRequestErr(err, "error reading request body"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := body.Validate(); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ func Revoke(w http.ResponseWriter, r *http.Request) {
|
||||
if body.OTT != "" {
|
||||
logOtt(w, body.OTT)
|
||||
if _, err := a.Authorize(ctx, body.OTT); err != nil {
|
||||
render.Error(w, errs.UnauthorizedErr(err))
|
||||
render.Error(w, r, errs.UnauthorizedErr(err))
|
||||
return
|
||||
}
|
||||
opts.OTT = body.OTT
|
||||
@@ -90,12 +90,12 @@ func Revoke(w http.ResponseWriter, r *http.Request) {
|
||||
// the client certificate Serial Number must match the serial number
|
||||
// being revoked.
|
||||
if r.TLS == nil || len(r.TLS.PeerCertificates) == 0 {
|
||||
render.Error(w, errs.BadRequest("missing ott or client certificate"))
|
||||
render.Error(w, r, errs.BadRequest("missing ott or client certificate"))
|
||||
return
|
||||
}
|
||||
opts.Crt = r.TLS.PeerCertificates[0]
|
||||
if opts.Crt.SerialNumber.String() != opts.Serial {
|
||||
render.Error(w, errs.BadRequest("serial number in client certificate different than body"))
|
||||
render.Error(w, r, errs.BadRequest("serial number in client certificate different than body"))
|
||||
return
|
||||
}
|
||||
// TODO: should probably be checking if the certificate was revoked here.
|
||||
@@ -106,12 +106,12 @@ func Revoke(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if err := a.Revoke(ctx, opts); err != nil {
|
||||
render.Error(w, errs.ForbiddenErr(err, "error revoking certificate"))
|
||||
render.Error(w, r, errs.ForbiddenErr(err, "error revoking certificate"))
|
||||
return
|
||||
}
|
||||
|
||||
logRevoke(w, opts)
|
||||
render.JSON(w, &RevokeResponse{Status: "ok"})
|
||||
render.JSON(w, r, &RevokeResponse{Status: "ok"})
|
||||
}
|
||||
|
||||
func logRevoke(w http.ResponseWriter, ri *authority.RevokeOptions) {
|
||||
|
||||
10
api/sign.go
10
api/sign.go
@@ -52,13 +52,13 @@ type SignResponse struct {
|
||||
func Sign(w http.ResponseWriter, r *http.Request) {
|
||||
var body SignRequest
|
||||
if err := read.JSON(r.Body, &body); err != nil {
|
||||
render.Error(w, errs.BadRequestErr(err, "error reading request body"))
|
||||
render.Error(w, r, errs.BadRequestErr(err, "error reading request body"))
|
||||
return
|
||||
}
|
||||
|
||||
logOtt(w, body.OTT)
|
||||
if err := body.Validate(); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -74,13 +74,13 @@ func Sign(w http.ResponseWriter, r *http.Request) {
|
||||
ctx = provisioner.NewContextWithMethod(ctx, provisioner.SignMethod)
|
||||
signOpts, err := a.Authorize(ctx, body.OTT)
|
||||
if err != nil {
|
||||
render.Error(w, errs.UnauthorizedErr(err))
|
||||
render.Error(w, r, errs.UnauthorizedErr(err))
|
||||
return
|
||||
}
|
||||
|
||||
certChain, err := a.SignWithContext(ctx, body.CsrPEM.CertificateRequest, opts, signOpts...)
|
||||
if err != nil {
|
||||
render.Error(w, errs.ForbiddenErr(err, "error signing certificate"))
|
||||
render.Error(w, r, errs.ForbiddenErr(err, "error signing certificate"))
|
||||
return
|
||||
}
|
||||
certChainPEM := certChainToPEM(certChain)
|
||||
@@ -90,7 +90,7 @@ func Sign(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
LogCertificate(w, certChain[0])
|
||||
render.JSONStatus(w, &SignResponse{
|
||||
render.JSONStatus(w, r, &SignResponse{
|
||||
ServerPEM: certChainPEM[0],
|
||||
CaPEM: caPEM,
|
||||
CertChainPEM: certChainPEM,
|
||||
|
||||
97
api/ssh.go
97
api/ssh.go
@@ -6,8 +6,11 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/crypto/ssh"
|
||||
|
||||
@@ -253,19 +256,19 @@ type SSHBastionResponse struct {
|
||||
func SSHSign(w http.ResponseWriter, r *http.Request) {
|
||||
var body SSHSignRequest
|
||||
if err := read.JSON(r.Body, &body); err != nil {
|
||||
render.Error(w, errs.BadRequestErr(err, "error reading request body"))
|
||||
render.Error(w, r, errs.BadRequestErr(err, "error reading request body"))
|
||||
return
|
||||
}
|
||||
|
||||
logOtt(w, body.OTT)
|
||||
if err := body.Validate(); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
publicKey, err := ssh.ParsePublicKey(body.PublicKey)
|
||||
if err != nil {
|
||||
render.Error(w, errs.BadRequestErr(err, "error parsing publicKey"))
|
||||
render.Error(w, r, errs.BadRequestErr(err, "error parsing publicKey"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -273,7 +276,7 @@ func SSHSign(w http.ResponseWriter, r *http.Request) {
|
||||
if body.AddUserPublicKey != nil {
|
||||
addUserPublicKey, err = ssh.ParsePublicKey(body.AddUserPublicKey)
|
||||
if err != nil {
|
||||
render.Error(w, errs.BadRequestErr(err, "error parsing addUserPublicKey"))
|
||||
render.Error(w, r, errs.BadRequestErr(err, "error parsing addUserPublicKey"))
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -289,17 +292,18 @@ func SSHSign(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
ctx := provisioner.NewContextWithMethod(r.Context(), provisioner.SSHSignMethod)
|
||||
ctx = provisioner.NewContextWithToken(ctx, body.OTT)
|
||||
ctx = provisioner.NewContextWithCertType(ctx, opts.CertType)
|
||||
|
||||
a := mustAuthority(ctx)
|
||||
signOpts, err := a.Authorize(ctx, body.OTT)
|
||||
if err != nil {
|
||||
render.Error(w, errs.UnauthorizedErr(err))
|
||||
render.Error(w, r, errs.UnauthorizedErr(err))
|
||||
return
|
||||
}
|
||||
|
||||
cert, err := a.SignSSH(ctx, publicKey, opts, signOpts...)
|
||||
if err != nil {
|
||||
render.Error(w, errs.ForbiddenErr(err, "error signing ssh certificate"))
|
||||
render.Error(w, r, errs.ForbiddenErr(err, "error signing ssh certificate"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -307,7 +311,7 @@ func SSHSign(w http.ResponseWriter, r *http.Request) {
|
||||
if addUserPublicKey != nil && authority.IsValidForAddUser(cert) == nil {
|
||||
addUserCert, err := a.SignSSHAddUser(ctx, addUserPublicKey, cert)
|
||||
if err != nil {
|
||||
render.Error(w, errs.ForbiddenErr(err, "error signing ssh certificate"))
|
||||
render.Error(w, r, errs.ForbiddenErr(err, "error signing ssh certificate"))
|
||||
return
|
||||
}
|
||||
addUserCertificate = &SSHCertificate{addUserCert}
|
||||
@@ -320,26 +324,27 @@ func SSHSign(w http.ResponseWriter, r *http.Request) {
|
||||
ctx = provisioner.NewContextWithMethod(ctx, provisioner.SignIdentityMethod)
|
||||
signOpts, err := a.Authorize(ctx, body.OTT)
|
||||
if err != nil {
|
||||
render.Error(w, errs.UnauthorizedErr(err))
|
||||
render.Error(w, r, errs.UnauthorizedErr(err))
|
||||
return
|
||||
}
|
||||
|
||||
// Enforce the same duration as ssh certificate.
|
||||
signOpts = append(signOpts, &identityModifier{
|
||||
Identity: getIdentityURI(cr),
|
||||
NotBefore: time.Unix(int64(cert.ValidAfter), 0),
|
||||
NotAfter: time.Unix(int64(cert.ValidBefore), 0),
|
||||
})
|
||||
|
||||
certChain, err := a.SignWithContext(ctx, cr, provisioner.SignOptions{}, signOpts...)
|
||||
if err != nil {
|
||||
render.Error(w, errs.ForbiddenErr(err, "error signing identity certificate"))
|
||||
render.Error(w, r, errs.ForbiddenErr(err, "error signing identity certificate"))
|
||||
return
|
||||
}
|
||||
identityCertificate = certChainToPEM(certChain)
|
||||
}
|
||||
|
||||
LogSSHCertificate(w, cert)
|
||||
render.JSONStatus(w, &SSHSignResponse{
|
||||
render.JSONStatus(w, r, &SSHSignResponse{
|
||||
Certificate: SSHCertificate{cert},
|
||||
AddUserCertificate: addUserCertificate,
|
||||
IdentityCertificate: identityCertificate,
|
||||
@@ -352,12 +357,12 @@ func SSHRoots(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
keys, err := mustAuthority(ctx).GetSSHRoots(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, errs.InternalServerErr(err))
|
||||
render.Error(w, r, errs.InternalServerErr(err))
|
||||
return
|
||||
}
|
||||
|
||||
if len(keys.HostKeys) == 0 && len(keys.UserKeys) == 0 {
|
||||
render.Error(w, errs.NotFound("no keys found"))
|
||||
render.Error(w, r, errs.NotFound("no keys found"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -369,7 +374,7 @@ func SSHRoots(w http.ResponseWriter, r *http.Request) {
|
||||
resp.UserKeys = append(resp.UserKeys, SSHPublicKey{PublicKey: k})
|
||||
}
|
||||
|
||||
render.JSON(w, resp)
|
||||
render.JSON(w, r, resp)
|
||||
}
|
||||
|
||||
// SSHFederation is an HTTP handler that returns the federated SSH public keys
|
||||
@@ -378,12 +383,12 @@ func SSHFederation(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
keys, err := mustAuthority(ctx).GetSSHFederation(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, errs.InternalServerErr(err))
|
||||
render.Error(w, r, errs.InternalServerErr(err))
|
||||
return
|
||||
}
|
||||
|
||||
if len(keys.HostKeys) == 0 && len(keys.UserKeys) == 0 {
|
||||
render.Error(w, errs.NotFound("no keys found"))
|
||||
render.Error(w, r, errs.NotFound("no keys found"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -395,7 +400,7 @@ func SSHFederation(w http.ResponseWriter, r *http.Request) {
|
||||
resp.UserKeys = append(resp.UserKeys, SSHPublicKey{PublicKey: k})
|
||||
}
|
||||
|
||||
render.JSON(w, resp)
|
||||
render.JSON(w, r, resp)
|
||||
}
|
||||
|
||||
// SSHConfig is an HTTP handler that returns rendered templates for ssh clients
|
||||
@@ -403,18 +408,18 @@ func SSHFederation(w http.ResponseWriter, r *http.Request) {
|
||||
func SSHConfig(w http.ResponseWriter, r *http.Request) {
|
||||
var body SSHConfigRequest
|
||||
if err := read.JSON(r.Body, &body); err != nil {
|
||||
render.Error(w, errs.BadRequestErr(err, "error reading request body"))
|
||||
render.Error(w, r, errs.BadRequestErr(err, "error reading request body"))
|
||||
return
|
||||
}
|
||||
if err := body.Validate(); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
ts, err := mustAuthority(ctx).GetSSHConfig(ctx, body.Type, body.Data)
|
||||
if err != nil {
|
||||
render.Error(w, errs.InternalServerErr(err))
|
||||
render.Error(w, r, errs.InternalServerErr(err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -425,32 +430,32 @@ func SSHConfig(w http.ResponseWriter, r *http.Request) {
|
||||
case provisioner.SSHHostCert:
|
||||
cfg.HostTemplates = ts
|
||||
default:
|
||||
render.Error(w, errs.InternalServer("it should hot get here"))
|
||||
render.Error(w, r, errs.InternalServer("it should hot get here"))
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, cfg)
|
||||
render.JSON(w, r, cfg)
|
||||
}
|
||||
|
||||
// SSHCheckHost is the HTTP handler that returns if a hosts certificate exists or not.
|
||||
func SSHCheckHost(w http.ResponseWriter, r *http.Request) {
|
||||
var body SSHCheckPrincipalRequest
|
||||
if err := read.JSON(r.Body, &body); err != nil {
|
||||
render.Error(w, errs.BadRequestErr(err, "error reading request body"))
|
||||
render.Error(w, r, errs.BadRequestErr(err, "error reading request body"))
|
||||
return
|
||||
}
|
||||
if err := body.Validate(); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
exists, err := mustAuthority(ctx).CheckSSHHost(ctx, body.Principal, body.Token)
|
||||
if err != nil {
|
||||
render.Error(w, errs.InternalServerErr(err))
|
||||
render.Error(w, r, errs.InternalServerErr(err))
|
||||
return
|
||||
}
|
||||
render.JSON(w, &SSHCheckPrincipalResponse{
|
||||
render.JSON(w, r, &SSHCheckPrincipalResponse{
|
||||
Exists: exists,
|
||||
})
|
||||
}
|
||||
@@ -465,10 +470,10 @@ func SSHGetHosts(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
hosts, err := mustAuthority(ctx).GetSSHHosts(ctx, cert)
|
||||
if err != nil {
|
||||
render.Error(w, errs.InternalServerErr(err))
|
||||
render.Error(w, r, errs.InternalServerErr(err))
|
||||
return
|
||||
}
|
||||
render.JSON(w, &SSHGetHostsResponse{
|
||||
render.JSON(w, r, &SSHGetHostsResponse{
|
||||
Hosts: hosts,
|
||||
})
|
||||
}
|
||||
@@ -477,35 +482,63 @@ func SSHGetHosts(w http.ResponseWriter, r *http.Request) {
|
||||
func SSHBastion(w http.ResponseWriter, r *http.Request) {
|
||||
var body SSHBastionRequest
|
||||
if err := read.JSON(r.Body, &body); err != nil {
|
||||
render.Error(w, errs.BadRequestErr(err, "error reading request body"))
|
||||
render.Error(w, r, errs.BadRequestErr(err, "error reading request body"))
|
||||
return
|
||||
}
|
||||
if err := body.Validate(); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
bastion, err := mustAuthority(ctx).GetSSHBastion(ctx, body.User, body.Hostname)
|
||||
if err != nil {
|
||||
render.Error(w, errs.InternalServerErr(err))
|
||||
render.Error(w, r, errs.InternalServerErr(err))
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, &SSHBastionResponse{
|
||||
render.JSON(w, r, &SSHBastionResponse{
|
||||
Hostname: body.Hostname,
|
||||
Bastion: bastion,
|
||||
})
|
||||
}
|
||||
|
||||
// identityModifier is a custom modifier used to force a fixed duration.
|
||||
// identityModifier is a custom modifier used to force a fixed duration, and set
|
||||
// the identity URI.
|
||||
type identityModifier struct {
|
||||
Identity *url.URL
|
||||
NotBefore time.Time
|
||||
NotAfter time.Time
|
||||
}
|
||||
|
||||
// Enforce implements the enforcer interface and sets the validity bounds and
|
||||
// the identity uri to the certificate.
|
||||
func (m *identityModifier) Enforce(cert *x509.Certificate) error {
|
||||
cert.NotBefore = m.NotBefore
|
||||
cert.NotAfter = m.NotAfter
|
||||
if m.Identity != nil {
|
||||
var identityURL = m.Identity.String()
|
||||
for _, u := range cert.URIs {
|
||||
if u.String() == identityURL {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
cert.URIs = append(cert.URIs, m.Identity)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getIdentityURI returns the first valid UUID URN from the given CSR.
|
||||
func getIdentityURI(cr *x509.CertificateRequest) *url.URL {
|
||||
for _, u := range cr.URIs {
|
||||
s := u.String()
|
||||
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
if len(s) == 9+36 && strings.EqualFold(s[:9], "urn:uuid:") {
|
||||
if _, err := uuid.Parse(s); err == nil {
|
||||
return u
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -42,19 +42,19 @@ type SSHRekeyResponse struct {
|
||||
func SSHRekey(w http.ResponseWriter, r *http.Request) {
|
||||
var body SSHRekeyRequest
|
||||
if err := read.JSON(r.Body, &body); err != nil {
|
||||
render.Error(w, errs.BadRequestErr(err, "error reading request body"))
|
||||
render.Error(w, r, errs.BadRequestErr(err, "error reading request body"))
|
||||
return
|
||||
}
|
||||
|
||||
logOtt(w, body.OTT)
|
||||
if err := body.Validate(); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
publicKey, err := ssh.ParsePublicKey(body.PublicKey)
|
||||
if err != nil {
|
||||
render.Error(w, errs.BadRequestErr(err, "error parsing publicKey"))
|
||||
render.Error(w, r, errs.BadRequestErr(err, "error parsing publicKey"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -64,18 +64,18 @@ func SSHRekey(w http.ResponseWriter, r *http.Request) {
|
||||
a := mustAuthority(ctx)
|
||||
signOpts, err := a.Authorize(ctx, body.OTT)
|
||||
if err != nil {
|
||||
render.Error(w, errs.UnauthorizedErr(err))
|
||||
render.Error(w, r, errs.UnauthorizedErr(err))
|
||||
return
|
||||
}
|
||||
oldCert, _, err := provisioner.ExtractSSHPOPCert(body.OTT)
|
||||
if err != nil {
|
||||
render.Error(w, errs.InternalServerErr(err))
|
||||
render.Error(w, r, errs.InternalServerErr(err))
|
||||
return
|
||||
}
|
||||
|
||||
newCert, err := a.RekeySSH(ctx, oldCert, publicKey, signOpts...)
|
||||
if err != nil {
|
||||
render.Error(w, errs.ForbiddenErr(err, "error rekeying ssh certificate"))
|
||||
render.Error(w, r, errs.ForbiddenErr(err, "error rekeying ssh certificate"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -85,12 +85,12 @@ func SSHRekey(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
identity, err := renewIdentityCertificate(r, notBefore, notAfter)
|
||||
if err != nil {
|
||||
render.Error(w, errs.ForbiddenErr(err, "error renewing identity certificate"))
|
||||
render.Error(w, r, errs.ForbiddenErr(err, "error renewing identity certificate"))
|
||||
return
|
||||
}
|
||||
|
||||
LogSSHCertificate(w, newCert)
|
||||
render.JSONStatus(w, &SSHRekeyResponse{
|
||||
render.JSONStatus(w, r, &SSHRekeyResponse{
|
||||
Certificate: SSHCertificate{newCert},
|
||||
IdentityCertificate: identity,
|
||||
}, http.StatusCreated)
|
||||
|
||||
@@ -40,13 +40,13 @@ type SSHRenewResponse struct {
|
||||
func SSHRenew(w http.ResponseWriter, r *http.Request) {
|
||||
var body SSHRenewRequest
|
||||
if err := read.JSON(r.Body, &body); err != nil {
|
||||
render.Error(w, errs.BadRequestErr(err, "error reading request body"))
|
||||
render.Error(w, r, errs.BadRequestErr(err, "error reading request body"))
|
||||
return
|
||||
}
|
||||
|
||||
logOtt(w, body.OTT)
|
||||
if err := body.Validate(); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -56,18 +56,18 @@ func SSHRenew(w http.ResponseWriter, r *http.Request) {
|
||||
a := mustAuthority(ctx)
|
||||
_, err := a.Authorize(ctx, body.OTT)
|
||||
if err != nil {
|
||||
render.Error(w, errs.UnauthorizedErr(err))
|
||||
render.Error(w, r, errs.UnauthorizedErr(err))
|
||||
return
|
||||
}
|
||||
oldCert, _, err := provisioner.ExtractSSHPOPCert(body.OTT)
|
||||
if err != nil {
|
||||
render.Error(w, errs.InternalServerErr(err))
|
||||
render.Error(w, r, errs.InternalServerErr(err))
|
||||
return
|
||||
}
|
||||
|
||||
newCert, err := a.RenewSSH(ctx, oldCert)
|
||||
if err != nil {
|
||||
render.Error(w, errs.ForbiddenErr(err, "error renewing ssh certificate"))
|
||||
render.Error(w, r, errs.ForbiddenErr(err, "error renewing ssh certificate"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -77,12 +77,12 @@ func SSHRenew(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
identity, err := renewIdentityCertificate(r, notBefore, notAfter)
|
||||
if err != nil {
|
||||
render.Error(w, errs.ForbiddenErr(err, "error renewing identity certificate"))
|
||||
render.Error(w, r, errs.ForbiddenErr(err, "error renewing identity certificate"))
|
||||
return
|
||||
}
|
||||
|
||||
LogSSHCertificate(w, newCert)
|
||||
render.JSONStatus(w, &SSHSignResponse{
|
||||
render.JSONStatus(w, r, &SSHSignResponse{
|
||||
Certificate: SSHCertificate{newCert},
|
||||
IdentityCertificate: identity,
|
||||
}, http.StatusCreated)
|
||||
|
||||
@@ -51,12 +51,12 @@ func (r *SSHRevokeRequest) Validate() (err error) {
|
||||
func SSHRevoke(w http.ResponseWriter, r *http.Request) {
|
||||
var body SSHRevokeRequest
|
||||
if err := read.JSON(r.Body, &body); err != nil {
|
||||
render.Error(w, errs.BadRequestErr(err, "error reading request body"))
|
||||
render.Error(w, r, errs.BadRequestErr(err, "error reading request body"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := body.Validate(); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -75,18 +75,18 @@ func SSHRevoke(w http.ResponseWriter, r *http.Request) {
|
||||
logOtt(w, body.OTT)
|
||||
|
||||
if _, err := a.Authorize(ctx, body.OTT); err != nil {
|
||||
render.Error(w, errs.UnauthorizedErr(err))
|
||||
render.Error(w, r, errs.UnauthorizedErr(err))
|
||||
return
|
||||
}
|
||||
opts.OTT = body.OTT
|
||||
|
||||
if err := a.Revoke(ctx, opts); err != nil {
|
||||
render.Error(w, errs.ForbiddenErr(err, "error revoking ssh certificate"))
|
||||
render.Error(w, r, errs.ForbiddenErr(err, "error revoking ssh certificate"))
|
||||
return
|
||||
}
|
||||
|
||||
logSSHRevoke(w, opts)
|
||||
render.JSON(w, &SSHRevokeResponse{Status: "ok"})
|
||||
render.JSON(w, r, &SSHRevokeResponse{Status: "ok"})
|
||||
}
|
||||
|
||||
func logSSHRevoke(w http.ResponseWriter, ri *authority.RevokeOptions) {
|
||||
|
||||
141
api/ssh_test.go
141
api/ssh_test.go
@@ -13,18 +13,20 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
|
||||
"github.com/smallstep/assert"
|
||||
"github.com/google/uuid"
|
||||
"github.com/smallstep/certificates/authority"
|
||||
"github.com/smallstep/certificates/authority/provisioner"
|
||||
"github.com/smallstep/certificates/logging"
|
||||
"github.com/smallstep/certificates/templates"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -123,9 +125,9 @@ func getSignedHostCertificate() (*ssh.Certificate, error) {
|
||||
|
||||
func TestSSHCertificate_MarshalJSON(t *testing.T) {
|
||||
user, err := getSignedUserCertificate()
|
||||
assert.FatalError(t, err)
|
||||
require.NoError(t, err)
|
||||
host, err := getSignedHostCertificate()
|
||||
assert.FatalError(t, err)
|
||||
require.NoError(t, err)
|
||||
userB64 := base64.StdEncoding.EncodeToString(user.Marshal())
|
||||
hostB64 := base64.StdEncoding.EncodeToString(host.Marshal())
|
||||
|
||||
@@ -161,9 +163,9 @@ func TestSSHCertificate_MarshalJSON(t *testing.T) {
|
||||
|
||||
func TestSSHCertificate_UnmarshalJSON(t *testing.T) {
|
||||
user, err := getSignedUserCertificate()
|
||||
assert.FatalError(t, err)
|
||||
require.NoError(t, err)
|
||||
host, err := getSignedHostCertificate()
|
||||
assert.FatalError(t, err)
|
||||
require.NoError(t, err)
|
||||
userB64 := base64.StdEncoding.EncodeToString(user.Marshal())
|
||||
hostB64 := base64.StdEncoding.EncodeToString(host.Marshal())
|
||||
keyB64 := base64.StdEncoding.EncodeToString(user.Key.Marshal())
|
||||
@@ -253,9 +255,9 @@ func TestSignSSHRequest_Validate(t *testing.T) {
|
||||
|
||||
func Test_SSHSign(t *testing.T) {
|
||||
user, err := getSignedUserCertificate()
|
||||
assert.FatalError(t, err)
|
||||
require.NoError(t, err)
|
||||
host, err := getSignedHostCertificate()
|
||||
assert.FatalError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
userB64 := base64.StdEncoding.EncodeToString(user.Marshal())
|
||||
hostB64 := base64.StdEncoding.EncodeToString(host.Marshal())
|
||||
@@ -264,24 +266,24 @@ func Test_SSHSign(t *testing.T) {
|
||||
PublicKey: user.Key.Marshal(),
|
||||
OTT: "ott",
|
||||
})
|
||||
assert.FatalError(t, err)
|
||||
require.NoError(t, err)
|
||||
hostReq, err := json.Marshal(SSHSignRequest{
|
||||
PublicKey: host.Key.Marshal(),
|
||||
OTT: "ott",
|
||||
})
|
||||
assert.FatalError(t, err)
|
||||
require.NoError(t, err)
|
||||
userAddReq, err := json.Marshal(SSHSignRequest{
|
||||
PublicKey: user.Key.Marshal(),
|
||||
OTT: "ott",
|
||||
AddUserPublicKey: user.Key.Marshal(),
|
||||
})
|
||||
assert.FatalError(t, err)
|
||||
require.NoError(t, err)
|
||||
userIdentityReq, err := json.Marshal(SSHSignRequest{
|
||||
PublicKey: user.Key.Marshal(),
|
||||
OTT: "ott",
|
||||
IdentityCSR: CertificateRequest{parseCertificateRequest(csrPEM)},
|
||||
})
|
||||
assert.FatalError(t, err)
|
||||
require.NoError(t, err)
|
||||
identityCerts := []*x509.Certificate{
|
||||
parseCertificate(certPEM),
|
||||
}
|
||||
@@ -355,11 +357,11 @@ func Test_SSHSign(t *testing.T) {
|
||||
|
||||
func Test_SSHRoots(t *testing.T) {
|
||||
user, err := ssh.NewPublicKey(sshUserKey.Public())
|
||||
assert.FatalError(t, err)
|
||||
require.NoError(t, err)
|
||||
userB64 := base64.StdEncoding.EncodeToString(user.Marshal())
|
||||
|
||||
host, err := ssh.NewPublicKey(sshHostKey.Public())
|
||||
assert.FatalError(t, err)
|
||||
require.NoError(t, err)
|
||||
hostB64 := base64.StdEncoding.EncodeToString(host.Marshal())
|
||||
|
||||
tests := []struct {
|
||||
@@ -409,11 +411,11 @@ func Test_SSHRoots(t *testing.T) {
|
||||
|
||||
func Test_SSHFederation(t *testing.T) {
|
||||
user, err := ssh.NewPublicKey(sshUserKey.Public())
|
||||
assert.FatalError(t, err)
|
||||
require.NoError(t, err)
|
||||
userB64 := base64.StdEncoding.EncodeToString(user.Marshal())
|
||||
|
||||
host, err := ssh.NewPublicKey(sshHostKey.Public())
|
||||
assert.FatalError(t, err)
|
||||
require.NoError(t, err)
|
||||
hostB64 := base64.StdEncoding.EncodeToString(host.Marshal())
|
||||
|
||||
tests := []struct {
|
||||
@@ -471,9 +473,9 @@ func Test_SSHConfig(t *testing.T) {
|
||||
{Name: "ca.tpl", Type: templates.File, Comment: "#", Path: "/etc/ssh/ca.pub", Content: []byte("ecdsa-sha2-nistp256 AAAA...=")},
|
||||
}
|
||||
userJSON, err := json.Marshal(userOutput)
|
||||
assert.FatalError(t, err)
|
||||
require.NoError(t, err)
|
||||
hostJSON, err := json.Marshal(hostOutput)
|
||||
assert.FatalError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -574,7 +576,7 @@ func Test_SSHGetHosts(t *testing.T) {
|
||||
{HostID: "2", HostTags: []authority.HostTag{{ID: "1", Name: "group", Value: "1"}, {ID: "2", Name: "group", Value: "2"}}, Hostname: "host2"},
|
||||
}
|
||||
hostsJSON, err := json.Marshal(hosts)
|
||||
assert.FatalError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -676,7 +678,7 @@ func Test_SSHBastion(t *testing.T) {
|
||||
|
||||
func TestSSHPublicKey_MarshalJSON(t *testing.T) {
|
||||
key, err := ssh.NewPublicKey(sshUserKey.Public())
|
||||
assert.FatalError(t, err)
|
||||
require.NoError(t, err)
|
||||
keyB64 := base64.StdEncoding.EncodeToString(key.Marshal())
|
||||
|
||||
tests := []struct {
|
||||
@@ -705,7 +707,7 @@ func TestSSHPublicKey_MarshalJSON(t *testing.T) {
|
||||
|
||||
func TestSSHPublicKey_UnmarshalJSON(t *testing.T) {
|
||||
key, err := ssh.NewPublicKey(sshUserKey.Public())
|
||||
assert.FatalError(t, err)
|
||||
require.NoError(t, err)
|
||||
keyB64 := base64.StdEncoding.EncodeToString(key.Marshal())
|
||||
|
||||
type args struct {
|
||||
@@ -736,3 +738,98 @@ func TestSSHPublicKey_UnmarshalJSON(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_identityModifier_Enforce(t *testing.T) {
|
||||
now := time.Now()
|
||||
type fields struct {
|
||||
Identity *url.URL
|
||||
NotBefore time.Time
|
||||
NotAfter time.Time
|
||||
}
|
||||
type args struct {
|
||||
cert *x509.Certificate
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want *x509.Certificate
|
||||
assertion assert.ErrorAssertionFunc
|
||||
}{
|
||||
{"ok", fields{&url.URL{Scheme: "urn", Opaque: "uuid:0c4670b2-d9f1-42bb-9045-184836f16733"}, now, now.Add(time.Hour)},
|
||||
args{&x509.Certificate{}}, &x509.Certificate{
|
||||
NotBefore: now,
|
||||
NotAfter: now.Add(time.Hour),
|
||||
URIs: []*url.URL{{Scheme: "urn", Opaque: "uuid:0c4670b2-d9f1-42bb-9045-184836f16733"}},
|
||||
}, assert.NoError},
|
||||
{"ok exists", fields{&url.URL{Scheme: "urn", Opaque: "uuid:0c4670b2-d9f1-42bb-9045-184836f16733"}, now, now.Add(time.Hour)},
|
||||
args{&x509.Certificate{
|
||||
URIs: []*url.URL{{Scheme: "urn", Opaque: "uuid:0c4670b2-d9f1-42bb-9045-184836f16733"}},
|
||||
}}, &x509.Certificate{
|
||||
NotBefore: now,
|
||||
NotAfter: now.Add(time.Hour),
|
||||
URIs: []*url.URL{{Scheme: "urn", Opaque: "uuid:0c4670b2-d9f1-42bb-9045-184836f16733"}},
|
||||
}, assert.NoError},
|
||||
{"ok append", fields{&url.URL{Scheme: "urn", Opaque: "uuid:0c4670b2-d9f1-42bb-9045-184836f16733"}, now, now.Add(time.Hour)},
|
||||
args{&x509.Certificate{
|
||||
URIs: []*url.URL{{Scheme: "urn", Opaque: "uuid:27bb66db-e12a-4ff6-9161-aa6b0a98f914"}},
|
||||
}}, &x509.Certificate{
|
||||
NotBefore: now,
|
||||
NotAfter: now.Add(time.Hour),
|
||||
URIs: []*url.URL{
|
||||
{Scheme: "urn", Opaque: "uuid:27bb66db-e12a-4ff6-9161-aa6b0a98f914"},
|
||||
{Scheme: "urn", Opaque: "uuid:0c4670b2-d9f1-42bb-9045-184836f16733"},
|
||||
},
|
||||
}, assert.NoError},
|
||||
{"ok no identity", fields{nil, now, now.Add(time.Hour)},
|
||||
args{&x509.Certificate{}}, &x509.Certificate{
|
||||
NotBefore: now,
|
||||
NotAfter: now.Add(time.Hour),
|
||||
}, assert.NoError},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
m := &identityModifier{
|
||||
Identity: tt.fields.Identity,
|
||||
NotBefore: tt.fields.NotBefore,
|
||||
NotAfter: tt.fields.NotAfter,
|
||||
}
|
||||
tt.assertion(t, m.Enforce(tt.args.cert))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_getIdentityURI(t *testing.T) {
|
||||
id, err := uuid.Parse("54a2ec9d-a7d9-4b53-8f9a-efcd275e35e1")
|
||||
require.NoError(t, err)
|
||||
u, err := url.Parse(id.URN())
|
||||
require.NoError(t, err)
|
||||
|
||||
type args struct {
|
||||
cr *x509.CertificateRequest
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *url.URL
|
||||
}{
|
||||
{"ok", args{&x509.CertificateRequest{
|
||||
URIs: []*url.URL{u},
|
||||
}}, &url.URL{Scheme: "urn", Opaque: "uuid:54a2ec9d-a7d9-4b53-8f9a-efcd275e35e1"}},
|
||||
{"ok multiple", args{&x509.CertificateRequest{
|
||||
URIs: []*url.URL{u, {Scheme: "urn", Opaque: "uuid:f0e74f3a-95fe-4cf6-98e3-68e55b69ba48"}},
|
||||
}}, &url.URL{Scheme: "urn", Opaque: "uuid:54a2ec9d-a7d9-4b53-8f9a-efcd275e35e1"}},
|
||||
{"ok multiple with invalid", args{&x509.CertificateRequest{
|
||||
URIs: []*url.URL{{Scheme: "urn", Opaque: "uuid:f0e74f3a+95fe+4cf6+98e3+68e55b69ba48"}, u},
|
||||
}}, &url.URL{Scheme: "urn", Opaque: "uuid:54a2ec9d-a7d9-4b53-8f9a-efcd275e35e1"}},
|
||||
{"ok missing", args{&x509.CertificateRequest{
|
||||
URIs: []*url.URL{{Scheme: "https", Host: "example.com", Path: "/54a2ec9d-a7d9-4b53-8f9a-efcd275e35e1"}},
|
||||
}}, nil},
|
||||
{"ok empty", args{&x509.CertificateRequest{}}, nil},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equal(t, tt.want, getIdentityURI(tt.args.cr))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,12 +40,12 @@ func requireEABEnabled(next http.HandlerFunc) http.HandlerFunc {
|
||||
|
||||
acmeProvisioner := prov.GetDetails().GetACME()
|
||||
if acmeProvisioner == nil {
|
||||
render.Error(w, admin.NewErrorISE("error getting ACME details for provisioner '%s'", prov.GetName()))
|
||||
render.Error(w, r, admin.NewErrorISE("error getting ACME details for provisioner '%s'", prov.GetName()))
|
||||
return
|
||||
}
|
||||
|
||||
if !acmeProvisioner.RequireEab {
|
||||
render.Error(w, admin.NewError(admin.ErrorBadRequestType, "ACME EAB not enabled for provisioner '%s'", prov.GetName()))
|
||||
render.Error(w, r, admin.NewError(admin.ErrorBadRequestType, "ACME EAB not enabled for provisioner '%s'", prov.GetName()))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -69,18 +69,18 @@ func NewACMEAdminResponder() ACMEAdminResponder {
|
||||
}
|
||||
|
||||
// GetExternalAccountKeys writes the response for the EAB keys GET endpoint
|
||||
func (h *acmeAdminResponder) GetExternalAccountKeys(w http.ResponseWriter, _ *http.Request) {
|
||||
render.Error(w, admin.NewError(admin.ErrorNotImplementedType, "this functionality is currently only available in Certificate Manager: https://u.step.sm/cm"))
|
||||
func (h *acmeAdminResponder) GetExternalAccountKeys(w http.ResponseWriter, r *http.Request) {
|
||||
render.Error(w, r, admin.NewError(admin.ErrorNotImplementedType, "this functionality is currently only available in Certificate Manager: https://u.step.sm/cm"))
|
||||
}
|
||||
|
||||
// CreateExternalAccountKey writes the response for the EAB key POST endpoint
|
||||
func (h *acmeAdminResponder) CreateExternalAccountKey(w http.ResponseWriter, _ *http.Request) {
|
||||
render.Error(w, admin.NewError(admin.ErrorNotImplementedType, "this functionality is currently only available in Certificate Manager: https://u.step.sm/cm"))
|
||||
func (h *acmeAdminResponder) CreateExternalAccountKey(w http.ResponseWriter, r *http.Request) {
|
||||
render.Error(w, r, admin.NewError(admin.ErrorNotImplementedType, "this functionality is currently only available in Certificate Manager: https://u.step.sm/cm"))
|
||||
}
|
||||
|
||||
// DeleteExternalAccountKey writes the response for the EAB key DELETE endpoint
|
||||
func (h *acmeAdminResponder) DeleteExternalAccountKey(w http.ResponseWriter, _ *http.Request) {
|
||||
render.Error(w, admin.NewError(admin.ErrorNotImplementedType, "this functionality is currently only available in Certificate Manager: https://u.step.sm/cm"))
|
||||
func (h *acmeAdminResponder) DeleteExternalAccountKey(w http.ResponseWriter, r *http.Request) {
|
||||
render.Error(w, r, admin.NewError(admin.ErrorNotImplementedType, "this functionality is currently only available in Certificate Manager: https://u.step.sm/cm"))
|
||||
}
|
||||
|
||||
func eakToLinked(k *acme.ExternalAccountKey) *linkedca.EABKey {
|
||||
|
||||
@@ -90,7 +90,7 @@ func GetAdmin(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
adm, ok := mustAuthority(r.Context()).LoadAdminByID(id)
|
||||
if !ok {
|
||||
render.Error(w, admin.NewError(admin.ErrorNotFoundType,
|
||||
render.Error(w, r, admin.NewError(admin.ErrorNotFoundType,
|
||||
"admin %s not found", id))
|
||||
return
|
||||
}
|
||||
@@ -101,17 +101,17 @@ func GetAdmin(w http.ResponseWriter, r *http.Request) {
|
||||
func GetAdmins(w http.ResponseWriter, r *http.Request) {
|
||||
cursor, limit, err := api.ParseCursor(r)
|
||||
if err != nil {
|
||||
render.Error(w, admin.WrapError(admin.ErrorBadRequestType, err,
|
||||
render.Error(w, r, admin.WrapError(admin.ErrorBadRequestType, err,
|
||||
"error parsing cursor and limit from query params"))
|
||||
return
|
||||
}
|
||||
|
||||
admins, nextCursor, err := mustAuthority(r.Context()).GetAdmins(cursor, limit)
|
||||
if err != nil {
|
||||
render.Error(w, admin.WrapErrorISE(err, "error retrieving paginated admins"))
|
||||
render.Error(w, r, admin.WrapErrorISE(err, "error retrieving paginated admins"))
|
||||
return
|
||||
}
|
||||
render.JSON(w, &GetAdminsResponse{
|
||||
render.JSON(w, r, &GetAdminsResponse{
|
||||
Admins: admins,
|
||||
NextCursor: nextCursor,
|
||||
})
|
||||
@@ -121,19 +121,19 @@ func GetAdmins(w http.ResponseWriter, r *http.Request) {
|
||||
func CreateAdmin(w http.ResponseWriter, r *http.Request) {
|
||||
var body CreateAdminRequest
|
||||
if err := read.JSON(r.Body, &body); err != nil {
|
||||
render.Error(w, admin.WrapError(admin.ErrorBadRequestType, err, "error reading request body"))
|
||||
render.Error(w, r, admin.WrapError(admin.ErrorBadRequestType, err, "error reading request body"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := body.Validate(); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
auth := mustAuthority(r.Context())
|
||||
p, err := auth.LoadProvisionerByName(body.Provisioner)
|
||||
if err != nil {
|
||||
render.Error(w, admin.WrapErrorISE(err, "error loading provisioner %s", body.Provisioner))
|
||||
render.Error(w, r, admin.WrapErrorISE(err, "error loading provisioner %s", body.Provisioner))
|
||||
return
|
||||
}
|
||||
adm := &linkedca.Admin{
|
||||
@@ -143,7 +143,7 @@ func CreateAdmin(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
// Store to authority collection.
|
||||
if err := auth.StoreAdmin(r.Context(), adm, p); err != nil {
|
||||
render.Error(w, admin.WrapErrorISE(err, "error storing admin"))
|
||||
render.Error(w, r, admin.WrapErrorISE(err, "error storing admin"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -155,23 +155,23 @@ func DeleteAdmin(w http.ResponseWriter, r *http.Request) {
|
||||
id := chi.URLParam(r, "id")
|
||||
|
||||
if err := mustAuthority(r.Context()).RemoveAdmin(r.Context(), id); err != nil {
|
||||
render.Error(w, admin.WrapErrorISE(err, "error deleting admin %s", id))
|
||||
render.Error(w, r, admin.WrapErrorISE(err, "error deleting admin %s", id))
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, &DeleteResponse{Status: "ok"})
|
||||
render.JSON(w, r, &DeleteResponse{Status: "ok"})
|
||||
}
|
||||
|
||||
// UpdateAdmin updates an existing admin.
|
||||
func UpdateAdmin(w http.ResponseWriter, r *http.Request) {
|
||||
var body UpdateAdminRequest
|
||||
if err := read.JSON(r.Body, &body); err != nil {
|
||||
render.Error(w, admin.WrapError(admin.ErrorBadRequestType, err, "error reading request body"))
|
||||
render.Error(w, r, admin.WrapError(admin.ErrorBadRequestType, err, "error reading request body"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := body.Validate(); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -179,7 +179,7 @@ func UpdateAdmin(w http.ResponseWriter, r *http.Request) {
|
||||
auth := mustAuthority(r.Context())
|
||||
adm, err := auth.UpdateAdmin(r.Context(), id, &linkedca.Admin{Type: body.Type})
|
||||
if err != nil {
|
||||
render.Error(w, admin.WrapErrorISE(err, "error updating admin %s", id))
|
||||
render.Error(w, r, admin.WrapErrorISE(err, "error updating admin %s", id))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
@@ -20,7 +19,7 @@ import (
|
||||
func requireAPIEnabled(next http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if !mustAuthority(r.Context()).IsAdminAPIEnabled() {
|
||||
render.Error(w, admin.NewError(admin.ErrorNotImplementedType, "administration API not enabled"))
|
||||
render.Error(w, r, admin.NewError(admin.ErrorNotImplementedType, "administration API not enabled"))
|
||||
return
|
||||
}
|
||||
next(w, r)
|
||||
@@ -32,7 +31,7 @@ func extractAuthorizeTokenAdmin(next http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
tok := r.Header.Get("Authorization")
|
||||
if tok == "" {
|
||||
render.Error(w, admin.NewError(admin.ErrorUnauthorizedType,
|
||||
render.Error(w, r, admin.NewError(admin.ErrorUnauthorizedType,
|
||||
"missing authorization header token"))
|
||||
return
|
||||
}
|
||||
@@ -40,7 +39,7 @@ func extractAuthorizeTokenAdmin(next http.HandlerFunc) http.HandlerFunc {
|
||||
ctx := r.Context()
|
||||
adm, err := mustAuthority(ctx).AuthorizeAdminToken(r, tok)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -65,13 +64,13 @@ func loadProvisionerByName(next http.HandlerFunc) http.HandlerFunc {
|
||||
|
||||
// TODO(hs): distinguish 404 vs. 500
|
||||
if p, err = auth.LoadProvisionerByName(name); err != nil {
|
||||
render.Error(w, admin.WrapErrorISE(err, "error loading provisioner %s", name))
|
||||
render.Error(w, r, admin.WrapErrorISE(err, "error loading provisioner %s", name))
|
||||
return
|
||||
}
|
||||
|
||||
prov, err := adminDB.GetProvisioner(ctx, p.GetID())
|
||||
if err != nil {
|
||||
render.Error(w, admin.WrapErrorISE(err, "error retrieving provisioner %s", name))
|
||||
render.Error(w, r, admin.WrapErrorISE(err, "error retrieving provisioner %s", name))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -92,7 +91,7 @@ func checkAction(next http.HandlerFunc, supportedInStandalone bool) http.Handler
|
||||
// when an action is not supported in standalone mode and when
|
||||
// using a nosql.DB backend, actions are not supported
|
||||
if _, ok := admin.MustFromContext(r.Context()).(*nosql.DB); ok {
|
||||
render.Error(w, admin.NewError(admin.ErrorNotImplementedType,
|
||||
render.Error(w, r, admin.NewError(admin.ErrorNotImplementedType,
|
||||
"operation not supported in standalone mode"))
|
||||
return
|
||||
}
|
||||
@@ -125,16 +124,16 @@ func loadExternalAccountKey(next http.HandlerFunc) http.HandlerFunc {
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if errors.Is(err, acme.ErrNotFound) {
|
||||
render.Error(w, admin.NewError(admin.ErrorNotFoundType, "ACME External Account Key not found"))
|
||||
if acme.IsErrNotFound(err) {
|
||||
render.Error(w, r, admin.NewError(admin.ErrorNotFoundType, "ACME External Account Key not found"))
|
||||
return
|
||||
}
|
||||
render.Error(w, admin.WrapErrorISE(err, "error retrieving ACME External Account Key"))
|
||||
render.Error(w, r, admin.WrapErrorISE(err, "error retrieving ACME External Account Key"))
|
||||
return
|
||||
}
|
||||
|
||||
if eak == nil {
|
||||
render.Error(w, admin.NewError(admin.ErrorNotFoundType, "ACME External Account Key not found"))
|
||||
render.Error(w, r, admin.NewError(admin.ErrorNotFoundType, "ACME External Account Key not found"))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ type PolicyAdminResponder interface {
|
||||
// policyAdminResponder implements PolicyAdminResponder.
|
||||
type policyAdminResponder struct{}
|
||||
|
||||
// NewACMEAdminResponder returns a new PolicyAdminResponder.
|
||||
// NewPolicyAdminResponder returns a new PolicyAdminResponder.
|
||||
func NewPolicyAdminResponder() PolicyAdminResponder {
|
||||
return &policyAdminResponder{}
|
||||
}
|
||||
@@ -44,7 +44,7 @@ func NewPolicyAdminResponder() PolicyAdminResponder {
|
||||
func (par *policyAdminResponder) GetAuthorityPolicy(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
if err := blockLinkedCA(ctx); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -52,12 +52,12 @@ func (par *policyAdminResponder) GetAuthorityPolicy(w http.ResponseWriter, r *ht
|
||||
authorityPolicy, err := auth.GetAuthorityPolicy(r.Context())
|
||||
var ae *admin.Error
|
||||
if errors.As(err, &ae) && !ae.IsType(admin.ErrorNotFoundType) {
|
||||
render.Error(w, admin.WrapErrorISE(ae, "error retrieving authority policy"))
|
||||
render.Error(w, r, admin.WrapErrorISE(ae, "error retrieving authority policy"))
|
||||
return
|
||||
}
|
||||
|
||||
if authorityPolicy == nil {
|
||||
render.Error(w, admin.NewError(admin.ErrorNotFoundType, "authority policy does not exist"))
|
||||
render.Error(w, r, admin.NewError(admin.ErrorNotFoundType, "authority policy does not exist"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ func (par *policyAdminResponder) GetAuthorityPolicy(w http.ResponseWriter, r *ht
|
||||
func (par *policyAdminResponder) CreateAuthorityPolicy(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
if err := blockLinkedCA(ctx); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -77,26 +77,26 @@ func (par *policyAdminResponder) CreateAuthorityPolicy(w http.ResponseWriter, r
|
||||
|
||||
var ae *admin.Error
|
||||
if errors.As(err, &ae) && !ae.IsType(admin.ErrorNotFoundType) {
|
||||
render.Error(w, admin.WrapErrorISE(err, "error retrieving authority policy"))
|
||||
render.Error(w, r, admin.WrapErrorISE(err, "error retrieving authority policy"))
|
||||
return
|
||||
}
|
||||
|
||||
if authorityPolicy != nil {
|
||||
adminErr := admin.NewError(admin.ErrorConflictType, "authority already has a policy")
|
||||
render.Error(w, adminErr)
|
||||
render.Error(w, r, adminErr)
|
||||
return
|
||||
}
|
||||
|
||||
var newPolicy = new(linkedca.Policy)
|
||||
if err := read.ProtoJSON(r.Body, newPolicy); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
newPolicy.Deduplicate()
|
||||
|
||||
if err := validatePolicy(newPolicy); err != nil {
|
||||
render.Error(w, admin.WrapError(admin.ErrorBadRequestType, err, "error validating authority policy"))
|
||||
render.Error(w, r, admin.WrapError(admin.ErrorBadRequestType, err, "error validating authority policy"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -105,11 +105,11 @@ func (par *policyAdminResponder) CreateAuthorityPolicy(w http.ResponseWriter, r
|
||||
var createdPolicy *linkedca.Policy
|
||||
if createdPolicy, err = auth.CreateAuthorityPolicy(ctx, adm, newPolicy); err != nil {
|
||||
if isBadRequest(err) {
|
||||
render.Error(w, admin.WrapError(admin.ErrorBadRequestType, err, "error storing authority policy"))
|
||||
render.Error(w, r, admin.WrapError(admin.ErrorBadRequestType, err, "error storing authority policy"))
|
||||
return
|
||||
}
|
||||
|
||||
render.Error(w, admin.WrapErrorISE(err, "error storing authority policy"))
|
||||
render.Error(w, r, admin.WrapErrorISE(err, "error storing authority policy"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ func (par *policyAdminResponder) CreateAuthorityPolicy(w http.ResponseWriter, r
|
||||
func (par *policyAdminResponder) UpdateAuthorityPolicy(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
if err := blockLinkedCA(ctx); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -129,25 +129,25 @@ func (par *policyAdminResponder) UpdateAuthorityPolicy(w http.ResponseWriter, r
|
||||
|
||||
var ae *admin.Error
|
||||
if errors.As(err, &ae) && !ae.IsType(admin.ErrorNotFoundType) {
|
||||
render.Error(w, admin.WrapErrorISE(err, "error retrieving authority policy"))
|
||||
render.Error(w, r, admin.WrapErrorISE(err, "error retrieving authority policy"))
|
||||
return
|
||||
}
|
||||
|
||||
if authorityPolicy == nil {
|
||||
render.Error(w, admin.NewError(admin.ErrorNotFoundType, "authority policy does not exist"))
|
||||
render.Error(w, r, admin.NewError(admin.ErrorNotFoundType, "authority policy does not exist"))
|
||||
return
|
||||
}
|
||||
|
||||
var newPolicy = new(linkedca.Policy)
|
||||
if err := read.ProtoJSON(r.Body, newPolicy); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
newPolicy.Deduplicate()
|
||||
|
||||
if err := validatePolicy(newPolicy); err != nil {
|
||||
render.Error(w, admin.WrapError(admin.ErrorBadRequestType, err, "error validating authority policy"))
|
||||
render.Error(w, r, admin.WrapError(admin.ErrorBadRequestType, err, "error validating authority policy"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -156,11 +156,11 @@ func (par *policyAdminResponder) UpdateAuthorityPolicy(w http.ResponseWriter, r
|
||||
var updatedPolicy *linkedca.Policy
|
||||
if updatedPolicy, err = auth.UpdateAuthorityPolicy(ctx, adm, newPolicy); err != nil {
|
||||
if isBadRequest(err) {
|
||||
render.Error(w, admin.WrapError(admin.ErrorBadRequestType, err, "error updating authority policy"))
|
||||
render.Error(w, r, admin.WrapError(admin.ErrorBadRequestType, err, "error updating authority policy"))
|
||||
return
|
||||
}
|
||||
|
||||
render.Error(w, admin.WrapErrorISE(err, "error updating authority policy"))
|
||||
render.Error(w, r, admin.WrapErrorISE(err, "error updating authority policy"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@ func (par *policyAdminResponder) UpdateAuthorityPolicy(w http.ResponseWriter, r
|
||||
func (par *policyAdminResponder) DeleteAuthorityPolicy(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
if err := blockLinkedCA(ctx); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -180,35 +180,35 @@ func (par *policyAdminResponder) DeleteAuthorityPolicy(w http.ResponseWriter, r
|
||||
|
||||
var ae *admin.Error
|
||||
if errors.As(err, &ae) && !ae.IsType(admin.ErrorNotFoundType) {
|
||||
render.Error(w, admin.WrapErrorISE(ae, "error retrieving authority policy"))
|
||||
render.Error(w, r, admin.WrapErrorISE(ae, "error retrieving authority policy"))
|
||||
return
|
||||
}
|
||||
|
||||
if authorityPolicy == nil {
|
||||
render.Error(w, admin.NewError(admin.ErrorNotFoundType, "authority policy does not exist"))
|
||||
render.Error(w, r, admin.NewError(admin.ErrorNotFoundType, "authority policy does not exist"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := auth.RemoveAuthorityPolicy(ctx); err != nil {
|
||||
render.Error(w, admin.WrapErrorISE(err, "error deleting authority policy"))
|
||||
render.Error(w, r, admin.WrapErrorISE(err, "error deleting authority policy"))
|
||||
return
|
||||
}
|
||||
|
||||
render.JSONStatus(w, DeleteResponse{Status: "ok"}, http.StatusOK)
|
||||
render.JSONStatus(w, r, DeleteResponse{Status: "ok"}, http.StatusOK)
|
||||
}
|
||||
|
||||
// GetProvisionerPolicy handles the GET /admin/provisioners/{name}/policy request
|
||||
func (par *policyAdminResponder) GetProvisionerPolicy(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
if err := blockLinkedCA(ctx); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
prov := linkedca.MustProvisionerFromContext(ctx)
|
||||
provisionerPolicy := prov.GetPolicy()
|
||||
if provisionerPolicy == nil {
|
||||
render.Error(w, admin.NewError(admin.ErrorNotFoundType, "provisioner policy does not exist"))
|
||||
render.Error(w, r, admin.NewError(admin.ErrorNotFoundType, "provisioner policy does not exist"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -219,7 +219,7 @@ func (par *policyAdminResponder) GetProvisionerPolicy(w http.ResponseWriter, r *
|
||||
func (par *policyAdminResponder) CreateProvisionerPolicy(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
if err := blockLinkedCA(ctx); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -227,20 +227,20 @@ func (par *policyAdminResponder) CreateProvisionerPolicy(w http.ResponseWriter,
|
||||
provisionerPolicy := prov.GetPolicy()
|
||||
if provisionerPolicy != nil {
|
||||
adminErr := admin.NewError(admin.ErrorConflictType, "provisioner %s already has a policy", prov.Name)
|
||||
render.Error(w, adminErr)
|
||||
render.Error(w, r, adminErr)
|
||||
return
|
||||
}
|
||||
|
||||
var newPolicy = new(linkedca.Policy)
|
||||
if err := read.ProtoJSON(r.Body, newPolicy); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
newPolicy.Deduplicate()
|
||||
|
||||
if err := validatePolicy(newPolicy); err != nil {
|
||||
render.Error(w, admin.WrapError(admin.ErrorBadRequestType, err, "error validating provisioner policy"))
|
||||
render.Error(w, r, admin.WrapError(admin.ErrorBadRequestType, err, "error validating provisioner policy"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -248,11 +248,11 @@ func (par *policyAdminResponder) CreateProvisionerPolicy(w http.ResponseWriter,
|
||||
auth := mustAuthority(ctx)
|
||||
if err := auth.UpdateProvisioner(ctx, prov); err != nil {
|
||||
if isBadRequest(err) {
|
||||
render.Error(w, admin.WrapError(admin.ErrorBadRequestType, err, "error creating provisioner policy"))
|
||||
render.Error(w, r, admin.WrapError(admin.ErrorBadRequestType, err, "error creating provisioner policy"))
|
||||
return
|
||||
}
|
||||
|
||||
render.Error(w, admin.WrapErrorISE(err, "error creating provisioner policy"))
|
||||
render.Error(w, r, admin.WrapErrorISE(err, "error creating provisioner policy"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -263,27 +263,27 @@ func (par *policyAdminResponder) CreateProvisionerPolicy(w http.ResponseWriter,
|
||||
func (par *policyAdminResponder) UpdateProvisionerPolicy(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
if err := blockLinkedCA(ctx); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
prov := linkedca.MustProvisionerFromContext(ctx)
|
||||
provisionerPolicy := prov.GetPolicy()
|
||||
if provisionerPolicy == nil {
|
||||
render.Error(w, admin.NewError(admin.ErrorNotFoundType, "provisioner policy does not exist"))
|
||||
render.Error(w, r, admin.NewError(admin.ErrorNotFoundType, "provisioner policy does not exist"))
|
||||
return
|
||||
}
|
||||
|
||||
var newPolicy = new(linkedca.Policy)
|
||||
if err := read.ProtoJSON(r.Body, newPolicy); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
newPolicy.Deduplicate()
|
||||
|
||||
if err := validatePolicy(newPolicy); err != nil {
|
||||
render.Error(w, admin.WrapError(admin.ErrorBadRequestType, err, "error validating provisioner policy"))
|
||||
render.Error(w, r, admin.WrapError(admin.ErrorBadRequestType, err, "error validating provisioner policy"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -291,11 +291,11 @@ func (par *policyAdminResponder) UpdateProvisionerPolicy(w http.ResponseWriter,
|
||||
auth := mustAuthority(ctx)
|
||||
if err := auth.UpdateProvisioner(ctx, prov); err != nil {
|
||||
if isBadRequest(err) {
|
||||
render.Error(w, admin.WrapError(admin.ErrorBadRequestType, err, "error updating provisioner policy"))
|
||||
render.Error(w, r, admin.WrapError(admin.ErrorBadRequestType, err, "error updating provisioner policy"))
|
||||
return
|
||||
}
|
||||
|
||||
render.Error(w, admin.WrapErrorISE(err, "error updating provisioner policy"))
|
||||
render.Error(w, r, admin.WrapErrorISE(err, "error updating provisioner policy"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -306,13 +306,13 @@ func (par *policyAdminResponder) UpdateProvisionerPolicy(w http.ResponseWriter,
|
||||
func (par *policyAdminResponder) DeleteProvisionerPolicy(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
if err := blockLinkedCA(ctx); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
prov := linkedca.MustProvisionerFromContext(ctx)
|
||||
if prov.Policy == nil {
|
||||
render.Error(w, admin.NewError(admin.ErrorNotFoundType, "provisioner policy does not exist"))
|
||||
render.Error(w, r, admin.NewError(admin.ErrorNotFoundType, "provisioner policy does not exist"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -321,24 +321,24 @@ func (par *policyAdminResponder) DeleteProvisionerPolicy(w http.ResponseWriter,
|
||||
|
||||
auth := mustAuthority(ctx)
|
||||
if err := auth.UpdateProvisioner(ctx, prov); err != nil {
|
||||
render.Error(w, admin.WrapErrorISE(err, "error deleting provisioner policy"))
|
||||
render.Error(w, r, admin.WrapErrorISE(err, "error deleting provisioner policy"))
|
||||
return
|
||||
}
|
||||
|
||||
render.JSONStatus(w, DeleteResponse{Status: "ok"}, http.StatusOK)
|
||||
render.JSONStatus(w, r, DeleteResponse{Status: "ok"}, http.StatusOK)
|
||||
}
|
||||
|
||||
func (par *policyAdminResponder) GetACMEAccountPolicy(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
if err := blockLinkedCA(ctx); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
eak := linkedca.MustExternalAccountKeyFromContext(ctx)
|
||||
eakPolicy := eak.GetPolicy()
|
||||
if eakPolicy == nil {
|
||||
render.Error(w, admin.NewError(admin.ErrorNotFoundType, "ACME EAK policy does not exist"))
|
||||
render.Error(w, r, admin.NewError(admin.ErrorNotFoundType, "ACME EAK policy does not exist"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -348,7 +348,7 @@ func (par *policyAdminResponder) GetACMEAccountPolicy(w http.ResponseWriter, r *
|
||||
func (par *policyAdminResponder) CreateACMEAccountPolicy(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
if err := blockLinkedCA(ctx); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -357,20 +357,20 @@ func (par *policyAdminResponder) CreateACMEAccountPolicy(w http.ResponseWriter,
|
||||
eakPolicy := eak.GetPolicy()
|
||||
if eakPolicy != nil {
|
||||
adminErr := admin.NewError(admin.ErrorConflictType, "ACME EAK %s already has a policy", eak.Id)
|
||||
render.Error(w, adminErr)
|
||||
render.Error(w, r, adminErr)
|
||||
return
|
||||
}
|
||||
|
||||
var newPolicy = new(linkedca.Policy)
|
||||
if err := read.ProtoJSON(r.Body, newPolicy); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
newPolicy.Deduplicate()
|
||||
|
||||
if err := validatePolicy(newPolicy); err != nil {
|
||||
render.Error(w, admin.WrapError(admin.ErrorBadRequestType, err, "error validating ACME EAK policy"))
|
||||
render.Error(w, r, admin.WrapError(admin.ErrorBadRequestType, err, "error validating ACME EAK policy"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -379,7 +379,7 @@ func (par *policyAdminResponder) CreateACMEAccountPolicy(w http.ResponseWriter,
|
||||
acmeEAK := linkedEAKToCertificates(eak)
|
||||
acmeDB := acme.MustDatabaseFromContext(ctx)
|
||||
if err := acmeDB.UpdateExternalAccountKey(ctx, prov.GetId(), acmeEAK); err != nil {
|
||||
render.Error(w, admin.WrapErrorISE(err, "error creating ACME EAK policy"))
|
||||
render.Error(w, r, admin.WrapErrorISE(err, "error creating ACME EAK policy"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -389,7 +389,7 @@ func (par *policyAdminResponder) CreateACMEAccountPolicy(w http.ResponseWriter,
|
||||
func (par *policyAdminResponder) UpdateACMEAccountPolicy(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
if err := blockLinkedCA(ctx); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -397,20 +397,20 @@ func (par *policyAdminResponder) UpdateACMEAccountPolicy(w http.ResponseWriter,
|
||||
eak := linkedca.MustExternalAccountKeyFromContext(ctx)
|
||||
eakPolicy := eak.GetPolicy()
|
||||
if eakPolicy == nil {
|
||||
render.Error(w, admin.NewError(admin.ErrorNotFoundType, "ACME EAK policy does not exist"))
|
||||
render.Error(w, r, admin.NewError(admin.ErrorNotFoundType, "ACME EAK policy does not exist"))
|
||||
return
|
||||
}
|
||||
|
||||
var newPolicy = new(linkedca.Policy)
|
||||
if err := read.ProtoJSON(r.Body, newPolicy); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
newPolicy.Deduplicate()
|
||||
|
||||
if err := validatePolicy(newPolicy); err != nil {
|
||||
render.Error(w, admin.WrapError(admin.ErrorBadRequestType, err, "error validating ACME EAK policy"))
|
||||
render.Error(w, r, admin.WrapError(admin.ErrorBadRequestType, err, "error validating ACME EAK policy"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -418,7 +418,7 @@ func (par *policyAdminResponder) UpdateACMEAccountPolicy(w http.ResponseWriter,
|
||||
acmeEAK := linkedEAKToCertificates(eak)
|
||||
acmeDB := acme.MustDatabaseFromContext(ctx)
|
||||
if err := acmeDB.UpdateExternalAccountKey(ctx, prov.GetId(), acmeEAK); err != nil {
|
||||
render.Error(w, admin.WrapErrorISE(err, "error updating ACME EAK policy"))
|
||||
render.Error(w, r, admin.WrapErrorISE(err, "error updating ACME EAK policy"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -428,7 +428,7 @@ func (par *policyAdminResponder) UpdateACMEAccountPolicy(w http.ResponseWriter,
|
||||
func (par *policyAdminResponder) DeleteACMEAccountPolicy(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
if err := blockLinkedCA(ctx); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -436,7 +436,7 @@ func (par *policyAdminResponder) DeleteACMEAccountPolicy(w http.ResponseWriter,
|
||||
eak := linkedca.MustExternalAccountKeyFromContext(ctx)
|
||||
eakPolicy := eak.GetPolicy()
|
||||
if eakPolicy == nil {
|
||||
render.Error(w, admin.NewError(admin.ErrorNotFoundType, "ACME EAK policy does not exist"))
|
||||
render.Error(w, r, admin.NewError(admin.ErrorNotFoundType, "ACME EAK policy does not exist"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -446,11 +446,11 @@ func (par *policyAdminResponder) DeleteACMEAccountPolicy(w http.ResponseWriter,
|
||||
acmeEAK := linkedEAKToCertificates(eak)
|
||||
acmeDB := acme.MustDatabaseFromContext(ctx)
|
||||
if err := acmeDB.UpdateExternalAccountKey(ctx, prov.GetId(), acmeEAK); err != nil {
|
||||
render.Error(w, admin.WrapErrorISE(err, "error deleting ACME EAK policy"))
|
||||
render.Error(w, r, admin.WrapErrorISE(err, "error deleting ACME EAK policy"))
|
||||
return
|
||||
}
|
||||
|
||||
render.JSONStatus(w, DeleteResponse{Status: "ok"}, http.StatusOK)
|
||||
render.JSONStatus(w, r, DeleteResponse{Status: "ok"}, http.StatusOK)
|
||||
}
|
||||
|
||||
// blockLinkedCA blocks all API operations on linked deployments
|
||||
|
||||
@@ -40,19 +40,19 @@ func GetProvisioner(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
if id != "" {
|
||||
if p, err = auth.LoadProvisionerByID(id); err != nil {
|
||||
render.Error(w, admin.WrapErrorISE(err, "error loading provisioner %s", id))
|
||||
render.Error(w, r, admin.WrapErrorISE(err, "error loading provisioner %s", id))
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if p, err = auth.LoadProvisionerByName(name); err != nil {
|
||||
render.Error(w, admin.WrapErrorISE(err, "error loading provisioner %s", name))
|
||||
render.Error(w, r, admin.WrapErrorISE(err, "error loading provisioner %s", name))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
prov, err := db.GetProvisioner(ctx, p.GetID())
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
render.ProtoJSON(w, prov)
|
||||
@@ -62,17 +62,17 @@ func GetProvisioner(w http.ResponseWriter, r *http.Request) {
|
||||
func GetProvisioners(w http.ResponseWriter, r *http.Request) {
|
||||
cursor, limit, err := api.ParseCursor(r)
|
||||
if err != nil {
|
||||
render.Error(w, admin.WrapError(admin.ErrorBadRequestType, err,
|
||||
render.Error(w, r, admin.WrapError(admin.ErrorBadRequestType, err,
|
||||
"error parsing cursor and limit from query params"))
|
||||
return
|
||||
}
|
||||
|
||||
p, next, err := mustAuthority(r.Context()).GetProvisioners(cursor, limit)
|
||||
if err != nil {
|
||||
render.Error(w, errs.InternalServerErr(err))
|
||||
render.Error(w, r, errs.InternalServerErr(err))
|
||||
return
|
||||
}
|
||||
render.JSON(w, &GetProvisionersResponse{
|
||||
render.JSON(w, r, &GetProvisionersResponse{
|
||||
Provisioners: p,
|
||||
NextCursor: next,
|
||||
})
|
||||
@@ -82,24 +82,24 @@ func GetProvisioners(w http.ResponseWriter, r *http.Request) {
|
||||
func CreateProvisioner(w http.ResponseWriter, r *http.Request) {
|
||||
var prov = new(linkedca.Provisioner)
|
||||
if err := read.ProtoJSON(r.Body, prov); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Validate inputs
|
||||
if err := authority.ValidateClaims(prov.Claims); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
// validate the templates and template data
|
||||
if err := validateTemplates(prov.X509Template, prov.SshTemplate); err != nil {
|
||||
render.Error(w, admin.WrapError(admin.ErrorBadRequestType, err, "invalid template"))
|
||||
render.Error(w, r, admin.WrapError(admin.ErrorBadRequestType, err, "invalid template"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := mustAuthority(r.Context()).StoreProvisioner(r.Context(), prov); err != nil {
|
||||
render.Error(w, admin.WrapErrorISE(err, "error storing provisioner %s", prov.Name))
|
||||
render.Error(w, r, admin.WrapErrorISE(err, "error storing provisioner %s", prov.Name))
|
||||
return
|
||||
}
|
||||
render.ProtoJSONStatus(w, prov, http.StatusCreated)
|
||||
@@ -118,29 +118,29 @@ func DeleteProvisioner(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
if id != "" {
|
||||
if p, err = auth.LoadProvisionerByID(id); err != nil {
|
||||
render.Error(w, admin.WrapErrorISE(err, "error loading provisioner %s", id))
|
||||
render.Error(w, r, admin.WrapErrorISE(err, "error loading provisioner %s", id))
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if p, err = auth.LoadProvisionerByName(name); err != nil {
|
||||
render.Error(w, admin.WrapErrorISE(err, "error loading provisioner %s", name))
|
||||
render.Error(w, r, admin.WrapErrorISE(err, "error loading provisioner %s", name))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := auth.RemoveProvisioner(r.Context(), p.GetID()); err != nil {
|
||||
render.Error(w, admin.WrapErrorISE(err, "error removing provisioner %s", p.GetName()))
|
||||
render.Error(w, r, admin.WrapErrorISE(err, "error removing provisioner %s", p.GetName()))
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, &DeleteResponse{Status: "ok"})
|
||||
render.JSON(w, r, &DeleteResponse{Status: "ok"})
|
||||
}
|
||||
|
||||
// UpdateProvisioner updates an existing prov.
|
||||
func UpdateProvisioner(w http.ResponseWriter, r *http.Request) {
|
||||
var nu = new(linkedca.Provisioner)
|
||||
if err := read.ProtoJSON(r.Body, nu); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -151,51 +151,51 @@ func UpdateProvisioner(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
p, err := auth.LoadProvisionerByName(name)
|
||||
if err != nil {
|
||||
render.Error(w, admin.WrapErrorISE(err, "error loading provisioner from cached configuration '%s'", name))
|
||||
render.Error(w, r, admin.WrapErrorISE(err, "error loading provisioner from cached configuration '%s'", name))
|
||||
return
|
||||
}
|
||||
|
||||
old, err := db.GetProvisioner(r.Context(), p.GetID())
|
||||
if err != nil {
|
||||
render.Error(w, admin.WrapErrorISE(err, "error loading provisioner from db '%s'", p.GetID()))
|
||||
render.Error(w, r, admin.WrapErrorISE(err, "error loading provisioner from db '%s'", p.GetID()))
|
||||
return
|
||||
}
|
||||
|
||||
if nu.Id != old.Id {
|
||||
render.Error(w, admin.NewErrorISE("cannot change provisioner ID"))
|
||||
render.Error(w, r, admin.NewErrorISE("cannot change provisioner ID"))
|
||||
return
|
||||
}
|
||||
if nu.Type != old.Type {
|
||||
render.Error(w, admin.NewErrorISE("cannot change provisioner type"))
|
||||
render.Error(w, r, admin.NewErrorISE("cannot change provisioner type"))
|
||||
return
|
||||
}
|
||||
if nu.AuthorityId != old.AuthorityId {
|
||||
render.Error(w, admin.NewErrorISE("cannot change provisioner authorityID"))
|
||||
render.Error(w, r, admin.NewErrorISE("cannot change provisioner authorityID"))
|
||||
return
|
||||
}
|
||||
if !nu.CreatedAt.AsTime().Equal(old.CreatedAt.AsTime()) {
|
||||
render.Error(w, admin.NewErrorISE("cannot change provisioner createdAt"))
|
||||
render.Error(w, r, admin.NewErrorISE("cannot change provisioner createdAt"))
|
||||
return
|
||||
}
|
||||
if !nu.DeletedAt.AsTime().Equal(old.DeletedAt.AsTime()) {
|
||||
render.Error(w, admin.NewErrorISE("cannot change provisioner deletedAt"))
|
||||
render.Error(w, r, admin.NewErrorISE("cannot change provisioner deletedAt"))
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Validate inputs
|
||||
if err := authority.ValidateClaims(nu.Claims); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
// validate the templates and template data
|
||||
if err := validateTemplates(nu.X509Template, nu.SshTemplate); err != nil {
|
||||
render.Error(w, admin.WrapError(admin.ErrorBadRequestType, err, "invalid template"))
|
||||
render.Error(w, r, admin.WrapError(admin.ErrorBadRequestType, err, "invalid template"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := auth.UpdateProvisioner(r.Context(), nu); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
render.ProtoJSON(w, nu)
|
||||
|
||||
@@ -71,28 +71,28 @@ func (war *webhookAdminResponder) CreateProvisionerWebhook(w http.ResponseWriter
|
||||
|
||||
var newWebhook = new(linkedca.Webhook)
|
||||
if err := read.ProtoJSON(r.Body, newWebhook); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := validateWebhook(newWebhook); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
if newWebhook.Secret != "" {
|
||||
err := admin.NewError(admin.ErrorBadRequestType, "webhook secret must not be set")
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
if newWebhook.Id != "" {
|
||||
err := admin.NewError(admin.ErrorBadRequestType, "webhook ID must not be set")
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
id, err := randutil.UUIDv4()
|
||||
if err != nil {
|
||||
render.Error(w, admin.WrapErrorISE(err, "error generating webhook id"))
|
||||
render.Error(w, r, admin.WrapErrorISE(err, "error generating webhook id"))
|
||||
return
|
||||
}
|
||||
newWebhook.Id = id
|
||||
@@ -101,14 +101,14 @@ func (war *webhookAdminResponder) CreateProvisionerWebhook(w http.ResponseWriter
|
||||
for _, wh := range prov.Webhooks {
|
||||
if wh.Name == newWebhook.Name {
|
||||
err := admin.NewError(admin.ErrorConflictType, "provisioner %q already has a webhook with the name %q", prov.Name, newWebhook.Name)
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
secret, err := randutil.Bytes(64)
|
||||
if err != nil {
|
||||
render.Error(w, admin.WrapErrorISE(err, "error generating webhook secret"))
|
||||
render.Error(w, r, admin.WrapErrorISE(err, "error generating webhook secret"))
|
||||
return
|
||||
}
|
||||
newWebhook.Secret = base64.StdEncoding.EncodeToString(secret)
|
||||
@@ -117,11 +117,11 @@ func (war *webhookAdminResponder) CreateProvisionerWebhook(w http.ResponseWriter
|
||||
|
||||
if err := auth.UpdateProvisioner(ctx, prov); err != nil {
|
||||
if isBadRequest(err) {
|
||||
render.Error(w, admin.WrapError(admin.ErrorBadRequestType, err, "error creating provisioner webhook"))
|
||||
render.Error(w, r, admin.WrapError(admin.ErrorBadRequestType, err, "error creating provisioner webhook"))
|
||||
return
|
||||
}
|
||||
|
||||
render.Error(w, admin.WrapErrorISE(err, "error creating provisioner webhook"))
|
||||
render.Error(w, r, admin.WrapErrorISE(err, "error creating provisioner webhook"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -145,21 +145,21 @@ func (war *webhookAdminResponder) DeleteProvisionerWebhook(w http.ResponseWriter
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
render.JSONStatus(w, DeleteResponse{Status: "ok"}, http.StatusOK)
|
||||
render.JSONStatus(w, r, DeleteResponse{Status: "ok"}, http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
if err := auth.UpdateProvisioner(ctx, prov); err != nil {
|
||||
if isBadRequest(err) {
|
||||
render.Error(w, admin.WrapError(admin.ErrorBadRequestType, err, "error deleting provisioner webhook"))
|
||||
render.Error(w, r, admin.WrapError(admin.ErrorBadRequestType, err, "error deleting provisioner webhook"))
|
||||
return
|
||||
}
|
||||
|
||||
render.Error(w, admin.WrapErrorISE(err, "error deleting provisioner webhook"))
|
||||
render.Error(w, r, admin.WrapErrorISE(err, "error deleting provisioner webhook"))
|
||||
return
|
||||
}
|
||||
|
||||
render.JSONStatus(w, DeleteResponse{Status: "ok"}, http.StatusOK)
|
||||
render.JSONStatus(w, r, DeleteResponse{Status: "ok"}, http.StatusOK)
|
||||
}
|
||||
|
||||
func (war *webhookAdminResponder) UpdateProvisionerWebhook(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -170,12 +170,12 @@ func (war *webhookAdminResponder) UpdateProvisionerWebhook(w http.ResponseWriter
|
||||
|
||||
var newWebhook = new(linkedca.Webhook)
|
||||
if err := read.ProtoJSON(r.Body, newWebhook); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := validateWebhook(newWebhook); err != nil {
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -186,13 +186,13 @@ func (war *webhookAdminResponder) UpdateProvisionerWebhook(w http.ResponseWriter
|
||||
}
|
||||
if newWebhook.Secret != "" && newWebhook.Secret != wh.Secret {
|
||||
err := admin.NewError(admin.ErrorBadRequestType, "webhook secret cannot be updated")
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
newWebhook.Secret = wh.Secret
|
||||
if newWebhook.Id != "" && newWebhook.Id != wh.Id {
|
||||
err := admin.NewError(admin.ErrorBadRequestType, "webhook ID cannot be updated")
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
newWebhook.Id = wh.Id
|
||||
@@ -203,17 +203,17 @@ func (war *webhookAdminResponder) UpdateProvisionerWebhook(w http.ResponseWriter
|
||||
if !found {
|
||||
msg := fmt.Sprintf("provisioner %q has no webhook with the name %q", prov.Name, newWebhook.Name)
|
||||
err := admin.NewError(admin.ErrorNotFoundType, msg)
|
||||
render.Error(w, err)
|
||||
render.Error(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := auth.UpdateProvisioner(ctx, prov); err != nil {
|
||||
if isBadRequest(err) {
|
||||
render.Error(w, admin.WrapError(admin.ErrorBadRequestType, err, "error updating provisioner webhook"))
|
||||
render.Error(w, r, admin.WrapError(admin.ErrorBadRequestType, err, "error updating provisioner webhook"))
|
||||
return
|
||||
}
|
||||
|
||||
render.Error(w, admin.WrapErrorISE(err, "error updating provisioner webhook"))
|
||||
render.Error(w, r, admin.WrapErrorISE(err, "error updating provisioner webhook"))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -205,8 +205,8 @@ func (e *Error) ToLog() (interface{}, error) {
|
||||
}
|
||||
|
||||
// Render implements render.RenderableError for Error.
|
||||
func (e *Error) Render(w http.ResponseWriter) {
|
||||
func (e *Error) Render(w http.ResponseWriter, r *http.Request) {
|
||||
e.Message = e.Err.Error()
|
||||
|
||||
render.JSONStatus(w, e, e.StatusCode())
|
||||
render.JSONStatus(w, r, e, e.StatusCode())
|
||||
}
|
||||
|
||||
@@ -62,9 +62,10 @@ type Authority struct {
|
||||
x509Enforcers []provisioner.CertificateEnforcer
|
||||
|
||||
// SCEP CA
|
||||
scepOptions *scep.Options
|
||||
validateSCEP bool
|
||||
scepAuthority *scep.Authority
|
||||
scepOptions *scep.Options
|
||||
validateSCEP bool
|
||||
scepAuthority *scep.Authority
|
||||
scepKeyManager provisioner.SCEPKeyManager
|
||||
|
||||
// SSH CA
|
||||
sshHostPassword []byte
|
||||
@@ -139,7 +140,7 @@ func New(cfg *config.Config, opts ...Option) (*Authority, error) {
|
||||
}
|
||||
}
|
||||
if a.keyManager != nil {
|
||||
a.keyManager = &instrumentedKeyManager{a.keyManager, a.meter}
|
||||
a.keyManager = newInstrumentedKeyManager(a.keyManager, a.meter)
|
||||
}
|
||||
|
||||
if !a.skipInit {
|
||||
@@ -168,7 +169,7 @@ func NewEmbedded(opts ...Option) (*Authority, error) {
|
||||
}
|
||||
}
|
||||
if a.keyManager != nil {
|
||||
a.keyManager = &instrumentedKeyManager{a.keyManager, a.meter}
|
||||
a.keyManager = newInstrumentedKeyManager(a.keyManager, a.meter)
|
||||
}
|
||||
|
||||
// Validate required options
|
||||
@@ -255,7 +256,10 @@ func (a *Authority) ReloadAdminResources(ctx context.Context) error {
|
||||
provClxn := provisioner.NewCollection(provisionerConfig.Audiences)
|
||||
for _, p := range provList {
|
||||
if err := p.Init(provisionerConfig); err != nil {
|
||||
return err
|
||||
log.Printf("failed to initialize %s provisioner %q: %v\n", p.GetType(), p.GetName(), err)
|
||||
p = provisioner.Uninitialized{
|
||||
Interface: p, Reason: err,
|
||||
}
|
||||
}
|
||||
if err := provClxn.Store(p); err != nil {
|
||||
return err
|
||||
@@ -349,7 +353,7 @@ func (a *Authority) init() error {
|
||||
return err
|
||||
}
|
||||
|
||||
a.keyManager = &instrumentedKeyManager{a.keyManager, a.meter}
|
||||
a.keyManager = newInstrumentedKeyManager(a.keyManager, a.meter)
|
||||
}
|
||||
|
||||
// Initialize linkedca client if necessary. On a linked RA, the issuer
|
||||
@@ -446,6 +450,7 @@ func (a *Authority) init() error {
|
||||
return err
|
||||
}
|
||||
a.rootX509Certs = append(a.rootX509Certs, resp.RootCertificate)
|
||||
a.intermediateX509Certs = append(a.intermediateX509Certs, resp.IntermediateCertificates...)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -694,32 +699,42 @@ func (a *Authority) init() error {
|
||||
options := &scep.Options{
|
||||
Roots: a.rootX509Certs,
|
||||
Intermediates: a.intermediateX509Certs,
|
||||
SignerCert: a.intermediateX509Certs[0],
|
||||
}
|
||||
if options.Signer, err = a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{
|
||||
SigningKey: a.config.IntermediateKey,
|
||||
Password: a.password,
|
||||
}); err != nil {
|
||||
return err
|
||||
|
||||
// intermediate certificates can be empty in RA mode
|
||||
if len(a.intermediateX509Certs) > 0 {
|
||||
options.SignerCert = a.intermediateX509Certs[0]
|
||||
}
|
||||
// TODO(hs): instead of creating the decrypter here, pass the
|
||||
// intermediate key + chain down to the SCEP authority,
|
||||
// and only instantiate it when required there. Is that possible?
|
||||
// Also with entering passwords?
|
||||
// TODO(hs): if moving the logic, try improving the logic for the
|
||||
// decrypter password too? Right now it needs to be entered multiple
|
||||
// times; I've observed it to be three times maximum, every time
|
||||
// the intermediate key is read.
|
||||
_, isRSA := options.Signer.Public().(*rsa.PublicKey)
|
||||
if km, ok := a.keyManager.(kmsapi.Decrypter); ok && isRSA {
|
||||
if decrypter, err := km.CreateDecrypter(&kmsapi.CreateDecrypterRequest{
|
||||
DecryptionKey: a.config.IntermediateKey,
|
||||
Password: a.password,
|
||||
}); err == nil {
|
||||
// only pass the decrypter down when it was successfully created,
|
||||
// meaning it's an RSA key, and `CreateDecrypter` did not fail.
|
||||
options.Decrypter = decrypter
|
||||
options.DecrypterCert = options.Intermediates[0]
|
||||
|
||||
// attempt to create the (default) SCEP signer if the intermediate
|
||||
// key is configured.
|
||||
if a.config.IntermediateKey != "" {
|
||||
if options.Signer, err = a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{
|
||||
SigningKey: a.config.IntermediateKey,
|
||||
Password: a.password,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO(hs): instead of creating the decrypter here, pass the
|
||||
// intermediate key + chain down to the SCEP authority,
|
||||
// and only instantiate it when required there. Is that possible?
|
||||
// Also with entering passwords?
|
||||
// TODO(hs): if moving the logic, try improving the logic for the
|
||||
// decrypter password too? Right now it needs to be entered multiple
|
||||
// times; I've observed it to be three times maximum, every time
|
||||
// the intermediate key is read.
|
||||
_, isRSAKey := options.Signer.Public().(*rsa.PublicKey)
|
||||
if km, ok := a.keyManager.(kmsapi.Decrypter); ok && isRSAKey {
|
||||
if decrypter, err := km.CreateDecrypter(&kmsapi.CreateDecrypterRequest{
|
||||
DecryptionKey: a.config.IntermediateKey,
|
||||
Password: a.password,
|
||||
}); err == nil {
|
||||
// only pass the decrypter down when it was successfully created,
|
||||
// meaning it's an RSA key, and `CreateDecrypter` did not fail.
|
||||
options.Decrypter = decrypter
|
||||
options.DecrypterCert = options.Intermediates[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -69,6 +69,15 @@ func testAuthority(t *testing.T, opts ...Option) *Authority {
|
||||
EnableSSHCA: &enableSSHCA,
|
||||
},
|
||||
},
|
||||
&provisioner.JWK{
|
||||
Name: "uninitialized",
|
||||
Type: "JWK",
|
||||
Key: clijwk,
|
||||
Claims: &provisioner.Claims{
|
||||
MinTLSDur: &provisioner.Duration{Duration: 5 * time.Minute},
|
||||
MaxTLSDur: &provisioner.Duration{Duration: time.Minute},
|
||||
},
|
||||
},
|
||||
}
|
||||
c := &Config{
|
||||
Address: "127.0.0.1:443",
|
||||
@@ -113,7 +122,7 @@ func TestAuthorityNew(t *testing.T) {
|
||||
c.Root = []string{"foo"}
|
||||
return &newTest{
|
||||
config: c,
|
||||
err: errors.New("error reading foo: no such file or directory"),
|
||||
err: errors.New(`error reading "foo": no such file or directory`),
|
||||
}
|
||||
},
|
||||
"fail bad password": func(t *testing.T) *newTest {
|
||||
@@ -131,7 +140,7 @@ func TestAuthorityNew(t *testing.T) {
|
||||
c.IntermediateCert = "wrong"
|
||||
return &newTest{
|
||||
config: c,
|
||||
err: errors.New("error reading wrong: no such file or directory"),
|
||||
err: errors.New(`error reading "wrong": no such file or directory`),
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -64,6 +64,10 @@ func (a *Authority) getProvisionerFromToken(token string) (provisioner.Interface
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("provisioner not found or invalid audience (%s)", strings.Join(claims.Audience, ", "))
|
||||
}
|
||||
// If the provisioner is disabled, send an appropriate message to the client
|
||||
if _, ok := p.(provisioner.Uninitialized); ok {
|
||||
return nil, nil, errs.New(http.StatusUnauthorized, "provisioner %q is disabled due to an initialization error", p.GetName())
|
||||
}
|
||||
|
||||
return p, &claims, nil
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"go.step.sm/crypto/randutil"
|
||||
"go.step.sm/crypto/x509util"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/smallstep/assert"
|
||||
"github.com/smallstep/certificates/api/render"
|
||||
"github.com/smallstep/certificates/authority/provisioner"
|
||||
@@ -337,6 +338,24 @@ func TestAuthority_authorizeToken(t *testing.T) {
|
||||
code: http.StatusUnauthorized,
|
||||
}
|
||||
},
|
||||
"fail/uninitialized": func(t *testing.T) *authorizeTest {
|
||||
cl := jose.Claims{
|
||||
Subject: "test.smallstep.com",
|
||||
Issuer: "uninitialized",
|
||||
NotBefore: jose.NewNumericDate(now),
|
||||
Expiry: jose.NewNumericDate(now.Add(time.Minute)),
|
||||
Audience: validAudience,
|
||||
ID: uuid.NewString(),
|
||||
}
|
||||
raw, err := jose.Signed(sig).Claims(cl).CompactSerialize()
|
||||
assert.FatalError(t, err)
|
||||
return &authorizeTest{
|
||||
auth: a,
|
||||
token: raw,
|
||||
err: errors.New(`provisioner "uninitialized" is disabled due to an initialization error`),
|
||||
code: http.StatusUnauthorized,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
for name, genTestCase := range tests {
|
||||
|
||||
@@ -110,7 +110,7 @@ func newLinkedCAClient(token string) (*linkedCaClient, error) {
|
||||
tlsConfig.GetClientCertificate = renewer.GetClientCertificate
|
||||
|
||||
// Start mTLS client
|
||||
conn, err := grpc.Dial(u.Host, grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)))
|
||||
conn, err := grpc.NewClient(u.Host, grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)))
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error connecting %s", u.Host)
|
||||
}
|
||||
@@ -478,10 +478,7 @@ func getAuthority(sans []string) (string, error) {
|
||||
// getRootCertificate creates an insecure majordomo client and returns the
|
||||
// verified root certificate.
|
||||
func getRootCertificate(endpoint, fingerprint string) (*x509.Certificate, error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
conn, err := grpc.DialContext(ctx, endpoint, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{
|
||||
conn, err := grpc.NewClient(endpoint, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{
|
||||
//nolint:gosec // used in bootstrap protocol
|
||||
InsecureSkipVerify: true, // lgtm[go/disabled-certificate-check]
|
||||
})))
|
||||
@@ -489,7 +486,7 @@ func getRootCertificate(endpoint, fingerprint string) (*x509.Certificate, error)
|
||||
return nil, errors.Wrapf(err, "error connecting %s", endpoint)
|
||||
}
|
||||
|
||||
ctx, cancel = context.WithTimeout(context.Background(), 15*time.Second)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer cancel()
|
||||
|
||||
client := linkedca.NewMajordomoClient(conn)
|
||||
@@ -531,11 +528,7 @@ func getRootCertificate(endpoint, fingerprint string) (*x509.Certificate, error)
|
||||
// login creates a new majordomo client with just the root ca pool and returns
|
||||
// the signed certificate and tls configuration.
|
||||
func login(authority, token string, csr *x509.CertificateRequest, signer crypto.PrivateKey, endpoint string, rootCAs *x509.CertPool) (*tls.Certificate, *tls.Config, error) {
|
||||
// Connect to majordomo
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
conn, err := grpc.DialContext(ctx, endpoint, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{
|
||||
conn, err := grpc.NewClient(endpoint, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
RootCAs: rootCAs,
|
||||
})))
|
||||
@@ -544,7 +537,7 @@ func login(authority, token string, csr *x509.CertificateRequest, signer crypto.
|
||||
}
|
||||
|
||||
// Login to get the signed certificate
|
||||
ctx, cancel = context.WithTimeout(context.Background(), 15*time.Second)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer cancel()
|
||||
|
||||
client := linkedca.NewMajordomoClient(conn)
|
||||
|
||||
@@ -66,6 +66,22 @@ type instrumentedKeyManager struct {
|
||||
meter Meter
|
||||
}
|
||||
|
||||
type instrumentedKeyAndDecrypterManager struct {
|
||||
kms.KeyManager
|
||||
decrypter kmsapi.Decrypter
|
||||
meter Meter
|
||||
}
|
||||
|
||||
func newInstrumentedKeyManager(k kms.KeyManager, m Meter) kms.KeyManager {
|
||||
decrypter, isDecrypter := k.(kmsapi.Decrypter)
|
||||
switch {
|
||||
case isDecrypter:
|
||||
return &instrumentedKeyAndDecrypterManager{&instrumentedKeyManager{k, m}, decrypter, m}
|
||||
default:
|
||||
return &instrumentedKeyManager{k, m}
|
||||
}
|
||||
}
|
||||
|
||||
func (i *instrumentedKeyManager) CreateSigner(req *kmsapi.CreateSignerRequest) (s crypto.Signer, err error) {
|
||||
if s, err = i.KeyManager.CreateSigner(req); err == nil {
|
||||
s = &instrumentedKMSSigner{s, i.meter}
|
||||
@@ -74,6 +90,10 @@ func (i *instrumentedKeyManager) CreateSigner(req *kmsapi.CreateSignerRequest) (
|
||||
return
|
||||
}
|
||||
|
||||
func (i *instrumentedKeyAndDecrypterManager) CreateDecrypter(req *kmsapi.CreateDecrypterRequest) (s crypto.Decrypter, err error) {
|
||||
return i.decrypter.CreateDecrypter(req)
|
||||
}
|
||||
|
||||
type instrumentedKMSSigner struct {
|
||||
crypto.Signer
|
||||
meter Meter
|
||||
@@ -85,3 +105,7 @@ func (i *instrumentedKMSSigner) Sign(rand io.Reader, digest []byte, opts crypto.
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
var _ kms.KeyManager = (*instrumentedKeyManager)(nil)
|
||||
var _ kms.KeyManager = (*instrumentedKeyAndDecrypterManager)(nil)
|
||||
var _ kmsapi.Decrypter = (*instrumentedKeyAndDecrypterManager)(nil)
|
||||
|
||||
@@ -226,6 +226,16 @@ func WithFullSCEPOptions(options *scep.Options) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// WithSCEPKeyManager defines the key manager used on SCEP provisioners.
|
||||
//
|
||||
// This feature is EXPERIMENTAL and might change at any time.
|
||||
func WithSCEPKeyManager(skm provisioner.SCEPKeyManager) Option {
|
||||
return func(a *Authority) error {
|
||||
a.scepKeyManager = skm
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithSSHUserSigner defines the signer used to sign SSH user certificates.
|
||||
func WithSSHUserSigner(s crypto.Signer) Option {
|
||||
return func(a *Authority) error {
|
||||
|
||||
@@ -21,6 +21,7 @@ type HostPolicy policy.SSHNamePolicyEngine
|
||||
func NewX509PolicyEngine(policyOptions X509PolicyOptionsInterface) (X509Policy, error) {
|
||||
// return early if no policy engine options to configure
|
||||
if policyOptions == nil {
|
||||
//nolint:nilnil,nolintlint // expected values
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -50,6 +51,7 @@ func NewX509PolicyEngine(policyOptions X509PolicyOptionsInterface) (X509Policy,
|
||||
|
||||
// ensure no policy engine is returned when no name options were provided
|
||||
if len(options) == 0 {
|
||||
//nolint:nilnil,nolintlint // expected values
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -93,6 +95,7 @@ func NewSSHHostPolicyEngine(policyOptions SSHPolicyOptionsInterface) (HostPolicy
|
||||
func newSSHPolicyEngine(policyOptions SSHPolicyOptionsInterface, typ sshPolicyEngineType) (policy.SSHNamePolicyEngine, error) {
|
||||
// return early if no policy engine options to configure
|
||||
if policyOptions == nil {
|
||||
//nolint:nilnil,nolintlint // expected values
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -134,6 +137,7 @@ func newSSHPolicyEngine(policyOptions SSHPolicyOptionsInterface, typ sshPolicyEn
|
||||
|
||||
// ensure no policy engine is returned when no name options were provided
|
||||
if len(options) == 0 {
|
||||
//nolint:nilnil,nolintlint // expected values
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,25 +1,89 @@
|
||||
# https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/verify-signature.html
|
||||
# https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/regions-certs.html use RSA format
|
||||
|
||||
# default certificate for "other regions"
|
||||
|
||||
# certificate for us-east-2
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDIjCCAougAwIBAgIJAKnL4UEDMN/FMA0GCSqGSIb3DQEBBQUAMGoxCzAJBgNV
|
||||
BAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdTZWF0dGxlMRgw
|
||||
FgYDVQQKEw9BbWF6b24uY29tIEluYy4xGjAYBgNVBAMTEWVjMi5hbWF6b25hd3Mu
|
||||
Y29tMB4XDTE0MDYwNTE0MjgwMloXDTI0MDYwNTE0MjgwMlowajELMAkGA1UEBhMC
|
||||
VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1NlYXR0bGUxGDAWBgNV
|
||||
BAoTD0FtYXpvbi5jb20gSW5jLjEaMBgGA1UEAxMRZWMyLmFtYXpvbmF3cy5jb20w
|
||||
gZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAIe9GN//SRK2knbjySG0ho3yqQM3
|
||||
e2TDhWO8D2e8+XZqck754gFSo99AbT2RmXClambI7xsYHZFapbELC4H91ycihvrD
|
||||
jbST1ZjkLQgga0NE1q43eS68ZeTDccScXQSNivSlzJZS8HJZjgqzBlXjZftjtdJL
|
||||
XeE4hwvo0sD4f3j9AgMBAAGjgc8wgcwwHQYDVR0OBBYEFCXWzAgVyrbwnFncFFIs
|
||||
77VBdlE4MIGcBgNVHSMEgZQwgZGAFCXWzAgVyrbwnFncFFIs77VBdlE4oW6kbDBq
|
||||
MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHU2Vh
|
||||
dHRsZTEYMBYGA1UEChMPQW1hem9uLmNvbSBJbmMuMRowGAYDVQQDExFlYzIuYW1h
|
||||
em9uYXdzLmNvbYIJAKnL4UEDMN/FMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEF
|
||||
BQADgYEAFYcz1OgEhQBXIwIdsgCOS8vEtiJYF+j9uO6jz7VOmJqO+pRlAbRlvY8T
|
||||
C1haGgSI/A1uZUKs/Zfnph0oEI0/hu1IIJ/SKBDtN5lvmZ/IzbOPIJWirlsllQIQ
|
||||
7zvWbGd9c9+Rm3p04oTvhup99la7kZqevJK0QRdD/6NpCKsqP/0=
|
||||
MIIDITCCAoqgAwIBAgIUVJTc+hOU+8Gk3JlqsX438Dk5c58wDQYJKoZIhvcNAQEL
|
||||
BQAwXDELMAkGA1UEBhMCVVMxGTAXBgNVBAgTEFdhc2hpbmd0b24gU3RhdGUxEDAO
|
||||
BgNVBAcTB1NlYXR0bGUxIDAeBgNVBAoTF0FtYXpvbiBXZWIgU2VydmljZXMgTExD
|
||||
MB4XDTI0MDQyOTE3MTE0OVoXDTI5MDQyODE3MTE0OVowXDELMAkGA1UEBhMCVVMx
|
||||
GTAXBgNVBAgTEFdhc2hpbmd0b24gU3RhdGUxEDAOBgNVBAcTB1NlYXR0bGUxIDAe
|
||||
BgNVBAoTF0FtYXpvbiBXZWIgU2VydmljZXMgTExDMIGfMA0GCSqGSIb3DQEBAQUA
|
||||
A4GNADCBiQKBgQCHvRjf/0kStpJ248khtIaN8qkDN3tkw4VjvA9nvPl2anJO+eIB
|
||||
UqPfQG09kZlwpWpmyO8bGB2RWqWxCwuB/dcnIob6w420k9WY5C0IIGtDRNauN3ku
|
||||
vGXkw3HEnF0EjYr0pcyWUvByWY4KswZV42X7Y7XSS13hOIcL6NLA+H94/QIDAQAB
|
||||
o4HfMIHcMAsGA1UdDwQEAwIHgDAdBgNVHQ4EFgQUJdbMCBXKtvCcWdwUUizvtUF2
|
||||
UTgwgZkGA1UdIwSBkTCBjoAUJdbMCBXKtvCcWdwUUizvtUF2UTihYKReMFwxCzAJ
|
||||
BgNVBAYTAlVTMRkwFwYDVQQIExBXYXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdT
|
||||
ZWF0dGxlMSAwHgYDVQQKExdBbWF6b24gV2ViIFNlcnZpY2VzIExMQ4IUVJTc+hOU
|
||||
+8Gk3JlqsX438Dk5c58wEgYDVR0TAQH/BAgwBgEB/wIBADANBgkqhkiG9w0BAQsF
|
||||
AAOBgQAywJQaVNWJqW0R0T0xVOSoN1GLk9x9kKEuN67RN9CLin4dA97qa7Mr5W4P
|
||||
FZ6vnh5CjOhQBRXV9xJUeYSdqVItNAUFK/fEzDdjf1nUfPlQ3OJ49u6CV01NoJ9m
|
||||
usvY9kWcV46dqn2bk2MyfTTgvmeqP8fiMRPxxnVRkSzlldP5Fg==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# certificate for us-east-1
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDITCCAoqgAwIBAgIUE1y2NIKCU+Rg4uu4u32koG9QEYIwDQYJKoZIhvcNAQEL
|
||||
BQAwXDELMAkGA1UEBhMCVVMxGTAXBgNVBAgTEFdhc2hpbmd0b24gU3RhdGUxEDAO
|
||||
BgNVBAcTB1NlYXR0bGUxIDAeBgNVBAoTF0FtYXpvbiBXZWIgU2VydmljZXMgTExD
|
||||
MB4XDTI0MDQyOTE3MzQwMVoXDTI5MDQyODE3MzQwMVowXDELMAkGA1UEBhMCVVMx
|
||||
GTAXBgNVBAgTEFdhc2hpbmd0b24gU3RhdGUxEDAOBgNVBAcTB1NlYXR0bGUxIDAe
|
||||
BgNVBAoTF0FtYXpvbiBXZWIgU2VydmljZXMgTExDMIGfMA0GCSqGSIb3DQEBAQUA
|
||||
A4GNADCBiQKBgQCHvRjf/0kStpJ248khtIaN8qkDN3tkw4VjvA9nvPl2anJO+eIB
|
||||
UqPfQG09kZlwpWpmyO8bGB2RWqWxCwuB/dcnIob6w420k9WY5C0IIGtDRNauN3ku
|
||||
vGXkw3HEnF0EjYr0pcyWUvByWY4KswZV42X7Y7XSS13hOIcL6NLA+H94/QIDAQAB
|
||||
o4HfMIHcMAsGA1UdDwQEAwIHgDAdBgNVHQ4EFgQUJdbMCBXKtvCcWdwUUizvtUF2
|
||||
UTgwgZkGA1UdIwSBkTCBjoAUJdbMCBXKtvCcWdwUUizvtUF2UTihYKReMFwxCzAJ
|
||||
BgNVBAYTAlVTMRkwFwYDVQQIExBXYXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdT
|
||||
ZWF0dGxlMSAwHgYDVQQKExdBbWF6b24gV2ViIFNlcnZpY2VzIExMQ4IUE1y2NIKC
|
||||
U+Rg4uu4u32koG9QEYIwEgYDVR0TAQH/BAgwBgEB/wIBADANBgkqhkiG9w0BAQsF
|
||||
AAOBgQAlxSmwcWnhT4uAeSinJuz+1BTcKhVSWb5jT8pYjQb8ZoZkXXRGb09mvYeU
|
||||
NeqOBr27rvRAnaQ/9LUQf72+SahDFuS4CMI8nwowytqbmwquqFr4dxA/SDADyRiF
|
||||
ea1UoMuNHTY49J/1vPomqsVn7mugTp+TbjqCfOJTpu0temHcFA==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# certificate for us-west-1
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDITCCAoqgAwIBAgIUK2zmY9PUSTR7rc1k2OwPYu4+g7wwDQYJKoZIhvcNAQEL
|
||||
BQAwXDELMAkGA1UEBhMCVVMxGTAXBgNVBAgTEFdhc2hpbmd0b24gU3RhdGUxEDAO
|
||||
BgNVBAcTB1NlYXR0bGUxIDAeBgNVBAoTF0FtYXpvbiBXZWIgU2VydmljZXMgTExD
|
||||
MB4XDTI0MDQyOTE3MDI0M1oXDTI5MDQyODE3MDI0M1owXDELMAkGA1UEBhMCVVMx
|
||||
GTAXBgNVBAgTEFdhc2hpbmd0b24gU3RhdGUxEDAOBgNVBAcTB1NlYXR0bGUxIDAe
|
||||
BgNVBAoTF0FtYXpvbiBXZWIgU2VydmljZXMgTExDMIGfMA0GCSqGSIb3DQEBAQUA
|
||||
A4GNADCBiQKBgQCHvRjf/0kStpJ248khtIaN8qkDN3tkw4VjvA9nvPl2anJO+eIB
|
||||
UqPfQG09kZlwpWpmyO8bGB2RWqWxCwuB/dcnIob6w420k9WY5C0IIGtDRNauN3ku
|
||||
vGXkw3HEnF0EjYr0pcyWUvByWY4KswZV42X7Y7XSS13hOIcL6NLA+H94/QIDAQAB
|
||||
o4HfMIHcMAsGA1UdDwQEAwIHgDAdBgNVHQ4EFgQUJdbMCBXKtvCcWdwUUizvtUF2
|
||||
UTgwgZkGA1UdIwSBkTCBjoAUJdbMCBXKtvCcWdwUUizvtUF2UTihYKReMFwxCzAJ
|
||||
BgNVBAYTAlVTMRkwFwYDVQQIExBXYXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdT
|
||||
ZWF0dGxlMSAwHgYDVQQKExdBbWF6b24gV2ViIFNlcnZpY2VzIExMQ4IUK2zmY9PU
|
||||
STR7rc1k2OwPYu4+g7wwEgYDVR0TAQH/BAgwBgEB/wIBADANBgkqhkiG9w0BAQsF
|
||||
AAOBgQA1Ng4QmN4n7iPh5CnadSOc0ZfM7by0dBePwZJyGvOHdaw6P6E/vEk76KsC
|
||||
Q8p+akuzVzVPkU4kBK/TRqLp19wEWoVwhhTaxHjQ1tTRHqXIVlrkw4JrtFbeNM21
|
||||
GlkSLonuzmNZdivn9WuQYeGe7nUD4w3q9GgiF3CPorJe+UxtbA==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# certificate for us-west-2
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDITCCAoqgAwIBAgIUFx8PxCkbHwpD31bOyCtyz3GclbgwDQYJKoZIhvcNAQEL
|
||||
BQAwXDELMAkGA1UEBhMCVVMxGTAXBgNVBAgTEFdhc2hpbmd0b24gU3RhdGUxEDAO
|
||||
BgNVBAcTB1NlYXR0bGUxIDAeBgNVBAoTF0FtYXpvbiBXZWIgU2VydmljZXMgTExD
|
||||
MB4XDTI0MDQyOTE3MjM1OVoXDTI5MDQyODE3MjM1OVowXDELMAkGA1UEBhMCVVMx
|
||||
GTAXBgNVBAgTEFdhc2hpbmd0b24gU3RhdGUxEDAOBgNVBAcTB1NlYXR0bGUxIDAe
|
||||
BgNVBAoTF0FtYXpvbiBXZWIgU2VydmljZXMgTExDMIGfMA0GCSqGSIb3DQEBAQUA
|
||||
A4GNADCBiQKBgQCHvRjf/0kStpJ248khtIaN8qkDN3tkw4VjvA9nvPl2anJO+eIB
|
||||
UqPfQG09kZlwpWpmyO8bGB2RWqWxCwuB/dcnIob6w420k9WY5C0IIGtDRNauN3ku
|
||||
vGXkw3HEnF0EjYr0pcyWUvByWY4KswZV42X7Y7XSS13hOIcL6NLA+H94/QIDAQAB
|
||||
o4HfMIHcMAsGA1UdDwQEAwIHgDAdBgNVHQ4EFgQUJdbMCBXKtvCcWdwUUizvtUF2
|
||||
UTgwgZkGA1UdIwSBkTCBjoAUJdbMCBXKtvCcWdwUUizvtUF2UTihYKReMFwxCzAJ
|
||||
BgNVBAYTAlVTMRkwFwYDVQQIExBXYXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdT
|
||||
ZWF0dGxlMSAwHgYDVQQKExdBbWF6b24gV2ViIFNlcnZpY2VzIExMQ4IUFx8PxCkb
|
||||
HwpD31bOyCtyz3GclbgwEgYDVR0TAQH/BAgwBgEB/wIBADANBgkqhkiG9w0BAQsF
|
||||
AAOBgQBzOl+9Xy1+UsbUBI95HO9mbbdnuX+aMJXgG9uFZNjgNEbMcvx+h8P9IMko
|
||||
z7PzFdheQQ1NLjsHH9mSR1SyC4m9ja6BsejH5nLBWyCdjfdP3muZM4O5+r7vUa1O
|
||||
dWU+hP/T7DUrPAIVMOE7mpYa+WPWJrN6BlRwQkKQ7twm9kDalA==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# certificate for eu-south-1
|
||||
@@ -93,7 +157,7 @@ NTpxxcXmUKquX+pHmIkK1LKDO8rNE84jqxrxRsfDi6by82fjVYf2pgjJW8R1FAw+
|
||||
mL5WQRFexbfB5aXhcMo0AA==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# certificate for cn-north-1, cn-northwest-1
|
||||
# certificate for cn-north-1
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDCzCCAnSgAwIBAgIJALSOMbOoU2svMA0GCSqGSIb3DQEBCwUAMFwxCzAJBgNV
|
||||
BAYTAlVTMRkwFwYDVQQIExBXYXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdTZWF0
|
||||
@@ -114,6 +178,48 @@ oADS0ph+YUz5P/bUCm61wFjlxaTfwKcuTR3ytj7bFLoW5Bm7Sa+TCl3lOGb2taon
|
||||
SUDlRyNy1jJFstEZjOhs
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# certificate for cn-northwest-1
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDCzCCAnSgAwIBAgIJALSOMbOoU2svMA0GCSqGSIb3DQEBCwUAMFwxCzAJBgNV
|
||||
BAYTAlVTMRkwFwYDVQQIExBXYXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdTZWF0
|
||||
dGxlMSAwHgYDVQQKExdBbWF6b24gV2ViIFNlcnZpY2VzIExMQzAeFw0yMzA3MDQw
|
||||
ODM1MzlaFw0yODA3MDIwODM1MzlaMFwxCzAJBgNVBAYTAlVTMRkwFwYDVQQIExBX
|
||||
YXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdTZWF0dGxlMSAwHgYDVQQKExdBbWF6
|
||||
b24gV2ViIFNlcnZpY2VzIExMQzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA
|
||||
uhhUNlqAZdcWWB/OSDVDGk3OA99EFzOn/mJlmciQ/Xwu2dFJWmSCqEAE6gjufCjQ
|
||||
q3voxAhC2CF+elKtJW/C0Sz/LYo60PUqd6iXF4h+upB9HkOOGuWHXsHBTsvgkgGA
|
||||
1CGgel4U0Cdq+23eANr8N8m28UzljjSnTlrYCHtzN4sCAwEAAaOB1DCB0TALBgNV
|
||||
HQ8EBAMCB4AwHQYDVR0OBBYEFBkZu3wT27NnYgrfH+xJz4HJaNJoMIGOBgNVHSME
|
||||
gYYwgYOAFBkZu3wT27NnYgrfH+xJz4HJaNJooWCkXjBcMQswCQYDVQQGEwJVUzEZ
|
||||
MBcGA1UECBMQV2FzaGluZ3RvbiBTdGF0ZTEQMA4GA1UEBxMHU2VhdHRsZTEgMB4G
|
||||
A1UEChMXQW1hem9uIFdlYiBTZXJ2aWNlcyBMTEOCCQC0jjGzqFNrLzASBgNVHRMB
|
||||
Af8ECDAGAQH/AgEAMA0GCSqGSIb3DQEBCwUAA4GBAECji43p+oPkYqmzll7e8Hgb
|
||||
oADS0ph+YUz5P/bUCm61wFjlxaTfwKcuTR3ytj7bFLoW5Bm7Sa+TCl3lOGb2taon
|
||||
2h+9NirRK6JYk87LMNvbS40HGPFumJL2NzEsGUeK+MRiWu+Oh5/lJGii3qw4YByx
|
||||
SUDlRyNy1jJFstEZjOhs
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# certificate for eu-central-1
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDITCCAoqgAwIBAgIUFD5GsmkxRuecttwsCG763m3u63UwDQYJKoZIhvcNAQEL
|
||||
BQAwXDELMAkGA1UEBhMCVVMxGTAXBgNVBAgTEFdhc2hpbmd0b24gU3RhdGUxEDAO
|
||||
BgNVBAcTB1NlYXR0bGUxIDAeBgNVBAoTF0FtYXpvbiBXZWIgU2VydmljZXMgTExD
|
||||
MB4XDTI0MDQyOTE1NTUyOVoXDTI5MDQyODE1NTUyOVowXDELMAkGA1UEBhMCVVMx
|
||||
GTAXBgNVBAgTEFdhc2hpbmd0b24gU3RhdGUxEDAOBgNVBAcTB1NlYXR0bGUxIDAe
|
||||
BgNVBAoTF0FtYXpvbiBXZWIgU2VydmljZXMgTExDMIGfMA0GCSqGSIb3DQEBAQUA
|
||||
A4GNADCBiQKBgQCHvRjf/0kStpJ248khtIaN8qkDN3tkw4VjvA9nvPl2anJO+eIB
|
||||
UqPfQG09kZlwpWpmyO8bGB2RWqWxCwuB/dcnIob6w420k9WY5C0IIGtDRNauN3ku
|
||||
vGXkw3HEnF0EjYr0pcyWUvByWY4KswZV42X7Y7XSS13hOIcL6NLA+H94/QIDAQAB
|
||||
o4HfMIHcMAsGA1UdDwQEAwIHgDAdBgNVHQ4EFgQUJdbMCBXKtvCcWdwUUizvtUF2
|
||||
UTgwgZkGA1UdIwSBkTCBjoAUJdbMCBXKtvCcWdwUUizvtUF2UTihYKReMFwxCzAJ
|
||||
BgNVBAYTAlVTMRkwFwYDVQQIExBXYXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdT
|
||||
ZWF0dGxlMSAwHgYDVQQKExdBbWF6b24gV2ViIFNlcnZpY2VzIExMQ4IUFD5Gsmkx
|
||||
RuecttwsCG763m3u63UwEgYDVR0TAQH/BAgwBgEB/wIBADANBgkqhkiG9w0BAQsF
|
||||
AAOBgQBBh0WaXlBsW56Hqk588MmJxsOrvcKfDjF57RgEDgnGnQaJcStCVWDO9UYO
|
||||
JX2tdsPw+E7AjDqjsuxYaotLn3Mr3mK0sNOXq9BljBnWD4pARg89KZnZI8FN35HQ
|
||||
O/LYOVHCknuPL123VmVRNs51qQA9hkPjvw21UzpDLxaUxt9Z/w==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# certificate for eu-central-2
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICMzCCAZygAwIBAgIGAXjSGFGiMA0GCSqGSIb3DQEBBQUAMFwxCzAJBgNVBAYT
|
||||
@@ -130,6 +236,27 @@ NBElvPCDKFvTJl4QQhToy056llO5GvdS9RK+H8xrP2mrqngApoKTApv93vHBixgF
|
||||
Sn5KrczRO0YSm3OjkqbydU7DFlmkXXR7GYE+5jbHvQHYiT1J5sMu
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# certificate for ap-south-1
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDITCCAoqgAwIBAgIUDLA+x6tTAP3LRTr0z6nOxfsozdMwDQYJKoZIhvcNAQEL
|
||||
BQAwXDELMAkGA1UEBhMCVVMxGTAXBgNVBAgTEFdhc2hpbmd0b24gU3RhdGUxEDAO
|
||||
BgNVBAcTB1NlYXR0bGUxIDAeBgNVBAoTF0FtYXpvbiBXZWIgU2VydmljZXMgTExD
|
||||
MB4XDTI0MDQyOTE0MTMwMVoXDTI5MDQyODE0MTMwMVowXDELMAkGA1UEBhMCVVMx
|
||||
GTAXBgNVBAgTEFdhc2hpbmd0b24gU3RhdGUxEDAOBgNVBAcTB1NlYXR0bGUxIDAe
|
||||
BgNVBAoTF0FtYXpvbiBXZWIgU2VydmljZXMgTExDMIGfMA0GCSqGSIb3DQEBAQUA
|
||||
A4GNADCBiQKBgQCHvRjf/0kStpJ248khtIaN8qkDN3tkw4VjvA9nvPl2anJO+eIB
|
||||
UqPfQG09kZlwpWpmyO8bGB2RWqWxCwuB/dcnIob6w420k9WY5C0IIGtDRNauN3ku
|
||||
vGXkw3HEnF0EjYr0pcyWUvByWY4KswZV42X7Y7XSS13hOIcL6NLA+H94/QIDAQAB
|
||||
o4HfMIHcMAsGA1UdDwQEAwIHgDAdBgNVHQ4EFgQUJdbMCBXKtvCcWdwUUizvtUF2
|
||||
UTgwgZkGA1UdIwSBkTCBjoAUJdbMCBXKtvCcWdwUUizvtUF2UTihYKReMFwxCzAJ
|
||||
BgNVBAYTAlVTMRkwFwYDVQQIExBXYXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdT
|
||||
ZWF0dGxlMSAwHgYDVQQKExdBbWF6b24gV2ViIFNlcnZpY2VzIExMQ4IUDLA+x6tT
|
||||
AP3LRTr0z6nOxfsozdMwEgYDVR0TAQH/BAgwBgEB/wIBADANBgkqhkiG9w0BAQsF
|
||||
AAOBgQAZ7rYKoAwwiiH1M5GJbrT/BEk3OO2VrEPw8ZxgpqQ/EKlzMlOs/0Cyrmp7
|
||||
UYyUgYFQe5nq37Z94rOUSeMgv/WRxaMwrLlLqD78cuF9DSkXaZIX/kECtVaUnjk8
|
||||
BZx0QhoIHOpQocJUSlm/dLeMuE0+0A3HNR6JVktGsUdv9ulmKw==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# certificate for ap-south-2
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICMzCCAZygAwIBAgIGAXjwLj9CMA0GCSqGSIb3DQEBBQUAMFwxCzAJBgNVBAYT
|
||||
@@ -146,6 +273,48 @@ ETwUZ9mTq2vxlV0KvuetCDNS5u4cJsxe/TGGbYP0yP2qfMl0cCImzRI5W0gn8gog
|
||||
dervfeT7nH5ih0TWEy/QDWfkQ601L4erm4yh4YQq8vcqAPSkf04N
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# certificate for ap-southeast-1
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDITCCAoqgAwIBAgIUSqP6ih+++5KF07NXngrWf26mhSUwDQYJKoZIhvcNAQEL
|
||||
BQAwXDELMAkGA1UEBhMCVVMxGTAXBgNVBAgTEFdhc2hpbmd0b24gU3RhdGUxEDAO
|
||||
BgNVBAcTB1NlYXR0bGUxIDAeBgNVBAoTF0FtYXpvbiBXZWIgU2VydmljZXMgTExD
|
||||
MB4XDTI0MDQyOTE0MzAxNFoXDTI5MDQyODE0MzAxNFowXDELMAkGA1UEBhMCVVMx
|
||||
GTAXBgNVBAgTEFdhc2hpbmd0b24gU3RhdGUxEDAOBgNVBAcTB1NlYXR0bGUxIDAe
|
||||
BgNVBAoTF0FtYXpvbiBXZWIgU2VydmljZXMgTExDMIGfMA0GCSqGSIb3DQEBAQUA
|
||||
A4GNADCBiQKBgQCHvRjf/0kStpJ248khtIaN8qkDN3tkw4VjvA9nvPl2anJO+eIB
|
||||
UqPfQG09kZlwpWpmyO8bGB2RWqWxCwuB/dcnIob6w420k9WY5C0IIGtDRNauN3ku
|
||||
vGXkw3HEnF0EjYr0pcyWUvByWY4KswZV42X7Y7XSS13hOIcL6NLA+H94/QIDAQAB
|
||||
o4HfMIHcMAsGA1UdDwQEAwIHgDAdBgNVHQ4EFgQUJdbMCBXKtvCcWdwUUizvtUF2
|
||||
UTgwgZkGA1UdIwSBkTCBjoAUJdbMCBXKtvCcWdwUUizvtUF2UTihYKReMFwxCzAJ
|
||||
BgNVBAYTAlVTMRkwFwYDVQQIExBXYXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdT
|
||||
ZWF0dGxlMSAwHgYDVQQKExdBbWF6b24gV2ViIFNlcnZpY2VzIExMQ4IUSqP6ih++
|
||||
+5KF07NXngrWf26mhSUwEgYDVR0TAQH/BAgwBgEB/wIBADANBgkqhkiG9w0BAQsF
|
||||
AAOBgQAw13BxW11U/JL58j//Fmk7qqtrZTqXmaz1qm2WlIpJpW750MOcP4ux1uPy
|
||||
eM0RdVZ4jHSMv5gtLAv/PjExBfw9n6vNCk+5GZG4Xec5DoapBZHXmfMo93sjxBFP
|
||||
4x9rWn0GuwAVO9ukjYPevq2Rerilrq5VvppHtbATVNY2qecXDA==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# certificate for ap-southeast-2
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDITCCAoqgAwIBAgIUFxWyAdk4oiXIOC9PxcgjYYh71mwwDQYJKoZIhvcNAQEL
|
||||
BQAwXDELMAkGA1UEBhMCVVMxGTAXBgNVBAgTEFdhc2hpbmd0b24gU3RhdGUxEDAO
|
||||
BgNVBAcTB1NlYXR0bGUxIDAeBgNVBAoTF0FtYXpvbiBXZWIgU2VydmljZXMgTExD
|
||||
MB4XDTI0MDQyOTE1MjE0M1oXDTI5MDQyODE1MjE0M1owXDELMAkGA1UEBhMCVVMx
|
||||
GTAXBgNVBAgTEFdhc2hpbmd0b24gU3RhdGUxEDAOBgNVBAcTB1NlYXR0bGUxIDAe
|
||||
BgNVBAoTF0FtYXpvbiBXZWIgU2VydmljZXMgTExDMIGfMA0GCSqGSIb3DQEBAQUA
|
||||
A4GNADCBiQKBgQCHvRjf/0kStpJ248khtIaN8qkDN3tkw4VjvA9nvPl2anJO+eIB
|
||||
UqPfQG09kZlwpWpmyO8bGB2RWqWxCwuB/dcnIob6w420k9WY5C0IIGtDRNauN3ku
|
||||
vGXkw3HEnF0EjYr0pcyWUvByWY4KswZV42X7Y7XSS13hOIcL6NLA+H94/QIDAQAB
|
||||
o4HfMIHcMAsGA1UdDwQEAwIHgDAdBgNVHQ4EFgQUJdbMCBXKtvCcWdwUUizvtUF2
|
||||
UTgwgZkGA1UdIwSBkTCBjoAUJdbMCBXKtvCcWdwUUizvtUF2UTihYKReMFwxCzAJ
|
||||
BgNVBAYTAlVTMRkwFwYDVQQIExBXYXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdT
|
||||
ZWF0dGxlMSAwHgYDVQQKExdBbWF6b24gV2ViIFNlcnZpY2VzIExMQ4IUFxWyAdk4
|
||||
oiXIOC9PxcgjYYh71mwwEgYDVR0TAQH/BAgwBgEB/wIBADANBgkqhkiG9w0BAQsF
|
||||
AAOBgQByjeQe6lr7fiIhoGdjBXYzDfkX0lGGvMIhRh57G1bbceQfaYdZd7Ptc0jl
|
||||
bpycKGaTvhUdkpMOiV2Hi9dOOYawkdhyJDstmDNKu6P9+b6Kak8He5z3NU1tUR2Y
|
||||
uTwcz7Ye8Nldx//ws3raErfTI7D6s9m63OX8cAJ/f8bNgikwpw==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# certificate for ap-southeast-3
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICMzCCAZygAwIBAgIGAXbVDG2yMA0GCSqGSIb3DQEBBQUAMFwxCzAJBgNVBAYT
|
||||
@@ -226,25 +395,46 @@ WX00FTEj4hRVjameE1nENoO8Z7fUVloAFDlDo69fhkJeSvn51D1WRrPnoWGgEfr1
|
||||
+OfK1bAcKTtfkkkP9r4RdwSjKzO5Zu/B+Wqm3kVEz/QNcz6npmA6
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# certificate for us-gov-east-1 and us-gov-west-1
|
||||
# certificate for us-gov-east-1
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDCzCCAnSgAwIBAgIJAIe9Hnq82O7UMA0GCSqGSIb3DQEBCwUAMFwxCzAJBgNV
|
||||
BAYTAlVTMRkwFwYDVQQIExBXYXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdTZWF0
|
||||
dGxlMSAwHgYDVQQKExdBbWF6b24gV2ViIFNlcnZpY2VzIExMQzAeFw0yMTA3MTQx
|
||||
NDI3NTdaFw0yNDA3MTMxNDI3NTdaMFwxCzAJBgNVBAYTAlVTMRkwFwYDVQQIExBX
|
||||
YXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdTZWF0dGxlMSAwHgYDVQQKExdBbWF6
|
||||
b24gV2ViIFNlcnZpY2VzIExMQzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA
|
||||
qaIcGFFTx/SO1W5G91jHvyQdGP25n1Y91aXCuOOWAUTvSvNGpXrI4AXNrQF+CmIO
|
||||
C4beBASnHCx082jYudWBBl9Wiza0psYc9flrczSzVLMmN8w/c78F/95NfiQdnUQP
|
||||
pvgqcMeJo82cgHkLR7XoFWgMrZJqrcUK0gnsQcb6kakCAwEAAaOB1DCB0TALBgNV
|
||||
HQ8EBAMCB4AwHQYDVR0OBBYEFNWV53gWJz72F5B1ZVY4O/dfFYBPMIGOBgNVHSME
|
||||
gYYwgYOAFNWV53gWJz72F5B1ZVY4O/dfFYBPoWCkXjBcMQswCQYDVQQGEwJVUzEZ
|
||||
MBcGA1UECBMQV2FzaGluZ3RvbiBTdGF0ZTEQMA4GA1UEBxMHU2VhdHRsZTEgMB4G
|
||||
A1UEChMXQW1hem9uIFdlYiBTZXJ2aWNlcyBMTEOCCQCHvR56vNju1DASBgNVHRMB
|
||||
Af8ECDAGAQH/AgEAMA0GCSqGSIb3DQEBCwUAA4GBACrKjWj460GUPZCGm3/z0dIz
|
||||
M2BPuH769wcOsqfFZcMKEysSFK91tVtUb1soFwH4/Lb/T0PqNrvtEwD1Nva5k0h2
|
||||
xZhNNRmDuhOhW1K9wCcnHGRBwY5t4lYL6hNV6hcrqYwGMjTjcAjBG2yMgznSNFle
|
||||
Rwi/S3BFXISixNx9cILu
|
||||
MIIDITCCAoqgAwIBAgIULVyrqjjwZ461qelPCiShB1KCCj4wDQYJKoZIhvcNAQEL
|
||||
BQAwXDELMAkGA1UEBhMCVVMxGTAXBgNVBAgTEFdhc2hpbmd0b24gU3RhdGUxEDAO
|
||||
BgNVBAcTB1NlYXR0bGUxIDAeBgNVBAoTF0FtYXpvbiBXZWIgU2VydmljZXMgTExD
|
||||
MB4XDTI0MDUwNzE1MjIzNloXDTI5MDUwNjE1MjIzNlowXDELMAkGA1UEBhMCVVMx
|
||||
GTAXBgNVBAgTEFdhc2hpbmd0b24gU3RhdGUxEDAOBgNVBAcTB1NlYXR0bGUxIDAe
|
||||
BgNVBAoTF0FtYXpvbiBXZWIgU2VydmljZXMgTExDMIGfMA0GCSqGSIb3DQEBAQUA
|
||||
A4GNADCBiQKBgQCpohwYUVPH9I7Vbkb3WMe/JB0Y/bmfVj3VpcK445YBRO9K80al
|
||||
esjgBc2tAX4KYg4Lht4EBKccLHTzaNi51YEGX1aLNrSmxhz1+WtzNLNUsyY3zD9z
|
||||
vwX/3k1+JB2dRA+m+Cpwx4mjzZyAeQtHtegVaAytkmqtxQrSCexBxvqRqQIDAQAB
|
||||
o4HfMIHcMAsGA1UdDwQEAwIHgDAdBgNVHQ4EFgQU1ZXneBYnPvYXkHVlVjg7918V
|
||||
gE8wgZkGA1UdIwSBkTCBjoAU1ZXneBYnPvYXkHVlVjg7918VgE+hYKReMFwxCzAJ
|
||||
BgNVBAYTAlVTMRkwFwYDVQQIExBXYXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdT
|
||||
ZWF0dGxlMSAwHgYDVQQKExdBbWF6b24gV2ViIFNlcnZpY2VzIExMQ4IULVyrqjjw
|
||||
Z461qelPCiShB1KCCj4wEgYDVR0TAQH/BAgwBgEB/wIBADANBgkqhkiG9w0BAQsF
|
||||
AAOBgQBfAL/YZv0y3zmVbXjyxQCsDloeDCJjFKIu3ameEckeIWJbST9LMto0zViZ
|
||||
puIAf05x6GQiEqfBMk+YMxJfcTmJB4Ebaj4egFlslJPSHyC2xuydHlr3B04INOH5
|
||||
Z2oCM68u6GGbj0jZjg7GJonkReG9N72kDva/ukwZKgq8zErQVQ==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# certificate for us-gov-west-1
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDITCCAoqgAwIBAgIUe5wGF3jfb7lUHzvDxmM/ktGCLwwwDQYJKoZIhvcNAQEL
|
||||
BQAwXDELMAkGA1UEBhMCVVMxGTAXBgNVBAgTEFdhc2hpbmd0b24gU3RhdGUxEDAO
|
||||
BgNVBAcTB1NlYXR0bGUxIDAeBgNVBAoTF0FtYXpvbiBXZWIgU2VydmljZXMgTExD
|
||||
MB4XDTI0MDUwNzE3MzAzMloXDTI5MDUwNjE3MzAzMlowXDELMAkGA1UEBhMCVVMx
|
||||
GTAXBgNVBAgTEFdhc2hpbmd0b24gU3RhdGUxEDAOBgNVBAcTB1NlYXR0bGUxIDAe
|
||||
BgNVBAoTF0FtYXpvbiBXZWIgU2VydmljZXMgTExDMIGfMA0GCSqGSIb3DQEBAQUA
|
||||
A4GNADCBiQKBgQCpohwYUVPH9I7Vbkb3WMe/JB0Y/bmfVj3VpcK445YBRO9K80al
|
||||
esjgBc2tAX4KYg4Lht4EBKccLHTzaNi51YEGX1aLNrSmxhz1+WtzNLNUsyY3zD9z
|
||||
vwX/3k1+JB2dRA+m+Cpwx4mjzZyAeQtHtegVaAytkmqtxQrSCexBxvqRqQIDAQAB
|
||||
o4HfMIHcMAsGA1UdDwQEAwIHgDAdBgNVHQ4EFgQU1ZXneBYnPvYXkHVlVjg7918V
|
||||
gE8wgZkGA1UdIwSBkTCBjoAU1ZXneBYnPvYXkHVlVjg7918VgE+hYKReMFwxCzAJ
|
||||
BgNVBAYTAlVTMRkwFwYDVQQIExBXYXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdT
|
||||
ZWF0dGxlMSAwHgYDVQQKExdBbWF6b24gV2ViIFNlcnZpY2VzIExMQ4IUe5wGF3jf
|
||||
b7lUHzvDxmM/ktGCLwwwEgYDVR0TAQH/BAgwBgEB/wIBADANBgkqhkiG9w0BAQsF
|
||||
AAOBgQCbTdpx1Iob9SwUReY4exMnlwQlmkTLyA8tYGWzchCJOJJEPfsW0ryy1A0H
|
||||
YIuvyUty3rJdp9ib8h3GZR71BkZnNddHhy06kPs4p8ewF8+d8OWtOJQcI+ZnFfG4
|
||||
KyM4rUsBrljpG2aOCm12iACEyrvgJJrS8VZwUDZS6mZEnn/lhA==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# certificate for ca-west-1
|
||||
@@ -261,4 +451,188 @@ RZWaBDBJy9x8C2hW+w9lMQjFHkJ7Jy/PHCJ69EzebQIDAQABMA0GCSqGSIb3DQEB
|
||||
BQUAA4GBAGe9Snkz1A6rHBH6/5kDtYvtPYwhx2sXNxztbhkXErFk40Nw5l459NZx
|
||||
EeudxJBLoCkkSgYjhRcOZ/gvDVtWG7qyb6fAqgoisyAbk8K9LzxSim2S1nmT9vD8
|
||||
4B/t/VvwQBylc+ej8kRxMH7fquZLp7IXfmtBzyUqu6Dpbne+chG2
|
||||
-----END CERTIFICATE-----
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# certificate for ap-northeast-1
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDITCCAoqgAwIBAgIULgwDh7TiDrPPBJwscqDwiBHkEFQwDQYJKoZIhvcNAQEL
|
||||
BQAwXDELMAkGA1UEBhMCVVMxGTAXBgNVBAgTEFdhc2hpbmd0b24gU3RhdGUxEDAO
|
||||
BgNVBAcTB1NlYXR0bGUxIDAeBgNVBAoTF0FtYXpvbiBXZWIgU2VydmljZXMgTExD
|
||||
MB4XDTI0MDQyOTEyMjMxMFoXDTI5MDQyODEyMjMxMFowXDELMAkGA1UEBhMCVVMx
|
||||
GTAXBgNVBAgTEFdhc2hpbmd0b24gU3RhdGUxEDAOBgNVBAcTB1NlYXR0bGUxIDAe
|
||||
BgNVBAoTF0FtYXpvbiBXZWIgU2VydmljZXMgTExDMIGfMA0GCSqGSIb3DQEBAQUA
|
||||
A4GNADCBiQKBgQCHvRjf/0kStpJ248khtIaN8qkDN3tkw4VjvA9nvPl2anJO+eIB
|
||||
UqPfQG09kZlwpWpmyO8bGB2RWqWxCwuB/dcnIob6w420k9WY5C0IIGtDRNauN3ku
|
||||
vGXkw3HEnF0EjYr0pcyWUvByWY4KswZV42X7Y7XSS13hOIcL6NLA+H94/QIDAQAB
|
||||
o4HfMIHcMAsGA1UdDwQEAwIHgDAdBgNVHQ4EFgQUJdbMCBXKtvCcWdwUUizvtUF2
|
||||
UTgwgZkGA1UdIwSBkTCBjoAUJdbMCBXKtvCcWdwUUizvtUF2UTihYKReMFwxCzAJ
|
||||
BgNVBAYTAlVTMRkwFwYDVQQIExBXYXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdT
|
||||
ZWF0dGxlMSAwHgYDVQQKExdBbWF6b24gV2ViIFNlcnZpY2VzIExMQ4IULgwDh7Ti
|
||||
DrPPBJwscqDwiBHkEFQwEgYDVR0TAQH/BAgwBgEB/wIBADANBgkqhkiG9w0BAQsF
|
||||
AAOBgQBtjAglBde1t4F9EHCZOj4qnY6Gigy07Ou54i+lR77MhbpzE8V28Li9l+YT
|
||||
QMIn6SzJqU3/fIycIro1OVY1lHmaKYgPGSEZxBenSBHfzwDLRmC9oRp4QMe0BjOC
|
||||
gepj1lUoiN7OA6PtA+ycNlsP0oJvdBjhvayLiuM3tUfLTrgHbw==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# certificate for ap-northeast-2
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDITCCAoqgAwIBAgIUbBSn2UIO6vYk4iNWV0RPxJJtHlgwDQYJKoZIhvcNAQEL
|
||||
BQAwXDELMAkGA1UEBhMCVVMxGTAXBgNVBAgTEFdhc2hpbmd0b24gU3RhdGUxEDAO
|
||||
BgNVBAcTB1NlYXR0bGUxIDAeBgNVBAoTF0FtYXpvbiBXZWIgU2VydmljZXMgTExD
|
||||
MB4XDTI0MDQyOTEzMzg0NloXDTI5MDQyODEzMzg0NlowXDELMAkGA1UEBhMCVVMx
|
||||
GTAXBgNVBAgTEFdhc2hpbmd0b24gU3RhdGUxEDAOBgNVBAcTB1NlYXR0bGUxIDAe
|
||||
BgNVBAoTF0FtYXpvbiBXZWIgU2VydmljZXMgTExDMIGfMA0GCSqGSIb3DQEBAQUA
|
||||
A4GNADCBiQKBgQCHvRjf/0kStpJ248khtIaN8qkDN3tkw4VjvA9nvPl2anJO+eIB
|
||||
UqPfQG09kZlwpWpmyO8bGB2RWqWxCwuB/dcnIob6w420k9WY5C0IIGtDRNauN3ku
|
||||
vGXkw3HEnF0EjYr0pcyWUvByWY4KswZV42X7Y7XSS13hOIcL6NLA+H94/QIDAQAB
|
||||
o4HfMIHcMAsGA1UdDwQEAwIHgDAdBgNVHQ4EFgQUJdbMCBXKtvCcWdwUUizvtUF2
|
||||
UTgwgZkGA1UdIwSBkTCBjoAUJdbMCBXKtvCcWdwUUizvtUF2UTihYKReMFwxCzAJ
|
||||
BgNVBAYTAlVTMRkwFwYDVQQIExBXYXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdT
|
||||
ZWF0dGxlMSAwHgYDVQQKExdBbWF6b24gV2ViIFNlcnZpY2VzIExMQ4IUbBSn2UIO
|
||||
6vYk4iNWV0RPxJJtHlgwEgYDVR0TAQH/BAgwBgEB/wIBADANBgkqhkiG9w0BAQsF
|
||||
AAOBgQAmjTjalG8MGLqWTC2uYqEM8nzI3px1eo0ArvFRsyqQ3fgmWcQpxExqUqRy
|
||||
l3+2134Kv8dFab04Gut5wlfRtc2OwPKKicmv/IXGN+9bKFnQFjTqif08NIzrDZch
|
||||
aFT/uvxrIiM+oN2YsHq66GUhO2+xVRXDXVxM/VObFgPERbJpyA==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# certificate for ap-northeast-3
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICMzCCAZygAwIBAgIGAYPou9weMA0GCSqGSIb3DQEBBQUAMFwxCzAJBgNVBAYT
|
||||
AlVTMRkwFwYDVQQIDBBXYXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHDAdTZWF0dGxl
|
||||
MSAwHgYDVQQKDBdBbWF6b24gV2ViIFNlcnZpY2VzIExMQzAgFw0yMjEwMTgwMTM2
|
||||
MDlaGA8yMjAxMTAxODAxMzYwOVowXDELMAkGA1UEBhMCVVMxGTAXBgNVBAgMEFdh
|
||||
c2hpbmd0b24gU3RhdGUxEDAOBgNVBAcMB1NlYXR0bGUxIDAeBgNVBAoMF0FtYXpv
|
||||
biBXZWIgU2VydmljZXMgTExDMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDK
|
||||
1kIcG5Q6adBXQM75GldfTSiXl7tn54p10TnspI0ErDdb2B6q2Ji/v4XBVH13ZCMg
|
||||
qlRHMqV8AWI5iO6gFn2A9sN3AZXTMqwtZeiDdebq3k6Wt7ieYvpXTg0qvgsjQIov
|
||||
RZWaBDBJy9x8C2hW+w9lMQjFHkJ7Jy/PHCJ69EzebQIDAQABMA0GCSqGSIb3DQEB
|
||||
BQUAA4GBAGe9Snkz1A6rHBH6/5kDtYvtPYwhx2sXNxztbhkXErFk40Nw5l459NZx
|
||||
EeudxJBLoCkkSgYjhRcOZ/gvDVtWG7qyb6fAqgoisyAbk8K9LzxSim2S1nmT9vD8
|
||||
4B/t/VvwQBylc+ej8kRxMH7fquZLp7IXfmtBzyUqu6Dpbne+chG2
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# certificate for ca-central-1
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDITCCAoqgAwIBAgIUIrLgixJJB5C4G8z6pZ5rB0JU2aQwDQYJKoZIhvcNAQEL
|
||||
BQAwXDELMAkGA1UEBhMCVVMxGTAXBgNVBAgTEFdhc2hpbmd0b24gU3RhdGUxEDAO
|
||||
BgNVBAcTB1NlYXR0bGUxIDAeBgNVBAoTF0FtYXpvbiBXZWIgU2VydmljZXMgTExD
|
||||
MB4XDTI0MDQyOTE1MzU0M1oXDTI5MDQyODE1MzU0M1owXDELMAkGA1UEBhMCVVMx
|
||||
GTAXBgNVBAgTEFdhc2hpbmd0b24gU3RhdGUxEDAOBgNVBAcTB1NlYXR0bGUxIDAe
|
||||
BgNVBAoTF0FtYXpvbiBXZWIgU2VydmljZXMgTExDMIGfMA0GCSqGSIb3DQEBAQUA
|
||||
A4GNADCBiQKBgQCHvRjf/0kStpJ248khtIaN8qkDN3tkw4VjvA9nvPl2anJO+eIB
|
||||
UqPfQG09kZlwpWpmyO8bGB2RWqWxCwuB/dcnIob6w420k9WY5C0IIGtDRNauN3ku
|
||||
vGXkw3HEnF0EjYr0pcyWUvByWY4KswZV42X7Y7XSS13hOIcL6NLA+H94/QIDAQAB
|
||||
o4HfMIHcMAsGA1UdDwQEAwIHgDAdBgNVHQ4EFgQUJdbMCBXKtvCcWdwUUizvtUF2
|
||||
UTgwgZkGA1UdIwSBkTCBjoAUJdbMCBXKtvCcWdwUUizvtUF2UTihYKReMFwxCzAJ
|
||||
BgNVBAYTAlVTMRkwFwYDVQQIExBXYXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdT
|
||||
ZWF0dGxlMSAwHgYDVQQKExdBbWF6b24gV2ViIFNlcnZpY2VzIExMQ4IUIrLgixJJ
|
||||
B5C4G8z6pZ5rB0JU2aQwEgYDVR0TAQH/BAgwBgEB/wIBADANBgkqhkiG9w0BAQsF
|
||||
AAOBgQBHiQJmzyFAaSYs8SpiRijIDZW2RIo7qBKb/pI3rqK6yOWDlPuMr6yNI81D
|
||||
IrKGGftg4Z+2KETYU4x76HSf0s//vfH3QA57qFaAwddhKYy4BhteFQl/Wex3xTlX
|
||||
LiwI07kwJvJy3mS6UfQ4HcvZy219tY+0iyOWrz/jVxwq7TOkCw==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# certificate for eu-west-1
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDITCCAoqgAwIBAgIUakDaQ1Zqy87Hy9ESXA1pFC116HkwDQYJKoZIhvcNAQEL
|
||||
BQAwXDELMAkGA1UEBhMCVVMxGTAXBgNVBAgTEFdhc2hpbmd0b24gU3RhdGUxEDAO
|
||||
BgNVBAcTB1NlYXR0bGUxIDAeBgNVBAoTF0FtYXpvbiBXZWIgU2VydmljZXMgTExD
|
||||
MB4XDTI0MDQyOTE2MTgxMFoXDTI5MDQyODE2MTgxMFowXDELMAkGA1UEBhMCVVMx
|
||||
GTAXBgNVBAgTEFdhc2hpbmd0b24gU3RhdGUxEDAOBgNVBAcTB1NlYXR0bGUxIDAe
|
||||
BgNVBAoTF0FtYXpvbiBXZWIgU2VydmljZXMgTExDMIGfMA0GCSqGSIb3DQEBAQUA
|
||||
A4GNADCBiQKBgQCHvRjf/0kStpJ248khtIaN8qkDN3tkw4VjvA9nvPl2anJO+eIB
|
||||
UqPfQG09kZlwpWpmyO8bGB2RWqWxCwuB/dcnIob6w420k9WY5C0IIGtDRNauN3ku
|
||||
vGXkw3HEnF0EjYr0pcyWUvByWY4KswZV42X7Y7XSS13hOIcL6NLA+H94/QIDAQAB
|
||||
o4HfMIHcMAsGA1UdDwQEAwIHgDAdBgNVHQ4EFgQUJdbMCBXKtvCcWdwUUizvtUF2
|
||||
UTgwgZkGA1UdIwSBkTCBjoAUJdbMCBXKtvCcWdwUUizvtUF2UTihYKReMFwxCzAJ
|
||||
BgNVBAYTAlVTMRkwFwYDVQQIExBXYXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdT
|
||||
ZWF0dGxlMSAwHgYDVQQKExdBbWF6b24gV2ViIFNlcnZpY2VzIExMQ4IUakDaQ1Zq
|
||||
y87Hy9ESXA1pFC116HkwEgYDVR0TAQH/BAgwBgEB/wIBADANBgkqhkiG9w0BAQsF
|
||||
AAOBgQADIkn/MqaLGPuK5+prZZ5Ox4bBZLPtreO2C7r0pqU2kPM2lVPyYYydkvP0
|
||||
lgSmmsErGu/oL9JNztDe2oCA+kNy17ehcsf8cw0uP861czNFKCeU8b7FgBbL+sIm
|
||||
qi33rAq6owWGi/5uEcfCR+JP7W+oSYVir5r/yDmWzx+BVH5S/g==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# certificate for eu-west-2
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDITCCAoqgAwIBAgIUCgCV/DPxYNND/swDgEKGiC5I+EwwDQYJKoZIhvcNAQEL
|
||||
BQAwXDELMAkGA1UEBhMCVVMxGTAXBgNVBAgTEFdhc2hpbmd0b24gU3RhdGUxEDAO
|
||||
BgNVBAcTB1NlYXR0bGUxIDAeBgNVBAoTF0FtYXpvbiBXZWIgU2VydmljZXMgTExD
|
||||
MB4XDTI0MDQyOTE2MjkxNFoXDTI5MDQyODE2MjkxNFowXDELMAkGA1UEBhMCVVMx
|
||||
GTAXBgNVBAgTEFdhc2hpbmd0b24gU3RhdGUxEDAOBgNVBAcTB1NlYXR0bGUxIDAe
|
||||
BgNVBAoTF0FtYXpvbiBXZWIgU2VydmljZXMgTExDMIGfMA0GCSqGSIb3DQEBAQUA
|
||||
A4GNADCBiQKBgQCHvRjf/0kStpJ248khtIaN8qkDN3tkw4VjvA9nvPl2anJO+eIB
|
||||
UqPfQG09kZlwpWpmyO8bGB2RWqWxCwuB/dcnIob6w420k9WY5C0IIGtDRNauN3ku
|
||||
vGXkw3HEnF0EjYr0pcyWUvByWY4KswZV42X7Y7XSS13hOIcL6NLA+H94/QIDAQAB
|
||||
o4HfMIHcMAsGA1UdDwQEAwIHgDAdBgNVHQ4EFgQUJdbMCBXKtvCcWdwUUizvtUF2
|
||||
UTgwgZkGA1UdIwSBkTCBjoAUJdbMCBXKtvCcWdwUUizvtUF2UTihYKReMFwxCzAJ
|
||||
BgNVBAYTAlVTMRkwFwYDVQQIExBXYXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdT
|
||||
ZWF0dGxlMSAwHgYDVQQKExdBbWF6b24gV2ViIFNlcnZpY2VzIExMQ4IUCgCV/DPx
|
||||
YNND/swDgEKGiC5I+EwwEgYDVR0TAQH/BAgwBgEB/wIBADANBgkqhkiG9w0BAQsF
|
||||
AAOBgQATPu/sOE2esNa4+XPEGKlEJSgqzyBSQLQc+VWo6FAJhGG9fp7D97jhHeLC
|
||||
5vwfmtTAfnGBxadfAOT3ASkxnOZhXtnRna460LtnNHm7ArCVgXKJo7uBn6ViXtFh
|
||||
uEEw4y6p9YaLQna+VC8Xtgw6WKq2JXuKzuhuNKSFaGGw9vRcHg==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# certificate for eu-west-3
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDITCCAoqgAwIBAgIUaC9fX57UDr6u1vBvsCsECKBZQyIwDQYJKoZIhvcNAQEL
|
||||
BQAwXDELMAkGA1UEBhMCVVMxGTAXBgNVBAgTEFdhc2hpbmd0b24gU3RhdGUxEDAO
|
||||
BgNVBAcTB1NlYXR0bGUxIDAeBgNVBAoTF0FtYXpvbiBXZWIgU2VydmljZXMgTExD
|
||||
MB4XDTI0MDQyOTE2MzczOFoXDTI5MDQyODE2MzczOFowXDELMAkGA1UEBhMCVVMx
|
||||
GTAXBgNVBAgTEFdhc2hpbmd0b24gU3RhdGUxEDAOBgNVBAcTB1NlYXR0bGUxIDAe
|
||||
BgNVBAoTF0FtYXpvbiBXZWIgU2VydmljZXMgTExDMIGfMA0GCSqGSIb3DQEBAQUA
|
||||
A4GNADCBiQKBgQCHvRjf/0kStpJ248khtIaN8qkDN3tkw4VjvA9nvPl2anJO+eIB
|
||||
UqPfQG09kZlwpWpmyO8bGB2RWqWxCwuB/dcnIob6w420k9WY5C0IIGtDRNauN3ku
|
||||
vGXkw3HEnF0EjYr0pcyWUvByWY4KswZV42X7Y7XSS13hOIcL6NLA+H94/QIDAQAB
|
||||
o4HfMIHcMAsGA1UdDwQEAwIHgDAdBgNVHQ4EFgQUJdbMCBXKtvCcWdwUUizvtUF2
|
||||
UTgwgZkGA1UdIwSBkTCBjoAUJdbMCBXKtvCcWdwUUizvtUF2UTihYKReMFwxCzAJ
|
||||
BgNVBAYTAlVTMRkwFwYDVQQIExBXYXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdT
|
||||
ZWF0dGxlMSAwHgYDVQQKExdBbWF6b24gV2ViIFNlcnZpY2VzIExMQ4IUaC9fX57U
|
||||
Dr6u1vBvsCsECKBZQyIwEgYDVR0TAQH/BAgwBgEB/wIBADANBgkqhkiG9w0BAQsF
|
||||
AAOBgQCARv1bQEDaMEzYI0nPlu8GHcMXgmgA94HyrXhMMcaIlQwocGBs6VILGVhM
|
||||
TXP2r3JFaPEpmXSQNQHvGA13clKwAZbni8wtzv6qXb4L4muF34iQRHF0nYrEDoK7
|
||||
mMPR8+oXKKuPO/mv/XKo6XAV5DDERdSYHX5kkA2R9wtvyZjPnQ==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# certificate for eu-north-1
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDITCCAoqgAwIBAgIUN1c9U6U/xiVDFgJcYKZB4NkH1QEwDQYJKoZIhvcNAQEL
|
||||
BQAwXDELMAkGA1UEBhMCVVMxGTAXBgNVBAgTEFdhc2hpbmd0b24gU3RhdGUxEDAO
|
||||
BgNVBAcTB1NlYXR0bGUxIDAeBgNVBAoTF0FtYXpvbiBXZWIgU2VydmljZXMgTExD
|
||||
MB4XDTI0MDQyOTE2MDYwM1oXDTI5MDQyODE2MDYwM1owXDELMAkGA1UEBhMCVVMx
|
||||
GTAXBgNVBAgTEFdhc2hpbmd0b24gU3RhdGUxEDAOBgNVBAcTB1NlYXR0bGUxIDAe
|
||||
BgNVBAoTF0FtYXpvbiBXZWIgU2VydmljZXMgTExDMIGfMA0GCSqGSIb3DQEBAQUA
|
||||
A4GNADCBiQKBgQCHvRjf/0kStpJ248khtIaN8qkDN3tkw4VjvA9nvPl2anJO+eIB
|
||||
UqPfQG09kZlwpWpmyO8bGB2RWqWxCwuB/dcnIob6w420k9WY5C0IIGtDRNauN3ku
|
||||
vGXkw3HEnF0EjYr0pcyWUvByWY4KswZV42X7Y7XSS13hOIcL6NLA+H94/QIDAQAB
|
||||
o4HfMIHcMAsGA1UdDwQEAwIHgDAdBgNVHQ4EFgQUJdbMCBXKtvCcWdwUUizvtUF2
|
||||
UTgwgZkGA1UdIwSBkTCBjoAUJdbMCBXKtvCcWdwUUizvtUF2UTihYKReMFwxCzAJ
|
||||
BgNVBAYTAlVTMRkwFwYDVQQIExBXYXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdT
|
||||
ZWF0dGxlMSAwHgYDVQQKExdBbWF6b24gV2ViIFNlcnZpY2VzIExMQ4IUN1c9U6U/
|
||||
xiVDFgJcYKZB4NkH1QEwEgYDVR0TAQH/BAgwBgEB/wIBADANBgkqhkiG9w0BAQsF
|
||||
AAOBgQBTIQdoFSDRHkpqNPUbZ9WXR2O5v/9bpmHojMYZb3Hw46wsaRso7STiGGX/
|
||||
tRqjIkPUIXsdhZ3+7S/RmhFznmZc8e0bjU4n5vi9CJtQSt+1u4E17+V2bF+D3h/7
|
||||
wcfE0l3414Q8JaTDtfEf/aF3F0uyBvr4MDMd7mFvAMmDmBPSlA==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# certificate for sa-east-1
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDITCCAoqgAwIBAgIUX4Bh4MQ86Roh37VDRRX1MNOB3TcwDQYJKoZIhvcNAQEL
|
||||
BQAwXDELMAkGA1UEBhMCVVMxGTAXBgNVBAgTEFdhc2hpbmd0b24gU3RhdGUxEDAO
|
||||
BgNVBAcTB1NlYXR0bGUxIDAeBgNVBAoTF0FtYXpvbiBXZWIgU2VydmljZXMgTExD
|
||||
MB4XDTI0MDQyOTE2NDYwOVoXDTI5MDQyODE2NDYwOVowXDELMAkGA1UEBhMCVVMx
|
||||
GTAXBgNVBAgTEFdhc2hpbmd0b24gU3RhdGUxEDAOBgNVBAcTB1NlYXR0bGUxIDAe
|
||||
BgNVBAoTF0FtYXpvbiBXZWIgU2VydmljZXMgTExDMIGfMA0GCSqGSIb3DQEBAQUA
|
||||
A4GNADCBiQKBgQCHvRjf/0kStpJ248khtIaN8qkDN3tkw4VjvA9nvPl2anJO+eIB
|
||||
UqPfQG09kZlwpWpmyO8bGB2RWqWxCwuB/dcnIob6w420k9WY5C0IIGtDRNauN3ku
|
||||
vGXkw3HEnF0EjYr0pcyWUvByWY4KswZV42X7Y7XSS13hOIcL6NLA+H94/QIDAQAB
|
||||
o4HfMIHcMAsGA1UdDwQEAwIHgDAdBgNVHQ4EFgQUJdbMCBXKtvCcWdwUUizvtUF2
|
||||
UTgwgZkGA1UdIwSBkTCBjoAUJdbMCBXKtvCcWdwUUizvtUF2UTihYKReMFwxCzAJ
|
||||
BgNVBAYTAlVTMRkwFwYDVQQIExBXYXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdT
|
||||
ZWF0dGxlMSAwHgYDVQQKExdBbWF6b24gV2ViIFNlcnZpY2VzIExMQ4IUX4Bh4MQ8
|
||||
6Roh37VDRRX1MNOB3TcwEgYDVR0TAQH/BAgwBgEB/wIBADANBgkqhkiG9w0BAQsF
|
||||
AAOBgQBnhocfH6ZIX6F5K9+Y9V4HFk8vSaaKL5ytw/P5td1h9ej94KF3xkZ5fyjN
|
||||
URvGQv3kNmNJBoNarcP9I7JIMjsNPmVzqWawyCEGCZImoARxSS3Fc5EAs2PyBfcD
|
||||
9nCtzMTaKO09Xyq0wqXVYn1xJsE5d5yBDsGrzaTHKjxo61+ezQ==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
@@ -896,5 +896,5 @@ func TestAWS_HardcodedCertificates(t *testing.T) {
|
||||
assert.True(t, cert.NotAfter.After(time.Now()))
|
||||
certs = append(certs, cert)
|
||||
}
|
||||
assert.Len(t, 15, certs, "expected 15 certificates in aws_certificates.pem")
|
||||
assert.Len(t, 33, certs, "expected 33 certificates in aws_certificates.pem, but got %d", len(certs))
|
||||
}
|
||||
|
||||
@@ -234,24 +234,24 @@ func (c *Claimer) IsSSHCAEnabled() bool {
|
||||
// Validate validates and modifies the Claims with default values.
|
||||
func (c *Claimer) Validate() error {
|
||||
var (
|
||||
min = c.MinTLSCertDuration()
|
||||
max = c.MaxTLSCertDuration()
|
||||
def = c.DefaultTLSCertDuration()
|
||||
minDur = c.MinTLSCertDuration()
|
||||
maxDur = c.MaxTLSCertDuration()
|
||||
defDur = c.DefaultTLSCertDuration()
|
||||
)
|
||||
switch {
|
||||
case min <= 0:
|
||||
case minDur <= 0:
|
||||
return errors.Errorf("claims: MinTLSCertDuration must be greater than 0")
|
||||
case max <= 0:
|
||||
case maxDur <= 0:
|
||||
return errors.Errorf("claims: MaxTLSCertDuration must be greater than 0")
|
||||
case def <= 0:
|
||||
case defDur <= 0:
|
||||
return errors.Errorf("claims: DefaultTLSCertDuration must be greater than 0")
|
||||
case max < min:
|
||||
case maxDur < minDur:
|
||||
return errors.Errorf("claims: MaxCertDuration cannot be less "+
|
||||
"than MinCertDuration: MaxCertDuration - %v, MinCertDuration - %v", max, min)
|
||||
case def < min:
|
||||
return errors.Errorf("claims: DefaultCertDuration cannot be less than MinCertDuration: DefaultCertDuration - %v, MinCertDuration - %v", def, min)
|
||||
case max < def:
|
||||
return errors.Errorf("claims: MaxCertDuration cannot be less than DefaultCertDuration: MaxCertDuration - %v, DefaultCertDuration - %v", max, def)
|
||||
"than MinCertDuration: MaxCertDuration - %v, MinCertDuration - %v", maxDur, minDur)
|
||||
case defDur < minDur:
|
||||
return errors.Errorf("claims: DefaultCertDuration cannot be less than MinCertDuration: DefaultCertDuration - %v, MinCertDuration - %v", defDur, minDur)
|
||||
case maxDur < defDur:
|
||||
return errors.Errorf("claims: MaxCertDuration cannot be less than DefaultCertDuration: MaxCertDuration - %v, DefaultCertDuration - %v", maxDur, defDur)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -30,6 +30,12 @@ const gcpCertsURL = "https://www.googleapis.com/oauth2/v3/certs"
|
||||
// gcpIdentityURL is the base url for the identity document in GCP.
|
||||
const gcpIdentityURL = "http://metadata/computeMetadata/v1/instance/service-accounts/default/identity"
|
||||
|
||||
// DefaultDisableSSHCAHost is the default value for SSH Host CA used when DisableSSHCAHost is not set
|
||||
var DefaultDisableSSHCAHost = false
|
||||
|
||||
// DefaultDisableSSHCAUser is the default value for SSH User CA used when DisableSSHCAUser is not set
|
||||
var DefaultDisableSSHCAUser = true
|
||||
|
||||
// gcpPayload extends jwt.Claims with custom GCP attributes.
|
||||
type gcpPayload struct {
|
||||
jose.Claims
|
||||
@@ -89,6 +95,8 @@ type GCP struct {
|
||||
ProjectIDs []string `json:"projectIDs"`
|
||||
DisableCustomSANs bool `json:"disableCustomSANs"`
|
||||
DisableTrustOnFirstUse bool `json:"disableTrustOnFirstUse"`
|
||||
DisableSSHCAUser *bool `json:"disableSSHCAUser,omitempty"`
|
||||
DisableSSHCAHost *bool `json:"disableSSHCAHost,omitempty"`
|
||||
InstanceAge Duration `json:"instanceAge,omitempty"`
|
||||
Claims *Claims `json:"claims,omitempty"`
|
||||
Options *Options `json:"options,omitempty"`
|
||||
@@ -199,6 +207,14 @@ func (p *GCP) GetIdentityToken(subject, caURL string) (string, error) {
|
||||
|
||||
// Init validates and initializes the GCP provisioner.
|
||||
func (p *GCP) Init(config Config) (err error) {
|
||||
if p.DisableSSHCAHost == nil {
|
||||
p.DisableSSHCAHost = &DefaultDisableSSHCAHost
|
||||
}
|
||||
|
||||
if p.DisableSSHCAUser == nil {
|
||||
p.DisableSSHCAUser = &DefaultDisableSSHCAUser
|
||||
}
|
||||
|
||||
switch {
|
||||
case p.Type == "":
|
||||
return errors.New("provisioner type cannot be empty")
|
||||
@@ -387,31 +403,41 @@ func (p *GCP) authorizeToken(token string) (*gcpPayload, error) {
|
||||
}
|
||||
|
||||
// AuthorizeSSHSign returns the list of SignOption for a SignSSH request.
|
||||
func (p *GCP) AuthorizeSSHSign(_ context.Context, token string) ([]SignOption, error) {
|
||||
if !p.ctl.Claimer.IsSSHCAEnabled() {
|
||||
return nil, errs.Unauthorized("gcp.AuthorizeSSHSign; sshCA is disabled for gcp provisioner '%s'", p.GetName())
|
||||
func (p *GCP) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption, error) {
|
||||
certType, hasCertType := CertTypeFromContext(ctx)
|
||||
if !hasCertType {
|
||||
certType = SSHHostCert
|
||||
}
|
||||
|
||||
err := p.isUnauthorizedToIssueSSHCert(certType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
claims, err := p.authorizeToken(token)
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(http.StatusInternalServerError, err, "gcp.AuthorizeSSHSign")
|
||||
}
|
||||
|
||||
ce := claims.Google.ComputeEngine
|
||||
var principals []string
|
||||
var keyID string
|
||||
var defaults SignSSHOptions
|
||||
var ct sshutil.CertType
|
||||
var template string
|
||||
|
||||
switch certType {
|
||||
case SSHHostCert:
|
||||
defaults, keyID, principals, ct, template = p.genHostOptions(ctx, claims)
|
||||
case SSHUserCert:
|
||||
defaults, keyID, principals, ct, template = p.genUserOptions(ctx, claims)
|
||||
default:
|
||||
return nil, errs.Unauthorized("gcp.AuthorizeSSHSign; invalid requested certType")
|
||||
}
|
||||
|
||||
signOptions := []SignOption{}
|
||||
|
||||
// Enforce host certificate.
|
||||
defaults := SignSSHOptions{
|
||||
CertType: SSHHostCert,
|
||||
}
|
||||
|
||||
// Validated principals.
|
||||
principals := []string{
|
||||
fmt.Sprintf("%s.c.%s.internal", ce.InstanceName, ce.ProjectID),
|
||||
fmt.Sprintf("%s.%s.c.%s.internal", ce.InstanceName, ce.Zone, ce.ProjectID),
|
||||
}
|
||||
|
||||
// Only enforce known principals if disable custom sans is true.
|
||||
if p.DisableCustomSANs {
|
||||
// Only enforce known principals if disable custom sans is true, or it is a user cert request
|
||||
if p.DisableCustomSANs || certType == SSHUserCert {
|
||||
defaults.Principals = principals
|
||||
} else {
|
||||
// Check that at least one principal is sent in the request.
|
||||
@@ -421,12 +447,12 @@ func (p *GCP) AuthorizeSSHSign(_ context.Context, token string) ([]SignOption, e
|
||||
}
|
||||
|
||||
// Certificate templates.
|
||||
data := sshutil.CreateTemplateData(sshutil.HostCert, ce.InstanceName, principals)
|
||||
data := sshutil.CreateTemplateData(ct, keyID, principals)
|
||||
if v, err := unsafeParseSigned(token); err == nil {
|
||||
data.SetToken(v)
|
||||
}
|
||||
|
||||
templateOptions, err := CustomSSHTemplateOptions(p.Options, data, sshutil.DefaultIIDTemplate)
|
||||
templateOptions, err := CustomSSHTemplateOptions(p.Options, data, template)
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(http.StatusInternalServerError, err, "gcp.AuthorizeSSHSign")
|
||||
}
|
||||
@@ -445,12 +471,54 @@ func (p *GCP) AuthorizeSSHSign(_ context.Context, token string) ([]SignOption, e
|
||||
// Require all the fields in the SSH certificate
|
||||
&sshCertDefaultValidator{},
|
||||
// Ensure that all principal names are allowed
|
||||
newSSHNamePolicyValidator(p.ctl.getPolicy().getSSHHost(), nil),
|
||||
newSSHNamePolicyValidator(p.ctl.getPolicy().getSSHHost(), p.ctl.getPolicy().getSSHUser()),
|
||||
// Call webhooks
|
||||
p.ctl.newWebhookController(
|
||||
data,
|
||||
linkedca.Webhook_SSH,
|
||||
webhook.WithAuthorizationPrincipal(ce.InstanceID),
|
||||
webhook.WithAuthorizationPrincipal(keyID),
|
||||
),
|
||||
), nil
|
||||
}
|
||||
|
||||
func (p *GCP) genHostOptions(_ context.Context, claims *gcpPayload) (SignSSHOptions, string, []string, sshutil.CertType, string) {
|
||||
ce := claims.Google.ComputeEngine
|
||||
keyID := ce.InstanceName
|
||||
|
||||
principals := []string{
|
||||
fmt.Sprintf("%s.c.%s.internal", ce.InstanceName, ce.ProjectID),
|
||||
fmt.Sprintf("%s.%s.c.%s.internal", ce.InstanceName, ce.Zone, ce.ProjectID),
|
||||
}
|
||||
|
||||
return SignSSHOptions{CertType: SSHHostCert}, keyID, principals, sshutil.HostCert, sshutil.DefaultIIDTemplate
|
||||
}
|
||||
|
||||
func FormatServiceAccountUsername(serviceAccountId string) string {
|
||||
return fmt.Sprintf("sa_%v", serviceAccountId)
|
||||
}
|
||||
|
||||
func (p *GCP) genUserOptions(_ context.Context, claims *gcpPayload) (SignSSHOptions, string, []string, sshutil.CertType, string) {
|
||||
keyID := claims.Email
|
||||
principals := []string{
|
||||
FormatServiceAccountUsername(claims.Subject),
|
||||
claims.Email,
|
||||
}
|
||||
|
||||
return SignSSHOptions{CertType: SSHUserCert}, keyID, principals, sshutil.UserCert, sshutil.DefaultTemplate
|
||||
}
|
||||
|
||||
func (p *GCP) isUnauthorizedToIssueSSHCert(certType string) error {
|
||||
if !p.ctl.Claimer.IsSSHCAEnabled() {
|
||||
return errs.Unauthorized("gcp.AuthorizeSSHSign; sshCA is disabled for gcp provisioner '%s'", p.GetName())
|
||||
}
|
||||
|
||||
if certType == SSHHostCert && *p.DisableSSHCAHost {
|
||||
return errs.Unauthorized("gcp.AuthorizeSSHSign; sshCA for Hosts is disabled for gcp provisioner '%s'", p.GetName())
|
||||
}
|
||||
|
||||
if certType == SSHUserCert && *p.DisableSSHCAUser {
|
||||
return errs.Unauthorized("gcp.AuthorizeSSHSign; sshCA for Users is disabled for gcp provisioner '%s'", p.GetName())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -186,6 +186,7 @@ func TestGCP_Init(t *testing.T) {
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
{"ok", fields{"GCP", "name", nil, zero, nil}, args{config, srv.URL}, false},
|
||||
{"ok", fields{"GCP", "name", nil, zero, nil}, args{config, srv.URL}, false},
|
||||
{"ok", fields{"GCP", "name", []string{"service-account"}, zero, nil}, args{config, srv.URL}, false},
|
||||
{"ok", fields{"GCP", "name", []string{"service-account"}, Duration{Duration: 1 * time.Minute}, nil}, args{config, srv.URL}, false},
|
||||
@@ -211,6 +212,14 @@ func TestGCP_Init(t *testing.T) {
|
||||
if err := p.Init(tt.args.config); (err != nil) != tt.wantErr {
|
||||
t.Errorf("GCP.Init() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
|
||||
if *p.DisableSSHCAUser != true {
|
||||
t.Errorf("By default DisableSSHCAUser should be true")
|
||||
}
|
||||
|
||||
if *p.DisableSSHCAHost != false {
|
||||
t.Errorf("By default DisableSSHCAHost should be false")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -592,6 +601,9 @@ func TestGCP_AuthorizeSSHSign(t *testing.T) {
|
||||
p1, err := generateGCP()
|
||||
assert.FatalError(t, err)
|
||||
p1.DisableCustomSANs = true
|
||||
// enable ssh user CA
|
||||
disableSSCAUser := false
|
||||
p1.DisableSSHCAUser = &disableSSCAUser
|
||||
|
||||
p2, err := generateGCP()
|
||||
assert.FatalError(t, err)
|
||||
@@ -605,6 +617,12 @@ func TestGCP_AuthorizeSSHSign(t *testing.T) {
|
||||
p3.ctl.Claimer, err = NewClaimer(p3.Claims, globalProvisionerClaims)
|
||||
assert.FatalError(t, err)
|
||||
|
||||
p4, err := generateGCP()
|
||||
assert.FatalError(t, err)
|
||||
// disable ssh host CA
|
||||
disableSSCAHost := true
|
||||
p4.DisableSSHCAHost = &disableSSCAHost
|
||||
|
||||
t1, err := generateGCPToken(p1.ServiceAccounts[0],
|
||||
"https://accounts.google.com", p1.GetID(),
|
||||
"instance-id", "instance-name", "project-id", "zone",
|
||||
@@ -647,6 +665,10 @@ func TestGCP_AuthorizeSSHSign(t *testing.T) {
|
||||
CertType: "host", Principals: []string{"foo.bar", "bar.foo"},
|
||||
ValidAfter: NewTimeDuration(tm), ValidBefore: NewTimeDuration(tm.Add(hostDuration)),
|
||||
}
|
||||
expectedUserOptions := &SignSSHOptions{
|
||||
CertType: "user", Principals: []string{FormatServiceAccountUsername(p1.ServiceAccounts[0]), "foo@developer.gserviceaccount.com"},
|
||||
ValidAfter: NewTimeDuration(tm), ValidBefore: NewTimeDuration(tm.Add(p1.ctl.Claimer.DefaultUserSSHCertDuration())),
|
||||
}
|
||||
|
||||
type args struct {
|
||||
token string
|
||||
@@ -664,22 +686,29 @@ func TestGCP_AuthorizeSSHSign(t *testing.T) {
|
||||
}{
|
||||
{"ok", p1, args{t1, SignSSHOptions{}, pub}, expectedHostOptions, http.StatusOK, false, false},
|
||||
{"ok-rsa2048", p1, args{t1, SignSSHOptions{}, rsa2048.Public()}, expectedHostOptions, http.StatusOK, false, false},
|
||||
{"ok-type", p1, args{t1, SignSSHOptions{CertType: "host"}, pub}, expectedHostOptions, http.StatusOK, false, false},
|
||||
{"ok-type-host", p1, args{t1, SignSSHOptions{CertType: "host"}, pub}, expectedHostOptions, http.StatusOK, false, false},
|
||||
{"ok-type-user", p1, args{t1, SignSSHOptions{CertType: "user"}, pub}, expectedUserOptions, http.StatusOK, false, false},
|
||||
{"ok-principals", p1, args{t1, SignSSHOptions{Principals: []string{"instance-name.c.project-id.internal", "instance-name.zone.c.project-id.internal"}}, pub}, expectedHostOptions, http.StatusOK, false, false},
|
||||
{"ok-principal1", p1, args{t1, SignSSHOptions{Principals: []string{"instance-name.c.project-id.internal"}}, pub}, expectedHostOptionsPrincipal1, http.StatusOK, false, false},
|
||||
{"ok-principal2", p1, args{t1, SignSSHOptions{Principals: []string{"instance-name.zone.c.project-id.internal"}}, pub}, expectedHostOptionsPrincipal2, http.StatusOK, false, false},
|
||||
{"ok-options", p1, args{t1, SignSSHOptions{CertType: "host", Principals: []string{"instance-name.c.project-id.internal", "instance-name.zone.c.project-id.internal"}}, pub}, expectedHostOptions, http.StatusOK, false, false},
|
||||
{"ok-custom", p2, args{t2, SignSSHOptions{Principals: []string{"foo.bar", "bar.foo"}}, pub}, expectedCustomOptions, http.StatusOK, false, false},
|
||||
{"fail-rsa1024", p1, args{t1, SignSSHOptions{}, rsa1024.Public()}, expectedHostOptions, http.StatusOK, false, true},
|
||||
{"fail-type", p1, args{t1, SignSSHOptions{CertType: "user"}, pub}, nil, http.StatusOK, false, true},
|
||||
{"fail-principal", p1, args{t1, SignSSHOptions{Principals: []string{"smallstep.com"}}, pub}, nil, http.StatusOK, false, true},
|
||||
{"fail-extra-principal", p1, args{t1, SignSSHOptions{Principals: []string{"instance-name.c.project-id.internal", "instance-name.zone.c.project-id.internal", "smallstep.com"}}, pub}, nil, http.StatusOK, false, true},
|
||||
{"fail-sshCA-disabled", p3, args{"foo", SignSSHOptions{}, pub}, expectedHostOptions, http.StatusUnauthorized, true, false},
|
||||
{"fail-type-host", p4, args{"foo", SignSSHOptions{CertType: "host"}, pub}, nil, http.StatusUnauthorized, true, false},
|
||||
{"fail-type-user", p4, args{"foo", SignSSHOptions{CertType: "host"}, pub}, nil, http.StatusUnauthorized, true, false},
|
||||
{"fail-invalid-token", p1, args{"foo", SignSSHOptions{}, pub}, expectedHostOptions, http.StatusUnauthorized, true, false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := tt.gcp.AuthorizeSSHSign(context.Background(), tt.args.token)
|
||||
ctx := context.Background()
|
||||
if tt.args.sshOpts.CertType == SSHUserCert {
|
||||
ctx = NewContextWithCertType(ctx, SSHUserCert)
|
||||
}
|
||||
|
||||
got, err := tt.gcp.AuthorizeSSHSign(ctx, tt.args.token)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("GCP.AuthorizeSSHSign() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
|
||||
@@ -78,3 +78,17 @@ func TokenFromContext(ctx context.Context) (string, bool) {
|
||||
token, ok := ctx.Value(tokenKey{}).(string)
|
||||
return token, ok
|
||||
}
|
||||
|
||||
// The key to save the certTypeKey in the context.
|
||||
type certTypeKey struct{}
|
||||
|
||||
// NewContextWithCertType creates a new context with the given CertType.
|
||||
func NewContextWithCertType(ctx context.Context, certType string) context.Context {
|
||||
return context.WithValue(ctx, certTypeKey{}, certType)
|
||||
}
|
||||
|
||||
// CertTypeFromContext returns the certType stored in the given context.
|
||||
func CertTypeFromContext(ctx context.Context) (string, bool) {
|
||||
certType, ok := ctx.Value(certTypeKey{}).(string)
|
||||
return certType, ok
|
||||
}
|
||||
|
||||
@@ -93,6 +93,8 @@ type OIDC struct {
|
||||
ListenAddress string `json:"listenAddress,omitempty"`
|
||||
Claims *Claims `json:"claims,omitempty"`
|
||||
Options *Options `json:"options,omitempty"`
|
||||
Scopes []string `json:"scopes,omitempty"`
|
||||
AuthParams []string `json:"authParams,omitempty"`
|
||||
configuration openIDConfiguration
|
||||
keyStore *keyStore
|
||||
ctl *Controller
|
||||
|
||||
@@ -189,7 +189,7 @@ func TestTemplateOptions(t *testing.T) {
|
||||
|
||||
func TestCustomTemplateOptions(t *testing.T) {
|
||||
csr := parseCertificateRequest(t, "testdata/certs/ecdsa.csr")
|
||||
csrCertificate := `{"version":0,"subject":{"commonName":"foo"},"dnsNames":["foo"],"emailAddresses":null,"ipAddresses":null,"uris":null,"sans":null,"extensions":[{"id":"2.5.29.17","critical":false,"value":"MAWCA2Zvbw=="}],"signatureAlgorithm":""}`
|
||||
csrCertificate := `{"version":0,"subject":{"commonName":"foo"},"rawSubject":"MA4xDDAKBgNVBAMTA2Zvbw==","dnsNames":["foo"],"emailAddresses":null,"ipAddresses":null,"uris":null,"sans":null,"extensions":[{"id":"2.5.29.17","critical":false,"value":"MAWCA2Zvbw=="}],"signatureAlgorithm":""}`
|
||||
data := x509util.TemplateData{
|
||||
x509util.SubjectKey: x509util.Subject{
|
||||
CommonName: "foobar",
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
kmsapi "go.step.sm/crypto/kms/apiv1"
|
||||
"golang.org/x/crypto/ssh"
|
||||
|
||||
"github.com/smallstep/certificates/errs"
|
||||
@@ -33,6 +34,31 @@ type Interface interface {
|
||||
AuthorizeSSHRekey(ctx context.Context, token string) (*ssh.Certificate, []SignOption, error)
|
||||
}
|
||||
|
||||
// Uninitialized represents a disabled provisioner. Uninitialized provisioners
|
||||
// are created when the Init methods fails.
|
||||
type Uninitialized struct {
|
||||
Interface
|
||||
Reason error
|
||||
}
|
||||
|
||||
// MarshalJSON returns the JSON encoding of the provisioner with the disabled
|
||||
// reason.
|
||||
func (p Uninitialized) MarshalJSON() ([]byte, error) {
|
||||
provisionerJSON, err := json.Marshal(p.Interface)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reasonJSON, err := json.Marshal(struct {
|
||||
State string `json:"state"`
|
||||
StateReason string `json:"stateReason"`
|
||||
}{"Uninitialized", p.Reason.Error()})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reasonJSON[0] = ','
|
||||
return append(provisionerJSON[:len(provisionerJSON)-1], reasonJSON...), nil
|
||||
}
|
||||
|
||||
// ErrAllowTokenReuse is an error that is returned by provisioners that allows
|
||||
// the reuse of tokens.
|
||||
//
|
||||
@@ -206,6 +232,13 @@ type SSHKeys struct {
|
||||
HostKeys []ssh.PublicKey
|
||||
}
|
||||
|
||||
// SCEPKeyManager is a KMS interface that combines a KeyManager with a
|
||||
// Decrypter.
|
||||
type SCEPKeyManager interface {
|
||||
kmsapi.KeyManager
|
||||
kmsapi.Decrypter
|
||||
}
|
||||
|
||||
// Config defines the default parameters used in the initialization of
|
||||
// provisioners.
|
||||
type Config struct {
|
||||
@@ -226,6 +259,8 @@ type Config struct {
|
||||
AuthorizeSSHRenewFunc AuthorizeSSHRenewFunc
|
||||
// WebhookClient is an http client to use in webhook request
|
||||
WebhookClient *http.Client
|
||||
// SCEPKeyManager, if defined, is the interface used by SCEP provisioners.
|
||||
SCEPKeyManager SCEPKeyManager
|
||||
}
|
||||
|
||||
type provisioner struct {
|
||||
@@ -320,7 +355,7 @@ func (b *base) AuthorizeSSHSign(context.Context, string) ([]SignOption, error) {
|
||||
return nil, errs.Unauthorized("provisioner.AuthorizeSSHSign not implemented")
|
||||
}
|
||||
|
||||
// AuthorizeRevoke returns an unimplemented error. Provisioners should overwrite
|
||||
// AuthorizeSSHRevoke returns an unimplemented error. Provisioners should overwrite
|
||||
// this method if they will support authorizing tokens for revoking SSH Certificates.
|
||||
func (b *base) AuthorizeSSHRevoke(context.Context, string) error {
|
||||
return errs.Unauthorized("provisioner.AuthorizeSSHRevoke not implemented")
|
||||
|
||||
@@ -6,10 +6,10 @@ import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
|
||||
"github.com/smallstep/assert"
|
||||
"github.com/go-jose/go-jose/v3"
|
||||
"github.com/smallstep/certificates/api/render"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
func TestType_String(t *testing.T) {
|
||||
@@ -149,11 +149,11 @@ func TestDefaultIdentityFunc(t *testing.T) {
|
||||
identity, err := DefaultIdentityFunc(context.Background(), tc.p, tc.email)
|
||||
if err != nil {
|
||||
if assert.NotNil(t, tc.err) {
|
||||
assert.Equals(t, tc.err.Error(), err.Error())
|
||||
assert.Equal(t, tc.err.Error(), err.Error())
|
||||
}
|
||||
} else {
|
||||
if assert.Nil(t, tc.err) {
|
||||
assert.Equals(t, identity.Usernames, tc.identity.Usernames)
|
||||
assert.Equal(t, identity.Usernames, tc.identity.Usernames)
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -243,9 +243,43 @@ func TestUnimplementedMethods(t *testing.T) {
|
||||
}
|
||||
var sc render.StatusCodedError
|
||||
if assert.True(t, errors.As(err, &sc), "error does not implement StatusCodedError interface") {
|
||||
assert.Equals(t, sc.StatusCode(), http.StatusUnauthorized)
|
||||
assert.Equal(t, http.StatusUnauthorized, sc.StatusCode())
|
||||
}
|
||||
assert.Equals(t, err.Error(), msg)
|
||||
assert.Equal(t, msg, err.Error())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUninitialized_MarshalJSON(t *testing.T) {
|
||||
p := &JWK{
|
||||
Name: "bad-provisioner",
|
||||
Type: "JWK",
|
||||
Key: &jose.JSONWebKey{
|
||||
Key: []byte("foo"),
|
||||
},
|
||||
}
|
||||
|
||||
type fields struct {
|
||||
Interface Interface
|
||||
Reason error
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
want []byte
|
||||
assertion assert.ErrorAssertionFunc
|
||||
}{
|
||||
{"ok", fields{p, errors.New("bad key")}, []byte(`{"type":"JWK","name":"bad-provisioner","key":{"kty":"oct","k":"Zm9v"},"state":"Uninitialized","stateReason":"bad key"}`), assert.NoError},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
p := Uninitialized{
|
||||
Interface: tt.fields.Interface,
|
||||
Reason: tt.fields.Reason,
|
||||
}
|
||||
got, err := p.MarshalJSON()
|
||||
tt.assertion(t, err)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
|
||||
"go.step.sm/crypto/kms"
|
||||
kmsapi "go.step.sm/crypto/kms/apiv1"
|
||||
"go.step.sm/crypto/kms/uri"
|
||||
"go.step.sm/linkedca"
|
||||
|
||||
"github.com/smallstep/certificates/webhook"
|
||||
@@ -59,7 +58,7 @@ type SCEP struct {
|
||||
encryptionAlgorithm int
|
||||
challengeValidationController *challengeValidationController
|
||||
notificationController *notificationController
|
||||
keyManager kmsapi.KeyManager
|
||||
keyManager SCEPKeyManager
|
||||
decrypter crypto.Decrypter
|
||||
decrypterCertificate *x509.Certificate
|
||||
signer crypto.Signer
|
||||
@@ -269,34 +268,38 @@ func (s *SCEP) Init(config Config) (err error) {
|
||||
)
|
||||
|
||||
// parse the decrypter key PEM contents if available
|
||||
if decryptionKeyPEM := s.DecrypterKeyPEM; len(decryptionKeyPEM) > 0 {
|
||||
if len(s.DecrypterKeyPEM) > 0 {
|
||||
// try reading the PEM for validation
|
||||
block, rest := pem.Decode(decryptionKeyPEM)
|
||||
block, rest := pem.Decode(s.DecrypterKeyPEM)
|
||||
if len(rest) > 0 {
|
||||
return errors.New("failed parsing decrypter key: trailing data")
|
||||
}
|
||||
if block == nil {
|
||||
return errors.New("failed parsing decrypter key: no PEM block found")
|
||||
}
|
||||
|
||||
opts := kms.Options{
|
||||
Type: kmsapi.SoftKMS,
|
||||
}
|
||||
if s.keyManager, err = kms.New(context.Background(), opts); err != nil {
|
||||
km, err := kms.New(context.Background(), opts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed initializing kms: %w", err)
|
||||
}
|
||||
kmsDecrypter, ok := s.keyManager.(kmsapi.Decrypter)
|
||||
scepKeyManager, ok := km.(SCEPKeyManager)
|
||||
if !ok {
|
||||
return fmt.Errorf("%q is not a kmsapi.Decrypter", opts.Type)
|
||||
}
|
||||
if s.decrypter, err = kmsDecrypter.CreateDecrypter(&kmsapi.CreateDecrypterRequest{
|
||||
DecryptionKeyPEM: decryptionKeyPEM,
|
||||
s.keyManager = scepKeyManager
|
||||
|
||||
if s.decrypter, err = s.keyManager.CreateDecrypter(&kmsapi.CreateDecrypterRequest{
|
||||
DecryptionKeyPEM: s.DecrypterKeyPEM,
|
||||
Password: []byte(s.DecrypterKeyPassword),
|
||||
PasswordPrompter: kmsapi.NonInteractivePasswordPrompter,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("failed creating decrypter: %w", err)
|
||||
}
|
||||
if s.signer, err = s.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{
|
||||
SigningKeyPEM: decryptionKeyPEM, // TODO(hs): support distinct signer key in the future?
|
||||
SigningKeyPEM: s.DecrypterKeyPEM, // TODO(hs): support distinct signer key in the future?
|
||||
Password: []byte(s.DecrypterKeyPassword),
|
||||
PasswordPrompter: kmsapi.NonInteractivePasswordPrompter,
|
||||
}); err != nil {
|
||||
@@ -304,41 +307,44 @@ func (s *SCEP) Init(config Config) (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
if decryptionKeyURI := s.DecrypterKeyURI; decryptionKeyURI != "" {
|
||||
u, err := uri.Parse(s.DecrypterKeyURI)
|
||||
if s.DecrypterKeyURI != "" {
|
||||
kmsType, err := kmsapi.TypeOf(s.DecrypterKeyURI)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed parsing decrypter key: %w", err)
|
||||
}
|
||||
var kmsType kmsapi.Type
|
||||
switch {
|
||||
case u.Scheme != "":
|
||||
kmsType = kms.Type(u.Scheme)
|
||||
default:
|
||||
kmsType = kmsapi.SoftKMS
|
||||
|
||||
if config.SCEPKeyManager != nil {
|
||||
s.keyManager = config.SCEPKeyManager
|
||||
} else {
|
||||
if kmsType == kmsapi.DefaultKMS {
|
||||
kmsType = kmsapi.SoftKMS
|
||||
}
|
||||
opts := kms.Options{
|
||||
Type: kmsType,
|
||||
URI: s.DecrypterKeyURI,
|
||||
}
|
||||
km, err := kms.New(context.Background(), opts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed initializing kms: %w", err)
|
||||
}
|
||||
scepKeyManager, ok := km.(SCEPKeyManager)
|
||||
if !ok {
|
||||
return fmt.Errorf("%q is not a kmsapi.Decrypter", opts.Type)
|
||||
}
|
||||
s.keyManager = scepKeyManager
|
||||
}
|
||||
opts := kms.Options{
|
||||
Type: kmsType,
|
||||
URI: s.DecrypterKeyURI,
|
||||
}
|
||||
if s.keyManager, err = kms.New(context.Background(), opts); err != nil {
|
||||
return fmt.Errorf("failed initializing kms: %w", err)
|
||||
}
|
||||
kmsDecrypter, ok := s.keyManager.(kmsapi.Decrypter)
|
||||
if !ok {
|
||||
return fmt.Errorf("%q is not a kmsapi.Decrypter", opts.Type)
|
||||
}
|
||||
if kmsType != "softkms" { // TODO(hs): this should likely become more transparent?
|
||||
decryptionKeyURI = u.Opaque
|
||||
}
|
||||
if s.decrypter, err = kmsDecrypter.CreateDecrypter(&kmsapi.CreateDecrypterRequest{
|
||||
DecryptionKey: decryptionKeyURI,
|
||||
|
||||
// Create decrypter and signer with the same key:
|
||||
// TODO(hs): support distinct signer key in the future?
|
||||
if s.decrypter, err = s.keyManager.CreateDecrypter(&kmsapi.CreateDecrypterRequest{
|
||||
DecryptionKey: s.DecrypterKeyURI,
|
||||
Password: []byte(s.DecrypterKeyPassword),
|
||||
PasswordPrompter: kmsapi.NonInteractivePasswordPrompter,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("failed creating decrypter: %w", err)
|
||||
}
|
||||
if s.signer, err = s.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{
|
||||
SigningKey: decryptionKeyURI, // TODO(hs): support distinct signer key in the future?
|
||||
SigningKey: s.DecrypterKeyURI,
|
||||
Password: []byte(s.DecrypterKeyPassword),
|
||||
PasswordPrompter: kmsapi.NonInteractivePasswordPrompter,
|
||||
}); err != nil {
|
||||
|
||||
@@ -2,19 +2,27 @@ package provisioner
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/smallstep/certificates/webhook"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"go.step.sm/crypto/kms/softkms"
|
||||
"go.step.sm/crypto/minica"
|
||||
"go.step.sm/crypto/pemutil"
|
||||
"go.step.sm/linkedca"
|
||||
|
||||
"github.com/smallstep/certificates/webhook"
|
||||
)
|
||||
|
||||
func Test_challengeValidationController_Validate(t *testing.T) {
|
||||
@@ -366,3 +374,270 @@ func TestSCEP_ValidateChallenge(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSCEP_Init(t *testing.T) {
|
||||
serialize := func(key crypto.PrivateKey, password string) []byte {
|
||||
var opts []pemutil.Options
|
||||
if password == "" {
|
||||
opts = append(opts, pemutil.WithPasswordPrompt("no password", func(s string) ([]byte, error) {
|
||||
return nil, nil
|
||||
}))
|
||||
} else {
|
||||
opts = append(opts, pemutil.WithPassword([]byte("password")))
|
||||
}
|
||||
block, err := pemutil.Serialize(key, opts...)
|
||||
require.NoError(t, err)
|
||||
return pem.EncodeToMemory(block)
|
||||
}
|
||||
|
||||
ca, err := minica.New()
|
||||
require.NoError(t, err)
|
||||
|
||||
key, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
require.NoError(t, err)
|
||||
badKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
require.NoError(t, err)
|
||||
|
||||
cert, err := ca.Sign(&x509.Certificate{
|
||||
Subject: pkix.Name{CommonName: "SCEP decryptor"},
|
||||
PublicKey: key.Public(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
certPEM := pem.EncodeToMemory(&pem.Block{
|
||||
Type: "CERTIFICATE", Bytes: cert.Raw,
|
||||
})
|
||||
certPEMWithIntermediate := append(pem.EncodeToMemory(&pem.Block{
|
||||
Type: "CERTIFICATE", Bytes: cert.Raw,
|
||||
}), pem.EncodeToMemory(&pem.Block{
|
||||
Type: "CERTIFICATE", Bytes: ca.Intermediate.Raw,
|
||||
})...)
|
||||
|
||||
keyPEM := serialize(key, "password")
|
||||
keyPEMNoPassword := serialize(key, "")
|
||||
badKeyPEM := serialize(badKey, "password")
|
||||
|
||||
tmp := t.TempDir()
|
||||
path := filepath.Join(tmp, "rsa.priv")
|
||||
pathNoPassword := filepath.Join(tmp, "rsa.key")
|
||||
|
||||
require.NoError(t, os.WriteFile(path, keyPEM, 0600))
|
||||
require.NoError(t, os.WriteFile(pathNoPassword, keyPEMNoPassword, 0600))
|
||||
|
||||
type args struct {
|
||||
config Config
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
s *SCEP
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
{"ok", &SCEP{
|
||||
Type: "SCEP",
|
||||
Name: "scep",
|
||||
ChallengePassword: "password123",
|
||||
MinimumPublicKeyLength: 0,
|
||||
DecrypterCertificate: certPEM,
|
||||
DecrypterKeyPEM: keyPEM,
|
||||
DecrypterKeyPassword: "password",
|
||||
EncryptionAlgorithmIdentifier: 0,
|
||||
}, args{Config{Claims: globalProvisionerClaims}}, false},
|
||||
{"ok no password", &SCEP{
|
||||
Type: "SCEP",
|
||||
Name: "scep",
|
||||
ChallengePassword: "password123",
|
||||
MinimumPublicKeyLength: 0,
|
||||
DecrypterCertificate: certPEM,
|
||||
DecrypterKeyPEM: keyPEMNoPassword,
|
||||
DecrypterKeyPassword: "",
|
||||
EncryptionAlgorithmIdentifier: 1,
|
||||
}, args{Config{Claims: globalProvisionerClaims}}, false},
|
||||
{"ok with uri", &SCEP{
|
||||
Type: "SCEP",
|
||||
Name: "scep",
|
||||
ChallengePassword: "password123",
|
||||
MinimumPublicKeyLength: 1024,
|
||||
DecrypterCertificate: certPEM,
|
||||
DecrypterKeyURI: "softkms:path=" + path,
|
||||
DecrypterKeyPassword: "password",
|
||||
EncryptionAlgorithmIdentifier: 2,
|
||||
}, args{Config{Claims: globalProvisionerClaims}}, false},
|
||||
{"ok with uri no password", &SCEP{
|
||||
Type: "SCEP",
|
||||
Name: "scep",
|
||||
ChallengePassword: "password123",
|
||||
MinimumPublicKeyLength: 2048,
|
||||
DecrypterCertificate: certPEM,
|
||||
DecrypterKeyURI: "softkms:path=" + pathNoPassword,
|
||||
DecrypterKeyPassword: "",
|
||||
EncryptionAlgorithmIdentifier: 3,
|
||||
}, args{Config{Claims: globalProvisionerClaims}}, false},
|
||||
{"ok with SCEPKeyManager", &SCEP{
|
||||
Type: "SCEP",
|
||||
Name: "scep",
|
||||
ChallengePassword: "password123",
|
||||
MinimumPublicKeyLength: 2048,
|
||||
DecrypterCertificate: certPEM,
|
||||
DecrypterKeyURI: "softkms:path=" + pathNoPassword,
|
||||
DecrypterKeyPassword: "",
|
||||
EncryptionAlgorithmIdentifier: 4,
|
||||
}, args{Config{Claims: globalProvisionerClaims, SCEPKeyManager: &softkms.SoftKMS{}}}, false},
|
||||
{"ok intermediate", &SCEP{
|
||||
Type: "SCEP",
|
||||
Name: "scep",
|
||||
ChallengePassword: "password123",
|
||||
MinimumPublicKeyLength: 0,
|
||||
DecrypterCertificate: nil,
|
||||
DecrypterKeyPEM: nil,
|
||||
DecrypterKeyPassword: "",
|
||||
EncryptionAlgorithmIdentifier: 0,
|
||||
}, args{Config{Claims: globalProvisionerClaims}}, false},
|
||||
{"fail type", &SCEP{
|
||||
Type: "",
|
||||
Name: "scep",
|
||||
ChallengePassword: "password123",
|
||||
MinimumPublicKeyLength: 0,
|
||||
DecrypterCertificate: certPEM,
|
||||
DecrypterKeyPEM: keyPEM,
|
||||
DecrypterKeyPassword: "password",
|
||||
EncryptionAlgorithmIdentifier: 0,
|
||||
}, args{Config{Claims: globalProvisionerClaims}}, true},
|
||||
{"fail name", &SCEP{
|
||||
Type: "SCEP",
|
||||
Name: "",
|
||||
ChallengePassword: "password123",
|
||||
MinimumPublicKeyLength: 0,
|
||||
DecrypterCertificate: certPEM,
|
||||
DecrypterKeyPEM: keyPEM,
|
||||
DecrypterKeyPassword: "password",
|
||||
EncryptionAlgorithmIdentifier: 0,
|
||||
}, args{Config{Claims: globalProvisionerClaims}}, true},
|
||||
{"fail minimumPublicKeyLength", &SCEP{
|
||||
Type: "SCEP",
|
||||
Name: "scep",
|
||||
ChallengePassword: "password123",
|
||||
MinimumPublicKeyLength: 2001,
|
||||
DecrypterCertificate: certPEM,
|
||||
DecrypterKeyPEM: keyPEM,
|
||||
DecrypterKeyPassword: "password",
|
||||
EncryptionAlgorithmIdentifier: 0,
|
||||
}, args{Config{Claims: globalProvisionerClaims}}, true},
|
||||
{"fail encryptionAlgorithmIdentifier", &SCEP{
|
||||
Type: "SCEP",
|
||||
Name: "scep",
|
||||
ChallengePassword: "password123",
|
||||
MinimumPublicKeyLength: 0,
|
||||
DecrypterCertificate: certPEM,
|
||||
DecrypterKeyPEM: keyPEM,
|
||||
DecrypterKeyPassword: "password",
|
||||
EncryptionAlgorithmIdentifier: 5,
|
||||
}, args{Config{Claims: globalProvisionerClaims}}, true},
|
||||
{"fail negative encryptionAlgorithmIdentifier", &SCEP{
|
||||
Type: "SCEP",
|
||||
Name: "scep",
|
||||
ChallengePassword: "password123",
|
||||
MinimumPublicKeyLength: 0,
|
||||
DecrypterCertificate: certPEM,
|
||||
DecrypterKeyPEM: keyPEM,
|
||||
DecrypterKeyPassword: "password",
|
||||
EncryptionAlgorithmIdentifier: -1,
|
||||
}, args{Config{Claims: globalProvisionerClaims}}, true},
|
||||
{"fail key decode", &SCEP{
|
||||
Type: "SCEP",
|
||||
Name: "scep",
|
||||
ChallengePassword: "password123",
|
||||
MinimumPublicKeyLength: 0,
|
||||
DecrypterCertificate: certPEM,
|
||||
DecrypterKeyPEM: []byte("not a pem"),
|
||||
DecrypterKeyPassword: "password",
|
||||
EncryptionAlgorithmIdentifier: 0,
|
||||
}, args{Config{Claims: globalProvisionerClaims}}, true},
|
||||
{"fail certificate decode", &SCEP{
|
||||
Type: "SCEP",
|
||||
Name: "scep",
|
||||
ChallengePassword: "password123",
|
||||
MinimumPublicKeyLength: 0,
|
||||
DecrypterCertificate: []byte("not a pem"),
|
||||
DecrypterKeyPEM: keyPEM,
|
||||
DecrypterKeyPassword: "password",
|
||||
EncryptionAlgorithmIdentifier: 0,
|
||||
}, args{Config{Claims: globalProvisionerClaims}}, true},
|
||||
{"fail certificate with intermediate", &SCEP{
|
||||
Type: "SCEP",
|
||||
Name: "scep",
|
||||
ChallengePassword: "password123",
|
||||
MinimumPublicKeyLength: 0,
|
||||
DecrypterCertificate: certPEMWithIntermediate,
|
||||
DecrypterKeyPEM: keyPEM,
|
||||
DecrypterKeyPassword: "password",
|
||||
}, args{Config{Claims: globalProvisionerClaims}}, true},
|
||||
{"fail decrypter password", &SCEP{
|
||||
Type: "SCEP",
|
||||
Name: "scep",
|
||||
ChallengePassword: "password123",
|
||||
MinimumPublicKeyLength: 0,
|
||||
DecrypterCertificate: certPEM,
|
||||
DecrypterKeyPEM: keyPEM,
|
||||
DecrypterKeyPassword: "badpassword",
|
||||
EncryptionAlgorithmIdentifier: 0,
|
||||
}, args{Config{Claims: globalProvisionerClaims}}, true},
|
||||
{"fail uri", &SCEP{
|
||||
Type: "SCEP",
|
||||
Name: "scep",
|
||||
ChallengePassword: "password123",
|
||||
MinimumPublicKeyLength: 0,
|
||||
DecrypterCertificate: certPEM,
|
||||
DecrypterKeyURI: "softkms:path=missing.key",
|
||||
DecrypterKeyPassword: "password",
|
||||
EncryptionAlgorithmIdentifier: 0,
|
||||
}, args{Config{Claims: globalProvisionerClaims}}, true},
|
||||
{"fail uri password", &SCEP{
|
||||
Type: "SCEP",
|
||||
Name: "scep",
|
||||
ChallengePassword: "password123",
|
||||
MinimumPublicKeyLength: 0,
|
||||
DecrypterCertificate: certPEM,
|
||||
DecrypterKeyURI: "softkms:path=" + path,
|
||||
DecrypterKeyPassword: "badpassword",
|
||||
EncryptionAlgorithmIdentifier: 0,
|
||||
}, args{Config{Claims: globalProvisionerClaims}}, true},
|
||||
{"fail uri type", &SCEP{
|
||||
Type: "SCEP",
|
||||
Name: "scep",
|
||||
ChallengePassword: "password123",
|
||||
MinimumPublicKeyLength: 0,
|
||||
DecrypterCertificate: certPEM,
|
||||
DecrypterKeyURI: "foo:path=" + path,
|
||||
DecrypterKeyPassword: "password",
|
||||
EncryptionAlgorithmIdentifier: 0,
|
||||
}, args{Config{Claims: globalProvisionerClaims}}, true},
|
||||
{"fail missing certificate", &SCEP{
|
||||
Type: "SCEP",
|
||||
Name: "scep",
|
||||
ChallengePassword: "password123",
|
||||
MinimumPublicKeyLength: 0,
|
||||
DecrypterCertificate: nil,
|
||||
DecrypterKeyPEM: keyPEM,
|
||||
DecrypterKeyPassword: "password",
|
||||
EncryptionAlgorithmIdentifier: 0,
|
||||
}, args{Config{Claims: globalProvisionerClaims}}, true},
|
||||
{"fail key match", &SCEP{
|
||||
Type: "SCEP",
|
||||
Name: "scep",
|
||||
ChallengePassword: "password123",
|
||||
MinimumPublicKeyLength: 0,
|
||||
DecrypterCertificate: certPEM,
|
||||
DecrypterKeyPEM: badKeyPEM,
|
||||
DecrypterKeyPassword: "password",
|
||||
EncryptionAlgorithmIdentifier: 0,
|
||||
}, args{Config{Claims: globalProvisionerClaims}}, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if err := tt.s.Init(tt.args.config); (err != nil) != tt.wantErr {
|
||||
t.Errorf("SCEP.Init() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,14 +302,19 @@ func (v defaultSANsValidator) Valid(req *x509.CertificateRequest) (err error) {
|
||||
// duration.
|
||||
type profileDefaultDuration time.Duration
|
||||
|
||||
// Modify sets the certificate NotBefore and NotAfter using the following order:
|
||||
// - From the SignOptions that we get from flags.
|
||||
// - From x509.Certificate that we get from the template.
|
||||
// - NotBefore from the current time with a backdate.
|
||||
// - NotAfter from NotBefore plus the duration in v.
|
||||
func (v profileDefaultDuration) Modify(cert *x509.Certificate, so SignOptions) error {
|
||||
var backdate time.Duration
|
||||
notBefore := so.NotBefore.Time()
|
||||
notBefore := timeOr(so.NotBefore.Time(), cert.NotBefore)
|
||||
if notBefore.IsZero() {
|
||||
notBefore = now()
|
||||
backdate = -1 * so.Backdate
|
||||
}
|
||||
notAfter := so.NotAfter.RelativeTime(notBefore)
|
||||
notAfter := timeOr(so.NotAfter.RelativeTime(notBefore), cert.NotAfter)
|
||||
if notAfter.IsZero() {
|
||||
if v != 0 {
|
||||
notAfter = notBefore.Add(time.Duration(v))
|
||||
@@ -330,11 +335,17 @@ type profileLimitDuration struct {
|
||||
notBefore, notAfter time.Time
|
||||
}
|
||||
|
||||
// Option returns an x509util option that limits the validity period of a
|
||||
// certificate to one that is superficially imposed.
|
||||
// Modify sets the certificate NotBefore and NotAfter but limits the validity
|
||||
// period to the certificate to one that is superficially imposed.
|
||||
//
|
||||
// The expected NotBefore and NotAfter are set using the following order:
|
||||
// - From the SignOptions that we get from flags.
|
||||
// - From x509.Certificate that we get from the template.
|
||||
// - NotBefore from the current time with a backdate.
|
||||
// - NotAfter from NotBefore plus the duration v or the notAfter in v if lower.
|
||||
func (v profileLimitDuration) Modify(cert *x509.Certificate, so SignOptions) error {
|
||||
var backdate time.Duration
|
||||
notBefore := so.NotBefore.Time()
|
||||
notBefore := timeOr(so.NotBefore.Time(), cert.NotBefore)
|
||||
if notBefore.IsZero() {
|
||||
notBefore = now()
|
||||
backdate = -1 * so.Backdate
|
||||
@@ -345,7 +356,7 @@ func (v profileLimitDuration) Modify(cert *x509.Certificate, so SignOptions) err
|
||||
notBefore, v.notBefore)
|
||||
}
|
||||
|
||||
notAfter := so.NotAfter.RelativeTime(notBefore)
|
||||
notAfter := timeOr(so.NotAfter.RelativeTime(notBefore), cert.NotAfter)
|
||||
if notAfter.After(v.notAfter) {
|
||||
return errs.Forbidden(
|
||||
"requested certificate notAfter (%s) is after the expiration of the provisioning credential (%s)",
|
||||
@@ -372,8 +383,8 @@ type validityValidator struct {
|
||||
}
|
||||
|
||||
// newValidityValidator return a new validity validator.
|
||||
func newValidityValidator(min, max time.Duration) *validityValidator {
|
||||
return &validityValidator{min: min, max: max}
|
||||
func newValidityValidator(minDur, maxDur time.Duration) *validityValidator {
|
||||
return &validityValidator{min: minDur, max: maxDur}
|
||||
}
|
||||
|
||||
// Valid validates the certificate validity settings (notBefore/notAfter) and
|
||||
|
||||
@@ -598,9 +598,61 @@ func Test_profileDefaultDuration_Option(t *testing.T) {
|
||||
na := time.Now().Add(10 * time.Minute).UTC()
|
||||
d := 4 * time.Hour
|
||||
return test{
|
||||
pdd: profileDefaultDuration(d),
|
||||
so: SignOptions{NotBefore: NewTimeDuration(nb), NotAfter: NewTimeDuration(na)},
|
||||
cert: new(x509.Certificate),
|
||||
pdd: profileDefaultDuration(d),
|
||||
so: SignOptions{NotBefore: NewTimeDuration(nb), NotAfter: NewTimeDuration(na)},
|
||||
cert: &x509.Certificate{
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: time.Now().Add(time.Hour),
|
||||
},
|
||||
valid: func(cert *x509.Certificate) {
|
||||
assert.Equals(t, nb, cert.NotBefore)
|
||||
assert.Equals(t, na, cert.NotAfter)
|
||||
},
|
||||
}
|
||||
},
|
||||
"ok/cert-with-validity": func() test {
|
||||
nb := time.Now().Add(5 * time.Minute).UTC()
|
||||
na := time.Now().Add(10 * time.Minute).UTC()
|
||||
d := 4 * time.Hour
|
||||
return test{
|
||||
pdd: profileDefaultDuration(d),
|
||||
so: SignOptions{},
|
||||
cert: &x509.Certificate{
|
||||
NotBefore: nb,
|
||||
NotAfter: na,
|
||||
},
|
||||
valid: func(cert *x509.Certificate) {
|
||||
assert.Equals(t, nb, cert.NotBefore)
|
||||
assert.Equals(t, na, cert.NotAfter)
|
||||
},
|
||||
}
|
||||
},
|
||||
"ok/cert-notBefore-option-notafter": func() test {
|
||||
nb := time.Now().Add(5 * time.Minute).UTC()
|
||||
na := time.Now().Add(10 * time.Minute).UTC()
|
||||
d := 4 * time.Hour
|
||||
return test{
|
||||
pdd: profileDefaultDuration(d),
|
||||
so: SignOptions{NotAfter: NewTimeDuration(na)},
|
||||
cert: &x509.Certificate{
|
||||
NotBefore: nb,
|
||||
},
|
||||
valid: func(cert *x509.Certificate) {
|
||||
assert.Equals(t, nb, cert.NotBefore)
|
||||
assert.Equals(t, na, cert.NotAfter)
|
||||
},
|
||||
}
|
||||
},
|
||||
"ok/cert-notAfter-option-notBefore": func() test {
|
||||
nb := time.Now().Add(5 * time.Minute).UTC()
|
||||
na := time.Now().Add(10 * time.Minute).UTC()
|
||||
d := 4 * time.Hour
|
||||
return test{
|
||||
pdd: profileDefaultDuration(d),
|
||||
so: SignOptions{NotBefore: NewTimeDuration(nb)},
|
||||
cert: &x509.Certificate{
|
||||
NotAfter: na,
|
||||
},
|
||||
valid: func(cert *x509.Certificate) {
|
||||
assert.Equals(t, cert.NotBefore, nb)
|
||||
assert.Equals(t, cert.NotAfter, na)
|
||||
@@ -725,6 +777,28 @@ func Test_profileLimitDuration_Option(t *testing.T) {
|
||||
err: errors.New("requested certificate notAfter ("),
|
||||
}
|
||||
},
|
||||
"fail/cert-validity-notBefore": func() test {
|
||||
return test{
|
||||
pld: profileLimitDuration{def: 4 * time.Hour, notBefore: n, notAfter: n.Add(6 * time.Hour)},
|
||||
so: SignOptions{},
|
||||
cert: &x509.Certificate{
|
||||
NotBefore: n.Add(-time.Second),
|
||||
NotAfter: n.Add(5 * time.Hour),
|
||||
},
|
||||
err: errors.New("requested certificate notBefore ("),
|
||||
}
|
||||
},
|
||||
"fail/cert-validity-notAfter": func() test {
|
||||
return test{
|
||||
pld: profileLimitDuration{def: 4 * time.Hour, notBefore: n, notAfter: n.Add(6 * time.Hour)},
|
||||
so: SignOptions{},
|
||||
cert: &x509.Certificate{
|
||||
NotBefore: n,
|
||||
NotAfter: n.Add(6*time.Hour + time.Second),
|
||||
},
|
||||
err: errors.New("requested certificate notAfter ("),
|
||||
}
|
||||
},
|
||||
"ok/valid-notAfter-requested": func() test {
|
||||
d, err := ParseTimeDuration("2h")
|
||||
assert.FatalError(t, err)
|
||||
@@ -782,6 +856,72 @@ func Test_profileLimitDuration_Option(t *testing.T) {
|
||||
},
|
||||
}
|
||||
},
|
||||
"ok/cert-validity": func() test {
|
||||
return test{
|
||||
pld: profileLimitDuration{def: 4 * time.Hour, notBefore: n, notAfter: n.Add(6 * time.Hour)},
|
||||
so: SignOptions{},
|
||||
cert: &x509.Certificate{
|
||||
NotBefore: n,
|
||||
NotAfter: n.Add(5 * time.Hour),
|
||||
},
|
||||
valid: func(cert *x509.Certificate) {
|
||||
assert.Equals(t, n, cert.NotBefore)
|
||||
assert.Equals(t, n.Add(5*time.Hour), cert.NotAfter)
|
||||
},
|
||||
}
|
||||
},
|
||||
"ok/cert-notBefore-default": func() test {
|
||||
return test{
|
||||
pld: profileLimitDuration{def: 4 * time.Hour, notBefore: n, notAfter: n.Add(6 * time.Hour)},
|
||||
so: SignOptions{},
|
||||
cert: &x509.Certificate{
|
||||
NotBefore: n,
|
||||
},
|
||||
valid: func(cert *x509.Certificate) {
|
||||
assert.Equals(t, n, cert.NotBefore)
|
||||
assert.Equals(t, n.Add(4*time.Hour), cert.NotAfter)
|
||||
},
|
||||
}
|
||||
},
|
||||
"ok/cert-notAfter-default": func() test {
|
||||
return test{
|
||||
pld: profileLimitDuration{def: 4 * time.Hour, notBefore: n, notAfter: n.Add(6 * time.Hour)},
|
||||
so: SignOptions{},
|
||||
cert: &x509.Certificate{
|
||||
NotAfter: n.Add(5 * time.Hour),
|
||||
},
|
||||
valid: func(cert *x509.Certificate) {
|
||||
assert.Equals(t, n, cert.NotBefore)
|
||||
assert.Equals(t, n.Add(5*time.Hour), cert.NotAfter)
|
||||
},
|
||||
}
|
||||
},
|
||||
"ok/cert-notBefore-option": func() test {
|
||||
return test{
|
||||
pld: profileLimitDuration{def: 4 * time.Hour, notBefore: n, notAfter: n.Add(6 * time.Hour)},
|
||||
so: SignOptions{NotAfter: NewTimeDuration(n.Add(5 * time.Hour))},
|
||||
cert: &x509.Certificate{
|
||||
NotBefore: n,
|
||||
},
|
||||
valid: func(cert *x509.Certificate) {
|
||||
assert.Equals(t, n, cert.NotBefore)
|
||||
assert.Equals(t, n.Add(5*time.Hour), cert.NotAfter)
|
||||
},
|
||||
}
|
||||
},
|
||||
"ok/cert-notAfter-option": func() test {
|
||||
return test{
|
||||
pld: profileLimitDuration{def: 4 * time.Hour, notBefore: n, notAfter: n.Add(6 * time.Hour)},
|
||||
so: SignOptions{NotBefore: NewTimeDuration(n.Add(4 * time.Hour))},
|
||||
cert: &x509.Certificate{
|
||||
NotAfter: n.Add(5 * time.Hour),
|
||||
},
|
||||
valid: func(cert *x509.Certificate) {
|
||||
assert.Equals(t, n.Add(4*time.Hour), cert.NotBefore)
|
||||
assert.Equals(t, n.Add(5*time.Hour), cert.NotAfter)
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
for name, run := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
|
||||
@@ -286,14 +286,14 @@ func (v *sshCertValidityValidator) Valid(cert *ssh.Certificate, opts SignSSHOpti
|
||||
return errs.BadRequest("ssh certificate validBefore cannot be before validAfter")
|
||||
}
|
||||
|
||||
var min, max time.Duration
|
||||
var minDur, maxDur time.Duration
|
||||
switch cert.CertType {
|
||||
case ssh.UserCert:
|
||||
min = v.MinUserSSHCertDuration()
|
||||
max = v.MaxUserSSHCertDuration()
|
||||
minDur = v.MinUserSSHCertDuration()
|
||||
maxDur = v.MaxUserSSHCertDuration()
|
||||
case ssh.HostCert:
|
||||
min = v.MinHostSSHCertDuration()
|
||||
max = v.MaxHostSSHCertDuration()
|
||||
minDur = v.MinHostSSHCertDuration()
|
||||
maxDur = v.MaxHostSSHCertDuration()
|
||||
case 0:
|
||||
return errs.BadRequest("ssh certificate type has not been set")
|
||||
default:
|
||||
@@ -305,10 +305,10 @@ func (v *sshCertValidityValidator) Valid(cert *ssh.Certificate, opts SignSSHOpti
|
||||
dur := time.Duration(cert.ValidBefore-cert.ValidAfter) * time.Second
|
||||
|
||||
switch {
|
||||
case dur < min:
|
||||
return errs.Forbidden("requested duration of %s is less than minimum accepted duration for selected provisioner of %s", dur, min)
|
||||
case dur > max+opts.Backdate:
|
||||
return errs.Forbidden("requested duration of %s is greater than maximum accepted duration for selected provisioner of %s", dur, max+opts.Backdate)
|
||||
case dur < minDur:
|
||||
return errs.Forbidden("requested duration of %s is less than minimum accepted duration for selected provisioner of %s", dur, minDur)
|
||||
case dur > maxDur+opts.Backdate:
|
||||
return errs.Forbidden("requested duration of %s is greater than maximum accepted duration for selected provisioner of %s", dur, maxDur+opts.Backdate)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -11,6 +11,17 @@ var now = func() time.Time {
|
||||
return time.Now().UTC()
|
||||
}
|
||||
|
||||
// timeOr returns the first of its arguments that is not equal to the zero time.
|
||||
// This method can be replaced with cmp.Or when step-ca requires Go 1.22.
|
||||
func timeOr(ts ...time.Time) time.Time {
|
||||
for _, t := range ts {
|
||||
if !t.IsZero() {
|
||||
return t
|
||||
}
|
||||
}
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
// TimeDuration is a type that represents a time but the JSON unmarshaling can
|
||||
// use a time using the RFC 3339 format or a time.Duration string. If a duration
|
||||
// is used, the time will be set on the first call to TimeDuration.Time.
|
||||
|
||||
@@ -363,11 +363,13 @@ func generateGCP() (*GCP, error) {
|
||||
return nil, err
|
||||
}
|
||||
p := &GCP{
|
||||
Type: "GCP",
|
||||
Name: name,
|
||||
ServiceAccounts: []string{serviceAccount},
|
||||
Claims: &globalProvisionerClaims,
|
||||
config: newGCPConfig(),
|
||||
Type: "GCP",
|
||||
Name: name,
|
||||
ServiceAccounts: []string{serviceAccount},
|
||||
Claims: &globalProvisionerClaims,
|
||||
DisableSSHCAHost: &DefaultDisableSSHCAHost,
|
||||
DisableSSHCAUser: &DefaultDisableSSHCAUser,
|
||||
config: newGCPConfig(),
|
||||
keyStore: &keyStore{
|
||||
keySet: jose.JSONWebKeySet{Keys: []jose.JSONWebKey{*jwk}},
|
||||
expiry: time.Now().Add(24 * time.Hour),
|
||||
|
||||
@@ -201,6 +201,7 @@ func (a *Authority) generateProvisionerConfig(ctx context.Context) (provisioner.
|
||||
AuthorizeRenewFunc: a.authorizeRenewFunc,
|
||||
AuthorizeSSHRenewFunc: a.authorizeSSHRenewFunc,
|
||||
WebhookClient: a.webhookClient,
|
||||
SCEPKeyManager: a.scepKeyManager,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -425,25 +426,25 @@ func ValidateClaims(c *linkedca.Claims) error {
|
||||
// ValidateDurations validates the Durations type.
|
||||
func ValidateDurations(d *linkedca.Durations) error {
|
||||
var (
|
||||
err error
|
||||
min, max, def *provisioner.Duration
|
||||
err error
|
||||
minDur, maxDur, def *provisioner.Duration
|
||||
)
|
||||
|
||||
if d.Min != "" {
|
||||
min, err = provisioner.NewDuration(d.Min)
|
||||
minDur, err = provisioner.NewDuration(d.Min)
|
||||
if err != nil {
|
||||
return admin.WrapError(admin.ErrorBadRequestType, err, "min duration '%s' is invalid", d.Min)
|
||||
}
|
||||
if min.Value() < 0 {
|
||||
if minDur.Value() < 0 {
|
||||
return admin.WrapError(admin.ErrorBadRequestType, err, "min duration '%s' cannot be less than 0", d.Min)
|
||||
}
|
||||
}
|
||||
if d.Max != "" {
|
||||
max, err = provisioner.NewDuration(d.Max)
|
||||
maxDur, err = provisioner.NewDuration(d.Max)
|
||||
if err != nil {
|
||||
return admin.WrapError(admin.ErrorBadRequestType, err, "max duration '%s' is invalid", d.Max)
|
||||
}
|
||||
if max.Value() < 0 {
|
||||
if maxDur.Value() < 0 {
|
||||
return admin.WrapError(admin.ErrorBadRequestType, err, "max duration '%s' cannot be less than 0", d.Max)
|
||||
}
|
||||
}
|
||||
@@ -456,15 +457,15 @@ func ValidateDurations(d *linkedca.Durations) error {
|
||||
return admin.WrapError(admin.ErrorBadRequestType, err, "default duration '%s' cannot be less than 0", d.Default)
|
||||
}
|
||||
}
|
||||
if d.Min != "" && d.Max != "" && min.Value() > max.Value() {
|
||||
if d.Min != "" && d.Max != "" && minDur.Value() > maxDur.Value() {
|
||||
return admin.NewError(admin.ErrorBadRequestType,
|
||||
"min duration '%s' cannot be greater than max duration '%s'", d.Min, d.Max)
|
||||
}
|
||||
if d.Min != "" && d.Default != "" && min.Value() > def.Value() {
|
||||
if d.Min != "" && d.Default != "" && minDur.Value() > def.Value() {
|
||||
return admin.NewError(admin.ErrorBadRequestType,
|
||||
"min duration '%s' cannot be greater than default duration '%s'", d.Min, d.Default)
|
||||
}
|
||||
if d.Default != "" && d.Max != "" && min.Value() > def.Value() {
|
||||
if d.Default != "" && d.Max != "" && minDur.Value() > def.Value() {
|
||||
return admin.NewError(admin.ErrorBadRequestType,
|
||||
"default duration '%s' cannot be greater than max duration '%s'", d.Default, d.Max)
|
||||
}
|
||||
@@ -607,15 +608,15 @@ func provisionerWebhookToLinkedca(pwh *provisioner.Webhook) *linkedca.Webhook {
|
||||
return lwh
|
||||
}
|
||||
|
||||
func durationsToCertificates(d *linkedca.Durations) (min, max, def *provisioner.Duration, err error) {
|
||||
func durationsToCertificates(d *linkedca.Durations) (minDur, maxDur, def *provisioner.Duration, err error) {
|
||||
if d.Min != "" {
|
||||
min, err = provisioner.NewDuration(d.Min)
|
||||
minDur, err = provisioner.NewDuration(d.Min)
|
||||
if err != nil {
|
||||
return nil, nil, nil, admin.WrapErrorISE(err, "error parsing minimum duration '%s'", d.Min)
|
||||
}
|
||||
}
|
||||
if d.Max != "" {
|
||||
max, err = provisioner.NewDuration(d.Max)
|
||||
maxDur, err = provisioner.NewDuration(d.Max)
|
||||
if err != nil {
|
||||
return nil, nil, nil, admin.WrapErrorISE(err, "error parsing maximum duration '%s'", d.Max)
|
||||
}
|
||||
@@ -917,6 +918,8 @@ func ProvisionerToCertificates(p *linkedca.Provisioner) (provisioner.Interface,
|
||||
Domains: cfg.Domains,
|
||||
Groups: cfg.Groups,
|
||||
ListenAddress: cfg.ListenAddress,
|
||||
Scopes: cfg.Scopes,
|
||||
AuthParams: cfg.AuthParams,
|
||||
Claims: claims,
|
||||
Options: options,
|
||||
}, nil
|
||||
@@ -1065,6 +1068,8 @@ func ProvisionerToLinkedca(p provisioner.Interface) (*linkedca.Provisioner, erro
|
||||
Groups: p.Groups,
|
||||
ListenAddress: p.ListenAddress,
|
||||
TenantId: p.TenantID,
|
||||
Scopes: p.Scopes,
|
||||
AuthParams: p.AuthParams,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -57,3 +57,26 @@ func (a *Authority) GetFederation() (federation []*x509.Certificate, err error)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// GetIntermediateCertificate return the intermediate certificate that issues
|
||||
// the leaf certificates in the CA.
|
||||
//
|
||||
// This method can return nil if the CA is configured with a Certificate
|
||||
// Authority Service (CAS) that does not implement the
|
||||
// CertificateAuthorityGetter interface.
|
||||
func (a *Authority) GetIntermediateCertificate() *x509.Certificate {
|
||||
if len(a.intermediateX509Certs) > 0 {
|
||||
return a.intermediateX509Certs[0]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetIntermediateCertificates returns a list of all intermediate certificates
|
||||
// configured. The first certificate in the list will be the issuer certificate.
|
||||
//
|
||||
// This method can return an empty list or nil if the CA is configured with a
|
||||
// Certificate Authority Service (CAS) that does not implement the
|
||||
// CertificateAuthorityGetter interface.
|
||||
func (a *Authority) GetIntermediateCertificates() []*x509.Certificate {
|
||||
return a.intermediateX509Certs
|
||||
}
|
||||
|
||||
@@ -2,15 +2,18 @@ package authority
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"errors"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"go.step.sm/crypto/pemutil"
|
||||
|
||||
"github.com/smallstep/assert"
|
||||
"github.com/smallstep/certificates/api/render"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.step.sm/crypto/keyutil"
|
||||
"go.step.sm/crypto/minica"
|
||||
"go.step.sm/crypto/pemutil"
|
||||
)
|
||||
|
||||
func TestRoot(t *testing.T) {
|
||||
@@ -152,3 +155,63 @@ func TestAuthority_GetFederation(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthority_GetIntermediateCertificate(t *testing.T) {
|
||||
ca, err := minica.New(minica.WithRootTemplate(`{
|
||||
"subject": {{ toJson .Subject }},
|
||||
"issuer": {{ toJson .Subject }},
|
||||
"keyUsage": ["certSign", "crlSign"],
|
||||
"basicConstraints": {
|
||||
"isCA": true,
|
||||
"maxPathLen": -1
|
||||
}
|
||||
}`), minica.WithIntermediateTemplate(`{
|
||||
"subject": {{ toJson .Subject }},
|
||||
"keyUsage": ["certSign", "crlSign"],
|
||||
"basicConstraints": {
|
||||
"isCA": true,
|
||||
"maxPathLen": 1
|
||||
}
|
||||
}`))
|
||||
require.NoError(t, err)
|
||||
|
||||
signer, err := keyutil.GenerateDefaultSigner()
|
||||
require.NoError(t, err)
|
||||
|
||||
cert, err := ca.Sign(&x509.Certificate{
|
||||
Subject: pkix.Name{CommonName: "MiniCA Intermediate CA 0"},
|
||||
PublicKey: signer.Public(),
|
||||
BasicConstraintsValid: true,
|
||||
IsCA: true,
|
||||
MaxPathLen: 0,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
type fields struct {
|
||||
intermediateX509Certs []*x509.Certificate
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
want *x509.Certificate
|
||||
wantSlice []*x509.Certificate
|
||||
}{
|
||||
{"ok one", fields{[]*x509.Certificate{ca.Intermediate}}, ca.Intermediate, []*x509.Certificate{ca.Intermediate}},
|
||||
{"ok multiple", fields{[]*x509.Certificate{cert, ca.Intermediate}}, cert, []*x509.Certificate{cert, ca.Intermediate}},
|
||||
{"ok empty", fields{[]*x509.Certificate{}}, nil, []*x509.Certificate{}},
|
||||
{"ok nil", fields{nil}, nil, nil},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
a := &Authority{
|
||||
intermediateX509Certs: tt.fields.intermediateX509Certs,
|
||||
}
|
||||
if got := a.GetIntermediateCertificate(); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("Authority.GetIntermediateCertificate() = %v, want %v", got, tt.want)
|
||||
}
|
||||
if got := a.GetIntermediateCertificates(); !reflect.DeepEqual(got, tt.wantSlice) {
|
||||
t.Errorf("Authority.GetIntermediateCertificates() = %v, want %v", got, tt.wantSlice)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,6 +91,21 @@ func withDefaultASN1DN(def *config.ASN1DN) provisioner.CertificateModifierFunc {
|
||||
}
|
||||
}
|
||||
|
||||
// GetX509Signer returns a [crypto.Signer] implementation using the intermediate
|
||||
// key.
|
||||
//
|
||||
// This method can return a [NotImplementedError] if the CA is configured with a
|
||||
// Certificate Authority Service (CAS) that does not implement the
|
||||
// CertificateAuthoritySigner interface.
|
||||
//
|
||||
// [NotImplementedError]: https://pkg.go.dev/github.com/smallstep/certificates/cas/apiv1#NotImplementedError
|
||||
func (a *Authority) GetX509Signer() (crypto.Signer, error) {
|
||||
if s, ok := a.x509CAService.(casapi.CertificateAuthoritySigner); ok {
|
||||
return s.GetSigner()
|
||||
}
|
||||
return nil, casapi.NotImplementedError{}
|
||||
}
|
||||
|
||||
// Sign creates a signed certificate from a certificate signing request. It
|
||||
// creates a new context.Context, and calls into SignWithContext.
|
||||
//
|
||||
|
||||
@@ -30,6 +30,7 @@ import (
|
||||
"github.com/smallstep/certificates/authority/config"
|
||||
"github.com/smallstep/certificates/authority/policy"
|
||||
"github.com/smallstep/certificates/authority/provisioner"
|
||||
"github.com/smallstep/certificates/cas/apiv1"
|
||||
"github.com/smallstep/certificates/cas/softcas"
|
||||
"github.com/smallstep/certificates/db"
|
||||
"github.com/smallstep/certificates/errs"
|
||||
@@ -240,7 +241,7 @@ func (e *testEnforcer) Enforce(cert *x509.Certificate) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func assertHasPrefix(t *testing.T, s, p string, msg ...interface{}) bool {
|
||||
func assertHasPrefix(t *testing.T, s, p string) bool {
|
||||
t.Helper()
|
||||
return assert.True(t, strings.HasPrefix(s, p), "%q is not a prefix of %q", p, s)
|
||||
}
|
||||
@@ -424,7 +425,7 @@ ZYtQ9Ot36qc=
|
||||
require.NoError(t, err)
|
||||
testAuthority.db = &db.MockAuthDB{
|
||||
MStoreCertificate: func(crt *x509.Certificate) error {
|
||||
assert.Equal(t, crt.Subject.CommonName, "smallstep test")
|
||||
assert.Equal(t, "smallstep test", crt.Subject.CommonName)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
@@ -453,7 +454,7 @@ ZYtQ9Ot36qc=
|
||||
require.NoError(t, err)
|
||||
testAuthority.db = &db.MockAuthDB{
|
||||
MStoreCertificate: func(crt *x509.Certificate) error {
|
||||
assert.Equal(t, crt.Subject.CommonName, "smallstep test")
|
||||
assert.Equal(t, "smallstep test", crt.Subject.CommonName)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
@@ -482,7 +483,7 @@ ZYtQ9Ot36qc=
|
||||
require.NoError(t, err)
|
||||
testAuthority.db = &db.MockAuthDB{
|
||||
MStoreCertificate: func(crt *x509.Certificate) error {
|
||||
assert.Equal(t, crt.Subject.CommonName, "smallstep test")
|
||||
assert.Equal(t, "smallstep test", crt.Subject.CommonName)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
@@ -500,7 +501,7 @@ ZYtQ9Ot36qc=
|
||||
aa := testAuthority(t)
|
||||
aa.db = &db.MockAuthDB{
|
||||
MStoreCertificate: func(crt *x509.Certificate) error {
|
||||
assert.Equal(t, crt.Subject.CommonName, "smallstep test")
|
||||
assert.Equal(t, "smallstep test", crt.Subject.CommonName)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
@@ -525,7 +526,7 @@ ZYtQ9Ot36qc=
|
||||
}))
|
||||
aa.db = &db.MockAuthDB{
|
||||
MStoreCertificate: func(crt *x509.Certificate) error {
|
||||
assert.Equal(t, crt.Subject.CommonName, "smallstep test")
|
||||
assert.Equal(t, "smallstep test", crt.Subject.CommonName)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
@@ -545,7 +546,7 @@ ZYtQ9Ot36qc=
|
||||
aa.db = &db.MockAuthDB{
|
||||
MStoreCertificate: func(crt *x509.Certificate) error {
|
||||
fmt.Println(crt.Subject)
|
||||
assert.Equal(t, crt.Subject.CommonName, "smallstep test")
|
||||
assert.Equal(t, "smallstep test", crt.Subject.CommonName)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
@@ -643,7 +644,7 @@ ZYtQ9Ot36qc=
|
||||
_a := testAuthority(t)
|
||||
_a.db = &db.MockAuthDB{
|
||||
MStoreCertificate: func(crt *x509.Certificate) error {
|
||||
assert.Equal(t, crt.Subject.CommonName, "smallstep test")
|
||||
assert.Equal(t, "smallstep test", crt.Subject.CommonName)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
@@ -677,7 +678,7 @@ ZYtQ9Ot36qc=
|
||||
_a := testAuthority(t)
|
||||
_a.db = &db.MockAuthDB{
|
||||
MStoreCertificate: func(crt *x509.Certificate) error {
|
||||
assert.Equal(t, crt.Subject.CommonName, "smallstep test")
|
||||
assert.Equal(t, "smallstep test", crt.Subject.CommonName)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
@@ -711,7 +712,7 @@ ZYtQ9Ot36qc=
|
||||
require.NoError(t, err)
|
||||
testAuthority.db = &db.MockAuthDB{
|
||||
MStoreCertificate: func(crt *x509.Certificate) error {
|
||||
assert.Equal(t, crt.Subject.CommonName, "smallstep test")
|
||||
assert.Equal(t, "smallstep test", crt.Subject.CommonName)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
@@ -745,7 +746,7 @@ ZYtQ9Ot36qc=
|
||||
require.NoError(t, err)
|
||||
testAuthority.db = &db.MockAuthDB{
|
||||
MStoreCertificate: func(crt *x509.Certificate) error {
|
||||
assert.Equal(t, crt.Subject.CommonName, "smallstep test")
|
||||
assert.Equal(t, "smallstep test", crt.Subject.CommonName)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
@@ -782,7 +783,7 @@ ZYtQ9Ot36qc=
|
||||
_a.config.AuthorityConfig.Template = &ASN1DN{}
|
||||
_a.db = &db.MockAuthDB{
|
||||
MStoreCertificate: func(crt *x509.Certificate) error {
|
||||
assert.Equal(t, crt.Subject, pkix.Name{})
|
||||
assert.Equal(t, pkix.Name{}, crt.Subject)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
@@ -807,8 +808,8 @@ ZYtQ9Ot36qc=
|
||||
aa.config.AuthorityConfig.Template = a.config.AuthorityConfig.Template
|
||||
aa.db = &db.MockAuthDB{
|
||||
MStoreCertificate: func(crt *x509.Certificate) error {
|
||||
assert.Equal(t, crt.Subject.CommonName, "smallstep test")
|
||||
assert.Equal(t, crt.CRLDistributionPoints, []string{"http://ca.example.org/leaf.crl"})
|
||||
assert.Equal(t, "smallstep test", crt.Subject.CommonName)
|
||||
assert.Equal(t, []string{"http://ca.example.org/leaf.crl"}, crt.CRLDistributionPoints)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
@@ -866,8 +867,8 @@ ZYtQ9Ot36qc=
|
||||
}, p.AttestationData())
|
||||
}
|
||||
if assert.Len(t, certs, 2) {
|
||||
assert.Equal(t, certs[0].Subject.CommonName, "smallstep test")
|
||||
assert.Equal(t, certs[1].Subject.CommonName, "smallstep Intermediate CA")
|
||||
assert.Equal(t, "smallstep test", certs[0].Subject.CommonName)
|
||||
assert.Equal(t, "smallstep Intermediate CA", certs[1].Subject.CommonName)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
@@ -935,45 +936,44 @@ ZYtQ9Ot36qc=
|
||||
assert.Nil(t, certChain)
|
||||
var sc render.StatusCodedError
|
||||
require.True(t, errors.As(err, &sc), "error does not implement StatusCodedError interface")
|
||||
assert.Equal(t, sc.StatusCode(), tc.code)
|
||||
assert.Equal(t, tc.code, sc.StatusCode())
|
||||
assertHasPrefix(t, err.Error(), tc.err.Error())
|
||||
|
||||
var ctxErr *errs.Error
|
||||
require.True(t, errors.As(err, &ctxErr), "error is not of type *errs.Error")
|
||||
assert.Equal(t, ctxErr.Details["csr"], tc.csr)
|
||||
assert.Equal(t, ctxErr.Details["signOptions"], tc.signOpts)
|
||||
assert.Equal(t, tc.csr, ctxErr.Details["csr"])
|
||||
assert.Equal(t, tc.signOpts, ctxErr.Details["signOptions"])
|
||||
}
|
||||
} else {
|
||||
leaf := certChain[0]
|
||||
intermediate := certChain[1]
|
||||
if assert.Nil(t, tc.err) {
|
||||
assert.Equal(t, leaf.NotBefore, tc.notBefore)
|
||||
assert.Equal(t, leaf.NotAfter, tc.notAfter)
|
||||
assert.Equal(t, tc.notBefore, leaf.NotBefore)
|
||||
assert.Equal(t, tc.notAfter, leaf.NotAfter)
|
||||
tmplt := a.config.AuthorityConfig.Template
|
||||
if tc.csr.Subject.CommonName == "" {
|
||||
assert.Equal(t, leaf.Subject, pkix.Name{})
|
||||
assert.Equal(t, pkix.Name{}, leaf.Subject)
|
||||
} else {
|
||||
assert.Equal(t, leaf.Subject.String(),
|
||||
pkix.Name{
|
||||
Country: []string{tmplt.Country},
|
||||
Organization: []string{tmplt.Organization},
|
||||
Locality: []string{tmplt.Locality},
|
||||
StreetAddress: []string{tmplt.StreetAddress},
|
||||
Province: []string{tmplt.Province},
|
||||
CommonName: "smallstep test",
|
||||
}.String())
|
||||
assert.Equal(t, leaf.DNSNames, []string{"test.smallstep.com"})
|
||||
assert.Equal(t, pkix.Name{
|
||||
Country: []string{tmplt.Country},
|
||||
Organization: []string{tmplt.Organization},
|
||||
Locality: []string{tmplt.Locality},
|
||||
StreetAddress: []string{tmplt.StreetAddress},
|
||||
Province: []string{tmplt.Province},
|
||||
CommonName: "smallstep test",
|
||||
}.String(), leaf.Subject.String())
|
||||
assert.Equal(t, []string{"test.smallstep.com"}, leaf.DNSNames)
|
||||
}
|
||||
assert.Equal(t, leaf.Issuer, intermediate.Subject)
|
||||
assert.Equal(t, leaf.SignatureAlgorithm, x509.ECDSAWithSHA256)
|
||||
assert.Equal(t, leaf.PublicKeyAlgorithm, x509.ECDSA)
|
||||
assert.Equal(t, leaf.ExtKeyUsage, []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth})
|
||||
assert.Equal(t, intermediate.Subject, leaf.Issuer)
|
||||
assert.Equal(t, x509.ECDSAWithSHA256, leaf.SignatureAlgorithm)
|
||||
assert.Equal(t, x509.ECDSA, leaf.PublicKeyAlgorithm)
|
||||
assert.Equal(t, []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, leaf.ExtKeyUsage)
|
||||
|
||||
issuer := getDefaultIssuer(a)
|
||||
subjectKeyID, err := generateSubjectKeyID(pub)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, leaf.SubjectKeyId, subjectKeyID)
|
||||
assert.Equal(t, leaf.AuthorityKeyId, issuer.SubjectKeyId)
|
||||
assert.Equal(t, subjectKeyID, leaf.SubjectKeyId)
|
||||
assert.Equal(t, issuer.SubjectKeyId, leaf.AuthorityKeyId)
|
||||
|
||||
// Verify Provisioner OID
|
||||
found := 0
|
||||
@@ -984,9 +984,9 @@ ZYtQ9Ot36qc=
|
||||
val := stepProvisionerASN1{}
|
||||
_, err := asn1.Unmarshal(ext.Value, &val)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, val.Type, provisionerTypeJWK)
|
||||
assert.Equal(t, val.Name, []byte(p.Name))
|
||||
assert.Equal(t, val.CredentialID, []byte(p.Key.KeyID))
|
||||
assert.Equal(t, provisionerTypeJWK, val.Type)
|
||||
assert.Equal(t, []byte(p.Name), val.Name)
|
||||
assert.Equal(t, []byte(p.Key.KeyID), val.CredentialID)
|
||||
|
||||
// Basic Constraints
|
||||
case ext.Id.Equal(asn1.ObjectIdentifier([]int{2, 5, 29, 19})):
|
||||
@@ -1008,7 +1008,7 @@ ZYtQ9Ot36qc=
|
||||
assert.Equal(t, found, 1)
|
||||
realIntermediate, err := x509.ParseCertificate(issuer.Raw)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, intermediate, realIntermediate)
|
||||
assert.Equal(t, realIntermediate, intermediate)
|
||||
assert.Len(t, leaf.Extensions, tc.extensionsCount)
|
||||
}
|
||||
}
|
||||
@@ -1152,18 +1152,18 @@ func TestAuthority_Renew(t *testing.T) {
|
||||
assert.Nil(t, certChain)
|
||||
var sc render.StatusCodedError
|
||||
require.True(t, errors.As(err, &sc), "error does not implement StatusCodedError interface")
|
||||
assert.Equal(t, sc.StatusCode(), tc.code)
|
||||
assert.Equal(t, tc.code, sc.StatusCode())
|
||||
assertHasPrefix(t, err.Error(), tc.err.Error())
|
||||
|
||||
var ctxErr *errs.Error
|
||||
require.True(t, errors.As(err, &ctxErr), "error is not of type *errs.Error")
|
||||
assert.Equal(t, ctxErr.Details["serialNumber"], tc.cert.SerialNumber.String())
|
||||
assert.Equal(t, tc.cert.SerialNumber.String(), ctxErr.Details["serialNumber"])
|
||||
}
|
||||
} else {
|
||||
leaf := certChain[0]
|
||||
intermediate := certChain[1]
|
||||
if assert.Nil(t, tc.err) {
|
||||
assert.Equal(t, leaf.NotAfter.Sub(leaf.NotBefore), tc.cert.NotAfter.Sub(cert.NotBefore))
|
||||
assert.Equal(t, tc.cert.NotAfter.Sub(cert.NotBefore), leaf.NotAfter.Sub(leaf.NotBefore))
|
||||
|
||||
assert.True(t, leaf.NotBefore.After(now.Add(-2*time.Minute)))
|
||||
assert.True(t, leaf.NotBefore.Before(now.Add(time.Minute)))
|
||||
@@ -1173,30 +1173,29 @@ func TestAuthority_Renew(t *testing.T) {
|
||||
assert.True(t, leaf.NotAfter.Before(expiry.Add(time.Hour)))
|
||||
|
||||
tmplt := a.config.AuthorityConfig.Template
|
||||
assert.Equal(t, leaf.RawSubject, tc.cert.RawSubject)
|
||||
assert.Equal(t, leaf.Subject.Country, []string{tmplt.Country})
|
||||
assert.Equal(t, leaf.Subject.Organization, []string{tmplt.Organization})
|
||||
assert.Equal(t, leaf.Subject.Locality, []string{tmplt.Locality})
|
||||
assert.Equal(t, leaf.Subject.StreetAddress, []string{tmplt.StreetAddress})
|
||||
assert.Equal(t, leaf.Subject.Province, []string{tmplt.Province})
|
||||
assert.Equal(t, leaf.Subject.CommonName, tmplt.CommonName)
|
||||
assert.Equal(t, tc.cert.RawSubject, leaf.RawSubject)
|
||||
assert.Equal(t, []string{tmplt.Country}, leaf.Subject.Country)
|
||||
assert.Equal(t, []string{tmplt.Organization}, leaf.Subject.Organization)
|
||||
assert.Equal(t, []string{tmplt.Locality}, leaf.Subject.Locality)
|
||||
assert.Equal(t, []string{tmplt.StreetAddress}, leaf.Subject.StreetAddress)
|
||||
assert.Equal(t, []string{tmplt.Province}, leaf.Subject.Province)
|
||||
assert.Equal(t, tmplt.CommonName, leaf.Subject.CommonName)
|
||||
|
||||
assert.Equal(t, leaf.Issuer, intermediate.Subject)
|
||||
assert.Equal(t, intermediate.Subject, leaf.Issuer)
|
||||
|
||||
assert.Equal(t, leaf.SignatureAlgorithm, x509.ECDSAWithSHA256)
|
||||
assert.Equal(t, leaf.PublicKeyAlgorithm, x509.ECDSA)
|
||||
assert.Equal(t, leaf.ExtKeyUsage,
|
||||
[]x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth})
|
||||
assert.Equal(t, leaf.DNSNames, []string{"test.smallstep.com", "test"})
|
||||
assert.Equal(t, x509.ECDSAWithSHA256, leaf.SignatureAlgorithm)
|
||||
assert.Equal(t, x509.ECDSA, leaf.PublicKeyAlgorithm)
|
||||
assert.Equal(t, []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, leaf.ExtKeyUsage)
|
||||
assert.Equal(t, []string{"test.smallstep.com", "test"}, leaf.DNSNames)
|
||||
|
||||
subjectKeyID, err := generateSubjectKeyID(leaf.PublicKey)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, leaf.SubjectKeyId, subjectKeyID)
|
||||
assert.Equal(t, subjectKeyID, leaf.SubjectKeyId)
|
||||
|
||||
// We did not change the intermediate before renewing.
|
||||
authIssuer := getDefaultIssuer(tc.auth)
|
||||
if issuer.SerialNumber == authIssuer.SerialNumber {
|
||||
assert.Equal(t, leaf.AuthorityKeyId, issuer.SubjectKeyId)
|
||||
assert.Equal(t, issuer.SubjectKeyId, leaf.AuthorityKeyId)
|
||||
// Compare extensions: they can be in a different order
|
||||
for _, ext1 := range tc.cert.Extensions {
|
||||
//skip SubjectKeyIdentifier
|
||||
@@ -1216,7 +1215,7 @@ func TestAuthority_Renew(t *testing.T) {
|
||||
}
|
||||
} else {
|
||||
// We did change the intermediate before renewing.
|
||||
assert.Equal(t, leaf.AuthorityKeyId, authIssuer.SubjectKeyId)
|
||||
assert.Equal(t, authIssuer.SubjectKeyId, leaf.AuthorityKeyId)
|
||||
// Compare extensions: they can be in a different order
|
||||
for _, ext1 := range tc.cert.Extensions {
|
||||
//skip SubjectKeyIdentifier
|
||||
@@ -1245,7 +1244,7 @@ func TestAuthority_Renew(t *testing.T) {
|
||||
|
||||
realIntermediate, err := x509.ParseCertificate(authIssuer.Raw)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, intermediate, realIntermediate)
|
||||
assert.Equal(t, realIntermediate, intermediate)
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -1357,18 +1356,18 @@ func TestAuthority_Rekey(t *testing.T) {
|
||||
assert.Nil(t, certChain)
|
||||
var sc render.StatusCodedError
|
||||
require.True(t, errors.As(err, &sc), "error does not implement StatusCodedError interface")
|
||||
assert.Equal(t, sc.StatusCode(), tc.code)
|
||||
assert.Equal(t, tc.code, sc.StatusCode())
|
||||
assertHasPrefix(t, err.Error(), tc.err.Error())
|
||||
|
||||
var ctxErr *errs.Error
|
||||
require.True(t, errors.As(err, &ctxErr), "error is not of type *errs.Error")
|
||||
assert.Equal(t, ctxErr.Details["serialNumber"], tc.cert.SerialNumber.String())
|
||||
assert.Equal(t, tc.cert.SerialNumber.String(), ctxErr.Details["serialNumber"])
|
||||
}
|
||||
} else {
|
||||
leaf := certChain[0]
|
||||
intermediate := certChain[1]
|
||||
if assert.Nil(t, tc.err) {
|
||||
assert.Equal(t, leaf.NotAfter.Sub(leaf.NotBefore), tc.cert.NotAfter.Sub(cert.NotBefore))
|
||||
assert.Equal(t, tc.cert.NotAfter.Sub(cert.NotBefore), leaf.NotAfter.Sub(leaf.NotBefore))
|
||||
|
||||
assert.True(t, leaf.NotBefore.After(now.Add(-2*time.Minute)))
|
||||
assert.True(t, leaf.NotBefore.Before(now.Add(time.Minute)))
|
||||
@@ -1378,41 +1377,39 @@ func TestAuthority_Rekey(t *testing.T) {
|
||||
assert.True(t, leaf.NotAfter.Before(expiry.Add(time.Hour)))
|
||||
|
||||
tmplt := a.config.AuthorityConfig.Template
|
||||
assert.Equal(t, leaf.Subject.String(),
|
||||
pkix.Name{
|
||||
Country: []string{tmplt.Country},
|
||||
Organization: []string{tmplt.Organization},
|
||||
Locality: []string{tmplt.Locality},
|
||||
StreetAddress: []string{tmplt.StreetAddress},
|
||||
Province: []string{tmplt.Province},
|
||||
CommonName: tmplt.CommonName,
|
||||
}.String())
|
||||
assert.Equal(t, leaf.Issuer, intermediate.Subject)
|
||||
assert.Equal(t, pkix.Name{
|
||||
Country: []string{tmplt.Country},
|
||||
Organization: []string{tmplt.Organization},
|
||||
Locality: []string{tmplt.Locality},
|
||||
StreetAddress: []string{tmplt.StreetAddress},
|
||||
Province: []string{tmplt.Province},
|
||||
CommonName: tmplt.CommonName,
|
||||
}.String(), leaf.Subject.String())
|
||||
assert.Equal(t, intermediate.Subject, leaf.Issuer)
|
||||
|
||||
assert.Equal(t, leaf.SignatureAlgorithm, x509.ECDSAWithSHA256)
|
||||
assert.Equal(t, leaf.PublicKeyAlgorithm, x509.ECDSA)
|
||||
assert.Equal(t, leaf.ExtKeyUsage,
|
||||
[]x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth})
|
||||
assert.Equal(t, leaf.DNSNames, []string{"test.smallstep.com", "test"})
|
||||
assert.Equal(t, x509.ECDSAWithSHA256, leaf.SignatureAlgorithm)
|
||||
assert.Equal(t, x509.ECDSA, leaf.PublicKeyAlgorithm)
|
||||
assert.Equal(t, []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, leaf.ExtKeyUsage)
|
||||
assert.Equal(t, []string{"test.smallstep.com", "test"}, leaf.DNSNames)
|
||||
|
||||
// Test Public Key and SubjectKeyId
|
||||
expectedPK := tc.pk
|
||||
if tc.pk == nil {
|
||||
expectedPK = cert.PublicKey
|
||||
}
|
||||
assert.Equal(t, leaf.PublicKey, expectedPK)
|
||||
assert.Equal(t, expectedPK, leaf.PublicKey)
|
||||
|
||||
subjectKeyID, err := generateSubjectKeyID(expectedPK)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, leaf.SubjectKeyId, subjectKeyID)
|
||||
assert.Equal(t, subjectKeyID, leaf.SubjectKeyId)
|
||||
if tc.pk == nil {
|
||||
assert.Equal(t, leaf.SubjectKeyId, cert.SubjectKeyId)
|
||||
assert.Equal(t, cert.SubjectKeyId, leaf.SubjectKeyId)
|
||||
}
|
||||
|
||||
// We did not change the intermediate before renewing.
|
||||
authIssuer := getDefaultIssuer(tc.auth)
|
||||
if issuer.SerialNumber == authIssuer.SerialNumber {
|
||||
assert.Equal(t, leaf.AuthorityKeyId, issuer.SubjectKeyId)
|
||||
assert.Equal(t, issuer.SubjectKeyId, leaf.AuthorityKeyId)
|
||||
// Compare extensions: they can be in a different order
|
||||
for _, ext1 := range tc.cert.Extensions {
|
||||
//skip SubjectKeyIdentifier
|
||||
@@ -1432,7 +1429,7 @@ func TestAuthority_Rekey(t *testing.T) {
|
||||
}
|
||||
} else {
|
||||
// We did change the intermediate before renewing.
|
||||
assert.Equal(t, leaf.AuthorityKeyId, authIssuer.SubjectKeyId)
|
||||
assert.Equal(t, authIssuer.SubjectKeyId, leaf.AuthorityKeyId)
|
||||
// Compare extensions: they can be in a different order
|
||||
for _, ext1 := range tc.cert.Extensions {
|
||||
//skip SubjectKeyIdentifier
|
||||
@@ -1461,7 +1458,7 @@ func TestAuthority_Rekey(t *testing.T) {
|
||||
|
||||
realIntermediate, err := x509.ParseCertificate(authIssuer.Raw)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, intermediate, realIntermediate)
|
||||
assert.Equal(t, realIntermediate, intermediate)
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -1499,7 +1496,7 @@ func TestAuthority_GetTLSOptions(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
opts := tc.auth.GetTLSOptions()
|
||||
assert.Equal(t, opts, tc.opts)
|
||||
assert.Equal(t, tc.opts, opts)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1569,9 +1566,9 @@ func TestAuthority_Revoke(t *testing.T) {
|
||||
err: errors.New("authority.Revoke; no persistence layer configured"),
|
||||
code: http.StatusNotImplemented,
|
||||
checkErrDetails: func(err *errs.Error) {
|
||||
assert.Equal(t, err.Details["token"], raw)
|
||||
assert.Equal(t, err.Details["tokenID"], "44")
|
||||
assert.Equal(t, err.Details["provisionerID"], "step-cli:4UELJx8e0aS9m0CH3fZ0EB7D5aUPICb759zALHFejvc")
|
||||
assert.Equal(t, raw, err.Details["token"])
|
||||
assert.Equal(t, "44", err.Details["tokenID"])
|
||||
assert.Equal(t, "step-cli:4UELJx8e0aS9m0CH3fZ0EB7D5aUPICb759zALHFejvc", err.Details["provisionerID"])
|
||||
},
|
||||
}
|
||||
},
|
||||
@@ -1609,9 +1606,9 @@ func TestAuthority_Revoke(t *testing.T) {
|
||||
err: errors.New("authority.Revoke: force"),
|
||||
code: http.StatusInternalServerError,
|
||||
checkErrDetails: func(err *errs.Error) {
|
||||
assert.Equal(t, err.Details["token"], raw)
|
||||
assert.Equal(t, err.Details["tokenID"], "44")
|
||||
assert.Equal(t, err.Details["provisionerID"], "step-cli:4UELJx8e0aS9m0CH3fZ0EB7D5aUPICb759zALHFejvc")
|
||||
assert.Equal(t, raw, err.Details["token"])
|
||||
assert.Equal(t, "44", err.Details["tokenID"])
|
||||
assert.Equal(t, "step-cli:4UELJx8e0aS9m0CH3fZ0EB7D5aUPICb759zALHFejvc", err.Details["provisionerID"])
|
||||
},
|
||||
}
|
||||
},
|
||||
@@ -1649,9 +1646,9 @@ func TestAuthority_Revoke(t *testing.T) {
|
||||
err: errors.New("certificate with serial number 'sn' is already revoked"),
|
||||
code: http.StatusBadRequest,
|
||||
checkErrDetails: func(err *errs.Error) {
|
||||
assert.Equal(t, err.Details["token"], raw)
|
||||
assert.Equal(t, err.Details["tokenID"], "44")
|
||||
assert.Equal(t, err.Details["provisionerID"], "step-cli:4UELJx8e0aS9m0CH3fZ0EB7D5aUPICb759zALHFejvc")
|
||||
assert.Equal(t, raw, err.Details["token"])
|
||||
assert.Equal(t, "44", err.Details["tokenID"])
|
||||
assert.Equal(t, "step-cli:4UELJx8e0aS9m0CH3fZ0EB7D5aUPICb759zALHFejvc", err.Details["provisionerID"])
|
||||
},
|
||||
}
|
||||
},
|
||||
@@ -1786,16 +1783,16 @@ func TestAuthority_Revoke(t *testing.T) {
|
||||
if assert.NotNil(t, tc.err, fmt.Sprintf("unexpected error: %s", err)) {
|
||||
var sc render.StatusCodedError
|
||||
require.True(t, errors.As(err, &sc), "error does not implement StatusCodedError interface")
|
||||
assert.Equal(t, sc.StatusCode(), tc.code)
|
||||
assert.Equal(t, tc.code, sc.StatusCode())
|
||||
assertHasPrefix(t, err.Error(), tc.err.Error())
|
||||
|
||||
var ctxErr *errs.Error
|
||||
require.True(t, errors.As(err, &ctxErr), "error is not of type *errs.Error")
|
||||
assert.Equal(t, ctxErr.Details["serialNumber"], tc.opts.Serial)
|
||||
assert.Equal(t, ctxErr.Details["reasonCode"], tc.opts.ReasonCode)
|
||||
assert.Equal(t, ctxErr.Details["reason"], tc.opts.Reason)
|
||||
assert.Equal(t, ctxErr.Details["MTLS"], tc.opts.MTLS)
|
||||
assert.Equal(t, ctxErr.Details["context"], provisioner.RevokeMethod.String())
|
||||
assert.Equal(t, tc.opts.Serial, ctxErr.Details["serialNumber"])
|
||||
assert.Equal(t, tc.opts.ReasonCode, ctxErr.Details["reasonCode"])
|
||||
assert.Equal(t, tc.opts.Reason, ctxErr.Details["reason"])
|
||||
assert.Equal(t, tc.opts.MTLS, ctxErr.Details["MTLS"])
|
||||
assert.Equal(t, provisioner.RevokeMethod.String(), ctxErr.Details["context"])
|
||||
|
||||
if tc.checkErrDetails != nil {
|
||||
tc.checkErrDetails(ctxErr)
|
||||
@@ -2033,3 +2030,39 @@ func TestAuthority_CRL(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type notImplementedCAS struct{}
|
||||
|
||||
func (notImplementedCAS) CreateCertificate(req *apiv1.CreateCertificateRequest) (*apiv1.CreateCertificateResponse, error) {
|
||||
return nil, apiv1.NotImplementedError{}
|
||||
}
|
||||
func (notImplementedCAS) RenewCertificate(req *apiv1.RenewCertificateRequest) (*apiv1.RenewCertificateResponse, error) {
|
||||
return nil, apiv1.NotImplementedError{}
|
||||
}
|
||||
func (notImplementedCAS) RevokeCertificate(req *apiv1.RevokeCertificateRequest) (*apiv1.RevokeCertificateResponse, error) {
|
||||
return nil, apiv1.NotImplementedError{}
|
||||
}
|
||||
|
||||
func TestAuthority_GetX509Signer(t *testing.T) {
|
||||
auth := testAuthority(t)
|
||||
require.IsType(t, &softcas.SoftCAS{}, auth.x509CAService)
|
||||
signer := auth.x509CAService.(*softcas.SoftCAS).Signer
|
||||
require.NotNil(t, signer)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
authority *Authority
|
||||
want crypto.Signer
|
||||
assertion assert.ErrorAssertionFunc
|
||||
}{
|
||||
{"ok", auth, signer, assert.NoError},
|
||||
{"fail", testAuthority(t, WithX509CAService(notImplementedCAS{})), nil, assert.Error},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := tt.authority.GetX509Signer()
|
||||
tt.assertion(t, err)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,19 +108,19 @@ func TestNewACMEClient(t *testing.T) {
|
||||
tc := run(t)
|
||||
|
||||
i := 0
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
assert.Equals(t, "step-http-client/1.0", req.Header.Get("User-Agent")) // check default User-Agent header
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equals(t, "step-http-client/1.0", r.Header.Get("User-Agent")) // check default User-Agent header
|
||||
switch {
|
||||
case i == 0:
|
||||
render.JSONStatus(w, tc.r1, tc.rc1)
|
||||
render.JSONStatus(w, r, tc.r1, tc.rc1)
|
||||
i++
|
||||
case i == 1:
|
||||
w.Header().Set("Replay-Nonce", "abc123")
|
||||
render.JSONStatus(w, []byte{}, 200)
|
||||
render.JSONStatus(w, r, []byte{}, 200)
|
||||
i++
|
||||
default:
|
||||
w.Header().Set("Location", accLocation)
|
||||
render.JSONStatus(w, tc.r2, tc.rc2)
|
||||
render.JSONStatus(w, r, tc.r2, tc.rc2)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -203,10 +203,10 @@ func TestACMEClient_GetNonce(t *testing.T) {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
tc := run(t)
|
||||
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
assert.Equals(t, "step-http-client/1.0", req.Header.Get("User-Agent")) // check default User-Agent header
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equals(t, "step-http-client/1.0", r.Header.Get("User-Agent")) // check default User-Agent header
|
||||
w.Header().Set("Replay-Nonce", expectedNonce)
|
||||
render.JSONStatus(w, tc.r1, tc.rc1)
|
||||
render.JSONStatus(w, r, tc.r1, tc.rc1)
|
||||
})
|
||||
|
||||
if nonce, err := ac.GetNonce(); err != nil {
|
||||
@@ -310,18 +310,18 @@ func TestACMEClient_post(t *testing.T) {
|
||||
tc := run(t)
|
||||
|
||||
i := 0
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
assert.Equals(t, "step-http-client/1.0", req.Header.Get("User-Agent")) // check default User-Agent header
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equals(t, "step-http-client/1.0", r.Header.Get("User-Agent")) // check default User-Agent header
|
||||
|
||||
w.Header().Set("Replay-Nonce", expectedNonce)
|
||||
if i == 0 {
|
||||
render.JSONStatus(w, tc.r1, tc.rc1)
|
||||
render.JSONStatus(w, r, tc.r1, tc.rc1)
|
||||
i++
|
||||
return
|
||||
}
|
||||
|
||||
// validate jws request protected headers and body
|
||||
body, err := io.ReadAll(req.Body)
|
||||
body, err := io.ReadAll(r.Body)
|
||||
assert.FatalError(t, err)
|
||||
jws, err := jose.ParseJWS(string(body))
|
||||
assert.FatalError(t, err)
|
||||
@@ -338,7 +338,7 @@ func TestACMEClient_post(t *testing.T) {
|
||||
assert.Equals(t, hdr.KeyID, ac.kid)
|
||||
}
|
||||
|
||||
render.JSONStatus(w, tc.r2, tc.rc2)
|
||||
render.JSONStatus(w, r, tc.r2, tc.rc2)
|
||||
})
|
||||
|
||||
if resp, err := tc.client.post(tc.payload, url, tc.ops...); err != nil {
|
||||
@@ -450,18 +450,18 @@ func TestACMEClient_NewOrder(t *testing.T) {
|
||||
tc := run(t)
|
||||
|
||||
i := 0
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
assert.Equals(t, "step-http-client/1.0", req.Header.Get("User-Agent")) // check default User-Agent header
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equals(t, "step-http-client/1.0", r.Header.Get("User-Agent")) // check default User-Agent header
|
||||
|
||||
w.Header().Set("Replay-Nonce", expectedNonce)
|
||||
if i == 0 {
|
||||
render.JSONStatus(w, tc.r1, tc.rc1)
|
||||
render.JSONStatus(w, r, tc.r1, tc.rc1)
|
||||
i++
|
||||
return
|
||||
}
|
||||
|
||||
// validate jws request protected headers and body
|
||||
body, err := io.ReadAll(req.Body)
|
||||
body, err := io.ReadAll(r.Body)
|
||||
assert.FatalError(t, err)
|
||||
jws, err := jose.ParseJWS(string(body))
|
||||
assert.FatalError(t, err)
|
||||
@@ -477,7 +477,7 @@ func TestACMEClient_NewOrder(t *testing.T) {
|
||||
assert.FatalError(t, err)
|
||||
assert.Equals(t, payload, norb)
|
||||
|
||||
render.JSONStatus(w, tc.r2, tc.rc2)
|
||||
render.JSONStatus(w, r, tc.r2, tc.rc2)
|
||||
})
|
||||
|
||||
if res, err := ac.NewOrder(norb); err != nil {
|
||||
@@ -572,18 +572,18 @@ func TestACMEClient_GetOrder(t *testing.T) {
|
||||
tc := run(t)
|
||||
|
||||
i := 0
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
assert.Equals(t, "step-http-client/1.0", req.Header.Get("User-Agent")) // check default User-Agent header
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equals(t, "step-http-client/1.0", r.Header.Get("User-Agent")) // check default User-Agent header
|
||||
|
||||
w.Header().Set("Replay-Nonce", expectedNonce)
|
||||
if i == 0 {
|
||||
render.JSONStatus(w, tc.r1, tc.rc1)
|
||||
render.JSONStatus(w, r, tc.r1, tc.rc1)
|
||||
i++
|
||||
return
|
||||
}
|
||||
|
||||
// validate jws request protected headers and body
|
||||
body, err := io.ReadAll(req.Body)
|
||||
body, err := io.ReadAll(r.Body)
|
||||
assert.FatalError(t, err)
|
||||
jws, err := jose.ParseJWS(string(body))
|
||||
assert.FatalError(t, err)
|
||||
@@ -599,7 +599,7 @@ func TestACMEClient_GetOrder(t *testing.T) {
|
||||
assert.FatalError(t, err)
|
||||
assert.Equals(t, len(payload), 0)
|
||||
|
||||
render.JSONStatus(w, tc.r2, tc.rc2)
|
||||
render.JSONStatus(w, r, tc.r2, tc.rc2)
|
||||
})
|
||||
|
||||
if res, err := ac.GetOrder(url); err != nil {
|
||||
@@ -694,18 +694,18 @@ func TestACMEClient_GetAuthz(t *testing.T) {
|
||||
tc := run(t)
|
||||
|
||||
i := 0
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
assert.Equals(t, "step-http-client/1.0", req.Header.Get("User-Agent")) // check default User-Agent header
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equals(t, "step-http-client/1.0", r.Header.Get("User-Agent")) // check default User-Agent header
|
||||
|
||||
w.Header().Set("Replay-Nonce", expectedNonce)
|
||||
if i == 0 {
|
||||
render.JSONStatus(w, tc.r1, tc.rc1)
|
||||
render.JSONStatus(w, r, tc.r1, tc.rc1)
|
||||
i++
|
||||
return
|
||||
}
|
||||
|
||||
// validate jws request protected headers and body
|
||||
body, err := io.ReadAll(req.Body)
|
||||
body, err := io.ReadAll(r.Body)
|
||||
assert.FatalError(t, err)
|
||||
jws, err := jose.ParseJWS(string(body))
|
||||
assert.FatalError(t, err)
|
||||
@@ -721,7 +721,7 @@ func TestACMEClient_GetAuthz(t *testing.T) {
|
||||
assert.FatalError(t, err)
|
||||
assert.Equals(t, len(payload), 0)
|
||||
|
||||
render.JSONStatus(w, tc.r2, tc.rc2)
|
||||
render.JSONStatus(w, r, tc.r2, tc.rc2)
|
||||
})
|
||||
|
||||
if res, err := ac.GetAuthz(url); err != nil {
|
||||
@@ -816,18 +816,18 @@ func TestACMEClient_GetChallenge(t *testing.T) {
|
||||
tc := run(t)
|
||||
|
||||
i := 0
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
assert.Equals(t, "step-http-client/1.0", req.Header.Get("User-Agent")) // check default User-Agent header
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equals(t, "step-http-client/1.0", r.Header.Get("User-Agent")) // check default User-Agent header
|
||||
|
||||
w.Header().Set("Replay-Nonce", expectedNonce)
|
||||
if i == 0 {
|
||||
render.JSONStatus(w, tc.r1, tc.rc1)
|
||||
render.JSONStatus(w, r, tc.r1, tc.rc1)
|
||||
i++
|
||||
return
|
||||
}
|
||||
|
||||
// validate jws request protected headers and body
|
||||
body, err := io.ReadAll(req.Body)
|
||||
body, err := io.ReadAll(r.Body)
|
||||
assert.FatalError(t, err)
|
||||
jws, err := jose.ParseJWS(string(body))
|
||||
assert.FatalError(t, err)
|
||||
@@ -844,7 +844,7 @@ func TestACMEClient_GetChallenge(t *testing.T) {
|
||||
|
||||
assert.Equals(t, len(payload), 0)
|
||||
|
||||
render.JSONStatus(w, tc.r2, tc.rc2)
|
||||
render.JSONStatus(w, r, tc.r2, tc.rc2)
|
||||
})
|
||||
|
||||
if res, err := ac.GetChallenge(url); err != nil {
|
||||
@@ -939,18 +939,18 @@ func TestACMEClient_ValidateChallenge(t *testing.T) {
|
||||
tc := run(t)
|
||||
|
||||
i := 0
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
assert.Equals(t, "step-http-client/1.0", req.Header.Get("User-Agent")) // check default User-Agent header
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equals(t, "step-http-client/1.0", r.Header.Get("User-Agent")) // check default User-Agent header
|
||||
|
||||
w.Header().Set("Replay-Nonce", expectedNonce)
|
||||
if i == 0 {
|
||||
render.JSONStatus(w, tc.r1, tc.rc1)
|
||||
render.JSONStatus(w, r, tc.r1, tc.rc1)
|
||||
i++
|
||||
return
|
||||
}
|
||||
|
||||
// validate jws request protected headers and body
|
||||
body, err := io.ReadAll(req.Body)
|
||||
body, err := io.ReadAll(r.Body)
|
||||
assert.FatalError(t, err)
|
||||
jws, err := jose.ParseJWS(string(body))
|
||||
assert.FatalError(t, err)
|
||||
@@ -967,7 +967,7 @@ func TestACMEClient_ValidateChallenge(t *testing.T) {
|
||||
|
||||
assert.Equals(t, payload, []byte("{}"))
|
||||
|
||||
render.JSONStatus(w, tc.r2, tc.rc2)
|
||||
render.JSONStatus(w, r, tc.r2, tc.rc2)
|
||||
})
|
||||
|
||||
if err := ac.ValidateChallenge(url); err != nil {
|
||||
@@ -983,22 +983,22 @@ func TestACMEClient_ValidateWithPayload(t *testing.T) {
|
||||
key, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
|
||||
assert.FatalError(t, err)
|
||||
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
assert.Equals(t, "step-http-client/1.0", req.Header.Get("User-Agent")) // check default User-Agent header
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equals(t, "step-http-client/1.0", r.Header.Get("User-Agent")) // check default User-Agent header
|
||||
|
||||
t.Log(req.RequestURI)
|
||||
t.Log(r.RequestURI)
|
||||
w.Header().Set("Replay-Nonce", "nonce")
|
||||
switch req.RequestURI {
|
||||
switch r.RequestURI {
|
||||
case "/nonce":
|
||||
render.JSONStatus(w, []byte{}, 200)
|
||||
render.JSONStatus(w, r, []byte{}, 200)
|
||||
return
|
||||
case "/fail-nonce":
|
||||
render.JSONStatus(w, acme.NewError(acme.ErrorMalformedType, "malformed request"), 400)
|
||||
render.JSONStatus(w, r, acme.NewError(acme.ErrorMalformedType, "malformed request"), 400)
|
||||
return
|
||||
}
|
||||
|
||||
// validate jws request protected headers and body
|
||||
body, err := io.ReadAll(req.Body)
|
||||
body, err := io.ReadAll(r.Body)
|
||||
assert.FatalError(t, err)
|
||||
|
||||
jws, err := jose.ParseJWS(string(body))
|
||||
@@ -1015,15 +1015,15 @@ func TestACMEClient_ValidateWithPayload(t *testing.T) {
|
||||
assert.FatalError(t, err)
|
||||
assert.Equals(t, payload, []byte("the-payload"))
|
||||
|
||||
switch req.RequestURI {
|
||||
switch r.RequestURI {
|
||||
case "/ok":
|
||||
render.JSONStatus(w, acme.Challenge{
|
||||
render.JSONStatus(w, r, acme.Challenge{
|
||||
Type: "device-attestation-01",
|
||||
Status: "valid",
|
||||
Token: "foo",
|
||||
}, 200)
|
||||
case "/fail":
|
||||
render.JSONStatus(w, acme.NewError(acme.ErrorMalformedType, "malformed request"), 400)
|
||||
render.JSONStatus(w, r, acme.NewError(acme.ErrorMalformedType, "malformed request"), 400)
|
||||
}
|
||||
}))
|
||||
defer srv.Close()
|
||||
@@ -1160,18 +1160,18 @@ func TestACMEClient_FinalizeOrder(t *testing.T) {
|
||||
tc := run(t)
|
||||
|
||||
i := 0
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
assert.Equals(t, "step-http-client/1.0", req.Header.Get("User-Agent")) // check default User-Agent header
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equals(t, "step-http-client/1.0", r.Header.Get("User-Agent")) // check default User-Agent header
|
||||
|
||||
w.Header().Set("Replay-Nonce", expectedNonce)
|
||||
if i == 0 {
|
||||
render.JSONStatus(w, tc.r1, tc.rc1)
|
||||
render.JSONStatus(w, r, tc.r1, tc.rc1)
|
||||
i++
|
||||
return
|
||||
}
|
||||
|
||||
// validate jws request protected headers and body
|
||||
body, err := io.ReadAll(req.Body)
|
||||
body, err := io.ReadAll(r.Body)
|
||||
assert.FatalError(t, err)
|
||||
jws, err := jose.ParseJWS(string(body))
|
||||
assert.FatalError(t, err)
|
||||
@@ -1187,7 +1187,7 @@ func TestACMEClient_FinalizeOrder(t *testing.T) {
|
||||
assert.FatalError(t, err)
|
||||
assert.Equals(t, payload, frb)
|
||||
|
||||
render.JSONStatus(w, tc.r2, tc.rc2)
|
||||
render.JSONStatus(w, r, tc.r2, tc.rc2)
|
||||
})
|
||||
|
||||
if err := ac.FinalizeOrder(url, csr); err != nil {
|
||||
@@ -1289,18 +1289,18 @@ func TestACMEClient_GetAccountOrders(t *testing.T) {
|
||||
tc := run(t)
|
||||
|
||||
i := 0
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
assert.Equals(t, "step-http-client/1.0", req.Header.Get("User-Agent")) // check default User-Agent header
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equals(t, "step-http-client/1.0", r.Header.Get("User-Agent")) // check default User-Agent header
|
||||
|
||||
w.Header().Set("Replay-Nonce", expectedNonce)
|
||||
if i == 0 {
|
||||
render.JSONStatus(w, tc.r1, tc.rc1)
|
||||
render.JSONStatus(w, r, tc.r1, tc.rc1)
|
||||
i++
|
||||
return
|
||||
}
|
||||
|
||||
// validate jws request protected headers and body
|
||||
body, err := io.ReadAll(req.Body)
|
||||
body, err := io.ReadAll(r.Body)
|
||||
assert.FatalError(t, err)
|
||||
jws, err := jose.ParseJWS(string(body))
|
||||
assert.FatalError(t, err)
|
||||
@@ -1316,7 +1316,7 @@ func TestACMEClient_GetAccountOrders(t *testing.T) {
|
||||
assert.FatalError(t, err)
|
||||
assert.Equals(t, len(payload), 0)
|
||||
|
||||
render.JSONStatus(w, tc.r2, tc.rc2)
|
||||
render.JSONStatus(w, r, tc.r2, tc.rc2)
|
||||
})
|
||||
|
||||
if res, err := tc.client.GetAccountOrders(); err != nil {
|
||||
@@ -1420,18 +1420,18 @@ func TestACMEClient_GetCertificate(t *testing.T) {
|
||||
tc := run(t)
|
||||
|
||||
i := 0
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
assert.Equals(t, "step-http-client/1.0", req.Header.Get("User-Agent")) // check default User-Agent header
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equals(t, "step-http-client/1.0", r.Header.Get("User-Agent")) // check default User-Agent header
|
||||
|
||||
w.Header().Set("Replay-Nonce", expectedNonce)
|
||||
if i == 0 {
|
||||
render.JSONStatus(w, tc.r1, tc.rc1)
|
||||
render.JSONStatus(w, r, tc.r1, tc.rc1)
|
||||
i++
|
||||
return
|
||||
}
|
||||
|
||||
// validate jws request protected headers and body
|
||||
body, err := io.ReadAll(req.Body)
|
||||
body, err := io.ReadAll(r.Body)
|
||||
assert.FatalError(t, err)
|
||||
jws, err := jose.ParseJWS(string(body))
|
||||
assert.FatalError(t, err)
|
||||
@@ -1450,7 +1450,7 @@ func TestACMEClient_GetCertificate(t *testing.T) {
|
||||
if tc.certBytes != nil {
|
||||
w.Write(tc.certBytes)
|
||||
} else {
|
||||
render.JSONStatus(w, tc.r2, tc.rc2)
|
||||
render.JSONStatus(w, r, tc.r2, tc.rc2)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ func startCAServer(configFile string) (*CA, string, error) {
|
||||
func mTLSMiddleware(next http.Handler, nonAuthenticatedPaths ...string) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == "/version" {
|
||||
render.JSON(w, api.VersionResponse{
|
||||
render.JSON(w, r, api.VersionResponse{
|
||||
Version: "test",
|
||||
RequireClientAuthentication: true,
|
||||
})
|
||||
@@ -102,7 +102,7 @@ func mTLSMiddleware(next http.Handler, nonAuthenticatedPaths ...string) http.Han
|
||||
}
|
||||
isMTLS := r.TLS != nil && len(r.TLS.PeerCertificates) > 0
|
||||
if !isMTLS {
|
||||
render.Error(w, errs.Unauthorized("missing peer certificate"))
|
||||
render.Error(w, r, errs.Unauthorized("missing peer certificate"))
|
||||
} else {
|
||||
next.ServeHTTP(w, r)
|
||||
}
|
||||
@@ -412,7 +412,7 @@ func TestBootstrapClientServerRotation(t *testing.T) {
|
||||
//nolint:gosec // insecure test server
|
||||
server, err := BootstrapServer(context.Background(), token, &http.Server{
|
||||
Addr: ":0",
|
||||
Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("ok"))
|
||||
}),
|
||||
}, RequireAndVerifyClientCert())
|
||||
@@ -531,7 +531,7 @@ func TestBootstrapClientServerFederation(t *testing.T) {
|
||||
//nolint:gosec // insecure test server
|
||||
server, err := BootstrapServer(context.Background(), token, &http.Server{
|
||||
Addr: ":0",
|
||||
Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("ok"))
|
||||
}),
|
||||
}, RequireAndVerifyClientCert(), AddFederationToClientCAs())
|
||||
|
||||
@@ -177,8 +177,8 @@ func TestClient_Version(t *testing.T) {
|
||||
c, err := NewClient(srv.URL, WithTransport(http.DefaultTransport))
|
||||
require.NoError(t, err)
|
||||
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
render.JSONStatus(w, tt.response, tt.responseCode)
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
render.JSONStatus(w, r, tt.response, tt.responseCode)
|
||||
})
|
||||
|
||||
got, err := c.Version()
|
||||
@@ -218,8 +218,8 @@ func TestClient_Health(t *testing.T) {
|
||||
c, err := NewClient(srv.URL, WithTransport(http.DefaultTransport))
|
||||
require.NoError(t, err)
|
||||
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
render.JSONStatus(w, tt.response, tt.responseCode)
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
render.JSONStatus(w, r, tt.response, tt.responseCode)
|
||||
})
|
||||
|
||||
got, err := c.Health()
|
||||
@@ -262,12 +262,12 @@ func TestClient_Root(t *testing.T) {
|
||||
c, err := NewClient(srv.URL, WithTransport(http.DefaultTransport))
|
||||
require.NoError(t, err)
|
||||
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
expected := "/root/" + tt.shasum
|
||||
if req.RequestURI != expected {
|
||||
t.Errorf("RequestURI = %s, want %s", req.RequestURI, expected)
|
||||
if r.RequestURI != expected {
|
||||
t.Errorf("RequestURI = %s, want %s", r.RequestURI, expected)
|
||||
}
|
||||
render.JSONStatus(w, tt.response, tt.responseCode)
|
||||
render.JSONStatus(w, r, tt.response, tt.responseCode)
|
||||
})
|
||||
|
||||
got, err := c.Root(tt.shasum)
|
||||
@@ -323,12 +323,12 @@ func TestClient_Sign(t *testing.T) {
|
||||
c, err := NewClient(srv.URL, WithTransport(http.DefaultTransport))
|
||||
require.NoError(t, err)
|
||||
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
body := new(api.SignRequest)
|
||||
if err := read.JSON(req.Body, body); err != nil {
|
||||
if err := read.JSON(r.Body, body); err != nil {
|
||||
e, ok := tt.response.(error)
|
||||
require.True(t, ok, "response expected to be error type")
|
||||
render.Error(w, e)
|
||||
render.Error(w, r, e)
|
||||
return
|
||||
} else if !equalJSON(t, body, tt.request) {
|
||||
if tt.request == nil {
|
||||
@@ -339,7 +339,7 @@ func TestClient_Sign(t *testing.T) {
|
||||
t.Errorf("Client.Sign() request = %v, wants %v", body, tt.request)
|
||||
}
|
||||
}
|
||||
render.JSONStatus(w, tt.response, tt.responseCode)
|
||||
render.JSONStatus(w, r, tt.response, tt.responseCode)
|
||||
})
|
||||
|
||||
got, err := c.Sign(tt.request)
|
||||
@@ -385,12 +385,12 @@ func TestClient_Revoke(t *testing.T) {
|
||||
c, err := NewClient(srv.URL, WithTransport(http.DefaultTransport))
|
||||
require.NoError(t, err)
|
||||
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
body := new(api.RevokeRequest)
|
||||
if err := read.JSON(req.Body, body); err != nil {
|
||||
if err := read.JSON(r.Body, body); err != nil {
|
||||
e, ok := tt.response.(error)
|
||||
require.True(t, ok, "response expected to be error type")
|
||||
render.Error(w, e)
|
||||
render.Error(w, r, e)
|
||||
return
|
||||
} else if !equalJSON(t, body, tt.request) {
|
||||
if tt.request == nil {
|
||||
@@ -401,7 +401,7 @@ func TestClient_Revoke(t *testing.T) {
|
||||
t.Errorf("Client.Revoke() request = %v, wants %v", body, tt.request)
|
||||
}
|
||||
}
|
||||
render.JSONStatus(w, tt.response, tt.responseCode)
|
||||
render.JSONStatus(w, r, tt.response, tt.responseCode)
|
||||
})
|
||||
|
||||
got, err := c.Revoke(tt.request, nil)
|
||||
@@ -450,8 +450,8 @@ func TestClient_Renew(t *testing.T) {
|
||||
c, err := NewClient(srv.URL, WithTransport(http.DefaultTransport))
|
||||
require.NoError(t, err)
|
||||
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
render.JSONStatus(w, tt.response, tt.responseCode)
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
render.JSONStatus(w, r, tt.response, tt.responseCode)
|
||||
})
|
||||
|
||||
got, err := c.Renew(nil)
|
||||
@@ -504,11 +504,11 @@ func TestClient_RenewWithToken(t *testing.T) {
|
||||
c, err := NewClient(srv.URL, WithTransport(http.DefaultTransport))
|
||||
require.NoError(t, err)
|
||||
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
if req.Header.Get("Authorization") != "Bearer token" {
|
||||
render.JSONStatus(w, errs.InternalServer("force"), 500)
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Header.Get("Authorization") != "Bearer token" {
|
||||
render.JSONStatus(w, r, errs.InternalServer("force"), 500)
|
||||
} else {
|
||||
render.JSONStatus(w, tt.response, tt.responseCode)
|
||||
render.JSONStatus(w, r, tt.response, tt.responseCode)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -567,8 +567,8 @@ func TestClient_Rekey(t *testing.T) {
|
||||
c, err := NewClient(srv.URL, WithTransport(http.DefaultTransport))
|
||||
require.NoError(t, err)
|
||||
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
render.JSONStatus(w, tt.response, tt.responseCode)
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
render.JSONStatus(w, r, tt.response, tt.responseCode)
|
||||
})
|
||||
|
||||
got, err := c.Rekey(tt.request, nil)
|
||||
@@ -619,11 +619,11 @@ func TestClient_Provisioners(t *testing.T) {
|
||||
c, err := NewClient(srv.URL, WithTransport(http.DefaultTransport))
|
||||
require.NoError(t, err)
|
||||
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
if req.RequestURI != tt.expectedURI {
|
||||
t.Errorf("RequestURI = %s, want %s", req.RequestURI, tt.expectedURI)
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.RequestURI != tt.expectedURI {
|
||||
t.Errorf("RequestURI = %s, want %s", r.RequestURI, tt.expectedURI)
|
||||
}
|
||||
render.JSONStatus(w, tt.response, tt.responseCode)
|
||||
render.JSONStatus(w, r, tt.response, tt.responseCode)
|
||||
})
|
||||
|
||||
got, err := c.Provisioners(tt.args...)
|
||||
@@ -666,12 +666,12 @@ func TestClient_ProvisionerKey(t *testing.T) {
|
||||
c, err := NewClient(srv.URL, WithTransport(http.DefaultTransport))
|
||||
require.NoError(t, err)
|
||||
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
expected := "/provisioners/" + tt.kid + "/encrypted-key"
|
||||
if req.RequestURI != expected {
|
||||
t.Errorf("RequestURI = %s, want %s", req.RequestURI, expected)
|
||||
if r.RequestURI != expected {
|
||||
t.Errorf("RequestURI = %s, want %s", r.RequestURI, expected)
|
||||
}
|
||||
render.JSONStatus(w, tt.response, tt.responseCode)
|
||||
render.JSONStatus(w, r, tt.response, tt.responseCode)
|
||||
})
|
||||
|
||||
got, err := c.ProvisionerKey(tt.kid)
|
||||
@@ -720,8 +720,8 @@ func TestClient_Roots(t *testing.T) {
|
||||
c, err := NewClient(srv.URL, WithTransport(http.DefaultTransport))
|
||||
require.NoError(t, err)
|
||||
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
render.JSONStatus(w, tt.response, tt.responseCode)
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
render.JSONStatus(w, r, tt.response, tt.responseCode)
|
||||
})
|
||||
|
||||
got, err := c.Roots()
|
||||
@@ -769,8 +769,8 @@ func TestClient_Federation(t *testing.T) {
|
||||
c, err := NewClient(srv.URL, WithTransport(http.DefaultTransport))
|
||||
require.NoError(t, err)
|
||||
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
render.JSONStatus(w, tt.response, tt.responseCode)
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
render.JSONStatus(w, r, tt.response, tt.responseCode)
|
||||
})
|
||||
|
||||
got, err := c.Federation()
|
||||
@@ -820,8 +820,8 @@ func TestClient_SSHRoots(t *testing.T) {
|
||||
c, err := NewClient(srv.URL, WithTransport(http.DefaultTransport))
|
||||
require.NoError(t, err)
|
||||
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
render.JSONStatus(w, tt.response, tt.responseCode)
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
render.JSONStatus(w, r, tt.response, tt.responseCode)
|
||||
})
|
||||
|
||||
got, err := c.SSHRoots()
|
||||
@@ -912,8 +912,8 @@ func TestClient_RootFingerprint(t *testing.T) {
|
||||
c, err := NewClient(tt.server.URL, WithTransport(tr))
|
||||
require.NoError(t, err)
|
||||
|
||||
tt.server.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
render.JSONStatus(w, tt.response, tt.responseCode)
|
||||
tt.server.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
render.JSONStatus(w, r, tt.response, tt.responseCode)
|
||||
})
|
||||
|
||||
got, err := c.RootFingerprint()
|
||||
@@ -970,8 +970,8 @@ func TestClient_SSHBastion(t *testing.T) {
|
||||
c, err := NewClient(srv.URL, WithTransport(http.DefaultTransport))
|
||||
require.NoError(t, err)
|
||||
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
render.JSONStatus(w, tt.response, tt.responseCode)
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
render.JSONStatus(w, r, tt.response, tt.responseCode)
|
||||
})
|
||||
|
||||
got, err := c.SSHBastion(tt.request)
|
||||
|
||||
@@ -116,7 +116,8 @@ type GetCertificateAuthorityRequest struct {
|
||||
// GetCertificateAuthorityResponse is the response that contains
|
||||
// the root certificate.
|
||||
type GetCertificateAuthorityResponse struct {
|
||||
RootCertificate *x509.Certificate
|
||||
RootCertificate *x509.Certificate
|
||||
IntermediateCertificates []*x509.Certificate
|
||||
}
|
||||
|
||||
// CreateKeyRequest is the request used to generate a new key using a KMS.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package apiv1
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
"net/http"
|
||||
"strings"
|
||||
@@ -26,13 +27,20 @@ type CertificateAuthorityGetter interface {
|
||||
GetCertificateAuthority(req *GetCertificateAuthorityRequest) (*GetCertificateAuthorityResponse, error)
|
||||
}
|
||||
|
||||
// CertificateAuthorityCreator is an interface implamented by a
|
||||
// CertificateAuthorityCreator is an interface implemented by a
|
||||
// CertificateAuthorityService that has a method to create a new certificate
|
||||
// authority.
|
||||
type CertificateAuthorityCreator interface {
|
||||
CreateCertificateAuthority(req *CreateCertificateAuthorityRequest) (*CreateCertificateAuthorityResponse, error)
|
||||
}
|
||||
|
||||
// CertificateAuthoritySigner is an optional interface implemented by a
|
||||
// CertificateAuthorityService that has a method that returns a [crypto.Signer]
|
||||
// using the same key used to issue certificates.
|
||||
type CertificateAuthoritySigner interface {
|
||||
GetSigner() (crypto.Signer, error)
|
||||
}
|
||||
|
||||
// SignatureAlgorithmGetter is an optional implementation in a crypto.Signer
|
||||
// that returns the SignatureAlgorithm to use.
|
||||
type SignatureAlgorithmGetter interface {
|
||||
|
||||
@@ -248,6 +248,7 @@ func mustParseECKey(t *testing.T, pemKey string) *ecdsa.PrivateKey {
|
||||
block, _ := pem.Decode([]byte(pemKey))
|
||||
if block == nil {
|
||||
t.Fatal("failed to parse key")
|
||||
return nil
|
||||
}
|
||||
key, err := x509.ParseECPrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
@@ -882,7 +883,7 @@ func TestCloudCAS_CreateCertificateAuthority(t *testing.T) {
|
||||
defer srv.Stop()
|
||||
|
||||
// Create fake privateca client
|
||||
conn, err := grpc.DialContext(context.Background(), "", grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||
conn, err := grpc.NewClient("localhost", grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||
grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) {
|
||||
return lis.Dial()
|
||||
}))
|
||||
|
||||
@@ -58,6 +58,13 @@ func (c *SoftCAS) Type() apiv1.Type {
|
||||
return apiv1.SoftCAS
|
||||
}
|
||||
|
||||
// GetSigner implements [apiv1.CertificateAuthoritySigner] and returns a
|
||||
// [crypto.Signer] with the intermediate key.
|
||||
func (c *SoftCAS) GetSigner() (crypto.Signer, error) {
|
||||
_, signer, err := c.getCertSigner()
|
||||
return signer, err
|
||||
}
|
||||
|
||||
// CreateCertificate signs a new certificate using Golang or KMS crypto.
|
||||
func (c *SoftCAS) CreateCertificate(req *apiv1.CreateCertificateRequest) (*apiv1.CreateCertificateResponse, error) {
|
||||
switch {
|
||||
|
||||
@@ -19,8 +19,11 @@ import (
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/smallstep/certificates/cas/apiv1"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.step.sm/crypto/kms"
|
||||
kmsapi "go.step.sm/crypto/kms/apiv1"
|
||||
"go.step.sm/crypto/minica"
|
||||
"go.step.sm/crypto/pemutil"
|
||||
"go.step.sm/crypto/x509util"
|
||||
)
|
||||
@@ -269,6 +272,45 @@ func TestSoftCAS_Type(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSoftCAS_GetSigner(t *testing.T) {
|
||||
ca, err := minica.New()
|
||||
require.NoError(t, err)
|
||||
|
||||
type fields struct {
|
||||
CertificateChain []*x509.Certificate
|
||||
Signer crypto.Signer
|
||||
CertificateSigner func() ([]*x509.Certificate, crypto.Signer, error)
|
||||
KeyManager kms.KeyManager
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
want crypto.Signer
|
||||
assertion assert.ErrorAssertionFunc
|
||||
}{
|
||||
{"ok signer", fields{[]*x509.Certificate{ca.Intermediate}, ca.Signer, nil, nil}, ca.Signer, assert.NoError},
|
||||
{"ok certificateSigner", fields{[]*x509.Certificate{ca.Intermediate}, nil, func() ([]*x509.Certificate, crypto.Signer, error) {
|
||||
return []*x509.Certificate{ca.Intermediate}, ca.Signer, nil
|
||||
}, nil}, ca.Signer, assert.NoError},
|
||||
{"fail certificateSigner", fields{[]*x509.Certificate{ca.Intermediate}, nil, func() ([]*x509.Certificate, crypto.Signer, error) {
|
||||
return nil, nil, apiv1.NotImplementedError{}
|
||||
}, nil}, nil, assert.Error},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &SoftCAS{
|
||||
CertificateChain: tt.fields.CertificateChain,
|
||||
Signer: tt.fields.Signer,
|
||||
CertificateSigner: tt.fields.CertificateSigner,
|
||||
KeyManager: tt.fields.KeyManager,
|
||||
}
|
||||
got, err := c.GetSigner()
|
||||
tt.assertion(t, err)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSoftCAS_CreateCertificate(t *testing.T) {
|
||||
mockNow(t)
|
||||
// Set rand.Reader to EOF
|
||||
|
||||
@@ -165,7 +165,8 @@ func (v *VaultCAS) GetCertificateAuthority(*apiv1.GetCertificateAuthorityRequest
|
||||
}
|
||||
|
||||
return &apiv1.GetCertificateAuthorityResponse{
|
||||
RootCertificate: cert.root,
|
||||
RootCertificate: cert.root,
|
||||
IntermediateCertificates: cert.intermediates,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -83,6 +83,10 @@ Requires **--insecure** flag.`,
|
||||
Usage: `the <port> used on tls-alpn-01 challenges. It can be changed for testing purposes.
|
||||
Requires **--insecure** flag.`,
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "acme-strict-fqdn",
|
||||
Usage: `enable strict DNS resolution using a fully qualified domain name.`,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "pidfile",
|
||||
Usage: "the path to the <file> to write the process ID.",
|
||||
@@ -126,6 +130,9 @@ func appAction(ctx *cli.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Set the strict DNS resolution on ACME challenges. Defaults to false.
|
||||
acme.StrictFQDN = ctx.Bool("acme-strict-fqdn")
|
||||
|
||||
// Allow custom contexts.
|
||||
if caCtx := ctx.String("context"); caCtx != "" {
|
||||
if _, ok := step.Contexts().Get(caCtx); ok {
|
||||
|
||||
129
go.mod
129
go.mod
@@ -1,112 +1,114 @@
|
||||
module github.com/smallstep/certificates
|
||||
|
||||
go 1.20
|
||||
go 1.21
|
||||
|
||||
require (
|
||||
cloud.google.com/go/longrunning v0.5.6
|
||||
cloud.google.com/go/security v1.15.6
|
||||
cloud.google.com/go/longrunning v0.5.10
|
||||
cloud.google.com/go/security v1.17.3
|
||||
github.com/Masterminds/sprig/v3 v3.2.3
|
||||
github.com/dgraph-io/badger v1.6.2
|
||||
github.com/dgraph-io/badger/v2 v2.2007.4
|
||||
github.com/fxamacker/cbor/v2 v2.6.0
|
||||
github.com/go-chi/chi/v5 v5.0.12
|
||||
github.com/fxamacker/cbor/v2 v2.7.0
|
||||
github.com/go-chi/chi/v5 v5.1.0
|
||||
github.com/go-jose/go-jose/v3 v3.0.3
|
||||
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.6.0
|
||||
github.com/googleapis/gax-go/v2 v2.12.3
|
||||
github.com/hashicorp/vault/api v1.12.2
|
||||
github.com/hashicorp/vault/api/auth/approle v0.6.0
|
||||
github.com/hashicorp/vault/api/auth/kubernetes v0.6.0
|
||||
github.com/newrelic/go-agent/v3 v3.30.0
|
||||
github.com/googleapis/gax-go/v2 v2.13.0
|
||||
github.com/hashicorp/vault/api v1.14.0
|
||||
github.com/hashicorp/vault/api/auth/approle v0.7.0
|
||||
github.com/hashicorp/vault/api/auth/kubernetes v0.7.0
|
||||
github.com/newrelic/go-agent/v3 v3.33.1
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/prometheus/client_golang v1.19.0
|
||||
github.com/prometheus/client_golang v1.19.1
|
||||
github.com/rs/xid v1.5.0
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/slackhq/nebula v1.6.1
|
||||
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
|
||||
github.com/smallstep/nosql v0.7.0
|
||||
github.com/smallstep/pkcs7 v0.0.0-20231024181729-3b98ecc1ca81
|
||||
github.com/smallstep/scep v0.0.0-20231024192529-aee96d7ad34d
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/urfave/cli v1.22.14
|
||||
github.com/urfave/cli v1.22.15
|
||||
go.step.sm/cli-utils v0.9.0
|
||||
go.step.sm/crypto v0.44.1
|
||||
go.step.sm/linkedca v0.20.1
|
||||
golang.org/x/crypto v0.21.0
|
||||
golang.org/x/exp v0.0.0-20230310171629-522b1b587ee0
|
||||
golang.org/x/net v0.22.0
|
||||
google.golang.org/api v0.171.0
|
||||
google.golang.org/grpc v1.62.1
|
||||
google.golang.org/protobuf v1.33.0
|
||||
go.step.sm/crypto v0.50.0
|
||||
go.step.sm/linkedca v0.22.1
|
||||
golang.org/x/crypto v0.25.0
|
||||
golang.org/x/exp v0.0.0-20240318143956-a85f2c67cd81
|
||||
golang.org/x/net v0.27.0
|
||||
google.golang.org/api v0.189.0
|
||||
google.golang.org/grpc v1.65.0
|
||||
google.golang.org/protobuf v1.34.2
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.112.1 // indirect
|
||||
cloud.google.com/go/compute v1.24.0 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||
cloud.google.com/go/iam v1.1.6 // indirect
|
||||
cloud.google.com/go/kms v1.15.7 // indirect
|
||||
cloud.google.com/go v0.115.0 // indirect
|
||||
cloud.google.com/go/auth v0.7.2 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.3 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.5.0 // indirect
|
||||
cloud.google.com/go/iam v1.1.10 // indirect
|
||||
cloud.google.com/go/kms v1.18.2 // indirect
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.10.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.12.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.9.0 // 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.2.1 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // 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.26.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.8 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.8 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.30.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.24 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.24 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.9 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/kms v1.30.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.20.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.28.5 // indirect
|
||||
github.com/aws/smithy-go v1.20.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.15 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/kms v1.35.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.22.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.30.1 // indirect
|
||||
github.com/aws/smithy-go v1.20.3 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cenkalti/backoff/v3 v3.0.0 // indirect
|
||||
github.com/cespare/xxhash v1.1.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/chzyer/readline v1.5.1 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 // 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
|
||||
github.com/go-jose/go-jose/v4 v4.0.1 // indirect
|
||||
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.4.1 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // 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
|
||||
github.com/golang-jwt/jwt/v5 v5.2.0 // indirect
|
||||
github.com/golang/glog v1.2.0 // indirect
|
||||
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
|
||||
github.com/golang/glog v1.2.1 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/btree v1.1.2 // indirect
|
||||
github.com/google/certificate-transparency-go v1.1.6 // indirect
|
||||
github.com/google/go-tpm-tools v0.4.3 // indirect
|
||||
github.com/google/go-tpm-tools v0.4.4 // indirect
|
||||
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/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.7 // 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/strutil v0.1.2 // indirect
|
||||
@@ -114,14 +116,10 @@ require (
|
||||
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/jackc/chunkreader/v2 v2.0.1 // indirect
|
||||
github.com/jackc/pgconn v1.14.3 // indirect
|
||||
github.com/jackc/pgio v1.0.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgproto3/v2 v2.3.3 // 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.3 // indirect
|
||||
github.com/jackc/pgx/v5 v5.6.0 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
||||
github.com/klauspost/compress v1.16.3 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/manifoldco/promptui v0.9.0 // indirect
|
||||
@@ -147,21 +145,20 @@ require (
|
||||
github.com/spf13/cast v1.4.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
|
||||
go.etcd.io/bbolt v1.3.10 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
|
||||
go.opentelemetry.io/otel v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.24.0 // indirect
|
||||
golang.org/x/oauth2 v0.18.0 // indirect
|
||||
golang.org/x/sync v0.6.0 // indirect
|
||||
golang.org/x/sys v0.18.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/oauth2 v0.21.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/sys v0.22.0 // indirect
|
||||
golang.org/x/text v0.16.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-20240213162025-012b6fc9bca9 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c // indirect
|
||||
google.golang.org/genproto v0.0.0-20240722135656-d784300faade // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240722135656-d784300faade // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
417
go.sum
417
go.sum
@@ -1,39 +1,40 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.112.1 h1:uJSeirPke5UNZHIb4SxfZklVSiWWVqW4oXlETwZziwM=
|
||||
cloud.google.com/go v0.112.1/go.mod h1:+Vbu+Y1UU+I1rjmzeMOb/8RfkKJK2Gyxi1X6jJCZLo4=
|
||||
cloud.google.com/go/compute v1.24.0 h1:phWcR2eWzRJaL/kOiJwfFsPs4BaKq1j6vnpZrc1YlVg=
|
||||
cloud.google.com/go/compute v1.24.0/go.mod h1:kw1/T+h/+tK2LJK0wiPPx1intgdAM3j/g3hFDlscY40=
|
||||
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
|
||||
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
|
||||
cloud.google.com/go/iam v1.1.6 h1:bEa06k05IO4f4uJonbB5iAgKTPpABy1ayxaIZV/GHVc=
|
||||
cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI=
|
||||
cloud.google.com/go/kms v1.15.7 h1:7caV9K3yIxvlQPAcaFffhlT7d1qpxjB1wHBtjWa13SM=
|
||||
cloud.google.com/go/kms v1.15.7/go.mod h1:ub54lbsa6tDkUwnu4W7Yt1aAIFLnspgh0kPGToDukeI=
|
||||
cloud.google.com/go/longrunning v0.5.6 h1:xAe8+0YaWoCKr9t1+aWe+OeQgN/iJK1fEgZSXmjuEaE=
|
||||
cloud.google.com/go/longrunning v0.5.6/go.mod h1:vUaDrWYOMKRuhiv6JBnn49YxCPz2Ayn9GqyjaBT8/mA=
|
||||
cloud.google.com/go/security v1.15.6 h1:LYMj7ISEEjVQ0ub6E6ygGhjVbNQTH5CawKZz0bbPMVE=
|
||||
cloud.google.com/go/security v1.15.6/go.mod h1:UMEAGVBMqE6xZvkCR1FvUIeBEmGOCRIDwtwT357xmok=
|
||||
cloud.google.com/go v0.115.0 h1:CnFSK6Xo3lDYRoBKEcAtia6VSC837/ZkJuRduSFnr14=
|
||||
cloud.google.com/go v0.115.0/go.mod h1:8jIM5vVgoAEoiVxQ/O4BFTfHqulPZgs/ufEzMcFMdWU=
|
||||
cloud.google.com/go/auth v0.7.2 h1:uiha352VrCDMXg+yoBtaD0tUF4Kv9vrtrWPYXwutnDE=
|
||||
cloud.google.com/go/auth v0.7.2/go.mod h1:VEc4p5NNxycWQTMQEDQF0bd6aTMb6VgYDXEwiJJQAbs=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.3 h1:MlxF+Pd3OmSudg/b1yZ5lJwoXCEaeedAguodky1PcKI=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.3/go.mod h1:tMQXOfZzFuNuUxOypHlQEXgdfX5cuhwU+ffUuXRJE8I=
|
||||
cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY=
|
||||
cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY=
|
||||
cloud.google.com/go/iam v1.1.10 h1:ZSAr64oEhQSClwBL670MsJAW5/RLiC6kfw3Bqmd5ZDI=
|
||||
cloud.google.com/go/iam v1.1.10/go.mod h1:iEgMq62sg8zx446GCaijmA2Miwg5o3UbO+nI47WHJps=
|
||||
cloud.google.com/go/kms v1.18.2 h1:EGgD0B9k9tOOkbPhYW1PHo2W0teamAUYMOUIcDRMfPk=
|
||||
cloud.google.com/go/kms v1.18.2/go.mod h1:YFz1LYrnGsXARuRePL729oINmN5J/5e7nYijgvfiIeY=
|
||||
cloud.google.com/go/longrunning v0.5.10 h1:eB/BniENNRKhjz/xgiillrdcH3G74TGSl3BXinGlI7E=
|
||||
cloud.google.com/go/longrunning v0.5.10/go.mod h1:tljz5guTr5oc/qhlUjBlk7UAIFMOGuPNxkNDZXlLics=
|
||||
cloud.google.com/go/security v1.17.3 h1:LTNqcCljAsgCEqZzir/+dSE8cEsc5wXKDQzTIoY9lxE=
|
||||
cloud.google.com/go/security v1.17.3/go.mod h1:CuKzQq5OD6TXAYaZs/jI0d7CNHoD0LXbpsznIIIn4f4=
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M=
|
||||
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.10.0 h1:n1DH8TPV4qqPTje2RcUBYwtrTWlabVp4n46+74X2pn4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.10.0/go.mod h1:HDcZnuGbiyppErN6lB+idp4CKhjbc8gwjto6OPpyggM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1 h1:sO0/P7g68FrryJzljemN+6GTssUXdANk6aJ7T1ZxnsQ=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1/go.mod h1:h8hyGFDsU5HMivxiS2iYFZsgDbU9OnnJ163x5UGVKYo=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 h1:LqbJ/WzJUwBf8UiaSzgX7aMclParm9/5Vgp+TY51uBQ=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2/go.mod h1:yInRyqWXAuaPrgI7p70+lDDgh3mlBohis29jGMISnmc=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.12.0 h1:1nGuui+4POelzDwI7RG56yfQJHCnKvwfMoU7VsEp+Zg=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.12.0/go.mod h1:99EvauvlcJ1U06amZiksfYz/3aFGyIhWGHVyiZXtBAI=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 h1:tfLQ34V6F7tVSwoTf/4lH5sE0o6eCJuNDTmH09nDpbc=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.9.0 h1:H+U3Gk9zY56G3u872L82bk4thcsy2Gghb9ExT4Zvm1o=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.9.0/go.mod h1:mgrmMSgaLp9hmax62XQTd0N4aAqSE5E0DulSpVYK7vc=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.10.0 h1:m/sWOGCREuSBqg2htVQTBY8nOZpyajYztF0vUvSZTuM=
|
||||
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.2.1 h1:DzHpqpoJVaCgOUdVHxE8QB52S6NiVdDQvGlny1qvPqA=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/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/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
|
||||
@@ -44,34 +45,34 @@ github.com/ThalesIgnite/crypto11 v1.2.5 h1:1IiIIEqYmBvUYFeMnHqRft4bwf/O36jryEUpY
|
||||
github.com/ThalesIgnite/crypto11 v1.2.5/go.mod h1:ILDKtnCKiQ7zRoNxcp36Y1ZR8LBPmR2E23+wTQe/MlE=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/aws/aws-sdk-go-v2 v1.26.0 h1:/Ce4OCiM3EkpW7Y+xUnfAFpchU78K7/Ug01sZni9PgA=
|
||||
github.com/aws/aws-sdk-go-v2 v1.26.0/go.mod h1:35hUlJVYd+M++iLI3ALmVwMOyRYMmRqUXpTtRGW+K9I=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.8 h1:0r8epOsiJ7YJz65MGcb8i91ehFp4kvvFe2qkq5oYeRI=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.8/go.mod h1:XsmYKxYNuIhLsFddpNds+j9H5XKzjWDdg/SZngiwFio=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.8 h1:WUdNLXbyNbU07V/WFrSOBXqZTDgmmMNMgUFzpYOKJhw=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.8/go.mod h1:iPZzLpaBIfhyvVS/XGD3JvR1GP3YdHTqpySKDlqkfs8=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.4 h1:S+L2QSKhUuShih3aq9P/mkzDBiOO5tTyVg+vXREfsfg=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.4/go.mod h1:nQ3how7DMnFMWiU1SpECohgC82fpn4cKZ875NDMmwtA=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.4 h1:0ScVK/4qZ8CIW0k8jOeFVsyS/sAiXpYxRBLolMkuLQM=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.4/go.mod h1:84KyjNZdHC6QZW08nfHI6yZgPd+qRgaWcYsyLUo3QY8=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.4 h1:sHmMWWX5E7guWEFQ9SVo6A3S4xpPrWnd77a6y4WM6PU=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.4/go.mod h1:WjpDrhWisWOIoS9n3nk67A3Ll1vfULJ9Kq6h29HTD48=
|
||||
github.com/aws/aws-sdk-go-v2 v1.30.1 h1:4y/5Dvfrhd1MxRDD77SrfsDaj8kUkkljU7XE83NPV+o=
|
||||
github.com/aws/aws-sdk-go-v2 v1.30.1/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.24 h1:NM9XicZ5o1CBU/MZaHwFtimRpWx9ohAUAqkG6AqSqPo=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.24/go.mod h1:aXzi6QJTuQRVVusAO8/NxpdTeTyr/wRcybdDtfUwJSs=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.24 h1:YclAsrnb1/GTQNt2nzv+756Iw4mF8AOzcDfweWwwm/M=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.24/go.mod h1:Hld7tmnAkoBQdTMNYZGzztzKRdA4fCdn9L83LOoigac=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.9 h1:Aznqksmd6Rfv2HQN9cpqIV/lQRMaIpJkLLaJ1ZI76no=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.9/go.mod h1:WQr3MY7AxGNxaqAtsDWn+fBxmd4XvLkzeqQ8P1VM0/w=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.13 h1:5SAoZ4jYpGH4721ZNoS1znQrhOfZinOhc4XuTXx/nVc=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.13/go.mod h1:+rdA6ZLpaSeM7tSg/B0IEDinCIBJGmW8rKDFkYpP04g=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.13 h1:WIijqeaAO7TYFLbhsZmi2rgLEAtWOC1LhxCAVTJlSKw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.13/go.mod h1:i+kbfa76PQbWw/ULoWnp51EYVWH4ENln76fLQE3lXT8=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.1 h1:EyBZibRTVAs6ECHZOw5/wlylS9OcTzwyjeQMudmREjE=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.1/go.mod h1:JKpmtYhhPs7D97NL/ltqz7yCkERFW5dOlHyVl66ZYF8=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.6 h1:b+E7zIUHMmcB4Dckjpkapoy47W6C9QBv/zoUP+Hn8Kc=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.6/go.mod h1:S2fNV0rxrP78NhPbCZeQgY8H9jdDMeGtwcfZIRxzBqU=
|
||||
github.com/aws/aws-sdk-go-v2/service/kms v1.30.0 h1:yS0JkEdV6h9JOo8sy2JSpjX+i7vsKifU8SIeHrqiDhU=
|
||||
github.com/aws/aws-sdk-go-v2/service/kms v1.30.0/go.mod h1:+I8VUUSVD4p5ISQtzpgSva4I8cJ4SQ4b1dcBcof7O+g=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.20.3 h1:mnbuWHOcM70/OFUlZZ5rcdfA8PflGXXiefU/O+1S3+8=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.20.3/go.mod h1:5HFu51Elk+4oRBZVxmHrSds5jFXmFj8C3w7DVF2gnrs=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.3 h1:uLq0BKatTmDzWa/Nu4WO0M1AaQDaPpwTKAeByEc6WFM=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.3/go.mod h1:b+qdhjnxj8GSR6t5YfphOffeoQSQ1KmpoVVuBn+PWxs=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.28.5 h1:J/PpTf/hllOjx8Xu9DMflff3FajfLxqM5+tepvVXmxg=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.28.5/go.mod h1:0ih0Z83YDH/QeQ6Ori2yGE2XvWYv/Xm+cZc01LC6oK0=
|
||||
github.com/aws/smithy-go v1.20.1 h1:4SZlSlMr36UEqC7XOyRVb27XMeZubNcBNN+9IgEPIQw=
|
||||
github.com/aws/smithy-go v1.20.1/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 h1:dT3MqvGhSoaIhRseqw2I0yH81l7wiR2vjs57O51EAm8=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3/go.mod h1:GlAeCkHwugxdHaueRr4nhPuY+WW+gR8UjlcqzPr1SPI=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.15 h1:I9zMeF107l0rJrpnHpjEiiTSCKYAIw8mALiXcPsGBiA=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.15/go.mod h1:9xWJ3Q/S6Ojusz1UIkfycgD1mGirJfLLKqq3LPT7WN8=
|
||||
github.com/aws/aws-sdk-go-v2/service/kms v1.35.1 h1:0gP2OJJT6HM2BYltZ9x+A87OE8LJL96DXeAAdLv3t1M=
|
||||
github.com/aws/aws-sdk-go-v2/service/kms v1.35.1/go.mod h1:hGONorZkQCfR5DW6l2xdy7zC8vfO0r9pJlwyg6gmGeo=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.22.1 h1:p1GahKIjyMDZtiKoIn0/jAj/TkMzfzndDv5+zi2Mhgc=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.22.1/go.mod h1:/vWdhoIoYA5hYoPZ6fm7Sv4d8701PiG5VKe8/pPJL60=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.2 h1:ORnrOK0C4WmYV/uYt3koHEWBLYsRDwk2Np+eEoyV4Z0=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.2/go.mod h1:xyFHA4zGxgYkdD73VeezHt3vSKEG9EmFnGwoKlP00u4=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.30.1 h1:+woJ607dllHJQtsnJLi52ycuqHMwlW+Wqm2Ppsfp4nQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.30.1/go.mod h1:jiNR3JqT15Dm+QWq2SRgh0x0bCNSRP2L25+CqPNpJlQ=
|
||||
github.com/aws/smithy-go v1.20.3 h1:ryHwveWzPV5BIof6fyDvor6V3iUL7nTfiTKXHiW05nE=
|
||||
github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
|
||||
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=
|
||||
@@ -81,8 +82,8 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM=
|
||||
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
|
||||
@@ -94,17 +95,12 @@ 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/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=
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -119,7 +115,6 @@ github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu
|
||||
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=
|
||||
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=
|
||||
@@ -129,46 +124,43 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
||||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fxamacker/cbor/v2 v2.6.0 h1:sU6J2usfADwWlYDAFhZBQ6TnLFBHxgesMrQfQgk1tWA=
|
||||
github.com/fxamacker/cbor/v2 v2.6.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||
github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s=
|
||||
github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/go-jose/go-jose/v3 v3.0.1/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
|
||||
github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k=
|
||||
github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
|
||||
github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U=
|
||||
github.com/go-jose/go-jose/v4 v4.0.1/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY=
|
||||
github.com/go-kit/kit v0.4.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.13.0 h1:OoneCcHKHQ03LfBpoQCUfCluwd2Vt3ohz+kvbJneZAU=
|
||||
github.com/go-kit/kit v0.13.0/go.mod h1:phqEHMMUbyrCFCTgH48JueqrM3md2HcAZ8N3XE4FKDg=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU=
|
||||
github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||
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.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/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=
|
||||
github.com/go-piv/piv-go v1.11.0/go.mod h1:NZ2zmjVkfFaL/CF8cVQ/pXdXtuj110zEKGdJM6fJZZM=
|
||||
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
|
||||
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||
github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw=
|
||||
github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68=
|
||||
github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
|
||||
github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4=
|
||||
github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
@@ -185,8 +177,6 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
@@ -204,22 +194,23 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-configfs-tsm v0.2.2 h1:YnJ9rXIOj5BYD7/0DNnzs8AOp7UcvjfTvt215EWcs98=
|
||||
github.com/google/go-configfs-tsm v0.2.2/go.mod h1:EL1GTDFMb5PZQWDviGfZV9n87WeGTR/JUg13RfwkgRo=
|
||||
github.com/google/go-sev-guest v0.9.3 h1:GOJ+EipURdeWFl/YYdgcCxyPeMgQUWlI056iFkBD8UU=
|
||||
github.com/google/go-sev-guest v0.9.3/go.mod h1:hc1R4R6f8+NcJwITs0L90fYWTsBpd1Ix+Gur15sqHDs=
|
||||
github.com/google/go-tdx-guest v0.3.1 h1:gl0KvjdsD4RrJzyLefDOvFOUH3NAJri/3qvaL5m83Iw=
|
||||
github.com/google/go-tdx-guest v0.3.1/go.mod h1:/rc3d7rnPykOPuY8U9saMyEps0PZDThLk/RygXm04nE=
|
||||
github.com/google/go-tpm v0.9.0 h1:sQF6YqWMi+SCXpsmS3fd21oPy/vSddwZry4JnmltHVk=
|
||||
github.com/google/go-tpm v0.9.0/go.mod h1:FkNVkc6C+IsvDI9Jw1OveJmxGZUUaKxtrpOS47QWKfU=
|
||||
github.com/google/go-tpm-tools v0.4.3 h1:L5dc34fttMIREoKRmnIJfv2NSZDSZ+RfBD+izN0EZoA=
|
||||
github.com/google/go-tpm-tools v0.4.3/go.mod h1:T8jXkp2s+eltnCDIsXR84/MTcVU9Ja7bh3Mit0pa4AY=
|
||||
github.com/google/go-tpm-tools v0.4.4 h1:oiQfAIkc6xTy9Fl5NKTeTJkBTlXdHsxAofmQyxBKY98=
|
||||
github.com/google/go-tpm-tools v0.4.4/go.mod h1:T8jXkp2s+eltnCDIsXR84/MTcVU9Ja7bh3Mit0pa4AY=
|
||||
github.com/google/go-tspi v0.3.0 h1:ADtq8RKfP+jrTyIWIZDIYcKOMecRqNJFOew2IT0Inus=
|
||||
github.com/google/go-tspi v0.3.0/go.mod h1:xfMGI3G0PhxCdNVcYr1C4C+EizojDg/TXuX5by8CiHI=
|
||||
github.com/google/logger v1.1.1 h1:+6Z2geNxc9G+4D4oDO9njjjn2d0wN5d7uOo0vOIW1NQ=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/logger v1.1.1/go.mod h1:BkeJZ+1FhQ+/d087r4dzojEg1u2ZX+ZqG1jTUrLM+zQ=
|
||||
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=
|
||||
@@ -228,22 +219,20 @@ 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.3 h1:5/zPPDvw8Q1SuXjrqrZslrqT7dL/uJT2CQii/cLCKqA=
|
||||
github.com/googleapis/gax-go/v2 v2.12.3/go.mod h1:AKloxT6GtNbaLm8QTNSidHUVsHYcBHwWRvkNFJUQcS4=
|
||||
github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s=
|
||||
github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A=
|
||||
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=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-hclog v1.2.2 h1:ihRI7YFwcZdiSD7SIenIhHfQH3OuDvWerAUBZbeQS3M=
|
||||
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
|
||||
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
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.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
|
||||
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=
|
||||
@@ -255,101 +244,46 @@ github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0S
|
||||
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
|
||||
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.12.0/go.mod h1:si+lJCYO7oGkIoNPAN8j3azBLTn9SjMGS+jFaHd1Cck=
|
||||
github.com/hashicorp/vault/api v1.12.2 h1:7YkCTE5Ni90TcmYHDBExdt4WGJxhpzaHqR6uGbQb/rE=
|
||||
github.com/hashicorp/vault/api v1.12.2/go.mod h1:LSGf1NGT1BnvFFnKVtnvcaLBM2Lz+gJdpL6HUYed8KE=
|
||||
github.com/hashicorp/vault/api/auth/approle v0.6.0 h1:ELfFFQlTM/e97WJKu1HvNFa7lQ3tlTwwzrR1NJE1V7Y=
|
||||
github.com/hashicorp/vault/api/auth/approle v0.6.0/go.mod h1:CCoIl1xBC3lAWpd1HV+0ovk76Z8b8Mdepyk21h3pGk0=
|
||||
github.com/hashicorp/vault/api/auth/kubernetes v0.6.0 h1:K8sKGhtTAqGKfzaaYvUSIOAqTOIn3Gk1EsCEAMzZHtM=
|
||||
github.com/hashicorp/vault/api/auth/kubernetes v0.6.0/go.mod h1:Htwcjez5J9PwAHaZ1EYMBlgGq3/in5ajUV4+WCPihPE=
|
||||
github.com/hashicorp/vault/api v1.14.0 h1:Ah3CFLixD5jmjusOgm8grfN9M0d+Y8fVR2SW0K6pJLU=
|
||||
github.com/hashicorp/vault/api v1.14.0/go.mod h1:pV9YLxBGSz+cItFDd8Ii4G17waWOQ32zVjMWHe/cOqk=
|
||||
github.com/hashicorp/vault/api/auth/approle v0.7.0 h1:R5IRVuFA5JSdG3UdGVcGysi0StrL1lPmyJnrawiV0Ss=
|
||||
github.com/hashicorp/vault/api/auth/approle v0.7.0/go.mod h1:B+WaC6VR+aSXiUxykpaPUoFiiZAhic53tDLbGjWZmRA=
|
||||
github.com/hashicorp/vault/api/auth/kubernetes v0.7.0 h1:pHCbeeyD6E5KmMMCc9vwwZZ5OVlM6yFayxFHWodiOUU=
|
||||
github.com/hashicorp/vault/api/auth/kubernetes v0.7.0/go.mod h1:Eey0x0X2g+b2LYWgBrQFyf5W0fp+Y1HGrEckP8Q0wns=
|
||||
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/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=
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
|
||||
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||
github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
|
||||
github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
|
||||
github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
|
||||
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.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w=
|
||||
github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM=
|
||||
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=
|
||||
github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=
|
||||
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc=
|
||||
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag=
|
||||
github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
|
||||
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
|
||||
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
|
||||
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
|
||||
github.com/jackc/pgtype v1.14.0 h1:y+xUdabmyMkJLyApYuPj38mW+aAIqCe5uuBB51rH3Vw=
|
||||
github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
|
||||
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
|
||||
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.3 h1:dE2/TrEsGX3RBprb3qryqSV9Y60iZN1C6i8IrmW9/BA=
|
||||
github.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw=
|
||||
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/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY=
|
||||
github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw=
|
||||
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
|
||||
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
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/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=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA=
|
||||
github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
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/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
@@ -372,9 +306,10 @@ 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.30.0 h1:ZXHCT/Cot4iIPwcegCZURuRQOsfmGA6wilW+S3bfBjY=
|
||||
github.com/newrelic/go-agent/v3 v3.30.0/go.mod h1:9utrgxlSryNqRrTvII2XBL+0lpofXbqXApvVWPpbzUg=
|
||||
github.com/newrelic/go-agent/v3 v3.33.1 h1:eWOtty43cyxrMKws4VNPdebgEB6ujFTf0yxPsgB0M80=
|
||||
github.com/newrelic/go-agent/v3 v3.33.1/go.mod h1:SMdqPzE/ghkWdY0rYGSD7Clw2daK/XH6pUnVd4albg4=
|
||||
github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw=
|
||||
github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
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=
|
||||
github.com/peterbourgon/diskv/v3 v3.0.1/go.mod h1:kJ5Ny7vLdARGU3WUuy6uzO6T0nb/2gWcT1JiBvRmb5o=
|
||||
@@ -386,8 +321,8 @@ 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.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
|
||||
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
|
||||
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
|
||||
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
|
||||
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
|
||||
@@ -395,29 +330,22 @@ github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSz
|
||||
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
|
||||
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=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
|
||||
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
|
||||
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
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/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=
|
||||
@@ -426,8 +354,8 @@ 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/nosql v0.7.0 h1:YiWC9ZAHcrLCrayfaF+QJUv16I2bZ7KdLC3RpJcnAnE=
|
||||
github.com/smallstep/nosql v0.7.0/go.mod h1:H5VnKMCbeq9QA6SRY5iqPylfxLfYcLwvUff3onQ8+HU=
|
||||
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=
|
||||
@@ -445,15 +373,13 @@ github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb6
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
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/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
@@ -464,16 +390,15 @@ github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8
|
||||
github.com/thales-e-security/pool v0.0.2 h1:RAPs4q2EbWsTit6tpzuvTFlgFRJ3S8Evf5gtvVDbmPg=
|
||||
github.com/thales-e-security/pool v0.0.2/go.mod h1:qtpMm2+thHtqhLzTwgDBj/OuNnMpupY8mv0Phz0gjhU=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk=
|
||||
github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA=
|
||||
github.com/urfave/cli v1.22.15 h1:nuqt+pdC/KqswQKhETJjo7pvn/k4xMUxgW6liI7XpnM=
|
||||
github.com/urfave/cli v1.22.15/go.mod h1:wSan1hmo5zeyLGBjRJbzRTNk8gwoYa2B9n4q9dmRIc0=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||
go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
|
||||
go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
|
||||
go.etcd.io/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0=
|
||||
go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ=
|
||||
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.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg=
|
||||
@@ -484,54 +409,33 @@ go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
|
||||
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
|
||||
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
|
||||
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
|
||||
go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw=
|
||||
go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw=
|
||||
go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg=
|
||||
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
|
||||
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
||||
go.step.sm/cli-utils v0.9.0 h1:55jYcsQbnArNqepZyAwcato6Zy2MoZDRkWW+jF+aPfQ=
|
||||
go.step.sm/cli-utils v0.9.0/go.mod h1:Y/CRoWl1FVR9j+7PnAewufAwKmBOTzR6l9+7EYGAnp8=
|
||||
go.step.sm/crypto v0.44.1 h1:8ouq8JEYXVxSymuVuX54Ilh5X2dqyjgOGGXyPeXDzV8=
|
||||
go.step.sm/crypto v0.44.1/go.mod h1:hKl+QUIS4oJFRwQBRcVPz8NOYhRaoOJmwXHd2Y3XTA0=
|
||||
go.step.sm/linkedca v0.20.1 h1:bHDn1+UG1NgRrERkWbbCiAIvv4lD5NOFaswPDTyO5vU=
|
||||
go.step.sm/linkedca v0.20.1/go.mod h1:Vaq4+Umtjh7DLFI1KuIxeo598vfBzgSYZUjgVJ7Syxw=
|
||||
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/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=
|
||||
go.step.sm/crypto v0.50.0 h1:BqI9sEgocoHDLLHiZnFqdqXl5FjdMvOWKMm/fKL/lrw=
|
||||
go.step.sm/crypto v0.50.0/go.mod h1:NCFMhLS6FJXQ9sD9PP282oHtsBWLrI6wXZY0eOkq7t8=
|
||||
go.step.sm/linkedca v0.22.1 h1:GvprpH9P4Sv9U+eZ3bxDgRSSpW14cFDYpe1kS6yWLkw=
|
||||
go.step.sm/linkedca v0.22.1/go.mod h1:dOKdF4HSn73YUEkfS5/FECngZmBtj2Il5DTKWXY4S6Y=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
||||
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
||||
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-20240318143956-a85f2c67cd81 h1:6R2FC06FonbXQ8pK11/PDFY6N6LWlf9KlzibaCapmqc=
|
||||
golang.org/x/exp v0.0.0-20240318143956-a85f2c67cd81/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ=
|
||||
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=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
@@ -541,7 +445,6 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
@@ -549,36 +452,26 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
|
||||
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
||||
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI=
|
||||
golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8=
|
||||
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
|
||||
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
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-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=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -594,80 +487,61 @@ 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.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
|
||||
golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
|
||||
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
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.171.0 h1:w174hnBPqut76FzW5Qaupt7zY8Kql6fiVjgys4f58sU=
|
||||
google.golang.org/api v0.171.0/go.mod h1:Hnq5AHm4OTMt2BUVjael2CWZFD6vksJdWCWiUAmjC9o=
|
||||
google.golang.org/api v0.189.0 h1:equMo30LypAkdkLMBqfeIqtyAnlyig1JSZArl4XPwdI=
|
||||
google.golang.org/api v0.189.0/go.mod h1:FLWGJKb0hb+pU2j+rJqwbnsF+ym+fQs73rbJ+KAUgy8=
|
||||
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=
|
||||
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
|
||||
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-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y=
|
||||
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2 h1:rIo7ocm2roD9DcFIX67Ym8icoGCKSARAiPljFhh5suQ=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2/go.mod h1:O1cOfN1Cy6QEYr7VxtjOyP5AdAuR0aJ/MYZaaof623Y=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c h1:lfpJ/2rWPa/kJgxyyXM8PrNnfCzcmxJ265mADgwmvLI=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
|
||||
google.golang.org/genproto v0.0.0-20240722135656-d784300faade h1:lKFsS7wpngDgSCeFn7MoLy+wBDQZ1UQIJD4UNM1Qvkg=
|
||||
google.golang.org/genproto v0.0.0-20240722135656-d784300faade/go.mod h1:FfBgJBJg9GcpPvKIuHSZ/aE1g2ecGL74upMzGZjiGEY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d h1:kHjw/5UfflP/L5EbledDrcG4C2597RtymmGRZvHiCuY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d/go.mod h1:mw8MG/Qz5wfgYr6VqVCiZcHe/GJEfI+oGGDCohaVgB0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240722135656-d784300faade h1:oCRSWfwGXQsqlVdErcyTt4A93Y8fo0/9D4b1gnI++qo=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240722135656-d784300faade/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
|
||||
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=
|
||||
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.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
|
||||
google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
|
||||
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
|
||||
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
|
||||
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=
|
||||
@@ -677,16 +551,12 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
@@ -696,4 +566,3 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
|
||||
@@ -91,12 +91,12 @@ func (m *Meter) SSHSigned(p provisioner.Interface, err error) {
|
||||
incrProvisionerCounter(m.ssh.signed, p, err)
|
||||
}
|
||||
|
||||
// SSHAuthorized implements [authority.Meter] for [Meter].
|
||||
// SSHWebhookAuthorized implements [authority.Meter] for [Meter].
|
||||
func (m *Meter) SSHWebhookAuthorized(p provisioner.Interface, err error) {
|
||||
incrProvisionerCounter(m.ssh.webhookAuthorized, p, err)
|
||||
}
|
||||
|
||||
// SSHEnriched implements [authority.Meter] for [Meter].
|
||||
// SSHWebhookEnriched implements [authority.Meter] for [Meter].
|
||||
func (m *Meter) SSHWebhookEnriched(p provisioner.Interface, err error) {
|
||||
incrProvisionerCounter(m.ssh.webhookEnriched, p, err)
|
||||
}
|
||||
@@ -116,12 +116,12 @@ func (m *Meter) X509Signed(p provisioner.Interface, err error) {
|
||||
incrProvisionerCounter(m.x509.signed, p, err)
|
||||
}
|
||||
|
||||
// X509Authorized implements [authority.Meter] for [Meter].
|
||||
// X509WebhookAuthorized implements [authority.Meter] for [Meter].
|
||||
func (m *Meter) X509WebhookAuthorized(p provisioner.Interface, err error) {
|
||||
incrProvisionerCounter(m.x509.webhookAuthorized, p, err)
|
||||
}
|
||||
|
||||
// X509Enriched implements [authority.Meter] for [Meter].
|
||||
// X509WebhookEnriched implements [authority.Meter] for [Meter].
|
||||
func (m *Meter) X509WebhookEnriched(p provisioner.Interface, err error) {
|
||||
incrProvisionerCounter(m.x509.webhookEnriched, p, err)
|
||||
}
|
||||
|
||||
@@ -270,7 +270,7 @@ func (e *NamePolicyEngine) IsSSHCertificateAllowed(cert *ssh.Certificate) error
|
||||
return e.validateNames(dnsNames, ips, emails, []*url.URL{}, principals)
|
||||
}
|
||||
|
||||
// splitPrincipals splits SSH certificate principals into DNS names, emails and usernames.
|
||||
// splitSSHPrincipals splits SSH certificate principals into DNS names, emails and usernames.
|
||||
func splitSSHPrincipals(cert *ssh.Certificate) (dnsNames []string, ips []net.IP, emails, principals []string, err error) {
|
||||
dnsNames = []string{}
|
||||
ips = []net.IP{}
|
||||
|
||||
@@ -97,7 +97,7 @@ func route(r api.Router, middleware func(next http.HandlerFunc) http.HandlerFunc
|
||||
func Get(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := decodeRequest(r)
|
||||
if err != nil {
|
||||
fail(w, fmt.Errorf("invalid scep get request: %w", err))
|
||||
fail(w, r, fmt.Errorf("invalid scep get request: %w", err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -116,18 +116,18 @@ func Get(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
fail(w, fmt.Errorf("scep get request failed: %w", err))
|
||||
fail(w, r, fmt.Errorf("scep get request failed: %w", err))
|
||||
return
|
||||
}
|
||||
|
||||
writeResponse(w, res)
|
||||
writeResponse(w, r, res)
|
||||
}
|
||||
|
||||
// Post handles all SCEP POST requests
|
||||
func Post(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := decodeRequest(r)
|
||||
if err != nil {
|
||||
fail(w, fmt.Errorf("invalid scep post request: %w", err))
|
||||
fail(w, r, fmt.Errorf("invalid scep post request: %w", err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -140,11 +140,11 @@ func Post(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
fail(w, fmt.Errorf("scep post request failed: %w", err))
|
||||
fail(w, r, fmt.Errorf("scep post request failed: %w", err))
|
||||
return
|
||||
}
|
||||
|
||||
writeResponse(w, res)
|
||||
writeResponse(w, r, res)
|
||||
}
|
||||
|
||||
func decodeRequest(r *http.Request) (request, error) {
|
||||
@@ -274,7 +274,7 @@ func lookupProvisioner(next http.HandlerFunc) http.HandlerFunc {
|
||||
name := chi.URLParam(r, "provisionerName")
|
||||
provisionerName, err := url.PathUnescape(name)
|
||||
if err != nil {
|
||||
fail(w, fmt.Errorf("error url unescaping provisioner name '%s'", name))
|
||||
fail(w, r, fmt.Errorf("error url unescaping provisioner name '%s'", name))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -282,13 +282,13 @@ func lookupProvisioner(next http.HandlerFunc) http.HandlerFunc {
|
||||
auth := authority.MustFromContext(ctx)
|
||||
p, err := auth.LoadProvisionerByName(provisionerName)
|
||||
if err != nil {
|
||||
fail(w, err)
|
||||
fail(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
prov, ok := p.(*provisioner.SCEP)
|
||||
if !ok {
|
||||
fail(w, errors.New("provisioner must be of type SCEP"))
|
||||
fail(w, r, errors.New("provisioner must be of type SCEP"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -387,9 +387,10 @@ func PKIOperation(ctx context.Context, req request) (Response, error) {
|
||||
if msg.MessageType == smallscep.PKCSReq || msg.MessageType == smallscep.RenewalReq {
|
||||
if err := auth.ValidateChallenge(ctx, csr, challengePassword, transactionID); err != nil {
|
||||
if errors.Is(err, provisioner.ErrSCEPChallengeInvalid) {
|
||||
return createFailureResponse(ctx, csr, msg, smallscep.BadRequest, err)
|
||||
return createFailureResponse(ctx, csr, msg, smallscep.BadRequest, err.Error(), err)
|
||||
}
|
||||
return createFailureResponse(ctx, csr, msg, smallscep.BadRequest, errors.New("failed validating challenge password"))
|
||||
scepErr := errors.New("failed validating challenge password")
|
||||
return createFailureResponse(ctx, csr, msg, smallscep.BadRequest, scepErr.Error(), scepErr)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -407,7 +408,7 @@ func PKIOperation(ctx context.Context, req request) (Response, error) {
|
||||
// TODO(hs): ignore this error case? It's not critical if the notification fails; but logging it might be good
|
||||
_ = notifyErr
|
||||
}
|
||||
return createFailureResponse(ctx, csr, msg, smallscep.BadRequest, fmt.Errorf("error when signing new certificate: %w", err))
|
||||
return createFailureResponse(ctx, csr, msg, smallscep.BadRequest, "internal server error; please see the certificate authority logs for more info", fmt.Errorf("error when signing new certificate: %w", err))
|
||||
}
|
||||
|
||||
if notifyErr := auth.NotifySuccess(ctx, csr, certRep.Certificate, transactionID); notifyErr != nil {
|
||||
@@ -429,9 +430,9 @@ func formatCapabilities(caps []string) []byte {
|
||||
}
|
||||
|
||||
// writeResponse writes a SCEP response back to the SCEP client.
|
||||
func writeResponse(w http.ResponseWriter, res Response) {
|
||||
func writeResponse(w http.ResponseWriter, r *http.Request, res Response) {
|
||||
if res.Error != nil {
|
||||
log.Error(w, res.Error)
|
||||
log.Error(w, r, res.Error)
|
||||
}
|
||||
|
||||
if res.Certificate != nil {
|
||||
@@ -442,15 +443,15 @@ func writeResponse(w http.ResponseWriter, res Response) {
|
||||
_, _ = w.Write(res.Data)
|
||||
}
|
||||
|
||||
func fail(w http.ResponseWriter, err error) {
|
||||
log.Error(w, err)
|
||||
func fail(w http.ResponseWriter, r *http.Request, err error) {
|
||||
log.Error(w, r, err)
|
||||
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
func createFailureResponse(ctx context.Context, csr *x509.CertificateRequest, msg *scep.PKIMessage, info smallscep.FailInfo, failError error) (Response, error) {
|
||||
func createFailureResponse(ctx context.Context, csr *x509.CertificateRequest, msg *scep.PKIMessage, info smallscep.FailInfo, infoText string, failError error) (Response, error) {
|
||||
auth := scep.MustFromContext(ctx)
|
||||
certRepMsg, err := auth.CreateFailureResponse(ctx, csr, msg, scep.FailInfoName(info), failError.Error())
|
||||
certRepMsg, err := auth.CreateFailureResponse(ctx, csr, msg, scep.FailInfoName(info), infoText)
|
||||
if err != nil {
|
||||
return Response{}, err
|
||||
}
|
||||
|
||||
@@ -308,7 +308,7 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m
|
||||
|
||||
certChain, err := a.signAuth.SignWithContext(ctx, csr, opts, signOps...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error generating certificate for order: %w", err)
|
||||
return nil, fmt.Errorf("error generating certificate: %w", err)
|
||||
}
|
||||
|
||||
// take the issued certificate (only); https://tools.ietf.org/html/rfc8894#section-3.3.2
|
||||
|
||||
@@ -37,19 +37,21 @@ func (o *Options) Validate() error {
|
||||
switch {
|
||||
case len(o.Intermediates) == 0:
|
||||
return errors.New("no intermediate certificate available for SCEP authority")
|
||||
case o.Signer == nil:
|
||||
return errors.New("no signer available for SCEP authority")
|
||||
case o.SignerCert == nil:
|
||||
return errors.New("no signer certificate available for SCEP authority")
|
||||
}
|
||||
|
||||
// check if the signer (intermediate CA) certificate has the same public key as
|
||||
// the signer. According to the RFC it seems valid to have different keys for
|
||||
// the intermediate and the CA signing new certificates, so this might change
|
||||
// in the future.
|
||||
signerPublicKey := o.Signer.Public().(comparablePublicKey)
|
||||
if !signerPublicKey.Equal(o.SignerCert.PublicKey) {
|
||||
return errors.New("mismatch between signer certificate and public key")
|
||||
// the signer is optional, but if it's set, its public key must match the signer
|
||||
// certificate public key.
|
||||
if o.Signer != nil {
|
||||
// check if the signer (intermediate CA) certificate has the same public key as
|
||||
// the signer. According to the RFC it seems valid to have different keys for
|
||||
// the intermediate and the CA signing new certificates, so this might change
|
||||
// in the future.
|
||||
signerPublicKey := o.Signer.Public().(comparablePublicKey)
|
||||
if !signerPublicKey.Equal(o.SignerCert.PublicKey) {
|
||||
return errors.New("mismatch between signer certificate and public key")
|
||||
}
|
||||
}
|
||||
|
||||
// decrypter can be nil in case a signing only key is used; validation complete.
|
||||
|
||||
@@ -29,7 +29,7 @@ var (
|
||||
oidSCEPsenderNonce = asn1.ObjectIdentifier{2, 16, 840, 1, 113733, 1, 9, 5}
|
||||
oidSCEPrecipientNonce = asn1.ObjectIdentifier{2, 16, 840, 1, 113733, 1, 9, 6}
|
||||
oidSCEPtransactionID = asn1.ObjectIdentifier{2, 16, 840, 1, 113733, 1, 9, 7}
|
||||
oidSCEPfailInfoText = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 24}
|
||||
oidSCEPfailInfoText = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 24, 1}
|
||||
//oidChallengePassword = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 7}
|
||||
)
|
||||
|
||||
|
||||
@@ -108,10 +108,10 @@ var DefaultSSHTemplateData = map[string]string{
|
||||
{{- end }}
|
||||
{{- if or .User.GOOS "none" | eq "windows" }}
|
||||
UserKnownHostsFile "{{.User.StepPath}}\ssh\known_hosts"
|
||||
ProxyCommand C:\Windows\System32\cmd.exe /c step ssh proxycommand{{- if .User.Context }} --context {{ .User.Context }}{{- end }}{{- if .User.Provisioner }} --provisioner {{ .User.Provisioner }}{{- end }} %r %h %p
|
||||
ProxyCommand C:\Windows\System32\cmd.exe /c step ssh proxycommand{{- if .User.Context }} --context {{ .User.Context }}{{- end }}{{- if .User.Console}} --console {{- end }}{{- if .User.Provisioner }} --provisioner {{ .User.Provisioner }}{{- end }} %r %h %p
|
||||
{{- else }}
|
||||
UserKnownHostsFile "{{.User.StepPath}}/ssh/known_hosts"
|
||||
ProxyCommand step ssh proxycommand{{- if .User.Context }} --context {{ .User.Context }}{{- end }}{{- if .User.Provisioner }} --provisioner {{ .User.Provisioner }}{{- end }} %r %h %p
|
||||
ProxyCommand step ssh proxycommand{{- if .User.Context }} --context {{ .User.Context }}{{- end }}{{- if .User.Console}} --console {{- end }}{{- if .User.Provisioner }} --provisioner {{ .User.Provisioner }}{{- end }} %r %h %p
|
||||
{{- end }}
|
||||
`,
|
||||
|
||||
|
||||
277
test/integration/scep/common_test.go
Normal file
277
test/integration/scep/common_test.go
Normal file
@@ -0,0 +1,277 @@
|
||||
package sceptest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/smallstep/pkcs7"
|
||||
"github.com/smallstep/scep"
|
||||
"go.step.sm/crypto/minica"
|
||||
"go.step.sm/crypto/x509util"
|
||||
|
||||
"github.com/smallstep/certificates/ca"
|
||||
"github.com/smallstep/certificates/cas/apiv1"
|
||||
)
|
||||
|
||||
func newCAClient(t *testing.T, caURL, rootFilepath string) *ca.Client {
|
||||
caClient, err := ca.NewClient(
|
||||
caURL,
|
||||
ca.WithRootFile(rootFilepath),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
return caClient
|
||||
}
|
||||
|
||||
func requireHealthyCA(t *testing.T, caClient *ca.Client) {
|
||||
t.Helper()
|
||||
// Wait for CA
|
||||
time.Sleep(time.Second)
|
||||
|
||||
ctx := context.Background()
|
||||
healthResponse, err := caClient.HealthWithContext(ctx)
|
||||
require.NoError(t, err)
|
||||
if assert.NotNil(t, healthResponse) {
|
||||
require.Equal(t, "ok", healthResponse.Status)
|
||||
}
|
||||
}
|
||||
|
||||
// reservePort "reserves" a TCP port by opening a listener on a random
|
||||
// port and immediately closing it. The port can then be assumed to be
|
||||
// available for running a server on.
|
||||
func reservePort(t *testing.T) (host, port string) {
|
||||
t.Helper()
|
||||
l, err := net.Listen("tcp", ":0")
|
||||
require.NoError(t, err)
|
||||
|
||||
address := l.Addr().String()
|
||||
err = l.Close()
|
||||
require.NoError(t, err)
|
||||
|
||||
host, port, err = net.SplitHostPort(address)
|
||||
require.NoError(t, err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type client struct {
|
||||
caURL string
|
||||
caCert *x509.Certificate
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
func createSCEPClient(t *testing.T, caURL string, root *x509.Certificate) *client {
|
||||
t.Helper()
|
||||
trustedRoots := x509.NewCertPool()
|
||||
trustedRoots.AddCert(root)
|
||||
transport := http.DefaultTransport.(*http.Transport).Clone()
|
||||
transport.TLSClientConfig = &tls.Config{
|
||||
RootCAs: trustedRoots,
|
||||
}
|
||||
httpClient := &http.Client{
|
||||
Transport: transport,
|
||||
}
|
||||
return &client{
|
||||
caURL: caURL,
|
||||
httpClient: httpClient,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *client) getCACert(t *testing.T) error {
|
||||
// return early if CA certificate already available
|
||||
if c.caCert != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
resp, err := c.httpClient.Get(fmt.Sprintf("%s?operation=GetCACert&message=test", c.caURL))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed get request: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed reading response body: %w", err)
|
||||
}
|
||||
|
||||
t.Log(string(body))
|
||||
|
||||
// SCEP CA/RA certificate selection. If there's only a single certificate, it will
|
||||
// be used as the CA certificate at all times. If there's multiple, the first certificate
|
||||
// is assumed to be the certificate of the recipient to encrypt messages to.
|
||||
switch ct := resp.Header.Get("Content-Type"); ct {
|
||||
case "application/x-x509-ca-cert":
|
||||
cert, err := x509.ParseCertificate(body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed parsing response body: %w", err)
|
||||
}
|
||||
if _, ok := cert.PublicKey.(*rsa.PublicKey); !ok {
|
||||
return fmt.Errorf("certificate has unexpected public key type %T", cert.PublicKey)
|
||||
}
|
||||
c.caCert = cert
|
||||
case "application/x-x509-ca-ra-cert":
|
||||
certs, err := scep.CACerts(body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed parsing response body: %w", err)
|
||||
}
|
||||
cert := certs[0]
|
||||
if _, ok := cert.PublicKey.(*rsa.PublicKey); !ok {
|
||||
return fmt.Errorf("certificate has unexpected public key type %T", cert.PublicKey)
|
||||
}
|
||||
c.caCert = cert
|
||||
default:
|
||||
return fmt.Errorf("unexpected content-type value %q", ct)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *client) requestCertificate(t *testing.T, commonName string, sans []string) (*x509.Certificate, error) {
|
||||
if err := c.getCACert(t); err != nil {
|
||||
return nil, fmt.Errorf("failed getting CA certificate: %w", err)
|
||||
}
|
||||
|
||||
signer, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed creating SCEP private key: %w", err)
|
||||
}
|
||||
|
||||
csr, err := x509util.CreateCertificateRequest(commonName, sans, signer)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed creating CSR: %w", err)
|
||||
}
|
||||
|
||||
tmpl := &x509.Certificate{
|
||||
Subject: csr.Subject,
|
||||
PublicKey: signer.Public(),
|
||||
SerialNumber: big.NewInt(1),
|
||||
NotBefore: time.Now().Add(-1 * time.Hour),
|
||||
NotAfter: time.Now().Add(1 * time.Hour),
|
||||
DNSNames: csr.DNSNames,
|
||||
IPAddresses: csr.IPAddresses,
|
||||
EmailAddresses: csr.EmailAddresses,
|
||||
URIs: csr.URIs,
|
||||
}
|
||||
|
||||
selfSigned, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, signer.Public(), signer)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed creating self signed certificate: %w", err)
|
||||
}
|
||||
selfSignedCertificate, err := x509.ParseCertificate(selfSigned)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed parsing self signed certificate: %w", err)
|
||||
}
|
||||
|
||||
msgTmpl := &scep.PKIMessage{
|
||||
TransactionID: "test-1",
|
||||
MessageType: scep.PKCSReq,
|
||||
SenderNonce: []byte("test-nonce-1"),
|
||||
Recipients: []*x509.Certificate{c.caCert},
|
||||
SignerCert: selfSignedCertificate,
|
||||
SignerKey: signer,
|
||||
}
|
||||
|
||||
msg, err := scep.NewCSRRequest(csr, msgTmpl)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed creating SCEP PKCSReq message: %w", err)
|
||||
}
|
||||
|
||||
t.Log(string(msg.Raw))
|
||||
|
||||
u, err := url.Parse(c.caURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed parsing CA URL: %w", err)
|
||||
}
|
||||
|
||||
opURL := u.ResolveReference(&url.URL{RawQuery: fmt.Sprintf("operation=PKIOperation&message=%s", url.QueryEscape(base64.StdEncoding.EncodeToString(msg.Raw)))})
|
||||
resp, err := c.httpClient.Get(opURL.String())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed get request: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if ct := resp.Header.Get("Content-Type"); ct != "application/x-pki-message" {
|
||||
return nil, fmt.Errorf("received unexpected content type %q", ct)
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed reading response body: %w", err)
|
||||
}
|
||||
|
||||
t.Log(string(body))
|
||||
|
||||
signedData, err := pkcs7.Parse(body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed parsing response body: %w", err)
|
||||
}
|
||||
|
||||
// TODO: verify the signature?
|
||||
|
||||
p7, err := pkcs7.Parse(signedData.Content)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed decrypting inner p7: %w", err)
|
||||
}
|
||||
|
||||
content, err := p7.Decrypt(selfSignedCertificate, signer)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed decrypting response: %w", err)
|
||||
}
|
||||
|
||||
p7, err = pkcs7.Parse(content)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed parsing p7 content: %w", err)
|
||||
}
|
||||
|
||||
cert := p7.Certificates[0]
|
||||
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
type testCAS struct {
|
||||
ca *minica.CA
|
||||
}
|
||||
|
||||
func (c *testCAS) CreateCertificate(req *apiv1.CreateCertificateRequest) (*apiv1.CreateCertificateResponse, error) {
|
||||
cert, err := c.ca.SignCSR(req.CSR)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed signing CSR: %w", err)
|
||||
}
|
||||
|
||||
return &apiv1.CreateCertificateResponse{
|
||||
Certificate: cert,
|
||||
CertificateChain: []*x509.Certificate{cert, c.ca.Intermediate},
|
||||
}, nil
|
||||
}
|
||||
func (c *testCAS) RenewCertificate(req *apiv1.RenewCertificateRequest) (*apiv1.RenewCertificateResponse, error) {
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (c *testCAS) RevokeCertificate(req *apiv1.RevokeCertificateRequest) (*apiv1.RevokeCertificateResponse, error) {
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (c *testCAS) GetCertificateAuthority(req *apiv1.GetCertificateAuthorityRequest) (*apiv1.GetCertificateAuthorityResponse, error) {
|
||||
return &apiv1.GetCertificateAuthorityResponse{
|
||||
RootCertificate: c.ca.Root,
|
||||
IntermediateCertificates: []*x509.Certificate{c.ca.Intermediate},
|
||||
}, nil
|
||||
}
|
||||
|
||||
var _ apiv1.CertificateAuthorityService = (*testCAS)(nil)
|
||||
var _ apiv1.CertificateAuthorityGetter = (*testCAS)(nil)
|
||||
149
test/integration/scep/decrypter_cas_test.go
Normal file
149
test/integration/scep/decrypter_cas_test.go
Normal file
@@ -0,0 +1,149 @@
|
||||
package sceptest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"net"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"go.step.sm/crypto/keyutil"
|
||||
"go.step.sm/crypto/minica"
|
||||
"go.step.sm/crypto/pemutil"
|
||||
|
||||
"github.com/smallstep/certificates/authority/config"
|
||||
"github.com/smallstep/certificates/authority/provisioner"
|
||||
"github.com/smallstep/certificates/ca"
|
||||
"github.com/smallstep/certificates/cas/apiv1"
|
||||
)
|
||||
|
||||
func TestIssuesCertificateUsingSCEPWithDecrypterAndUpstreamCAS(t *testing.T) {
|
||||
signer, err := keyutil.GenerateSigner("EC", "P-256", 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
dir := t.TempDir()
|
||||
m, err := minica.New(minica.WithName("Step E2E | SCEP Decrypter w/ Upstream CAS"), minica.WithGetSignerFunc(func() (crypto.Signer, error) {
|
||||
return signer, nil
|
||||
}))
|
||||
require.NoError(t, err)
|
||||
|
||||
rootFilepath := filepath.Join(dir, "root.crt")
|
||||
_, err = pemutil.Serialize(m.Root, pemutil.WithFilename(rootFilepath))
|
||||
require.NoError(t, err)
|
||||
|
||||
intermediateCertFilepath := filepath.Join(dir, "intermediate.crt")
|
||||
_, err = pemutil.Serialize(m.Intermediate, pemutil.WithFilename(intermediateCertFilepath))
|
||||
require.NoError(t, err)
|
||||
|
||||
intermediateKeyFilepath := filepath.Join(dir, "intermediate.key")
|
||||
_, err = pemutil.Serialize(m.Signer, pemutil.WithFilename(intermediateKeyFilepath))
|
||||
require.NoError(t, err)
|
||||
|
||||
decrypterKey, err := keyutil.GenerateKey("RSA", "", 2048)
|
||||
require.NoError(t, err)
|
||||
|
||||
decrypter, ok := decrypterKey.(crypto.Decrypter)
|
||||
require.True(t, ok)
|
||||
|
||||
decrypterCertifiate, err := m.Sign(&x509.Certificate{
|
||||
Subject: pkix.Name{CommonName: "decrypter"},
|
||||
PublicKey: decrypter.Public(),
|
||||
SerialNumber: big.NewInt(1),
|
||||
NotBefore: time.Now().Add(-1 * time.Hour),
|
||||
NotAfter: time.Now().Add(1 * time.Hour),
|
||||
DNSNames: []string{"decrypter"},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
b, err := pemutil.Serialize(decrypterCertifiate)
|
||||
require.NoError(t, err)
|
||||
decrypterCertificatePEMBytes := pem.EncodeToMemory(b)
|
||||
|
||||
b, err = pemutil.Serialize(decrypter, pemutil.WithPassword([]byte("1234")))
|
||||
require.NoError(t, err)
|
||||
decrypterKeyPEMBytes := pem.EncodeToMemory(b)
|
||||
|
||||
// get a random address to listen on and connect to; currently no nicer way to get one before starting the server
|
||||
// TODO(hs): find/implement a nicer way to expose the CA URL, similar to how e.g. httptest.Server exposes it?
|
||||
host, port := reservePort(t)
|
||||
|
||||
prov := &provisioner.SCEP{
|
||||
ID: "scep",
|
||||
Name: "scep",
|
||||
Type: "SCEP",
|
||||
ForceCN: false,
|
||||
ChallengePassword: "",
|
||||
EncryptionAlgorithmIdentifier: 2,
|
||||
MinimumPublicKeyLength: 2048,
|
||||
Claims: &config.GlobalProvisionerClaims,
|
||||
DecrypterCertificate: decrypterCertificatePEMBytes,
|
||||
DecrypterKeyPEM: decrypterKeyPEMBytes,
|
||||
DecrypterKeyPassword: "1234",
|
||||
}
|
||||
|
||||
err = prov.Init(provisioner.Config{})
|
||||
require.NoError(t, err)
|
||||
|
||||
apiv1.Register("test-scep-cas", func(_ context.Context, opts apiv1.Options) (apiv1.CertificateAuthorityService, error) {
|
||||
return &testCAS{
|
||||
ca: m,
|
||||
}, nil
|
||||
})
|
||||
|
||||
cfg := &config.Config{
|
||||
Address: net.JoinHostPort(host, port), // reuse the address that was just "reserved"
|
||||
DNSNames: []string{"127.0.0.1", "[::1]", "localhost"},
|
||||
AuthorityConfig: &config.AuthConfig{
|
||||
Options: &apiv1.Options{
|
||||
AuthorityID: "stepca-test-scep",
|
||||
Type: "test-scep-cas",
|
||||
CertificateAuthority: "test-cas",
|
||||
},
|
||||
AuthorityID: "stepca-test-scep",
|
||||
DeploymentType: "standalone-test",
|
||||
Provisioners: provisioner.List{prov},
|
||||
},
|
||||
Logger: json.RawMessage(`{"format": "text"}`),
|
||||
}
|
||||
c, err := ca.New(cfg)
|
||||
require.NoError(t, err)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
err = c.Run()
|
||||
require.ErrorIs(t, err, http.ErrServerClosed)
|
||||
}()
|
||||
|
||||
// instantiate a client for the CA running at the random address
|
||||
caClient := newCAClient(t, fmt.Sprintf("https://localhost:%s", port), rootFilepath)
|
||||
requireHealthyCA(t, caClient)
|
||||
|
||||
scepClient := createSCEPClient(t, fmt.Sprintf("https://localhost:%s/scep/scep", port), m.Root)
|
||||
cert, err := scepClient.requestCertificate(t, "test.localhost", []string{"test.localhost"})
|
||||
assert.NoError(t, err)
|
||||
require.NotNil(t, cert)
|
||||
|
||||
assert.Equal(t, "test.localhost", cert.Subject.CommonName)
|
||||
assert.Equal(t, "Step E2E | SCEP Decrypter w/ Upstream CAS Intermediate CA", cert.Issuer.CommonName)
|
||||
|
||||
// done testing; stop and wait for the server to quit
|
||||
err = c.Stop()
|
||||
require.NoError(t, err)
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user