Skip to main content
Back to qURL Connector install guide

Advanced customer enablement

Advanced: attested key custody for qURL Connectors.

This optional upper-tier path is only for customers who require AWS Nitro or GCP Confidential Space to gate release of the qURL sidecar private key. The standard qURL Connector install remains the simple default; use this guide after that requirement is confirmed.

1Persistent state

Stores private_key.sealed.json instead of durable plaintext key material.

2Cloud attestation

KMS releases only to the approved Nitro or Confidential Space workload.

3Runtime config

OpenNHP plaintext config exists only in the process-lifetime runtime dir.

Outcomes

The advanced tier seals the key and releases it only to the approved workload.

Standard qURL Connector onboarding does not require this tier. When a customer opts into attested custody, the sidecar still uses the normal first bootstrap or recovery key, but steady state should run from persisted sealed state without keeping QURL_API_KEY or QURL_API_KEY_FILE mounted.

Success criteria

  • State contains private_key.sealed.json, not durable plaintext private_key.
  • Replacement instances unseal only when cloud attestation policy matches.
  • Cloud audit logs show expected KMS operations and no unexpected decrypts.
  • The qURL Connector reconnects after removing the bootstrap API key.

Non-goals

  • It does not replace IAM or KMS policy review.
  • It does not treat the NHP public key as secret.
  • It does not make the GCP local token parse a JWT verifier.
  • It does not make AWS first seal host-compromise-resistant on the parent host.

Architecture model

The optional upper tier makes cloud policy the release boundary.

For this advanced mode, the qURL client fails closed and checks local config, but confidentiality comes from the AWS KMS key policy for Nitro or the GCP Workload Identity Federation and Cloud KMS IAM binding for Confidential Space.

Baseline

file stores durable plaintext key material in the sidecar state directory. Use for development or low-sensitivity local deployments.

Tier 1

aws-kms and gcp-kms store sealed state, but do not add an attested release condition.

Tier 2 (optional advanced)

aws-nitro and gcp-confidential-space tie unseal to cloud attestation and approved workload identity. Use this only when a customer requires attested key release.

Inputs

Collect these before the advanced attestation working session.

Do not start this optional migration until the platform owner and security owner agree on provider, rollout gates, and rollback ownership.

qURL bootstrap

Collect the one-time qURL API key, a stable LAYERV_AGENT_ID, and the persistent state path before first start.

Runtime directory

Use a tmpfs-backed path such as /dev/shm, or set LAYERV_NHP_RUNTIME_DIR. Disk fallback requires explicit LAYERV_ALLOW_DISK_NHP_RUNTIME_DIR opt-in.

Cloud policy

Name the KMS key, steady-state principal, attestation claims, negative-test owner, and audit-log evidence location.

Rollback

For migration, preserve a pre-migration state backup if the customer policy requires rollback to the file provider.

Provider choice

Pick one provider per state volume.

Switching an existing file provider volume to a sealed provider is a one-way migration unless the customer restores a pre-migration backup.

Choose AWS Nitro when

  • The workload runs in AWS Nitro Enclaves or a NitroTPM-backed attested runtime.
  • The customer can enforce kms:RecipientAttestation conditions on the KMS key.
  • An unwrap helper can run inside the attested runtime.
  • First seal or migration can happen inside the attested runtime or a controlled provisioning window.

Choose GCP Confidential Space when

  • The workload runs in Google Cloud Confidential Space.
  • The customer can bind Workload Identity Federation and Cloud KMS IAM to image and service-account claims.
  • The container can read the Confidential Space attestation token file.
  • The security owner will validate negative IAM tests before production enablement.

AWS Nitro onboarding

Require KMS Recipient decrypt and reject host plaintext.

aws-nitro uses normal KMS encrypt for first seal, then requires AWS KMS Recipient decrypt on unseal. The client expects CiphertextForRecipient and fails closed if AWS returns host plaintext.

Prerequisites

  • A customer-managed AWS KMS key in the target region.
  • A first-seal or migration principal with kms:Encrypt.
  • A steady-state decrypt principal constrained by Recipient Attestation.
  • An unwrap helper inside the attested runtime.
