Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion cmd/create_env_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ var _ = Describe("CreateEnvCmd", func() {

mockBlobstoreFactory = mockblobstore.NewMockFactory(mockCtrl)
mockBlobstore = mockblobstore.NewMockBlobstore(mockCtrl)
mockBlobstoreFactory.EXPECT().Create(mbusURL, gomock.Any()).Return(mockBlobstore, nil).AnyTimes()
mockBlobstoreFactory.EXPECT().Create(mbusURL, SecureTLSClientMatcher()).Return(mockBlobstore, nil).AnyTimes()

mockVMManagerFactory = mockvm.NewMockManagerFactory(mockCtrl)
fakeVMManager = fakebivm.NewFakeManager()
Expand Down Expand Up @@ -254,6 +254,9 @@ var _ = Describe("CreateEnvCmd", func() {
{Name: "fake-cpi-release-job-name", Release: "fake-cpi-release-name"},
},
Mbus: mbusURL,
Cert: biinstallmanifest.Certificate{
CA: validCACert,
},
}

// parsed BOSH deployment manifest
Expand Down
6 changes: 5 additions & 1 deletion cmd/deployment_deleter.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,11 @@ func (c *deploymentDeleter) deploymentManager(installation biinstall.Installatio

c.logger.Debug(c.logTag, "Creating blobstore client...")

blobstore, err := c.blobstoreFactory.Create(installationMbus, bihttpclient.CreateDefaultClientInsecureSkipVerify())
certPool, err := biinstallmanifest.Certificate{CA: caCert}.CACertPool()
if err != nil {
return nil, bosherr.WrapError(err, "Parsing CA certificate for blobstore client")
}
blobstore, err := c.blobstoreFactory.Create(installationMbus, bihttpclient.CreateDefaultClient(certPool))
if err != nil {
return nil, bosherr.WrapError(err, "Creating blobstore client")
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/deployment_deleter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ cloud_provider:

mockBlobstoreFactory = mockblobstore.NewMockFactory(mockCtrl)
mockBlobstore = mockblobstore.NewMockBlobstore(mockCtrl)
mockBlobstoreFactory.EXPECT().Create(mbusURL, gomock.Any()).Return(mockBlobstore, nil).AnyTimes()
mockBlobstoreFactory.EXPECT().Create(mbusURL, SecureTLSClientMatcher()).Return(mockBlobstore, nil).AnyTimes()

mockDeploymentManagerFactory = mockdeployment.NewMockManagerFactory(mockCtrl)
mockDeploymentManager = mockdeployment.NewMockManager(mockCtrl)
Expand Down
6 changes: 5 additions & 1 deletion cmd/deployment_preparer.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,11 @@ func (c *DeploymentPreparer) deploy(
}
vmManager := c.vmManagerFactory.NewManager(cloud, agentClient)

blobstore, err := c.blobstoreFactory.Create(installationManifest.Mbus, bihttpclient.CreateDefaultClientInsecureSkipVerify())
certPool, err := installationManifest.Cert.CACertPool()
if err != nil {
return bosherr.WrapError(err, "Parsing CA certificate for blobstore client")
}
blobstore, err := c.blobstoreFactory.Create(installationManifest.Mbus, bihttpclient.CreateDefaultClient(certPool))
if err != nil {
return bosherr.WrapError(err, "Creating blobstore client")
}
Expand Down
28 changes: 28 additions & 0 deletions cmd/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package cmd_test

import (
"crypto/tls"
"net/http"
"testing"

"github.com/golang/mock/gomock"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"

Expand All @@ -15,6 +17,7 @@ var (
cacertBytes []byte
validCACert string
)

var _ = BeforeSuite(func() {
var err error
cert, cacertBytes, err = testutils.CertSetup()
Expand All @@ -26,3 +29,28 @@ func TestReg(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "cmd")
}

// secureTLSClientMatcher is a gomock.Matcher that asserts an *http.Client
// has TLS certificate verification enabled (InsecureSkipVerify == false).
// Use SecureTLSClientMatcher() to obtain an instance.
type secureTLSClientMatcher struct{}

func SecureTLSClientMatcher() gomock.Matcher {
return secureTLSClientMatcher{}
}

func (m secureTLSClientMatcher) Matches(x interface{}) bool {
client, ok := x.(*http.Client)
if !ok {
return false
}
transport, ok := client.Transport.(*http.Transport)
if !ok {
return false
}
return transport.TLSClientConfig != nil && !transport.TLSClientConfig.InsecureSkipVerify
Comment thread
selzoc marked this conversation as resolved.
}

func (m secureTLSClientMatcher) String() string {
return "is a secure *http.Client with TLS certificate verification enabled (InsecureSkipVerify=false)"
}
52 changes: 52 additions & 0 deletions installation/manifest/certificate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package manifest_test

import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"

. "github.com/cloudfoundry/bosh-cli/v7/installation/manifest"
)

var _ = Describe("Certificate", func() {
Describe("CACertPool", func() {
It("returns nil when CA is empty", func() {
certPool, err := Certificate{CA: ""}.CACertPool()
Expect(err).ToNot(HaveOccurred())
Expect(certPool).To(BeNil())
})

It("returns a cert pool when a valid PEM CA cert is provided", func() {
caCert := `-----BEGIN CERTIFICATE-----
MIIDXzCCAkegAwIBAgIJAPerMgLAne5vMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQwIBcNMTYwMTE2MDY0NTA0WhgPMjI4OTEwMzAwNjQ1MDRa
MEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJ
bnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
ggEKAoIBAQCtSo3KPjnVPzodb6+mNwbCdcpzVop8OmfwJ3ynQtyBEzGaKsAn4tlz
/wfQQrKFHgxqVpqcoxAlWPNMs5+iO2Jst3Gz2+oLcaDyz/EWorw0iF5q1F6+WYHp
EijY20MzaWYMyu4UhhlbJCkSGZSjujh5SFOAXQwWYJXsqjyxA9KaTD6OdH5Kpger
B9D4zogX0We00eouyvvz/sAeDbTshk9sJRGWHNFJr+TjVx2D01alU49liAL94yF6
1eEOEbE50OAhv9RNsRh6O58idaHg30bbMf1yAzcgBvh8CzIHH0BPofoF2pRfztoY
uudZ0ftJjTz4fA2h/7GOVzxemrTjx88vAgMBAAGjUDBOMB0GA1UdDgQWBBQjz5Q2
YW2kBTb4XLqKFZMSBLpi6zAfBgNVHSMEGDAWgBQjz5Q2YW2kBTb4XLqKFZMSBLpi
6zAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQA/s94M/mSGELHJWIb1
oE0IKHWajBd3Pc8+O1TZRE+ke3q+rZRfcxd2dAjq6zQHJUs2+fs0B3DyT9Wtyyoq
UrRdsgprOdf2Cuw8bMIsCQOvqWKhhdlLTnCi2xaGJawGsIkheuD1n+Il9gRQ2WGy
lACxVngPwjNYxjOE+CUnSZCuAmAfQYzqto3bNPqkgEwb7ueODeOiyhR8SKsH7ySW
QAOSxgrLBblGLWcDF9fjMeYaUnI34pHviCKeVxfgsxDR+Jg11F78sPdYLOF6ipBe
/5qTYucsY20B2EKtlscD0mSYBRwbVrSQt2RYbTCwaibxWUC13VV+YEk0NAv9Mm04
6sKO
-----END CERTIFICATE-----`

certPool, err := Certificate{CA: caCert}.CACertPool()
Expect(err).ToNot(HaveOccurred())
Expect(certPool).ToNot(BeNil())
})

It("returns an error when the CA cert is not valid PEM", func() {
_, err := Certificate{CA: "not-a-valid-pem"}.CACertPool()
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("Parsing certificate 1: Missing PEM block"))
})
})
})
12 changes: 12 additions & 0 deletions installation/manifest/manifest.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package manifest

import (
"crypto/x509"

boshcrypto "github.com/cloudfoundry/bosh-utils/crypto"
biproperty "github.com/cloudfoundry/bosh-utils/property"
)

Expand All @@ -18,6 +21,15 @@ type Certificate struct {
CA string
}

// CACertPool parses the PEM-encoded CA certificate and returns an *x509.CertPool.
// Returns (nil, nil) when CA is empty, meaning the system root CAs will be used.
func (c Certificate) CACertPool() (*x509.CertPool, error) {
if len(c.CA) == 0 {
return nil, nil
}
return boshcrypto.CertPoolFromPEM([]byte(c.CA))
}

type ReleaseJobRef struct {
Name string
Release string
Expand Down