Skip to content

Commit

Permalink
Add CA cert buildpack (#341)
Browse files Browse the repository at this point in the history
add ca cert buildpack test

Signed-off-by: Sophie Wigmore <[email protected]>
Co-authored-by: Andy Brown <[email protected]>
  • Loading branch information
Sophie Wigmore and Andy Brown authored Mar 9, 2021
1 parent 2a38dcb commit 615f8bd
Show file tree
Hide file tree
Showing 27 changed files with 810 additions and 3 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,11 @@ Usage examples can be found in the
#### The Node.js buildpack is compatible with the following builder(s):
- [Paketo Full Builder](https://github.com/paketo-buildpacks/full-builder)
- [Paketo Base Builder](https://github.com/paketo-buildpacks/base-builder) (for apps which do not leverage common C libraries)

This buildpack also includes the following utility buildpacks:
- [Procfile CNB](https://github.com/paketo-buildpacks/procfile)
- [Environment Variables CNB](https://github.com/paketo-buildpacks/environment-variables)
- [Image Labels CNB](https://github.com/paketo-buildpacks/image-labels)
- [CA Certificates CNB](https://github.com/paketo-buildpacks/ca-certificates)

Check out the [Paketo Node.js docs](https://paketo.io/docs/buildpacks/language-family-buildpacks/nodejs/) for more information.
15 changes: 15 additions & 0 deletions buildpack.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ api = "0.2"

[[order]]

[[order.group]]
id = "paketo-buildpacks/ca-certificates"
optional = true
version = "2.1.0"

[[order.group]]
id = "paketo-buildpacks/node-engine"
version = "0.2.2"
Expand Down Expand Up @@ -43,6 +48,11 @@ api = "0.2"

[[order]]

[[order.group]]
id = "paketo-buildpacks/ca-certificates"
optional = true
version = "2.1.0"

[[order.group]]
id = "paketo-buildpacks/node-engine"
version = "0.2.2"
Expand Down Expand Up @@ -72,6 +82,11 @@ api = "0.2"

[[order]]

[[order.group]]
id = "paketo-buildpacks/ca-certificates"
optional = true
version = "2.1.0"

[[order.group]]
id = "paketo-buildpacks/node-engine"
version = "0.2.2"
Expand Down
87 changes: 86 additions & 1 deletion integration/node_start_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package integration_test

import (
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"net/http"
Expand Down Expand Up @@ -111,7 +113,7 @@ func testNodeStart(t *testing.T, context spec.G, it spec.S) {
Expect(logs).To(ContainLines(ContainSubstring("Environment Variables Buildpack")))
Expect(logs).To(ContainLines(ContainSubstring("Image Labels Buildpack")))

Expect(image.Buildpacks[3].Layers["environment-variables"].Metadata["variables"]).To(Equal(map[string]interface{}{"SOME_VARIABLE": "some-value"}))
Expect(image.Buildpacks[4].Layers["environment-variables"].Metadata["variables"]).To(Equal(map[string]interface{}{"SOME_VARIABLE": "some-value"}))
Expect(image.Labels["some-label"]).To(Equal("some-value"))

container, err = docker.Container.Run.
Expand All @@ -133,5 +135,88 @@ func testNodeStart(t *testing.T, context spec.G, it spec.S) {
Expect(string(content)).To(ContainSubstring("hello world"))
})
})

context("when using CA certificates", func() {
var (
client *http.Client
)

it.Before(func() {
var err error
name, err = occam.RandomName()
Expect(err).NotTo(HaveOccurred())
source, err = occam.Source(filepath.Join("testdata", "ca_cert_apps"))
Expect(err).NotTo(HaveOccurred())

caCert, err := ioutil.ReadFile(fmt.Sprintf("%s/client-certs/ca.pem", source))
Expect(err).ToNot(HaveOccurred())

caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)

cert, err := tls.LoadX509KeyPair(fmt.Sprintf("%s/client-certs/cert.pem", source), fmt.Sprintf("%s/client-certs/key.pem", source))
Expect(err).ToNot(HaveOccurred())

client = &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: caCertPool,
Certificates: []tls.Certificate{cert},
MinVersion: tls.VersionTLS12,
},
},
}
})

it("builds a working OCI image and uses a client-side CA cert for requests", func() {
var err error
var logs fmt.Stringer
image, logs, err = pack.WithNoColor().Build.
WithBuildpacks(nodeBuildpack).
WithPullPolicy("never").
Execute(name, filepath.Join(source, "node_server"))
Expect(err).NotTo(HaveOccurred())

Expect(logs).To(ContainLines(ContainSubstring("CA Certificates Buildpack")))
Expect(logs).To(ContainLines(ContainSubstring("Node Engine Buildpack")))
Expect(logs).To(ContainLines(ContainSubstring("Node Start Buildpack")))

// NOTE: NODE_OPTIONS="--use-openssl-ca" is NOT required since the node binary is compiled with `--openssl-use-def-ca-store`
container, err = docker.Container.Run.
WithPublish("8080").
WithEnv(map[string]string{
"PORT": "8080",
"SERVICE_BINDING_ROOT": "/bindings",
}).
WithVolume(fmt.Sprintf("%s/binding:/bindings/ca-certificates", source)).
Execute(image.ID)
Expect(err).NotTo(HaveOccurred())

Eventually(func() string {
cLogs, err := docker.Container.Logs.Execute(container.ID)
Expect(err).NotTo(HaveOccurred())
return cLogs.String()
}).Should(
ContainSubstring("Added 1 additional CA certificate(s) to system truststore"),
)

request, err := http.NewRequest("GET", fmt.Sprintf("https://localhost:%s", container.HostPort("8080")), nil)
Expect(err).NotTo(HaveOccurred())

var response *http.Response
Eventually(func() error {
var err error
response, err = client.Do(request)
return err
}).Should(BeNil())
defer response.Body.Close()

Expect(response.StatusCode).To(Equal(http.StatusOK))

content, err := ioutil.ReadAll(response.Body)
Expect(err).NotTo(HaveOccurred())
Expect(string(content)).To(ContainSubstring("Hello, world!"))
})
})
})
}
89 changes: 88 additions & 1 deletion integration/npm_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package integration_test

import (
"crypto/tls"
"crypto/x509"
"encoding/json"
"fmt"
"io/ioutil"
Expand Down Expand Up @@ -117,7 +119,7 @@ func testNPM(t *testing.T, context spec.G, it spec.S) {
Expect(logs).To(ContainLines(ContainSubstring("Environment Variables Buildpack")))
Expect(logs).To(ContainLines(ContainSubstring("Image Labels Buildpack")))

Expect(image.Buildpacks[4].Layers["environment-variables"].Metadata["variables"]).To(Equal(map[string]interface{}{"SOME_VARIABLE": "some-value"}))
Expect(image.Buildpacks[5].Layers["environment-variables"].Metadata["variables"]).To(Equal(map[string]interface{}{"SOME_VARIABLE": "some-value"}))
Expect(image.Labels["some-label"]).To(Equal("some-value"))

container, err = docker.Container.Run.
Expand All @@ -140,5 +142,90 @@ func testNPM(t *testing.T, context spec.G, it spec.S) {
Expect(env.NpmConfigLoglevel).To(Equal("error"))
})
})

context("when using CA certificates", func() {
var (
client *http.Client
)

it.Before(func() {
var err error
source, err = occam.Source(filepath.Join("testdata", "ca_cert_apps"))
Expect(err).NotTo(HaveOccurred())

caCert, err := ioutil.ReadFile(fmt.Sprintf("%s/client-certs/ca.pem", source))
Expect(err).ToNot(HaveOccurred())

caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)

cert, err := tls.LoadX509KeyPair(fmt.Sprintf("%s/client-certs/cert.pem", source), fmt.Sprintf("%s/client-certs/key.pem", source))
Expect(err).ToNot(HaveOccurred())

client = &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: caCertPool,
Certificates: []tls.Certificate{cert},
MinVersion: tls.VersionTLS12,
},
},
}
})

