Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨Add flag to disable default CSR controller #1480

Merged
merged 2 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 15 additions & 12 deletions controllers/hetznercluster_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ type HetznerClusterReconciler struct {
targetClusterManagersLock sync.Mutex
TargetClusterManagersWaitGroup *sync.WaitGroup
WatchFilterValue string
DisableCSRApproval bool
}

//+kubebuilder:rbac:groups=cluster.x-k8s.io,resources=clusters;clusters/status,verbs=get;list;watch
Expand Down Expand Up @@ -708,19 +709,21 @@ func (r *HetznerClusterReconciler) newTargetClusterManager(ctx context.Context,
return nil, fmt.Errorf("failed to setup guest cluster manager: %w", err)
}

gr := &GuestCSRReconciler{
Client: clusterMgr.GetClient(),
mCluster: &managementCluster{
Client: r.Client,
hetznerCluster: hetznerCluster,
},
WatchFilterValue: r.WatchFilterValue,
clientSet: clientSet,
clusterName: clusterScope.Cluster.Name,
}
if !r.DisableCSRApproval {
gr := &GuestCSRReconciler{
Client: clusterMgr.GetClient(),
mCluster: &managementCluster{
Client: r.Client,
hetznerCluster: hetznerCluster,
},
WatchFilterValue: r.WatchFilterValue,
clientSet: clientSet,
clusterName: clusterScope.Cluster.Name,
}

if err := gr.SetupWithManager(ctx, clusterMgr, controller.Options{}); err != nil {
return nil, fmt.Errorf("failed to setup CSR controller: %w", err)
if err := gr.SetupWithManager(ctx, clusterMgr, controller.Options{}); err != nil {
return nil, fmt.Errorf("failed to setup CSR controller: %w", err)
}
}

return clusterMgr, nil
Expand Down
19 changes: 18 additions & 1 deletion docs/caph/02-topics/06-advanced/01-csr-controller.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ title: CSR controller

For the secure operation of Kubernetes, it is necessary to sign the kubelet serving certificates. By default, these are self-signed by kubeadm. By using the kubelet flag `rotate-server-certificates: "true"`, which can be found in initConfiguration/joinConfiguration.nodeRegistration.kubeletExtraArgs, the kubelet will do a certificate signing request (CSR) to the certificates API of Kubernetes.

These CSRs are not approved by default for security reasons. As described in the docs, this should be done manually by the cloud provider or with a custom approval controller. Since the provider integration is the responsible cloud provider in a way, it makes sense to implement such a controller directly here. The CSR controller that we implemented checks the DNS name and the IP address and thus ensures that only those nodes receive the signed certificate that are supposed to.
These CSRs are not approved by default for security reasons. As described in the docs, this should be done manually by the cloud provider or with a custom approval controller.

## Default CSR controller

Since the provider integration is the responsible cloud provider in a way, it makes sense to implement such a controller directly here. The CSR controller that we implemented checks the DNS name and the IP address and thus ensures that only those nodes receive the signed certificate that are supposed to.

For error-free operation, the following kubelet flags should not be set:

Expand All @@ -17,3 +21,16 @@ For more information, see:

- [https://kubernetes.io/docs/tasks/administer-cluster/kubeadm/kubeadm-certs/](https://kubernetes.io/docs/tasks/administer-cluster/kubeadm/kubeadm-certs/)
- [https://kubernetes.io/docs/reference/access-authn-authz/kubelet-tls-bootstrapping/#client-and-serving-certificates](https://kubernetes.io/docs/reference/access-authn-authz/kubelet-tls-bootstrapping/#client-and-serving-certificates)

## Custom CSR controller

It is possible to disable the CSR controller using the flag `--disable-csr-approval`. However, this flag disables this feature globally, for all `HetznerCluster` objects in the management cluster. There is currently no way to toggle this on or off for just a single cluster.

This is useful for cases where the validation or approval logic of the default CSR controller is insufficient for your use cases.

If you disable the default CSR controller, you'll need to deploy an equivalent controller that can validate and approve CSRs securely. This is a security critical process and needs to be handled with care.

For more information, see:

- [https://kubernetes.io/docs/reference/access-authn-authz/certificate-signing-requests/](https://kubernetes.io/docs/reference/access-authn-authz/certificate-signing-requests/)
- [https://kubernetes.io/docs/reference/access-authn-authz/kubelet-tls-bootstrapping/#certificate-rotation](https://kubernetes.io/docs/reference/access-authn-authz/kubelet-tls-bootstrapping/#certificate-rotation)
3 changes: 3 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ func init() {
var (
metricsAddr string
enableLeaderElection bool
disableCSRApproval bool
leaderElectionNamespace string
probeAddr string
watchFilterValue string
Expand All @@ -83,6 +84,7 @@ func main() {
fs.StringVar(&metricsAddr, "metrics-bind-address", "localhost:8080", "The address the metric endpoint binds to.")
fs.StringVar(&probeAddr, "health-probe-bind-address", ":9440", "The address the probe endpoint binds to.")
fs.BoolVar(&enableLeaderElection, "leader-elect", true, "Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager.")
fs.BoolVar(&disableCSRApproval, "disable-csr-approval", false, "Disables builtin workload cluster CSR validation and approval.")
fs.StringVar(&leaderElectionNamespace, "leader-elect-namespace", "", "Namespace that the controller performs leader election in. If unspecified, the controller will discover which namespace it is running in.")
fs.StringVar(&watchFilterValue, "watch-filter", "", fmt.Sprintf("Label value that the controller watches to reconcile cluster-api objects. Label key is always %s. If unspecified, the controller watches for all cluster-api objects.", clusterv1.WatchLabel))
fs.StringVar(&watchNamespace, "namespace", "", "Namespace that the controller watches to reconcile cluster-api objects. If unspecified, the controller watches for cluster-api objects across all namespaces.")
Expand Down Expand Up @@ -147,6 +149,7 @@ func main() {
RateLimitWaitTime: rateLimitWaitTime,
HCloudClientFactory: hcloudClientFactory,
WatchFilterValue: watchFilterValue,
DisableCSRApproval: disableCSRApproval,
TargetClusterManagersWaitGroup: &wg,
}).SetupWithManager(ctx, mgr, controller.Options{MaxConcurrentReconciles: hetznerClusterConcurrency}); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "HetznerCluster")
Expand Down