Skip to content

securitypolicy: Handle SCITT envelope and transparent fragments#2779

Open
micromaomao wants to merge 7 commits into
microsoft:mainfrom
micromaomao:new-style-fragment-mainrebase
Open

securitypolicy: Handle SCITT envelope and transparent fragments#2779
micromaomao wants to merge 7 commits into
microsoft:mainfrom
micromaomao:new-style-fragment-mainrebase

Conversation

@micromaomao

@micromaomao micromaomao commented Jun 17, 2026

Copy link
Copy Markdown
Member

Require corresponding containerd / azcri changes to send MediaType in PolicyFragment struct.

TODO:

  • Test WCOW
  • Use a proper released version of cosesign1go

Tested with LCOW using this policy:

package policy

import future.keywords.every
import future.keywords.in

api_version := "0.12.0"
framework_version := "0.5.0"

fragments := [
  {
    "feed": "acidevacr.azurecr.io/internal/aci/aci-cc-infra-fragment",
    "includes": [
      "containers",
      "fragments"
    ],
    "issuer": "did:x509:0:sha256:wVkSM46SDpw4LuyEYoH6r5ym07whL5lWxNlGN-UPtf4::eku:1.3.6.1.4.1.311.10.3.13",
    "minimum_svn": "0",
    "required_receipt_issuers": [
      "esrp-cts-dev.confidential-ledger.azure.com"
    ]
  }
]

transparency_roots := [
  {
    "issuer": "did:x509:0:sha256:wVkSM46SDpw4LuyEYoH6r5ym07whL5lWxNlGN-UPtf4::eku:1.3.6.1.4.1.311.10.3.13",
    "subject": "acidevacr.azurecr.io/internal/aci/aci-cc-ttl",
    "minimum_svn": 0,
    "allowed_ledgers": [
      "*"
    ]
  }
]

...
PS C:\Users\azureuser\lcow_volume> .\runp.ps1
Started pod lcow_volume with ID:
18ffa67e5663b49ee3ad3b1471fc5760db61f2c1af1f941fdf707fd5633cac54
PS C:\Users\azureuser\lcow_volume> $env:podId="18ffa67e5663b49ee3ad3b1471fc5760db61f2c1af1f941fdf707fd5633cac54"
PS C:\Users\azureuser\lcow_volume> azcrictl inject-fragment $env:podId 'acidevacr.azurecr.io/internal/aci/aci-cc-infra-fragment:transparent-fragment-signing-0-20260615.3'
time="2026-06-17T18:16:49Z" level=fatal msg="injecting policy fragment: rpc error: code = Unknown desc = failed to inject any fragment: guest modify: guest RPC failure: error loading security policy fragment: policyDecision< eyJkZWNpc2lvbiI6ImRlbnkiLCJpbnB1dCI6eyJmZWVkIjoiYWNpZGV2YWNyLmF6dXJlY3IuaW8vaW50ZXJuYWwvYWNpL2FjaS1jYy1pbmZyYS1mcmFnbWVudCIsImZyYWdtZW50X2xvYWRlZCI6ZmFsc2UsImhhc19oZWFkZXJfc3ZuIjpmYWxzZSwiaGVhZGVyX3N2biI6bnVsbCwiaXNzdWVyIjoiZGlkOng1MDk6MDpzaGEyNTY6d1ZrU000NlNEcHc0THV5RVlvSDZyNXltMDd3aEw1bFd4TmxHTi1VUHRmNDo6ZWt1OjEuMy42LjEuNC4xLjMxMS4xMC4zLjEzIiwibmFtZXNwYWNlIjoiYXp1cmVjb250YWluZXJpbnN0YW5jZSIsInJlY2VpcHRfaXNzdWVycyI6W10sInJ1bGUiOiJsb2FkX2ZyYWdtZW50In0sInJlYXNvbiI6eyJlcnJvcnMiOlsibWlzc2luZyByZWNlaXB0IGZyb20gZXNycC1jdHMtZGV2LmNvbmZpZGVudGlhbC1sZWRnZXIuYXp1cmUuY29tIl19fQ== >policyDecision\nguest modify: guest RPC failure: error loading security policy fragment: policyDecision< eyJkZWNpc2lvbiI6ImRlbnkiLCJpbnB1dCI6eyJmZWVkIjoiYWNpZGV2YWNyLmF6dXJlY3IuaW8vaW50ZXJuYWwvYWNpL2FjaS1jYy1pbmZyYS1mcmFnbWVudCIsImZyYWdtZW50X2xvYWRlZCI6ZmFsc2UsImhhc19oZWFkZXJfc3ZuIjp0cnVlLCJoZWFkZXJfc3ZuIjowLCJpc3N1ZXIiOiJkaWQ6eDUwOTowOnNoYTI1Njp3VmtTTTQ2U0RwdzRMdXlFWW9INnI1eW0wN3doTDVsV3hObEdOLVVQdGY0Ojpla3U6MS4zLjYuMS40LjEuMzExLjEwLjMuMTMiLCJuYW1lc3BhY2UiOiJhenVyZWNvbnRhaW5lcmluc3RhbmNlIiwicmVjZWlwdF9pc3N1ZXJzIjpbXSwicnVsZSI6ImxvYWRfZnJhZ21lbnQifSwicmVhc29uIjp7ImVycm9ycyI6WyJtaXNzaW5nIHJlY2VpcHQgZnJvbSBlc3JwLWN0cy1kZXYuY29uZmlkZW50aWFsLWxlZGdlci5henVyZS5jb20iXX19 >policyDecision"

