8000 Enable ContainerAutodiscovery to scan images from private repos (ADR 17)(closes #1189) by the-simmon · Pull Request #1557 · secureCodeBox/secureCodeBox · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
9d732c9
Add initcontianer that extracts matching imagepullsecrets #1189
the-simmon Jan 20, 2023
08ecdbc
Add ownerreference to temporary container autodiscovery secret #1189
the-simmon Jan 20, 2023
292e4a1
Fix python docstrings in secretextractioninitcontainer #1189
the-simmon Jan 21, 2023
9294ecf
Add readme to secretextractioninitcontainer explaining steps to creat…
the-simmon Jan 21, 2023
cde0395
Mock kubernetes import in unit test to be able to run tests with the …
the-simmon Jan 21, 2023
22c2683
Add makefile #1189
the-simmon Jan 21, 2023
0e7ae97
Add secretextractioninitcontainer to ci workflow #1189
the-simmon Jan 21, 2023
5cf8d9e
Update ci python version to 3.9 to support type hints used in secrete…
the-simmon Jan 21, 2023
be13ac0
Fix type in tests
the-simmon Jan 21, 2023
2f75c85
Add unit tests for container autodiscovery with imagepullsecrets #1189
the-simmon Jan 21, 2023
715a9cc
Fix seg fault in test #1189
the-simmon Jan 21, 2023
229821d
Fix bug with nil arrays in existing container autodiscovery tests #1189
the-simmon Jan 21, 2023
0424929
Add switch to enable secret mapping to container autodiscovery config…
the-simmon Jan 21, 2023
cf3cbc8
Add imagepullsecret logic to container autodiscovery #1189
the-simmon Jan 21, 2023
c331c47
Fix docker export name of secretextractioncontainer in ci #1189
the-simmon Jan 21, 2023
3850793
Add args to initcontainer in container autodiscovery #1189
the-simmon Jan 22, 2023
4b78ac7
Move secret volume mounts to initcontainer instead of scan #1189
the-simmon Jan 22, 2023
459af28
Add envvars with temporary secret to trivy container #1189
the-simmon Jan 22, 2023
3d29862
Rename temporary secret #1189
the-simmon Jan 22, 2023
55f21c6
Only map secrets to env when enabled #1189
the-simmon Jan 22, 2023
4bb56fd
Remove redundant statement #1189
the-simmon Jan 22, 2023
8200ec1
Rename secretextractioninitcontainer to pull-secret-extractor #1189
the-simmon Jan 24, 2023
efbd164
Read pod name and namespace from environment variables set by kuberne…
the-simmon Jan 24, 2023
eb625b9
Add pod name and namespace varialbes to env vars of initcontainer usi…
the-simmon Jan 24, 2023
5689edf
Make env vars for pull-secret-extractor configurable #1189
the-simmon Jan 24, 2023
0956af2
Update values.yaml with imagepullsecret config #1189
the-simmon Jan 24, 2023
0d539a4
Fix bug where secret mapping toggle got ignored #1189
the-simmon Jan 24, 2023
8af2928
Move env vars for pull-secret-extractor to correct location #1189
the-simmon Jan 24, 2023
8fec7b3
Enable service autodiscovery in config.yaml that was disabled by mist…
the-simmon Jan 24, 2023
33ecd37
Fix command of pull secret extractor #1189
the-simmon Jan 24, 2023
9ddf339
Add title to ci workflow #1189
the-simmon Jan 25, 2023
5dcdbfd
Add integration test #1189
the-simmon Jan 25, 2023
11a2f74
Add pull secret extractor integration test to ci #1189
the-simmon Jan 25, 2023
8bd4356
Add temporarily hardcoded rbac configs
J12934 Jan 31, 2023
16b225c
Add specific trivy scan type for container autodiscovery with service…
the-simmon Feb 7, 2023
6f17072
Add new autodiscovery trivy scan type to autodiscovry config #1189
the-simmon Feb 7, 2023
df58863
Extract domain from docker image before checking secrets #1189
the-simmon Feb 7, 2023
fdd8c51
Dont create temporary secret when no credentials are found #1189
the-simmon Feb 7, 2023
d8f6c1f
Add section in readme explaining how the initconainer works #1189
the-simmon Feb 8, 2023
59c3eb9
Add decision to adr17 #1189
the-simmon Feb 8, 2023
ab78db5
Fix path in ci yaml ##1189
the-simmon Feb 8, 2023
d00740a
Fix typo #1189
the-simmon Feb 8, 2023
c978036
Set correct python in ci unit test #1189
the-simmon Feb 8, 2023
bd03c56
Fix ci syntax #1189
the-simmon Feb 8, 2023
acbaa50
Replace assertEqual with assertCountEqual to ensure tests are indepen…
the-simmon Feb 8, 2023
bd06b02
Fix python unit test where list assertion fails because list order is…
the-simmon Feb 8, 2023
1582605
Add working dir to ci integration test for pull secret extractation c…
the-simmon Feb 8, 2023
5fa3abe
Add npm ci to makefile to install required modules before integration…
the-simmon Feb 8, 2023
3949390
Add pull secret extractor to release yaml #1189
the-simmon Feb 8, 2023
718356d
Add prereleased trigger to release build yaml #1189
the-simmon Feb 8, 2023
1be050a
Fix dockerhub name of pull secret extractor #1189
the-simmon Feb 8, 2023
37b37ee
Change name in release build to unique one #1189
the-simmon Feb 8, 2023
cdb6a6d
Merge branch 'main' into adr_17_autodiscovery_private_registry
Weltraumschaf Feb 21, 2023
c6ac930
Merge branch 'main' into adr_17_autodiscovery_private_registry
Weltraumschaf Mar 13, 2023
77fd1ae
Merge branch 'main' into adr_17_autodiscovery_private_registry
J12934 Mar 17, 2023
56178f3
Auto format test file
J12934 Mar 17, 2023
f5b1d29
Fix author and license for package json in test dir
J12934 Mar 17, 2023
57de936
Fix name of trivy environment variables
J12934 Mar 17, 2023
82e69c8
Only create trivy auto-discovery scanType when explicitly enabled
J12934 Mar 17, 2023
2c0da66
Change baseimage for consistency with our other python containers
J12934 Mar 17, 2023
5064c6c
Fix message formatting and add success log
J12934 Mar 17, 2023
17c6127
Change casing of Dockerfile to match all our other dockerfile
J12934 Mar 17, 2023
0849b0d
Explicitly copy files to a folder
J12934 Mar 17, 2023
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
48 changes: 47 additions & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ env:
# ---- Language Versions ----

GO_VERSION: "1.19"
PYTHON_VERSION: "3.8.10"
PYTHON_VERSION: "3.9.16"
NODE_VERSION: "16"
NPM_VERSION: "8.19.2"

Expand Down Expand Up @@ -123,6 +123,52 @@ jobs:
name: auto-discovery-image
path: ./auto-discovery/kubernetes/auto-discovery-kubernetes.tar
retention-days: 1

# ---- Build Stage | AutoDiscovery | PullSecretExtractor ----
auto-discovery-kubernetes-secret-extraction-container:
name: "Autodiscovery | Kubernetes | SecretExtractionInitContainer"
runs-on: ubuntu-20.04
steps:
- name: Checkout
uses: actions/checkout@v2

- name: Setup Python Version
uses: actions/setup-python@v4
with:
python-version: '${{ env.PYTHON_VERSION }}'
- name: Unit Tests
working-directory: ./auto-discovery/kubernetes/pull-secret-extractor
run: make unit-test

- name: Build Container Image
working-directory: ./auto-discovery/kubernetes/pull-secret-extractor
run: make docker-build

- name: Export Container Image
working-directory: ./auto-discovery/kubernetes/pull-secret-extractor
run: make docker-export

- name: Upload Image A 527E s Artifact
uses: actions/upload-artifact@v2
with:
name: auto-discovery-pull-secret-extractor
path: ./auto-discovery/kubernetes/pull-secret-extractor/auto-discovery-secret-extractor.tar
retention-days: 1

- name: "Start kind cluster"
run: |
kind version
kind create cluster --wait 3m --image kindest/node:v1.24.0@sha256:0866296e693efe1fed79d5e6c7af8df71fc73ae45e3679af05342239cdc5bc8e

- name: "Inspect kind cluster"
run: |
kubectl config current-context
kubectl get node

- name: "Run integration tests"
working-directory: ./auto-discovery/kubernetes/pull-secret-extractor
run: |
make integration-test

# ---- Build Stage | SDK Matrix ----

Expand Down
49 changes: 48 additions & 1 deletion .github/workflows/release-build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
name: "Release Build"
on:
release:
types: [released]
types: [released, prereleased]

env:
# ---- Language Versions ----
Expand Down Expand Up @@ -118,6 +118,53 @@ jobs:
repository: ${{ env.DOCKER_NAMESPACE }}/auto-discovery-kubernetes
readme-filepath: ./auto-discovery/kubernetes/docs/README.DockerHub-Core.md

# ---- AutoDiscovery | PullSecretExtractor ----

auto-discovery-kubernetes-pull-secret-extractor:
name: "AutoDiscovery | Kubernetes | Pull Secret Extractor"
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2

- name: Docker Meta
id: docker_meta
uses: docker/metadata-action@v3
with:
images: ${{ env.DOCKER_NAMESPACE }}/auto-discovery-pull-secret-extractor
tags: |
type=sha
type=semver,pattern={{version}}

- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1

- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_TOKEN }}

