Rename SSH methods.

This commit is contained in:
Mariano Cano 2019-10-08 18:35:28 -07:00 committed by max furman
parent b5bc249e1c
commit d08db4df23
7 changed files with 94 additions and 85 deletions

View file

@ -251,15 +251,15 @@ func (h *caHandler) Route(r Router) {
r.MethodFunc("GET", "/roots", h.Roots) r.MethodFunc("GET", "/roots", h.Roots)
r.MethodFunc("GET", "/federation", h.Federation) r.MethodFunc("GET", "/federation", h.Federation)
// SSH CA // SSH CA
r.MethodFunc("POST", "/ssh/sign", h.SignSSH) r.MethodFunc("POST", "/ssh/sign", h.SSHSign)
r.MethodFunc("GET", "/ssh/keys", h.SSHKeys) r.MethodFunc("GET", "/ssh/roots", h.SSHRoots)
r.MethodFunc("GET", "/ssh/federation", h.SSHFederatedKeys) r.MethodFunc("GET", "/ssh/federation", h.SSHFederation)
r.MethodFunc("POST", "/ssh/config", h.SSHConfig) r.MethodFunc("POST", "/ssh/config", h.SSHConfig)
r.MethodFunc("POST", "/ssh/config/{type}", h.SSHConfig) r.MethodFunc("POST", "/ssh/config/{type}", h.SSHConfig)
// For compatibility with old code: // For compatibility with old code:
r.MethodFunc("POST", "/re-sign", h.Renew) r.MethodFunc("POST", "/re-sign", h.Renew)
r.MethodFunc("POST", "/sign-ssh", h.SignSSH) r.MethodFunc("POST", "/sign-ssh", h.SSHSign)
} }
// Health is an HTTP handler that returns the status of the server. // Health is an HTTP handler that returns the status of the server.

View file

@ -513,7 +513,8 @@ type mockAuthority struct {
getEncryptedKey func(kid string) (string, error) getEncryptedKey func(kid string) (string, error)
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) getSSHRoots func() (*authority.SSHKeys, error)
getSSHFederation func() (*authority.SSHKeys, error)
getSSHConfig func(typ string, data map[string]string) ([]templates.Output, error) getSSHConfig func(typ string, data map[string]string) ([]templates.Output, error)
} }
@ -620,9 +621,16 @@ func (m *mockAuthority) GetFederation() ([]*x509.Certificate, error) {
return m.ret1.([]*x509.Certificate), m.err return m.ret1.([]*x509.Certificate), m.err
} }
func (m *mockAuthority) GetSSHKeys() (*authority.SSHKeys, error) { func (m *mockAuthority) GetSSHRoots() (*authority.SSHKeys, error) {
if m.getSSHKeys != nil { if m.getSSHRoots != nil {
return m.getSSHKeys() return m.getSSHRoots()
}
return m.ret1.(*authority.SSHKeys), m.err
}
func (m *mockAuthority) GetSSHFederation() (*authority.SSHKeys, error) {
if m.getSSHFederation != nil {
return m.getSSHFederation()
} }
return m.ret1.(*authority.SSHKeys), m.err return m.ret1.(*authority.SSHKeys), m.err
} }

View file

