forked from TrueCloudLab/certificates
Add support for user data in templates.
This commit is contained in:
parent
eb210ccc70
commit
50c8b10a4f
5 changed files with 36 additions and 22 deletions
|
@ -514,7 +514,7 @@ type mockAuthority struct {
|
||||||
getRoots func() ([]*x509.Certificate, error)
|
getRoots func() ([]*x509.Certificate, error)
|
||||||
getFederation func() ([]*x509.Certificate, error)
|
getFederation func() ([]*x509.Certificate, error)
|
||||||
getSSHKeys func() (*authority.SSHKeys, 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.
|
// TODO: remove once Authorize is deprecated.
|
||||||
|
@ -627,9 +627,9 @@ func (m *mockAuthority) GetSSHKeys() (*authority.SSHKeys, error) {
|
||||||
return m.ret1.(*authority.SSHKeys), m.err
|
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 {
|
if m.getSSHConfig != nil {
|
||||||
return m.getSSHConfig(typ)
|
return m.getSSHConfig(typ, data)
|
||||||
}
|
}
|
||||||
return m.ret1.([]templates.Output), m.err
|
return m.ret1.([]templates.Output), m.err
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ type SSHAuthority interface {
|
||||||
SignSSH(key ssh.PublicKey, opts provisioner.SSHOptions, signOpts ...provisioner.SignOption) (*ssh.Certificate, error)
|
SignSSH(key ssh.PublicKey, opts provisioner.SSHOptions, signOpts ...provisioner.SignOption) (*ssh.Certificate, error)
|
||||||
SignSSHAddUser(key ssh.PublicKey, cert *ssh.Certificate) (*ssh.Certificate, error)
|
SignSSHAddUser(key ssh.PublicKey, cert *ssh.Certificate) (*ssh.Certificate, error)
|
||||||
GetSSHKeys() (*authority.SSHKeys, 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.
|
// 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
|
// SSHConfigRequest is the request body used to get the SSH configuration
|
||||||
// templates.
|
// templates.
|
||||||
type SSHConfigRequest struct {
|
type SSHConfigRequest struct {
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
|
Data map[string]string `json:"data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate checks the values of the SSHConfigurationRequest.
|
// Validate checks the values of the SSHConfigurationRequest.
|
||||||
|
@ -272,7 +273,7 @@ func (h *caHandler) SSHConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ts, err := h.Authority.GetSSHConfig(body.Type)
|
ts, err := h.Authority.GetSSHConfig(body.Type, body.Data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
WriteError(w, InternalServerError(err))
|
WriteError(w, InternalServerError(err))
|
||||||
return
|
return
|
||||||
|
|
|
@ -158,8 +158,8 @@ func (a *Authority) init() error {
|
||||||
|
|
||||||
// Configure protected template variables:
|
// Configure protected template variables:
|
||||||
if t := a.config.Templates; t != nil {
|
if t := a.config.Templates; t != nil {
|
||||||
if t.Variables == nil {
|
if t.Data == nil {
|
||||||
t.Variables = make(map[string]interface{})
|
t.Data = make(map[string]interface{})
|
||||||
}
|
}
|
||||||
var vars templates.Step
|
var vars templates.Step
|
||||||
if a.config.SSH != nil {
|
if a.config.SSH != nil {
|
||||||
|
@ -170,7 +170,7 @@ func (a *Authority) init() error {
|
||||||
vars.SSH.UserKey = a.sshCAUserCertSignKey.PublicKey()
|
vars.SSH.UserKey = a.sshCAUserCertSignKey.PublicKey()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t.Variables["Step"] = vars
|
t.Data["Step"] = vars
|
||||||
}
|
}
|
||||||
|
|
||||||
// JWT numeric dates are seconds.
|
// JWT numeric dates are seconds.
|
||||||
|
|
|
@ -6,10 +6,9 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/smallstep/certificates/templates"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/smallstep/certificates/authority/provisioner"
|
"github.com/smallstep/certificates/authority/provisioner"
|
||||||
|
"github.com/smallstep/certificates/templates"
|
||||||
"github.com/smallstep/cli/crypto/randutil"
|
"github.com/smallstep/cli/crypto/randutil"
|
||||||
"golang.org/x/crypto/ssh"
|
"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).
|
// 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 {
|
if a.sshCAUserCertSignKey == nil && a.sshCAHostCertSignKey == nil {
|
||||||
return nil, &apiError{
|
return nil, &apiError{
|
||||||
err: errors.New("getSSHConfig: ssh is not configured"),
|
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{}
|
output := []templates.Output{}
|
||||||
for _, t := range ts {
|
for _, t := range ts {
|
||||||
o, err := t.Output(a.config.Templates.Variables)
|
o, err := t.Output(mergedData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ package templates
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
|
@ -31,8 +30,8 @@ type Output struct {
|
||||||
|
|
||||||
// Templates is a collection of templates and variables.
|
// Templates is a collection of templates and variables.
|
||||||
type Templates struct {
|
type Templates struct {
|
||||||
SSH *SSHTemplates `json:"ssh,omitempty"`
|
SSH *SSHTemplates `json:"ssh,omitempty"`
|
||||||
Variables map[string]interface{} `json:"variables,omitempty"`
|
Data map[string]interface{} `json:"data,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate returns an error if a template is not valid.
|
// Validate returns an error if a template is not valid.
|
||||||
|
@ -46,10 +45,13 @@ func (t *Templates) Validate() (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not allow "Step"
|
// Do not allow "Step" and "User"
|
||||||
if t.Variables != nil {
|
if t.Data != nil {
|
||||||
if _, ok := t.Variables["Step"]; ok {
|
if _, ok := t.Data["Step"]; ok {
|
||||||
return errors.New("templates variables cannot contain 'step' as a property")
|
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
|
return nil
|
||||||
|
@ -156,7 +158,6 @@ func (t *Template) Render(data interface{}) ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
fmt.Println(data)
|
|
||||||
if err := t.Execute(buf, data); err != nil {
|
if err := t.Execute(buf, data); err != nil {
|
||||||
return nil, errors.Wrapf(err, "error executing %s", t.TemplatePath)
|
return nil, errors.Wrapf(err, "error executing %s", t.TemplatePath)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue