ENG-5: the bootstrap makes a one-off push credential for the holders' first images, writes it for the store's holder and forgets it when its run ends (design 51, issue 1966)
Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
This commit is contained in:
@@ -188,7 +188,9 @@ func parseArgs(args []string) (string, bootstrap.Options, bool, error) {
|
||||
Out: defaultOut,
|
||||
State: store.DefaultPath,
|
||||
Registry: defaultRegistry,
|
||||
Host: defaultHost,
|
||||
// Where the store's holder reads the one-off credential the holders' first images are pushed with (design 51, ENG-5).
|
||||
PushCredentialFile: "/var/lib/mesh/registry/bootstrap-push",
|
||||
Host: defaultHost,
|
||||
// The machine's own name, because that is what a person already calls it and an installer
|
||||
// inventing a different one would leave the mesh naming a machine nobody recognises. It is
|
||||
// read here rather than inside the bootstrap so that --node overrides a fact rather than a
|
||||
|
||||
@@ -157,6 +157,12 @@ type Options struct {
|
||||
// an address the others can reach.
|
||||
Registry string
|
||||
|
||||
// PushCredentialFile is where the bootstrap writes its one-off push credential for the store's holder to find, the
|
||||
// holder's state file `bootstrap-push` on the node that holds it (design 51 *The move*, issue 1966 ENG-5); empty writes
|
||||
// none. PushCredentialOwner is the user id and group id the file is given when the holder's account is known.
|
||||
PushCredentialFile string
|
||||
PushCredentialOwner *[2]int
|
||||
|
||||
// Host is the `mesh-host` binary on this machine — the program that enrols and then holds the
|
||||
// machine to what the mesh says. The installer runs it; it does not contain it.
|
||||
Host string
|
||||
@@ -296,10 +302,12 @@ type Result struct {
|
||||
Agent string `json:"host-agent,omitempty"`
|
||||
|
||||
// Registry is the mesh's own artifact store, once it answers.
|
||||
Registry string `json:"registry,omitempty"`
|
||||
RegistryReplied int `json:"registry-replied,omitempty"`
|
||||
RegistryKnown bool `json:"registry-already-registered,omitempty"`
|
||||
RegistryRunning string `json:"registry-container,omitempty"`
|
||||
Registry string `json:"registry,omitempty"`
|
||||
// PushCredentialFile is where this run left its one-off push credential (never the credential itself).
|
||||
PushCredentialFile string `json:"push-credential-file,omitempty"`
|
||||
RegistryReplied int `json:"registry-replied,omitempty"`
|
||||
RegistryKnown bool `json:"registry-already-registered,omitempty"`
|
||||
RegistryRunning string `json:"registry-container,omitempty"`
|
||||
// RegistryTrusted are the names the runtime was told to trust the registry by, before the push.
|
||||
RegistryTrusted []string `json:"registry-trusted,omitempty"`
|
||||
PublishedAs string `json:"control-plane-image,omitempty"`
|
||||
@@ -815,6 +823,17 @@ func RegistryAndPublish(ctx context.Context, o Options, d Deps, sys system.Syste
|
||||
if err != nil {
|
||||
return Published{}, failed(StepRegistry, err)
|
||||
}
|
||||
// The one-off credential the holders' first images are pushed to the store's front with, written for its holder to find
|
||||
// and forgotten when this run ends; the holder discards the file once it answers its first publish (ENG-5).
|
||||
if o.PushCredentialFile != "" {
|
||||
cred, err := MintPushCredential(o.PushCredentialFile, o.PushCredentialOwner)
|
||||
if err != nil {
|
||||
return Published{}, failed(StepRegistry, err)
|
||||
}
|
||||
defer cred.Forget()
|
||||
result.PushCredentialFile = cred.File
|
||||
say(" a one-off credential for the holders' first images is at " + cred.File + "; the store's holder discards it once it serves publish")
|
||||
}
|
||||
|
||||
// ---- 8. publish -----------------------------------------------------------------------
|
||||
say("publish — the control plane's image gets its first manifest digest")
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package bootstrap
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// PushCredential is the bootstrap's one-off credential for the holders' first images (design 51 *The move*; issue 1966 ENG-5):
|
||||
// a bearer token of 32 random bytes, hex, on one line, made for one fresh-mesh run. No vault and no bus holds it: this run
|
||||
// holds it and writes it once, to the file the store's holder reads on each write (`Authorization: Bearer <token>` on an OCI
|
||||
// write to the store's port, from any node); the holder removes the file when it answers its first `publish`, and this run
|
||||
// forgets the token when it ends. On the holder's own node the bootstrap may push to the registry container's loopback with
|
||||
// no credential at all.
|
||||
type PushCredential struct {
|
||||
// File is where the token was written.
|
||||
File string
|
||||
token string
|
||||
}
|
||||
|
||||
// Token is the bearer token, for a push through the store's front from another node.
|
||||
func (c *PushCredential) Token() string { return c.token }
|
||||
|
||||
// Forget drops the token from this run's memory. The file is the holder's to remove.
|
||||
func (c *PushCredential) Forget() { c.token = "" }
|
||||
|
||||
// MintPushCredential makes a token and writes it to file, mode 0600, owned by owner (user id, group id) when given; the
|
||||
// directory is made if it is not there. A file that is there is replaced: a credential left by an earlier run is never reused.
|
||||
func MintPushCredential(file string, owner *[2]int) (*PushCredential, error) {
|
||||
raw := make([]byte, 32)
|
||||
if _, err := rand.Read(raw); err != nil {
|
||||
return nil, fmt.Errorf("a push credential cannot be made: %w", err)
|
||||
}
|
||||
token := hex.EncodeToString(raw)
|
||||
if err := os.MkdirAll(filepath.Dir(file), 0o755); err != nil {
|
||||
return nil, fmt.Errorf("the push credential's directory %s cannot be made: %w", filepath.Dir(file), err)
|
||||
}
|
||||
tmp := file + ".new"
|
||||
if err := os.WriteFile(tmp, []byte(token+"\n"), 0o600); err != nil {
|
||||
return nil, fmt.Errorf("the push credential cannot be written to %s: %w", file, err)
|
||||
}
|
||||
if owner != nil {
|
||||
if err := os.Chown(tmp, owner[0], owner[1]); err != nil {
|
||||
os.Remove(tmp)
|
||||
return nil, fmt.Errorf("the push credential cannot be given to the store's holder: %w", err)
|
||||
}
|
||||
}
|
||||
if err := os.Rename(tmp, file); err != nil {
|
||||
os.Remove(tmp)
|
||||
return nil, fmt.Errorf("the push credential cannot be put at %s: %w", file, err)
|
||||
}
|
||||
return &PushCredential{File: file, token: token}, nil
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -263,6 +264,9 @@ func TestNothingIsPushedToTheRegistryBeforeTheRuntimeTrustsIt(t *testing.T) {
|
||||
return http.StatusNotFound, "", nil
|
||||
}}
|
||||
var result Result
|
||||
// And the one-off credential for the holders' first images (ENG-5): written once, mode 0600, one line of 64 hex digits,
|
||||
// named in the result and never put in it.
|
||||
o.PushCredentialFile = filepath.Join(t.TempDir(), "registry", "bootstrap-push")
|
||||
published, err := RegistryAndPublish(context.Background(), o, d, sys,
|
||||
controlPlane{container: "temp-mesh-controller", run: run, timeout: time.Second}, held, &result, func(string) {})
|
||||
if err != nil {
|
||||
@@ -287,6 +291,19 @@ func TestNothingIsPushedToTheRegistryBeforeTheRuntimeTrustsIt(t *testing.T) {
|
||||
t.Errorf("the trust was not written (%d) and confirmed (%d) before the first tag or push (%d): %v",
|
||||
reloaded, confirmed, firstPush, runtime.commands)
|
||||
}
|
||||
if raw, err := os.ReadFile(o.PushCredentialFile); err != nil || !regexp.MustCompile(`^[0-9a-f]{64}\n$`).Match(raw) {
|
||||
t.Errorf("the push credential is %q (%v)", raw, err)
|
||||
} else if st, _ := os.Stat(o.PushCredentialFile); st.Mode().Perm() != 0o600 {
|
||||
t.Errorf("the push credential is mode %v", st.Mode().Perm())
|
||||
}
|
||||
if result.PushCredentialFile != o.PushCredentialFile {
|
||||
t.Errorf("the result names %q", result.PushCredentialFile)
|
||||
}
|
||||
if again, err := MintPushCredential(o.PushCredentialFile, nil); err != nil || again.Token() == "" {
|
||||
t.Errorf("a credential of an earlier run was not replaced: %v", err)
|
||||
} else if again.Forget(); again.Token() != "" {
|
||||
t.Errorf("a forgotten credential still holds its token")
|
||||
}
|
||||
if fmt.Sprint(result.RegistryTrusted) != "[192.0.2.10:5100 anchor.internal:5100]" {
|
||||
t.Errorf("the result says it trusted %v", result.RegistryTrusted)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user