diff options
Diffstat (limited to 'vendor/github.com/smallstep/certificates/api/ssh.go')
| -rw-r--r-- | vendor/github.com/smallstep/certificates/api/ssh.go | 102 |
1 files changed, 68 insertions, 34 deletions
diff --git a/vendor/github.com/smallstep/certificates/api/ssh.go b/vendor/github.com/smallstep/certificates/api/ssh.go index 08294c7..dd70e5e 100644 --- a/vendor/github.com/smallstep/certificates/api/ssh.go +++ b/vendor/github.com/smallstep/certificates/api/ssh.go @@ -6,8 +6,11 @@ import ( "encoding/base64" "encoding/json" "net/http" + "net/url" + "strings" "time" + "github.com/google/uuid" "github.com/pkg/errors" "golang.org/x/crypto/ssh" @@ -17,6 +20,7 @@ import ( "github.com/smallstep/certificates/authority/config" "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/errs" + "github.com/smallstep/certificates/internal/cast" "github.com/smallstep/certificates/templates" ) @@ -253,19 +257,19 @@ type SSHBastionResponse struct { func SSHSign(w http.ResponseWriter, r *http.Request) { var body SSHSignRequest if err := read.JSON(r.Body, &body); err != nil { - render.Error(w, errs.BadRequestErr(err, "error reading request body")) + render.Error(w, r, errs.BadRequestErr(err, "error reading request body")) return } logOtt(w, body.OTT) if err := body.Validate(); err != nil { - render.Error(w, err) + render.Error(w, r, err) return } publicKey, err := ssh.ParsePublicKey(body.PublicKey) if err != nil { - render.Error(w, errs.BadRequestErr(err, "error parsing publicKey")) + render.Error(w, r, errs.BadRequestErr(err, "error parsing publicKey")) return } @@ -273,7 +277,7 @@ func SSHSign(w http.ResponseWriter, r *http.Request) { if body.AddUserPublicKey != nil { addUserPublicKey, err = ssh.ParsePublicKey(body.AddUserPublicKey) if err != nil { - render.Error(w, errs.BadRequestErr(err, "error parsing addUserPublicKey")) + render.Error(w, r, errs.BadRequestErr(err, "error parsing addUserPublicKey")) return } } @@ -289,17 +293,18 @@ func SSHSign(w http.ResponseWriter, r *http.Request) { ctx := provisioner.NewContextWithMethod(r.Context(), provisioner.SSHSignMethod) ctx = provisioner.NewContextWithToken(ctx, body.OTT) + ctx = provisioner.NewContextWithCertType(ctx, opts.CertType) a := mustAuthority(ctx) signOpts, err := a.Authorize(ctx, body.OTT) if err != nil { - render.Error(w, errs.UnauthorizedErr(err)) + render.Error(w, r, errs.UnauthorizedErr(err)) return } cert, err := a.SignSSH(ctx, publicKey, opts, signOpts...) if err != nil { - render.Error(w, errs.ForbiddenErr(err, "error signing ssh certificate")) + render.Error(w, r, errs.ForbiddenErr(err, "error signing ssh certificate")) return } @@ -307,7 +312,7 @@ func SSHSign(w http.ResponseWriter, r *http.Request) { if addUserPublicKey != nil && authority.IsValidForAddUser(cert) == nil { addUserCert, err := a.SignSSHAddUser(ctx, addUserPublicKey, cert) if err != nil { - render.Error(w, errs.ForbiddenErr(err, "error signing ssh certificate")) + render.Error(w, r, errs.ForbiddenErr(err, "error signing ssh certificate")) return } addUserCertificate = &SSHCertificate{addUserCert} @@ -320,26 +325,27 @@ func SSHSign(w http.ResponseWriter, r *http.Request) { ctx = provisioner.NewContextWithMethod(ctx, provisioner.SignIdentityMethod) signOpts, err := a.Authorize(ctx, body.OTT) if err != nil { - render.Error(w, errs.UnauthorizedErr(err)) + render.Error(w, r, errs.UnauthorizedErr(err)) return } // Enforce the same duration as ssh certificate. signOpts = append(signOpts, &identityModifier{ - NotBefore: time.Unix(int64(cert.ValidAfter), 0), - NotAfter: time.Unix(int64(cert.ValidBefore), 0), + Identity: getIdentityURI(cr), + NotBefore: time.Unix(cast.Int64(cert.ValidAfter), 0), + NotAfter: time.Unix(cast.Int64(cert.ValidBefore), 0), }) certChain, err := a.SignWithContext(ctx, cr, provisioner.SignOptions{}, signOpts...) if err != nil { - render.Error(w, errs.ForbiddenErr(err, "error signing identity certificate")) + render.Error(w, r, errs.ForbiddenErr(err, "error signing identity certificate")) return } identityCertificate = certChainToPEM(certChain) } LogSSHCertificate(w, cert) - render.JSONStatus(w, &SSHSignResponse{ + render.JSONStatus(w, r, &SSHSignResponse{ Certificate: SSHCertificate{cert}, AddUserCertificate: addUserCertificate, IdentityCertificate: identityCertificate, @@ -352,12 +358,12 @@ func SSHRoots(w http.ResponseWriter, r *http.Request) { ctx := r.Context() keys, err := mustAuthority(ctx).GetSSHRoots(ctx) if err != nil { - render.Error(w, errs.InternalServerErr(err)) + render.Error(w, r, errs.InternalServerErr(err)) return } if len(keys.HostKeys) == 0 && len(keys.UserKeys) == 0 { - render.Error(w, errs.NotFound("no keys found")) + render.Error(w, r, errs.NotFound("no keys found")) return } @@ -369,7 +375,7 @@ func SSHRoots(w http.ResponseWriter, r *http.Request) { resp.UserKeys = append(resp.UserKeys, SSHPublicKey{PublicKey: k}) } - render.JSON(w, resp) + render.JSON(w, r, resp) } // SSHFederation is an HTTP handler that returns the federated SSH public keys @@ -378,12 +384,12 @@ func SSHFederation(w http.ResponseWriter, r *http.Request) { ctx := r.Context() keys, err := mustAuthority(ctx).GetSSHFederation(ctx) if err != nil { - render.Error(w, errs.InternalServerErr(err)) + render.Error(w, r, errs.InternalServerErr(err)) return } if len(keys.HostKeys) == 0 && len(keys.UserKeys) == 0 { - render.Error(w, errs.NotFound("no keys found")) + render.Error(w, r, errs.NotFound("no keys found")) return } @@ -395,7 +401,7 @@ func SSHFederation(w http.ResponseWriter, r *http.Request) { resp.UserKeys = append(resp.UserKeys, SSHPublicKey{PublicKey: k}) } - render.JSON(w, resp) + render.JSON(w, r, resp) } // SSHConfig is an HTTP handler that returns rendered templates for ssh clients @@ -403,18 +409,18 @@ func SSHFederation(w http.ResponseWriter, r *http.Request) { func SSHConfig(w http.ResponseWriter, r *http.Request) { var body SSHConfigRequest if err := read.JSON(r.Body, &body); err != nil { - render.Error(w, errs.BadRequestErr(err, "error reading request body")) + render.Error(w, r, errs.BadRequestErr(err, "error reading request body")) return } if err := body.Validate(); err != nil { - render.Error(w, err) + render.Error(w, r, err) return } ctx := r.Context() ts, err := mustAuthority(ctx).GetSSHConfig(ctx, body.Type, body.Data) if err != nil { - render.Error(w, errs.InternalServerErr(err)) + render.Error(w, r, errs.InternalServerErr(err)) return } @@ -425,32 +431,32 @@ func SSHConfig(w http.ResponseWriter, r *http.Request) { case provisioner.SSHHostCert: cfg.HostTemplates = ts default: - render.Error(w, errs.InternalServer("it should hot get here")) + render.Error(w, r, errs.InternalServer("it should hot get here")) return } - render.JSON(w, cfg) + render.JSON(w, r, cfg) } // SSHCheckHost is the HTTP handler that returns if a hosts certificate exists or not. func SSHCheckHost(w http.ResponseWriter, r *http.Request) { var body SSHCheckPrincipalRequest if err := read.JSON(r.Body, &body); err != nil { - render.Error(w, errs.BadRequestErr(err, "error reading request body")) + render.Error(w, r, errs.BadRequestErr(err, "error reading request body")) return } if err := body.Validate(); err != nil { - render.Error(w, err) + render.Error(w, r, err) return } ctx := r.Context() exists, err := mustAuthority(ctx).CheckSSHHost(ctx, body.Principal, body.Token) if err != nil { - render.Error(w, errs.InternalServerErr(err)) + render.Error(w, r, errs.InternalServerErr(err)) return } - render.JSON(w, &SSHCheckPrincipalResponse{ + render.JSON(w, r, &SSHCheckPrincipalResponse{ Exists: exists, }) } @@ -465,10 +471,10 @@ func SSHGetHosts(w http.ResponseWriter, r *http.Request) { ctx := r.Context() hosts, err := mustAuthority(ctx).GetSSHHosts(ctx, cert) if err != nil { - render.Error(w, errs.InternalServerErr(err)) + render.Error(w, r, errs.InternalServerErr(err)) return } - render.JSON(w, &SSHGetHostsResponse{ + render.JSON(w, r, &SSHGetHostsResponse{ Hosts: hosts, }) } @@ -477,35 +483,63 @@ func SSHGetHosts(w http.ResponseWriter, r *http.Request) { func SSHBastion(w http.ResponseWriter, r *http.Request) { var body SSHBastionRequest if err := read.JSON(r.Body, &body); err != nil { - render.Error(w, errs.BadRequestErr(err, "error reading request body")) + render.Error(w, r, errs.BadRequestErr(err, "error reading request body")) return } if err := body.Validate(); err != nil { - render.Error(w, err) + render.Error(w, r, err) return } ctx := r.Context() bastion, err := mustAuthority(ctx).GetSSHBastion(ctx, body.User, body.Hostname) if err != nil { - render.Error(w, errs.InternalServerErr(err)) + render.Error(w, r, errs.InternalServerErr(err)) return } - render.JSON(w, &SSHBastionResponse{ + render.JSON(w, r, &SSHBastionResponse{ Hostname: body.Hostname, Bastion: bastion, }) } -// identityModifier is a custom modifier used to force a fixed duration. +// identityModifier is a custom modifier used to force a fixed duration, and set +// the identity URI. type identityModifier struct { + Identity *url.URL NotBefore time.Time NotAfter time.Time } +// Enforce implements the enforcer interface and sets the validity bounds and +// the identity uri to the certificate. func (m *identityModifier) Enforce(cert *x509.Certificate) error { cert.NotBefore = m.NotBefore cert.NotAfter = m.NotAfter + if m.Identity != nil { + var identityURL = m.Identity.String() + for _, u := range cert.URIs { + if u.String() == identityURL { + return nil + } + } + cert.URIs = append(cert.URIs, m.Identity) + } + + return nil +} + +// getIdentityURI returns the first valid UUID URN from the given CSR. +func getIdentityURI(cr *x509.CertificateRequest) *url.URL { + for _, u := range cr.URIs { + s := u.String() + // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + if len(s) == 9+36 && strings.EqualFold(s[:9], "urn:uuid:") { + if _, err := uuid.Parse(s); err == nil { + return u + } + } + } return nil } |
