forked from TrueCloudLab/certificates
Merge branch 'master' into herman/ignore-empty-acme-meta
This commit is contained in:
commit
02d679e160
37 changed files with 2004 additions and 277 deletions
75
.github/workflows/release.yml
vendored
75
.github/workflows/release.yml
vendored
|
@ -13,10 +13,14 @@ jobs:
|
||||||
|
|
||||||
create_release:
|
create_release:
|
||||||
name: Create Release
|
name: Create Release
|
||||||
#needs: ci
|
needs: ci
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
DOCKER_IMAGE: smallstep/step-ca
|
||||||
outputs:
|
outputs:
|
||||||
|
version: ${{ steps.extract-tag.outputs.VERSION }}
|
||||||
is_prerelease: ${{ steps.is_prerelease.outputs.IS_PRERELEASE }}
|
is_prerelease: ${{ steps.is_prerelease.outputs.IS_PRERELEASE }}
|
||||||
|
docker_tags: ${{ env.DOCKER_TAGS }}
|
||||||
steps:
|
steps:
|
||||||
- name: Is Pre-release
|
- name: Is Pre-release
|
||||||
id: is_prerelease
|
id: is_prerelease
|
||||||
|
@ -25,7 +29,17 @@ jobs:
|
||||||
echo ${{ github.ref }} | grep "\-rc.*"
|
echo ${{ github.ref }} | grep "\-rc.*"
|
||||||
OUT=$?
|
OUT=$?
|
||||||
if [ $OUT -eq 0 ]; then IS_PRERELEASE=true; else IS_PRERELEASE=false; fi
|
if [ $OUT -eq 0 ]; then IS_PRERELEASE=true; else IS_PRERELEASE=false; fi
|
||||||
echo "::set-output name=IS_PRERELEASE::${IS_PRERELEASE}"
|
echo "IS_PRERELEASE=${IS_PRERELEASE}" >> ${GITHUB_OUTPUT}
|
||||||
|
- name: Extract Tag Names
|
||||||
|
id: extract-tag
|
||||||
|
run: |
|
||||||
|
VERSION=${GITHUB_REF#refs/tags/v}
|
||||||
|
echo "VERSION=${VERSION}" >> ${GITHUB_OUTPUT}
|
||||||
|
echo "DOCKER_TAGS=${{ env.DOCKER_IMAGE }}:${VERSION}" >> ${GITHUB_ENV}
|
||||||
|
- name: Add Latest Tag
|
||||||
|
if: steps.is_prerelease.outputs.IS_PRERELEASE == 'false'
|
||||||
|
run: |
|
||||||
|
echo "DOCKER_TAGS=${{ env.DOCKER_TAGS }},${{ env.DOCKER_IMAGE }}:latest" >> ${GITHUB_ENV}
|
||||||
- name: Create Release
|
- name: Create Release
|
||||||
id: create_release
|
id: create_release
|
||||||
uses: actions/create-release@v1
|
uses: actions/create-release@v1
|
||||||
|
@ -39,8 +53,11 @@ jobs:
|
||||||
|
|
||||||
goreleaser:
|
goreleaser:
|
||||||
name: Upload Assets To Github w/ goreleaser
|
name: Upload Assets To Github w/ goreleaser
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-latest
|
||||||
needs: create_release
|
needs: create_release
|
||||||
|
permissions:
|
||||||
|
id-token: write
|
||||||
|
contents: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
@ -50,17 +67,14 @@ jobs:
|
||||||
go-version: 1.19
|
go-version: 1.19
|
||||||
check-latest: true
|
check-latest: true
|
||||||
- name: Install cosign
|
- name: Install cosign
|
||||||
uses: sigstore/cosign-installer@v2.7.0
|
uses: sigstore/cosign-installer@v2
|
||||||
with:
|
with:
|
||||||
cosign-release: 'v1.12.1'
|
cosign-release: 'v1.13.1'
|
||||||
- name: Write cosign key to disk
|
|
||||||
id: write_key
|
|
||||||
run: echo "${{ secrets.COSIGN_KEY }}" > "/tmp/cosign.key"
|
|
||||||
- name: Get Release Date
|
- name: Get Release Date
|
||||||
id: release_date
|
id: release_date
|
||||||
run: |
|
run: |
|
||||||
RELEASE_DATE=$(date +"%y-%m-%d")
|
RELEASE_DATE=$(date +"%y-%m-%d")
|
||||||
echo "::set-output name=RELEASE_DATE::${RELEASE_DATE}"
|
echo "RELEASE_DATE=${RELEASE_DATE}" >> ${GITHUB_ENV}
|
||||||
- name: Run GoReleaser
|
- name: Run GoReleaser
|
||||||
uses: goreleaser/goreleaser-action@v3
|
uses: goreleaser/goreleaser-action@v3
|
||||||
with:
|
with:
|
||||||
|
@ -68,34 +82,19 @@ jobs:
|
||||||
args: release --rm-dist
|
args: release --rm-dist
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GORELEASER_PAT }}
|
GITHUB_TOKEN: ${{ secrets.GORELEASER_PAT }}
|
||||||
COSIGN_PWD: ${{ secrets.COSIGN_PWD }}
|
RELEASE_DATE: ${{ env.RELEASE_DATE }}
|
||||||
RELEASE_DATE: ${{ steps.release_date.outputs.RELEASE_DATE }}
|
COSIGN_EXPERIMENTAL: 1
|
||||||
|
|
||||||
build_upload_docker:
|
build_upload_docker:
|
||||||
name: Build & Upload Docker Images
|
name: Build & Upload Docker Images
|
||||||
runs-on: ubuntu-20.04
|
needs: create_release
|
||||||
needs: ci
|
permissions:
|
||||||
steps:
|
id-token: write
|
||||||
- name: Checkout
|
contents: write
|
||||||
uses: actions/checkout@v3
|
uses: smallstep/workflows/.github/workflows/docker-buildx-push.yml@main
|
||||||
- name: Setup Go
|
with:
|
||||||
uses: actions/setup-go@v3
|
platforms: linux/amd64,linux/386,linux/arm,linux/arm64
|
||||||
with:
|
tags: ${{ needs.create_release.outputs.docker_tags }}
|
||||||
go-version: '1.19'
|
docker_image: smallstep/step-ca
|
||||||
check-latest: true
|
docker_file: docker/Dockerfile.step-ca
|
||||||
- name: Install cosign
|
secrets: inherit
|
||||||
uses: sigstore/cosign-installer@v1.1.0
|
|
||||||
with:
|
|
||||||
cosign-release: 'v1.1.0'
|
|
||||||
- name: Write cosign key to disk
|
|
||||||
id: write_key
|
|
||||||
run: echo "${{ secrets.COSIGN_KEY }}" > "/tmp/cosign.key"
|
|
||||||
- name: Build
|
|
||||||
id: build
|
|
||||||
run: |
|
|
||||||
PATH=$PATH:/usr/local/go/bin:/home/admin/go/bin
|
|
||||||
make docker-artifacts
|
|
||||||
env:
|
|
||||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
|
||||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
|
||||||
COSIGN_PWD: ${{ secrets.COSIGN_PWD }}
|
|
||||||
|
|
|
@ -87,8 +87,9 @@ checksum:
|
||||||
|
|
||||||
signs:
|
signs:
|
||||||
- cmd: cosign
|
- cmd: cosign
|
||||||
stdin: '{{ .Env.COSIGN_PWD }}'
|
signature: "${artifact}.sig"
|
||||||
args: ["sign-blob", "-key=/tmp/cosign.key", "-output-signature=${signature}", "${artifact}"]
|
certificate: "${artifact}.pem"
|
||||||
|
args: ["sign-blob", "--oidc-issuer=https://token.actions.githubusercontent.com", "--output-certificate=${certificate}", "--output-signature=${signature}", "${artifact}"]
|
||||||
artifacts: all
|
artifacts: all
|
||||||
|
|
||||||
snapshot:
|
snapshot:
|
||||||
|
@ -153,9 +154,9 @@ release:
|
||||||
Below is an example using `cosign` to verify a release artifact:
|
Below is an example using `cosign` to verify a release artifact:
|
||||||
|
|
||||||
```
|
```
|
||||||
cosign verify-blob \
|
COSIGN_EXPERIMENTAL=1 cosign verify-blob \
|
||||||
-key https://raw.githubusercontent.com/smallstep/certificates/master/cosign.pub \
|
--certificate ~/Downloads/step-ca_darwin_{{ .Version }}_amd64.tar.gz.sig.pem \
|
||||||
-signature ~/Downloads/step-ca_darwin_{{ .Version }}_amd64.tar.gz.sig
|
--signature ~/Downloads/step-ca_darwin_{{ .Version }}_amd64.tar.gz.sig \
|
||||||
~/Downloads/step-ca_darwin_{{ .Version }}_amd64.tar.gz
|
~/Downloads/step-ca_darwin_{{ .Version }}_amd64.tar.gz
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env sh
|
||||||
read -r firstline < .VERSION
|
read -r firstline < .VERSION
|
||||||
last_half="${firstline##*tag: }"
|
last_half="${firstline##*tag: }"
|
||||||
if [[ ${last_half::1} == "v" ]]; then
|
if [[ ${last_half::1} == "v" ]]; then
|
||||||
|
|
|
@ -21,6 +21,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||||
- Added name constraints evaluation and enforcement when issuing or renewing
|
- Added name constraints evaluation and enforcement when issuing or renewing
|
||||||
X.509 certificates.
|
X.509 certificates.
|
||||||
- Added provisioner webhooks for augmenting template data and authorizing certificate requests before signing.
|
- Added provisioner webhooks for augmenting template data and authorizing certificate requests before signing.
|
||||||
|
- Added automatic migration of provisioners when enabling remote managment.
|
||||||
|
- Added experimental support for CRLs.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- MySQL DSN parsing issues fixed with upgrade to [smallstep/nosql@v0.5.0](https://github.com/smallstep/nosql/releases/tag/v0.5.0).
|
- MySQL DSN parsing issues fixed with upgrade to [smallstep/nosql@v0.5.0](https://github.com/smallstep/nosql/releases/tag/v0.5.0).
|
||||||
|
|
10
Makefile
10
Makefile
|
@ -79,8 +79,6 @@ $(info DEB_VERSION is $(DEB_VERSION))
|
||||||
$(info PUSHTYPE is $(PUSHTYPE))
|
$(info PUSHTYPE is $(PUSHTYPE))
|
||||||
endif
|
endif
|
||||||
|
|
||||||
include make/docker.mk
|
|
||||||
|
|
||||||
#########################################
|
#########################################
|
||||||
# Build
|
# Build
|
||||||
#########################################
|
#########################################
|
||||||
|
@ -232,11 +230,3 @@ debian: changelog
|
||||||
distclean: clean
|
distclean: clean
|
||||||
|
|
||||||
.PHONY: changelog debian distclean
|
.PHONY: changelog debian distclean
|
||||||
|
|
||||||
#################################################
|
|
||||||
# Targets for creating step artifacts
|
|
||||||
#################################################
|
|
||||||
|
|
||||||
docker-artifacts: docker-$(PUSHTYPE)
|
|
||||||
|
|
||||||
.PHONY: docker-artifacts
|
|
||||||
|
|
|
@ -44,6 +44,18 @@ const (
|
||||||
DEVICEATTEST01 ChallengeType = "device-attest-01"
|
DEVICEATTEST01 ChallengeType = "device-attest-01"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// InsecurePortHTTP01 is the port used to verify http-01 challenges. If not set it
|
||||||
|
// defaults to 80.
|
||||||
|
InsecurePortHTTP01 int
|
||||||
|
|
||||||
|
// InsecurePortTLSALPN01 is the port used to verify tls-alpn-01 challenges. If not
|
||||||
|
// set it defaults to 443.
|
||||||
|
//
|
||||||
|
// This variable can be used for testing purposes.
|
||||||
|
InsecurePortTLSALPN01 int
|
||||||
|
)
|
||||||
|
|
||||||
// Challenge represents an ACME response Challenge type.
|
// Challenge represents an ACME response Challenge type.
|
||||||
type Challenge struct {
|
type Challenge struct {
|
||||||
ID string `json:"-"`
|
ID string `json:"-"`
|
||||||
|
@ -93,6 +105,12 @@ 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 {
|
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: 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)
|
||||||
|
}
|
||||||
|
|
||||||
vc := MustClientFromContext(ctx)
|
vc := MustClientFromContext(ctx)
|
||||||
resp, err := vc.Get(u.String())
|
resp, err := vc.Get(u.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -165,7 +183,14 @@ func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSON
|
||||||
InsecureSkipVerify: true, //nolint:gosec // we expect a self-signed challenge certificate
|
InsecureSkipVerify: true, //nolint:gosec // we expect a self-signed challenge certificate
|
||||||
}
|
}
|
||||||
|
|
||||||
hostPort := net.JoinHostPort(ch.Value, "443")
|
var hostPort string
|
||||||
|
|
||||||
|
// Allow to change TLS port for testing purposes.
|
||||||
|
if port := InsecurePortTLSALPN01; port == 0 {
|
||||||
|
hostPort = net.JoinHostPort(ch.Value, "443")
|
||||||
|
} else {
|
||||||
|
hostPort = net.JoinHostPort(ch.Value, strconv.Itoa(port))
|
||||||
|
}
|
||||||
|
|
||||||
vc := MustClientFromContext(ctx)
|
vc := MustClientFromContext(ctx)
|
||||||
conn, err := vc.TLSDial("tcp", hostPort, config)
|
conn, err := vc.TLSDial("tcp", hostPort, config)
|
||||||
|
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
@ -370,6 +371,47 @@ func TestChallenge_Validate(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ok/http-01-insecure": func(t *testing.T) test {
|
||||||
|
t.Cleanup(func() {
|
||||||
|
InsecurePortHTTP01 = 0
|
||||||
|
})
|
||||||
|
|
||||||
|
ch := &Challenge{
|
||||||
|
ID: "chID",
|
||||||
|
Status: StatusPending,
|
||||||
|
Type: "http-01",
|
||||||
|
Token: "token",
|
||||||
|
Value: "zap.internal",
|
||||||
|
}
|
||||||
|
|
||||||
|
InsecurePortHTTP01 = 8080
|
||||||
|
|
||||||
|
return test{
|
||||||
|
ch: ch,
|
||||||
|
vc: &mockClient{
|
||||||
|
get: func(url string) (*http.Response, error) {
|
||||||
|
return nil, errors.New("force")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
db: &MockDB{
|
||||||
|
MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error {
|
||||||
|
assert.Equals(t, updch.ID, ch.ID)
|
||||||
|
assert.Equals(t, updch.Token, ch.Token)
|
||||||
|
assert.Equals(t, updch.Type, ch.Type)
|
||||||
|
assert.Equals(t, updch.Status, ch.Status)
|
||||||
|
assert.Equals(t, updch.Value, ch.Value)
|
||||||
|
|
||||||
|
err := NewError(ErrorConnectionType, "error doing http GET for url http://zap.internal:8080/.well-known/acme-challenge/%s: force", ch.Token)
|
||||||
|
assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error())
|
||||||
|
assert.Equals(t, updch.Error.Type, err.Type)
|
||||||
|
assert.Equals(t, updch.Error.Detail, err.Detail)
|
||||||
|
assert.Equals(t, updch.Error.Status, err.Status)
|
||||||
|
assert.Equals(t, updch.Error.Detail, err.Detail)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
"fail/dns-01": func(t *testing.T) test {
|
"fail/dns-01": func(t *testing.T) test {
|
||||||
ch := &Challenge{
|
ch := &Challenge{
|
||||||
ID: "chID",
|
ID: "chID",
|
||||||
|
@ -501,6 +543,72 @@ func TestChallenge_Validate(t *testing.T) {
|
||||||
srv, tlsDial := newTestTLSALPNServer(cert)
|
srv, tlsDial := newTestTLSALPNServer(cert)
|
||||||
srv.Start()
|
srv.Start()
|
||||||
|
|
||||||
|
return test{
|
||||||
|
ch: ch,
|
||||||
|
vc: &mockClient{
|
||||||
|
tlsDial: tlsDial,
|
||||||
|
},
|
||||||
|
db: &MockDB{
|
||||||
|
MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error {
|
||||||
|
assert.Equals(t, updch.ID, ch.ID)
|
||||||
|
assert.Equals(t, updch.Token, ch.Token)
|
||||||
|
assert.Equals(t, updch.Status, ch.Status)
|
||||||
|
assert.Equals(t, updch.Type, ch.Type)
|
||||||
|
assert.Equals(t, updch.Value, ch.Value)
|
||||||
|
assert.Equals(t, updch.Error, nil)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
srv: srv,
|
||||||
|
jwk: jwk,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ok/tls-alpn-01-insecure": func(t *testing.T) test {
|
||||||
|
t.Cleanup(func() {
|
||||||
|
InsecurePortTLSALPN01 = 0
|
||||||
|
})
|
||||||
|
|
||||||
|
ch := &Challenge{
|
||||||
|
ID: "chID",
|
||||||
|
Token: "token",
|
||||||
|
Type: "tls-alpn-01",
|
||||||
|
Status: StatusPending,
|
||||||
|
Value: "zap.internal",
|
||||||
|
}
|
||||||
|
|
||||||
|
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
|
||||||
|
expKeyAuth, err := KeyAuthorization(ch.Token, jwk)
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
expKeyAuthHash := sha256.Sum256([]byte(expKeyAuth))
|
||||||
|
|
||||||
|
cert, err := newTLSALPNValidationCert(expKeyAuthHash[:], false, true, ch.Value)
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
|
||||||
|
l, err := net.Listen("tcp", "127.0.0.1:0")
|
||||||
|
if err != nil {
|
||||||
|
if l, err = net.Listen("tcp6", "[::1]:0"); err != nil {
|
||||||
|
t.Fatalf("failed to listen on a port: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, port, err := net.SplitHostPort(l.Addr().String())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to split host port: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use an insecure port
|
||||||
|
InsecurePortTLSALPN01, err = strconv.Atoi(port)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to convert port to int: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
srv, tlsDial := newTestTLSALPNServer(cert, func(srv *httptest.Server) {
|
||||||
|
srv.Listener.Close()
|
||||||
|
srv.Listener = l
|
||||||
|
})
|
||||||
|
srv.Start()
|
||||||
|
|
||||||
return test{
|
return test{
|
||||||
ch: ch,
|
ch: ch,
|
||||||
vc: &mockClient{
|
vc: &mockClient{
|
||||||
|
@ -1248,7 +1356,7 @@ func TestDNS01Validate(t *testing.T) {
|
||||||
|
|
||||||
type tlsDialer func(network, addr string, config *tls.Config) (conn *tls.Conn, err error)
|
type tlsDialer func(network, addr string, config *tls.Config) (conn *tls.Conn, err error)
|
||||||
|
|
||||||
func newTestTLSALPNServer(validationCert *tls.Certificate) (*httptest.Server, tlsDialer) {
|
func newTestTLSALPNServer(validationCert *tls.Certificate, opts ...func(*httptest.Server)) (*httptest.Server, tlsDialer) {
|
||||||
srv := httptest.NewUnstartedServer(http.NewServeMux())
|
srv := httptest.NewUnstartedServer(http.NewServeMux())
|
||||||
|
|
||||||
srv.Config.TLSNextProto = map[string]func(*http.Server, *tls.Conn, http.Handler){
|
srv.Config.TLSNextProto = map[string]func(*http.Server, *tls.Conn, http.Handler){
|
||||||
|
@ -1273,6 +1381,11 @@ func newTestTLSALPNServer(validationCert *tls.Certificate) (*httptest.Server, tl
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply options
|
||||||
|
for _, fn := range opts {
|
||||||
|
fn(srv)
|
||||||
|
}
|
||||||
|
|
||||||
srv.Listener = tls.NewListener(srv.Listener, srv.TLS)
|
srv.Listener = tls.NewListener(srv.Listener, srv.TLS)
|
||||||
//srv.Config.ErrorLog = log.New(ioutil.Discard, "", 0) // hush
|
//srv.Config.ErrorLog = log.New(ioutil.Discard, "", 0) // hush
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,7 @@ type Authority interface {
|
||||||
GetRoots() ([]*x509.Certificate, error)
|
GetRoots() ([]*x509.Certificate, error)
|
||||||
GetFederation() ([]*x509.Certificate, error)
|
GetFederation() ([]*x509.Certificate, error)
|
||||||
Version() authority.Version
|
Version() authority.Version
|
||||||
|
GetCertificateRevocationList() ([]byte, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// mustAuthority will be replaced on unit tests.
|
// mustAuthority will be replaced on unit tests.
|
||||||
|
@ -267,6 +268,7 @@ func Route(r Router) {
|
||||||
r.MethodFunc("POST", "/renew", Renew)
|
r.MethodFunc("POST", "/renew", Renew)
|
||||||
r.MethodFunc("POST", "/rekey", Rekey)
|
r.MethodFunc("POST", "/rekey", Rekey)
|
||||||
r.MethodFunc("POST", "/revoke", Revoke)
|
r.MethodFunc("POST", "/revoke", Revoke)
|
||||||
|
r.MethodFunc("GET", "/crl", CRL)
|
||||||
r.MethodFunc("GET", "/provisioners", Provisioners)
|
r.MethodFunc("GET", "/provisioners", Provisioners)
|
||||||
r.MethodFunc("GET", "/provisioners/{kid}/encrypted-key", ProvisionerKey)
|
r.MethodFunc("GET", "/provisioners/{kid}/encrypted-key", ProvisionerKey)
|
||||||
r.MethodFunc("GET", "/roots", Roots)
|
r.MethodFunc("GET", "/roots", Roots)
|
||||||
|
|
|
@ -199,6 +199,7 @@ type mockAuthority struct {
|
||||||
getEncryptedKey func(kid string) (string, error)
|
getEncryptedKey func(kid string) (string, error)
|
||||||
getRoots func() ([]*x509.Certificate, error)
|
getRoots func() ([]*x509.Certificate, error)
|
||||||
getFederation func() ([]*x509.Certificate, error)
|
getFederation func() ([]*x509.Certificate, error)
|
||||||
|
getCRL func() ([]byte, error)
|
||||||
signSSH func(ctx context.Context, key ssh.PublicKey, opts provisioner.SignSSHOptions, signOpts ...provisioner.SignOption) (*ssh.Certificate, error)
|
signSSH func(ctx context.Context, key ssh.PublicKey, opts provisioner.SignSSHOptions, signOpts ...provisioner.SignOption) (*ssh.Certificate, error)
|
||||||
signSSHAddUser func(ctx context.Context, key ssh.PublicKey, cert *ssh.Certificate) (*ssh.Certificate, error)
|
signSSHAddUser func(ctx context.Context, key ssh.PublicKey, cert *ssh.Certificate) (*ssh.Certificate, error)
|
||||||
renewSSH func(ctx context.Context, cert *ssh.Certificate) (*ssh.Certificate, error)
|
renewSSH func(ctx context.Context, cert *ssh.Certificate) (*ssh.Certificate, error)
|
||||||
|
@ -212,6 +213,14 @@ type mockAuthority struct {
|
||||||
version func() authority.Version
|
version func() authority.Version
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *mockAuthority) GetCertificateRevocationList() ([]byte, error) {
|
||||||
|
if m.getCRL != nil {
|
||||||
|
return m.getCRL()
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.ret1.([]byte), m.err
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: remove once Authorize is deprecated.
|
// TODO: remove once Authorize is deprecated.
|
||||||
func (m *mockAuthority) Authorize(ctx context.Context, ott string) ([]provisioner.SignOption, error) {
|
func (m *mockAuthority) Authorize(ctx context.Context, ott string) ([]provisioner.SignOption, error) {
|
||||||
if m.authorize != nil {
|
if m.authorize != nil {
|
||||||
|
@ -772,6 +781,45 @@ func (m *mockProvisioner) AuthorizeSSHRekey(ctx context.Context, token string) (
|
||||||
return m.ret1.(*ssh.Certificate), m.ret2.([]provisioner.SignOption), m.err
|
return m.ret1.(*ssh.Certificate), m.ret2.([]provisioner.SignOption), m.err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_CRLGeneration(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
err error
|
||||||
|
statusCode int
|
||||||
|
expected []byte
|
||||||
|
}{
|
||||||
|
{"empty", nil, http.StatusOK, nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
chiCtx := chi.NewRouteContext()
|
||||||
|
req := httptest.NewRequest("GET", "http://example.com/crl", nil)
|
||||||
|
req = req.WithContext(context.WithValue(context.Background(), chi.RouteCtxKey, chiCtx))
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
mockMustAuthority(t, &mockAuthority{ret1: tt.expected, err: tt.err})
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
CRL(w, req)
|
||||||
|
res := w.Result()
|
||||||
|
|
||||||
|
if res.StatusCode != tt.statusCode {
|
||||||
|
t.Errorf("caHandler.CRL StatusCode = %d, wants %d", res.StatusCode, tt.statusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := io.ReadAll(res.Body)
|
||||||
|
res.Body.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("caHandler.Root unexpected error = %v", err)
|
||||||
|
}
|
||||||
|
if tt.statusCode == 200 {
|
||||||
|
if !bytes.Equal(bytes.TrimSpace(body), tt.expected) {
|
||||||
|
t.Errorf("caHandler.Root CRL = %s, wants %s", body, tt.expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func Test_caHandler_Route(t *testing.T) {
|
func Test_caHandler_Route(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
Authority Authority
|
Authority Authority
|
||||||
|
|
32
api/crl.go
Normal file
32
api/crl.go
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/pem"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/smallstep/certificates/api/render"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CRL is an HTTP handler that returns the current CRL in DER or PEM format
|
||||||
|
func CRL(w http.ResponseWriter, r *http.Request) {
|
||||||
|
crlBytes, err := mustAuthority(r.Context()).GetCertificateRevocationList()
|
||||||
|
if err != nil {
|
||||||
|
render.Error(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, formatAsPEM := r.URL.Query()["pem"]
|
||||||
|
if formatAsPEM {
|
||||||
|
pemBytes := pem.EncodeToMemory(&pem.Block{
|
||||||
|
Type: "X509 CRL",
|
||||||
|
Bytes: crlBytes,
|
||||||
|
})
|
||||||
|
w.Header().Add("Content-Type", "application/x-pem-file")
|
||||||
|
w.Header().Add("Content-Disposition", "attachment; filename=\"crl.pem\"")
|
||||||
|
w.Write(pemBytes)
|
||||||
|
} else {
|
||||||
|
w.Header().Add("Content-Type", "application/pkix-crl")
|
||||||
|
w.Header().Add("Content-Disposition", "attachment; filename=\"crl.der\"")
|
||||||
|
w.Write(crlBytes)
|
||||||
|
}
|
||||||
|
}
|
|
@ -73,7 +73,12 @@ type Authority struct {
|
||||||
sshCAUserFederatedCerts []ssh.PublicKey
|
sshCAUserFederatedCerts []ssh.PublicKey
|
||||||
sshCAHostFederatedCerts []ssh.PublicKey
|
sshCAHostFederatedCerts []ssh.PublicKey
|
||||||
|
|
||||||
// Do not re-initialize
|
// CRL vars
|
||||||
|
crlTicker *time.Ticker
|
||||||
|
crlStopper chan struct{}
|
||||||
|
crlMutex sync.Mutex
|
||||||
|
|
||||||
|
// If true, do not re-initialize
|
||||||
initOnce bool
|
initOnce bool
|
||||||
startTime time.Time
|
startTime time.Time
|
||||||
|
|
||||||
|
@ -91,8 +96,11 @@ type Authority struct {
|
||||||
|
|
||||||
adminMutex sync.RWMutex
|
adminMutex sync.RWMutex
|
||||||
|
|
||||||
// Do Not initialize the authority
|
// If true, do not initialize the authority
|
||||||
skipInit bool
|
skipInit bool
|
||||||
|
|
||||||
|
// If true, do not output initialization logs
|
||||||
|
quietInit bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Info contains information about the authority.
|
// Info contains information about the authority.
|
||||||
|
@ -600,20 +608,74 @@ func (a *Authority) init() error {
|
||||||
return admin.WrapErrorISE(err, "error loading provisioners to initialize authority")
|
return admin.WrapErrorISE(err, "error loading provisioners to initialize authority")
|
||||||
}
|
}
|
||||||
if len(provs) == 0 && !strings.EqualFold(a.config.AuthorityConfig.DeploymentType, "linked") {
|
if len(provs) == 0 && !strings.EqualFold(a.config.AuthorityConfig.DeploymentType, "linked") {
|
||||||
// Create First Provisioner
|
// Migration will currently only be kicked off once, because either one or more provisioners
|
||||||
prov, err := CreateFirstProvisioner(ctx, a.adminDB, string(a.password))
|
// are migrated or a default JWK provisioner will be created in the DB. It won't run for
|
||||||
if err != nil {
|
// linked or hosted deployments. Not for linked, because that case is explicitly checked
|
||||||
return admin.WrapErrorISE(err, "error creating first provisioner")
|
// for above. Not for hosted, because there'll be at least an existing OIDC provisioner.
|
||||||
|
var firstJWKProvisioner *linkedca.Provisioner
|
||||||
|
if len(a.config.AuthorityConfig.Provisioners) > 0 {
|
||||||
|
// Existing provisioners detected; try migrating them to DB storage.
|
||||||
|
a.initLogf("Starting migration of provisioners")
|
||||||
|
for _, p := range a.config.AuthorityConfig.Provisioners {
|
||||||
|
lp, err := ProvisionerToLinkedca(p)
|
||||||
|
if err != nil {
|
||||||
|
return admin.WrapErrorISE(err, "error transforming provisioner %q while migrating", p.GetName())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the provisioner to be migrated
|
||||||
|
if err := a.adminDB.CreateProvisioner(ctx, lp); err != nil {
|
||||||
|
return admin.WrapErrorISE(err, "error creating provisioner %q while migrating", p.GetName())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark the first JWK provisioner, so that it can be used for administration purposes
|
||||||
|
if firstJWKProvisioner == nil && lp.Type == linkedca.Provisioner_JWK {
|
||||||
|
firstJWKProvisioner = lp
|
||||||
|
a.initLogf("Migrated JWK provisioner %q with admin permissions", p.GetName())
|
||||||
|
} else {
|
||||||
|
a.initLogf("Migrated %s provisioner %q", p.GetType(), p.GetName())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c := a.config
|
||||||
|
if c.WasLoadedFromFile() {
|
||||||
|
// The provisioners in the configuration file can be deleted from
|
||||||
|
// the file by editing it. Automatic rewriting of the file was considered
|
||||||
|
// to be too surprising for users and not the right solution for all
|
||||||
|
// use cases, so we leave it up to users to this themselves.
|
||||||
|
a.initLogf("Provisioners that were migrated can now be removed from `ca.json` by editing it")
|
||||||
|
}
|
||||||
|
|
||||||
|
a.initLogf("Finished migrating provisioners")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create first admin
|
// Create first JWK provisioner for remote administration purposes if none exists yet
|
||||||
|
if firstJWKProvisioner == nil {
|
||||||
|
firstJWKProvisioner, err = CreateFirstProvisioner(ctx, a.adminDB, string(a.password))
|
||||||
|
if err != nil {
|
||||||
|
return admin.WrapErrorISE(err, "error creating first provisioner")
|
||||||
|
}
|
||||||
|
a.initLogf("Created JWK provisioner %q with admin permissions", firstJWKProvisioner.GetName())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create first super admin, belonging to the first JWK provisioner
|
||||||
|
// TODO(hs): pass a user-provided first super admin subject to here. With `ca init` it's
|
||||||
|
// added to the DB immediately if using remote management. But when migrating from
|
||||||
|
// ca.json to the DB, this option doesn't exist. Adding a flag just to do it during
|
||||||
|
// migration isn't nice. We could opt for a user to change it afterwards. There exist
|
||||||
|
// cases in which creation of `step` could lock out a user from API access. This is the
|
||||||
|
// case if `step` isn't allowed to be signed by Name Constraints or the X.509 policy.
|
||||||
|
// We have protection for that when creating and updating a policy, but if a policy or
|
||||||
|
// Name Constraints are in use at the time of migration, that could lock the user out.
|
||||||
|
superAdminSubject := "step"
|
||||||
if err := a.adminDB.CreateAdmin(ctx, &linkedca.Admin{
|
if err := a.adminDB.CreateAdmin(ctx, &linkedca.Admin{
|
||||||
ProvisionerId: prov.Id,
|
ProvisionerId: firstJWKProvisioner.Id,
|
||||||
Subject: "step",
|
Subject: superAdminSubject,
|
||||||
Type: linkedca.Admin_SUPER_ADMIN,
|
Type: linkedca.Admin_SUPER_ADMIN,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return admin.WrapErrorISE(err, "error creating first admin")
|
return admin.WrapErrorISE(err, "error creating first admin")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.initLogf("Created super admin %q for JWK provisioner %q", superAdminSubject, firstJWKProvisioner.GetName())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -654,6 +716,18 @@ func (a *Authority) init() error {
|
||||||
a.templates.Data["Step"] = tmplVars
|
a.templates.Data["Step"] = tmplVars
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start the CRL generator, we can assume the configuration is validated.
|
||||||
|
if a.config.CRL.IsEnabled() {
|
||||||
|
// Default cache duration to the default one
|
||||||
|
if v := a.config.CRL.CacheDuration; v == nil || v.Duration <= 0 {
|
||||||
|
a.config.CRL.CacheDuration = config.DefaultCRLCacheDuration
|
||||||
|
}
|
||||||
|
// Start CRL generator
|
||||||
|
if err := a.startCRLGenerator(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// JWT numeric dates are seconds.
|
// JWT numeric dates are seconds.
|
||||||
a.startTime = time.Now().Truncate(time.Second)
|
a.startTime = time.Now().Truncate(time.Second)
|
||||||
// Set flag indicating that initialization has been completed, and should
|
// Set flag indicating that initialization has been completed, and should
|
||||||
|
@ -663,6 +737,14 @@ func (a *Authority) init() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// initLogf is used to log initialization information. The output
|
||||||
|
// can be disabled by starting the CA with the `--quiet` flag.
|
||||||
|
func (a *Authority) initLogf(format string, v ...any) {
|
||||||
|
if !a.quietInit {
|
||||||
|
log.Printf(format, v...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// GetID returns the define authority id or a zero uuid.
|
// GetID returns the define authority id or a zero uuid.
|
||||||
func (a *Authority) GetID() string {
|
func (a *Authority) GetID() string {
|
||||||
const zeroUUID = "00000000-0000-0000-0000-000000000000"
|
const zeroUUID = "00000000-0000-0000-0000-000000000000"
|
||||||
|
@ -712,6 +794,11 @@ func (a *Authority) IsAdminAPIEnabled() bool {
|
||||||
|
|
||||||
// Shutdown safely shuts down any clients, databases, etc. held by the Authority.
|
// Shutdown safely shuts down any clients, databases, etc. held by the Authority.
|
||||||
func (a *Authority) Shutdown() error {
|
func (a *Authority) Shutdown() error {
|
||||||
|
if a.crlTicker != nil {
|
||||||
|
a.crlTicker.Stop()
|
||||||
|
close(a.crlStopper)
|
||||||
|
}
|
||||||
|
|
||||||
if err := a.keyManager.Close(); err != nil {
|
if err := a.keyManager.Close(); err != nil {
|
||||||
log.Printf("error closing the key manager: %v", err)
|
log.Printf("error closing the key manager: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -720,6 +807,11 @@ func (a *Authority) Shutdown() error {
|
||||||
|
|
||||||
// CloseForReload closes internal services, to allow a safe reload.
|
// CloseForReload closes internal services, to allow a safe reload.
|
||||||
func (a *Authority) CloseForReload() {
|
func (a *Authority) CloseForReload() {
|
||||||
|
if a.crlTicker != nil {
|
||||||
|
a.crlTicker.Stop()
|
||||||
|
close(a.crlStopper)
|
||||||
|
}
|
||||||
|
|
||||||
if err := a.keyManager.Close(); err != nil {
|
if err := a.keyManager.Close(); err != nil {
|
||||||
log.Printf("error closing the key manager: %v", err)
|
log.Printf("error closing the key manager: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -760,11 +852,49 @@ func (a *Authority) requiresSCEPService() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSCEPService returns the configured SCEP Service
|
// GetSCEPService returns the configured SCEP Service.
|
||||||
// TODO: this function is intended to exist temporarily
|
//
|
||||||
// in order to make SCEP work more easily. It can be
|
// TODO: this function is intended to exist temporarily in order to make SCEP
|
||||||
// made more correct by using the right interfaces/abstractions
|
// work more easily. It can be made more correct by using the right
|
||||||
// after it works as expected.
|
// interfaces/abstractions after it works as expected.
|
||||||
func (a *Authority) GetSCEPService() *scep.Service {
|
func (a *Authority) GetSCEPService() *scep.Service {
|
||||||
return a.scepService
|
return a.scepService
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *Authority) startCRLGenerator() error {
|
||||||
|
if !a.config.CRL.IsEnabled() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that there is a valid CRL in the DB right now. If it doesn't exist
|
||||||
|
// or is expired, generate one now
|
||||||
|
_, ok := a.db.(db.CertificateRevocationListDB)
|
||||||
|
if !ok {
|
||||||
|
return errors.Errorf("CRL Generation requested, but database does not support CRL generation")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always create a new CRL on startup in case the CA has been down and the
|
||||||
|
// time to next expected CRL update is less than the cache duration.
|
||||||
|
if err := a.GenerateCertificateRevocationList(); err != nil {
|
||||||
|
return errors.Wrap(err, "could not generate a CRL")
|
||||||
|
}
|
||||||
|
|
||||||
|
a.crlStopper = make(chan struct{}, 1)
|
||||||
|
a.crlTicker = time.NewTicker(a.config.CRL.TickerDuration())
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-a.crlTicker.C:
|
||||||
|
log.Println("Regenerating CRL")
|
||||||
|
if err := a.GenerateCertificateRevocationList(); err != nil {
|
||||||
|
log.Printf("error regenerating the CRL: %v", err)
|
||||||
|
}
|
||||||
|
case <-a.crlStopper:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -35,8 +35,13 @@ var (
|
||||||
// DefaultEnableSSHCA enable SSH CA features per provisioner or globally
|
// DefaultEnableSSHCA enable SSH CA features per provisioner or globally
|
||||||
// for all provisioners.
|
// for all provisioners.
|
||||||
DefaultEnableSSHCA = false
|
DefaultEnableSSHCA = false
|
||||||
// GlobalProvisionerClaims default claims for the Authority. Can be overridden
|
// DefaultCRLCacheDuration is the default cache duration for the CRL.
|
||||||
// by provisioner specific claims.
|
DefaultCRLCacheDuration = &provisioner.Duration{Duration: 24 * time.Hour}
|
||||||
|
// DefaultCRLExpiredDuration is the default duration in which expired
|
||||||
|
// certificates will remain in the CRL after expiration.
|
||||||
|
DefaultCRLExpiredDuration = time.Hour
|
||||||
|
// GlobalProvisionerClaims is the default duration that expired certificates
|
||||||
|
// remain in the CRL after expiration.
|
||||||
GlobalProvisionerClaims = provisioner.Claims{
|
GlobalProvisionerClaims = provisioner.Claims{
|
||||||
MinTLSDur: &provisioner.Duration{Duration: 5 * time.Minute}, // TLS certs
|
MinTLSDur: &provisioner.Duration{Duration: 5 * time.Minute}, // TLS certs
|
||||||
MaxTLSDur: &provisioner.Duration{Duration: 24 * time.Hour},
|
MaxTLSDur: &provisioner.Duration{Duration: 24 * time.Hour},
|
||||||
|
@ -72,7 +77,60 @@ type Config struct {
|
||||||
Password string `json:"password,omitempty"`
|
Password string `json:"password,omitempty"`
|
||||||
Templates *templates.Templates `json:"templates,omitempty"`
|
Templates *templates.Templates `json:"templates,omitempty"`
|
||||||
CommonName string `json:"commonName,omitempty"`
|
CommonName string `json:"commonName,omitempty"`
|
||||||
|
CRL *CRLConfig `json:"crl,omitempty"`
|
||||||
SkipValidation bool `json:"-"`
|
SkipValidation bool `json:"-"`
|
||||||
|
|
||||||
|
// Keeps record of the filename the Config is read from
|
||||||
|
loadedFromFilepath string
|
||||||
|
}
|
||||||
|
|
||||||
|
// CRLConfig represents config options for CRL generation
|
||||||
|
type CRLConfig struct {
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
GenerateOnRevoke bool `json:"generateOnRevoke,omitempty"`
|
||||||
|
CacheDuration *provisioner.Duration `json:"cacheDuration,omitempty"`
|
||||||
|
RenewPeriod *provisioner.Duration `json:"renewPeriod,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEnabled returns if the CRL is enabled.
|
||||||
|
func (c *CRLConfig) IsEnabled() bool {
|
||||||
|
return c != nil && c.Enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates the CRL configuration.
|
||||||
|
func (c *CRLConfig) Validate() error {
|
||||||
|
if c == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.CacheDuration != nil && c.CacheDuration.Duration < 0 {
|
||||||
|
return errors.New("crl.cacheDuration must be greater than or equal to 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.RenewPeriod != nil && c.RenewPeriod.Duration < 0 {
|
||||||
|
return errors.New("crl.renewPeriod must be greater than or equal to 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.RenewPeriod != nil && c.CacheDuration != nil &&
|
||||||
|
c.RenewPeriod.Duration > c.CacheDuration.Duration {
|
||||||
|
return errors.New("crl.cacheDuration must be greater than or equal to crl.renewPeriod")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TickerDuration the renewal ticker duration. This is set by renewPeriod, of it
|
||||||
|
// is not set is ~2/3 of cacheDuration.
|
||||||
|
func (c *CRLConfig) TickerDuration() time.Duration {
|
||||||
|
if !c.IsEnabled() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.RenewPeriod != nil && c.RenewPeriod.Duration > 0 {
|
||||||
|
return c.RenewPeriod.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
return (c.CacheDuration.Duration / 3) * 2
|
||||||
}
|
}
|
||||||
|
|
||||||
// ASN1DN contains ASN1.DN attributes that are used in Subject and Issuer
|
// ASN1DN contains ASN1.DN attributes that are used in Subject and Issuer
|
||||||
|
@ -163,6 +221,10 @@ func LoadConfiguration(filename string) (*Config, error) {
|
||||||
return nil, errors.Wrapf(err, "error parsing %s", filename)
|
return nil, errors.Wrapf(err, "error parsing %s", filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// store filename that was read to populate Config
|
||||||
|
c.loadedFromFilepath = filename
|
||||||
|
|
||||||
|
// initialize the Config
|
||||||
c.Init()
|
c.Init()
|
||||||
|
|
||||||
return &c, nil
|
return &c, nil
|
||||||
|
@ -183,6 +245,9 @@ func (c *Config) Init() {
|
||||||
if c.CommonName == "" {
|
if c.CommonName == "" {
|
||||||
c.CommonName = "Step Online CA"
|
c.CommonName = "Step Online CA"
|
||||||
}
|
}
|
||||||
|
if c.CRL != nil && c.CRL.Enabled && c.CRL.CacheDuration == nil {
|
||||||
|
c.CRL.CacheDuration = DefaultCRLCacheDuration
|
||||||
|
}
|
||||||
c.AuthorityConfig.init()
|
c.AuthorityConfig.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,6 +264,30 @@ func (c *Config) Save(filename string) error {
|
||||||
return errors.Wrapf(enc.Encode(c), "error writing %s", filename)
|
return errors.Wrapf(enc.Encode(c), "error writing %s", filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Commit saves the current configuration to the same
|
||||||
|
// file it was initially loaded from.
|
||||||
|
//
|
||||||
|
// TODO(hs): rename Save() to WriteTo() and replace this
|
||||||
|
// with Save()? Or is Commit clear enough.
|
||||||
|
func (c *Config) Commit() error {
|
||||||
|
if !c.WasLoadedFromFile() {
|
||||||
|
return errors.New("cannot commit configuration if not loaded from file")
|
||||||
|
}
|
||||||
|
return c.Save(c.loadedFromFilepath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WasLoadedFromFile returns whether or not the Config was
|
||||||
|
// loaded from a file.
|
||||||
|
func (c *Config) WasLoadedFromFile() bool {
|
||||||
|
return c.loadedFromFilepath != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filepath returns the path to the file the Config was
|
||||||
|
// loaded from.
|
||||||
|
func (c *Config) Filepath() string {
|
||||||
|
return c.loadedFromFilepath
|
||||||
|
}
|
||||||
|
|
||||||
// Validate validates the configuration.
|
// Validate validates the configuration.
|
||||||
func (c *Config) Validate() error {
|
func (c *Config) Validate() error {
|
||||||
switch {
|
switch {
|
||||||
|
@ -269,6 +358,11 @@ func (c *Config) Validate() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate crl config: nil is ok
|
||||||
|
if err := c.CRL.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return c.AuthorityConfig.Validate(c.GetAudiences())
|
return c.AuthorityConfig.Validate(c.GetAudiences())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -86,6 +86,14 @@ func WithDatabase(d db.AuthDB) Option {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithQuietInit disables log output when the authority is initialized.
|
||||||
|
func WithQuietInit() Option {
|
||||||
|
return func(a *Authority) error {
|
||||||
|
a.quietInit = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// WithWebhookClient sets the http.Client to be used for outbound requests.
|
// WithWebhookClient sets the http.Client to be used for outbound requests.
|
||||||
func WithWebhookClient(c *http.Client) Option {
|
func WithWebhookClient(c *http.Client) Option {
|
||||||
return func(a *Authority) error {
|
return func(a *Authority) error {
|
||||||
|
|
243
authority/tls.go
243
authority/tls.go
|
@ -5,11 +5,13 @@ import (
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
"crypto/x509/pkix"
|
||||||
"encoding/asn1"
|
"encoding/asn1"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/big"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -29,6 +31,7 @@ import (
|
||||||
"github.com/smallstep/certificates/db"
|
"github.com/smallstep/certificates/db"
|
||||||
"github.com/smallstep/certificates/errs"
|
"github.com/smallstep/certificates/errs"
|
||||||
"github.com/smallstep/certificates/webhook"
|
"github.com/smallstep/certificates/webhook"
|
||||||
|
"github.com/smallstep/nosql/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetTLSOptions returns the tls options configured.
|
// GetTLSOptions returns the tls options configured.
|
||||||
|
@ -36,8 +39,11 @@ func (a *Authority) GetTLSOptions() *config.TLSOptions {
|
||||||
return a.config.TLS
|
return a.config.TLS
|
||||||
}
|
}
|
||||||
|
|
||||||
var oidAuthorityKeyIdentifier = asn1.ObjectIdentifier{2, 5, 29, 35}
|
var (
|
||||||
var oidSubjectKeyIdentifier = asn1.ObjectIdentifier{2, 5, 29, 14}
|
oidAuthorityKeyIdentifier = asn1.ObjectIdentifier{2, 5, 29, 35}
|
||||||
|
oidSubjectKeyIdentifier = asn1.ObjectIdentifier{2, 5, 29, 14}
|
||||||
|
oidExtensionIssuingDistributionPoint = asn1.ObjectIdentifier{2, 5, 29, 28}
|
||||||
|
)
|
||||||
|
|
||||||
func withDefaultASN1DN(def *config.ASN1DN) provisioner.CertificateModifierFunc {
|
func withDefaultASN1DN(def *config.ASN1DN) provisioner.CertificateModifierFunc {
|
||||||
return func(crt *x509.Certificate, opts provisioner.SignOptions) error {
|
return func(crt *x509.Certificate, opts provisioner.SignOptions) error {
|
||||||
|
@ -320,7 +326,7 @@ func (a *Authority) Rekey(oldCert *x509.Certificate, pk crypto.PublicKey) ([]*x5
|
||||||
// Create new certificate from previous values.
|
// Create new certificate from previous values.
|
||||||
// Issuer, NotBefore, NotAfter and SubjectKeyId will be set by the CAS.
|
// Issuer, NotBefore, NotAfter and SubjectKeyId will be set by the CAS.
|
||||||
newCert := &x509.Certificate{
|
newCert := &x509.Certificate{
|
||||||
Subject: oldCert.Subject,
|
RawSubject: oldCert.RawSubject,
|
||||||
KeyUsage: oldCert.KeyUsage,
|
KeyUsage: oldCert.KeyUsage,
|
||||||
UnhandledCriticalExtensions: oldCert.UnhandledCriticalExtensions,
|
UnhandledCriticalExtensions: oldCert.UnhandledCriticalExtensions,
|
||||||
ExtKeyUsage: oldCert.ExtKeyUsage,
|
ExtKeyUsage: oldCert.ExtKeyUsage,
|
||||||
|
@ -512,16 +518,23 @@ func (a *Authority) Revoke(ctx context.Context, revokeOpts *RevokeOptions) error
|
||||||
RevokedAt: time.Now().UTC(),
|
RevokedAt: time.Now().UTC(),
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
// For X509 CRLs attempt to get the expiration date of the certificate.
|
||||||
p provisioner.Interface
|
if provisioner.MethodFromContext(ctx) == provisioner.RevokeMethod {
|
||||||
err error
|
if revokeOpts.Crt == nil {
|
||||||
)
|
cert, err := a.db.GetCertificate(revokeOpts.Serial)
|
||||||
|
if err == nil {
|
||||||
|
rci.ExpiresAt = cert.NotAfter
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rci.ExpiresAt = revokeOpts.Crt.NotAfter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If not mTLS nor ACME, then get the TokenID of the token.
|
// If not mTLS nor ACME, then get the TokenID of the token.
|
||||||
if !(revokeOpts.MTLS || revokeOpts.ACME) {
|
if !(revokeOpts.MTLS || revokeOpts.ACME) {
|
||||||
token, err := jose.ParseSigned(revokeOpts.OTT)
|
token, err := jose.ParseSigned(revokeOpts.OTT)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errs.Wrap(http.StatusUnauthorized, err,
|
return errs.Wrap(http.StatusUnauthorized, err, "authority.Revoke; error parsing token", opts...)
|
||||||
"authority.Revoke; error parsing token", opts...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get claims w/out verification.
|
// Get claims w/out verification.
|
||||||
|
@ -531,28 +544,43 @@ func (a *Authority) Revoke(ctx context.Context, revokeOpts *RevokeOptions) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method will also validate the audiences for JWK provisioners.
|
// This method will also validate the audiences for JWK provisioners.
|
||||||
p, err = a.LoadProvisionerByToken(token, &claims.Claims)
|
p, err := a.LoadProvisionerByToken(token, &claims.Claims)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
rci.ProvisionerID = p.GetID()
|
rci.ProvisionerID = p.GetID()
|
||||||
rci.TokenID, err = p.GetTokenID(revokeOpts.OTT)
|
rci.TokenID, err = p.GetTokenID(revokeOpts.OTT)
|
||||||
if err != nil && !errors.Is(err, provisioner.ErrAllowTokenReuse) {
|
if err != nil && !errors.Is(err, provisioner.ErrAllowTokenReuse) {
|
||||||
return errs.Wrap(http.StatusInternalServerError, err,
|
return errs.Wrap(http.StatusInternalServerError, err, "authority.Revoke; could not get ID for token")
|
||||||
"authority.Revoke; could not get ID for token")
|
|
||||||
}
|
}
|
||||||
opts = append(opts,
|
opts = append(opts,
|
||||||
errs.WithKeyVal("provisionerID", rci.ProvisionerID),
|
errs.WithKeyVal("provisionerID", rci.ProvisionerID),
|
||||||
errs.WithKeyVal("tokenID", rci.TokenID),
|
errs.WithKeyVal("tokenID", rci.TokenID),
|
||||||
)
|
)
|
||||||
} else if p, err = a.LoadProvisionerByCertificate(revokeOpts.Crt); err == nil {
|
} else if p, err := a.LoadProvisionerByCertificate(revokeOpts.Crt); err == nil {
|
||||||
// Load the Certificate provisioner if one exists.
|
// Load the Certificate provisioner if one exists.
|
||||||
rci.ProvisionerID = p.GetID()
|
rci.ProvisionerID = p.GetID()
|
||||||
opts = append(opts, errs.WithKeyVal("provisionerID", rci.ProvisionerID))
|
opts = append(opts, errs.WithKeyVal("provisionerID", rci.ProvisionerID))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
failRevoke := func(err error) error {
|
||||||
|
switch {
|
||||||
|
case errors.Is(err, db.ErrNotImplemented):
|
||||||
|
return errs.NotImplemented("authority.Revoke; no persistence layer configured", opts...)
|
||||||
|
case errors.Is(err, db.ErrAlreadyExists):
|
||||||
|
return errs.ApplyOptions(
|
||||||
|
errs.BadRequest("certificate with serial number '%s' is already revoked", rci.Serial),
|
||||||
|
opts...,
|
||||||
|
)
|
||||||
|
default:
|
||||||
|
return errs.Wrap(http.StatusInternalServerError, err, "authority.Revoke", opts...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if provisioner.MethodFromContext(ctx) == provisioner.SSHRevokeMethod {
|
if provisioner.MethodFromContext(ctx) == provisioner.SSHRevokeMethod {
|
||||||
err = a.revokeSSH(nil, rci)
|
if err := a.revokeSSH(nil, rci); err != nil {
|
||||||
|
return failRevoke(err)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Revoke an X.509 certificate using CAS. If the certificate is not
|
// Revoke an X.509 certificate using CAS. If the certificate is not
|
||||||
// provided we will try to read it from the db. If the read fails we
|
// provided we will try to read it from the db. If the read fails we
|
||||||
|
@ -567,7 +595,7 @@ func (a *Authority) Revoke(ctx context.Context, revokeOpts *RevokeOptions) error
|
||||||
|
|
||||||
// CAS operation, note that SoftCAS (default) is a noop.
|
// CAS operation, note that SoftCAS (default) is a noop.
|
||||||
// The revoke happens when this is stored in the db.
|
// The revoke happens when this is stored in the db.
|
||||||
_, err = a.x509CAService.RevokeCertificate(&casapi.RevokeCertificateRequest{
|
_, err := a.x509CAService.RevokeCertificate(&casapi.RevokeCertificateRequest{
|
||||||
Certificate: revokedCert,
|
Certificate: revokedCert,
|
||||||
SerialNumber: rci.Serial,
|
SerialNumber: rci.Serial,
|
||||||
Reason: rci.Reason,
|
Reason: rci.Reason,
|
||||||
|
@ -579,21 +607,20 @@ func (a *Authority) Revoke(ctx context.Context, revokeOpts *RevokeOptions) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save as revoked in the Db.
|
// Save as revoked in the Db.
|
||||||
err = a.revoke(revokedCert, rci)
|
if err := a.revoke(revokedCert, rci); err != nil {
|
||||||
}
|
return failRevoke(err)
|
||||||
switch {
|
}
|
||||||
case err == nil:
|
|
||||||
return nil
|
// Generate a new CRL so CRL requesters will always get an up-to-date
|
||||||
case errors.Is(err, db.ErrNotImplemented):
|
// CRL whenever they request it.
|
||||||
return errs.NotImplemented("authority.Revoke; no persistence layer configured", opts...)
|
if a.config.CRL.IsEnabled() && a.config.CRL.GenerateOnRevoke {
|
||||||
case errors.Is(err, db.ErrAlreadyExists):
|
if err := a.GenerateCertificateRevocationList(); err != nil {
|
||||||
return errs.ApplyOptions(
|
return errs.Wrap(http.StatusInternalServerError, err, "authority.Revoke", opts...)
|
||||||
errs.BadRequest("certificate with serial number '%s' is already revoked", rci.Serial),
|
}
|
||||||
opts...,
|
}
|
||||||
)
|
|
||||||
default:
|
|
||||||
return errs.Wrap(http.StatusInternalServerError, err, "authority.Revoke", opts...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Authority) revoke(crt *x509.Certificate, rci *db.RevokedCertificateInfo) error {
|
func (a *Authority) revoke(crt *x509.Certificate, rci *db.RevokedCertificateInfo) error {
|
||||||
|
@ -614,6 +641,137 @@ func (a *Authority) revokeSSH(crt *ssh.Certificate, rci *db.RevokedCertificateIn
|
||||||
return a.db.RevokeSSH(rci)
|
return a.db.RevokeSSH(rci)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetCertificateRevocationList will return the currently generated CRL from the DB, or a not implemented
|
||||||
|
// error if the underlying AuthDB does not support CRLs
|
||||||
|
func (a *Authority) GetCertificateRevocationList() ([]byte, error) {
|
||||||
|
if !a.config.CRL.IsEnabled() {
|
||||||
|
return nil, errs.Wrap(http.StatusNotFound, errors.Errorf("Certificate Revocation Lists are not enabled"), "authority.GetCertificateRevocationList")
|
||||||
|
}
|
||||||
|
|
||||||
|
crlDB, ok := a.db.(db.CertificateRevocationListDB)
|
||||||
|
if !ok {
|
||||||
|
return nil, errs.Wrap(http.StatusNotImplemented, errors.Errorf("Database does not support Certificate Revocation Lists"), "authority.GetCertificateRevocationList")
|
||||||
|
}
|
||||||
|
|
||||||
|
crlInfo, err := crlDB.GetCRL()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errs.Wrap(http.StatusInternalServerError, err, "authority.GetCertificateRevocationList")
|
||||||
|
}
|
||||||
|
|
||||||
|
return crlInfo.DER, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateCertificateRevocationList generates a DER representation of a signed CRL and stores it in the
|
||||||
|
// database. Returns nil if CRL generation has been disabled in the config
|
||||||
|
func (a *Authority) GenerateCertificateRevocationList() error {
|
||||||
|
if !a.config.CRL.IsEnabled() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
crlDB, ok := a.db.(db.CertificateRevocationListDB)
|
||||||
|
if !ok {
|
||||||
|
return errors.Errorf("Database does not support CRL generation")
|
||||||
|
}
|
||||||
|
|
||||||
|
// some CAS may not implement the CRLGenerator interface, so check before we proceed
|
||||||
|
caCRLGenerator, ok := a.x509CAService.(casapi.CertificateAuthorityCRLGenerator)
|
||||||
|
if !ok {
|
||||||
|
return errors.Errorf("CA does not support CRL Generation")
|
||||||
|
}
|
||||||
|
|
||||||
|
// use a mutex to ensure only one CRL is generated at a time to avoid
|
||||||
|
// concurrency issues
|
||||||
|
a.crlMutex.Lock()
|
||||||
|
defer a.crlMutex.Unlock()
|
||||||
|
|
||||||
|
crlInfo, err := crlDB.GetCRL()
|
||||||
|
if err != nil && !database.IsErrNotFound(err) {
|
||||||
|
return errors.Wrap(err, "could not retrieve CRL from database")
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now().Truncate(time.Second).UTC()
|
||||||
|
revokedList, err := crlDB.GetRevokedCertificates()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "could not retrieve revoked certificates list from database")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Number is a monotonically increasing integer (essentially the CRL version
|
||||||
|
// number) that we need to keep track of and increase every time we generate
|
||||||
|
// a new CRL
|
||||||
|
var bn big.Int
|
||||||
|
if crlInfo != nil {
|
||||||
|
bn.SetInt64(crlInfo.Number + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert our database db.RevokedCertificateInfo types into the pkix
|
||||||
|
// representation ready for the CAS to sign it
|
||||||
|
var revokedCertificates []pkix.RevokedCertificate
|
||||||
|
skipExpiredTime := now.Add(-config.DefaultCRLExpiredDuration)
|
||||||
|
for _, revokedCert := range *revokedList {
|
||||||
|
// skip expired certificates
|
||||||
|
if !revokedCert.ExpiresAt.IsZero() && revokedCert.ExpiresAt.Before(skipExpiredTime) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var sn big.Int
|
||||||
|
sn.SetString(revokedCert.Serial, 10)
|
||||||
|
revokedCertificates = append(revokedCertificates, pkix.RevokedCertificate{
|
||||||
|
SerialNumber: &sn,
|
||||||
|
RevocationTime: revokedCert.RevokedAt,
|
||||||
|
Extensions: nil,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var updateDuration time.Duration
|
||||||
|
if a.config.CRL.CacheDuration != nil {
|
||||||
|
updateDuration = a.config.CRL.CacheDuration.Duration
|
||||||
|
} else if crlInfo != nil {
|
||||||
|
updateDuration = crlInfo.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a RevocationList representation ready for the CAS to sign
|
||||||
|
// TODO: allow SignatureAlgorithm to be specified?
|
||||||
|
revocationList := x509.RevocationList{
|
||||||
|
SignatureAlgorithm: 0,
|
||||||
|
RevokedCertificates: revokedCertificates,
|
||||||
|
Number: &bn,
|
||||||
|
ThisUpdate: now,
|
||||||
|
NextUpdate: now.Add(updateDuration),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add distribution point.
|
||||||
|
//
|
||||||
|
// Note that this is currently using the port 443 by default.
|
||||||
|
fullName := a.config.Audience("/1.0/crl")[0]
|
||||||
|
if b, err := marshalDistributionPoint(fullName, false); err == nil {
|
||||||
|
revocationList.ExtraExtensions = []pkix.Extension{
|
||||||
|
{Id: oidExtensionIssuingDistributionPoint, Value: b},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
certificateRevocationList, err := caCRLGenerator.CreateCRL(&casapi.CreateCRLRequest{RevocationList: &revocationList})
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "could not create CRL")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new db.CertificateRevocationListInfo, which stores the new Number we just generated, the
|
||||||
|
// expiry time, duration, and the DER-encoded CRL
|
||||||
|
newCRLInfo := db.CertificateRevocationListInfo{
|
||||||
|
Number: bn.Int64(),
|
||||||
|
ExpiresAt: revocationList.NextUpdate,
|
||||||
|
DER: certificateRevocationList.CRL,
|
||||||
|
Duration: updateDuration,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the CRL in the database ready for retrieval by api endpoints
|
||||||
|
err = crlDB.StoreCRL(&newCRLInfo)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "could not store CRL in database")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetTLSCertificate creates a new leaf certificate to be used by the CA HTTPS server.
|
// GetTLSCertificate creates a new leaf certificate to be used by the CA HTTPS server.
|
||||||
func (a *Authority) GetTLSCertificate() (*tls.Certificate, error) {
|
func (a *Authority) GetTLSCertificate() (*tls.Certificate, error) {
|
||||||
fatal := func(err error) (*tls.Certificate, error) {
|
fatal := func(err error) (*tls.Certificate, error) {
|
||||||
|
@ -707,6 +865,33 @@ func (a *Authority) GetTLSCertificate() (*tls.Certificate, error) {
|
||||||
return &tlsCrt, nil
|
return &tlsCrt, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RFC 5280, 5.2.5
|
||||||
|
type distributionPoint struct {
|
||||||
|
DistributionPoint distributionPointName `asn1:"optional,tag:0"`
|
||||||
|
OnlyContainsUserCerts bool `asn1:"optional,tag:1"`
|
||||||
|
OnlyContainsCACerts bool `asn1:"optional,tag:2"`
|
||||||
|
OnlySomeReasons asn1.BitString `asn1:"optional,tag:3"`
|
||||||
|
IndirectCRL bool `asn1:"optional,tag:4"`
|
||||||
|
OnlyContainsAttributeCerts bool `asn1:"optional,tag:5"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type distributionPointName struct {
|
||||||
|
FullName []asn1.RawValue `asn1:"optional,tag:0"`
|
||||||
|
RelativeName pkix.RDNSequence `asn1:"optional,tag:1"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func marshalDistributionPoint(fullName string, isCA bool) ([]byte, error) {
|
||||||
|
return asn1.Marshal(distributionPoint{
|
||||||
|
DistributionPoint: distributionPointName{
|
||||||
|
FullName: []asn1.RawValue{
|
||||||
|
{Class: 2, Tag: 6, Bytes: []byte(fullName)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
OnlyContainsUserCerts: !isCA,
|
||||||
|
OnlyContainsCACerts: isCA,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// templatingError tries to extract more information about the cause of
|
// templatingError tries to extract more information about the cause of
|
||||||
// an error related to (most probably) malformed template data and adds
|
// an error related to (most probably) malformed template data and adds
|
||||||
// this to the error message.
|
// this to the error message.
|
||||||
|
|
|
@ -26,11 +26,13 @@ import (
|
||||||
|
|
||||||
"github.com/smallstep/assert"
|
"github.com/smallstep/assert"
|
||||||
"github.com/smallstep/certificates/api/render"
|
"github.com/smallstep/certificates/api/render"
|
||||||
|
"github.com/smallstep/certificates/authority/config"
|
||||||
"github.com/smallstep/certificates/authority/policy"
|
"github.com/smallstep/certificates/authority/policy"
|
||||||
"github.com/smallstep/certificates/authority/provisioner"
|
"github.com/smallstep/certificates/authority/provisioner"
|
||||||
"github.com/smallstep/certificates/cas/softcas"
|
"github.com/smallstep/certificates/cas/softcas"
|
||||||
"github.com/smallstep/certificates/db"
|
"github.com/smallstep/certificates/db"
|
||||||
"github.com/smallstep/certificates/errs"
|
"github.com/smallstep/certificates/errs"
|
||||||
|
"github.com/smallstep/nosql/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -139,6 +141,13 @@ func generateIntermidiateCertificate(t *testing.T, issuer *x509.Certificate, sig
|
||||||
return cert, priv
|
return cert, priv
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func withSubject(sub pkix.Name) provisioner.CertificateModifierFunc {
|
||||||
|
return func(crt *x509.Certificate, _ provisioner.SignOptions) error {
|
||||||
|
crt.Subject = sub
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func withProvisionerOID(name, kid string) provisioner.CertificateModifierFunc {
|
func withProvisionerOID(name, kid string) provisioner.CertificateModifierFunc {
|
||||||
return func(crt *x509.Certificate, _ provisioner.SignOptions) error {
|
return func(crt *x509.Certificate, _ provisioner.SignOptions) error {
|
||||||
b, err := asn1.Marshal(stepProvisionerASN1{
|
b, err := asn1.Marshal(stepProvisionerASN1{
|
||||||
|
@ -952,6 +961,18 @@ func TestAuthority_Renew(t *testing.T) {
|
||||||
withProvisionerOID("Max", a.config.AuthorityConfig.Provisioners[0].(*provisioner.JWK).Key.KeyID),
|
withProvisionerOID("Max", a.config.AuthorityConfig.Provisioners[0].(*provisioner.JWK).Key.KeyID),
|
||||||
withSigner(issuer, signer))
|
withSigner(issuer, signer))
|
||||||
|
|
||||||
|
certExtraNames := generateCertificate(t, "renew", []string{"test.smallstep.com", "test"},
|
||||||
|
withSubject(pkix.Name{
|
||||||
|
CommonName: "renew",
|
||||||
|
ExtraNames: []pkix.AttributeTypeAndValue{
|
||||||
|
{Type: asn1.ObjectIdentifier{0, 9, 2342, 19200300, 100, 1, 25}, Value: "dc"},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
withNotBeforeNotAfter(so.NotBefore.Time(), so.NotAfter.Time()),
|
||||||
|
withDefaultASN1DN(a.config.AuthorityConfig.Template),
|
||||||
|
withProvisionerOID("Max", a.config.AuthorityConfig.Provisioners[0].(*provisioner.JWK).Key.KeyID),
|
||||||
|
withSigner(issuer, signer))
|
||||||
|
|
||||||
certNoRenew := generateCertificate(t, "renew", []string{"test.smallstep.com", "test"},
|
certNoRenew := generateCertificate(t, "renew", []string{"test.smallstep.com", "test"},
|
||||||
withNotBeforeNotAfter(so.NotBefore.Time(), so.NotAfter.Time()),
|
withNotBeforeNotAfter(so.NotBefore.Time(), so.NotAfter.Time()),
|
||||||
withDefaultASN1DN(a.config.AuthorityConfig.Template),
|
withDefaultASN1DN(a.config.AuthorityConfig.Template),
|
||||||
|
@ -1001,6 +1022,12 @@ func TestAuthority_Renew(t *testing.T) {
|
||||||
cert: cert,
|
cert: cert,
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
|
"ok/WithExtraNames": func() (*renewTest, error) {
|
||||||
|
return &renewTest{
|
||||||
|
auth: a,
|
||||||
|
cert: certExtraNames,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
"ok/success-new-intermediate": func() (*renewTest, error) {
|
"ok/success-new-intermediate": func() (*renewTest, error) {
|
||||||
rootCert, rootSigner := generateRootCertificate(t)
|
rootCert, rootSigner := generateRootCertificate(t)
|
||||||
intCert, intSigner := generateIntermidiateCertificate(t, rootCert, rootSigner)
|
intCert, intSigner := generateIntermidiateCertificate(t, rootCert, rootSigner)
|
||||||
|
@ -1063,15 +1090,14 @@ func TestAuthority_Renew(t *testing.T) {
|
||||||
assert.True(t, leaf.NotAfter.Before(expiry.Add(time.Hour)))
|
assert.True(t, leaf.NotAfter.Before(expiry.Add(time.Hour)))
|
||||||
|
|
||||||
tmplt := a.config.AuthorityConfig.Template
|
tmplt := a.config.AuthorityConfig.Template
|
||||||
assert.Equals(t, leaf.Subject.String(),
|
assert.Equals(t, leaf.RawSubject, tc.cert.RawSubject)
|
||||||
pkix.Name{
|
assert.Equals(t, leaf.Subject.Country, []string{tmplt.Country})
|
||||||
Country: []string{tmplt.Country},
|
assert.Equals(t, leaf.Subject.Organization, []string{tmplt.Organization})
|
||||||
Organization: []string{tmplt.Organization},
|
assert.Equals(t, leaf.Subject.Locality, []string{tmplt.Locality})
|
||||||
Locality: []string{tmplt.Locality},
|
assert.Equals(t, leaf.Subject.StreetAddress, []string{tmplt.StreetAddress})
|
||||||
StreetAddress: []string{tmplt.StreetAddress},
|
assert.Equals(t, leaf.Subject.Province, []string{tmplt.Province})
|
||||||
Province: []string{tmplt.Province},
|
assert.Equals(t, leaf.Subject.CommonName, tmplt.CommonName)
|
||||||
CommonName: tmplt.CommonName,
|
|
||||||
}.String())
|
|
||||||
assert.Equals(t, leaf.Issuer, intermediate.Subject)
|
assert.Equals(t, leaf.Issuer, intermediate.Subject)
|
||||||
|
|
||||||
assert.Equals(t, leaf.SignatureAlgorithm, x509.ECDSAWithSHA256)
|
assert.Equals(t, leaf.SignatureAlgorithm, x509.ECDSAWithSHA256)
|
||||||
|
@ -1474,7 +1500,7 @@ func TestAuthority_Revoke(t *testing.T) {
|
||||||
return true, nil
|
return true, nil
|
||||||
},
|
},
|
||||||
MGetCertificate: func(sn string) (*x509.Certificate, error) {
|
MGetCertificate: func(sn string) (*x509.Certificate, error) {
|
||||||
return nil, nil
|
return nil, errors.New("not found")
|
||||||
},
|
},
|
||||||
Err: errors.New("force"),
|
Err: errors.New("force"),
|
||||||
}))
|
}))
|
||||||
|
@ -1514,7 +1540,7 @@ func TestAuthority_Revoke(t *testing.T) {
|
||||||
return true, nil
|
return true, nil
|
||||||
},
|
},
|
||||||
MGetCertificate: func(sn string) (*x509.Certificate, error) {
|
MGetCertificate: func(sn string) (*x509.Certificate, error) {
|
||||||
return nil, nil
|
return nil, errors.New("not found")
|
||||||
},
|
},
|
||||||
Err: db.ErrAlreadyExists,
|
Err: db.ErrAlreadyExists,
|
||||||
}))
|
}))
|
||||||
|
@ -1783,3 +1809,148 @@ func TestAuthority_constraints(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAuthority_CRL(t *testing.T) {
|
||||||
|
reasonCode := 2
|
||||||
|
reason := "bob was let go"
|
||||||
|
validIssuer := "step-cli"
|
||||||
|
validAudience := testAudiences.Revoke
|
||||||
|
now := time.Now().UTC()
|
||||||
|
//
|
||||||
|
jwk, err := jose.ReadKey("testdata/secrets/step_cli_key_priv.jwk", jose.WithPassword([]byte("pass")))
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
//
|
||||||
|
sig, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.ES256, Key: jwk.Key},
|
||||||
|
(&jose.SignerOptions{}).WithType("JWT").WithHeader("kid", jwk.KeyID))
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
|
||||||
|
crlCtx := provisioner.NewContextWithMethod(context.Background(), provisioner.RevokeMethod)
|
||||||
|
|
||||||
|
var crlStore db.CertificateRevocationListInfo
|
||||||
|
var revokedList []db.RevokedCertificateInfo
|
||||||
|
|
||||||
|
type test struct {
|
||||||
|
auth *Authority
|
||||||
|
ctx context.Context
|
||||||
|
expected []string
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
tests := map[string]func() test{
|
||||||
|
"fail/empty-crl": func() test {
|
||||||
|
a := testAuthority(t, WithDatabase(&db.MockAuthDB{
|
||||||
|
MUseToken: func(id, tok string) (bool, error) {
|
||||||
|
return true, nil
|
||||||
|
},
|
||||||
|
MGetCertificate: func(sn string) (*x509.Certificate, error) {
|
||||||
|
return nil, errors.New("not found")
|
||||||
|
},
|
||||||
|
MStoreCRL: func(i *db.CertificateRevocationListInfo) error {
|
||||||
|
crlStore = *i
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
MGetCRL: func() (*db.CertificateRevocationListInfo, error) {
|
||||||
|
return nil, database.ErrNotFound
|
||||||
|
},
|
||||||
|
MGetRevokedCertificates: func() (*[]db.RevokedCertificateInfo, error) {
|
||||||
|
return &revokedList, nil
|
||||||
|
},
|
||||||
|
MRevoke: func(rci *db.RevokedCertificateInfo) error {
|
||||||
|
revokedList = append(revokedList, *rci)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
a.config.CRL = &config.CRLConfig{
|
||||||
|
Enabled: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
return test{
|
||||||
|
auth: a,
|
||||||
|
ctx: crlCtx,
|
||||||
|
expected: nil,
|
||||||
|
err: database.ErrNotFound,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ok/crl-full": func() test {
|
||||||
|
a := testAuthority(t, WithDatabase(&db.MockAuthDB{
|
||||||
|
MUseToken: func(id, tok string) (bool, error) {
|
||||||
|
return true, nil
|
||||||
|
},
|
||||||
|
MGetCertificate: func(sn string) (*x509.Certificate, error) {
|
||||||
|
return nil, errors.New("not found")
|
||||||
|
},
|
||||||
|
MStoreCRL: func(i *db.CertificateRevocationListInfo) error {
|
||||||
|
crlStore = *i
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
MGetCRL: func() (*db.CertificateRevocationListInfo, error) {
|
||||||
|
return &crlStore, nil
|
||||||
|
},
|
||||||
|
MGetRevokedCertificates: func() (*[]db.RevokedCertificateInfo, error) {
|
||||||
|
return &revokedList, nil
|
||||||
|
},
|
||||||
|
MRevoke: func(rci *db.RevokedCertificateInfo) error {
|
||||||
|
revokedList = append(revokedList, *rci)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
a.config.CRL = &config.CRLConfig{
|
||||||
|
Enabled: true,
|
||||||
|
GenerateOnRevoke: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
var ex []string
|
||||||
|
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
sn := fmt.Sprintf("%v", i)
|
||||||
|
|
||||||
|
cl := jose.Claims{
|
||||||
|
Subject: fmt.Sprintf("sn-%v", i),
|
||||||
|
Issuer: validIssuer,
|
||||||
|
NotBefore: jose.NewNumericDate(now),
|
||||||
|
Expiry: jose.NewNumericDate(now.Add(time.Minute)),
|
||||||
|
Audience: validAudience,
|
||||||
|
ID: sn,
|
||||||
|
}
|
||||||
|
raw, err := jose.Signed(sig).Claims(cl).CompactSerialize()
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
err = a.Revoke(crlCtx, &RevokeOptions{
|
||||||
|
Serial: sn,
|
||||||
|
ReasonCode: reasonCode,
|
||||||
|
Reason: reason,
|
||||||
|
OTT: raw,
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
|
||||||
|
ex = append(ex, sn)
|
||||||
|
}
|
||||||
|
|
||||||
|
return test{
|
||||||
|
auth: a,
|
||||||
|
ctx: crlCtx,
|
||||||
|
expected: ex,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for name, f := range tests {
|
||||||
|
tc := f()
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
if crlBytes, err := tc.auth.GetCertificateRevocationList(); err == nil {
|
||||||
|
crl, parseErr := x509.ParseCRL(crlBytes)
|
||||||
|
if parseErr != nil {
|
||||||
|
t.Errorf("x509.ParseCertificateRequest() error = %v, wantErr %v", parseErr, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var cmpList []string
|
||||||
|
for _, c := range crl.TBSCertList.RevokedCertificates {
|
||||||
|
cmpList = append(cmpList, c.SerialNumber.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equals(t, cmpList, tc.expected)
|
||||||
|
} else {
|
||||||
|
assert.NotNil(t, tc.err, err.Error())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
13
ca/ca.go
13
ca/ca.go
|
@ -156,6 +156,10 @@ func (ca *CA) Init(cfg *config.Config) (*CA, error) {
|
||||||
opts = append(opts, authority.WithDatabase(ca.opts.database))
|
opts = append(opts, authority.WithDatabase(ca.opts.database))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ca.opts.quiet {
|
||||||
|
opts = append(opts, authority.WithQuietInit())
|
||||||
|
}
|
||||||
|
|
||||||
webhookTransport := http.DefaultTransport.(*http.Transport).Clone()
|
webhookTransport := http.DefaultTransport.(*http.Transport).Clone()
|
||||||
opts = append(opts, authority.WithWebhookClient(&http.Client{Transport: webhookTransport}))
|
opts = append(opts, authority.WithWebhookClient(&http.Client{Transport: webhookTransport}))
|
||||||
|
|
||||||
|
@ -345,7 +349,7 @@ func (ca *CA) Run() error {
|
||||||
if step.Contexts().GetCurrent() != nil {
|
if step.Contexts().GetCurrent() != nil {
|
||||||
log.Printf("Current context: %s", step.Contexts().GetCurrent().Name)
|
log.Printf("Current context: %s", step.Contexts().GetCurrent().Name)
|
||||||
}
|
}
|
||||||
log.Printf("Config file: %s", ca.opts.configFile)
|
log.Printf("Config file: %s", ca.getConfigFileOutput())
|
||||||
baseURL := fmt.Sprintf("https://%s%s",
|
baseURL := fmt.Sprintf("https://%s%s",
|
||||||
authorityInfo.DNSNames[0],
|
authorityInfo.DNSNames[0],
|
||||||
ca.config.Address[strings.LastIndex(ca.config.Address, ":"):])
|
ca.config.Address[strings.LastIndex(ca.config.Address, ":"):])
|
||||||
|
@ -565,3 +569,10 @@ func dumpRoutes(mux chi.Routes) {
|
||||||
fmt.Printf("Logging err: %s\n", err.Error())
|
fmt.Printf("Logging err: %s\n", err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ca *CA) getConfigFileOutput() string {
|
||||||
|
if ca.config.WasLoadedFromFile() {
|
||||||
|
return ca.config.Filepath()
|
||||||
|
}
|
||||||
|
return "loaded from token"
|
||||||
|
}
|
||||||
|
|
|
@ -154,3 +154,13 @@ type CreateCertificateAuthorityResponse struct {
|
||||||
PrivateKey crypto.PrivateKey
|
PrivateKey crypto.PrivateKey
|
||||||
Signer crypto.Signer
|
Signer crypto.Signer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateCRLRequest is the request to create a Certificate Revocation List.
|
||||||
|
type CreateCRLRequest struct {
|
||||||
|
RevocationList *x509.RevocationList
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateCRLResponse is the response to a Certificate Revocation List request.
|
||||||
|
type CreateCRLResponse struct {
|
||||||
|
CRL []byte //the CRL in DER format
|
||||||
|
}
|
||||||
|
|
|
@ -14,6 +14,12 @@ type CertificateAuthorityService interface {
|
||||||
RevokeCertificate(req *RevokeCertificateRequest) (*RevokeCertificateResponse, error)
|
RevokeCertificate(req *RevokeCertificateRequest) (*RevokeCertificateResponse, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CertificateAuthorityCRLGenerator is an optional interface implemented by CertificateAuthorityService
|
||||||
|
// that has a method to create a CRL
|
||||||
|
type CertificateAuthorityCRLGenerator interface {
|
||||||
|
CreateCRL(req *CreateCRLRequest) (*CreateCRLResponse, error)
|
||||||
|
}
|
||||||
|
|
||||||
// CertificateAuthorityGetter is an interface implemented by a
|
// CertificateAuthorityGetter is an interface implemented by a
|
||||||
// CertificateAuthorityService that has a method to get the root certificate.
|
// CertificateAuthorityService that has a method to get the root certificate.
|
||||||
type CertificateAuthorityGetter interface {
|
type CertificateAuthorityGetter interface {
|
||||||
|
|
|
@ -3,6 +3,7 @@ package softcas
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto"
|
"crypto"
|
||||||
|
"crypto/rand"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"time"
|
"time"
|
||||||
|
@ -132,6 +133,20 @@ func (c *SoftCAS) RevokeCertificate(req *apiv1.RevokeCertificateRequest) (*apiv1
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateCRL will create a new CRL based on the RevocationList passed to it
|
||||||
|
func (c *SoftCAS) CreateCRL(req *apiv1.CreateCRLRequest) (*apiv1.CreateCRLResponse, error) {
|
||||||
|
certChain, signer, err := c.getCertSigner()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
revocationListBytes, err := x509.CreateRevocationList(rand.Reader, req.RevocationList, certChain[0], signer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &apiv1.CreateCRLResponse{CRL: revocationListBytes}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// CreateCertificateAuthority creates a root or an intermediate certificate.
|
// CreateCertificateAuthority creates a root or an intermediate certificate.
|
||||||
func (c *SoftCAS) CreateCertificateAuthority(req *apiv1.CreateCertificateAuthorityRequest) (*apiv1.CreateCertificateAuthorityResponse, error) {
|
func (c *SoftCAS) CreateCertificateAuthority(req *apiv1.CreateCertificateAuthorityRequest) (*apiv1.CreateCertificateAuthorityResponse, error) {
|
||||||
switch {
|
switch {
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"github.com/smallstep/certificates/acme"
|
||||||
"github.com/smallstep/certificates/authority/config"
|
"github.com/smallstep/certificates/authority/config"
|
||||||
"github.com/smallstep/certificates/authority/provisioner"
|
"github.com/smallstep/certificates/authority/provisioner"
|
||||||
"github.com/smallstep/certificates/ca"
|
"github.com/smallstep/certificates/ca"
|
||||||
|
@ -68,9 +69,23 @@ certificate issuer private key used in the RA mode.`,
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "context",
|
Name: "context",
|
||||||
Usage: "The name of the authority's context.",
|
Usage: "the <name> of the authority's context.",
|
||||||
EnvVar: "STEP_CA_CONTEXT",
|
EnvVar: "STEP_CA_CONTEXT",
|
||||||
},
|
},
|
||||||
|
cli.IntFlag{
|
||||||
|
Name: "acme-http-port",
|
||||||
|
Usage: `the <port> used on http-01 challenges. It can be changed for testing purposes.
|
||||||
|
Requires **--insecure** flag.`,
|
||||||
|
},
|
||||||
|
cli.IntFlag{
|
||||||
|
Name: "acme-tls-port",
|
||||||
|
Usage: `the <port> used on tls-alpn-01 challenges. It can be changed for testing purposes.
|
||||||
|
Requires **--insecure** flag.`,
|
||||||
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "insecure",
|
||||||
|
Usage: "enable insecure flags.",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,6 +103,23 @@ func appAction(ctx *cli.Context) error {
|
||||||
return errs.TooManyArguments(ctx)
|
return errs.TooManyArguments(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Allow custom ACME ports with insecure
|
||||||
|
if acmePort := ctx.Int("acme-http-port"); acmePort != 0 {
|
||||||
|
if ctx.Bool("insecure") {
|
||||||
|
acme.InsecurePortHTTP01 = acmePort
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("flag '--acme-http-port' requires the '--insecure' flag")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if acmePort := ctx.Int("acme-tls-port"); acmePort != 0 {
|
||||||
|
if ctx.Bool("insecure") {
|
||||||
|
acme.InsecurePortTLSALPN01 = acmePort
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("flag '--acme-tls-port' requires the '--insecure' flag")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow custom contexts.
|
||||||
if caCtx := ctx.String("context"); caCtx != "" {
|
if caCtx := ctx.String("context"); caCtx != "" {
|
||||||
if err := step.Contexts().SetCurrent(caCtx); err != nil {
|
if err := step.Contexts().SetCurrent(caCtx); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
120
db/db.go
120
db/db.go
|
@ -19,6 +19,7 @@ var (
|
||||||
certsTable = []byte("x509_certs")
|
certsTable = []byte("x509_certs")
|
||||||
certsDataTable = []byte("x509_certs_data")
|
certsDataTable = []byte("x509_certs_data")
|
||||||
revokedCertsTable = []byte("revoked_x509_certs")
|
revokedCertsTable = []byte("revoked_x509_certs")
|
||||||
|
crlTable = []byte("x509_crl")
|
||||||
revokedSSHCertsTable = []byte("revoked_ssh_certs")
|
revokedSSHCertsTable = []byte("revoked_ssh_certs")
|
||||||
usedOTTTable = []byte("used_ott")
|
usedOTTTable = []byte("used_ott")
|
||||||
sshCertsTable = []byte("ssh_certs")
|
sshCertsTable = []byte("ssh_certs")
|
||||||
|
@ -27,6 +28,9 @@ var (
|
||||||
sshHostPrincipalsTable = []byte("ssh_host_principals")
|
sshHostPrincipalsTable = []byte("ssh_host_principals")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var crlKey = []byte("crl") //TODO: at the moment we store a single CRL in the database, in a dedicated table.
|
||||||
|
// is this acceptable? probably not....
|
||||||
|
|
||||||
// ErrAlreadyExists can be returned if the DB attempts to set a key that has
|
// ErrAlreadyExists can be returned if the DB attempts to set a key that has
|
||||||
// been previously set.
|
// been previously set.
|
||||||
var ErrAlreadyExists = errors.New("already exists")
|
var ErrAlreadyExists = errors.New("already exists")
|
||||||
|
@ -87,6 +91,13 @@ type CertificateStorer interface {
|
||||||
StoreSSHCertificate(crt *ssh.Certificate) error
|
StoreSSHCertificate(crt *ssh.Certificate) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CertificateRevocationListDB is an interface to indicate whether the DB supports CRL generation
|
||||||
|
type CertificateRevocationListDB interface {
|
||||||
|
GetRevokedCertificates() (*[]RevokedCertificateInfo, error)
|
||||||
|
GetCRL() (*CertificateRevocationListInfo, error)
|
||||||
|
StoreCRL(*CertificateRevocationListInfo) error
|
||||||
|
}
|
||||||
|
|
||||||
// DB is a wrapper over the nosql.DB interface.
|
// DB is a wrapper over the nosql.DB interface.
|
||||||
type DB struct {
|
type DB struct {
|
||||||
nosql.DB
|
nosql.DB
|
||||||
|
@ -113,7 +124,7 @@ func New(c *Config) (AuthDB, error) {
|
||||||
tables := [][]byte{
|
tables := [][]byte{
|
||||||
revokedCertsTable, certsTable, usedOTTTable,
|
revokedCertsTable, certsTable, usedOTTTable,
|
||||||
sshCertsTable, sshHostsTable, sshHostPrincipalsTable, sshUsersTable,
|
sshCertsTable, sshHostsTable, sshHostPrincipalsTable, sshUsersTable,
|
||||||
revokedSSHCertsTable, certsDataTable,
|
revokedSSHCertsTable, certsDataTable, crlTable,
|
||||||
}
|
}
|
||||||
for _, b := range tables {
|
for _, b := range tables {
|
||||||
if err := db.CreateTable(b); err != nil {
|
if err := db.CreateTable(b); err != nil {
|
||||||
|
@ -133,11 +144,21 @@ type RevokedCertificateInfo struct {
|
||||||
ReasonCode int
|
ReasonCode int
|
||||||
Reason string
|
Reason string
|
||||||
RevokedAt time.Time
|
RevokedAt time.Time
|
||||||
|
ExpiresAt time.Time
|
||||||
TokenID string
|
TokenID string
|
||||||
MTLS bool
|
MTLS bool
|
||||||
ACME bool
|
ACME bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CertificateRevocationListInfo contains a CRL in DER format and associated
|
||||||
|
// metadata to allow a decision on whether to regenerate the CRL or not easier
|
||||||
|
type CertificateRevocationListInfo struct {
|
||||||
|
Number int64
|
||||||
|
ExpiresAt time.Time
|
||||||
|
Duration time.Duration
|
||||||
|
DER []byte
|
||||||
|
}
|
||||||
|
|
||||||
// IsRevoked returns whether or not a certificate with the given identifier
|
// IsRevoked returns whether or not a certificate with the given identifier
|
||||||
// has been revoked.
|
// has been revoked.
|
||||||
// In the case of an X509 Certificate the `id` should be the Serial Number of
|
// In the case of an X509 Certificate the `id` should be the Serial Number of
|
||||||
|
@ -220,6 +241,51 @@ func (db *DB) RevokeSSH(rci *RevokedCertificateInfo) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetRevokedCertificates gets a list of all revoked certificates.
|
||||||
|
func (db *DB) GetRevokedCertificates() (*[]RevokedCertificateInfo, error) {
|
||||||
|
entries, err := db.List(revokedCertsTable)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var revokedCerts []RevokedCertificateInfo
|
||||||
|
for _, e := range entries {
|
||||||
|
var data RevokedCertificateInfo
|
||||||
|
if err := json.Unmarshal(e.Value, &data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
revokedCerts = append(revokedCerts, data)
|
||||||
|
}
|
||||||
|
return &revokedCerts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StoreCRL stores a CRL in the DB
|
||||||
|
func (db *DB) StoreCRL(crlInfo *CertificateRevocationListInfo) error {
|
||||||
|
crlInfoBytes, err := json.Marshal(crlInfo)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "json Marshal error")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.Set(crlTable, crlKey, crlInfoBytes); err != nil {
|
||||||
|
return errors.Wrap(err, "database Set error")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCRL gets the existing CRL from the database
|
||||||
|
func (db *DB) GetCRL() (*CertificateRevocationListInfo, error) {
|
||||||
|
crlInfoBytes, err := db.Get(crlTable, crlKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "database Get error")
|
||||||
|
}
|
||||||
|
|
||||||
|
var crlInfo CertificateRevocationListInfo
|
||||||
|
err = json.Unmarshal(crlInfoBytes, &crlInfo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "json Unmarshal error")
|
||||||
|
}
|
||||||
|
return &crlInfo, err
|
||||||
|
}
|
||||||
|
|
||||||
// GetCertificate retrieves a certificate by the serial number.
|
// GetCertificate retrieves a certificate by the serial number.
|
||||||
func (db *DB) GetCertificate(serialNumber string) (*x509.Certificate, error) {
|
func (db *DB) GetCertificate(serialNumber string) (*x509.Certificate, error) {
|
||||||
asn1Data, err := db.Get(certsTable, []byte(serialNumber))
|
asn1Data, err := db.Get(certsTable, []byte(serialNumber))
|
||||||
|
@ -382,20 +448,44 @@ func (db *DB) Shutdown() error {
|
||||||
|
|
||||||
// MockAuthDB mocks the AuthDB interface. //
|
// MockAuthDB mocks the AuthDB interface. //
|
||||||
type MockAuthDB struct {
|
type MockAuthDB struct {
|
||||||
Err error
|
Err error
|
||||||
Ret1 interface{}
|
Ret1 interface{}
|
||||||
MIsRevoked func(string) (bool, error)
|
MIsRevoked func(string) (bool, error)
|
||||||
MIsSSHRevoked func(string) (bool, error)
|
MIsSSHRevoked func(string) (bool, error)
|
||||||
MRevoke func(rci *RevokedCertificateInfo) error
|
MRevoke func(rci *RevokedCertificateInfo) error
|
||||||
MRevokeSSH func(rci *RevokedCertificateInfo) error
|
MRevokeSSH func(rci *RevokedCertificateInfo) error
|
||||||
MGetCertificate func(serialNumber string) (*x509.Certificate, error)
|
MGetCertificate func(serialNumber string) (*x509.Certificate, error)
|
||||||
MGetCertificateData func(serialNumber string) (*CertificateData, error)
|
MGetCertificateData func(serialNumber string) (*CertificateData, error)
|
||||||
MStoreCertificate func(crt *x509.Certificate) error
|
MStoreCertificate func(crt *x509.Certificate) error
|
||||||
MUseToken func(id, tok string) (bool, error)
|
MUseToken func(id, tok string) (bool, error)
|
||||||
MIsSSHHost func(principal string) (bool, error)
|
MIsSSHHost func(principal string) (bool, error)
|
||||||
MStoreSSHCertificate func(crt *ssh.Certificate) error
|
MStoreSSHCertificate func(crt *ssh.Certificate) error
|
||||||
MGetSSHHostPrincipals func() ([]string, error)
|
MGetSSHHostPrincipals func() ([]string, error)
|
||||||
MShutdown func() error
|
MShutdown func() error
|
||||||
|
MGetRevokedCertificates func() (*[]RevokedCertificateInfo, error)
|
||||||
|
MGetCRL func() (*CertificateRevocationListInfo, error)
|
||||||
|
MStoreCRL func(*CertificateRevocationListInfo) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockAuthDB) GetRevokedCertificates() (*[]RevokedCertificateInfo, error) {
|
||||||
|
if m.MGetRevokedCertificates != nil {
|
||||||
|
return m.MGetRevokedCertificates()
|
||||||
|
}
|
||||||
|
return m.Ret1.(*[]RevokedCertificateInfo), m.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockAuthDB) GetCRL() (*CertificateRevocationListInfo, error) {
|
||||||
|
if m.MGetCRL != nil {
|
||||||
|
return m.MGetCRL()
|
||||||
|
}
|
||||||
|
return m.Ret1.(*CertificateRevocationListInfo), m.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockAuthDB) StoreCRL(info *CertificateRevocationListInfo) error {
|
||||||
|
if m.MStoreCRL != nil {
|
||||||
|
return m.MStoreCRL(info)
|
||||||
|
}
|
||||||
|
return m.Err
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsRevoked mock.
|
// IsRevoked mock.
|
||||||
|
|
15
db/simple.go
15
db/simple.go
|
@ -41,6 +41,21 @@ func (s *SimpleDB) Revoke(rci *RevokedCertificateInfo) error {
|
||||||
return ErrNotImplemented
|
return ErrNotImplemented
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetRevokedCertificates returns a "NotImplemented" error.
|
||||||
|
func (s *SimpleDB) GetRevokedCertificates() (*[]RevokedCertificateInfo, error) {
|
||||||
|
return nil, ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCRL returns a "NotImplemented" error.
|
||||||
|
func (s *SimpleDB) GetCRL() (*CertificateRevocationListInfo, error) {
|
||||||
|
return nil, ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
// StoreCRL returns a "NotImplemented" error.
|
||||||
|
func (s *SimpleDB) StoreCRL(crlInfo *CertificateRevocationListInfo) error {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
// RevokeSSH returns a "NotImplemented" error.
|
// RevokeSSH returns a "NotImplemented" error.
|
||||||
func (s *SimpleDB) RevokeSSH(rci *RevokedCertificateInfo) error {
|
func (s *SimpleDB) RevokeSSH(rci *RevokedCertificateInfo) error {
|
||||||
return ErrNotImplemented
|
return ErrNotImplemented
|
||||||
|
|
|
@ -4,6 +4,7 @@ WORKDIR /src
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
RUN apk add --no-cache curl git make
|
RUN apk add --no-cache curl git make
|
||||||
|
RUN make V=1 download
|
||||||
RUN make V=1 bin/step-ca bin/step-awskms-init bin/step-cloudkms-init
|
RUN make V=1 bin/step-ca bin/step-awskms-init bin/step-cloudkms-init
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ COPY . .
|
||||||
|
|
||||||
RUN apk add --no-cache curl git make
|
RUN apk add --no-cache curl git make
|
||||||
RUN apk add --no-cache gcc musl-dev pkgconf pcsc-lite-dev
|
RUN apk add --no-cache gcc musl-dev pkgconf pcsc-lite-dev
|
||||||
|
RUN make V=1 download
|
||||||
RUN make V=1 GOFLAGS="" build
|
RUN make V=1 GOFLAGS="" build
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -126,17 +126,20 @@ is `json`.
|
||||||
|
|
||||||
* `db`: data persistence layer. See [database documentation](./database.md) for more
|
* `db`: data persistence layer. See [database documentation](./database.md) for more
|
||||||
info.
|
info.
|
||||||
|
* `type`: `badger`, `bbolt`, `mysql`, etc.
|
||||||
|
* `dataSource`: string that can be interpreted differently depending on the
|
||||||
|
type of the database. Usually a path to where the data is stored. See the
|
||||||
|
[database configuration docs](./database.md#configuration) for more info.
|
||||||
|
* `database`: name of the database. Used for backends that may have multiple
|
||||||
|
databases. e.g. MySQL
|
||||||
|
* `valueDir`: directory to store the value log in (Badger specific).
|
||||||
|
|
||||||
- type: `badger`, `bbolt`, `mysql`, etc.
|
* `crl`: Certificate Revocation List settings:
|
||||||
|
* `enable`: enables CRL generation (`true` to generate, `false` to disable)
|
||||||
- dataSource: `string` that can be interpreted differently depending on the
|
* `generateOnRevoke`: a revoke will generate a new CRL if the crl is enabled.
|
||||||
type of the database. Usually a path to where the data is stored. See
|
* `cacheDuration`: the duration until next update of the CRL, defaults to 24h.
|
||||||
the [database configuration docs](./database.md#configuration) for more info.
|
* `renewPeriod`: the time between CRL regeneration. If not set ~2/3 of the
|
||||||
|
cacheDuration will be used.
|
||||||
- database: name of the database. Used for backends that may have
|
|
||||||
multiple databases. e.g. MySQL
|
|
||||||
|
|
||||||
- valueDir: directory to store the value log in (Badger specific).
|
|
||||||
|
|
||||||
* `tls`: settings for negotiating communication with the CA; includes acceptable
|
* `tls`: settings for negotiating communication with the CA; includes acceptable
|
||||||
ciphersuites, min/max TLS version, etc.
|
ciphersuites, min/max TLS version, etc.
|
||||||
|
|
29
go.mod
29
go.mod
|
@ -3,15 +3,16 @@ module github.com/smallstep/certificates
|
||||||
go 1.18
|
go 1.18
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go v0.104.0
|
cloud.google.com/go v0.105.0 // indirect
|
||||||
cloud.google.com/go/security v1.8.0
|
cloud.google.com/go/longrunning v0.2.1
|
||||||
github.com/Azure/azure-sdk-for-go v65.0.0+incompatible // indirect
|
cloud.google.com/go/security v1.9.0
|
||||||
|
github.com/Azure/azure-sdk-for-go v67.0.0+incompatible // indirect
|
||||||
github.com/Azure/go-autorest/autorest v0.11.28 // indirect
|
github.com/Azure/go-autorest/autorest v0.11.28 // indirect
|
||||||
github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 // indirect
|
github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 // indirect
|
||||||
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
|
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
|
||||||
github.com/Masterminds/sprig/v3 v3.2.2
|
github.com/Masterminds/sprig/v3 v3.2.2
|
||||||
github.com/ThalesIgnite/crypto11 v1.2.5 // indirect
|
github.com/ThalesIgnite/crypto11 v1.2.5 // indirect
|
||||||
github.com/aws/aws-sdk-go v1.44.111 // indirect
|
github.com/aws/aws-sdk-go v1.44.117 // indirect
|
||||||
github.com/dgraph-io/ristretto v0.1.0 // indirect
|
github.com/dgraph-io/ristretto v0.1.0 // indirect
|
||||||
github.com/fatih/color v1.9.0 // indirect
|
github.com/fatih/color v1.9.0 // indirect
|
||||||
github.com/fxamacker/cbor/v2 v2.4.0
|
github.com/fxamacker/cbor/v2 v2.4.0
|
||||||
|
@ -23,7 +24,7 @@ require (
|
||||||
github.com/google/go-cmp v0.5.9
|
github.com/google/go-cmp v0.5.9
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
github.com/googleapis/gax-go/v2 v2.6.0
|
github.com/googleapis/gax-go/v2 v2.6.0
|
||||||
github.com/hashicorp/vault/api v1.8.1
|
github.com/hashicorp/vault/api v1.8.2
|
||||||
github.com/hashicorp/vault/api/auth/approle v0.3.0
|
github.com/hashicorp/vault/api/auth/approle v0.3.0
|
||||||
github.com/hashicorp/vault/api/auth/kubernetes v0.3.0
|
github.com/hashicorp/vault/api/auth/kubernetes v0.3.0
|
||||||
github.com/jhump/protoreflect v1.9.0 // indirect
|
github.com/jhump/protoreflect v1.9.0 // indirect
|
||||||
|
@ -38,18 +39,18 @@ require (
|
||||||
github.com/slackhq/nebula v1.6.1
|
github.com/slackhq/nebula v1.6.1
|
||||||
github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262
|
github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262
|
||||||
github.com/smallstep/nosql v0.5.0
|
github.com/smallstep/nosql v0.5.0
|
||||||
github.com/stretchr/testify v1.8.0
|
github.com/stretchr/testify v1.8.1
|
||||||
github.com/urfave/cli v1.22.10
|
github.com/urfave/cli v1.22.10
|
||||||
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352
|
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352
|
||||||
go.step.sm/cli-utils v0.7.5
|
go.step.sm/cli-utils v0.7.5
|
||||||
go.step.sm/crypto v0.21.0
|
go.step.sm/crypto v0.22.0
|
||||||
go.step.sm/linkedca v0.19.0-rc.3
|
go.step.sm/linkedca v0.19.0-rc.3
|
||||||
golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b
|
golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b
|
||||||
golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458
|
golang.org/x/net v0.0.0-20221014081412-f15817d10f9b
|
||||||
golang.org/x/sys v0.0.0-20221006211917-84dc82d7e875 // indirect
|
golang.org/x/sys v0.0.0-20221006211917-84dc82d7e875 // indirect
|
||||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
|
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
|
||||||
google.golang.org/api v0.99.0
|
google.golang.org/api v0.101.0
|
||||||
google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e
|
google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e
|
||||||
google.golang.org/grpc v1.50.1
|
google.golang.org/grpc v1.50.1
|
||||||
google.golang.org/protobuf v1.28.1
|
google.golang.org/protobuf v1.28.1
|
||||||
gopkg.in/square/go-jose.v2 v2.6.0
|
gopkg.in/square/go-jose.v2 v2.6.0
|
||||||
|
@ -95,7 +96,7 @@ require (
|
||||||
github.com/hashicorp/go-hclog v0.16.2 // indirect
|
github.com/hashicorp/go-hclog v0.16.2 // indirect
|
||||||
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
|
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
|
||||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
github.com/hashicorp/go-plugin v1.4.3 // indirect
|
github.com/hashicorp/go-plugin v1.4.5 // indirect
|
||||||
github.com/hashicorp/go-retryablehttp v0.6.6 // indirect
|
github.com/hashicorp/go-retryablehttp v0.6.6 // indirect
|
||||||
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
|
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
|
||||||
github.com/hashicorp/go-secure-stdlib/mlock v0.1.1 // indirect
|
github.com/hashicorp/go-secure-stdlib/mlock v0.1.1 // indirect
|
||||||
|
@ -141,8 +142,8 @@ require (
|
||||||
go.etcd.io/bbolt v1.3.6 // indirect
|
go.etcd.io/bbolt v1.3.6 // indirect
|
||||||
go.opencensus.io v0.23.0 // indirect
|
go.opencensus.io v0.23.0 // indirect
|
||||||
go.uber.org/atomic v1.9.0 // indirect
|
go.uber.org/atomic v1.9.0 // indirect
|
||||||
golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1 // indirect
|
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 // indirect
|
||||||
golang.org/x/text v0.3.8 // indirect
|
golang.org/x/text v0.4.0 // indirect
|
||||||
google.golang.org/appengine v1.6.7 // indirect
|
google.golang.org/appengine v1.6.7 // indirect
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
@ -155,4 +156,4 @@ require (
|
||||||
// replace go.step.sm/linkedca => ../linkedca
|
// replace go.step.sm/linkedca => ../linkedca
|
||||||
|
|
||||||
// use github.com/smallstep/pkcs7 fork with patches applied
|
// use github.com/smallstep/pkcs7 fork with patches applied
|
||||||
replace go.mozilla.org/pkcs7 => github.com/smallstep/pkcs7 v0.0.0-20211016004704-52592125d6f6
|
replace go.mozilla.org/pkcs7 => github.com/smallstep/pkcs7 v0.0.0-20221024180420-e1aab68dda05
|
||||||
|
|
59
go.sum
59
go.sum
|
@ -28,8 +28,8 @@ cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Ud
|
||||||
cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
|
cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
|
||||||
cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U=
|
cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U=
|
||||||
cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A=
|
cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A=
|
||||||
cloud.google.com/go v0.104.0 h1:gSmWO7DY1vOm0MVU6DNXM11BWHHsTUmsC5cv1fuW5X8=
|
cloud.google.com/go v0.105.0 h1:DNtEKRBAAzeS4KyIory52wWHuClNaXJ5x1F7xa4q+5Y=
|
||||||
cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA=
|
cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM=
|
||||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||||
|
@ -47,12 +47,14 @@ cloud.google.com/go/iam v0.3.0 h1:exkAomrVUuzx9kWFI1wm3KI0uoDeUFPB4kKGzx6x+Gc=
|
||||||
cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY=
|
cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY=
|
||||||
cloud.google.com/go/kms v1.4.0 h1:iElbfoE61VeLhnZcGOltqL8HIly8Nhbe5t6JlH9GXjo=
|
cloud.google.com/go/kms v1.4.0 h1:iElbfoE61VeLhnZcGOltqL8HIly8Nhbe5t6JlH9GXjo=
|
||||||
cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA=
|
cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA=
|
||||||
|
cloud.google.com/go/longrunning v0.2.1 h1:x3E/YapFCMe2G1D9qCv9COrBldOwK/n0OC7w9PLzeX0=
|
||||||
|
cloud.google.com/go/longrunning v0.2.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE=
|
||||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||||
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
||||||
cloud.google.com/go/security v1.8.0 h1:linnRc3/gJYDfKbAtNixVQ52+66DpOx5MmCz0NNxal8=
|
cloud.google.com/go/security v1.9.0 h1:o9frPOtXW2f4zMlw4SYPE42LRz/hhrYVWtAEUkPvyA4=
|
||||||
cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU=
|
cloud.google.com/go/security v1.9.0/go.mod h1:6Ta1bO8LXI89nZnmnsZGp9lVoVWXqsVbIq/t9dzI+2Q=
|
||||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||||
|
@ -63,8 +65,8 @@ filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek=
|
||||||
filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
|
filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
|
||||||
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M=
|
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/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
|
||||||
github.com/Azure/azure-sdk-for-go v65.0.0+incompatible h1:HzKLt3kIwMm4KeJYTdx9EbjRYTySD/t8i1Ee/W5EGXw=
|
github.com/Azure/azure-sdk-for-go v67.0.0+incompatible h1:SVBwznSETB0Sipd0uyGJr7khLhJOFRUEUb+0JgkCvDo=
|
||||||
github.com/Azure/azure-sdk-for-go v65.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
github.com/Azure/azure-sdk-for-go v67.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||||
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
|
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
|
||||||
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||||
github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc=
|
github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc=
|
||||||
|
@ -128,8 +130,8 @@ github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgI
|
||||||
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
|
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
|
||||||
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
|
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
|
||||||
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||||
github.com/aws/aws-sdk-go v1.44.111 h1:AcWfOgeedSQ4gQVwcIe6aLxpQNJMloZQyqnr7Dzki+s=
|
github.com/aws/aws-sdk-go v1.44.117 h1:mZuODB3Y4soG9QWAXyGb2po+6Easa/enifpj4MnZ91s=
|
||||||
github.com/aws/aws-sdk-go v1.44.111/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
|
github.com/aws/aws-sdk-go v1.44.117/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
|
||||||
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
|
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||||
|
@ -393,8 +395,9 @@ github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iP
|
||||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
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 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||||
github.com/hashicorp/go-plugin v1.4.3 h1:DXmvivbWD5qdiBts9TpBC7BYL1Aia5sxbRgQB+v6UZM=
|
|
||||||
github.com/hashicorp/go-plugin v1.4.3/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ=
|
github.com/hashicorp/go-plugin v1.4.3/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ=
|
||||||
|
github.com/hashicorp/go-plugin v1.4.5 h1:oTE/oQR4eghggRg8VY7PAz3dr++VwDNBGCcOfIvHpBo=
|
||||||
|
github.com/hashicorp/go-plugin v1.4.5/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s=
|
||||||
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
||||||
github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM=
|
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.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||||
|
@ -434,8 +437,8 @@ github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0m
|
||||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||||
github.com/hashicorp/vault/api v1.8.0/go.mod h1:uJrw6D3y9Rv7hhmS17JQC50jbPDAZdjZoTtrCCxxs7E=
|
github.com/hashicorp/vault/api v1.8.0/go.mod h1:uJrw6D3y9Rv7hhmS17JQC50jbPDAZdjZoTtrCCxxs7E=
|
||||||
github.com/hashicorp/vault/api v1.8.1 h1:bMieWIe6dAlqAAPReZO/8zYtXaWUg/21umwqGZpEjCI=
|
github.com/hashicorp/vault/api v1.8.2 h1:C7OL9YtOtwQbTKI9ogB0A1wffRbCN+rH/LLCHO3d8HM=
|
||||||
github.com/hashicorp/vault/api v1.8.1/go.mod h1:uJrw6D3y9Rv7hhmS17JQC50jbPDAZdjZoTtrCCxxs7E=
|
github.com/hashicorp/vault/api v1.8.2/go.mod h1:ML8aYzBIhY5m1MD1B2Q0JV89cC85YVH4t5kBaZiyVaE=
|
||||||
github.com/hashicorp/vault/api/auth/approle v0.3.0 h1:Ib0oCNXsCq/QZhPYtXPzJEbGS5WR/KoZf8c84QoFdkU=
|
github.com/hashicorp/vault/api/auth/approle v0.3.0 h1:Ib0oCNXsCq/QZhPYtXPzJEbGS5WR/KoZf8c84QoFdkU=
|
||||||
github.com/hashicorp/vault/api/auth/approle v0.3.0/go.mod h1:hm51TbjzUkPO0Y17wkrpwOpvyyMRpXJNueTHiG04t3k=
|
github.com/hashicorp/vault/api/auth/approle v0.3.0/go.mod h1:hm51TbjzUkPO0Y17wkrpwOpvyyMRpXJNueTHiG04t3k=
|
||||||
github.com/hashicorp/vault/api/auth/kubernetes v0.3.0 h1:HkaCmTKzcgLa2tjdiAid1rbmyQNmQGHfnmvIIM2WorY=
|
github.com/hashicorp/vault/api/auth/kubernetes v0.3.0 h1:HkaCmTKzcgLa2tjdiAid1rbmyQNmQGHfnmvIIM2WorY=
|
||||||
|
@ -710,8 +713,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/assert v0.0.0-20200723003110-82e2b9b3b262/go.mod h1:MyOHs9Po2fbM1LHej6sBUT8ozbxmMOFG+E+rx/GSGuc=
|
||||||
github.com/smallstep/nosql v0.5.0 h1:1BPyHy8bha8qSaxgULGEdqhXpNFXimAfudnauFVqmxw=
|
github.com/smallstep/nosql v0.5.0 h1:1BPyHy8bha8qSaxgULGEdqhXpNFXimAfudnauFVqmxw=
|
||||||
github.com/smallstep/nosql v0.5.0/go.mod h1:yKZT5h7cdIVm6wEKM9+jN5dgK80Hljpuy8HNsnI7Gzo=
|
github.com/smallstep/nosql v0.5.0/go.mod h1:yKZT5h7cdIVm6wEKM9+jN5dgK80Hljpuy8HNsnI7Gzo=
|
||||||
github.com/smallstep/pkcs7 v0.0.0-20211016004704-52592125d6f6 h1:8Rjy6IZbSM/jcYgBWCoLIGjug7QcoLtF9sUuhDrHD2U=
|
github.com/smallstep/pkcs7 v0.0.0-20221024180420-e1aab68dda05 h1:nVZXaJTwrUcfPUSZknkOidfITqOXSO0wE8pkOUTOdSM=
|
||||||
github.com/smallstep/pkcs7 v0.0.0-20211016004704-52592125d6f6/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk=
|
github.com/smallstep/pkcs7 v0.0.0-20221024180420-e1aab68dda05/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||||
|
@ -736,8 +739,9 @@ github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5J
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
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.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||||
github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4=
|
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
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.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
@ -745,8 +749,9 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
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.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||||
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/thales-e-security/pool v0.0.2 h1:RAPs4q2EbWsTit6tpzuvTFlgFRJ3S8Evf5gtvVDbmPg=
|
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/thales-e-security/pool v0.0.2/go.mod h1:qtpMm2+thHtqhLzTwgDBj/OuNnMpupY8mv0Phz0gjhU=
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
|
@ -786,8 +791,8 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe
|
||||||
go.step.sm/cli-utils v0.7.5 h1:jyp6X8k8mN1B0uWJydTid0C++8tQhm2kaaAdXKQQzdk=
|
go.step.sm/cli-utils v0.7.5 h1:jyp6X8k8mN1B0uWJydTid0C++8tQhm2kaaAdXKQQzdk=
|
||||||
go.step.sm/cli-utils v0.7.5/go.mod h1:taSsY8haLmXoXM3ZkywIyRmVij/4Aj0fQbNTlJvv71I=
|
go.step.sm/cli-utils v0.7.5/go.mod h1:taSsY8haLmXoXM3ZkywIyRmVij/4Aj0fQbNTlJvv71I=
|
||||||
go.step.sm/crypto v0.9.0/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0=
|
go.step.sm/crypto v0.9.0/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0=
|
||||||
go.step.sm/crypto v0.21.0 h1:Qwxk5JrqG0Q1t8tOfDM3zKTNECG6J5J24qgWZCRM0Ic=
|
go.step.sm/crypto v0.22.0 h1:aloAQsNYEX87e4xL+jqsd6zCA4mGThPBmz2akMKss8Q=
|
||||||
go.step.sm/crypto v0.21.0/go.mod h1:diT2XWIHQy0397UI3i78qCKeLLLp2wu0/DIJI66u/MU=
|
go.step.sm/crypto v0.22.0/go.mod h1:7PTDUApWUTmQbDt4x82cbk3nx5k7JwBo2dFG9CGEp9A=
|
||||||
go.step.sm/linkedca v0.19.0-rc.3 h1:3Uu8j187wm7mby+/pz/aQ0wHKRm7w/2AsVPpvcAn4v8=
|
go.step.sm/linkedca v0.19.0-rc.3 h1:3Uu8j187wm7mby+/pz/aQ0wHKRm7w/2AsVPpvcAn4v8=
|
||||||
go.step.sm/linkedca v0.19.0-rc.3/go.mod h1:MCZmPIdzElEofZbiw4eyUHayXgFTwa94cNAV34aJ5ew=
|
go.step.sm/linkedca v0.19.0-rc.3/go.mod h1:MCZmPIdzElEofZbiw4eyUHayXgFTwa94cNAV34aJ5ew=
|
||||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
|
@ -906,8 +911,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
|
||||||
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458 h1:MgJ6t2zo8v0tbmLCueaCbF1RM+TtB0rs3Lv8DGtOIpY=
|
golang.org/x/net v0.0.0-20221014081412-f15817d10f9b h1:tvrvnPFcdzp294diPnrdZZZ8XUt2Tyj7svb7X52iDuU=
|
||||||
golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
@ -924,8 +929,8 @@ golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ
|
||||||
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1 h1:3VPzK7eqH25j7GYw5w6g/GzNRc0/fYtrxz27z1gD4W0=
|
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 h1:nt+Q6cXKz4MosCSpnbMtqiQ8Oz0pxTef2B4Vca2lvfk=
|
||||||
golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
|
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
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-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
@ -1025,8 +1030,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/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.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
|
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
|
||||||
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/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
@ -1135,8 +1140,8 @@ google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3h
|
||||||
google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo=
|
google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo=
|
||||||
google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g=
|
google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g=
|
||||||
google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA=
|
google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA=
|
||||||
google.golang.org/api v0.99.0 h1:tsBtOIklCE2OFxhmcYSVqGwSAN/Y897srxmcvAQnwK8=
|
google.golang.org/api v0.101.0 h1:lJPPeEBIRxGpGLwnBTam1NPEM8Z2BmmXEd3z812pjwM=
|
||||||
google.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91A08=
|
google.golang.org/api v0.101.0/go.mod h1:CjxAAWWt3A3VrUE2IGDY2bgK5qhoG/OkyWVlYcP05MY=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
@ -1212,8 +1217,8 @@ google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ6
|
||||||
google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||||
google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
|
google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
|
||||||
google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
|
google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
|
||||||
google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e h1:halCgTFuLWDRD61piiNSxPsARANGD3Xl16hPrLgLiIg=
|
google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e h1:S9GbmC1iCgvbLyAokVCwiO6tVIrU9Y7c5oMx1V/ki/Y=
|
||||||
google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U=
|
google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s=
|
||||||
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
|
|
@ -1,91 +0,0 @@
|
||||||
#########################################
|
|
||||||
# Building Docker Image
|
|
||||||
#
|
|
||||||
# This uses a multi-stage build file. The first stage is a builder (that might
|
|
||||||
# be large in size). After the build has succeeded, the statically linked
|
|
||||||
# binary is copied to a new image that is optimized for size.
|
|
||||||
#########################################
|
|
||||||
|
|
||||||
ifeq (, $(shell which docker))
|
|
||||||
DOCKER_CLIENT_OS := linux
|
|
||||||
else
|
|
||||||
DOCKER_CLIENT_OS := $(strip $(shell docker version -f '{{.Client.Os}}' 2>/dev/null))
|
|
||||||
endif
|
|
||||||
|
|
||||||
DOCKER_PLATFORMS = linux/amd64,linux/386,linux/arm,linux/arm64
|
|
||||||
DOCKER_IMAGE_NAME = smallstep/step-ca
|
|
||||||
|
|
||||||
docker-prepare:
|
|
||||||
# Ensure, we can build for ARM architecture
|
|
||||||
ifeq (linux,$(DOCKER_CLIENT_OS))
|
|
||||||
[ -f /proc/sys/fs/binfmt_misc/qemu-arm ] || docker run --rm --privileged linuxkit/binfmt:v0.8-amd64
|
|
||||||
endif
|
|
||||||
|
|
||||||
# Register buildx builder
|
|
||||||
mkdir -p $$HOME/.docker/cli-plugins
|
|
||||||
|
|
||||||
test -f $$HOME/.docker/cli-plugins/docker-buildx || \
|
|
||||||
(wget -q -O $$HOME/.docker/cli-plugins/docker-buildx https://github.com/docker/buildx/releases/download/v0.4.1/buildx-v0.4.1.$(DOCKER_CLIENT_OS)-amd64 && \
|
|
||||||
chmod +x $$HOME/.docker/cli-plugins/docker-buildx)
|
|
||||||
|
|
||||||
docker buildx create --use --name mybuilder --platform="$(DOCKER_PLATFORMS)" || true
|
|
||||||
|
|
||||||
.PHONY: docker-prepare
|
|
||||||
|
|
||||||
#################################################
|
|
||||||
# Releasing Docker Images
|
|
||||||
#
|
|
||||||
# Using the docker build infrastructure, this section is responsible for
|
|
||||||
# logging into docker hub.
|
|
||||||
#################################################
|
|
||||||
|
|
||||||
# Rely on DOCKER_USERNAME and DOCKER_PASSWORD being set inside the CI or
|
|
||||||
# equivalent environment
|
|
||||||
docker-login:
|
|
||||||
$Q docker login -u="$(DOCKER_USERNAME)" -p="$(DOCKER_PASSWORD)"
|
|
||||||
|
|
||||||
.PHONY: docker-login
|
|
||||||
|
|
||||||
#################################################
|
|
||||||
# Targets for different type of builds
|
|
||||||
#################################################
|
|
||||||
|
|
||||||
define DOCKER_BUILDX
|
|
||||||
# $(1) -- Image Tag
|
|
||||||
# $(2) -- Push (empty is no push | --push will push to dockerhub)
|
|
||||||
docker buildx build . --progress plain -t $(DOCKER_IMAGE_NAME):$(1) -f docker/Dockerfile.step-ca --platform="$(DOCKER_PLATFORMS)" $(2)
|
|
||||||
echo -n "$(COSIGN_PWD)" | cosign sign -key /tmp/cosign.key -r $(DOCKER_IMAGE_NAME):$(1)
|
|
||||||
|
|
||||||
endef
|
|
||||||
|
|
||||||
# For non-master builds don't build the docker containers.
|
|
||||||
docker-branch:
|
|
||||||
|
|
||||||
# For master builds don't build the docker containers.
|
|
||||||
docker-master:
|
|
||||||
|
|
||||||
# For all builds with a release candidate tag build and push the containers.
|
|
||||||
docker-release-candidate: docker-prepare docker-login
|
|
||||||
$(call DOCKER_BUILDX,$(VERSION),--push)
|
|
||||||
|
|
||||||
# For all builds with a release tag build and push the containers.
|
|
||||||
docker-release: docker-prepare docker-login
|
|
||||||
$(call DOCKER_BUILDX,latest,--push)
|
|
||||||
$(call DOCKER_BUILDX,$(VERSION),--push)
|
|
||||||
|
|
||||||
.PHONY: docker-branch docker-master docker-release-candidate docker-release
|
|
||||||
|
|
||||||
# XXX We put the output for the build in 'output' so we don't mess with how we
|
|
||||||
# do rule overriding from the base Makefile (if you name it 'build' it messes up
|
|
||||||
# the wildcarding).
|
|
||||||
DOCKER_OUTPUT=$(OUTPUT_ROOT)docker/
|
|
||||||
|
|
||||||
DOCKER_MAKE=V=$V GOOS_OVERRIDE='GOOS=linux GOARCH=amd64' PREFIX=$(1) make $(1)bin/$(BINNAME)
|
|
||||||
DOCKER_BUILD=$Q docker build -t $(DOCKER_IMAGE_NAME):latest -f docker/Dockerfile.step-ca --build-arg BINPATH=$(DOCKER_OUTPUT)bin/$(BINNAME) .
|
|
||||||
|
|
||||||
docker-dev: docker/Dockerfile.step-ca
|
|
||||||
mkdir -p $(DOCKER_OUTPUT)
|
|
||||||
$(call DOCKER_MAKE,$(DOCKER_OUTPUT),step-ca)
|
|
||||||
$(call DOCKER_BUILD)
|
|
||||||
|
|
||||||
.PHONY: docker-dev
|
|
36
pki/helm.go
36
pki/helm.go
|
@ -17,6 +17,7 @@ type helmVariables struct {
|
||||||
Defaults *linkedca.Defaults
|
Defaults *linkedca.Defaults
|
||||||
Password string
|
Password string
|
||||||
EnableSSH bool
|
EnableSSH bool
|
||||||
|
EnableAdmin bool
|
||||||
TLS authconfig.TLSOptions
|
TLS authconfig.TLSOptions
|
||||||
Provisioners []provisioner.Interface
|
Provisioners []provisioner.Interface
|
||||||
}
|
}
|
||||||
|
@ -34,14 +35,39 @@ func (p *PKI) WriteHelmTemplate(w io.Writer) error {
|
||||||
p.Ssh = nil
|
p.Ssh = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert provisioner to ca.json
|
// Convert provisioners to ca.json representation
|
||||||
provisioners := make([]provisioner.Interface, len(p.Authority.Provisioners))
|
provisioners := []provisioner.Interface{}
|
||||||
for i, p := range p.Authority.Provisioners {
|
for _, p := range p.Authority.Provisioners {
|
||||||
pp, err := authority.ProvisionerToCertificates(p)
|
pp, err := authority.ProvisionerToCertificates(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
provisioners[i] = pp
|
provisioners = append(provisioners, pp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add default ACME provisioner if enabled. Note that this logic is similar
|
||||||
|
// to what's in p.GenerateConfig(), but that codepath isn't taken when
|
||||||
|
// writing the Helm template. The default JWK provisioner is added earlier in
|
||||||
|
// the process and that's part of the provisioners above.
|
||||||
|
// TODO(hs): consider refactoring the initialization, so that this becomes
|
||||||
|
// easier to reason about and maintain.
|
||||||
|
if p.options.enableACME {
|
||||||
|
provisioners = append(provisioners, &provisioner.ACME{
|
||||||
|
Type: "ACME",
|
||||||
|
Name: "acme",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add default SSHPOP provisioner if enabled. Similar to the above, this is
|
||||||
|
// the same as what happens in p.GenerateConfig().
|
||||||
|
if p.options.enableSSH {
|
||||||
|
provisioners = append(provisioners, &provisioner.SSHPOP{
|
||||||
|
Type: "SSHPOP",
|
||||||
|
Name: "sshpop",
|
||||||
|
Claims: &provisioner.Claims{
|
||||||
|
EnableSSHCA: &p.options.enableSSH,
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := tmpl.Execute(w, helmVariables{
|
if err := tmpl.Execute(w, helmVariables{
|
||||||
|
@ -49,6 +75,7 @@ func (p *PKI) WriteHelmTemplate(w io.Writer) error {
|
||||||
Defaults: &p.Defaults,
|
Defaults: &p.Defaults,
|
||||||
Password: "",
|
Password: "",
|
||||||
EnableSSH: p.options.enableSSH,
|
EnableSSH: p.options.enableSSH,
|
||||||
|
EnableAdmin: p.options.enableAdmin,
|
||||||
TLS: authconfig.DefaultTLSOptions,
|
TLS: authconfig.DefaultTLSOptions,
|
||||||
Provisioners: provisioners,
|
Provisioners: provisioners,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
|
@ -88,6 +115,7 @@ inject:
|
||||||
type: badgerv2
|
type: badgerv2
|
||||||
dataSource: /home/step/db
|
dataSource: /home/step/db
|
||||||
authority:
|
authority:
|
||||||
|
enableAdmin: {{ .EnableAdmin }}
|
||||||
provisioners:
|
provisioners:
|
||||||
{{- range .Provisioners }}
|
{{- range .Provisioners }}
|
||||||
- {{ . | toJson }}
|
- {{ . | toJson }}
|
||||||
|
|
232
pki/helm_test.go
Normal file
232
pki/helm_test.go
Normal file
|
@ -0,0 +1,232 @@
|
||||||
|
package pki
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/sha256"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"encoding/pem"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"go.step.sm/crypto/jose"
|
||||||
|
"go.step.sm/linkedca"
|
||||||
|
|
||||||
|
"github.com/smallstep/certificates/cas/apiv1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPKI_WriteHelmTemplate(t *testing.T) {
|
||||||
|
var preparePKI = func(t *testing.T, opts ...Option) *PKI {
|
||||||
|
o := apiv1.Options{
|
||||||
|
Type: "softcas",
|
||||||
|
IsCreator: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add default WithHelm option
|
||||||
|
opts = append(opts, WithHelm())
|
||||||
|
|
||||||
|
// TODO(hs): invoking `New` doesn't perform all operations that are executed
|
||||||
|
// when `ca init --helm` is executed. Ideally this logic should be handled
|
||||||
|
// in one place and probably inside of the PKI initialization. For testing
|
||||||
|
// purposes the missing operations to fill a Helm template fully are faked
|
||||||
|
// by `setKeyPair`, `setCertificates` and `setSSHSigningKeys`
|
||||||
|
p, err := New(o, opts...)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// setKeyPair sets a predefined JWK and a default JWK provisioner. This is one
|
||||||
|
// of the things performed in the `ca init` code that's not part of `New`, but
|
||||||
|
// performed after that in p.GenerateKeyPairs`. We're currently using the same
|
||||||
|
// JWK for every test to keep test variance small: we're not testing JWK generation
|
||||||
|
// here after all. It's a bit dangerous to redefine the function here, but it's
|
||||||
|
// the simplest way to make this fully testable without refactoring the init now.
|
||||||
|
// The password for the predefined encrypted key is \x01\x03\x03\x07.
|
||||||
|
setKeyPair(t, p)
|
||||||
|
|
||||||
|
// setCertificates sets some static intermediate and root CA certificate bytes. It
|
||||||
|
// replaces the logic executed in `p.GenerateRootCertificate`, `p.WriteRootCertificate`,
|
||||||
|
// and `p.GenerateIntermediateCertificate`.
|
||||||
|
setCertificates(t, p)
|
||||||
|
|
||||||
|
// setSSHSigningKeys sets predefined SSH user and host certificate and key bytes.
|
||||||
|
// This replaces the logic in `p.GenerateSSHSigningKeys`
|
||||||
|
setSSHSigningKeys(t, p)
|
||||||
|
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
type test struct {
|
||||||
|
pki *PKI
|
||||||
|
testFile string
|
||||||
|
wantErr bool
|
||||||
|
}
|
||||||
|
var tests = map[string]func(t *testing.T) test{
|
||||||
|
"ok/simple": func(t *testing.T) test {
|
||||||
|
return test{
|
||||||
|
pki: preparePKI(t),
|
||||||
|
testFile: "testdata/helm/simple.yml",
|
||||||
|
wantErr: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ok/with-provisioner": func(t *testing.T) test {
|
||||||
|
return test{
|
||||||
|
pki: preparePKI(t, WithProvisioner("a-provisioner")),
|
||||||
|
testFile: "testdata/helm/with-provisioner.yml",
|
||||||
|
wantErr: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ok/with-acme": func(t *testing.T) test {
|
||||||
|
return test{
|
||||||
|
pki: preparePKI(t, WithACME()),
|
||||||
|
testFile: "testdata/helm/with-acme.yml",
|
||||||
|
wantErr: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ok/with-admin": func(t *testing.T) test {
|
||||||
|
return test{
|
||||||
|
pki: preparePKI(t, WithAdmin()),
|
||||||
|
testFile: "testdata/helm/with-admin.yml",
|
||||||
|
wantErr: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ok/with-ssh": func(t *testing.T) test {
|
||||||
|
return test{
|
||||||
|
pki: preparePKI(t, WithSSH()),
|
||||||
|
testFile: "testdata/helm/with-ssh.yml",
|
||||||
|
wantErr: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ok/with-ssh-and-acme": func(t *testing.T) test {
|
||||||
|
return test{
|
||||||
|
pki: preparePKI(t, WithSSH(), WithACME()),
|
||||||
|
testFile: "testdata/helm/with-ssh-and-acme.yml",
|
||||||
|
wantErr: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fail/authority.ProvisionerToCertificates": func(t *testing.T) test {
|
||||||
|
pki := preparePKI(t)
|
||||||
|
pki.Authority.Provisioners = append(pki.Authority.Provisioners,
|
||||||
|
&linkedca.Provisioner{
|
||||||
|
Type: linkedca.Provisioner_JWK,
|
||||||
|
Name: "Broken JWK",
|
||||||
|
Details: nil,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return test{
|
||||||
|
pki: pki,
|
||||||
|
wantErr: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for name, run := range tests {
|
||||||
|
tc := run(t)
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
|
||||||
|
w := &bytes.Buffer{}
|
||||||
|
if err := tc.pki.WriteHelmTemplate(w); (err != nil) != tc.wantErr {
|
||||||
|
t.Errorf("PKI.WriteHelmTemplate() error = %v, wantErr %v", err, tc.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if tc.wantErr {
|
||||||
|
// don't compare output if an error was expected on output
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
wantBytes, err := os.ReadFile(tc.testFile)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
if diff := cmp.Diff(wantBytes, w.Bytes()); diff != "" {
|
||||||
|
t.Logf("Generated Helm template did not match reference %q\n", tc.testFile)
|
||||||
|
t.Errorf("Diff follows:\n%s\n", diff)
|
||||||
|
t.Errorf("Full output:\n%s\n", w.Bytes())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// setKeyPair sets a predefined JWK and a default JWK provisioner.
|
||||||
|
func setKeyPair(t *testing.T, p *PKI) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
p.ottPublicKey, err = jose.ParseKey([]byte(`{"use":"sig","kty":"EC","kid":"zsUmysmDVoGJ71YoPHyZ-68tNihDaDaO5Mu7xX3M-_I","crv":"P-256","alg":"ES256","x":"Pqnua4CzqKz6ua41J3yeWZ1sRkGt0UlCkbHv8H2DGuY","y":"UhoZ_2ItDen9KQTcjay-ph-SBXH0mwqhHyvrrqIFDOI"}`))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
p.ottPrivateKey, err = jose.ParseEncrypted("eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJjdHkiOiJqd2sranNvbiIsImVuYyI6IkEyNTZHQ00iLCJwMmMiOjEwMDAwMCwicDJzIjoiZjVvdGVRS2hvOXl4MmQtSGlMZi05QSJ9.eYA6tt3fNuUpoxKWDT7P0Lbn2juxhEbTxEnwEMbjlYLLQ3sxL-dYTA.ven-FhmdjlC9itH0.a2jRTarN9vPd6F_mWnNBlOn6KbfMjCApmci2t65XbAsLzYFzhI_79Ykm5ueMYTupWLTjBJctl-g51ZHmsSB55pStbpoyyLNAsUX2E1fTmHe-Ni8bRrspwLv15FoN1Xo1g0mpR-ufWIFxOsW-QIfnMmMIIkygVuHFXmg2tFpzTNNG5aS29K3dN2nyk0WJrdIq79hZSTqVkkBU25Yu3A46sgjcM86XcIJJ2XUEih_KWEa6T1YrkixGu96pebjVqbO0R6dbDckfPF7FqNnwPHVtb1ACFpEYoOJVIbUCMaARBpWsxYhjJZlEM__XA46l8snFQDkNY3CdN0p1_gF3ckA.JLmq9nmu1h9oUi1S8ZxYjA")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var claims *linkedca.Claims
|
||||||
|
if p.options.enableSSH {
|
||||||
|
claims = &linkedca.Claims{
|
||||||
|
Ssh: &linkedca.SSHClaims{
|
||||||
|
Enabled: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
publicKey, err := json.Marshal(p.ottPublicKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
encryptedKey, err := p.ottPrivateKey.CompactSerialize()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
p.Authority.Provisioners = append(p.Authority.Provisioners, &linkedca.Provisioner{
|
||||||
|
Type: linkedca.Provisioner_JWK,
|
||||||
|
Name: p.options.provisioner,
|
||||||
|
Claims: claims,
|
||||||
|
Details: &linkedca.ProvisionerDetails{
|
||||||
|
Data: &linkedca.ProvisionerDetails_JWK{
|
||||||
|
JWK: &linkedca.JWKProvisioner{
|
||||||
|
PublicKey: publicKey,
|
||||||
|
EncryptedPrivateKey: []byte(encryptedKey),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// setCertificates sets some static, gibberish intermediate and root CA certificate and key bytes.
|
||||||
|
func setCertificates(t *testing.T, p *PKI) {
|
||||||
|
raw := []byte("these are just some fake root CA cert bytes")
|
||||||
|
p.Files[p.Root[0]] = encodeCertificate(&x509.Certificate{Raw: raw})
|
||||||
|
p.Files[p.RootKey[0]] = pem.EncodeToMemory(&pem.Block{
|
||||||
|
Type: "EC PRIVATE KEY",
|
||||||
|
Bytes: []byte("these are just some fake root CA key bytes"),
|
||||||
|
})
|
||||||
|
p.Files[p.Intermediate] = encodeCertificate(&x509.Certificate{Raw: []byte("these are just some fake intermediate CA cert bytes")})
|
||||||
|
p.Files[p.IntermediateKey] = pem.EncodeToMemory(&pem.Block{
|
||||||
|
Type: "EC PRIVATE KEY",
|
||||||
|
Bytes: []byte("these are just some fake intermediate CA key bytes"),
|
||||||
|
})
|
||||||
|
sum := sha256.Sum256(raw)
|
||||||
|
p.Defaults.Fingerprint = strings.ToLower(hex.EncodeToString(sum[:]))
|
||||||
|
}
|
||||||
|
|
||||||
|
// setSSHSigningKeys sets some static, gibberish ssh user and host CA certificate and key bytes.
|
||||||
|
func setSSHSigningKeys(t *testing.T, p *PKI) {
|
||||||
|
|
||||||
|
if !p.options.enableSSH {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Files[p.Ssh.HostKey] = pem.EncodeToMemory(&pem.Block{
|
||||||
|
Type: "EC PRIVATE KEY",
|
||||||
|
Bytes: []byte("fake ssh host key bytes"),
|
||||||
|
})
|
||||||
|
p.Files[p.Ssh.HostPublicKey] = []byte("ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJ0IdS5sZm6KITBMZLEJD6b5ROVraYHcAOr3feFel8r1Wp4DRPR1oU0W00J/zjNBRBbANlJoYN4x/8WNNVZ49Ms=")
|
||||||
|
p.Files[p.Ssh.UserKey] = pem.EncodeToMemory(&pem.Block{
|
||||||
|
Type: "EC PRIVATE KEY",
|
||||||
|
Bytes: []byte("fake ssh user key bytes"),
|
||||||
|
})
|
||||||
|
p.Files[p.Ssh.UserPublicKey] = []byte("ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEWA1qUxaGwVNErsvEOGe2d6TvLMF+aiVpuOiIEvpMJ3JeJmecLQctjWqeIbpSvy6/gRa7c82Ge5rLlapYmOChs=")
|
||||||
|
}
|
38
pki/pki.go
38
pki/pki.go
|
@ -176,6 +176,7 @@ func GetProvisionerKey(caURL, rootFile, kid string) (string, error) {
|
||||||
|
|
||||||
type options struct {
|
type options struct {
|
||||||
provisioner string
|
provisioner string
|
||||||
|
superAdminSubject string
|
||||||
pkiOnly bool
|
pkiOnly bool
|
||||||
enableACME bool
|
enableACME bool
|
||||||
enableSSH bool
|
enableSSH bool
|
||||||
|
@ -220,6 +221,15 @@ func WithProvisioner(s string) Option {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithSuperAdminSubject defines the subject of the first
|
||||||
|
// super admin for use with the Admin API. The admin will belong
|
||||||
|
// to the first JWK provisioner.
|
||||||
|
func WithSuperAdminSubject(s string) Option {
|
||||||
|
return func(p *PKI) {
|
||||||
|
p.options.superAdminSubject = s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// WithPKIOnly will only generate the PKI without the step-ca config files.
|
// WithPKIOnly will only generate the PKI without the step-ca config files.
|
||||||
func WithPKIOnly() Option {
|
func WithPKIOnly() Option {
|
||||||
return func(p *PKI) {
|
return func(p *PKI) {
|
||||||
|
@ -307,6 +317,9 @@ type PKI struct {
|
||||||
|
|
||||||
// New creates a new PKI configuration.
|
// New creates a new PKI configuration.
|
||||||
func New(o apiv1.Options, opts ...Option) (*PKI, error) {
|
func New(o apiv1.Options, opts ...Option) (*PKI, error) {
|
||||||
|
// TODO(hs): invoking `New` with a context active will use values from
|
||||||
|
// that CA context while generating the context. Thay may or may not
|
||||||
|
// be fully expected and/or what we want. Check that.
|
||||||
currentCtx := step.Contexts().GetCurrent()
|
currentCtx := step.Contexts().GetCurrent()
|
||||||
caService, err := cas.New(context.Background(), o)
|
caService, err := cas.New(context.Background(), o)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -645,7 +658,7 @@ func (p *PKI) GetCertificateAuthority() error {
|
||||||
// SSH user certificates and a private key used for signing host certificates.
|
// SSH user certificates and a private key used for signing host certificates.
|
||||||
func (p *PKI) GenerateSSHSigningKeys(password []byte) error {
|
func (p *PKI) GenerateSSHSigningKeys(password []byte) error {
|
||||||
// Enable SSH
|
// Enable SSH
|
||||||
p.options.enableSSH = true
|
p.options.enableSSH = true // TODO(hs): change this function to not mutate configuration state
|
||||||
|
|
||||||
// Create SSH key used to sign host certificates. Using
|
// Create SSH key used to sign host certificates. Using
|
||||||
// kmsapi.UnspecifiedSignAlgorithm will default to the default algorithm.
|
// kmsapi.UnspecifiedSignAlgorithm will default to the default algorithm.
|
||||||
|
@ -883,6 +896,11 @@ func (p *PKI) GenerateConfig(opt ...ConfigOption) (*authconfig.Config, error) {
|
||||||
//
|
//
|
||||||
// Note that we might want to be able to define the database as a
|
// Note that we might want to be able to define the database as a
|
||||||
// flag in `step ca init` so we can write to the proper place.
|
// flag in `step ca init` so we can write to the proper place.
|
||||||
|
//
|
||||||
|
// TODO(hs): the logic for creating the provisioners and the super admin
|
||||||
|
// is similar to what's done when automatically migrating the provisioners.
|
||||||
|
// This is related to the existing comment above. Refactor this to exist in
|
||||||
|
// a single place and ensure it happens only once.
|
||||||
_db, err := db.New(cfg.DB)
|
_db, err := db.New(cfg.DB)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -906,9 +924,13 @@ func (p *PKI) GenerateConfig(opt ...ConfigOption) (*authconfig.Config, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Add the first provisioner as an admin.
|
// Add the first provisioner as an admin.
|
||||||
|
superAdminSubject := "step"
|
||||||
|
if p.options.superAdminSubject != "" {
|
||||||
|
superAdminSubject = p.options.superAdminSubject
|
||||||
|
}
|
||||||
if err := adminDB.CreateAdmin(context.Background(), &linkedca.Admin{
|
if err := adminDB.CreateAdmin(context.Background(), &linkedca.Admin{
|
||||||
AuthorityId: admin.DefaultAuthorityID,
|
AuthorityId: admin.DefaultAuthorityID,
|
||||||
Subject: "step",
|
Subject: superAdminSubject,
|
||||||
Type: linkedca.Admin_SUPER_ADMIN,
|
Type: linkedca.Admin_SUPER_ADMIN,
|
||||||
ProvisionerId: adminID,
|
ProvisionerId: adminID,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
|
@ -991,6 +1013,18 @@ func (p *PKI) Save(opt ...ConfigOption) error {
|
||||||
ui.PrintSelected("Default profile configuration", p.profileDefaults)
|
ui.PrintSelected("Default profile configuration", p.profileDefaults)
|
||||||
}
|
}
|
||||||
ui.PrintSelected("Certificate Authority configuration", p.config)
|
ui.PrintSelected("Certificate Authority configuration", p.config)
|
||||||
|
if cfg.AuthorityConfig.EnableAdmin && p.options.deploymentType != LinkedDeployment {
|
||||||
|
// TODO(hs): we may want to get this information from the DB, because that's
|
||||||
|
// where the admin and provisioner are stored in this case. Requires some
|
||||||
|
// refactoring.
|
||||||
|
superAdminSubject := "step"
|
||||||
|
if p.options.superAdminSubject != "" {
|
||||||
|
superAdminSubject = p.options.superAdminSubject
|
||||||
|
}
|
||||||
|
ui.PrintSelected("Admin provisioner", fmt.Sprintf("%s (JWK)", p.options.provisioner))
|
||||||
|
ui.PrintSelected("Super admin subject", superAdminSubject)
|
||||||
|
}
|
||||||
|
|
||||||
if p.options.deploymentType != LinkedDeployment {
|
if p.options.deploymentType != LinkedDeployment {
|
||||||
ui.Println()
|
ui.Println()
|
||||||
if p.casOptions.Is(apiv1.SoftCAS) {
|
if p.casOptions.Is(apiv1.SoftCAS) {
|
||||||
|
|
81
pki/testdata/helm/simple.yml
vendored
Normal file
81
pki/testdata/helm/simple.yml
vendored
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
# Helm template
|
||||||
|
inject:
|
||||||
|
enabled: true
|
||||||
|
# Config contains the configuration files ca.json and defaults.json
|
||||||
|
config:
|
||||||
|
files:
|
||||||
|
ca.json:
|
||||||
|
root: /home/step/certs/root_ca.crt
|
||||||
|
federateRoots: []
|
||||||
|
crt: /home/step/certs/intermediate_ca.crt
|
||||||
|
key: /home/step/secrets/intermediate_ca_key
|
||||||
|
address: 127.0.0.1:9000
|
||||||
|
dnsNames:
|
||||||
|
- 127.0.0.1
|
||||||
|
logger:
|
||||||
|
format: json
|
||||||
|
db:
|
||||||
|
type: badgerv2
|
||||||
|
dataSource: /home/step/db
|
||||||
|
authority:
|
||||||
|
enableAdmin: false
|
||||||
|
provisioners:
|
||||||
|
- {"type":"JWK","name":"step-cli","key":{"use":"sig","kty":"EC","kid":"zsUmysmDVoGJ71YoPHyZ-68tNihDaDaO5Mu7xX3M-_I","crv":"P-256","alg":"ES256","x":"Pqnua4CzqKz6ua41J3yeWZ1sRkGt0UlCkbHv8H2DGuY","y":"UhoZ_2ItDen9KQTcjay-ph-SBXH0mwqhHyvrrqIFDOI"},"encryptedKey":"eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJjdHkiOiJqd2sranNvbiIsImVuYyI6IkEyNTZHQ00iLCJwMmMiOjEwMDAwMCwicDJzIjoiZjVvdGVRS2hvOXl4MmQtSGlMZi05QSJ9.eYA6tt3fNuUpoxKWDT7P0Lbn2juxhEbTxEnwEMbjlYLLQ3sxL-dYTA.ven-FhmdjlC9itH0.a2jRTarN9vPd6F_mWnNBlOn6KbfMjCApmci2t65XbAsLzYFzhI_79Ykm5ueMYTupWLTjBJctl-g51ZHmsSB55pStbpoyyLNAsUX2E1fTmHe-Ni8bRrspwLv15FoN1Xo1g0mpR-ufWIFxOsW-QIfnMmMIIkygVuHFXmg2tFpzTNNG5aS29K3dN2nyk0WJrdIq79hZSTqVkkBU25Yu3A46sgjcM86XcIJJ2XUEih_KWEa6T1YrkixGu96pebjVqbO0R6dbDckfPF7FqNnwPHVtb1ACFpEYoOJVIbUCMaARBpWsxYhjJZlEM__XA46l8snFQDkNY3CdN0p1_gF3ckA.JLmq9nmu1h9oUi1S8ZxYjA","options":{"x509":{},"ssh":{}}}
|
||||||
|
tls:
|
||||||
|
cipherSuites:
|
||||||
|
- TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
|
||||||
|
- TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
|
||||||
|
minVersion: 1.2
|
||||||
|
maxVersion: 1.3
|
||||||
|
renegotiation: false
|
||||||
|
|
||||||
|
defaults.json:
|
||||||
|
ca-url: https://127.0.0.1
|
||||||
|
ca-config: /home/step/config/ca.json
|
||||||
|
fingerprint: e543cad8e9f6417076bb5aed3471c588152118aac1e0ca7984a43ee7f76da5e3
|
||||||
|
root: /home/step/certs/root_ca.crt
|
||||||
|
|
||||||
|
# Certificates contains the root and intermediate certificate and
|
||||||
|
# optionally the SSH host and user public keys
|
||||||
|
certificates:
|
||||||
|
# intermediate_ca contains the text of the intermediate CA Certificate
|
||||||
|
intermediate_ca: |
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
dGhlc2UgYXJlIGp1c3Qgc29tZSBmYWtlIGludGVybWVkaWF0ZSBDQSBjZXJ0IGJ5
|
||||||
|
dGVz
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
|
|
||||||
|
# root_ca contains the text of the root CA Certificate
|
||||||
|
root_ca: |
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
dGhlc2UgYXJlIGp1c3Qgc29tZSBmYWtlIHJvb3QgQ0EgY2VydCBieXRlcw==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
|
|
||||||
|
# Secrets contains the root and intermediate keys and optionally the SSH
|
||||||
|
# private keys
|
||||||
|
secrets:
|
||||||
|
# ca_password contains the password used to encrypt x509.intermediate_ca_key, ssh.host_ca_key and ssh.user_ca_key
|
||||||
|
# This value must be base64 encoded.
|
||||||
|
ca_password:
|
||||||
|
provisioner_password:
|
||||||
|
|
||||||
|
x509:
|
||||||
|
# intermediate_ca_key contains the contents of your encrypted intermediate CA key
|
||||||
|
intermediate_ca_key: |
|
||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
dGhlc2UgYXJlIGp1c3Qgc29tZSBmYWtlIGludGVybWVkaWF0ZSBDQSBrZXkgYnl0
|
||||||
|
ZXM=
|
||||||
|
-----END EC PRIVATE KEY-----
|
||||||
|
|
||||||
|
|
||||||
|
# root_ca_key contains the contents of your encrypted root CA key
|
||||||
|
# Note that this value can be omitted without impacting the functionality of step-certificates
|
||||||
|
# If supplied, this should be encrypted using a unique password that is not used for encrypting
|
||||||
|
# the intermediate_ca_key, ssh.host_ca_key or ssh.user_ca_key.
|
||||||
|
root_ca_key: |
|
||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
dGhlc2UgYXJlIGp1c3Qgc29tZSBmYWtlIHJvb3QgQ0Ega2V5IGJ5dGVz
|
||||||
|
-----END EC PRIVATE KEY-----
|
||||||
|
|
82
pki/testdata/helm/with-acme.yml
vendored
Normal file
82
pki/testdata/helm/with-acme.yml
vendored
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
# Helm template
|
||||||
|
inject:
|
||||||
|
enabled: true
|
||||||
|
# Config contains the configuration files ca.json and defaults.json
|
||||||
|
config:
|
||||||
|
files:
|
||||||
|
ca.json:
|
||||||
|
root: /home/step/certs/root_ca.crt
|
||||||
|
federateRoots: []
|
||||||
|
crt: /home/step/certs/intermediate_ca.crt
|
||||||
|
key: /home/step/secrets/intermediate_ca_key
|
||||||
|
address: 127.0.0.1:9000
|
||||||
|
dnsNames:
|
||||||
|
- 127.0.0.1
|
||||||
|
logger:
|
||||||
|
format: json
|
||||||
|
db:
|
||||||
|
type: badgerv2
|
||||||
|
dataSource: /home/step/db
|
||||||
|
authority:
|
||||||
|
enableAdmin: false
|
||||||
|
provisioners:
|
||||||
|
- {"type":"JWK","name":"step-cli","key":{"use":"sig","kty":"EC","kid":"zsUmysmDVoGJ71YoPHyZ-68tNihDaDaO5Mu7xX3M-_I","crv":"P-256","alg":"ES256","x":"Pqnua4CzqKz6ua41J3yeWZ1sRkGt0UlCkbHv8H2DGuY","y":"UhoZ_2ItDen9KQTcjay-ph-SBXH0mwqhHyvrrqIFDOI"},"encryptedKey":"eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJjdHkiOiJqd2sranNvbiIsImVuYyI6IkEyNTZHQ00iLCJwMmMiOjEwMDAwMCwicDJzIjoiZjVvdGVRS2hvOXl4MmQtSGlMZi05QSJ9.eYA6tt3fNuUpoxKWDT7P0Lbn2juxhEbTxEnwEMbjlYLLQ3sxL-dYTA.ven-FhmdjlC9itH0.a2jRTarN9vPd6F_mWnNBlOn6KbfMjCApmci2t65XbAsLzYFzhI_79Ykm5ueMYTupWLTjBJctl-g51ZHmsSB55pStbpoyyLNAsUX2E1fTmHe-Ni8bRrspwLv15FoN1Xo1g0mpR-ufWIFxOsW-QIfnMmMIIkygVuHFXmg2tFpzTNNG5aS29K3dN2nyk0WJrdIq79hZSTqVkkBU25Yu3A46sgjcM86XcIJJ2XUEih_KWEa6T1YrkixGu96pebjVqbO0R6dbDckfPF7FqNnwPHVtb1ACFpEYoOJVIbUCMaARBpWsxYhjJZlEM__XA46l8snFQDkNY3CdN0p1_gF3ckA.JLmq9nmu1h9oUi1S8ZxYjA","options":{"x509":{},"ssh":{}}}
|
||||||
|
- {"type":"ACME","name":"acme"}
|
||||||
|
tls:
|
||||||
|
cipherSuites:
|
||||||
|
- TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
|
||||||
|
- TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
|
||||||
|
minVersion: 1.2
|
||||||
|
maxVersion: 1.3
|
||||||
|
renegotiation: false
|
||||||
|
|
||||||
|
defaults.json:
|
||||||
|
ca-url: https://127.0.0.1
|
||||||
|
ca-config: /home/step/config/ca.json
|
||||||
|
fingerprint: e543cad8e9f6417076bb5aed3471c588152118aac1e0ca7984a43ee7f76da5e3
|
||||||
|
root: /home/step/certs/root_ca.crt
|
||||||
|
|
||||||
|
# Certificates contains the root and intermediate certificate and
|
||||||
|
# optionally the SSH host and user public keys
|
||||||
|
certificates:
|
||||||
|
# intermediate_ca contains the text of the intermediate CA Certificate
|
||||||
|
intermediate_ca: |
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
dGhlc2UgYXJlIGp1c3Qgc29tZSBmYWtlIGludGVybWVkaWF0ZSBDQSBjZXJ0IGJ5
|
||||||
|
dGVz
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
|
|
||||||
|
# root_ca contains the text of the root CA Certificate
|
||||||
|
root_ca: |
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
dGhlc2UgYXJlIGp1c3Qgc29tZSBmYWtlIHJvb3QgQ0EgY2VydCBieXRlcw==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
|
|
||||||
|
# Secrets contains the root and intermediate keys and optionally the SSH
|
||||||
|
# private keys
|
||||||
|
secrets:
|
||||||
|
# ca_password contains the password used to encrypt x509.intermediate_ca_key, ssh.host_ca_key and ssh.user_ca_key
|
||||||
|
# This value must be base64 encoded.
|
||||||
|
ca_password:
|
||||||
|
provisioner_password:
|
||||||
|
|
||||||
|
x509:
|
||||||
|
# intermediate_ca_key contains the contents of your encrypted intermediate CA key
|
||||||
|
intermediate_ca_key: |
|
||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
dGhlc2UgYXJlIGp1c3Qgc29tZSBmYWtlIGludGVybWVkaWF0ZSBDQSBrZXkgYnl0
|
||||||
|
ZXM=
|
||||||
|
-----END EC PRIVATE KEY-----
|
||||||
|
|
||||||
|
|
||||||
|
# root_ca_key contains the contents of your encrypted root CA key
|
||||||
|
# Note that this value can be omitted without impacting the functionality of step-certificates
|
||||||
|
# If supplied, this should be encrypted using a unique password that is not used for encrypting
|
||||||
|
# the intermediate_ca_key, ssh.host_ca_key or ssh.user_ca_key.
|
||||||
|
root_ca_key: |
|
||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
dGhlc2UgYXJlIGp1c3Qgc29tZSBmYWtlIHJvb3QgQ0Ega2V5IGJ5dGVz
|
||||||
|
-----END EC PRIVATE KEY-----
|
||||||
|
|
81
pki/testdata/helm/with-admin.yml
vendored
Normal file
81
pki/testdata/helm/with-admin.yml
vendored
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
# Helm template
|
||||||
|
inject:
|
||||||
|
enabled: true
|
||||||
|
# Config contains the configuration files ca.json and defaults.json
|
||||||
|
config:
|
||||||
|
files:
|
||||||
|
ca.json:
|
||||||
|
root: /home/step/certs/root_ca.crt
|
||||||
|
federateRoots: []
|
||||||
|
crt: /home/step/certs/intermediate_ca.crt
|
||||||
|
key: /home/step/secrets/intermediate_ca_key
|
||||||
|
address: 127.0.0.1:9000
|
||||||
|
dnsNames:
|
||||||
|
- 127.0.0.1
|
||||||
|
logger:
|
||||||
|
format: json
|
||||||
|
db:
|
||||||
|
type: badgerv2
|
||||||
|
dataSource: /home/step/db
|
||||||
|
authority:
|
||||||
|
enableAdmin: true
|
||||||
|
provisioners:
|
||||||
|
- {"type":"JWK","name":"step-cli","key":{"use":"sig","kty":"EC","kid":"zsUmysmDVoGJ71YoPHyZ-68tNihDaDaO5Mu7xX3M-_I","crv":"P-256","alg":"ES256","x":"Pqnua4CzqKz6ua41J3yeWZ1sRkGt0UlCkbHv8H2DGuY","y":"UhoZ_2ItDen9KQTcjay-ph-SBXH0mwqhHyvrrqIFDOI"},"encryptedKey":"eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJjdHkiOiJqd2sranNvbiIsImVuYyI6IkEyNTZHQ00iLCJwMmMiOjEwMDAwMCwicDJzIjoiZjVvdGVRS2hvOXl4MmQtSGlMZi05QSJ9.eYA6tt3fNuUpoxKWDT7P0Lbn2juxhEbTxEnwEMbjlYLLQ3sxL-dYTA.ven-FhmdjlC9itH0.a2jRTarN9vPd6F_mWnNBlOn6KbfMjCApmci2t65XbAsLzYFzhI_79Ykm5ueMYTupWLTjBJctl-g51ZHmsSB55pStbpoyyLNAsUX2E1fTmHe-Ni8bRrspwLv15FoN1Xo1g0mpR-ufWIFxOsW-QIfnMmMIIkygVuHFXmg2tFpzTNNG5aS29K3dN2nyk0WJrdIq79hZSTqVkkBU25Yu3A46sgjcM86XcIJJ2XUEih_KWEa6T1YrkixGu96pebjVqbO0R6dbDckfPF7FqNnwPHVtb1ACFpEYoOJVIbUCMaARBpWsxYhjJZlEM__XA46l8snFQDkNY3CdN0p1_gF3ckA.JLmq9nmu1h9oUi1S8ZxYjA","options":{"x509":{},"ssh":{}}}
|
||||||
|
tls:
|
||||||
|
cipherSuites:
|
||||||
|
- TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
|
||||||
|
- TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
|
||||||
|
minVersion: 1.2
|
||||||
|
maxVersion: 1.3
|
||||||
|
renegotiation: false
|
||||||
|
|
||||||
|
defaults.json:
|
||||||
|
ca-url: https://127.0.0.1
|
||||||
|
ca-config: /home/step/config/ca.json
|
||||||
|
fingerprint: e543cad8e9f6417076bb5aed3471c588152118aac1e0ca7984a43ee7f76da5e3
|
||||||
|
root: /home/step/certs/root_ca.crt
|
||||||
|
|
||||||
|
# Certificates contains the root and intermediate certificate and
|
||||||
|
# optionally the SSH host and user public keys
|
||||||
|
certificates:
|
||||||
|
# intermediate_ca contains the text of the intermediate CA Certificate
|
||||||
|
intermediate_ca: |
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
dGhlc2UgYXJlIGp1c3Qgc29tZSBmYWtlIGludGVybWVkaWF0ZSBDQSBjZXJ0IGJ5
|
||||||
|
dGVz
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
|
|
||||||
|
# root_ca contains the text of the root CA Certificate
|
||||||
|
root_ca: |
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
dGhlc2UgYXJlIGp1c3Qgc29tZSBmYWtlIHJvb3QgQ0EgY2VydCBieXRlcw==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
|
|
||||||
|
# Secrets contains the root and intermediate keys and optionally the SSH
|
||||||
|
# private keys
|
||||||
|
secrets:
|
||||||
|
# ca_password contains the password used to encrypt x509.intermediate_ca_key, ssh.host_ca_key and ssh.user_ca_key
|
||||||
|
# This value must be base64 encoded.
|
||||||
|
ca_password:
|
||||||
|
provisioner_password:
|
||||||
|
|
||||||
|
x509:
|
||||||
|
# intermediate_ca_key contains the contents of your encrypted intermediate CA key
|
||||||
|
intermediate_ca_key: |
|
||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
dGhlc2UgYXJlIGp1c3Qgc29tZSBmYWtlIGludGVybWVkaWF0ZSBDQSBrZXkgYnl0
|
||||||
|
ZXM=
|
||||||
|
-----END EC PRIVATE KEY-----
|
||||||
|
|
||||||
|
|
||||||
|
# root_ca_key contains the contents of your encrypted root CA key
|
||||||
|
# Note that this value can be omitted without impacting the functionality of step-certificates
|
||||||
|
# If supplied, this should be encrypted using a unique password that is not used for encrypting
|
||||||
|
# the intermediate_ca_key, ssh.host_ca_key or ssh.user_ca_key.
|
||||||
|
root_ca_key: |
|
||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
dGhlc2UgYXJlIGp1c3Qgc29tZSBmYWtlIHJvb3QgQ0Ega2V5IGJ5dGVz
|
||||||
|
-----END EC PRIVATE KEY-----
|
||||||
|
|
81
pki/testdata/helm/with-provisioner.yml
vendored
Normal file
81
pki/testdata/helm/with-provisioner.yml
vendored
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
# Helm template
|
||||||
|
inject:
|
||||||
|
enabled: true
|
||||||
|
# Config contains the configuration files ca.json and defaults.json
|
||||||
|
config:
|
||||||
|
files:
|
||||||
|
ca.json:
|
||||||
|
root: /home/step/certs/root_ca.crt
|
||||||
|
federateRoots: []
|
||||||
|
crt: /home/step/certs/intermediate_ca.crt
|
||||||
|
key: /home/step/secrets/intermediate_ca_key
|
||||||
|
address: 127.0.0.1:9000
|
||||||
|
dnsNames:
|
||||||
|
- 127.0.0.1
|
||||||
|
logger:
|
||||||
|
format: json
|
||||||
|
db:
|
||||||
|
type: badgerv2
|
||||||
|
dataSource: /home/step/db
|
||||||
|
authority:
|
||||||
|
enableAdmin: false
|
||||||
|
provisioners:
|
||||||
|
- {"type":"JWK","name":"a-provisioner","key":{"use":"sig","kty":"EC","kid":"zsUmysmDVoGJ71YoPHyZ-68tNihDaDaO5Mu7xX3M-_I","crv":"P-256","alg":"ES256","x":"Pqnua4CzqKz6ua41J3yeWZ1sRkGt0UlCkbHv8H2DGuY","y":"UhoZ_2ItDen9KQTcjay-ph-SBXH0mwqhHyvrrqIFDOI"},"encryptedKey":"eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJjdHkiOiJqd2sranNvbiIsImVuYyI6IkEyNTZHQ00iLCJwMmMiOjEwMDAwMCwicDJzIjoiZjVvdGVRS2hvOXl4MmQtSGlMZi05QSJ9.eYA6tt3fNuUpoxKWDT7P0Lbn2juxhEbTxEnwEMbjlYLLQ3sxL-dYTA.ven-FhmdjlC9itH0.a2jRTarN9vPd6F_mWnNBlOn6KbfMjCApmci2t65XbAsLzYFzhI_79Ykm5ueMYTupWLTjBJctl-g51ZHmsSB55pStbpoyyLNAsUX2E1fTmHe-Ni8bRrspwLv15FoN1Xo1g0mpR-ufWIFxOsW-QIfnMmMIIkygVuHFXmg2tFpzTNNG5aS29K3dN2nyk0WJrdIq79hZSTqVkkBU25Yu3A46sgjcM86XcIJJ2XUEih_KWEa6T1YrkixGu96pebjVqbO0R6dbDckfPF7FqNnwPHVtb1ACFpEYoOJVIbUCMaARBpWsxYhjJZlEM__XA46l8snFQDkNY3CdN0p1_gF3ckA.JLmq9nmu1h9oUi1S8ZxYjA","options":{"x509":{},"ssh":{}}}
|
||||||
|
tls:
|
||||||
|
cipherSuites:
|
||||||
|
- TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
|
||||||
|
- TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
|
||||||
|
minVersion: 1.2
|
||||||
|
maxVersion: 1.3
|
||||||
|
renegotiation: false
|
||||||
|
|
||||||
|
defaults.json:
|
||||||
|
ca-url: https://127.0.0.1
|
||||||
|
ca-config: /home/step/config/ca.json
|
||||||
|
fingerprint: e543cad8e9f6417076bb5aed3471c588152118aac1e0ca7984a43ee7f76da5e3
|
||||||
|
root: /home/step/certs/root_ca.crt
|
||||||
|
|
||||||
|
# Certificates contains the root and intermediate certificate and
|
||||||
|
# optionally the SSH host and user public keys
|
||||||
|
certificates:
|
||||||
|
# intermediate_ca contains the text of the intermediate CA Certificate
|
||||||
|
intermediate_ca: |
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
dGhlc2UgYXJlIGp1c3Qgc29tZSBmYWtlIGludGVybWVkaWF0ZSBDQSBjZXJ0IGJ5
|
||||||
|
dGVz
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
|
|
||||||
|
# root_ca contains the text of the root CA Certificate
|
||||||
|
root_ca: |
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
dGhlc2UgYXJlIGp1c3Qgc29tZSBmYWtlIHJvb3QgQ0EgY2VydCBieXRlcw==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
|
|
||||||
|
# Secrets contains the root and intermediate keys and optionally the SSH
|
||||||
|
# private keys
|
||||||
|
secrets:
|
||||||
|
# ca_password contains the password used to encrypt x509.intermediate_ca_key, ssh.host_ca_key and ssh.user_ca_key
|
||||||
|
# This value must be base64 encoded.
|
||||||
|
ca_password:
|
||||||
|
provisioner_password:
|
||||||
|
|
||||||
|
x509:
|
||||||
|
# intermediate_ca_key contains the contents of your encrypted intermediate CA key
|
||||||
|
intermediate_ca_key: |
|
||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
dGhlc2UgYXJlIGp1c3Qgc29tZSBmYWtlIGludGVybWVkaWF0ZSBDQSBrZXkgYnl0
|
||||||
|
ZXM=
|
||||||
|
-----END EC PRIVATE KEY-----
|
||||||
|
|
||||||
|
|
||||||
|
# root_ca_key contains the contents of your encrypted root CA key
|
||||||
|
# Note that this value can be omitted without impacting the functionality of step-certificates
|
||||||
|
# If supplied, this should be encrypted using a unique password that is not used for encrypting
|
||||||
|
# the intermediate_ca_key, ssh.host_ca_key or ssh.user_ca_key.
|
||||||
|
root_ca_key: |
|
||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
dGhlc2UgYXJlIGp1c3Qgc29tZSBmYWtlIHJvb3QgQ0Ega2V5IGJ5dGVz
|
||||||
|
-----END EC PRIVATE KEY-----
|
||||||
|
|
105
pki/testdata/helm/with-ssh-and-acme.yml
vendored
Normal file
105
pki/testdata/helm/with-ssh-and-acme.yml
vendored
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
# Helm template
|
||||||
|
inject:
|
||||||
|
enabled: true
|
||||||
|
# Config contains the configuration files ca.json and defaults.json
|
||||||
|
config:
|
||||||
|
files:
|
||||||
|
ca.json:
|
||||||
|
root: /home/step/certs/root_ca.crt
|
||||||
|
federateRoots: []
|
||||||
|
crt: /home/step/certs/intermediate_ca.crt
|
||||||
|
key: /home/step/secrets/intermediate_ca_key
|
||||||
|
ssh:
|
||||||
|
hostKey: /home/step/secrets/ssh_host_ca_key
|
||||||
|
userKey: /home/step/secrets/ssh_user_ca_key
|
||||||
|
address: 127.0.0.1:9000
|
||||||
|
dnsNames:
|
||||||
|
- 127.0.0.1
|
||||||
|
logger:
|
||||||
|
format: json
|
||||||
|
db:
|
||||||
|
type: badgerv2
|
||||||
|
dataSource: /home/step/db
|
||||||
|
authority:
|
||||||
|
enableAdmin: false
|
||||||
|
provisioners:
|
||||||
|
- {"type":"JWK","name":"step-cli","key":{"use":"sig","kty":"EC","kid":"zsUmysmDVoGJ71YoPHyZ-68tNihDaDaO5Mu7xX3M-_I","crv":"P-256","alg":"ES256","x":"Pqnua4CzqKz6ua41J3yeWZ1sRkGt0UlCkbHv8H2DGuY","y":"UhoZ_2ItDen9KQTcjay-ph-SBXH0mwqhHyvrrqIFDOI"},"encryptedKey":"eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJjdHkiOiJqd2sranNvbiIsImVuYyI6IkEyNTZHQ00iLCJwMmMiOjEwMDAwMCwicDJzIjoiZjVvdGVRS2hvOXl4MmQtSGlMZi05QSJ9.eYA6tt3fNuUpoxKWDT7P0Lbn2juxhEbTxEnwEMbjlYLLQ3sxL-dYTA.ven-FhmdjlC9itH0.a2jRTarN9vPd6F_mWnNBlOn6KbfMjCApmci2t65XbAsLzYFzhI_79Ykm5ueMYTupWLTjBJctl-g51ZHmsSB55pStbpoyyLNAsUX2E1fTmHe-Ni8bRrspwLv15FoN1Xo1g0mpR-ufWIFxOsW-QIfnMmMIIkygVuHFXmg2tFpzTNNG5aS29K3dN2nyk0WJrdIq79hZSTqVkkBU25Yu3A46sgjcM86XcIJJ2XUEih_KWEa6T1YrkixGu96pebjVqbO0R6dbDckfPF7FqNnwPHVtb1ACFpEYoOJVIbUCMaARBpWsxYhjJZlEM__XA46l8snFQDkNY3CdN0p1_gF3ckA.JLmq9nmu1h9oUi1S8ZxYjA","claims":{"enableSSHCA":true,"disableRenewal":false,"allowRenewalAfterExpiry":false},"options":{"x509":{},"ssh":{}}}
|
||||||
|
- {"type":"ACME","name":"acme"}
|
||||||
|
- {"type":"SSHPOP","name":"sshpop","claims":{"enableSSHCA":true}}
|
||||||
|
tls:
|
||||||
|
cipherSuites:
|
||||||
|
- TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
|
||||||
|
- TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
|
||||||
|
minVersion: 1.2
|
||||||
|
maxVersion: 1.3
|
||||||
|
renegotiation: false
|
||||||
|
|
||||||
|
defaults.json:
|
||||||
|
ca-url: https://127.0.0.1
|
||||||
|
ca-config: /home/step/config/ca.json
|
||||||
|
fingerprint: e543cad8e9f6417076bb5aed3471c588152118aac1e0ca7984a43ee7f76da5e3
|
||||||
|
root: /home/step/certs/root_ca.crt
|
||||||
|
|
||||||
|
# Certificates contains the root and intermediate certificate and
|
||||||
|
# optionally the SSH host and user public keys
|
||||||
|
certificates:
|
||||||
|
# intermediate_ca contains the text of the intermediate CA Certificate
|
||||||
|
intermediate_ca: |
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
dGhlc2UgYXJlIGp1c3Qgc29tZSBmYWtlIGludGVybWVkaWF0ZSBDQSBjZXJ0IGJ5
|
||||||
|
dGVz
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
|
|
||||||
|
# root_ca contains the text of the root CA Certificate
|
||||||
|
root_ca: |
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
dGhlc2UgYXJlIGp1c3Qgc29tZSBmYWtlIHJvb3QgQ0EgY2VydCBieXRlcw==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
|
# ssh_host_ca contains the text of the public ssh key for the SSH root CA
|
||||||
|
ssh_host_ca: ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJ0IdS5sZm6KITBMZLEJD6b5ROVraYHcAOr3feFel8r1Wp4DRPR1oU0W00J/zjNBRBbANlJoYN4x/8WNNVZ49Ms=
|
||||||
|
|
||||||
|
# ssh_user_ca contains the text of the public ssh key for the SSH root CA
|
||||||
|
ssh_user_ca: ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEWA1qUxaGwVNErsvEOGe2d6TvLMF+aiVpuOiIEvpMJ3JeJmecLQctjWqeIbpSvy6/gRa7c82Ge5rLlapYmOChs=
|
||||||
|
|
||||||
|
# Secrets contains the root and intermediate keys and optionally the SSH
|
||||||
|
# private keys
|
||||||
|
secrets:
|
||||||
|
# ca_password contains the password used to encrypt x509.intermediate_ca_key, ssh.host_ca_key and ssh.user_ca_key
|
||||||
|
# This value must be base64 encoded.
|
||||||
|
ca_password:
|
||||||
|
provisioner_password:
|
||||||
|
|
||||||
|
x509:
|
||||||
|
# intermediate_ca_key contains the contents of your encrypted intermediate CA key
|
||||||
|
intermediate_ca_key: |
|
||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
dGhlc2UgYXJlIGp1c3Qgc29tZSBmYWtlIGludGVybWVkaWF0ZSBDQSBrZXkgYnl0
|
||||||
|
ZXM=
|
||||||
|
-----END EC PRIVATE KEY-----
|
||||||
|
|
||||||
|
|
||||||
|
# root_ca_key contains the contents of your encrypted root CA key
|
||||||
|
# Note that this value can be omitted without impacting the functionality of step-certificates
|
||||||
|
# If supplied, this should be encrypted using a unique password that is not used for encrypting
|
||||||
|
# the intermediate_ca_key, ssh.host_ca_key or ssh.user_ca_key.
|
||||||
|
root_ca_key: |
|
||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
dGhlc2UgYXJlIGp1c3Qgc29tZSBmYWtlIHJvb3QgQ0Ega2V5IGJ5dGVz
|
||||||
|
-----END EC PRIVATE KEY-----
|
||||||
|
|
||||||
|
ssh:
|
||||||
|
# ssh_host_ca_key contains the contents of your encrypted SSH Host CA key
|
||||||
|
host_ca_key: |
|
||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
ZmFrZSBzc2ggaG9zdCBrZXkgYnl0ZXM=
|
||||||
|
-----END EC PRIVATE KEY-----
|
||||||
|
|
||||||
|
|
||||||
|
# ssh_user_ca_key contains the contents of your encrypted SSH User CA key
|
||||||
|
user_ca_key: |
|
||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
ZmFrZSBzc2ggdXNlciBrZXkgYnl0ZXM=
|
||||||
|
-----END EC PRIVATE KEY-----
|
||||||
|
|
104
pki/testdata/helm/with-ssh.yml
vendored
Normal file
104
pki/testdata/helm/with-ssh.yml
vendored
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
# Helm template
|
||||||
|
inject:
|
||||||
|
enabled: true
|
||||||
|
# Config contains the configuration files ca.json and defaults.json
|
||||||
|
config:
|
||||||
|
files:
|
||||||
|
ca.json:
|
||||||
|
root: /home/step/certs/root_ca.crt
|
||||||
|
federateRoots: []
|
||||||
|
crt: /home/step/certs/intermediate_ca.crt
|
||||||
|
key: /home/step/secrets/intermediate_ca_key
|
||||||
|
ssh:
|
||||||
|
hostKey: /home/step/secrets/ssh_host_ca_key
|
||||||
|
userKey: /home/step/secrets/ssh_user_ca_key
|
||||||
|
address: 127.0.0.1:9000
|
||||||
|
dnsNames:
|
||||||
|
- 127.0.0.1
|
||||||
|
logger:
|
||||||
|
format: json
|
||||||
|
db:
|
||||||
|
type: badgerv2
|
||||||
|
dataSource: /home/step/db
|
||||||
|
authority:
|
||||||
|
enableAdmin: false
|
||||||
|
provisioners:
|
||||||
|
- {"type":"JWK","name":"step-cli","key":{"use":"sig","kty":"EC","kid":"zsUmysmDVoGJ71YoPHyZ-68tNihDaDaO5Mu7xX3M-_I","crv":"P-256","alg":"ES256","x":"Pqnua4CzqKz6ua41J3yeWZ1sRkGt0UlCkbHv8H2DGuY","y":"UhoZ_2ItDen9KQTcjay-ph-SBXH0mwqhHyvrrqIFDOI"},"encryptedKey":"eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJjdHkiOiJqd2sranNvbiIsImVuYyI6IkEyNTZHQ00iLCJwMmMiOjEwMDAwMCwicDJzIjoiZjVvdGVRS2hvOXl4MmQtSGlMZi05QSJ9.eYA6tt3fNuUpoxKWDT7P0Lbn2juxhEbTxEnwEMbjlYLLQ3sxL-dYTA.ven-FhmdjlC9itH0.a2jRTarN9vPd6F_mWnNBlOn6KbfMjCApmci2t65XbAsLzYFzhI_79Ykm5ueMYTupWLTjBJctl-g51ZHmsSB55pStbpoyyLNAsUX2E1fTmHe-Ni8bRrspwLv15FoN1Xo1g0mpR-ufWIFxOsW-QIfnMmMIIkygVuHFXmg2tFpzTNNG5aS29K3dN2nyk0WJrdIq79hZSTqVkkBU25Yu3A46sgjcM86XcIJJ2XUEih_KWEa6T1YrkixGu96pebjVqbO0R6dbDckfPF7FqNnwPHVtb1ACFpEYoOJVIbUCMaARBpWsxYhjJZlEM__XA46l8snFQDkNY3CdN0p1_gF3ckA.JLmq9nmu1h9oUi1S8ZxYjA","claims":{"enableSSHCA":true,"disableRenewal":false,"allowRenewalAfterExpiry":false},"options":{"x509":{},"ssh":{}}}
|
||||||
|
- {"type":"SSHPOP","name":"sshpop","claims":{"enableSSHCA":true}}
|
||||||
|
tls:
|
||||||
|
cipherSuites:
|
||||||
|
- TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
|
||||||
|
- TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
|
||||||
|
minVersion: 1.2
|
||||||
|
maxVersion: 1.3
|
||||||
|
renegotiation: false
|
||||||
|
|
||||||
|
defaults.json:
|
||||||
|
ca-url: https://127.0.0.1
|
||||||
|
ca-config: /home/step/config/ca.json
|
||||||
|
fingerprint: e543cad8e9f6417076bb5aed3471c588152118aac1e0ca7984a43ee7f76da5e3
|
||||||
|
root: /home/step/certs/root_ca.crt
|
||||||
|
|
||||||
|
# Certificates contains the root and intermediate certificate and
|
||||||
|
# optionally the SSH host and user public keys
|
||||||
|
certificates:
|
||||||
|
# intermediate_ca contains the text of the intermediate CA Certificate
|
||||||
|
intermediate_ca: |
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
dGhlc2UgYXJlIGp1c3Qgc29tZSBmYWtlIGludGVybWVkaWF0ZSBDQSBjZXJ0IGJ5
|
||||||
|
dGVz
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
|
|
||||||
|
# root_ca contains the text of the root CA Certificate
|
||||||
|
root_ca: |
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
dGhlc2UgYXJlIGp1c3Qgc29tZSBmYWtlIHJvb3QgQ0EgY2VydCBieXRlcw==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
|
# ssh_host_ca contains the text of the public ssh key for the SSH root CA
|
||||||
|
ssh_host_ca: ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJ0IdS5sZm6KITBMZLEJD6b5ROVraYHcAOr3feFel8r1Wp4DRPR1oU0W00J/zjNBRBbANlJoYN4x/8WNNVZ49Ms=
|
||||||
|
|
||||||
|
# ssh_user_ca contains the text of the public ssh key for the SSH root CA
|
||||||
|
ssh_user_ca: ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEWA1qUxaGwVNErsvEOGe2d6TvLMF+aiVpuOiIEvpMJ3JeJmecLQctjWqeIbpSvy6/gRa7c82Ge5rLlapYmOChs=
|
||||||
|
|
||||||
|
# Secrets contains the root and intermediate keys and optionally the SSH
|
||||||
|
# private keys
|
||||||
|
secrets:
|
||||||
|
# ca_password contains the password used to encrypt x509.intermediate_ca_key, ssh.host_ca_key and ssh.user_ca_key
|
||||||
|
# This value must be base64 encoded.
|
||||||
|
ca_password:
|
||||||
|
provisioner_password:
|
||||||
|
|
||||||
|
x509:
|
||||||
|
# intermediate_ca_key contains the contents of your encrypted intermediate CA key
|
||||||
|
intermediate_ca_key: |
|
||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
dGhlc2UgYXJlIGp1c3Qgc29tZSBmYWtlIGludGVybWVkaWF0ZSBDQSBrZXkgYnl0
|
||||||
|
ZXM=
|
||||||
|
-----END EC PRIVATE KEY-----
|
||||||
|
|
||||||
|
|
||||||
|
# root_ca_key contains the contents of your encrypted root CA key
|
||||||
|
# Note that this value can be omitted without impacting the functionality of step-certificates
|
||||||
|
# If supplied, this should be encrypted using a unique password that is not used for encrypting
|
||||||
|
# the intermediate_ca_key, ssh.host_ca_key or ssh.user_ca_key.
|
||||||
|
root_ca_key: |
|
||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
dGhlc2UgYXJlIGp1c3Qgc29tZSBmYWtlIHJvb3QgQ0Ega2V5IGJ5dGVz
|
||||||
|
-----END EC PRIVATE KEY-----
|
||||||
|
|
||||||
|
ssh:
|
||||||
|
# ssh_host_ca_key contains the contents of your encrypted SSH Host CA key
|
||||||
|
host_ca_key: |
|
||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
ZmFrZSBzc2ggaG9zdCBrZXkgYnl0ZXM=
|
||||||
|
-----END EC PRIVATE KEY-----
|
||||||
|
|
||||||
|
|
||||||
|
# ssh_user_ca_key contains the contents of your encrypted SSH User CA key
|
||||||
|
user_ca_key: |
|
||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
ZmFrZSBzc2ggdXNlciBrZXkgYnl0ZXM=
|
||||||
|
-----END EC PRIVATE KEY-----
|
||||||
|
|
Loading…
Add table
Reference in a new issue