Skip to content
Open
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
7 changes: 3 additions & 4 deletions cmd/liqo-controller-manager/modules/authentication.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import (
"github.com/liqotech/liqo/pkg/liqo-controller-manager/authentication"
identitycontroller "github.com/liqotech/liqo/pkg/liqo-controller-manager/authentication/identity-controller"
identitycreatorcontroller "github.com/liqotech/liqo/pkg/liqo-controller-manager/authentication/identitycreator-controller"
localrenwercontroller "github.com/liqotech/liqo/pkg/liqo-controller-manager/authentication/localrenwer-controller"
localrenwercontroller "github.com/liqotech/liqo/pkg/liqo-controller-manager/authentication/localrenewer-controller"
localresourceslicecontroller "github.com/liqotech/liqo/pkg/liqo-controller-manager/authentication/localresourceslice-controller"
noncecreatorcontroller "github.com/liqotech/liqo/pkg/liqo-controller-manager/authentication/noncecreator-controller"
noncesigner "github.com/liqotech/liqo/pkg/liqo-controller-manager/authentication/noncesigner-controller"
Expand Down Expand Up @@ -161,16 +161,15 @@ func SetupAuthenticationModule(ctx context.Context, mgr manager.Manager, uncache

// Configure controllers that handle the certificate rotation.
localRenewerReconciler := localrenwercontroller.NewLocalRenewerReconciler(mgr.GetClient(), mgr.GetScheme(),
opts.LiqoNamespace, opts.LocalClusterID,
opts.LocalClusterID,
mgr.GetEventRecorderFor("local-renewer-controller"))
if err := localRenewerReconciler.SetupWithManager(mgr); err != nil {
klog.Errorf("Unable to setup the local renewer reconciler: %v", err)
return err
}

remoteRenewerReconciler := remoterenwercontroller.NewRemoteRenewerReconciler(mgr.GetClient(), mgr.GetScheme(),
opts.IdentityProvider, opts.NamespaceManager,
opts.APIServerAddressOverride, caOverride, opts.TrustedCA,
opts.NamespaceManager,
mgr.GetEventRecorderFor("remote-renewer-controller"))
if err := remoteRenewerReconciler.SetupWithManager(mgr); err != nil {
klog.Errorf("Unable to setup the remote renewer reconciler: %v", err)
Expand Down
1 change: 1 addition & 0 deletions cmd/virtual-kubelet/root/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ func runRootCommand(ctx context.Context, c *Opts) error {
return err
}

restcfg.UpdateCfgCertOnSecretChange(ctx, remoteConfig, localClient, c.TenantNamespace, c.RemoteKubeconfigSecretName)
restcfg.SetRateLimiter(remoteConfig)

// Get reflectors configurations
Expand Down
3 changes: 0 additions & 3 deletions pkg/consts/authentication.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,6 @@ const (
// CordonTenantAnnotation is the value of the annotation that enables the cordon of a tenant.
CordonTenantAnnotation = "liqo.io/cordon-tenant"

// RenewAnnotation is the value of the annotation that enables the renewal of a resource.
RenewAnnotation = "liqo.io/renew"

// PeeringUserNameLabelKey labels all the resources created to grant peering permissions to the user doing a pering toward this cluster.
PeeringUserNameLabelKey = "liqo.io/peering-user-name"
)
8 changes: 3 additions & 5 deletions pkg/identityManager/certificateIdentityProvider.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,9 @@ func (identityProvider *certificateIdentityProvider) GetRemoteCertificate(ctx co
return response, err
}

// check that this certificate is related to this signing request
if !bytes.Equal(signingRequestSecret, options.SigningRequest) && !options.IsUpdate {
err = kerrors.NewBadRequest(fmt.Sprintf("the stored and the provided CSR for cluster %s does not match", options.Cluster))
klog.Error(err)
return response, err
if !bytes.Equal(signingRequestSecret, options.SigningRequest) {
klog.Errorf("the stored and the provided CSR for cluster %s does not match", options.Cluster)
return response, NotMatchingCSRError
}

response.Certificate, ok = secret.Data[certificateSecretKey]
Expand Down
6 changes: 5 additions & 1 deletion pkg/identityManager/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"context"

corev1 "k8s.io/api/core/v1"
kerrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/rest"

Expand All @@ -27,6 +28,9 @@ import (
responsetypes "github.com/liqotech/liqo/pkg/identityManager/responseTypes"
)

// NotMatchingCSRError is returned when the previously stored and CSR provided to the identity manager do not match.
var NotMatchingCSRError = kerrors.NewBadRequest("the stored and the provided CSR do not match")

// IdentityReader provides the interface to retrieve the identities for the remote clusters.
type IdentityReader interface {
GetConfig(remoteCluster liqov1beta1.ClusterID, namespace string) (*rest.Config, error)
Expand Down Expand Up @@ -57,7 +61,7 @@ type SigningRequestOptions struct {
TrustedCA bool
ResourceSlice *authv1beta1.ResourceSlice
ProxyURL *string
IsUpdate bool
IsRenew bool
}

// IdentityProvider provides the interface to retrieve and approve remote cluster identities.
Expand Down
12 changes: 12 additions & 0 deletions pkg/identityManager/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,22 @@ import (
)

// EnsureCertificate ensures that the certificate is present with the identity provider.
// When IsUpdate is true, it skips the cache entirely and directly approves a new signing request,
// which is used to force certificate renewal when the provider detects an expiring certificate.
func EnsureCertificate(ctx context.Context, idp IdentityProvider, options *SigningRequestOptions) (*responsetypes.SigningRequestResponse, error) {
if options.IsRenew {
// Skip cache entirely, directly approve a new signing request.
resp, err := idp.ApproveSigningRequest(ctx, options)
if err != nil {
return nil, err
}
return resp, nil
}

resp, err := idp.GetRemoteCertificate(ctx, options)
switch {
case apierrors.IsNotFound(err):
// Certificate not found or CSR changed — generate new cert.
resp, err = idp.ApproveSigningRequest(ctx, options)
if err != nil {
return nil, err
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright 2019-2026 The Liqo Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package localrenewercontroller contains the logic of the LocalRenewer controller, in charge of making sure to update the AuthParams
// of the Identity resource in the local cluster, whenever the certificate is renewed.
package localrenewercontroller
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
// Copyright 2019-2026 The Liqo Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package localrenewercontroller

import (
"bytes"
"context"
"fmt"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/tools/record"
"k8s.io/klog/v2"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/predicate"

authv1beta1 "github.com/liqotech/liqo/apis/authentication/v1beta1"
liqov1beta1 "github.com/liqotech/liqo/apis/core/v1beta1"
"github.com/liqotech/liqo/pkg/consts"
"github.com/liqotech/liqo/pkg/utils/events"
)

// LocalRenewerReconciler reconciles an Identity object.
type LocalRenewerReconciler struct {
client.Client
Scheme *runtime.Scheme

LocalClusterID liqov1beta1.ClusterID
recorder record.EventRecorder
}

// NewLocalRenewerReconciler returns a new LocalRenewerReconciler.
func NewLocalRenewerReconciler(cl client.Client, s *runtime.Scheme,
localClusterID liqov1beta1.ClusterID,
recorder record.EventRecorder) *LocalRenewerReconciler {
return &LocalRenewerReconciler{
Client: cl,
Scheme: s,
LocalClusterID: localClusterID,
recorder: recorder,
}
}

//+kubebuilder:rbac:groups=authentication.liqo.io,resources=identities,verbs=get;list;watch;update;patch
//+kubebuilder:rbac:groups=authentication.liqo.io,resources=identities/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=authentication.liqo.io,resources=identities/finalizers,verbs=update
//+kubebuilder:rbac:groups=authentication.liqo.io,resources=renews,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=authentication.liqo.io,resources=renews/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=authentication.liqo.io,resources=renews/finalizers,verbs=update

// Reconcile ensures a permanent Renew object exists for the Identity, and syncs
// renewed AuthParams from the Renew status back to the Identity spec.
// Certificate renewal is driven by the provider (Tenant controller); the consumer
// only maintains the Renew as a sync channel.
func (r *LocalRenewerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
var identity authv1beta1.Identity
if err := r.Get(ctx, req.NamespacedName, &identity); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}

// Skip reconciliation if the Identity is being deleted.
if !identity.DeletionTimestamp.IsZero() {
return ctrl.Result{}, nil
}

// Ensure a permanent Renew object exists for this Identity.
if err := r.ensureRenew(ctx, &identity); err != nil {
klog.Errorf("Unable to ensure Renew for Identity %q: %s", req.NamespacedName, err)
return ctrl.Result{}, err
}

// Check if there's renewed AuthParams and update the Identity accordingly.
if err := r.handleRenewedAuthParams(ctx, &identity); err != nil {
klog.Errorf("Unable to handle renewed AuthParams for Identity %q: %s", req.NamespacedName, err)
return ctrl.Result{}, err
}

return ctrl.Result{}, nil
}

// SetupWithManager sets up the controller with the Manager.
func (r *LocalRenewerReconciler) SetupWithManager(mgr ctrl.Manager) error {
// Only reconcile ControlPlane identities.
controlPlaneFilter := predicate.NewPredicateFuncs(func(object client.Object) bool {
identity, ok := object.(*authv1beta1.Identity)
if !ok {
return false
}
return identity.Spec.Type == authv1beta1.ControlPlaneIdentityType
})

return ctrl.NewControllerManagedBy(mgr).Named(consts.CtrlRenewLocal).
Owns(&authv1beta1.Renew{}).
For(&authv1beta1.Identity{}, builder.WithPredicates(controlPlaneFilter)).
Complete(r)
}

// ensureRenew ensures a permanent Renew object exists for the given Identity.
// The Renew is created once and never deleted; it serves as a sync channel
// between consumer and provider clusters via the CRD replicator.
func (r *LocalRenewerReconciler) ensureRenew(ctx context.Context, identity *authv1beta1.Identity) error {
renew := &authv1beta1.Renew{
ObjectMeta: metav1.ObjectMeta{
Name: identity.Name,
Namespace: identity.Namespace,
},
}

_, err := controllerutil.CreateOrUpdate(ctx, r.Client, renew, func() error {
if renew.Labels == nil {
renew.Labels = make(map[string]string)
}
renew.Labels[consts.ReplicationRequestedLabel] = consts.ReplicationRequestedLabelValue
renew.Labels[consts.ReplicationDestinationLabel] = string(identity.Spec.ClusterID)
renew.Labels[consts.RemoteClusterID] = string(identity.Spec.ClusterID)

if err := controllerutil.SetControllerReference(identity, renew, r.Scheme); err != nil {
return err
}

renew.Spec.ConsumerClusterID = r.LocalClusterID
renew.Spec.IdentityType = identity.Spec.Type

return nil
})

return err
}

// handleRenewedAuthParams checks if the Renew object has newer AuthParams than the Identity.
// If found, it updates the Identity's Spec.AuthParams with the renewed values.
func (r *LocalRenewerReconciler) handleRenewedAuthParams(ctx context.Context, identity *authv1beta1.Identity) error {
var renew authv1beta1.Renew
if err := r.Get(ctx, client.ObjectKey{
Namespace: identity.Namespace,
Name: identity.Name,
}, &renew); err != nil {
return client.IgnoreNotFound(err)
}

// If the Renew is being deleted or has no AuthParams yet, skip.
if renew.DeletionTimestamp != nil || renew.Status.AuthParams == nil {
return nil
}

// If the certificate is already up to date, skip.
if bytes.Equal(renew.Status.AuthParams.SignedCRT, identity.Spec.AuthParams.SignedCRT) {
return nil
}

// Update the Identity's AuthParams with the renewed values.
identity.Spec.AuthParams = *renew.Status.AuthParams

if err := r.Update(ctx, identity); err != nil {
events.EventWithOptions(r.recorder, identity, fmt.Sprintf("Failed to update AuthParams: %s", err),
&events.Option{EventType: events.Error, Reason: "AuthParamsUpdateFailed"})
return fmt.Errorf("failed to update Identity AuthParams: %w", err)
}

klog.V(4).Infof("Updated AuthParams for Identity %s/%s from completed Renew", identity.Namespace, identity.Name)
events.Event(r.recorder, identity, "Updated AuthParams from completed certificate renewal")

return nil
}

This file was deleted.

Loading
Loading