169 lines
4.7 KiB
Go
169 lines
4.7 KiB
Go
package server
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"path/filepath"
|
|
"sync"
|
|
"time"
|
|
|
|
appShared "git.nevets.tech/Keys/certman/app/shared"
|
|
"git.nevets.tech/Keys/certman/common"
|
|
"git.nevets.tech/Keys/certman/server"
|
|
"github.com/go-git/go-billy/v5/memfs"
|
|
"github.com/go-git/go-git/v5/storage/memory"
|
|
)
|
|
|
|
type Daemon struct {
|
|
ACMEManager *server.ACMEManager
|
|
TickMu sync.Mutex
|
|
MgrMu sync.Mutex
|
|
}
|
|
|
|
func (d *Daemon) loadACMEManager() error {
|
|
d.MgrMu.Lock()
|
|
defer d.MgrMu.Unlock()
|
|
|
|
if d.ACMEManager == nil {
|
|
var err error
|
|
d.ACMEManager, err = server.NewACMEManager(appShared.Config())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (d *Daemon) Init() {
|
|
fmt.Println("Starting CertManager in server mode...")
|
|
err := appShared.LoadDomainConfigs()
|
|
if err != nil {
|
|
log.Fatalf("Error loading domain configs: %v", err)
|
|
}
|
|
|
|
d.Tick()
|
|
}
|
|
|
|
func (d *Daemon) Tick() {
|
|
d.TickMu.Lock()
|
|
defer d.TickMu.Unlock()
|
|
fmt.Println("Tick!")
|
|
|
|
if err := d.loadACMEManager(); err != nil {
|
|
fmt.Printf("Error getting acme manager: %v\n", err)
|
|
return
|
|
}
|
|
|
|
now := time.Now().UTC()
|
|
|
|
config := appShared.Config()
|
|
localDomainConfigs := appShared.DomainStore().Snapshot()
|
|
|
|
for domainStr, domainConfig := range localDomainConfigs {
|
|
if !domainConfig.Domain.Enabled {
|
|
continue
|
|
}
|
|
renewPeriod := domainConfig.Certificates.RenewPeriod
|
|
lastIssued := time.Unix(domainConfig.Internal.LastIssued, 0).UTC()
|
|
renewalDue := lastIssued.AddDate(0, 0, renewPeriod)
|
|
if now.After(renewalDue) {
|
|
//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
|
|
}
|
|
}
|
|
|
|
domainConfig.Internal.LastIssued = time.Now().UTC().Unix()
|
|
err = appShared.WriteDomainConfig(domainConfig)
|
|
if err != nil {
|
|
fmt.Printf("Error saving domain config %s: %v\n", domainStr, err)
|
|
continue
|
|
}
|
|
|
|
err = common.EncryptFileXChaCha(domainConfig.Certificates.CryptoKey, filepath.Join(d.ACMEManager.CertsRoot, domainStr, domainStr+".crt"), filepath.Join(d.ACMEManager.CertsRoot, domainStr, domainStr+".crt.crpt"), nil)
|
|
if err != nil {
|
|
fmt.Printf("Error encrypting domain cert for domain %s: %v\n", domainStr, err)
|
|
continue
|
|
}
|
|
err = common.EncryptFileXChaCha(domainConfig.Certificates.CryptoKey, filepath.Join(d.ACMEManager.CertsRoot, domainStr, domainStr+".key"), filepath.Join(d.ACMEManager.CertsRoot, domainStr, domainStr+".key.crpt"), nil)
|
|
if err != nil {
|
|
fmt.Printf("Error encrypting domain key for domain %s: %v\n", domainStr, err)
|
|
continue
|
|
}
|
|
|
|
giteaClient := common.CreateGiteaClient(config)
|
|
if giteaClient == nil {
|
|
fmt.Printf("Error creating gitea client for domain %s: %v\n", domainStr, err)
|
|
continue
|
|
}
|
|
gitWorkspace := &common.GitWorkspace{
|
|
Storage: memory.NewStorage(),
|
|
FS: memfs.New(),
|
|
}
|
|
|
|
var repoUrl string
|
|
if !domainConfig.Internal.RepoExists {
|
|
repoUrl = common.CreateGiteaRepo(domainStr, giteaClient, config, domainConfig)
|
|
if repoUrl == "" {
|
|
fmt.Printf("Error creating Gitea repo for domain %s\n", domainStr)
|
|
continue
|
|
}
|
|
domainConfig.Internal.RepoExists = true
|
|
err = appShared.WriteDomainConfig(domainConfig)
|
|
if err != nil {
|
|
fmt.Printf("Error saving domain config %s: %v\n", domainStr, err)
|
|
continue
|
|
}
|
|
|
|
err = common.InitRepo(repoUrl, gitWorkspace)
|
|
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)
|
|
if err != nil {
|
|
fmt.Printf("Error cloning repo for domain %s: %v\n", domainStr, err)
|
|
continue
|
|
}
|
|
}
|
|
|
|
err = common.AddAndPushCerts(domainStr, gitWorkspace, config, domainConfig)
|
|
if err != nil {
|
|
fmt.Printf("Error pushing certificates for domain %s: %v\n", domainStr, err)
|
|
continue
|
|
}
|
|
fmt.Printf("Successfully pushed certificates for domain %s\n", domainStr)
|
|
}
|
|
}
|
|
if err := appShared.SaveDomainConfigs(); err != nil {
|
|
fmt.Printf("Error saving domain configs: %v\n", err)
|
|
}
|
|
}
|
|
|
|
func (d *Daemon) Reload() {
|
|
fmt.Println("Reloading configs...")
|
|
err := appShared.LoadDomainConfigs()
|
|
|
|
if err != nil {
|
|
fmt.Printf("Error loading domain configs: %v\n", err)
|
|
return
|
|
}
|
|
|
|
d.MgrMu.Lock()
|
|
d.ACMEManager = nil
|
|
d.MgrMu.Unlock()
|
|
|
|
fmt.Println("Successfully reloaded configs")
|
|
}
|
|
|
|
func (d *Daemon) Stop() {
|
|
fmt.Println("Shutting down server")
|
|
}
|