[go: up one dir, main page]

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

Support encrypted and signed user data #5599

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Prev Previous commit
Next Next commit
unwrap before verify
  • Loading branch information
TheRealFalcon committed Oct 21, 2024
commit 5c8743a319b4900470524956bec6c1f83767d62e
26 changes: 22 additions & 4 deletions cloudinit/gpg.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,30 @@ def decrypt(self, data: str, *, require_signature=False) -> str:
data=data,
update_env=self.env,
)
except subp.ProcessExecutionError as e:
if e.exit_code == 2:
except subp.ProcessExecutionError:
# If the message is signed then encrypted (the default),
# the message can't be verified until it's decrypted
try:
stdout, _ = subp.subp(
["gpg", "--unwrap"],
data=data,
update_env=self.env,
decode=False,
)
except subp.ProcessExecutionError as e:
raise GpgVerificationError(
"Signature verification failed"
"Signature verification failed. Could not unwrap."
) from e
try:
subp.subp(
["gpg", "--verify"],
data=stdout,
update_env=self.env,
)
except subp.ProcessExecutionError as e:
raise GpgVerificationError(
"Signature verification failed. Could not verify."
) from e
raise
result = subp.subp(
[
"gpg",
Expand Down
4 changes: 2 additions & 2 deletions cloudinit/subp.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import subprocess
from errno import ENOEXEC
from io import TextIOWrapper
from typing import List, Optional, Union
from typing import List, Literal, Optional, Union

from cloudinit import performance

Expand Down Expand Up @@ -170,7 +170,7 @@ def subp(
capture=True,
shell=False,
logstring=False,
decode="replace",
decode: Literal[False, "strict", "ignore", "replace"] = "replace",
update_env=None,
cwd=None,
timeout=None,
Expand Down
19 changes: 14 additions & 5 deletions tests/integration_tests/userdata/test_pgp.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Test PGP signed and encrypted userdata."""

from pathlib import Path

import pytest

from cloudinit import subp
Expand All @@ -16,20 +18,19 @@


@pytest.fixture(scope="module")
def gpg_dir(tmp_path_factory):
def gpg_dir(tmp_path_factory: pytest.TempPathFactory):
yield tmp_path_factory.mktemp("gpg_dir")


@pytest.fixture(scope="module")
def public_key(gpg_dir):
def public_key(gpg_dir: Path):
subp.subp(
[
"gpg",
"--homedir",
str(gpg_dir),
"--quick-generate-key",
"--batch",
# "loopback",
"--passphrase",
"",
"signing_user",
Expand Down Expand Up @@ -197,6 +198,14 @@ def _invalidate_key(client, key_path):
)
def test_signed_and_encrypted(pgp_client: IntegrationInstance):
client = pgp_client

client.write_to_file(
"/etc/cloud/cloud.cfg.d/99_pgp.cfg",
"user_data:\n require_signature: true",
)
client.execute("cloud-init clean --logs")
client.restart()

assert client.execute("test -f /var/tmp/signed_and_encrypted")
verify_clean_boot(client)

Expand All @@ -209,7 +218,7 @@ def test_signed_and_encrypted(pgp_client: IntegrationInstance):
assert not client.execute("test -f /var/tmp/signed_and_encrypted")
result = client.execute("cloud-init status --format=json")
assert result.failed
assert "Failed decrypting user data" in result.stdout
assert "Signature verification failed. Could not verify." in result.stdout

# Restore the public key, invalidate the private key, and ensure we fail
client.execute("cp /var/tmp/pub_key /etc/cloud/keys/pub_key")
Expand All @@ -221,7 +230,7 @@ def test_signed_and_encrypted(pgp_client: IntegrationInstance):
assert not client.execute("test -f /var/tmp/signed_and_encrypted")
result = client.execute("cloud-init status --format=json")
assert result.failed
assert "Failed decrypting user data" in result.stdout
assert "Signature verification failed. Could not unwrap." in result.stdout


@pytest.mark.parametrize(
Expand Down