diff --git a/api/client/api.go b/api/client/api.go index 8ea42a9b2..22abf8777 100644 --- a/api/client/api.go +++ b/api/client/api.go @@ -4,16 +4,3 @@ package client type SignResult struct { Certificate []byte `json:"certificate"` } - -// InfoReq is the request struct for an info API request. -type InfoReq struct { - Label string `json:"label"` - Profile string `json:"profile"` -} - -// InfoResp is the response for an Info API request. -type InfoResp struct { - Certificate string `json:"certificate"` - Usage []string `json:"usages"` - ExpiryString string `json:"expiry"` -} diff --git a/api/client/client.go b/api/client/client.go index 73cc0d1ea..c649b4343 100644 --- a/api/client/client.go +++ b/api/client/client.go @@ -15,6 +15,7 @@ import ( "github.com/cloudflare/cfssl/api" "github.com/cloudflare/cfssl/auth" "github.com/cloudflare/cfssl/errors" + "github.com/cloudflare/cfssl/info" ) // A Server points to a remote CFSSL instance. @@ -148,13 +149,13 @@ func (srv *Server) Sign(jsonData []byte) ([]byte, error) { // Info sends an info request to the remote CFSSL server, receiving a // response or an error in response. // It takes the serialized JSON request to send. -func (srv *Server) Info(jsonData []byte) (*InfoResp, error) { +func (srv *Server) Info(jsonData []byte) (*info.Resp, error) { res, err := srv.getResultMap(jsonData, "info") if err != nil { return nil, err } - info := new(InfoResp) + info := new(info.Resp) if val, ok := res["certificate"]; ok { info.Certificate = val.(string) diff --git a/api/info/info.go b/api/info/info.go index 4e527c09f..fc401c84c 100644 --- a/api/info/info.go +++ b/api/info/info.go @@ -8,10 +8,10 @@ import ( "net/http" "github.com/cloudflare/cfssl/api" - "github.com/cloudflare/cfssl/api/client" "github.com/cloudflare/cfssl/bundler" "github.com/cloudflare/cfssl/config" "github.com/cloudflare/cfssl/errors" + "github.com/cloudflare/cfssl/info" "github.com/cloudflare/cfssl/log" "github.com/cloudflare/cfssl/signer" @@ -38,7 +38,7 @@ func NewHandler(s signer.Signer) (http.Handler, error) { // Handle listens for incoming requests for CA information, and returns // a list containing information on each root certificate. func (h *Handler) Handle(w http.ResponseWriter, r *http.Request) error { - req := new(client.InfoReq) + req := new(info.Req) body, err := ioutil.ReadAll(r.Body) if err != nil { log.Warningf("failed to read request body: %v", err) @@ -70,7 +70,7 @@ func (h *Handler) Handle(w http.ResponseWriter, r *http.Request) error { return errors.Wrap(errors.APIClientError, errors.ClientHTTPError, goerr.New("profile must not be nil")) } - resp := client.InfoResp{ + resp := info.Resp{ Certificate: bundler.PemBlockToString(&pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}), Usage: profile.Usage, ExpiryString: profile.ExpiryString, @@ -107,7 +107,7 @@ func NewMultiHandler(signers map[string]signer.Signer, defaultLabel string) (htt // look up the signer whose public certificate should be retrieved. If // the label is empty, the default label is used. func (h *MultiHandler) Handle(w http.ResponseWriter, r *http.Request) error { - req := new(client.InfoReq) + req := new(info.Req) body, err := ioutil.ReadAll(r.Body) if err != nil { log.Warningf("failed to read request body: %v", err) @@ -136,7 +136,7 @@ func (h *MultiHandler) Handle(w http.ResponseWriter, r *http.Request) error { return err } - resp := client.InfoResp{ + resp := info.Resp{ Certificate: bundler.PemBlockToString(&pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}), } diff --git a/cli/info/info.go b/cli/info/info.go index 8ca37b42d..e2304aadb 100644 --- a/cli/info/info.go +++ b/cli/info/info.go @@ -13,6 +13,7 @@ import ( "github.com/cloudflare/cfssl/config" "github.com/cloudflare/cfssl/errors" "github.com/cloudflare/cfssl/helpers" + "github.com/cloudflare/cfssl/info" goerr "errors" ) @@ -29,9 +30,9 @@ Flags: var infoFlags = []string{"remote", "label", "profile", "config"} -func getInfoFromRemote(c cli.Config) (resp *client.InfoResp, err error) { +func getInfoFromRemote(c cli.Config) (resp *info.Resp, err error) { - req := new(client.InfoReq) + req := new(info.Req) req.Label = c.Label req.Profile = c.Profile @@ -51,7 +52,7 @@ func getInfoFromRemote(c cli.Config) (resp *client.InfoResp, err error) { return } -func getInfoFromConfig(c cli.Config) (resp *client.InfoResp, err error) { +func getInfoFromConfig(c cli.Config) (resp *info.Resp, err error) { s, err := sign.SignerFromConfig(c) if err != nil { return @@ -81,7 +82,7 @@ func getInfoFromConfig(c cli.Config) (resp *client.InfoResp, err error) { profile = policy.Default } - resp = &client.InfoResp{ + resp = &info.Resp{ Certificate: string(certPem), Usage: profile.Usage, ExpiryString: profile.ExpiryString, @@ -95,7 +96,7 @@ func infoMain(args []string, c cli.Config) (err error) { return goerr.New("argument is provided but not defined; please refer to the usage by flag -h.") } - var resp *client.InfoResp + var resp *info.Resp if c.Remote != "" { resp, err = getInfoFromRemote(c) diff --git a/info/info.go b/info/info.go new file mode 100644 index 000000000..926a411ff --- /dev/null +++ b/info/info.go @@ -0,0 +1,15 @@ +// Package info contains the definitions for the info endpoint +package info + +// Req is the request struct for an info API request. +type Req struct { + Label string `json:"label"` + Profile string `json:"profile"` +} + +// Resp is the response for an Info API request. +type Resp struct { + Certificate string `json:"certificate"` + Usage []string `json:"usages"` + ExpiryString string `json:"expiry"` +} diff --git a/signer/remote/remote.go b/signer/remote/remote.go index b29d6e035..5d734ffb5 100644 --- a/signer/remote/remote.go +++ b/signer/remote/remote.go @@ -9,6 +9,7 @@ import ( "github.com/cloudflare/cfssl/config" cferr "github.com/cloudflare/cfssl/errors" "github.com/cloudflare/cfssl/helpers" + "github.com/cloudflare/cfssl/info" "github.com/cloudflare/cfssl/signer" ) @@ -38,17 +39,31 @@ func NewSigner(policy *config.Signing) (*Signer, error) { // csr, and profileName are used as with a local signing operation, and // the label is used to select a signing root in a multi-root CA. func (s *Signer) Sign(req signer.SignRequest) (cert []byte, err error) { - return s.remoteOp(req, req.Profile, "sign") + resp, err := s.remoteOp(req, req.Profile, "sign") + if err != nil { + return + } + if cert, ok := resp.([]byte); ok { + return cert, nil + } + return } -// Info sends an info request to the remote CFSSL server, receiving a signed -// certificate or an error in response. -func (s *Signer) Info(req client.InfoReq) (cert []byte, err error) { - return s.remoteOp(req, req.Profile, "info") +// Info sends an info request to the remote CFSSL server, receiving an +// Resp struct or an error in response. +func (s *Signer) Info(req info.Req) (resp *info.Resp, err error) { + respInterface, err := s.remoteOp(req, req.Profile, "info") + if err != nil { + return + } + if resp, ok := respInterface.(*info.Resp); ok { + return resp, nil + } + return } // Helper function to perform a remote sign or info request. -func (s *Signer) remoteOp(req interface{}, profile, target string) (cert []byte, err error) { +func (s *Signer) remoteOp(req interface{}, profile, target string) (resp interface{}, err error) { jsonData, err := json.Marshal(req) if err != nil { return nil, cferr.Wrap(cferr.APIClientError, cferr.JSONError, err) @@ -69,19 +84,20 @@ func (s *Signer) remoteOp(req interface{}, profile, target string) (cert []byte, errors.New("failed to connect to remote")) } - // There's no server-side auth provider for the "info" method - // TODO: Revert this change once there is an AuthInfo provider. - if p.Provider != nil && target != "info" { - cert, err = server.AuthReq(jsonData, nil, p.Provider, target) + // There's no auth provider for the "info" method + if target == "info" { + resp, err = server.Info(jsonData) + } else if p.Provider != nil { + resp, err = server.AuthReq(jsonData, nil, p.Provider, target) } else { - cert, err = server.Req(jsonData, target) + resp, err = server.Req(jsonData, target) } if err != nil { return nil, err } - return []byte(cert), nil + return } // SigAlgo returns the RSA signer's signature algorithm. @@ -92,11 +108,11 @@ func (s *Signer) SigAlgo() x509.SignatureAlgorithm { // Certificate returns the signer's certificate. func (s *Signer) Certificate(label, profile string) (*x509.Certificate, error) { - certStr, err := s.Info(client.InfoReq{Label: label, Profile: profile}) + resp, err := s.Info(info.Req{Label: label, Profile: profile}) if err != nil { return nil, err } - cert, err := helpers.ParseCertificatePEM(certStr) + cert, err := helpers.ParseCertificatePEM([]byte(resp.Certificate)) if err != nil { return nil, err } diff --git a/signer/remote/remote_test.go b/signer/remote/remote_test.go index bbe6b3b34..cf26b66d0 100644 --- a/signer/remote/remote_test.go +++ b/signer/remote/remote_test.go @@ -11,11 +11,11 @@ import ( "time" "github.com/cloudflare/cfssl/api" - "github.com/cloudflare/cfssl/api/client" - "github.com/cloudflare/cfssl/api/info" + apiinfo "github.com/cloudflare/cfssl/api/info" "github.com/cloudflare/cfssl/config" "github.com/cloudflare/cfssl/errors" "github.com/cloudflare/cfssl/helpers" + "github.com/cloudflare/cfssl/info" "github.com/cloudflare/cfssl/log" "github.com/cloudflare/cfssl/signer" "github.com/cloudflare/cfssl/signer/local" @@ -83,8 +83,8 @@ func TestRemoteInfo(t *testing.T) { // override with test server address, ignore url prefix "http://" remoteConfig.Signing.OverrideRemotes(remoteServer.URL[7:]) s := newRemoteSigner(t, remoteConfig.Signing) - req := client.InfoReq{} - certBytes, err := s.Info(req) + req := info.Req{} + resp, err := s.Info(req) if err != nil { t.Fatal("remote info failed:", err) } @@ -95,8 +95,8 @@ func TestRemoteInfo(t *testing.T) { t.Fatal("fail to read test CA cert:", err) } - if bytes.Compare(caBytes, certBytes) != 0 { - t.Fatal("Get a different CA cert through info api.", len(certBytes), len(caBytes)) + if bytes.Compare(caBytes, []byte(resp.Certificate)) != 0 { + t.Fatal("Get a different CA cert through info api.", len(resp.Certificate), len(caBytes)) } } @@ -305,7 +305,7 @@ func newHandler(t *testing.T, caFile, caKeyFile, op string) (http.Handler, error if op == "sign" { return NewSignHandlerFromSigner(s) } else if op == "info" { - return info.NewHandler(s) + return apiinfo.NewHandler(s) } t.Fatal("Bad op code")