# This is because we haven't loaded the TTL yet. The denial message will say "receipt_issuers: []" since we did not successfully validate any receipts: 

PS C:\Users\azureuser\lcow_volume> base64d ... | ConvertFrom-Json | ConvertTo-Json
{
  "decision": "deny",
  "input": {
    "feed": "acidevacr.azurecr.io/internal/aci/aci-cc-infra-fragment",
    "fragment_loaded": false,
    "has_header_svn": true,
    "header_svn": 0,
    "issuer": "did:x509:0:sha256:wVkSM46SDpw4LuyEYoH6r5ym07whL5lWxNlGN-UPtf4::eku:1.3.6.1.4.1.311.10.3.13",
    "namespace": "azurecontainerinstance",
    "receipt_issuers": [],
    "rule": "load_fragment"
  },
  "reason": {
    "errors": [
      "missing receipt from esrp-cts-dev.confidential-ledger.azure.com"
    ]
  }
}
PS C:\Users\azureuser\lcow_volume> azcrictl inject-fragment $env:podId 'acidevacr.azurecr.io/internal/aci/aci-cc-ttl:transparent-fragment-signing-0-20260615.1'
sha256:2cacf5a79d1d6f28eeeb8ff5e30bb634a6caffb6afd85b9e4549a90556cd45f3

# TTL injection succeed

PS C:\Users\azureuser\lcow_volume> azcrictl inject-fragment $env:podId 'acidevacr.azurecr.io/internal/aci/aci-cc-infra-fragment:transparent-fragment-signing-0-20260615.3'
sha256:b59e1ac5ea85d58a515d1369b05f21245f76627858803ccbdb543e0b3b64362c

# Infra fragment injection succeed

PS C:\Users\azureuser\lcow_volume> .\createc.ps1
936784644be5bd9eacd76594ddde31f859011182755ad1e882b728373bc7c51a
PS C:\Users\azureuser\lcow_volume> .\startc.ps1
936784644be5bd9eacd76594ddde31f859011182755ad1e882b728373bc7c51a
Started container lcow_volume_volume_mounthost_volume with ID:
936784644be5bd9eacd76594ddde31f859011182755ad1e882b728373bc7c51a
PS C:\Users\azureuser\lcow_volume> cat -wait container_mounthost_volume.log
2026-06-17T18:22:38.7144427Z stderr F mount error: could not resolve address for aaa.file.core.windows.net: Unknown error
2026-06-17T18:22:38.7144427Z stdout F Error: Failed to mount the Azure File Share.

@micromaomao micromaomao requested a review from a team as a code owner June 17, 2026 17:31
@micromaomao micromaomao force-pushed the new-style-fragment-mainrebase branch from d3be079 to 05c03c7 Compare June 17, 2026 17:33
@micromaomao micromaomao requested a review from Copilot June 17, 2026 17:35

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR adds support for transparency receipts and Transparency Trust Lists (TTLs) to the security policy fragment injection workflow, and bumps the policy framework/API versions and cosesign1go dependency accordingly.

Changes:

  • Introduces TTL ingestion (load_transparency_trust_list) and receipt validation during LoadFragment.
  • Extends fragment injection to support multiple blob media types (policy fragments vs TTLs) and allows SVN to be provided via COSE header.
  • Updates framework/API versions and vendored module/dependency versions.

Reviewed changes

