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

feat(insights): mount Insights token in Cryostat container #596

Closed
wants to merge 5 commits into from
Closed
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
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,9 @@ test: test-envtest test-scorecard
.PHONY: test-envtest
test-envtest: generate manifests fmt vet setup-envtest
ifneq ($(SKIP_TESTS), true)
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" $(GO_TEST) -v -coverprofile cover.out ./...
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" \
OPENSHIFT_API_MOD_VERSION=$(shell go list -m -f '{{if .Replace}}{{.Replace.Version}}{{else}}{{.Version}}{{end}}' github.com/openshift/api) \
$(GO_TEST) -v -coverprofile cover.out ./...
endif

.PHONY: test-scorecard
Expand Down
26 changes: 20 additions & 6 deletions internal/controllers/clustercryostat_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,16 @@ type ClusterCryostatReconciler struct {
*ReconcilerConfig
}

func NewClusterCryostatReconciler(config *ReconcilerConfig) *ClusterCryostatReconciler {
func NewClusterCryostatReconciler(config *ReconcilerConfig) (*ClusterCryostatReconciler, error) {
delegate, err := newReconciler(config, &operatorv1beta1.ClusterCryostat{},
&operatorv1beta1.ClusterCryostatList{}, false)
if err != nil {
return nil, err
}
return &ClusterCryostatReconciler{
ReconcilerConfig: config,
delegate: &Reconciler{
ReconcilerConfig: config,
},
}
delegate: delegate,
}, nil
}

// +kubebuilder:rbac:groups="",resources=pods;services;services/finalizers;endpoints;persistentvolumeclaims;events;configmaps;secrets;serviceaccounts,verbs=*
Expand All @@ -89,6 +92,17 @@ func NewClusterCryostatReconciler(config *ReconcilerConfig) *ClusterCryostatReco
// +kubebuilder:rbac:groups=console.openshift.io,resources=consolelinks,verbs=get;create;list;update;delete
// +kubebuilder:rbac:groups=networking.k8s.io,resources=ingresses,verbs=*

// XXX
// Note: You cannot restrict create or deletecollection requests by their
// resource name. For create, this limitation is because the name of the
// new object may not be known at authorization time. If you restrict list
// or watch by resourceName, clients must include a metadata.name field
// selector in their list or watch request that matches the specified
// resourceName in order to be authorized. For example, kubectl get
// configmaps --field-selector=metadata.name=my-configmap
//
// https://kubernetes.io/docs/reference/access-authn-authz/rbac/#referring-to-resources

