client.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  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"
  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. defer resp.Body.Close()
  145. body, err := io.ReadAll(resp.Body)
  146. if err != nil {
  147. return nil, errors.Wrap(errors.APIClientError, errors.IOError, err)
  148. }
  149. if resp.StatusCode != http.StatusOK {
  150. log.Errorf("http error with %s", url)
  151. return nil, errors.Wrap(errors.APIClientError, errors.ClientHTTPError, stderr.New(string(body)))
  152. }
  153. var response api.Response
  154. err = json.Unmarshal(body, &response)
  155. if err != nil {
  156. log.Debug("Unable to parse response body:", string(body))
  157. return nil, errors.Wrap(errors.APIClientError, errors.JSONError, err)
  158. }
  159. if !response.Success || response.Result == nil {
  160. if len(response.Errors) > 0 {
  161. return nil, errors.Wrap(errors.APIClientError, errors.ServerRequestFailed, stderr.New(response.Errors[0].Message))
  162. }
  163. return nil, errors.New(errors.APIClientError, errors.ServerRequestFailed)
  164. }
  165. return &response, nil
  166. }
  167. // AuthSign fills out an authenticated signing request to the server,
  168. // receiving a certificate or error in response.
  169. // It takes the serialized JSON request to send, remote address and
  170. // authentication provider.
  171. func (srv *server) AuthSign(req, id []byte, provider auth.Provider) ([]byte, error) {
  172. return srv.authReq(req, id, provider, "sign")
  173. }
  174. // AuthInfo fills out an authenticated info request to the server,
  175. // receiving a certificate or error in response.
  176. // It takes the serialized JSON request to send, remote address and
  177. // authentication provider.
  178. func (srv *server) AuthInfo(req, id []byte, provider auth.Provider) ([]byte, error) {
  179. return srv.authReq(req, id, provider, "info")
  180. }
  181. // authReq is the common logic for AuthSign and AuthInfo -- perform the given
  182. // request, and return the resultant certificate.
  183. // The target is either 'sign' or 'info'.
  184. func (srv *server) authReq(req, ID []byte, provider auth.Provider, target string) ([]byte, error) {
  185. url := srv.getURL("auth" + target)
  186. token, err := provider.Token(req)
  187. if err != nil {
  188. return nil, errors.Wrap(errors.APIClientError, errors.AuthenticationFailure, err)
  189. }
  190. aReq := &auth.AuthenticatedRequest{
  191. Timestamp: time.Now().Unix(),
  192. RemoteAddress: ID,
  193. Token: token,
  194. Request: req,
  195. }
  196. jsonData, err := json.Marshal(aReq)
  197. if err != nil {
  198. return nil, errors.Wrap(errors.APIClientError, errors.JSONError, err)
  199. }
  200. response, err := srv.post(url, jsonData)
  201. if err != nil {
  202. return nil, err
  203. }
  204. result, ok := response.Result.(map[string]interface{})
  205. if !ok {
  206. return nil, errors.New(errors.APIClientError, errors.JSONError)
  207. }
  208. cert, ok := result["certificate"].(string)
  209. if !ok {
  210. return nil, errors.New(errors.APIClientError, errors.JSONError)
  211. }
  212. return []byte(cert), nil
  213. }
  214. // Sign sends a signature request to the remote CFSSL server,
  215. // receiving a signed certificate or an error in response.
  216. // It takes the serialized JSON request to send.
  217. func (srv *server) Sign(jsonData []byte) ([]byte, error) {
  218. return srv.request(jsonData, "sign")
  219. }
  220. // Info sends an info request to the remote CFSSL server, receiving a
  221. // response or an error in response.
  222. // It takes the serialized JSON request to send.
  223. func (srv *server) Info(jsonData []byte) (*info.Resp, error) {
  224. res, err := srv.getResultMap(jsonData, "info")
  225. if err != nil {
  226. return nil, err
  227. }
  228. info := new(info.Resp)
  229. if val, ok := res["certificate"]; ok {
  230. info.Certificate = val.(string)
  231. }
  232. var usages []interface{}
  233. if val, ok := res["usages"]; ok && val != nil {
  234. usages = val.([]interface{})
  235. }
  236. if val, ok := res["expiry"]; ok && val != nil {
  237. info.ExpiryString = val.(string)
  238. }
  239. info.Usage = make([]string, len(usages))
  240. for i, s := range usages {
  241. info.Usage[i] = s.(string)
  242. }
  243. return info, nil
  244. }
  245. func (srv *server) getResultMap(jsonData []byte, target string) (result map[string]interface{}, err error) {
  246. url := srv.getURL(target)
  247. response, err := srv.post(url, jsonData)
  248. if err != nil {
  249. return
  250. }
  251. result, ok := response.Result.(map[string]interface{})
  252. if !ok {
  253. err = errors.Wrap(errors.APIClientError, errors.ClientHTTPError, stderr.New("response is formatted improperly"))
  254. return
  255. }
  256. return
  257. }
  258. // request performs the common logic for Sign and Info, performing the actual
  259. // request and returning the resultant certificate.
  260. func (srv *server) request(jsonData []byte, target string) ([]byte, error) {
  261. result, err := srv.getResultMap(jsonData, target)
  262. if err != nil {
  263. return nil, err
  264. }
  265. cert := result["certificate"].(string)
  266. if cert != "" {
  267. return []byte(cert), nil
  268. }
  269. return nil, errors.Wrap(errors.APIClientError, errors.ClientHTTPError, stderr.New("response doesn't contain certificate."))
  270. }
  271. // AuthRemote acts as a Remote with a default Provider for AuthSign.
  272. type AuthRemote struct {
  273. Remote
  274. provider auth.Provider
  275. }
  276. // NewAuthServer sets up a new auth server target with an addr
  277. // in the same format at NewServer and a default authentication provider to
  278. // use for Sign requests.
  279. func NewAuthServer(addr string, tlsConfig *tls.Config, provider auth.Provider) *AuthRemote {
  280. return &AuthRemote{
  281. Remote: NewServerTLS(addr, tlsConfig),
  282. provider: provider,
  283. }
  284. }
  285. // Sign is overloaded to perform an AuthSign request using the default auth provider.
  286. func (ar *AuthRemote) Sign(req []byte) ([]byte, error) {
  287. return ar.AuthSign(req, nil, ar.provider)
  288. }
  289. // normalizeURL checks for http/https protocol, appends "http" as default protocol if not defined in url
  290. func normalizeURL(addr string) (*url.URL, error) {
  291. addr = strings.TrimSpace(addr)
  292. u, err := url.Parse(addr)
  293. if err != nil {
  294. return nil, err
  295. }
  296. if u.Opaque != "" {
  297. u.Host = net.JoinHostPort(u.Scheme, u.Opaque)
  298. u.Opaque = ""
  299. } else if u.Path != "" && !strings.Contains(u.Path, ":") {
  300. u.Host = net.JoinHostPort(u.Path, "8888")
  301. u.Path = ""
  302. } else if u.Scheme == "" {
  303. u.Host = u.Path
  304. u.Path = ""
  305. }
  306. if u.Scheme != "https" {
  307. u.Scheme = "http"
  308. }
  309. _, port, err := net.SplitHostPort(u.Host)
  310. if err != nil {
  311. _, port, err = net.SplitHostPort(u.Host + ":8888")
  312. if err != nil {
  313. return nil, err
  314. }
  315. }
  316. if port != "" {
  317. _, err = strconv.Atoi(port)
  318. if err != nil {
  319. return nil, err
  320. }
  321. }
  322. return u, nil
  323. }