Skip to content

feat: Add Trustee quadlet and secret registration server#2

Open
litian1992 wants to merge 5 commits intolinux-system-roles:mainfrom
litian1992:main
Open

feat: Add Trustee quadlet and secret registration server#2
litian1992 wants to merge 5 commits intolinux-system-roles:mainfrom
litian1992:main

Conversation

@litian1992
Copy link
Copy Markdown
Collaborator

@litian1992 litian1992 commented Mar 11, 2026

Enhancement:
Screenshot From 2026-03-11 09-07-50

Trustee attestation server includes components AS, KBS and RVPS. Its deployment is via Podman Quadlet.
Secret registration server serves as a companion to Trustee server that pushes secrets and updates the policies.
This way Trustee server can serve each CVM as individuals.

Reason:

Result:

Issue Tracker Tickets (Jira or BZ if any):

Summary by Sourcery

Add a dedicated trustee_attestation_server Ansible role that deploys Trustee attestation components via Podman Quadlets and an optional secret registration server for disk encryption key management.

New Features:

  • Introduce configuration variables to control deployment of Trustee server Quadlet files and secret registration server settings, including repository location and listen port.
  • Provide a secret registration HTTP(S) service that validates attestation tokens, generates disk encryption keys, stores them in Trustee KBS, and updates KBS policies.
  • Add example playbooks demonstrating how to deploy Trustee server components from a GitHub Quadlet repository.

Enhancements:

  • Replace the generic template README and metadata with trustee_attestation_server-specific documentation, badges, and role description.
  • Define internal variables and platform-specific service lists tailored for the trustee_attestation_server role instead of the generic template variables.
  • Ensure firewall rules are configured automatically for Trustee KBS and the secret registration server when firewalld is running.
  • Add tasks to fetch Trustee Quadlet definitions from a Git repository, install required packages, generate certificates and keys, and manage the Trustee pod service lifecycle.

Documentation:

  • Update README and contributing documentation to describe the trustee_attestation_server role, its features, requirements, and usage, and to point CI links to the new repository.
  • Update CI plans documentation to reference the trustee_attestation_server repository in Testing Farm workflow examples.

Tests:

  • Expand default role tests to verify Trustee Quadlet deployment, certificate generation, and Trustee pod service availability.
  • Add tests to ensure the secret registration server is deployed, its dependencies are installed, and its systemd service is enabled and running when configured.

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai bot commented Mar 11, 2026

Reviewer's Guide

Implements the initial trustee_attestation_server Ansible role that deploys Trustee attestation components via Podman Quadlets and introduces a companion HTTPS secret registration service, wiring them into tests, metadata, and documentation while renaming template-era variables and references.

Sequence diagram for secret registration flow via secret registration server

sequenceDiagram
  actor ClientCVM
  participant SecretRegistrationServer
  participant TokenParser as get_evidence_from_attestation_token
  participant KBSClient as kbs_client_container
  participant TrusteeKBS as KBS
  participant PolicyFile as policy_rego

  ClientCVM->>SecretRegistrationServer: POST /register-encryption-key\n{ attestation_token, client_id }
  SecretRegistrationServer->>TokenParser: get_evidence_from_attestation_token(attestation_token)
  TokenParser-->>SecretRegistrationServer: evidence (includes pcr15)

  SecretRegistrationServer->>SecretRegistrationServer: Generate 32-byte disk key
  SecretRegistrationServer->>KBSClient: podman run kbs-client\nset-resource --path disk-encryption/client_id/luks-key-0
  KBSClient->>TrusteeKBS: HTTPS set-resource\n(auth.key, server.crt)
  TrusteeKBS-->>KBSClient: 200 OK
  KBSClient-->>SecretRegistrationServer: success

  SecretRegistrationServer->>PolicyFile: append_resource_policy(resource_path, evidence, client_id)
  PolicyFile-->>SecretRegistrationServer: policy updated

  SecretRegistrationServer-->>ClientCVM: 200 OK\n{ resource_path, message }
Loading

Class diagram for SecretRegistrationHandler and helper functions

classDiagram
  class SecretRegistrationHandler {
    +do_POST()
    -_send_json(status int, data dict)
  }

  class get_evidence_from_attestation_token {
    +get_evidence_from_attestation_token(token str) dict
  }

  class store_key_in_kbs {
    +store_key_in_kbs(resource_path str, key_data bytes) bool
  }

  class append_resource_policy {
    +append_resource_policy(resource_path str, evidence dict, client_id str) bool
  }

  class HTTPServer {
    +serve_forever()
    +server_close()
  }

  class main_entrypoint {
    +main()
  }

  SecretRegistrationHandler ..> get_evidence_from_attestation_token : uses
  SecretRegistrationHandler ..> store_key_in_kbs : uses
  SecretRegistrationHandler ..> append_resource_policy : uses
  main_entrypoint ..> HTTPServer : creates
  HTTPServer ..> SecretRegistrationHandler : handles_requests
