dockerls 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dockerls/__init__.py +31 -0
- dockerls/application/__init__.py +0 -0
- dockerls/application/dto/__init__.py +3 -0
- dockerls/application/dto/analysis.py +257 -0
- dockerls/application/services/__init__.py +0 -0
- dockerls/application/services/alternatives_lookup.py +167 -0
- dockerls/application/services/composite_repository.py +106 -0
- dockerls/application/services/cross_validation.py +233 -0
- dockerls/application/services/ecosystems.py +350 -0
- dockerls/application/services/fallback_scanner.py +97 -0
- dockerls/application/services/hardening_analysis.py +174 -0
- dockerls/application/services/migration.py +297 -0
- dockerls/application/services/progress.py +56 -0
- dockerls/application/services/remediation.py +321 -0
- dockerls/application/services/scan_history_store.py +88 -0
- dockerls/application/services/scanner_factory.py +88 -0
- dockerls/application/services/source_registry.py +151 -0
- dockerls/application/services/tag_history_store.py +76 -0
- dockerls/application/services/teardown.py +50 -0
- dockerls/application/services/verdict.py +298 -0
- dockerls/application/services/version_discovery.py +108 -0
- dockerls/application/use_cases/__init__.py +0 -0
- dockerls/application/use_cases/analyze_dockerfile.py +103 -0
- dockerls/application/use_cases/analyze_image.py +173 -0
- dockerls/application/use_cases/build_image.py +1795 -0
- dockerls/application/use_cases/compare_images.py +91 -0
- dockerls/application/use_cases/fleet_scan.py +240 -0
- dockerls/application/use_cases/recommend_images.py +1078 -0
- dockerls/application/use_cases/registry_audit.py +133 -0
- dockerls/application/use_cases/search_images.py +23 -0
- dockerls/application/use_cases/upgrade_base.py +167 -0
- dockerls/cache/__init__.py +0 -0
- dockerls/cache/sqlite_cache.py +184 -0
- dockerls/cli/__init__.py +0 -0
- dockerls/cli/analysis_baseline.py +98 -0
- dockerls/cli/app.py +294 -0
- dockerls/cli/commands/__init__.py +0 -0
- dockerls/cli/commands/advisor.py +262 -0
- dockerls/cli/commands/alternatives.py +291 -0
- dockerls/cli/commands/analyze.py +429 -0
- dockerls/cli/commands/analyze_dockerfile.py +104 -0
- dockerls/cli/commands/base_cmd.py +244 -0
- dockerls/cli/commands/base_image.py +551 -0
- dockerls/cli/commands/build.py +1300 -0
- dockerls/cli/commands/cache_cmd.py +104 -0
- dockerls/cli/commands/compare.py +177 -0
- dockerls/cli/commands/controls.py +110 -0
- dockerls/cli/commands/doctor.py +566 -0
- dockerls/cli/commands/export.py +81 -0
- dockerls/cli/commands/fleet.py +159 -0
- dockerls/cli/commands/health.py +84 -0
- dockerls/cli/commands/login.py +53 -0
- dockerls/cli/commands/policy_cmd.py +111 -0
- dockerls/cli/commands/provenance_cmd.py +162 -0
- dockerls/cli/commands/recommend.py +761 -0
- dockerls/cli/commands/registry_audit_cmd.py +103 -0
- dockerls/cli/commands/sbom.py +144 -0
- dockerls/cli/commands/search.py +86 -0
- dockerls/cli/commands/verify.py +115 -0
- dockerls/cli/commands/version.py +12 -0
- dockerls/cli/commands/vex_cmd.py +117 -0
- dockerls/cli/dependencies.py +530 -0
- dockerls/cli/image_names.py +79 -0
- dockerls/cli/options.py +42 -0
- dockerls/cli/progress.py +145 -0
- dockerls/cli/publish_prompt.py +123 -0
- dockerls/cli/rendering.py +214 -0
- dockerls/cli/runtime.py +65 -0
- dockerls/cli/scan_failure.py +71 -0
- dockerls/cli/text.py +39 -0
- dockerls/cli/validators.py +35 -0
- dockerls/cli/vulnerability_view.py +154 -0
- dockerls/domain/__init__.py +0 -0
- dockerls/domain/entities/__init__.py +75 -0
- dockerls/domain/entities/declared_metadata.py +147 -0
- dockerls/domain/entities/dockerfile_analysis.py +318 -0
- dockerls/domain/entities/image.py +109 -0
- dockerls/domain/entities/image_facts.py +137 -0
- dockerls/domain/entities/recommendation.py +33 -0
- dockerls/domain/entities/scan_result.py +129 -0
- dockerls/domain/entities/vulnerability.py +232 -0
- dockerls/domain/interfaces/__init__.py +17 -0
- dockerls/domain/interfaces/cache_store.py +18 -0
- dockerls/domain/interfaces/dockerfile_validator.py +99 -0
- dockerls/domain/interfaces/eol_checker.py +11 -0
- dockerls/domain/interfaces/image_repository.py +15 -0
- dockerls/domain/interfaces/scanner.py +15 -0
- dockerls/domain/security_controls.py +362 -0
- dockerls/domain/value_objects/__init__.py +49 -0
- dockerls/domain/value_objects/attack_surface.py +198 -0
- dockerls/domain/value_objects/base_recipe.py +600 -0
- dockerls/domain/value_objects/base_upgrade.py +292 -0
- dockerls/domain/value_objects/build_labels.py +99 -0
- dockerls/domain/value_objects/build_policy.py +412 -0
- dockerls/domain/value_objects/confidence.py +156 -0
- dockerls/domain/value_objects/fleet.py +174 -0
- dockerls/domain/value_objects/gate.py +327 -0
- dockerls/domain/value_objects/hardening.py +303 -0
- dockerls/domain/value_objects/image_reference.py +90 -0
- dockerls/domain/value_objects/inheritance.py +352 -0
- dockerls/domain/value_objects/network_policy.py +280 -0
- dockerls/domain/value_objects/production_readiness.py +145 -0
- dockerls/domain/value_objects/provenance.py +211 -0
- dockerls/domain/value_objects/recipe_diff.py +188 -0
- dockerls/domain/value_objects/registry_audit.py +195 -0
- dockerls/domain/value_objects/registry_target.py +225 -0
- dockerls/domain/value_objects/remediation_score.py +62 -0
- dockerls/domain/value_objects/scan_history.py +183 -0
- dockerls/domain/value_objects/scan_plan.py +193 -0
- dockerls/domain/value_objects/scanner_db.py +143 -0
- dockerls/domain/value_objects/security_score.py +160 -0
- dockerls/domain/value_objects/security_tier.py +122 -0
- dockerls/domain/value_objects/tag_history.py +180 -0
- dockerls/domain/value_objects/tool_release.py +253 -0
- dockerls/domain/value_objects/tristate.py +47 -0
- dockerls/domain/value_objects/vex.py +249 -0
- dockerls/exit_codes.py +23 -0
- dockerls/exporters/__init__.py +0 -0
- dockerls/exporters/base.py +17 -0
- dockerls/exporters/csv_exporter.py +85 -0
- dockerls/exporters/factory.py +31 -0
- dockerls/exporters/html_exporter.py +105 -0
- dockerls/exporters/json_exporter.py +19 -0
- dockerls/exporters/markdown_exporter.py +84 -0
- dockerls/exporters/sarif_exporter.py +245 -0
- dockerls/infrastructure/__init__.py +0 -0
- dockerls/infrastructure/config/__init__.py +0 -0
- dockerls/infrastructure/config/policy_file.py +165 -0
- dockerls/infrastructure/config/settings.py +197 -0
- dockerls/infrastructure/database/__init__.py +0 -0
- dockerls/infrastructure/database/models.py +78 -0
- dockerls/infrastructure/dockerfile_validator.py +1899 -0
- dockerls/infrastructure/evidence.py +99 -0
- dockerls/infrastructure/hashing.py +165 -0
- dockerls/infrastructure/logging/__init__.py +0 -0
- dockerls/infrastructure/logging/setup.py +98 -0
- dockerls/infrastructure/network/__init__.py +0 -0
- dockerls/infrastructure/network/guarded_client.py +107 -0
- dockerls/infrastructure/network/host_guard.py +117 -0
- dockerls/infrastructure/redaction.py +135 -0
- dockerls/infrastructure/templates/hardening/alpine.dockerfile +44 -0
- dockerls/infrastructure/templates/hardening/debian.dockerfile +45 -0
- dockerls/infrastructure/templates/hardening/distroless.dockerfile +34 -0
- dockerls/infrastructure/templates/hardening/go-alpine.dockerfile +52 -0
- dockerls/infrastructure/templates/hardening/go-debian.dockerfile +54 -0
- dockerls/infrastructure/templates/hardening/go-distroless.dockerfile +43 -0
- dockerls/infrastructure/templates/hardening/go-scratch.dockerfile +48 -0
- dockerls/infrastructure/templates/hardening/go.dockerfile +50 -0
- dockerls/infrastructure/templates/hardening/gradle-alpine.dockerfile +51 -0
- dockerls/infrastructure/templates/hardening/gradle.dockerfile +52 -0
- dockerls/infrastructure/templates/hardening/java-alpine.dockerfile +50 -0
- dockerls/infrastructure/templates/hardening/java-debian.dockerfile +50 -0
- dockerls/infrastructure/templates/hardening/java-distroless.dockerfile +39 -0
- dockerls/infrastructure/templates/hardening/java-ubuntu.dockerfile +54 -0
- dockerls/infrastructure/templates/hardening/java.dockerfile +60 -0
- dockerls/infrastructure/templates/hardening/maven-alpine.dockerfile +56 -0
- dockerls/infrastructure/templates/hardening/maven.dockerfile +57 -0
- dockerls/infrastructure/templates/hardening/node-alpine.dockerfile +47 -0
- dockerls/infrastructure/templates/hardening/node-debian.dockerfile +54 -0
- dockerls/infrastructure/templates/hardening/node-distroless.dockerfile +46 -0
- dockerls/infrastructure/templates/hardening/node-ubuntu.dockerfile +63 -0
- dockerls/infrastructure/templates/hardening/node.dockerfile +61 -0
- dockerls/infrastructure/templates/hardening/php-alpine.dockerfile +45 -0
- dockerls/infrastructure/templates/hardening/php-debian.dockerfile +45 -0
- dockerls/infrastructure/templates/hardening/php-ubuntu.dockerfile +49 -0
- dockerls/infrastructure/templates/hardening/php.dockerfile +44 -0
- dockerls/infrastructure/templates/hardening/python-alpine.dockerfile +51 -0
- dockerls/infrastructure/templates/hardening/python-debian.dockerfile +54 -0
- dockerls/infrastructure/templates/hardening/python-distroless.dockerfile +51 -0
- dockerls/infrastructure/templates/hardening/python-ubuntu.dockerfile +60 -0
- dockerls/infrastructure/templates/hardening/python.dockerfile +58 -0
- dockerls/infrastructure/templates/hardening/ruby-alpine.dockerfile +48 -0
- dockerls/infrastructure/templates/hardening/ruby-debian.dockerfile +50 -0
- dockerls/infrastructure/templates/hardening/rust-alpine.dockerfile +50 -0
- dockerls/infrastructure/templates/hardening/rust-debian.dockerfile +48 -0
- dockerls/infrastructure/templates/hardening/rust-scratch.dockerfile +44 -0
- dockerls/infrastructure/templates/hardening/rust.dockerfile +54 -0
- dockerls/infrastructure/templates/hardening/ubuntu.dockerfile +49 -0
- dockerls/infrastructure/toolchain/__init__.py +0 -0
- dockerls/infrastructure/toolchain/db_metadata.py +115 -0
- dockerls/infrastructure/toolchain/installer.py +435 -0
- dockerls/integrations/__init__.py +0 -0
- dockerls/integrations/dhi/__init__.py +0 -0
- dockerls/integrations/dhi/catalog.py +457 -0
- dockerls/integrations/dhi/definition.py +151 -0
- dockerls/integrations/dhi/repository.py +238 -0
- dockerls/integrations/dockerhub/__init__.py +0 -0
- dockerls/integrations/dockerhub/client.py +318 -0
- dockerls/integrations/dockerhub/urls.py +75 -0
- dockerls/integrations/endoflife/__init__.py +0 -0
- dockerls/integrations/endoflife/checker.py +216 -0
- dockerls/integrations/engine/__init__.py +0 -0
- dockerls/integrations/engine/batch.py +197 -0
- dockerls/integrations/engine/client.py +330 -0
- dockerls/integrations/engine/locator.py +96 -0
- dockerls/integrations/exploitdb/__init__.py +0 -0
- dockerls/integrations/exploitdb/client.py +271 -0
- dockerls/integrations/grype/__init__.py +0 -0
- dockerls/integrations/grype/scanner.py +336 -0
- dockerls/integrations/registry/__init__.py +0 -0
- dockerls/integrations/registry/hardened.py +284 -0
- dockerls/integrations/registry/inspector.py +420 -0
- dockerls/integrations/registry/oci.py +259 -0
- dockerls/integrations/registry/private.py +79 -0
- dockerls/integrations/registry/urls.py +36 -0
- dockerls/integrations/scan_errors.py +67 -0
- dockerls/integrations/scan_target.py +57 -0
- dockerls/integrations/signing/__init__.py +0 -0
- dockerls/integrations/signing/cosign.py +484 -0
- dockerls/integrations/threat_intel/__init__.py +0 -0
- dockerls/integrations/threat_intel/client.py +290 -0
- dockerls/integrations/trivy/__init__.py +0 -0
- dockerls/integrations/trivy/cache_pool.py +176 -0
- dockerls/integrations/trivy/scanner.py +447 -0
- dockerls/utils/__init__.py +0 -0
- dockerls/utils/auth.py +108 -0
- dockerls/utils/executables.py +39 -0
- dockerls/utils/ignore_file.py +130 -0
- dockerls/utils/rate_limit.py +130 -0
- dockerls/utils/resources.py +183 -0
- dockerls/utils/retry.py +31 -0
- dockerls/utils/safe_yaml.py +166 -0
- dockerls/utils/subprocess_runner.py +216 -0
- dockerls/utils/validation.py +72 -0
- dockerls-1.0.0.dist-info/METADATA +563 -0
- dockerls-1.0.0.dist-info/RECORD +230 -0
- dockerls-1.0.0.dist-info/WHEEL +5 -0
- dockerls-1.0.0.dist-info/entry_points.txt +2 -0
- dockerls-1.0.0.dist-info/licenses/LICENSE +21 -0
- dockerls-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
r"""One redactor, used everywhere text leaves this process.
|
|
2
|
+
|
|
3
|
+
Credentials reach this tool through several doors: a registry token in a
|
|
4
|
+
`WWW-Authenticate` exchange, a Docker Hub PAT in an exception message, a
|
|
5
|
+
`user:password` embedded in a URL someone put in a config file, an
|
|
6
|
+
`Authorization` header echoed back by a scanner that failed. They then leave
|
|
7
|
+
through several more: the log file, the raw scan artifacts kept as evidence,
|
|
8
|
+
and anything a user attaches to a ticket.
|
|
9
|
+
|
|
10
|
+
Having the masking live inside the logging sink covered exactly one of those
|
|
11
|
+
doors. This module is the shared implementation; the log filter and the
|
|
12
|
+
evidence store both call it, so a pattern added here protects every path at
|
|
13
|
+
once.
|
|
14
|
+
|
|
15
|
+
Three rules shape the patterns:
|
|
16
|
+
|
|
17
|
+
* **Only the key and its separator survive.** `token=` stays so the line
|
|
18
|
+
remains readable; no part of the value does.
|
|
19
|
+
* **Over-masking is the acceptable failure.** A redacted benign string costs
|
|
20
|
+
a moment of confusion. A leaked token costs a credential rotation, and
|
|
21
|
+
the leak is discovered later, by someone else.
|
|
22
|
+
* **The keyword comes first.** Every key pattern begins with the literal
|
|
23
|
+
alternation rather than with `[\w.-]*`, so the engine can scan for a
|
|
24
|
+
literal instead of trying every split of an unbounded character class at
|
|
25
|
+
every position. This is not a micro-optimisation: with the leading star,
|
|
26
|
+
redacting one 2 MB scan artifact took **19 seconds** of pure CPU --
|
|
27
|
+
catastrophic backtracking over the long runs of word characters in a
|
|
28
|
+
vulnerability description. Once per scanned image, that is the whole
|
|
29
|
+
run's budget spent on masking. Keyword-first is 176x faster and masks
|
|
30
|
+
exactly the same strings; the text before the keyword is simply left
|
|
31
|
+
outside the match instead of being consumed and re-emitted.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
from __future__ import annotations
|
|
35
|
+
|
|
36
|
+
import re
|
|
37
|
+
|
|
38
|
+
#: What replaces a redacted value. Recognisable on sight, and distinct from
|
|
39
|
+
#: anything a real credential looks like. The exact string is part of the
|
|
40
|
+
#: observable contract -- it appears in every log file this tool has ever
|
|
41
|
+
#: written -- so it is carried over verbatim rather than modernised.
|
|
42
|
+
MASK = "***MASKED***"
|
|
43
|
+
|
|
44
|
+
# The credential-introducing keywords, as a bare literal alternation. It is
|
|
45
|
+
# deliberately *not* wrapped in `[\w.-]*` on both sides: see the module
|
|
46
|
+
# docstring for what that cost. Surrounding word characters are matched
|
|
47
|
+
# after the keyword, where they are bounded by the word itself.
|
|
48
|
+
_SENSITIVE_KEYWORD = r"(?:token|password|passwd|senha|secret|api[-_]?key|credential|auth)"
|
|
49
|
+
#: A key, from its keyword to the end of the identifier: `token`,
|
|
50
|
+
#: `api_key`, and the tail of `x_api_key` (the `x_` is left outside the
|
|
51
|
+
#: match and survives untouched, which renders identically).
|
|
52
|
+
_SENSITIVE_KEY_PATTERN = _SENSITIVE_KEYWORD + r"[\w.-]*"
|
|
53
|
+
|
|
54
|
+
# A quoted key/value pair, as it appears in JSON or a dict repr:
|
|
55
|
+
# "token": "value" 'apiKey' : 'value' "auth": {"token": "value"}
|
|
56
|
+
# The quote between the key and the separator is exactly what the previous
|
|
57
|
+
# pattern could not cross, which left every JSON-shaped log line in clear.
|
|
58
|
+
# The value is matched possessively: when no closing quote follows, the
|
|
59
|
+
# match fails at once instead of backtracking through every prefix of a
|
|
60
|
+
# multi-kilobyte string.
|
|
61
|
+
_QUOTED_KV = re.compile(
|
|
62
|
+
rf"""(?P<prefix>{_SENSITIVE_KEY_PATTERN}["']?\s*[:=]\s*)"""
|
|
63
|
+
rf"""(?P<quote>["'])(?P<value>(?:[^"'\\]|\\.)*+)(?P=quote)""",
|
|
64
|
+
re.IGNORECASE,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# An unquoted key/value pair: token=abc, senha: abc, x-api-key: abc.
|
|
68
|
+
_BARE_KV = re.compile(
|
|
69
|
+
rf"(?P<prefix>{_SENSITIVE_KEY_PATTERN}\s*[=:]\s*)(?P<value>[^\s,;&\"'}}\]]+)", re.IGNORECASE
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
# Authorization schemes.
|
|
73
|
+
_SCHEME = re.compile(
|
|
74
|
+
r"\b(?P<scheme>Bearer|Basic|Token|Digest)\s+(?P<value>[^\s,;\"'=:][^\s,;\"']*)",
|
|
75
|
+
re.IGNORECASE,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
# Credentials embedded in a URL: https://user:secret@host
|
|
79
|
+
_URL_USERINFO = re.compile(r"(?P<prefix>://[^/\s:@]+:)(?P<value>[^@/\s]+)(?P<at>@)")
|
|
80
|
+
|
|
81
|
+
# curl-style inline credentials: -u user:secret, --user user:secret
|
|
82
|
+
_CURL_USER = re.compile(r"(?P<prefix>(?:-u|--user)\s+[^\s:]+:)(?P<value>\S+)")
|
|
83
|
+
|
|
84
|
+
# Credential formats that are self-identifying, so they are redacted even
|
|
85
|
+
# when they appear with no key to introduce them -- a bare token inside a
|
|
86
|
+
# list or an exception message has no "token=" in front of it.
|
|
87
|
+
_KNOWN_SECRET_VALUE = re.compile(
|
|
88
|
+
r"""
|
|
89
|
+
\bdckr_pat_[A-Za-z0-9_-]{8,} # Docker Hub PAT
|
|
90
|
+
| \bgh[pousr]_[A-Za-z0-9]{20,} # GitHub classic token
|
|
91
|
+
| \bgithub_pat_[A-Za-z0-9_]{20,} # GitHub fine-grained PAT
|
|
92
|
+
| \beyJ[A-Za-z0-9_-]{6,}\.[A-Za-z0-9_-]{6,}\.[A-Za-z0-9_-]+ # JWT
|
|
93
|
+
| \bAKIA[0-9A-Z]{16}\b # AWS access key id
|
|
94
|
+
| \bxox[baprs]-[A-Za-z0-9-]{10,} # Slack token
|
|
95
|
+
""",
|
|
96
|
+
re.VERBOSE,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# multipart/form-data, where the value sits on its own line after a blank
|
|
100
|
+
# line rather than next to the key.
|
|
101
|
+
_MULTIPART = re.compile(
|
|
102
|
+
rf"""(?P<prefix>name=["'][\w.-]{{0,32}}{_SENSITIVE_KEY_PATTERN}["'][^\n]*\r?\n\r?\n)(?P<value>[^\r\n]+)""",
|
|
103
|
+
re.IGNORECASE,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
_FILE_FORMAT = "{time:YYYY-MM-DD HH:mm:ss.SSS} | {level: <8} | {name}:{function}:{line} - {message}"
|
|
107
|
+
_CONSOLE_FORMAT = (
|
|
108
|
+
"<level>{level: <8}</level> | <cyan>{name}</cyan>:<cyan>{function}</cyan> - {message}"
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def redact(message: str) -> str:
|
|
113
|
+
"""Redact credentials from a log line in every shape they arrive in.
|
|
114
|
+
|
|
115
|
+
Only the key name and separator (e.g. `token=`) are kept, never any part
|
|
116
|
+
of the value. Over-masking a benign line is an acceptable cost; leaking
|
|
117
|
+
a token into a log file is not, so the key patterns are deliberately
|
|
118
|
+
broad.
|
|
119
|
+
|
|
120
|
+
Order matters: scheme patterns run before the key/value ones, because
|
|
121
|
+
in `auth: Bearer <token>` a key/value match would consume only the word
|
|
122
|
+
`Bearer` and stop, leaving the credential in the clear.
|
|
123
|
+
"""
|
|
124
|
+
result = _SCHEME.sub(lambda m: f"{m.group('scheme')} {MASK}", message)
|
|
125
|
+
result = _URL_USERINFO.sub(lambda m: f"{m.group('prefix')}{MASK}{m.group('at')}", result)
|
|
126
|
+
result = _CURL_USER.sub(lambda m: f"{m.group('prefix')}{MASK}", result)
|
|
127
|
+
result = _MULTIPART.sub(lambda m: f"{m.group('prefix')}{MASK}", result)
|
|
128
|
+
result = _QUOTED_KV.sub(
|
|
129
|
+
lambda m: f"{m.group('prefix')}{m.group('quote')}{MASK}{m.group('quote')}", result
|
|
130
|
+
)
|
|
131
|
+
result = _BARE_KV.sub(lambda m: f"{m.group('prefix')}{MASK}", result)
|
|
132
|
+
# Last: catch self-identifying credential formats that appeared with no
|
|
133
|
+
# key in front of them for any of the patterns above to anchor on.
|
|
134
|
+
result = _KNOWN_SECRET_VALUE.sub(MASK, result)
|
|
135
|
+
return result
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Dockerfile.hardened.alpine
|
|
2
|
+
# Hardened template for Alpine Linux - Minimal Base Container
|
|
3
|
+
|
|
4
|
+
ARG BASE_VERSION=3.20
|
|
5
|
+
|
|
6
|
+
# Stage 1: Build / Setup
|
|
7
|
+
FROM alpine:${BASE_VERSION} AS builder
|
|
8
|
+
|
|
9
|
+
RUN apk update && apk upgrade --no-cache \
|
|
10
|
+
&& apk add --no-cache ca-certificates tzdata \
|
|
11
|
+
&& rm -rf /var/cache/apk/*
|
|
12
|
+
|
|
13
|
+
# Stage 2: Runtime
|
|
14
|
+
FROM alpine:${BASE_VERSION}
|
|
15
|
+
|
|
16
|
+
LABEL security.scanner="dockerls"
|
|
17
|
+
LABEL security.hardened="true"
|
|
18
|
+
LABEL maintainer="security@company.com"
|
|
19
|
+
LABEL security.cve-contact="security@company.com"
|
|
20
|
+
|
|
21
|
+
# Update security packages and create a dedicated non-root user (UID 10001)
|
|
22
|
+
RUN apk update && apk upgrade --no-cache \
|
|
23
|
+
&& apk add --no-cache ca-certificates tzdata \
|
|
24
|
+
&& addgroup -g 10001 appgroup \
|
|
25
|
+
&& adduser -u 10001 -G appgroup -s /sbin/nologin -D appuser \
|
|
26
|
+
&& rm -rf /var/cache/apk/*
|
|
27
|
+
|
|
28
|
+
WORKDIR /app
|
|
29
|
+
|
|
30
|
+
# Copy application files with proper ownership
|
|
31
|
+
COPY --chown=appuser:appgroup . .
|
|
32
|
+
|
|
33
|
+
ARG GIT_SHA=unknown
|
|
34
|
+
ARG BUILD_TIME=unknown
|
|
35
|
+
LABEL org.opencontainers.image.revision="${GIT_SHA}"
|
|
36
|
+
LABEL org.opencontainers.image.created="${BUILD_TIME}"
|
|
37
|
+
|
|
38
|
+
USER appuser
|
|
39
|
+
|
|
40
|
+
HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \
|
|
41
|
+
CMD [ -f /app/health.ok ] || exit 0
|
|
42
|
+
|
|
43
|
+
ENTRYPOINT ["sh", "-c"]
|
|
44
|
+
CMD ["echo 'DockerLs Alpine container ready'"]
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Dockerfile.hardened.debian
|
|
2
|
+
# Hardened template for Debian GNU/Linux (Bookworm Slim)
|
|
3
|
+
|
|
4
|
+
ARG DEBIAN_VERSION=bookworm-slim
|
|
5
|
+
|
|
6
|
+
# Stage 1: Build / Setup
|
|
7
|
+
FROM debian:${DEBIAN_VERSION} AS builder
|
|
8
|
+
|
|
9
|
+
RUN apt-get update \
|
|
10
|
+
&& apt-get upgrade -y \
|
|
11
|
+
&& apt-get install -y --no-install-recommends ca-certificates tzdata \
|
|
12
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
13
|
+
|
|
14
|
+
# Stage 2: Runtime
|
|
15
|
+
FROM debian:${DEBIAN_VERSION}
|
|
16
|
+
|
|
17
|
+
LABEL security.scanner="dockerls"
|
|
18
|
+
LABEL security.hardened="true"
|
|
19
|
+
LABEL maintainer="security@company.com"
|
|
20
|
+
LABEL security.cve-contact="security@company.com"
|
|
21
|
+
|
|
22
|
+
# Update OS packages and create a non-root user (UID 10001)
|
|
23
|
+
RUN apt-get update \
|
|
24
|
+
&& apt-get upgrade -y \
|
|
25
|
+
&& apt-get install -y --no-install-recommends ca-certificates tzdata \
|
|
26
|
+
&& groupadd -g 10001 appgroup \
|
|
27
|
+
&& useradd -u 10001 -g appgroup -s /usr/sbin/nologin -m appuser \
|
|
28
|
+
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
|
29
|
+
|
|
30
|
+
WORKDIR /app
|
|
31
|
+
|
|
32
|
+
COPY --chown=appuser:appgroup . .
|
|
33
|
+
|
|
34
|
+
ARG GIT_SHA=unknown
|
|
35
|
+
ARG BUILD_TIME=unknown
|
|
36
|
+
LABEL org.opencontainers.image.revision="${GIT_SHA}"
|
|
37
|
+
LABEL org.opencontainers.image.created="${BUILD_TIME}"
|
|
38
|
+
|
|
39
|
+
USER appuser
|
|
40
|
+
|
|
41
|
+
HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \
|
|
42
|
+
CMD [ -f /app/health.ok ] || exit 0
|
|
43
|
+
|
|
44
|
+
ENTRYPOINT ["sh", "-c"]
|
|
45
|
+
CMD ["echo 'DockerLs Debian container ready'"]
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Dockerfile.hardened.distroless
|
|
2
|
+
# Hardened template for Google Distroless (Debian 12 base)
|
|
3
|
+
|
|
4
|
+
ARG BUILDER_VERSION=3.20
|
|
5
|
+
|
|
6
|
+
# Stage 1: Builder
|
|
7
|
+
FROM alpine:${BUILDER_VERSION} AS builder
|
|
8
|
+
|
|
9
|
+
RUN apk update && apk add --no-cache ca-certificates tzdata
|
|
10
|
+
|
|
11
|
+
WORKDIR /app
|
|
12
|
+
COPY . .
|
|
13
|
+
|
|
14
|
+
# Stage 2: Distroless Runtime
|
|
15
|
+
FROM gcr.io/distroless/base-debian12:nonroot
|
|
16
|
+
|
|
17
|
+
LABEL security.scanner="dockerls"
|
|
18
|
+
LABEL security.hardened="true"
|
|
19
|
+
LABEL maintainer="security@company.com"
|
|
20
|
+
LABEL security.cve-contact="security@company.com"
|
|
21
|
+
|
|
22
|
+
WORKDIR /app
|
|
23
|
+
|
|
24
|
+
COPY --from=builder /app /app
|
|
25
|
+
|
|
26
|
+
ARG GIT_SHA=unknown
|
|
27
|
+
ARG BUILD_TIME=unknown
|
|
28
|
+
LABEL org.opencontainers.image.revision="${GIT_SHA}"
|
|
29
|
+
LABEL org.opencontainers.image.created="${BUILD_TIME}"
|
|
30
|
+
|
|
31
|
+
# nonroot user in Distroless is 65532:65532
|
|
32
|
+
USER 65532:65532
|
|
33
|
+
|
|
34
|
+
ENTRYPOINT ["/app/binary"]
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Dockerfile.hardened.go-alpine
|
|
2
|
+
# Hardened template for Go (Alpine Linux)
|
|
3
|
+
|
|
4
|
+
ARG GO_VERSION=1.23-alpine
|
|
5
|
+
ARG ALPINE_VERSION=3.20
|
|
6
|
+
|
|
7
|
+
# Stage 1: Builder
|
|
8
|
+
FROM golang:${GO_VERSION} AS builder
|
|
9
|
+
|
|
10
|
+
RUN apk update && apk add --no-cache git ca-certificates tzdata
|
|
11
|
+
|
|
12
|
+
WORKDIR /app
|
|
13
|
+
|
|
14
|
+
COPY go.mod go.sum ./
|
|
15
|
+
RUN go mod download
|
|
16
|
+
|
|
17
|
+
COPY . .
|
|
18
|
+
|
|
19
|
+
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
|
|
20
|
+
go build -trimpath -ldflags="-s -w" -o /app/binary .
|
|
21
|
+
|
|
22
|
+
# Stage 2: Alpine Runtime
|
|
23
|
+
FROM alpine:${ALPINE_VERSION}
|
|
24
|
+
|
|
25
|
+
LABEL security.scanner="dockerls"
|
|
26
|
+
LABEL security.hardened="true"
|
|
27
|
+
LABEL maintainer="security@company.com"
|
|
28
|
+
LABEL security.cve-contact="security@company.com"
|
|
29
|
+
|
|
30
|
+
RUN apk update && apk upgrade --no-cache \
|
|
31
|
+
&& apk add --no-cache ca-certificates tzdata \
|
|
32
|
+
&& addgroup -g 10001 appgroup \
|
|
33
|
+
&& adduser -u 10001 -G appgroup -s /sbin/nologin -D appuser \
|
|
34
|
+
&& rm -rf /var/cache/apk/*
|
|
35
|
+
|
|
36
|
+
WORKDIR /app
|
|
37
|
+
|
|
38
|
+
COPY --from=builder /app/binary /app/binary
|
|
39
|
+
|
|
40
|
+
ARG GIT_SHA=unknown
|
|
41
|
+
ARG BUILD_TIME=unknown
|
|
42
|
+
LABEL org.opencontainers.image.revision="${GIT_SHA}"
|
|
43
|
+
LABEL org.opencontainers.image.created="${BUILD_TIME}"
|
|
44
|
+
|
|
45
|
+
USER appuser
|
|
46
|
+
|
|
47
|
+
HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \
|
|
48
|
+
CMD [ -f /app/binary ] || exit 1
|
|
49
|
+
|
|
50
|
+
EXPOSE 8080
|
|
51
|
+
|
|
52
|
+
ENTRYPOINT ["/app/binary"]
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Dockerfile.hardened.go-debian
|
|
2
|
+
# Hardened template for Go (Debian Bookworm Slim)
|
|
3
|
+
|
|
4
|
+
ARG GO_VERSION=1.23-bookworm
|
|
5
|
+
ARG DEBIAN_VERSION=bookworm-slim
|
|
6
|
+
|
|
7
|
+
# Stage 1: Builder
|
|
8
|
+
FROM golang:${GO_VERSION} AS builder
|
|
9
|
+
|
|
10
|
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
11
|
+
git ca-certificates tzdata \
|
|
12
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
13
|
+
|
|
14
|
+
WORKDIR /app
|
|
15
|
+
|
|
16
|
+
COPY go.mod go.sum ./
|
|
17
|
+
RUN go mod download
|
|
18
|
+
|
|
19
|
+
COPY . .
|
|
20
|
+
|
|
21
|
+
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
|
|
22
|
+
go build -trimpath -ldflags="-s -w" -o /app/binary .
|
|
23
|
+
|
|
24
|
+
# Stage 2: Debian Runtime
|
|
25
|
+
FROM debian:${DEBIAN_VERSION}
|
|
26
|
+
|
|
27
|
+
LABEL security.scanner="dockerls"
|
|
28
|
+
LABEL security.hardened="true"
|
|
29
|
+
LABEL maintainer="security@company.com"
|
|
30
|
+
LABEL security.cve-contact="security@company.com"
|
|
31
|
+
|
|
32
|
+
RUN apt-get update && apt-get upgrade -y \
|
|
33
|
+
&& apt-get install -y --no-install-recommends ca-certificates tzdata \
|
|
34
|
+
&& groupadd -g 10001 appgroup \
|
|
35
|
+
&& useradd -u 10001 -g appgroup -s /usr/sbin/nologin -m appuser \
|
|
36
|
+
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
|
37
|
+
|
|
38
|
+
WORKDIR /app
|
|
39
|
+
|
|
40
|
+
COPY --from=builder /app/binary /app/binary
|
|
41
|
+
|
|
42
|
+
ARG GIT_SHA=unknown
|
|
43
|
+
ARG BUILD_TIME=unknown
|
|
44
|
+
LABEL org.opencontainers.image.revision="${GIT_SHA}"
|
|
45
|
+
LABEL org.opencontainers.image.created="${BUILD_TIME}"
|
|
46
|
+
|
|
47
|
+
USER appuser
|
|
48
|
+
|
|
49
|
+
HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \
|
|
50
|
+
CMD [ -f /app/binary ] || exit 1
|
|
51
|
+
|
|
52
|
+
EXPOSE 8080
|
|
53
|
+
|
|
54
|
+
ENTRYPOINT ["/app/binary"]
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Dockerfile.hardened.go-distroless
|
|
2
|
+
# Hardened template for Go (Google Distroless Static)
|
|
3
|
+
|
|
4
|
+
ARG GO_VERSION=1.23-alpine
|
|
5
|
+
|
|
6
|
+
# Stage 1: Builder
|
|
7
|
+
FROM golang:${GO_VERSION} AS builder
|
|
8
|
+
|
|
9
|
+
RUN apk update && apk add --no-cache git ca-certificates tzdata
|
|
10
|
+
|
|
11
|
+
WORKDIR /app
|
|
12
|
+
|
|
13
|
+
COPY go.mod go.sum ./
|
|
14
|
+
RUN go mod download
|
|
15
|
+
|
|
16
|
+
COPY . .
|
|
17
|
+
|
|
18
|
+
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
|
|
19
|
+
go build -trimpath -ldflags="-s -w" -o /app/binary .
|
|
20
|
+
|
|
21
|
+
# Stage 2: Distroless Static Runtime
|
|
22
|
+
FROM gcr.io/distroless/static-debian12:nonroot
|
|
23
|
+
|
|
24
|
+
LABEL security.scanner="dockerls"
|
|
25
|
+
LABEL security.hardened="true"
|
|
26
|
+
LABEL maintainer="security@company.com"
|
|
27
|
+
LABEL security.cve-contact="security@company.com"
|
|
28
|
+
|
|
29
|
+
WORKDIR /app
|
|
30
|
+
|
|
31
|
+
COPY --from=builder /app/binary /app/binary
|
|
32
|
+
|
|
33
|
+
ARG GIT_SHA=unknown
|
|
34
|
+
ARG BUILD_TIME=unknown
|
|
35
|
+
LABEL org.opencontainers.image.revision="${GIT_SHA}"
|
|
36
|
+
LABEL org.opencontainers.image.created="${BUILD_TIME}"
|
|
37
|
+
|
|
38
|
+
# nonroot user in Distroless
|
|
39
|
+
USER 65532:65532
|
|
40
|
+
|
|
41
|
+
EXPOSE 8080
|
|
42
|
+
|
|
43
|
+
ENTRYPOINT ["/app/binary"]
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Dockerfile.hardened.go-scratch
|
|
2
|
+
# Hardened template for Go (Scratch - Zero OS CVEs)
|
|
3
|
+
|
|
4
|
+
ARG GO_VERSION=1.23-alpine
|
|
5
|
+
|
|
6
|
+
# Stage 1: Builder
|
|
7
|
+
FROM golang:${GO_VERSION} AS builder
|
|
8
|
+
|
|
9
|
+
RUN apk update && apk add --no-cache git ca-certificates tzdata \
|
|
10
|
+
&& update-ca-certificates
|
|
11
|
+
|
|
12
|
+
WORKDIR /app
|
|
13
|
+
|
|
14
|
+
COPY go.mod go.sum ./
|
|
15
|
+
RUN go mod download
|
|
16
|
+
|
|
17
|
+
COPY . .
|
|
18
|
+
|
|
19
|
+
# Secure static compilation (no CGO)
|
|
20
|
+
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
|
|
21
|
+
go build -trimpath -ldflags="-s -w" -o /app/binary .
|
|
22
|
+
|
|
23
|
+
# Stage 2: Scratch Runtime
|
|
24
|
+
FROM scratch
|
|
25
|
+
|
|
26
|
+
LABEL security.scanner="dockerls"
|
|
27
|
+
LABEL security.hardened="true"
|
|
28
|
+
LABEL maintainer="security@company.com"
|
|
29
|
+
LABEL security.cve-contact="security@company.com"
|
|
30
|
+
|
|
31
|
+
# Import TLS certificates and timezone data from the builder
|
|
32
|
+
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
|
33
|
+
COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
|
|
34
|
+
|
|
35
|
+
# Copy only the static binary
|
|
36
|
+
COPY --from=builder /app/binary /app/binary
|
|
37
|
+
|
|
38
|
+
ARG GIT_SHA=unknown
|
|
39
|
+
ARG BUILD_TIME=unknown
|
|
40
|
+
LABEL org.opencontainers.image.revision="${GIT_SHA}"
|
|
41
|
+
LABEL org.opencontainers.image.created="${BUILD_TIME}"
|
|
42
|
+
|
|
43
|
+
# nobody user (UID 65534:65534)
|
|
44
|
+
USER 65534:65534
|
|
45
|
+
|
|
46
|
+
EXPOSE 8080
|
|
47
|
+
|
|
48
|
+
ENTRYPOINT ["/app/binary"]
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Dockerfile.hardened.go
|
|
2
|
+
# Hardened template for Go - Minimal Production Ready
|
|
3
|
+
|
|
4
|
+
FROM golang:1.23-alpine AS builder
|
|
5
|
+
|
|
6
|
+
WORKDIR /app
|
|
7
|
+
|
|
8
|
+
# Install CA certificates and git
|
|
9
|
+
RUN apk add --no-cache ca-certificates git && update-ca-certificates && rm -rf /var/cache/apk/*
|
|
10
|
+
|
|
11
|
+
COPY go.mod go.sum* ./
|
|
12
|
+
RUN go mod download || true
|
|
13
|
+
|
|
14
|
+
COPY . .
|
|
15
|
+
|
|
16
|
+
# Static build with CGO disabled and stripping flags
|
|
17
|
+
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o app .
|
|
18
|
+
|
|
19
|
+
# Stage 2: Runtime minimal (scratch)
|
|
20
|
+
FROM scratch
|
|
21
|
+
|
|
22
|
+
LABEL security.scanner="dockerls"
|
|
23
|
+
LABEL security.hardened="true"
|
|
24
|
+
LABEL maintainer="your-team@company.com"
|
|
25
|
+
LABEL security.cve-contact="security@company.com"
|
|
26
|
+
|
|
27
|
+
# Build metadata
|
|
28
|
+
ARG GIT_SHA=unknown
|
|
29
|
+
ARG BUILD_TIME=unknown
|
|
30
|
+
LABEL org.opencontainers.image.revision="${GIT_SHA}"
|
|
31
|
+
LABEL org.opencontainers.image.created="${BUILD_TIME}"
|
|
32
|
+
|
|
33
|
+
# Copy SSL root certificates for HTTPS calls
|
|
34
|
+
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
|
35
|
+
|
|
36
|
+
# Copy only the static binary
|
|
37
|
+
COPY --from=builder /app/app /app
|
|
38
|
+
|
|
39
|
+
# Expose default port
|
|
40
|
+
EXPOSE 8080
|
|
41
|
+
|
|
42
|
+
# Numeric non-root user (nobody) for scratch
|
|
43
|
+
USER 65534:65534
|
|
44
|
+
|
|
45
|
+
# Secure health check in exec form
|
|
46
|
+
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
|
47
|
+
CMD ["/app", "-health"]
|
|
48
|
+
|
|
49
|
+
# No shell - exec form only
|
|
50
|
+
ENTRYPOINT ["/app"]
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Dockerfile.hardened.gradle-alpine
|
|
2
|
+
# Hardened template for a Java application built with Gradle (Alpine).
|
|
3
|
+
#
|
|
4
|
+
# Same separation as the Maven template: the official Gradle image builds,
|
|
5
|
+
# and the runtime carries only the JRE. The Gradle daemon is disabled during
|
|
6
|
+
# the build -- inside a container it is never reused across runs, so keeping
|
|
7
|
+
# it running only wastes memory.
|
|
8
|
+
|
|
9
|
+
ARG GRADLE_VERSION=8-jdk21-alpine
|
|
10
|
+
ARG JRE_VERSION=21-jre-alpine
|
|
11
|
+
|
|
12
|
+
# Stage 1: Build
|
|
13
|
+
FROM gradle:${GRADLE_VERSION} AS builder
|
|
14
|
+
|
|
15
|
+
WORKDIR /app
|
|
16
|
+
|
|
17
|
+
COPY --chown=gradle:gradle build.gradle* settings.gradle* ./
|
|
18
|
+
COPY --chown=gradle:gradle src ./src
|
|
19
|
+
RUN gradle --no-daemon clean build -x test
|
|
20
|
+
|
|
21
|
+
# Stage 2: Runtime
|
|
22
|
+
FROM eclipse-temurin:${JRE_VERSION}
|
|
23
|
+
|
|
24
|
+
LABEL security.scanner="dockerls"
|
|
25
|
+
LABEL security.hardened="true"
|
|
26
|
+
LABEL maintainer="security@company.com"
|
|
27
|
+
LABEL security.cve-contact="security@company.com"
|
|
28
|
+
|
|
29
|
+
ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -Djava.security.egd=file:/dev/./urandom"
|
|
30
|
+
|
|
31
|
+
RUN apk upgrade --no-cache \
|
|
32
|
+
&& addgroup -g 10001 appgroup \
|
|
33
|
+
&& adduser -u 10001 -G appgroup -s /sbin/nologin -D appuser
|
|
34
|
+
|
|
35
|
+
WORKDIR /app
|
|
36
|
+
|
|
37
|
+
COPY --from=builder --chown=appuser:appgroup /app/build/libs/*.jar /app/app.jar
|
|
38
|
+
|
|
39
|
+
ARG GIT_SHA=unknown
|
|
40
|
+
ARG BUILD_TIME=unknown
|
|
41
|
+
LABEL org.opencontainers.image.revision="${GIT_SHA}"
|
|
42
|
+
LABEL org.opencontainers.image.created="${BUILD_TIME}"
|
|
43
|
+
|
|
44
|
+
USER appuser
|
|
45
|
+
|
|
46
|
+
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \
|
|
47
|
+
CMD [ -f /app/app.jar ] || exit 1
|
|
48
|
+
|
|
49
|
+
EXPOSE 8080
|
|
50
|
+
|
|
51
|
+
ENTRYPOINT ["sh", "-c", "exec java $JAVA_OPTS -jar /app/app.jar"]
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Dockerfile.hardened.gradle
|
|
2
|
+
# Hardened template for a Java application built with Gradle (Debian slim).
|
|
3
|
+
#
|
|
4
|
+
# Same separation as the Maven template: the official Gradle image builds,
|
|
5
|
+
# and the runtime carries only the JRE. The Gradle daemon is disabled during
|
|
6
|
+
# the build -- inside a container it is never reused across runs, so keeping
|
|
7
|
+
# it running only wastes memory.
|
|
8
|
+
|
|
9
|
+
ARG GRADLE_VERSION=8-jdk21
|
|
10
|
+
ARG JRE_VERSION=21-jre
|
|
11
|
+
|
|
12
|
+
# Stage 1: Build
|
|
13
|
+
FROM gradle:${GRADLE_VERSION} AS builder
|
|
14
|
+
|
|
15
|
+
WORKDIR /app
|
|
16
|
+
|
|
17
|
+
COPY --chown=gradle:gradle build.gradle* settings.gradle* ./
|
|
18
|
+
COPY --chown=gradle:gradle src ./src
|
|
19
|
+
RUN gradle --no-daemon clean build -x test
|
|
20
|
+
|
|
21
|
+
# Stage 2: Runtime
|
|
22
|
+
FROM eclipse-temurin:${JRE_VERSION}
|
|
23
|
+
|
|
24
|
+
LABEL security.scanner="dockerls"
|
|
25
|
+
LABEL security.hardened="true"
|
|
26
|
+
LABEL maintainer="security@company.com"
|
|
27
|
+
LABEL security.cve-contact="security@company.com"
|
|
28
|
+
|
|
29
|
+
ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -Djava.security.egd=file:/dev/./urandom"
|
|
30
|
+
|
|
31
|
+
RUN apt-get update && apt-get upgrade -y --no-install-recommends \
|
|
32
|
+
&& rm -rf /var/lib/apt/lists/* \
|
|
33
|
+
&& groupadd -g 10001 appgroup \
|
|
34
|
+
&& useradd -u 10001 -g appgroup -s /usr/sbin/nologin -m appuser
|
|
35
|
+
|
|
36
|
+
WORKDIR /app
|
|
37
|
+
|
|
38
|
+
COPY --from=builder --chown=appuser:appgroup /app/build/libs/*.jar /app/app.jar
|
|
39
|
+
|
|
40
|
+
ARG GIT_SHA=unknown
|
|
41
|
+
ARG BUILD_TIME=unknown
|
|
42
|
+
LABEL org.opencontainers.image.revision="${GIT_SHA}"
|
|
43
|
+
LABEL org.opencontainers.image.created="${BUILD_TIME}"
|
|
44
|
+
|
|
45
|
+
USER appuser
|
|
46
|
+
|
|
47
|
+
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \
|
|
48
|
+
CMD [ -f /app/app.jar ] || exit 1
|
|
49
|
+
|
|
50
|
+
EXPOSE 8080
|
|
51
|
+
|
|
52
|
+
ENTRYPOINT ["sh", "-c", "exec java $JAVA_OPTS -jar /app/app.jar"]
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Dockerfile.hardened.java-alpine
|
|
2
|
+
# Hardened template for Java 21 (Eclipse Temurin JRE Alpine)
|
|
3
|
+
|
|
4
|
+
ARG JDK_VERSION=21-jdk-alpine
|
|
5
|
+
ARG JRE_VERSION=21-jre-alpine
|
|
6
|
+
|
|
7
|
+
# Stage 1: Build (Maven / Gradle)
|
|
8
|
+
FROM eclipse-temurin:${JDK_VERSION} AS builder
|
|
9
|
+
|
|
10
|
+
WORKDIR /app
|
|
11
|
+
|
|
12
|
+
RUN apk add --no-cache maven gradle \
|
|
13
|
+
&& rm -rf /var/cache/apk/*
|
|
14
|
+
|
|
15
|
+
COPY pom.xml ./
|
|
16
|
+
COPY src ./src
|
|
17
|
+
RUN mvn clean package -DskipTests || true
|
|
18
|
+
|
|
19
|
+
# Stage 2: Runtime
|
|
20
|
+
FROM eclipse-temurin:${JRE_VERSION}
|
|
21
|
+
|
|
22
|
+
LABEL security.scanner="dockerls"
|
|
23
|
+
LABEL security.hardened="true"
|
|
24
|
+
LABEL maintainer="security@company.com"
|
|
25
|
+
LABEL security.cve-contact="security@company.com"
|
|
26
|
+
|
|
27
|
+
ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -Djava.security.egd=file:/dev/./urandom"
|
|
28
|
+
|
|
29
|
+
RUN apk update && apk upgrade --no-cache \
|
|
30
|
+
&& addgroup -g 10001 appgroup \
|
|
31
|
+
&& adduser -u 10001 -G appgroup -s /sbin/nologin -D appuser \
|
|
32
|
+
&& rm -rf /var/cache/apk/*
|
|
33
|
+
|
|
34
|
+
WORKDIR /app
|
|
35
|
+
|
|
36
|
+
COPY --from=builder --chown=appuser:appgroup /app/target/*.jar /app/app.jar
|
|
37
|
+
|
|
38
|
+
ARG GIT_SHA=unknown
|
|
39
|
+
ARG BUILD_TIME=unknown
|
|
40
|
+
LABEL org.opencontainers.image.revision="${GIT_SHA}"
|
|
41
|
+
LABEL org.opencontainers.image.created="${BUILD_TIME}"
|
|
42
|
+
|
|
43
|
+
USER appuser
|
|
44
|
+
|
|
45
|
+
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \
|
|
46
|
+
CMD [ -f /app/app.jar ] || exit 1
|
|
47
|
+
|
|
48
|
+
EXPOSE 8080
|
|
49
|
+
|
|
50
|
+
ENTRYPOINT ["sh", "-c", "exec java $JAVA_OPTS -jar /app/app.jar"]
|