Files
mesh-host/internal/bootstrap/push_credential.go
T

56 lines
2.3 KiB
Go

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
}