You've already forked step-ca-cli
mirror of
https://github.com/smallstep/cli.git
synced 2025-08-09 03:22:43 +03:00
sshpop provisioner + ssh renew | revoke | rekey
This commit is contained in:
@@ -77,7 +77,10 @@ and must be one of:
|
|||||||
: Uses an X509 Certificate / private key pair to sign provisioning tokens.
|
: Uses an X509 Certificate / private key pair to sign provisioning tokens.
|
||||||
|
|
||||||
**K8sSA**
|
**K8sSA**
|
||||||
: Uses Kubernetes Service Account tokens.`,
|
: Uses Kubernetes Service Account tokens.
|
||||||
|
|
||||||
|
**SSHPOP**
|
||||||
|
: Uses an SSH Certificate / private key pair to sign provisioning tokens.`,
|
||||||
},
|
},
|
||||||
flags.PasswordFile,
|
flags.PasswordFile,
|
||||||
cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
@@ -281,6 +284,10 @@ $ step ca provisioner add x5c-smallstep --type X5C --x5c-root x5cRoot.crt
|
|||||||
Add a K8s Service Account provisioner.
|
Add a K8s Service Account provisioner.
|
||||||
'''
|
'''
|
||||||
$ step ca provisioner add my-kube-provisioner --type K8sSA --pem-keys keys.pub
|
$ step ca provisioner add my-kube-provisioner --type K8sSA --pem-keys keys.pub
|
||||||
|
|
||||||
|
Add an SSH-POP provisioner.
|
||||||
|
'''
|
||||||
|
$ step ca provisioner add sshpop-smallstep --type SSHPOP
|
||||||
'''`,
|
'''`,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -331,6 +338,8 @@ func addAction(ctx *cli.Context) (err error) {
|
|||||||
list, err = addX5CProvisioner(ctx, name, provMap)
|
list, err = addX5CProvisioner(ctx, name, provMap)
|
||||||
case provisioner.TypeK8sSA:
|
case provisioner.TypeK8sSA:
|
||||||
list, err = addK8sSAProvisioner(ctx, name, provMap)
|
list, err = addK8sSAProvisioner(ctx, name, provMap)
|
||||||
|
case provisioner.TypeSSHPOP:
|
||||||
|
list, err = addSSHPOPProvisioner(ctx, name, provMap)
|
||||||
default:
|
default:
|
||||||
return errors.Errorf("unknown type %s: this should not happen", typ)
|
return errors.Errorf("unknown type %s: this should not happen", typ)
|
||||||
}
|
}
|
||||||
@@ -689,6 +698,26 @@ func addK8sSAProvisioner(ctx *cli.Context, name string, provMap map[string]bool)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// addSSHPOPProvisioner returns a provisioner list containing a SSHPOP provisioner.
|
||||||
|
func addSSHPOPProvisioner(ctx *cli.Context, name string, provMap map[string]bool) (list provisioner.List, err error) {
|
||||||
|
ctx.Set("ssh", "true")
|
||||||
|
p := &provisioner.SSHPOP{
|
||||||
|
Type: provisioner.TypeSSHPOP.String(),
|
||||||
|
Name: name,
|
||||||
|
Claims: getClaims(ctx),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for duplicates
|
||||||
|
if _, ok := provMap[p.GetID()]; !ok {
|
||||||
|
provMap[p.GetID()] = true
|
||||||
|
} else {
|
||||||
|
return nil, errors.Errorf("duplicated provisioner: CA config already contains a provisioner with ID=%s", p.GetID())
|
||||||
|
}
|
||||||
|
|
||||||
|
list = append(list, p)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func getClaims(ctx *cli.Context) *provisioner.Claims {
|
func getClaims(ctx *cli.Context) *provisioner.Claims {
|
||||||
if ctx.Bool("ssh") {
|
if ctx.Bool("ssh") {
|
||||||
enable := true
|
enable := true
|
||||||
@@ -727,9 +756,11 @@ func parseProvisionerType(ctx *cli.Context) (provisioner.Type, error) {
|
|||||||
return provisioner.TypeACME, nil
|
return provisioner.TypeACME, nil
|
||||||
case "x5c":
|
case "x5c":
|
||||||
return provisioner.TypeX5C, nil
|
return provisioner.TypeX5C, nil
|
||||||
|
case "sshpop":
|
||||||
|
return provisioner.TypeSSHPOP, nil
|
||||||
case "k8ssa":
|
case "k8ssa":
|
||||||
return provisioner.TypeK8sSA, nil
|
return provisioner.TypeK8sSA, nil
|
||||||
default:
|
default:
|
||||||
return 0, errs.InvalidFlagValue(ctx, "type", typ, "JWK, OIDC, AWS, Azure, GCP, ACME, X5C, K8sSA")
|
return 0, errs.InvalidFlagValue(ctx, "type", typ, "JWK, OIDC, AWS, Azure, GCP, ACME, X5C, SSHPOP, K8sSA")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -116,11 +116,14 @@ the step CA):
|
|||||||
$ step ca revoke --offline --cert foo.crt --key foo.key
|
$ step ca revoke --offline --cert foo.crt --key foo.key
|
||||||
'''`,
|
'''`,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
flags.CaConfig,
|
cli.StringFlag{
|
||||||
flags.CaURL,
|
Name: "cert",
|
||||||
flags.Offline,
|
Usage: `The path to the <cert> that should be revoked.`,
|
||||||
flags.Root,
|
},
|
||||||
flags.Token,
|
cli.StringFlag{
|
||||||
|
Name: "key",
|
||||||
|
Usage: `The <path> to the key corresponding to the cert that should be revoked.`,
|
||||||
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "reasonCode",
|
Name: "reasonCode",
|
||||||
Value: "",
|
Value: "",
|
||||||
@@ -180,14 +183,11 @@ attribute certificate have been compromised (reasonCode=10).
|
|||||||
Name: "reason",
|
Name: "reason",
|
||||||
Usage: `The <string> representing the reason for which the cert is being revoked.`,
|
Usage: `The <string> representing the reason for which the cert is being revoked.`,
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
flags.CaConfig,
|
||||||
Name: "cert",
|
flags.CaURL,
|
||||||
Usage: `The path to the <cert> that should be revoked.`,
|
flags.Offline,
|
||||||
},
|
flags.Root,
|
||||||
cli.StringFlag{
|
flags.Token,
|
||||||
Name: "key",
|
|
||||||
Usage: `The <path> to the key corresponding to the cert that should be revoked.`,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -31,6 +31,7 @@ func tokenCommand() cli.Command {
|
|||||||
[**--password-file**=<file>] [**--output-file**=<file>] [**--key**=<path>]
|
[**--password-file**=<file>] [**--output-file**=<file>] [**--key**=<path>]
|
||||||
[**--san**=<SAN>] [**--offline**] [**--revoke**]
|
[**--san**=<SAN>] [**--offline**] [**--revoke**]
|
||||||
[**--x5c-cert**=<path>] [**--x5c-key**=<path>]
|
[**--x5c-cert**=<path>] [**--x5c-key**=<path>]
|
||||||
|
[**--sshpop-cert**=<path>] [**--sshpop-key**=<path>]
|
||||||
[**--ssh**] [**--host**] [**--principal**=<string>]
|
[**--ssh**] [**--host**] [**--principal**=<string>]
|
||||||
[**--k8ssa-token-path**=<file>`,
|
[**--k8ssa-token-path**=<file>`,
|
||||||
Description: `**step ca token** command generates a one-time token granting access to the
|
Description: `**step ca token** command generates a one-time token granting access to the
|
||||||
@@ -154,6 +155,8 @@ $ step ca token my-remote.hostname remote_ecdsa --ssh --host
|
|||||||
flags.Provisioner,
|
flags.Provisioner,
|
||||||
flags.X5cCert,
|
flags.X5cCert,
|
||||||
flags.X5cKey,
|
flags.X5cKey,
|
||||||
|
flags.SSHPOPCert,
|
||||||
|
flags.SSHPOPKey,
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "key",
|
Name: "key",
|
||||||
Usage: `The private key <path> used to sign the JWT. This is usually downloaded from
|
Usage: `The private key <path> used to sign the JWT. This is usually downloaded from
|
||||||
@@ -166,6 +169,16 @@ the certificate authority.`,
|
|||||||
cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "revoke",
|
Name: "revoke",
|
||||||
Usage: `Create a token for authorizing 'Revoke' requests. The audience will
|
Usage: `Create a token for authorizing 'Revoke' requests. The audience will
|
||||||
|
be invalid for any other API request.`,
|
||||||
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "renew",
|
||||||
|
Usage: `Create a token for authorizing 'renew' requests. The audience will
|
||||||
|
be invalid for any other API request.`,
|
||||||
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "rekey",
|
||||||
|
Usage: `Create a token for authorizing 'rekey' requests. The audience will
|
||||||
be invalid for any other API request.`,
|
be invalid for any other API request.`,
|
||||||
},
|
},
|
||||||
cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
@@ -188,14 +201,14 @@ func tokenAction(ctx *cli.Context) error {
|
|||||||
// x.509 flags
|
// x.509 flags
|
||||||
sans := ctx.StringSlice("san")
|
sans := ctx.StringSlice("san")
|
||||||
isRevoke := ctx.Bool("revoke")
|
isRevoke := ctx.Bool("revoke")
|
||||||
|
isRenew := ctx.Bool("renew")
|
||||||
|
isRekey := ctx.Bool("rekey")
|
||||||
// ssh flags
|
// ssh flags
|
||||||
isSSH := ctx.Bool("ssh")
|
isSSH := ctx.Bool("ssh")
|
||||||
isHost := ctx.Bool("host")
|
isHost := ctx.Bool("host")
|
||||||
principals := ctx.StringSlice("principal")
|
principals := ctx.StringSlice("principal")
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case isSSH && isRevoke:
|
|
||||||
return errs.IncompatibleFlagWithFlag(ctx, "ssh", "revoke")
|
|
||||||
case isSSH && len(sans) > 0:
|
case isSSH && len(sans) > 0:
|
||||||
return errs.IncompatibleFlagWithFlag(ctx, "ssh", "san")
|
return errs.IncompatibleFlagWithFlag(ctx, "ssh", "san")
|
||||||
case isHost && len(sans) > 0:
|
case isHost && len(sans) > 0:
|
||||||
@@ -210,18 +223,29 @@ func tokenAction(ctx *cli.Context) error {
|
|||||||
|
|
||||||
// Default token type is always a 'Sign' token.
|
// Default token type is always a 'Sign' token.
|
||||||
var typ int
|
var typ int
|
||||||
|
if isSSH {
|
||||||
switch {
|
switch {
|
||||||
case isSSH && isHost:
|
case isRevoke:
|
||||||
|
typ = cautils.SSHRevokeType
|
||||||
|
case isRenew:
|
||||||
|
typ = cautils.SSHRenewType
|
||||||
|
case isRekey:
|
||||||
|
typ = cautils.SSHRekeyType
|
||||||
|
case isHost:
|
||||||
typ = cautils.SSHHostSignType
|
typ = cautils.SSHHostSignType
|
||||||
sans = principals
|
sans = principals
|
||||||
case isSSH && !isHost:
|
default:
|
||||||
typ = cautils.SSHUserSignType
|
typ = cautils.SSHUserSignType
|
||||||
sans = principals
|
sans = principals
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch {
|
||||||
case isRevoke:
|
case isRevoke:
|
||||||
typ = cautils.RevokeType
|
typ = cautils.RevokeType
|
||||||
default:
|
default:
|
||||||
typ = cautils.SignType
|
typ = cautils.SignType
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
caURL := ctx.String("ca-url")
|
caURL := ctx.String("ca-url")
|
||||||
if len(caURL) == 0 {
|
if len(caURL) == 0 {
|
||||||
|
@@ -197,11 +197,17 @@ func certificateAction(ctx *cli.Context) error {
|
|||||||
crtFile = baseName + "-cert.pub"
|
crtFile = baseName + "-cert.pub"
|
||||||
}
|
}
|
||||||
|
|
||||||
var certType string
|
var (
|
||||||
|
certType string
|
||||||
|
tokType int
|
||||||
|
)
|
||||||
|
|
||||||
if isHost {
|
if isHost {
|
||||||
certType = provisioner.SSHHostCert
|
certType = provisioner.SSHHostCert
|
||||||
|
tokType = cautils.SSHHostSignType
|
||||||
} else {
|
} else {
|
||||||
certType = provisioner.SSHUserCert
|
certType = provisioner.SSHUserCert
|
||||||
|
tokType = cautils.SSHUserSignType
|
||||||
}
|
}
|
||||||
|
|
||||||
// By default use the first part of the subject as a principal
|
// By default use the first part of the subject as a principal
|
||||||
@@ -218,12 +224,12 @@ func certificateAction(ctx *cli.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if len(token) == 0 {
|
if len(token) == 0 {
|
||||||
if token, err = flow.GenerateSSHToken(ctx, subject, certType, principals, validAfter, validBefore); err != nil {
|
if token, err = flow.GenerateSSHToken(ctx, subject, tokType, principals, validAfter, validBefore); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
caClient, err := flow.GetClient(ctx, subject, token)
|
caClient, err := flow.GetClient(ctx, token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
58
command/ssh/getHosts.go
Normal file
58
command/ssh/getHosts.go
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
package ssh
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/smallstep/cli/command"
|
||||||
|
"github.com/smallstep/cli/errs"
|
||||||
|
"github.com/smallstep/cli/flags"
|
||||||
|
"github.com/smallstep/cli/utils/cautils"
|
||||||
|
"github.com/urfave/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getHostsCommand() cli.Command {
|
||||||
|
return cli.Command{
|
||||||
|
Name: "get-hosts",
|
||||||
|
Action: command.ActionFunc(getHostsAction),
|
||||||
|
Usage: "returns a list of all valid hosts",
|
||||||
|
UsageText: `**step ssh get-hosts**`,
|
||||||
|
Description: `**step ssh get-hosts** returns a list of valid hosts for SSH.
|
||||||
|
|
||||||
|
This command returns a zero exit status then the server exists, it will return 1
|
||||||
|
otherwise.
|
||||||
|
|
||||||
|
## POSITIONAL ARGUMENTS
|
||||||
|
|
||||||
|
## EXAMPLES
|
||||||
|
|
||||||
|
Get a list of valid hosts for SSH:
|
||||||
|
'''
|
||||||
|
$ step ssh get-hosts
|
||||||
|
'''`,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
flags.CaURL,
|
||||||
|
flags.Root,
|
||||||
|
flags.Offline,
|
||||||
|
flags.CaConfig,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getHostsAction(ctx *cli.Context) error {
|
||||||
|
if err := errs.NumberOfArguments(ctx, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := cautils.NewClient(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := client.SSHGetHosts()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(resp.Hosts)
|
||||||
|
return nil
|
||||||
|
}
|
@@ -142,12 +142,12 @@ func loginAction(ctx *cli.Context) error {
|
|||||||
validAfter = provisioner.NewTimeDuration(time.Now().Add(-1 * time.Minute))
|
validAfter = provisioner.NewTimeDuration(time.Now().Add(-1 * time.Minute))
|
||||||
}
|
}
|
||||||
|
|
||||||
if token, err = flow.GenerateSSHToken(ctx, subject, provisioner.SSHUserCert, principals, validAfter, validBefore); err != nil {
|
if token, err = flow.GenerateSSHToken(ctx, subject, cautils.SSHUserSignType, principals, validAfter, validBefore); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
caClient, err := flow.GetClient(ctx, subject, token)
|
caClient, err := flow.GetClient(ctx, token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@@ -167,12 +167,12 @@ func doLoginIfNeeded(ctx *cli.Context, subject string) error {
|
|||||||
validAfter := provisioner.NewTimeDuration(time.Now().Add(-1 * time.Minute))
|
validAfter := provisioner.NewTimeDuration(time.Now().Add(-1 * time.Minute))
|
||||||
validBefore := provisioner.TimeDuration{}
|
validBefore := provisioner.TimeDuration{}
|
||||||
|
|
||||||
token, err := flow.GenerateSSHToken(ctx, subject, provisioner.SSHUserCert, principals, validAfter, validBefore)
|
token, err := flow.GenerateSSHToken(ctx, subject, cautils.SSHUserSignType, principals, validAfter, validBefore)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
caClient, err := flow.GetClient(ctx, subject, token)
|
caClient, err := flow.GetClient(ctx, token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
176
command/ssh/rekey.go
Normal file
176
command/ssh/rekey.go
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
package ssh
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/smallstep/certificates/api"
|
||||||
|
"github.com/smallstep/certificates/authority/provisioner"
|
||||||
|
"github.com/smallstep/cli/command"
|
||||||
|
"github.com/smallstep/cli/crypto/keys"
|
||||||
|
"github.com/smallstep/cli/crypto/pemutil"
|
||||||
|
"github.com/smallstep/cli/errs"
|
||||||
|
"github.com/smallstep/cli/flags"
|
||||||
|
"github.com/smallstep/cli/ui"
|
||||||
|
"github.com/smallstep/cli/utils"
|
||||||
|
"github.com/smallstep/cli/utils/cautils"
|
||||||
|
"github.com/urfave/cli"
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
|
)
|
||||||
|
|
||||||
|
func rekeyCommand() cli.Command {
|
||||||
|
return cli.Command{
|
||||||
|
Name: "rekey",
|
||||||
|
Action: command.ActionFunc(rekeyAction),
|
||||||
|
Usage: "rekey a SSH certificate using the SSH CA",
|
||||||
|
UsageText: `**step ssh rekey** <ssh-cert> <ssh-key>
|
||||||
|
[**--new-cert**=<path>] [**--new-key**=<path>]
|
||||||
|
[**--issuer**=<name>] [**--ca-url**=<uri>] [**--root**=<path>]
|
||||||
|
[**--password-file**=<path>] [**--offline**] [**--ca-config**=<path>]
|
||||||
|
[**--force**]`,
|
||||||
|
Description: `**step ssh rerekey** command generates a new SSH Certificate
|
||||||
|
and key using an existing SSH Cerfificate (signed by **step-ca**) as a template.
|
||||||
|
This command uses [step certificates](https://github.com/smallstep/certificates).
|
||||||
|
|
||||||
|
## POSITIONAL ARGUMENTS
|
||||||
|
|
||||||
|
<ssh-cert>
|
||||||
|
: The ssh certificate to renew.
|
||||||
|
|
||||||
|
<ssh-key>
|
||||||
|
: The ssh certificate private key.
|
||||||
|
|
||||||
|
## EXAMPLES
|
||||||
|
|
||||||
|
Rekey an ssh certificate:
|
||||||
|
'''
|
||||||
|
$ step ssh rekey id_ecdsa-cert.pub id_ecdsa
|
||||||
|
'''`,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "cert",
|
||||||
|
Usage: `The path to the <cert> that should be revoked.`,
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "key",
|
||||||
|
Usage: `The <path> to the key corresponding to the cert that should be revoked.`,
|
||||||
|
},
|
||||||
|
sshProvisionerPasswordFlag,
|
||||||
|
flags.Provisioner,
|
||||||
|
flags.NoPassword,
|
||||||
|
flags.Insecure,
|
||||||
|
flags.CaURL,
|
||||||
|
flags.Root,
|
||||||
|
flags.Offline,
|
||||||
|
flags.CaConfig,
|
||||||
|
flags.Force,
|
||||||
|
flags.SSHPOPCert,
|
||||||
|
flags.SSHPOPKey,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func rekeyAction(ctx *cli.Context) error {
|
||||||
|
if err := errs.NumberOfArguments(ctx, 2); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
args := ctx.Args()
|
||||||
|
certFile := args.Get(0)
|
||||||
|
keyFile := args.Get(1)
|
||||||
|
|
||||||
|
newCertFile := ctx.String("new-cert")
|
||||||
|
newKeyFile := ctx.String("new-key")
|
||||||
|
passwordFile := ctx.String("password-file")
|
||||||
|
noPassword := ctx.Bool("no-password")
|
||||||
|
insecure := ctx.Bool("insecure")
|
||||||
|
if len(newCertFile) == 0 {
|
||||||
|
newCertFile = certFile
|
||||||
|
}
|
||||||
|
if len(newKeyFile) == 0 {
|
||||||
|
newKeyFile = keyFile
|
||||||
|
}
|
||||||
|
pubFile := newKeyFile + ".pub"
|
||||||
|
|
||||||
|
flow, err := cautils.NewCertificateFlow(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the cert, because we need the serial number.
|
||||||
|
certBytes, err := ioutil.ReadFile(certFile)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error reading ssh certificate from %s", certFile)
|
||||||
|
}
|
||||||
|
sshpub, _, _, _, err := ssh.ParseAuthorizedKey(certBytes)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error parsing ssh public key from %s", certFile)
|
||||||
|
}
|
||||||
|
cert, ok := sshpub.(*ssh.Certificate)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("error casting ssh public key to ssh certificate")
|
||||||
|
}
|
||||||
|
serial := strconv.FormatUint(cert.Serial, 10)
|
||||||
|
|
||||||
|
ctx.Set("sshpop-cert", certFile)
|
||||||
|
ctx.Set("sshpop-key", keyFile)
|
||||||
|
token, err := flow.GenerateSSHToken(ctx, serial, cautils.SSHRekeyType, nil, provisioner.TimeDuration{}, provisioner.TimeDuration{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
caClient, err := flow.GetClient(ctx, token)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate keypair
|
||||||
|
pub, priv, err := keys.GenerateDefaultKeyPair()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
sshPub, err := ssh.NewPublicKey(pub)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "error creating public key")
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := caClient.SSHRekey(&api.SSHRekeyRequest{
|
||||||
|
OTT: token,
|
||||||
|
PublicKey: sshPub.Marshal(),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private key (with password unless --no-password --insecure)
|
||||||
|
opts := []pemutil.Options{
|
||||||
|
pemutil.ToFile(newKeyFile, 0600),
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case noPassword && insecure:
|
||||||
|
case passwordFile != "":
|
||||||
|
opts = append(opts, pemutil.WithPasswordFile(passwordFile))
|
||||||
|
default:
|
||||||
|
opts = append(opts, pemutil.WithPasswordPrompt("Please enter the password to encrypt the private key"))
|
||||||
|
}
|
||||||
|
_, err = pemutil.Serialize(priv, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := utils.WriteFile(pubFile, marshalPublicKey(sshPub, cert.KeyId), 0644); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write certificate
|
||||||
|
if err := utils.WriteFile(newCertFile, marshalPublicKey(resp.Certificate, cert.KeyId), 0644); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ui.PrintSelected("Private Key", newKeyFile)
|
||||||
|
ui.PrintSelected("Public Key", pubFile)
|
||||||
|
ui.PrintSelected("Certificate", newCertFile)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
119
command/ssh/renew.go
Normal file
119
command/ssh/renew.go
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
package ssh
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/smallstep/certificates/api"
|
||||||
|
"github.com/smallstep/certificates/authority/provisioner"
|
||||||
|
"github.com/smallstep/cli/command"
|
||||||
|
"github.com/smallstep/cli/errs"
|
||||||
|
"github.com/smallstep/cli/flags"
|
||||||
|
"github.com/smallstep/cli/ui"
|
||||||
|
"github.com/smallstep/cli/utils"
|
||||||
|
"github.com/smallstep/cli/utils/cautils"
|
||||||
|
"github.com/urfave/cli"
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
|
)
|
||||||
|
|
||||||
|
func renewCommand() cli.Command {
|
||||||
|
return cli.Command{
|
||||||
|
Name: "renew",
|
||||||
|
Action: command.ActionFunc(renewAction),
|
||||||
|
Usage: "renew a SSH certificate using the SSH CA",
|
||||||
|
UsageText: `**step ssh renew** <ssh-cert> <ssh-key> <new-ssh-cert>
|
||||||
|
[**--issuer**=<name>] [**--ca-url**=<uri>] [**--root**=<path>]
|
||||||
|
[**--password-file**=<path>] [**--offline**] [**--ca-config**=<path>]
|
||||||
|
[**--force**]`,
|
||||||
|
Description: `**step ssh renew** command renews an SSH Cerfificate
|
||||||
|
using [step certificates](https://github.com/smallstep/certificates).
|
||||||
|
|
||||||
|
## POSITIONAL ARGUMENTS
|
||||||
|
|
||||||
|
<ssh-cert>
|
||||||
|
: The ssh certificate to renew.
|
||||||
|
|
||||||
|
<ssh-key>
|
||||||
|
: The ssh certificate private key.
|
||||||
|
|
||||||
|
<new-ssh-cert>
|
||||||
|
: The path where the new SSH Certificate should be written.
|
||||||
|
|
||||||
|
## EXAMPLES
|
||||||
|
|
||||||
|
Renew an ssh certificate:
|
||||||
|
'''
|
||||||
|
$ step ssh renew id_ecdsa-cert.pub id_ecdsa new-id_ecdsa-cer.pub
|
||||||
|
'''`,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
sshProvisionerPasswordFlag,
|
||||||
|
flags.Provisioner,
|
||||||
|
flags.CaURL,
|
||||||
|
flags.Root,
|
||||||
|
flags.Offline,
|
||||||
|
flags.CaConfig,
|
||||||
|
flags.Force,
|
||||||
|
flags.SSHPOPCert,
|
||||||
|
flags.SSHPOPKey,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func renewAction(ctx *cli.Context) error {
|
||||||
|
if err := errs.NumberOfArguments(ctx, 3); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
args := ctx.Args()
|
||||||
|
certFile := args.Get(0)
|
||||||
|
keyFile := args.Get(1)
|
||||||
|
newCertFile := args.Get(2)
|
||||||
|
|
||||||
|
flow, err := cautils.NewCertificateFlow(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the cert, because we need the serial number.
|
||||||
|
certBytes, err := ioutil.ReadFile(certFile)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error reading ssh certificate from %s", certFile)
|
||||||
|
}
|
||||||
|
sshpub, _, _, _, err := ssh.ParseAuthorizedKey(certBytes)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error parsing ssh public key from %s", certFile)
|
||||||
|
}
|
||||||
|
cert, ok := sshpub.(*ssh.Certificate)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("error casting ssh public key to ssh certificate")
|
||||||
|
}
|
||||||
|
serial := strconv.FormatUint(cert.Serial, 10)
|
||||||
|
|
||||||
|
ctx.Set("sshpop-cert", certFile)
|
||||||
|
ctx.Set("sshpop-key", keyFile)
|
||||||
|
token, err := flow.GenerateSSHToken(ctx, serial, cautils.SSHRenewType, nil, provisioner.TimeDuration{}, provisioner.TimeDuration{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
caClient, err := flow.GetClient(ctx, token)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := caClient.SSHRenew(&api.SSHRenewRequest{
|
||||||
|
OTT: token,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write certificate
|
||||||
|
if err := utils.WriteFile(newCertFile, marshalPublicKey(resp.Certificate, cert.KeyId), 0644); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ui.PrintSelected("Certificate", newCertFile)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
189
command/ssh/revoke.go
Normal file
189
command/ssh/revoke.go
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
package ssh
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/smallstep/certificates/api"
|
||||||
|
"github.com/smallstep/certificates/authority/provisioner"
|
||||||
|
"github.com/smallstep/cli/command"
|
||||||
|
"github.com/smallstep/cli/command/ca"
|
||||||
|
"github.com/smallstep/cli/errs"
|
||||||
|
"github.com/smallstep/cli/flags"
|
||||||
|
"github.com/smallstep/cli/ui"
|
||||||
|
"github.com/smallstep/cli/utils/cautils"
|
||||||
|
"github.com/urfave/cli"
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
|
)
|
||||||
|
|
||||||
|
func revokeCommand() cli.Command {
|
||||||
|
return cli.Command{
|
||||||
|
Name: "revoke",
|
||||||
|
Action: command.ActionFunc(revokeAction),
|
||||||
|
Usage: "revoke a SSH certificate using the SSH CA",
|
||||||
|
UsageText: `**step ssh revoke** <serial-number>
|
||||||
|
[**--token**=<token>] [**--issuer**=<name>] [**--ca-url**=<uri>] [**--root**=<path>]
|
||||||
|
[**--ca-config**=<path>] [**--password-file**=<path>] [**--offline**]
|
||||||
|
[**--reason**=<string>] [**--reasonCode**=<code>]
|
||||||
|
[**--sshpop-cert**=<path>] [**--sshpop-key**=<key>]`,
|
||||||
|
Description: `**step ssh revoke** command renews an SSH Cerfificate
|
||||||
|
using [step certificates](https://github.com/smallstep/certificates).
|
||||||
|
|
||||||
|
## POSITIONAL ARGUMENTS
|
||||||
|
|
||||||
|
<ssh-cert>
|
||||||
|
: The ssh certificate to renew.
|
||||||
|
|
||||||
|
<ssh-key>
|
||||||
|
: The ssh certificate private key.
|
||||||
|
|
||||||
|
## EXAMPLES
|
||||||
|
|
||||||
|
Renew an ssh certificate:
|
||||||
|
'''
|
||||||
|
$ step ssh renew id_ecdsa-cert.pub id_ecdsa new-id_ecdsa-cer.pub
|
||||||
|
'''`,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
flags.Token,
|
||||||
|
sshProvisionerPasswordFlag,
|
||||||
|
flags.Provisioner,
|
||||||
|
flags.CaURL,
|
||||||
|
flags.Root,
|
||||||
|
flags.Offline,
|
||||||
|
flags.CaConfig,
|
||||||
|
flags.SSHPOPCert,
|
||||||
|
flags.SSHPOPKey,
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "reasonCode",
|
||||||
|
Value: "",
|
||||||
|
Usage: `The <reasonCode> specifies the reason for revocation - chose from a list of
|
||||||
|
common revocation reasons. If unset, the default is Unspecified.
|
||||||
|
|
||||||
|
|
||||||
|
: <reasonCode> can be a number from 0-9 or a case insensitive string matching
|
||||||
|
one of the following options:
|
||||||
|
|
||||||
|
**Unspecified**
|
||||||
|
: No reason given (Default -- reasonCode=0).
|
||||||
|
|
||||||
|
**KeyCompromise**
|
||||||
|
: The key is believed to have been compromised (reasonCode=1).
|
||||||
|
|
||||||
|
**CACompromise**
|
||||||
|
: The issuing Certificate Authority itself has been compromised (reasonCode=2).
|
||||||
|
|
||||||
|
**AffiliationChanged**
|
||||||
|
: The certificate contained affiliation information, for example, it may
|
||||||
|
have been an EV certificate and the associated business is no longer owned by
|
||||||
|
the same entity (reasonCode=3).
|
||||||
|
|
||||||
|
**Superseded**
|
||||||
|
: The certificate is being replaced (reasonCode=4).
|
||||||
|
|
||||||
|
**CessationOfOperation**
|
||||||
|
: If a CA is decommissioned, no longer to be used, the CA's certificate
|
||||||
|
should be revoked with this reason code. Do not revoke the CA's certificate if
|
||||||
|
the CA no longer issues new certificates, yet still publishes CRLs for the
|
||||||
|
currently issued certificates (reasonCode=5).
|
||||||
|
|
||||||
|
**CertificateHold**
|
||||||
|
: A temporary revocation that indicates that a CA will not vouch for a
|
||||||
|
certificate at a specific point in time. Once a certificate is revoked with a
|
||||||
|
CertificateHold reason code, the certificate can then be revoked with another
|
||||||
|
Reason Code, or unrevoked and returned to use (reasonCode=6).
|
||||||
|
|
||||||
|
**RemoveFromCRL**
|
||||||
|
: If a certificate is revoked with the CertificateHold reason code, it is
|
||||||
|
possible to "unrevoke" a certificate. The unrevoking process still lists the
|
||||||
|
certificate in the CRL, but with the reason code set to RemoveFromCRL.
|
||||||
|
Note: This is specific to the CertificateHold reason and is only used in DeltaCRLs
|
||||||
|
(reasonCode=8).
|
||||||
|
|
||||||
|
**PrivilegeWithdrawn**
|
||||||
|
: The right to represent the given entity was revoked for some reason
|
||||||
|
(reasonCode=9).
|
||||||
|
|
||||||
|
**AACompromise**
|
||||||
|
: It is known or suspected that aspects of the AA validated in the
|
||||||
|
attribute certificate have been compromised (reasonCode=10).
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "reason",
|
||||||
|
Usage: `The <string> representing the reason for which the cert is being revoked.`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func revokeAction(ctx *cli.Context) error {
|
||||||
|
args := ctx.Args()
|
||||||
|
token := ctx.String("token")
|
||||||
|
var serial string
|
||||||
|
|
||||||
|
switch ctx.NArg() {
|
||||||
|
case 0:
|
||||||
|
certFile := ctx.String("sshpop-cert")
|
||||||
|
keyFile := ctx.String("sshpop-key")
|
||||||
|
if len(certFile) == 0 || len(keyFile) == 0 {
|
||||||
|
return errors.New("--sshpop-cert and --sshpop-key must be supplied if serial number is not supplied as first argument")
|
||||||
|
}
|
||||||
|
// Load the cert, because we need the serial number.
|
||||||
|
certBytes, err := ioutil.ReadFile(certFile)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error reading ssh certificate from %s", certFile)
|
||||||
|
}
|
||||||
|
sshpub, _, _, _, err := ssh.ParseAuthorizedKey(certBytes)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error parsing ssh public key from %s", certFile)
|
||||||
|
}
|
||||||
|
cert, ok := sshpub.(*ssh.Certificate)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("error casting ssh public key to ssh certificate")
|
||||||
|
}
|
||||||
|
serial = strconv.FormatUint(cert.Serial, 10)
|
||||||
|
case 1:
|
||||||
|
serial = args.Get(0)
|
||||||
|
default:
|
||||||
|
return errs.TooManyArguments(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
reason := ctx.String("reason")
|
||||||
|
// Convert the reasonCode flag to an OCSP revocation code.
|
||||||
|
reasonCode, err := ca.ReasonCodeToNum(ctx.String("reasonCode"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
flow, err := cautils.NewCertificateFlow(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(token) == 0 {
|
||||||
|
token, err = flow.GenerateSSHToken(ctx, serial, cautils.SSHRevokeType, nil, provisioner.TimeDuration{}, provisioner.TimeDuration{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
caClient, err := flow.GetClient(ctx, token)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = caClient.SSHRevoke(&api.SSHRevokeRequest{
|
||||||
|
Serial: serial,
|
||||||
|
Reason: reason,
|
||||||
|
ReasonCode: reasonCode,
|
||||||
|
OTT: token,
|
||||||
|
Passive: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.Printf("SSH Certificate with Serial Number %s has been revoked.\n", serial)
|
||||||
|
return nil
|
||||||
|
}
|
@@ -35,6 +35,10 @@ $ step ssh certificate --host internal.example.com ssh_host_ecdsa_key
|
|||||||
proxyCommand(),
|
proxyCommand(),
|
||||||
proxycommandCommand(),
|
proxycommandCommand(),
|
||||||
checkHostCommand(),
|
checkHostCommand(),
|
||||||
|
getHostsCommand(),
|
||||||
|
renewCommand(),
|
||||||
|
revokeCommand(),
|
||||||
|
rekeyCommand(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package keys
|
package keys
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
@@ -11,6 +12,7 @@ import (
|
|||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
stepx509 "github.com/smallstep/cli/pkg/x509"
|
stepx509 "github.com/smallstep/cli/pkg/x509"
|
||||||
"golang.org/x/crypto/ed25519"
|
"golang.org/x/crypto/ed25519"
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -82,11 +84,56 @@ func ExtractKey(in interface{}) (interface{}, error) {
|
|||||||
return k.PublicKey, nil
|
return k.PublicKey, nil
|
||||||
case *stepx509.CertificateRequest:
|
case *stepx509.CertificateRequest:
|
||||||
return k.PublicKey, nil
|
return k.PublicKey, nil
|
||||||
|
case *ssh.Certificate:
|
||||||
|
sshCryptoPubKey, ok := k.Key.(ssh.CryptoPublicKey)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("ssh public key could not be cast to ssh CryptoPublicKey")
|
||||||
|
}
|
||||||
|
return sshCryptoPubKey.CryptoPublicKey(), nil
|
||||||
|
case ssh.PublicKey:
|
||||||
|
sshCryptoPubKey, ok := k.(ssh.CryptoPublicKey)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("ssh public key could not be cast to ssh CryptoPublicKey")
|
||||||
|
}
|
||||||
|
return sshCryptoPubKey.CryptoPublicKey(), nil
|
||||||
default:
|
default:
|
||||||
return nil, errors.Errorf("cannot extract the key from type '%T'", k)
|
return nil, errors.Errorf("cannot extract the key from type '%T'", k)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VerifyPair that the public key matches the given private key.
|
||||||
|
func VerifyPair(pubkey interface{}, key interface{}) error {
|
||||||
|
switch pub := pubkey.(type) {
|
||||||
|
case *rsa.PublicKey:
|
||||||
|
priv, ok := key.(*rsa.PrivateKey)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("private key type does not match public key type")
|
||||||
|
}
|
||||||
|
if pub.N.Cmp(priv.N) != 0 {
|
||||||
|
return errors.New("private key does not match public key")
|
||||||
|
}
|
||||||
|
case *ecdsa.PublicKey:
|
||||||
|
priv, ok := key.(*ecdsa.PrivateKey)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("private key type does not match public key type")
|
||||||
|
}
|
||||||
|
if pub.X.Cmp(priv.X) != 0 || pub.Y.Cmp(priv.Y) != 0 {
|
||||||
|
return errors.New("private key does not match public key")
|
||||||
|
}
|
||||||
|
case ed25519.PublicKey:
|
||||||
|
priv, ok := key.(ed25519.PrivateKey)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("private key type does not match public key type")
|
||||||
|
}
|
||||||
|
if !bytes.Equal(priv.Public().(ed25519.PublicKey), pub) {
|
||||||
|
return errors.New("private key does not match public key")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return errors.Errorf("unsupported public key type %T", pub)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func generateECKey(crv string) (interface{}, error) {
|
func generateECKey(crv string) (interface{}, error) {
|
||||||
var c elliptic.Curve
|
var c elliptic.Curve
|
||||||
switch crv {
|
switch crv {
|
||||||
|
@@ -1,9 +1,6 @@
|
|||||||
package x509util
|
package x509util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/rsa"
|
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
@@ -16,7 +13,6 @@ import (
|
|||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/smallstep/cli/errs"
|
"github.com/smallstep/cli/errs"
|
||||||
"golang.org/x/crypto/ed25519"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Fingerprint returns the SHA-256 fingerprint of the certificate.
|
// Fingerprint returns the SHA-256 fingerprint of the certificate.
|
||||||
@@ -25,39 +21,6 @@ func Fingerprint(cert *x509.Certificate) string {
|
|||||||
return strings.ToLower(hex.EncodeToString(sum[:]))
|
return strings.ToLower(hex.EncodeToString(sum[:]))
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerifyCertKey that the public key of a certificate matches the given private key.
|
|
||||||
func VerifyCertKey(cert *x509.Certificate, key interface{}) error {
|
|
||||||
switch pub := cert.PublicKey.(type) {
|
|
||||||
case *rsa.PublicKey:
|
|
||||||
priv, ok := key.(*rsa.PrivateKey)
|
|
||||||
if !ok {
|
|
||||||
return errors.New("private key type does not match public key type")
|
|
||||||
}
|
|
||||||
if pub.N.Cmp(priv.N) != 0 {
|
|
||||||
return errors.New("private key does not match public key")
|
|
||||||
}
|
|
||||||
case *ecdsa.PublicKey:
|
|
||||||
priv, ok := key.(*ecdsa.PrivateKey)
|
|
||||||
if !ok {
|
|
||||||
return errors.New("private key type does not match public key type")
|
|
||||||
}
|
|
||||||
if pub.X.Cmp(priv.X) != 0 || pub.Y.Cmp(priv.Y) != 0 {
|
|
||||||
return errors.New("private key does not match public key")
|
|
||||||
}
|
|
||||||
case ed25519.PublicKey:
|
|
||||||
priv, ok := key.(ed25519.PrivateKey)
|
|
||||||
if !ok {
|
|
||||||
return errors.New("private key type does not match public key type")
|
|
||||||
}
|
|
||||||
if !bytes.Equal(priv.Public().(ed25519.PublicKey), pub) {
|
|
||||||
return errors.New("private key does not match public key")
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return errors.Errorf("unsupported public key type %T", pub)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SplitSANs splits a slice of Subject Alternative Names into slices of
|
// SplitSANs splits a slice of Subject Alternative Names into slices of
|
||||||
// IP Addresses and DNS Names. If an element is not an IP address, then it
|
// IP Addresses and DNS Names. If an element is not an IP address, then it
|
||||||
// is bucketed as a DNS Name.
|
// is bucketed as a DNS Name.
|
||||||
|
@@ -165,13 +165,27 @@ $STEPPATH/config/ca.json`,
|
|||||||
Usage: "Certificate (<chain>) in PEM format to store in the 'x5c' header of a JWT.",
|
Usage: "Certificate (<chain>) in PEM format to store in the 'x5c' header of a JWT.",
|
||||||
}
|
}
|
||||||
|
|
||||||
// X5cKey is a cli.Flag used to pass the private key corresponding to the x5c-cert
|
// X5cKey is a cli.Flag used to pass the private key (corresponding to the x5c-cert)
|
||||||
// that is used to sign the token.
|
// that is used to sign the token.
|
||||||
X5cKey = cli.StringFlag{
|
X5cKey = cli.StringFlag{
|
||||||
Name: "x5c-key",
|
Name: "x5c-key",
|
||||||
Usage: `Private key <path>, used to sign a JWT, corresponding to the certificate that will
|
Usage: `Private key <path>, used to sign a JWT, corresponding to the certificate that will
|
||||||
be stored in the 'x5c' header.`,
|
be stored in the 'x5c' header.`,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SSHPOPCert is a cli.Flag used to pass the sshpop header certificate for a JWT.
|
||||||
|
SSHPOPCert = cli.StringFlag{
|
||||||
|
Name: "sshpop-cert",
|
||||||
|
Usage: "Certificate (<chain>) in PEM format to store in the 'sshpop' header of a JWT.",
|
||||||
|
}
|
||||||
|
|
||||||
|
// SSHPOPKey is a cli.Flag used to pass the private key (corresponding to the sshpop-cert)
|
||||||
|
// that is used to sign the token.
|
||||||
|
SSHPOPKey = cli.StringFlag{
|
||||||
|
Name: "sshpop-key",
|
||||||
|
Usage: `Private key <path>, used to sign a JWT, corresponding to the certificate that will
|
||||||
|
be stored in the 'sshpop' header.`,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// ParseTimeOrDuration is a helper that returns the time or the current time
|
// ParseTimeOrDuration is a helper that returns the time or the current time
|
||||||
|
10
go.mod
10
go.mod
@@ -4,12 +4,10 @@ go 1.13
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/ThomasRooney/gexpect v0.0.0-20161231170123-5482f0350944
|
github.com/ThomasRooney/gexpect v0.0.0-20161231170123-5482f0350944
|
||||||
|
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a // indirect
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e
|
||||||
github.com/corpix/uarand v0.0.0-20170903190822-2b8494104d86 // indirect
|
|
||||||
github.com/go-chi/chi v4.0.2+incompatible // indirect
|
github.com/go-chi/chi v4.0.2+incompatible // indirect
|
||||||
github.com/icrowley/fake v0.0.0-20180203215853-4178557ae428
|
github.com/icrowley/fake v0.0.0-20180203215853-4178557ae428
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
|
||||||
github.com/kr/pty v1.1.3 // indirect
|
|
||||||
github.com/lunixbochs/vtclean v1.0.0 // indirect
|
github.com/lunixbochs/vtclean v1.0.0 // indirect
|
||||||
github.com/manifoldco/promptui v0.3.1
|
github.com/manifoldco/promptui v0.3.1
|
||||||
github.com/mattn/go-colorable v0.1.4 // indirect
|
github.com/mattn/go-colorable v0.1.4 // indirect
|
||||||
@@ -21,16 +19,16 @@ require (
|
|||||||
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95
|
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95
|
||||||
github.com/sirupsen/logrus v1.4.2 // indirect
|
github.com/sirupsen/logrus v1.4.2 // indirect
|
||||||
github.com/smallstep/assert v0.0.0-20180720014142-de77670473b5
|
github.com/smallstep/assert v0.0.0-20180720014142-de77670473b5
|
||||||
github.com/smallstep/certificates v0.14.0-rc.1.0.20191030004513-ff13b2a6991d
|
github.com/smallstep/certificates v0.14.0-rc.1.0.20191106004142-a9ea292bd480
|
||||||
github.com/smallstep/certinfo v0.0.0-20191029235839-00563809d483
|
github.com/smallstep/certinfo v0.0.0-20191029235839-00563809d483
|
||||||
github.com/smallstep/nosql v0.1.1 // indirect
|
|
||||||
github.com/smallstep/truststore v0.9.3
|
github.com/smallstep/truststore v0.9.3
|
||||||
github.com/smallstep/zcrypto v0.0.0-20191030000234-ab27e7ba0886
|
github.com/smallstep/zcrypto v0.0.0-20191030000234-ab27e7ba0886
|
||||||
github.com/smallstep/zlint v0.0.0-20180727184541-d84eaafe274f
|
github.com/smallstep/zlint v0.0.0-20180727184541-d84eaafe274f
|
||||||
github.com/stretchr/testify v1.4.0
|
github.com/stretchr/testify v1.4.0
|
||||||
github.com/urfave/cli v1.20.1-0.20181029213200-b67dcf995b6a
|
github.com/urfave/cli v1.20.1-0.20181029213200-b67dcf995b6a
|
||||||
github.com/weppos/publicsuffix-go v0.4.0 // indirect
|
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859
|
||||||
gopkg.in/square/go-jose.v2 v2.4.0
|
gopkg.in/square/go-jose.v2 v2.4.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//replace github.com/smallstep/certificates => ../certificates
|
||||||
|
67
go.sum
67
go.sum
@@ -1,12 +1,20 @@
|
|||||||
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9 h1:HD8gA2tkByhMAwYaFAX9w2l7vxvBQ5NMoxDrkhqhtn4=
|
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9 h1:HD8gA2tkByhMAwYaFAX9w2l7vxvBQ5NMoxDrkhqhtn4=
|
||||||
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
|
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg=
|
||||||
|
github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||||
|
github.com/Masterminds/semver/v3 v3.0.1 h1:2kKm5lb7dKVrt5TYUiAavE6oFc1cFT0057UVGT+JqLk=
|
||||||
|
github.com/Masterminds/semver/v3 v3.0.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||||
|
github.com/Masterminds/sprig/v3 v3.0.0 h1:KSQz7Nb08/3VU9E4ns29dDxcczhOD1q7O1UfM4G3t3g=
|
||||||
|
github.com/Masterminds/sprig/v3 v3.0.0/go.mod h1:NEUY/Qq8Gdm2xgYA+NwJM6wmfdRV9xkh8h/Rld20R0U=
|
||||||
github.com/OpenPeeDeeP/depguard v1.0.0/go.mod h1:7/4sitnI9YlQgTLLk734QlzXT8DuHVnAyztLplQjk+o=
|
github.com/OpenPeeDeeP/depguard v1.0.0/go.mod h1:7/4sitnI9YlQgTLLk734QlzXT8DuHVnAyztLplQjk+o=
|
||||||
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||||
github.com/ThomasRooney/gexpect v0.0.0-20161231170123-5482f0350944 h1:CjexZrggt4RldpEUXFZf52vSO3cnmFaqW6B4wADj05Q=
|
github.com/ThomasRooney/gexpect v0.0.0-20161231170123-5482f0350944 h1:CjexZrggt4RldpEUXFZf52vSO3cnmFaqW6B4wADj05Q=
|
||||||
github.com/ThomasRooney/gexpect v0.0.0-20161231170123-5482f0350944/go.mod h1:sPML5WwI6oxLRLPuuqbtoOKhtmpVDCYtwsps+I+vjIY=
|
github.com/ThomasRooney/gexpect v0.0.0-20161231170123-5482f0350944/go.mod h1:sPML5WwI6oxLRLPuuqbtoOKhtmpVDCYtwsps+I+vjIY=
|
||||||
github.com/asaskevich/govalidator v0.0.0-20180315120708-ccb8e960c48f h1:y2hSFdXeA1y5z5f0vfNO0Dg5qVY036qzlz3Pds0B92o=
|
github.com/asaskevich/govalidator v0.0.0-20180315120708-ccb8e960c48f h1:y2hSFdXeA1y5z5f0vfNO0Dg5qVY036qzlz3Pds0B92o=
|
||||||
github.com/asaskevich/govalidator v0.0.0-20180315120708-ccb8e960c48f/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
github.com/asaskevich/govalidator v0.0.0-20180315120708-ccb8e960c48f/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||||
|
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=
|
||||||
|
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||||
github.com/boombuler/barcode v1.0.0 h1:s1TvRnXwL2xJRaccrdcBQMZxq6X7DvsMogtmJeHDdrc=
|
github.com/boombuler/barcode v1.0.0 h1:s1TvRnXwL2xJRaccrdcBQMZxq6X7DvsMogtmJeHDdrc=
|
||||||
github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||||
github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
|
github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
|
||||||
@@ -51,6 +59,7 @@ github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2
|
|||||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
github.com/golang/mock v1.0.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.0.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
|
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
@@ -74,11 +83,24 @@ github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg
|
|||||||
github.com/google/certificate-transparency-go v1.0.21 h1:Yf1aXowfZ2nuboBsg7iYGLmwsOARdV86pfH3g95wXmE=
|
github.com/google/certificate-transparency-go v1.0.21 h1:Yf1aXowfZ2nuboBsg7iYGLmwsOARdV86pfH3g95wXmE=
|
||||||
github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
|
github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||||
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE=
|
||||||
github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE=
|
github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE=
|
||||||
github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=
|
github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=
|
||||||
|
github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
|
github.com/huandu/xstrings v1.2.0 h1:yPeWdRnmynF7p+lLYz0H2tthW9lqhMJrQV/U7yy4wX0=
|
||||||
|
github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4=
|
||||||
|
github.com/icrowley/fake v0.0.0-20180203215853-4178557ae428 h1:Mo9W14pwbO9VfRe+ygqZ8dFbPpoIK1HFrG/zjTuQ+nc=
|
||||||
github.com/icrowley/fake v0.0.0-20180203215853-4178557ae428 h1:Mo9W14pwbO9VfRe+ygqZ8dFbPpoIK1HFrG/zjTuQ+nc=
|
github.com/icrowley/fake v0.0.0-20180203215853-4178557ae428 h1:Mo9W14pwbO9VfRe+ygqZ8dFbPpoIK1HFrG/zjTuQ+nc=
|
||||||
github.com/icrowley/fake v0.0.0-20180203215853-4178557ae428/go.mod h1:uhpZMVGznybq1itEKXj6RYw9I71qK4kH+OGMjRC4KEo=
|
github.com/icrowley/fake v0.0.0-20180203215853-4178557ae428/go.mod h1:uhpZMVGznybq1itEKXj6RYw9I71qK4kH+OGMjRC4KEo=
|
||||||
|
github.com/icrowley/fake v0.0.0-20180203215853-4178557ae428/go.mod h1:uhpZMVGznybq1itEKXj6RYw9I71qK4kH+OGMjRC4KEo=
|
||||||
|
github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI=
|
||||||
|
github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||||
|
github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
|
||||||
|
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||||
github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a h1:FaWFmfWdAUKbSCtOU2QjDaorUexogfaMgbipgYATUMU=
|
github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a h1:FaWFmfWdAUKbSCtOU2QjDaorUexogfaMgbipgYATUMU=
|
||||||
@@ -120,9 +142,16 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
|
|||||||
github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
|
github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
|
||||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||||
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
|
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
|
||||||
|
github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
|
||||||
|
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
|
||||||
|
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
github.com/mitchellh/go-ps v0.0.0-20170309133038-4fdf99ab2936/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk=
|
github.com/mitchellh/go-ps v0.0.0-20170309133038-4fdf99ab2936/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk=
|
||||||
|
github.com/mitchellh/go-ps v0.0.0-20170309133038-4fdf99ab2936/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk=
|
||||||
github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
|
github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
|
github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
|
||||||
|
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||||
github.com/mozilla/tls-observatory v0.0.0-20180409132520-8791a200eb40/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk=
|
github.com/mozilla/tls-observatory v0.0.0-20180409132520-8791a200eb40/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk=
|
||||||
github.com/nbutton23/zxcvbn-go v0.0.0-20160627004424-a22cb81b2ecd/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
|
github.com/nbutton23/zxcvbn-go v0.0.0-20160627004424-a22cb81b2ecd/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
|
||||||
github.com/nbutton23/zxcvbn-go v0.0.0-20171102151520-eafdab6b0663/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
|
github.com/nbutton23/zxcvbn-go v0.0.0-20171102151520-eafdab6b0663/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
|
||||||
@@ -162,37 +191,39 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd
|
|||||||
github.com/smallstep/assert v0.0.0-20180720014142-de77670473b5 h1:lX6ybsQW9Agn3qK/W1Z39Z4a6RyEMGem/gXUYW0axYk=
|
github.com/smallstep/assert v0.0.0-20180720014142-de77670473b5 h1:lX6ybsQW9Agn3qK/W1Z39Z4a6RyEMGem/gXUYW0axYk=
|
||||||
github.com/smallstep/assert v0.0.0-20180720014142-de77670473b5/go.mod h1:TC9A4+RjIOS+HyTH7wG17/gSqVv95uDw2J64dQZx7RE=
|
github.com/smallstep/assert v0.0.0-20180720014142-de77670473b5/go.mod h1:TC9A4+RjIOS+HyTH7wG17/gSqVv95uDw2J64dQZx7RE=
|
||||||
github.com/smallstep/certificates v0.13.3 h1:iCb4FbthYMQe4PmSyWFw5sLlJA2QUS2XK0mJDpGey1U=
|
github.com/smallstep/certificates v0.13.3 h1:iCb4FbthYMQe4PmSyWFw5sLlJA2QUS2XK0mJDpGey1U=
|
||||||
|
github.com/smallstep/certificates v0.13.3 h1:iCb4FbthYMQe4PmSyWFw5sLlJA2QUS2XK0mJDpGey1U=
|
||||||
github.com/smallstep/certificates v0.13.4-0.20191018210018-0a96062b7696 h1:opSHLqzpZzfpMTmiZMzd+XCKwEqF33Kjsrviq5xzfxc=
|
github.com/smallstep/certificates v0.13.4-0.20191018210018-0a96062b7696 h1:opSHLqzpZzfpMTmiZMzd+XCKwEqF33Kjsrviq5xzfxc=
|
||||||
github.com/smallstep/certificates v0.13.4-0.20191018210018-0a96062b7696/go.mod h1:eZ90IBg01k2RKND3sAKXVI0atx5/lzCNX0sA0DqwcUg=
|
github.com/smallstep/certificates v0.13.4-0.20191018210018-0a96062b7696/go.mod h1:eZ90IBg01k2RKND3sAKXVI0atx5/lzCNX0sA0DqwcUg=
|
||||||
github.com/smallstep/certificates v0.14.0-rc.1.0.20191030004513-ff13b2a6991d h1:ToJHQ5qrZsOBYoTmOaMRiqxTTaO2jVRpN+WtrVrJz2U=
|
|
||||||
github.com/smallstep/certificates v0.14.0-rc.1.0.20191030004513-ff13b2a6991d/go.mod h1:DEXc84DU0EEALvAVb3QKecOaWWn+pj0KT7etWJs5M0g=
|
|
||||||
github.com/smallstep/certinfo v0.0.0-20191008000228-b0e530932339 h1:5T/ToU20n2NinSGxSsq72Wx55ETzXU3WbrYVs96oiiw=
|
|
||||||
github.com/smallstep/certinfo v0.0.0-20191008000228-b0e530932339/go.mod h1:n4YHPL9hJIyB+N4F2rPBy3mpPxMxTGJP5Pdsyaoc2Ns=
|
|
||||||
github.com/smallstep/certinfo v0.0.0-20191029235839-00563809d483 h1:D7D2SPv6XS5wqTejlecJdRJdnsuehQ1s4NaKssQHUwo=
|
|
||||||
github.com/smallstep/certinfo v0.0.0-20191029235839-00563809d483/go.mod h1:xmx5n8+7jI0lrjTUwc8WMMqXeOHRyxYUW9U1wrvP3Vo=
|
|
||||||
github.com/smallstep/cli v0.13.3/go.mod h1:zGPm8vWCqzvDqkdC1laFJNdIOjNSB8V4qDp68Ny538o=
|
|
||||||
github.com/smallstep/cli v0.13.4-0.20191014220846-775cfe98ef76/go.mod h1:zGPm8vWCqzvDqkdC1laFJNdIOjNSB8V4qDp68Ny538o=
|
|
||||||
github.com/smallstep/nosql v0.1.1-0.20190924212324-f80b3f432de0 h1:9ruO770GHSBu8ktiAiCs4DW1wbnvm+Ur+syz7TJ8hvw=
|
|
||||||
github.com/smallstep/nosql v0.1.1-0.20190924212324-f80b3f432de0/go.mod h1:MFhYHIE/0V7OOHjYzjnWHqySJ40PVbwhjy24UBkJI2g=
|
|
||||||
github.com/smallstep/nosql v0.1.1-0.20191009043502-4b26d8029e61 h1:XM3mkHNBc6bEQhrZNEma+iz63xrmRFfCocmAEObeg/s=
|
|
||||||
github.com/smallstep/nosql v0.1.1-0.20191009043502-4b26d8029e61/go.mod h1:MFhYHIE/0V7OOHjYzjnWHqySJ40PVbwhjy24UBkJI2g=
|
|
||||||
github.com/smallstep/nosql v0.1.1 h1:ijeE3CM00SddioodNl/LWRQINNNCK1dLUsjZDwpUbNg=
|
|
||||||
github.com/smallstep/nosql v0.1.1/go.mod h1:qyxCqeyGwkuM6bfJSY3sg+aiXEiD0GbQOPzIF8/ZD8Q=
|
|
||||||
github.com/smallstep/certificates v0.13.3 h1:iCb4FbthYMQe4PmSyWFw5sLlJA2QUS2XK0mJDpGey1U=
|
|
||||||
github.com/smallstep/certificates v0.14.0-rc.1.0.20191023014154-4669bef8c700 h1:uIDgnm6/yq07DzGHGXmzg0/sHdaW8G/KxepeNGgiHN8=
|
github.com/smallstep/certificates v0.14.0-rc.1.0.20191023014154-4669bef8c700 h1:uIDgnm6/yq07DzGHGXmzg0/sHdaW8G/KxepeNGgiHN8=
|
||||||
github.com/smallstep/certificates v0.14.0-rc.1.0.20191023014154-4669bef8c700/go.mod h1:/WOAB2LkcjkEbKG5rDol+A22Lp3UsttkLPLkY7tVtuk=
|
github.com/smallstep/certificates v0.14.0-rc.1.0.20191023014154-4669bef8c700/go.mod h1:/WOAB2LkcjkEbKG5rDol+A22Lp3UsttkLPLkY7tVtuk=
|
||||||
github.com/smallstep/certificates v0.14.0-rc.1.0.20191025192352-8ef9b020ed24 h1:BnutRqtot5ywM7MBBi9rhzrVrGIMeIbDXNuaJhakl6M=
|
github.com/smallstep/certificates v0.14.0-rc.1.0.20191025192352-8ef9b020ed24 h1:BnutRqtot5ywM7MBBi9rhzrVrGIMeIbDXNuaJhakl6M=
|
||||||
github.com/smallstep/certificates v0.14.0-rc.1.0.20191025192352-8ef9b020ed24/go.mod h1:043iBnsMvNhQ+QFwSh0N6JR3H2yamHPPAc78vCf+8Tc=
|
github.com/smallstep/certificates v0.14.0-rc.1.0.20191025192352-8ef9b020ed24/go.mod h1:043iBnsMvNhQ+QFwSh0N6JR3H2yamHPPAc78vCf+8Tc=
|
||||||
|
github.com/smallstep/certificates v0.14.0-rc.1.0.20191030004513-ff13b2a6991d h1:ToJHQ5qrZsOBYoTmOaMRiqxTTaO2jVRpN+WtrVrJz2U=
|
||||||
|
github.com/smallstep/certificates v0.14.0-rc.1.0.20191030004513-ff13b2a6991d/go.mod h1:DEXc84DU0EEALvAVb3QKecOaWWn+pj0KT7etWJs5M0g=
|
||||||
github.com/smallstep/certificates v0.14.0-rc.1.0.20191105020910-861a6537690f h1:ymNMADW3nbsgikuTMGm4Lrv1rdD7BR4h21nd7yltdJQ=
|
github.com/smallstep/certificates v0.14.0-rc.1.0.20191105020910-861a6537690f h1:ymNMADW3nbsgikuTMGm4Lrv1rdD7BR4h21nd7yltdJQ=
|
||||||
github.com/smallstep/certificates v0.14.0-rc.1.0.20191105020910-861a6537690f/go.mod h1:r2UTcAZNriKlwvNNXymNAcF3iKL6mTYOYrOCtBYYGJU=
|
github.com/smallstep/certificates v0.14.0-rc.1.0.20191105020910-861a6537690f/go.mod h1:r2UTcAZNriKlwvNNXymNAcF3iKL6mTYOYrOCtBYYGJU=
|
||||||
|
github.com/smallstep/certificates v0.14.0-rc.1.0.20191106004142-a9ea292bd480 h1:B9eSECXfP/oX5ZZ6x4m9DuXQHHzylP9VnP8/n/dKKrc=
|
||||||
|
github.com/smallstep/certificates v0.14.0-rc.1.0.20191106004142-a9ea292bd480/go.mod h1:tfT13ADY1ov+7Xb/h23xP5w+iuQp67oKJUv5gvoKIcI=
|
||||||
|
github.com/smallstep/certinfo v0.0.0-20191008000228-b0e530932339 h1:5T/ToU20n2NinSGxSsq72Wx55ETzXU3WbrYVs96oiiw=
|
||||||
github.com/smallstep/certinfo v0.0.0-20191008000228-b0e530932339 h1:5T/ToU20n2NinSGxSsq72Wx55ETzXU3WbrYVs96oiiw=
|
github.com/smallstep/certinfo v0.0.0-20191008000228-b0e530932339 h1:5T/ToU20n2NinSGxSsq72Wx55ETzXU3WbrYVs96oiiw=
|
||||||
github.com/smallstep/certinfo v0.0.0-20191008000228-b0e530932339/go.mod h1:n4YHPL9hJIyB+N4F2rPBy3mpPxMxTGJP5Pdsyaoc2Ns=
|
github.com/smallstep/certinfo v0.0.0-20191008000228-b0e530932339/go.mod h1:n4YHPL9hJIyB+N4F2rPBy3mpPxMxTGJP5Pdsyaoc2Ns=
|
||||||
|
github.com/smallstep/certinfo v0.0.0-20191008000228-b0e530932339/go.mod h1:n4YHPL9hJIyB+N4F2rPBy3mpPxMxTGJP5Pdsyaoc2Ns=
|
||||||
|
github.com/smallstep/certinfo v0.0.0-20191029235839-00563809d483 h1:D7D2SPv6XS5wqTejlecJdRJdnsuehQ1s4NaKssQHUwo=
|
||||||
|
github.com/smallstep/certinfo v0.0.0-20191029235839-00563809d483/go.mod h1:xmx5n8+7jI0lrjTUwc8WMMqXeOHRyxYUW9U1wrvP3Vo=
|
||||||
github.com/smallstep/cli v0.12.1-0.20191016010425-15911d8625df/go.mod h1:zGPm8vWCqzvDqkdC1laFJNdIOjNSB8V4qDp68Ny538o=
|
github.com/smallstep/cli v0.12.1-0.20191016010425-15911d8625df/go.mod h1:zGPm8vWCqzvDqkdC1laFJNdIOjNSB8V4qDp68Ny538o=
|
||||||
|
github.com/smallstep/cli v0.13.3/go.mod h1:zGPm8vWCqzvDqkdC1laFJNdIOjNSB8V4qDp68Ny538o=
|
||||||
|
github.com/smallstep/cli v0.13.4-0.20191014220846-775cfe98ef76/go.mod h1:zGPm8vWCqzvDqkdC1laFJNdIOjNSB8V4qDp68Ny538o=
|
||||||
github.com/smallstep/cli v0.14.0-rc.1.0.20191024214139-914a67ed80c2/go.mod h1:GoA1cE4YrZRRvVbFlPKJUsMuWHnFBX+R88j1pmpbGgk=
|
github.com/smallstep/cli v0.14.0-rc.1.0.20191024214139-914a67ed80c2/go.mod h1:GoA1cE4YrZRRvVbFlPKJUsMuWHnFBX+R88j1pmpbGgk=
|
||||||
github.com/smallstep/cli v0.14.0-rc.1.0.20191105013638-8cf838b56d03/go.mod h1:dklnISxr+GzUmurBngEF9Jvj0aI9KK5uVgZwOdFniNs=
|
github.com/smallstep/cli v0.14.0-rc.1.0.20191105013638-8cf838b56d03/go.mod h1:dklnISxr+GzUmurBngEF9Jvj0aI9KK5uVgZwOdFniNs=
|
||||||
|
github.com/smallstep/nosql v0.1.1-0.20190924212324-f80b3f432de0 h1:9ruO770GHSBu8ktiAiCs4DW1wbnvm+Ur+syz7TJ8hvw=
|
||||||
|
github.com/smallstep/nosql v0.1.1-0.20190924212324-f80b3f432de0/go.mod h1:MFhYHIE/0V7OOHjYzjnWHqySJ40PVbwhjy24UBkJI2g=
|
||||||
|
github.com/smallstep/nosql v0.1.1-0.20191009043502-4b26d8029e61 h1:XM3mkHNBc6bEQhrZNEma+iz63xrmRFfCocmAEObeg/s=
|
||||||
github.com/smallstep/nosql v0.1.1-0.20191009043502-4b26d8029e61 h1:XM3mkHNBc6bEQhrZNEma+iz63xrmRFfCocmAEObeg/s=
|
github.com/smallstep/nosql v0.1.1-0.20191009043502-4b26d8029e61 h1:XM3mkHNBc6bEQhrZNEma+iz63xrmRFfCocmAEObeg/s=
|
||||||
github.com/smallstep/nosql v0.1.1-0.20191009043502-4b26d8029e61/go.mod h1:MFhYHIE/0V7OOHjYzjnWHqySJ40PVbwhjy24UBkJI2g=
|
github.com/smallstep/nosql v0.1.1-0.20191009043502-4b26d8029e61/go.mod h1:MFhYHIE/0V7OOHjYzjnWHqySJ40PVbwhjy24UBkJI2g=
|
||||||
|
github.com/smallstep/nosql v0.1.1-0.20191009043502-4b26d8029e61/go.mod h1:MFhYHIE/0V7OOHjYzjnWHqySJ40PVbwhjy24UBkJI2g=
|
||||||
github.com/smallstep/nosql v0.1.1 h1:ijeE3CM00SddioodNl/LWRQINNNCK1dLUsjZDwpUbNg=
|
github.com/smallstep/nosql v0.1.1 h1:ijeE3CM00SddioodNl/LWRQINNNCK1dLUsjZDwpUbNg=
|
||||||
|
github.com/smallstep/nosql v0.1.1 h1:ijeE3CM00SddioodNl/LWRQINNNCK1dLUsjZDwpUbNg=
|
||||||
|
github.com/smallstep/nosql v0.1.1/go.mod h1:qyxCqeyGwkuM6bfJSY3sg+aiXEiD0GbQOPzIF8/ZD8Q=
|
||||||
github.com/smallstep/nosql v0.1.1/go.mod h1:qyxCqeyGwkuM6bfJSY3sg+aiXEiD0GbQOPzIF8/ZD8Q=
|
github.com/smallstep/nosql v0.1.1/go.mod h1:qyxCqeyGwkuM6bfJSY3sg+aiXEiD0GbQOPzIF8/ZD8Q=
|
||||||
github.com/smallstep/truststore v0.9.3 h1:YAbCOZN4PrGApIPwUd95b71blpRJ/ZlbM9o6XwUugjo=
|
github.com/smallstep/truststore v0.9.3 h1:YAbCOZN4PrGApIPwUd95b71blpRJ/ZlbM9o6XwUugjo=
|
||||||
github.com/smallstep/truststore v0.9.3/go.mod h1:PRSkpRIhAYBK/KLWkHNgRdYgzWMEy45bN7PSJCfKKGE=
|
github.com/smallstep/truststore v0.9.3/go.mod h1:PRSkpRIhAYBK/KLWkHNgRdYgzWMEy45bN7PSJCfKKGE=
|
||||||
@@ -205,6 +236,8 @@ github.com/smallstep/zlint v0.0.0-20180727184541-d84eaafe274f/go.mod h1:GeHHT7sJ
|
|||||||
github.com/sourcegraph/go-diff v0.5.1/go.mod h1:j2dHj3m8aZgQO8lMTcTnBcXkRRRqi34cd2MNlA9u1mE=
|
github.com/sourcegraph/go-diff v0.5.1/go.mod h1:j2dHj3m8aZgQO8lMTcTnBcXkRRRqi34cd2MNlA9u1mE=
|
||||||
github.com/spf13/afero v1.1.0/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
github.com/spf13/afero v1.1.0/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||||
github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
|
github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
|
||||||
|
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
|
||||||
|
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
github.com/spf13/cobra v0.0.2/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
github.com/spf13/cobra v0.0.2/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||||
github.com/spf13/jwalterweatherman v0.0.0-20180109140146-7c0cea34c8ec/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
github.com/spf13/jwalterweatherman v0.0.0-20180109140146-7c0cea34c8ec/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||||
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
@@ -233,6 +266,10 @@ golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnf
|
|||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a h1:YX8ljsm6wXlHZO+aRz9Exqr0evNhKRNe5K/gi+zKh4U=
|
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a h1:YX8ljsm6wXlHZO+aRz9Exqr0evNhKRNe5K/gi+zKh4U=
|
||||||
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7 h1:0hQKqeLdqlt5iIwVOBErRisrHJAN57yOiPRQItI20fU=
|
||||||
|
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/net v0.0.0-20170915142106-8351a756f30f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20170915142106-8351a756f30f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
@@ -6,13 +6,44 @@ import (
|
|||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"github.com/smallstep/cli/crypto/keys"
|
||||||
"github.com/smallstep/cli/crypto/pemutil"
|
"github.com/smallstep/cli/crypto/pemutil"
|
||||||
"github.com/smallstep/cli/crypto/x509util"
|
|
||||||
"golang.org/x/crypto/ed25519"
|
"golang.org/x/crypto/ed25519"
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ValidateSSHPOP validates the given SSH certificate and key for use in an
|
||||||
|
// sshpop header.
|
||||||
|
func ValidateSSHPOP(certFile string, key interface{}) (string, error) {
|
||||||
|
if certFile == "" {
|
||||||
|
return "", errors.New("ssh certfile cannot be empty")
|
||||||
|
}
|
||||||
|
certBytes, err := ioutil.ReadFile(certFile)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrapf(err, "error reading ssh certificate from %s", certFile)
|
||||||
|
}
|
||||||
|
sshpub, _, _, _, err := ssh.ParseAuthorizedKey(certBytes)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrapf(err, "error parsing ssh public key from %s", certFile)
|
||||||
|
}
|
||||||
|
cert, ok := sshpub.(*ssh.Certificate)
|
||||||
|
if !ok {
|
||||||
|
return "", errors.New("error casting ssh public key to ssh certificate")
|
||||||
|
}
|
||||||
|
pubkey, err := keys.ExtractKey(cert)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrap(err, "error extracting public key from ssh public key interface")
|
||||||
|
}
|
||||||
|
if err = keys.VerifyPair(pubkey, key); err != nil {
|
||||||
|
return "", errors.Wrap(err, "error verifying ssh key pair")
|
||||||
|
}
|
||||||
|
|
||||||
|
return base64.StdEncoding.EncodeToString(cert.Marshal()), nil
|
||||||
|
}
|
||||||
|
|
||||||
// ValidateX5C validates the given x5c certificate chain and key for use in an
|
// ValidateX5C validates the given x5c certificate chain and key for use in an
|
||||||
// x5c header.
|
// x5c header.
|
||||||
func ValidateX5C(certFile string, key interface{}) ([]string, error) {
|
func ValidateX5C(certFile string, key interface{}) ([]string, error) {
|
||||||
@@ -24,7 +55,7 @@ func ValidateX5C(certFile string, key interface{}) ([]string, error) {
|
|||||||
return nil, errors.Wrap(err, "error reading x5c certificate chain from file")
|
return nil, errors.Wrap(err, "error reading x5c certificate chain from file")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = x509util.VerifyCertKey(certs[0], key); err != nil {
|
if err = keys.VerifyPair(certs[0].PublicKey, key); err != nil {
|
||||||
return nil, errors.Wrap(err, "error verifying x5c certificate and key")
|
return nil, errors.Wrap(err, "error verifying x5c certificate and key")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -169,3 +169,15 @@ func WithX5CFile(certFile string, key interface{}) Options {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithSSHPOPFile returns a Options that sets the header sshpop claims.
|
||||||
|
func WithSSHPOPFile(certFile string, key interface{}) Options {
|
||||||
|
return func(c *Claims) error {
|
||||||
|
certStrs, err := jose.ValidateSSHPOP(certFile, key)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "error validating SSH certificate and key for use in sshpop header")
|
||||||
|
}
|
||||||
|
c.SetHeader("sshpop", certStrs)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -62,7 +62,7 @@ func NewCertificateFlow(ctx *cli.Context) (*CertificateFlow, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetClient returns the client used to send requests to the CA.
|
// GetClient returns the client used to send requests to the CA.
|
||||||
func (f *CertificateFlow) GetClient(ctx *cli.Context, subject, tok string) (CaClient, error) {
|
func (f *CertificateFlow) GetClient(ctx *cli.Context, tok string) (CaClient, error) {
|
||||||
if f.offline {
|
if f.offline {
|
||||||
return f.offlineCA, nil
|
return f.offlineCA, nil
|
||||||
}
|
}
|
||||||
@@ -134,17 +134,7 @@ func (f *CertificateFlow) GenerateToken(ctx *cli.Context, subject string, sans [
|
|||||||
|
|
||||||
// GenerateSSHToken generates a token used to authorize the sign of an SSH
|
// GenerateSSHToken generates a token used to authorize the sign of an SSH
|
||||||
// certificate.
|
// certificate.
|
||||||
func (f *CertificateFlow) GenerateSSHToken(ctx *cli.Context, subject, certType string, principals []string, validAfter, validBefore provisioner.TimeDuration) (string, error) {
|
func (f *CertificateFlow) GenerateSSHToken(ctx *cli.Context, subject string, typ int, principals []string, validAfter, validBefore provisioner.TimeDuration) (string, error) {
|
||||||
var typ int
|
|
||||||
switch certType {
|
|
||||||
case provisioner.SSHUserCert:
|
|
||||||
typ = SSHUserSignType
|
|
||||||
case provisioner.SSHHostCert:
|
|
||||||
typ = SSHHostSignType
|
|
||||||
default:
|
|
||||||
return "", errors.Errorf("unsupported cert type %s", certType)
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.offline {
|
if f.offline {
|
||||||
return f.offlineCA.GenerateToken(ctx, typ, subject, principals, time.Time{}, time.Time{}, validAfter, validBefore)
|
return f.offlineCA.GenerateToken(ctx, typ, subject, principals, time.Time{}, time.Time{}, validAfter, validBefore)
|
||||||
}
|
}
|
||||||
@@ -176,7 +166,7 @@ func (f *CertificateFlow) GenerateSSHToken(ctx *cli.Context, subject, certType s
|
|||||||
|
|
||||||
// Sign signs the CSR using the online or the offline certificate authority.
|
// Sign signs the CSR using the online or the offline certificate authority.
|
||||||
func (f *CertificateFlow) Sign(ctx *cli.Context, token string, csr api.CertificateRequest, crtFile string) error {
|
func (f *CertificateFlow) Sign(ctx *cli.Context, token string, csr api.CertificateRequest, crtFile string) error {
|
||||||
client, err := f.GetClient(ctx, csr.Subject.CommonName, token)
|
client, err := f.GetClient(ctx, token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@@ -18,10 +18,14 @@ type CaClient interface {
|
|||||||
Renew(tr http.RoundTripper) (*api.SignResponse, error)
|
Renew(tr http.RoundTripper) (*api.SignResponse, error)
|
||||||
Revoke(req *api.RevokeRequest, tr http.RoundTripper) (*api.RevokeResponse, error)
|
Revoke(req *api.RevokeRequest, tr http.RoundTripper) (*api.RevokeResponse, error)
|
||||||
SSHSign(req *api.SSHSignRequest) (*api.SSHSignResponse, error)
|
SSHSign(req *api.SSHSignRequest) (*api.SSHSignResponse, error)
|
||||||
|
SSHRenew(req *api.SSHRenewRequest) (*api.SSHRenewResponse, error)
|
||||||
|
SSHRekey(req *api.SSHRekeyRequest) (*api.SSHRekeyResponse, error)
|
||||||
|
SSHRevoke(req *api.SSHRevokeRequest) (*api.SSHRevokeResponse, error)
|
||||||
SSHRoots() (*api.SSHRootsResponse, error)
|
SSHRoots() (*api.SSHRootsResponse, error)
|
||||||
SSHFederation() (*api.SSHRootsResponse, error)
|
SSHFederation() (*api.SSHRootsResponse, error)
|
||||||
SSHConfig(req *api.SSHConfigRequest) (*api.SSHConfigResponse, error)
|
SSHConfig(req *api.SSHConfigRequest) (*api.SSHConfigResponse, error)
|
||||||
SSHCheckHost(principal string) (*api.SSHCheckPrincipalResponse, error)
|
SSHCheckHost(principal string) (*api.SSHCheckPrincipalResponse, error)
|
||||||
|
SSHGetHosts() (*api.SSHGetHostsResponse, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClient returns a client of an online or offline CA. Requires the flags
|
// NewClient returns a client of an online or offline CA. Requires the flags
|
||||||
|
@@ -108,6 +108,14 @@ func (c *OfflineCA) Audience(tokType int) string {
|
|||||||
switch tokType {
|
switch tokType {
|
||||||
case RevokeType:
|
case RevokeType:
|
||||||
return fmt.Sprintf("https://%s/revoke", c.config.DNSNames[0])
|
return fmt.Sprintf("https://%s/revoke", c.config.DNSNames[0])
|
||||||
|
case SSHUserSignType, SSHHostSignType:
|
||||||
|
return fmt.Sprintf("https://%s/ssh/sign", c.config.DNSNames[0])
|
||||||
|
case SSHRenewType:
|
||||||
|
return fmt.Sprintf("https://%s/ssh/renew", c.config.DNSNames[0])
|
||||||
|
case SSHRevokeType:
|
||||||
|
return fmt.Sprintf("https://%s/ssh/revoke", c.config.DNSNames[0])
|
||||||
|
case SSHRekeyType:
|
||||||
|
return fmt.Sprintf("https://%s/ssh/rekey", c.config.DNSNames[0])
|
||||||
default:
|
default:
|
||||||
return fmt.Sprintf("https://%s/sign", c.config.DNSNames[0])
|
return fmt.Sprintf("https://%s/sign", c.config.DNSNames[0])
|
||||||
}
|
}
|
||||||
@@ -204,11 +212,15 @@ func (c *OfflineCA) Revoke(req *api.RevokeRequest, rt http.RoundTripper) (*api.R
|
|||||||
ReasonCode: req.ReasonCode,
|
ReasonCode: req.ReasonCode,
|
||||||
PassiveOnly: req.Passive,
|
PassiveOnly: req.Passive,
|
||||||
}
|
}
|
||||||
|
ctx = provisioner.NewContextWithMethod(context.Background(), provisioner.RevokeMethod)
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
if len(req.OTT) > 0 {
|
if len(req.OTT) > 0 {
|
||||||
opts.OTT = req.OTT
|
opts.OTT = req.OTT
|
||||||
opts.MTLS = false
|
opts.MTLS = false
|
||||||
|
if _, err = c.authority.Authorize(ctx, opts.OTT); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// it should not panic as this is always internal code
|
// it should not panic as this is always internal code
|
||||||
tr := rt.(*http.Transport)
|
tr := rt.(*http.Transport)
|
||||||
@@ -221,7 +233,7 @@ func (c *OfflineCA) Revoke(req *api.RevokeRequest, rt http.RoundTripper) (*api.R
|
|||||||
}
|
}
|
||||||
|
|
||||||
// revoke cert using authority
|
// revoke cert using authority
|
||||||
if err := c.authority.Revoke(&opts); err != nil {
|
if err := c.authority.Revoke(ctx, &opts); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,6 +269,74 @@ func (c *OfflineCA) SSHSign(req *api.SSHSignRequest) (*api.SSHSignResponse, erro
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SSHRevoke is a wrapper on top of certificates SSHRevoke method. It returns an
|
||||||
|
// api.SSHRevokeResponse.
|
||||||
|
func (c *OfflineCA) SSHRevoke(req *api.SSHRevokeRequest) (*api.SSHRevokeResponse, error) {
|
||||||
|
opts := authority.RevokeOptions{
|
||||||
|
Serial: req.Serial,
|
||||||
|
Reason: req.Reason,
|
||||||
|
ReasonCode: req.ReasonCode,
|
||||||
|
PassiveOnly: req.Passive,
|
||||||
|
OTT: req.OTT,
|
||||||
|
MTLS: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := provisioner.NewContextWithMethod(context.Background(), provisioner.RevokeSSHMethod)
|
||||||
|
if _, err := c.authority.Authorize(ctx, opts.OTT); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := c.authority.Revoke(ctx, &opts); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &api.SSHRevokeResponse{Status: "ok"}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SSHRenew is a wrapper on top of certificates SSHRenew method. It returns an
|
||||||
|
// api.SSHRenewResponse.
|
||||||
|
func (c *OfflineCA) SSHRenew(req *api.SSHRenewRequest) (*api.SSHRenewResponse, error) {
|
||||||
|
ctx := provisioner.NewContextWithMethod(context.Background(), provisioner.RenewSSHMethod)
|
||||||
|
_, err := c.authority.Authorize(ctx, req.OTT)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
oldCert, err := provisioner.ExtractSSHPOPCert(req.OTT)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cert, err := c.authority.RenewSSH(oldCert)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &api.SSHRenewResponse{Certificate: api.SSHCertificate{Certificate: cert}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SSHRekey is a wrapper on top of certificates SSHRekey method. It returns an
|
||||||
|
// api.SSHRekeyResponse.
|
||||||
|
func (c *OfflineCA) SSHRekey(req *api.SSHRekeyRequest) (*api.SSHRekeyResponse, error) {
|
||||||
|
ctx := provisioner.NewContextWithMethod(context.Background(), provisioner.RekeySSHMethod)
|
||||||
|
signOpts, err := c.authority.Authorize(ctx, req.OTT)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
oldCert, err := provisioner.ExtractSSHPOPCert(req.OTT)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sshPub, err := ssh.ParsePublicKey(req.PublicKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cert, err := c.authority.RekeySSH(oldCert, sshPub, signOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &api.SSHRekeyResponse{Certificate: api.SSHCertificate{Certificate: cert}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// SSHRoots is a wrapper on top of the GetSSHRoots method. It returns an
|
// SSHRoots is a wrapper on top of the GetSSHRoots method. It returns an
|
||||||
// api.SSHRootsResponse.
|
// api.SSHRootsResponse.
|
||||||
func (c *OfflineCA) SSHRoots() (*api.SSHRootsResponse, error) {
|
func (c *OfflineCA) SSHRoots() (*api.SSHRootsResponse, error) {
|
||||||
@@ -328,6 +408,18 @@ func (c *OfflineCA) SSHCheckHost(principal string) (*api.SSHCheckPrincipalRespon
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SSHGetHosts is a wrapper on top of the CheckSSHHost method. It returns an
|
||||||
|
// api.SSHCheckPrincipalResponse.
|
||||||
|
func (c *OfflineCA) SSHGetHosts() (*api.SSHGetHostsResponse, error) {
|
||||||
|
hosts, err := c.authority.GetSSHHosts()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &api.SSHGetHostsResponse{
|
||||||
|
Hosts: hosts,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GenerateToken creates the token used by the authority to authorize requests.
|
// GenerateToken creates the token used by the authority to authorize requests.
|
||||||
func (c *OfflineCA) GenerateToken(ctx *cli.Context, tokType int, subject string, sans []string, notBefore, notAfter time.Time, certNotBefore, certNotAfter provisioner.TimeDuration) (string, error) {
|
func (c *OfflineCA) GenerateToken(ctx *cli.Context, tokType int, subject string, sans []string, notBefore, notAfter time.Time, certNotBefore, certNotAfter provisioner.TimeDuration) (string, error) {
|
||||||
// Use ca.json configuration for the root and audience
|
// Use ca.json configuration for the root and audience
|
||||||
@@ -358,6 +450,10 @@ func (c *OfflineCA) GenerateToken(ctx *cli.Context, tokType int, subject string,
|
|||||||
return generateOIDCToken(ctx, p)
|
return generateOIDCToken(ctx, p)
|
||||||
case *provisioner.X5C: // Get a JWT with an X5C header and signature.
|
case *provisioner.X5C: // Get a JWT with an X5C header and signature.
|
||||||
return generateX5CToken(ctx, p, tokType, tokAttrs)
|
return generateX5CToken(ctx, p, tokType, tokAttrs)
|
||||||
|
case *provisioner.SSHPOP: // Generate an SSHPOP token using ssh cert + key.
|
||||||
|
return generateSSHPOPToken(ctx, p, tokType, tokAttrs)
|
||||||
|
case *provisioner.K8sSA: // Get the Kubernetes service account token.
|
||||||
|
return generateK8sSAToken(ctx, p)
|
||||||
case *provisioner.GCP: // Do the identity request to get the token.
|
case *provisioner.GCP: // Do the identity request to get the token.
|
||||||
sharedContext.DisableCustomSANs = p.DisableCustomSANs
|
sharedContext.DisableCustomSANs = p.DisableCustomSANs
|
||||||
return p.GetIdentityToken(subject, c.CaURL())
|
return p.GetIdentityToken(subject, c.CaURL())
|
||||||
|
@@ -26,6 +26,9 @@ const (
|
|||||||
RevokeType
|
RevokeType
|
||||||
SSHUserSignType
|
SSHUserSignType
|
||||||
SSHHostSignType
|
SSHHostSignType
|
||||||
|
SSHRevokeType
|
||||||
|
SSHRenewType
|
||||||
|
SSHRekeyType
|
||||||
)
|
)
|
||||||
|
|
||||||
// parseAudience creates the ca audience url from the ca-url
|
// parseAudience creates the ca audience url from the ca-url
|
||||||
@@ -49,6 +52,12 @@ func parseAudience(ctx *cli.Context, tokType int) (string, error) {
|
|||||||
// revocation token
|
// revocation token
|
||||||
case RevokeType:
|
case RevokeType:
|
||||||
path = "/1.0/revoke"
|
path = "/1.0/revoke"
|
||||||
|
case SSHRevokeType:
|
||||||
|
path = "/1.0/ssh/revoke"
|
||||||
|
case SSHRenewType:
|
||||||
|
path = "/1.0/ssh/renew"
|
||||||
|
case SSHRekeyType:
|
||||||
|
path = "/1.0/ssh/rekey"
|
||||||
default:
|
default:
|
||||||
return "", errors.Errorf("unexpected token type: %d", tokType)
|
return "", errors.Errorf("unexpected token type: %d", tokType)
|
||||||
}
|
}
|
||||||
@@ -105,6 +114,10 @@ func NewTokenFlow(ctx *cli.Context, tokType int, subject string, sans []string,
|
|||||||
return generateOIDCToken(ctx, p)
|
return generateOIDCToken(ctx, p)
|
||||||
case *provisioner.X5C: // Get a JWT with an X5C header and signature.
|
case *provisioner.X5C: // Get a JWT with an X5C header and signature.
|
||||||
return generateX5CToken(ctx, p, tokType, tokAttrs)
|
return generateX5CToken(ctx, p, tokType, tokAttrs)
|
||||||
|
case *provisioner.SSHPOP: // Generate a SSHPOP token using an ssh cert + key.
|
||||||
|
return generateSSHPOPToken(ctx, p, tokType, tokAttrs)
|
||||||
|
case *provisioner.K8sSA: // Get the Kubernetes service account token.
|
||||||
|
return generateK8sSAToken(ctx, p)
|
||||||
case *provisioner.GCP: // Do the identity request to get the token.
|
case *provisioner.GCP: // Do the identity request to get the token.
|
||||||
sharedContext.DisableCustomSANs = p.DisableCustomSANs
|
sharedContext.DisableCustomSANs = p.DisableCustomSANs
|
||||||
return p.GetIdentityToken(subject, caURL)
|
return p.GetIdentityToken(subject, caURL)
|
||||||
@@ -116,8 +129,6 @@ func NewTokenFlow(ctx *cli.Context, tokType int, subject string, sans []string,
|
|||||||
return p.GetIdentityToken(subject, caURL)
|
return p.GetIdentityToken(subject, caURL)
|
||||||
case *provisioner.ACME: // Return an error with the provisioner ID.
|
case *provisioner.ACME: // Return an error with the provisioner ID.
|
||||||
return "", &ErrACMEToken{p.GetName()}
|
return "", &ErrACMEToken{p.GetName()}
|
||||||
case *provisioner.K8sSA: // Ge the Kubernetes service account token.
|
|
||||||
return generateK8sSAToken(ctx, p)
|
|
||||||
default: // Default is assumed to be a standard JWT.
|
default: // Default is assumed to be a standard JWT.
|
||||||
jwkP, ok := p.(*provisioner.JWK)
|
jwkP, ok := p.(*provisioner.JWK)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -198,17 +209,24 @@ func allowX5CProvisionerFilter(p provisioner.Interface) bool {
|
|||||||
return p.GetType() == provisioner.TypeX5C
|
return p.GetType() == provisioner.TypeX5C
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func allowSSHPOPProvisionerFilter(p provisioner.Interface) bool {
|
||||||
|
return p.GetType() == provisioner.TypeSSHPOP
|
||||||
|
}
|
||||||
|
|
||||||
func provisionerPrompt(ctx *cli.Context, provisioners provisioner.List) (provisioner.Interface, error) {
|
func provisionerPrompt(ctx *cli.Context, provisioners provisioner.List) (provisioner.Interface, error) {
|
||||||
switch {
|
switch {
|
||||||
// If x5c flags then only list x5c provisioners.
|
// If x5c flags then only list x5c provisioners.
|
||||||
case ctx.IsSet("x5c-cert") || ctx.IsSet("x5c-key"):
|
case ctx.IsSet("x5c-cert") || ctx.IsSet("x5c-key"):
|
||||||
provisioners = provisionerFilter(provisioners, allowX5CProvisionerFilter)
|
provisioners = provisionerFilter(provisioners, allowX5CProvisionerFilter)
|
||||||
|
// If sshpop flags then only list sshpop provisioners.
|
||||||
|
case ctx.IsSet("sshpop-cert") || ctx.IsSet("sshpop-key"):
|
||||||
|
provisioners = provisionerFilter(provisioners, allowSSHPOPProvisionerFilter)
|
||||||
// List all available provisioners.
|
// List all available provisioners.
|
||||||
default:
|
default:
|
||||||
provisioners = provisionerFilter(provisioners, func(p provisioner.Interface) bool {
|
provisioners = provisionerFilter(provisioners, func(p provisioner.Interface) bool {
|
||||||
switch p.GetType() {
|
switch p.GetType() {
|
||||||
case provisioner.TypeJWK, provisioner.TypeX5C, provisioner.TypeOIDC,
|
case provisioner.TypeJWK, provisioner.TypeX5C, provisioner.TypeOIDC,
|
||||||
provisioner.TypeACME, provisioner.TypeK8sSA:
|
provisioner.TypeACME, provisioner.TypeK8sSA, provisioner.TypeSSHPOP:
|
||||||
return true
|
return true
|
||||||
case provisioner.TypeGCP, provisioner.TypeAWS, provisioner.TypeAzure:
|
case provisioner.TypeGCP, provisioner.TypeAWS, provisioner.TypeAzure:
|
||||||
return true
|
return true
|
||||||
@@ -263,29 +281,14 @@ func provisionerPrompt(ctx *cli.Context, provisioners provisioner.List) (provisi
|
|||||||
Name: fmt.Sprintf("%s (%s) [client: %s]", p.Name, p.GetType(), p.ClientID),
|
Name: fmt.Sprintf("%s (%s) [client: %s]", p.Name, p.GetType(), p.ClientID),
|
||||||
Provisioner: p,
|
Provisioner: p,
|
||||||
})
|
})
|
||||||
case *provisioner.GCP:
|
|
||||||
items = append(items, &provisionersSelect{
|
|
||||||
Name: fmt.Sprintf("%s (%s)", p.Name, p.GetType()),
|
|
||||||
Provisioner: p,
|
|
||||||
})
|
|
||||||
case *provisioner.AWS:
|
|
||||||
items = append(items, &provisionersSelect{
|
|
||||||
Name: fmt.Sprintf("%s (%s)", p.Name, p.GetType()),
|
|
||||||
Provisioner: p,
|
|
||||||
})
|
|
||||||
case *provisioner.Azure:
|
case *provisioner.Azure:
|
||||||
items = append(items, &provisionersSelect{
|
items = append(items, &provisionersSelect{
|
||||||
Name: fmt.Sprintf("%s (%s) [tenant: %s]", p.Name, p.GetType(), p.TenantID),
|
Name: fmt.Sprintf("%s (%s) [tenant: %s]", p.Name, p.GetType(), p.TenantID),
|
||||||
Provisioner: p,
|
Provisioner: p,
|
||||||
})
|
})
|
||||||
case *provisioner.ACME:
|
case *provisioner.GCP, *provisioner.AWS, *provisioner.X5C, *provisioner.SSHPOP, *provisioner.ACME:
|
||||||
items = append(items, &provisionersSelect{
|
items = append(items, &provisionersSelect{
|
||||||
Name: fmt.Sprintf("%s (%s)", p.Name, p.GetType()),
|
Name: fmt.Sprintf("%s (%s)", p.GetName(), p.GetType()),
|
||||||
Provisioner: p,
|
|
||||||
})
|
|
||||||
case *provisioner.X5C:
|
|
||||||
items = append(items, &provisionersSelect{
|
|
||||||
Name: fmt.Sprintf("%s (%s)", p.Name, p.GetType()),
|
|
||||||
Provisioner: p,
|
Provisioner: p,
|
||||||
})
|
})
|
||||||
case *provisioner.K8sSA:
|
case *provisioner.K8sSA:
|
||||||
|
@@ -192,6 +192,40 @@ func generateX5CToken(ctx *cli.Context, p *provisioner.X5C, tokType int, tokAttr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func generateSSHPOPToken(ctx *cli.Context, p *provisioner.SSHPOP, tokType int, tokAttrs tokenAttrs) (string, error) {
|
||||||
|
sshPOPCertFile := ctx.String("sshpop-cert")
|
||||||
|
sshPOPKeyFile := ctx.String("sshpop-key")
|
||||||
|
if len(sshPOPCertFile) == 0 {
|
||||||
|
return "", errs.RequiredWithProvisionerTypeFlag(ctx, "SSHPOP", "sshpop-cert")
|
||||||
|
}
|
||||||
|
if len(sshPOPKeyFile) == 0 {
|
||||||
|
return "", errs.RequiredWithProvisionerTypeFlag(ctx, "SSHPOP", "sshpop-key")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get private key from given key file
|
||||||
|
var opts []jose.Option
|
||||||
|
if passwordFile := ctx.String("password-file"); len(passwordFile) != 0 {
|
||||||
|
opts = append(opts, jose.WithPasswordFile(passwordFile))
|
||||||
|
}
|
||||||
|
jwk, err := jose.ParseKey(sshPOPKeyFile, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
tokenGen := NewTokenGenerator(jwk.KeyID, p.Name,
|
||||||
|
fmt.Sprintf("%s#%s", tokAttrs.audience, p.GetID()), tokAttrs.root,
|
||||||
|
tokAttrs.notBefore, tokAttrs.notAfter, jwk)
|
||||||
|
switch tokType {
|
||||||
|
case SSHRevokeType:
|
||||||
|
return tokenGen.Token(tokAttrs.subject, token.WithSSHPOPFile(sshPOPCertFile, jwk.Key))
|
||||||
|
case SSHRenewType:
|
||||||
|
return tokenGen.Token(tokAttrs.subject, token.WithSSHPOPFile(sshPOPCertFile, jwk.Key))
|
||||||
|
case SSHRekeyType:
|
||||||
|
return tokenGen.Token(tokAttrs.subject, token.WithSSHPOPFile(sshPOPCertFile, jwk.Key))
|
||||||
|
default:
|
||||||
|
return "", errors.Errorf("unexpected requested token type for SSHPOP token: %d", tokType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// loadJWK loads a JWK based on the following system:
|
// loadJWK loads a JWK based on the following system:
|
||||||
// 1. If a private key is specified on the command line, then load the JWK from
|
// 1. If a private key is specified on the command line, then load the JWK from
|
||||||
// that private key.
|
// that private key.
|
||||||
|
Reference in New Issue
Block a user