Documentation
¶
Overview ¶
Package artifact packages a zygote as an OCI artifact and moves it through a registry, so every home that holds a grant for a template warms byte-identical zygote pages from one content-addressed source.
Layout (one manifest, three layers, all titled):
zygote the executable, application/vnd.fiberd.zygote.bin.v1
config.json argv, arch, kernel and libc of the build host, digests,
and how images.tar is sealed
images.tar CRIU images of the zygote taken right after READY: the
parent pages that fiber deltas are computed against.
Sealed to the fleet's wrap key (wrap.go), never plain.
Or a Hyperlight guest's snapshot (snapshot.go), under
its own media type, and then no zygote
The manifest digest is the template digest a grant carries.
Index ¶
- Constants
- Variables
- func Build(ctx context.Context, o BuildOptions) (string, error)
- func Delete(ctx context.Context, ref string) (removed bool, err error)
- func GenerateSealKey() (*jose.JSONWebKey, error)
- func HostInfo() (arch, kernel, libc string)
- func ImagesDir(dir string) string
- func IsFileRef(ref string) bool
- func KernelSeries(release string) string
- func LibcVersion(bin string) string
- func Linking(path string) string
- func OpenImages(src, dst string, k *SealKey, s ImagesSeal) error
- func OpenParentDir(dir string, k *SealKey, sha string) error
- func OpenSnapshot(dir string, cfg Config, k *SealKey, dst string) error
- func Pack(ctx context.Context, dir string) (string, error)
- func PullDir(ctx context.Context, ref, dst string) (string, error)
- func Push(ctx context.Context, dir, ref string) (string, error)
- func PushDir(ctx context.Context, dir, ref, artifactType string, ...) (string, error)
- func ReadDigest(dir string) (string, error)
- func RemoveSnapshot(dst string) error
- func Repository(ref string) (*remote.Repository, string, error)
- func SealDir(src, dst string, k *SealKey, c SealContext) error
- func SealParentDir(src, dst string, k *SealKey, sha string) error
- func TarDir(src, dst string) error
- func TarSnapshot(src, dst string) error
- func UnpackImages(dir string, cfg Config, k *SealKey) error
- func Untar(src, dst string) error
- func ZygotePath(dir string) string
- type BuildOptions
- type Config
- type File
- type ImagesSeal
- type Keys
- type Located
- type Parity
- type Platform
- type SealContext
- type SealKey
- type WrapKey
- type WrapPublicKey
Constants ¶
const ( ArtifactType = "application/vnd.fiberd.zygote.v1" MediaTypeBinary = "application/vnd.fiberd.zygote.bin.v1" MediaTypeConfig = "application/vnd.fiberd.zygote.config.v1+json" MediaTypeImages = "application/vnd.fiberd.zygote.criu.v1.tar" // MediaTypeHyperlight is images.tar holding a Hyperlight guest's // snapshot, a tree a helper restores the warm template from, rather // than CRIU images (ImagesFormatHyperlight). MediaTypeHyperlight = "application/vnd.fiberd.zygote.hyperlight.v1.tar" // ImagesFormatHyperlight is Config.ImagesFormat for a Hyperlight // snapshot. Its artifact needs no zygote, since the helper restores // the guest from the snapshot and runs no executable of the template's. ImagesFormatHyperlight = "hyperlight" )
const ( ArtifactTypeDelta = "application/vnd.fiberd.delta.v1" ArtifactTypeParent = "application/vnd.fiberd.parent.v1" AnnotationWBytes = "io.fiberd.w_bytes" AnnotationParent = "io.fiberd.parent_sha256" AnnotationHome = "io.fiberd.home" AnnotationFence = "io.fiberd.fence" AnnotationGrant = "io.fiberd.grant_uid" AnnotationSession = "io.fiberd.session" // AnnotationGeneration is the parked fiber's generation, so a home // that claims the delta resumes it as the next one of its lineage. AnnotationGeneration = "io.fiberd.generation" )
const ( // LinkStatic is an ELF executable with no program interpreter. LinkStatic = "static" // LinkDynamic is anything else: an ELF with an interpreter, or a // script, which needs its interpreter from the root filesystem. LinkDynamic = "dynamic" )
How the zygote executable is linked. A home whose backend runs the template inside a root filesystem of its own (gVisor, runc) cannot load a dynamically linked executable unless that filesystem holds the libc it was built against, which the home cannot tell. A static executable needs nothing from it. Build records the fact so the home can refuse at warm, with a clear error, instead of failing inside the sandbox.
const ( AnnotationArch = "io.fiberd.arch" AnnotationKernel = "io.fiberd.kernel" AnnotationLibc = "io.fiberd.libc" AnnotationBackend = "io.fiberd.backend" AnnotationTemplates = "io.fiberd.templates" )
Annotations for platform facts on delta and parent manifests.
const ( AnnotationEngineClass = "io.fiberd.engine_class" AnnotationEngineLayout = "io.fiberd.engine_layout" AnnotationEngineVersion = "io.fiberd.engine_version" AnnotationGPUName = "io.fiberd.gpu_name" AnnotationGPUCC = "io.fiberd.gpu_cc" AnnotationGPUDriver = "io.fiberd.gpu_driver" )
Annotations for the device engine a parked fiber's slice was saved by, on a delta of a grant with an engine.
const ( ParityExact = "exact" // must be identical ParitySeries = "series" // kernel only: same major.minor ParityOff = "off" // not compared )
Parity levels for one fact.
const ( AnnotationDomain = "io.fiberd.domain" AnnotationExpires = "io.fiberd.expires" AnnotationSealKey = "io.fiberd.seal_key" )
const ( AnnotationSignature = "io.fiberd.signature" AnnotationSigner = "io.fiberd.signer" )
const FileScheme = "file://"
Variables ¶
var ErrEntryName = errors.New("artifact: archive entry is not a plain file name")
ErrEntryName means an images archive or a file registry manifest names a file that is not a plain file name, as TarDir and PushDir write them.
var ErrExpired = errors.New("artifact: sealed content has expired")
ErrExpired means a sealed delta is past the expiry it was sealed with.
var ErrParity = errors.New("platform parity")
ErrParity is wrapped by every mismatch.
var ErrSealed = errors.New("artifact: sealed content does not open")
ErrSealed means a sealed file does not open with this home's key for the domain and session asked for, or it was changed after sealing.
var ErrUntrusted = errors.New("artifact: not signed by a trusted key")
ErrUntrusted means a directory artifact is unsigned, signed by a key the home does not trust, or signed over content it no longer holds.
var Strict = Parity{Kernel: ParityExact, Libc: ParityExact}
Strict requires everything to match exactly.
Functions ¶
func Build ¶
func Build(ctx context.Context, o BuildOptions) (string, error)
Build runs the zygote the way a home does (control channel on fd 3), waits for READY, checkpoints it with criu --leave-running, seals the images to SealTo, and writes the artifact directory. Returns the packed manifest digest. The plaintext images and their tar are removed once sealed. With SkipImages it only checks that the zygote reaches READY, which needs no criu, and with NoSmoke as well it runs nothing.
SkipImages NoSmoke runs the zygote criu images.tar false any yes yes yes, sealed to SealTo true false yes, to READY no no true true no no no
func Delete ¶
Delete removes the manifest at ref (a tag or digest). Registries must allow deletes (registry:2: REGISTRY_STORAGE_DELETE_ENABLED=true). A missing manifest is not an error: the goal is that it be gone. A tag is deleted as a tag first (some registries keep tag entries when the manifest is deleted by digest), then the manifest by digest.
removed reports whether this call took the reference away. Two deleters of one tag race on the registry's one delete, and only the one whose delete found it wins. A claim of a parked session is that delete, so a caller that is told false lost the claim.
func GenerateSealKey ¶
func GenerateSealKey() (*jose.JSONWebKey, error)
GenerateSealKey returns a fresh master seal key as a symmetric JWK.
func HostInfo ¶
func HostInfo() (arch, kernel, libc string)
HostInfo fills the parity fields for the running host.
func KernelSeries ¶
KernelSeries is the "major.minor" of a release string ("6.10.14-linuxkit" -> "6.10"): the level at which the syscall and image ABI usually holds.
func LibcVersion ¶
LibcVersion is the libc the ldd called bin names.
func Linking ¶
Linking reports how the executable at path is linked. Anything that is not a static ELF, including a file that cannot be read, is dynamic: the conservative answer, since a home refuses what it cannot vouch for.
func OpenImages ¶
func OpenImages(src, dst string, k *SealKey, s ImagesSeal) error
OpenImages opens the sealed tar at src into dst, written owner-only, using the fleet key derived from k and the metadata config.json carries. The file is refused before any of it is decrypted when its header does not say what s says, and the plaintext is refused after when it does not hash to s.SHA256. On error dst is removed.
func OpenParentDir ¶
OpenParentDir replaces every sealed file in dir by its plaintext. It refuses any file not sealed with k as the parent sha, or sealed as a delta or as author images, or changed since. On error, dir holds a mix of sealed and plain files, and the caller must remove it. On success the caller still loads dir with the delta codec and checks that it hashes to sha.
func OpenSnapshot ¶
OpenSnapshot opens the sealed Hyperlight snapshot of the verified artifact in dir with the fleet key derived from k, checks it hashes to what cfg names, and unpacks it into dst for one warm alone, which must not exist. A copy of its own, so nothing another warm or a later resolve does reaches it, and read-only once made: files 0400 and directories 0500, fiberd's alone. The plaintext tar is made beside dst and is gone when it returns. On any error dst is removed. RemoveSnapshot removes it.
Read-only is by mode. It keeps a process without CAP_DAC_OVERRIDE from writing the template by mistake, and is no boundary: one running as fiberd's user could make it writable, and anything else that user can write besides.
func Pack ¶
Pack computes the manifest for an artifact directory and records its digest in the DIGEST side file (never inside a layer, or the digest would change itself). It is deterministic for a given directory: the created annotation is the build time from the config, not now.
func PullDir ¶
PullDir downloads the artifact at ref into dst (files land under their titles) and returns the manifest digest.
func Push ¶
Push uploads the artifact directory to ref ("host/repo:tag") and returns the manifest digest.
func PushDir ¶
func PushDir(ctx context.Context, dir, ref, artifactType string, annotations map[string]string, signer *jose.JSONWebKey) (string, error)
PushDir uploads every regular file in dir (logs excluded) as the artifact at ref, with the annotations on the manifest. It signs with signer when that is set, and returns the manifest digest.
func ReadDigest ¶
ReadDigest returns the digest Pack recorded.
func RemoveSnapshot ¶
RemoveSnapshot removes a snapshot OpenSnapshot opened, giving its directories back the write bit a removal needs first, and any plaintext tar an open left beside it. A missing dst is no error.
func Repository ¶
func Repository(ref string) (*remote.Repository, string, error)
Repository is the remote.Repository for callers that need it directly.
func SealDir ¶
func SealDir(src, dst string, k *SealKey, c SealContext) error
SealDir writes every file PushDir would push from src, sealed for c, into dst under the same names. A delta always expires, so a zero Expires is refused here rather than sealed as a file nothing opens.
func SealParentDir ¶
SealParentDir writes every file PushDir would push from src, sealed as the parent checkpoint sha, into dst under the same names. src must be the store entry whose codec hash is sha.
func TarDir ¶
TarDir writes every regular file directly under src into the tar archive dst (flat, reproducible). Untar unpacks such an archive into dst and fails with ErrEntryName on any entry that is not a plain name.
func TarSnapshot ¶
TarSnapshot writes the tree at src into the tar at dst, directories and regular files only, in a stable order with fixed times and owners, so one tree always tars to the same bytes. Anything else in the tree (a symlink, a device, a FIFO) refuses the tar rather than be dropped, and so do a src that is itself a symlink, which would tar as nothing, and a tree that holds no file.
func UnpackImages ¶
UnpackImages opens the sealed images.tar of the verified artifact in dir with the fleet key derived from k, checks it hashes to what cfg names, and unpacks it into ImagesDir(dir), owner-only. The unpack goes beside and is swapped in, so a parent already mapped from the old files keeps them until it is closed. Without k it says the home needs the fleet's seal key.
func ZygotePath ¶
ZygotePath is where the executable lives in an artifact directory.
Types ¶
type BuildOptions ¶
type BuildOptions struct {
Zygote string // path to the executable
Args []string // arguments the home will pass
Out string // artifact directory to create
// SkipImages builds an artifact without a CRIU checkpoint (no criu on
// this host, or a template that does not need deltas). The zygote is
// still run to READY once, as a smoke test, unless NoSmoke.
SkipImages bool
// NoSmoke skips that run, for a template whose warm-up is slow or one
// this host cannot run, such as a gVisor-mode template that waits on
// /proc/gvisor/checkpoint, another architecture's, or any off Linux.
NoSmoke bool
// SealTo is the fleet's public wrap key (fleet-pub.json, which
// grant-issuer keygen -alg A256GCM writes). images.tar is sealed to
// it, and a build with images refuses to run without it, so no
// template memory is ever packed in the clear. A build with
// SkipImages needs none.
SealTo *WrapPublicKey
CRIU criu.Options
ReadyTimeout time.Duration
// Platform is the build host the artifact records. Empty fields are
// detected (ldd for the libc on Linux), so a caller that names all
// three runs nothing. Backend and Templates are not recorded.
Platform Platform
// HyperlightSnapshot is the directory of a Hyperlight guest's
// snapshot to carry as the artifact's images instead of CRIU images
// of a zygote (ImagesFormatHyperlight), sealed to SealTo. Zygote must
// then be empty, and Platform must name the kernel and libc a
// Hyperlight home reports, the helper's facts (hyperlight.PlatformOf),
// since the snapshot depends on the helper and not on this host.
HyperlightSnapshot string
}
BuildOptions describe how to turn a zygote executable into an artifact.
type Config ¶
type Config struct {
Args []string `json:"args"`
Arch string `json:"arch"`
Kernel string `json:"kernel"`
Libc string `json:"libc"`
ZygoteSHA256 string `json:"zygote_sha256"`
// Linking is LinkStatic or LinkDynamic (linking.go). A home whose
// backend runs the template inside a root filesystem of its own
// refuses a dynamic one, since it cannot tell whether that
// filesystem holds the libc. Empty in a config written before the
// fact was recorded, which counts as dynamic.
Linking string `json:"linking,omitempty"`
// Images says how images.tar is sealed. Nil when the artifact has no
// images. An artifact never carries images in the clear.
Images *ImagesSeal `json:"images,omitempty"`
// ImagesFormat is what images.tar holds: empty for CRIU images of
// the zygote, ImagesFormatHyperlight for a Hyperlight snapshot.
ImagesFormat string `json:"images_format,omitempty"`
BuiltAt time.Time `json:"built_at"`
// Digest is the manifest digest. It is never stored in config.json
// (it is a layer); Pull fills it from the transfer, and ReadDigest
// reads the DIGEST side file Pack and Pull write.
Digest string `json:"-"`
}
Config describes the zygote and the host it was checkpointed on. The parity fields gate cross-host resume: a delta over these pages only restores where the kernel and libc match.
func Pull ¶
Pull downloads the artifact at repo@digest (or repo:tag) into dst. The manifest digest is verified by the transfer, and the directory must pack to it, as Reverify holds it at every warm. Sealed images stay sealed. A home opens them with UnpackImages.
func ReadConfig ¶
ReadConfig loads config.json from an artifact directory.
func Reverify ¶
Reverify re-checks a template cache entry a previous Pull left. The directory must still pack to digest and its zygote still hash to its config. A home then unpacks the verified images.tar afresh with UnpackImages. Without this, anything that can write the cache could change what every later warm runs.
func (Config) HasZygote ¶
HasZygote reports whether the artifact carries an executable. A Hyperlight snapshot artifact never does: the helper restores the guest and runs no executable of the template's, and a fiberd from before the format, which would run its own guest in the snapshot's place, cannot pull an artifact without one.
type File ¶
type File struct {
Name string `json:"name"`
Digest string `json:"digest"`
Size int64 `json:"size"`
}
File is one file of a directory artifact as its manifest lists it.
type ImagesSeal ¶
type ImagesSeal struct {
KeyID string `json:"wrap_key"`
Enc []byte `json:"enc"`
Wrapped []byte `json:"wrapped_key"`
SHA256 string `json:"sha256"`
}
ImagesSeal is what config.json carries about a sealed images.tar. A home needs all four to open it, and each is also bound into the sealed file.
func SealImages ¶
func SealImages(src, dst string, pub *WrapPublicKey) (ImagesSeal, error)
SealImages seals the plaintext tar at src into dst for the fleet key pub and returns what config.json must carry. src is read twice, once to hash it and once to seal it, so a 512 MiB tar never sits in memory. The name images.tar is bound into the file key whatever dst is called.
type Keys ¶
type Keys struct {
// Signer is the private Ed25519 JWK pushes are signed with.
Signer *jose.JSONWebKey
// Trust holds the public keys whose signatures are accepted, by kid.
// Signer's own public half is always accepted.
Trust []jose.JSONWebKey
// Seal is the master key deltas are sealed under (seal.go).
Seal *SealKey
}
Keys is what a home signs with and what it accepts.
func LoadKeys ¶
LoadKeys reads the signing key (a private Ed25519 JWK, as `grant-issuer keygen -alg EdDSA` writes) and, when trustPath is set, a JWKS of further public keys to accept.
type Located ¶
type Located struct {
Digest string
ArtifactType string
Annotations map[string]string
Files []File
}
Located describes an artifact found in a registry without pulling it.
type Parity ¶
type Parity struct {
Kernel string // ParityExact (default), ParitySeries or ParityOff
Libc string // ParityExact (default) or ParityOff
}
Parity says how strictly a home requires a checkpoint's platform to match its own. The architecture, the backend and the template cache path are always required to match; there is no level at which a foreign instruction set, another mechanism's image format or a missing template executable can restore. The zero value is strict: exact kernel release and exact libc.
func ParseParity ¶
ParseParity reads the -parity flag: "strict", "off", or a comma list of kernel=exact|series|off and libc=exact|off.
type Platform ¶
type Platform struct {
Arch string `json:"arch"`
Kernel string `json:"kernel"` // uname -r
Libc string `json:"libc"` // "(gnu libc) 2.36"
// Backend is the sandbox mechanism that made the checkpoint ("proc",
// "gvisor", ...). Empty for a template artifact (any backend that
// reads the format may warm it) and for facts recorded before it.
Backend string `json:"backend,omitempty"`
// Templates is the template cache path, for a backend whose restore
// reopens a cached template executable by that path (proc). Empty
// for any other, and for a session whose template is not cached.
Templates string `json:"templates,omitempty"`
}
Platform is what a checkpoint's pages assume about the host that made them.
func Host ¶
func Host() Platform
Host is the platform this process runs on. It runs ldd on Linux, so a caller that already knows its facts completes a Platform instead.
func PlatformFromAnnotations ¶
PlatformFromAnnotations reads a platform back; ok is false when the manifest carries none (a publisher from before the parity gate).
type SealContext ¶
type SealContext struct {
Domain string `json:"domain"`
Session string `json:"session"`
Fence string `json:"fence,omitempty"`
Expires time.Time `json:"expires"`
}
SealContext is what a sealed file is bound to. A delta's Expires is the instant it stops opening. A parent's is zero, which means never, and a zero expiry is accepted for parent contexts only.
func OpenDir ¶
OpenDir replaces every sealed file in dir by its plaintext. It refuses any file not sealed with k for domain and session, or expired at now, and a directory whose files were sealed for different contexts, so one delta cannot be assembled from files of several parks. It returns the one context the files were sealed with. On error, dir holds a mix of sealed and plain files, and the caller must remove it.
func ParentContext ¶
func ParentContext(sha string) SealContext
ParentContext is the context a parent checkpoint is sealed for. The zero Expires means never.
type SealKey ¶
SealKey is the master key deltas are sealed under.
func LoadSealKey ¶
LoadSealKey reads a master seal key (a 32-byte symmetric JWK with a kid, as GenerateSealKey makes).
func SealKeyFromJWK ¶
func SealKeyFromJWK(jwk *jose.JSONWebKey) (*SealKey, error)
SealKeyFromJWK takes the master seal key out of its JWK.
type WrapKey ¶
type WrapKey struct {
ID string
// contains filtered or unexported fields
}
WrapKey is the fleet's X25519 key pair. The private half is derived on a home from the master and never serialised.
func DeriveWrapKey ¶
DeriveWrapKey derives the fleet wrap pair from the master seal key. The same master always gives the same pair and the same key id.
func (*WrapKey) Public ¶
func (w *WrapKey) Public() *WrapPublicKey
Public returns the half authors get.
type WrapPublicKey ¶
type WrapPublicKey struct {
ID string
// contains filtered or unexported fields
}
WrapPublicKey is the public half authors seal images to.
func LoadWrapPublicKey ¶
func LoadWrapPublicKey(path string) (*WrapPublicKey, error)
LoadWrapPublicKey reads fleet-pub.json.
func ParseWrapPublicKey ¶
func ParseWrapPublicKey(b []byte) (*WrapPublicKey, error)
ParseWrapPublicKey reads an OKP X25519 public JWK. The kid must be the one its public key gives, so a mislabelled file is refused.
func (*WrapPublicKey) JWK ¶
func (p *WrapPublicKey) JWK() ([]byte, error)
JWK renders the public key as an OKP X25519 JWK.