mirror of
https://github.com/jmozd/cert-manager-webhook-variomedia.git
synced 2025-12-25 02:02:38 +01:00
updated Dockerfile to use latest Alpine image, optimized logging and updated documentation
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
7
Makefile
7
Makefile
@@ -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 \
|
||||
|
||||
35
README.md
35
README.md
@@ -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
2
go.mod
@@ -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
18
main.go
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user