Skip to content

Commit

Permalink
OSSM-5873: Add test case for certificate revocation list (#659)
Browse files Browse the repository at this point in the history
* Update a generate script for test certificates to support CRL

* all related sample certs were regenerated

* OSSM-5873: Add test case for certificate revocation list

* Add check for error message from curl request
  • Loading branch information
mkralik3 committed Feb 20, 2024
1 parent eb96d5a commit 88b4985
Show file tree
Hide file tree
Showing 28 changed files with 560 additions and 309 deletions.
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,10 @@ tests/jmeter-report.out
tests/jmeter.log
tests/debug
tests/rrr.yaml

## config files generated by sampleCerts/generate.sh
sampleCerts/httpbin.example.com/client.conf
sampleCerts/httpbin.example.com/server.conf
sampleCerts/httpbin.example.com/crl.conf
sampleCerts/httpbin.example.com/index.txt.attr
sampleCerts/httpbin.example.com/index.txt.old
61 changes: 60 additions & 1 deletion pkg/tests/tasks/traffic/ingress/secure_gateways_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,11 @@ func TestSecureGateways(t *testing.T) {
request.WithTLS(httpbinSampleCACert, "httpbin.example.com", gatewayHost, gatewayPort),
assert.RequestFails(
"request failed as expected",
"expected request to fail because no client certificate was provided"))
"expected request to fail because no client certificate was provided"),
assert.RequestFailsWithErrorMessage(
"Get \"https://httpbin.example.com:443/status/418\": remote error: tls: certificate require",
"request failed with expected error message",
"request failed but with different error message"))
})

t.LogStep("check if SSL handshake succeeds when client certificate is given")
Expand All @@ -151,5 +155,60 @@ func TestSecureGateways(t *testing.T) {
assert.ResponseContains("-=[ teapot ]=-"))
})
})

t.NewSubTest("mutual_tls_with_crl").Run(func(t TestHelper) {
t.Log("Reference: https://issues.redhat.com/browse/OSSM-414")
if env.GetSMCPVersion().LessThan(version.SMCP_2_5) {
t.Skip("Skipping until 2.5")
}
t.LogStep("configure Gateway with tls.mode=Mutual and provide CRL file")
oc.CreateGenericSecretFromFiles(t, meshNamespace, "httpbin-credential",
"tls.key="+httpbinSampleServerCertKey,
"tls.crt="+httpbinSampleServerCert,
"ca.crt="+httpbinSampleCACert,
"ca.crl="+httpbinSampleCACrl)
oc.ApplyString(t, ns, gatewayHttpbinMTLSYaml)

createRouteWithTLS(t, meshNamespace, "httpbin.example.com", "https", "istio-ingressgateway", "passthrough")
t.LogStep("check if SSL handshake fails when no client certificate is given")
retry.UntilSuccess(t, func(t TestHelper) {
curl.Request(t,
teapotURL,
request.WithTLS(httpbinSampleCACert, "httpbin.example.com", gatewayHost, gatewayPort),
assert.RequestFails(
"request failed as expected",
"expected request to fail because no client certificate was provided"),
assert.RequestFailsWithErrorMessage(
"Get \"https://httpbin.example.com:443/status/418\": remote error: tls: certificate require",
"request failed with expected error message",
"request failed but with different error message"))
})

t.LogStep("check if SSL handshake succeeds when client certificate is given")
retry.UntilSuccess(t, func(t TestHelper) {
curl.Request(t,
teapotURL,
request.
WithTLS(httpbinSampleCACert, "httpbin.example.com", gatewayHost, gatewayPort).
WithClientCertificate(httpbinSampleClientCert, httpbinSampleClientCertKey),
assert.ResponseContains("-=[ teapot ]=-"))
})

t.LogStep("check if SSL handshake fails when revoked client certificate is given")
retry.UntilSuccess(t, func(t TestHelper) {
curl.Request(t,
teapotURL,
request.
WithTLS(httpbinSampleCACert, "httpbin.example.com", gatewayHost, gatewayPort).
WithClientCertificate(httpbinSampleClientRevokedCert, httpbinSampleClientRevokedCertKey),
assert.RequestFails(
"request failed as expected",
"expected request to fail because revoked client certificate was provided"),
assert.RequestFailsWithErrorMessage(
"Get \"https://httpbin.example.com:443/status/418\": remote error: tls: revoked certificate",
"request failed with expected error message",
"request failed but with different error message"))
})
})
})
}
13 changes: 8 additions & 5 deletions pkg/tests/tasks/traffic/ingress/yaml_vars.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,14 @@ import (
)

var (
httpbinSampleServerCertKey = env.GetRootDir() + "/sampleCerts/httpbin.example.com/httpbin.example.com.key"
httpbinSampleServerCert = env.GetRootDir() + "/sampleCerts/httpbin.example.com/httpbin.example.com.crt"
httpbinSampleCACert = env.GetRootDir() + "/sampleCerts/httpbin.example.com/example.com.crt"
httpbinSampleClientCert = env.GetRootDir() + "/sampleCerts/httpbin.example.com/httpbin-client.example.com.crt"
httpbinSampleClientCertKey = env.GetRootDir() + "/sampleCerts/httpbin.example.com/httpbin-client.example.com.key"
httpbinSampleServerCertKey = env.GetRootDir() + "/sampleCerts/httpbin.example.com/httpbin.example.com.key"
httpbinSampleServerCert = env.GetRootDir() + "/sampleCerts/httpbin.example.com/httpbin.example.com.crt"
httpbinSampleCACert = env.GetRootDir() + "/sampleCerts/httpbin.example.com/example.com.crt"
httpbinSampleCACrl = env.GetRootDir() + "/sampleCerts/httpbin.example.com/example.com.crl"
httpbinSampleClientCert = env.GetRootDir() + "/sampleCerts/httpbin.example.com/httpbin-client.example.com.crt"
httpbinSampleClientCertKey = env.GetRootDir() + "/sampleCerts/httpbin.example.com/httpbin-client.example.com.key"
httpbinSampleClientRevokedCert = env.GetRootDir() + "/sampleCerts/httpbin.example.com/httpbin-client-revoked.example.com.crt"
httpbinSampleClientRevokedCertKey = env.GetRootDir() + "/sampleCerts/httpbin.example.com/httpbin-client-revoked.example.com.key"

helloworldServerCertKey = env.GetRootDir() + "/sampleCerts/helloworldv1/helloworld-v1.example.com.key"
helloworldServerCert = env.GetRootDir() + "/sampleCerts/helloworldv1/helloworld-v1.example.com.crt"
Expand Down
2 changes: 1 addition & 1 deletion pkg/tests/tasks/traffic/traffic_shifting_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func checkTrafficRatio(t TestHelper, url string, numberOfRequests int, tolerance
curl.Request(t,
url, nil,
require.ResponseStatus(http.StatusOK),
func(t TestHelper, response *http.Response, responseBody []byte, duration time.Duration) {
func(t TestHelper, response *http.Response, responseBody []byte, responseErr error, duration time.Duration) {
comparisonErrors := map[string]error{}
matched := false
for file := range ratios {
Expand Down
21 changes: 14 additions & 7 deletions pkg/util/check/assert/http_response.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,50 +10,57 @@ import (
)

func ResponseMatchesFile(file string, successMsg, failureMsg string, otherFiles ...string) curl.HTTPResponseCheckFunc {
return func(t test.TestHelper, resp *http.Response, responseBody []byte, duration time.Duration) {
return func(t test.TestHelper, resp *http.Response, responseBody []byte, responseErr error, duration time.Duration) {
t.T().Helper()
common.CheckResponseMatchesFile(t, resp, responseBody, file, successMsg, failureMsg, assertFailure, otherFiles...)
}
}

func ResponseStatus(expectedStatus int) curl.HTTPResponseCheckFunc {
return func(t test.TestHelper, resp *http.Response, responseBody []byte, duration time.Duration) {
return func(t test.TestHelper, resp *http.Response, responseBody []byte, responseErr error, duration time.Duration) {
t.T().Helper()
common.CheckResponseStatus(t, resp, responseBody, expectedStatus, assertFailure)
}
}

func ResponseContains(str string) curl.HTTPResponseCheckFunc {
return func(t test.TestHelper, resp *http.Response, responseBody []byte, duration time.Duration) {
return func(t test.TestHelper, resp *http.Response, responseBody []byte, responseErr error, duration time.Duration) {
t.T().Helper()
common.CheckResponseContains(t, resp, responseBody, str, assertFailure)
}
}

func ResponseDoesNotContain(str string) curl.HTTPResponseCheckFunc {
return func(t test.TestHelper, resp *http.Response, responseBody []byte, duration time.Duration) {
return func(t test.TestHelper, resp *http.Response, responseBody []byte, responseErr error, duration time.Duration) {
t.T().Helper()
common.CheckResponseDoesNotContain(t, resp, responseBody, str, assertFailure)
}
}

func DurationInRange(minDuration, maxDuration time.Duration) curl.HTTPResponseCheckFunc {
return func(t test.TestHelper, resp *http.Response, responseBody []byte, duration time.Duration) {
return func(t test.TestHelper, resp *http.Response, responseBody []byte, responseErr error, duration time.Duration) {
t.T().Helper()
common.CheckDurationInRange(t, resp, duration, minDuration, maxDuration, assertFailure)
}
}

func RequestSucceeds(successMsg, failureMsg string) curl.HTTPResponseCheckFunc {
return func(t test.TestHelper, resp *http.Response, responseBody []byte, duration time.Duration) {
return func(t test.TestHelper, resp *http.Response, responseBody []byte, responseErr error, duration time.Duration) {
t.T().Helper()
common.CheckRequestSucceeds(t, resp, responseBody, successMsg, failureMsg, assertFailure)
}
}

func RequestFails(successMsg, failureMsg string) curl.HTTPResponseCheckFunc {
return func(t test.TestHelper, resp *http.Response, responseBody []byte, duration time.Duration) {
return func(t test.TestHelper, resp *http.Response, responseBody []byte, responseErr error, duration time.Duration) {
t.T().Helper()
common.CheckRequestFails(t, resp, responseBody, successMsg, failureMsg, assertFailure)
}
}

func RequestFailsWithErrorMessage(expectedErrorMessage, successMsg, failureMsg string) curl.HTTPResponseCheckFunc {
return func(t test.TestHelper, resp *http.Response, responseBody []byte, responseErr error, duration time.Duration) {
t.T().Helper()
common.CheckRequestFailureMessage(t, responseErr, expectedErrorMessage, successMsg, failureMsg, assertFailure)
}
}
14 changes: 14 additions & 0 deletions pkg/util/check/common/http_response.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,20 @@ func CheckRequestFails(t test.TestHelper, resp *http.Response, responseBody []by
}
}

func CheckRequestFailureMessage(t test.TestHelper, requestError error, expectedErrorMessage string, successMsg, failureMsg string, failure FailureFunc) {
t.T().Helper()
if requestError == nil {
failure(t, "expected request error, but it is nil", "")
} else if strings.Contains(requestError.Error(), expectedErrorMessage) {
if successMsg != "" {
logSuccess(t, successMsg)
}
} else {
detailMsg := fmt.Sprintf("\nexpected error message:'%s'\nactual error message:'%s'", expectedErrorMessage, requestError.Error())
failure(t, failureMsg, detailMsg)
}
}

func requireNonNilResponse(t test.TestHelper, resp *http.Response) {
t.T().Helper()
if resp == nil {
Expand Down
12 changes: 6 additions & 6 deletions pkg/util/check/require/http_response.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,42 +10,42 @@ import (
)

func ResponseMatchesFile(file string, successMsg, failureMsg string, otherFiles ...string) curl.HTTPResponseCheckFunc {
return func(t test.TestHelper, resp *http.Response, responseBody []byte, duration time.Duration) {
return func(t test.TestHelper, resp *http.Response, responseBody []byte, responseErr error, duration time.Duration) {
t.T().Helper()
common.CheckResponseMatchesFile(t, resp, responseBody, file, successMsg, failureMsg, requireFailure, otherFiles...)
}
}

func ResponseStatus(expectedStatus int) curl.HTTPResponseCheckFunc {
return func(t test.TestHelper, resp *http.Response, responseBody []byte, duration time.Duration) {
return func(t test.TestHelper, resp *http.Response, responseBody []byte, responseErr error, duration time.Duration) {
t.T().Helper()
common.CheckResponseStatus(t, resp, responseBody, expectedStatus, requireFailure)
}
}

func ResponseContains(str string) curl.HTTPResponseCheckFunc {
return func(t test.TestHelper, resp *http.Response, responseBody []byte, duration time.Duration) {
return func(t test.TestHelper, resp *http.Response, responseBody []byte, responseErr error, duration time.Duration) {
t.T().Helper()
common.CheckResponseContains(t, resp, responseBody, str, requireFailure)
}
}

func DurationInRange(minDuration, maxDuration time.Duration) curl.HTTPResponseCheckFunc {
return func(t test.TestHelper, resp *http.Response, responseBody []byte, duration time.Duration) {
return func(t test.TestHelper, resp *http.Response, responseBody []byte, responseErr error, duration time.Duration) {
t.T().Helper()
common.CheckDurationInRange(t, resp, duration, minDuration, maxDuration, requireFailure)
}
}

func RequestSucceeds(successMsg, failureMsg string) curl.HTTPResponseCheckFunc {
return func(t test.TestHelper, resp *http.Response, responseBody []byte, duration time.Duration) {
return func(t test.TestHelper, resp *http.Response, responseBody []byte, responseErr error, duration time.Duration) {
t.T().Helper()
common.CheckRequestSucceeds(t, resp, responseBody, successMsg, failureMsg, requireFailure)
}
}

func RequestFails(successMsg, failureMsg string) curl.HTTPResponseCheckFunc {
return func(t test.TestHelper, resp *http.Response, responseBody []byte, duration time.Duration) {
return func(t test.TestHelper, resp *http.Response, responseBody []byte, responseErr error, duration time.Duration) {
t.T().Helper()
common.CheckRequestFails(t, resp, responseBody, successMsg, failureMsg, requireFailure)
}
Expand Down
10 changes: 5 additions & 5 deletions pkg/util/curl/curl.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"github.com/maistra/maistra-test-tool/pkg/util/test"
)

type HTTPResponseCheckFunc func(t test.TestHelper, response *http.Response, responseBody []byte, duration time.Duration)
type HTTPResponseCheckFunc func(t test.TestHelper, response *http.Response, responseBody []byte, responseErr error, duration time.Duration)

func Request(t test.TestHelper, url string, requestOption RequestOption, checks ...HTTPResponseCheckFunc) []byte {
t.T().Helper()
Expand All @@ -34,9 +34,9 @@ func Request(t test.TestHelper, url string, requestOption RequestOption, checks
t.Fatalf("failed to modify request: %v", err)
}

resp, err := client.Do(req)
if err != nil {
t.Logf("failed to get HTTP Response: %v", err)
resp, resp_err := client.Do(req)
if resp_err != nil {
t.Logf("failed to get HTTP Response: %v", resp_err)
}

var responseBody []byte
Expand All @@ -54,7 +54,7 @@ func Request(t test.TestHelper, url string, requestOption RequestOption, checks

duration := time.Since(startT)
for _, check := range checks {
check(t, resp, responseBody, duration)
check(t, resp, responseBody, resp_err, duration)
}
return responseBody
}
Expand Down
38 changes: 19 additions & 19 deletions sampleCerts/bookinfo.com/bookinfo.com.crt
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDXzCCAkegAwIBAgIUd8blxsferSIM9fUuQhTHWq7goJswDQYJKoZIhvcNAQEL
BQAwLTEVMBMGA1UECgwMZXhhbXBsZSBJbmMuMRQwEgYDVQQDDAtleGFtcGxlLmNv
bTAeFw0yMjEwMTkyMTAwNDVaFw0zMjEwMTYyMTAwNDVaMDcxFTATBgNVBAMMDGJv
b2tpbmZvLmNvbTEeMBwGA1UECgwVYm9va2luZm8gb3JnYW5pemF0aW9uMIIBIjAN
BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtMe6OWCIDg5VTNaXC9SjUqOXWjJa
wkgATJYx4usxC6FGSVLf2qKbRNMAb4tTPlVECNfAqYSPo8DMPOjd/XXeF/3NQujk
1uNq8c2JoNDk9uIaszagy4e9bBl/hg3MWdBX7nflEBEGrk6L2G2hqMONhGqVjRQv
fOsT2NBqIo2gYYaGx3u2Wz96VXJl+J5avGhyaGqlqTSzxkbFX27Xl9kRsw+l271s
56RzJ+uLiyIWKI5BvABvMMkkMrXRqghXgvsFsoFOuOICTX0G2PX9a+sgrzGHWUy1
efL94dMQ0EgPmxizTar8UhLdn4JV5vjln7iFW1BvRK29WipU0TBLv5WGcQIDAQAB
o20wazApBgNVHREEIjAgggxib29raW5mby5jb22CEHd3dy5ib29raW5mby5jb20w
HQYDVR0OBBYEFKXbRNszNtZy0CYs2CrxOC0lMyYEMB8GA1UdIwQYMBaAFAMysFN+
gf9j2KLfyts65QUzs5HIMA0GCSqGSIb3DQEBCwUAA4IBAQCSPv6Ra282x1XPjlMi
OaZfP6e/rhakF5tT2G/NFXRlcFSU01e4axXFbxcntuD3G43Sit3gexMopGyYIDTR
JnZPiZ36tqHQXpdR96MLCIpDn08RlQJR+M6NHGhgp89XEhX0SPMxanbgcvVMPjft
RAldnPjzU5HZFmxzRrW+/g5cTuixda5NC4zGoweFb/i1xUs1Skrah7SJ7wXq9Yg+
KjkjSErr6xNlaU64AGFxDduf3U5trzoR4jx8/WriVtSRY2v2A71s9eLcUHUbVrK/
czp9McNLPed8VTI76Y8arSC8WUXPGqM1bj6K9TV181Oa2hpDLlG3tEt1p+NQ6+4W
6Yq/
MIIDYTCCAkmgAwIBAgIUSmSSphmm+CDh8cQ0hhebRBM0m68wDQYJKoZIhvcNAQEL
BQAwLzEVMBMGA1UECgwMZXhhbXBsZSBJbmMuMRYwFAYDVQQDDA0qLmV4YW1wbGUu
Y29tMB4XDTI0MDIwNjEzMDYxMFoXDTM0MDIwMzEzMDYxMFowNzEVMBMGA1UEAwwM
Ym9va2luZm8uY29tMR4wHAYDVQQKDBVib29raW5mbyBvcmdhbml6YXRpb24wggEi
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC/kHrEInlnzP2oe/mlIzBZWZpU
emDoyy4ddsUjYo6pxFbyGJVRd1jUKWSV7GP8euWJc5di5gD6RWlAJ8oBVej91j86
1yMPu73M6oZvVpbuWd2MpQmwzTTXqavnRxLvMvB7rRDLzdOR+fhhtIsnrwW+cTSi
rXZ4kc5X3qBJzTeKSE820Li0NOPq40zqnhpDhUFdM1yOGkshkjoicZ9JxoClYaaS
x3GbqDWAt5cdbFBfjMsGshRLH2FV50idxSr4Q5UE2oh2XEsnVILMV1P8TPUJHZ9b
nD9RZbcwZ/HPQRxkyEtV2Tyy/wLikYf9lqlrF7POqQsi9snYLiDYamEJ5S5RAgMB
AAGjbTBrMCkGA1UdEQQiMCCCDGJvb2tpbmZvLmNvbYIQd3d3LmJvb2tpbmZvLmNv
bTAdBgNVHQ4EFgQUQP/xSztCG34L97gY9jwC9aJwwUowHwYDVR0jBBgwFoAUDTeN
QbyIBtCo4+nzshgXBdcZdeIwDQYJKoZIhvcNAQELBQADggEBADi+2dzRU3+SVCi9
fRRp/TrJ1Ryg9LbX5WdHYL6Nkx/8vkOHVfG6JTSwzvFw+JCYREGkcYEssY6MYi5W
28qLCTdXXc+xqaWBxaJc1/HXiThsvK+eRsZqY2QKEc+1+PPxcPDUorTyIiMz3KFY
zP8fG+jxMZ9zDA3Dt112MgJasXPl49z5ERqVzCzQkX7SztvOUu5UjqM+U6JQWbNH
DV1Yw07rSpAM6RKN8ZPUztuzmF8MRKvHw1n/MJpk8G7QOgH6VXiH0Sr7+UhflyVT
uh9G1dGgziZLG5kl0vCQ1WOY43uO151tmV7FShJA8kXb+RDEw/UrMKCAOMAT1CaY
N60+Tys=
-----END CERTIFICATE-----
24 changes: 12 additions & 12 deletions sampleCerts/bookinfo.com/bookinfo.com.csr
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
-----BEGIN CERTIFICATE REQUEST-----
MIICfDCCAWQCAQAwNzEVMBMGA1UEAwwMYm9va2luZm8uY29tMR4wHAYDVQQKDBVi
b29raW5mbyBvcmdhbml6YXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQC0x7o5YIgODlVM1pcL1KNSo5daMlrCSABMljHi6zELoUZJUt/aoptE0wBv
i1M+VUQI18CphI+jwMw86N39dd4X/c1C6OTW42rxzYmg0OT24hqzNqDLh71sGX+G
DcxZ0Ffud+UQEQauTovYbaGow42EapWNFC986xPY0GoijaBhhobHe7ZbP3pVcmX4
nlq8aHJoaqWpNLPGRsVfbteX2RGzD6XbvWznpHMn64uLIhYojkG8AG8wySQytdGq
CFeC+wWygU644gJNfQbY9f1r6yCvMYdZTLV58v3h0xDQSA+bGLNNqvxSEt2fglXm
+OWfuIVbUG9Erb1aKlTRMEu/lYZxAgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAQEA
p7PjocgNZ9q4rBY7DRB/CdjCV27PQzeobEoZ4S+9Y5JQhcoz4EJIzPh1eAzx6Dff
XKpvmp+b8TuHdz8h9zuYiTlVMrviweDGgaHi/XSUzaQ+18ETGkAnlOj84UDSxhWN
ipFjE5R1KbXvJ0r3QcWhIEE3JdowHaSyqsBca9VB9Hwb32CjaoaX1hPwLESy5OT+
5lWUEd+LXp0uFm0PXY4vq12N76tAwiSzm7PqltiTUGfIYtJh7TCBareQQTtVsvaf
OjZZ45xJFGBOclw58CUInLGDMiu2UurSiySBa5ddN/DRDr3z6s0pjDSFUCZKRg0X
OfczxQRigv0PHOKUMbCaVg==
AoIBAQC/kHrEInlnzP2oe/mlIzBZWZpUemDoyy4ddsUjYo6pxFbyGJVRd1jUKWSV
7GP8euWJc5di5gD6RWlAJ8oBVej91j861yMPu73M6oZvVpbuWd2MpQmwzTTXqavn
RxLvMvB7rRDLzdOR+fhhtIsnrwW+cTSirXZ4kc5X3qBJzTeKSE820Li0NOPq40zq
nhpDhUFdM1yOGkshkjoicZ9JxoClYaaSx3GbqDWAt5cdbFBfjMsGshRLH2FV50id
xSr4Q5UE2oh2XEsnVILMV1P8TPUJHZ9bnD9RZbcwZ/HPQRxkyEtV2Tyy/wLikYf9
lqlrF7POqQsi9snYLiDYamEJ5S5RAgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAQEA
FqAHagDz0Rmcpsld4umBvwP4og2VBj6N0cO88nzC4qN+3nlLQlajHz2ILnkS/3Bd
eMFHh2ouny9L+Okkszd6/Yf7nTrEM4RFU/EsO733ksaAFFG9lIxHvRofdDpHyRlL
gPkMFVX87Ie7F5roHJV722P4KfEzErrrzebrSyWmqI915K2jwmg2MCrBearo45LX
CKj6nm7AVzANL9PfxFr4HG6UmBh026bSoUSua7SKMsivndlkOV0ZLYTw3iWplPm4
FW8SrD5qqFS4RoJiwImyenz5KpHdK3+/PRG9O5AamLr/jVzjbdzfKqON34ZAq5Xb
N2BKvltLpffw4caxDXyTzg==
-----END CERTIFICATE REQUEST-----
Loading

0 comments on commit 88b4985

Please sign in to comment.