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

refactor: code re-user for certs, pods ready check, ImageRefresher, etc. #413

Merged
merged 1 commit into from
Aug 14, 2023
Merged
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
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
Loading