Select Git revision
-
Thomas Schneider authoredThomas Schneider authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
main.go 3.96 KiB
package main
import (
"context"
"encoding/json"
"fmt"
"net/http"
"git.fsmpi.rwth-aachen.de/thomas/go-rwthdns"
whapi "github.com/cert-manager/cert-manager/pkg/acme/webhook/apis/acme/v1alpha1"
"github.com/cert-manager/cert-manager/pkg/acme/webhook/cmd"
cmmetav1 "github.com/cert-manager/cert-manager/pkg/apis/meta/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
restclient "k8s.io/client-go/rest"
extapi "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
"k8s.io/klog/v2"
"k8s.io/klog/v2/klogr"
)
type solver struct {
client *kubernetes.Clientset
}
func (s *solver) Name() string {
return "rwth"
}
func (s *solver) Present(ch *whapi.ChallengeRequest) error {
client, err := s.newClientFromChallenge(ch)
if err != nil {
return err
}
txt := fmt.Sprintf(`%s IN TXT "%s"`, ch.ResolvedFQDN, ch.Key)
search := ch.ResolvedFQDN + " *"
records, err := client.ListRecords(nil, &search)
if err != nil {
klog.Error(err)
return err
}
var record rwthdns.Record
if len(records) == 0 {
record, err = client.CreateRecord(txt)
} else if len(records) == 1 {
record, err = client.UpdateRecord(records[0].Id, txt)
} else {
err = fmt.Errorf("More than one search result for %s", ch.ResolvedFQDN)
}
if err != nil {
klog.Error(err)
return err
}
klog.V(2).Infof("record %+v", record)
zone, err := client.DeployZone(record.ZoneId)
klog.V(2).Infof("deployed zone %+v", zone)
if err != nil {
klog.Error(err)
}
return err
}
func (s *solver) CleanUp(ch *whapi.ChallengeRequest) error {
client, err := s.newClientFromChallenge(ch)
if err != nil {
return err
}
records, err := client.ListRecords(nil, &ch.ResolvedFQDN)
if err != nil {
return fmt.Errorf("list records: %w", err)
}
var lastErr error
zones := map[int]interface{}{}
for _, r := range records {
if r.Type != "txt_record" {
continue
}
zones[r.ZoneId] = nil
_, err = client.DestroyRecord(r.Id)
if err != nil {
klog.Error(err)
lastErr = fmt.Errorf("destroy record: %w", err)
}
klog.V(2).Infof("deleted record %+v", r)
}
if lastErr != nil {
return lastErr
}
for zid := range zones {
zone, err := client.DeployZone(zid)
klog.V(2).Infof("deployed zone %+v", zone)
if err != nil {
klog.Error(err)
lastErr = fmt.Errorf("deploy zone: %w", err)
}
}
return lastErr
}
func (s *solver) Initialize(kubeClientConfig *restclient.Config, stopCh <-chan struct{}) error {
cl, err := kubernetes.NewForConfig(kubeClientConfig)
if err != nil {
return err
}
s.client = cl
return nil
}
func (s *solver) getCredentials(keyref cmmetav1.SecretKeySelector, ns string) (string, error) {
secret, err := s.client.CoreV1().Secrets(ns).Get(context.Background(), keyref.Name,
metav1.GetOptions{})
if err != nil {
return "", fmt.Errorf("failed to load secret %q: %w", ns+"/"+keyref.Name, err)
}
if apikey, ok := secret.Data[keyref.Key]; ok {
return string(apikey), nil
} else {
return "", fmt.Errorf("no key %q in secret %q", keyref.Key, ns+"/"+keyref.Name)
}
}
func (s *solver) newClientFromChallenge(ch *whapi.ChallengeRequest) (*rwthdns.Client, error) {
cfgJSON := extapi.JSON(*ch.Config)
cfg, err := loadConfig(&cfgJSON)
if err != nil {
return nil, err
}
klog.V(5).Infof("decoded config: %v", cfg)
apikey, err := s.getCredentials(cfg.APIKeySecretKeyRef, ch.ResourceNamespace)
if err != nil {
return nil, fmt.Errorf("getting credentials: %w", err)
}
logger := klogr.New()
client := rwthdns.Client{ApiToken: apikey, Client: http.DefaultClient, Log: &logger}
return &client, nil
}
type config struct {
APIKeySecretKeyRef cmmetav1.SecretKeySelector `json:"apiKeySecretKeyRef"`
}
func loadConfig(cfgJSON *extapi.JSON) (config, error) {
cfg := config{}
if cfgJSON == nil {
return cfg, nil
}
err := json.Unmarshal(cfgJSON.Raw, &cfg)
if err != nil {
err = fmt.Errorf("loadConfig: %w", err)
}
return cfg, err
}
func main() {
klog.InitFlags(nil)
cmd.RunWebhookServer("cert-manager-webhook-rwth.thomas.fsmpi.eu", &solver{})
}