client.go 10 KB

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