Introduction to Golden Image Enforcement
Golden Image Enforcement ensures that deployed container images comply with organizational security standards, vulnerability policies, and configuration baselines. This implementation provides comprehensive image validation, policy enforcement, and compliance reporting.
Maven Dependencies
<properties>
<jib.version>0.23.0</jib.version>
<docker-java.version>3.3.0</docker-java.version>
<kubernetes-client.version>18.0.0</kubernetes-client.version>
<trivy-client.version>1.0.0</trivy-client.version>
<opentelemetry.version>1.28.0</opentelemetry.version>
</properties>
<dependencies>
<!-- Container Image Building -->
<dependency>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-core</artifactId>
<version>${jib.version}</version>
</dependency>
<!-- Docker Client -->
<dependency>
<groupId>com.github.docker-java</groupId>
<artifactId>docker-java</artifactId>
<version>${docker-java.version}</version>
</dependency>
<!-- Kubernetes Client -->
<dependency>
<groupId>io.kubernetes</groupId>
<artifactId>client-java</artifactId>
<version>${kubernetes-client.version}</version>
</dependency>
<!-- Vulnerability Scanning -->
<dependency>
<groupId>com.aquasecurity</groupId>
<artifactId>trivy-client</artifactId>
<version>${trivy-client.version}</version>
</dependency>
<!-- OpenTelemetry for Observability -->
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-api</artifactId>
<version>${opentelemetry.version}</version>
</dependency>
<!-- JSON Processing -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>
<!-- YAML Processing -->
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>2.0</version>
</dependency>
</dependencies>
Core Models and Policies
Golden Image Policy Definition
public class GoldenImagePolicy {
private String policyId;
private String name;
private String description;
private PolicySeverity severity;
private List<ImageRule> rules;
private List<String> enforcedNamespaces;
private List<String> exemptedImages;
private PolicyEnforcement enforcement;
private Instant createdAt;
private Instant updatedAt;
public GoldenImagePolicy() {
this.rules = new ArrayList<>();
this.enforcedNamespaces = new ArrayList<>();
this.exemptedImages = new ArrayList<>();
this.enforcement = PolicyEnforcement.ENFORCE;
this.createdAt = Instant.now();
}
public enum PolicySeverity {
CRITICAL,
HIGH,
MEDIUM,
LOW
}
public enum PolicyEnforcement {
ENFORCE, // Block non-compliant images
WARN, // Allow but generate warnings
AUDIT, // Only audit and report
DISABLED // Policy is disabled
}
// Getters and setters
public String getPolicyId() { return policyId; }
public void setPolicyId(String policyId) { this.policyId = policyId; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public PolicySeverity getSeverity() { return severity; }
public void setSeverity(PolicySeverity severity) { this.severity = severity; }
public List<ImageRule> getRules() { return rules; }
public void setRules(List<ImageRule> rules) { this.rules = rules; }
public List<String> getEnforcedNamespaces() { return enforcedNamespaces; }
public void setEnforcedNamespaces(List<String> enforcedNamespaces) { this.enforcedNamespaces = enforcedNamespaces; }
public List<String> getExemptedImages() { return exemptedImages; }
public void setExemptedImages(List<String> exemptedImages) { this.exemptedImages = exemptedImages; }
public PolicyEnforcement getEnforcement() { return enforcement; }
public void setEnforcement(PolicyEnforcement enforcement) { this.enforcement = enforcement; }
public Instant getCreatedAt() { return createdAt; }
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
public Instant getUpdatedAt() { return updatedAt; }
public void setUpdatedAt(Instant updatedAt) { this.updatedAt = updatedAt; }
public void addRule(ImageRule rule) {
this.rules.add(rule);
}
public void addEnforcedNamespace(String namespace) {
this.enforcedNamespaces.add(namespace);
}
public void addExemptedImage(String image) {
this.exemptedImages.add(image);
}
}
public class ImageRule {
private String ruleId;
private RuleType type;
private String description;
private Map<String, Object> parameters;
private RuleCondition condition;
public enum RuleType {
VULNERABILITY_SCAN,
BASE_IMAGE,
PACKAGE_MANAGER,
FILE_SYSTEM,
CONFIGURATION,
SECURITY_CONTEXT,
RESOURCE_LIMITS,
SIGNATURE_VERIFICATION
}
public enum RuleCondition {
MUST_EXIST,
MUST_NOT_EXIST,
MUST_EQUAL,
MUST_NOT_EQUAL,
MUST_CONTAIN,
MUST_NOT_CONTAIN,
MUST_BE_LESS_THAN,
MUST_BE_GREATER_THAN
}
// Getters and setters
public String getRuleId() { return ruleId; }
public void setRuleId(String ruleId) { this.ruleId = ruleId; }
public RuleType getType() { return type; }
public void setType(RuleType type) { this.type = type; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public Map<String, Object> getParameters() { return parameters; }
public void setParameters(Map<String, Object> parameters) { this.parameters = parameters; }
public RuleCondition getCondition() { return condition; }
public void setCondition(RuleCondition condition) { this.condition = condition; }
public void addParameter(String key, Object value) {
if (this.parameters == null) {
this.parameters = new HashMap<>();
}
this.parameters.put(key, value);
}
}
public class ImageValidationResult {
private String imageReference;
private boolean compliant;
private List<PolicyViolation> violations;
private List<PolicyCompliance> compliances;
private ImageMetadata metadata;
private VulnerabilityReport vulnerabilityReport;
private Instant validatedAt;
private String validatorVersion;
public ImageValidationResult(String imageReference) {
this.imageReference = imageReference;
this.violations = new ArrayList<>();
this.compliances = new ArrayList<>();
this.validatedAt = Instant.now();
}
// Getters and setters
public String getImageReference() { return imageReference; }
public void setImageReference(String imageReference) { this.imageReference = imageReference; }
public boolean isCompliant() { return compliant; }
public void setCompliant(boolean compliant) { this.compliant = compliant; }
public List<PolicyViolation> getViolations() { return violations; }
public void setViolations(List<PolicyViolation> violations) { this.violations = violations; }
public List<PolicyCompliance> getCompliances() { return compliances; }
public void setCompliances(List<PolicyCompliance> compliances) { this.compliances = compliances; }
public ImageMetadata getMetadata() { return metadata; }
public void setMetadata(ImageMetadata metadata) { this.metadata = metadata; }
public VulnerabilityReport getVulnerabilityReport() { return vulnerabilityReport; }
public void setVulnerabilityReport(VulnerabilityReport vulnerabilityReport) {
this.vulnerabilityReport = vulnerabilityReport;
}
public Instant getValidatedAt() { return validatedAt; }
public void setValidatedAt(Instant validatedAt) { this.validatedAt = validatedAt; }
public String getValidatorVersion() { return validatorVersion; }
public void setValidatorVersion(String validatorVersion) { this.validatorVersion = validatorVersion; }
public void addViolation(PolicyViolation violation) {
this.violations.add(violation);
this.compliant = false;
}
public void addCompliance(PolicyCompliance compliance) {
this.compliances.add(compliance);
}
public boolean hasCriticalViolations() {
return violations.stream()
.anyMatch(v -> v.getSeverity() == GoldenImagePolicy.PolicySeverity.CRITICAL);
}
}
public class PolicyViolation {
private String policyId;
private String ruleId;
private GoldenImagePolicy.PolicySeverity severity;
private String message;
private String details;
private String remediation;
private Map<String, Object> context;
// Getters and setters
public String getPolicyId() { return policyId; }
public void setPolicyId(String policyId) { this.policyId = policyId; }
public String getRuleId() { return ruleId; }
public void setRuleId(String ruleId) { this.ruleId = ruleId; }
public GoldenImagePolicy.PolicySeverity getSeverity() { return severity; }
public void setSeverity(GoldenImagePolicy.PolicySeverity severity) { this.severity = severity; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public String getDetails() { return details; }
public void setDetails(String details) { this.details = details; }
public String getRemediation() { return remediation; }
public void setRemediation(String remediation) { this.remediation = remediation; }
public Map<String, Object> getContext() { return context; }
public void setContext(Map<String, Object> context) { this.context = context; }
}
Image Validation Engine
Core Validation Service
@Component
public class GoldenImageValidator {
private static final Logger logger = LoggerFactory.getLogger(GoldenImageValidator.class);
private final PolicyRepository policyRepository;
private final VulnerabilityScanner vulnerabilityScanner;
private final ImageMetadataExtractor metadataExtractor;
private final SignatureVerifier signatureVerifier;
private final ComplianceReporter complianceReporter;
public GoldenImageValidator(PolicyRepository policyRepository,
VulnerabilityScanner vulnerabilityScanner,
ImageMetadataExtractor metadataExtractor,
SignatureVerifier signatureVerifier,
ComplianceReporter complianceReporter) {
this.policyRepository = policyRepository;
this.vulnerabilityScanner = vulnerabilityScanner;
this.metadataExtractor = metadataExtractor;
this.signatureVerifier = signatureVerifier;
this.complianceReporter = complianceReporter;
}
public ImageValidationResult validateImage(String imageReference, String namespace) {
Span span = tracer.spanBuilder("validateImage")
.setAttribute("image.reference", imageReference)
.setAttribute("namespace", namespace)
.startSpan();
try (Scope scope = span.makeCurrent()) {
ImageValidationResult result = new ImageValidationResult(imageReference);
// Check if image is exempted
if (isImageExempted(imageReference, namespace)) {
result.setCompliant(true);
result.addCompliance(createExemptionCompliance(imageReference));
return result;
}
// Extract image metadata
ImageMetadata metadata = metadataExtractor.extractMetadata(imageReference);
result.setMetadata(metadata);
// Get applicable policies
List<GoldenImagePolicy> policies = policyRepository.findApplicablePolicies(namespace);
// Validate against each policy
for (GoldenImagePolicy policy : policies) {
validateAgainstPolicy(result, policy, metadata);
}
// Set overall compliance
result.setCompliant(result.getViolations().isEmpty());
// Generate compliance report
complianceReporter.generateReport(result);
logger.info("Image validation completed for {}: compliant={}",
imageReference, result.isCompliant());
return result;
} catch (Exception e) {
span.recordException(e);
logger.error("Image validation failed for: {}", imageReference, e);
throw new ImageValidationException("Validation failed for image: " + imageReference, e);
} finally {
span.end();
}
}
public BatchValidationResult validateImages(List<String> imageReferences, String namespace) {
BatchValidationResult batchResult = new BatchValidationResult();
imageReferences.parallelStream().forEach(imageRef -> {
try {
ImageValidationResult result = validateImage(imageRef, namespace);
batchResult.addResult(imageRef, result);
} catch (Exception e) {
batchResult.addError(imageRef, e.getMessage());
}
});
return batchResult;
}
private void validateAgainstPolicy(ImageValidationResult result,
GoldenImagePolicy policy,
ImageMetadata metadata) {
for (ImageRule rule : policy.getRules()) {
try {
boolean ruleCompliant = validateRule(rule, metadata, result);
if (ruleCompliant) {
result.addCompliance(createRuleCompliance(policy, rule));
} else {
PolicyViolation violation = createPolicyViolation(policy, rule, metadata);
result.addViolation(violation);
// If enforcement is strict and violation is critical, short-circuit
if (policy.getEnforcement() == GoldenImagePolicy.PolicyEnforcement.ENFORCE &&
violation.getSeverity() == GoldenImagePolicy.PolicySeverity.CRITICAL) {
break;
}
}
} catch (Exception e) {
logger.warn("Rule validation failed for rule {}: {}", rule.getRuleId(), e.getMessage());
PolicyViolation violation = createRuleErrorViolation(policy, rule, e);
result.addViolation(violation);
}
}
}
private boolean validateRule(ImageRule rule, ImageMetadata metadata, ImageValidationResult result) {
switch (rule.getType()) {
case VULNERABILITY_SCAN:
return validateVulnerabilityRule(rule, metadata, result);
case BASE_IMAGE:
return validateBaseImageRule(rule, metadata);
case PACKAGE_MANAGER:
return validatePackageManagerRule(rule, metadata);
case FILE_SYSTEM:
return validateFileSystemRule(rule, metadata);
case CONFIGURATION:
return validateConfigurationRule(rule, metadata);
case SECURITY_CONTEXT:
return validateSecurityContextRule(rule, metadata);
case RESOURCE_LIMITS:
return validateResourceLimitsRule(rule, metadata);
case SIGNATURE_VERIFICATION:
return validateSignatureRule(rule, metadata);
default:
throw new IllegalArgumentException("Unknown rule type: " + rule.getType());
}
}
private boolean validateVulnerabilityRule(ImageRule rule, ImageMetadata metadata,
ImageValidationResult result) {
// Get or perform vulnerability scan
VulnerabilityReport report = result.getVulnerabilityReport();
if (report == null) {
report = vulnerabilityScanner.scan(metadata.getImageReference());
result.setVulnerabilityReport(report);
}
String maxSeverity = (String) rule.getParameters().get("maxSeverity");
Integer maxCritical = (Integer) rule.getParameters().get("maxCriticalVulnerabilities");
Integer maxHigh = (Integer) rule.getParameters().get("maxHighVulnerabilities");
return vulnerabilityScanner.evaluateAgainstPolicy(report, maxSeverity, maxCritical, maxHigh);
}
private boolean validateBaseImageRule(ImageRule rule, ImageMetadata metadata) {
List<String> allowedBaseImages = (List<String>) rule.getParameters().get("allowedBaseImages");
String baseImage = metadata.getBaseImage();
if (rule.getCondition() == RuleCondition.MUST_EXIST) {
return baseImage != null && allowedBaseImages.contains(baseImage);
} else if (rule.getCondition() == RuleCondition.MUST_NOT_EXIST) {
return baseImage == null || !allowedBaseImages.contains(baseImage);
}
return false;
}
private boolean validatePackageManagerRule(ImageRule rule, ImageMetadata metadata) {
List<String> bannedPackages = (List<String>) rule.getParameters().get("bannedPackages");
List<PackageInfo> installedPackages = metadata.getInstalledPackages();
if (rule.getCondition() == RuleCondition.MUST_NOT_EXIST) {
return installedPackages.stream()
.noneMatch(pkg -> bannedPackages.contains(pkg.getName()));
}
return true;
}
private boolean validateFileSystemRule(ImageRule rule, ImageMetadata metadata) {
String filePath = (String) rule.getParameters().get("filePath");
String expectedHash = (String) rule.getParameters().get("expectedHash");
FileInfo fileInfo = metadata.getFileInfo(filePath);
if (fileInfo == null) {
return rule.getCondition() == RuleCondition.MUST_NOT_EXIST;
}
if (rule.getCondition() == RuleCondition.MUST_EQUAL) {
return expectedHash.equals(fileInfo.getSha256Hash());
}
return true;
}
private boolean validateConfigurationRule(ImageRule rule, ImageMetadata metadata) {
String configPath = (String) rule.getParameters().get("configPath");
String expectedValue = (String) rule.getParameters().get("expectedValue");
String actualValue = metadata.getConfigurationValue(configPath);
switch (rule.getCondition()) {
case MUST_EXIST:
return actualValue != null;
case MUST_EQUAL:
return expectedValue.equals(actualValue);
case MUST_NOT_EXIST:
return actualValue == null;
default:
return true;
}
}
private boolean validateSecurityContextRule(ImageRule rule, ImageMetadata metadata) {
Boolean runAsNonRoot = (Boolean) rule.getParameters().get("runAsNonRoot");
Boolean readOnlyRootFilesystem = (Boolean) rule.getParameters().get("readOnlyRootFilesystem");
SecurityContext securityContext = metadata.getSecurityContext();
if (runAsNonRoot != null) {
if (rule.getCondition() == RuleCondition.MUST_EQUAL) {
return runAsNonRoot.equals(securityContext.isRunAsNonRoot());
}
}
if (readOnlyRootFilesystem != null) {
if (rule.getCondition() == RuleCondition.MUST_EQUAL) {
return readOnlyRootFilesystem.equals(securityContext.isReadOnlyRootFilesystem());
}
}
return true;
}
private boolean validateResourceLimitsRule(ImageRule rule, ImageMetadata metadata) {
Map<String, String> resourceLimits = metadata.getResourceLimits();
String resourceType = (String) rule.getParameters().get("resourceType");
String maxValue = (String) rule.getParameters().get("maxValue");
String actualValue = resourceLimits.get(resourceType);
if (actualValue == null) {
return rule.getCondition() == RuleCondition.MUST_NOT_EXIST;
}
if (rule.getCondition() == RuleCondition.MUST_BE_LESS_THAN) {
return compareResourceValues(actualValue, maxValue) < 0;
}
return true;
}
private boolean validateSignatureRule(ImageRule rule, ImageMetadata metadata) {
String publicKey = (String) rule.getParameters().get("publicKey");
return signatureVerifier.verify(metadata.getImageReference(), publicKey);
}
private boolean isImageExempted(String imageReference, String namespace) {
List<GoldenImagePolicy> policies = policyRepository.findApplicablePolicies(namespace);
return policies.stream()
.flatMap(policy -> policy.getExemptedImages().stream())
.anyMatch(exempted -> matchesImagePattern(imageReference, exempted));
}
private boolean matchesImagePattern(String imageReference, String pattern) {
if (pattern.contains("*")) {
String regex = pattern.replace(".", "\\.").replace("*", ".*");
return imageReference.matches(regex);
}
return imageReference.equals(pattern);
}
private int compareResourceValues(String value1, String value2) {
// Simple comparison for resource values (e.g., "100m", "1Gi")
// In production, use proper Kubernetes resource quantity parsing
return value1.compareTo(value2);
}
private PolicyCompliance createExemptionCompliance(String imageReference) {
PolicyCompliance compliance = new PolicyCompliance();
compliance.setPolicyId("EXEMPTION");
compliance.setRuleId("IMAGE_EXEMPTION");
compliance.setMessage("Image is exempted from policy enforcement");
compliance.setDetails("Image " + imageReference + " is in exempted list");
return compliance;
}
private PolicyCompliance createRuleCompliance(GoldenImagePolicy policy, ImageRule rule) {
PolicyCompliance compliance = new PolicyCompliance();
compliance.setPolicyId(policy.getPolicyId());
compliance.setRuleId(rule.getRuleId());
compliance.setMessage("Rule compliance satisfied");
compliance.setDetails(rule.getDescription());
return compliance;
}
private PolicyViolation createPolicyViolation(GoldenImagePolicy policy, ImageRule rule,
ImageMetadata metadata) {
PolicyViolation violation = new PolicyViolation();
violation.setPolicyId(policy.getPolicyId());
violation.setRuleId(rule.getRuleId());
violation.setSeverity(policy.getSeverity());
violation.setMessage("Policy violation: " + rule.getDescription());
violation.setDetails(createViolationDetails(rule, metadata));
violation.setRemediation(createRemediation(rule));
return violation;
}
private PolicyViolation createRuleErrorViolation(GoldenImagePolicy policy, ImageRule rule,
Exception error) {
PolicyViolation violation = new PolicyViolation();
violation.setPolicyId(policy.getPolicyId());
violation.setRuleId(rule.getRuleId());
violation.setSeverity(GoldenImagePolicy.PolicySeverity.HIGH);
violation.setMessage("Rule validation error: " + error.getMessage());
violation.setDetails("Rule type: " + rule.getType());
violation.setRemediation("Check rule configuration and validator logs");
return violation;
}
private String createViolationDetails(ImageRule rule, ImageMetadata metadata) {
// Create detailed violation message based on rule type and metadata
return String.format("Rule %s violation for image %s",
rule.getRuleId(), metadata.getImageReference());
}
private String createRemediation(ImageRule rule) {
// Provide remediation steps based on rule type
switch (rule.getType()) {
case VULNERABILITY_SCAN:
return "Update base image or patch vulnerable packages";
case BASE_IMAGE:
return "Use an approved base image from the allowed list";
case PACKAGE_MANAGER:
return "Remove banned packages from the image";
case FILE_SYSTEM:
return "Fix file permissions or remove sensitive files";
default:
return "Review and fix the configuration issue";
}
}
}
Kubernetes Admission Controller
Webhook Server for Image Validation
@Component
public class ImageAdmissionWebhook {
private static final Logger logger = LoggerFactory.getLogger(ImageAdmissionWebhook.class);
private final GoldenImageValidator validator;
private final PolicyRepository policyRepository;
private final ObjectMapper objectMapper;
public ImageAdmissionWebhook(GoldenImageValidator validator,
PolicyRepository policyRepository) {
this.validator = validator;
this.policyRepository = policyRepository;
this.objectMapper = new ObjectMapper();
}
@PostMapping("/validate")
public AdmissionReview validateImage(@RequestBody AdmissionReview review) {
Span span = tracer.spanBuilder("admission.validate")
.setAttribute("k8s.operation", review.getRequest().getOperation())
.startSpan();
try (Scope scope = span.makeCurrent()) {
AdmissionResponse response = new AdmissionResponse();
response.setUid(review.getRequest().getUid());
response.setAllowed(true);
// Extract images from the request
List<String> images = extractImagesFromRequest(review.getRequest());
String namespace = review.getRequest().getNamespace();
// Validate each image
List<ImageValidationResult> validationResults = new ArrayList<>();
List<String> warnings = new ArrayList<>();
for (String image : images) {
try {
ImageValidationResult result = validator.validateImage(image, namespace);
validationResults.add(result);
if (!result.isCompliant()) {
if (shouldBlockDeployment(result, namespace)) {
response.setAllowed(false);
response.setStatus(createErrorStatus(result));
break;
} else {
warnings.add(createWarningMessage(result));
}
}
} catch (Exception e) {
logger.warn("Image validation failed for {}: {}", image, e.getMessage());
warnings.add("Validation failed for image: " + image);
}
}
if (!warnings.isEmpty()) {
response.setWarnings(warnings);
}
// Audit the validation results
auditValidationResults(validationResults, review.getRequest(), response.isAllowed());
AdmissionReview responseReview = new AdmissionReview();
responseReview.setResponse(response);
responseReview.setApiVersion(review.getApiVersion());
responseReview.setKind(review.getKind());
return responseReview;
} catch (Exception e) {
span.recordException(e);
logger.error("Admission webhook processing failed", e);
return createErrorResponse(review.getRequest().getUid(), e.getMessage());
} finally {
span.end();
}
}
private List<String> extractImagesFromRequest(AdmissionRequest request) {
List<String> images = new ArrayList<>();
try {
JsonNode object = objectMapper.readTree(request.getObject().toString());
// Extract from Pod spec
if (object.has("spec")) {
JsonNode spec = object.get("spec");
// Extract container images
if (spec.has("containers")) {
extractContainerImages(spec.get("containers"), images);
}
// Extract init container images
if (spec.has("initContainers")) {
extractContainerImages(spec.get("initContainers"), images);
}
}
// Extract from Deployment, StatefulSet, etc.
if (object.has("spec") && object.get("spec").has("template")) {
JsonNode template = object.get("spec").get("template");
if (template.has("spec")) {
JsonNode podSpec = template.get("spec");
if (podSpec.has("containers")) {
extractContainerImages(podSpec.get("containers"), images);
}
if (podSpec.has("initContainers")) {
extractContainerImages(podSpec.get("initContainers"), images);
}
}
}
} catch (Exception e) {
logger.error("Failed to extract images from admission request", e);
}
return images.stream().distinct().collect(Collectors.toList());
}
private void extractContainerImages(JsonNode containers, List<String> images) {
for (JsonNode container : containers) {
if (container.has("image")) {
String image = container.get("image").asText();
if (image != null && !image.isEmpty()) {
images.add(image);
}
}
}
}
private boolean shouldBlockDeployment(ImageValidationResult result, String namespace) {
// Check policies for this namespace to determine enforcement level
List<GoldenImagePolicy> policies = policyRepository.findApplicablePolicies(namespace);
boolean hasCriticalViolation = result.hasCriticalViolations();
boolean hasEnforcingPolicy = policies.stream()
.anyMatch(p -> p.getEnforcement() == GoldenImagePolicy.PolicyEnforcement.ENFORCE);
return hasCriticalViolation && hasEnforcingPolicy;
}
private AdmissionResponse.Status createErrorStatus(ImageValidationResult result) {
AdmissionResponse.Status status = new AdmissionResponse.Status();
status.setCode(403);
status.setMessage("Image validation failed");
// Include violation details in status
String details = result.getViolations().stream()
.map(v -> String.format("[%s] %s", v.getSeverity(), v.getMessage()))
.collect(Collectors.joining("; "));
status.setDetails(new AdmissionResponse.StatusDetails(details));
return status;
}
private String createWarningMessage(ImageValidationResult result) {
return String.format("Image %s has policy violations: %s",
result.getImageReference(),
result.getViolations().stream()
.map(PolicyViolation::getMessage)
.collect(Collectors.joining(", ")));
}
private void auditValidationResults(List<ImageValidationResult> results,
AdmissionRequest request,
boolean allowed) {
// Log validation results for auditing
for (ImageValidationResult result : results) {
AuditEvent event = new AuditEvent();
event.setTimestamp(Instant.now());
event.setOperation(request.getOperation());
event.setNamespace(request.getNamespace());
event.setUserInfo(request.getUserInfo());
event.setImageReference(result.getImageReference());
event.setCompliant(result.isCompliant());
event.setViolations(result.getViolations());
event.setAllowed(allowed);
// Send to audit log
auditLogger.log(event);
}
}
private AdmissionReview createErrorResponse(String uid, String message) {
AdmissionResponse response = new AdmissionResponse();
response.setUid(uid);
response.setAllowed(false);
response.setStatus(new AdmissionResponse.Status(500, message));
AdmissionReview review = new AdmissionReview();
review.setResponse(response);
return review;
}
}
// Kubernetes Admission Review Models
public class AdmissionReview {
private String apiVersion;
private String kind;
private AdmissionRequest request;
private AdmissionResponse response;
// Getters and setters
public String getApiVersion() { return apiVersion; }
public void setApiVersion(String apiVersion) { this.apiVersion = apiVersion; }
public String getKind() { return kind; }
public void setKind(String kind) { this.kind = kind; }
public AdmissionRequest getRequest() { return request; }
public void setRequest(AdmissionRequest request) { this.request = request; }
public AdmissionResponse getResponse() { return response; }
public void setResponse(AdmissionResponse response) { this.response = response; }
}
public class AdmissionRequest {
private String uid;
private String operation;
private Object object;
private String namespace;
private Object userInfo;
// Getters and setters
public String getUid() { return uid; }
public void setUid(String uid) { this.uid = uid; }
public String getOperation() { return operation; }
public void setOperation(String operation) { this.operation = operation; }
public Object getObject() { return object; }
public void setObject(Object object) { this.object = object; }
public String getNamespace() { return namespace; }
public void setNamespace(String namespace) { this.namespace = namespace; }
public Object getUserInfo() { return userInfo; }
public void setUserInfo(Object userInfo) { this.userInfo = userInfo; }
}
public class AdmissionResponse {
private String uid;
private boolean allowed;
private Status status;
private List<String> warnings;
public static class Status {
private int code;
private String message;
private StatusDetails details;
public Status() {}
public Status(int code, String message) {
this.code = code;
this.message = message;
}
// Getters and setters
public int getCode() { return code; }
public void setCode(int code) { this.code = code; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public StatusDetails getDetails() { return details; }
public void setDetails(StatusDetails details) { this.details = details; }
}
public static class StatusDetails {
private String message;
public StatusDetails() {}
public StatusDetails(String message) {
this.message = message;
}
// Getters and setters
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
}
// Getters and setters
public String getUid() { return uid; }
public void setUid(String uid) { this.uid = uid; }
public boolean isAllowed() { return allowed; }
public void setAllowed(boolean allowed) { this.allowed = allowed; }
public Status getStatus() { return status; }
public void setStatus(Status status) { this.status = status; }
public List<String> getWarnings() { return warnings; }
public void setWarnings(List<String> warnings) { this.warnings = warnings; }
}
Vulnerability Scanning Integration
@Component
public class TrivyVulnerabilityScanner implements VulnerabilityScanner {
private static final Logger logger = LoggerFactory.getLogger(TrivyVulnerabilityScanner.class);
private final TrivyClient trivyClient;
private final ObjectMapper objectMapper;
public TrivyVulnerabilityScanner(TrivyClient trivyClient) {
this.trivyClient = trivyClient;
this.objectMapper = new ObjectMapper();
}
@Override
public VulnerabilityReport scan(String imageReference) {
Span span = tracer.spanBuilder("vulnerability.scan")
.setAttribute("image.reference", imageReference)
.startSpan();
try (Scope scope = span.makeCurrent()) {
logger.info("Starting vulnerability scan for: {}", imageReference);
// Execute trivy scan
ProcessBuilder processBuilder = new ProcessBuilder(
"trivy", "image", "--format", "json", imageReference
);
Process process = processBuilder.start();
String output = new String(process.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
int exitCode = process.waitFor();
if (exitCode != 0) {
String error = new String(process.getErrorStream().readAllBytes(), StandardCharsets.UTF_8);
throw new VulnerabilityScanException("Trivy scan failed: " + error);
}
// Parse trivy output
VulnerabilityReport report = parseTrivyOutput(output, imageReference);
logger.info("Vulnerability scan completed for {}: {} vulnerabilities found",
imageReference, report.getVulnerabilities().size());
return report;
} catch (IOException | InterruptedException e) {
span.recordException(e);
logger.error("Vulnerability scan failed for: {}", imageReference, e);
throw new VulnerabilityScanException("Scan failed for image: " + imageReference, e);
} finally {
span.end();
}
}
@Override
public boolean evaluateAgainstPolicy(VulnerabilityReport report, String maxSeverity,
Integer maxCritical, Integer maxHigh) {
if (maxSeverity != null) {
String highestSeverity = report.getHighestSeverity();
if (compareSeverity(highestSeverity, maxSeverity) > 0) {
return false;
}
}
if (maxCritical != null) {
long criticalCount = report.getVulnerabilities().stream()
.filter(v -> "CRITICAL".equals(v.getSeverity()))
.count();
if (criticalCount > maxCritical) {
return false;
}
}
if (maxHigh != null) {
long highCount = report.getVulnerabilities().stream()
.filter(v -> "HIGH".equals(v.getSeverity()))
.count();
if (highCount > maxHigh) {
return false;
}
}
return true;
}
private VulnerabilityReport parseTrivyOutput(String jsonOutput, String imageReference) throws IOException {
JsonNode root = objectMapper.readTree(jsonOutput);
VulnerabilityReport report = new VulnerabilityReport();
report.setImageReference(imageReference);
report.setScanTimestamp(Instant.now());
report.setScannerVersion("trivy");
if (root.isArray() && root.size() > 0) {
JsonNode results = root.get(0);
if (results.has("Vulnerabilities")) {
JsonNode vulnerabilities = results.get("Vulnerabilities");
for (JsonNode vulnNode : vulnerabilities) {
Vulnerability vuln = new Vulnerability();
vuln.setVulnerabilityId(vulnNode.get("VulnerabilityID").asText());
vuln.setPackageName(vulnNode.get("PkgName").asText());
vuln.setInstalledVersion(vulnNode.get("InstalledVersion").asText());
vuln.setFixedVersion(vulnNode.has("FixedVersion") ?
vulnNode.get("FixedVersion").asText() : null);
vuln.setSeverity(vulnNode.get("Severity").asText());
vuln.setTitle(vulnNode.has("Title") ? vulnNode.get("Title").asText() : null);
vuln.setDescription(vulnNode.has("Description") ?
vulnNode.get("Description").asText() : null);
report.addVulnerability(vuln);
}
}
}
return report;
}
private int compareSeverity(String severity1, String severity2) {
Map<String, Integer> severityOrder = Map.of(
"UNKNOWN", 0,
"LOW", 1,
"MEDIUM", 2,
"HIGH", 3,
"CRITICAL", 4
);
return Integer.compare(
severityOrder.getOrDefault(severity1.toUpperCase(), 0),
severityOrder.getOrDefault(severity2.toUpperCase(), 0)
);
}
}
public class VulnerabilityReport {
private String imageReference;
private Instant scanTimestamp;
private String scannerVersion;
private List<Vulnerability> vulnerabilities;
public VulnerabilityReport() {
this.vulnerabilities = new ArrayList<>();
}
// Getters and setters
public String getImageReference() { return imageReference; }
public void setImageReference(String imageReference) { this.imageReference = imageReference; }
public Instant getScanTimestamp() { return scanTimestamp; }
public void setScanTimestamp(Instant scanTimestamp) { this.scanTimestamp = scanTimestamp; }
public String getScannerVersion() { return scannerVersion; }
public void setScannerVersion(String scannerVersion) { this.scannerVersion = scannerVersion; }
public List<Vulnerability> getVulnerabilities() { return vulnerabilities; }
public void setVulnerabilities(List<Vulnerability> vulnerabilities) {
this.vulnerabilities = vulnerabilities;
}
public void addVulnerability(Vulnerability vulnerability) {
this.vulnerabilities.add(vulnerability);
}
public String getHighestSeverity() {
return vulnerabilities.stream()
.map(Vulnerability::getSeverity)
.max(this::compareSeverity)
.orElse("UNKNOWN");
}
public long getVulnerabilityCountBySeverity(String severity) {
return vulnerabilities.stream()
.filter(v -> severity.equals(v.getSeverity()))
.count();
}
private int compareSeverity(String s1, String s2) {
Map<String, Integer> order = Map.of("LOW", 1, "MEDIUM", 2, "HIGH", 3, "CRITICAL", 4);
return Integer.compare(order.getOrDefault(s1, 0), order.getOrDefault(s2, 0));
}
}
Policy Management and Reporting
@Component
public class PolicyManager {
private final PolicyRepository policyRepository;
private final ComplianceReporter complianceReporter;
private final KubernetesClient k8sClient;
public PolicyManager(PolicyRepository policyRepository,
ComplianceReporter complianceReporter,
KubernetesClient k8sClient) {
this.policyRepository = policyRepository;
this.complianceReporter = complianceReporter;
this.k8sClient = k8sClient;
}
public GoldenImagePolicy createPolicy(GoldenImagePolicy policy) {
validatePolicy(policy);
policy.setPolicyId(generatePolicyId());
return policyRepository.save(policy);
}
public GoldenImagePolicy updatePolicy(String policyId, GoldenImagePolicy policy) {
if (!policyRepository.existsById(policyId)) {
throw new PolicyNotFoundException("Policy not found: " + policyId);
}
validatePolicy(policy);
policy.setPolicyId(policyId);
policy.setUpdatedAt(Instant.now());
return policyRepository.save(policy);
}
public void deletePolicy(String policyId) {
if (!policyRepository.existsById(policyId)) {
throw new PolicyNotFoundException("Policy not found: " + policyId);
}
policyRepository.deleteById(policyId);
}
public ComplianceReport generateComplianceReport(String namespace) {
List<GoldenImagePolicy> policies = policyRepository.findApplicablePolicies(namespace);
List<Pod> pods = k8sClient.pods().inNamespace(namespace).list().getItems();
ComplianceReport report = new ComplianceReport();
report.setNamespace(namespace);
report.setGeneratedAt(Instant.now());
report.setPoliciesEvaluated(policies.size());
// Collect all images from pods
Set<String> images = pods.stream()
.flatMap(pod -> pod.getSpec().getContainers().stream())
.map(container -> container.getImage())
.collect(Collectors.toSet());
pods.stream()
.flatMap(pod -> pod.getSpec().getInitContainers().stream())
.map(container -> container.getImage())
.forEach(images::add);
// Validate each image
List<ImageCompliance> imageCompliances = new ArrayList<>();
int compliantImages = 0;
int nonCompliantImages = 0;
for (String image : images) {
ImageCompliance compliance = new ImageCompliance();
compliance.setImageReference(image);
compliance.setPods(findPodsUsingImage(pods, image));
try {
ImageValidationResult result = validator.validateImage(image, namespace);
compliance.setCompliant(result.isCompliant());
compliance.setViolations(result.getViolations());
if (result.isCompliant()) {
compliantImages++;
} else {
nonCompliantImages++;
}
} catch (Exception e) {
compliance.setCompliant(false);
compliance.setValidationError(e.getMessage());
nonCompliantImages++;
}
imageCompliances.add(compliance);
}
report.setImageCompliances(imageCompliances);
report.setCompliantImages(compliantImages);
report.setNonCompliantImages(nonCompliantImages);
report.setComplianceRate(images.isEmpty() ? 100.0 : (double) compliantImages / images.size() * 100);
return report;
}
public void enforceNamespacePolicies(String namespace) {
List<GoldenImagePolicy> policies = policyRepository.findApplicablePolicies(namespace);
List<Pod> pods = k8sClient.pods().inNamespace(namespace).list().getItems();
for (Pod pod : pods) {
for (Container container : pod.getSpec().getContainers()) {
String image = container.getImage();
try {
ImageValidationResult result = validator.validateImage(image, namespace);
if (!result.isCompliant() && shouldEnforceRemediation(result, policies)) {
remediateNonCompliantPod(pod, container, result);
}
} catch (Exception e) {
logger.warn("Failed to validate image {} in pod {}: {}",
image, pod.getMetadata().getName(), e.getMessage());
}
}
}
}
private void validatePolicy(GoldenImagePolicy policy) {
if (policy.getName() == null || policy.getName().trim().isEmpty()) {
throw new InvalidPolicyException("Policy name is required");
}
if (policy.getSeverity() == null) {
throw new InvalidPolicyException("Policy severity is required");
}
if (policy.getRules() == null || policy.getRules().isEmpty()) {
throw new InvalidPolicyException("Policy must contain at least one rule");
}
for (ImageRule rule : policy.getRules()) {
validateRule(rule);
}
}
private void validateRule(ImageRule rule) {
if (rule.getRuleId() == null || rule.getRuleId().trim().isEmpty()) {
throw new InvalidPolicyException("Rule ID is required");
}
if (rule.getType() == null) {
throw new InvalidPolicyException("Rule type is required");
}
if (rule.getDescription() == null || rule.getDescription().trim().isEmpty()) {
throw new InvalidPolicyException("Rule description is required");
}
}
private String generatePolicyId() {
return "policy-" + UUID.randomUUID().toString().substring(0, 8);
}
private List<String> findPodsUsingImage(List<Pod> pods, String image) {
return pods.stream()
.filter(pod -> pod.getSpec().getContainers().stream()
.anyMatch(container -> image.equals(container.getImage())) ||
pod.getSpec().getInitContainers().stream()
.anyMatch(container -> image.equals(container.getImage())))
.map(pod -> pod.getMetadata().getName())
.collect(Collectors.toList());
}
private boolean shouldEnforceRemediation(ImageValidationResult result, List<GoldenImagePolicy> policies) {
return result.hasCriticalViolations() &&
policies.stream().anyMatch(p ->
p.getEnforcement() == GoldenImagePolicy.PolicyEnforcement.ENFORCE);
}
private void remediateNonCompliantPod(Pod pod, Container container, ImageValidationResult result) {
// Implementation depends on remediation strategy
// Options:
// 1. Add annotations for manual remediation
// 2. Scale down the deployment
// 3. Replace with compliant image
// 4. Isolate the pod
logger.warn("Pod {} using non-compliant image {}: {}",
pod.getMetadata().getName(),
container.getImage(),
result.getViolations().stream()
.map(PolicyViolation::getMessage)
.collect(Collectors.joining(", ")));
// Example: Add warning annotation
k8sClient.pods()
.inNamespace(pod.getMetadata().getNamespace())
.withName(pod.getMetadata().getName())
.edit(p -> new PodBuilder(p)
.editMetadata()
.addToAnnotations("golden-image-warning",
"Using non-compliant image: " + container.getImage())
.endMetadata()
.build());
}
}
Spring Boot Configuration
# application.yml golden-image: enabled: true validator: version: 1.0.0 strict-mode: false cache-results: true cache-ttl: 3600 vulnerability-scanning: enabled: true scanner: trivy trivy-path: /usr/local/bin/trivy cache-dir: /tmp/trivy-cache skip-update: false policies: config-map: golden-image-policies namespace: default auto-reload: true reload-interval: 300 admission-webhook: enabled: true port: 8443 tls-secret: golden-image-tls failure-policy: Fail reporting: enabled: true schedule: "0 2 * * *" # Daily at 2 AM formats: [ "json", "html", "pdf" ] storage: type: s3 bucket: compliance-reports
Conclusion
This comprehensive Golden Image Enforcement implementation provides:
- Policy-Driven Validation - Configurable security and compliance policies
- Vulnerability Scanning - Integration with Trivy for CVE detection
- Kubernetes Admission Control - Real-time validation of deployed images
- Compliance Reporting - Detailed compliance reports and dashboards
- Automated Remediation - Enforcement actions for non-compliant images
- Audit Logging - Comprehensive audit trail for compliance requirements
- Multi-Format Support - JSON, YAML, and human-readable reports
The implementation ensures that only approved, secure, and compliant container images are deployed in Kubernetes environments, significantly reducing the attack surface and maintaining organizational security standards.