1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
|
package ssh
import (
"io/ioutil"
"strconv"
"github.com/smallstep/certificates/ca/identity"
"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>
[**--out**=<file>] [**--issuer**=<name>] [**--password-file**=<path>]
[**--force**] [**--ca-url**=<uri>] [**--root**=<path>]
[**--offline**] [**--ca-config**=<path>]`,
Description: `**step ssh renew** command renews an SSH Cerfificate
using [step certificates](https://github.com/smallstep/certificates).
It writes the new certificate to disk - either overwriting <ssh-cert> or
using a new file when the **--out**=<file> flag is used.
## POSITIONAL ARGUMENTS
<ssh-cert>
: The ssh certificate to renew.
<ssh-key>
: The ssh certificate private key.
## EXAMPLES
Renew an ssh certificate overwriting the previous one:
'''
$ step ssh renew -f id_ecdsa-cert.pub id_ecdsa
'''
Renew an ssh certificate with a custom out file:
'''
$ step ssh renew -out new-id_ecdsa-cer.pub id_ecdsa-cert.pub id_ecdsa
'''`,
Flags: []cli.Flag{
cli.StringFlag{
Name: "out,output-file",
Usage: "The new certificate <file> path. Defaults to overwriting the <ssh-cert> positional argument",
},
flags.Provisioner,
sshProvisionerPasswordFlag,
flags.Force,
flags.CaURL,
flags.Root,
flags.Offline,
flags.CaConfig,
flags.SSHPOPCert,
flags.SSHPOPKey,
},
}
}
func renewAction(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)
// Flags
outFile := ctx.String("out")
if outFile == "" {
outFile = certFile
}
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(outFile, marshalPublicKey(resp.Certificate, cert.KeyId), 0644); err != nil {
return err
}
// Write renewed identity
if len(resp.IdentityCertificate) > 0 {
if err := identity.WriteIdentityCertificate(resp.IdentityCertificate); err != nil {
return err
}
}
ui.PrintSelected("Certificate", outFile)
return nil
}
|