Skip to content

Commit

Permalink
test/suites: Improve PKI test coverage.
Browse files Browse the repository at this point in the history
Signed-off-by: Mark Laing <mark.laing@canonical.com>
  • Loading branch information
markylaing committed Jul 5, 2024
1 parent 6f8cdf6 commit ee0c2d2
Showing 1 changed file with 155 additions and 29 deletions.
184 changes: 155 additions & 29 deletions test/suites/pki.sh
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
# shellcheck disable=2031
test_pki() {
if [ ! -d "/usr/share/easy-rsa/" ]; then
echo "==> SKIP: The pki test requires easy-rsa to be installed"
return
fi

cert_fingerprint() {
openssl x509 -noout -fingerprint -sha256 -in "${1}" | sed 's/.*=//; s/://g; s/\(.*\)/\L\1/'
}

# Setup the PKI.
cp -R /usr/share/easy-rsa "${TEST_DIR}/pki"
(
set -e
cd "${TEST_DIR}/pki"
export EASYRSA_KEY_SIZE=4096

# shellcheck disable=SC1091
if [ -e pkitool ]; then
. ./vars
Expand All @@ -23,12 +30,13 @@ test_pki() {
echo "lxd" | ./easyrsa build-ca nopass
./easyrsa gen-crl
./easyrsa build-client-full lxd-client nopass
./easyrsa build-client-full lxd-client-revoked nopass
./easyrsa build-client-full ca-trusted nopass
./easyrsa build-client-full prior-revoked nopass
mkdir keys
cp pki/private/* keys/
cp pki/issued/* keys/
cp pki/ca.crt keys/
echo "yes" | ./easyrsa revoke lxd-client-revoked
echo "yes" | ./easyrsa revoke prior-revoked
./easyrsa gen-crl
cp pki/crl.pem keys/
fi
Expand All @@ -37,65 +45,183 @@ test_pki() {
# Setup the daemon.
LXD5_DIR=$(mktemp -d -p "${TEST_DIR}" XXX)
chmod +x "${LXD5_DIR}"
cp "${TEST_DIR}/pki/keys/ca.crt" "${LXD5_DIR}/server.ca"
cp "${TEST_DIR}/pki/keys/crl.pem" "${LXD5_DIR}/ca.crl"
spawn_lxd "${LXD5_DIR}" true
LXD5_ADDR=$(cat "${LXD5_DIR}/lxd.addr")

# Setup the client.
# Add a certificate to the trust store that is not signed by the CA before enabling CA mode.
lxc_remote remote add pki-lxd "${LXD5_ADDR}" --accept-certificate --password foo

# Shutdown LXD. The CA certificate and revokation list must be present at start up to enable PKI.
shutdown_lxd "${LXD5_DIR}"
cp "${TEST_DIR}/pki/keys/ca.crt" "${LXD5_DIR}/server.ca"
cp "${TEST_DIR}/pki/keys/crl.pem" "${LXD5_DIR}/ca.crl"
respawn_lxd "${LXD5_DIR}" true

# New tmp directory for lxc client config.
LXC5_DIR=$(mktemp -d -p "${TEST_DIR}" XXX)
cp "${TEST_DIR}/pki/keys/lxd-client.crt" "${LXC5_DIR}/client.crt"
cp "${TEST_DIR}/pki/keys/lxd-client.key" "${LXC5_DIR}/client.key"
cp "${TEST_DIR}/pki/keys/ca.crt" "${LXC5_DIR}/client.ca"

# Confirm that a valid client certificate works.
(
set -e
export LXD_CONF=${LXC5_DIR}
# shellcheck disable=2030
export LXD_CONF="${LXC5_DIR}"

### CA signed certificate with `core.trust_ca_certificates` disabled.

# Set up the client config
cp "${TEST_DIR}/pki/keys/lxd-client.crt" "${LXD_CONF}/client.crt"
cp "${TEST_DIR}/pki/keys/lxd-client.key" "${LXD_CONF}/client.key"
cp "${TEST_DIR}/pki/keys/ca.crt" "${LXD_CONF}/client.ca"
cat "${LXD_CONF}/client.crt" "${LXD_CONF}/client.key" > "${LXD_CONF}/client.pem"

# Try adding remote using an incorrect password.
# This should fail, as if the certificate is unknown and password is wrong then no access should be allowed.
# Try adding remote using an incorrect password. This should fail even though the client certificate
# has been signed by the CA because `core.trust_ca_certificates` is not enabled.
! lxc_remote remote add pki-lxd "${LXD5_ADDR}" --accept-certificate --password=bar || false

# Add remote using the correct password.
# This should work because the client certificate is signed by the CA.
lxc_remote remote add pki-lxd "${LXD5_ADDR}" --accept-certificate --password=foo
lxc_remote remote add pki-lxd "${LXD5_ADDR}" --accept-certificate --password foo

# Should have trust store entry because `core.trust_ca_certificates` is disabled.
lxc_remote config trust ls pki-lxd: | grep lxd-client

# Should be able to view server config
lxc_remote info pki-lxd: | grep -F 'core.https_address'
curl -s --cert "${LXD_CONF}/client.pem" --cacert "${LXD5_DIR}/server.crt" "https://${LXD5_ADDR}/1.0" | jq -e '.metadata.config."core.https_address"'

# Revoke the client certificate
cd "${TEST_DIR}/pki" && "${TEST_DIR}/pki/easyrsa" --batch revoke lxd-client keyCompromise && "${TEST_DIR}/pki/easyrsa" gen-crl && cd -

# Restart LXD with the revoked certificate in the CRL.
shutdown_lxd "${LXD5_DIR}"
cp "${TEST_DIR}/pki/pki/crl.pem" "${LXD5_DIR}/ca.crl"
respawn_lxd "${LXD5_DIR}" true

# Revoked certificate no longer has access even though it is in the trust store.
lxc_remote info pki-lxd: | grep -F 'auth: untrusted'
! lxc_remote ls pki-lxd: || false
[ "$(curl -s --cert "${LXD_CONF}/client.pem" --cacert "${LXD5_DIR}/server.crt" "https://${LXD5_ADDR}/1.0/instances" | jq -e -r '.error')" = "not authorized" ]

# Remove cert from truststore.
fingerprint="$(cert_fingerprint "${LXD_CONF}/client.crt")"
LXD_DIR="${LXD5_DIR}" lxc config trust remove "${fingerprint}"
lxc_remote remote remove pki-lxd

# Add remote using a CA-signed client certificate, and not providing a password.
# This should succeed and tests that the CA trust is working, as adding the client certificate to the trust
# store without a trust password would normally fail.

### CA signed certificate with `core.trust_ca_certificates` enabled.

# Set up the client config
cp "${TEST_DIR}/pki/keys/ca-trusted.crt" "${LXD_CONF}/client.crt"
cp "${TEST_DIR}/pki/keys/ca-trusted.key" "${LXD_CONF}/client.key"
cat "${LXD_CONF}/client.crt" "${LXD_CONF}/client.key" > "${LXD_CONF}/client.pem"

# Enable `core.trust_ca_certificates`.
LXD_DIR=${LXD5_DIR} lxc config set core.trust_ca_certificates true

# Add remote using a CA-signed client certificate, and not providing a password.
# This should succeed because `core.trust_ca_certificates` is enabled.
lxc_remote remote add pki-lxd "${LXD5_ADDR}" --accept-certificate
lxc_remote config trust ls pki-lxd: | grep lxd-client

# Client cert should not be present in trust store.
! lxc_remote config trust ls pki-lxd: | grep ca-trusted || false

# Remove remote
lxc_remote remote remove pki-lxd

# Add remote using a CA-signed client certificate, and providing an incorrect password.
# Add the remote again using an incorrect password.
# This should succeed as is the same as the test above but with an incorrect password rather than no password.
lxc_remote remote add pki-lxd "${LXD5_ADDR}" --accept-certificate --password=bar
lxc_remote config trust ls pki-lxd: | grep lxd-client
lxc_remote remote remove pki-lxd

# Client cert should not be present in trust store.
! lxc_remote config trust ls pki-lxd: | grep ca-trusted || false

# The certificate is trusted as root because `core.trust_ca_certificates` is enabled.
lxc_remote info pki-lxd: | grep -F 'core.https_address'
curl -s --cert "${LXD_CONF}/client.pem" --cacert "${LXD5_DIR}/server.crt" "https://${LXD5_ADDR}/1.0" | jq -e '.metadata.config."core.https_address"'

# Unset `core.trust_ca_certificates` (this should work because the certificate is trusted as root as `core.trust_ca_certificates` is still enabled).
lxc_remote config unset pki-lxd: core.trust_ca_certificates

# Check that we no longer have access.
lxc_remote info pki-lxd: | grep -F 'auth: untrusted'
! lxc_remote ls pki-lxd: || false
[ "$(curl -s --cert "${LXD_CONF}/client.pem" --cacert "${LXD5_DIR}/server.crt" "https://${LXD5_ADDR}/1.0/instances" | jq -e -r '.error')" = "not authorized" ]

# Re-enable `core.trust_ca_certificates`.
LXD_DIR=${LXD5_DIR} lxc config set core.trust_ca_certificates true

# Revoke the client certificate
cd "${TEST_DIR}/pki" && "${TEST_DIR}/pki/easyrsa" --batch revoke ca-trusted keyCompromise && "${TEST_DIR}/pki/easyrsa" gen-crl && cd -

# Restart LXD with the revoked certificate.
shutdown_lxd "${LXD5_DIR}"
cp "${TEST_DIR}/pki/pki/crl.pem" "${LXD5_DIR}/ca.crl"
respawn_lxd "${LXD5_DIR}" true

# Check that we no longer have access (certificate was previously trusted, but is now revoked).
lxc_remote info pki-lxd: | grep -F 'auth: untrusted'
! lxc_remote ls pki-lxd: || false
[ "$(curl -s --cert "${LXD_CONF}/client.pem" --cacert "${LXD5_DIR}/server.crt" "https://${LXD5_ADDR}/1.0/instances" | jq -e -r '.error')" = "not authorized" ]

# Remove remote.
lxc remote remove pki-lxd

### CA signed certificate that has been revoked prior to connecting to LXD.
# `core.trust_ca_certificates` is currently enabled.

# Replace the client certificate with a revoked certificate in the CRL.
cp "${TEST_DIR}/pki/keys/lxd-client-revoked.crt" "${LXC5_DIR}/client.crt"
cp "${TEST_DIR}/pki/keys/lxd-client-revoked.key" "${LXC5_DIR}/client.key"
cp "${TEST_DIR}/pki/keys/prior-revoked.crt" "${LXC5_DIR}/client.crt"
cp "${TEST_DIR}/pki/keys/prior-revoked.key" "${LXC5_DIR}/client.key"

# Try adding a remote using a revoked client certificate, and the correct password.
# This should fail, as although revoked certificates can be added to the trust store, they will not be usable.
! lxc_remote remote add pki-lxd "${LXD5_ADDR}" --accept-certificate --password=foo || false
# This should fail, and the revoked certificate should not be added to the trust store.
! lxc_remote remote add pki-lxd "${LXD5_ADDR}" --accept-certificate --password foo || false
! lxc config trust ls | grep prior-revoked || false

# Try adding a remote using a revoked client certificate, and an incorrect password.
# This should fail, as if the certificate is revoked and password is wrong then no access should be allowed.
! lxc_remote remote add pki-lxd "${LXD5_ADDR}" --accept-certificate --password=incorrect || false

# Unset `core.trust_ca_certificates` and re-test, there should be no change in behaviour as the certificate is revoked.
LXD_DIR=${LXD5_DIR} lxc config unset core.trust_ca_certificates

# Try adding a remote using a revoked client certificate, and the correct password.
# This should fail, and the revoked certificate should not be added to the trust store.
! lxc_remote remote add pki-lxd "${LXD5_ADDR}" --accept-certificate --password foo || false
! lxc config trust ls | grep prior-revoked || false

# Try adding a remote using a revoked client certificate, and an incorrect password.
# This should fail, as if the certificate is revoked and password is wrong then no access should be allowed.
! lxc_remote remote add pki-lxd "${LXD5_ADDR}" --accept-certificate --password=incorrect || false

# Check we can't access anything with the revoked certificate.
[ "$(curl -s --cert "${LXD_CONF}/client.pem" --cacert "${LXD5_DIR}/server.crt" "https://${LXD5_ADDR}/1.0/instances" | jq -e -r '.error')" = "not authorized" ]
)

# Confirm that a normal, non-PKI certificate doesn't.
# As LXD_CONF is not set to LXC5_DIR where the CA signed client certs are, this will cause the lxc command to
# generate a new certificate that isn't trusted by the CA certificate and thus will not be allowed, even with a
# correct trust password. This is because the LXD TLS listener in CA mode will not consider a client cert that
# is not signed by the CA as valid.
! lxc_remote remote add pki-lxd "${LXD5_ADDR}" --accept-certificate --password=foo || false
# Confirm that we cannot add a remote using a certificate that is not signed by the CA.
# Outside of the subshell above, `LXD_CONF` is not set to `LXD5_DIR` where the CA trusted certs are.
# Since we added a certificate to the trust store prior to enabling PKI, the certificates in current `LXD_CONF` are
# in the trust store, but not signed by the CA. So here we are checking that mTLS for a client does not work when CA
# mode is enabled.
! lxc_remote remote add pki-lxd2 "${LXD5_ADDR}" --accept-certificate --password foo || false

# Confirm that the certificate we added earlier cannot authenticate with LXD.
lxc_remote info pki-lxd: | grep -F 'auth: untrusted'
! lxc_remote ls pki-lxd: || false
cat "${LXD_CONF}/client.crt" "${LXD_CONF}/client.key" > "${LXD_CONF}/client.pem"
[ "$(curl -s --cert "${LXD_CONF}/client.pem" --cacert "${LXD5_DIR}/server.crt" "https://${LXD5_ADDR}/1.0/instances" | jq -e -r '.error')" = "not authorized" ]

# Trick LXD into thinking the client cert is a server certificate.
LXD_DIR="${LXD5_DIR}" lxd sql global "UPDATE certificates SET type = 2 WHERE fingerprint = '$(cert_fingerprint "${LXD_CONF}/client.crt")'"
shutdown_lxd "${LXD5_DIR}"
respawn_lxd "${LXD5_DIR}" true

# Show that mTLS still works for server certificates. A server certificate has root access, so it can see server config.
lxc_remote info pki-lxd: | grep -F 'auth: trusted'
curl -s --cert "${LXD_CONF}/client.pem" --cacert "${LXD5_DIR}/server.crt" "https://${LXD5_ADDR}/1.0" | jq -e '.metadata.config."core.https_address"'

rm "${LXD_CONF}/client.pem"
lxc remote rm pki-lxd

kill_lxd "${LXD5_DIR}"
}

0 comments on commit ee0c2d2

Please sign in to comment.