From b14bacbfc67d1c7cc1d19ec1674e43c83eaf286f Mon Sep 17 00:00:00 2001 From: Lemon-miaow Date: Thu, 24 Sep 2026 03:11:01 +0800 Subject: [PATCH] =?UTF-8?q?fix(build):=20mirror=20the=20Trivy=20Java=20DB?= =?UTF-8?q?=20=E2=80=94=20jar-bearing=20builds=20failed=20closed=20at=20th?= =?UTF-8?q?e=20scan=20gate=20(#72)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/felis/api.go | 3 ++- deploy/bootstrap.sh | 2 +- deploy/bootstrap_test.sh | 5 +++++ docs/deferred-seams.md | 10 ++++++---- docs/troubleshooting.md | 11 +++++++++++ internal/build/build.go | 32 +++++++++++++++++++------------- internal/build/jobspec.go | 18 +++++++++++++----- internal/build/jobspec_test.go | 17 +++++++++++------ internal/config/config.go | 9 +++++++++ 9 files changed, 77 insertions(+), 30 deletions(-) diff --git a/cmd/felis/api.go b/cmd/felis/api.go index dd147a4..af987c4 100644 --- a/cmd/felis/api.go +++ b/cmd/felis/api.go @@ -423,7 +423,8 @@ func buildConfig(cfg *config.Config) build.Config { MemLimit: cfg.Registry.BuildMemLimit, // Empty keeps Trivy's own default; an install with builds points this at // the internal DB mirror (see config.RegistryConfig.TrivyDBRepository). - TrivyDBRepository: cfg.Registry.TrivyDBRepository, + TrivyDBRepository: cfg.Registry.TrivyDBRepository, + TrivyJavaDBRepository: cfg.Registry.TrivyJavaDBRepository, } } diff --git a/deploy/bootstrap.sh b/deploy/bootstrap.sh index 06cc9a1..f7b00ac 100644 --- a/deploy/bootstrap.sh +++ b/deploy/bootstrap.sh @@ -2188,7 +2188,7 @@ persisted_registry_block() { out="$(awk ' /^[[:space:]]*\[/ { sect = $0; next } sect ~ /^[[:space:]]*\[registry\][[:space:]]*$/ && - /^[[:space:]]*(kaniko_image|trivy_image|trivy_db_repository|build_cpu_limit|build_mem_limit|user_uploads_context)[[:space:]]*=/ { print } + /^[[:space:]]*(kaniko_image|trivy_image|trivy_db_repository|trivy_java_db_repository|build_cpu_limit|build_mem_limit|user_uploads_context)[[:space:]]*=/ { print } sect ~ /^[[:space:]]*\[registry\.s3\][[:space:]]*$/ && /^[[:space:]]*[A-Za-z_]+[[:space:]]*=/ { if (!s3hdr) { printf "[registry.s3]\n"; s3hdr = 1 } print diff --git a/deploy/bootstrap_test.sh b/deploy/bootstrap_test.sh index 119f60e..85d5460 100644 --- a/deploy/bootstrap_test.sh +++ b/deploy/bootstrap_test.sh @@ -878,6 +878,7 @@ url = "stale.invalid:5000" build_namespace = "stale-ns" kaniko_image = "registry.felis.svc:5000/mirror/kaniko-executor:v1.24.0" trivy_db_repository = "registry.felis.svc:5000/mirror/trivy-db:2" +trivy_java_db_repository = "registry.felis.svc:5000/mirror/trivy-java-db:1" [registry.s3] endpoint = "https://s3.example" @@ -905,6 +906,10 @@ run_write "$rdir/out.toml" out="$(cat "$rdir/out.toml")" expect "a re-run carries the build-lane executor mirrors" \ 'kaniko_image = "registry.felis.svc:5000/mirror/kaniko-executor:v1.24.0"' "$out" +expect "a re-run carries the trivy vulnerability-DB mirror" \ + 'trivy_db_repository = "registry.felis.svc:5000/mirror/trivy-db:2"' "$out" +expect "a re-run carries the trivy java-DB mirror" \ + 'trivy_java_db_repository = "registry.felis.svc:5000/mirror/trivy-java-db:1"' "$out" expect "a re-run carries the [registry.s3] uploads subtable" "[registry.s3]" "$out" expect "the carried subtable keeps its keys" 'endpoint = "https://s3.example"' "$out" expect "url stays installer-owned" 'url = "registry.felis.svc:5000"' "$out" diff --git a/docs/deferred-seams.md b/docs/deferred-seams.md index b324c13..18ffab6 100644 --- a/docs/deferred-seams.md +++ b/docs/deferred-seams.md @@ -57,10 +57,12 @@ A grep across `*.md` and `*.go` returns both sets; only the Go ones are seams. build_cpu_limit / build_mem_limit` override them for mirrored or air-gapped installs. Trivy's vulnerability DB is the same story, and now has its own knob: `[registry] trivy_db_repository` points `--db-repository` at an internal mirror - (recipe in docs/troubleshooting.md §8e). Left unset on an egress-locked box the - scan step fails closed — Kaniko pushes, Trivy exits on the DB download — which - is the correct fail direction but leaves the build unfinished, so the mirror is - part of a production build install. + (recipe in docs/troubleshooting.md §8e); `trivy_java_db_repository` does the + same for the Java DB, which Trivy fetches so soon as the scanned image contains + a jar — i.e. for every real modpack build. Left unset on an egress-locked box + the scan step fails closed — Kaniko pushes, Trivy exits on the DB download — + which is the correct fail direction but leaves the build unfinished, so the + mirrors are part of a production build install. ## Built; only its I/O is unverifiable from this repo diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index c31bee1..529e57b 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -411,6 +411,7 @@ build_namespace = "felis-build" kaniko_image = "registry.felis.svc:5000/mirror/kaniko-executor:v1.24.0" trivy_image = "registry.felis.svc:5000/mirror/trivy:0.74.0" trivy_db_repository = "registry.felis.svc:5000/mirror/trivy-db:2" +trivy_java_db_repository = "registry.felis.svc:5000/mirror/trivy-java-db:1" build_cpu_limit = "2" build_mem_limit = "4Gi" ``` @@ -423,10 +424,13 @@ through the loopback hostPort the registry Deployment binds (docker treats ```sh docker pull gcr.io/kaniko-project/executor:v1.24.0 # any versions you pin docker pull aquasec/trivy:0.74.0 +docker pull mirror.gcr.io/aquasec/trivy-java-db:1 docker tag gcr.io/kaniko-project/executor:v1.24.0 127.0.0.1:5000/mirror/kaniko-executor:v1.24.0 docker tag aquasec/trivy:0.74.0 127.0.0.1:5000/mirror/trivy:0.74.0 +docker tag mirror.gcr.io/aquasec/trivy-java-db:1 127.0.0.1:5000/mirror/trivy-java-db:1 docker push 127.0.0.1:5000/mirror/kaniko-executor:v1.24.0 docker push 127.0.0.1:5000/mirror/trivy:0.74.0 +docker push 127.0.0.1:5000/mirror/trivy-java-db:1 ``` From another machine, port-forward the registry instead (`kubectl -n felis @@ -469,6 +473,13 @@ registry's plain HTTP works for the DB pull exactly as it does for the scanned image. Re-mirror the tag periodically (Trivy refreshes the DB several times a day upstream; a stale mirror only means stale CVE data, never a failed gate). +`trivy_java_db_repository` is the same story one step lazier: Trivy downloads +the Java DB on demand the first time it scans an image containing Java +artifacts — every real modpack — and that download fails closed too. Mirror +`mirror.gcr.io/aquasec/trivy-java-db:1` alongside the vulnerability DB (commands +above); the Java DB refreshes far less often than the vulnerability DB, so a +one-off mirror is usually fine. + --- ## 9. Registry push/pull failures (spec §15) diff --git a/internal/build/build.go b/internal/build/build.go index 6aca390..dcde97c 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -225,6 +225,11 @@ type Config struct { // egress lock denies — an install with builds must point this at an internal // mirror (see config.RegistryConfig.TrivyDBRepository). TrivyDBRepository string + // TrivyJavaDBRepository overrides where Trivy fetches its Java DB + // (--java-db-repository), downloaded lazily for images that contain Java + // artifacts — i.e. every real modpack. Same egress story as the + // vulnerability DB (see config.RegistryConfig.TrivyJavaDBRepository). + TrivyJavaDBRepository string // KanikoImage / TrivyImage are the executor images. KanikoImage string TrivyImage string @@ -359,19 +364,20 @@ func (b *Builder) Submit(ctx context.Context, req Request) (*Build, error) { // jobParams projects a build + config onto the inputs jobspec.go renders. func (b *Builder) jobParams(bld *Build, cfg Config) JobParams { return JobParams{ - BuildID: bld.ID, - ImageRef: bld.ImageRef, - ContextRef: bld.ContextRef, - Namespace: cfg.Namespace, - ServiceAccount: cfg.ServiceAccount, - RegistryURL: cfg.RegistryURL, - FelisImage: cfg.FelisImage, - TrivyDBRepository: cfg.TrivyDBRepository, - KanikoImage: cfg.KanikoImage, - TrivyImage: cfg.TrivyImage, - Deadline: cfg.Deadline, - CPULimit: cfg.CPULimit, - MemLimit: cfg.MemLimit, + BuildID: bld.ID, + ImageRef: bld.ImageRef, + ContextRef: bld.ContextRef, + Namespace: cfg.Namespace, + ServiceAccount: cfg.ServiceAccount, + RegistryURL: cfg.RegistryURL, + FelisImage: cfg.FelisImage, + TrivyDBRepository: cfg.TrivyDBRepository, + TrivyJavaDBRepository: cfg.TrivyJavaDBRepository, + KanikoImage: cfg.KanikoImage, + TrivyImage: cfg.TrivyImage, + Deadline: cfg.Deadline, + CPULimit: cfg.CPULimit, + MemLimit: cfg.MemLimit, } } diff --git a/internal/build/jobspec.go b/internal/build/jobspec.go index aff5515..9427319 100644 --- a/internal/build/jobspec.go +++ b/internal/build/jobspec.go @@ -82,11 +82,16 @@ type JobParams struct { // --db-repository flag). Empty keeps Trivy's own default; see // build.Config.TrivyDBRepository for why an in-cluster install sets it. TrivyDBRepository string - KanikoImage string - TrivyImage string - Deadline time.Duration - CPULimit string - MemLimit string + // TrivyJavaDBRepository overrides Trivy's Java-DB source (the + // --java-db-repository flag), fetched lazily when the image contains Java + // artifacts; empty keeps Trivy's own default, which the build egress lock + // denies — a jar-bearing image then fails the scan. + TrivyJavaDBRepository string + KanikoImage string + TrivyImage string + Deadline time.Duration + CPULimit string + MemLimit string } // BuildJobName is the deterministic Job name for a build id. @@ -257,6 +262,9 @@ func BuildJob(p JobParams) (*batchv1.Job, error) { if p.TrivyDBRepository != "" { trivyArgs = append(trivyArgs, "--db-repository", p.TrivyDBRepository) } + if p.TrivyJavaDBRepository != "" { + trivyArgs = append(trivyArgs, "--java-db-repository", p.TrivyJavaDBRepository) + } trivyArgs = append(trivyArgs, p.ImageRef) trivy := corev1.Container{ Name: ContainerTrivy, diff --git a/internal/build/jobspec_test.go b/internal/build/jobspec_test.go index 9424ee8..effae8d 100644 --- a/internal/build/jobspec_test.go +++ b/internal/build/jobspec_test.go @@ -204,9 +204,9 @@ func TestBuildJobKanikoPushesAndTrivyGates(t *testing.T) { if !hasArg(trivy.Args, p.ImageRef) { t.Errorf("trivy must scan the pushed ref %q, args=%v", p.ImageRef, trivy.Args) } - // No DB repository configured: Trivy keeps its own default. - if hasArg(trivy.Args, "--db-repository") { - t.Errorf("unset TrivyDBRepository must not render --db-repository, args=%v", trivy.Args) + // No DB repositories configured: Trivy keeps its own defaults. + if hasArg(trivy.Args, "--db-repository") || hasArg(trivy.Args, "--java-db-repository") { + t.Errorf("unset DB repositories must not render --db-repository/--java-db-repository, args=%v", trivy.Args) } } @@ -262,12 +262,14 @@ func TestBuildJobKanikoGetsOnlyUnpackCapabilities(t *testing.T) { } } -// A configured DB repository (the internal mirror) must reach Trivy as -// --db-repository: without it the scan tries the internet, which the build egress -// lock denies, and every build fails closed at the scan gate. +// Configured DB repositories (the internal mirrors) must reach Trivy as +// --db-repository / --java-db-repository: without them the scan tries the +// internet, which the build egress lock denies, and every build fails closed at +// the scan gate — the Java DB the moment the image contains a jar. func TestBuildJobTrivyDBRepositoryOverride(t *testing.T) { p := sampleJobParams() p.TrivyDBRepository = "registry.felis.svc:5000/mirror/trivy-db:2" + p.TrivyJavaDBRepository = "registry.felis.svc:5000/mirror/trivy-java-db:1" job, err := BuildJob(p) if err != nil { t.Fatalf("BuildJob: %v", err) @@ -276,6 +278,9 @@ func TestBuildJobTrivyDBRepositoryOverride(t *testing.T) { if !argPairPresent(trivy.Args, "--db-repository", p.TrivyDBRepository) { t.Errorf("trivy args = %v, want --db-repository %s", trivy.Args, p.TrivyDBRepository) } + if !argPairPresent(trivy.Args, "--java-db-repository", p.TrivyJavaDBRepository) { + t.Errorf("trivy args = %v, want --java-db-repository %s", trivy.Args, p.TrivyJavaDBRepository) + } // The scanned image ref must stay the last argument. if last := trivy.Args[len(trivy.Args)-1]; last != p.ImageRef { t.Errorf("image ref must remain the last argument, args=%v", trivy.Args) diff --git a/internal/config/config.go b/internal/config/config.go index d06dd8b..c6fae61 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -145,6 +145,15 @@ type RegistryConfig struct { // default (only usable on an install that deliberately opens internet // egress to the DB hosts). TrivyDBRepository string `toml:"trivy_db_repository"` + // TrivyJavaDBRepository points Trivy at an OCI repository holding the Java + // DB (--java-db-repository). Trivy fetches it lazily whenever the scanned + // image contains Java artifacts — every real modpack image does — so on an + // egress-locked box the scan fails closed without this mirror exactly like + // the vulnerability DB. The supported shape is an internal mirror: copy + // mirror.gcr.io/aquasec/trivy-java-db:1 into this cluster's registry and + // set this to registry..svc:5000/mirror/trivy-java-db:1 (recipe in + // docs/troubleshooting.md §8e). Empty keeps Trivy's own default. + TrivyJavaDBRepository string `toml:"trivy_java_db_repository"` // UserUploadsContext is the object-store base under which a user-submitted // modpack's Kaniko build context is pinned. It belongs to the §16 build // subsystem's input domain (the build-context store), introduced by the