feat: manage GPG credentials via Juju secrets#122
Open
jansdhillon wants to merge 10 commits intocanonical:mainfrom
Open
feat: manage GPG credentials via Juju secrets#122jansdhillon wants to merge 10 commits intocanonical:mainfrom
jansdhillon wants to merge 10 commits intocanonical:mainfrom
Conversation
Adds gpg_secret_id config option that accepts a Juju secret URI containing 'passphrase' and 'private-key' fields. When set, the charm writes /etc/landscape/gpg-passphrase and imports the private key into /etc/landscape/gpg on install and config-changed, and reacts to secret-changed to reconfigure and restart services when the secret is rotated. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds support for managing Landscape Server’s GPG signing credentials via Juju secrets, allowing credentials to be retrieved/rotated at runtime and applied automatically by the charm.
Changes:
- Introduces a new
gpg_secret_idcharm config option (Juju secret URI) and wires it into the charm config model. - Implements secret-backed GPG configuration in the charm (import private key into a dedicated homedir and write passphrase file), and reacts to
secret-changed. - Adds unit tests covering the main GPG secret scenarios (missing secret, missing fields, import failure, and successful import).
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
src/charm.py |
Adds GPG secret retrieval/import + secret-changed handling and hooks it into install/config-changed. |
src/config.py |
Extends the Pydantic config model with gpg_secret_id. |
config.yaml |
Documents and exposes the new gpg_secret_id config option. |
tests/unit/test_charm.py |
Adds unit tests for the new secret-driven GPG configuration flow. |
- Use landscape gid (not root) for GPG_HOME_DIR and passphrase file ownership - Rename secret fields to gpg-passphrase and gpg-private-key to match landscape-saas setup-gpg.sh convention - Enforce GPG_HOME_DIR permissions with explicit chmod after makedirs to guard against umask interference - Write passphrase file with restricted permissions from the start by setting umask to 0o177 before open(), eliminating the race window where the file could be world-readable - Pass --passphrase-file to gpg --import for encrypted private keys - Guard _on_install ActiveStatus against overriding a BlockedStatus set by _configure_gpg() - Update tests to assert passphrase file path, permissions, and ownership; fix secret field names throughout Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
jansdhillon
commented
Apr 22, 2026
jansdhillon
commented
Apr 22, 2026
- Downgrade secret-not-found and missing-fields log calls from error to warning since these are operator configuration issues, not program errors - Set MaintenanceStatus at the top of _configure_gpg() and WaitingStatus on success, following the same pattern as the other _configure_* methods (_configure_oidc, _configure_openid) - Move _configure_gpg() call in _on_install to after ActiveStatus is set, so it can naturally override with BlockedStatus on failure; _update_ready_status() then preserves that blocked state Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove explicit ActiveStatus before _configure_gpg(); instead let _update_ready_status() determine final status, matching the pattern used in _on_config_changed and other _configure_* functions. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
GPG home and passphrase paths should match the Landscape Server config: /etc/landscape-server/gpg and /etc/landscape-server/gpg-passphrase.txt Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ops raises ModelError (permission denied) when get_secret is called for a secret that exists but hasn't been granted to the app. Catch both SecretNotFoundError and ModelError for consistent BlockedStatus. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
Author
|
@copilot resolve the merge conflicts in this pull request |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description of changes
Adds a
gpg_secret_idconfig option that accepts a Juju secret URI (e.g.secret:xxxx). The secret must contain two fields:gpg-passphrase: the GPG key passphrasegpg-private-key: the ASCII-armored GPG private keyWhen configured, the charm:
/etc/landscape-server/gpg-passphrase.txt(mode0600, owned bylandscape:landscape)/etc/landscape-server/gpg(mode0700, owned bylandscape:landscape) viagpg --import --passphrase-filesecret-changedto reconfigure and restart services when the secret is rotatedThis is wired into both
installandconfig-changed, and setsBlockedStatuswith a descriptive message on any error (secret not found, missing fields, or GPG import failure).Manual testing instructions
Create the Juju secret. Fields must be named exactly
gpg-passphraseandgpg-private-key.juju add-secret gpg-creds \ gpg-passphrase=my-passphrase \ gpg-private-key="$(cat my-private-key.asc)"Grant the secret to the app and configure the charm with the URI returned above.
Verify the passphrase file was written and has correct ownership/permissions.
Expected:
my-passphraseandlandscape:landscape 600Verify the key was imported into the GPG keyring.
juju exec --unit landscape-server/0 -- gpg --homedir /etc/landscape-server/gpg --list-secret-keysTest secret rotation — update the secret and verify the charm reacts.
Expected:
new-passphraseTest misconfiguration: secret not granted. Unit should enter
BlockedStatus: "GPG secret not found or not accessible".Test missing fields. Unit should enter
BlockedStatus: "GPG secret missing required fields".Clear the config. Unit should return to
ActiveStatus/WaitingStatus.