- name: Build and Push
uses: docker/build-push-action@v2
with:
context: ./auto-discovery/kubernetes/pull-secret-extractor
file: ./auto-discovery/kubernetes/pull-secret-extractor/Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.docker_meta.outputs.tags }}
labels: ${{ steps.docker_meta.outputs.labels }}

- name: Update Docker Hub Description
uses: peter-evans/dockerhub-description@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
repository: ${{ env.DOCKER_NAMESPACE }}/auto-discovery-pull-secret-extractor
readme-filepath: ./auto-discovery/kubernetes/pull-secret-extractor/readme.md

# ---- SDK Matrix ----

sdk:
Expand Down
13 changes: 10 additions & 3 deletions auto-discovery/kubernetes/api/v1/autodiscoveryconfig_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,16 @@ type ServiceAutoDiscoveryConfig struct {
}

type ContainerAutoDiscoveryConfig struct {
Enabled bool `json:"enabled"`
PassiveReconcileInterval metav1.Duration `json:"passiveReconcileInterval"`
ScanConfig ScanConfig `json:"scanConfig"`
Enabled bool `json:"enabled"`
ImagePullSecretConfig ImagePullSecretConfig `json:"imagePullSecretConfig"`
PassiveReconcileInterval metav1.Duration `json:"passiveReconcileInterval"`
ScanConfig ScanConfig `json:"scanConfig"`
}

