Skip to content

Commit

Permalink
Merge pull request #106 from Hsn723/cname-delegation
Browse files Browse the repository at this point in the history
Add support for using delegated domains for DNS-01
  • Loading branch information
tkna authored Jul 24, 2024
2 parents 9b00d3b + 33e7d41 commit 662b415
Show file tree
Hide file tree
Showing 5 changed files with 356 additions and 41 deletions.
22 changes: 13 additions & 9 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ func init() {
fs.String("service-name", "", "NamespacedName of the Contour LoadBalancer Service")
fs.String("default-issuer-name", "", "Issuer name used by default")
fs.String("default-issuer-kind", controllers.ClusterIssuerKind, "Issuer kind used by default")
fs.String("default-delegated-domain", "", "Delegated domain used by default")
fs.Bool("allow-custom-delegations", false, "Allow custom delegated domains via annotations")
fs.Uint("csr-revision-limit", 0, "Maximum number of CertificateRequest revisions to keep")
fs.String("ingress-class-name", "", "Ingress class name that watched by Contour Plus. If not specified, then all classes are watched")
fs.Bool("leader-election", true, "Enable/disable leader election")
Expand All @@ -60,15 +62,17 @@ var rootCmd = &cobra.Command{
In addition to flags, the following environment variables are read:
CP_METRICS_ADDR Bind address for the metrics endpoint
CP_CRDS Comma-separated list of CRD names
CP_NAME_PREFIX Prefix of CRD names to be created
CP_SERVICE_NAME NamespacedName of the Contour LoadBalancer Service
CP_DEFAULT_ISSUER_NAME Issuer name used by default
CP_DEFAULT_ISSUER_KIND Issuer kind used by default
CP_CSR_REVISION_LIMIT Maximum number of CertificateRequest revisions to keep
CP_LEADER_ELECTION Disable leader election if set to "false"
CP_INGRESS_CLASS_NAME Ingress class name that watched by Contour Plus. If not specified, then all classes are watched`,
CP_METRICS_ADDR Bind address for the metrics endpoint
CP_CRDS Comma-separated list of CRD names
CP_NAME_PREFIX Prefix of CRD names to be created
CP_SERVICE_NAME NamespacedName of the Contour LoadBalancer Service
CP_DEFAULT_ISSUER_NAME Issuer name used by default
CP_DEFAULT_ISSUER_KIND Issuer kind used by default
CP_DEFAULT_DELEGATED_DOMAIN Delegation domain used by default
CP_ALLOW_CUSTOM_DELEGATIONS Allow custom delegated domains via annotations
CP_CSR_REVISION_LIMIT Maximum number of CertificateRequest revisions to keep
CP_LEADER_ELECTION Disable leader election if set to "false"
CP_INGRESS_CLASS_NAME Ingress class name that watched by Contour Plus. If not specified, then all classes are watched`,
RunE: func(cmd *cobra.Command, args []string) error {
cmd.SilenceUsage = true
return run()
Expand Down
88 changes: 77 additions & 11 deletions controllers/httpproxy_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package controllers
import (
"context"
"net"
"strings"

"github.com/go-logr/logr"
projectcontourv1 "github.com/projectcontour/contour/apis/projectcontour/v1"
Expand All @@ -26,22 +27,25 @@ const (
clusterIssuerNameAnnotation = "cert-manager.io/cluster-issuer"
ingressClassNameAnnotation = "kubernetes.io/ingress.class"
contourIngressClassNameAnnotation = "projectcontour.io/ingress.class"
delegatedDomainAnnotation = "contour-plus.cybozu.com/delegated-domain"
)

// HTTPProxyReconciler reconciles a HTTPProxy object
type HTTPProxyReconciler struct {
client.Client
Log logr.Logger
Scheme *runtime.Scheme
ServiceKey client.ObjectKey
IssuerKey client.ObjectKey
Prefix string
DefaultIssuerName string
DefaultIssuerKind string
CSRRevisionLimit uint
CreateDNSEndpoint bool
CreateCertificate bool
IngressClassName string
Log logr.Logger
Scheme *runtime.Scheme
ServiceKey client.ObjectKey
IssuerKey client.ObjectKey
Prefix string
DefaultIssuerName string
DefaultIssuerKind string
DefaultDelegatedDomain string
AllowCustomDelegations bool
CSRRevisionLimit uint
CreateDNSEndpoint bool
CreateCertificate bool
IngressClassName string
}

// +kubebuilder:rbac:groups=projectcontour.io,resources=httpproxies,verbs=get;list;watch
Expand Down Expand Up @@ -89,6 +93,11 @@ func (r *HTTPProxyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
return ctrl.Result{}, err
}

if err := r.reconcileDelegationDNSEndpoint(ctx, hp, log); err != nil {
log.Error(err, "unable to reconcile delegation DNSEndpoint")
return ctrl.Result{}, err
}

if err := r.reconcileCertificate(ctx, hp, log); err != nil {
log.Error(err, "unable to reconcile Certificate")
return ctrl.Result{}, err
Expand Down Expand Up @@ -182,6 +191,51 @@ func (r *HTTPProxyReconciler) reconcileDNSEndpoint(ctx context.Context, hp *proj
return nil
}

func (r *HTTPProxyReconciler) reconcileDelegationDNSEndpoint(ctx context.Context, hp *projectcontourv1.HTTPProxy, log logr.Logger) error {
if !r.CreateDNSEndpoint {
return nil
}

delegatedDomain := r.DefaultDelegatedDomain
if hp.Annotations[delegatedDomainAnnotation] != "" && r.AllowCustomDelegations {
delegatedDomain = hp.Annotations[delegatedDomainAnnotation]
}

if delegatedDomain == "" {
return nil
}

if hp.Spec.VirtualHost == nil {
return nil
}
fqdn := hp.Spec.VirtualHost.Fqdn
if len(fqdn) == 0 {
return nil
}

obj := &unstructured.Unstructured{}
obj.SetGroupVersionKind(externalDNSGroupVersion.WithKind(DNSEndpointKind))
obj.SetName(r.Prefix + hp.Name + "-delegation")
obj.SetNamespace(hp.Namespace)
obj.UnstructuredContent()["spec"] = map[string]interface{}{
"endpoints": makeDelegationEndpoint(fqdn, delegatedDomain),
}

if err := ctrl.SetControllerReference(hp, obj, r.Scheme); err != nil {
return err
}

if err := r.Patch(ctx, obj, client.Apply, &client.PatchOptions{
Force: ptr.To(true),
FieldManager: "contour-plus",
}); err != nil {
return err
}

log.Info("Delegation DNSEndpoint successfully reconciled")
return nil
}

func (r *HTTPProxyReconciler) reconcileCertificate(ctx context.Context, hp *projectcontourv1.HTTPProxy, log logr.Logger) error {
if !r.CreateCertificate {
return nil
Expand Down Expand Up @@ -336,3 +390,15 @@ func ipsToTargets(ips []net.IP) ([]string, []string) {
}
return ipv4Targets, ipv6Targets
}

func makeDelegationEndpoint(hostname, delegatedDomain string) []map[string]interface{} {
fqdn := strings.Trim(hostname, ".")
return []map[string]interface{}{
{
"dnsName": "_acme-challenge." + fqdn,
"targets": []string{"_acme-challenge." + fqdn + "." + delegatedDomain},
"recordType": "CNAME",
"recordTTL": 3600,
},
}
}
Loading

0 comments on commit 662b415

Please sign in to comment.