Add support for multiple SCEP provisioners
Similarly to how ACME suppors multiple provisioners, it's now possible to load the right provisioner based on the URL.
This commit is contained in:
parent
a191319da9
commit
5df60c5a9b
4 changed files with 78 additions and 26 deletions
2
ca/ca.go
2
ca/ca.go
|
@ -212,7 +212,7 @@ func (ca *CA) Init(config *config.Config) (*CA, error) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// helpful routine for logging all routes //
|
// helpful routine for logging all routes
|
||||||
//dumpRoutes(mux)
|
//dumpRoutes(mux)
|
||||||
|
|
||||||
// Add monitoring if configured
|
// Add monitoring if configured
|
||||||
|
|
|
@ -9,8 +9,10 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/go-chi/chi"
|
||||||
"github.com/smallstep/certificates/api"
|
"github.com/smallstep/certificates/api"
|
||||||
"github.com/smallstep/certificates/authority/provisioner"
|
"github.com/smallstep/certificates/authority/provisioner"
|
||||||
"github.com/smallstep/certificates/scep"
|
"github.com/smallstep/certificates/scep"
|
||||||
|
@ -76,14 +78,10 @@ func New(scepAuth scep.Interface) api.RouterHandler {
|
||||||
|
|
||||||
// Route traffic and implement the Router interface.
|
// Route traffic and implement the Router interface.
|
||||||
func (h *Handler) Route(r api.Router) {
|
func (h *Handler) Route(r api.Router) {
|
||||||
//getLink := h.Auth.GetLinkExplicit
|
getLink := h.Auth.GetLinkExplicit
|
||||||
//fmt.Println(getLink)
|
|
||||||
|
|
||||||
//r.MethodFunc("GET", "/bla", h.baseURLFromRequest(h.lookupProvisioner(nil)))
|
r.MethodFunc(http.MethodGet, getLink("{provisionerID}", false, nil), h.lookupProvisioner(h.Get))
|
||||||
//r.MethodFunc("GET", getLink(acme.NewNonceLink, "{provisionerID}", false, nil), h.baseURLFromRequest(h.lookupProvisioner(h.addNonce(h.GetNonce))))
|
r.MethodFunc(http.MethodPost, getLink("{provisionerID}", false, nil), h.lookupProvisioner(h.Post))
|
||||||
|
|
||||||
r.MethodFunc(http.MethodGet, "/", h.lookupProvisioner(h.Get))
|
|
||||||
r.MethodFunc(http.MethodPost, "/", h.lookupProvisioner(h.Post))
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,16 +200,12 @@ func decodeSCEPRequest(r *http.Request) (SCEPRequest, error) {
|
||||||
func (h *Handler) lookupProvisioner(next nextHTTP) nextHTTP {
|
func (h *Handler) lookupProvisioner(next nextHTTP) nextHTTP {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
// name := chi.URLParam(r, "provisionerID")
|
name := chi.URLParam(r, "provisionerID")
|
||||||
// provisionerID, err := url.PathUnescape(name)
|
provisionerID, err := url.PathUnescape(name)
|
||||||
// if err != nil {
|
if err != nil {
|
||||||
// api.WriteError(w, fmt.Errorf("error url unescaping provisioner id '%s'", name))
|
api.WriteError(w, fmt.Errorf("error url unescaping provisioner id '%s'", name))
|
||||||
// return
|
return
|
||||||
// }
|
}
|
||||||
|
|
||||||
// TODO: make this configurable; and we might want to look at being able to provide multiple,
|
|
||||||
// like the ACME one? The below assumes a SCEP provider (scep/) called "scep1" exists.
|
|
||||||
provisionerID := "scep1"
|
|
||||||
|
|
||||||
p, err := h.Auth.LoadProvisionerByID("scep/" + provisionerID)
|
p, err := h.Auth.LoadProvisionerByID("scep/" + provisionerID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -275,6 +269,8 @@ func (h *Handler) PKIOperation(ctx context.Context, request SCEPRequest) (SCEPRe
|
||||||
|
|
||||||
response := SCEPResponse{Operation: opnPKIOperation}
|
response := SCEPResponse{Operation: opnPKIOperation}
|
||||||
|
|
||||||
|
fmt.Println("BEFORE PARSING")
|
||||||
|
|
||||||
microMsg, err := microscep.ParsePKIMessage(request.Message)
|
microMsg, err := microscep.ParsePKIMessage(request.Message)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return SCEPResponse{}, err
|
return SCEPResponse{}, err
|
||||||
|
@ -287,7 +283,12 @@ func (h *Handler) PKIOperation(ctx context.Context, request SCEPRequest) (SCEPRe
|
||||||
Raw: microMsg.Raw,
|
Raw: microMsg.Raw,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.Println("len raw:", len(microMsg.Raw))
|
||||||
|
|
||||||
|
fmt.Println("AFTER PARSING")
|
||||||
|
|
||||||
if err := h.Auth.DecryptPKIEnvelope(ctx, msg); err != nil {
|
if err := h.Auth.DecryptPKIEnvelope(ctx, msg); err != nil {
|
||||||
|
fmt.Println("ERROR IN DECRYPTPKIENVELOPE")
|
||||||
return SCEPResponse{}, err
|
return SCEPResponse{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,6 +312,8 @@ func (h *Handler) PKIOperation(ctx context.Context, request SCEPRequest) (SCEPRe
|
||||||
response.Data = certRep.Raw
|
response.Data = certRep.Raw
|
||||||
response.Certificate = certRep.Certificate
|
response.Certificate = certRep.Certificate
|
||||||
|
|
||||||
|
fmt.Println("HERE!!!")
|
||||||
|
|
||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -336,8 +339,8 @@ func writeSCEPResponse(w http.ResponseWriter, response SCEPResponse) {
|
||||||
|
|
||||||
func writeError(w http.ResponseWriter, err error) {
|
func writeError(w http.ResponseWriter, err error) {
|
||||||
scepError := &scep.Error{
|
scepError := &scep.Error{
|
||||||
Err: fmt.Errorf("post request failed: %w", err),
|
Message: err.Error(),
|
||||||
Status: http.StatusInternalServerError, // TODO: make this a param?
|
Status: http.StatusInternalServerError, // TODO: make this a param?
|
||||||
}
|
}
|
||||||
api.WriteError(w, scepError)
|
api.WriteError(w, scepError)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
"github.com/smallstep/certificates/authority/provisioner"
|
"github.com/smallstep/certificates/authority/provisioner"
|
||||||
database "github.com/smallstep/certificates/db"
|
database "github.com/smallstep/certificates/db"
|
||||||
|
@ -55,6 +56,8 @@ type Interface interface {
|
||||||
GetCACertificates() ([]*x509.Certificate, error)
|
GetCACertificates() ([]*x509.Certificate, error)
|
||||||
DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) error
|
DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) error
|
||||||
SignCSR(ctx context.Context, csr *x509.CertificateRequest, msg *PKIMessage) (*PKIMessage, error)
|
SignCSR(ctx context.Context, csr *x509.CertificateRequest, msg *PKIMessage) (*PKIMessage, error)
|
||||||
|
|
||||||
|
GetLinkExplicit(provName string, absoluteLink bool, baseURL *url.URL, inputs ...string) string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Authority is the layer that handles all SCEP interactions.
|
// Authority is the layer that handles all SCEP interactions.
|
||||||
|
@ -130,6 +133,44 @@ func (a *Authority) LoadProvisionerByID(id string) (provisioner.Interface, error
|
||||||
return a.signAuth.LoadProvisionerByID(id)
|
return a.signAuth.LoadProvisionerByID(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetLinkExplicit returns the requested link from the directory.
|
||||||
|
func (a *Authority) GetLinkExplicit(provName string, abs bool, baseURL *url.URL, inputs ...string) string {
|
||||||
|
// TODO: taken from ACME; move it to directory (if we need a directory in SCEP)?
|
||||||
|
return a.getLinkExplicit(provName, abs, baseURL, inputs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getLinkExplicit returns an absolute or partial path to the given resource and a base
|
||||||
|
// URL dynamically obtained from the request for which the link is being calculated.
|
||||||
|
func (a *Authority) getLinkExplicit(provisionerName string, abs bool, baseURL *url.URL, inputs ...string) string {
|
||||||
|
|
||||||
|
// TODO: do we need to provide a way to provide a different suffix/base?
|
||||||
|
// Like "/cgi-bin/pkiclient.exe"? Or would it be enough to have that as the name?
|
||||||
|
link := fmt.Sprintf("/%s", provisionerName)
|
||||||
|
|
||||||
|
if abs {
|
||||||
|
// Copy the baseURL value from the pointer. https://github.com/golang/go/issues/38351
|
||||||
|
u := url.URL{}
|
||||||
|
if baseURL != nil {
|
||||||
|
u = *baseURL
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no Scheme is set, then default to https.
|
||||||
|
if u.Scheme == "" {
|
||||||
|
u.Scheme = "https"
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no Host is set, then use the default (first DNS attr in the ca.json).
|
||||||
|
if u.Host == "" {
|
||||||
|
u.Host = a.dns
|
||||||
|
}
|
||||||
|
|
||||||
|
u.Path = a.prefix + link
|
||||||
|
return u.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
return link
|
||||||
|
}
|
||||||
|
|
||||||
// GetCACertificates returns the certificate (chain) for the CA
|
// GetCACertificates returns the certificate (chain) for the CA
|
||||||
func (a *Authority) GetCACertificates() ([]*x509.Certificate, error) {
|
func (a *Authority) GetCACertificates() ([]*x509.Certificate, error) {
|
||||||
|
|
||||||
|
@ -164,6 +205,8 @@ func (a *Authority) DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) err
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.Println("len content:", len(p7.Content))
|
||||||
|
|
||||||
var tID microscep.TransactionID
|
var tID microscep.TransactionID
|
||||||
if err := p7.UnmarshalSignedAttribute(oidSCEPtransactionID, &tID); err != nil {
|
if err := p7.UnmarshalSignedAttribute(oidSCEPtransactionID, &tID); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -176,11 +219,17 @@ func (a *Authority) DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) err
|
||||||
|
|
||||||
msg.p7 = p7
|
msg.p7 = p7
|
||||||
|
|
||||||
|
//p7c, err := pkcs7.Parse(p7.Content)
|
||||||
p7c, err := pkcs7.Parse(p7.Content)
|
p7c, err := pkcs7.Parse(p7.Content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.Println(tID)
|
||||||
|
fmt.Println(msgType)
|
||||||
|
|
||||||
|
fmt.Println("len p7c content:", len(p7c.Content))
|
||||||
|
|
||||||
envelope, err := p7c.Decrypt(a.intermediateCertificate, a.service.Decrypter)
|
envelope, err := p7c.Decrypt(a.intermediateCertificate, a.service.Decrypter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -308,7 +357,7 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m
|
||||||
// fmt.Println(string(cert.SubjectKeyId))
|
// fmt.Println(string(cert.SubjectKeyId))
|
||||||
|
|
||||||
// create a degenerate cert structure
|
// create a degenerate cert structure
|
||||||
deg, err := DegenerateCertificates([]*x509.Certificate{cert})
|
deg, err := degenerateCertificates([]*x509.Certificate{cert})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -380,8 +429,8 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m
|
||||||
return crepMsg, nil
|
return crepMsg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DegenerateCertificates creates degenerate certificates pkcs#7 type
|
// degenerateCertificates creates degenerate certificates pkcs#7 type
|
||||||
func DegenerateCertificates(certs []*x509.Certificate) ([]byte, error) {
|
func degenerateCertificates(certs []*x509.Certificate) ([]byte, error) {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
for _, cert := range certs {
|
for _, cert := range certs {
|
||||||
buf.Write(cert.Raw)
|
buf.Write(cert.Raw)
|
||||||
|
|
|
@ -4,8 +4,8 @@ package scep
|
||||||
type Error struct {
|
type Error struct {
|
||||||
// Type ProbType
|
// Type ProbType
|
||||||
// Detail string
|
// Detail string
|
||||||
Err error
|
Message string `json:"message"`
|
||||||
Status int
|
Status int `json:"-"`
|
||||||
// Sub []*Error
|
// Sub []*Error
|
||||||
// Identifier *Identifier
|
// Identifier *Identifier
|
||||||
}
|
}
|
||||||
|
@ -15,5 +15,5 @@ func (e *Error) Error() string {
|
||||||
// if e.Err == nil {
|
// if e.Err == nil {
|
||||||
// return e.Detail
|
// return e.Detail
|
||||||
// }
|
// }
|
||||||
return e.Err.Error()
|
return e.Message
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue