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