diff --git a/certs/certs.go b/certs/certs.go index 446a426..2d54eed 100644 --- a/certs/certs.go +++ b/certs/certs.go @@ -1,8 +1,10 @@ package certs import ( - "fmt" + "encoding/json" "log" + "os" + "strings" "github.com/go-acme/lego/v4/certificate" "github.com/go-acme/lego/v4/challenge/dns01" @@ -11,10 +13,17 @@ import ( ) type certsClient struct { - legoClient *lego.Client + legoClient *lego.Client + lastCertificate *certificate.Resource } func (c *certsClient) RequestCertificate() { + log.Println("requesting a certificate") + if c.lastCertificate != nil { + c.RenewCertificate() + return + } + certificates, err := c.legoClient.Certificate.Obtain(certificate.ObtainRequest{ Domains: []string{"*.local-ip.sh"}, Bundle: true, @@ -23,7 +32,33 @@ func (c *certsClient) RequestCertificate() { log.Fatal(err) } - fmt.Printf("%#v\n", certificates) + c.lastCertificate = certificates + + persistFiles(certificates) + log.Printf("%#v\n", certificates) +} + +func (c *certsClient) RenewCertificate() { + log.Println("renewing currently existing certificate") + certificates, err := c.legoClient.Certificate.Renew(*c.lastCertificate, true, false, "") + if err != nil { + log.Fatal(err) + } + + c.lastCertificate = certificates + + persistFiles(certificates) + log.Printf("%#v\n", certificates) +} + +func persistFiles(certificates *certificate.Resource) { + os.WriteFile("/certs/server.pem", certificates.Certificate, 0o644) + os.WriteFile("/certs/server.key", certificates.PrivateKey, 0o644) + jsonBytes, err := json.MarshalIndent(certificates, "", "\t") + if err != nil { + log.Fatal(err) + } + os.WriteFile("/certs/output.json", jsonBytes, 0o644) } func NewCertsClient(xip *xip.Xip, user *Account) *certsClient { @@ -35,9 +70,41 @@ func NewCertsClient(xip *xip.Xip, user *Account) *certsClient { } provider := newProviderLocalIp(xip) - legoClient.Challenge.SetDNS01Provider(provider, dns01.AddRecursiveNameservers([]string{"1.1.1.1:53", "8.8.8.8:53"})) + legoClient.Challenge.SetDNS01Provider(provider, dns01.AddRecursiveNameservers([]string{"1.1.1.1:53", "8.8.8.8:53"}), dns01.DisableCompletePropagationRequirement()) + + lastCertificate := getLastCertificate(legoClient) return &certsClient{ legoClient, + lastCertificate, } } + +func getLastCertificate(legoClient *lego.Client) *certificate.Resource { + jsonBytes, err := os.ReadFile("/certs/output.json") + if err != nil { + if strings.Contains(err.Error(), "no such file or directory") { + return nil + } + log.Println(err) + log.Println("falling back to getting a brand new cert") + return nil + } + + var lastCertificate = &certificate.Resource{} + err = json.Unmarshal(jsonBytes, lastCertificate) + if err != nil { + log.Println(err) + log.Println("falling back to getting a brand new cert") + return nil + } + + lastCertificate, err = legoClient.Certificate.Get(lastCertificate.CertURL, true) + if err != nil { + log.Println(err) + log.Println("falling back to getting a brand new cert") + return nil + } + + return lastCertificate +} diff --git a/certs/config.go b/certs/config.go index 2425334..ebba381 100644 --- a/certs/config.go +++ b/certs/config.go @@ -9,7 +9,7 @@ import ( const ( email = "admin@local-ip.sh" - caDirUrl = lego.LEDirectoryStaging + caDirUrl = lego.LEDirectoryProduction ) var parsedCaDirUrl, _ = url.Parse(caDirUrl) diff --git a/fly.toml b/fly.toml index e19c0ef..3f33db2 100644 --- a/fly.toml +++ b/fly.toml @@ -16,3 +16,7 @@ processes = [] [[services.ports]] port = "53" + +[mounts] + source="certs" + destination="/certs" \ No newline at end of file diff --git a/main.go b/main.go index 34b89ad..f3e5008 100644 --- a/main.go +++ b/main.go @@ -2,8 +2,11 @@ package main import ( "flag" + "log" "strings" + "time" + "local-ip.sh/certs" "local-ip.sh/xip" ) @@ -18,16 +21,20 @@ func main() { n := xip.NewXip(zone, strings.Split(nameservers, ","), *port) - // not functional yet - /* go func() { + go func() { account := certs.LoadAccount() log.Println(account.Registration.Body.Contact) - ddd := certs.NewCertsClient(n, account) + certsClient := certs.NewCertsClient(n, account) time.Sleep(5 * time.Second) - fmt.Println("requesting certs") - ddd.RequestCertificate() - }() */ + certsClient.RequestCertificate() + + for { + // renew certificate every month + time.Sleep(30 * 24 * time.Hour) + certsClient.RenewCertificate() + } + }() n.StartServer() } diff --git a/xip/xip.go b/xip/xip.go index 500483b..5811185 100644 --- a/xip/xip.go +++ b/xip/xip.go @@ -92,9 +92,7 @@ var ( {Target: "local-ip.sh.n2kl11.flydns.net."}, }, */ // if manual - TXT: &dns.TXT{ - Txt: []string{"FRMKrfS9tGP5Gap6ruCG7qRprBlMrsBnQPtMD-dmOGk"}, - }, + TXT: &dns.TXT{}, }, } ) @@ -334,13 +332,13 @@ func (xip *Xip) soaRecord(question dns.Question) *dns.SOA { func (xip *Xip) handleQuery(message *dns.Msg) { for _, question := range message.Question { - log.Printf("name: %s\n", question.Name) - log.Printf("class: %d\n", question.Qclass) - log.Printf("type: %d\n", question.Qtype) + // log.Printf("name: %s\n", question.Name) + // log.Printf("class: %d\n", question.Qclass) + // log.Printf("type: %d\n", question.Qtype) - if strings.HasPrefix(strings.ToLower(question.Name), "_acme-challenge.") { + /* if strings.HasPrefix(strings.ToLower(question.Name), "_acme-challenge.") { message.Authoritative = false - } + } */ switch question.Qtype { case dns.TypeA: @@ -399,7 +397,7 @@ func NewXip(zone string, nameservers []string, port int) (xip *Xip) { } xip.server = dns.Server{ - Addr: "0.0.0.0:" + strconv.Itoa(port), + Addr: ":" + strconv.Itoa(port), Net: "udp", }