forked from TrueCloudLab/certificates
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,7 +339,7 @@ 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