Add support for user data in templates.

This commit is contained in:
Mariano Cano 2019-10-04 17:08:42 -07:00 committed by max furman
parent a35988ff08
commit 91130b9c3f
5 changed files with 36 additions and 22 deletions

View file

@ -514,7 +514,7 @@ type mockAuthority struct {
getRoots func() ([]*x509.Certificate, error)
getFederation func() ([]*x509.Certificate, error)
getSSHKeys func() (*authority.SSHKeys, error)
getSSHConfig func(typ string) ([]templates.Output, error)
getSSHConfig func(typ string, data map[string]string) ([]templates.Output, error)
}
// TODO: remove once Authorize is deprecated.
@ -627,9 +627,9 @@ func (m *mockAuthority) GetSSHKeys() (*authority.SSHKeys, error) {
return m.ret1.(*authority.SSHKeys), m.err
}
func (m *mockAuthority) GetSSHConfig(typ string) ([]templates.Output, error) {
func (m *mockAuthority) GetSSHConfig(typ string, data map[string]string) ([]templates.Output, error) {
if m.getSSHConfig != nil {
return m.getSSHConfig(typ)
return m.getSSHConfig(typ, data)
}
return m.ret1.([]templates.Output), m.err
}

View file

@ -18,7 +18,7 @@ type SSHAuthority interface {
SignSSH(key ssh.PublicKey, opts provisioner.SSHOptions, signOpts ...provisioner.SignOption) (*ssh.Certificate, error)
SignSSHAddUser(key ssh.PublicKey, cert *ssh.Certificate) (*ssh.Certificate, error)
GetSSHKeys() (*authority.SSHKeys, error)
GetSSHConfig(typ string) ([]templates.Output, error)
GetSSHConfig(typ string, data map[string]string) ([]templates.Output, error)
}
// SignSSHRequest is the request body of an SSH certificate request.
@ -146,7 +146,8 @@ type Template = templates.Output
// SSHConfigRequest is the request body used to get the SSH configuration
// templates.
type SSHConfigRequest struct {
Type string `json:"type"`
Type string `json:"type"`
Data map[string]string `json:"data"`
}
// Validate checks the values of the SSHConfigurationRequest.
@ -272,7 +273,7 @@ func (h *caHandler) SSHConfig(w http.ResponseWriter, r *http.Request) {
return
}
ts, err := h.Authority.GetSSHConfig(body.Type)
ts, err := h.Authority.GetSSHConfig(body.Type, body.Data)
if err != nil {
WriteError(w, InternalServerError(err))
return

View file

@ -158,8 +158,8 @@ func (a *Authority) init() error {
// Configure protected template variables:
if t := a.config.Templates; t != nil {
if t.Variables == nil {
t.Variables = make(map[string]interface{})
if t.Data == nil {
t.Data = make(map[string]interface{})
}
var vars templates.Step
if a.config.SSH != nil {
@ -170,7 +170,7 @@ func (a *Authority) init() error {
vars.SSH.UserKey = a.sshCAUserCertSignKey.PublicKey()
}
}
t.Variables["Step"] = vars
t.Data["Step"] = vars
}
// JWT numeric dates are seconds.

View file

@ -6,10 +6,9 @@ import (
"net/http"
"strings"
"github.com/smallstep/certificates/templates"
"github.com/pkg/errors"
"github.com/smallstep/certificates/authority/provisioner"
"github.com/smallstep/certificates/templates"
"github.com/smallstep/cli/crypto/randutil"
"golang.org/x/crypto/ssh"
)
@ -51,7 +50,7 @@ func (a *Authority) GetSSHKeys() (*SSHKeys, error) {
}
// GetSSHConfig returns rendered templates for clients (user) or servers (host).
func (a *Authority) GetSSHConfig(typ string) ([]templates.Output, error) {
func (a *Authority) GetSSHConfig(typ string, data map[string]string) ([]templates.Output, error) {
if a.sshCAUserCertSignKey == nil && a.sshCAHostCertSignKey == nil {
return nil, &apiError{
err: errors.New("getSSHConfig: ssh is not configured"),
@ -76,10 +75,23 @@ func (a *Authority) GetSSHConfig(typ string) ([]templates.Output, error) {
}
}
// Render templates.
// Merge user and default data
var mergedData map[string]interface{}
if len(data) == 0 {
mergedData = a.config.Templates.Data
} else {
mergedData = make(map[string]interface{}, len(a.config.Templates.Data)+1)
mergedData["User"] = data
for k, v := range a.config.Templates.Data {
mergedData[k] = v
}
}
// Render templates
output := []templates.Output{}
for _, t := range ts {
o, err := t.Output(a.config.Templates.Variables)
o, err := t.Output(mergedData)
if err != nil {
return nil, err
}

View file

@ -2,7 +2,6 @@ package templates
import (
"bytes"
"fmt"
"io/ioutil"
"text/template"
@ -31,8 +30,8 @@ type Output struct {
// Templates is a collection of templates and variables.
type Templates struct {
SSH *SSHTemplates `json:"ssh,omitempty"`
Variables map[string]interface{} `json:"variables,omitempty"`
SSH *SSHTemplates `json:"ssh,omitempty"`
Data map[string]interface{} `json:"data,omitempty"`
}
// Validate returns an error if a template is not valid.
@ -46,10 +45,13 @@ func (t *Templates) Validate() (err error) {
return
}
// Do not allow "Step"
if t.Variables != nil {
if _, ok := t.Variables["Step"]; ok {
return errors.New("templates variables cannot contain 'step' as a property")
// Do not allow "Step" and "User"
if t.Data != nil {
if _, ok := t.Data["Step"]; ok {
return errors.New("templates variables cannot contain 'Step' as a property")
}
if _, ok := t.Data["User"]; ok {
return errors.New("templates variables cannot contain 'User' as a property")
}
}
return nil
@ -156,7 +158,6 @@ func (t *Template) Render(data interface{}) ([]byte, error) {
}
buf := new(bytes.Buffer)
fmt.Println(data)
if err := t.Execute(buf, data); err != nil {
return nil, errors.Wrapf(err, "error executing %s", t.TemplatePath)
}