diff --git a/Dockerfile b/Dockerfile index 9b49cee..c0d84bf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.16-alpine AS build_deps +FROM golang:1.17-alpine AS build_deps RUN apk add --no-cache git diff --git a/deploy/freedns-webhook/values.yaml b/deploy/freedns-webhook/values.yaml index 239aa2a..a631632 100644 --- a/deploy/freedns-webhook/values.yaml +++ b/deploy/freedns-webhook/values.yaml @@ -1,19 +1,19 @@ # The GroupName here is used to identify your company or business unit that # created this webhook. -# For freedns, this may be "acme.mycompany.com". +# For freedns, this may be "acme.freedns.afraid.org". # This name will need to be referenced in each Issuer's `webhook` stanza to # inform cert-manager of where to send ChallengePayload resources in order to # solve the DNS01 challenge. # This group name should be **unique**, hence using your own company's domain # here is recommended. -groupName: acme.mycompany.com +groupName: acme.freedns.afraid.org certManager: namespace: cert-manager serviceAccountName: cert-manager image: - repository: mycompany/webhook-image + repository: penguinade/cert-manager-webhook-freedns tag: latest pullPolicy: IfNotPresent diff --git a/freedns/freedns.go b/freedns/freedns.go index 093021f..2e863b7 100755 --- a/freedns/freedns.go +++ b/freedns/freedns.go @@ -6,8 +6,10 @@ import ( "io/ioutil" "net/http" "net/url" + "strconv" "strings" + logf "github.com/jetstack/cert-manager/pkg/logs" "golang.org/x/net/html" ) @@ -32,7 +34,15 @@ const URI_SUBDOMAIN_EDIT = "https://freedns.afraid.org/subdomain/edit.php?data_i const URI_LOGOUT = "https://freedns.afraid.org/logout/" const URI_DELETE_RECORD = "https://freedns.afraid.org/subdomain/delete2.php?data_id[]=%s&submit=delete%%20selected" -// const URI_LOGIN string = "http://127.0.0.1:1234/" +func LogInfo(Mesg string) { + // fmt.Println(Mesg) + logf.V(logf.InfoLevel).Info(Mesg) +} + +func LogDebug(Mesg string) { + // fmt.Println(Mesg) + logf.V(logf.DebugLevel).Info(Mesg) +} func GetDomainFromZone(Zone string) string { _segs := strings.Split(strings.TrimSuffix(Zone, "."), ".") @@ -113,10 +123,12 @@ func (dnsObj *FreeDNS) Logout() error { return nil } - _, _, err := _HttpRequest("GET", URI_DOMAIN, nil, dnsObj.AuthCookie) + _, _, err := _HttpRequest("GET", URI_LOGOUT, nil, dnsObj.AuthCookie) if err != nil { return err } + + dnsObj.AuthCookie = nil return nil } @@ -149,7 +161,7 @@ loop: break loop case html.TextToken: if inBold && strings.TrimSpace(htmlTokens.Token().Data) == DomainName { - fmt.Println("Found " + DomainName + ", looking for domain id") + LogInfo("Found " + DomainName + ", looking for domain id") lookForA = true } // The [Manage] anchor is next to the bold tag @@ -162,9 +174,9 @@ loop: for { attrKey, attrValue, moreAttr := htmlTokens.TagAttr() _href := string(attrValue) - if string(attrKey) == "href" && strings.Contains(_href, "/subdomain/?limit=") { + if string(attrKey) == "href" && strings.HasPrefix(_href, "/subdomain/?limit=") { dnsObj.DomainId = strings.TrimPrefix(_href, "/subdomain/?limit=") - fmt.Printf("Domain id for \"%s\" is %s\n", DomainName, dnsObj.DomainId) + LogDebug(fmt.Sprintf("Domain id for \"%s\" is %s\n", DomainName, dnsObj.DomainId)) break loop } if !moreAttr { @@ -205,10 +217,15 @@ func (dnsObj *FreeDNS) AddRecord(RecordType string, Subdomain string, Address st // Record already exists, treat this as success if strings.Contains(respStr, "already have another already existent") { - fmt.Println("Record already exists") + LogInfo("Record already exists") return nil } + // Try get a sense of the problem + var errorMesgs []string + lookForNextEl := 0 + lookForText := false + _strBuffer := "" htmlTokens := html.NewTokenizer(strings.NewReader(respStr)) loop: for { @@ -217,10 +234,40 @@ func (dnsObj *FreeDNS) AddRecord(RecordType string, Subdomain string, Address st case html.ErrorToken: break loop case html.TextToken: + _text := strings.TrimSpace(string(htmlTokens.Text())) + // Search for the "1 error" / "N errors" message + if strings.HasSuffix(_text, "error") || strings.HasSuffix(_text, "errors") { + _text = strings.TrimSpace(strings.TrimSuffix(strings.TrimSuffix(_text, "s"), "error")) + _n, _ := strconv.ParseInt(_text, 10, 8) + // + 1 because we are already inside a font tag + // The next closing is ourself + lookForNextEl = int(_n) + 1 + } else if lookForText { + _strBuffer = _strBuffer + _text + } + case html.StartTagToken: + _t, _ := htmlTokens.TagName() + tagName := string(_t) + if tagName == "font" && 0 < lookForNextEl { + lookForText = true + _strBuffer = "" + } + case html.EndTagToken: + _t, _ := htmlTokens.TagName() + tagName := string(_t) + if tagName == "font" && 0 < lookForNextEl { + lookForText = false + errorMesgs = append(errorMesgs, strings.TrimSpace(_strBuffer)) + lookForNextEl-- + } } } + if 0 < len(errorMesgs) { + return errors.New(strings.Join(errorMesgs, ", ")) + } + return errors.New("Unknown error while submitting record") } @@ -229,8 +276,8 @@ func (dnsObj *FreeDNS) AddRecord(RecordType string, Subdomain string, Address st return err } - if strings.Contains(_Location.Path, "/zc.php") { - fmt.Println("Error on AddRecord: Cookie expired") + if strings.HasPrefix(_Location.Path, "/zc.php") { + LogDebug("Error on AddRecord: Cookie expired") return errors.New("dns_cookie maybe expired") } @@ -278,7 +325,7 @@ func (dnsObj *FreeDNS) FindRecord(Subdomain string, RecordType string, Address s CurrRecordType := "" CurrRecordAddr := "" CurrTagName := "" - lookForNextTD := 0 + lookForNextEl := 0 htmlTokens := html.NewTokenizer(strings.NewReader(respStr)) loop: @@ -288,13 +335,13 @@ loop: case html.ErrorToken: break loop case html.TextToken: - if CurrTagName == "a" && lookForNextTD == 1 && CurrRecordAddr == "" { + if CurrTagName == "a" && lookForNextEl == 1 && CurrRecordAddr == "" { CurrRecordAddr = strings.TrimSpace(string(htmlTokens.Text())) } else if CurrTagName == "td" { - if lookForNextTD == 1 { + if lookForNextEl == 1 { CurrRecordType = string(htmlTokens.Text()) - lookForNextTD = 2 - } else if lookForNextTD == 2 { + lookForNextEl = 2 + } else if lookForNextEl == 2 { _Addr := string(htmlTokens.Text()) if CurrRecordType == RecordType && CurrRecordAddr == Subdomain { if _Addr == Address { @@ -303,7 +350,7 @@ loop: DeepSearchCandidates = append(DeepSearchCandidates, CurrRecordId) } } - lookForNextTD = 0 + lookForNextEl = 0 } } /** Each record is displayed with the following structure @@ -323,7 +370,7 @@ loop: attrKey, attrValue, moreAttr := htmlTokens.TagAttr() _href := string(attrValue) if string(attrKey) == "href" && strings.Contains(_href, "edit.php?data_id=") { - lookForNextTD = 1 + lookForNextEl = 1 CurrRecordAddr = "" CurrRecordId = strings.TrimPrefix(_href, "edit.php?data_id=") break @@ -340,7 +387,7 @@ loop: // Begin deep search for truncated records htmlAddr := strings.ReplaceAll(html.EscapeString(Address), """, """) for _, RecordId := range DeepSearchCandidates { - fmt.Println("Searching in " + RecordId) + LogDebug("Searching in " + RecordId) _, respStr, err := _HttpRequest("GET", URI_SUBDOMAIN_EDIT+RecordId, nil, dnsObj.AuthCookie) if err != nil { continue diff --git a/go.mod b/go.mod index efd84bd..df9c028 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/cert-manager/webhook-freedns go 1.17 require ( - github.com/jetstack/cert-manager v1.7.0 + github.com/jetstack/cert-manager v1.7.1 github.com/miekg/dns v1.1.34 github.com/stretchr/testify v1.7.0 k8s.io/apiextensions-apiserver v0.23.1 diff --git a/go.sum b/go.sum index 51d9de6..bedafae 100644 --- a/go.sum +++ b/go.sum @@ -305,6 +305,8 @@ github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NH github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jetstack/cert-manager v1.7.0 h1:XLLDmREJ8MlBg/Z1bySjHdyzT4yYTzGlbBsxqddJzxU= github.com/jetstack/cert-manager v1.7.0/go.mod h1:xj0TPp31HE0Jub5mNOnF3Fp3XvhIsiP+tsPZVOmU/Qs= +github.com/jetstack/cert-manager v1.7.1 h1:qIIP0RN5FzBChJLJ3uGCGJmdAAonwDMdcsJExATa64I= +github.com/jetstack/cert-manager v1.7.1/go.mod h1:xj0TPp31HE0Jub5mNOnF3Fp3XvhIsiP+tsPZVOmU/Qs= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= diff --git a/main.go b/main.go index 0737116..4499da2 100644 --- a/main.go +++ b/main.go @@ -121,7 +121,7 @@ func (c *customDNSProviderSolver) Present(ch *v1alpha1.ChallengeRequest) error { _zone = strings.TrimRight(_zone, ".") _key := "\"" + ch.Key + "\"" - fmt.Println("ADD", _zone, _key) + freedns.LogInfo(fmt.Sprintf("ADD %s %s", _zone, _key)) err = dnsObj.AddRecord("TXT", _zone, _key, false, "") if err != nil { @@ -140,11 +140,17 @@ func (c *customDNSProviderSolver) Present(ch *v1alpha1.ChallengeRequest) error { // concurrently. func (c *customDNSProviderSolver) CleanUp(ch *v1alpha1.ChallengeRequest) error { + if c.freedns.AuthCookie == nil { + return nil + } + + dnsObj := c.freedns + _addr := strings.TrimRight(ch.ResolvedFQDN, ".") _key := "\"" + ch.Key + "\"" _id, err := c.freedns.FindRecord(_addr, "TXT", _key) - fmt.Println("DEL", _addr) + freedns.LogInfo(fmt.Sprintf("DEL %s %s", _addr, _key)) if _id != "" { err = c.freedns.DeleteRecord(_id) @@ -153,7 +159,7 @@ func (c *customDNSProviderSolver) CleanUp(ch *v1alpha1.ChallengeRequest) error { } } - return c.freedns.Logout() + return dnsObj.Logout() } // Initialize will be called when the webhook first starts.