updated Dockerfile to use latest Alpine image, optimized logging and updated documentation

This commit is contained in:
Jens-U. Mozdzen
2022-07-03 23:48:49 +02:00
parent ecfaaacf38
commit 6f180147ac
6 changed files with 64 additions and 56 deletions

View File

@@ -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
@@ -15,7 +15,7 @@ COPY . .
RUN CGO_ENABLED=0 go build -o webhook -ldflags '-w -extldflags "-static"' .
FROM alpine:3.9
FROM alpine
RUN apk add --no-cache ca-certificates

View File

@@ -3,7 +3,7 @@ ARCH ?= $(shell go env GOARCH)
PROVIDER := "variomedia"
IMAGE_NAME := "${REGISTRY}cert-manager-webhook-${PROVIDER}"
IMAGE_TAG := "latest"
IMAGE_TAG := "2.0.0"
OUT := $(shell pwd)/_out
@@ -30,9 +30,12 @@ clean: clean-kubebuilder
clean-kubebuilder:
rm -Rf _test/kubebuilder
build:
build: test
docker build -t "$(IMAGE_NAME):$(IMAGE_TAG)" .
push: build
docker push $(IMAGE_NAME):$(IMAGE_TAG)
.PHONY: rendered-manifest.yaml
rendered-manifest.yaml:
helm template \

View File

@@ -49,25 +49,15 @@ webhook to complete ACME challenge validations and obtain certificates.
The Variomedia AG webhook implementation is based on the example webhook provided
by the cert-manager project (https://github.com/cert-manager/webhook-example).
### Creating your own repository
### Using your own repository
The GitHub version of the Variomedia webhook implementation is focussed on providing
an implementation in a decentral container registry, i.e. "Harbor". The Docker image
is currently *not* published on docker.io.
Once you have your registry up & running (which is not part of this README description),
you can build your local copy of the software using the following commands:
```bash
# to upload the container image to your registry
export REGISTRY='your.registry.company.com'
docker login $REGISTRY
make build
```
#### Running the test suite
**It is essential that you configure and run the test suite after creating the
**It is essential that you configure and run the test suite after modifying the
DNS01 webhook.**
You can run the test suite with:
@@ -76,8 +66,23 @@ You can run the test suite with:
$ TEST_ZONE_NAME=example.com. make test
```
The example file has a number of areas you must fill in and replace with your
own options in order for tests to pass.
Setting the trailing "." on the zone name (for which you have the Variomedia API key
and set up the files in the testdata/my-custom-solver/ subdirectory) is required, the
test run might otherwise fail.
### Pushing the Docker image
Once you have your registry up & running (which is not part of this README description),
you can build and upload your local copy of the software using the following commands:
```bash
# to upload the container image to your registry
export REGISTRY='your.registry.company.com/yourproject'
docker login $REGISTRY
# push the resulting image to your repository
# will invoke via dependencies test -> build -> push
TEST_ZONE_NAME=example.com. make push
```
## Installation via Helm chart
@@ -119,7 +124,7 @@ kubectl apply -f - << EOF
- dns01:
webhook:
groupName: acme.cert-manager-webhook-variomedia.local
solverName: variomedia
solverName: variomedia-APIv2019
config:
example.com: variomedia-credentials-01
someotherdomain.com: variomedia-credentials-01

2
go.mod
View File

@@ -1,4 +1,4 @@
module github.com/cert-manager/webhook-example
module github.com/jmozd/cert-manager-webhook-variomedia
go 1.17

18
main.go
View File

@@ -124,14 +124,14 @@ func (c *customDNSProviderSolver) Present(ch *v1alpha1.ChallengeRequest) error {
cfg, err := c.loadApiKeys(ch.Config, ch.ResourceNamespace)
if err != nil {
klog.V(2).ErrorS( err, "Present() finished with error while loading API keys")
klog.ErrorS( err, "Present() finished with error while loading API keys")
return err
}
klog.V(6).Infof("decoded configuration %v", cfg)
entry, domain, apiKey, err := c.getDomainAndEntryAndApiKey( ch, &cfg)
if err != nil {
klog.V(2).ErrorS( err, "Present() finished with error while determining domain and entry name")
klog.ErrorS( err, "Present() finished with error while determining domain and entry name")
return fmt.Errorf("unable to get domain key for zone %s: %v", ch.ResolvedZone, err)
}
klog.V(4).InfoS( "present", "entry", entry, "domain", domain, "entry", entry, "API key", apiKey)
@@ -140,7 +140,7 @@ func (c *customDNSProviderSolver) Present(ch *v1alpha1.ChallengeRequest) error {
url, err := variomediaClient.UpdateTxtRecord(&domain, &entry, &ch.Key, variomediaMinTtl)
if err != nil {
klog.V(2).ErrorS( err, "Present() finished with error while trying to update the DNS record")
klog.ErrorS( err, "Present() finished with error while trying to update the DNS record")
return fmt.Errorf("unable to change TXT record: %v", err)
}
@@ -170,14 +170,14 @@ func (c *customDNSProviderSolver) CleanUp(ch *v1alpha1.ChallengeRequest) error {
cfg, err := c.loadApiKeys(ch.Config, ch.ResourceNamespace)
if err != nil {
klog.V(2).ErrorS( err, "CleanUp() finished with error while loading API keys")
klog.ErrorS( err, "CleanUp() finished with error while loading API keys")
return err
}
klog.V(6).Infof("decoded configuration %v", cfg)
entry, domain, apiKey, err := c.getDomainAndEntryAndApiKey( ch, &cfg)
if err != nil {
klog.V(2).ErrorS( err, "CleanUp() finished with error while determining domain and entry name")
klog.ErrorS( err, "CleanUp() finished with error while determining domain and entry name")
return fmt.Errorf("unable to get domain key for zone %s: %v", ch.ResolvedZone, err)
}
klog.V(4).InfoS( "clean up", "entry", entry, "domain", domain, "entry", entry, "API key", apiKey)
@@ -188,7 +188,7 @@ func (c *customDNSProviderSolver) CleanUp(ch *v1alpha1.ChallengeRequest) error {
err = variomediaClient.DeleteTxtRecord( url, variomediaMinTtl)
if err != nil {
klog.V(2).ErrorS( err, "CleanUp() finished with error while trying to delete the DNS record")
klog.ErrorS( err, "CleanUp() finished with error while trying to delete the DNS record")
return fmt.Errorf("unable to delete TXT record: %v", err)
}
@@ -238,13 +238,13 @@ func (c *customDNSProviderSolver) loadApiKeys(cfgJSON *extapi.JSON, namespace st
klog.V(6).Infof("try to load secret `%s` with key `%s`", secretName, "api-token")
sec, err := c.client.CoreV1().Secrets(namespace).Get(context.Background(), secretName, metav1.GetOptions{})
if err != nil {
klog.V(2).ErrorS( err, "loadApiKeys() finished with error")
klog.ErrorS( err, "loadApiKeys() finished with error")
return nil, fmt.Errorf("unable to get secret `%s`; %v", secretName, err)
}
secBytes, ok := sec.Data["api-token"]
if !ok {
klog.V(2).ErrorS( err, "loadApiKeys() finished with error")
klog.ErrorS( err, "loadApiKeys() finished with error")
return nil, fmt.Errorf("key %q not found in secret \"%s/%s\"", "api-token",
secretName, namespace)
}
@@ -268,7 +268,7 @@ func (c *customDNSProviderSolver) getDomainAndEntryAndApiKey(ch *v1alpha1.Challe
domain := strings.TrimSuffix(ch.ResolvedZone, ".")
apiKey, ok := (*cfg)[domain]
if !ok {
klog.V(2).ErrorS( fmt.Errorf("domain '%s' not found in config.", domain), "getDomainAndEntryAndApiKey() finished with error")
klog.ErrorS( fmt.Errorf("domain '%s' not found in config.", domain), "getDomainAndEntryAndApiKey() finished with error")
return entry, domain, apiKey, fmt.Errorf("domain '%s' not found in config.", domain)
}

View File

@@ -124,32 +124,32 @@ func (c *variomediaClient) UpdateTxtRecord(domain *string, name *string, value *
// the actual request is encoded in JSON
body, err := json.Marshal( reqData)
if err != nil {
klog.V(2).ErrorS(err, "UpdateTxtRecord() finished with error")
klog.ErrorS(err, "UpdateTxtRecord() finished with error")
return "", fmt.Errorf("cannot marshall to json: %v", err)
}
req, err := http.NewRequest("POST", variomediaLiveDnsBaseUrl, bytes.NewReader(body))
if err != nil {
klog.V(2).ErrorS(err, "UpdateTxtRecord() finished with error")
klog.ErrorS(err, "UpdateTxtRecord() finished with error")
return "", err
}
// contact Variomedia and check the results
status, respData, err := c.doRequest(req, true)
if err != nil {
klog.V(2).ErrorS(err, "UpdateTxtRecord() finished with error")
klog.ErrorS(err, "UpdateTxtRecord() finished with error")
return "", err
}
// have we hit the rate limit?
if status == http.StatusTooManyRequests {
klog.V(2).ErrorS( fmt.Errorf("Variomedia rate limit reached (HTTP code %d)", http.StatusTooManyRequests), "UpdateTxtRecord() finished with error")
klog.ErrorS( nil, "UpdateTxtRecord() finished with errori 'too many requests' reported by Variomedia")
return "", fmt.Errorf("Variomedia rate limit reached (HTTP code %d)", http.StatusTooManyRequests)
}
if status != http.StatusCreated && status != http.StatusOK && status != http.StatusAccepted {
klog.V(2).ErrorS(err, "UpdateTxtRecord() finished with error")
return "", fmt.Errorf("failed creating TXT record: %v", err)
klog.ErrorS(nil, "UpdateTxtRecord() finished with error reported by server", "status code", status)
return "", fmt.Errorf("failed creating TXT record: server reported status code %d", status)
}
// the request has succeeded - but is the job already finished?
@@ -157,7 +157,7 @@ func (c *variomediaClient) UpdateTxtRecord(domain *string, name *string, value *
var reply variomediaResponse
err = json.Unmarshal( respData, &reply)
if err != nil {
klog.V(2).ErrorS(err, "UpdateTxtRecord() finished with error")
klog.ErrorS(err, "UpdateTxtRecord() finished with error")
return "", fmt.Errorf("cannot unmarshall response to json: %v", err)
}
klog.V(5).InfoS( "HTTP finished", "JSON reply", reply)
@@ -174,33 +174,33 @@ func (c *variomediaClient) UpdateTxtRecord(domain *string, name *string, value *
// re-fetch the job status
req, err := http.NewRequest("GET", reply.Data.Links[ "queue-job"], nil)
if err != nil {
klog.V(2).ErrorS(err, "UpdateTxtRecord() finished with error")
klog.ErrorS(err, "UpdateTxtRecord() finished with error")
return "", err
}
// contact Variomedia and check the results
status, respData, err := c.doRequest(req, true)
if err != nil {
klog.V(2).ErrorS(err, "UpdateTxtRecord() finished with error")
klog.ErrorS(err, "UpdateTxtRecord() finished with error")
return "", err
}
// have we hit the rate limit?
if status == http.StatusTooManyRequests {
klog.V(2).ErrorS(fmt.Errorf("Variomedia rate limit reached (HTTP code %d)", http.StatusTooManyRequests), "UpdateTxtRecord() finished with error")
klog.ErrorS( nil, "UpdateTxtRecord() finished with errori 'too many requests' reported by Variomedia")
return "", fmt.Errorf("Variomedia rate limit reached (HTTP code %d)", http.StatusTooManyRequests)
}
if status != http.StatusCreated && status != http.StatusOK && status != http.StatusAccepted {
klog.V(2).ErrorS(err, "UpdateTxtRecord() finished with error")
return "", fmt.Errorf("failed creating TXT record: %v", err)
klog.ErrorS(nil, "UpdateTxtRecord() finished with error reported by server", "status code", status)
return "", fmt.Errorf("failed creating TXT record: server reported status code %d", status)
}
// the request has succeeded - but is the job already finished?
// check the response for an according '' element
err = json.Unmarshal( respData, &reply)
if err != nil {
klog.V(2).ErrorS(err, "UpdateTxtRecord() finished with error")
klog.ErrorS(err, "UpdateTxtRecord() finished with error")
return "", fmt.Errorf("cannot unmarshall response to json: %v", err)
}
klog.V(5).InfoS( "HTTP finished", "JSON reply", reply)
@@ -213,7 +213,7 @@ func (c *variomediaClient) UpdateTxtRecord(domain *string, name *string, value *
break;
}
if (loopcount == 0) {
klog.V(2).ErrorS(fmt.Errorf("DNS update job timed out with most recent status '%s'", reply.Data.Attributes[ "status"]), "UpdateTxtRecord() finished with error")
klog.ErrorS(nil, "UpdateTxtRecord() finished with error: job timed out", "most recent status", reply.Data.Attributes[ "status"])
return "", fmt.Errorf("DNS update job timed out with most recent status '%s'", reply.Data.Attributes[ "status"])
}
} // emulated do until
@@ -237,25 +237,25 @@ func (c *variomediaClient) DeleteTxtRecord(url string, ttl int) error {
// deleting a record happens by sending a HTTP "DELETE" request to the DNS entry's URL
req, err := http.NewRequest("DELETE", url, nil)
if err != nil {
klog.V(2).ErrorS(err, "DeleteTxtRecord() finished with error")
klog.ErrorS(err, "DeleteTxtRecord() finished with error")
return err
}
// contact Variomedia and check the results
status, respData, err := c.doRequest(req, true)
if err != nil {
klog.V(2).ErrorS(err, "DeleteTxtRecord() finished with error")
klog.ErrorS(err, "DeleteTxtRecord() finished with error")
return err
}
// have we hit the rate limit?
if status == http.StatusTooManyRequests {
klog.V(2).ErrorS(fmt.Errorf("Variomedia rate limit reached (HTTP code %d)", http.StatusTooManyRequests), "DeleteTxtRecord() finished with error")
klog.ErrorS( nil, "DeleteTxtRecord() finished with errori 'too many requests' reported by Variomedia")
return fmt.Errorf("Variomedia rate limit reached (HTTP code %d)", http.StatusTooManyRequests)
}
if status != http.StatusCreated && status != http.StatusOK && status != http.StatusAccepted {
klog.V(2).ErrorS(err, "DeleteTxtRecord() finished with error")
klog.ErrorS(nil, "DeleteTxtRecord() finished with error reported by server", "status code", status)
return fmt.Errorf("failed deleting TXT record: %v", err)
}
@@ -264,7 +264,7 @@ func (c *variomediaClient) DeleteTxtRecord(url string, ttl int) error {
var reply variomediaResponse
err = json.Unmarshal( respData, &reply)
if err != nil {
klog.V(2).ErrorS(err, "DeleteTxtRecord() finished with error")
klog.ErrorS(err, "DeleteTxtRecord() finished with error")
return fmt.Errorf("cannot unmarshall response to json: %v", err)
}
klog.V(5).InfoS( "HTTP finished", "JSON reply", reply)
@@ -281,25 +281,25 @@ func (c *variomediaClient) DeleteTxtRecord(url string, ttl int) error {
// re-fetch the job status
req, err := http.NewRequest("GET", reply.Data.Links[ "queue-job"], nil)
if err != nil {
klog.V(2).ErrorS(err, "DeleteTxtRecord() finished with error")
klog.ErrorS(err, "DeleteTxtRecord() finished with error")
return err
}
// contact Variomedia and check the results
status, respData, err := c.doRequest(req, true)
if err != nil {
klog.V(2).ErrorS(err, "DeleteTxtRecord() finished with error")
klog.ErrorS(err, "DeleteTxtRecord() finished with error")
return err
}
// have we hit the rate limit?
if status == http.StatusTooManyRequests {
klog.V(2).ErrorS(fmt.Errorf("Variomedia rate limit reached (HTTP code %d)", http.StatusTooManyRequests), "DeleteTxtRecord() finished with error")
klog.ErrorS( nil, "DeleteTxtRecord() finished with errori 'too many requests' reported by Variomedia")
return fmt.Errorf("Variomedia rate limit reached (HTTP code %d)", http.StatusTooManyRequests)
}
if status != http.StatusCreated && status != http.StatusOK && status != http.StatusAccepted && status != http.StatusNotFound {
klog.V(2).ErrorS(err, "DeleteTxtRecord() finished with error")
klog.ErrorS(nil, "DeleteTxtRecord() finished with error reported by server", "status code", status)
return fmt.Errorf("failed creating TXT record: %v", err)
}
@@ -307,7 +307,7 @@ func (c *variomediaClient) DeleteTxtRecord(url string, ttl int) error {
// check the response for an according '' element
err = json.Unmarshal( respData, &reply)
if err != nil {
klog.V(2).ErrorS(err, "DeleteTxtRecord() finished with error")
klog.ErrorS(err, "DeleteTxtRecord() finished with error")
return fmt.Errorf("cannot unmarshall response to json: %v", err)
}
klog.V(5).InfoS( "HTTP finished", "JSON reply", reply)
@@ -327,7 +327,7 @@ func (c *variomediaClient) DeleteTxtRecord(url string, ttl int) error {
break;
}
if (loopcount == 0) {
klog.V(2).ErrorS(fmt.Errorf("DNS update job timed out with most recent status '%s'", reply.Data.Attributes[ "status"]), "DeleteTxtRecord() finished with error")
klog.ErrorS(nil, "DeleteTxtRecord() finished with error: job timed out", "most recent status", reply.Data.Attributes[ "status"])
return fmt.Errorf("DNS update job timed out with most recent status '%s'", reply.Data.Attributes[ "status"])
}
} // emulated do until
@@ -363,7 +363,7 @@ func (c *variomediaClient) doRequest(req *http.Request, readResponseBody bool) (
res, err := client.Do(req)
if err != nil {
klog.V(2).ErrorS(err, "doRequest() finished with error")
klog.ErrorS(err, "doRequest() finished with error")
return 0, nil, err
}
@@ -373,7 +373,7 @@ func (c *variomediaClient) doRequest(req *http.Request, readResponseBody bool) (
if (res.StatusCode == http.StatusOK || res.StatusCode == http.StatusAccepted) && readResponseBody {
data, err := ioutil.ReadAll(res.Body)
if err != nil {
klog.V(2).ErrorS(err, "HTTP request finished with error")
klog.ErrorS(err, "HTTP request finished with error")
return 0, nil, err
}
klog.V(4).InfoS( "HTTP request succeeded", "status code", res.StatusCode)