From 07db4ab5bd8765631ba5073041190a2f11324992 Mon Sep 17 00:00:00 2001 From: Julien Dessaux Date: Mon, 10 Jun 2024 23:22:33 +0200 Subject: feat(stdlib): added borg server custom promise --- stdlib/backups/borg/common.go | 9 ++++ stdlib/backups/borg/server.go | 100 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 stdlib/backups/borg/common.go create mode 100644 stdlib/backups/borg/server.go diff --git a/stdlib/backups/borg/common.go b/stdlib/backups/borg/common.go new file mode 100644 index 0000000..60d14f2 --- /dev/null +++ b/stdlib/backups/borg/common.go @@ -0,0 +1,9 @@ +package borg + +import gonf "git.adyxax.org/adyxax/gonf/v2/pkg" + +func installBorgPackage() gonf.Status { + packag := gonf.Package("borgbackup") + packag.Resolve() + return packag.Status() +} diff --git a/stdlib/backups/borg/server.go b/stdlib/backups/borg/server.go new file mode 100644 index 0000000..95768da --- /dev/null +++ b/stdlib/backups/borg/server.go @@ -0,0 +1,100 @@ +package borg + +import ( + "log/slog" + "path/filepath" + + gonf "git.adyxax.org/adyxax/gonf/v2/pkg" +) + +type BorgServer struct { + chain []gonf.Promise + clients map[string][]byte // name -> publicKey + path string + user string + status gonf.Status +} + +func (b *BorgServer) IfRepaired(p ...gonf.Promise) gonf.Promise { + b.chain = append(b.chain, p...) + return b +} + +func (b *BorgServer) Promise() gonf.Promise { + gonf.MakeCustomPromise(b).Promise() + return b +} + +func (b *BorgServer) Resolve() { + b.status = gonf.KEPT + // Borg user + user := gonf.User(gonf.UserData{ + HomeDir: b.path, + Name: "borg", + System: true, + }) + user.Resolve() + switch user.Status() { + case gonf.BROKEN: + b.status = gonf.BROKEN + return + case gonf.REPAIRED: + b.status = gonf.REPAIRED + } + // borg package + switch installBorgPackage() { + case gonf.BROKEN: + b.status = gonf.BROKEN + return + case gonf.REPAIRED: + b.status = gonf.REPAIRED + } + // authorized_keys + borgDir := gonf.ModeUserGroup(0700, "borg", "borg") + borgRO := gonf.ModeUserGroup(0400, "borg", "borg") + file := gonf.File(filepath.Join(b.path, ".ssh/authorized_keys")). + DirectoriesPermissions(borgDir). + Permissions(borgRO) + authorizedKeys := "" + // we sort the names so that the file contents are stable + names := make([]string, len(b.clients)-1) + for name := range b.clients { + names = append(names, name) + } + for _, name := range names { + key := b.clients[name] + authorizedKeys += "command=\"borg serve --restrict-to-path " + filepath.Join(b.path, name) + "\",restrict " + string(key) + "\n" + } + file.Contents(authorizedKeys).Resolve() + switch file.Status() { + case gonf.BROKEN: + b.status = gonf.BROKEN + return + case gonf.REPAIRED: + b.status = gonf.REPAIRED + } + // TODO init repositories? or let the borg client do it? +} + +func (b BorgServer) Status() gonf.Status { + return b.status +} + +func Server() *BorgServer { + return &BorgServer{ + chain: nil, + clients: make(map[string][]byte), + path: "/srv/borg/", + user: "borg", + status: gonf.PROMISED, + } +} + +func (b *BorgServer) Add(name string, publicKey []byte) *BorgServer { + if _, ok := b.clients[name]; ok { + slog.Debug("Duplicate name for BorgServer", "name", name) + panic("Duplicate name for BorgServer") + } + b.clients[name] = publicKey + return b +} -- cgit v1.2.3