Article
Snyk Container provides comprehensive security scanning for container images, identifying vulnerabilities in both your application dependencies and the underlying OS packages. For Java applications running in containers, this is crucial for securing your deployment pipeline.
This guide covers everything from basic Snyk Container setup to advanced integration patterns with Java applications.
Snyk Container Architecture Overview
- Image Scanning: Analyzes container images for vulnerabilities
- Dependency Scanning: Java-specific vulnerability detection
- Base Image Monitoring: Tracks vulnerabilities in parent images
- CI/CD Integration: Automated scanning in pipelines
- Policy Enforcement: Custom security rules and thresholds
1. Project Setup and Dependencies
Maven Dependencies:
<properties>
<snyk.java.version>1.119.0</snyk.java.version>
<jib.version>3.4.0</jib.version>
</properties>
<dependencies>
<!-- Snyk Java SDK (if available for programmatic access) -->
<dependency>
<groupId>io.snyk</groupId>
<artifactId>snyk-java-sdk</artifactId>
<version>${snyk.java.version}</version>
</dependency>
<!-- Snyk Maven Plugin -->
<dependency>
<groupId>io.snyk</groupId>
<artifactId>snyk-maven-plugin</artifactId>
<version>${snyk.java.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Jib for container building -->
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>${jib.version}</version>
</plugin>
</plugins>
</build>
2. Dockerfile Best Practices for Java
Secure Java Dockerfile:
# Multi-stage build for security and minimal image size FROM eclipse-temurin:17-jdk-jammy as builder WORKDIR /app COPY . . RUN ./mvnw clean package -DskipTests # Production stage FROM eclipse-temurin:17-jre-jammy as production # Security best practices RUN groupadd -r spring && useradd -r -g spring spring \ && mkdir -p /app \ && chown -R spring:spring /app USER spring:spring WORKDIR /app # Copy application from builder stage COPY --from=builder --chown=spring:spring /app/target/*.jar app.jar # Security configurations ENV JAVA_OPTS="-Djava.security.egd=file:/dev/./urandom -Dspring.profiles.active=prod" ENV JAVA_TOOL_OPTIONS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0" # Non-root user already set EXPOSE 8080 # Health check HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD curl -f http://localhost:8080/actuator/health || exit 1 # Secure entrypoint ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
Distroless Java Dockerfile:
# Using distroless for minimal attack surface FROM eclipse-temurin:17-jdk-jammy as builder WORKDIR /app COPY . . RUN ./mvnw clean package -DskipTests # Distroless Java runtime FROM gcr.io/distroless/java17-debian11:nonroot # Copy application COPY --from=builder /app/target/*.jar /app/app.jar WORKDIR /app USER nonroot:nonroot EXPOSE 8080 CMD ["app.jar"]
3. Snyk CLI Container Scanning
Basic Container Scanning:
# Install Snyk CLI curl https://static.snyk.io/cli/latest/snyk-linux -o snyk chmod +x snyk sudo mv snyk /usr/local/bin/ # Authenticate with Snyk snyk auth $SNYK_TOKEN # Scan local Docker image snyk container test my-java-app:1.0.0 # Scan with specific options snyk container test my-java-app:1.0.0 \ --file=Dockerfile \ --org=my-team \ --project-name="my-java-app" \ --severity-threshold=high # Monitor and track results snyk container monitor my-java-app:1.0.0 \ --file=Dockerfile \ --project-name="my-java-app" \ --tags=production,backend
Advanced Scanning Script:
#!/bin/bash
# snyk-container-scan.sh
set -e
IMAGE_NAME="my-java-app"
IMAGE_TAG="${1:-latest}"
SNYK_ORG="${SNYK_ORG:-my-team}"
SEVERITY_THRESHOLD="${SEVERITY_THRESHOLD:-high}"
FAIL_ON_ISSUES="${FAIL_ON_ISSUES:-true}"
echo "🔍 Scanning container image: ${IMAGE_NAME}:${IMAGE_TAG}"
# Build image if not exists
if ! docker image inspect "${IMAGE_NAME}:${IMAGE_TAG}" > /dev/null 2>&1; then
echo "📦 Building Docker image..."
docker build -t "${IMAGE_NAME}:${IMAGE_TAG}" .
fi
# Run Snyk container test
echo "🚀 Starting Snyk container scan..."
snyk container test "${IMAGE_NAME}:${IMAGE_TAG}" \
--file=Dockerfile \
--org="${SNYK_ORG}" \
--severity-threshold="${SEVERITY_THRESHOLD}" \
--json > snyk-container-results.json
# Check for critical vulnerabilities
CRITICAL_COUNT=$(jq '.uniqueCounts.critical // 0' snyk-container-results.json)
HIGH_COUNT=$(jq '.uniqueCounts.high // 0' snyk-container-results.json)
echo "📊 Scan Results:"
echo " Critical: $CRITICAL_COUNT"
echo " High: $HIGH_COUNT"
# Fail build if critical/high vulnerabilities found and FAIL_ON_ISSUES is true
if [ "$FAIL_ON_ISSUES" = "true" ] && [ "$CRITICAL_COUNT" -gt 0 ] || [ "$HIGH_COUNT" -gt 0 ]; then
echo "❌ Critical or High vulnerabilities found. Failing build."
exit 1
fi
# Monitor to Snyk platform
echo "📈 Monitoring results to Snyk platform..."
snyk container monitor "${IMAGE_NAME}:${IMAGE_TAG}" \
--file=Dockerfile \
--org="${SNYK_ORG}" \
--project-name="${IMAGE_NAME}" \
--tags="java,spring-boot,${IMAGE_TAG}"
echo "✅ Container scan completed successfully"
4. Maven Integration
Snyk Maven Plugin Configuration:
<plugin>
<groupId>io.snyk</groupId>
<artifactId>snyk-maven-plugin</artifactId>
<version>1.119.0</version>
<configuration>
<apiToken>${env.SNYK_TOKEN}</apiToken>
<org>my-team</org>
<projectName>${project.artifactId}</projectName>
<tags>
<tag>java</tag>
<tag>container</tag>
<tag>${project.version}</tag>
</tags>
<severityThreshold>high</severityThreshold>
<failOnSeverityThreshold>true</failOnSeverityThreshold>
</configuration>
<executions>
<execution>
<id>snyk-test</id>
<phase>verify</phase>
<goals>
<goal>test</goal>
</goals>
</execution>
<execution>
<id>snyk-monitor</id>
<phase>deploy</phase>
<goals>
<goal>monitor</goal>
</goals>
</execution>
</executions>
</plugin>
Jib + Snyk Integration:
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>3.4.0</version>
<configuration>
<from>
<image>eclipse-temurin:17-jre-jammy</image>
<platforms>
<platform>
<architecture>amd64</architecture>
<os>linux</os>
</platform>
</platforms>
</from>
<to>
<image>my-registry.com/my-java-app:${project.version}</image>
</to>
<container>
<entrypoint>
<shell>bash</shell>
<option>-c</option>
<arg>chmod +x /entrypoint.sh && /entrypoint.sh</arg>
</entrypoint>
<ports>
<port>8080</port>
</ports>
<environment>
<JAVA_TOOL_OPTIONS>-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0</JAVA_TOOL_OPTIONS>
</environment>
<creationTime>USE_CURRENT_TIMESTAMP</creationTime>
</container>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
</plugin>
5. CI/CD Integration
Jenkins Pipeline:
// Jenkinsfile
pipeline {
agent any
environment {
SNYK_TOKEN = credentials('snyk-api-token')
DOCKER_REGISTRY = 'my-registry.com'
}
stages {
stage('Build') {
steps {
sh 'mvn clean package -DskipTests'
}
}
stage('Build Container') {
steps {
script {
// Build with Jib
sh 'mvn compile jib:build -DskipTests'
// Or traditional Docker build
sh '''
docker build -t ${DOCKER_REGISTRY}/my-java-app:${BUILD_NUMBER} .
docker push ${DOCKER_REGISTRY}/my-java-app:${BUILD_NUMBER}
'''
}
}
}
stage('Snyk Container Scan') {
steps {
script {
// Test container image
sh '''
snyk container test ${DOCKER_REGISTRY}/my-java-app:${BUILD_NUMBER} \
--file=Dockerfile \
--org=my-team \
--severity-threshold=high \
--json-file-output=snyk-container-results.json
'''
// Monitor to Snyk platform
sh '''
snyk container monitor ${DOCKER_REGISTRY}/my-java-app:${BUILD_NUMBER} \
--file=Dockerfile \
--org=my-team \
--project-name="my-java-app"
'''
// Check for critical vulnerabilities
def scanResults = readJSON file: 'snyk-container-results.json'
def criticalCount = scanResults.uniqueCounts.critical ?: 0
def highCount = scanResults.uniqueCounts.high ?: 0
if (criticalCount > 0 || highCount > 0) {
error "Found ${criticalCount} critical and ${highCount} high vulnerabilities"
}
}
}
}
stage('Dependency Scan') {
steps {
sh 'mvn snyk:test'
}
}
}
post {
always {
// Archive Snyk reports
archiveArtifacts artifacts: 'snyk-*.json', fingerprint: true
// Cleanup
sh 'docker system prune -f'
}
}
}
GitHub Actions Workflow:
# .github/workflows/snyk-container.yml
name: Snyk Container Security
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
schedule:
- cron: '0 0 * * 1' # Weekly scan
env:
IMAGE_NAME: my-java-app
REGISTRY: ghcr.io
jobs:
snyk-container-scan:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
security-events: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Java
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: Build Docker image
run: |
docker build -t $IMAGE_NAME:${{ github.sha }} .
- name: Run Snyk to check Docker image for vulnerabilities
uses: snyk/actions/docker@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
image: $IMAGE_NAME:${{ github.sha }}
args: |
--file=Dockerfile
--org=my-team
--severity-threshold=high
--sarif-file-output=snyk-container.sarif
- name: Upload SARIF results to GitHub Security
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: snyk-container.sarif
- name: Monitor container in Snyk
uses: snyk/actions/docker@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
image: $IMAGE_NAME:${{ github.sha }}
command: monitor
args: |
--file=Dockerfile
--org=my-team
--project-name=$IMAGE_NAME
snyk-dependency-scan:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Java
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: Run Snyk to check for vulnerabilities
uses: snyk/actions/maven@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: |
--org=my-team
--severity-threshold=high
--sarif-file-output=snyk-dependency.sarif
- name: Upload SARIF results to GitHub Security
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: snyk-dependency.sarif
6. Programmatic Java Integration
Snyk Container Scanner Service:
package com.example.security.snyk;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.stereotype.Service;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
@Service
public class SnykContainerScanner {
private final ObjectMapper objectMapper;
private final String snykToken;
private final String snykOrg;
public SnykContainerScanner() {
this.objectMapper = new ObjectMapper();
this.snykToken = System.getenv("SNYK_TOKEN");
this.snykOrg = System.getenv("SNYK_ORG");
}
public ContainerScanResult scanImage(String imageName, String imageTag)
throws IOException, InterruptedException {
String fullImageName = imageName + ":" + imageTag;
// Prepare Snyk command
List<String> command = new ArrayList<>();
command.add("snyk");
command.add("container");
command.add("test");
command.add(fullImageName);
command.add("--org=" + snykOrg);
command.add("--json");
command.add("--severity-threshold=high");
ProcessBuilder processBuilder = new ProcessBuilder(command);
processBuilder.environment().put("SNYK_TOKEN", snykToken);
// Execute Snyk scan
Process process = processBuilder.start();
boolean completed = process.waitFor(10, TimeUnit.MINUTES);
if (!completed) {
throw new RuntimeException("Snyk scan timed out");
}
// Parse JSON output
try (InputStream inputStream = process.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
StringBuilder jsonOutput = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
jsonOutput.append(line);
}
return parseScanResults(jsonOutput.toString(), fullImageName);
}
}
private ContainerScanResult parseScanResults(String jsonOutput, String imageName)
throws IOException {
JsonNode root = objectMapper.readTree(jsonOutput);
ContainerScanResult result = new ContainerScanResult();
result.setImageName(imageName);
result.setScanTimestamp(System.currentTimeMillis());
// Parse vulnerabilities
if (root.has("vulnerabilities")) {
for (JsonNode vuln : root.get("vulnerabilities")) {
Vulnerability vulnerability = new Vulnerability();
vulnerability.setId(vuln.get("id").asText());
vulnerability.setTitle(vuln.get("title").asText());
vulnerability.setSeverity(vuln.get("severity").asText());
vulnerability.setPackageName(vuln.get("packageName").asText());
if (vuln.has("cvssScore")) {
vulnerability.setCvssScore(vuln.get("cvssScore").asDouble());
}
result.addVulnerability(vulnerability);
}
}
// Parse summary
if (root.has("uniqueCounts")) {
JsonNode counts = root.get("uniqueCounts");
result.setCriticalCount(counts.has("critical") ? counts.get("critical").asInt() : 0);
result.setHighCount(counts.has("high") ? counts.get("high").asInt() : 0);
result.setMediumCount(counts.has("medium") ? counts.get("medium").asInt() : 0);
result.setLowCount(counts.has("low") ? counts.get("low").asInt() : 0);
}
return result;
}
public void monitorImage(String imageName, String imageTag, String projectName)
throws IOException, InterruptedException {
String fullImageName = imageName + ":" + imageTag;
List<String> command = new ArrayList<>();
command.add("snyk");
command.add("container");
command.add("monitor");
command.add(fullImageName);
command.add("--org=" + snykOrg);
command.add("--project-name=" + projectName);
ProcessBuilder processBuilder = new ProcessBuilder(command);
processBuilder.environment().put("SNYK_TOKEN", snykToken);
Process process = processBuilder.start();
process.waitFor(2, TimeUnit.MINUTES);
}
public boolean passesSecurityGate(ContainerScanResult result, SecurityPolicy policy) {
return result.getCriticalCount() <= policy.getMaxCritical() &&
result.getHighCount() <= policy.getMaxHigh() &&
result.getMediumCount() <= policy.getMaxMedium();
}
}
class ContainerScanResult {
private String imageName;
private long scanTimestamp;
private int criticalCount;
private int highCount;
private int mediumCount;
private int lowCount;
private List<Vulnerability> vulnerabilities = new ArrayList<>();
// Getters and setters
public String getImageName() { return imageName; }
public void setImageName(String imageName) { this.imageName = imageName; }
public long getScanTimestamp() { return scanTimestamp; }
public void setScanTimestamp(long scanTimestamp) { this.scanTimestamp = scanTimestamp; }
public int getCriticalCount() { return criticalCount; }
public void setCriticalCount(int criticalCount) { this.criticalCount = criticalCount; }
public int getHighCount() { return highCount; }
public void setHighCount(int highCount) { this.highCount = highCount; }
public int getMediumCount() { return mediumCount; }
public void setMediumCount(int mediumCount) { this.mediumCount = mediumCount; }
public int getLowCount() { return lowCount; }
public void setLowCount(int lowCount) { this.lowCount = lowCount; }
public List<Vulnerability> getVulnerabilities() { return vulnerabilities; }
public void addVulnerability(Vulnerability vulnerability) { this.vulnerabilities.add(vulnerability); }
}
class Vulnerability {
private String id;
private String title;
private String severity;
private String packageName;
private double cvssScore;
private String description;
private List<String> identifiers = new ArrayList<>();
// Getters and setters
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getSeverity() { return severity; }
public void setSeverity(String severity) { this.severity = severity; }
public String getPackageName() { return packageName; }
public void setPackageName(String packageName) { this.packageName = packageName; }
public double getCvssScore() { return cvssScore; }
public void setCvssScore(double cvssScore) { this.cvssScore = cvssScore; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public List<String> getIdentifiers() { return identifiers; }
public void addIdentifier(String identifier) { this.identifiers.add(identifier); }
}
class SecurityPolicy {
private int maxCritical = 0;
private int maxHigh = 5;
private int maxMedium = 20;
// Getters and setters
public int getMaxCritical() { return maxCritical; }
public void setMaxCritical(int maxCritical) { this.maxCritical = maxCritical; }
public int getMaxHigh() { return maxHigh; }
public void setMaxHigh(int maxHigh) { this.maxHigh = maxHigh; }
public int getMaxMedium() { return maxMedium; }
public void setMaxMedium(int maxMedium) { this.maxMedium = maxMedium; }
}
7. Advanced Container Security Features
Runtime Security Monitoring:
package com.example.security.snyk;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@Component
public class ContainerSecurityHealthIndicator implements HealthIndicator {
private final SnykContainerScanner containerScanner;
private final ScheduledExecutorService scheduler;
private ContainerScanResult lastScanResult;
private Instant lastScanTime;
public ContainerSecurityHealthIndicator(SnykContainerScanner containerScanner) {
this.containerScanner = containerScanner;
this.scheduler = Executors.newSingleThreadScheduledExecutor();
startPeriodicScans();
}
private void startPeriodicScans() {
// Scan every 24 hours
scheduler.scheduleAtFixedRate(this::performSecurityScan, 0, 24, TimeUnit.HOURS);
}
private void performSecurityScan() {
try {
String imageName = System.getenv("CONTAINER_IMAGE_NAME");
String imageTag = System.getenv("CONTAINER_IMAGE_TAG");
if (imageName != null && imageTag != null) {
lastScanResult = containerScanner.scanImage(imageName, imageTag);
lastScanTime = Instant.now();
}
} catch (Exception e) {
System.err.println("Failed to perform security scan: " + e.getMessage());
}
}
@Override
public Health health() {
if (lastScanResult == null) {
return Health.unknown()
.withDetail("message", "Security scan not yet performed")
.build();
}
long hoursSinceScan = ChronoUnit.HOURS.between(lastScanTime, Instant.now());
Health.Builder healthBuilder;
if (lastScanResult.getCriticalCount() > 0) {
healthBuilder = Health.down();
} else if (lastScanResult.getHighCount() > 5 || hoursSinceScan > 48) {
healthBuilder = Health.outOfService();
} else {
healthBuilder = Health.up();
}
return healthBuilder
.withDetail("lastScan", lastScanTime.toString())
.withDetail("criticalVulnerabilities", lastScanResult.getCriticalCount())
.withDetail("highVulnerabilities", lastScanResult.getHighCount())
.withDetail("mediumVulnerabilities", lastScanResult.getMediumCount())
.build();
}
@PreDestroy
public void cleanup() {
scheduler.shutdown();
}
}
Security Policy Enforcement:
package com.example.security.snyk;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
@Component
public class ContainerSecurityEnforcer implements ApplicationListener<ContextRefreshedEvent> {
private final SnykContainerScanner containerScanner;
private final SecurityPolicy securityPolicy;
public ContainerSecurityEnforcer(SnykContainerScanner containerScanner,
SecurityPolicy securityPolicy) {
this.containerScanner = containerScanner;
this.securityPolicy = securityPolicy;
}
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
try {
String imageName = System.getenv("CONTAINER_IMAGE_NAME");
String imageTag = System.getenv("CONTAINER_IMAGE_TAG");
if (imageName != null && imageTag != null) {
ContainerScanResult result = containerScanner.scanImage(imageName, imageTag);
if (!containerScanner.passesSecurityGate(result, securityPolicy)) {
throw new SecurityException(
"Container security policy violation: " +
result.getCriticalCount() + " critical, " +
result.getHighCount() + " high vulnerabilities found"
);
}
// Monitor to Snyk platform
containerScanner.monitorImage(imageName, imageTag, "my-java-app");
}
} catch (Exception e) {
System.err.println("Security enforcement failed: " + e.getMessage());
// In production, you might want to exit the application
// System.exit(1);
}
}
}
8. Custom Base Image Security
Secure Base Image Builder:
# secure-java-base/Dockerfile FROM eclipse-temurin:17-jre-jammy # Security updates RUN apt-get update && apt-get upgrade -y && \ apt-get clean && rm -rf /var/lib/apt/lists/* # Security hardening RUN groupadd -r spring && useradd -r -g spring spring && \ mkdir -p /app && chown -R spring:spring /app # Remove unnecessary packages RUN apt-get purge -y --auto-remove && \ rm -rf /var/lib/apt/lists/* # Security configurations RUN echo "networkaddress.cache.ttl=60" >> $JAVA_HOME/conf/security/java.security USER spring:spring WORKDIR /app
Base Image Scanning Script:
#!/bin/bash
# scan-base-images.sh
BASE_IMAGES=(
"eclipse-temurin:17-jre-jammy"
"eclipse-temurin:11-jre-jammy"
"gcr.io/distroless/java17-debian11:nonroot"
)
for image in "${BASE_IMAGES[@]}"; do
echo "🔍 Scanning base image: $image"
# Pull latest version
docker pull "$image"
# Scan with Snyk
snyk container test "$image" \
--severity-threshold=high \
--json > "scan-$(echo $image | tr '/:' '-').json"
# Check for critical vulnerabilities
critical_count=$(jq '.uniqueCounts.critical // 0' "scan-$(echo $image | tr '/:' '-').json")
if [ "$critical_count" -gt 0 ]; then
echo "❌ $image has $critical_count critical vulnerabilities"
else
echo "✅ $image passed security scan"
fi
done
9. Kubernetes Integration
Kubernetes Deployment with Security Context:
# k8s/deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: my-java-app labels: app: my-java-app spec: replicas: 3 selector: matchLabels: app: my-java-app template: metadata: labels: app: my-java-app annotations: snyk.io/container-image: "my-registry.com/my-java-app:1.0.0" snyk.io/monitored: "true" spec: securityContext: runAsNonRoot: true runAsUser: 1000 runAsGroup: 1000 fsGroup: 1000 containers: - name: java-app image: my-registry.com/my-java-app:1.0.0 securityContext: allowPrivilegeEscalation: false runAsNonRoot: true runAsUser: 1000 capabilities: drop: - ALL readOnlyRootFilesystem: true ports: - containerPort: 8080 livenessProbe: httpGet: path: /actuator/health port: 8080 initialDelaySeconds: 30 periodSeconds: 10 readinessProbe: httpGet: path: /actuator/health port: 8080 initialDelaySeconds: 5 periodSeconds: 5 resources: requests: memory: "512Mi" cpu: "250m" limits: memory: "1Gi" cpu: "500m" env: - name: JAVA_TOOL_OPTIONS value: "-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0"
Snyk Kubernetes Monitoring:
# Install Snyk Monitor for Kubernetes helm repo add snyk https://snyk.github.io/kubernetes-monitor helm repo update helm upgrade --install snyk-monitor snyk/snyk-monitor \ --namespace snyk-monitor \ --create-namespace \ --set clusterName="production" \ --set integrationApi=$SNYK_INTEGRATION_ID
10. Reporting and Alerting
Security Dashboard Integration:
package com.example.security.snyk;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.stream.Collectors;
@Component
public class SecurityReportingService {
private final SnykContainerScanner containerScanner;
private final SecurityAlertService alertService;
public SecurityReportingService(SnykContainerScanner containerScanner,
SecurityAlertService alertService) {
this.containerScanner = containerScanner;
this.alertService = alertService;
}
@Scheduled(cron = "0 0 9 * * MON") // Every Monday at 9 AM
public void generateWeeklySecurityReport() {
try {
List<String> images = List.of(
"my-java-app:latest",
"my-java-app:1.0.0",
"my-base-image:latest"
);
List<ContainerScanResult> results = images.stream()
.map(image -> {
try {
String[] parts = image.split(":");
return containerScanner.scanImage(parts[0], parts[1]);
} catch (Exception e) {
return null;
}
})
.filter(result -> result != null)
.collect(Collectors.toList());
SecurityReport report = generateReport(results);
alertService.sendWeeklyReport(report);
} catch (Exception e) {
System.err.println("Failed to generate security report: " + e.getMessage());
}
}
private SecurityReport generateReport(List<ContainerScanResult> results) {
SecurityReport report = new SecurityReport();
int totalCritical = results.stream().mapToInt(ContainerScanResult::getCriticalCount).sum();
int totalHigh = results.stream().mapToInt(ContainerScanResult::getHighCount).sum();
report.setTotalCritical(totalCritical);
report.setTotalHigh(totalHigh);
report.setScanResults(results);
report.setGeneratedAt(System.currentTimeMillis());
return report;
}
}
class SecurityReport {
private int totalCritical;
private int totalHigh;
private List<ContainerScanResult> scanResults;
private long generatedAt;
// Getters and setters
public int getTotalCritical() { return totalCritical; }
public void setTotalCritical(int totalCritical) { this.totalCritical = totalCritical; }
public int getTotalHigh() { return totalHigh; }
public void setTotalHigh(int totalHigh) { this.totalHigh = totalHigh; }
public List<ContainerScanResult> getScanResults() { return scanResults; }
public void setScanResults(List<ContainerScanResult> scanResults) { this.scanResults = scanResults; }
public long getGeneratedAt() { return generatedAt; }
public void setGeneratedAt(long generatedAt) { this.generatedAt = generatedAt; }
}
Conclusion
Snyk Container provides comprehensive security scanning for Java applications in containers:
- Vulnerability Detection: OS packages and Java dependencies
- Base Image Security: Monitor parent image vulnerabilities
- CI/CD Integration: Automated scanning in pipelines
- Policy Enforcement: Custom security gates
- Runtime Monitoring: Continuous security assessment
Key Benefits for Java Applications:
- Comprehensive vulnerability coverage
- Integration with existing Java tooling
- Automated security enforcement
- Regulatory compliance support
- Supply chain security
By implementing Snyk Container scanning throughout your Java container lifecycle, you can significantly improve your application security posture and catch vulnerabilities before they reach production.
Secure Java Supply Chain, Minimal Containers & Runtime Security (Alpine, Distroless, Signing, SBOM & Kubernetes Controls)
https://macronepal.com/blog/alpine-linux-security-in-java-complete-guide/
Explains how Alpine Linux is used as a lightweight base for Java containers to reduce image size and attack surface, while discussing tradeoffs like musl compatibility, CVE handling, and additional hardening requirements for production security.
https://macronepal.com/blog/the-minimalists-approach-building-ultra-secure-java-applications-with-scratch-base-images/
Explains using scratch base images for Java applications to create extremely minimal containers with almost zero attack surface, where only the compiled Java application and runtime dependencies exist.
https://macronepal.com/blog/distroless-containers-in-java-minimal-secure-containers-for-jvm-applications/
Explains distroless Java containers that remove shells, package managers, and unnecessary OS tools, significantly reducing vulnerabilities while improving security posture for JVM workloads.
https://macronepal.com/blog/revolutionizing-container-security-implementing-chainguard-images-for-java-applications/
Explains Chainguard images for Java, which are secure-by-default, CVE-minimized container images with SBOMs and cryptographic signing, designed for modern supply-chain security.
https://macronepal.com/blog/seccomp-filtering-in-java-comprehensive-security-sandboxing/
Explains seccomp syscall filtering in Linux to restrict what system calls Java applications can make, reducing the impact of exploits by limiting kernel-level access.
https://macronepal.com/blog/in-toto-attestations-in-java/
Explains in-toto framework integration in Java to create cryptographically verifiable attestations across the software supply chain, ensuring every build step is trusted and auditable.
https://macronepal.com/blog/fulcio-integration-in-java-code-signing-certificate-infrastructure/
Explains Fulcio integration for Java, which issues short-lived certificates for code signing in a zero-trust supply chain, enabling secure identity-based signing of artifacts.
https://macronepal.com/blog/tekton-supply-chain-in-java-comprehensive-ci-cd-pipeline-implementation/
Explains using Tekton CI/CD pipelines for Java applications to automate secure builds, testing, signing, and deployment with supply-chain security controls built in.
https://macronepal.com/blog/slsa-provenance-in-java-complete-guide-to-supply-chain-security-2/
Explains SLSA (Supply-chain Levels for Software Artifacts) provenance in Java builds, ensuring traceability of how software is built, from source code to final container image.
https://macronepal.com/blog/notary-project-in-java-complete-implementation-guide/
Explains the Notary Project for Java container security, enabling cryptographic signing and verification of container images and artifacts to prevent tampering in deployment pipelines.