@@ -28,6 +28,7 @@ import (
2828 "k8s.io/apimachinery/pkg/runtime/schema"
2929 "k8s.io/apimachinery/pkg/types"
3030 "k8s.io/client-go/dynamic"
31+ "k8s.io/client-go/kubernetes"
3132 "k8s.io/client-go/rest"
3233 "k8s.io/client-go/tools/record"
3334 "k8s.io/client-go/util/workqueue"
@@ -44,7 +45,10 @@ import (
4445 "open-cluster-management.io/governance-policy-framework-addon/controllers/utils"
4546)
4647
47- const ControllerName string = "policy-status-sync"
48+ const (
49+ ControllerName string = "policy-status-sync"
50+ hubComplianceAPISAName string = "open-cluster-management-compliance-history-api-recorder"
51+ )
4852
4953var (
5054 clusterClaimGVR = schema.GroupVersionResource {
@@ -93,6 +97,7 @@ type PolicyReconciler struct {
9397//+kubebuilder:rbac:groups=policy.open-cluster-management.io,resources=policies/finalizers,verbs=update
9498//+kubebuilder:rbac:groups=cluster.open-cluster-management.io,resources=clusterclaims,resourceNames=id.k8s.io,verbs=get
9599//+kubebuilder:rbac:groups=core,resources=events,verbs=get;list;watch;create;update;patch;delete
100+ //+kubebuilder:rbac:groups=core,resources=secrets,verbs=get,resourceNames="open-cluster-management-compliance-history-api-recorder"
96101// This is required for the status lease for the addon framework
97102//+kubebuilder:rbac:groups=core,resources=pods,verbs=get;list
98103
@@ -552,6 +557,12 @@ func StartComplianceEventsSyncer(
552557 apiURL string ,
553558 events workqueue.RateLimitingInterface ,
554559) error {
560+ var hubToken string
561+
562+ if hubCfg .BearerToken != "" {
563+ hubToken = hubCfg .BearerToken
564+ }
565+
555566 managedClient , err := dynamic .NewForConfig (managedCfg )
556567 if err != nil {
557568 return err
@@ -583,42 +594,26 @@ func StartComplianceEventsSyncer(
583594 caCertPool = x509 .NewCertPool ()
584595 }
585596
586- // Append the Kubernete API Server CA in case the Service is exposed directly as opposed to using something like the
587- // OpenShift router.
597+ // Append the Kubernete API Server CAs in case the Hub cluster's ingress CA is not trusted by the system pool.
588598 if hubCfg .CAData != nil {
589- caCertPool .AppendCertsFromPEM (hubCfg .CAData )
590- }
591-
592- httpClient := & http.Client {Timeout : 60 * time .Second }
593-
594- var usesCertAuth bool
595-
596- if hubCfg .CertData != nil && hubCfg .KeyData != nil {
597- log .Info ("Using certificate authentication with the compliance API server" )
598-
599- cert , err := tls .X509KeyPair (hubCfg .CertData , hubCfg .KeyData )
600- if err != nil {
601- log .Error (err , "Failed to load the hub kubeconfig for certificate authentication on the compliance API" )
602-
603- return err
599+ _ = caCertPool .AppendCertsFromPEM (hubCfg .CAData )
600+ } else if hubCfg .CAFile != "" {
601+ caData , err := os .ReadFile (hubCfg .CAFile )
602+ if err == nil {
603+ log .Info ("The hub kubeconfig CA file can't be read. Ignoring it." , "path" , hubCfg .CAFile )
604604 }
605605
606- httpClient .Transport = & http.Transport {
607- TLSClientConfig : & tls.Config {
608- MinVersion : tls .VersionTLS12 ,
609- Certificates : []tls.Certificate {cert },
610- RootCAs : caCertPool ,
611- },
612- }
606+ _ = caCertPool .AppendCertsFromPEM (caData )
607+ }
613608
614- usesCertAuth = true
615- } else {
616- httpClient . Transport = & http.Transport {
609+ httpClient := & http. Client {
610+ Timeout : 60 * time . Second ,
611+ Transport : & http.Transport {
617612 TLSClientConfig : & tls.Config {
618613 MinVersion : tls .VersionTLS12 ,
619614 RootCAs : caCertPool ,
620615 },
621- }
616+ },
622617 }
623618
624619 for {
@@ -671,10 +666,32 @@ func StartComplianceEventsSyncer(
671666
672667 httpRequest .Header .Set ("Content-Type" , "application/json" )
673668
674- if ! usesCertAuth && hubCfg .BearerToken != "" {
675- httpRequest .Header .Set ("Authorization" , "Bearer " + hubCfg .BearerToken )
669+ if hubToken == "" {
670+ var err error
671+
672+ hubToken , err = getHubComplianceAPIToken (ctx , hubCfg , clusterName )
673+ if err != nil || hubToken == "" {
674+ var msg string
675+
676+ if err != nil {
677+ msg = err .Error ()
678+ } else {
679+ msg = "the token was not set on the secret"
680+ }
681+
682+ log .Info (
683+ "Failed to get the compliance API hub token. Will requeue in 10 seconds." , "error" , msg ,
684+ )
685+
686+ events .AddAfter (ceUntyped , 10 * time .Second )
687+ events .Done (ceUntyped )
688+
689+ continue
690+ }
676691 }
677692
693+ httpRequest .Header .Set ("Authorization" , "Bearer " + hubToken )
694+
678695 httpResponse , err := httpClient .Do (httpRequest )
679696 if err != nil {
680697 log .Info ("Failed to record the compliance event with the compliance API. Will requeue in 10 seconds." )
@@ -705,6 +722,11 @@ func StartComplianceEventsSyncer(
705722 "message" , message ,
706723 )
707724
725+ if httpResponse .StatusCode == http .StatusUnauthorized || httpResponse .StatusCode == http .StatusForbidden {
726+ // Wipe out the hubToken so that the token is fetched again on the next try.
727+ hubToken = ""
728+ }
729+
708730 events .AddRateLimited (ceUntyped )
709731 events .Done (ceUntyped )
710732
@@ -721,6 +743,24 @@ func StartComplianceEventsSyncer(
721743 }
722744}
723745
746+ // getHubComplianceAPIToken retrieves the token associated with the service account with compliance history API
747+ // recording permssions in the cluster namespace.
748+ func getHubComplianceAPIToken (ctx context.Context , hubCfg * rest.Config , clusterNamespace string ) (string , error ) {
749+ client , err := kubernetes .NewForConfig (hubCfg )
750+ if err != nil {
751+ return "" , err
752+ }
753+
754+ saTokenSecret , err := client .CoreV1 ().Secrets (clusterNamespace ).Get (
755+ ctx , hubComplianceAPISAName , metav1.GetOptions {},
756+ )
757+ if err != nil {
758+ return "" , err
759+ }
760+
761+ return string (saTokenSecret .Data ["token" ]), nil
762+ }
763+
724764type historyEvent struct {
725765 policiesv1.ComplianceHistory
726766 eventTime metav1.MicroTime
0 commit comments