diff --git a/.gitignore b/.gitignore index ffc6e67..cb8c2a8 100644 --- a/.gitignore +++ b/.gitignore @@ -126,4 +126,5 @@ $RECYCLE.BIN/ # Windows shortcuts *.lnk -config.ini \ No newline at end of file +config.ini +certman diff --git a/build.bat b/build.bat new file mode 100644 index 0000000..652e9ae --- /dev/null +++ b/build.bat @@ -0,0 +1,5 @@ +@echo off +set GOARCH=amd64 +set GOOS=linux + +go build -o ./certman . \ No newline at end of file diff --git a/certs.go b/certs.go new file mode 100644 index 0000000..f5d0829 --- /dev/null +++ b/certs.go @@ -0,0 +1,96 @@ +package main + +import ( + "crypto" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "fmt" + "github.com/go-acme/lego/v4/providers/dns/cloudflare" + "log" + "os" + + "github.com/go-acme/lego/v4/certcrypto" + "github.com/go-acme/lego/v4/certificate" + "github.com/go-acme/lego/v4/lego" + "github.com/go-acme/lego/v4/registration" +) + +type User struct { + Email string + Registration *registration.Resource + key crypto.PrivateKey +} + +func (u *User) GetEmail() string { + return u.Email +} +func (u User) GetRegistration() *registration.Resource { + return u.Registration +} +func (u *User) GetPrivateKey() crypto.PrivateKey { + return u.key +} + +func mainexample() { + + // Create a user. New accounts need an email and private key to start. + privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + log.Fatal(err) + } + + user := User{ + Email: config.GetAsString("Certificates.email"), + key: privateKey, + } + + configLE := lego.NewConfig(&user) + + // This CA URL is configured for a local dev instance of Boulder running in Docker in a VM. + configLE.CADirURL = "http://192.168.99.100:4000/directory" + configLE.Certificate.KeyType = certcrypto.RSA2048 + + // A client facilitates communication with the CA server. + client, err := lego.NewClient(configLE) + if err != nil { + log.Fatal(err) + } + + dnsConfig := cloudflare.NewDefaultConfig() + dnsConfig.AuthEmail = "" //TODO Pull from config + dnsConfig.AuthKey = "" //TODO Pull from config + + provider, err := cloudflare.NewDNSProviderConfig(dnsConfig) + if err != nil { + fmt.Printf("Error creating DNS provider: %v\n", err) + os.Exit(1) + } + err = client.Challenge.SetDNS01Provider(provider) + if err != nil { + fmt.Printf("Error setting dns provider: %v\n", err) + os.Exit(1) + } + + // New users will need to register + reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true}) + if err != nil { + log.Fatal(err) + } + user.Registration = reg + + request := certificate.ObtainRequest{ + Domains: []string{"mydomain.com"}, + Bundle: true, + } + certificates, err := client.Certificate.Obtain(request) + if err != nil { + log.Fatal(err) + } + + // Each certificate comes back with the cert bytes, the bytes of the client's + // private key, and a certificate URL. SAVE THESE TO DISK. + fmt.Printf("%#v\n", certificates) + + // ... all done. +} diff --git a/config.go b/config.go index 7340fda..7e26f2c 100644 --- a/config.go +++ b/config.go @@ -3,6 +3,7 @@ package main import ( "fmt" "os" + "strings" ) func makeDirs() { @@ -21,17 +22,29 @@ func makeDirs() { os.Exit(1) } } -} -// TODO make domain level configs override global config -func createConfig() { - confPath := "/etc/certman/certman.conf" - configBytes := []byte("[Git]\nhost = github\nserver = https://gitea.instance.com\nusername = user\napi_token = xxxxxxxxxxxxxxxx\norg_name = org\ntemplate_name = template\n\n[Crypto]\ncert_path = /etc/certman/crypto/cert.pem\nkey_path = /etc/certman/crypto/key.pem") - - createFile(confPath, configBytes) + err = os.Mkdir("/var/local/certman", 0660) + if err != nil { + if !os.IsExist(err) { + fmt.Printf("Unable to create certman directory: %v\n", err) + os.Exit(1) + } + } } func createNewDomainConfig(domain string) { - data := []byte("[Cloudflare]\ncf_email = email@example.com\ncf_api_token = xxxxxxxxxxxxxxxx\n\n[Certificates]\ncerts_path = ./certs/" + domain + "\nsubdomains =") - createFile("/etc/certman/"+domain+".conf", data) + data := []byte(strings.ReplaceAll(defaultDomainConfig, "{domain}", domain)) + createFile("/etc/certman/conf/"+domain+".conf", 0755, data) +} + +func createNewDomainCertsDir(domain string) { + err := os.Mkdir("/var/local/certman/"+domain, 0660) + if err != nil { + if os.IsExist(err) { + fmt.Println("Directory already exists...") + } else { + fmt.Printf("Error creating certificate directory for %s: %v\n", domain, err) + os.Exit(1) + } + } } diff --git a/crypto.go b/crypto.go index 973ee23..81774f1 100644 --- a/crypto.go +++ b/crypto.go @@ -1,58 +1,160 @@ package main import ( + "bufio" "crypto/rand" - "crypto/rsa" - "crypto/x509" + "encoding/base64" + "errors" "fmt" + "io" "os" + "strings" + + _ "filippo.io/age" + "filippo.io/age/armor" ) -var cert *x509.Certificate -var key *rsa.PrivateKey +//var cert *x509.Certificate +//var key *rsa.PrivateKey +// +//func encryptBytes(data []byte) []byte { +// if cert == nil || key == nil { +// loadCerts() +// } +// +// encrypted, err := rsa.EncryptPKCS1v15(rand.Reader, cert.PublicKey.(*rsa.PublicKey), data) +// if err != nil { +// fmt.Println("Error encrypting data,", err) +// os.Exit(1) +// } +// return encrypted +//} +// +//func decryptBytes(data []byte) []byte { +// if cert == nil || key == nil { +// loadCerts() +// } +// +// decrypted, err := rsa.DecryptPKCS1v15(rand.Reader, key, data) +// if err != nil { +// fmt.Println("Error decrypting data,", err) +// os.Exit(1) +// } +// return decrypted +//} +// +//func loadCerts() { +// var err error +// certBytes, err := os.ReadFile(config.GetAsString("Crypto.cert_path")) +// keyBytes, err := os.ReadFile(config.GetAsString("Crypto.key_path")) +// if err != nil { +// fmt.Println("Error reading cert or key,", err) +// os.Exit(1) +// } +// +// cert, err = x509.ParseCertificate(certBytes) +// if err != nil { +// fmt.Println("Error parsing certificate,", err) +// os.Exit(1) +// } +// key, err = x509.ParsePKCS1PrivateKey(keyBytes) +// if err != nil { +// fmt.Println("Error parsing private key,", err) +// } +//} -func encryptBytes(data []byte) []byte { - if cert == nil || key == nil { - loadCerts() +// GenerateKey returns a base64-encoded 32-byte random key suitable to use as the +// symmetric passphrase for age scrypt mode. Store this securely (never in Git). +func GenerateKey() (string, error) { + k := make([]byte, 32) + if _, err := rand.Read(k); err != nil { + return "", err } - - encrypted, err := rsa.EncryptPKCS1v15(rand.Reader, cert.PublicKey.(*rsa.PublicKey), data) - if err != nil { - fmt.Println("Error encrypting data,", err) - os.Exit(1) - } - return encrypted + out := make([]byte, base64.StdEncoding.EncodedLen(len(k))) + base64.StdEncoding.Encode(out, k) + return string(out), nil } -func decryptBytes(data []byte) []byte { - if cert == nil || key == nil { - loadCerts() - } - - decrypted, err := rsa.DecryptPKCS1v15(rand.Reader, key, data) +// LoadKeyFromFile reads a key file that contains either a raw base64 string or +// "AGE_SYM_KEY=" (handy for dotenv). Whitespace is trimmed. +func LoadKeyFromFile(path string) (string, error) { + b, err := os.ReadFile(path) if err != nil { - fmt.Println("Error decrypting data,", err) - os.Exit(1) + return "", err } - return decrypted + s := strings.TrimSpace(string(b)) + if i := strings.Index(s, "AGE_SYM_KEY="); i >= 0 { + s = strings.TrimSpace(strings.TrimPrefix(s, "AGE_SYM_KEY=")) + } + if s == "" { + return "", errors.New("empty symmetric key") + } + // Quick sanity check that it’s base64 and ~32 bytes after decode. + if _, err := base64.StdEncoding.DecodeString(s); err != nil { + return "", fmt.Errorf("invalid base64 key: %w", err) + } + return s, nil } -func loadCerts() { +// Encrypt streams plaintext from r to w using a symmetric passphrase. +// If armorOut is true, output is ASCII-armored (BEGIN AGE ENCRYPTED FILE). +func Encrypt(r io.Reader, w io.Writer, passphrase string, armorOut bool) error { + passphrase = strings.TrimSpace(passphrase) + if passphrase == "" { + return errors.New("missing passphrase") + } + + var out io.WriteCloser var err error - certBytes, err := os.ReadFile(config.GetAsString("Crypto.cert_path")) - keyBytes, err := os.ReadFile(config.GetAsString("Crypto.key_path")) + + if armorOut { + aw := armor.NewWriter(w) + defer aw.Close() + //out, err = age.Encrypt(aw, age.NewScryptRecipient(passphrase)) + } else { + //out, err = age.Encrypt(w, age.NewScryptRecipient(passphrase)) + } if err != nil { - fmt.Println("Error reading cert or key,", err) - os.Exit(1) + return err } - cert, err = x509.ParseCertificate(certBytes) - if err != nil { - fmt.Println("Error parsing certificate,", err) - os.Exit(1) - } - key, err = x509.ParsePKCS1PrivateKey(keyBytes) - if err != nil { - fmt.Println("Error parsing private key,", err) + _, copyErr := io.Copy(out, bufio.NewReader(r)) + closeErr := out.Close() + if copyErr != nil { + return copyErr } + return closeErr +} + +// Decrypt streams ciphertext from r to w using the same symmetric passphrase. +// It auto-detects armored vs binary ciphertext. +func Decrypt(r io.Reader, w io.Writer, passphrase string) error { + passphrase = strings.TrimSpace(passphrase) + if passphrase == "" { + return errors.New("missing passphrase") + } + + br := bufio.NewReader(r) + peek, _ := br.Peek(32) + //var in io.Reader = br + if strings.HasPrefix(string(peek), "-----BEGIN AGE ENCRYPTED FILE-----") { + // in = armor.NewReader(br) + } + + //dr, err := age.Decrypt(in, age.NewScryptIdentity(passphrase)) + //if err != nil { + // return err + //} + //_, err = io.Copy(w, bufio.NewWriter(wrap0600(w))) + //return err + return nil +} + +// wrap0600 ensures that when w is an *os.File newly created by caller, +// its perms are 0600. If it’s not an *os.File, it’s returned unchanged. +func wrap0600(w io.Writer) io.Writer { + if f, ok := w.(*os.File); ok { + _ = f.Chmod(0600) + } + return w } diff --git a/example.config.ini b/example.config.ini index 0ab5551..0f0f407 100644 --- a/example.config.ini +++ b/example.config.ini @@ -1,5 +1,5 @@ [Git] -host = github +host = gitea server = https://gitea.instance.com username = user api_token = xxxxxxxxxxxxxxxx @@ -11,5 +11,4 @@ cf_email = email@example.com cf_api_token = xxxxxxxxxxxxxxxx [Certificates] -certs_path = ./certs -subdomains = \ No newline at end of file +data_root = /var/local/certman \ No newline at end of file diff --git a/example.domainconfig.ini b/example.domainconfig.ini new file mode 100644 index 0000000..9b8b648 --- /dev/null +++ b/example.domainconfig.ini @@ -0,0 +1,6 @@ +[Domain] +domain_name = example.com + +[Certificates] +subdomains = +expiry = 90 \ No newline at end of file diff --git a/fullbuild.bat b/fullbuild.bat new file mode 100644 index 0000000..3ab35dd --- /dev/null +++ b/fullbuild.bat @@ -0,0 +1,6 @@ +@echo off +set GOARCH=amd64 +set GOOS=linux +go install -v -a std + +go build -o ./certman . \ No newline at end of file diff --git a/git.go b/git.go index 5662665..aa40134 100644 --- a/git.go +++ b/git.go @@ -98,7 +98,7 @@ func addAndPushCerts() { os.Exit(1) } certFile, err := os.ReadFile(config.GetAsString("Certificates.certs_path") + "/certificates/" + cert.Name()) - certFile = encryptBytes(certFile) + //certFile = encryptBytes(certFile) _, err = file.Write(certFile) err = file.Close() if err != nil { diff --git a/go.mod b/go.mod index 8ec5aed..7335523 100644 --- a/go.mod +++ b/go.mod @@ -1,39 +1,46 @@ module main -go 1.20 +go 1.24.0 + +toolchain go1.24.7 require ( code.gitea.io/sdk/gitea v0.15.1 - git.nevets.tech/Steven/ezconf v0.0.0-20230519201913-88e928e51a73 - github.com/go-git/go-git v4.7.0+incompatible + filippo.io/age v1.2.1 + git.nevets.tech/Steven/ezconf v0.1.1 + github.com/go-acme/lego/v4 v4.26.0 + github.com/go-git/go-billy/v5 v5.4.1 github.com/go-git/go-git/v5 v5.7.0 - gopkg.in/src-d/go-billy.v4 v4.3.2 + github.com/google/go-github/v55 v55.0.0 ) require ( github.com/Microsoft/go-winio v0.5.2 // indirect github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903 // indirect github.com/acomagu/bufpipe v1.0.4 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cloudflare/circl v1.3.3 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect - github.com/go-git/go-billy/v5 v5.4.1 // indirect + github.com/go-jose/go-jose/v4 v4.1.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/google/go-github/v55 v55.0.0 // indirect github.com/google/go-querystring v1.1.0 // indirect - github.com/hashicorp/go-version v1.2.1 // indirect + github.com/hashicorp/go-version v1.7.0 // indirect github.com/imdario/mergo v0.3.15 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect + github.com/miekg/dns v1.1.68 // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/sergi/go-diff v1.3.1 // indirect github.com/skeema/knownhosts v1.1.1 // indirect - github.com/src-d/gcfg v1.4.0 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect - golang.org/x/crypto v0.12.0 // indirect - golang.org/x/net v0.10.0 // indirect - golang.org/x/sys v0.11.0 // indirect + golang.org/x/crypto v0.42.0 // indirect + golang.org/x/mod v0.27.0 // indirect + golang.org/x/net v0.44.0 // indirect + golang.org/x/sync v0.17.0 // indirect + golang.org/x/sys v0.36.0 // indirect + golang.org/x/text v0.29.0 // indirect + golang.org/x/tools v0.36.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/src-d/go-git.v4 v4.13.1 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect ) diff --git a/go.sum b/go.sum index 6b9c990..f0a19ae 100644 --- a/go.sum +++ b/go.sum @@ -1,136 +1,122 @@ +c2sp.org/CCTV/age v0.0.0-20240306222714-3ec4d716e805 h1:u2qwJeEvnypw+OCPUHmoZE3IqwfuN5kgDfo5MLzpNM0= code.gitea.io/gitea-vet v0.2.1/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFjGGfE= code.gitea.io/sdk/gitea v0.15.1 h1:WJreC7YYuxbn0UDaPuWIe/mtiNKTvLN8MLkaw71yx/M= code.gitea.io/sdk/gitea v0.15.1/go.mod h1:klY2LVI3s3NChzIk/MzMn7G1FHrfU7qd63iSMVoHRBA= -git.nevets.tech/Steven/ezconf v0.0.0-20230519201913-88e928e51a73 h1:G/f1lIJeaZK/g60cdk2YzDSvhGjqumVa/d/NJ9SzxFA= -git.nevets.tech/Steven/ezconf v0.0.0-20230519201913-88e928e51a73/go.mod h1:O8svyJLWVPYdxPeZeiTkfmwz77BM0Wq2ZhDrHtdRhvI= +filippo.io/age v1.2.1 h1:X0TZjehAZylOIj4DubWYU1vWQxv9bJpo+Uu2/LGhi1o= +filippo.io/age v1.2.1/go.mod h1:JL9ew2lTN+Pyft4RiNGguFfOpewKwSHm5ayKD/A4004= +git.nevets.tech/Steven/ezconf v0.1.1 h1:dEqV9Q0zVKX9UkPg5UTchGLd0J0WhiuV4dVg0o3blnY= +git.nevets.tech/Steven/ezconf v0.1.1/go.mod h1:O8svyJLWVPYdxPeZeiTkfmwz77BM0Wq2ZhDrHtdRhvI= github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903 h1:ZK3C5DtzV2nVAQTx5S5jQvMeDqWtD1By5mOoyY/xJek= github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903/go.mod h1:8TI4H3IbrackdNgv+92dI+rhpCaLqM0IfpgCgenFvRE= github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ= github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= -github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= -github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= -github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= -github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819 h1:RIB4cRk+lBqKK3Oy0r2gRX4ui7tuhiZq2SuTtTCi0/0= -github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= -github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= +github.com/go-acme/lego/v4 v4.26.0 h1:521aEQxNstXvPQcFDDPrJiFfixcCQuvAvm35R4GbyYA= +github.com/go-acme/lego/v4 v4.26.0/go.mod h1:BQVAWgcyzW4IT9eIKHY/RxYlVhoyKyOMXOkq7jK1eEQ= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4= github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg= -github.com/go-git/go-git v4.7.0+incompatible h1:+W9rgGY4DOKKdX2x6HxSR7HNeTxqiKrOvKnuittYVdA= -github.com/go-git/go-git v4.7.0+incompatible/go.mod h1:6+421e08gnZWn30y26Vchf7efgYLe4dl5OQbBSUXShE= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f h1:Pz0DHeFij3XFhoBRGUDPzSJ+w2UcK5/0JvF8DRI58r8= github.com/go-git/go-git/v5 v5.7.0 h1:t9AudWVLmqzlo+4bqdf7GY+46SUuRsx59SboFxkq2aE= github.com/go-git/go-git/v5 v5.7.0/go.mod h1:coJHKEOk5kUClpsNlXrUvPrDxY3w3gjHvhcZd8Fodw8= +github.com/go-jose/go-jose/v4 v4.1.2 h1:TK/7NqRQZfgAh+Td8AlsrvtPoUyiHh0LqVvokh+1vHI= +github.com/go-jose/go-jose/v4 v4.1.2/go.mod h1:22cg9HWM1pOlnRiY+9cQYJ9XHmya1bYW8OeDM6Ku6Oo= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-github/v55 v55.0.0 h1:4pp/1tNMB9X/LuAhs5i0KQAE40NmiR/y6prLNb9x9cg= github.com/google/go-github/v55 v55.0.0/go.mod h1:JLahOTA1DnXzhxEymmFF5PP2tSS9JVNj68mSZNDwskA= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= -github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI= github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= +github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM= github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA= +github.com/miekg/dns v1.1.68/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/skeema/knownhosts v1.1.1 h1:MTk78x9FPgDFVFkDLTrsnnfCJl7g1C/nnKvePgrIngE= github.com/skeema/knownhosts v1.1.1/go.mod h1:g4fPeYpque7P0xefxtGzV81ihjC8sX2IqpAoNkjxbMo= -github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4= -github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= -golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= -golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= +golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= +golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= +golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -143,47 +129,37 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= -golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0= +golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= +golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg= -gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= -gopkg.in/src-d/go-git-fixtures.v3 v3.5.0 h1:ivZFOIltbce2Mo8IjzUHAFoq/IylO9WHhNOAJK+LsJg= -gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g= -gopkg.in/src-d/go-git.v4 v4.13.1 h1:SRtFyV8Kxc0UP7aCHcijOMQGPxHSmMOPrzulQWolkYE= -gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/main.go b/main.go index c4cf449..fea7c8c 100644 --- a/main.go +++ b/main.go @@ -3,6 +3,7 @@ package main import ( "bufio" "code.gitea.io/sdk/gitea" + "flag" "fmt" "git.nevets.tech/Steven/ezconf" "github.com/go-git/go-billy/v5" @@ -14,7 +15,9 @@ import ( "io" "os" "os/exec" + "os/signal" "strings" + "syscall" ) var config *ezconf.Configuration @@ -30,16 +33,61 @@ var creds *http.BasicAuth var repo *git.Repository -//TODO create logic for domain based configs //TODO create logic for gh vs gt repos func main() { - makeDirs() - createConfig() + devFlag := flag.Bool("dev", false, "Developer Mode") + + newDomainFlag := flag.String("new-domain", "example.com", "Domain to create new configs and directories for") + installFlag := flag.Bool("install", false, "Install Certman") + daemonFlag := flag.Bool("d", false, "Daemon Mode") + + flag.Parse() + + if *devFlag { + + os.Exit(0) + } + if *newDomainFlag != "example.com" { + fmt.Printf("Creating new domain %s\n", *newDomainFlag) + createNewDomainConfig(*newDomainFlag) + createNewDomainCertsDir(*newDomainFlag) + } + if *installFlag { + makeDirs() + config = ezconf.NewConfiguration("/etc/certman/certman.conf", defaultConfig) + } + if *daemonFlag { + // Check if main config exists + if _, err := os.Stat("/etc/certman/certman.conf"); os.IsNotExist(err) { + fmt.Println("Main config file not found, please run 'certman --install', then properly configure /etc/certman/certman.conf.") + os.Exit(1) + } else if err != nil { + fmt.Printf("Error opening /etc/certman/certman.conf: %v\n", err) + } + // Setup SIGINT and SIGTERM listeners + sigChannel := make(chan os.Signal, 1) + signal.Notify(sigChannel, syscall.SIGINT, syscall.SIGTERM) + + // Task loop + go func() { + + }() + + // Cleanup on stop + sig := <-sigChannel + fmt.Printf("Program terminated with %v\n", sig) + + close() + } +} + +func close() { + } func maindis() { - config = ezconf.NewConfiguration("/etc/certman/certman.conf") + config = ezconf.NewConfiguration("/etc/certman/certman.conf", "") var err error args := os.Args @@ -130,12 +178,12 @@ func maindis() { ) stdout, err := cmd.StdoutPipe() if err != nil { - fmt.Printf("Error getting stdout from lego process: %v", err) + fmt.Printf("Error getting stdout from lego process: %v\n", err) os.Exit(1) } err = cmd.Start() if err != nil { - fmt.Printf("Error creating certs with lego: %v", err) + fmt.Printf("Error creating certs with lego: %v\n", err) os.Exit(1) } scanner := bufio.NewScanner(stdout) @@ -149,7 +197,7 @@ func maindis() { }() err = cmd.Wait() if err != nil { - fmt.Printf("Error waiting for lego command to finish: %v", err) + fmt.Printf("Error waiting for lego command to finish: %v\n", err) os.Exit(1) } addAndPushCerts() @@ -158,12 +206,12 @@ func maindis() { func fixUpdateSh() { oldUpdateSh, err := fs.Open("update.sh") if err != nil { - fmt.Printf("Error opening update.sh: %v", err) + fmt.Printf("Error opening update.sh: %v\n", err) os.Exit(1) } contentBytes, err := io.ReadAll(oldUpdateSh) if err != nil { - fmt.Printf("Error reading update.sh: %v", err) + fmt.Printf("Error reading update.sh: %v\n", err) os.Exit(1) } content := string(contentBytes) @@ -172,29 +220,12 @@ func fixUpdateSh() { _, err = updateSh.Write([]byte(content)) err = updateSh.Close() if err != nil { - fmt.Printf("Error writing update.sh: %v", err) + fmt.Printf("Error writing update.sh: %v\n", err) os.Exit(1) } _, err = workTree.Add("update.sh") if err != nil { - fmt.Printf("Error adding update.sh: %v", err) + fmt.Printf("Error adding update.sh: %v\n", err) os.Exit(1) } } - -func contains(slice []string, value string) (sliceHas bool, index int) { - for i, entry := range slice { - if entry == value { - return true, i - } - } - return false, -1 -} - -func insert(a []string, index int, value string) []string { - last := len(a) - 1 - a = append(a, a[last]) - copy(a[index+1:], a[index:last]) - a[index] = value - return a -} diff --git a/util.go b/util.go index 480457f..550e316 100644 --- a/util.go +++ b/util.go @@ -2,6 +2,7 @@ package main import ( "code.gitea.io/sdk/gitea" + "errors" "fmt" "git.nevets.tech/Steven/ezconf" "github.com/google/go-github/v55/github" @@ -16,30 +17,47 @@ type Domain struct { gtClient *gitea.Client } -func createFile(fileName string, data []byte) { +type GlobalConfig struct { + Git struct { + Host string + Endpoint string + Username string + Password string + ApiToken string + } +} + +type DomainConfig struct { +} + +func createFile(fileName string, filePermission os.FileMode, data []byte) { fileInfo, err := os.Stat(fileName) if err != nil { if os.IsNotExist(err) { file, err := os.Create(fileName) if err != nil { - fmt.Println("Error creating configuration file:", err) + fmt.Println("Error creating configuration file: ", err) os.Exit(1) } _, err = file.Write(data) if err != nil { - fmt.Println("Error writing to file;", err) + fmt.Println("Error writing to file: ", err) os.Exit(1) } + err = file.Chmod(filePermission) + if err != nil { + fmt.Println("Error changing file permission: ", err) + } } else { - fmt.Println("Error opening configuration file:", err) + fmt.Println("Error opening configuration file: ", err) os.Exit(1) } } else { if fileInfo.Size() == 0 { file, err := os.Create(fileName) if err != nil { - fmt.Println("Error creating configuration file:", err) + fmt.Println("Error creating configuration file: ", err) os.Exit(1) } @@ -51,3 +69,63 @@ func createFile(fileName string, data []byte) { } } } + +func fileExists(filePath string) bool { + if _, err := os.Stat(filePath); err == nil { + return true + } else if errors.Is(err, os.ErrNotExist) { + return false + } else { + fmt.Println("Error checking file existence: ", err) + os.Exit(1) + return false + } +} + +func contains(slice []string, value string) (sliceHas bool, index int) { + for i, entry := range slice { + if entry == value { + return true, i + } + } + return false, -1 +} + +func insert(a []string, index int, value string) []string { + last := len(a) - 1 + a = append(a, a[last]) + copy(a[index+1:], a[index:last]) + a[index] = value + return a +} + +const defaultConfig = `[Git] +host = gitea +server = https://gitea.instance.com +username = user +org_name = org +template_name = template + +[Certificates] +email = user@example.com +data_root = /var/local/certman +request_method = dns + +[Cloudflare] +cf_email = email@example.com + +` + +const defaultDomainConfig = `[Domain] +domain_name = {domain} +; default (use system dns) or IPv4 Address (1.1.1.1) +dns_server = default + +[Certificates] +subdomains = +expiry = 90 + +; Don't change setting below here unless you know what you're doing! +[Internal] +last_issued = never +`