Loading

File-Level Changes

Change Details Files
Introduce Trustee Quadlet deployment workflow for KBS/AS/RVPS using an external Git repository and systemd/podman integration.
  • Add trustee_quadlet.yml task file to install Podman/git, clone a configurable quadlet repo, copy .container/.volume/.network/.pod units to the target Quadlet directory, and optionally copy configs to /etc/trustee/
  • Generate TLS and auth materials for Trustee (KBS server cert/key, KBS auth keypair, AS token signing key/cert, and trusted_certs link) via an idempotent shell block
  • Open KBS port 8080 in firewalld when running, discover the installed *.pod unit name, and enable/restart the corresponding -pod.service, with cleanup of the temporary quadlet repo
tasks/trustee_quadlet.yml
defaults/main.yml
vars/main.yml
vars/Fedora.yml
vars/RedHat_7.yml
vars/RedHat_8.yml
vars/RedHat_9.yml
vars/RedHat_10.yml
tests/setup-snapshot.yml
tests/tests_default.yml
Add a Python-based HTTPS secret registration server that derives policy from attestation tokens and stores LUKS keys into Trustee KBS.
  • Create templated secret_registration_server.py implementing an HTTPS server that accepts POST /register-encryption-key, parses an attestation_token (Azure TPM-based), derives PCR15 evidence, generates a 32-byte disk key, and stores it in KBS via podman-run kbs-client
  • Append per-client resource policies to policy.rego keyed by machine-id and evidence, with guard to avoid duplicates
  • Provide a systemd unit template to run the registration server, plus an Ansible task file to install dependencies, deploy the script/unit, open the configured port in firewalld, and enable/start the service
templates/secret_registration_server.py.j2
templates/secret_registration_server.service.j2
tasks/secret_registration_server.yml
defaults/main.yml
vars/main.yml
tests/tests_secret_registration_server.yml
Wire the new role behaviour into the main task entrypoint and tests, and ensure required packages and firewall settings are validated.
  • Replace template role main tasks with includes for trustee_quadlet.yml and secret_registration_server.yml, gated by trustee_attestation_server_trustee and trustee_attestation_server_secret_registration_server_enabled
  • Expand tests_default.yml to assert packages are installed, Quadlet directory and files exist, Trustee pod service is started, and Trustee certs/keys are generated
  • Add a dedicated tests_secret_registration_server.yml playbook to validate that enabling the feature installs packages, deploys the script, and starts/enables the systemd unit
tasks/main.yml
tests/tests_default.yml
tests/tests_secret_registration_server.yml
Rename and rebrand the role from the generic template to trustee_attestation_server across variables, metadata, documentation, and tests.
  • Update README to describe the Trustee attestation server role, its features, example playbook, and secret registration workflow, replacing template scaffolding and CI badges/links
  • Rename internal variables and distro fact helpers from _template* to _trustee_attestation_server* in vars files, tests vars, set_vars tasks, handlers, and template files, and adjust the example foo.conf template content
  • Update meta/main.yml, contributing.md, plans README, example playbook, and test playbooks to reference linux-system-roles.trustee_attestation_server and trustee-specific GitHub URLs
README.md
meta/main.yml
contributing.md
plans/README-plans.md
examples/simple.yml
handlers/main.yml
tasks/set_vars.yml
templates/foo.conf.j2
tests/vars/rh_distros_vars.yml
tests/tests_include_vars_from_parent.yml

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 7 issues, and left some high level feedback:

  • In templates/secret_registration_server.py.j2 there are several unguarded assumptions about the token/evidence structure and temp file handling (e.g. direct indexing into payload['submods']['cpu0'] and using tmp_path in finally even if creation failed) which can raise unexpected exceptions; consider using dict.get/try/except around the structure parsing and initializing tmp_path to None before the try so cleanup is safe.
  • The example playbook in examples/simple.yml references the role as linux-system-roles.trustee-server whereas the rest of the repo uses linux-system-roles.trustee_attestation_server, which will confuse consumers; align the role name in the example with the actual published role name.
  • The secret_registration_server systemd unit is configured with ProtectHome=yes and ReadWritePaths=/tmp, but the service writes to /etc/trustee (e.g. POLICY_FILE) and shells out to podman; review the hardening options so the service retains the required write and execution access to /etc/trustee and the container runtime paths.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In templates/secret_registration_server.py.j2 there are several unguarded assumptions about the token/evidence structure and temp file handling (e.g. direct indexing into payload['submods']['cpu0'] and using tmp_path in finally even if creation failed) which can raise unexpected exceptions; consider using dict.get/try/except around the structure parsing and initializing tmp_path to None before the try so cleanup is safe.
