From ad7dc28285d410e3a085083ff5fb335aeddac78d Mon Sep 17 00:00:00 2001 From: Christian Kotzbauer Date: Sun, 29 May 2022 10:26:50 +0200 Subject: [PATCH] Multiple pull-secrets and optional fallback-secret (#98) feat: use multiple pull-secrets and add fallback-secret support Signed-off-by: Christian Kotzbauer Co-authored-by: Mario Ofner * adding all pull secrets to the ContainerImage objects and loading one by one when necessary * try to debug what exactly is used for authentication * more debugging * add the secret name to the structure for better understanding * remove debugging messages * add global credentials from sbom-operator namespace * fix secret localObjectName * get ownNamespace * fix add missing variables to error message * try reading ownNamespace * add custom global pull-secret * add new parameter to documentation * - The name of the startup-parameter - refactor the ContainerImage struct. - refactor secrets iteration * prevent the upgrade from having a breaking change because of the namespace_name env-variable * add the POD_NAMESPACE env variable to the documentation * finally renamed the configParameter always return allImageCreds removed unnecessary error message from log-message removed unnecessary info message * fix condition for reading the fallbackPullSecret * fix environment variable value for POD_NAMESPACE * use pullSecret without index and fix the situation where no pullsecret is present * fix pullSecret iteration handling * return proper error when necessary update testcases for registry and syft-analysis * several fixes and improvements Signed-off-by: Christian Kotzbauer * fix: add log-message again Signed-off-by: Christian Kotzbauer * fix: cleanup --- README.md | 2 +- deploy/standard/deployment.yaml | 4 + go.mod | 2 - go.sum | 35 +------- internal/config.go | 1 + internal/kubernetes/kubernetes.go | 72 ++++++++++------- internal/registry/registry.go | 95 ++++++++++++++++------ internal/registry/registry_test.go | 126 +++++++++++++++++++++++------ internal/syft/syft_test.go | 29 ++++++- main.go | 1 + 10 files changed, 249 insertions(+), 118 deletions(-) diff --git a/README.md b/README.md index 86aaa9cc..2f1a4e23 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,7 @@ All parameters are cli-flags. | `dtrack-base-url` | `true` when `dtrack` target is used | `""` | Dependency-Track base URL, e.g. 'https://dtrack.example.com' | | `dtrack-api-key` | `true` when `dtrack` target is used | `""` | Dependency-Track API key | | `kubernetes-cluster-id` | `false` | `"default"` | Kubernetes Cluster ID (to be used in Dependency-Track or Job-Images) | +| `fallback-image-pull-secret` | `false` | `""` | Kubernetes Pull-Secret Name to load as a fallback when all others fail (must be in the same namespace as the sbom-operator) | | `job-image` | `false` | `""` | Job-Image to process images with instead of Syft | | `job-image-pull-secret` | `false` | `""` | Pre-existing pull-secret-name for private job-images | | `job-timeout` | `false` | `3600` | Job-Timeout in seconds (`activeDeadlineSeconds`) | @@ -113,7 +114,6 @@ envVars: secretKeyRef: name: "sbom-operator" key: "accessToken" - ``` diff --git a/deploy/standard/deployment.yaml b/deploy/standard/deployment.yaml index 3ab96789..3cf1b555 100644 --- a/deploy/standard/deployment.yaml +++ b/deploy/standard/deployment.yaml @@ -23,6 +23,10 @@ spec: secretKeyRef: name: "sbom-operator" key: "accessToken" + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace args: # example values - --cron="0 6 * * * *" diff --git a/go.mod b/go.mod index d562e6c6..4835db56 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,6 @@ require ( github.com/acomagu/bufpipe v1.0.3 // indirect github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb // indirect github.com/anchore/go-rpmdb v0.0.0-20210914181456-a9c52348da63 // indirect - github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b // indirect github.com/anchore/packageurl-go v0.1.1-0.20220428202044-a072fa3cb6d7 // indirect github.com/anchore/stereoscope v0.0.0-20220518185348-c97a3c6ffc67 // indirect github.com/andybalholm/brotli v1.0.4 // indirect @@ -66,7 +65,6 @@ require ( github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect github.com/klauspost/compress v1.15.4 // indirect github.com/klauspost/pgzip v1.2.5 // indirect - github.com/kr/pretty v0.3.0 // indirect github.com/magiconair/properties v1.8.6 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect diff --git a/go.sum b/go.sum index 306f0b21..c4b0cce1 100644 --- a/go.sum +++ b/go.sum @@ -144,16 +144,10 @@ github.com/anchore/go-rpmdb v0.0.0-20210914181456-a9c52348da63 h1:C9W/LAydEz/qdU github.com/anchore/go-rpmdb v0.0.0-20210914181456-a9c52348da63/go.mod h1:6qH8c6U/3CBVvDDDBZnPSTbTINq3cIdADUYTaVf75EM= github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04 h1:VzprUTpc0vW0nnNKJfJieyH/TZ9UYAnTZs5/gHTdAe8= github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04/go.mod h1:6dK64g27Qi1qGQZ67gFmBFvEHScy0/C8qhQhNe5B5pQ= -github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZVsCYMrIZBpFxwV26CbsuoEh5muXD5I1Ods= -github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E= github.com/anchore/packageurl-go v0.1.1-0.20220428202044-a072fa3cb6d7 h1:kDrYkTSM9uIxaX/P9s0F4nKYNM+hnSgLJdLpqvsaQ/g= github.com/anchore/packageurl-go v0.1.1-0.20220428202044-a072fa3cb6d7/go.mod h1:Blo6OgJNiYF41ufcgHKkbCKF2MDOMlrqhXv/ij6ocR4= -github.com/anchore/stereoscope v0.0.0-20220406160859-c03a18a6b270 h1:NmxPDR6vo3xjwCL6o+tpF1vUad/BVo+WaVSwueB9W9w= -github.com/anchore/stereoscope v0.0.0-20220406160859-c03a18a6b270/go.mod h1:yoCLUZY0k/pYLNIy0L80p2Ko0PKVNXm8rHtgxp4OiSc= github.com/anchore/stereoscope v0.0.0-20220518185348-c97a3c6ffc67 h1:nbcYgEEv9CLnuKg/8ExvXDiEpCA9pwZcyyraZyBE+aw= github.com/anchore/stereoscope v0.0.0-20220518185348-c97a3c6ffc67/go.mod h1:yoCLUZY0k/pYLNIy0L80p2Ko0PKVNXm8rHtgxp4OiSc= -github.com/anchore/syft v0.46.1 h1:K6JyyOgUZUG499UV2gNXOgNmpJcpJHh4bRNj5KWq3ec= -github.com/anchore/syft v0.46.1/go.mod h1:rRBTv3k0Rlr82R2TRZnfv1YpsMq9OuzU4ARarjqgCbY= github.com/anchore/syft v0.46.3 h1:TKD14BrLiLrWamQI2r46v1Rhl2khNlQXzomhTmkqQ4Y= github.com/anchore/syft v0.46.3/go.mod h1:7H/MkAj2uV4cD08e1MB04m3IJp8oGRJ9Y9CDfGRc94U= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= @@ -462,9 +456,9 @@ github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= +github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= @@ -842,7 +836,6 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -1065,12 +1058,9 @@ github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtP github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM= github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml/v2 v2.0.0-beta.8 h1:dy81yyLYJDwMTifq24Oi/IslOslRrDSb3jwDggjz3Z0= -github.com/pelletier/go-toml/v2 v2.0.0-beta.8/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU= github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= @@ -1142,7 +1132,6 @@ github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfm github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.6.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= @@ -1203,7 +1192,6 @@ github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo= github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA= github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= @@ -1229,8 +1217,6 @@ github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/y github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/spf13/viper v1.9.0/go.mod h1:+i6ajR7OX2XaiBkrcZJFK21htRk7eDeLg7+O6bhUPP4= -github.com/spf13/viper v1.11.0 h1:7OX/1FS6n7jHD1zGrZTM7WtY13ZELRyosK4k93oPr44= -github.com/spf13/viper v1.11.0/go.mod h1:djo0X/bA5+tYVoCn+C7cAYJGcVn/qYLFTG8gdUsX7Zk= github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ= github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI= github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I= @@ -1251,7 +1237,6 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.3.0 h1:mjC+YW8QpAdXibNi+vNWgzmgBH4+5l5dCXv8cNysBLI= github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs= @@ -1533,8 +1518,6 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220516155154-20f960328961 h1:+W/iTMPG0EL7aW+/atntZwZrvSRIj3m3yX414dSULUU= -golang.org/x/net v0.0.0-20220516155154-20f960328961/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 h1:NWy5+hlRbC7HK+PmcXVUmW1IMyFce7to56IUvhUFm7Y= golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1690,8 +1673,6 @@ golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a h1:N2T1jUrTQE9Re6TFF5PhvEHXHCguynGhKjWVsIUt5cY= -golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -1834,8 +1815,6 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f h1:GGU+dLjvlC3qDwqYgL6UgRmHXhOOgns0bZu2Ty5mm6U= -golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df h1:5Pf6pFKu98ODmgnpvkJ3kFUOQGGLIzLIkbzUHp47618= golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= @@ -1950,8 +1929,6 @@ google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEc google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211111162719-482062a4217b/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4 h1:myaecH64R0bIEDjNORIel4iXubqzaHU1K2z8ajBwWcM= -google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd h1:e0TwkXOdbnH/1x5rc5MZ/VYyiZ4v+RdVfrGMqEwT68I= google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= @@ -1987,8 +1964,6 @@ google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnD google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.46.0 h1:oCjezcn6g6A75TGoKYBPgKmVBLexhYLM6MebdrPApP8= google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.46.2 h1:u+MLGgVf7vRdjEYZ8wDFhAVNmhkbJ5hmrA1LMWK1CAQ= google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= @@ -2050,8 +2025,6 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA= -gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= @@ -2071,8 +2044,6 @@ k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= k8s.io/api v0.22.5/go.mod h1:mEhXyLaSD1qTOf40rRiKXkc+2iCem09rWLlFwhCEiAs= -k8s.io/api v0.24.0 h1:J0hann2hfxWr1hinZIDefw7Q96wmCBx6SSB8IY0MdDg= -k8s.io/api v0.24.0/go.mod h1:5Jl90IUrJHUJYEMANRURMiVvJ0g7Ax7r3R1bqO8zx8I= k8s.io/api v0.24.1 h1:BjCMRDcyEYz03joa3K1+rbshwh1Ay6oB53+iUx2H8UY= k8s.io/api v0.24.1/go.mod h1:JhoOvNiLXKTPQ60zh2g0ewpA+bnEYf5q44Flhquh4vQ= k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= @@ -2080,8 +2051,6 @@ k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRp k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= k8s.io/apimachinery v0.22.1/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= k8s.io/apimachinery v0.22.5/go.mod h1:xziclGKwuuJ2RM5/rSFQSYAj0zdbci3DH8kj+WvyN0U= -k8s.io/apimachinery v0.24.0 h1:ydFCyC/DjCvFCHK5OPMKBlxayQytB8pxy8YQInd5UyQ= -k8s.io/apimachinery v0.24.0/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= k8s.io/apimachinery v0.24.1 h1:ShD4aDxTQKN5zNf8K1RQ2u98ELLdIW7jEnlO9uAMX/I= k8s.io/apimachinery v0.24.1/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= @@ -2092,8 +2061,6 @@ k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= k8s.io/client-go v0.22.5/go.mod h1:cs6yf/61q2T1SdQL5Rdcjg9J1ElXSwbjSrW2vFImM4Y= -k8s.io/client-go v0.24.0 h1:lbE4aB1gTHvYFSwm6eD3OF14NhFDKCejlnsGYlSJe5U= -k8s.io/client-go v0.24.0/go.mod h1:VFPQET+cAFpYxh6Bq6f4xyMY80G6jKKktU6G0m00VDw= k8s.io/client-go v0.24.1 h1:w1hNdI9PFrzu3OlovVeTnf4oHDt+FJLd9Ndluvnb42E= k8s.io/client-go v0.24.1/go.mod h1:f1kIDqcEYmwXS/vTbbhopMUbhKp2JhOeVTfxgaCIlF8= k8s.io/code-generator v0.19.7/go.mod h1:lwEq3YnLYb/7uVXLorOJfxg+cUu2oihFhHZ0n9NIla0= diff --git a/internal/config.go b/internal/config.go index fcda5513..74fa8da7 100644 --- a/internal/config.go +++ b/internal/config.go @@ -24,4 +24,5 @@ var ( ConfigKeyOciRegistry = "oci-registry" ConfigKeyOciUser = "oci-user" ConfigKeyOciToken = "oci-token" + ConfigKeyFallbackPullSecret = "fallback-pull-secret" ) diff --git a/internal/kubernetes/kubernetes.go b/internal/kubernetes/kubernetes.go index f969c1f7..c2d1599d 100644 --- a/internal/kubernetes/kubernetes.go +++ b/internal/kubernetes/kubernetes.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/sirupsen/logrus" + "github.com/spf13/viper" batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" @@ -18,17 +19,23 @@ import ( "github.com/ckotzbauer/sbom-operator/internal" ) +type KubeCreds struct { + SecretName string + SecretCredsData []byte + IsLegacySecret bool +} + type ContainerImage struct { - Image string - ImageID string - Auth []byte - LegacyAuth bool - Pods []corev1.Pod + Image string + ImageID string + Pods []corev1.Pod + PullSecrets []KubeCreds } type KubeClient struct { - Client *kubernetes.Clientset - ignoreAnnotations bool + Client *kubernetes.Clientset + ignoreAnnotations bool + SbomOperatorNamespace string } var ( @@ -51,7 +58,8 @@ func NewClient(ignoreAnnotations bool) *KubeClient { logrus.WithError(err).Fatal("Could not create Kubernetes client from config!") } - return &KubeClient{Client: client, ignoreAnnotations: ignoreAnnotations} + sbomOperatorNamespace := os.Getenv("POD_NAMESPACE") + return &KubeClient{Client: client, ignoreAnnotations: ignoreAnnotations, SbomOperatorNamespace: sbomOperatorNamespace} } func prepareLabelSelector(selector string) meta.ListOptions { @@ -97,17 +105,24 @@ func (client *KubeClient) LoadImageInfos(namespaces []corev1.Namespace, podLabel } for _, pod := range pods { + allImageCreds := []KubeCreds{} + annotations := pod.Annotations statuses := []corev1.ContainerStatus{} statuses = append(statuses, pod.Status.ContainerStatuses...) statuses = append(statuses, pod.Status.InitContainerStatuses...) statuses = append(statuses, pod.Status.EphemeralContainerStatuses...) - pullSecrets, legacy, err := client.loadSecrets(pod.Namespace, pod.Spec.ImagePullSecrets) + allImageCreds = client.loadSecrets(pod.Namespace, pod.Spec.ImagePullSecrets) - if err != nil { - logrus.WithError(err).Errorf("PullSecrets could not be retrieved for pod %s/%s", ns.Name, pod.Name) - continue + fallbackPullSecretName := viper.GetString(internal.ConfigKeyFallbackPullSecret) + if fallbackPullSecretName != "" { + if client.SbomOperatorNamespace == "" { + logrus.Debugf("please specify the environment variable 'POD_NAMESPACE' in order to use the fallbackPullSecret") + } else { + fallbackPullSecret := client.loadSecrets(client.SbomOperatorNamespace, []corev1.LocalObjectReference{{Name: fallbackPullSecretName}}) + allImageCreds = append(allImageCreds, fallbackPullSecret...) + } } for _, c := range statuses { @@ -119,11 +134,10 @@ func (client *KubeClient) LoadImageInfos(namespaces []corev1.Namespace, podLabel img, ok := images[trimmedImageID] if !ok { img = ContainerImage{ - Image: c.Image, - ImageID: trimmedImageID, - Auth: pullSecrets, - LegacyAuth: legacy, - Pods: []corev1.Pod{}, + Image: c.Image, + ImageID: trimmedImageID, + Pods: []corev1.Pod{}, + PullSecrets: allImageCreds, } } @@ -133,11 +147,10 @@ func (client *KubeClient) LoadImageInfos(namespaces []corev1.Namespace, podLabel } else { logrus.Debugf("Skip image %s", trimmedImageID) allImages = append(allImages, ContainerImage{ - Image: c.Image, - ImageID: trimmedImageID, - Auth: pullSecrets, - LegacyAuth: legacy, - Pods: []corev1.Pod{}, + Image: c.Image, + ImageID: trimmedImageID, + Pods: []corev1.Pod{}, + PullSecrets: allImageCreds, }) } } @@ -196,16 +209,19 @@ func (client *KubeClient) hasAnnotation(annotations map[string]string, status co return false } -func (client *KubeClient) loadSecrets(namespace string, secrets []corev1.LocalObjectReference) ([]byte, bool, error) { - // TODO: Support all secrets which are referenced as imagePullSecrets instead of only the first one. +func (client *KubeClient) loadSecrets(namespace string, secrets []corev1.LocalObjectReference) []KubeCreds { + allImageCreds := []KubeCreds{} + for _, s := range secrets { secret, err := client.Client.CoreV1().Secrets(namespace).Get(context.Background(), s.Name, meta.GetOptions{}) if err != nil { - return nil, false, err + logrus.WithError(err).Errorf("Could not load secret: %s/%s", namespace, s.Name) + continue } var creds []byte legacy := false + name := secret.Name if secret.Type == corev1.SecretTypeDockerConfigJson { creds = secret.Data[corev1.DockerConfigJsonKey] @@ -213,15 +229,15 @@ func (client *KubeClient) loadSecrets(namespace string, secrets []corev1.LocalOb creds = secret.Data[corev1.DockerConfigKey] legacy = true } else { - return nil, false, fmt.Errorf("invalid secret-type %s for pullSecret %s/%s", secret.Type, secret.Namespace, secret.Name) + logrus.WithError(err).Errorf("invalid secret-type %s for pullSecret %s/%s", secret.Type, secret.Namespace, secret.Name) } if len(creds) > 0 { - return creds, legacy, nil + allImageCreds = append(allImageCreds, KubeCreds{SecretName: name, SecretCredsData: creds, IsLegacySecret: legacy}) } } - return nil, false, nil + return allImageCreds } func (client *KubeClient) CreateJobSecret(namespace, suffix string, data []byte) error { diff --git a/internal/registry/registry.go b/internal/registry/registry.go index 815f0a97..60bff636 100644 --- a/internal/registry/registry.go +++ b/internal/registry/registry.go @@ -12,72 +12,108 @@ import ( "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/remote" + "github.com/sirupsen/logrus" "github.com/ckotzbauer/sbom-operator/internal/kubernetes" parser "github.com/novln/docker-parser" ) -func SaveImage(imagePath string, image kubernetes.ContainerImage) error { - imageMap := map[string]v1.Image{} +var ( + ErrorNoValidPullSecret = fmt.Errorf("No valid or valid-but-unauthorized PullSecret found from ContainerImage") +) +func SaveImage(imagePath string, image kubernetes.ContainerImage) error { o := crane.GetOptions() + var err error + var cfg types.AuthConfig + empty := types.AuthConfig{} - if len(image.Auth) > 0 { - cfg, err := ResolveAuthConfig(image) - empty := types.AuthConfig{} - + if len(image.PullSecrets) == 0 { + _, err := downloadImage(imagePath, image, o) if err != nil { - return err + logrus.WithError(err).Error() } - if cfg != empty { - o.Remote = []remote.Option{ - remote.WithAuth(authn.FromConfig(authn.AuthConfig{ - Username: cfg.Username, - Password: cfg.Password, - Auth: cfg.Auth, - IdentityToken: cfg.IdentityToken, - RegistryToken: cfg.RegistryToken, - })), + return err + } else { + for _, pullSecret := range image.PullSecrets { + cfg, err = resolveAuthConfigWithPullSecret(image, pullSecret) + if err != nil { + logrus.WithError(err).Warnf("image: %s, Read authentication configuration from secret: %s failed", image.ImageID, pullSecret.SecretName) + continue + } + + if cfg != empty { + o.Remote = []remote.Option{ + remote.WithAuth(authn.FromConfig(authn.AuthConfig{ + Username: cfg.Username, + Password: cfg.Password, + Auth: cfg.Auth, + IdentityToken: cfg.IdentityToken, + RegistryToken: cfg.RegistryToken, + })), + } + } + + proceed, err := downloadImage(imagePath, image, o) + if !proceed && err == nil { + logrus.Debugf("Image %s successfully pulled with PullSecret: %s", image.ImageID, pullSecret.SecretName) + return nil + } else if !proceed && err != nil { + logrus.WithError(err).Error() + return err + } else if proceed && err != nil { + logrus.WithError(err).Debug() } } } + // no valid pull request found for this image - returning an error + return ErrorNoValidPullSecret +} + +func downloadImage(imagePath string, image kubernetes.ContainerImage, o crane.Options) (bool, error) { + imageMap := map[string]v1.Image{} ref, err := name.ParseReference(image.ImageID, o.Name...) if err != nil { - return fmt.Errorf("parsing reference %q: %w", image.ImageID, err) + // should stop immediately because it seems that no other pullSecret will solve this problem + return false, fmt.Errorf("parsing reference %q: %w", image.ImageID, err) } rmt, err := remote.Get(ref, o.Remote...) if err != nil { - return err + // should continue, because, this might be an Authentication Error + return true, fmt.Errorf("image: %s, Image-Pull Error: %w", image.ImageID, err) } img, err := rmt.Image() if err != nil { - return err + // should stop immediately because no other pullSecret will solve this problem + return false, err } imageMap[image.ImageID] = img if err := crane.MultiSave(imageMap, imagePath); err != nil { - return fmt.Errorf("saving tarball %s: %w", imagePath, err) + // should stop immediately because no other pullSecret will solve this problem + return false, fmt.Errorf("saving tarball %s: %w", imagePath, err) } - return nil + // pull was sucessfull - no error occurred + return false, nil } -func ResolveAuthConfig(image kubernetes.ContainerImage) (types.AuthConfig, error) { +func resolveAuthConfigWithPullSecret(image kubernetes.ContainerImage, pullSecret kubernetes.KubeCreds) (types.AuthConfig, error) { var cf *configfile.ConfigFile var err error - if image.LegacyAuth { + if pullSecret.IsLegacySecret { cf = configfile.New("") - err = LegacyLoadFromReader(bytes.NewReader(image.Auth), cf) + err = LegacyLoadFromReader(bytes.NewReader(pullSecret.SecretCredsData), cf) } else { - cf, err = config.LoadFromReader(bytes.NewReader(image.Auth)) + cf, err = config.LoadFromReader(bytes.NewReader(pullSecret.SecretCredsData)) } if err != nil { @@ -107,3 +143,12 @@ func ResolveAuthConfig(image kubernetes.ContainerImage) (types.AuthConfig, error return cfg, nil } + +func ResolveAuthConfig(image kubernetes.ContainerImage) (types.AuthConfig, error) { + // to not break JobImages this function needs to redirect to the actual resolve-function, using the first pullSecret from the list if exists + if len(image.PullSecrets) > 0 { + return resolveAuthConfigWithPullSecret(image, image.PullSecrets[0]) + } else { + return types.AuthConfig{}, nil + } +} diff --git a/internal/registry/registry_test.go b/internal/registry/registry_test.go index bdb93789..d6d132c5 100644 --- a/internal/registry/registry_test.go +++ b/internal/registry/registry_test.go @@ -11,58 +11,92 @@ import ( ) type testData struct { - registry string - image string - legacy bool + registry string + image string + legacy bool + imageSize int64 } func TestRegistry(t *testing.T) { tests := []testData{ { - registry: "gcr", - image: "gcr.io/sbom-git-operator/integration-test-image:1.0.0", - legacy: false, + registry: "gcr", + image: "gcr.io/sbom-git-operator/integration-test-image:1.0.0", + legacy: false, + imageSize: 2823168, }, { - registry: "gar", - image: "europe-west3-docker.pkg.dev/sbom-git-operator/sbom-git-operator/integration-test-image:1.0.0", - legacy: false, + registry: "gar", + image: "europe-west3-docker.pkg.dev/sbom-git-operator/sbom-git-operator/integration-test-image:1.0.0", + legacy: false, + imageSize: 2823168, }, { - registry: "ecr", - image: "055403865123.dkr.ecr.eu-central-1.amazonaws.com/sbom-git-operator/integration-test-image:1.0.0", - legacy: false, + registry: "ecr", + image: "055403865123.dkr.ecr.eu-central-1.amazonaws.com/sbom-git-operator/integration-test-image:1.0.0", + legacy: false, + imageSize: 2823168, }, /*{ registry: "acr", image: "sbomgitoperator.azurecr.io/integration-test-image:1.0.0", legacy: false, + imageSize: 2823168, },*/ { - registry: "hub", - image: "docker.io/ckotzbauer/integration-test-image:1.0.0", - legacy: false, + registry: "hub", + image: "docker.io/ckotzbauer/integration-test-image:1.0.0", + legacy: false, + imageSize: 2823168, }, { - registry: "ghcr", - image: "ghcr.io/ckotzbauer-kubernetes-bot/sbom-git-operator-integration-test:1.0.0", - legacy: false, + registry: "ghcr", + image: "ghcr.io/ckotzbauer-kubernetes-bot/sbom-git-operator-integration-test:1.0.0", + legacy: false, + imageSize: 2823168, }, { - registry: "legacy-ghcr", - image: "ghcr.io/ckotzbauer-kubernetes-bot/sbom-git-operator-integration-test:1.0.0", - legacy: true, + registry: "legacy-ghcr", + image: "ghcr.io/ckotzbauer-kubernetes-bot/sbom-git-operator-integration-test:1.0.0", + legacy: true, + imageSize: 2823168, }, } + unauthenticatedPositiveTest := testData{ + registry: "docker-io", + image: "hello-world:latest", + legacy: false, + imageSize: 7168, + } + + unauthenticatedNegativeTest := testData{ + registry: "ghcr", + image: "ghcr.io/ckotzbauer-kubernetes-bot/sbom-git-operator-integration-test:1.0.0", + legacy: false, + imageSize: 2823168, + } + for _, v := range tests { t.Run(v.registry, func(t *testing.T) { - testRegistry(t, v.registry, v.image, v.legacy) + testRegistry(t, v.registry, v.image, v.legacy, v.imageSize) }) } + + t.Run("unauthenticated positive", func(t *testing.T) { + testRegistryWithoutPullSecretsPositive(t, unauthenticatedPositiveTest.image, unauthenticatedPositiveTest.imageSize) + }) + + t.Run("unauthenticated negative", func(t *testing.T) { + testRegistryWithoutPullSecretsNegative(t, unauthenticatedNegativeTest.image) + }) + + t.Run("wrong secret negative", func(t *testing.T) { + testRegistryWithWrongPullSecretNegative(t, unauthenticatedNegativeTest.image) + }) } -func testRegistry(t *testing.T, name, image string, legacy bool) { +func testRegistry(t *testing.T, name, image string, legacy bool, imageSize int64) { b, err := os.ReadFile("../../auth/" + name + ".yaml") assert.NoError(t, err) @@ -70,13 +104,55 @@ func testRegistry(t *testing.T, name, image string, legacy bool) { assert.NoError(t, err) file := "/tmp/1.0.0.tar.gz" - err = registry.SaveImage(file, kubernetes.ContainerImage{ImageID: image, Auth: []byte(decoded), LegacyAuth: legacy}) + err = registry.SaveImage(file, kubernetes.ContainerImage{ + ImageID: image, + PullSecrets: []kubernetes.KubeCreds{{SecretName: name, SecretCredsData: []byte(decoded), IsLegacySecret: legacy}}, + }) + + if err == nil { + stat, _ := os.Stat(file) + assert.Equal(t, imageSize, stat.Size()) + } + + os.Remove(file) + assert.NoError(t, err) +} + +// this test should check if an image is pullable without pullSecrets (e.g. dockerhub - where it is really possible) +func testRegistryWithoutPullSecretsPositive(t *testing.T, image string, imageSize int64) { + file := "/tmp/1.0.0.tar.gz" + err := registry.SaveImage(file, kubernetes.ContainerImage{ImageID: image, PullSecrets: []kubernetes.KubeCreds{}}) if err == nil { stat, _ := os.Stat(file) - assert.Equal(t, int64(2823168), stat.Size()) + assert.Equal(t, imageSize, stat.Size()) } os.Remove(file) assert.NoError(t, err) } + +// this test should check if an image is not pullable without pullSecrets (e.g. internal registry - where it is forbidden) +// an error must be returned +func testRegistryWithoutPullSecretsNegative(t *testing.T, image string) { + file := "/tmp/1.0.0.tar.gz" + err := registry.SaveImage(file, kubernetes.ContainerImage{ImageID: image, PullSecrets: []kubernetes.KubeCreds{}}) + assert.Error(t, err) + os.Remove(file) +} + +// this test should check if an image is not pullable with wrong pullSecrets +// an error must be returned +func testRegistryWithWrongPullSecretNegative(t *testing.T, image string) { + file := "/tmp/1.0.0.tar.gz" + err := registry.SaveImage(file, kubernetes.ContainerImage{ + ImageID: image, + PullSecrets: []kubernetes.KubeCreds{{SecretName: "test", SecretCredsData: []byte{}, IsLegacySecret: false}}, + }) + + if assert.Error(t, err) { + assert.Equal(t, registry.ErrorNoValidPullSecret, err) + } + + os.Remove(file) +} diff --git a/internal/syft/syft_test.go b/internal/syft/syft_test.go index 76aabf6a..d583485e 100644 --- a/internal/syft/syft_test.go +++ b/internal/syft/syft_test.go @@ -60,7 +60,8 @@ func marshalCyclonedx(t *testing.T, x interface{}) string { func testJsonSbom(t *testing.T, name, imageID string) { format := "json" s := syft.New(format).WithVersion("v9.9.9") - sbom, err := s.ExecuteSyft(kubernetes.ContainerImage{ImageID: imageID, Auth: []byte{}}) + sbom, err := s.ExecuteSyft(kubernetes.ContainerImage{ImageID: imageID, PullSecrets: []kubernetes.KubeCreds{}}) + assert.NoError(t, err) var output syftJsonOutput @@ -83,7 +84,7 @@ func testJsonSbom(t *testing.T, name, imageID string) { func testCyclonedxSbom(t *testing.T, name, imageID string) { format := "cyclonedx" s := syft.New(format).WithVersion("v9.9.9") - sbom, err := s.ExecuteSyft(kubernetes.ContainerImage{ImageID: imageID, Auth: []byte{}}) + sbom, err := s.ExecuteSyft(kubernetes.ContainerImage{ImageID: imageID, PullSecrets: []kubernetes.KubeCreds{}}) assert.NoError(t, err) var output syftCyclonedxOutput @@ -103,7 +104,7 @@ func testCyclonedxSbom(t *testing.T, name, imageID string) { func testSpdxSbom(t *testing.T, name, imageID string) { format := "spdxjson" s := syft.New(format).WithVersion("v9.9.9") - sbom, err := s.ExecuteSyft(kubernetes.ContainerImage{ImageID: imageID, Auth: []byte{}}) + sbom, err := s.ExecuteSyft(kubernetes.ContainerImage{ImageID: imageID, PullSecrets: []kubernetes.KubeCreds{}}) assert.NoError(t, err) var output syftSpdxOutput @@ -123,6 +124,27 @@ func testSpdxSbom(t *testing.T, name, imageID string) { assert.Equal(t, output.SpdxVersion, fixture.SpdxVersion) } +// test for analysing an image completely without pullSecret +func testCyclonedxSbomWithoutPullSecrets(t *testing.T, name, imageID string) { + format := "cyclonedx" + s := syft.New(format).WithVersion("v9.9.9") + sbom, err := s.ExecuteSyft(kubernetes.ContainerImage{ImageID: imageID, PullSecrets: []kubernetes.KubeCreds{}}) + assert.NoError(t, err) + + var output syftCyclonedxOutput + err = xml.Unmarshal([]byte(sbom), &output) + assert.NoError(t, err) + + data, err := os.ReadFile("./fixtures/" + name + "." + format) + assert.NoError(t, err) + + var fixture syftCyclonedxOutput + err = xml.Unmarshal(data, &fixture) + assert.NoError(t, err) + + assert.Equal(t, marshalCyclonedx(t, output.Components), marshalCyclonedx(t, fixture.Components)) +} + func TestSyft(t *testing.T) { tests := []testData{ { @@ -178,6 +200,7 @@ func TestSyft(t *testing.T) { testJsonSbom(t, v.image, v.digest) } else if v.format == "cyclonedx" { testCyclonedxSbom(t, v.image, v.digest) + testCyclonedxSbomWithoutPullSecrets(t, v.image, v.digest) } else if v.format == "spdxjson" { testSpdxSbom(t, v.image, v.digest) } diff --git a/main.go b/main.go index b302f947..49d199d2 100644 --- a/main.go +++ b/main.go @@ -62,6 +62,7 @@ func init() { rootCmd.PersistentFlags().String(internal.ConfigKeyKubernetesClusterId, "default", "Kubernetes Cluster ID") rootCmd.PersistentFlags().String(internal.ConfigKeyJobImage, "", "Custom Job-Image") rootCmd.PersistentFlags().String(internal.ConfigKeyJobImagePullSecret, "", "Custom Job-Image-Pull-Secret") + rootCmd.PersistentFlags().String(internal.ConfigKeyFallbackPullSecret, "", "Fallback-Pull-Secret") rootCmd.PersistentFlags().Int64(internal.ConfigKeyJobTimeout, 60*60, "Job-Timeout") rootCmd.PersistentFlags().String(internal.ConfigKeyOciRegistry, "", "OCI-Registry") rootCmd.PersistentFlags().String(internal.ConfigKeyOciUser, "", "OCI-User")