type ImagePullSecretConfig struct {
MapImagePullSecretsToEnvironmentVariables bool `json:"mapImagePullSecretsToEnvironmentVariables"`
UsernameEnvironmentVariableName string `json:"usernameEnvironmentVariableName"`
PasswordNameEnvironmentVariableName string `json:"passwordEnvironmentVariableName"`
}

type ClusterConfig struct {
Expand Down
16 changes: 16 additions & 0 deletions auto-discovery/kubernetes/api/v1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion auto-discovery/kubernetes/auto-discovery-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ containerAutoDiscovery:
enabled: true
passiveReconcileInterval: 10s
scanConfig:
scanType: trivy-image
scanType: trivy-image-autodiscovery
# -- parameters used for the scans created by the containerAutoDiscovery
parameters:
- "{{ .ImageID }}"
Expand All @@ -66,3 +66,7 @@ containerAutoDiscovery:
defectdojo.securecodebox.io/engagement-version: "{{if (index .Target.Labels `app.kubernetes.io/version`) }}{{ index .Target.Labels `app.kubernetes.io/version` }}{{end}}"
# -- hookSelector allows to specify a LabelSelector with which the hooks are selected
hookSelector: {}
imagePullSecretConfig:
mapImagePullSecretsToEnvironmentVariables: true
usernameEnvironmentVariableName: "TRIVY_USERNAME"
passwordEnvironmentVariableName: "TRIVY_PASSWORD"
154 changes: 153 additions & 1 deletion auto-discovery/kubernetes/controllers/container_scan_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,11 +177,26 @@ func getScanName(imageID string) string {
}

func (r *ContainerScanReconciler) createScheduledScans(ctx context.Context, pod corev1.Pod, imageIDs []string) {
secretsDefined, secrets := checkForImagePullSecrets(pod)
mapSecretsToEnv := r.Config.ContainerAutoDiscoveryConfig.ImagePullSecretConfig.MapImagePullSecretsToEnvironmentVariables

for _, imageID := range imageIDs {
r.createScheduledScan(ctx, pod, imageID)
if secretsDefined {
if mapSecretsToEnv {
r.createScheduledScanWithImagePullSecrets(ctx, pod, imageID, secrets)
}
} else {
r.createScheduledScan(ctx, pod, imageID)
}
}
}

func checkForImagePullSecrets(pod corev1.Pod) (bool, []corev1.LocalObjectReference) {
imagePullSecrets := pod.Spec.ImagePullSecrets
secretsDefined := len(imagePullSecrets) > 0
return secretsDefined, imagePullSecrets
}

func (r *ContainerScanReconciler) createScheduledScan(ctx context.Context, pod corev1.Pod, imageID string) {
namespace := r.getNamespace(ctx, pod)

Expand All @@ -194,6 +209,20 @@ func (r *ContainerScanReconciler) createScheduledScan(ctx context.Context, pod c
r.Log.V(6).Info("Created scheduled scan", "pod", pod.Name, "namespace", pod.Namespace)
}
}

func (r *ContainerScanReconciler) createScheduledScanWithImagePullSecrets(ctx context.Context, pod corev1.Pod, imageID string, secrets []corev1.LocalObjectReference) {
namespace := r.getNamespace(ctx, pod)

newScheduledScan := r.generateScanWithVolumeMounts(pod, imageID, namespace, secrets)
err := r.Client.Create(ctx, &newScheduledScan)

if err != nil {
r.Log.Error(err, "Failed to create scheduled scan", "scan", newScheduledScan, "namespace", namespace)
} else {
r.Log.V(6).Info("Created scheduled scan", "pod", pod.Name, "namespace", pod.Namespace)
}
}

func (r *ContainerScanReconciler) getNamespace(ctx context.Context, pod corev1.Pod) corev1.Namespace {
var result corev1.Namespace
err := r.Client.Get(ctx, types.NamespacedName{Name: pod.Namespace, Namespace: ""}, &result)
Expand Down Expand Up @@ -228,6 +257,129 @@ func (r *ContainerScanReconciler) generateScan(pod corev1.Pod, imageID string, n
return newScheduledScan
}

func (r *ContainerScanReconciler) generateScanWithVolumeMounts(pod corev1.Pod, imageID string, namespace corev1.Namespace, secrets []corev1.LocalObjectReference) executionv1.ScheduledScan {
scanConfig := r.Config.ContainerAutoDiscoveryConfig.ScanConfig
templateArgs := ContainerAutoDiscoveryTemplateArgs{
Config: r.Config,
ScanConfig: scanConfig,
Cluster: r.Config.Cluster,
Target: pod.ObjectMeta,
Namespace: namespace,
ImageID: imageID,
}

extraVolumes, extraMounts := getVolumesForSecrets(secrets, imageID)
scanConfig.Volumes = append(scanConfig.Volumes, extraVolumes...)

scanSpec := util.GenerateScanSpec(scanConfig, templateArgs)
scanSpec.ScanSpec.InitContainers = append(scanSpec.ScanSpec.InitContainers, getSecretExtractionInitContainer(imageID, extraMounts))

pullSecretConfig := r.Config.ContainerAutoDiscoveryConfig.ImagePullSecretConfig
usernameEnvVarName := pullSecretConfig.UsernameEnvironmentVariableName
passwordEnvVarName := pullSecretConfig.PasswordNameEnvironmentVariableName
scanSpec.ScanSpec.Env = append(scanSpec.ScanSpec.Env, getTemporarySecretEnvironmentVariableMount(imageID, usernameEnvVarName, passwordEnvVarName)...)

newScheduledScan := executionv1.ScheduledScan{
ObjectMeta: metav1.ObjectMeta{
Name: getScanName(imageID),
Namespace: pod.Namespace,
Annotations: getScanAnnotations(scanConfig, templateArgs),
Labels: getScanLabels(scanConfig, templateArgs),
},
Spec: scanSpec,
}
return newScheduledScan
}

func getVolumesForSecrets(secrets []corev1.LocalObjectReference, imageID string) ([]corev1.Volume, []corev1.VolumeMount) {
var volumes []corev1.Volume
var mounts []corev1.VolumeMount
for _, secret := range secrets {
volume := corev1.Volume{
Name: secret.Name + "-volume",
VolumeSource: corev1.VolumeSource{
Secret: &corev1.SecretVolumeSource{
SecretName: secret.Name,
},
},
}

mount := corev1.VolumeMount{
Name: secret.Name + "-volume",
MountPath: "/secrets/" + secret.Name,
}

volumes = append(volumes, volume)
mounts = append(mounts, mount)
}
return volumes, mounts
}

func getSecretExtractionInitContainer(imageID string, volumeMounts []corev1.VolumeMount) corev1.Container {
temporarySecretName := getTemporarySecretName(imageID)

return corev1.Container{
Name: "secret-extraction-to-env",
Image: "docker.io/securecodebox/auto-discovery-pull-secret-extractor",
Command: []string{"python"},
Args: []string{"secret_extraction.py", imageID, temporarySecretName},
VolumeMounts: volumeMounts,
Env: []corev1.EnvVar{
{
Name: "POD_NAME",
ValueFrom: &corev1.EnvVarSource{
FieldRef: &corev1.ObjectFieldSelector{
FieldPath: "metadata.name",
},
},
},
{
Name: "NAMESPACE",
ValueFrom: &corev1.EnvVarSource{
FieldRef: &corev1.ObjectFieldSelector{
FieldPath: "metadata.namespace",
},
},
},
},
}
}

func getTemporarySecretName(imageID string) string {
//limit name to kubernetes max length
return ("temporary-secret-" + getScanName(imageID))[:62]
}

func getTemporarySecretEnvironmentVariableMount(imageID string, usernameEnvVarName string, passwordEnvVarName string) []corev1.EnvVar {
trueBool := true
return []corev1.EnvVar{
{
Name: usernameEnvVarName,
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
Optional: &trueBool,
LocalObjectReference: corev1.LocalObjectReference{
Name: getTemporarySecretName(imageID),
},
Key: "username",
},
},
},
{
Name: passwordEnvVarName,
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
Optional: &trueBool,
LocalObjectReference: corev1.LocalObjectReference{
Name: getTemporarySecretName(imageID),
},
Key: "password",
},
},
},
}
}

func (r *ContainerScanReconciler) checkForScanType(ctx context.Context, pod corev1.Pod) bool {
namespace := r.getNamespace(ctx, pod)

Expand Down
Loading
0