Restricting use of signing keys

User story

A Debusine instance should be able to hold signing keys for use by a small number of trusted users. No other (non-sysadmin) user on the Debusine instance should be able to make Debusine take any action with these keys.

Requirements

  • Access to signing keys is controlled by debusine:signing-key assets. Assets are a new primitive similar to an artifact, but containing only JSON data.

  • Access can be granted to tasks in a set of Workspaces.

  • Access can be restricted to a Group of users.

Security Model

Key Creation:

  • Sysadmins can generate HSM-keys, outside Debusine.

  • Sysadmins can import HSM keys into Debusine.

  • Sysadmins can generate keys in Debusine.

  • In the future, we expect key generation (of some sort, but it may as well be HSM) to be a routine part of creating a workspace that holds a repository. Probably delegated by a workflow, once #634 has landed.

Assets:

  • While artifacts may be created by any user with contributor access to a workspace, assets can only be created by administrators or certain tasks (e.g. key generation).

  • Assets have a category (like artifacts).

  • Asset data is immutable (like artifacts).

  • Unlike artifacts, which have permissions applied by the containing workspace, Assets have permissions directly associated with them, that can be altered by their owner.

  • Asset’s permissions may be tied to workspaces. Roles are defined on an (asset, workspace) pair. There may be optional extra JSON restrictions attached to a permission for defence-in-depth.

Signing Key Assets:

  • Signing key assets are used to manage the security of signing keys. They contain the fingerprint, purpose, and public part of the key.

  • The GenerateKey task will create a signing key asset.

  • The GenerateKey task can be directly executed by scope owners.

  • The GenerateKey task may be incorporated in workflows.

  • In the future, the execution of the GenerateKey task may be unrestricted.

  • The extra restrictions field in a signing key asset can include restrictions for any/all of: debian:repository, debian:suite, and debian:source-package.

  • Assets may be visible in the UI. If they aren’t, then the public part of the key still needs to be exposed. We expect repositories to have UI with instructions for configuring sources.list including the public key. This would be sufficient, no dedicated asset view is required.

Signing:

  • Debusine’s signing service has no concept of permissions, itself. Currently, if a user can generate a Sign task, it will be acted on, with the specified private key (by public key fingerprint, as a string).

  • The Sign task may be incorporated into workflows. The workflow creator can then specify the signing key to be used.

  • Sign tasks should be permitted to execute if the user running the task has the SIGNER role on the asset (via a group membership) in the workspace that the task is executing in. If any other defence-in-depth restrictions are set, they must be met.

  • Sign tasks should not be dispatched unless the user is permitted to use the given signing key.

  • The signing service can make API requests back to the Debusine server to make permission determinations, as a final check.

  • In the future, once we have more comprehensive permissions for workflows, we may grant permission to execute a Sign task if the workflow was created and blessed by a user who has permission to use a given signing key. See #634.

Automated Signing:

  • When we have APT repositories implemented, it will be necessary for Sign tasks to be triggered periodically, by Debusine, without any user linked to the request.

  • These Signing Key Assets for these repositories will have a permission granting the SIGNER role to the workspace hosting the repository, without any associated group.

Work Requests:

  • It’s expected that core work request properties are immutable once the work request has been created. At least: workspace, created_by, created_at, task_type, task_name, and task_data.

Implementation

Stage 1: Implement Assets

  • Create the Asset DB model, with the following fields:

    • id int: unique ID (autogenerated)

    • category string: an ontology, e.g. debusine:signing-key.

    • workspace string: owning workspace.

    • created_at timestamp: creation date.

    • data: JSON data.

  • Create the debusine:signing-key Asset Pydantic model, modelling the following data:

    • purpose string ENUM containing: uefi, or openpgp.

    • fingerprint string containing the fingerprint for the key, calculated using the standard hash algorithm for the type of key.

    • public_key string containing the public part of the key.

    • description optional string description.

  • Put a constraint on assets that ensures all debusine:signing-key assets are unique by fingerprint.

  • Create the AssetRole DB model with the following fields:

    • resource Foreign Key to Asset.

    • role string ENUM containing: OWNER.

    • group Foreign Key to Group.

  • Create the AssetUsage DB model with the following fields:

    • asset Foreign Key to Asset.

    • workspace Foreign Key to Workspace.

    • restrictions JSON. Optional additional defense-in-depth restrictions.

  • Create the AssetUsageRole DB model with the following fields:

    • resource Foreign Key to AssetUsage.

    • role string ENUM containing: SIGNER.

    • group Foreign Key to Group.

  • Implement AssetQuerySet with the following permission filters:

    • can_manage_permissions, checks if the user has access to the OWNER role.

  • Implement AssetUsageQuerySet with the following permission filters:

    • can_sign_with, checks if the user has the ability to sign with this key in the given workspace.

Stage 2: Migrate to Signing Key Assets

  • Remove the debian:suite-signing-keys collection, it’s no longer needed.

  • Create debusine:signing-key assets in the GenerateKey task.

  • Update documentation for manual key creation.

  • Migrate debusine:signing-key artifacts to create debusine:signing-key assets. Create a group for each asset, granting the creator OWNER rights. Create an AssetUsage row for each asset, granting the OWNER group the SIGNER role in the workspace that used to own the artifact.

  • Remove debusine:signing-key artifacts.

Stage 3: Permissions for Signing Tasks

  • We implement an API for a worker to query whether it has the required permission: 1.0/asset/<str:asset_category>/<str:asset_slug>/<str:permission-name>/ ex: 1.0/asset/debusine:signing-key/uefi:FBE...64/can-sign/

    Input Data:

    • artifact_id: int (debusine:signing-input)

    • work_request_id: int

    • workspace_id: int

    Response Data:

    • has_permission: boolean

    • username: (optional) string (for the audit log)

    • resource: (optional) JSON description of the resource being signed (e.g. repository, suite, package) (for the audit log)

  • We modify the Sign task to call this API.

  • Store identifying data (username and resource) in data in the AuditLog model.

Future Utilization

  • We define data in the task-configuration collection to instruct workflows to use specific keys for each task.