Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add custom CA capability #215

Merged
merged 9 commits into from
May 30, 2024
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
14 changes: 14 additions & 0 deletions docs/service_spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,16 @@ If specified, the SDK will send a `tls` object containing a `skipVerifyPeer` pro
true to indicate that verification should be skipped. This should allow the SDK to connect to the test harness
over HTTPS even though the test harness's certificate is self-signed.

#### Capability `"tls:custom-ca"`

This means the SDK is capable of configuring its trust store to use a specific CA file. This can be useful for
customers who have their own, internal certificate authority, and want to establish authenticity of their servers when
using the SDK.

If specified, the SDK will send a `tls` object containing a `customCAFile` property. The property will be set to a
a file path containing one or more PEM-encoded x509 certificates. The SDK should configure its TLS stack to use
this file when verifying the peer's certificate chain.

### Stop test service: `DELETE /`

The test harness sends this request at the end of a test run if you have specified `--stop-service-at-end` on the [command line](./running.md). The test service should simply quit. This is a convenience so CI scripts can simply start the test service in the background and assume it will be stopped for them.
Expand Down Expand Up @@ -223,6 +233,10 @@ A `POST` request indicates that the test harness wants to start an instance of t
* `afterEvaluation` (string, optional): The error/exception message that should be generated in the `afterEvaluation` stage of the test hook.
* `tls` (object, optional): If specified, contains configuration for establishing TLS connections.
* `skipVerifyPeer` (bool, optional): If true, the SDK's TLS configuration should be set skip verification of the peer. If false or omitted, the SDK should perform peer verification.
* `customCAFile` (string, optional): If set, contains a file path pointing to a custom CA file that should be
used by the SDK to verify the peer's certificate chain. The file will contain one or more PEM-encoded x509
certificates. The root of the certificate chain presented to the SDK during the TLS handshake must be signed by
one of these CA certificates.

The response to a valid request is any HTTP `2xx` status, with a `Location` header whose value is the URL of the test service resource representing this SDK client instance (that is, the one that would be used for "Close client" or "Send command" as described below).

Expand Down
14 changes: 14 additions & 0 deletions framework/capabilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,20 @@ func (cs Capabilities) Has(name string) bool {
return slices.Contains(cs, name)
}

// HasAll returns true if the specifies strings all appear in the list.
func (cs Capabilities) HasAll(names ...string) bool {
caps := make(map[string]struct{})
for _, c := range cs {
caps[c] = struct{}{}
}
for _, name := range names {
if _, ok := caps[name]; !ok {
return false
}
}
return true
}

// HasAny returns true if any of the specified strings appear in the list.
func (cs Capabilities) HasAny(names ...string) bool {
caps := make(map[string]struct{})
Expand Down
9 changes: 9 additions & 0 deletions framework/harness/certificate/ca.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[req]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: validate that all of these are necessary

x509_extensions = v3_ca

[v3_ca]
keyUsage=critical,keyCertSign
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer:always
basicConstraints=critical,CA:true,pathlen:0
extendedKeyUsage=serverAuth
52 changes: 52 additions & 0 deletions framework/harness/certificate/ca_private.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
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
4fmao3ypheGgGVOiXRyo+xMmwpsgUA==
-----END PRIVATE KEY-----
36 changes: 36 additions & 0 deletions framework/harness/certificate/ca_public.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
-----BEGIN CERTIFICATE-----
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
Y7pIypZcXjOD91lklxuQHEpxPmtZ
-----END CERTIFICATE-----
35 changes: 0 additions & 35 deletions framework/harness/certificate/cert.crt

This file was deleted.

52 changes: 0 additions & 52 deletions framework/harness/certificate/cert.key

This file was deleted.

36 changes: 33 additions & 3 deletions framework/harness/certificate/gen.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,38 @@

set -e

# Generates a certificate for testing purposes only. Validity 10 years.
# Expires: May 7, 2034
# Generates a certificate chain for testing purposes only. Validity 10 years.
# If tests start failing because this expires, congratulations!
#
# The chain consists of a self-signed CA, followed by a CA-signed leaf certificate.
# The CA cert is generated with extra config found in ca.conf.
#
# The concatenation of the leaf + CA is loaded into the test harness's
# http server via http.ListenAndServeTLS, along with the leaf private key.
#
# At test time, the SDK-under-test will be explicitly configured to trust the CA.

openssl req -new -newkey rsa:4096 -x509 -sha256 -days 3650 -nodes -out cert.crt -keyout cert.key
leaf_cert=leaf_public.pem
leaf_private_key=leaf_private.pem

ca_cert=ca_public.pem
ca_private_key=ca_private.pem

host=localhost
certValidityDays=3650

# Create CA
openssl req -newkey rsa:4096 -keyout "${ca_private_key}" -x509 -new -nodes -out "${ca_cert}" \
-subj "/OU=SDKTeam/O=LaunchDarkly/L=Oakland/ST=California/C=US" -days "${certValidityDays}" \
-config ca.conf

# Create Cert Signing Request
openssl req -new -newkey rsa:4096 -nodes -keyout "${leaf_private_key}" -out csr.pem \
-subj "/CN=${host}/OU=SDKTeam/O=LaunchDarkly/L=Oakland/ST=California/C=US"

# Sign Cert
openssl x509 -req -in csr.pem -CA "${ca_cert}" -CAkey "${ca_private_key}" -CAcreateserial -out "${leaf_cert}" \
-days "${certValidityDays}"

rm ca_public.srl
rm csr.pem
52 changes: 52 additions & 0 deletions framework/harness/certificate/leaf_private.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
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
Lie6kBC6bw1pF0CK23M7P53F8Oe49Q==
-----END PRIVATE KEY-----
Loading
Loading