diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index fc5c3c9..5c343b6 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -44,7 +44,7 @@ jobs: run: echo "COMMIT_MSG=$(git log -1 --pretty=%s)" >> $GITHUB_ENV - name: Build - run: make build + run: make client server - name: Upload artifact uses: https://github.com/actions/upload-artifact@v3 diff --git a/Makefile b/Makefile index ae36868..5a84d12 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -VERSION := 1.1.5-beta +VERSION := 1.1.6-beta BUILD := $(shell git rev-parse --short HEAD) GO := go @@ -6,7 +6,7 @@ GO := go BUILD_FLAGS := -buildmode=pie -trimpath LDFLAGS := -linkmode=external -extldflags="-Wl,-z,relro,-z,now" -X git.nevets.tech/Keys/certman/common.Version=$(VERSION) -X git.nevets.tech/Keys/certman/common.Build=$(BUILD) -.PHONY: proto bundle client server executor build debug stage +.PHONY: proto bundle client server executor build debug stage help proto: @protoc --go_out=./proto --go-grpc_out=./proto proto/hook.proto @@ -37,4 +37,14 @@ debug: proto stage: build @sudo cp ./certman /srv/vm-passthru/certman - @ssh steven@192.168.122.44 updateCertman.sh \ No newline at end of file + @ssh steven@192.168.122.44 updateCertman.sh + +help: + @echo "proto - Generate gRPC proto stubs" + @echo "bundle - Build bundled binary (client, server, executor)" + @echo "client - Build client binary" + @echo "server - Build server binary" + @echo "executor - Build executor binary" + @echo "build - Build all binaries" + @echo "debug - Build bundled binary without stripping and hardening" + @echo "stage - Build all binaries and upload to dev environment (vm)" \ No newline at end of file diff --git a/README.md b/README.md index 74cf50e..d22ce82 100644 --- a/README.md +++ b/README.md @@ -29,4 +29,7 @@ sudo certman new-domain example.com ## Scratch Board - Server Flow - - Read from \ No newline at end of file + - Read from + +# Coding Conventions +- Keep Config() calls in app space \ No newline at end of file diff --git a/app/client/certs.go b/app/client/certs.go index a12edef..ace4482 100644 --- a/app/client/certs.go +++ b/app/client/certs.go @@ -47,17 +47,19 @@ func init() { } func renewCert(domain string) error { - gitWorkspace := &common.GitWorkspace{ - Domain: domain, - Storage: memory.NewStorage(), - FS: memfs.New(), - } config := app.Config() domainConfig, exists := app.DomainStore().Get(domain) if !exists { return app.ErrConfigNotFound } - if err := client.PullCerts(config, domainConfig, gitWorkspace); err != nil { + + gitWorkspace := &common.GitWorkspace{ + Domain: domain, + URL: config.Git.Server + "/" + config.Git.OrgName + "/" + domain + domainConfig.Repo.RepoSuffix + ".git", + Storage: memory.NewStorage(), + FS: memfs.New(), + } + if err := client.PullCerts(config, gitWorkspace); err != nil { return err } certsDir := common.EffectiveDataRoot(config, domainConfig) diff --git a/app/client/daemon.go b/app/client/daemon.go index 3ea4059..086926f 100644 --- a/app/client/daemon.go +++ b/app/client/daemon.go @@ -67,13 +67,14 @@ func (d *Daemon) Tick() { } gitWorkspace := &common.GitWorkspace{ + Domain: domainStr, + URL: app.Config().Git.Server + "/" + config.Git.OrgName + "/" + domainStr + domainConfig.Repo.RepoSuffix + ".git", Storage: memory.NewStorage(), FS: memfs.New(), } // Ex: https://git.example.com/Org/Repo-suffix.git // Clones repo and stores in gitWorkspace, skip if clone fails (doesn't exist?) - repoUrl := app.Config().Git.Server + "/" + config.Git.OrgName + "/" + domainStr + domainConfig.Repo.RepoSuffix + ".git" - err := common.CloneRepo(repoUrl, gitWorkspace, common.Client, config) + err := gitWorkspace.CloneRepo(common.Client, config) if err != nil { fmt.Printf("Error cloning domain repo %s: %v\n", domainStr, err) continue @@ -121,7 +122,8 @@ func (d *Daemon) Tick() { continue } - err = common.WriteCommitHash(headRef.Hash().String(), config, domainConfig) + dataRoot := common.EffectiveDataRoot(config, domainConfig) + err = client.WriteCommitHash(headRef.Hash().String(), dataRoot) if err != nil { fmt.Printf("Error writing commit hash: %v\n", err) continue diff --git a/app/commands.go b/app/commands.go index eb26be6..8464c7d 100644 --- a/app/commands.go +++ b/app/commands.go @@ -55,17 +55,17 @@ func init() { } func devCmd(cmd *cobra.Command, args []string) { - testDomain := "lunamc.org" - err := LoadConfig() - if err != nil { - log.Fatalf("Error loading configuration: %v\n", err) - } - err = LoadDomainConfigs() - if err != nil { - log.Fatalf("Error loading configs: %v\n", err) - } - - fmt.Println(testDomain) + //testDomain := "lunamc.org" + //err := LoadConfig() + //if err != nil { + // log.Fatalf("Error loading configuration: %v\n", err) + //} + //err = LoadDomainConfigs() + //if err != nil { + // log.Fatalf("Error loading configs: %v\n", err) + //} + // + //fmt.Println(testDomain) } func versionCmd(cmd *cobra.Command, args []string) { diff --git a/app/server/certs.go b/app/server/certs.go index c3f29ec..69a9a7f 100644 --- a/app/server/certs.go +++ b/app/server/certs.go @@ -90,10 +90,9 @@ func renewCerts(domain string, noPush bool, mgr *server.ACMEManager) error { FS: memfs.New(), } - var repoUrl string if !domainConfig.Internal.RepoExists { - repoUrl = common.CreateGiteaRepo(domain, giteaClient, config, domainConfig) - if repoUrl == "" { + gitWorkspace.URL = common.CreateGiteaRepo(domain, giteaClient, config, domainConfig) + if gitWorkspace.URL == "" { return fmt.Errorf("error creating Gitea repo for domain %s", domain) } domainConfig.Internal.RepoExists = true @@ -102,19 +101,20 @@ func renewCerts(domain string, noPush bool, mgr *server.ACMEManager) error { return fmt.Errorf("error saving domain config %s: %v", domain, err) } - err = common.InitRepo(repoUrl, gitWorkspace) + err = gitWorkspace.InitRepo() if err != nil { return fmt.Errorf("error initializing repo for domain %s: %v", domain, err) } } else { - repoUrl = config.Git.Server + "/" + config.Git.OrgName + "/" + domain + domainConfig.Repo.RepoSuffix + ".git" - err = common.CloneRepo(repoUrl, gitWorkspace, common.Server, config) + gitWorkspace.URL = config.Git.Server + "/" + config.Git.OrgName + "/" + domain + domainConfig.Repo.RepoSuffix + ".git" + err = gitWorkspace.CloneRepo(common.Server, config) if err != nil { return fmt.Errorf("error cloning repo for domain %s: %v", domain, err) } } - err = common.AddAndPushCerts(domain, gitWorkspace, config, domainConfig) + dataRoot := common.EffectiveDataRoot(config, domainConfig) + err = server.AddAndPushCerts(gitWorkspace, dataRoot, domainConfig.Repo.RepoSuffix, config) if err != nil { return fmt.Errorf("error pushing certificates for domain %s: %v", domain, err) } diff --git a/app/server/daemon.go b/app/server/daemon.go index 3ff3960..34fc1e3 100644 --- a/app/server/daemon.go +++ b/app/server/daemon.go @@ -113,10 +113,9 @@ func (d *Daemon) Tick() { FS: memfs.New(), } - var repoUrl string if !domainConfig.Internal.RepoExists { - repoUrl = common.CreateGiteaRepo(domainStr, giteaClient, config, domainConfig) - if repoUrl == "" { + gitWorkspace.URL = common.CreateGiteaRepo(domainStr, giteaClient, config, domainConfig) + if gitWorkspace.URL == "" { fmt.Printf("Error creating Gitea repo for domain %s\n", domainStr) continue } @@ -127,21 +126,22 @@ func (d *Daemon) Tick() { continue } - err = common.InitRepo(repoUrl, gitWorkspace) + err = gitWorkspace.InitRepo() if err != nil { fmt.Printf("Error initializing repo for domain %s: %v\n", domainStr, err) continue } } else { - repoUrl = appShared.Config().Git.Server + "/" + appShared.Config().Git.OrgName + "/" + domainStr + domainConfig.Repo.RepoSuffix + ".git" - err = common.CloneRepo(repoUrl, gitWorkspace, common.Server, config) + gitWorkspace.URL = appShared.Config().Git.Server + "/" + appShared.Config().Git.OrgName + "/" + domainStr + domainConfig.Repo.RepoSuffix + ".git" + err = gitWorkspace.CloneRepo(common.Server, config) if err != nil { fmt.Printf("Error cloning repo for domain %s: %v\n", domainStr, err) continue } } - err = common.AddAndPushCerts(domainStr, gitWorkspace, config, domainConfig) + dataRoot := common.EffectiveDataRoot(config, domainConfig) + err = server.AddAndPushCerts(gitWorkspace, dataRoot, domainConfig.Repo.RepoSuffix, config) if err != nil { fmt.Printf("Error pushing certificates for domain %s: %v\n", domainStr, err) continue diff --git a/app/server/main.go b/app/server/main.go index f7d1436..6e4d8d2 100644 --- a/app/server/main.go +++ b/app/server/main.go @@ -1,7 +1,37 @@ package main -import "fmt" +import ( + "fmt" + "os" + + "git.nevets.tech/Steven/certman/app" + "github.com/spf13/cobra" +) func main() { - fmt.Println("Hello server") + rootCmd := &cobra.Command{ + Use: "certman", + Short: "CertMan", + Long: "Certificate Manager", + RunE: func(cmd *cobra.Command, args []string) error { + return cmd.Help() + }, + } + + rootCmd.AddCommand(app.VersionCmd) + rootCmd.AddCommand(app.NewKeyCmd) + rootCmd.AddCommand(app.DevCmd) + + rootCmd.AddCommand(app.NewDomainCmd) + //----------------------------------- + rootCmd.AddCommand(app.InstallCmd) + + rootCmd.AddCommand(app.CertCmd) + + rootCmd.AddCommand(app.DaemonCmd) + + if err := rootCmd.Execute(); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } } diff --git a/client/certificates.go b/client/certificates.go index e39b099..96b14b7 100644 --- a/client/certificates.go +++ b/client/certificates.go @@ -10,11 +10,10 @@ import ( "git.nevets.tech/Steven/certman/common" ) -func PullCerts(config *common.AppConfig, domainConfig *common.DomainConfig, gitWorkspace *common.GitWorkspace) error { +func PullCerts(config *common.AppConfig, gitWorkspace *common.GitWorkspace) error { // Ex: https://git.example.com/Org/Repo-suffix.git // Clones repo and stores in gitWorkspace, skip if clone fails (doesn't exist?) - repoUrl := config.Git.Server + "/" + config.Git.OrgName + "/" + gitWorkspace.Domain + domainConfig.Repo.RepoSuffix + ".git" - err := common.CloneRepo(repoUrl, gitWorkspace, common.Client, config) + err := gitWorkspace.CloneRepo(common.Client, config) if err != nil { return fmt.Errorf("Error cloning domain repo %s: %v\n", gitWorkspace.Domain, err) } @@ -60,7 +59,8 @@ func DecryptAndWriteCertificates(certsDir string, config *common.AppConfig, doma continue } - err = common.WriteCommitHash(headRef.Hash().String(), config, domainConfig) + dataRoot := common.EffectiveDataRoot(config, domainConfig) + err = WriteCommitHash(headRef.Hash().String(), dataRoot) if err != nil { fmt.Printf("Error writing commit hash: %v\n", err) continue diff --git a/server/acme_manager.go b/server/acme_manager.go index 36c1eaf..755a026 100644 --- a/server/acme_manager.go +++ b/server/acme_manager.go @@ -143,7 +143,7 @@ func NewACMEManager(config *common.AppConfig) (*ACMEManager, error) { return nil, fmt.Errorf("cloudflare dns provider: %w", err) } - if err := client.Challenge.SetDNS01Provider(cfProvider); err != nil { + if err = client.Challenge.SetDNS01Provider(cfProvider); err != nil { return nil, fmt.Errorf("set dns-01 provider: %w", err) } @@ -159,7 +159,7 @@ func NewACMEManager(config *common.AppConfig) (*ACMEManager, error) { return nil, fmt.Errorf("acme registration: %w", err) } mgr.User.Registration = reg - if err := saveACMEUser(mgr.accountRoot, mgr.User); err != nil { + if err = saveACMEUser(mgr.accountRoot, mgr.User); err != nil { return nil, fmt.Errorf("save acme User registration: %w", err) } } @@ -270,6 +270,7 @@ func buildDomainRuntimeConfig(config *common.AppConfig, domainConfig *common.Dom // If a subdomain entry looks like a full FQDN already, it is used as-is. func buildDomainList(baseDomain string, subs []string) []string { seen := map[string]struct{}{} + out := make([]string, 0, len(subs)+1) add := func(d string) { d = strings.TrimSpace(strings.ToLower(d)) if d == "" { @@ -279,6 +280,7 @@ func buildDomainList(baseDomain string, subs []string) []string { return } seen[d] = struct{}{} + out = append(out, d) } add(baseDomain) @@ -310,10 +312,6 @@ func buildDomainList(baseDomain string, subs []string) []string { add(s + "." + baseDomain) } - out := make([]string, 0, len(seen)) - for d := range seen { - out = append(out, d) - } return out } diff --git a/server/acme_manager_test.go b/server/acme_manager_test.go new file mode 100644 index 0000000..51235be --- /dev/null +++ b/server/acme_manager_test.go @@ -0,0 +1,15 @@ +package server + +import ( + "fmt" + "slices" + "testing" +) + +func TestBuildDomainList(t *testing.T) { + domains := buildDomainList("example.com", []string{"*", "dev"}) + fmt.Printf("domains: %v\n", domains) + if slices.Compare(domains, []string{"example.com", "*.example.com", "dev.example.com"}) != 0 { + t.Errorf("domains not equal") + } +} diff --git a/server/git.go b/server/git.go index 2a1f2ad..698438c 100644 --- a/server/git.go +++ b/server/git.go @@ -14,9 +14,7 @@ import ( "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 { +func AddAndPushCerts(ws *common.GitWorkspace, dataRoot, repoSuffix string, config *common.AppConfig) error { certFiles, err := os.ReadDir(dataRoot) if err != nil { fmt.Printf("Error reading from directory: %v\n", err)