Copilot reviewed 15 out of 24 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
vendor/modules.txt Updates vendored cosesign1go module version and go version metadata.
go.mod Bumps github.com/Microsoft/cosesign1go to v1.6.0-alpha1.
go.sum Adds checksums for updated cosesign1go versions.
pkg/securitypolicy/version_framework Bumps framework version to 0.5.0 for new enforcement behavior.
pkg/securitypolicy/version_api Bumps API version to 0.12.0 to expose the new enforcement point.
pkg/securitypolicy/securitypolicyenforcer.go Extends enforcer interface: LoadFragment options + TTL loading API.
pkg/securitypolicy/securitypolicyenforcer_rego.go Implements receipt validation/TTL storage + rollback snapshotting.
pkg/securitypolicy/securitypolicy_options.go Adds media type dispatch, header SVN extraction, TTL parsing/loading.
pkg/securitypolicy/framework.rego Adds new policy constructs: header SVN, required receipts, TTL roots + new rule.
pkg/securitypolicy/api.rego Registers load_transparency_trust_list enforcement point (0.12.0).
pkg/securitypolicy/policy.rego Wires load_transparency_trust_list to framework rule.
pkg/securitypolicy/open_door.rego Allows TTL loading under open-door policy.
pkg/securitypolicy/regopolicy_linux_test.go Adds/updates tests for header SVN, TTL loading, and receipt requirements.
pkg/ctrdtaskapi/update.go Adds mediaType to fragment injection request shape.
internal/uvm/security_policy.go Passes media type through to guest resource settings.
internal/protocol/guestresource/resources.go Adds MediaType field to guest SecurityPolicyFragment.
internal/regopolicyinterpreter/regopolicyinterpreter.go Adds RegoQueryResult.Array() helper for TTL rule results.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pkg/securitypolicy/securitypolicyenforcer_rego.go
Comment on lines +1144 to +1147
receiptIssuers := make([]string, 0, len(receiptIssuersSet))
for issuer := range receiptIssuersSet {
receiptIssuers = append(receiptIssuers, issuer)
}
Comment thread internal/regopolicyinterpreter/regopolicyinterpreter.go Outdated
Comment thread pkg/securitypolicy/regopolicy_linux_test.go Outdated
Comment thread pkg/securitypolicy/securitypolicy_options.go Outdated
@micromaomao micromaomao force-pushed the new-style-fragment-mainrebase branch from 05c03c7 to f2dae99 Compare June 17, 2026 18:08
Signed-off-by: Tingmao Wang <tingmaowang@microsoft.com>
This version update is backwards compatible with v1.4.0, so gcs still builds.
It adds support for parsing new "SCITT"-style COSESign1 envelops, and the issuer
and feed is automatically extracted from their new location in the CWT claims,
if we get a new style fragment.  It also adds support for parsing receipts,
which we will need later.

Signed-off-by: Tingmao Wang <tingmaowang@microsoft.com>
Refactor LoadFragment parameter to add a HeaderSvn field.  If the SVN is set in
the protected headers, pass it to LoadFragment, which will let framework
validate that it is at least the minimum required SVN before loading any
fragment Rego code.

Assisted-by: GitHub Copilot:auto copilot-review
Signed-off-by: Tingmao Wang <tingmaowang@microsoft.com>
Signed-off-by: Tingmao Wang <tingmaowang@microsoft.com>
Assisted-by: GitHub Copilot:auto copilot-review
Signed-off-by: Tingmao Wang <tingmaowang@microsoft.com>
Let InjectFragment accept an extra media type field, populated by azcri, which
will determine if we're handling a Rego policy fragment or a Transparency Trust
List (TTL).  Backward compatibility is maintained by defaulting to
application/cose-x509+rego.

Add a new load_transparency_trust_list enforcement point so that whether a TTL
is accepted can be gated by policy, and the policy can select which ledgers the
TTL can add keys for.

The framework uses transparency_roots in a policy / fragments as the set of
allowed TTL signers.

Use cosesign1.ParseTTLPayload to parse the payload.

Bump version_api to 0.12.0.

Add apply_defaults for transparency_roots

Assisted-by: GitHub Copilot copilot-review
Signed-off-by: Tingmao Wang <tingmaowang@microsoft.com>
Fragments may now declare required_receipt_issuers, a list of ledgers from which
a valid transparency receipt, verifiable with loaded TTLs, must be present on
the fragment's COSE envelope before the fragment is allowed to load.

Add a Receipts field to LoadFragmentOptions, populated from the COSE envelope in
InjectFragment.

The Rego enforcer validates each receipt against the keys for its claimed issuer
only, so a ledger cannot sign a receipt pretending to be a different ledger, and
passes the set of validated issuers to the policy as input.receipt_issuers in
both load_fragment phases.

framework.rego load_fragment now checks required_receipt_issuers against
input.receipt_issuers and denies with 'missing receipt from <ledger>';
check_fragment defaults the field to [] so older policies are unaffected.

Receipt validation is behind a swappable closure so tests can exercise the logic
without a real CCF receipt.

Signed-off-by: Tingmao Wang <tingmaowang@microsoft.com>
@micromaomao micromaomao force-pushed the new-style-fragment-mainrebase branch from f2dae99 to 9f91c1b Compare June 17, 2026 18:37
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.

2 participants