Skip to content

Commit

Permalink
refactor: code re-use for certs, pods ready check, ImageRefresher, etc.
Browse files Browse the repository at this point in the history
- Allows code re-use of TLS cert creation, checking if pods are ready before deploying,
  ImageFresher for registry pushing, etc. as more reconcilation types will need to be
  able to do the same thing.
  • Loading branch information
redhatrises committed Aug 14, 2023
1 parent ae596a8 commit c3c1b39
Show file tree
Hide file tree
Showing 8 changed files with 203 additions and 31 deletions.
7 changes: 5 additions & 2 deletions controllers/falcon_container/falconcontainer_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import (
"time"

falconv1alpha1 "github.com/crowdstrike/falcon-operator/api/falcon/v1alpha1"
k8sutils "github.com/crowdstrike/falcon-operator/internal/controller/common"
"github.com/crowdstrike/falcon-operator/pkg/aws"
"github.com/crowdstrike/falcon-operator/pkg/common"
"github.com/crowdstrike/falcon-operator/version"
"github.com/go-logr/logr"
arv1 "k8s.io/api/admissionregistration/v1"
Expand Down Expand Up @@ -117,7 +120,7 @@ func (r *FalconContainerReconciler) Reconcile(ctx context.Context, req ctrl.Requ
} else {
switch falconContainer.Spec.Registry.Type {
case falconv1alpha1.RegistryTypeECR:
if _, err := r.UpsertECRRepo(ctx); err != nil {
if _, err := aws.UpsertECRRepo(ctx, "falcon-container"); err != nil {
err = r.StatusUpdate(ctx, req, log, falconContainer, falconv1alpha1.ConditionFailed, metav1.ConditionFalse, "Reconciling", fmt.Sprintf("failed to reconcile ECR repository: %v", err))
if err != nil {
return ctrl.Result{}, err
Expand Down Expand Up @@ -239,7 +242,7 @@ func (r *FalconContainerReconciler) Reconcile(ctx context.Context, req ctrl.Requ
return ctrl.Result{}, fmt.Errorf("failed to reconcile injector Service: %v", err)
}

pod, err := r.injectorPodReady(ctx, falconContainer)
pod, err := k8sutils.GetReadyPod(r.Client, ctx, r.Namespace(), map[string]string{common.FalconComponentKey: common.FalconSidecarSensor})
if err != nil && err.Error() != "No Injector pod found in a Ready state" {
err = r.StatusUpdate(ctx, req, log, falconContainer, falconv1alpha1.ConditionFailed, metav1.ConditionFalse, "Reconciling", fmt.Sprintf("failed to find Ready injector pod: %v", err))
if err != nil {
Expand Down
6 changes: 4 additions & 2 deletions controllers/falcon_container/image_push.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

falconv1alpha1 "github.com/crowdstrike/falcon-operator/api/falcon/v1alpha1"
"github.com/crowdstrike/falcon-operator/internal/controller/image"
"github.com/crowdstrike/falcon-operator/pkg/aws"
"github.com/crowdstrike/falcon-operator/pkg/gcp"
"github.com/crowdstrike/falcon-operator/pkg/k8s_utils"
"github.com/crowdstrike/falcon-operator/pkg/registry/auth"
Expand All @@ -33,7 +35,7 @@ func (r *FalconContainerReconciler) PushImage(ctx context.Context, log logr.Logg
}

log.Info("Found secret for image push", "Secret.Name", pushAuth.Name())
image := NewImageRefresher(ctx, log, r.falconApiConfig(ctx, falconContainer), pushAuth, falconContainer.Spec.Registry.TLS.InsecureSkipVerify)
image := image.NewImageRefresher(ctx, log, r.falconApiConfig(ctx, falconContainer), pushAuth, falconContainer.Spec.Registry.TLS.InsecureSkipVerify)
version := falconContainer.Spec.Version

// If we have version locking enabled (as it is by default), use the already configured version if present
Expand Down Expand Up @@ -113,7 +115,7 @@ func (r *FalconContainerReconciler) registryUri(ctx context.Context, falconConta

return "gcr.io/" + projectId + "/falcon-container", nil
case falconv1alpha1.RegistryTypeECR:
repo, err := r.UpsertECRRepo(ctx)
repo, err := aws.UpsertECRRepo(ctx, "falcon-container")
if err != nil {
return "", fmt.Errorf("Cannot get target docker URI for ECR repository: %v", err)
}
Expand Down
31 changes: 7 additions & 24 deletions controllers/falcon_container/injector.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"github.com/go-logr/logr"
"github.com/operator-framework/operator-lib/proxy"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"

"github.com/crowdstrike/falcon-operator/pkg/common"
"github.com/crowdstrike/falcon-operator/pkg/tls"
Expand All @@ -37,7 +36,13 @@ func (r *FalconContainerReconciler) reconcileInjectorTLSSecret(ctx context.Conte
if falconContainer.Spec.Injector.TLS.Validity != nil {
validity = *falconContainer.Spec.Injector.TLS.Validity
}
c, k, b, err := tls.CertSetup(validity)

certInfo := tls.CertInfo{
CommonName: fmt.Sprintf("%s.%s.svc", injectorName, r.Namespace()),
DNSNames: []string{fmt.Sprintf("%s.%s.svc", injectorName, r.Namespace()), fmt.Sprintf("%s.%s.svc.cluster.local", injectorName, r.Namespace())},
}

c, k, b, err := tls.CertSetup(validity, certInfo)
if err != nil {
return &corev1.Secret{}, fmt.Errorf("failed to generate Falcon Container PKI: %v", err)
}
Expand Down Expand Up @@ -148,25 +153,3 @@ func (r *FalconContainerReconciler) reconcileDeployment(ctx context.Context, log
return existingDeployment, nil

}

func (r *FalconContainerReconciler) injectorPodReady(ctx context.Context, falconContainer *falconv1alpha1.FalconContainer) (*corev1.Pod, error) {
podList := &corev1.PodList{}
listOpts := []client.ListOption{
client.InNamespace(r.Namespace()),
client.MatchingLabels{common.FalconComponentKey: common.FalconSidecarSensor},
}

if err := r.List(ctx, podList, listOpts...); err != nil {
return nil, fmt.Errorf("unable to list pods: %v", err)
}

for _, pod := range podList.Items {
for _, cond := range pod.Status.Conditions {
if cond.Type == corev1.PodReady && cond.Status == corev1.ConditionTrue {
return &pod, nil
}
}
}

return &corev1.Pod{}, fmt.Errorf("No Injector pod found in a Ready state")
}
31 changes: 31 additions & 0 deletions internal/controller/common/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package common

import (
"context"
"fmt"

corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
)

func GetReadyPod(r client.Client, ctx context.Context, namespace string, matchingLabels client.MatchingLabels) (*corev1.Pod, error) {
podList := &corev1.PodList{}
listOpts := []client.ListOption{
client.InNamespace(namespace),
matchingLabels,
}

if err := r.List(ctx, podList, listOpts...); err != nil {
return nil, fmt.Errorf("unable to list pods: %v", err)
}

for _, pod := range podList.Items {
for _, cond := range pod.Status.Conditions {
if cond.Type == corev1.PodReady && cond.Status == corev1.ConditionTrue {
return &pod, nil
}
}
}

return &corev1.Pod{}, fmt.Errorf("No Injector pod found in a Ready state")
}
1 change: 1 addition & 0 deletions internal/controller/common/utils_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package common
132 changes: 132 additions & 0 deletions internal/controller/image/image_refresher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package image

import (
"context"
"fmt"
"os"
"strings"

"github.com/go-logr/logr"

"github.com/containers/image/v5/copy"
"github.com/containers/image/v5/signature"
"github.com/containers/image/v5/transports/alltransports"
"github.com/containers/image/v5/types"

"github.com/crowdstrike/falcon-operator/pkg/registry/auth"
"github.com/crowdstrike/falcon-operator/pkg/registry/falcon_registry"
"github.com/crowdstrike/gofalcon/falcon"
)

type ImageRefresher struct {
ctx context.Context
log logr.Logger
falconConfig *falcon.ApiConfig
insecureSkipTLSVerify bool
pushCredentials auth.Credentials
}

func NewImageRefresher(ctx context.Context, log logr.Logger, falconConfig *falcon.ApiConfig, pushAuth auth.Credentials, insecureSkipTLSVerify bool) *ImageRefresher {
return &ImageRefresher{
ctx: ctx,
log: log,
falconConfig: falconConfig,
insecureSkipTLSVerify: insecureSkipTLSVerify,
pushCredentials: pushAuth,
}
}

func (r *ImageRefresher) Refresh(imageDestination string, versionRequested *string) (string, error) {
falconTag, srcRef, sourceCtx, err := r.source(versionRequested)
if err != nil {
return "", err
}

r.log.Info("Identified the latest Falcon Container image", "reference", srcRef.DockerReference().String())

policy := &signature.Policy{Default: []signature.PolicyRequirement{signature.NewPRInsecureAcceptAnything()}}
policyContext, err := signature.NewPolicyContext(policy)
if err != nil {
return "", fmt.Errorf("Error loading trust policy: %v", err)
}
defer func() { _ = policyContext.Destroy() }()

destinationCtx, err := r.destinationContext(r.insecureSkipTLSVerify)
if err != nil {
return "", err
}

// Push to the registry with the falconTag
dest := fmt.Sprintf("docker://%s:%s", imageDestination, falconTag)
destRef, err := alltransports.ParseImageName(dest)

if err != nil {
return "", fmt.Errorf("Invalid destination name %s: %v", dest, err)
}

r.log.Info("Identified the target location for image push", "reference", destRef.DockerReference().String())
_, err = copy.Image(r.ctx, policyContext, destRef, srcRef,
&copy.Options{
ReportWriter: os.Stdout,
SourceCtx: sourceCtx,
DestinationCtx: destinationCtx,
},
)
if err != nil {
return "", wrapWithHint(err)
}

// Push to the registry with the latest tag
dest = fmt.Sprintf("docker://%s", imageDestination)
destRef, err = alltransports.ParseImageName(dest)
if err != nil {
return "", fmt.Errorf("Invalid destination name %s: %v", dest, err)
}

r.log.Info("Identified the target location for image push", "reference", destRef.DockerReference().String())
_, err = copy.Image(r.ctx, policyContext, destRef, srcRef,
&copy.Options{
ReportWriter: os.Stdout,
SourceCtx: sourceCtx,
DestinationCtx: destinationCtx,
},
)

return falconTag, wrapWithHint(err)
}

func (r *ImageRefresher) source(versionRequested *string) (falconTag string, falconImage types.ImageReference, systemContext *types.SystemContext, err error) {
registry, err := falcon_registry.NewFalconRegistry(r.ctx, r.falconConfig)
if err != nil {
return
}

return registry.PullInfo(r.ctx, versionRequested)
}

func (r *ImageRefresher) destinationContext(insecureSkipTLSVerify bool) (*types.SystemContext, error) {
ctx, err := r.pushCredentials.DestinationContext()
if err != nil {
return nil, err
}

if insecureSkipTLSVerify {
ctx.DockerInsecureSkipTLSVerify = 1
}

return ctx, nil
}

func wrapWithHint(in error) error {
// Use of credentials store outside of docker command is somewhat limited
// See https://github.com/moby/moby/issues/39377
// https://github.com/containers/image/pull/656
if in == nil {
return in
}

if strings.Contains(in.Error(), "authentication required") {
return fmt.Errorf("Could not authenticate to the registry: %w", in)
}
return in
}
15 changes: 15 additions & 0 deletions pkg/aws/ecr.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"

"github.com/aws/aws-sdk-go-v2/service/ecr"
"github.com/aws/aws-sdk-go-v2/service/ecr/types"
ecr_types "github.com/aws/aws-sdk-go-v2/service/ecr/types"
)

Expand Down Expand Up @@ -41,3 +42,17 @@ func (c *Config) ECRLogin(ctx context.Context) ([]byte, error) {
}
return base64.StdEncoding.DecodeString(*output.AuthorizationData[0].AuthorizationToken)
}

func UpsertECRRepo(ctx context.Context, name string) (*types.Repository, error) {
cfg, err := NewConfig()
if err != nil {
return nil, fmt.Errorf("Failed to initialise connection to AWS. Please make sure that kubernetes service account falcon-operator has access to AWS IAM role and OIDC Identity provider is running on the cluster. Error was: %v", err)
}

data, err := cfg.UpsertRepository(ctx, name)
if err != nil {
return nil, fmt.Errorf("Failed to upsert ECR repository: %v", err)
}

return data, nil
}
11 changes: 8 additions & 3 deletions pkg/tls/certs.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,13 @@ import (
"time"
)

type CertInfo struct {
CommonName string
DNSNames []string
}

// CertSetup will generate and return tls certs
func CertSetup(days int) ([]byte, []byte, []byte, error) {
func CertSetup(days int, certInfo CertInfo) ([]byte, []byte, []byte, error) {
// set up our CA certificate
ca := &x509.Certificate{
SerialNumber: new(big.Int).Lsh(big.NewInt(1), 128),
Expand Down Expand Up @@ -60,14 +65,14 @@ func CertSetup(days int) ([]byte, []byte, []byte, error) {
cert := &x509.Certificate{
SerialNumber: new(big.Int).Lsh(big.NewInt(1), 128),
Subject: pkix.Name{
CommonName: "falcon-sidecar-injector.falcon-system.svc",
CommonName: certInfo.CommonName,
},
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(0, 0, days),
SubjectKeyId: []byte("234567"),
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
KeyUsage: x509.KeyUsageDigitalSignature,
DNSNames: []string{"falcon-sidecar-injector.falcon-system.svc", "falcon-sidecar-injector.falcon-system.svc.cluster.local"},
DNSNames: certInfo.DNSNames,
}

certPrivKey, err := rsa.GenerateKey(rand.Reader, 2048)
Expand Down

0 comments on commit c3c1b39

Please sign in to comment.