forked from TrueCloudLab/certificates
Merge pull request #402 from smallstep/ra-init
Add support for CloudCAS on step ca init
This commit is contained in:
commit
426f846974
8 changed files with 102 additions and 43 deletions
|
@ -156,8 +156,8 @@ func (a *Authority) init() error {
|
||||||
// Initialize the X.509 CA Service if it has not been set in the options.
|
// Initialize the X.509 CA Service if it has not been set in the options.
|
||||||
if a.x509CAService == nil {
|
if a.x509CAService == nil {
|
||||||
var options casapi.Options
|
var options casapi.Options
|
||||||
if a.config.CAS != nil {
|
if a.config.AuthorityConfig.Options != nil {
|
||||||
options = *a.config.CAS
|
options = *a.config.AuthorityConfig.Options
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read intermediate and create X509 signer for default CAS.
|
// Read intermediate and create X509 signer for default CAS.
|
||||||
|
@ -183,7 +183,7 @@ func (a *Authority) init() error {
|
||||||
// Get root certificate from CAS.
|
// Get root certificate from CAS.
|
||||||
if srv, ok := a.x509CAService.(casapi.CertificateAuthorityGetter); ok {
|
if srv, ok := a.x509CAService.(casapi.CertificateAuthorityGetter); ok {
|
||||||
resp, err := srv.GetCertificateAuthority(&casapi.GetCertificateAuthorityRequest{
|
resp, err := srv.GetCertificateAuthority(&casapi.GetCertificateAuthorityRequest{
|
||||||
Name: options.Certificateauthority,
|
Name: options.CertificateAuthority,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -55,7 +55,6 @@ type Config struct {
|
||||||
Address string `json:"address"`
|
Address string `json:"address"`
|
||||||
DNSNames []string `json:"dnsNames"`
|
DNSNames []string `json:"dnsNames"`
|
||||||
KMS *kms.Options `json:"kms,omitempty"`
|
KMS *kms.Options `json:"kms,omitempty"`
|
||||||
CAS *cas.Options `json:"cas,omitempty"`
|
|
||||||
SSH *SSHConfig `json:"ssh,omitempty"`
|
SSH *SSHConfig `json:"ssh,omitempty"`
|
||||||
Logger json.RawMessage `json:"logger,omitempty"`
|
Logger json.RawMessage `json:"logger,omitempty"`
|
||||||
DB *db.Config `json:"db,omitempty"`
|
DB *db.Config `json:"db,omitempty"`
|
||||||
|
@ -78,8 +77,11 @@ type ASN1DN struct {
|
||||||
CommonName string `json:"commonName,omitempty" step:"commonName"`
|
CommonName string `json:"commonName,omitempty" step:"commonName"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AuthConfig represents the configuration options for the authority.
|
// AuthConfig represents the configuration options for the authority. An
|
||||||
|
// underlaying registration authority can also be configured using the
|
||||||
|
// cas.Options.
|
||||||
type AuthConfig struct {
|
type AuthConfig struct {
|
||||||
|
*cas.Options
|
||||||
Provisioners provisioner.List `json:"provisioners"`
|
Provisioners provisioner.List `json:"provisioners"`
|
||||||
Template *ASN1DN `json:"template,omitempty"`
|
Template *ASN1DN `json:"template,omitempty"`
|
||||||
Claims *provisioner.Claims `json:"claims,omitempty"`
|
Claims *provisioner.Claims `json:"claims,omitempty"`
|
||||||
|
@ -185,8 +187,11 @@ func (c *Config) Validate() error {
|
||||||
return errors.New("dnsNames cannot be empty")
|
return errors.New("dnsNames cannot be empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
// The default CAS requires root, crt and key.
|
// Options holds the RA/CAS configuration.
|
||||||
if c.CAS.Is(cas.SoftCAS) {
|
ra := c.AuthorityConfig.Options
|
||||||
|
|
||||||
|
// The default RA/CAS requires root, crt and key.
|
||||||
|
if ra.Is(cas.SoftCAS) {
|
||||||
switch {
|
switch {
|
||||||
case c.Root.HasEmpties():
|
case c.Root.HasEmpties():
|
||||||
return errors.New("root cannot be empty")
|
return errors.New("root cannot be empty")
|
||||||
|
@ -225,8 +230,8 @@ func (c *Config) Validate() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate CAS options, nil is ok.
|
// Validate RA/CAS options, nil is ok.
|
||||||
if err := c.CAS.Validate(); err != nil {
|
if err := ra.Validate(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ type Options struct {
|
||||||
|
|
||||||
// CertificateAuthority reference. In CloudCAS the format is
|
// CertificateAuthority reference. In CloudCAS the format is
|
||||||
// `projects/*/locations/*/certificateAuthorities/*`.
|
// `projects/*/locations/*/certificateAuthorities/*`.
|
||||||
Certificateauthority string `json:"certificateAuthority"`
|
CertificateAuthority string `json:"certificateAuthority"`
|
||||||
|
|
||||||
// Issuer and signer are the issuer certificate and signer used in SoftCAS.
|
// Issuer and signer are the issuer certificate and signer used in SoftCAS.
|
||||||
// They are configured in ca.json crt and key properties.
|
// They are configured in ca.json crt and key properties.
|
||||||
|
|
|
@ -42,7 +42,7 @@ func TestOptions_Validate(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
Type string
|
Type string
|
||||||
CredentialsFile string
|
CredentialsFile string
|
||||||
Certificateauthority string
|
CertificateAuthority string
|
||||||
Issuer *x509.Certificate
|
Issuer *x509.Certificate
|
||||||
Signer crypto.Signer
|
Signer crypto.Signer
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ func TestOptions_Validate(t *testing.T) {
|
||||||
o := &Options{
|
o := &Options{
|
||||||
Type: tt.fields.Type,
|
Type: tt.fields.Type,
|
||||||
CredentialsFile: tt.fields.CredentialsFile,
|
CredentialsFile: tt.fields.CredentialsFile,
|
||||||
Certificateauthority: tt.fields.Certificateauthority,
|
CertificateAuthority: tt.fields.CertificateAuthority,
|
||||||
Issuer: tt.fields.Issuer,
|
Issuer: tt.fields.Issuer,
|
||||||
Signer: tt.fields.Signer,
|
Signer: tt.fields.Signer,
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ func TestOptions_Is(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
Type string
|
Type string
|
||||||
CredentialsFile string
|
CredentialsFile string
|
||||||
Certificateauthority string
|
CertificateAuthority string
|
||||||
Issuer *x509.Certificate
|
Issuer *x509.Certificate
|
||||||
Signer crypto.Signer
|
Signer crypto.Signer
|
||||||
}
|
}
|
||||||
|
@ -119,7 +119,7 @@ func TestOptions_Is(t *testing.T) {
|
||||||
o := &Options{
|
o := &Options{
|
||||||
Type: tt.fields.Type,
|
Type: tt.fields.Type,
|
||||||
CredentialsFile: tt.fields.CredentialsFile,
|
CredentialsFile: tt.fields.CredentialsFile,
|
||||||
Certificateauthority: tt.fields.Certificateauthority,
|
CertificateAuthority: tt.fields.CertificateAuthority,
|
||||||
Issuer: tt.fields.Issuer,
|
Issuer: tt.fields.Issuer,
|
||||||
Signer: tt.fields.Signer,
|
Signer: tt.fields.Signer,
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,7 @@ var newCertificateAuthorityClient = func(ctx context.Context, credentialsFile st
|
||||||
// New creates a new CertificateAuthorityService implementation using Google
|
// New creates a new CertificateAuthorityService implementation using Google
|
||||||
// Cloud CAS.
|
// Cloud CAS.
|
||||||
func New(ctx context.Context, opts apiv1.Options) (*CloudCAS, error) {
|
func New(ctx context.Context, opts apiv1.Options) (*CloudCAS, error) {
|
||||||
if opts.Certificateauthority == "" {
|
if opts.CertificateAuthority == "" {
|
||||||
return nil, errors.New("cloudCAS 'certificateAuthority' cannot be empty")
|
return nil, errors.New("cloudCAS 'certificateAuthority' cannot be empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ func New(ctx context.Context, opts apiv1.Options) (*CloudCAS, error) {
|
||||||
|
|
||||||
return &CloudCAS{
|
return &CloudCAS{
|
||||||
client: client,
|
client: client,
|
||||||
certificateAuthority: opts.Certificateauthority,
|
certificateAuthority: opts.CertificateAuthority,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -174,20 +174,20 @@ func TestNew(t *testing.T) {
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{"ok", args{context.Background(), apiv1.Options{
|
{"ok", args{context.Background(), apiv1.Options{
|
||||||
Certificateauthority: testAuthorityName,
|
CertificateAuthority: testAuthorityName,
|
||||||
}}, &CloudCAS{
|
}}, &CloudCAS{
|
||||||
client: &testClient{},
|
client: &testClient{},
|
||||||
certificateAuthority: testAuthorityName,
|
certificateAuthority: testAuthorityName,
|
||||||
}, false},
|
}, false},
|
||||||
{"ok with credentials", args{context.Background(), apiv1.Options{
|
{"ok with credentials", args{context.Background(), apiv1.Options{
|
||||||
Certificateauthority: testAuthorityName, CredentialsFile: "testdata/credentials.json",
|
CertificateAuthority: testAuthorityName, CredentialsFile: "testdata/credentials.json",
|
||||||
}}, &CloudCAS{
|
}}, &CloudCAS{
|
||||||
client: &testClient{credentialsFile: "testdata/credentials.json"},
|
client: &testClient{credentialsFile: "testdata/credentials.json"},
|
||||||
certificateAuthority: testAuthorityName,
|
certificateAuthority: testAuthorityName,
|
||||||
}, false},
|
}, false},
|
||||||
{"fail certificate authority", args{context.Background(), apiv1.Options{}}, nil, true},
|
{"fail certificate authority", args{context.Background(), apiv1.Options{}}, nil, true},
|
||||||
{"fail with credentials", args{context.Background(), apiv1.Options{
|
{"fail with credentials", args{context.Background(), apiv1.Options{
|
||||||
Certificateauthority: testAuthorityName, CredentialsFile: "testdata/error.json",
|
CertificateAuthority: testAuthorityName, CredentialsFile: "testdata/error.json",
|
||||||
}}, nil, true},
|
}}, nil, true},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
@ -225,7 +225,7 @@ func TestNew_register(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
got, err := newFn(context.Background(), apiv1.Options{
|
got, err := newFn(context.Background(), apiv1.Options{
|
||||||
Certificateauthority: testAuthorityName, CredentialsFile: "testdata/credentials.json",
|
CertificateAuthority: testAuthorityName, CredentialsFile: "testdata/credentials.json",
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("New() error = %v", err)
|
t.Errorf("New() error = %v", err)
|
||||||
|
@ -255,10 +255,10 @@ func TestNew_real(t *testing.T) {
|
||||||
args args
|
args args
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{"fail default credentials", true, args{context.Background(), apiv1.Options{Certificateauthority: testAuthorityName}}, true},
|
{"fail default credentials", true, args{context.Background(), apiv1.Options{CertificateAuthority: testAuthorityName}}, true},
|
||||||
{"fail certificate authority", false, args{context.Background(), apiv1.Options{}}, true},
|
{"fail certificate authority", false, args{context.Background(), apiv1.Options{}}, true},
|
||||||
{"fail with credentials", false, args{context.Background(), apiv1.Options{
|
{"fail with credentials", false, args{context.Background(), apiv1.Options{
|
||||||
Certificateauthority: testAuthorityName, CredentialsFile: "testdata/missing.json",
|
CertificateAuthority: testAuthorityName, CredentialsFile: "testdata/missing.json",
|
||||||
}}, true},
|
}}, true},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
|
23
docs/cas.md
23
docs/cas.md
|
@ -1,14 +1,15 @@
|
||||||
# Certificate Authority Services
|
# Registration Authorities
|
||||||
|
|
||||||
This document describes how to use a certificate authority service or CAS to
|
This document describes how to use an external registration authority (RA), aka
|
||||||
sign X.509 certificates requests.
|
certificate authority service (CAS) to sign X.509 certificates requests.
|
||||||
|
|
||||||
A CAS is a system that implements an API to sign certificate requests, the
|
A CAS is a system that implements an API to sign certificate requests, the
|
||||||
difference between CAS and KMS is that the latter can sign any data, while CAS
|
difference between CAS and KMS is that the latter can sign any data, while CAS
|
||||||
is intended to sign only X.509 certificates.
|
is intended to sign only X.509 certificates.
|
||||||
|
|
||||||
`step-ca` defines an interface that can be implemented to support other
|
`step-ca` defines an interface that can be implemented to support other
|
||||||
services, currently only CloudCAS and the default SoftCAS are implemented.
|
registration authorities, currently only CloudCAS and the default SoftCAS are
|
||||||
|
implemented.
|
||||||
|
|
||||||
The `CertificateAuthorityService` is defined in the package
|
The `CertificateAuthorityService` is defined in the package
|
||||||
`github.com/smallstep/certificates/cas/apiv1` and it is:
|
`github.com/smallstep/certificates/cas/apiv1` and it is:
|
||||||
|
@ -123,15 +124,15 @@ or using `gcloud` CLI:
|
||||||
--reusable-config "subordinate-server-tls-pathlen-0"
|
--reusable-config "subordinate-server-tls-pathlen-0"
|
||||||
```
|
```
|
||||||
|
|
||||||
Not it's time to enable it in `step-ca` adding the new property `"cas"` must be added
|
Now it's time to enable it in `step-ca` by adding some new files in the
|
||||||
to the `ca.json`.
|
`"authority"` section of the `ca.json`.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"cas": {
|
"authority": {
|
||||||
"type": "cloudCAS",
|
"type": "cloudCAS",
|
||||||
"credentialsFile": "/path/to/credentials.json",
|
"credentialsFile": "/path/to/credentials.json",
|
||||||
"certificateAuthority": "projects/<name>/locations/<loc>/certificateAuthorities/<ca-name>"
|
"certificateAuthority": "projects/<name>/locations/<loc>/certificateAuthorities/<ca-name>",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -161,12 +162,10 @@ need to configure `"root"`, and because the intermediate is in Google Cloud,
|
||||||
"type": "badger",
|
"type": "badger",
|
||||||
"dataSource": "/home/jane/.step/db",
|
"dataSource": "/home/jane/.step/db",
|
||||||
},
|
},
|
||||||
"cas": {
|
"authority": {
|
||||||
"type": "cloudCAS",
|
"type": "cloudCAS",
|
||||||
"credentialsFile": "/home/jane/.step/credentials.json",
|
"credentialsFile": "/home/jane/.step/credentials.json",
|
||||||
"certificateAuthority": "projects/smallstep-cas-test/locations/us-west1/certificateAuthorities/prod-intermediate-ca"
|
"certificateAuthority": "projects/smallstep-cas-test/locations/us-west1/certificateAuthorities/prod-intermediate-ca",
|
||||||
},
|
|
||||||
"authority": {
|
|
||||||
"provisioners": [
|
"provisioners": [
|
||||||
{
|
{
|
||||||
"type": "JWK",
|
"type": "JWK",
|
||||||
|
|
73
pki/pki.go
73
pki/pki.go
|
@ -1,6 +1,7 @@
|
||||||
package pki
|
package pki
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
@ -20,6 +21,8 @@ import (
|
||||||
"github.com/smallstep/certificates/authority"
|
"github.com/smallstep/certificates/authority"
|
||||||
"github.com/smallstep/certificates/authority/provisioner"
|
"github.com/smallstep/certificates/authority/provisioner"
|
||||||
"github.com/smallstep/certificates/ca"
|
"github.com/smallstep/certificates/ca"
|
||||||
|
"github.com/smallstep/certificates/cas"
|
||||||
|
"github.com/smallstep/certificates/cas/apiv1"
|
||||||
"github.com/smallstep/certificates/db"
|
"github.com/smallstep/certificates/db"
|
||||||
"github.com/smallstep/cli/config"
|
"github.com/smallstep/cli/config"
|
||||||
"github.com/smallstep/cli/errs"
|
"github.com/smallstep/cli/errs"
|
||||||
|
@ -157,6 +160,7 @@ type PKI struct {
|
||||||
dnsNames []string
|
dnsNames []string
|
||||||
caURL string
|
caURL string
|
||||||
enableSSH bool
|
enableSSH bool
|
||||||
|
authorityOptions *apiv1.Options
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new PKI configuration.
|
// New creates a new PKI configuration.
|
||||||
|
@ -233,6 +237,12 @@ func (p *PKI) GetRootFingerprint() string {
|
||||||
return p.rootFingerprint
|
return p.rootFingerprint
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetAuthorityOptions sets the authority options object, these options are used
|
||||||
|
// to configure a registration authority.
|
||||||
|
func (p *PKI) SetAuthorityOptions(opts *apiv1.Options) {
|
||||||
|
p.authorityOptions = opts
|
||||||
|
}
|
||||||
|
|
||||||
// SetProvisioner sets the provisioner name of the OTT keys.
|
// SetProvisioner sets the provisioner name of the OTT keys.
|
||||||
func (p *PKI) SetProvisioner(s string) {
|
func (p *PKI) SetProvisioner(s string) {
|
||||||
p.provisioner = s
|
p.provisioner = s
|
||||||
|
@ -307,9 +317,11 @@ func (p *PKI) WriteRootCertificate(rootCrt *x509.Certificate, rootKey interface{
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := pemutil.Serialize(rootKey, pemutil.WithPassword(pass), pemutil.ToFile(p.rootKey, 0600))
|
if rootKey != nil {
|
||||||
if err != nil {
|
_, err := pemutil.Serialize(rootKey, pemutil.WithPassword(pass), pemutil.ToFile(p.rootKey, 0600))
|
||||||
return err
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sum := sha256.Sum256(rootCrt.Raw)
|
sum := sha256.Sum256(rootCrt.Raw)
|
||||||
|
@ -318,6 +330,37 @@ func (p *PKI) WriteRootCertificate(rootCrt *x509.Certificate, rootKey interface{
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetCertificateAuthority attempts to load the certificate authority from the
|
||||||
|
// RA.
|
||||||
|
func (p *PKI) GetCertificateAuthority() error {
|
||||||
|
ca, err := cas.New(context.Background(), *p.authorityOptions)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
srv, ok := ca.(apiv1.CertificateAuthorityGetter)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := srv.GetCertificateAuthority(&apiv1.GetCertificateAuthorityRequest{
|
||||||
|
Name: p.authorityOptions.CertificateAuthority,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := p.WriteRootCertificate(resp.RootCertificate, nil, nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Issuer is in the RA
|
||||||
|
p.intermediate = ""
|
||||||
|
p.intermediateKey = ""
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// GenerateIntermediateCertificate generates an intermediate certificate with
|
// GenerateIntermediateCertificate generates an intermediate certificate with
|
||||||
// the given name.
|
// the given name.
|
||||||
func (p *PKI) GenerateIntermediateCertificate(name string, rootCrt *x509.Certificate, rootKey interface{}, pass []byte) error {
|
func (p *PKI) GenerateIntermediateCertificate(name string, rootCrt *x509.Certificate, rootKey interface{}, pass []byte) error {
|
||||||
|
@ -414,11 +457,18 @@ func (p *PKI) TellPKI() {
|
||||||
|
|
||||||
func (p *PKI) tellPKI() {
|
func (p *PKI) tellPKI() {
|
||||||
ui.Println()
|
ui.Println()
|
||||||
ui.PrintSelected("Root certificate", p.root)
|
if p.authorityOptions == nil || p.authorityOptions.Is(apiv1.SoftCAS) {
|
||||||
ui.PrintSelected("Root private key", p.rootKey)
|
ui.PrintSelected("Root certificate", p.root)
|
||||||
ui.PrintSelected("Root fingerprint", p.rootFingerprint)
|
ui.PrintSelected("Root private key", p.rootKey)
|
||||||
ui.PrintSelected("Intermediate certificate", p.intermediate)
|
ui.PrintSelected("Root fingerprint", p.rootFingerprint)
|
||||||
ui.PrintSelected("Intermediate private key", p.intermediateKey)
|
ui.PrintSelected("Intermediate certificate", p.intermediate)
|
||||||
|
ui.PrintSelected("Intermediate private key", p.intermediateKey)
|
||||||
|
} else if p.rootFingerprint != "" {
|
||||||
|
ui.PrintSelected("Root certificate", p.root)
|
||||||
|
ui.PrintSelected("Root fingerprint", p.rootFingerprint)
|
||||||
|
} else {
|
||||||
|
ui.Printf(`{{ "%s" | red }} {{ "Root certificate:" | bold }} failed to retrieve it from RA`+"\n", ui.IconBad)
|
||||||
|
}
|
||||||
if p.enableSSH {
|
if p.enableSSH {
|
||||||
ui.PrintSelected("SSH user root certificate", p.sshUserPubKey)
|
ui.PrintSelected("SSH user root certificate", p.sshUserPubKey)
|
||||||
ui.PrintSelected("SSH user root private key", p.sshUserKey)
|
ui.PrintSelected("SSH user root private key", p.sshUserKey)
|
||||||
|
@ -485,6 +535,7 @@ func (p *PKI) GenerateConfig(opt ...Option) (*authority.Config, error) {
|
||||||
DataSource: GetDBPath(),
|
DataSource: GetDBPath(),
|
||||||
},
|
},
|
||||||
AuthorityConfig: &authority.AuthConfig{
|
AuthorityConfig: &authority.AuthConfig{
|
||||||
|
Options: p.authorityOptions,
|
||||||
DisableIssuedAtCheck: false,
|
DisableIssuedAtCheck: false,
|
||||||
Provisioners: provisioner.List{prov},
|
Provisioners: provisioner.List{prov},
|
||||||
},
|
},
|
||||||
|
@ -591,7 +642,11 @@ func (p *PKI) Save(opt ...Option) error {
|
||||||
ui.PrintSelected("Default configuration", p.defaults)
|
ui.PrintSelected("Default configuration", p.defaults)
|
||||||
ui.PrintSelected("Certificate Authority configuration", p.config)
|
ui.PrintSelected("Certificate Authority configuration", p.config)
|
||||||
ui.Println()
|
ui.Println()
|
||||||
ui.Println("Your PKI is ready to go. To generate certificates for individual services see 'step help ca'.")
|
if p.authorityOptions == nil || p.authorityOptions.Is(apiv1.SoftCAS) {
|
||||||
|
ui.Println("Your PKI is ready to go. To generate certificates for individual services see 'step help ca'.")
|
||||||
|
} else {
|
||||||
|
ui.Println("Your registration authority is ready to go. To generate certificates for individual services see 'step help ca'.")
|
||||||
|
}
|
||||||
|
|
||||||
p.askFeedback()
|
p.askFeedback()
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue