From 6aacbfbb71adef16fc7808f76d2995f12c2a0361 Mon Sep 17 00:00:00 2001 From: Steven Tracey Date: Wed, 22 Apr 2026 22:26:21 -0400 Subject: [PATCH] [CI-SKIP] Store Pre-Claude Code --- Makefile | 10 +-- README.md | 7 +- {cmd => app}/bundle/main.go | 19 ++--- app/{shared => }/certs.go | 2 +- app/client/certs.go | 21 ++--- app/client/client.go | 1 - {cmd => app}/client/commands.go | 7 +- app/client/daemon.go | 26 +++--- app/client/grpc.go | 6 +- {cmd => app}/client/main.go | 16 ++-- app/{shared => }/commands.go | 2 +- app/{shared => }/config.go | 4 +- app/{shared => }/daemon.go | 2 +- app/executor/commands.go | 2 +- app/executor/executor.go | 2 +- app/executor/hook.go | 2 +- {cmd => app}/executor/main.go | 0 app/executor/util.go | 2 +- app/server/certs.go | 20 ++--- {cmd => app}/server/commands.go | 7 +- app/server/daemon.go | 21 +++-- {cmd => app}/server/main.go | 0 app/server/server.go | 1 - app/shared/install.go | 1 - app/{shared => }/util.go | 16 +--- client/git.go | 17 ++++ common/git.go | 140 ++------------------------------ common/util.go | 2 +- server/acme_manager.go | 10 +-- server/git.go | 119 +++++++++++++++++++++++++++ 30 files changed, 239 insertions(+), 246 deletions(-) rename {cmd => app}/bundle/main.go (50%) rename app/{shared => }/certs.go (93%) delete mode 100644 app/client/client.go rename {cmd => app}/client/commands.go (52%) rename {cmd => app}/client/main.go (53%) rename app/{shared => }/commands.go (99%) rename app/{shared => }/config.go (99%) rename app/{shared => }/daemon.go (99%) rename {cmd => app}/executor/main.go (100%) rename {cmd => app}/server/commands.go (52%) rename {cmd => app}/server/main.go (100%) delete mode 100644 app/server/server.go delete mode 100644 app/shared/install.go rename app/{shared => }/util.go (70%) diff --git a/Makefile b/Makefile index 45954fe..ae36868 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -VERSION := 1.1.4-beta +VERSION := 1.1.5-beta BUILD := $(shell git rev-parse --short HEAD) GO := go @@ -14,19 +14,19 @@ proto: bundle: proto @echo "Building Bundled Certman" - $(GO) build $(BUILD_FLAGS) -ldflags="-s -w $(LDFLAGS)" -o ./bin/certman-$(VERSION)-amd64 ./cmd/bundle + $(GO) build $(BUILD_FLAGS) -ldflags="-s -w $(LDFLAGS)" -o ./bin/certman-$(VERSION)-amd64 ./app/bundle client: proto @echo "Building Certman Client" - $(GO) build $(BUILD_FLAGS) -ldflags="-s -w $(LDFLAGS)" -o ./bin/certman-client-$(VERSION)-amd64 ./cmd/client + $(GO) build $(BUILD_FLAGS) -ldflags="-s -w $(LDFLAGS)" -o ./bin/certman-client-$(VERSION)-amd64 ./app/client server: proto @echo "Building Certman Server" - $(GO) build $(BUILD_FLAGS) -ldflags="-s -w $(LDFLAGS)" -o ./bin/certman-server-$(VERSION)-amd64 ./cmd/server + $(GO) build $(BUILD_FLAGS) -ldflags="-s -w $(LDFLAGS)" -o ./bin/certman-server-$(VERSION)-amd64 ./app/server executor: proto @echo "Building Certman Executor" - $(GO) build $(BUILD_FLAGS) -ldflags="-s -w $(LDFLAGS)" -o ./bin/certman-executor-$(VERSION)-amd64 ./cmd/executor + $(GO) build $(BUILD_FLAGS) -ldflags="-s -w $(LDFLAGS)" -o ./bin/certman-executor-$(VERSION)-amd64 ./app/executor build: proto bundle client server executor @echo "All binaries successfully built" diff --git a/README.md b/README.md index 215a8b2..74cf50e 100644 --- a/README.md +++ b/README.md @@ -24,4 +24,9 @@ sudo certman new-domain example.com ### TODO - Add systemd units during install - - Add update command to pull from latest release \ No newline at end of file + - Add update command to pull from latest release + + +## Scratch Board +- Server Flow + - Read from \ No newline at end of file diff --git a/cmd/bundle/main.go b/app/bundle/main.go similarity index 50% rename from cmd/bundle/main.go rename to app/bundle/main.go index 22a19fa..ae342ce 100644 --- a/cmd/bundle/main.go +++ b/app/bundle/main.go @@ -4,8 +4,7 @@ import ( "fmt" "os" - "git.nevets.tech/Steven/certman/app/executor" - "git.nevets.tech/Steven/certman/app/shared" + "git.nevets.tech/Steven/certman/app" "github.com/spf13/cobra" ) @@ -21,18 +20,18 @@ func main() { }, } - rootCmd.AddCommand(shared.VersionCmd) - rootCmd.AddCommand(shared.NewKeyCmd) - rootCmd.AddCommand(shared.DevCmd) + rootCmd.AddCommand(app.VersionCmd) + rootCmd.AddCommand(app.NewKeyCmd) + rootCmd.AddCommand(app.DevCmd) - rootCmd.AddCommand(shared.NewDomainCmd) - rootCmd.AddCommand(shared.InstallCmd) + rootCmd.AddCommand(app.NewDomainCmd) + rootCmd.AddCommand(app.InstallCmd) - rootCmd.AddCommand(shared.CertCmd) + rootCmd.AddCommand(app.CertCmd) - rootCmd.AddCommand(executor.ExecutorCmd) + //rootCmd.AddCommand(executor.ExecutorCmd) - rootCmd.AddCommand(shared.DaemonCmd) + rootCmd.AddCommand(app.DaemonCmd) if err := rootCmd.Execute(); err != nil { fmt.Fprintln(os.Stderr, err) diff --git a/app/shared/certs.go b/app/certs.go similarity index 93% rename from app/shared/certs.go rename to app/certs.go index 5737428..631d551 100644 --- a/app/shared/certs.go +++ b/app/certs.go @@ -1,4 +1,4 @@ -package shared +package app import ( "github.com/spf13/cobra" diff --git a/app/client/certs.go b/app/client/certs.go index f8e3ce4..a12edef 100644 --- a/app/client/certs.go +++ b/app/client/certs.go @@ -1,10 +1,10 @@ -package client +package main import ( "fmt" "path/filepath" - "git.nevets.tech/Steven/certman/app/shared" + "git.nevets.tech/Steven/certman/app" "git.nevets.tech/Steven/certman/client" "git.nevets.tech/Steven/certman/common" "github.com/go-git/go-billy/v5/memfs" @@ -43,7 +43,7 @@ var ( func init() { renewCertSubCmd.AddCommand(updateCertLinkSubCmd, decryptCertsSubCmd) - shared.CertCmd.AddCommand(renewCertSubCmd) + app.CertCmd.AddCommand(renewCertSubCmd) } func renewCert(domain string) error { @@ -52,25 +52,26 @@ func renewCert(domain string) error { Storage: memory.NewStorage(), FS: memfs.New(), } - config := shared.Config() - domainConfig, exists := shared.DomainStore().Get(domain) + config := app.Config() + domainConfig, exists := app.DomainStore().Get(domain) if !exists { - return shared.ErrConfigNotFound + return app.ErrConfigNotFound } if err := client.PullCerts(config, domainConfig, gitWorkspace); err != nil { return err } - certsDir := common.CertsDir(config, domainConfig) + certsDir := common.EffectiveDataRoot(config, domainConfig) return client.DecryptAndWriteCertificates(certsDir, config, domainConfig, gitWorkspace) } func updateLinks(domain string) error { - domainConfig, exists := shared.DomainStore().Get(domain) + domainConfig, exists := app.DomainStore().Get(domain) if !exists { return fmt.Errorf("domain %s does not exist", domain) } - certsDir := shared.DomainCertsDirWConf(domain, domainConfig) + effectiveDataRoot := common.EffectiveDataRoot(app.Config(), domainConfig) + certsDir := filepath.Join(effectiveDataRoot, "certificates", domain) certLinks := domainConfig.Certificates.CertSymlinks for _, certLink := range certLinks { @@ -83,7 +84,7 @@ func updateLinks(domain string) error { keyLinks := domainConfig.Certificates.KeySymlinks for _, keyLink := range keyLinks { - err := common.LinkFile(filepath.Join(certsDir, domain+".crt"), keyLink, domain, ".key") + err := common.LinkFile(filepath.Join(certsDir, domain+".key"), keyLink, domain, ".key") if err != nil { fmt.Printf("Error linking cert %s to %s: %v", keyLink, domain, err) continue diff --git a/app/client/client.go b/app/client/client.go deleted file mode 100644 index da13c8e..0000000 --- a/app/client/client.go +++ /dev/null @@ -1 +0,0 @@ -package client diff --git a/cmd/client/commands.go b/app/client/commands.go similarity index 52% rename from cmd/client/commands.go rename to app/client/commands.go index 1d4f090..32f8f8e 100644 --- a/cmd/client/commands.go +++ b/app/client/commands.go @@ -1,18 +1,17 @@ package main import ( - "git.nevets.tech/Steven/certman/app/client" - "git.nevets.tech/Steven/certman/app/shared" + "git.nevets.tech/Steven/certman/app" "github.com/spf13/cobra" ) func init() { - shared.DaemonCmd.AddCommand(&cobra.Command{ + app.DaemonCmd.AddCommand(&cobra.Command{ Use: "start", Short: "Start the daemon", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - return shared.RunDaemonCmd(&client.Daemon{}) + return app.RunDaemonCmd(&Daemon{}) }, }) } diff --git a/app/client/daemon.go b/app/client/daemon.go index 0b0a29f..3ea4059 100644 --- a/app/client/daemon.go +++ b/app/client/daemon.go @@ -1,4 +1,4 @@ -package client +package main import ( "fmt" @@ -7,7 +7,7 @@ import ( "path/filepath" "strings" - appShared "git.nevets.tech/Steven/certman/app/shared" + "git.nevets.tech/Steven/certman/app" "git.nevets.tech/Steven/certman/client" "git.nevets.tech/Steven/certman/common" "github.com/go-git/go-billy/v5/memfs" @@ -18,7 +18,7 @@ type Daemon struct{} func (d *Daemon) Init() { fmt.Println("Starting CertManager in client mode...") - err := appShared.LoadDomainConfigs() + err := app.LoadDomainConfigs() if err != nil { log.Fatalf("Error loading domain configs: %v", err) } @@ -30,8 +30,8 @@ func (d *Daemon) Tick() { fmt.Println("tick!") // Get local copy of configs - config := appShared.Config() - localDomainConfigs := appShared.DomainStore().Snapshot() + config := app.Config() + localDomainConfigs := app.DomainStore().Snapshot() // Loop over all domain configs (domains) for domainStr, domainConfig := range localDomainConfigs { @@ -44,17 +44,12 @@ func (d *Daemon) Tick() { // If the repo doesn't exist, we can't check for a remote commit, so stop the rest of the check repoExists := domainConfig.Internal.RepoExists if repoExists { - var dataRoot string - if domainConfig.Certificates.DataRoot == "" { - config.Certificates.DataRoot = domainStr - } else { - dataRoot = domainConfig.Certificates.DataRoot - } + dataRoot := common.EffectiveDataRoot(config, domainConfig) localHash, err := client.LocalCommitHash(domainStr, dataRoot) if err != nil { fmt.Printf("No local commit hash found for domain %s\n", domainStr) } - gitSource, err := common.StrToGitSource(appShared.Config().Git.Host) + gitSource, err := common.StrToGitSource(app.Config().Git.Host) if err != nil { fmt.Printf("Error getting git source for domain %s: %v\n", domainStr, err) continue @@ -77,14 +72,15 @@ func (d *Daemon) Tick() { } // Ex: https://git.example.com/Org/Repo-suffix.git // Clones repo and stores in gitWorkspace, skip if clone fails (doesn't exist?) - repoUrl := appShared.Config().Git.Server + "/" + config.Git.OrgName + "/" + domainStr + domainConfig.Repo.RepoSuffix + ".git" + repoUrl := app.Config().Git.Server + "/" + config.Git.OrgName + "/" + domainStr + domainConfig.Repo.RepoSuffix + ".git" err := common.CloneRepo(repoUrl, gitWorkspace, common.Client, config) if err != nil { fmt.Printf("Error cloning domain repo %s: %v\n", domainStr, err) continue } - certsDir := appShared.DomainCertsDirWConf(domainStr, domainConfig) + effectiveDataRoot := common.EffectiveDataRoot(config, domainConfig) + certsDir := filepath.Join(effectiveDataRoot, "certificates", domainStr) // Get files in repo fileInfos, err := gitWorkspace.FS.ReadDir("/") @@ -156,7 +152,7 @@ func (d *Daemon) Tick() { func (d *Daemon) Reload() { fmt.Println("Reloading configs...") - err := appShared.LoadDomainConfigs() + err := app.LoadDomainConfigs() if err != nil { fmt.Printf("Error loading domain configs: %v\n", err) return diff --git a/app/client/grpc.go b/app/client/grpc.go index 1d085d1..4137beb 100644 --- a/app/client/grpc.go +++ b/app/client/grpc.go @@ -1,4 +1,4 @@ -package client +package main import ( "context" @@ -6,7 +6,7 @@ import ( "log" "time" - "git.nevets.tech/Steven/certman/app/shared" + "git.nevets.tech/Steven/certman/app" pb "git.nevets.tech/Steven/certman/proto/v1" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" @@ -23,7 +23,7 @@ func SendHook(domain string) { defer conn.Close() client := pb.NewHookServiceClient(conn) - hooks, err := shared.PostPullHooks(domain) + hooks, err := app.PostPullHooks(domain) if err != nil { fmt.Printf("Error getting hooks: %v\n", err) return diff --git a/cmd/client/main.go b/app/client/main.go similarity index 53% rename from cmd/client/main.go rename to app/client/main.go index f3a0e3e..32686c0 100644 --- a/cmd/client/main.go +++ b/app/client/main.go @@ -4,7 +4,7 @@ import ( "fmt" "os" - "git.nevets.tech/Steven/certman/app/shared" + "git.nevets.tech/Steven/certman/app" "github.com/spf13/cobra" ) @@ -18,16 +18,16 @@ func main() { }, } - rootCmd.AddCommand(shared.VersionCmd) - rootCmd.AddCommand(shared.NewKeyCmd) - rootCmd.AddCommand(shared.DevCmd) + rootCmd.AddCommand(app.VersionCmd) + rootCmd.AddCommand(app.NewKeyCmd) + rootCmd.AddCommand(app.DevCmd) - rootCmd.AddCommand(shared.NewDomainCmd) - rootCmd.AddCommand(shared.InstallCmd) + rootCmd.AddCommand(app.NewDomainCmd) + rootCmd.AddCommand(app.InstallCmd) - rootCmd.AddCommand(shared.CertCmd) + rootCmd.AddCommand(app.CertCmd) - rootCmd.AddCommand(shared.DaemonCmd) + rootCmd.AddCommand(app.DaemonCmd) if err := rootCmd.Execute(); err != nil { fmt.Fprintln(os.Stderr, err) diff --git a/app/shared/commands.go b/app/commands.go similarity index 99% rename from app/shared/commands.go rename to app/commands.go index c8f70ae..eb26be6 100644 --- a/app/shared/commands.go +++ b/app/commands.go @@ -1,4 +1,4 @@ -package shared +package app import ( "fmt" diff --git a/app/shared/config.go b/app/config.go similarity index 99% rename from app/shared/config.go rename to app/config.go index ddc0462..9b45569 100644 --- a/app/shared/config.go +++ b/app/config.go @@ -1,4 +1,4 @@ -package shared +package app import ( "errors" @@ -80,8 +80,6 @@ func Config() *common.AppConfig { } func DomainStore() *DomainConfigStore { - domainStore.mu.RLock() - defer domainStore.mu.RUnlock() return domainStore } diff --git a/app/shared/daemon.go b/app/daemon.go similarity index 99% rename from app/shared/daemon.go rename to app/daemon.go index 0a26dbd..943b15b 100644 --- a/app/shared/daemon.go +++ b/app/daemon.go @@ -1,4 +1,4 @@ -package shared +package app import ( "context" diff --git a/app/executor/commands.go b/app/executor/commands.go index 45a9f27..5c9393b 100644 --- a/app/executor/commands.go +++ b/app/executor/commands.go @@ -1,4 +1,4 @@ -package executor +package main import ( "fmt" diff --git a/app/executor/executor.go b/app/executor/executor.go index b259cc8..5add951 100644 --- a/app/executor/executor.go +++ b/app/executor/executor.go @@ -1,4 +1,4 @@ -package executor +package main import ( "fmt" diff --git a/app/executor/hook.go b/app/executor/hook.go index b46d1b0..c5990b5 100644 --- a/app/executor/hook.go +++ b/app/executor/hook.go @@ -1,4 +1,4 @@ -package executor +package main import ( "context" diff --git a/cmd/executor/main.go b/app/executor/main.go similarity index 100% rename from cmd/executor/main.go rename to app/executor/main.go diff --git a/app/executor/util.go b/app/executor/util.go index eb30ea8..1fe6a48 100644 --- a/app/executor/util.go +++ b/app/executor/util.go @@ -1,4 +1,4 @@ -package executor +package main import "fmt" diff --git a/app/server/certs.go b/app/server/certs.go index 6651342..c3f29ec 100644 --- a/app/server/certs.go +++ b/app/server/certs.go @@ -1,11 +1,11 @@ -package server +package main import ( "fmt" "path/filepath" "time" - "git.nevets.tech/Steven/certman/app/shared" + "git.nevets.tech/Steven/certman/app" "git.nevets.tech/Steven/certman/common" "git.nevets.tech/Steven/certman/server" "github.com/go-git/go-billy/v5/memfs" @@ -27,17 +27,17 @@ var ( func init() { renewCertSubCmd.Flags().BoolVar(&noPush, "no-push", false, "Don't push certs to repo, renew locally only [server mode only]") - shared.CertCmd.AddCommand(renewCertSubCmd) + app.CertCmd.AddCommand(renewCertSubCmd) } func renewCertCmd(domain string, noPush bool) error { - if err := shared.LoadConfig(); err != nil { + if err := app.LoadConfig(); err != nil { return err } - if err := shared.LoadDomainConfigs(); err != nil { + if err := app.LoadDomainConfigs(); err != nil { return err } - mgr, err := server.NewACMEManager(shared.Config()) + mgr, err := server.NewACMEManager(app.Config()) if err != nil { return err } @@ -50,8 +50,8 @@ func renewCertCmd(domain string, noPush bool) error { } func renewCerts(domain string, noPush bool, mgr *server.ACMEManager) error { - config := shared.Config() - domainConfig, exists := shared.DomainStore().Get(domain) + config := app.Config() + domainConfig, exists := app.DomainStore().Get(domain) if !exists { return fmt.Errorf("domain %s does not exist", domain) } @@ -66,7 +66,7 @@ func renewCerts(domain string, noPush bool, mgr *server.ACMEManager) error { } domainConfig.Internal.LastIssued = time.Now().UTC().Unix() - err = shared.WriteDomainConfig(domainConfig) + err = app.WriteDomainConfig(domainConfig) if err != nil { return fmt.Errorf("error saving domain config %s: %v", domain, err) } @@ -97,7 +97,7 @@ func renewCerts(domain string, noPush bool, mgr *server.ACMEManager) error { return fmt.Errorf("error creating Gitea repo for domain %s", domain) } domainConfig.Internal.RepoExists = true - err = shared.WriteDomainConfig(domainConfig) + err = app.WriteDomainConfig(domainConfig) if err != nil { return fmt.Errorf("error saving domain config %s: %v", domain, err) } diff --git a/cmd/server/commands.go b/app/server/commands.go similarity index 52% rename from cmd/server/commands.go rename to app/server/commands.go index 7c0e0c5..32f8f8e 100644 --- a/cmd/server/commands.go +++ b/app/server/commands.go @@ -1,18 +1,17 @@ package main import ( - "git.nevets.tech/Steven/certman/app/server" - "git.nevets.tech/Steven/certman/app/shared" + "git.nevets.tech/Steven/certman/app" "github.com/spf13/cobra" ) func init() { - shared.DaemonCmd.AddCommand(&cobra.Command{ + app.DaemonCmd.AddCommand(&cobra.Command{ Use: "start", Short: "Start the daemon", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - return shared.RunDaemonCmd(&server.Daemon{}) + return app.RunDaemonCmd(&Daemon{}) }, }) } diff --git a/app/server/daemon.go b/app/server/daemon.go index 140f5f8..3ff3960 100644 --- a/app/server/daemon.go +++ b/app/server/daemon.go @@ -1,13 +1,15 @@ -package server +package main import ( + "errors" "fmt" "log" + "os" "path/filepath" "sync" "time" - appShared "git.nevets.tech/Steven/certman/app/shared" + appShared "git.nevets.tech/Steven/certman/app" "git.nevets.tech/Steven/certman/common" "git.nevets.tech/Steven/certman/server" "github.com/go-git/go-billy/v5/memfs" @@ -63,6 +65,7 @@ func (d *Daemon) Tick() { if !domainConfig.Domain.Enabled { continue } + //TODO: have renewPeriod logic default to use certificate expiry if available renewPeriod := domainConfig.Certificates.RenewPeriod lastIssued := time.Unix(domainConfig.Internal.LastIssued, 0).UTC() renewalDue := lastIssued.AddDate(0, 0, renewPeriod) @@ -70,12 +73,16 @@ func (d *Daemon) Tick() { //TODO extra check if certificate expiry (create cache?) _, err := d.ACMEManager.RenewForDomain(domainStr) if err != nil { - // if no existing cert, obtain instead - _, err = d.ACMEManager.ObtainForDomain(domainStr, appShared.Config(), domainConfig) - if err != nil { - fmt.Printf("Error obtaining domain certificates for domain %s: %v\n", domainStr, err) - continue + if errors.Is(err, os.ErrNotExist) { + // if no existing cert, obtain instead + _, err = d.ACMEManager.ObtainForDomain(domainStr, appShared.Config(), domainConfig) + if err != nil { + fmt.Printf("Error obtaining domain certificates for domain %s: %v\n", domainStr, err) + continue + } } + fmt.Printf("Error: %v\n", err) + continue } domainConfig.Internal.LastIssued = time.Now().UTC().Unix() diff --git a/cmd/server/main.go b/app/server/main.go similarity index 100% rename from cmd/server/main.go rename to app/server/main.go diff --git a/app/server/server.go b/app/server/server.go deleted file mode 100644 index abb4e43..0000000 --- a/app/server/server.go +++ /dev/null @@ -1 +0,0 @@ -package server diff --git a/app/shared/install.go b/app/shared/install.go deleted file mode 100644 index a29b5e4..0000000 --- a/app/shared/install.go +++ /dev/null @@ -1 +0,0 @@ -package shared diff --git a/app/shared/util.go b/app/util.go similarity index 70% rename from app/shared/util.go rename to app/util.go index 3ddbabe..d101f41 100644 --- a/app/shared/util.go +++ b/app/util.go @@ -1,11 +1,9 @@ -package shared +package app import ( "fmt" "os" - "path/filepath" - "git.nevets.tech/Steven/certman/common" "github.com/spf13/cobra" ) @@ -51,18 +49,6 @@ func createFile(fileName string, filePermission os.FileMode, data []byte) { } } -// DomainCertsDirWConf Can return ErrBlankConfigEntry or other errors -func DomainCertsDirWConf(domain string, domainConfig *common.DomainConfig) string { - var effectiveDataRoot string - if domainConfig.Certificates.DataRoot == "" { - effectiveDataRoot = config.Certificates.DataRoot - } else { - effectiveDataRoot = domainConfig.Certificates.DataRoot - } - - return filepath.Join(effectiveDataRoot, "certificates", domain) -} - func basicCmd(use, short string, commandFunc func(cmd *cobra.Command, args []string)) *cobra.Command { return &cobra.Command{ Use: use, diff --git a/client/git.go b/client/git.go index be84706..ef39cae 100644 --- a/client/git.go +++ b/client/git.go @@ -10,6 +10,23 @@ import ( "git.nevets.tech/Steven/certman/common" ) +func WriteCommitHash(hash, dataRoot string) error { + //TODO: unfuck this logic, maybe use a domain struct with a flag for non-standard data root? + //var dataRoot string + //if domainConfig.Certificates.DataRoot == "" { + // dataRoot = filepath.Join(config.Certificates.DataRoot, "certificates", domainConfig.Domain.DomainName) + //} else { + // dataRoot = domainConfig.Certificates.DataRoot + //} + + err := os.WriteFile(filepath.Join(dataRoot, "hash"), []byte(hash), 0644) + if err != nil { + return err + } + + return nil +} + func LocalCommitHash(domain string, certsDir string) (string, error) { data, err := os.ReadFile(filepath.Join(certsDir, "hash")) if err != nil { diff --git a/common/git.go b/common/git.go index 6bfdc15..e2ea943 100644 --- a/common/git.go +++ b/common/git.go @@ -5,15 +5,12 @@ import ( "fmt" "io" "os" - "path/filepath" "strings" - "time" "code.gitea.io/sdk/gitea" "github.com/go-git/go-billy/v5" "github.com/go-git/go-git/v5" gitconf "github.com/go-git/go-git/v5/config" - "github.com/go-git/go-git/v5/plumbing/object" "github.com/go-git/go-git/v5/plumbing/transport/http" "github.com/go-git/go-git/v5/storage/memory" ) @@ -27,6 +24,7 @@ const ( type GitWorkspace struct { Domain string + URL string Repo *git.Repository Storage *memory.Storage FS billy.Filesystem @@ -121,7 +119,7 @@ func CreateGiteaRepo(domain string, giteaClient *gitea.Client, config *AppConfig return giteaRepo.CloneURL } -func InitRepo(url string, ws *GitWorkspace) error { +func (ws *GitWorkspace) InitRepo() error { var err error ws.Repo, err = git.Init(ws.Storage, ws.FS) if err != nil { @@ -131,7 +129,7 @@ func InitRepo(url string, ws *GitWorkspace) error { _, err = ws.Repo.CreateRemote(&gitconf.RemoteConfig{ Name: "origin", - URLs: []string{url}, + URLs: []string{ws.URL}, }) if err != nil && !errors.Is(err, git.ErrRemoteExists) { fmt.Printf("Error creating remote origin repo: %v\n", err) @@ -147,13 +145,13 @@ func InitRepo(url string, ws *GitWorkspace) error { return nil } -func CloneRepo(url string, ws *GitWorkspace, certmanMode CertManMode, config *AppConfig) error { +func (ws *GitWorkspace) CloneRepo(certmanMode CertManMode, config *AppConfig) error { creds := &http.BasicAuth{ Username: config.Git.Username, Password: config.Git.APIToken, } var err error - ws.Repo, err = git.Clone(ws.Storage, ws.FS, &git.CloneOptions{URL: url, Auth: creds}) + ws.Repo, err = git.Clone(ws.Storage, ws.FS, &git.CloneOptions{URL: ws.URL, Auth: creds}) if err != nil { fmt.Printf("Error cloning repo: %v\n", err) } @@ -167,7 +165,7 @@ func CloneRepo(url string, ws *GitWorkspace, certmanMode CertManMode, config *Ap serverIdFile, err := ws.FS.OpenFile("/SERVER_ID", os.O_RDWR, 0640) if err != nil { if os.IsNotExist(err) { - fmt.Printf("server ID file not found for %s, adopting domain\n", url) + fmt.Printf("server ID file not found for %s, adopting domain\n", ws.URL) return nil } return err @@ -183,129 +181,3 @@ func CloneRepo(url string, ws *GitWorkspace, certmanMode CertManMode, config *Ap } return nil } - -func AddAndPushCerts(domain string, ws *GitWorkspace, config *AppConfig, domainConfig *DomainConfig) error { - var dataRoot string - if domainConfig.Certificates.DataRoot == "" { - dataRoot = config.Certificates.DataRoot - } else { - dataRoot = domainConfig.Certificates.DataRoot - } - certFiles, err := os.ReadDir(dataRoot) - if err != nil { - fmt.Printf("Error reading from directory: %v\n", err) - return err - } - for _, entry := range certFiles { - if strings.HasSuffix(entry.Name(), ".crpt") { - file, err := ws.FS.Create(entry.Name()) - if err != nil { - fmt.Printf("Error copying file to memfs: %v\n", err) - return err - } - certFile, err := os.ReadFile(filepath.Join(dataRoot, entry.Name())) - if err != nil { - fmt.Printf("Error reading file to memfs: %v\n", err) - file.Close() - return err - } - _, err = file.Write(certFile) - if err != nil { - fmt.Printf("Error writing to memfs: %v\n", err) - file.Close() - return err - } - _, err = ws.WorkTree.Add(file.Name()) - if err != nil { - fmt.Printf("Error adding file %v: %v\n", file.Name(), err) - file.Close() - return err - } - err = file.Close() - if err != nil { - fmt.Printf("Error closing file: %v\n", err) - } - } - - file, err := ws.FS.Create("/SERVER_ID") - if err != nil { - fmt.Printf("Error creating file in memfs: %v\n", err) - return err - } - _, err = file.Write([]byte(config.App.UUID)) - if err != nil { - fmt.Printf("Error writing to memfs: %v\n", err) - file.Close() - return err - } - _, err = ws.WorkTree.Add(file.Name()) - if err != nil { - fmt.Printf("Error adding file %v: %v\n", file.Name(), err) - file.Close() - return err - } - err = file.Close() - if err != nil { - fmt.Printf("Error closing file: %v\n", err) - } - - } - - status, err := ws.WorkTree.Status() - if err != nil { - fmt.Printf("Error getting repo status: %v\n", err) - return err - } - if status.IsClean() { - fmt.Printf("Repository is clean, skipping commit...\n") - return nil - } - - fmt.Println("Work Tree Status:\n" + status.String()) - signature := &object.Signature{ - Name: "Cert Manager", - Email: config.Certificates.Email, - When: time.Now(), - } - _, err = ws.WorkTree.Commit("Update "+domain+" @ "+time.Now().Format("Mon Jan _2 2006 15:04:05 MST"), &git.CommitOptions{Author: signature, Committer: signature}) - if err != nil { - fmt.Printf("Error committing certs: %v\n", err) - return err - } - creds := &http.BasicAuth{ - Username: config.Git.Username, - Password: config.Git.APIToken, - } - err = ws.Repo.Push(&git.PushOptions{ - Auth: creds, - Force: true, - RemoteName: "origin", - RefSpecs: []gitconf.RefSpec{ - "refs/heads/master:refs/heads/master", - }, - }) - if err != nil { - fmt.Printf("Error pushing to origin: %v\n", err) - return err - } - - fmt.Println("Successfully uploaded to " + config.Git.Server + "/" + config.Git.OrgName + "/" + domain + domainConfig.Repo.RepoSuffix + ".git") - - return nil -} - -func WriteCommitHash(hash string, config *AppConfig, domainConfig *DomainConfig) error { - var dataRoot string - if domainConfig.Certificates.DataRoot == "" { - dataRoot = filepath.Join(config.Certificates.DataRoot, "certificates", domainConfig.Domain.DomainName) - } else { - dataRoot = domainConfig.Certificates.DataRoot - } - - err := os.WriteFile(filepath.Join(dataRoot, "hash"), []byte(hash), 0644) - if err != nil { - return err - } - - return nil -} diff --git a/common/util.go b/common/util.go index 9755675..e0a798f 100644 --- a/common/util.go +++ b/common/util.go @@ -293,7 +293,7 @@ func MakeCredential(username, groupname string) (*syscall.Credential, error) { return &syscall.Credential{Uid: uid, Gid: gid}, nil } -func CertsDir(config *AppConfig, domainConfig *DomainConfig) string { +func EffectiveDataRoot(config *AppConfig, domainConfig *DomainConfig) string { if config == nil { return "" } diff --git a/server/acme_manager.go b/server/acme_manager.go index aa0276a..36c1eaf 100644 --- a/server/acme_manager.go +++ b/server/acme_manager.go @@ -242,12 +242,7 @@ func buildDomainRuntimeConfig(config *common.AppConfig, domainConfig *common.Dom email := config.Certificates.Email // domain override data_root can be blank -> main fallback - var dataRoot string - if domainConfig.Certificates.DataRoot == "" { - dataRoot = config.Certificates.DataRoot - } else { - dataRoot = domainConfig.Certificates.DataRoot - } + dataRoot := common.EffectiveDataRoot(config, domainConfig) caDirURL := config.Certificates.CADirURL @@ -584,6 +579,9 @@ func (m *ACMEManager) loadStoredResource(domainKey string) (*certificate.Resourc dir := filepath.Join(m.CertsRoot, base) raw, err := os.ReadFile(filepath.Join(dir, base+".json")) if err != nil { + if errors.Is(err, os.ErrNotExist) { + return nil, os.ErrNotExist + } return nil, err } diff --git a/server/git.go b/server/git.go index abb4e43..2a1f2ad 100644 --- a/server/git.go +++ b/server/git.go @@ -1 +1,120 @@ package server + +import ( + "fmt" + "os" + "path/filepath" + "strings" + "time" + + "git.nevets.tech/Steven/certman/common" + "github.com/go-git/go-git/v5" + gitconf "github.com/go-git/go-git/v5/config" + "github.com/go-git/go-git/v5/plumbing/object" + "github.com/go-git/go-git/v5/plumbing/transport/http" +) + +type GitWorkspace common.GitWorkspace + +func (ws *GitWorkspace) AddAndPushCerts(dataRoot, repoSuffix string, config *common.AppConfig) error { + certFiles, err := os.ReadDir(dataRoot) + if err != nil { + fmt.Printf("Error reading from directory: %v\n", err) + return err + } + for _, entry := range certFiles { + if strings.HasSuffix(entry.Name(), ".crpt") { + file, err := ws.FS.Create(entry.Name()) + if err != nil { + fmt.Printf("Error copying file to memfs: %v\n", err) + return err + } + certFile, err := os.ReadFile(filepath.Join(dataRoot, entry.Name())) + if err != nil { + fmt.Printf("Error reading file to memfs: %v\n", err) + file.Close() + return err + } + _, err = file.Write(certFile) + if err != nil { + fmt.Printf("Error writing to memfs: %v\n", err) + file.Close() + return err + } + _, err = ws.WorkTree.Add(file.Name()) + if err != nil { + fmt.Printf("Error adding file %v: %v\n", file.Name(), err) + file.Close() + return err + } + err = file.Close() + if err != nil { + fmt.Printf("Error closing file: %v\n", err) + } + } + } + + file, err := ws.FS.Create("/SERVER_ID") + if err != nil { + fmt.Printf("Error creating file in memfs: %v\n", err) + return err + } + _, err = file.Write([]byte(config.App.UUID)) + if err != nil { + fmt.Printf("Error writing to memfs: %v\n", err) + file.Close() + return err + } + _, err = ws.WorkTree.Add(file.Name()) + if err != nil { + fmt.Printf("Error adding file %v: %v\n", file.Name(), err) + file.Close() + return err + } + err = file.Close() + if err != nil { + fmt.Printf("Error closing file: %v\n", err) + } + + status, err := ws.WorkTree.Status() + if err != nil { + fmt.Printf("Error getting repo status: %v\n", err) + return err + } + if status.IsClean() { + fmt.Printf("Repository is clean, skipping commit...\n") + return nil + } + + fmt.Println("Work Tree Status:\n" + status.String()) + signature := &object.Signature{ + Name: "Cert Manager", + Email: config.Certificates.Email, + When: time.Now(), + } + _, err = ws.WorkTree.Commit("Update "+ws.Domain+" @ "+time.Now().Format("Mon Jan _2 2006 15:04:05 MST"), &git.CommitOptions{Author: signature, Committer: signature}) + if err != nil { + fmt.Printf("Error committing certs: %v\n", err) + return err + } + creds := &http.BasicAuth{ + Username: config.Git.Username, + Password: config.Git.APIToken, + } + err = ws.Repo.Push(&git.PushOptions{ + Auth: creds, + Force: true, + RemoteName: "origin", + RefSpecs: []gitconf.RefSpec{ + "refs/heads/master:refs/heads/master", + }, + }) + if err != nil { + fmt.Printf("Error pushing to origin: %v\n", err) + return err + } + + fmt.Println("Successfully uploaded to " + config.Git.Server + "/" + config.Git.OrgName + "/" + ws.Domain + repoSuffix + ".git") + + return nil +}