From b583d8d65826f2907d0e9b9aaffb3ae33d97cbed Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 16 Jun 2020 17:24:47 -0700 Subject: [PATCH 01/10] Move default templates to the template package. --- pki/templates.go | 70 ++-------------------------- templates/values.go | 109 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 67 deletions(-) diff --git a/pki/templates.go b/pki/templates.go index 44d4a7b0..6c3e888a 100644 --- a/pki/templates.go +++ b/pki/templates.go @@ -11,77 +11,13 @@ import ( "github.com/smallstep/cli/utils" ) -// SSHTemplates contains the configuration of default templates used on ssh. -// Relative paths are relative to the StepPath. -var SSHTemplates = &templates.SSHTemplates{ - User: []templates.Template{ - {Name: "include.tpl", Type: templates.Snippet, TemplatePath: "templates/ssh/include.tpl", Path: "~/.ssh/config", Comment: "#"}, - {Name: "config.tpl", Type: templates.File, TemplatePath: "templates/ssh/config.tpl", Path: "ssh/config", Comment: "#"}, - {Name: "known_hosts.tpl", Type: templates.File, TemplatePath: "templates/ssh/known_hosts.tpl", Path: "ssh/known_hosts", Comment: "#"}, - }, - Host: []templates.Template{ - {Name: "sshd_config.tpl", Type: templates.Snippet, TemplatePath: "templates/ssh/sshd_config.tpl", Path: "/etc/ssh/sshd_config", Comment: "#"}, - {Name: "ca.tpl", Type: templates.Snippet, TemplatePath: "templates/ssh/ca.tpl", Path: "/etc/ssh/ca.pub", Comment: "#"}, - }, -} - -// SSHTemplateData contains the data of the default templates used on ssh. -var SSHTemplateData = map[string]string{ - // include.tpl adds the step ssh config file. - // - // Note: on windows `Include C:\...` is treated as a relative path. - "include.tpl": `Host * -{{- if or .User.GOOS "none" | eq "windows" }} - Include "{{ .User.StepPath | replace "\\" "/" | trimPrefix "C:" }}/ssh/config" -{{- else }} - Include "{{.User.StepPath}}/ssh/config" -{{- end }}`, - - // config.tpl is the step ssh config file, it includes the Match rule and - // references the step known_hosts file. - // - // Note: on windows ProxyCommand requires the full path - "config.tpl": `Match exec "step ssh check-host %h" -{{- if .User.User }} - User {{.User.User}} -{{- end }} -{{- if or .User.GOOS "none" | eq "windows" }} - UserKnownHostsFile "{{.User.StepPath}}\ssh\known_hosts" - ProxyCommand C:\Windows\System32\cmd.exe /c step ssh proxycommand %r %h %p -{{- else }} - UserKnownHostsFile "{{.User.StepPath}}/ssh/known_hosts" - ProxyCommand step ssh proxycommand %r %h %p -{{- end }} -`, - - // known_hosts.tpl authorizes the ssh hosts key - "known_hosts.tpl": `@cert-authority * {{.Step.SSH.HostKey.Type}} {{.Step.SSH.HostKey.Marshal | toString | b64enc}} -{{- range .Step.SSH.HostFederatedKeys}} -@cert-authority * {{.Type}} {{.Marshal | toString | b64enc}} -{{- end }} -`, - - // sshd_config.tpl adds the configuration to support certificates - "sshd_config.tpl": `TrustedUserCAKeys /etc/ssh/ca.pub -HostCertificate /etc/ssh/{{.User.Certificate}} -HostKey /etc/ssh/{{.User.Key}}`, - - // ca.tpl contains the public key used to authorized clients - "ca.tpl": `{{.Step.SSH.UserKey.Type}} {{.Step.SSH.UserKey.Marshal | toString | b64enc}} -{{- range .Step.SSH.UserFederatedKeys}} -{{.Type}} {{.Marshal | toString | b64enc}} -{{- end }} -`, -} - // getTemplates returns all the templates enabled func (p *PKI) getTemplates() *templates.Templates { if !p.enableSSH { return nil } - return &templates.Templates{ - SSH: SSHTemplates, + SSH: &templates.DefaultSSHTemplates, Data: map[string]interface{}{}, } } @@ -104,7 +40,7 @@ func generateTemplates(t *templates.Templates) error { } // Create all templates for _, t := range t.SSH.User { - data, ok := SSHTemplateData[t.Name] + data, ok := templates.DefaultSSHTemplateData[t.Name] if !ok { return errors.Errorf("template %s does not exists", t.Name) } @@ -113,7 +49,7 @@ func generateTemplates(t *templates.Templates) error { } } for _, t := range t.SSH.Host { - data, ok := SSHTemplateData[t.Name] + data, ok := templates.DefaultSSHTemplateData[t.Name] if !ok { return errors.Errorf("template %s does not exists", t.Name) } diff --git a/templates/values.go b/templates/values.go index ba03267b..1ecd7ea3 100644 --- a/templates/values.go +++ b/templates/values.go @@ -16,3 +16,112 @@ type StepSSH struct { HostFederatedKeys []ssh.PublicKey UserFederatedKeys []ssh.PublicKey } + +// DefaultSSHTemplates contains the configuration of default templates used on ssh. +// Relative paths are relative to the StepPath. +var DefaultSSHTemplates = SSHTemplates{ + User: []Template{ + { + Name: "include.tpl", + Type: Snippet, + TemplatePath: "templates/ssh/include.tpl", + Path: "~/.ssh/config", + Comment: "#", + }, + { + Name: "config.tpl", + Type: File, + TemplatePath: "templates/ssh/config.tpl", + Path: "ssh/config", + Comment: "#", + }, + { + Name: "known_hosts.tpl", + Type: File, + TemplatePath: "templates/ssh/known_hosts.tpl", + Path: "ssh/known_hosts", + Comment: "#", + }, + }, + Host: []Template{ + { + Name: "sshd_config.tpl", + Type: Snippet, + TemplatePath: "templates/ssh/sshd_config.tpl", + Path: "/etc/ssh/sshd_config", + Comment: "#", + RequiredData: []string{"Certificate", "Key"}, + }, + { + Name: "ca.tpl", + Type: Snippet, + TemplatePath: "templates/ssh/ca.tpl", + Path: "/etc/ssh/ca.pub", + Comment: "#", + }, + }, +} + +// DefaultSSHTemplateData contains the data of the default templates used on ssh. +var DefaultSSHTemplateData = map[string]string{ + // include.tpl adds the step ssh config file. + // + // Note: on windows `Include C:\...` is treated as a relative path. + "include.tpl": `Host * +{{- if or .User.GOOS "none" | eq "windows" }} + Include "{{ .User.StepPath | replace "\\" "/" | trimPrefix "C:" }}/ssh/config" +{{- else }} + Include "{{.User.StepPath}}/ssh/config" +{{- end }}`, + + // config.tpl is the step ssh config file, it includes the Match rule and + // references the step known_hosts file. + // + // Note: on windows ProxyCommand requires the full path + "config.tpl": `Match exec "step ssh check-host %h" +{{- if .User.User }} + User {{.User.User}} +{{- end }} +{{- if or .User.GOOS "none" | eq "windows" }} + UserKnownHostsFile "{{.User.StepPath}}\ssh\known_hosts" + ProxyCommand C:\Windows\System32\cmd.exe /c step ssh proxycommand %r %h %p +{{- else }} + UserKnownHostsFile "{{.User.StepPath}}/ssh/known_hosts" + ProxyCommand step ssh proxycommand %r %h %p +{{- end }} +`, + + // known_hosts.tpl authorizes the ssh hosts key + "known_hosts.tpl": `@cert-authority * {{.Step.SSH.HostKey.Type}} {{.Step.SSH.HostKey.Marshal | toString | b64enc}} +{{- range .Step.SSH.HostFederatedKeys}} +@cert-authority * {{.Type}} {{.Marshal | toString | b64enc}} +{{- end }} +`, + + // sshd_config.tpl adds the configuration to support certificates + "sshd_config.tpl": `TrustedUserCAKeys /etc/ssh/ca.pub +HostCertificate /etc/ssh/{{.User.Certificate}} +HostKey /etc/ssh/{{.User.Key}}`, + + // ca.tpl contains the public key used to authorized clients + "ca.tpl": `{{.Step.SSH.UserKey.Type}} {{.Step.SSH.UserKey.Marshal | toString | b64enc}} +{{- range .Step.SSH.UserFederatedKeys}} +{{.Type}} {{.Marshal | toString | b64enc}} +{{- end }} +`, +} + +// DefaultTemplates returns the default templates. +func DefaultTemplates() *Templates { + sshTemplates := DefaultSSHTemplates + for i, t := range sshTemplates.User { + sshTemplates.User[i].Content = []byte(DefaultSSHTemplateData[t.Name]) + } + for i, t := range sshTemplates.Host { + sshTemplates.Host[i].Content = []byte(DefaultSSHTemplateData[t.Name]) + } + return &Templates{ + SSH: &sshTemplates, + Data: map[string]interface{}{}, + } +} From 6c844a0618ed3e317c29bdb380da181a559d86b2 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 16 Jun 2020 17:26:18 -0700 Subject: [PATCH 02/10] Load default templates if no templates are configured. --- authority/authority.go | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/authority/authority.go b/authority/authority.go index cdf37953..89e3c5c9 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -202,6 +202,7 @@ func (a *Authority) init() error { } // Decrypt and load SSH keys + var tmplVars templates.Step if a.config.SSH != nil { if a.config.SSH.HostKey != "" { signer, err := a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{ @@ -218,6 +219,9 @@ func (a *Authority) init() error { // Append public key to list of host certs a.sshCAHostCerts = append(a.sshCAHostCerts, a.sshCAHostCertSignKey.PublicKey()) a.sshCAHostFederatedCerts = append(a.sshCAHostFederatedCerts, a.sshCAHostCertSignKey.PublicKey()) + // Configure template variables + tmplVars.SSH.HostKey = a.sshCAHostCertSignKey.PublicKey() + tmplVars.SSH.HostFederatedKeys = append(tmplVars.SSH.HostFederatedKeys, a.sshCAHostFederatedCerts[1:]...) } if a.config.SSH.UserKey != "" { signer, err := a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{ @@ -234,6 +238,9 @@ func (a *Authority) init() error { // Append public key to list of user certs a.sshCAUserCerts = append(a.sshCAUserCerts, a.sshCAUserCertSignKey.PublicKey()) a.sshCAUserFederatedCerts = append(a.sshCAUserFederatedCerts, a.sshCAUserCertSignKey.PublicKey()) + // Configure template variables + tmplVars.SSH.UserKey = a.sshCAUserCertSignKey.PublicKey() + tmplVars.SSH.UserFederatedKeys = append(tmplVars.SSH.UserFederatedKeys, a.sshCAUserFederatedCerts[1:]...) } // Append other public keys @@ -292,23 +299,15 @@ func (a *Authority) init() error { } } - // Configure protected template variables: - if t := a.config.Templates; t != nil { - if t.Data == nil { - t.Data = make(map[string]interface{}) + // Configure templates, currently only ssh templates are supported. + if a.sshCAHostCertSignKey != nil || a.sshCAUserCertSignKey != nil { + if a.config.Templates == nil { + a.config.Templates = templates.DefaultTemplates() } - var vars templates.Step - if a.config.SSH != nil { - if a.sshCAHostCertSignKey != nil { - vars.SSH.HostKey = a.sshCAHostCertSignKey.PublicKey() - vars.SSH.HostFederatedKeys = append(vars.SSH.HostFederatedKeys, a.sshCAHostFederatedCerts[1:]...) - } - if a.sshCAUserCertSignKey != nil { - vars.SSH.UserKey = a.sshCAUserCertSignKey.PublicKey() - vars.SSH.UserFederatedKeys = append(vars.SSH.UserFederatedKeys, a.sshCAUserFederatedCerts[1:]...) - } + if a.config.Templates.Data == nil { + a.config.Templates.Data = make(map[string]interface{}) } - t.Data["Step"] = vars + a.config.Templates.Data["Step"] = tmplVars } // JWT numeric dates are seconds. From 237baa51694c22b3d15abe13feb0b566da208572 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 16 Jun 2020 17:26:54 -0700 Subject: [PATCH 03/10] Check for required variables in templates. Fixes smallstep/cli#232 --- authority/ssh.go | 9 +++++++++ templates/templates.go | 29 +++++++++++++++++++++++++++++ templates/templates_test.go | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+) diff --git a/authority/ssh.go b/authority/ssh.go index b80797d0..3c29dd87 100644 --- a/authority/ssh.go +++ b/authority/ssh.go @@ -159,6 +159,15 @@ func (a *Authority) GetSSHConfig(ctx context.Context, typ string, data map[strin // Render templates output := []templates.Output{} for _, t := range ts { + if err := t.Load(); err != nil { + return nil, err + } + + // Check for required variables. + if err := t.ValidateRequiredData(data); err != nil { + return nil, errs.BadRequestErr(err, errs.WithMessage("%v, please use `--set ` flag", err)) + } + o, err := t.Output(mergedData) if err != nil { return nil, err diff --git a/templates/templates.go b/templates/templates.go index b5920974..dd575fca 100644 --- a/templates/templates.go +++ b/templates/templates.go @@ -106,6 +106,7 @@ type Template struct { TemplatePath string `json:"template"` Path string `json:"path"` Comment string `json:"comment"` + RequiredData []string `json:"requires"` Content []byte `json:"-"` } @@ -147,6 +148,17 @@ func (t *Template) Validate() error { return nil } +// ValidateRequiredData checks that the given data contains all the keys +// required. +func (t *Template) ValidateRequiredData(data map[string]string) error { + for _, key := range t.RequiredData { + if _, ok := data[key]; !ok { + return errors.Errorf("required variable '%s' is missing", key) + } + } + return nil +} + // Load loads the template in memory, returns an error if the parsing of the // template fails. func (t *Template) Load() error { @@ -166,7 +178,10 @@ func (t *Template) Load() error { return nil } +// LoadBytes loads the template in memory, returns an error if the parsing of +// the template fails. func (t *Template) LoadBytes(b []byte) error { + t.backfill(b) tmpl, err := template.New(t.Name).Funcs(sprig.TxtFuncMap()).Parse(string(b)) if err != nil { return errors.Wrapf(err, "error parsing template %s", t.Name) @@ -209,6 +224,20 @@ func (t *Template) Output(data interface{}) (Output, error) { }, nil } +// backfill updates old templates with the required data. +func (t *Template) backfill(b []byte) { + switch t.Name { + case "sshd_config.tpl": + if len(t.RequiredData) == 0 { + a := bytes.TrimSpace(b) + b := bytes.TrimSpace([]byte(DefaultSSHTemplateData[t.Name])) + if bytes.Equal(a, b) { + t.RequiredData = []string{"Certificate", "Key"} + } + } + } +} + // Output represents the text representation of a rendered template. type Output struct { Name string `json:"name"` diff --git a/templates/templates_test.go b/templates/templates_test.go index e00217c8..f8707a69 100644 --- a/templates/templates_test.go +++ b/templates/templates_test.go @@ -428,3 +428,39 @@ func TestOutput_Write(t *testing.T) { }) } } + +func TestTemplate_ValidateRequiredData(t *testing.T) { + data := map[string]string{ + "key1": "value1", + "key2": "value2", + } + type fields struct { + RequiredData []string + } + type args struct { + data map[string]string + } + tests := []struct { + name string + fields fields + args args + wantErr bool + }{ + {"ok nil", fields{nil}, args{nil}, false}, + {"ok empty", fields{[]string{}}, args{data}, false}, + {"ok one", fields{[]string{"key1"}}, args{data}, false}, + {"ok multiple", fields{[]string{"key1", "key2"}}, args{data}, false}, + {"fail nil", fields{[]string{"missing"}}, args{nil}, true}, + {"fail missing", fields{[]string{"missing"}}, args{data}, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tmpl := &Template{ + RequiredData: tt.fields.RequiredData, + } + if err := tmpl.ValidateRequiredData(tt.args.data); (err != nil) != tt.wantErr { + t.Errorf("Template.ValidateRequiredData() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} From a845b56283deccb37753bf92d0347828915bf949 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 16 Jun 2020 17:56:22 -0700 Subject: [PATCH 04/10] Remove the template path if we load the defaults. --- templates/values.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/templates/values.go b/templates/values.go index 1ecd7ea3..fd4ee4c2 100644 --- a/templates/values.go +++ b/templates/values.go @@ -115,9 +115,11 @@ HostKey /etc/ssh/{{.User.Key}}`, func DefaultTemplates() *Templates { sshTemplates := DefaultSSHTemplates for i, t := range sshTemplates.User { + sshTemplates.User[i].TemplatePath = "" sshTemplates.User[i].Content = []byte(DefaultSSHTemplateData[t.Name]) } for i, t := range sshTemplates.Host { + sshTemplates.Host[i].TemplatePath = "" sshTemplates.Host[i].Content = []byte(DefaultSSHTemplateData[t.Name]) } return &Templates{ From e3ae751b5793e19f6a2a96b643f36ccf2f38707a Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 16 Jun 2020 17:57:35 -0700 Subject: [PATCH 05/10] Use templates from authority instead of config. --- authority/authority.go | 12 +++++++----- authority/ssh.go | 16 ++++++++-------- authority/ssh_test.go | 2 +- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/authority/authority.go b/authority/authority.go index 89e3c5c9..6168e2e3 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -31,6 +31,7 @@ type Authority struct { keyManager kms.KeyManager provisioners *provisioner.Collection db db.AuthDB + templates *templates.Templates // X509 CA rootX509Certs []*x509.Certificate @@ -301,13 +302,14 @@ func (a *Authority) init() error { // Configure templates, currently only ssh templates are supported. if a.sshCAHostCertSignKey != nil || a.sshCAUserCertSignKey != nil { - if a.config.Templates == nil { - a.config.Templates = templates.DefaultTemplates() + a.templates = a.config.Templates + if a.templates == nil { + a.templates = templates.DefaultTemplates() } - if a.config.Templates.Data == nil { - a.config.Templates.Data = make(map[string]interface{}) + if a.templates.Data == nil { + a.templates.Data = make(map[string]interface{}) } - a.config.Templates.Data["Step"] = tmplVars + a.templates.Data["Step"] = tmplVars } // JWT numeric dates are seconds. diff --git a/authority/ssh.go b/authority/ssh.go index 3c29dd87..0bc0b343 100644 --- a/authority/ssh.go +++ b/authority/ssh.go @@ -125,19 +125,19 @@ func (a *Authority) GetSSHConfig(ctx context.Context, typ string, data map[strin return nil, errs.NotFound("getSSHConfig: ssh is not configured") } - if a.config.Templates == nil { + if a.templates == nil { return nil, errs.NotFound("getSSHConfig: ssh templates are not configured") } var ts []templates.Template switch typ { case provisioner.SSHUserCert: - if a.config.Templates != nil && a.config.Templates.SSH != nil { - ts = a.config.Templates.SSH.User + if a.templates != nil && a.templates.SSH != nil { + ts = a.templates.SSH.User } case provisioner.SSHHostCert: - if a.config.Templates != nil && a.config.Templates.SSH != nil { - ts = a.config.Templates.SSH.Host + if a.templates != nil && a.templates.SSH != nil { + ts = a.templates.SSH.Host } default: return nil, errs.BadRequest("getSSHConfig: type %s is not valid", typ) @@ -147,11 +147,11 @@ func (a *Authority) GetSSHConfig(ctx context.Context, typ string, data map[strin var mergedData map[string]interface{} if len(data) == 0 { - mergedData = a.config.Templates.Data + mergedData = a.templates.Data } else { - mergedData = make(map[string]interface{}, len(a.config.Templates.Data)+1) + mergedData = make(map[string]interface{}, len(a.templates.Data)+1) mergedData["User"] = data - for k, v := range a.config.Templates.Data { + for k, v := range a.templates.Data { mergedData[k] = v } } diff --git a/authority/ssh_test.go b/authority/ssh_test.go index 626c0da6..a07ed0db 100644 --- a/authority/ssh_test.go +++ b/authority/ssh_test.go @@ -460,7 +460,7 @@ func TestAuthority_GetSSHConfig(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { a := testAuthority(t) - a.config.Templates = tt.fields.templates + a.templates = tt.fields.templates a.sshCAUserCertSignKey = tt.fields.userSigner a.sshCAHostCertSignKey = tt.fields.hostSigner From ff32746312a1de5212168178189e87af7f73dd22 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 16 Jun 2020 18:21:44 -0700 Subject: [PATCH 06/10] Add test case for error executing template. --- authority/ssh_test.go | 19 ++++++++++++++++++- authority/testdata/templates/fail.tpl | 1 + 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 authority/testdata/templates/fail.tpl diff --git a/authority/ssh_test.go b/authority/ssh_test.go index a07ed0db..ba381786 100644 --- a/authority/ssh_test.go +++ b/authority/ssh_test.go @@ -398,7 +398,14 @@ func TestAuthority_GetSSHConfig(t *testing.T) { {Name: "config.tpl", Type: templates.File, TemplatePath: "./testdata/templates/config.tpl", Path: "ssh/config", Comment: "#"}, }, Host: []templates.Template{ - {Name: "sshd_config.tpl", Type: templates.File, TemplatePath: "./testdata/templates/sshd_config.tpl", Path: "/etc/ssh/sshd_config", Comment: "#"}, + { + Name: "sshd_config.tpl", + Type: templates.File, + TemplatePath: "./testdata/templates/sshd_config.tpl", + Path: "/etc/ssh/sshd_config", + Comment: "#", + RequiredData: []string{"Certificate", "Key"}, + }, }, }, Data: map[string]interface{}{ @@ -429,6 +436,14 @@ func TestAuthority_GetSSHConfig(t *testing.T) { }, } + tmplConfigFail := &templates.Templates{ + SSH: &templates.SSHTemplates{ + User: []templates.Template{ + {Name: "fail.tpl", Type: templates.File, TemplatePath: "./testdata/templates/fail.tpl", Path: "ssh/fail", Comment: "#"}, + }, + }, + } + type fields struct { templates *templates.Templates userSigner ssh.Signer @@ -456,6 +471,8 @@ func TestAuthority_GetSSHConfig(t *testing.T) { {"userError", fields{tmplConfigErr, userSigner, hostSigner}, args{"user", nil}, nil, true}, {"hostError", fields{tmplConfigErr, userSigner, hostSigner}, args{"host", map[string]string{"Function": "foo"}}, nil, true}, {"noTemplates", fields{nil, userSigner, hostSigner}, args{"user", nil}, nil, true}, + {"missingData", fields{tmplConfigWithUserData, userSigner, hostSigner}, args{"host", map[string]string{"Certificate": "ssh_host_ecdsa_key-cert.pub"}}, nil, true}, + {"failError", fields{tmplConfigFail, userSigner, hostSigner}, args{"user", nil}, nil, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/authority/testdata/templates/fail.tpl b/authority/testdata/templates/fail.tpl new file mode 100644 index 00000000..1823f491 --- /dev/null +++ b/authority/testdata/templates/fail.tpl @@ -0,0 +1 @@ +{{ fail "This template will fail" }} \ No newline at end of file From f82a24861755ca3bb839e08aa1a7b320d244518c Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 16 Jun 2020 18:26:41 -0700 Subject: [PATCH 07/10] Add test with backfill. --- templates/templates_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/templates/templates_test.go b/templates/templates_test.go index f8707a69..2f169dac 100644 --- a/templates/templates_test.go +++ b/templates/templates_test.go @@ -197,6 +197,7 @@ func TestTemplate_Load(t *testing.T) { wantErr bool }{ {"ok", fields{"include.tpl", Snippet, "../authority/testdata/templates/include.tpl", "~/.ssh/config", "#"}, false}, + {"ok backfill", fields{"sshd_config.tpl", Snippet, "../authority/testdata/templates/sshd_config.tpl", "/etc/ssh/sshd_config", "#"}, false}, {"error", fields{"error.tpl", Snippet, "../authority/testdata/templates/error.tpl", "/tmp/error", "#"}, true}, {"missing", fields{"include.tpl", Snippet, "./testdata/include.tpl", "~/.ssh/config", "#"}, true}, } From 907162be44661aa64f1b74212d853695b5a1dd20 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 16 Jun 2020 18:44:47 -0700 Subject: [PATCH 08/10] Add test for DefaultTemplates. --- templates/values_test.go | 52 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 templates/values_test.go diff --git a/templates/values_test.go b/templates/values_test.go new file mode 100644 index 00000000..4bbaa8d3 --- /dev/null +++ b/templates/values_test.go @@ -0,0 +1,52 @@ +package templates + +import ( + "reflect" + "testing" +) + +func TestDefaultTemplates(t *testing.T) { + sshTemplates := DefaultSSHTemplates + sshTemplatesData := DefaultSSHTemplateData + t.Cleanup(func() { + DefaultSSHTemplates = sshTemplates + DefaultSSHTemplateData = sshTemplatesData + }) + + DefaultSSHTemplates = SSHTemplates{ + User: []Template{ + {Name: "foo.tpl", Type: Snippet, TemplatePath: "templates/ssh/foo.tpl", Path: "/tmp/foo", Comment: "#"}, + }, + Host: []Template{ + {Name: "bar.tpl", Type: Snippet, TemplatePath: "templates/ssh/bar.tpl", Path: "/tmp/bar", Comment: "#"}, + }, + } + DefaultSSHTemplateData = map[string]string{ + "foo.tpl": "foo", + "bar.tpl": "bar", + } + + tests := []struct { + name string + want *Templates + }{ + {"ok", &Templates{ + SSH: &SSHTemplates{ + User: []Template{ + {Name: "foo.tpl", Type: Snippet, Content: []byte("foo"), Path: "/tmp/foo", Comment: "#"}, + }, + Host: []Template{ + {Name: "bar.tpl", Type: Snippet, Content: []byte("bar"), Path: "/tmp/bar", Comment: "#"}, + }, + }, + Data: map[string]interface{}{}, + }}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := DefaultTemplates(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("DefaultTemplates() = %v, want %v", got, tt.want) + } + }) + } +} From 528d0910abf64a6a300f966de84d7e4488ffef80 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 16 Jun 2020 19:03:33 -0700 Subject: [PATCH 09/10] Add omitempty to requires property. --- templates/templates.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/templates.go b/templates/templates.go index dd575fca..745e3571 100644 --- a/templates/templates.go +++ b/templates/templates.go @@ -106,7 +106,7 @@ type Template struct { TemplatePath string `json:"template"` Path string `json:"path"` Comment string `json:"comment"` - RequiredData []string `json:"requires"` + RequiredData []string `json:"requires,omitempty"` Content []byte `json:"-"` } From fcfc4e9b2b2bdf8c57cb1039785513f11e3b4cf7 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Tue, 23 Jun 2020 11:14:26 -0700 Subject: [PATCH 10/10] Fix ssh federated template variables. --- authority/authority.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/authority/authority.go b/authority/authority.go index 6168e2e3..78cfa608 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -220,9 +220,6 @@ func (a *Authority) init() error { // Append public key to list of host certs a.sshCAHostCerts = append(a.sshCAHostCerts, a.sshCAHostCertSignKey.PublicKey()) a.sshCAHostFederatedCerts = append(a.sshCAHostFederatedCerts, a.sshCAHostCertSignKey.PublicKey()) - // Configure template variables - tmplVars.SSH.HostKey = a.sshCAHostCertSignKey.PublicKey() - tmplVars.SSH.HostFederatedKeys = append(tmplVars.SSH.HostFederatedKeys, a.sshCAHostFederatedCerts[1:]...) } if a.config.SSH.UserKey != "" { signer, err := a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{ @@ -239,9 +236,6 @@ func (a *Authority) init() error { // Append public key to list of user certs a.sshCAUserCerts = append(a.sshCAUserCerts, a.sshCAUserCertSignKey.PublicKey()) a.sshCAUserFederatedCerts = append(a.sshCAUserFederatedCerts, a.sshCAUserCertSignKey.PublicKey()) - // Configure template variables - tmplVars.SSH.UserKey = a.sshCAUserCertSignKey.PublicKey() - tmplVars.SSH.UserFederatedKeys = append(tmplVars.SSH.UserFederatedKeys, a.sshCAUserFederatedCerts[1:]...) } // Append other public keys @@ -263,6 +257,14 @@ func (a *Authority) init() error { return errors.Errorf("unsupported type %s", key.Type) } } + + // Configure template variables. + tmplVars.SSH.HostKey = a.sshCAHostCertSignKey.PublicKey() + tmplVars.SSH.UserKey = a.sshCAUserCertSignKey.PublicKey() + // On the templates we skip the first one because there's a distinction + // between the main key and federated keys. + tmplVars.SSH.HostFederatedKeys = append(tmplVars.SSH.HostFederatedKeys, a.sshCAHostFederatedCerts[1:]...) + tmplVars.SSH.UserFederatedKeys = append(tmplVars.SSH.UserFederatedKeys, a.sshCAUserFederatedCerts[1:]...) } // Merge global and configuration claims