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"
|
|
|
|
"io/ioutil"
|
2020-02-01 11:00:39 +00:00
|
|
|
"net"
|
2019-09-12 19:51:07 +00:00
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"unicode"
|
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/smallstep/certificates/authority"
|
|
|
|
"github.com/smallstep/certificates/ca"
|
|
|
|
"github.com/urfave/cli"
|
2020-10-29 20:10:03 +00:00
|
|
|
"go.step.sm/cli-utils/errs"
|
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,
|
2019-09-27 00:02:49 +00:00
|
|
|
UsageText: `**step-ca** <config>
|
2020-02-01 11:00:39 +00:00
|
|
|
[**--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.`,
|
|
|
|
},
|
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
|
|
|
},
|
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")
|
2020-02-01 11:00:39 +00:00
|
|
|
resolver := ctx.String("resolver")
|
2019-09-12 19:51:07 +00:00
|
|
|
|
|
|
|
// If zero cmd line args show help, if >1 cmd line args show error.
|
|
|
|
if ctx.NArg() == 0 {
|
|
|
|
return cli.ShowAppHelp(ctx)
|
|
|
|
}
|
|
|
|
if err := errs.NumberOfArguments(ctx, 1); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
configFile := ctx.Args().Get(0)
|
|
|
|
config, err := authority.LoadConfiguration(configFile)
|
|
|
|
if err != nil {
|
|
|
|
fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var password []byte
|
|
|
|
if passFile != "" {
|
|
|
|
if password, err = ioutil.ReadFile(passFile); err != nil {
|
|
|
|
fatal(errors.Wrapf(err, "error reading %s", passFile))
|
|
|
|
}
|
|
|
|
password = bytes.TrimRightFunc(password, 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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-12 19:51:07 +00:00
|
|
|
srv, err := ca.New(config, ca.WithConfigFile(configFile), ca.WithPassword(password))
|
|
|
|
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)
|
|
|
|
}
|