- The example playbook in examples/simple.yml references the role as linux-system-roles.trustee-server whereas the rest of the repo uses linux-system-roles.trustee_attestation_server, which will confuse consumers; align the role name in the example with the actual published role name.
- The secret_registration_server systemd unit is configured with ProtectHome=yes and ReadWritePaths=/tmp, but the service writes to /etc/trustee (e.g. POLICY_FILE) and shells out to podman; review the hardening options so the service retains the required write and execution access to /etc/trustee and the container runtime paths.

## Individual Comments

### Comment 1
<location path="templates/secret_registration_server.py.j2" line_range="47-56" />
<code_context>
+    try:
</code_context>
<issue_to_address>
**issue:** Avoid referencing tmp_path in finally if an exception occurs before it is assigned.

If an exception occurs before `tmp_path` is assigned (e.g. `NamedTemporaryFile` fails), the `finally` block will raise `UnboundLocalError` when calling `os.path.exists(tmp_path)`. Initialize `tmp_path = None` before the `try` and wrap the cleanup with `if tmp_path and os.path.exists(tmp_path):` to avoid this.
</issue_to_address>

### Comment 2
<location path="templates/secret_registration_server.py.j2" line_range="36" />
<code_context>
+
+def get_evidence_from_attestation_token(token: str) -> dict:
+    """Get PCR15 from attestation token."""
+    payload = json.loads(base64.b64decode(token.split('.')[1] + "==").decode('utf-8'))
+    evidence = payload["submods"]["cpu0"]["ear.veraison.annotated-evidence"]
+    # Only support Azure TPM-based attestation for now
</code_context>
<issue_to_address>
**issue (bug_risk):** JWT/base64 parsing in get_evidence_from_attestation_token can raise unhandled exceptions for malformed tokens.

This assumes `token.split('.')[1]` exists and that padding can always be fixed by appending `"=="`, so malformed/non-JWT tokens can raise `IndexError` or `binascii.Error` and become 500s instead of a controlled 4xx. Please validate the token has the expected number of segments and catch base64/JSON decoding errors (e.g., return `None` so the caller can send a 400).
</issue_to_address>

