Skip to content

Commit

Permalink
setup --no-default should error if given incompatible config
Browse files Browse the repository at this point in the history
  • Loading branch information
yzp0n committed Apr 25, 2020
1 parent 74798d5 commit 9aa204f
Show file tree
Hide file tree
Showing 14 changed files with 288 additions and 113 deletions.
12 changes: 6 additions & 6 deletions action/issue/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@ import (

"github.com/IPA-CyberLab/kmgm/dname"
"github.com/IPA-CyberLab/kmgm/keyusage"
"github.com/IPA-CyberLab/kmgm/san"
"github.com/IPA-CyberLab/kmgm/period"
"github.com/IPA-CyberLab/kmgm/san"
"github.com/IPA-CyberLab/kmgm/wcrypto"
)

type Config struct {
Subject *dname.Config `yaml:"subject" flags:""`
Names san.Names `yaml:"subjectAltNames" flags:"subject-alt-name,set cert subjectAltNames,san"`
KeyUsage keyusage.KeyUsage `yaml:"keyUsage" flags:"key-usage,what the key/cert is used for (tlsServer, tlsClient, tlsClientServer),ku"`
Subject *dname.Config `yaml:"subject" flags:""`
Names san.Names `yaml:"subjectAltNames" flags:"subject-alt-name,set cert subjectAltNames,san"`
KeyUsage keyusage.KeyUsage `yaml:"keyUsage" flags:"key-usage,what the key/cert is used for (tlsServer, tlsClient, tlsClientServer),ku"`
Validity period.ValidityPeriod `yaml:"validity" flags:"validity,time duration/timestamp where the cert is valid to (examples: 30d, 1y, 20220530)"`
KeyType wcrypto.KeyType `yaml:"keyType" flags:"key-type,private key type (rsa, ecdsa),t"`
KeyType wcrypto.KeyType `yaml:"keyType" flags:"key-type,private key type (rsa, ecdsa),t"`

// Don't create issuedb entry.
NoIssueDBEntry bool
Expand Down Expand Up @@ -69,7 +69,7 @@ func (a *Config) CompatibleWith(b *Config) error {
return err
}
if !a.KeyUsage.Equals(b.KeyUsage) {
return fmt.Errorf("KeyUsage mismatch")
return fmt.Errorf("KeyUsage mismatch: %v != %v", a.KeyUsage, b.KeyUsage)
}
if a.KeyType != b.KeyType {
return fmt.Errorf("KeyType mismatch: %v != %v", a.KeyType, b.KeyType)
Expand Down
19 changes: 10 additions & 9 deletions action/serve/authprofile/authprofile.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ func Ensure(env *action.Environment) (*storage.Profile, error) {
return nil, err
}

if st := profile.Status(env.NowImpl()); st != nil {
if st.Code != storage.NotCA {
return nil, st
}

st := profile.Status(env.NowImpl())
switch st.Code {
case storage.ValidCA:
return profile, nil
case storage.NotCA:
slog.Infof("Setting up CA for kmgm HTTPS/gRPC server.")
start := time.Now()
defer func() {
Expand All @@ -51,10 +51,11 @@ func Ensure(env *action.Environment) (*storage.Profile, error) {
return nil, fmt.Errorf("Failed to setup serverauth CA: %v", err)
}

if st := profile.Status(env.NowImpl()); st != nil {
return nil, err
if st := profile.Status(env.NowImpl()); st.Code != storage.ValidCA {
return nil, st
}
return profile, nil
default:
return nil, st
}

return profile, err
}
7 changes: 4 additions & 3 deletions action/serve/issuehandler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ func writeKeyCertTar(w io.Writer, privbs, certbs []byte) error {

func (h *Handler) serveHTTPIfPossible(w http.ResponseWriter, r *http.Request) error {
slog := h.env.Logger.Sugar()
now := h.env.NowImpl()

q := r.URL.Query()

Expand All @@ -138,9 +139,9 @@ func (h *Handler) serveHTTPIfPossible(w http.ResponseWriter, r *http.Request) er
return err
}

caSubject, err := profile.ReadCASubject()
if err != nil {
return err
var caSubject *dname.Config
if st := profile.Status(now); st.Code == storage.ValidCA {
caSubject = dname.FromPkixName(st.CACert.Subject)
}

subject, err := dname.DefaultConfig("", caSubject)
Expand Down
28 changes: 26 additions & 2 deletions action/setup/config.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package setup

import (
"crypto/x509"
"errors"
"fmt"
"time"
Expand All @@ -15,9 +16,9 @@ var (
)

type Config struct {
Subject *dname.Config `yaml:"subject" flags:""`
Subject *dname.Config `yaml:"subject" flags:""`
Validity period.ValidityPeriod `yaml:"validity" flags:"validity,time duration/timestamp where the cert is valid to (examples: 30d, 1y, 20220530)"`
KeyType wcrypto.KeyType `yaml:"keyType" flags:"key-type,private key type (rsa, ecdsa),t"`
KeyType wcrypto.KeyType `yaml:"keyType" flags:"key-type,private key type (rsa, ecdsa),t"`
}

func DefaultConfig() (*Config, error) {
Expand All @@ -37,6 +38,29 @@ func EmptyConfig() *Config {
}
}

func ConfigFromCert(cert *x509.Certificate) (*Config, error) {
kt, err := wcrypto.KeyTypeOfPub(cert.PublicKey)
if err != nil {
return nil, err
}

return &Config{
Subject: dname.FromPkixName(cert.Subject),
KeyType: kt,
Validity: period.ValidityPeriod{NotAfter: cert.NotAfter},
}, nil
}

func (a *Config) CompatibleWith(b *Config) error {
if err := a.Subject.CompatibleWith(b.Subject); err != nil {
return err
}
if a.KeyType != b.KeyType {
return fmt.Errorf("KeyType mismatch: %v != %v", a.KeyType, b.KeyType)
}
return nil
}

const expireThreshold = 30 * time.Second

var ErrValidityPeriodExpired = errors.New("Declining to setup CA which expires within 30 seconds.")
Expand Down
39 changes: 25 additions & 14 deletions cmd/kmgm/issue/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"time"

"github.com/urfave/cli/v2"
"gopkg.in/yaml.v2"

"github.com/IPA-CyberLab/kmgm/action"
"github.com/IPA-CyberLab/kmgm/action/issue"
Expand Down Expand Up @@ -177,6 +178,10 @@ func PromptCertPath(env *action.Environment, privPath, certPath string) (string,
const ConfigTemplateText = `
---
# kmgm pki new cert config
privateKeyPath: {{ .PrivateKeyPath }}
certPath: {{ .CertPath }}
{{- with .Issue }}
issue:
{{ template "subject" .Subject }}
Expand Down Expand Up @@ -228,6 +233,8 @@ issue:
{{ CommentOutIfFalse (and (eq .KeyUsage.Preset "custom") (HasExtKeyUsage "serverAuth" .KeyUsage.ExtKeyUsages)) -}}
- serverAuth
{{ end -}}
renewBefore: {{ .RenewBefore }}
`

type Config struct {
Expand Down Expand Up @@ -392,18 +399,14 @@ var Command = &cli.Command{
}

cfg := &Config{}
if c.Bool("dump-template") || env.Frontend.ShouldLoadDefaults() {
if c.Bool("dump-template") || !c.Bool("no-default") {
slog.Debugf("Constructing default config.")

var caSubject *dname.Config
// Inherit CA subject iff CA is setup.
now := env.NowImpl()
if st := profile.Status(now); st == nil {
var err error
caSubject, err = profile.ReadCASubject()
if err != nil {
return err
}
if st := profile.Status(now); st.Code == storage.ValidCA {
caSubject = dname.FromPkixName(st.CACert.Subject)
}

issuecfg, err := issue.DefaultConfig(caSubject)
Expand All @@ -417,17 +420,28 @@ var Command = &cli.Command{
cfg.Issue = issue.EmptyConfig()
}

if c.Bool("dump-template") {
if err := frontend.DumpTemplate(ConfigTemplateText, cfg); err != nil {
if !c.Bool("dump-template") {
if err := setup.EnsureCA(env, nil, profile, setup.DisallowNonInteractiveSetup); err != nil {
return err
}
return nil
}

if err := setup.EnsureCA(env, nil, profile, false); err != nil {
if cfgbs, ok := c.App.Metadata["config"]; ok {
if err := yaml.UnmarshalStrict(cfgbs.([]byte), cfg); err != nil {
return err
}
}
if err := structflags.PopulateStructFromCliContext(cfg, c); err != nil {
return err
}

if c.Bool("dump-template") {
if err := frontend.DumpTemplate(ConfigTemplateText, cfg); err != nil {
return err
}
return nil
}

err = PrepareKeyTypePath(env, &cfg.Issue.KeyType, &cfg.PrivateKeyPath)
if err != nil {
return fmt.Errorf("Failed to acquire private key: %w", err)
Expand All @@ -448,9 +462,6 @@ var Command = &cli.Command{
}); err != nil {
return err
}
if err := structflags.PopulateStructFromCliContext(cfg, c); err != nil {
return err
}

priv, err := EnsurePrivateKey(env, cfg.Issue.KeyType, cfg.PrivateKeyPath)
if err != nil {
Expand Down
17 changes: 9 additions & 8 deletions cmd/kmgm/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,25 +129,26 @@ func NewApp() *cli.App {
}

configFile := c.String("config")
var configText string
if configFile != "" {
bs, err := ioutil.ReadFile(configFile)
if err != nil {
return fmt.Errorf("Failed to read specified config file: %w", err)
}
configText = string(bs)
configText := string(bs)
if strings.TrimSpace(configText) == "" {
return fmt.Errorf("The specified config file %s was empty.", configFile)
}
app.Metadata["config"] = bs

if frontend.IsNoDefaultSpecifiedInYaml(bs) {
logger.Debug("The specified config file has NoDefault set to true.")
c.Set("no-default", "true")
}
}

var fe frontend.Frontend
if configText != "" || c.Bool("non-interactive") || !isatty.IsTerminal(os.Stdin.Fd()) {
fe = &frontend.NonInteractive{
Logger: logger,
ConfigText: configText,
NoDefault: c.Bool("no-default"),
}
if c.Bool("non-interactive") || !isatty.IsTerminal(os.Stdin.Fd()) {
fe = &frontend.NonInteractive{Logger: logger}
} else {
fe = promptuife.Frontend{}
}
Expand Down
79 changes: 78 additions & 1 deletion cmd/kmgm/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ func TestIssue_NoCA(t *testing.T) {
t.Cleanup(teardown)

logs, err := runKmgm(t, basedir, nil, []string{"issue"}, nowDefault)
expectErr(t, err, setup.ErrCantRunInteractiveCaSetup)
expectErr(t, err, setup.CantRunInteractiveCASetupErr)
_ = logs //expectLogMessage(t, logs, "")
}

Expand Down Expand Up @@ -360,6 +360,83 @@ func setupCA(t *testing.T, basedir string) {
}
}

func TestSetup_AlreadyExists(t *testing.T) {
basedir, teardown := prepareBasedir(t)
t.Cleanup(teardown)

setupCA(t, basedir)

t.Run("Default", func(t *testing.T) {
logs, err := runKmgm(t, basedir, nil, []string{"setup"}, nowDefault)
expectErr(t, err, nil)
expectLogMessage(t, logs, "already has a CA setup.")
})

t.Run("NoDefault_MatchingConfig", func(t *testing.T) {
yaml := []byte(`
setup:
subject:
commonName: test_CA_CN
organization: test_CA_Org
organizationalUnit: test_CA_OU
country: JP
locality: test_CA_L
province: test_CA_P
streetAddress: test_CA_SA
postalCode: test_CA_PC
keyType: rsa
validity: 1y
noDefault: true
`)
logs, err := runKmgm(t, basedir, yaml, []string{"setup"}, nowDefault)
expectErr(t, err, nil)
expectLogMessage(t, logs, "already has a CA setup.")
})

t.Run("NoDefault_IncompatibleSubject", func(t *testing.T) {
yaml := []byte(`
setup:
subject:
commonName: test_CA_CN
organization: wrong_Org
organizationalUnit: test_CA_OU
country: JP
locality: test_CA_L
province: test_CA_P
streetAddress: test_CA_SA
postalCode: test_CA_PC
keyType: rsa
validity: 1y
noDefault: true
`)
_, err := runKmgm(t, basedir, yaml, []string{"setup"}, nowDefault)
expectErr(t, err, setup.IncompatibleCertErr{})
})

t.Run("NoDefault_IncompatibleKeyType", func(t *testing.T) {
yaml := []byte(`
setup:
subject:
commonName: test_CA_CN
organization: test_CA_Org
organizationalUnit: test_CA_OU
country: JP
locality: test_CA_L
province: test_CA_P
streetAddress: test_CA_SA
postalCode: test_CA_PC
keyType: ecdsa
validity: 1y
noDefault: true
`)
_, err := runKmgm(t, basedir, yaml, []string{"setup"}, nowDefault)
expectErr(t, err, setup.IncompatibleCertErr{})
})
}

func TestIssue_Default(t *testing.T) {
basedir, teardown := prepareBasedir(t)
t.Cleanup(teardown)
Expand Down
Loading

0 comments on commit 9aa204f

Please sign in to comment.