client.go 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. // Package client implements the a Go client for CFSSL API commands.
  2. package client
  3. import (
  4. "bytes"
  5. "crypto/tls"
  6. "encoding/json"
  7. stderr "errors"
  8. "fmt"
  9. "io/ioutil"
  10. "net"
  11. "net/http"
  12. "net/url"
  13. "strconv"
  14. "strings"
  15. "time"
  16. "github.com/cloudflare/cfssl/api"
  17. "github.com/cloudflare/cfssl/auth"
  18. "github.com/cloudflare/cfssl/errors"
  19. "github.com/cloudflare/cfssl/info"
  20. "github.com/cloudflare/cfssl/log"
  21. )
  22. // A server points to a single remote CFSSL instance.
  23. type server struct {
  24. URL string
  25. TLSConfig *tls.Config
  26. reqModifier func(*http.Request, []byte)
  27. RequestTimeout time.Duration
  28. }
  29. // A Remote points to at least one (but possibly multiple) remote
  30. // CFSSL instances. It must be able to perform a authenticated and
  31. // unauthenticated certificate signing requests, return information
  32. // about the CA on the other end, and return a list of the hosts that
  33. // are used by the remote.
  34. type Remote interface {
  35. AuthSign(req, id []byte, provider auth.Provider) ([]byte, error)
  36. Sign(jsonData []byte) ([]byte, error)
  37. Info(jsonData []byte) (*info.Resp, error)
  38. Hosts() []string
  39. SetReqModifier(func(*http.Request, []byte))
  40. SetRequestTimeout(d time.Duration)
  41. }
  42. // NewServer sets up a new server target. The address should be of
  43. // The format [protocol:]name[:port] of the remote CFSSL instance.
  44. // If no protocol is given http is default. If no port
  45. // is specified, the CFSSL default port (8888) is used. If the name is
  46. // a comma-separated list of hosts, an ordered group will be returned.
  47. func NewServer(addr string) Remote {
  48. return NewServerTLS(addr, nil)
  49. }
  50. // NewServerTLS is the TLS version of NewServer
  51. func NewServerTLS(addr string, tlsConfig *tls.Config) Remote {
  52. addrs := strings.Split(addr, ",")
  53. var remote Remote
  54. if len(addrs) > 1 {
  55. remote, _ = NewGroup(addrs, tlsConfig, StrategyOrderedList)
  56. } else {
  57. u, err := normalizeURL(addrs[0])
  58. if err != nil {
  59. log.Errorf("bad url: %v", err)
  60. return nil
  61. }
  62. srv := newServer(u, tlsConfig)
  63. if srv != nil {
  64. remote = srv
  65. }
  66. }
  67. return remote
  68. }
  69. func (srv *server) Hosts() []string {
  70. return []string{srv.URL}
  71. }
  72. func (srv *server) SetReqModifier(mod func(*http.Request, []byte)) {
  73. srv.reqModifier = mod
  74. }
  75. func (srv *server) SetRequestTimeout(timeout time.Duration) {
  76. srv.RequestTimeout = timeout
  77. }
  78. func newServer(u *url.URL, tlsConfig *tls.Config) *server {
  79. URL := u.String()
  80. return &server{
  81. URL: URL,
  82. TLSConfig: tlsConfig,
  83. }
  84. }
  85. func (srv *server) getURL(endpoint string) string {
  86. return fmt.Sprintf("%s/api/v1/cfssl/%s", srv.URL, endpoint)
  87. }
  88. func (srv *server) createTLSTransport() (transport *http.Transport) {
  89. // Setup HTTPS client
  90. tlsConfig := srv.TLSConfig
  91. tlsConfig.BuildNameToCertificate()
  92. return &http.Transport{TLSClientConfig: tlsConfig}
  93. }
  94. // post connects to the remote server and returns a Response struct
  95. func (srv *server) post(url string, jsonData []byte) (*api.Response, error) {
  96. var resp *http.Response
  97. var err error
  98. client := &http.Client{}
  99. if srv.TLSConfig != nil {
  100. client.Transport = srv.createTLSTransport()
  101. }
  102. if srv.RequestTimeout != 0 {
  103. client.Timeout = srv.RequestTimeout
  104. }
  105. req, err := http.NewRequest("POST", url, bytes.NewReader(jsonData))
  106. if err != nil {
  107. err = fmt.Errorf("failed POST to %s: %v", url, err)
  108. return nil, errors.Wrap(errors.APIClientError, errors.ClientHTTPError, err)
  109. }
  110. req.Close = true
  111. req.Header.Set("content-type", "application/json")
  112. if srv.reqModifier != nil {
  113. srv.reqModifier(req, jsonData)
  114. }
  115. resp, err = client.Do(req)
  116. if err != nil {
  117. err = fmt.Errorf("failed POST to %s: %v", url, err)
  118. return nil, errors.Wrap(errors.APIClientError, errors.ClientHTTPError, err)
  119. }
  120. defer req.Body.Close()
  121. body, err := ioutil.ReadAll(resp.Body)
  122. if err != nil {
  123. return nil, errors.Wrap(errors.APIClientError, errors.IOError, err)
  124. }
  125. if resp.StatusCode != http.StatusOK {
  126. log.Errorf("http error with %s", url)
  127. return nil, errors.Wrap(errors.APIClientError, errors.ClientHTTPError, stderr.New(string(body)))
  128. }
  129. var response api.Response
  130. err = json.Unmarshal(body, &response)
  131. if err != nil {
  132. log.Debug("Unable to parse response body:", string(body))
  133. return nil, errors.Wrap(errors.APIClientError, errors.JSONError, err)
  134. }
  135. if !response.Success || response.Result == nil {
  136. if len(response.Errors) > 0 {
  137. return nil, errors.Wrap(errors.APIClientError, errors.ServerRequestFailed, stderr.New(response.Errors[0].Message))
  138. }
  139. return nil, errors.New(errors.APIClientError, errors.ServerRequestFailed)
  140. }
  141. return &response, nil
  142. }
  143. // AuthSign fills out an authenticated signing request to the server,
  144. // receiving a certificate or error in response.
  145. // It takes the serialized JSON request to send, remote address and
  146. // authentication provider.
  147. func (srv *server) AuthSign(req, id []byte, provider auth.Provider) ([]byte, error) {
  148. return srv.authReq(req, id, provider, "sign")
  149. }
  150. // AuthInfo fills out an authenticated info request to the server,
  151. // receiving a certificate or error in response.
  152. // It takes the serialized JSON request to send, remote address and
  153. // authentication provider.
  154. func (srv *server) AuthInfo(req, id []byte, provider auth.Provider) ([]byte, error) {
  155. return srv.authReq(req, id, provider, "info")
  156. }
  157. // authReq is the common logic for AuthSign and AuthInfo -- perform the given
  158. // request, and return the resultant certificate.
  159. // The target is either 'sign' or 'info'.
  160. func (srv *server) authReq(req, ID []byte, provider auth.Provider, target string) ([]byte, error) {
  161. url := srv.getURL("auth" + target)
  162. token, err := provider.Token(req)
  163. if err != nil {
  164. return nil, errors.Wrap(errors.APIClientError, errors.AuthenticationFailure, err)
  165. }
  166. aReq := &auth.AuthenticatedRequest{
  167. Timestamp: time.Now().Unix(),
  168. RemoteAddress: ID,
  169. Token: token,
  170. Request: req,
  171. }
  172. jsonData, err := json.Marshal(aReq)
  173. if err != nil {
  174. return nil, errors.Wrap(errors.APIClientError, errors.JSONError, err)
  175. }
  176. response, err := srv.post(url, jsonData)
  177. if err != nil {
  178. return nil, err
  179. }
  180. result, ok := response.Result.(map[string]interface{})
  181. if !ok {
  182. return nil, errors.New(errors.APIClientError, errors.JSONError)
  183. }
  184. cert, ok := result["certificate"].(string)
  185. if !ok {
  186. return nil, errors.New(errors.APIClientError, errors.JSONError)
  187. }
  188. return []byte(cert), nil
  189. }
  190. // Sign sends a signature request to the remote CFSSL server,
  191. // receiving a signed certificate or an error in response.
  192. // It takes the serialized JSON request to send.
  193. func (srv *server) Sign(jsonData []byte) ([]byte, error) {
  194. return srv.request(jsonData, "sign")
  195. }
  196. // Info sends an info request to the remote CFSSL server, receiving a
  197. // response or an error in response.
  198. // It takes the serialized JSON request to send.
  199. func (srv *server) Info(jsonData []byte) (*info.Resp, error) {
  200. res, err := srv.getResultMap(jsonData, "info")
  201. if err != nil {
  202. return nil, err
  203. }
  204. info := new(info.Resp)
  205. if val, ok := res["certificate"]; ok {
  206. info.Certificate = val.(string)
  207. }
  208. var usages []interface{}
  209. if val, ok := res["usages"]; ok && val != nil {
  210. usages = val.([]interface{})
  211. }
  212. if val, ok := res["expiry"]; ok && val != nil {
  213. info.ExpiryString = val.(string)
  214. }
  215. info.Usage = make([]string, len(usages))
  216. for i, s := range usages {
  217. info.Usage[i] = s.(string)
  218. }
  219. return info, nil
  220. }
  221. func (srv *server) getResultMap(jsonData []byte, target string) (result map[string]interface{}, err error) {
  222. url := srv.getURL(target)
  223. response, err := srv.post(url, jsonData)
  224. if err != nil {
  225. return
  226. }
  227. result, ok := response.Result.(map[string]interface{})
  228. if !ok {
  229. err = errors.Wrap(errors.APIClientError, errors.ClientHTTPError, stderr.New("response is formatted improperly"))
  230. return
  231. }
  232. return
  233. }
  234. // request performs the common logic for Sign and Info, performing the actual
  235. // request and returning the resultant certificate.
  236. func (srv *server) request(jsonData []byte, target string) ([]byte, error) {
  237. result, err := srv.getResultMap(jsonData, target)
  238. if err != nil {
  239. return nil, err
  240. }
  241. cert := result["certificate"].(string)
  242. if cert != "" {
  243. return []byte(cert), nil
  244. }
  245. return nil, errors.Wrap(errors.APIClientError, errors.ClientHTTPError, stderr.New("response doesn't contain certificate."))
  246. }
  247. // AuthRemote acts as a Remote with a default Provider for AuthSign.
  248. type AuthRemote struct {
  249. Remote
  250. provider auth.Provider
  251. }
  252. // NewAuthServer sets up a new auth server target with an addr
  253. // in the same format at NewServer and a default authentication provider to
  254. // use for Sign requests.
  255. func NewAuthServer(addr string, tlsConfig *tls.Config, provider auth.Provider) *AuthRemote {
  256. return &AuthRemote{
  257. Remote: NewServerTLS(addr, tlsConfig),
  258. provider: provider,
  259. }
  260. }
  261. // Sign is overloaded to perform an AuthSign request using the default auth provider.
  262. func (ar *AuthRemote) Sign(req []byte) ([]byte, error) {
  263. return ar.AuthSign(req, nil, ar.provider)
  264. }
  265. // nomalizeURL checks for http/https protocol, appends "http" as default protocol if not defiend in url
  266. func normalizeURL(addr string) (*url.URL, error) {
  267. addr = strings.TrimSpace(addr)
  268. u, err := url.Parse(addr)
  269. if err != nil {
  270. return nil, err
  271. }
  272. if u.Opaque != "" {
  273. u.Host = net.JoinHostPort(u.Scheme, u.Opaque)
  274. u.Opaque = ""
  275. } else if u.Path != "" && !strings.Contains(u.Path, ":") {
  276. u.Host = net.JoinHostPort(u.Path, "8888")
  277. u.Path = ""
  278. } else if u.Scheme == "" {
  279. u.Host = u.Path
  280. u.Path = ""
  281. }
  282. if u.Scheme != "https" {
  283. u.Scheme = "http"
  284. }
  285. _, port, err := net.SplitHostPort(u.Host)
  286. if err != nil {
  287. _, port, err = net.SplitHostPort(u.Host + ":8888")
  288. if err != nil {
  289. return nil, err
  290. }
  291. }
  292. if port != "" {
  293. _, err = strconv.Atoi(port)
  294. if err != nil {
  295. return nil, err
  296. }
  297. }
  298. return u, nil
  299. }