### Comment 3
<location path="templates/secret_registration_server.py.j2" line_range="94" />
<code_context>
+            if f"# machine-id: {client_id}" in f.read():
+                LOG.exception("Resource policy already exists for machine-id: %s", client_id)
+                return False
+            tee_key = list(evidence.keys())[0]
+            pcr15_val = evidence[tee_key]["tpm"]["pcr15"]
+            {% raw %}policy = f'''# machine-id: {client_id}
</code_context>
<issue_to_address>
**🚨 issue (security):** Selecting the first evidence key is brittle and may not match the TPM-based attestation actually used.

`get_evidence_from_attestation_token` explicitly selects `azsnpvtpm` / `aztdxvtpm`, but `append_resource_policy` then uses `list(evidence.keys())[0]`. If other keys are present, the policy may be bound to the wrong TEE. Consider returning the chosen `tee_key` from `get_evidence_from_attestation_token`, or reapplying the same selection logic here instead of relying on dictionary key order.
</issue_to_address>

### Comment 4
<location path="tasks/trustee_quadlet.yml" line_range="70" />
<code_context>
+    force: true
+  when: __repo_configs_dir.stat.exists
+
+- name: Generate certificates for all components
+  ansible.builtin.shell: |
+    # Trustee Server SSL
</code_context>
<issue_to_address>
**issue (bug_risk):** Certificate generation assumes /etc/trustee/kbs exists, which may not be true.

The shell block writes to `/etc/trustee/kbs/server.key` and `/etc/trustee/kbs/auth.key` before ensuring `/etc/trustee/kbs` exists. If the configs copy step didn’t create that directory (e.g. no `configs/` in the repo), these `openssl` commands will fail. Please ensure the directories are created first, either with `mkdir -p /etc/trustee/kbs /etc/trustee/as` in the shell script or via a preceding `file` task.
</issue_to_address>

### Comment 5
<location path="README.md" line_range="49" />
<code_context>
-
-## Example Playbook
+1. Downloads the Podman Quadlets from designated repo
+2. Generates all required certficates of Trustee server components
+3. Add KBS port 8080 to firewalld
+3. Enables the services by default
</code_context>
<issue_to_address>
**issue (typo):** Fix the typo in "certficates" and consider adjusting the preposition.

`certficates``certificates`. Also consider `certificates for Trustee server components` for clearer wording.

```suggestion
2. Generates all required certificates for Trustee server components
```
</issue_to_address>

### Comment 6
<location path="contributing.md" line_range="14-20" />
<code_context>

 **Bugs and needed implementations** are listed on
-[Github Issues](https://github.com/linux-system-roles/template/issues).
+[Github Issues](https://github.com/linux-system-roles/trustee_attestation_server/issues).
 Issues labeled with
-[**help wanted**](https://github.com/linux-system-roles/template/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22)
</code_context>
<issue_to_address>
**issue (typo):** Use the correct capitalization "GitHub" instead of "Github".

Please update the link text to use the official `GitHub` capitalization, e.g. `[GitHub Issues](...)`.

```suggestion
**Bugs and needed implementations** are listed on
[GitHub Issues](https://github.com/linux-system-roles/trustee_attestation_server/issues).
Issues labeled with
[**help wanted**](https://github.com/linux-system-roles/trustee_attestation_server/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22)
are likely to be suitable for new contributors!

**Code** is managed on [GitHub](https://github.com/linux-system-roles/trustee_attestation_server), using
```
</issue_to_address>

### Comment 7
<location path="contributing.md" line_range="15" />
<code_context>
 are likely to be suitable for new contributors!

-**Code** is managed on [Github](https://github.com/linux-system-roles/template), using
+**Code** is managed on [Github](https://github.com/linux-system-roles/trustee_attestation_server), using
 [Pull Requests](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests).

</code_context>
<issue_to_address>
**issue (typo):** Correct "Github" to "GitHub" in the link text.

Use `GitHub` with a capital H here to match the official name.

```suggestion
[GitHub Issues](https://github.com/linux-system-roles/trustee_attestation_server/issues).
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.


def get_evidence_from_attestation_token(token: str) -> dict:
"""Get PCR15 from attestation token."""
payload = json.loads(base64.b64decode(token.split('.')[1] + "==").decode('utf-8'))
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): JWT/base64 parsing in get_evidence_from_attestation_token can raise unhandled exceptions for malformed tokens.

This assumes token.split('.')[1] exists and that padding can always be fixed by appending "==", so malformed/non-JWT tokens can raise IndexError or binascii.Error and become 500s instead of a controlled 4xx. Please validate the token has the expected number of segments and catch base64/JSON decoding errors (e.g., return None so the caller can send a 400).

if f"# machine-id: {client_id}" in f.read():
LOG.exception("Resource policy already exists for machine-id: %s", client_id)
return False
tee_key = list(evidence.keys())[0]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 issue (security): Selecting the first evidence key is brittle and may not match the TPM-based attestation actually used.

get_evidence_from_attestation_token explicitly selects azsnpvtpm / aztdxvtpm, but append_resource_policy then uses list(evidence.keys())[0]. If other keys are present, the policy may be bound to the wrong TEE. Consider returning the chosen tee_key from get_evidence_from_attestation_token, or reapplying the same selection logic here instead of relying on dictionary key order.

@litian1992 litian1992 force-pushed the main branch 2 times, most recently from a3c6ed0 to b3c8785 Compare March 16, 2026 06:47
The deployment of Trustee server includes quadlet and associated
configurations.

Signed-off-by: Li Tian <litian@redhat.com>
The secret registration server service is an HTTPS server. It receives only
request from the counterpart secret registration client service. It creates
a disk encryption key along with resource policy tied to PCR15 of the client.

Signed-off-by: Li Tian <litian@redhat.com>
@litian1992 litian1992 force-pushed the main branch 2 times, most recently from c8ae2c2 to bf6d2de Compare March 17, 2026 00:30
@richm
Copy link
Copy Markdown
Contributor

richm commented Mar 18, 2026

there are a couple of files that do not end in a newline

Integration tests for trustee_quadlet and secret_registration_server.

Signed-off-by: Li Tian <litian@redhat.com>
@richm
Copy link
Copy Markdown
Contributor

richm commented Mar 19, 2026

lgtm - @spetrosi ?

@richm
Copy link
Copy Markdown
Contributor

richm commented Mar 26, 2026

@litian1992 @spetrosi are we going to rename this to trustee_server as with client?

…s to private

We don't want users to easily move away from designated Trustee quadlets.
So move the repo and install path to private to reduce variants.

Signed-off-by: Li Tian <litian@redhat.com>
@litian1992
Copy link
Copy Markdown
Collaborator Author

@litian1992 @spetrosi are we going to rename this to trustee_server as with client?

Yes, intended. Should I just do it in this PR?

@richm
Copy link
Copy Markdown
Contributor

richm commented Mar 27, 2026

@litian1992 @spetrosi are we going to rename this to trustee_server as with client?

Yes, intended. Should I just do it in this PR?

Yes, since this PR is already doing the rename of template -> trustee_server

The present name 'trustee_attestation_server' is too long. The
keyword attestation is not obviously reflected in the role.

Signed-off-by: Li Tian <litian@redhat.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants