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

Improve PKI test coverage #13711

Closed
wants to merge 2 commits into from
Closed
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
265 changes: 227 additions & 38 deletions test/suites/pki.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# shellcheck disable=2031
test_pki() {
if [ ! -d "/usr/share/easy-rsa/" ]; then
echo "==> SKIP: The pki test requires easy-rsa to be installed"
Expand All @@ -24,13 +25,15 @@ test_pki() {
./easyrsa init-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 restricted nopass
./easyrsa build-client-full unrestricted 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 @@ -39,68 +42,254 @@ 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.
token="$(LXD_DIR=${LXD5_DIR} lxc config trust add --name foo --quiet --project default)"
lxc_remote remote add pki-lxd "${LXD5_ADDR}" --accept-certificate --token "${token}"

# 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
# shellcheck disable=2030
export LXD_CONF="${LXC5_DIR}"

# 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.
! lxc_remote remote add pki-lxd "${LXD5_ADDR}" --accept-certificate --password=bar || false
### Unrestricted CA signed client certificate with `core.trust_ca_certificates` disabled.

# Set up the client config
cp "${TEST_DIR}/pki/keys/unrestricted.crt" "${LXD_CONF}/client.crt"
cp "${TEST_DIR}/pki/keys/unrestricted.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 token. 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 --token=bar || false

# Add remote using the correct password.
# This should work because the client certificate is signed by the CA.
token="$(LXD_DIR=${LXD5_DIR} lxc config trust add --name foo -q)"
lxc_remote remote add pki-lxd "${LXD5_ADDR}" --accept-certificate --password "${token}"
lxc_remote config trust ls pki-lxd: | grep lxd-client
fingerprint="$(lxc_remote config trust ls pki-lxd: --format csv | cut -d, -f4)"
lxc_remote config trust remove pki-lxd:"${fingerprint}"
lxc_remote remote add pki-lxd "${LXD5_ADDR}" --accept-certificate --token "${token}"

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

# The certificate was not restricted, so 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 unrestricted 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 token would normally fail.
# The certificate is now revoked, we shouldn't be able to re-add it.
token="$(LXD_DIR=${LXD5_DIR} lxc config trust add --name foo -q)"
! lxc_remote remote add pki-lxd "${LXD5_ADDR}" --accept-certificate --token "${token}" || false
! lxc config trust ls | grep -wF unrestricted || false

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

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

# Try adding remote using an incorrect token. 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 --token=bar || false

# Add remote using the correct token (restricted).
# This should work because the client certificate is signed by the CA.
token="$(LXD_DIR=${LXD5_DIR} lxc config trust add --name foo --quiet --restricted)"
lxc_remote remote add pki-lxd "${LXD5_ADDR}" --accept-certificate --token "${token}"

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

# The certificate was restricted, so should not be able to view server config
! lxc_remote info pki-lxd: | grep -F 'core.https_address' || false
! curl -s --cert "${LXD_CONF}/client.pem" --cacert "${LXD5_DIR}/server.crt" "https://${LXD5_ADDR}/1.0" | jq -e '.metadata.config."core.https_address"' || false

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

# The certificate was restricted, so should not be able to view server config even though `core.trust_ca_certificates` is now enabled.
! lxc_remote info pki-lxd: | grep -F 'core.https_address' || false
! curl -s --cert "${LXD_CONF}/client.pem" --cacert "${LXD5_DIR}/server.crt" "https://${LXD5_ADDR}/1.0" | jq -e '.metadata.config."core.https_address"' || false

# Revoke the client certificate
cd "${TEST_DIR}/pki" && "${TEST_DIR}/pki/easyrsa" --batch revoke restricted 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

# Unset `core.trust_ca_certificates`.
LXD_DIR=${LXD5_DIR} lxc config unset core.trust_ca_certificates

# The certificate is now revoked, we shouldn't be able to re-add it.
token="$(LXD_DIR=${LXD5_DIR} lxc config trust add --name foo -q)"
! lxc_remote remote add pki-lxd "${LXD5_ADDR}" --accept-certificate --token "${token}" || false
! lxc config trust ls | grep -wF restricted || false

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

# NOTE: These certificates cannot be restricted/unrestricted because no trust store entries are created for them.

# 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 token.
# 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 || false

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

# Remove remote
lxc_remote remote remove pki-lxd

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

# Client cert should not be present in trust store.
! lxc_remote config trust ls pki-lxd: | grep -wF 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 token.
# This should fail, and the revoked certificate should not be added to the trust store.
token="$(LXD_DIR=${LXD5_DIR} lxc config trust add --name foo -q)"
! lxc_remote remote add pki-lxd "${LXD5_ADDR}" --accept-certificate --token "${token}" || false
! lxc config trust ls | grep -wF prior-revoked || false

# Try adding a remote using a revoked client certificate, and an incorrect token.
# This should fail, as if the certificate is revoked and token is wrong then no access should be allowed.
! lxc_remote remote add pki-lxd "${LXD5_ADDR}" --accept-certificate --token=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 token.
# This should fail, and the revoked certificate should not be added to the trust store.
token="$(LXD_DIR=${LXD5_DIR} lxc config trust add --name foo -q)"
! lxc_remote remote add pki-lxd "${LXD5_ADDR}" --accept-certificate --token "${token}" || false
! lxc config trust ls | grep -wF prior-revoked || false

# 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
# Try adding a remote using a revoked client certificate, and an incorrect token.
# This should fail, as if the certificate is revoked and token is wrong then no access should be allowed.
! lxc_remote remote add pki-lxd "${LXD5_ADDR}" --accept-certificate --token=incorrect || 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.
token="$(LXD_DIR=${LXD5_DIR} lxc config trust add --name foo -q)"
! lxc_remote remote add pki-lxd2 "${LXD5_ADDR}" --accept-certificate --token "${token}" || 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" ]


### Show that mTLS still works for server certificates:

# Trick LXD into thinking the client cert is a server certificate.
LXD_DIR="${LXD5_DIR}" lxd sql global "UPDATE identities SET type = 3 WHERE identifier = '$(cert_fingerprint "${LXD_CONF}/client.crt")'"
LXD_DIR="${LXD5_DIR}" lxc query -X POST "/internal/identity-cache-refresh"

# A server certificate should have root access, so we can see server configuration.
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"'

# Clean up.

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

kill_lxd "${LXD5_DIR}"
}
Loading