Skip to content
Snippets Groups Projects
Select Git revision
  • main default protected
  • debug
2 results

main.go

Blame
  • 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{})
    }