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  }