github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/topgun/core/credhub_test.go (about)

     1  package topgun_test
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"crypto/rand"
     7  	"crypto/rsa"
     8  	"crypto/x509"
     9  	"crypto/x509/pkix"
    10  	"encoding/json"
    11  	"encoding/pem"
    12  	"io/ioutil"
    13  	"math/big"
    14  	"os"
    15  	"path/filepath"
    16  	"strings"
    17  	"time"
    18  
    19  	"code.cloudfoundry.org/credhub-cli/credhub"
    20  	"code.cloudfoundry.org/credhub-cli/credhub/credentials/values"
    21  	"sigs.k8s.io/yaml"
    22  
    23  	. "github.com/pf-qiu/concourse/v6/topgun"
    24  	. "github.com/pf-qiu/concourse/v6/topgun/common"
    25  	. "github.com/onsi/ginkgo"
    26  	. "github.com/onsi/gomega"
    27  )
    28  
    29  var _ = Describe("Credhub", func() {
    30  	BeforeEach(func() {
    31  		if !strings.Contains(string(Bosh("releases").Out.Contents()), "credhub") {
    32  			Skip("credhub release not uploaded")
    33  		}
    34  	})
    35  
    36  	Describe("A deployment with credhub", func() {
    37  		var (
    38  			credhubClient   *credhub.CredHub
    39  			credhubInstance *BoshInstance
    40  		)
    41  
    42  		BeforeEach(func() {
    43  			Deploy(
    44  				"deployments/concourse.yml",
    45  				"-o", "operations/add-empty-credhub.yml",
    46  			)
    47  
    48  			credhubInstance = Instance("credhub")
    49  			postgresInstance := JobInstance("postgres")
    50  
    51  			varsDir, err := ioutil.TempDir("", "vars")
    52  			Expect(err).ToNot(HaveOccurred())
    53  
    54  			defer os.RemoveAll(varsDir)
    55  
    56  			varsStore := filepath.Join(varsDir, "vars.yml")
    57  			err = generateCredhubCerts(varsStore)
    58  			Expect(err).ToNot(HaveOccurred())
    59  
    60  			Deploy(
    61  				"deployments/concourse.yml",
    62  				"-o", "operations/add-credhub.yml",
    63  				"--vars-store", varsStore,
    64  				"-v", "credhub_ip="+credhubInstance.IP,
    65  				"-v", "postgres_ip="+postgresInstance.IP,
    66  			)
    67  
    68  			varsBytes, err := ioutil.ReadFile(varsStore)
    69  			Expect(err).ToNot(HaveOccurred())
    70  
    71  			var vars struct {
    72  				CredHubClient struct {
    73  					CA          string `json:"ca"`
    74  					Certificate string `json:"certificate"`
    75  					PrivateKey  string `json:"private_key"`
    76  				} `json:"credhub_client_topgun"`
    77  			}
    78  
    79  			err = yaml.Unmarshal(varsBytes, &vars)
    80  			Expect(err).ToNot(HaveOccurred())
    81  
    82  			clientCert := filepath.Join(varsDir, "client.cert")
    83  			err = ioutil.WriteFile(clientCert, []byte(vars.CredHubClient.Certificate), 0644)
    84  			Expect(err).ToNot(HaveOccurred())
    85  
    86  			clientKey := filepath.Join(varsDir, "client.key")
    87  			err = ioutil.WriteFile(clientKey, []byte(vars.CredHubClient.PrivateKey), 0644)
    88  			Expect(err).ToNot(HaveOccurred())
    89  
    90  			credhubClient, err = credhub.New(
    91  				"https://"+credhubInstance.IP+":8844",
    92  				credhub.CaCerts(vars.CredHubClient.CA),
    93  				credhub.ClientCert(clientCert, clientKey),
    94  			)
    95  			Expect(err).ToNot(HaveOccurred())
    96  		})
    97  
    98  		Describe("/api/v1/info/creds", func() {
    99  			type responseSkeleton struct {
   100  				CredHub struct {
   101  					URL     string   `json:"url"`
   102  					CACerts []string `json:"ca_certs"`
   103  					Health  struct {
   104  						Error    string `json:"error"`
   105  						Response struct {
   106  							Status string `json:"status"`
   107  						} `json:"response"`
   108  						Method string `json:"method"`
   109  					} `json:"health"`
   110  					PathPrefix  string `json:"path_prefix"`
   111  					UAAClientID string `json:"uaa_client_id"`
   112  				} `json:"credhub"`
   113  			}
   114  
   115  			var (
   116  				atcURL         string
   117  				parsedResponse responseSkeleton
   118  			)
   119  
   120  			BeforeEach(func() {
   121  				atcURL = "http://" + JobInstance("web").IP + ":8080"
   122  			})
   123  
   124  			JustBeforeEach(func() {
   125  				token, err := FetchToken(atcURL, AtcUsername, AtcPassword)
   126  				Expect(err).ToNot(HaveOccurred())
   127  
   128  				body, err := RequestCredsInfo(atcURL, token.AccessToken)
   129  				Expect(err).ToNot(HaveOccurred())
   130  
   131  				err = json.Unmarshal(body, &parsedResponse)
   132  				Expect(err).ToNot(HaveOccurred())
   133  			})
   134  
   135  			It("contains credhub config", func() {
   136  				Expect(parsedResponse.CredHub.URL).To(Equal("https://" + credhubInstance.IP + ":8844"))
   137  				Expect(parsedResponse.CredHub.Health.Response).ToNot(BeNil())
   138  				Expect(parsedResponse.CredHub.Health.Response.Status).To(Equal("UP"))
   139  				Expect(parsedResponse.CredHub.Health.Error).To(BeEmpty())
   140  			})
   141  		})
   142  
   143  		testCredentialManagement(func() {
   144  			_, err := credhubClient.SetValue("/concourse/main/team_secret", values.Value("some_team_secret"))
   145  			Expect(err).ToNot(HaveOccurred())
   146  
   147  			_, err = credhubClient.SetValue("/concourse/main/pipeline-creds-test/assertion_script", values.Value(assertionScript))
   148  			Expect(err).ToNot(HaveOccurred())
   149  
   150  			_, err = credhubClient.SetValue("/concourse/main/pipeline-creds-test/canary", values.Value("some_canary"))
   151  			Expect(err).ToNot(HaveOccurred())
   152  
   153  			_, err = credhubClient.SetValue("/concourse/main/pipeline-creds-test/resource_type_secret", values.Value("some_resource_type_secret"))
   154  			Expect(err).ToNot(HaveOccurred())
   155  
   156  			_, err = credhubClient.SetValue("/concourse/main/pipeline-creds-test/resource_secret", values.Value("some_resource_secret"))
   157  			Expect(err).ToNot(HaveOccurred())
   158  
   159  			_, err = credhubClient.SetUser("/concourse/main/pipeline-creds-test/job_secret", values.User{
   160  				Username: "some_username",
   161  				Password: "some_password",
   162  			})
   163  			Expect(err).ToNot(HaveOccurred())
   164  
   165  			_, err = credhubClient.SetValue("/concourse/main/pipeline-creds-test/resource_version", values.Value("some_exposed_version_secret"))
   166  			Expect(err).ToNot(HaveOccurred())
   167  		}, func() {
   168  			_, err := credhubClient.SetValue("/concourse/main/team_secret", values.Value("some_team_secret"))
   169  			Expect(err).ToNot(HaveOccurred())
   170  
   171  			_, err = credhubClient.SetValue("/concourse/main/resource_version", values.Value("some_exposed_version_secret"))
   172  			Expect(err).ToNot(HaveOccurred())
   173  		})
   174  	})
   175  })
   176  
   177  type Cert struct {
   178  	CA          string `json:"ca"`
   179  	Certificate string `json:"certificate"`
   180  	PrivateKey  string `json:"private_key"`
   181  }
   182  
   183  func generateCredhubCerts(filepath string) (err error) {
   184  	var vars struct {
   185  		CredHubCA           Cert `json:"credhub_ca"`
   186  		CredHubClientAtc    Cert `json:"credhub_client_atc"`
   187  		CredHubClientTopgun Cert `json:"credhub_client_topgun"`
   188  	}
   189  
   190  	key, _ := rsa.GenerateKey(rand.Reader, 2048)
   191  
   192  	// root ca cert
   193  	rootCaTemplate, rootCaCert, err := generateCert("credhubCA", "", true, x509.ExtKeyUsageServerAuth, nil, key)
   194  	if err != nil {
   195  		return err
   196  	}
   197  
   198  	var b bytes.Buffer
   199  	writer := bufio.NewWriter(&b)
   200  
   201  	pem.Encode(writer, &pem.Block{Type: "CERTIFICATE", Bytes: rootCaCert})
   202  	writer.Flush()
   203  	rootCa := b.String()
   204  	b.Reset()
   205  
   206  	pem.Encode(writer, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})
   207  	writer.Flush()
   208  	rootCaKey := b.String()
   209  	b.Reset()
   210  
   211  	vars.CredHubCA.CA = rootCa
   212  	vars.CredHubCA.Certificate = rootCa
   213  	vars.CredHubCA.PrivateKey = rootCaKey
   214  
   215  	// client topgun cert
   216  	_, clientTopgunCert, err := generateCert("credhubCA", "app:eef9440f-7d2b-44b4-99e2-a619cbec99e6", false, x509.ExtKeyUsageClientAuth, &rootCaTemplate, key)
   217  	if err != nil {
   218  		return err
   219  	}
   220  
   221  	pem.Encode(writer, &pem.Block{Type: "CERTIFICATE", Bytes: clientTopgunCert})
   222  	writer.Flush()
   223  	clientTopgun := b.String()
   224  	b.Reset()
   225  
   226  	vars.CredHubClientTopgun.CA = rootCa
   227  	vars.CredHubClientTopgun.Certificate = clientTopgun
   228  	vars.CredHubClientTopgun.PrivateKey = rootCaKey
   229  
   230  	// client atc cert
   231  	_, clientAtcCert, err := generateCert("concourse", "app:df4d7e2c-edfa-432d-ab7e-ee97846b06d0", false, x509.ExtKeyUsageClientAuth, &rootCaTemplate, key)
   232  	if err != nil {
   233  		return err
   234  	}
   235  
   236  	pem.Encode(writer, &pem.Block{Type: "CERTIFICATE", Bytes: clientAtcCert})
   237  	writer.Flush()
   238  	clientAtc := b.String()
   239  	b.Reset()
   240  
   241  	vars.CredHubClientAtc.CA = rootCa
   242  	vars.CredHubClientAtc.Certificate = clientAtc
   243  	vars.CredHubClientAtc.PrivateKey = rootCaKey
   244  
   245  	varsYaml, _ := yaml.Marshal(&vars)
   246  	ioutil.WriteFile(filepath, varsYaml, 0644)
   247  	return nil
   248  }
   249  
   250  func generateCert(commonName string, orgUnit string, isCA bool, extKeyUsage x509.ExtKeyUsage, parent *x509.Certificate, priv *rsa.PrivateKey) (template x509.Certificate, cert []byte, err error) {
   251  
   252  	random := rand.Reader
   253  	now := time.Now()
   254  	then := now.Add(60 * 60 * 24 * 1000 * 1000 * 1000) // 24 hours
   255  
   256  	template = x509.Certificate{
   257  		SerialNumber: big.NewInt(1),
   258  		Subject: pkix.Name{
   259  			CommonName:         commonName,
   260  			Organization:       []string{"Cloud Foundry"},
   261  			OrganizationalUnit: []string{orgUnit},
   262  		},
   263  		NotBefore: now,
   264  		NotAfter:  then,
   265  
   266  		SubjectKeyId: []byte{1, 2, 3, 4},
   267  		KeyUsage:     x509.KeyUsageCertSign | x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
   268  		ExtKeyUsage:  []x509.ExtKeyUsage{extKeyUsage},
   269  
   270  		BasicConstraintsValid: true,
   271  		IsCA:                  isCA,
   272  	}
   273  
   274  	if isCA {
   275  		parent = &template
   276  	}
   277  
   278  	cert, err = x509.CreateCertificate(random, &template, parent, &priv.PublicKey, priv)
   279  
   280  	return template, cert, err
   281  }