agent-substrate / agent-substrate/substrate

No extension point at restore, so a restored actor cannot be reconciled against current policy

未关闭
#1,284 5 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看
area/node area/security kind/feature
主要语言
Go
星标
1.8k
派生
316
平均合并
2 天 43 分钟
30 天内合并 PR
287

描述

## Problem

A snapshot captures execution state at a point in time. Any authorization derived
from outside the actor can change while it is checkpointed. On restore there is
no point at which an external component can reconcile the two.

Concretely, on the full-scope restore path:

- no container is created, so no OCI hook fires (`restore.go` calls neither
`CreateContainer` nor `StartContainer`; only `run.go` does)
- the guest resumes with whatever in-guest state the snapshot captured
- the actor becomes reachable with no opportunity to re-validate it

So a policy decision made while the actor slept cannot be applied to it. The
actor comes back with the authority it had when the snapshot was taken.

This is not specific to any one policy system. Anything that establishes state
before a workload runs and needs it to still be valid afterwards has the same
problem: capability tokens, short-lived credentials, admission decisions, lease
or quota state, tenancy or residency constraints.

## What already exists

The lifecycle point is arguably already there. `restoreFullScope` blocks before
returning:

```go
// Block until every readyz-enabled container reports 200.
if err := readyz.WaitAll(ctx, containers, ateomnet.ActorVethIP); err != nil {
```

and durable volumes are re-materialised even earlier, before the VM starts:

```go
// Restore the durable-dir volumes before anything can observe them
if hasDurableVolumes(p.containers) {
if err := untarDurableVolumes(durableDir, restoreDir); err != nil {
```

So restore already has a stage where the actor is not yet reachable. What is
missing is a way for anything to run there.

## Suggested shape

An optional, configured reconciliation step invoked on the restore path before
the actor becomes reachable, which can fail the restore. Roughly:

- runs after durable volumes are re-materialised and before the VM is resumed, so
a rejected actor never executes rather than being stopped mid-flight
- receives enough identity to make a decision: actor ref and uid, template, and
the restored durable directory
- a non-nil error fails the restore, and the existing teardown path already
handles cleanup
- absent configuration means no behavioural change

Placing it before the VM resumes matters more than the exact interface. Doing it
after readyz means the workload has already run.

## Why we are raising it

We are building kernel-level enforcement that binds a workload to an authority
epoch established before it runs, and we need that epoch re-checked when a
snapshot is restored so a revoked authority cannot come back with the snapshot.
We have this working locally by reading state the guest wrote onto the durable
share and comparing it at the untar point, which is enough for a demonstration
but relies on an implementation detail rather than a contract.

Happy to send a PR for whichever shape you prefer, or to adapt to something you
already have planned. Related: #1224 (`guest_hook_path` propagation), which is
the corresponding gap on the pre-activation side.

贡献指南

打开贡献指南

评估

这个 Issue 还没有评估数据。

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。