AWS environment contract
LAYERV_KEY_PROVIDER=aws-nitro
LAYERV_AWS_KMS_KEY_ID=arn:aws:kms:us-east-1:111122223333:key/...
LAYERV_AWS_KMS_REGION=us-east-1
LAYERV_AWS_NITRO_ATTESTATION_DOCUMENT_FILE=/run/qurl/attestation.cose
LAYERV_AWS_NITRO_ATTESTATION_DOCUMENT_ENCODING=raw
LAYERV_AWS_NITRO_RECIPIENT_UNWRAP_COMMAND=/opt/qurl/bin/unwrap-recipient
QURL_API_KEY_FILE=/run/secrets/qurl-api-key
LAYERV_AGENT_ID=customer-prod-agent-01
Recommended AWS KMS key policy shape
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowQurlAgentFirstSeal",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::111122223333:role/qurl-agent-provisioner"
      },
      "Action": "kms:Encrypt",
      "Resource": "*"
    },
    {
      "Sid": "AllowQurlAgentNitroDecrypt",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::111122223333:role/qurl-agent-parent"
      },
      "Action": "kms:Decrypt",
      "Resource": "*",
      "Condition": {
        "StringEqualsIgnoreCase": {
          "kms:RecipientAttestation:ImageSha384": "PCR0_OR_IMAGE_SHA384_HEX"
        }
      }
    }
  ]
}

AWS implementation rules

  • Treat the sample as a KMS key policy, not an IAM identity policy. In a key policy, "Resource": "*" refers to the current key.
  • IAM principal ARNs intentionally use an empty region segment, for example arn:aws:iam::111122223333:role/qurl-agent-parent, because IAM ARNs are regionless.
  • Use kms:RecipientAttestation:PCR<PCR_ID> conditions when the customer wants PCR1, PCR2, or custom PCR binding.
  • The unwrap command is split on whitespace with no shell or quote parsing; stdout must be exactly 32 raw private-key bytes with no trailing newline.
  • AWS KMS Encrypt has no Recipient mode. Run first seal in the attested runtime whenever possible, or use a controlled provisioning window.

GCP Confidential Space onboarding

Make WIF and Cloud KMS enforce the workload claims.

gcp-confidential-space checks local Confidential Space claims before KMS calls, but the local parse is not a JWT verifier. WIF/IAM plus Cloud KMS are the real cryptographic release boundary.

Mandatory warning

If the customer does not enforce the same image digest and service account in WIF/IAM, local token claims can pass startup while Cloud KMS still releases the key. Treat the negative IAM test as a production gate.

GCP environment contract
LAYERV_KEY_PROVIDER=gcp-confidential-space
LAYERV_GCP_KMS_KEY_NAME=projects/acme-prod/locations/us/keyRings/qurl/cryptoKeys/agent-identity
LAYERV_GCP_CONFIDENTIAL_SPACE_TOKEN_FILE=/run/container_launcher/attestation_verifier_claims_token
LAYERV_GCP_CONFIDENTIAL_SPACE_IMAGE_DIGEST=sha256:...
LAYERV_GCP_CONFIDENTIAL_SPACE_SERVICE_ACCOUNT=qurl-agent@acme-prod.iam.gserviceaccount.com
QURL_API_KEY_FILE=/run/secrets/qurl-api-key
LAYERV_AGENT_ID=customer-prod-agent-01
Recommended WIF and KMS shape
APPROVED_IMAGE_DIGEST="sha256:..."

gcloud iam workload-identity-pools providers create-oidc attestation-verifier \
  --location=global \
  --workload-identity-pool=QURL_AGENT_POOL \
  --issuer-uri="https://confidentialcomputing.googleapis.com" \
  --allowed-audiences="https://sts.googleapis.com" \
  --attribute-mapping='google.subject="gcpcs::"+assertion.submods.container.image_digest+"::"+assertion.submods.gce.project_number+"::"+assertion.submods.gce.instance_id,attribute.image_digest=assertion.submods.container.image_digest' \
  --attribute-condition="assertion.swname == 'CONFIDENTIAL_SPACE' && 'STABLE' in assertion.submods.confidential_space.support_attributes && assertion.submods.container.image_digest == '${APPROVED_IMAGE_DIGEST}'"

gcloud kms keys add-iam-policy-binding \
  projects/acme-prod/locations/us/keyRings/qurl/cryptoKeys/agent-identity \
  --member="principalSet://iam.googleapis.com/projects/123456789/locations/global/workloadIdentityPools/QURL_AGENT_POOL/attribute.image_digest/${APPROVED_IMAGE_DIGEST}" \
  --role=roles/cloudkms.cryptoKeyEncrypterDecrypter

The ${APPROVED_IMAGE_DIGEST} placeholders are shell variables set at the top of the snippet; keep them literal when copying the block.

GCP implementation rules

  • The default token path is /run/container_launcher/attestation_verifier_claims_token.
  • Local checks require swname=CONFIDENTIAL_SPACE, dbgstat=disabled-since-boot, and STABLE support attributes.
  • Image digest and service account pins must match the customer's WIF attribute mapping and Cloud KMS IAM binding.
  • Run a negative test with the wrong image or service account before production.

