A review of v0.10.4-rc.4, and a look at the live network, found: - Alertmanager listening on every interface: from any node its API took silences, with no login. Bound to loopback on the monitoring host, and the monitoring README now installs it that way; Prometheus beside it is its only client, and one Alertmanager needs no cluster port. - Login and signup unbounded in memory. Each attempt runs argon2 with 64 MiB, and nothing limited how many ran at once; the per-address throttle kept one entry per address ever tried. Hashing now takes one of a few slots and waits for one otherwise. The throttle expires entries whose window has passed when it fills, and if it is still full it refuses new addresses: evicting live ones instead would let a flood of made-up addresses reset the throttle on the address actually being guessed. - Metric labels taken from the request. A refused request counted the model name it asked for, so any key holder could mint a series per request. Requests are counted under the catalog's name, or "unknown". - Two in code written this week. A SQLite backup made its snapshot with the default mode in the listable backup directory, a full copy of the store readable while the backup ran; it is now made in a private directory, and the unit sets UMask=0077. And archive links were checked by name only, so "a -> ." then "a/a/b -> ../.." passed and a file written through them landed above the install directory, as root. Archives now unpack through os.Root, and a link may only point at a file beside it — which every llama.cpp b10991 archive, CPU and CUDA, was checked to follow. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
142 lines
4.0 KiB
Go
142 lines
4.0 KiB
Go
package cli
|
|
|
|
import (
|
|
"archive/tar"
|
|
"compress/gzip"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"gitea.mynodes.xyz/ilker/llmgw/internal/store"
|
|
)
|
|
|
|
func TestABackupHoldsTheStoreAndTheConfiguration(t *testing.T) {
|
|
tmp := t.TempDir()
|
|
dbPath := filepath.Join(tmp, "store.db")
|
|
db, err := store.Open(t.Context(), dbPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
secret, err := db.ClusterSecret(t.Context(), "engine-key")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_ = db.Close()
|
|
|
|
etc := filepath.Join(tmp, "etc")
|
|
_ = os.MkdirAll(filepath.Join(etc, "pki"), 0o700)
|
|
_ = os.WriteFile(filepath.Join(etc, "server.yaml"), []byte("listen: :8080\n"), 0o644)
|
|
_ = os.WriteFile(filepath.Join(etc, "pki", "ca-key.pem"), []byte("KEY"), 0o600)
|
|
|
|
out := filepath.Join(tmp, "backups")
|
|
path, err := writeBackup(t.Context(), dbPath, etc, out)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
info, _ := os.Stat(path)
|
|
if info.Mode().Perm() != 0o600 {
|
|
t.Errorf("the archive holds the CA's key and is %v", info.Mode().Perm())
|
|
}
|
|
|
|
files := map[string][]byte{}
|
|
f, _ := os.Open(path)
|
|
defer f.Close()
|
|
gz, err := gzip.NewReader(f)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
tr := tar.NewReader(gz)
|
|
for {
|
|
h, err := tr.Next()
|
|
if err == io.EOF {
|
|
break
|
|
}
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
b, _ := io.ReadAll(tr)
|
|
files[h.Name] = b
|
|
}
|
|
for _, want := range []string{"MANIFEST", "store.sqlite", "etc/llmgw/server.yaml", "etc/llmgw/pki/ca-key.pem"} {
|
|
if _, ok := files[want]; !ok {
|
|
t.Errorf("%s is not in the archive", want)
|
|
}
|
|
}
|
|
|
|
// And the store in it is a working store, with what was in it.
|
|
restored := filepath.Join(tmp, "restored.db")
|
|
_ = os.WriteFile(restored, files["store.sqlite"], 0o600)
|
|
rdb, err := store.Open(t.Context(), restored)
|
|
if err != nil {
|
|
t.Fatalf("the backed-up store does not open: %v", err)
|
|
}
|
|
defer rdb.Close()
|
|
if got, _ := rdb.ClusterSecret(t.Context(), "engine-key"); got != secret {
|
|
t.Error("the restored store lost the engine key secret; every engine would be locked out")
|
|
}
|
|
}
|
|
|
|
func TestOldBackupsArePrunedAndTheNewestIsFound(t *testing.T) {
|
|
dir := t.TempDir()
|
|
for i, stamp := range []string{"20260101T000000Z", "20260102T000000Z", "20260103T000000Z"} {
|
|
p := filepath.Join(dir, backupPrefix+"h-"+stamp+".tar.gz")
|
|
_ = os.WriteFile(p, []byte("x"), 0o600)
|
|
mt := time.Date(2026, 1, 1+i, 0, 0, 0, 0, time.UTC)
|
|
_ = os.Chtimes(p, mt, mt)
|
|
}
|
|
_ = os.WriteFile(filepath.Join(dir, "someone-elses-file"), []byte("x"), 0o600)
|
|
_ = os.WriteFile(filepath.Join(dir, backupPrefix+"h-20260104T000000Z.tar.gz.part"), []byte("x"), 0o600)
|
|
|
|
removed, err := pruneBackups(dir, 2)
|
|
if err != nil || removed != 1 {
|
|
t.Fatalf("removed %d, %v", removed, err)
|
|
}
|
|
if _, err := os.Stat(filepath.Join(dir, "someone-elses-file")); err != nil {
|
|
t.Error("a file the backup did not write was removed")
|
|
}
|
|
newest := NewestBackup(dir)
|
|
if !newest.Equal(time.Date(2026, 1, 3, 0, 0, 0, 0, time.UTC)) {
|
|
t.Errorf("newest %v; an interrupted .part must not count", newest)
|
|
}
|
|
if !NewestBackup(filepath.Join(dir, "none")).IsZero() {
|
|
t.Error("a missing directory reported a backup")
|
|
}
|
|
}
|
|
|
|
func TestABackupDirectoryTheControlPlaneCannotListIsSaid(t *testing.T) {
|
|
dir := t.TempDir()
|
|
_ = os.Chmod(dir, 0o700)
|
|
if unlistable(dir) == "" {
|
|
t.Error("a directory only its owner can list was taken as visible")
|
|
}
|
|
_ = os.Chmod(dir, 0o755)
|
|
if why := unlistable(dir); why != "" {
|
|
t.Errorf("a listable directory: %s", why)
|
|
}
|
|
}
|
|
|
|
func TestNoScratchCopyOfTheStoreIsLeftBehind(t *testing.T) {
|
|
// The snapshot is made before it is archived. Made straight into the
|
|
// listable archive directory, it was readable by anyone for that time.
|
|
tmp := t.TempDir()
|
|
dbPath := filepath.Join(tmp, "store.db")
|
|
db, err := store.Open(t.Context(), dbPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_ = db.Close()
|
|
out := filepath.Join(tmp, "backups")
|
|
if _, err := writeBackup(t.Context(), dbPath, t.TempDir(), out); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
entries, _ := os.ReadDir(out)
|
|
for _, e := range entries {
|
|
if strings.HasPrefix(e.Name(), ".store") {
|
|
t.Errorf("a scratch copy of the store was left behind: %s", e.Name())
|
|
}
|
|
}
|
|
}
|