diff --git a/contrib_testing/local-http-test.py b/contrib_testing/local-http-test.py index d3c0bba0..e53db56a 100755 --- a/contrib_testing/local-http-test.py +++ b/contrib_testing/local-http-test.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import datetime +import os import time import requests @@ -12,7 +13,7 @@ def log(s): - now = datetime.datetime.utcnow() + now = datetime.datetime.now(datetime.UTC) print(f"{now} - {s}") @@ -49,6 +50,11 @@ def create_and_update(client): if __name__ == "__main__": client = pylxd.Client("https://127.0.0.1:8443/", verify=False) log("Authenticating...") - client.authenticate("password") + if client.has_api_extension("explicit_trust_token"): + secret = os.getenv("LXD_TOKEN") + else: + secret = "password" + + client.authenticate(secret) create_and_update(client) diff --git a/contrib_testing/local-unix-test.py b/contrib_testing/local-unix-test.py index 25608ccd..1a3f956f 100755 --- a/contrib_testing/local-unix-test.py +++ b/contrib_testing/local-unix-test.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import datetime +import os import time import requests @@ -12,7 +13,7 @@ def log(s): - now = datetime.datetime.utcnow() + now = datetime.datetime.now(datetime.UTC) print(f"{now} - {s}") @@ -49,6 +50,11 @@ def create_and_update(client): if __name__ == "__main__": client = pylxd.Client() log("Authenticating...") - client.authenticate("password") + if client.has_api_extension("explicit_trust_token"): + secret = os.getenv("LXD_TOKEN") + else: + secret = "password" + + client.authenticate(secret) create_and_update(client) diff --git a/contrib_testing/remote-test.py b/contrib_testing/remote-test.py index a000889e..8e6c4887 100755 --- a/contrib_testing/remote-test.py +++ b/contrib_testing/remote-test.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import datetime +import os import time import requests @@ -12,7 +13,7 @@ def log(s): - now = datetime.datetime.utcnow() + now = datetime.datetime.now(datetime.UTC) print(f"{now} - {s}") @@ -50,6 +51,11 @@ def create_and_update(client): if __name__ == "__main__": client = pylxd.Client("https://10.245.162.33:8443/", verify=False) log("Authenticating...") - client.authenticate("password") + if client.has_api_extension("explicit_trust_token"): + secret = os.getenv("LXD_TOKEN") + else: + secret = "password" + + client.authenticate(secret) create_and_update(client) diff --git a/doc/source/authentication.rst b/doc/source/authentication.rst index 3d4a423a..f085a844 100644 --- a/doc/source/authentication.rst +++ b/doc/source/authentication.rst @@ -3,8 +3,9 @@ Client Authentication ===================== When using LXD over https, LXD uses an asymmetric keypair for authentication. -The keypairs are added to the authentication database after entering the LXD -instance's "trust password". +The keypairs are added to the authentication database after entering a secret. +The secret can be the LXD trust password, when using LXD 5.0 or older, or a +trust token otherwise. Generate a certificate @@ -35,11 +36,11 @@ essentially meaning that the authentication has not yet occurred. >>> client.trusted False -In order to authenticate the client, pass the lxd instance's trust -password to `Client.authenticate` +In order to authenticate the client, pass the LXD instance's trust +password or token to `Client.authenticate` .. code-block:: python - >>> client.authenticate('a-secret-trust-password') + >>> client.authenticate('a-secret') >>> client.trusted >>> True diff --git a/doc/source/certificates.rst b/doc/source/certificates.rst index 0927617c..34166986 100644 --- a/doc/source/certificates.rst +++ b/doc/source/certificates.rst @@ -13,9 +13,10 @@ methods: - `all()` - Retrieve all certificates. - `get()` - Get a specifit certificate, by its fingerprint. - - `create()` - Create a new certificate. This method requires - a first argument that is the LXD trust password, and the cert - data, in binary format. + - `create()` - Create a new certificate. This method requires a first argument + that is a secret and a second containing the cert data, in binary format. + The secret can be the LXD trust password, when using LXD 5.0 or older, + or a trust token otherwise. Certificate attributes diff --git a/integration/run-integration-tests b/integration/run-integration-tests index 3f18af38..f096f531 100755 --- a/integration/run-integration-tests +++ b/integration/run-integration-tests @@ -18,10 +18,13 @@ fi # Make sure a client.{crt,key} exist by trying to add a bogus remote lxc remote add foo 127.0.0.1:1234 2>/dev/null || true -if ! lxc info | grep -qwF explicit_trust_token; then +lxc config set core.https_address 127.0.0.1 +if lxc info | grep -qwF explicit_trust_token; then + LXD_TOKEN="$(lxc config trust add --name pylxd --quiet)" + export LXD_TOKEN +else lxc config set core.trust_password password fi -lxc config set core.https_address 127.0.0.1 if ! lxc storage show default >/dev/null 2>&1; then lxc storage create default dir diff --git a/integration/test_client.py b/integration/test_client.py index 01dc9c65..e0de35cf 100644 --- a/integration/test_client.py +++ b/integration/test_client.py @@ -12,6 +12,8 @@ # License for the specific language governing permissions and limitations # under the License. +import os + import pylxd from integration.testing import IntegrationTestCase from pylxd import exceptions @@ -21,16 +23,16 @@ class TestClient(IntegrationTestCase): """Tests for `Client`.""" def test_authenticate(self): - if self.client.has_api_extension("explicit_trust_token"): - self.skipTest( - "Required LXD support for password authentication not available!" - ) - client = pylxd.Client("https://127.0.0.1:8443/") + if client.has_api_extension("explicit_trust_token"): + secret = os.getenv("LXD_TOKEN") + else: + secret = "password" + self.assertFalse(client.trusted) - client.authenticate("password") + client.authenticate(secret) self.assertTrue(client.trusted) @@ -48,5 +50,10 @@ def test_authenticate_with_project(self): self.skipTest(message) raise - client.authenticate("password") + if client.has_api_extension("explicit_trust_token"): + secret = os.getenv("LXD_TOKEN") + else: + secret = "password" + + client.authenticate(secret) self.assertEqual(client.host_info["environment"]["project"], "test-project") diff --git a/migration/run_migration_integration_tests-18-04 b/migration/run_migration_integration_tests-18-04 index 68a7f9ce..c1bd636b 100755 --- a/migration/run_migration_integration_tests-18-04 +++ b/migration/run_migration_integration_tests-18-04 @@ -28,8 +28,13 @@ lxc start "$CONTAINER_ONE_NAME" sleep 5 # Wait for the network to come up lxc exec "$CONTAINER_ONE_NAME" -- apt update lxc exec "$CONTAINER_ONE_NAME" -- apt install -y tox python3-dev libssl-dev libffi-dev build-essential criu -lxc exec "$CONTAINER_ONE_NAME" -- lxc config set core.trust_password password lxc exec "$CONTAINER_ONE_NAME" -- lxc config set core.https_address "[::]" +if lxc exec "$CONTAINER_ONE_NAME" -- lxc info | grep -qwF explicit_trust_token; then + LXD_TOKEN_ONE="$(lxc exec "$CONTAINER_ONE_NAME" -- lxc config trust add --name pylxd --quiet)" + export LXD_TOKEN_ONE +else + lxc exec "$CONTAINER_ONE_NAME" -- lxc config set core.trust_password password +fi lxc exec "$CONTAINER_ONE_NAME" -- mkdir -p /root/.config/lxc lxc exec "$CONTAINER_ONE_NAME" -- openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:secp384r1 \ -sha384 -keyout /root/.config/lxc/client.key -out /root/.config/lxc/client.crt -nodes \ @@ -51,8 +56,13 @@ lxc start "$CONTAINER_TWO_NAME" sleep 5 # Wait for the network to come up lxc exec "$CONTAINER_TWO_NAME" -- apt update lxc exec "$CONTAINER_TWO_NAME" -- apt install -y tox python3-dev libssl-dev libffi-dev build-essential criu -lxc exec "$CONTAINER_TWO_NAME" -- lxc config set core.trust_password password lxc exec "$CONTAINER_TWO_NAME" -- lxc config set core.https_address "[::]:8443" +if lxc exec "$CONTAINER_TWO_NAME" -- lxc info | grep -qwF explicit_trust_token; then + LXD_TOKEN_TWO="$(lxc exec "$CONTAINER_TWO_NAME" -- lxc config trust add --name pylxd --quiet)" + export LXD_TOKEN_TWO +else + lxc exec "$CONTAINER_TWO_NAME" -- lxc config set core.trust_password password +fi lxc exec "$CONTAINER_ONE_NAME" -- mkdir -p /root/.config/lxc lxc exec "$CONTAINER_TWO_NAME" -- openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:secp384r1 \ -sha384 -keyout /root/.config/lxc/client.key -out /root/.config/lxc/client.crt -nodes \ diff --git a/migration/test_containers.py b/migration/test_containers.py index c2dc9cbc..2c1a2124 100644 --- a/migration/test_containers.py +++ b/migration/test_containers.py @@ -12,6 +12,8 @@ # License for the specific language governing permissions and limitations # under the License. +import os + from integration.testing import IntegrationTestCase @@ -35,10 +37,20 @@ def test_migrate_running(self): second_host = "https://10.0.3.222:8443/" client1 = Client(endpoint=first_host, verify=False) - client1.authenticate("password") + if client1.has_api_extension("explicit_trust_token"): + secret = os.getenv("LXD_TOKEN_ONE") + else: + secret = "password" + + client1.authenticate(secret) client2 = Client(endpoint=second_host, verify=False) - client2.authenticate("password") + if client2.has_api_extension("explicit_trust_token"): + secret = os.getenv("LXD_TOKEN_TWO") + else: + secret = "password" + + client2.authenticate(secret) an_container = client1.containers.get(self.container.name) an_container.start(wait=True) an_container.sync() @@ -53,7 +65,12 @@ def test_migrate_local_client(self): second_host = "https://10.0.3.222:8443/" client2 = Client(endpoint=second_host, verify=False) - client2.authenticate("password") + if client2.has_api_extension("explicit_trust_token"): + secret = os.getenv("LXD_TOKEN_TWO") + else: + secret = "password" + + client2.authenticate(secret) self.assertRaises(ValueError, self.container.migrate, client2) @@ -65,10 +82,20 @@ def test_migrate_stopped(self): second_host = "https://10.0.3.222:8443/" client1 = Client(endpoint=first_host, verify=False) - client1.authenticate("password") + if client1.has_api_extension("explicit_trust_token"): + secret = os.getenv("LXD_TOKEN_ONE") + else: + secret = "password" + + client1.authenticate(secret) client2 = Client(endpoint=second_host, verify=False) - client2.authenticate("password") + if client2.has_api_extension("explicit_trust_token"): + secret = os.getenv("LXD_TOKEN_TWO") + else: + secret = "password" + + client2.authenticate(secret) an_container = client1.containers.get(self.container.name) an_migrated_container = an_container.migrate(client2, wait=True) diff --git a/pylxd/client.py b/pylxd/client.py index cb2a5932..c5c5c1eb 100644 --- a/pylxd/client.py +++ b/pylxd/client.py @@ -478,11 +478,11 @@ def assert_has_api_extension(self, name): if not self.has_api_extension(name): raise exceptions.LXDAPIExtensionNotAvailable(name) - def authenticate(self, password): + def authenticate(self, secret): if self.trusted: return cert = open(self.api.session.cert[0]).read().encode("utf-8") - self.certificates.create(password, cert) + self.certificates.create(secret, cert) # Refresh the host info response = self.api.get() diff --git a/pylxd/models/certificate.py b/pylxd/models/certificate.py index 87dbafc4..eb616cf3 100644 --- a/pylxd/models/certificate.py +++ b/pylxd/models/certificate.py @@ -53,7 +53,7 @@ def all(cls, client): def create( cls, client, - password, + secret, cert_data, cert_type="client", name="", @@ -68,11 +68,14 @@ def create( data = { "type": cert_type, "certificate": base64_cert, - "password": password, "name": name, "restricted": restricted, "projects": projects, } + if client.has_api_extension("explicit_trust_token"): + data["trust_token"] = secret + else: + data["password"] = secret response = client.api.certificates.post(json=data) location = response.headers["Location"] fingerprint = location.split("/")[-1] diff --git a/pylxd/tests/test_client.py b/pylxd/tests/test_client.py index e11cd0b3..9d572cca 100644 --- a/pylxd/tests/test_client.py +++ b/pylxd/tests/test_client.py @@ -191,7 +191,12 @@ def test_authenticate(self): """A client is authenticated.""" response = mock.MagicMock(status_code=200) response.json.side_effect = [ - {"metadata": {"auth": "untrusted"}}, + { + "metadata": { + "auth": "untrusted", + "api_extensions": ["explicit_trust_token"], + } + }, { "metadata": { "type": "client", diff --git a/setup.cfg b/setup.cfg index 7c8361e6..1b0aa295 100644 --- a/setup.cfg +++ b/setup.cfg @@ -82,6 +82,7 @@ commands = pytest {posargs:pylxd} [testenv:integration] +passenv = LXD_* commands = pytest integration {posargs}