Skip to content

Commit ebc3cba

Browse files
authored
Merge pull request #118 from datum-cloud/feat/sharedtlsecretfordefaulthttps
feat: shared tls secret for default https listener
2 parents 5a64f09 + fc5ec8d commit ebc3cba

14 files changed

+956
-250
lines changed

cmd/main.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
_ "k8s.io/client-go/plugin/pkg/client/auth"
1414

1515
cmacmev1 "github.com/cert-manager/cert-manager/pkg/apis/acme/v1"
16+
cmv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
1617
envoygatewayv1alpha1 "github.com/envoyproxy/gateway/api/v1alpha1"
1718
multiclusterproviders "go.miloapis.com/milo/pkg/multicluster-runtime"
1819
milomulticluster "go.miloapis.com/milo/pkg/multicluster-runtime/milo"
@@ -65,6 +66,7 @@ func init() {
6566
utilruntime.Must(envoygatewayv1alpha1.AddToScheme(scheme))
6667
utilruntime.Must(networkingv1alpha1.AddToScheme(scheme))
6768
utilruntime.Must(cmacmev1.AddToScheme(scheme))
69+
utilruntime.Must(cmv1.AddToScheme(scheme))
6870
utilruntime.Must(dnsv1alpha1.AddToScheme(scheme))
6971
// +kubebuilder:scaffold:scheme
7072
}
@@ -121,6 +123,7 @@ func main() {
121123
// TODO(jreese) validate the config
122124

123125
cfg := ctrl.GetConfigOrDie()
126+
serverConfig.ControlPlaneClient.ApplyTo(cfg)
124127

125128
deploymentCluster, err := cluster.New(cfg, func(o *cluster.Options) {
126129
o.Scheme = scheme
@@ -187,6 +190,7 @@ func main() {
187190
setupLog.Error(err, "unable to load control plane kubeconfig")
188191
os.Exit(1)
189192
}
193+
serverConfig.DownstreamClient.ApplyTo(downstreamRestConfig)
190194

191195
downstreamCluster, err := cluster.New(downstreamRestConfig, func(o *cluster.Options) {
192196
o.Scheme = scheme
@@ -439,11 +443,13 @@ func initializeClusterDiscovery(
439443
if err != nil {
440444
return nil, nil, fmt.Errorf("unable to get discovery rest config: %w", err)
441445
}
446+
serverConfig.ProjectClient.ApplyTo(discoveryRestConfig)
442447

443448
projectRestConfig, err := serverConfig.Discovery.ProjectRestConfig()
444449
if err != nil {
445450
return nil, nil, fmt.Errorf("unable to get project rest config: %w", err)
446451
}
452+
serverConfig.ProjectClient.ApplyTo(projectRestConfig)
447453

448454
discoveryManager, err := manager.New(discoveryRestConfig, manager.Options{
449455
Client: client.Options{

config/rbac/role.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,12 @@ rules:
5252
resources:
5353
- certificates
5454
verbs:
55+
- create
56+
- delete
5557
- get
5658
- list
59+
- patch
60+
- update
5761
- watch
5862
- apiGroups:
5963
- coordination.k8s.io

config/rbac_downstream/role.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,12 @@ rules:
139139
resources:
140140
- certificates
141141
verbs:
142+
- create
143+
- delete
142144
- get
143145
- list
146+
- patch
147+
- update
144148
- watch
145149
- apiGroups:
146150
- gateway.envoyproxy.io

internal/config/config.go

Lines changed: 66 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,57 @@ type NetworkServicesOperator struct {
6060

6161
// DomainRegistration controls RDAP/WHOIS refresh behavior for Domain status.registration
6262
DomainRegistration DomainRegistrationConfig `json:"domainRegistration"`
63+
64+
// ControlPlaneClient configures the Kubernetes client connection to the
65+
// control plane where the operator runs (leader election, multicluster
66+
// coordination).
67+
ControlPlaneClient ClientConnectionConfig `json:"controlPlaneClient,omitempty"`
68+
69+
// DownstreamClient configures the Kubernetes client connection to the
70+
// downstream cluster where Gateways, HTTPRoutes, Certificates, and other
71+
// data-plane resources are materialized.
72+
DownstreamClient ClientConnectionConfig `json:"downstreamClient,omitempty"`
73+
74+
// ProjectClient configures the Kubernetes client connection used for both
75+
// project discovery and per-project cluster connections.
76+
ProjectClient ClientConnectionConfig `json:"projectClient,omitempty"`
77+
}
78+
79+
// +k8s:deepcopy-gen=true
80+
81+
// ClientConnectionConfig holds settings that control how the operator connects
82+
// to a Kubernetes API server.
83+
type ClientConnectionConfig struct {
84+
// QPS is the maximum sustained queries per second before client-side
85+
// throttling kicks in.
86+
//
87+
// +default=50
88+
QPS float32 `json:"qps,omitempty"`
89+
90+
// Burst is the maximum burst size for throttle. Requests above QPS but
91+
// below Burst are allowed immediately.
92+
//
93+
// +default=100
94+
Burst int `json:"burst,omitempty"`
95+
}
96+
97+
// ApplyTo applies the client connection settings to a rest.Config.
98+
func (c *ClientConnectionConfig) ApplyTo(cfg *rest.Config) {
99+
if c.QPS > 0 {
100+
cfg.QPS = c.QPS
101+
}
102+
if c.Burst > 0 {
103+
cfg.Burst = c.Burst
104+
}
105+
}
106+
107+
func SetDefaults_ClientConnectionConfig(obj *ClientConnectionConfig) {
108+
if obj.QPS == 0 {
109+
obj.QPS = 50
110+
}
111+
if obj.Burst == 0 {
112+
obj.Burst = 100
113+
}
63114
}
64115

65116
// +k8s:deepcopy-gen=true
@@ -486,17 +537,6 @@ type GatewayConfig struct {
486537
// issuer name, the operator will use the value as is.
487538
ClusterIssuerMap map[string]string `json:"clusterIssuerMap,omitempty"`
488539

489-
// PerGatewayCertificateIssuer will result in the operator to expect a
490-
// cert-manager Issuer to exist with the same name as the gateway. Any value
491-
// provided for the "gateway.networking.datumapis.com/certificate-issuer"
492-
// option will be replaced with the gateway's name. The Issuer resources will
493-
// be managed by Kyverno policies, and not by this operator.
494-
//
495-
// TODO(jreese) Remove this once we've either implemented DNS validation,
496-
// found a path to attach cert-manager generated routes to the gateway they're
497-
// needed for, or implement our own ACME integration.
498-
PerGatewayCertificateIssuer bool `json:"perGatewayCertificateIssuer,omitempty"`
499-
500540
// ListenerTLSOptions specifies the TLS options to program on generated
501541
// TLS listeners.
502542
// +default={"gateway.networking.datumapis.com/certificate-issuer": "auto"}
@@ -559,6 +599,15 @@ type GatewayConfig struct {
559599
// Defaults to false.
560600
EnableDNSIntegration bool `json:"enableDNSIntegration,omitempty"`
561601

602+
// DefaultListenerTLSSecretName, if provided, is the name of a
603+
// pre-provisioned TLS certificate secret to use for the default HTTPS
604+
// listener (named "default-https"). When set, this listener references
605+
// the shared secret instead of requesting an individual certificate
606+
// via cert-manager.
607+
//
608+
// The secret must exist in every downstream gateway namespace.
609+
DefaultListenerTLSSecretName string `json:"defaultListenerTLSSecretName,omitempty"`
610+
562611
// MaxConcurrentReconciles is the maximum number of concurrent gateway
563612
// reconciliations. Higher values allow the controller to process gateways
564613
// across multiple projects in parallel.
@@ -567,6 +616,12 @@ type GatewayConfig struct {
567616
MaxConcurrentReconciles int `json:"maxConcurrentReconciles,omitempty"`
568617
}
569618

619+
// HasDefaultListenerTLSSecret returns true when a shared TLS certificate
620+
// secret has been configured for default HTTPS listeners.
621+
func (c *GatewayConfig) HasDefaultListenerTLSSecret() bool {
622+
return c.DefaultListenerTLSSecretName != ""
623+
}
624+
570625
// ShouldDeleteErroredChallenges returns whether the operator should automatically
571626
// delete errored ACME challenges. Defaults to true if not explicitly set.
572627
func (c *GatewayConfig) ShouldDeleteErroredChallenges() bool {

internal/config/zz_generated.deepcopy.go

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/config/zz_generated.defaults.go

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/controller/challenge_controller.go

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -81,25 +81,16 @@ func (r *ChallengeReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
8181
}
8282

8383
// isGatewayRelatedIssuer checks if the given issuer reference is for a Gateway-managed
84-
// certificate issuer. This includes:
85-
// - ClusterIssuers that are mapped in the ClusterIssuerMap configuration
86-
// - Issuers (namespace-scoped) when PerGatewayCertificateIssuer mode is enabled
84+
// certificate issuer (ClusterIssuers that are mapped in the ClusterIssuerMap configuration).
8785
func (r *ChallengeReconciler) isGatewayRelatedIssuer(ref cmmeta.ObjectReference) bool {
88-
// Check if ClusterIssuer is in the configured map
8986
if ref.Kind == "ClusterIssuer" || ref.Kind == "" {
90-
// Default kind is ClusterIssuer for cert-manager
9187
for _, mappedIssuer := range r.Config.Gateway.ClusterIssuerMap {
9288
if mappedIssuer == ref.Name {
9389
return true
9490
}
9591
}
9692
}
9793

98-
// In per-gateway mode, any namespace-scoped Issuer is gateway-related
99-
if r.Config.Gateway.PerGatewayCertificateIssuer && ref.Kind == "Issuer" {
100-
return true
101-
}
102-
10394
return false
10495
}
10596

internal/controller/challenge_controller_test.go

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ func TestChallengeReconciler(t *testing.T) {
7575
expectDeleted: true,
7676
},
7777
{
78-
name: "errored challenge with Issuer in per-gateway mode is deleted",
78+
name: "errored challenge with namespace-scoped Issuer is not deleted",
7979
challenge: &cmacmev1.Challenge{
8080
ObjectMeta: metav1.ObjectMeta{
8181
Namespace: upstreamNamespace.Name,
@@ -95,11 +95,9 @@ func TestChallengeReconciler(t *testing.T) {
9595
},
9696
},
9797
config: config.NetworkServicesOperator{
98-
Gateway: config.GatewayConfig{
99-
PerGatewayCertificateIssuer: true,
100-
},
98+
Gateway: config.GatewayConfig{},
10199
},
102-
expectDeleted: true,
100+
expectDeleted: false,
103101
},
104102
{
105103
name: "errored challenge with non-gateway ClusterIssuer is not deleted",
@@ -258,7 +256,6 @@ func TestChallengeReconciler(t *testing.T) {
258256
},
259257
config: config.NetworkServicesOperator{
260258
Gateway: config.GatewayConfig{
261-
PerGatewayCertificateIssuer: false,
262259
ClusterIssuerMap: map[string]string{
263260
"letsencrypt": "letsencrypt-prod",
264261
},
@@ -368,25 +365,12 @@ func TestIsGatewayRelatedIssuer(t *testing.T) {
368365
expected: true,
369366
},
370367
{
371-
name: "Issuer in per-gateway mode returns true",
372-
ref: cmmeta.ObjectReference{
373-
Kind: "Issuer",
374-
Name: "gateway-issuer",
375-
},
376-
config: config.GatewayConfig{
377-
PerGatewayCertificateIssuer: true,
378-
},
379-
expected: true,
380-
},
381-
{
382-
name: "Issuer not in per-gateway mode returns false",
368+
name: "namespace-scoped Issuer returns false",
383369
ref: cmmeta.ObjectReference{
384370
Kind: "Issuer",
385371
Name: "gateway-issuer",
386372
},
387-
config: config.GatewayConfig{
388-
PerGatewayCertificateIssuer: false,
389-
},
373+
config: config.GatewayConfig{},
390374
expected: false,
391375
},
392376
{

0 commit comments

Comments
 (0)