Skip to content

Commit

Permalink
feat(clusterController): check actual token validity (#657)
Browse files Browse the repository at this point in the history
* fix(pkg) imports

* feat(token) check the token and not rely on status

* fix: use metav1 instead of time

* gofmt

* fix test

* Update pkg/controllers/cluster/util.go

Co-authored-by: IvoGoman <[email protected]>

* fix(clustercontroller) if token parse failed skip some steps

* fix lint

---------

Co-authored-by: IvoGoman <[email protected]>
  • Loading branch information
kengou and IvoGoman authored Oct 28, 2024
1 parent 0307f94 commit 2e53035
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 21 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ require (
github.com/cenkalti/backoff/v4 v4.3.0
github.com/dexidp/dex v0.0.0-20240807174518-43956db7fd75
github.com/ghodss/yaml v1.0.0
github.com/go-jose/go-jose/v4 v4.0.4
github.com/jeremywohl/flatten/v2 v2.0.0-20211013061545-07e4a09fb8e4
github.com/oklog/run v1.1.1-0.20240127200640-eee6e044b77c
github.com/onsi/ginkgo/v2 v2.20.2
Expand Down Expand Up @@ -62,7 +63,6 @@ require (
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
github.com/creack/pty v1.1.23 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/go-jose/go-jose/v4 v4.0.4 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
Expand Down
3 changes: 1 addition & 2 deletions pkg/controllers/cluster/cluster_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import (
"context"
"time"

"github.com/cloudoperators/greenhouse/pkg/lifecycle"

"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
Expand All @@ -22,6 +20,7 @@ import (
greenhouseapis "github.com/cloudoperators/greenhouse/pkg/apis"
greenhousev1alpha1 "github.com/cloudoperators/greenhouse/pkg/apis/greenhouse/v1alpha1"
"github.com/cloudoperators/greenhouse/pkg/clientutil"
"github.com/cloudoperators/greenhouse/pkg/lifecycle"
)

const serviceAccountName = "greenhouse"
Expand Down
4 changes: 1 addition & 3 deletions pkg/controllers/cluster/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@ import (
"errors"
"time"

"github.com/cloudoperators/greenhouse/pkg/lifecycle"

"github.com/go-logr/logr"

corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
Expand All @@ -20,6 +17,7 @@ import (

greenhousev1alpha1 "github.com/cloudoperators/greenhouse/pkg/apis/greenhouse/v1alpha1"
"github.com/cloudoperators/greenhouse/pkg/clientutil"
"github.com/cloudoperators/greenhouse/pkg/lifecycle"
)

func (r *RemoteClusterReconciler) setConditions() lifecycle.Conditioner {
Expand Down
79 changes: 73 additions & 6 deletions pkg/controllers/cluster/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ package cluster

import (
"context"
"encoding/json"
"fmt"
"time"

"github.com/go-jose/go-jose/v4"
authenticationv1 "k8s.io/api/authentication/v1"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
Expand All @@ -24,7 +26,24 @@ import (
"github.com/cloudoperators/greenhouse/pkg/clientutil"
)

var defaultRequeueInterval = 10 * time.Minute
var (
defaultRequeueInterval = 10 * time.Minute
allSignatureAlgorithms = []jose.SignatureAlgorithm{
jose.EdDSA,
jose.HS256,
jose.HS384,
jose.HS512,
jose.RS256,
jose.RS384,
jose.RS512,
jose.ES256,
jose.ES384,
jose.ES512,
jose.PS256,
jose.PS384,
jose.PS512,
}
)

const (
CRoleKind = "ClusterRole"
Expand All @@ -43,6 +62,31 @@ type KubeConfigHelper struct {
ClientKeyData []byte
}

type claims struct {
Issuer string `json:"iss,omitempty"`
Subject string `json:"sub,omitempty"`
Audience []string `json:"aud,omitempty"`
Expiry int64 `json:"exp,omitempty"`
NotBefore int64 `json:"nbf,omitempty"`
IssuedAt int64 `json:"iat,omitempty"`
ID string `json:"jti,omitempty"`
Kubernetes kubernetesClaims `json:"kubernetes.io,omitempty"`
}

type kubernetesClaims struct {
Namespace string `json:"namespace,omitempty"`
Svcacct ref `json:"serviceaccount,omitempty"`
Pod *ref `json:"pod,omitempty"`
Secret *ref `json:"secret,omitempty"`
Node *ref `json:"node,omitempty"`
WarnAfter int64 `json:"warnafter,omitempty"`
}

type ref struct {
Name string `json:"name,omitempty"`
UID string `json:"uid,omitempty"`
}

// RestConfigToAPIConfig converts a rest config to a clientcmdapi.Config
func (kubeconfig *KubeConfigHelper) RestConfigToAPIConfig(clusterName string) clientcmdapi.Config {
clientConfig := clientcmdapi.NewConfig()
Expand Down Expand Up @@ -175,16 +219,39 @@ type tokenHelper struct {

// ReconcileServiceAccountToken reconciles the service account token for the cluster and updates the secret containing the kube config
func (t *tokenHelper) ReconcileServiceAccountToken(ctx context.Context, restClientGetter *clientutil.RestClientGetter, cluster *greenhousev1alpha1.Cluster) error {
// TODO: Do not rely on the status but actually check the token expiration.
if !cluster.Status.BearerTokenExpirationTimestamp.IsZero() && cluster.Status.BearerTokenExpirationTimestamp.Time.After(time.Now().Add(t.RenewRemoteClusterBearerTokenAfter)) {
log.FromContext(ctx).V(5).Info("bearer token is still valid", "cluster", cluster.Name, "expirationTimestamp", cluster.Status.BearerTokenExpirationTimestamp.Time)
return nil
}
var jwtPayload []byte
var tokenInfo = new(claims)
var actualTokenExpiry metav1.Time

remoteRestConfig, err := restClientGetter.ToRESTConfig()
if err != nil {
return err
}
jwt, err := jose.ParseSigned(remoteRestConfig.BearerToken, allSignatureAlgorithms)
// If parsing the token is not possible we fall back to the old way of checking the token expiration
if err != nil {
if !cluster.Status.BearerTokenExpirationTimestamp.IsZero() && cluster.Status.BearerTokenExpirationTimestamp.Time.After(time.Now().Add(t.RenewRemoteClusterBearerTokenAfter)) {
log.FromContext(ctx).V(5).Info("bearer token is still valid", "cluster", cluster.Name, "expirationTimestamp", cluster.Status.BearerTokenExpirationTimestamp.Time)
return nil
}
}

if jwt != nil {
jwtPayload = jwt.UnsafePayloadWithoutVerification()
}
err = json.Unmarshal(jwtPayload, &tokenInfo)
// If parsing the token is not possible we fall back to the old way of checking the token expiration
if err == nil {
actualTokenExpiry = metav1.Unix(tokenInfo.Expiry, 0)
if actualTokenExpiry.After(time.Now().Add(t.RenewRemoteClusterBearerTokenAfter)) {
log.FromContext(ctx).V(5).Info("bearer token is still valid", "cluster", cluster.Name, "expirationTimestamp", cluster.Status.BearerTokenExpirationTimestamp.Time)
return nil
}
} else if !cluster.Status.BearerTokenExpirationTimestamp.IsZero() && cluster.Status.BearerTokenExpirationTimestamp.Time.After(time.Now().Add(t.RenewRemoteClusterBearerTokenAfter)) {
log.FromContext(ctx).V(5).Info("bearer token is still valid", "cluster", cluster.Name, "expirationTimestamp", cluster.Status.BearerTokenExpirationTimestamp.Time)
return nil
}

clientset, err := kubernetes.NewForConfig(remoteRestConfig)
if err != nil {
return err
Expand Down
6 changes: 2 additions & 4 deletions pkg/controllers/plugin/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@ import (
"strings"
"time"

"github.com/cloudoperators/greenhouse/pkg/lifecycle"

ctrl "sigs.k8s.io/controller-runtime"

"github.com/pkg/errors"
appsv1 "k8s.io/api/apps/v1"
batchv1 "k8s.io/api/batch/v1"
Expand All @@ -22,11 +18,13 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/cli-runtime/pkg/genericclioptions"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"

greenhouseapis "github.com/cloudoperators/greenhouse/pkg/apis"
greenhousev1alpha1 "github.com/cloudoperators/greenhouse/pkg/apis/greenhouse/v1alpha1"
"github.com/cloudoperators/greenhouse/pkg/clientutil"
"github.com/cloudoperators/greenhouse/pkg/lifecycle"
)

// exposedConditions are the conditions that are exposed in the StatusConditions of the Plugin.
Expand Down
8 changes: 3 additions & 5 deletions pkg/test/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,6 @@ import (
"os"
"time"

"k8s.io/client-go/util/retry"

greenhouseapis "github.com/cloudoperators/greenhouse/pkg/apis"
"github.com/cloudoperators/greenhouse/pkg/clientutil"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gstruct"
Expand All @@ -24,9 +19,12 @@ import (
"k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/util/retry"
"sigs.k8s.io/controller-runtime/pkg/client"

greenhouseapis "github.com/cloudoperators/greenhouse/pkg/apis"
greenhousev1alpha1 "github.com/cloudoperators/greenhouse/pkg/apis/greenhouse/v1alpha1"
"github.com/cloudoperators/greenhouse/pkg/clientutil"
)

func UpdateClusterWithDeletionAnnotation(ctx context.Context, c client.Client, id client.ObjectKey) {
Expand Down

0 comments on commit 2e53035

Please sign in to comment.