certificates/commands/app.go

181 lines
5.1 KiB
Go
Raw Permalink Normal View History

2019-09-12 19:51:07 +00:00
package commands
import (
"bytes"
2020-02-01 11:00:39 +00:00
"context"
2019-09-12 19:51:07 +00:00
"fmt"
2020-02-01 11:00:39 +00:00
"net"
2019-09-12 19:51:07 +00:00
"net/http"
"os"
"strings"
2019-09-12 19:51:07 +00:00
"unicode"
"github.com/pkg/errors"
2021-05-03 19:48:20 +00:00
"github.com/smallstep/certificates/authority/config"
2019-09-12 19:51:07 +00:00
"github.com/smallstep/certificates/ca"
"github.com/smallstep/certificates/pki"
2019-09-12 19:51:07 +00:00
"github.com/urfave/cli"
"go.step.sm/cli-utils/errs"
"go.step.sm/cli-utils/step"
2019-09-12 19:51:07 +00:00
)
// AppCommand is the action used as the top action.
var AppCommand = cli.Command{
Name: "start",
Action: appAction,
2021-09-16 19:05:23 +00:00
UsageText: `**step-ca** <config> [**--password-file**=<file>]
[**--ssh-host-password-file**=<file>] [**--ssh-user-password-file**=<file>]
2021-09-16 19:05:23 +00:00
[**--issuer-password-file**=<file>] [**--resolver**=<addr>]`,
2019-09-12 19:51:07 +00:00
Flags: []cli.Flag{
cli.StringFlag{
Name: "password-file",
Usage: `path to the <file> containing the password to decrypt the
intermediate private key.`,
},
cli.StringFlag{
Name: "ssh-host-password-file",
Usage: `path to the <file> containing the password to decrypt the
private key used to sign SSH host certificates. If the flag is not passed it
will default to --password-file.`,
},
cli.StringFlag{
Name: "ssh-user-password-file",
Usage: `path to the <file> containing the password to decrypt the
private key used to sign SSH user certificates. If the flag is not passed it
will default to --password-file.`,
},
cli.StringFlag{
Name: "issuer-password-file",
Usage: `path to the <file> containing the password to decrypt the
certificate issuer private key used in the RA mode.`,
2019-09-12 19:51:07 +00:00
},
2020-02-01 11:00:39 +00:00
cli.StringFlag{
2020-02-12 20:45:07 +00:00
Name: "resolver",
Usage: "address of a DNS resolver to be used instead of the default.",
2020-02-01 11:00:39 +00:00
},
cli.StringFlag{
Name: "token",
Usage: "token used to enable the linked ca.",
EnvVar: "STEP_CA_TOKEN",
},
cli.StringFlag{
Name: "context",
Usage: "The name of the authority's context.",
EnvVar: "STEP_CA_CONTEXT",
},
2019-09-12 19:51:07 +00:00
},
}
// AppAction is the action used when the top command runs.
func appAction(ctx *cli.Context) error {
passFile := ctx.String("password-file")
sshHostPassFile := ctx.String("ssh-host-password-file")
sshUserPassFile := ctx.String("ssh-user-password-file")
issuerPassFile := ctx.String("issuer-password-file")
2020-02-01 11:00:39 +00:00
resolver := ctx.String("resolver")
token := ctx.String("token")
2019-09-12 19:51:07 +00:00
if ctx.NArg() > 1 {
return errs.TooManyArguments(ctx)
2019-09-12 19:51:07 +00:00
}
if caCtx := ctx.String("context"); caCtx != "" {
if err := step.Contexts().SetCurrent(caCtx); err != nil {
return err
}
}
var configFile string
if ctx.NArg() > 0 {
configFile = ctx.Args().Get(0)
} else {
configFile = step.CaConfigFile()
2019-09-12 19:51:07 +00:00
}
cfg, err := config.LoadConfiguration(configFile)
2019-09-12 19:51:07 +00:00
if err != nil {
fatal(err)
}
if cfg.AuthorityConfig != nil {
if token == "" && strings.EqualFold(cfg.AuthorityConfig.DeploymentType, pki.LinkedDeployment.String()) {
return errors.New(`'step-ca' requires the '--token' flag for linked deploy type.
To get a linked authority token:
1. Log in or create a Certificate Manager account at ` + "\033[1mhttps://u.step.sm/linked\033[0m" + `
2021-08-13 18:59:12 +00:00
2. Add a new authority and select "Link a step-ca instance"
3. Follow instructions in browser to start 'step-ca' using the '--token' flag
`)
}
}
2019-09-12 19:51:07 +00:00
var password []byte
if passFile != "" {
if password, err = os.ReadFile(passFile); err != nil {
2019-09-12 19:51:07 +00:00
fatal(errors.Wrapf(err, "error reading %s", passFile))
}
password = bytes.TrimRightFunc(password, unicode.IsSpace)
}
var sshHostPassword []byte
if sshHostPassFile != "" {
if sshHostPassword, err = os.ReadFile(sshHostPassFile); err != nil {
fatal(errors.Wrapf(err, "error reading %s", sshHostPassFile))
}
sshHostPassword = bytes.TrimRightFunc(sshHostPassword, unicode.IsSpace)
}
var sshUserPassword []byte
if sshUserPassFile != "" {
if sshUserPassword, err = os.ReadFile(sshUserPassFile); err != nil {
fatal(errors.Wrapf(err, "error reading %s", sshUserPassFile))
}
sshUserPassword = bytes.TrimRightFunc(sshUserPassword, unicode.IsSpace)
}
var issuerPassword []byte
if issuerPassFile != "" {
if issuerPassword, err = os.ReadFile(issuerPassFile); err != nil {
fatal(errors.Wrapf(err, "error reading %s", issuerPassFile))
}
issuerPassword = bytes.TrimRightFunc(issuerPassword, unicode.IsSpace)
}
2020-02-01 11:00:39 +00:00
// replace resolver if requested
if resolver != "" {
net.DefaultResolver.PreferGo = true
net.DefaultResolver.Dial = func(ctx context.Context, network, address string) (net.Conn, error) {
return net.Dial(network, resolver)
}
}
srv, err := ca.New(cfg,
ca.WithConfigFile(configFile),
ca.WithPassword(password),
ca.WithSSHHostPassword(sshHostPassword),
ca.WithSSHUserPassword(sshUserPassword),
ca.WithIssuerPassword(issuerPassword),
ca.WithLinkedCAToken(token))
2019-09-12 19:51:07 +00:00
if err != nil {
fatal(err)
}
go ca.StopReloaderHandler(srv)
if err = srv.Run(); err != nil && err != http.ErrServerClosed {
fatal(err)
}
return nil
}
// fatal writes the passed error on the standard error and exits with the exit
// code 1. If the environment variable STEPDEBUG is set to 1 it shows the
// stack trace of the error.
func fatal(err error) {
if os.Getenv("STEPDEBUG") == "1" {
fmt.Fprintf(os.Stderr, "%+v\n", err)
} else {
fmt.Fprintln(os.Stderr, err)
}
os.Exit(2)
}