// Reconcile processes a ClusterCryostat CR and manages a Cryostat installation accordingly
func (r *ClusterCryostatReconciler) Reconcile(ctx context.Context, request ctrl.Request) (ctrl.Result, error) {
reqLogger := r.Log.WithValues("Request.Name", request.Name)
Expand Down Expand Up @@ -116,7 +130,7 @@ func (r *ClusterCryostatReconciler) Reconcile(ctx context.Context, request ctrl.

// SetupWithManager sets up the controller with the Manager.
func (r *ClusterCryostatReconciler) SetupWithManager(mgr ctrl.Manager) error {
return r.delegate.setupWithManager(mgr, &operatorv1beta1.ClusterCryostat{}, r)
return r.delegate.setupWithManager(mgr, r)
}

func (r *ClusterCryostatReconciler) GetConfig() *ReconcilerConfig {
Expand Down
2 changes: 1 addition & 1 deletion internal/controllers/clustercryostat_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,6 @@ func (t *cryostatTestInput) expectTargetNamespaces() {
Expect(*cr.TargetNamespaceStatus).To(ConsistOf(t.TargetNamespaces))
}

func newClusterCryostatController(config *controllers.ReconcilerConfig) controllers.CommonReconciler {
func newClusterCryostatController(config *controllers.ReconcilerConfig) (controllers.CommonReconciler, error) {
return controllers.NewClusterCryostatReconciler(config)
}
5 changes: 3 additions & 2 deletions internal/controllers/common/common_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import (
"strings"
"time"

"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
logf "sigs.k8s.io/controller-runtime/pkg/log"
)
Expand Down Expand Up @@ -84,9 +85,9 @@ func (o *defaultOSUtils) GenPasswd(length int) string {

// ClusterUniqueName returns a name for cluster-scoped objects that is
// uniquely identified by a namespace and name.
func ClusterUniqueName(kind string, name string, namespace string) string {
func ClusterUniqueName(gvk *schema.GroupVersionKind, name string, namespace string) string {
// Use the SHA256 checksum of the namespaced name as a suffix
nn := types.NamespacedName{Namespace: namespace, Name: name}
suffix := fmt.Sprintf("%x", sha256.Sum256([]byte(nn.String())))
return strings.ToLower(kind) + "-" + suffix
return strings.ToLower(gvk.Kind) + "-" + suffix
}
Original file line number Diff line number Diff line change
Expand Up @@ -310,26 +310,41 @@ func NewPodForCR(cr *model.CryostatInstance, specs *ServiceSpecs, imageTags *Ima
volumes = append(volumes, eventTemplateVolume)
}

// Add Auth properties as a volume if specified (on Openshift)
if openshift && cr.Spec.AuthProperties != nil {
authResourceVolume := corev1.Volume{
Name: "auth-properties-" + cr.Spec.AuthProperties.ConfigMapName,
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: cr.Spec.AuthProperties.ConfigMapName,
},
Items: []corev1.KeyToPath{
{
Key: cr.Spec.AuthProperties.Filename,
Path: "OpenShiftAuthManager.properties",
Mode: &readOnlyMode,
if openshift {
// Add Auth properties as a volume if specified (on Openshift)
if cr.Spec.AuthProperties != nil {
authResourceVolume := corev1.Volume{
Name: "auth-properties-" + cr.Spec.AuthProperties.ConfigMapName,
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: cr.Spec.AuthProperties.ConfigMapName,
},
Items: []corev1.KeyToPath{
{
Key: cr.Spec.AuthProperties.Filename,
Path: "OpenShiftAuthManager.properties",
Mode: &readOnlyMode,
},
},
},
},
}
volumes = append(volumes, authResourceVolume)
}

// Add Insights token secret as a volume
insightsVolume := corev1.Volume{
Name: "insights-token",
VolumeSource: corev1.VolumeSource{
Secret: &corev1.SecretVolumeSource{
SecretName: cr.Name + "-insights-token",
Optional: &[]bool{true}[0],
DefaultMode: &readOnlyMode,
},
},
}
volumes = append(volumes, authResourceVolume)
volumes = append(volumes, insightsVolume)
}

var podSc *corev1.PodSecurityContext
Expand Down Expand Up @@ -759,6 +774,13 @@ func NewCoreContainer(cr *model.CryostatInstance, specs *ServiceSpecs, imageTag
Value: cr.Spec.AuthProperties.ClusterRoleName,
})
}

// Mount Insights token
mounts = append(mounts, corev1.VolumeMount{
Name: "insights-token",
MountPath: "/var/run/secrets/operator.cryostat.io/insights-token",
ReadOnly: true,
})
}

disableBuiltInDiscovery := cr.Spec.TargetDiscoveryOptions != nil && cr.Spec.TargetDiscoveryOptions.BuiltInDiscoveryDisabled
Expand Down
15 changes: 9 additions & 6 deletions internal/controllers/cryostat_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,16 @@ type CryostatReconciler struct {
*ReconcilerConfig
}

func NewCryostatReconciler(config *ReconcilerConfig) *CryostatReconciler {
func NewCryostatReconciler(config *ReconcilerConfig) (*CryostatReconciler, error) {
delegate, err := newReconciler(config, &operatorv1beta1.Cryostat{},
&operatorv1beta1.CryostatList{}, true)
if err != nil {
return nil, err
}
return &CryostatReconciler{
ReconcilerConfig: config,
delegate: &Reconciler{
ReconcilerConfig: config,
},
}
delegate: delegate,
}, nil
}

// +kubebuilder:rbac:groups=operator.cryostat.io,resources=cryostats,verbs=*
Expand Down Expand Up @@ -97,7 +100,7 @@ func (r *CryostatReconciler) Reconcile(ctx context.Context, request ctrl.Request

// SetupWithManager sets up the controller with the Manager.
func (r *CryostatReconciler) SetupWithManager(mgr ctrl.Manager) error {
return r.delegate.setupWithManager(mgr, &operatorv1beta1.Cryostat{}, r)
return r.delegate.setupWithManager(mgr, r)
}

func (r *CryostatReconciler) GetConfig() *ReconcilerConfig {
Expand Down
2 changes: 1 addition & 1 deletion internal/controllers/cryostat_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,6 @@ var _ = Describe("CryostatController", func() {
c.commonTests()
})

func newCryostatController(config *controllers.ReconcilerConfig) controllers.CommonReconciler {
func newCryostatController(config *controllers.ReconcilerConfig) (controllers.CommonReconciler, error) {
return controllers.NewCryostatReconciler(config)
}
79 changes: 74 additions & 5 deletions internal/controllers/openshift.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,18 @@ package controllers

import (
"context"
"encoding/json"
"errors"
"fmt"
"regexp"
"strings"

"github.com/cryostatio/cryostat-operator/internal/controllers/common"
"github.com/cryostatio/cryostat-operator/internal/controllers/model"
"github.com/go-logr/logr"
configv1 "github.com/openshift/api/config/v1"
consolev1 "github.com/openshift/api/console/v1"
corev1 "k8s.io/api/core/v1"
kerrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
Expand All @@ -60,6 +64,10 @@ func (r *Reconciler) reconcileOpenShift(ctx context.Context, cr *model.CryostatI
if err != nil {
return err
}
err = r.reconcilePullSecret(ctx, cr)
if err != nil {
return err
}
return r.addCorsAllowedOriginIfNotPresent(ctx, cr)
}

Expand All @@ -68,26 +76,25 @@ func (r *Reconciler) finalizeOpenShift(ctx context.Context, cr *model.CryostatIn
return nil
}
reqLogger := r.Log.WithValues("Request.Namespace", cr.InstallNamespace, "Request.Name", cr.Name)
err := r.deleteConsoleLink(ctx, newConsoleLink(cr), reqLogger)
err := r.deleteConsoleLink(ctx, r.newConsoleLink(cr), reqLogger)
if err != nil {
return err
}
return r.deleteCorsAllowedOrigins(ctx, cr)
}

func newConsoleLink(cr *model.CryostatInstance) *consolev1.ConsoleLink {
func (r *Reconciler) newConsoleLink(cr *model.CryostatInstance) *consolev1.ConsoleLink {
// Cluster scoped, so use a unique name to avoid conflicts
return &consolev1.ConsoleLink{
ObjectMeta: metav1.ObjectMeta{
Name: common.ClusterUniqueName(cr.Object.GetObjectKind().GroupVersionKind().Kind,
cr.Name, cr.InstallNamespace),
Name: common.ClusterUniqueName(r.gvk, cr.Name, cr.InstallNamespace),
},
}
}

func (r *Reconciler) reconcileConsoleLink(ctx context.Context, cr *model.CryostatInstance) error {
reqLogger := r.Log.WithValues("Request.Namespace", cr.InstallNamespace, "Request.Name", cr.Name)
link := newConsoleLink(cr)
link := r.newConsoleLink(cr)

url := cr.Status.ApplicationURL
if len(url) == 0 {
Expand Down Expand Up @@ -204,3 +211,65 @@ func (r *Reconciler) deleteCorsAllowedOrigins(ctx context.Context, cr *model.Cry
reqLogger.Info("Removed from APIServer CORS allowed origins")
return nil
}

func (r *Reconciler) reconcilePullSecret(ctx context.Context, cr *model.CryostatInstance) error {
secret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: cr.Name + "-insights-token",
Namespace: cr.InstallNamespace,
},
}

token, err := r.getTokenFromPullSecret(ctx)
if err != nil {
// TODO warn instead of fail?
return err
}

return r.createOrUpdateSecret(ctx, secret, cr.Object, func() error {
if secret.StringData == nil {
secret.StringData = map[string]string{}
}
secret.StringData["token"] = *token
return nil
})
}

func (r *Reconciler) getTokenFromPullSecret(ctx context.Context) (*string, error) {
// Get the global pull secret
pullSecret := &corev1.Secret{}
err := r.Client.Get(ctx, types.NamespacedName{Namespace: "openshift-config", Name: "pull-secret"}, pullSecret)
if err != nil {
return nil, err
}

// Look for the .dockerconfigjson key within it
dockerConfigRaw, pres := pullSecret.Data[corev1.DockerConfigJsonKey]
if !pres {
return nil, fmt.Errorf("no %s key present in pull secret", corev1.DockerConfigJsonKey)
}

fmt.Println("dockerconfig: " + string(dockerConfigRaw))
// Unmarshal the .dockerconfigjson into a struct
dockerConfig := struct {
Auths map[string]struct {
Auth string `json:"auth"`
} `json:"auths"`
}{}
err = json.Unmarshal(dockerConfigRaw, &dockerConfig)
if err != nil {
return nil, err
}

// Look for the "cloud.openshift.com" auth
openshiftAuth, pres := dockerConfig.Auths["cloud.openshift.com"]
if !pres {
return nil, errors.New("no \"cloud.openshift.com\" auth within pull secret")
}

token := strings.TrimSpace(openshiftAuth.Auth)
if strings.Contains(token, "\n") || strings.Contains(token, "\r") {
return nil, fmt.Errorf("invalid cloud.openshift.com token")
}
return &token, nil
}
10 changes: 5 additions & 5 deletions internal/controllers/rbac.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func (r *Reconciler) reconcileRBAC(ctx context.Context, cr *model.CryostatInstan
}

func (r *Reconciler) finalizeRBAC(ctx context.Context, cr *model.CryostatInstance) error {
return r.deleteClusterRoleBinding(ctx, newClusterRoleBinding(cr))
return r.deleteClusterRoleBinding(ctx, r.newClusterRoleBinding(cr))
}

func newServiceAccount(cr *model.CryostatInstance) *corev1.ServiceAccount {
Expand Down Expand Up @@ -172,19 +172,19 @@ func (r *Reconciler) reconcileRoleBinding(ctx context.Context, cr *model.Cryosta
return nil
}

func newClusterRoleBinding(cr *model.CryostatInstance) *rbacv1.ClusterRoleBinding {
func (r *Reconciler) newClusterRoleBinding(cr *model.CryostatInstance) *rbacv1.ClusterRoleBinding {
fmt.Println("CRB KIND: " + r.gvk.Kind) // XXX
return &rbacv1.ClusterRoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: common.ClusterUniqueName(cr.Object.GetObjectKind().GroupVersionKind().Kind,
cr.Name, cr.InstallNamespace),
Name: common.ClusterUniqueName(r.gvk, cr.Name, cr.InstallNamespace),
},
}
}

const clusterRoleName = "cryostat-operator-cryostat"

func (r *Reconciler) reconcileClusterRoleBinding(ctx context.Context, cr *model.CryostatInstance) error {
binding := newClusterRoleBinding(cr)
binding := r.newClusterRoleBinding(cr)

sa := newServiceAccount(cr)
subjects := []rbacv1.Subject{
Expand Down
Loading