Bootstrap and migration

Seal only after verify-unseal succeeds.

The sidecar writes sealed state only after it proves the selected provider can unseal it. If verification fails, the client exits rather than writing an unusable sealed blob.

New deployment

  1. Mount the qURL API key as a file and set QURL_API_KEY_FILE.
  2. Set a stable LAYERV_AGENT_ID.
  3. Set the provider-specific environment variables.
  4. Start the agent and wait for seal, verify, and connection.
  5. Restart without the API key and confirm reconnect from sealed state.

File-provider migration

  1. Back up the state volume if rollback policy requires it.
  2. Set LAYERV_KEY_PROVIDER to the sealed provider.
  3. Set LAYERV_ALLOW_KEY_MIGRATION=true for one restart only.
  4. Confirm private_key.sealed.json exists.
  5. Remove the migration acknowledgement and restart again.

Validation plan

Capture evidence before production handoff.

The customer change ticket should include startup state, qURL Connector restart behavior, negative cloud policy tests, and cloud audit evidence.

Startup and state

  • Agent starts with the selected provider and fails when required provider env is missing.
  • State contains agent_id and private_key.sealed.json.
  • Durable plaintext private_key is absent after successful migration.
  • Runtime OpenNHP config is materialized only in the runtime directory.

qURL Connector behavior

  • Agent registers or loads the expected LAYERV_AGENT_ID.
  • Routes connect through the qURL Connector and survive an agent restart.
  • Restart without QURL_API_KEY or QURL_API_KEY_FILE succeeds from sealed state.
  • Removing attestation inputs causes startup failure before connection.

Cloud policy gates

  • AWS: approved attestation decrypt returns CiphertextForRecipient, not host plaintext.
  • AWS: unapproved image digest or PCR cannot decrypt.
  • GCP: approved Confidential Space workload obtains KMS access.
  • GCP: non-attested principal, wrong image digest, or wrong service account cannot decrypt.

Useful local checks

These examples use the default state directory. If the deployment sets LAYERV_AGENT_STATE_DIR, replace /var/lib/layerv/agent with that path.

Local validation
# Confirm sealed state
ls -la /var/lib/layerv/agent
test -f /var/lib/layerv/agent/private_key.sealed.json
test ! -f /var/lib/layerv/agent/private_key

# Confirm the provider recorded in the sealed blob
grep -m1 -E '"provider"[[:space:]]*:[[:space:]]*"(aws-nitro|gcp-confidential-space)"' \
  /var/lib/layerv/agent/private_key.sealed.json

# Confirm steady-state restart does not need the API key
# Remove QURL_API_KEY / QURL_API_KEY_FILE from compose or service env first.
docker compose up -d --force-recreate qurl-agent

Operations handoff

Leave the customer with ownership, evidence, and rollback notes.

After onboarding, the customer operations owner should know when an API key is needed, what state to preserve, how attestation failures surface, and which audit logs matter.

Keep the qURL API key available only for first bootstrap or state recovery.

Do not delete or share the state volume; it contains the stable agent identity and sealed key blob.

Coordinate KMS key policy changes with qURL restarts.

Review CloudTrail or Cloud Audit Logs for unexpected decrypt attempts.

Acceptance checklist

  • Security owner approved cloud KMS policy and negative tests.
  • Platform owner confirmed restart behavior without the API key.
  • Operations owner accepted troubleshooting and rollback notes.
  • LayerV owner confirmed qURL Connector connectivity and expected agent ID.

Troubleshooting

Common attested-provider failures.

Most failures are missing provider env vars, stale attestation inputs, cloud policy mismatch, or state volume drift.

Startup says a provider env var is missing.

Set the provider-specific env vars in this guide. For GCP, image digest and service account are required before the token file is read.

AWS unwrap helper fails or returns the wrong size.

Verify the helper runs in the attested runtime and writes exactly 32 raw private-key bytes to stdout with no trailing newline.

AWS KMS returns host plaintext.

Fix the key policy or attestation document. aws-nitro intentionally rejects host plaintext and requires CiphertextForRecipient.

GCP local checks pass, but KMS decrypt fails.

Compare local pins to WIF attribute mapping and Cloud KMS IAM binding. The federated principal must match the approved workload.

Agent asks for an API key after restart.

Restore the correct state directory or intentionally re-bootstrap. The API key is only for first bootstrap or state recovery.

Related docs

Use this with the sidecar install guide.

Start with the standard qURL Connector install guide, then use this optional advanced companion only when the customer needs attested key custody.