mirror of
https://github.com/cert-manager/webhook-example.git
synced 2025-07-01 22:35:49 +02:00
201 lines
5.1 KiB
Go
201 lines
5.1 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
extapi "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
|
"k8s.io/client-go/kubernetes"
|
|
"k8s.io/client-go/rest"
|
|
|
|
"github.com/cert-manager/cert-manager/pkg/acme/webhook/apis/acme/v1alpha1"
|
|
"github.com/cert-manager/cert-manager/pkg/acme/webhook/cmd"
|
|
cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1"
|
|
|
|
cis "github.com/IBM-Cloud/bluemix-go/api/cis/cisv1"
|
|
cissession "github.com/IBM-Cloud/bluemix-go/session"
|
|
)
|
|
|
|
var GroupName = os.Getenv("GROUP_NAME")
|
|
var IbmCloudApiKey = os.Getenv("IBMCLOUD_API_KEY")
|
|
|
|
func main() {
|
|
if GroupName == "" {
|
|
log.Fatal("GROUP_NAME must be specified")
|
|
}
|
|
|
|
if IbmCloudApiKey == "" {
|
|
log.Fatal("IBMCLOUD_API_KEY must be specified")
|
|
}
|
|
|
|
cmd.RunWebhookServer(GroupName, &ibmCloudCisProviderSolver{})
|
|
}
|
|
|
|
type ibmCloudCisProviderSolver struct {
|
|
client *kubernetes.Clientset
|
|
ibmCloudCisApi cis.CisServiceAPI
|
|
}
|
|
|
|
type ibmCloudCisDnsProviderConfig struct {
|
|
IbmCloudCisCrns []string `json:"ibmCloudCisCrns"`
|
|
APIKeySecretRef cmmeta.SecretKeySelector `json:"apiKeySecretRef"`
|
|
}
|
|
|
|
func (c *ibmCloudCisProviderSolver) Name() string {
|
|
return "ibm-cloud-cis"
|
|
}
|
|
|
|
func (c *ibmCloudCisProviderSolver) Present(ch *v1alpha1.ChallengeRequest) error {
|
|
cfg, err := loadConfig(ch.Config)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
zonesApi := c.ibmCloudCisApi.Zones()
|
|
|
|
for _, crn := range cfg.IbmCloudCisCrns {
|
|
myZones, err := zonesApi.ListZones(crn)
|
|
if err != nil {
|
|
log.WithError(err).WithField("crn", crn).Error("Error listing zones")
|
|
continue
|
|
}
|
|
|
|
longestMatchZone, err := findLongestMatchingZone(myZones, ch.ResolvedFQDN)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if longestMatchZone != nil {
|
|
if err := c.createDNSChallengeRecord(crn, longestMatchZone.Id, ch); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func findLongestMatchingZone(zones []cis.Zone, fqdn string) (*cis.Zone, error) {
|
|
var longestMatchZone *cis.Zone
|
|
var longestMatchLength int
|
|
var longestMatchIndex = -1
|
|
|
|
for i, zone := range zones {
|
|
zoneNameWithDot := zone.Name + "."
|
|
if strings.HasSuffix(fqdn, zoneNameWithDot) && len(zoneNameWithDot) > longestMatchLength {
|
|
longestMatchLength = len(zoneNameWithDot)
|
|
longestMatchIndex = i
|
|
}
|
|
}
|
|
|
|
if longestMatchIndex != -1 {
|
|
longestMatchZone = &zones[longestMatchIndex]
|
|
} else {
|
|
log.Printf("No matching zone found")
|
|
return nil, fmt.Errorf("No matching zone found for fqdn: %s", fqdn)
|
|
}
|
|
|
|
return longestMatchZone, nil
|
|
}
|
|
|
|
func (c *ibmCloudCisProviderSolver) createDNSChallengeRecord(crn, zoneID string, ch *v1alpha1.ChallengeRequest) error {
|
|
dnsAPI := c.ibmCloudCisApi.Dns()
|
|
|
|
_, err := dnsAPI.CreateDns(crn, zoneID, cis.DnsBody{
|
|
Name: ch.ResolvedFQDN,
|
|
DnsType: "TXT",
|
|
Content: ch.Key,
|
|
})
|
|
|
|
log.Printf("Creating challenge TXT record %s (content: %s), crn: %s, zoneId: %s", ch.ResolvedFQDN, ch.Key, crn, zoneID)
|
|
|
|
if err != nil {
|
|
log.WithError(err).WithFields(log.Fields{"crn": crn, "zoneID": zoneID}).Error("Error creating DNS01 challenge")
|
|
return err
|
|
}
|
|
|
|
log.WithFields(log.Fields{"fqdn": ch.ResolvedFQDN, "key": ch.Key}).Info("DNS01 challenge created")
|
|
return nil
|
|
}
|
|
|
|
func (c *ibmCloudCisProviderSolver) CleanUp(ch *v1alpha1.ChallengeRequest) error {
|
|
cfg, err := loadConfig(ch.Config)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
zonesApi := c.ibmCloudCisApi.Zones()
|
|
|
|
for _, crn := range cfg.IbmCloudCisCrns {
|
|
myZones, err := zonesApi.ListZones(crn)
|
|
if err != nil {
|
|
log.WithError(err).WithField("crn", crn).Error("Error listing zones")
|
|
continue
|
|
}
|
|
|
|
longestMatchZone, err := findLongestMatchingZone(myZones, ch.ResolvedFQDN)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if longestMatchZone != nil {
|
|
if err := c.deleteMatchingTXTRecords(crn, longestMatchZone.Id, ch); err != nil {
|
|
log.WithError(err).Error("Error deleting TXT record")
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *ibmCloudCisProviderSolver) deleteMatchingTXTRecords(crn, zoneID string, ch *v1alpha1.ChallengeRequest) error {
|
|
dnsAPI := c.ibmCloudCisApi.Dns()
|
|
|
|
myDnsrecs, err := dnsAPI.ListDns(crn, zoneID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, myDnsrec := range myDnsrecs {
|
|
if myDnsrec.DnsType == "TXT" && (myDnsrec.Name+".") == ch.ResolvedFQDN && myDnsrec.Content == ch.Key {
|
|
if err := dnsAPI.DeleteDns(crn, zoneID, myDnsrec.Id); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *ibmCloudCisProviderSolver) Initialize(kubeClientConfig *rest.Config, stopCh <-chan struct{}) error {
|
|
cl, err := kubernetes.NewForConfig(kubeClientConfig)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ibmSession, err := cissession.New()
|
|
if err != nil {
|
|
return fmt.Errorf("IBM Cloud session failed: %w", err)
|
|
}
|
|
|
|
ibmCloudCisApi, err := cis.New(ibmSession)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
c.ibmCloudCisApi = ibmCloudCisApi
|
|
c.client = cl
|
|
return nil
|
|
}
|
|
|
|
func loadConfig(cfgJSON *extapi.JSON) (ibmCloudCisDnsProviderConfig, error) {
|
|
cfg := ibmCloudCisDnsProviderConfig{}
|
|
if cfgJSON == nil {
|
|
return cfg, fmt.Errorf("config JSON is nil")
|
|
}
|
|
if err := json.Unmarshal(cfgJSON.Raw, &cfg); err != nil {
|
|
return cfg, fmt.Errorf("error decoding solver config: %w", err)
|
|
}
|
|
return cfg, nil
|
|
}
|