@ -17,13 +17,13 @@ import (
type SSHAuthority interface { 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) GetSSHRoots() (*authority.SSHKeys, error)
GetSSHFederatedKeys() (*authority.SSHKeys, error) GetSSHFederation() (*authority.SSHKeys, error)
GetSSHConfig(typ string, data map[string]string) ([]templates.Output, error) GetSSHConfig(typ string, data map[string]string) ([]templates.Output, error)
} }
// SignSSHRequest is the request body of an SSH certificate request. // SSHSignRequest is the request body of an SSH certificate request.
type SignSSHRequest struct { type SSHSignRequest struct {
PublicKey []byte `json:"publicKey"` //base64 encoded PublicKey []byte `json:"publicKey"` //base64 encoded
OTT string `json:"ott"` OTT string `json:"ott"`
CertType string `json:"certType,omitempty"` CertType string `json:"certType,omitempty"`
@ -33,8 +33,8 @@ type SignSSHRequest struct {
AddUserPublicKey []byte `json:"addUserPublicKey,omitempty"` AddUserPublicKey []byte `json:"addUserPublicKey,omitempty"`
} }
// Validate validates the SignSSHRequest. // Validate validates the SSHSignRequest.
func (s *SignSSHRequest) Validate() error { func (s *SSHSignRequest) Validate() error {
switch { switch {
case s.CertType != "" && s.CertType != provisioner.SSHUserCert && s.CertType != provisioner.SSHHostCert: case s.CertType != "" && s.CertType != provisioner.SSHUserCert && s.CertType != provisioner.SSHHostCert:
return errors.Errorf("unknown certType %s", s.CertType) return errors.Errorf("unknown certType %s", s.CertType)
@ -47,15 +47,15 @@ func (s *SignSSHRequest) Validate() error {
} }
} }
// SignSSHResponse is the response object that returns the SSH certificate. // SSHSignResponse is the response object that returns the SSH certificate.
type SignSSHResponse struct { type SSHSignResponse struct {
Certificate SSHCertificate `json:"crt"` Certificate SSHCertificate `json:"crt"`
AddUserCertificate *SSHCertificate `json:"addUserCrt,omitempty"` AddUserCertificate *SSHCertificate `json:"addUserCrt,omitempty"`
} }
// SSHKeysResponse represents the response object that returns the SSH user and // SSHRootsResponse represents the response object that returns the SSH user and
// host keys. // host keys.
type SSHKeysResponse struct { type SSHRootsResponse struct {
UserKeys []SSHPublicKey `json:"userKey,omitempty"` UserKeys []SSHPublicKey `json:"userKey,omitempty"`
HostKeys []SSHPublicKey `json:"hostKey,omitempty"` HostKeys []SSHPublicKey `json:"hostKey,omitempty"`
} }
@ -170,11 +170,11 @@ type SSHConfigResponse struct {
HostTemplates []Template `json:"hostTemplates,omitempty"` HostTemplates []Template `json:"hostTemplates,omitempty"`
} }
// SignSSH is an HTTP handler that reads an SignSSHRequest with a one-time-token // SSHSign is an HTTP handler that reads an SignSSHRequest with a one-time-token
// (ott) from the body and creates a new SSH certificate with the information in // (ott) from the body and creates a new SSH certificate with the information in
// the request. // the request.
func (h *caHandler) SignSSH(w http.ResponseWriter, r *http.Request) { func (h *caHandler) SSHSign(w http.ResponseWriter, r *http.Request) {
var body SignSSHRequest var body SSHSignRequest
if err := ReadJSON(r.Body, &body); err != nil { if err := ReadJSON(r.Body, &body); err != nil {
WriteError(w, BadRequest(errors.Wrap(err, "error reading request body"))) WriteError(w, BadRequest(errors.Wrap(err, "error reading request body")))
return return
@ -232,16 +232,16 @@ func (h *caHandler) SignSSH(w http.ResponseWriter, r *http.Request) {
} }
w.WriteHeader(http.StatusCreated) w.WriteHeader(http.StatusCreated)
JSON(w, &SignSSHResponse{ JSON(w, &SSHSignResponse{
Certificate: SSHCertificate{cert}, Certificate: SSHCertificate{cert},
AddUserCertificate: addUserCertificate, AddUserCertificate: addUserCertificate,
}) })
} }
// SSHKeys is an HTTP handler that returns the SSH public keys for user and host // SSHRoots is an HTTP handler that returns the SSH public keys for user and host
// certificates. // certificates.
func (h *caHandler) SSHKeys(w http.ResponseWriter, r *http.Request) { func (h *caHandler) SSHRoots(w http.ResponseWriter, r *http.Request) {
keys, err := h.Authority.GetSSHKeys() keys, err := h.Authority.GetSSHRoots()
if err != nil { if err != nil {
WriteError(w, InternalServerError(err)) WriteError(w, InternalServerError(err))
return return
@ -252,7 +252,7 @@ func (h *caHandler) SSHKeys(w http.ResponseWriter, r *http.Request) {
return return
} }
resp := new(SSHKeysResponse) resp := new(SSHRootsResponse)
for _, k := range keys.HostKeys { for _, k := range keys.HostKeys {
resp.HostKeys = append(resp.HostKeys, SSHPublicKey{PublicKey: k}) resp.HostKeys = append(resp.HostKeys, SSHPublicKey{PublicKey: k})
} }
@ -263,10 +263,10 @@ func (h *caHandler) SSHKeys(w http.ResponseWriter, r *http.Request) {
JSON(w, resp) JSON(w, resp)
} }
// SSHFederatedKeys is an HTTP handler that returns the federated SSH public // SSHFederation is an HTTP handler that returns the federated SSH public keys
// keys for user and host certificates. // for user and host certificates.
func (h *caHandler) SSHFederatedKeys(w http.ResponseWriter, r *http.Request) { func (h *caHandler) SSHFederation(w http.ResponseWriter, r *http.Request) {
keys, err := h.Authority.GetSSHFederatedKeys() keys, err := h.Authority.GetSSHFederation()
if err != nil { if err != nil {
WriteError(w, NotFound(err)) WriteError(w, NotFound(err))
return return
@ -277,7 +277,7 @@ func (h *caHandler) SSHFederatedKeys(w http.ResponseWriter, r *http.Request) {
return return
} }
resp := new(SSHKeysResponse) resp := new(SSHRootsResponse)
for _, k := range keys.HostKeys { for _, k := range keys.HostKeys {
resp.HostKeys = append(resp.HostKeys, SSHPublicKey{PublicKey: k}) resp.HostKeys = append(resp.HostKeys, SSHPublicKey{PublicKey: k})
} }

View file

@ -219,7 +219,7 @@ func TestSignSSHRequest_Validate(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
s := &SignSSHRequest{ s := &SSHSignRequest{
PublicKey: tt.fields.PublicKey, PublicKey: tt.fields.PublicKey,
OTT: tt.fields.OTT, OTT: tt.fields.OTT,
CertType: tt.fields.CertType, CertType: tt.fields.CertType,
@ -235,7 +235,7 @@ func TestSignSSHRequest_Validate(t *testing.T) {
} }
} }
func Test_caHandler_SignSSH(t *testing.T) { func Test_caHandler_SSHSign(t *testing.T) {
user, err := getSignedUserCertificate() user, err := getSignedUserCertificate()
assert.FatalError(t, err) assert.FatalError(t, err)
host, err := getSignedHostCertificate() host, err := getSignedHostCertificate()
@ -244,17 +244,17 @@ func Test_caHandler_SignSSH(t *testing.T) {
userB64 := base64.StdEncoding.EncodeToString(user.Marshal()) userB64 := base64.StdEncoding.EncodeToString(user.Marshal())
hostB64 := base64.StdEncoding.EncodeToString(host.Marshal()) hostB64 := base64.StdEncoding.EncodeToString(host.Marshal())
userReq, err := json.Marshal(SignSSHRequest{ userReq, err := json.Marshal(SSHSignRequest{
PublicKey: user.Key.Marshal(), PublicKey: user.Key.Marshal(),
OTT: "ott", OTT: "ott",
}) })
assert.FatalError(t, err) assert.FatalError(t, err)
hostReq, err := json.Marshal(SignSSHRequest{ hostReq, err := json.Marshal(SSHSignRequest{
PublicKey: host.Key.Marshal(), PublicKey: host.Key.Marshal(),
OTT: "ott", OTT: "ott",
}) })
assert.FatalError(t, err) assert.FatalError(t, err)
userAddReq, err := json.Marshal(SignSSHRequest{ userAddReq, err := json.Marshal(SSHSignRequest{
PublicKey: user.Key.Marshal(), PublicKey: user.Key.Marshal(),
OTT: "ott", OTT: "ott",
AddUserPublicKey: user.Key.Marshal(), AddUserPublicKey: user.Key.Marshal(),
@ -299,7 +299,7 @@ func Test_caHandler_SignSSH(t *testing.T) {
req := httptest.NewRequest("POST", "http://example.com/ssh/sign", bytes.NewReader(tt.req)) req := httptest.NewRequest("POST", "http://example.com/ssh/sign", bytes.NewReader(tt.req))
w := httptest.NewRecorder() w := httptest.NewRecorder()
h.SignSSH(logging.NewResponseLogger(w), req) h.SSHSign(logging.NewResponseLogger(w), req)
res := w.Result() res := w.Result()
if res.StatusCode != tt.statusCode { if res.StatusCode != tt.statusCode {
@ -320,7 +320,7 @@ func Test_caHandler_SignSSH(t *testing.T) {
} }
} }
func Test_caHandler_SSHKeys(t *testing.T) { func Test_caHandler_SSHRoots(t *testing.T) {
user, err := ssh.NewPublicKey(sshUserKey.Public()) user, err := ssh.NewPublicKey(sshUserKey.Public())
assert.FatalError(t, err) assert.FatalError(t, err)
userB64 := base64.StdEncoding.EncodeToString(user.Marshal()) userB64 := base64.StdEncoding.EncodeToString(user.Marshal())
@ -336,22 +336,23 @@ func Test_caHandler_SSHKeys(t *testing.T) {
body []byte body []byte
statusCode int statusCode int
}{ }{
{"ok", &authority.SSHKeys{HostKey: host, UserKey: user}, nil, []byte(fmt.Sprintf(`{"userKey":"%s","hostKey":"%s"}`, userB64, hostB64)), http.StatusOK}, {"ok", &authority.SSHKeys{HostKeys: []ssh.PublicKey{host}, UserKeys: []ssh.PublicKey{user}}, nil, []byte(fmt.Sprintf(`{"userKey":["%s"],"hostKey":["%s"]}`, userB64, hostB64)), http.StatusOK},
{"user", &authority.SSHKeys{UserKey: user}, nil, []byte(fmt.Sprintf(`{"userKey":"%s"}`, userB64)), http.StatusOK}, {"user", &authority.SSHKeys{UserKeys: []ssh.PublicKey{user}}, nil, []byte(fmt.Sprintf(`{"userKey":["%s"]}`, userB64)), http.StatusOK},
{"host", &authority.SSHKeys{HostKey: host}, nil, []byte(fmt.Sprintf(`{"hostKey":"%s"}`, hostB64)), http.StatusOK}, {"host", &authority.SSHKeys{HostKeys: []ssh.PublicKey{host}}, nil, []byte(fmt.Sprintf(`{"hostKey":["%s"]}`, hostB64)), http.StatusOK},
{"error", nil, fmt.Errorf("an error"), nil, http.StatusNotFound}, {"empty", &authority.SSHKeys{}, nil, nil, http.StatusNotFound},
{"error", nil, fmt.Errorf("an error"), nil, http.StatusInternalServerError},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
h := New(&mockAuthority{ h := New(&mockAuthority{
getSSHKeys: func() (*authority.SSHKeys, error) { getSSHRoots: func() (*authority.SSHKeys, error) {
return tt.keys, tt.keysErr return tt.keys, tt.keysErr
}, },
}).(*caHandler) }).(*caHandler)
req := httptest.NewRequest("GET", "http://example.com/ssh/keys", http.NoBody) req := httptest.NewRequest("GET", "http://example.com/ssh/keys", http.NoBody)
w := httptest.NewRecorder() w := httptest.NewRecorder()
h.SSHKeys(logging.NewResponseLogger(w), req) h.SSHRoots(logging.NewResponseLogger(w), req)
res := w.Result() res := w.Result()
if res.StatusCode != tt.statusCode { if res.StatusCode != tt.statusCode {

View file

@ -87,16 +87,16 @@ type SSHKeys struct {
HostKeys []ssh.PublicKey HostKeys []ssh.PublicKey
} }
// GetSSHKeys returns the SSH User and Host public keys. // GetSSHRoots returns the SSH User and Host public keys.
func (a *Authority) GetSSHKeys() (*SSHKeys, error) { func (a *Authority) GetSSHRoots() (*SSHKeys, error) {
return &SSHKeys{ return &SSHKeys{
HostKeys: a.sshCAHostCerts, HostKeys: a.sshCAHostCerts,
UserKeys: a.sshCAUserCerts, UserKeys: a.sshCAUserCerts,
}, nil }, nil
} }
// GetSSHFederatedKeys returns the public keys for federated SSH signers. // GetSSHFederation returns the public keys for federated SSH signers.
func (a *Authority) GetSSHFederatedKeys() (*SSHKeys, error) { func (a *Authority) GetSSHFederation() (*SSHKeys, error) {
return &SSHKeys{ return &SSHKeys{
HostKeys: a.sshCAHostFederatedCerts, HostKeys: a.sshCAHostFederatedCerts,
UserKeys: a.sshCAUserFederatedCerts, UserKeys: a.sshCAUserFederatedCerts,

View file

@ -373,28 +373,6 @@ func (c *Client) Sign(req *api.SignRequest) (*api.SignResponse, error) {
return &sign, nil return &sign, nil
} }
// SignSSH performs the SSH certificate sign request to the CA and returns the
// api.SignSSHResponse struct.
func (c *Client) SignSSH(req *api.SignSSHRequest) (*api.SignSSHResponse, error) {
body, err := json.Marshal(req)
if err != nil {
return nil, errors.Wrap(err, "error marshaling request")
}
u := c.endpoint.ResolveReference(&url.URL{Path: "/ssh/sign"})
resp, err := c.client.Post(u.String(), "application/json", bytes.NewReader(body))
if err != nil {
return nil, errors.Wrapf(err, "client POST %s failed", u)
}
if resp.StatusCode >= 400 {
return nil, readError(resp.Body)
}
var sign api.SignSSHResponse
if err := readJSON(resp.Body, &sign); err != nil {
return nil, errors.Wrapf(err, "error reading %s", u)
}
return &sign, nil
}
// Renew performs the renew request to the CA and returns the api.SignResponse // Renew performs the renew request to the CA and returns the api.SignResponse
// struct. // struct.
func (c *Client) Renew(tr http.RoundTripper) (*api.SignResponse, error) { func (c *Client) Renew(tr http.RoundTripper) (*api.SignResponse, error) {
@ -527,10 +505,32 @@ func (c *Client) Federation() (*api.FederationResponse, error) {
return &federation, nil return &federation, nil
} }
// SSHKeys performs the get /ssh/keys request to the CA and returns the // SSHSign performs the POST /ssh/sign request to the CA and returns the
// api.SSHKeysResponse struct. // api.SSHSignResponse struct.
func (c *Client) SSHKeys() (*api.SSHKeysResponse, error) { func (c *Client) SSHSign(req *api.SSHSignRequest) (*api.SSHSignResponse, error) {
u := c.endpoint.ResolveReference(&url.URL{Path: "/ssh/keys"}) body, err := json.Marshal(req)
if err != nil {
return nil, errors.Wrap(err, "error marshaling request")
}
u := c.endpoint.ResolveReference(&url.URL{Path: "/ssh/sign"})
resp, err := c.client.Post(u.String(), "application/json", bytes.NewReader(body))
if err != nil {
return nil, errors.Wrapf(err, "client POST %s failed", u)
}
if resp.StatusCode >= 400 {
return nil, readError(resp.Body)
}
var sign api.SSHSignResponse
if err := readJSON(resp.Body, &sign); err != nil {
return nil, errors.Wrapf(err, "error reading %s", u)
}
return &sign, nil
}
// SSHRoots performs the GET /ssh/roots request to the CA and returns the
// api.SSHRootsResponse struct.
func (c *Client) SSHRoots() (*api.SSHRootsResponse, error) {
u := c.endpoint.ResolveReference(&url.URL{Path: "/ssh/roots"})
resp, err := c.client.Get(u.String()) resp, err := c.client.Get(u.String())
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "client GET %s failed", u) return nil, errors.Wrapf(err, "client GET %s failed", u)
@ -538,7 +538,7 @@ func (c *Client) SSHKeys() (*api.SSHKeysResponse, error) {
if resp.StatusCode >= 400 { if resp.StatusCode >= 400 {
return nil, readError(resp.Body) return nil, readError(resp.Body)
} }
var keys api.SSHKeysResponse var keys api.SSHRootsResponse
if err := readJSON(resp.Body, &keys); err != nil { if err := readJSON(resp.Body, &keys); err != nil {
return nil, errors.Wrapf(err, "error reading %s", u) return nil, errors.Wrapf(err, "error reading %s", u)
} }
@ -546,8 +546,8 @@ func (c *Client) SSHKeys() (*api.SSHKeysResponse, error) {
} }
// SSHFederation performs the get /ssh/federation request to the CA and returns // SSHFederation performs the get /ssh/federation request to the CA and returns
// the api.SSHKeysResponse struct. // the api.SSHRootsResponse struct.
func (c *Client) SSHFederation() (*api.SSHKeysResponse, error) { func (c *Client) SSHFederation() (*api.SSHRootsResponse, error) {
u := c.endpoint.ResolveReference(&url.URL{Path: "/ssh/federation"}) u := c.endpoint.ResolveReference(&url.URL{Path: "/ssh/federation"})
resp, err := c.client.Get(u.String()) resp, err := c.client.Get(u.String())
if err != nil { if err != nil {
@ -556,15 +556,15 @@ func (c *Client) SSHFederation() (*api.SSHKeysResponse, error) {
if resp.StatusCode >= 400 { if resp.StatusCode >= 400 {
return nil, readError(resp.Body) return nil, readError(resp.Body)
} }
var keys api.SSHKeysResponse var keys api.SSHRootsResponse
if err := readJSON(resp.Body, &keys); err != nil { if err := readJSON(resp.Body, &keys); err != nil {
return nil, errors.Wrapf(err, "error reading %s", u) return nil, errors.Wrapf(err, "error reading %s", u)
} }
return &keys, nil return &keys, nil
} }
// SSHConfig performs the POST request to the CA to get the ssh configuration // SSHConfig performs the POST /ssh/config request to the CA to get the ssh
// templates. // configuration templates.
func (c *Client) SSHConfig(req *api.SSHConfigRequest) (*api.SSHConfigResponse, error) { func (c *Client) SSHConfig(req *api.SSHConfigRequest) (*api.SSHConfigResponse, error) {
body, err := json.Marshal(req) body, err := json.Marshal(req)
if err != nil { if err != nil {

View file

@ -722,15 +722,15 @@ func TestClient_Federation(t *testing.T) {
} }
} }
func TestClient_SSHKeys(t *testing.T) { func TestClient_SSHRoots(t *testing.T) {
key, err := ssh.NewPublicKey(mustKey().Public()) key, err := ssh.NewPublicKey(mustKey().Public())
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
ok := &api.SSHKeysResponse{ ok := &api.SSHRootsResponse{
HostKey: &api.SSHPublicKey{PublicKey: key}, HostKeys: []api.SSHPublicKey{{PublicKey: key}},
UserKey: &api.SSHPublicKey{PublicKey: key}, UserKeys: []api.SSHPublicKey{{PublicKey: key}},
} }
notFound := api.NotFound(fmt.Errorf("Not Found")) notFound := api.NotFound(fmt.Errorf("Not Found"))
@ -759,7 +759,7 @@ func TestClient_SSHKeys(t *testing.T) {
api.JSONStatus(w, tt.response, tt.responseCode) api.JSONStatus(w, tt.response, tt.responseCode)
}) })
got, err := c.SSHKeys() got, err := c.SSHRoots()
if (err != nil) != tt.wantErr { if (err != nil) != tt.wantErr {
fmt.Printf("%+v", err) fmt.Printf("%+v", err)
t.Errorf("Client.SSHKeys() error = %v, wantErr %v", err, tt.wantErr) t.Errorf("Client.SSHKeys() error = %v, wantErr %v", err, tt.wantErr)