it("builds a working OCI image and uses a client-side CA cert for requests", func() {
var err error
var logs fmt.Stringer
image, logs, err = pack.WithNoColor().Build.
WithBuildpacks(nodeBuildpack).
WithPullPolicy("never").
Execute(name, filepath.Join(source, "npm_server"))
Expect(err).NotTo(HaveOccurred())

Expect(logs).To(ContainLines(ContainSubstring("CA Certificates Buildpack")))
Expect(logs).To(ContainLines(ContainSubstring("Node Engine Buildpack")))
Expect(logs).To(ContainLines(ContainSubstring("NPM Install Buildpack")))
Expect(logs).To(ContainLines(ContainSubstring("NPM Start Buildpack")))

// NOTE: NODE_OPTIONS="--use-openssl-ca" is NOT required since the node binary is compiled with `--openssl-use-def-ca-store`
container, err = docker.Container.Run.
WithPublish("8080").
WithEnv(map[string]string{
"PORT": "8080",
"SERVICE_BINDING_ROOT": "/bindings",
}).
WithVolume(fmt.Sprintf("%s/binding:/bindings/ca-certificates", source)).
Execute(image.ID)
Expect(err).NotTo(HaveOccurred())

Eventually(func() string {
cLogs, err := docker.Container.Logs.Execute(container.ID)
Expect(err).NotTo(HaveOccurred())
return cLogs.String()
}).Should(
ContainSubstring("Added 1 additional CA certificate(s) to system truststore"),
)

request, err := http.NewRequest("GET", fmt.Sprintf("https://localhost:%s/env", container.HostPort("8080")), nil)
Expect(err).NotTo(HaveOccurred())

var response *http.Response
Eventually(func() error {
var err error
response, err = client.Do(request)
return err
}).Should(BeNil())
defer response.Body.Close()

Expect(response.StatusCode).To(Equal(http.StatusOK))

var env struct {
NpmConfigLoglevel string `json:"NPM_CONFIG_LOGLEVEL"`
}

Expect(json.NewDecoder(response.Body).Decode(&env)).To(Succeed())
Expect(env.NpmConfigLoglevel).To(Equal("error"))
})
})
})
}
32 changes: 32 additions & 0 deletions integration/testdata/ca_cert_apps/binding/ca.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFeTCCA2GgAwIBAgICB+UwDQYJKoZIhvcNAQELBQAwTjEaMBgGA1UEChMRUGFr
ZXRvIEJ1aWxkcGFja3MxMDAuBgNVBAMTJ1Bha2V0byBCdWlsZHBhY2tzIENlcnRp
ZmljYXRlIEF1dGhvcml0eTAeFw0yMTAzMDIxNjU5MjRaFw0zMTAzMDIxNjU5MjRa
ME4xGjAYBgNVBAoTEVBha2V0byBCdWlsZHBhY2tzMTAwLgYDVQQDEydQYWtldG8g
QnVpbGRwYWNrcyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEB
AQUAA4ICDwAwggIKAoICAQDGQS7V1sKolBHl7WV19S3sDZwUCP4BR0UrZqvR2hmm
v0D1tsxmVDepX0hbQOMUq9oiSsDga2uNrVfvLNC81PZF+KePgTawdIVCF6eSPZyY
muVDorvd7UGpKdx1AL3Ez+QhLj9PGP1EW6nU7oD/6XzJWeRkNZWxPNP0Ev6fChfN
qKXkPJYNHPrq3G7LQ6do7oaexXEOMd+lxLtUObSXyzMQn50olkxbogLC0ncNb17h
8f0YzogQnZxe0LIkXTkbq47UJcZyZVXe4Vjuf6YXP8NY8ABW380hSbI/aHR0btD1
mA24ups0XGcqD/4v8LtrEFm1pBC58faDQi435hVvaiuzz9wbhAMh1J18/FbiqnkR
P+l2nEQOButa1F4ClZZ989bhJboW6LSafQRJT4yAzfRuTXBzAWFYJrikdVabfnwZ
z2rZrR4lgj5IklEnTk1DiJVvNP87faTY4L0CZexrjj4YRq0BLvMG7hAkJ/7/5WZK
nNSJf+P7PNyDNWUPR+aN8R411PUG53Mxyi+lumi6eucmLQONCIuI2EdQ4iPP+QR4
7r1eLS/+Sxn4mifux6GcDbzTrJDHXtaqCFWRyKUdooEo7dJVceJ/lxJLpn8W/enH
OJs8J9IkUjtYizNkfDniIvHQoU3mQZx6P8AzKTJ7XA5i3Xw+YUVaM/z91aQML5sz
xQIDAQABo2EwXzAOBgNVHQ8BAf8EBAMCAoQwHQYDVR0lBBYwFAYIKwYBBQUHAwIG
CCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFEwwGrd7Rto0v2qq
YBRt7pucOSjzMA0GCSqGSIb3DQEBCwUAA4ICAQBHNA41hSIBvV7DNy7Winm1ls0R
bICOEawuUOZnKY3Feq1XPsPj1IHaOa4TEwPyOFs89tS5Fig5jIDtpHp+f2dYRknC
eLKjDBZRUWnsuHQIR0MS4k8Bp6ztMiUTdAUSiY+Xv11sa4qGo/NCQdVSzp4wBEBM
ZgnPTF9jU2OjxTufTDhbvYNAbM1l8+aBdF4/88s6D5OEbp3mYhCy8t82x7FKmLee
9GcQHoZxZjdcJfX5pBtDo9nakeLVbg5PgRUbFsaZyVXQL56VhfEYiM3UzqZCDDuh
L+WU0pFkg0TX1xZ6438N8WD0zg44ObKEORfEvPAxbcKa+Nb8p1WsMKJmbeDDaXib
wu7A+kWa5s4rf9zlDt0Y4wVFNuO602dO6VFyZXg8dcRyjRNa/0w/l4elgOPRonbl
8P9JY1KtIkZvQ8+f7NwloSGg+/Q0j2XqTSM04TP+RQ92iSNfsm+x8gZotKdUrvUN
/7sLRgJvlJoBQ+bSHveT7r1IXE0zkM8535kYD8UEQ68cqZUEDOwK8qo2n4HY5oQ3
4BTkHOAEu39Ov6MrgeiSDzINc2eBadJcb5CwTKH17gLGDeQz8H4fRKSv2+6Pty6H
esKmgqCE28Xnaz3snOEYSRfnG0+o5rgrXFUd0qAjpiu/rqCOXl9TeSEUM+YvjjRC
/HNPsIbBVFyt0n7mYw==
-----END CERTIFICATE-----
1 change: 1 addition & 0 deletions integration/testdata/ca_cert_apps/binding/type
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ca-certificates
32 changes: 32 additions & 0 deletions integration/testdata/ca_cert_apps/client-certs/ca.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFeTCCA2GgAwIBAgICB+UwDQYJKoZIhvcNAQELBQAwTjEaMBgGA1UEChMRUGFr
ZXRvIEJ1aWxkcGFja3MxMDAuBgNVBAMTJ1Bha2V0byBCdWlsZHBhY2tzIENlcnRp
ZmljYXRlIEF1dGhvcml0eTAeFw0yMTAzMDIxNjU5MjRaFw0zMTAzMDIxNjU5MjRa
ME4xGjAYBgNVBAoTEVBha2V0byBCdWlsZHBhY2tzMTAwLgYDVQQDEydQYWtldG8g
QnVpbGRwYWNrcyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEB
AQUAA4ICDwAwggIKAoICAQDGQS7V1sKolBHl7WV19S3sDZwUCP4BR0UrZqvR2hmm
v0D1tsxmVDepX0hbQOMUq9oiSsDga2uNrVfvLNC81PZF+KePgTawdIVCF6eSPZyY
muVDorvd7UGpKdx1AL3Ez+QhLj9PGP1EW6nU7oD/6XzJWeRkNZWxPNP0Ev6fChfN
qKXkPJYNHPrq3G7LQ6do7oaexXEOMd+lxLtUObSXyzMQn50olkxbogLC0ncNb17h
8f0YzogQnZxe0LIkXTkbq47UJcZyZVXe4Vjuf6YXP8NY8ABW380hSbI/aHR0btD1
mA24ups0XGcqD/4v8LtrEFm1pBC58faDQi435hVvaiuzz9wbhAMh1J18/FbiqnkR
P+l2nEQOButa1F4ClZZ989bhJboW6LSafQRJT4yAzfRuTXBzAWFYJrikdVabfnwZ
z2rZrR4lgj5IklEnTk1DiJVvNP87faTY4L0CZexrjj4YRq0BLvMG7hAkJ/7/5WZK
nNSJf+P7PNyDNWUPR+aN8R411PUG53Mxyi+lumi6eucmLQONCIuI2EdQ4iPP+QR4
7r1eLS/+Sxn4mifux6GcDbzTrJDHXtaqCFWRyKUdooEo7dJVceJ/lxJLpn8W/enH
OJs8J9IkUjtYizNkfDniIvHQoU3mQZx6P8AzKTJ7XA5i3Xw+YUVaM/z91aQML5sz
xQIDAQABo2EwXzAOBgNVHQ8BAf8EBAMCAoQwHQYDVR0lBBYwFAYIKwYBBQUHAwIG
CCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFEwwGrd7Rto0v2qq
YBRt7pucOSjzMA0GCSqGSIb3DQEBCwUAA4ICAQBHNA41hSIBvV7DNy7Winm1ls0R
bICOEawuUOZnKY3Feq1XPsPj1IHaOa4TEwPyOFs89tS5Fig5jIDtpHp+f2dYRknC
eLKjDBZRUWnsuHQIR0MS4k8Bp6ztMiUTdAUSiY+Xv11sa4qGo/NCQdVSzp4wBEBM
ZgnPTF9jU2OjxTufTDhbvYNAbM1l8+aBdF4/88s6D5OEbp3mYhCy8t82x7FKmLee
9GcQHoZxZjdcJfX5pBtDo9nakeLVbg5PgRUbFsaZyVXQL56VhfEYiM3UzqZCDDuh
L+WU0pFkg0TX1xZ6438N8WD0zg44ObKEORfEvPAxbcKa+Nb8p1WsMKJmbeDDaXib
wu7A+kWa5s4rf9zlDt0Y4wVFNuO602dO6VFyZXg8dcRyjRNa/0w/l4elgOPRonbl
8P9JY1KtIkZvQ8+f7NwloSGg+/Q0j2XqTSM04TP+RQ92iSNfsm+x8gZotKdUrvUN
/7sLRgJvlJoBQ+bSHveT7r1IXE0zkM8535kYD8UEQ68cqZUEDOwK8qo2n4HY5oQ3
4BTkHOAEu39Ov6MrgeiSDzINc2eBadJcb5CwTKH17gLGDeQz8H4fRKSv2+6Pty6H
esKmgqCE28Xnaz3snOEYSRfnG0+o5rgrXFUd0qAjpiu/rqCOXl9TeSEUM+YvjjRC
/HNPsIbBVFyt0n7mYw==
-----END CERTIFICATE-----
32 changes: 32 additions & 0 deletions integration/testdata/ca_cert_apps/client-certs/cert.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFfTCCA2WgAwIBAgICBnowDQYJKoZIhvcNAQELBQAwTjEaMBgGA1UEChMRUGFr
ZXRvIEJ1aWxkcGFja3MxMDAuBgNVBAMTJ1Bha2V0byBCdWlsZHBhY2tzIENlcnRp
ZmljYXRlIEF1dGhvcml0eTAeFw0yMTAzMDIxNjU5MjdaFw0zMTAzMDIxNjU5Mjda
MEQxGjAYBgNVBAoTEVBha2V0byBCdWlsZHBhY2tzMSYwJAYDVQQDEx1QYWtldG8g
QnVpbGRwYWNrcyBDZXJ0aWZpY2F0ZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC
AgoCggIBAKDexTQRJcn/3xPtCWUhJv20s1pjbr1QxaJlOv6sI+xXBg0nMSn2i0d4
G7BC9yt704WJEy3rnKpA7WPejGYqi5dW711Z/gkie8MNL3zR1FrOX5/MgRgGm2S0
3HNdjxs84zeanhRc64Z80Qyu7dfi7i68mUzp8Z5rwU9Lf8HuouAG9H94Uv4v/x4A
gIZydiSLepG4Mp1QTznogVUMsSynwG6xWLPTsfOTA9fMidTSKSFcHeLl/qBnecWv
MxQdIm8hkSlq+kGu+GXuZQA3eRBufvEhX/swZGIwla8XjDQqIWubQlLvNHm9ETMm
Eru3MTX1sO71Xiv2rZhaZeg/hnUFDZrR7cvFZLbGdhkApWpmq/5lAzKKu+QRgbXN
+TfMfw0Y650b4Nq718NEmwFjYp1TXWrcBB+uG4qvrtYbBC9W/BN/EuNeBUIBZIA0
66UpoKCRXbd3OKZV0OPZs4apqqi+y+P54ye5tFEoASdAN6g70P4gIklUz6ftCj8T
fssPXRysqIwtZB0LTdEU0GZtfhGd32xF5RswJwxayfTOvd9c5yrqYs7iGg21MoOL
uxp+LU3Nb+Q96NMW+JuJ6XNSjTzpBTTUmFKeggAgizh7r+gHiX/sJT/g50cRztc6
euJLEl1u8cSqiX/p5a6Ecpieu7ORjJZqJ6LPUEv5KDDWeNvGhoGBAgMBAAGjbzBt
MA4GA1UdDwEB/wQEAwIHgDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEw
DgYDVR0OBAcEBQECAwQGMCwGA1UdEQQlMCOCCWxvY2FsaG9zdIcEfwAAAYcQAAAA
AAAAAAAAAAAAAAAAATANBgkqhkiG9w0BAQsFAAOCAgEAiexbcqQJxIjKFSBQSLpX
q21pzxoCr8qULMoJEBTiBz+Aqn23nE076PBiKAd+8jWP2wIiQXqhdDqE0QV/MHvE
v3Atabd7L5hkulUw13Gl7JtG750b2nC9EoJpimgicmh/upWtzBUcBNXUN0cY9N+I
T58CfG/MYLbGR07mSeRlxnQiTMSfJ5m1G57UmqOSBqhqXai6x5/MCQHl+MIglLqh
+bRzeS1ZlgFKFWaoa+lHVQt5qqDsaaQmtFdjskudEjezlsKXNmHPOFbOH9enOJ7Z
1EobCIvb9eLCjc8FL6hEGyVFACuxAkDds23idwIJXcOVPMs7JOPa/Lb5jBNbOYLc
hYj1Op0piHEPosedtighQc3sFNvroDRYm46zBRrwbLTPQlplOBkZEssUnoDCJQJq
TwgwucnRj0nnZtHfSsaHv5LcWjW+GI9ox2qpxx8XJgTnmr2LfiNJIBViGppqjEoZ
FgGzqcd3Gle0LAXcz8mzCxHsGb0AyyVK7+GziZxTc7tiBorpn9LOA0tBlrWTrN0u
ptIfJIsKo//HVwv/rhEfRapQYTMv4trF6jFF/Kh/eyfbfkndRFlu3ywdtf+3peKI
vYZ8GeSBM86UvRyMA9nWbj8Q7NEsmg9ZHlzzG7iC32U+Zp1yy/rDrKqQWKxNojrv
o7kYPHxnraAtNlmQEDyEQ9s=
-----END CERTIFICATE-----
Loading

0 comments on commit 615f8bd

Please sign in to comment.