-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(config): Implement K8s client-go in secondary API client for secr…
…et/config retrieval
- Loading branch information
1 parent
40fefb7
commit cfa0956
Showing
9 changed files
with
330 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
/* | ||
Copyright 2023 The Keyfactor Command 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 controllers | ||
|
||
import ( | ||
"context" | ||
"github.com/Keyfactor/command-issuer/internal/issuer/util" | ||
corev1 "k8s.io/api/core/v1" | ||
"k8s.io/apimachinery/pkg/types" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
) | ||
|
||
// FakeConfigClient is a fake implementation of the util.ConfigClient interface | ||
// It forwards requests destined for the Kubernetes API server implemented by | ||
// the util.ConfigClient interface to a fake Kubernetes API server implemented | ||
// by the client.Client interface. | ||
|
||
// Force the compiler to check that FakeConfigClient implements the util.ConfigClient interface | ||
var _ util.ConfigClient = &FakeConfigClient{} | ||
|
||
type FakeConfigClient struct { | ||
client client.Client | ||
ctx context.Context | ||
} | ||
|
||
// NewFakeConfigClient uses the | ||
func NewFakeConfigClient(fakeControllerRuntimeClient client.Client) util.ConfigClient { | ||
return &FakeConfigClient{ | ||
client: fakeControllerRuntimeClient, | ||
} | ||
} | ||
|
||
func (f FakeConfigClient) SetContext(ctx context.Context) { | ||
f.ctx = ctx | ||
} | ||
|
||
func (f FakeConfigClient) GetConfigMap(name types.NamespacedName, out *corev1.ConfigMap) error { | ||
return f.client.Get(f.ctx, name, out) | ||
} | ||
|
||
func (f FakeConfigClient) GetSecret(name types.NamespacedName, out *corev1.Secret) error { | ||
return f.client.Get(f.ctx, name, out) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
/* | ||
Copyright 2023 The Keyfactor Command 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 util | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
authv1 "k8s.io/api/authorization/v1" | ||
corev1 "k8s.io/api/core/v1" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/apimachinery/pkg/types" | ||
"k8s.io/client-go/kubernetes" | ||
"k8s.io/klog/v2" | ||
ctrl "sigs.k8s.io/controller-runtime" | ||
) | ||
|
||
type ConfigClient interface { | ||
SetContext(ctx context.Context) | ||
GetConfigMap(name types.NamespacedName, out *corev1.ConfigMap) error | ||
GetSecret(name types.NamespacedName, out *corev1.Secret) error | ||
} | ||
|
||
type configClient struct { | ||
ctx context.Context | ||
logger klog.Logger | ||
client kubernetes.Interface | ||
accessCache map[string]bool | ||
|
||
verifyAccessFunc func(apiResource string, resource types.NamespacedName) error | ||
} | ||
|
||
func NewConfigClient(ctx context.Context) (ConfigClient, error) { | ||
config := ctrl.GetConfigOrDie() | ||
|
||
// Create the clientset | ||
clientset, err := kubernetes.NewForConfig(config) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to create clientset: %w", err) | ||
} | ||
|
||
client := &configClient{ | ||
client: clientset, | ||
accessCache: make(map[string]bool), | ||
ctx: ctx, | ||
logger: klog.NewKlogr(), | ||
} | ||
|
||
client.verifyAccessFunc = client.verifyAccessToResource | ||
|
||
return client, nil | ||
} | ||
|
||
func (c *configClient) SetContext(ctx context.Context) { | ||
c.ctx = ctx | ||
c.logger = klog.FromContext(ctx) | ||
} | ||
|
||
func (c *configClient) verifyAccessToResource(apiResource string, resource types.NamespacedName) error { | ||
verbs := []string{"get", "list", "watch"} | ||
|
||
for _, verb := range verbs { | ||
ssar := &authv1.SelfSubjectAccessReview{ | ||
Spec: authv1.SelfSubjectAccessReviewSpec{ | ||
ResourceAttributes: &authv1.ResourceAttributes{ | ||
Name: resource.Name, | ||
Namespace: resource.Namespace, | ||
|
||
Group: "", | ||
Resource: apiResource, | ||
Verb: verb, | ||
}, | ||
}, | ||
} | ||
|
||
ssar, err := c.client.AuthorizationV1().SelfSubjectAccessReviews().Create(c.ctx, ssar, metav1.CreateOptions{}) | ||
if err != nil { | ||
return fmt.Errorf("failed to create SelfSubjectAccessReview to check access to %s for verb %q: %w", apiResource, verb, err) | ||
} | ||
|
||
if !ssar.Status.Allowed { | ||
return fmt.Errorf("client does not have access to %s called %q for verb %q, reason: %v", apiResource, resource.String(), verb, ssar.Status.String()) | ||
} | ||
} | ||
|
||
c.logger.Info(fmt.Sprintf("Client has access to %s called %q", apiResource, resource.String())) | ||
|
||
return nil | ||
} | ||
|
||
func (c *configClient) GetConfigMap(name types.NamespacedName, out *corev1.ConfigMap) error { | ||
if c == nil { | ||
return fmt.Errorf("config client is nil") | ||
} | ||
|
||
// Check if the client has access to the configmap resource | ||
if ok, _ := c.accessCache[name.String()]; !ok { | ||
err := c.verifyAccessFunc("configmaps", name) | ||
if err != nil { | ||
return err | ||
} | ||
c.accessCache[name.String()] = true | ||
} | ||
|
||
// Get the configmap | ||
configmap, err := c.client.CoreV1().ConfigMaps(name.Namespace).Get(c.ctx, name.Name, metav1.GetOptions{}) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// Copy the configmap into the out parameter | ||
configmap.DeepCopyInto(out) | ||
return nil | ||
} | ||
|
||
func (c *configClient) GetSecret(name types.NamespacedName, out *corev1.Secret) error { | ||
if c == nil { | ||
return fmt.Errorf("config client is nil") | ||
} | ||
|
||
// Check if the client has access to the secret resource | ||
if ok, _ := c.accessCache[name.String()]; !ok { | ||
err := c.verifyAccessFunc("secrets", name) | ||
if err != nil { | ||
return err | ||
} | ||
c.accessCache[name.String()] = true | ||
} | ||
|
||
// Get the secret | ||
secret, err := c.client.CoreV1().Secrets(name.Namespace).Get(c.ctx, name.Name, metav1.GetOptions{}) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// Copy the secret into the out parameter | ||
secret.DeepCopyInto(out) | ||
return nil | ||
} |
Oops, something went wrong.