penguin/webhook-freedns

freedns webhook for cert-manager

commit 3eea4a96a6455f84fb209f4b76700cc500c1a344

author斟酌 鵬兄 <tgckpg@gmail.com>
date2022-03-14T02:14:53Z
subjectProper log handling
commit 3eea4a96a6455f84fb209f4b76700cc500c1a344
Author: 斟酌 鵬兄 <tgckpg@gmail.com>
Date:   2022-03-14T02:14:53Z

    Proper log handling
---
 Dockerfile                         |  2 +-
 deploy/freedns-webhook/values.yaml |  6 +--
 freedns/freedns.go                 | 79 ++++++++++++++++++++++++++++++--------
 go.mod                             |  2 +-
 go.sum                             |  2 +
 main.go                            | 12 ++++--
 6 files changed, 79 insertions(+), 24 deletions(-)

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 </font> 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), "&#34;", "&quot;")
 	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.