github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/creds/credhub/manager.go (about)

     1  package credhub
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"net/url"
     8  	"sync"
     9  
    10  	"code.cloudfoundry.org/credhub-cli/credhub"
    11  	"code.cloudfoundry.org/credhub-cli/credhub/auth"
    12  	"code.cloudfoundry.org/lager"
    13  	"github.com/pf-qiu/concourse/v6/atc/creds"
    14  )
    15  
    16  type CredHubManager struct {
    17  	URL string `long:"url" description:"CredHub server address used to access secrets."`
    18  
    19  	PathPrefix string `long:"path-prefix" default:"/concourse" description:"Path under which to namespace credential lookup."`
    20  
    21  	TLS    TLS
    22  	UAA    UAA
    23  	Client *LazyCredhub
    24  }
    25  
    26  type TLS struct {
    27  	CACerts    []string `long:"ca-cert"              description:"Paths to PEM-encoded CA cert files to use to verify the CredHub server SSL cert."`
    28  	ClientCert string   `long:"client-cert"          description:"Path to the client certificate for mutual TLS authorization."`
    29  	ClientKey  string   `long:"client-key"           description:"Path to the client private key for mutual TLS authorization."`
    30  	Insecure   bool     `long:"insecure-skip-verify" description:"Enable insecure SSL verification."`
    31  }
    32  
    33  type UAA struct {
    34  	ClientId     string `long:"client-id"     description:"Client ID for CredHub authorization."`
    35  	ClientSecret string `long:"client-secret" description:"Client secret for CredHub authorization."`
    36  }
    37  
    38  func (manager *CredHubManager) MarshalJSON() ([]byte, error) {
    39  	health, err := manager.Health()
    40  	if err != nil {
    41  		return nil, err
    42  	}
    43  
    44  	response := map[string]interface{}{
    45  		"url":           manager.URL,
    46  		"path_prefix":   manager.PathPrefix,
    47  		"ca_certs":      manager.TLS.CACerts,
    48  		"uaa_client_id": manager.UAA.ClientId,
    49  		"health":        health,
    50  	}
    51  
    52  	return json.Marshal(&response)
    53  }
    54  
    55  func (manager *CredHubManager) Init(log lager.Logger) error {
    56  	var options []credhub.Option
    57  	if manager.TLS.Insecure {
    58  		options = append(options, credhub.SkipTLSValidation(true))
    59  	}
    60  
    61  	caCerts := []string{}
    62  	for _, cert := range manager.TLS.CACerts {
    63  		contents, err := ioutil.ReadFile(cert)
    64  		if err != nil {
    65  			return err
    66  		}
    67  
    68  		caCerts = append(caCerts, string(contents))
    69  	}
    70  
    71  	if len(caCerts) > 0 {
    72  		options = append(options, credhub.CaCerts(caCerts...))
    73  	}
    74  
    75  	if manager.UAA.ClientId != "" && manager.UAA.ClientSecret != "" {
    76  		options = append(options, credhub.Auth(auth.UaaClientCredentials(
    77  			manager.UAA.ClientId,
    78  			manager.UAA.ClientSecret,
    79  		)))
    80  	}
    81  
    82  	if manager.TLS.ClientCert != "" && manager.TLS.ClientKey != "" {
    83  		options = append(options, credhub.ClientCert(manager.TLS.ClientCert, manager.TLS.ClientKey))
    84  	}
    85  
    86  	manager.Client = newLazyCredhub(manager.URL, options)
    87  
    88  	return nil
    89  }
    90  
    91  func (manager CredHubManager) IsConfigured() bool {
    92  	return manager.URL != "" ||
    93  		manager.UAA.ClientId != "" ||
    94  		manager.UAA.ClientSecret != "" ||
    95  		len(manager.TLS.CACerts) != 0 ||
    96  		manager.TLS.ClientCert != "" ||
    97  		manager.TLS.ClientKey != ""
    98  }
    99  
   100  func (manager CredHubManager) Validate() error {
   101  	parsedUrl, err := url.Parse(manager.URL)
   102  	if err != nil {
   103  		return fmt.Errorf("invalid URL: %s", err)
   104  	}
   105  
   106  	// "foo" will parse without error (as a Path, with an empty Host)
   107  	// so we'll do a few additional sanity checks that this is a valid URL
   108  	if parsedUrl.Host == "" || !(parsedUrl.Scheme == "http" || parsedUrl.Scheme == "https") {
   109  		return fmt.Errorf("invalid URL (must be http or https)")
   110  	}
   111  
   112  	return nil
   113  }
   114  
   115  func (manager CredHubManager) Health() (*creds.HealthResponse, error) {
   116  	healthResponse := &creds.HealthResponse{
   117  		Method: "/health",
   118  	}
   119  
   120  	credhubObject, err := manager.Client.CredHub()
   121  	if err != nil {
   122  		return healthResponse, err
   123  	}
   124  
   125  	response, err := credhubObject.Client().Get(manager.URL + "/health")
   126  	if err != nil {
   127  		healthResponse.Error = err.Error()
   128  		return healthResponse, nil
   129  	}
   130  
   131  	if response.StatusCode < 200 || response.StatusCode > 299 {
   132  		healthResponse.Error = "not ok"
   133  		return healthResponse, nil
   134  	}
   135  
   136  	var credhubHealth struct {
   137  		Status string `json:"status"`
   138  	}
   139  
   140  	defer response.Body.Close()
   141  	err = json.NewDecoder(response.Body).Decode(&credhubHealth)
   142  	if err != nil {
   143  		return nil, err
   144  	}
   145  
   146  	healthResponse.Response = credhubHealth
   147  
   148  	return healthResponse, nil
   149  }
   150  
   151  func (manager CredHubManager) NewSecretsFactory(logger lager.Logger) (creds.SecretsFactory, error) {
   152  	return NewCredHubFactory(logger, manager.Client, manager.PathPrefix), nil
   153  }
   154  
   155  type LazyCredhub struct {
   156  	url     string
   157  	options []credhub.Option
   158  	credhub *credhub.CredHub
   159  	mu      sync.Mutex
   160  }
   161  
   162  func newLazyCredhub(url string, options []credhub.Option) *LazyCredhub {
   163  	return &LazyCredhub{
   164  		url:     url,
   165  		options: options,
   166  	}
   167  }
   168  
   169  func (lc *LazyCredhub) CredHub() (*credhub.CredHub, error) {
   170  	lc.mu.Lock()
   171  	defer lc.mu.Unlock()
   172  	if lc.credhub != nil {
   173  		return lc.credhub, nil
   174  	}
   175  
   176  	ch, err := credhub.New(lc.url, lc.options...)
   177  	if err != nil {
   178  		return nil, err
   179  	}
   180  
   181  	lc.credhub = ch
   182  
   183  	return lc.credhub, nil
   184  }
   185  
   186  func (manager CredHubManager) Close(logger lager.Logger) {
   187  	// TODO - to implement
   188  }