Anchore Inline Scan in Java: Complete Guide

Introduction to Anchore Inline Scan

Anchore Engine is a container image scanning tool that analyzes Docker images for security vulnerabilities, policy compliance, and best practices. The inline scan allows you to scan images directly in your CI/CD pipeline without requiring a running Anchore Engine service.


System Architecture Overview

Anchore Inline Scan Pipeline
├── Image Building
│   ├ - Dockerfile Optimization
│   ├ - Multi-stage Builds
│   ├ - Base Image Security
│   └ - Dependency Management
├── Anchore Scanning
│   ├ - Vulnerability Analysis
│   ├ - Policy Evaluation
│   ├ - CVE Matching
│   └ - Compliance Checking
├── Security Gates
│   ├ - Vulnerability Thresholds
│   ├ - Policy Enforcement
│   ├ - Break-glass Procedures
│   └ - Approval Workflows
└── Results & Reporting
├ - Security Reports
├ - Compliance Dashboards
├ - SBOM Generation
└ - Audit Trails

Core Implementation

1. Dockerfile Security Best Practices

Secure Java Application Dockerfile

# Multi-stage build for security and optimization
FROM eclipse-temurin:17-jdk-jammy as builder
# Security: Run as non-root user
RUN groupadd -r spring && useradd -r -g spring spring
USER spring:spring
WORKDIR /app
# Copy dependency files first for better caching
COPY --chown=spring:spring mvnw .
COPY --chown=spring:spring .mvn .mvn
COPY --chown=spring:spring pom.xml .
# Download dependencies in separate layer
RUN ./mvnw dependency:go-offline -B
# Copy source code
COPY --chown=spring:spring src src
# Build application
RUN ./mvnw clean package -DskipTests
# Runtime stage
FROM eclipse-temurin:17-jre-jammy as runtime
# Security updates and basic packages
RUN apt-get update && \
apt-get upgrade -y && \
apt-get install -y --no-install-recommends \
curl \
ca-certificates && \
rm -rf /var/lib/apt/lists/*
# Create non-root user
RUN groupadd -r spring && useradd -r -g spring spring
# Security: Create app directory with proper permissions
RUN mkdir -p /app && chown spring:spring /app
WORKDIR /app
USER spring:spring
# Copy JAR from builder stage
COPY --from=builder --chown=spring:spring /app/target/*.jar app.jar
# Security: Use non-root user
USER spring:spring
# Security: Use secure JVM options
ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -Djava.security.egd=file:/dev/./urandom"
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8080/actuator/health || exit 1
# Security: Run as non-privileged port
EXPOSE 8080
# Security: Use exec form and don't use shell form
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]

Alternative Distroless Dockerfile

# Multi-stage build with distroless base
FROM eclipse-temurin:17-jdk-jammy as builder
WORKDIR /app
COPY mvnw .
COPY .mvn .mvn
COPY pom.xml .
RUN ./mvnw dependency:go-offline -B
COPY src src
RUN ./mvnw clean package -DskipTests
# Use distroless Java base for minimal attack surface
FROM gcr.io/distroless/java17:nonroot
# Copy application
COPY --from=builder /app/target/*.jar /app/app.jar
# Use non-root user (distroless images have nonroot user by default)
USER nonroot
# Security: Read-only root filesystem
# Note: This requires proper volume mounts for writable areas
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app/app.jar"]

2. Maven Configuration for Container Security

pom.xml with Jib and Security Plugins

<properties>
<jib-maven-plugin.version>3.4.0</jib-maven-plugin.version>
<spring-boot.version>2.7.0</spring-boot.version>
<jacoco.version>0.8.8</jacocy.version>
<owasp.dependency.check.version>8.4.2</owasp.dependency.check.version>
</properties>
<build>
<plugins>
<!-- Jib for secure container building -->
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>${jib-maven-plugin.version}</version>
<configuration>
<from>
<image>eclipse-temurin:17-jre-jammy</image>
<platforms>
<platform>
<architecture>amd64</architecture>
<os>linux</os>
</platform>
</platforms>
</from>
<to>
<image>${project.artifactId}:${project.version}</image>
</to>
<container>
<creationTime>USE_CURRENT_TIMESTAMP</creationTime>
<user>1000</user>
<workingDirectory>/app</workingDirectory>
<environment>
<JAVA_OPTS>-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0</JAVA_OPTS>
</environment>
<ports>
<port>8080</port>
</ports>
<labels>
<org.label-schema.name>${project.artifactId}</org.label-schema.name>
<org.label-schema.version>${project.version}</org.label-schema.version>
<org.label-schema.build-date>${maven.build.timestamp}</org.label-schema.build-date>
</labels>
<format>OCI</format>
</container>
</configuration>
</plugin>
<!-- OWASP Dependency Check for vulnerability scanning -->
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>${owasp.dependency.check.version}</version>
<configuration>
<format>HTML</format>
<format>JSON</format>
<failBuildOnAnyVulnerability>true</failBuildOnAnyVulnerability>
<failOnCVSS>7</failOnCVSS>
<suppressionFiles>
<suppressionFile>${project.basedir}/security/dependency-check-suppressions.xml</suppressionFile>
</suppressionFiles>
</configuration>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>container-security</id>
<build>
<plugins>
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<executions>
<execution>
<id>build-container</id>
<phase>package</phase>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>

3. GitHub Actions with Anchore Inline Scan

container-security-scan.yml

name: "Container Security Scan"
on:
push:
branches: [ main, develop ]
tags: [ 'v*' ]
pull_request:
branches: [ main, develop ]
schedule:
- cron: '0 2 * * 1'  # Weekly on Monday at 2 AM
env:
IMAGE_NAME: ${{ github.repository }}
VERSION: ${{ github.sha }}
jobs:
build-and-scan:
name: Build and Security Scan
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
cache: 'maven'
- name: Build application
run: mvn clean package -DskipTests
- name: Build Docker image
run: |
docker build \
--tag $IMAGE_NAME:$VERSION \
--tag $IMAGE_NAME:latest \
--file Dockerfile \
--build-arg BUILDKIT_INLINE_CACHE=1 \
.
- name: Run OWASP Dependency Check
run: mvn org.owasp:dependency-check-maven:check -DskipTests
- name: Download Anchore Inline Scan script
run: |
curl -s https://ci-tools.anchore.io/inline_scan-v0.10.0 > inline_scan.sh
chmod +x inline_scan.sh
- name: Run Anchore Inline Scan
run: |
./inline_scan.sh \
-r "$IMAGE_NAME:$VERSION" \
-t 300 \
-v \
--dockerfile Dockerfile
- name: Run Advanced Anchore Scan with Policy
run: |
./inline_scan.sh \
-r "$IMAGE_NAME:$VERSION" \
-p .github/anchore/policy_bundle.json \
-f .github/anchore/.anchore_policy.json \
-t 600 \
-v
- name: Generate SBOM
run: |
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
anchore/syft:latest \
$IMAGE_NAME:$VERSION \
-o cyclonedx-json > sbom.json
- name: Upload SBOM
uses: actions/upload-artifact@v4
with:
name: sbom
path: sbom.json
retention-days: 30
- name: Upload Security Reports
uses: actions/upload-artifact@v4
with:
name: security-reports
path: |
target/dependency-check-report.html
anchore-reports/
retention-days: 90
security-gate:
name: Security Gate
runs-on: ubuntu-latest
needs: build-and-scan
if: always()
steps:
- name: Check Security Results
run: |
# Check if scan passed security gates
echo "Security gate checks completed"
# In real implementation, this would parse scan results

advanced-anchore-scan.yml

name: "Advanced Anchore Security Scan"
on:
push:
branches: [ main, develop ]
release:
types: [ published ]
jobs:
comprehensive-scan:
name: Comprehensive Container Security
runs-on: ubuntu-latest
strategy:
matrix:
severity: [ critical, high, medium ]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Build container image
run: |
docker build \
--tag ${{ github.repository }}:${{ github.sha }} \
--file Dockerfile \
--security-opt=no-new-privileges \
.
- name: Run Trivy vulnerability scan
uses: aquasecurity/trivy-action@master
with:
image-ref: '${{ github.repository }}:${{ github.sha }}'
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'CRITICAL,HIGH'
- name: Upload Trivy results
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: 'trivy-results.sarif'
- name: Run Anchore inline scan with custom policy
run: |
curl -s https://ci-tools.anchore.io/inline_scan-v0.10.0 > inline_scan.sh
chmod +x inline_scan.sh
./inline_scan.sh \
-r "${{ github.repository }}:${{ github.sha }}" \
-p .github/anchore/policy_bundle.json \
-f .github/anchore/.anchore_policy.json \
--timeout 900 \
--verbose
- name: Check scan results
run: |
if [ -f "anchore_results.json" ]; then
echo "Scan completed. Analyzing results..."
# Add custom result analysis logic here
else
echo "Scan failed or no results generated"
exit 1
fi
- name: Notify on critical findings
if: failure()
uses: 8398a7/action-slack@v3
with:
status: failure
text: |
Critical security vulnerabilities found in container image.
Please review the scan results.
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_SECURITY_WEBHOOK }}

4. Anchore Policy Configuration

Policy Bundle Configuration

{
"action": "STOP",
"comment": "Default policy with security gates",
"id": "default_policy",
"mappings": [
{
"id": "critical_vulnerabilities",
"name": "Critical vulnerabilities gate",
"policy_id": "critical_vulnerabilities_policy",
"registry": "*",
"repository": "*",
"image": {
"type": "tag",
"value": "*"
},
"whitelist_ids": [],
"blacklist_ids": [],
"policy_filters": [
{
"name": "vulnerability_severity",
"operator": ">",
"value": "critical"
}
]
},
{
"id": "high_vulnerabilities",
"name": "High vulnerabilities gate",
"policy_id": "high_vulnerabilities_policy",
"registry": "*",
"repository": "*",
"image": {
"type": "tag",
"value": "*"
},
"whitelist_ids": ["allow_known_high_issues"],
"blacklist_ids": [],
"policy_filters": [
{
"name": "vulnerability_severity",
"operator": ">",
"value": "high"
}
]
}
],
"name": "security_gate_policy",
"policy_filters": [],
"policies": [
{
"id": "critical_vulnerabilities_policy",
"name": "Critical vulnerabilities policy",
"version": "1_0",
"rules": [
{
"action": "STOP",
"gate": "vulnerabilities",
"id": "critical_vuln_rule",
"params": [
{
"name": "package_type",
"value": "all"
},
{
"name": "severity_comparison",
"value": ">"
},
{
"name": "severity",
"value": "critical"
},
{
"name": "max_days_since_creation",
"value": "30"
}
],
"trigger": "package"
}
]
},
{
"id": "high_vulnerabilities_policy",
"name": "High vulnerabilities policy",
"version": "1_0",
"rules": [
{
"action": "WARN",
"gate": "vulnerabilities",
"id": "high_vuln_rule",
"params": [
{
"name": "package_type",
"value": "all"
},
{
"name": "severity_comparison",
"value": ">"
},
{
"name": "severity",
"value": "high"
}
],
"trigger": "package"
}
]
}
],
"version": "1_0",
"whitelists": [
{
"id": "allow_known_high_issues",
"name": "Allowed high severity issues",
"version": "1_0",
"items": [
{
"gate": "vulnerabilities",
"trigger_id": "CVE-2023-12345",
"id": "allow_specific_cve"
}
]
}
]
}

Anchore Policy Configuration

{
"image": {
"default_pull_timeout": 300,
"policy_bundle": "/policy_bundle.json"
},
"analyzer": {
"enable_package_filtering": true,
"filter_packages": [
{
"name": "*.doc",
"type": "whitelist"
}
]
},
"grype": {
"update_url": "https://toolbox-data.anchore.io/grype/databases/listing.json",
"db_verify": true
},
"security_scan": {
"max_severity": "critical",
"stop_on_severity": "critical",
"fail_on_severity": "high"
},
"output": {
"format": "json",
"file": "/scan_results/anchore_report.json"
}
}

5. Security-Focused Java Application

Secure Spring Boot Configuration

/**
* Security-focused Spring Boot configuration.
*/
@SpringBootApplication
public class SecureApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(SecureApplication.class);
// Security: Disable web server info exposure
application.setBannerMode(Banner.Mode.OFF);
// Security: Add shutdown hook for graceful shutdown
application.setRegisterShutdownHook(true);
application.run(args);
}
/**
* Security-focused web server configuration.
*/
@Bean
public TomcatServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
tomcat.addConnectorCustomizers(connector -> {
// Security: Disable server header
connector.setAttribute("server", "");
// Security: Set secure connection settings
if (connector.getProtocolHandler() instanceof AbstractHttp11Protocol) {
AbstractHttp11Protocol<?> protocol = 
(AbstractHttp11Protocol<?>) connector.getProtocolHandler();
protocol.setMaxSwallowSize(-1); // Prevent slowloris attacks
}
});
return tomcat;
}
/**
* Security headers configuration.
*/
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// Security headers
.headers(headers -> headers
.contentSecurityPolicy(csp -> csp
.policyDirectives("default-src 'self'; script-src 'self' 'unsafe-inline'")
)
.frameOptions(frame -> frame.deny())
.xssProtection(xss -> xss.block(true))
.contentTypeOptions(contentType -> {})
)
// CSRF protection
.csrf(csrf -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
)
// Session management
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.maximumSessions(1)
.maxSessionsPreventsLogin(true)
);
return http.build();
}
/**
* Secure actuator configuration.
*/
@Bean
public EndpointFilter secureActuatorEndpoints() {
return new EndpointFilter() {
@Override
public boolean match(Endpoint endpoint) {
// Only expose health and info endpoints
return endpoint instanceof HealthEndpoint || 
endpoint instanceof InfoEndpoint;
}
};
}
}

Container-Aware Health Check

/**
* Container-aware health check for Kubernetes and Docker.
*/
@Component
public class ContainerHealthIndicator implements HealthIndicator {
private final Runtime runtime = Runtime.getRuntime();
@Value("${container.memory.threshold:0.8}")
private double memoryThreshold;
@Value("${container.disk.threshold:0.9}")
private double diskThreshold;
@Override
public Health health() {
try {
// Check memory usage
Health.Builder builder = checkMemoryUsage();
// Check disk space
checkDiskSpace(builder);
// Check application-specific health
checkApplicationHealth(builder);
return builder.build();
} catch (Exception e) {
return Health.down()
.withDetail("error", "Health check failed: " + e.getMessage())
.build();
}
}
private Health.Builder checkMemoryUsage() {
long maxMemory = runtime.maxMemory();
long usedMemory = runtime.totalMemory() - runtime.freeMemory();
double memoryUsage = (double) usedMemory / maxMemory;
Health.Builder builder = Health.up();
if (memoryUsage > memoryThreshold) {
builder = Health.down()
.withDetail("memory_usage", String.format("%.2f%%", memoryUsage * 100))
.withDetail("memory_threshold", String.format("%.2f%%", memoryThreshold * 100))
.withDetail("message", "Memory usage exceeds threshold");
} else {
builder.withDetail("memory_usage", String.format("%.2f%%", memoryUsage * 100))
.withDetail("max_memory", formatBytes(maxMemory))
.withDetail("used_memory", formatBytes(usedMemory));
}
return builder;
}
private void checkDiskSpace(Health.Builder builder) {
try {
File root = new File("/");
long totalSpace = root.getTotalSpace();
long freeSpace = root.getFreeSpace();
double diskUsage = 1 - ((double) freeSpace / totalSpace);
if (diskUsage > diskThreshold) {
builder.down()
.withDetail("disk_usage", String.format("%.2f%%", diskUsage * 100))
.withDetail("disk_threshold", String.format("%.2f%%", diskThreshold * 100))
.withDetail("free_space", formatBytes(freeSpace))
.withDetail("message", "Disk space usage exceeds threshold");
} else {
builder.withDetail("disk_usage", String.format("%.2f%%", diskUsage * 100))
.withDetail("free_space", formatBytes(freeSpace));
}
} catch (SecurityException e) {
builder.withDetail("disk_check", "Unable to check disk space: " + e.getMessage());
}
}
private void checkApplicationHealth(Health.Builder builder) {
// Add application-specific health checks
try {
// Check database connectivity
// Check external service availability
// Check cache health
builder.withDetail("application", "healthy");
} catch (Exception e) {
builder.down().withDetail("application", "unhealthy: " + e.getMessage());
}
}
private String formatBytes(long bytes) {
if (bytes < 1024) return bytes + " B";
int exp = (int) (Math.log(bytes) / Math.log(1024));
String pre = "KMGTPE".charAt(exp-1) + "i";
return String.format("%.1f %sB", bytes / Math.pow(1024, exp), pre);
}
}

6. Security Testing for Containers

Container Security Test Suite

/**
* Container security test suite.
*/
@SpringBootTest
@Testcontainers
class ContainerSecurityTest {
@Container
private static final GenericContainer<?> appContainer = 
new GenericContainer<>(DockerImageName.parse("myapp:latest"))
.withExposedPorts(8080)
.withEnv("JAVA_OPTS", "-XX:+UseContainerSupport")
.withCreateContainerCmdModifier(cmd -> 
cmd.withUser("1000:1000"));
@Test
void testContainerRunsAsNonRoot() {
// Verify container runs as non-root user
String whoami = executeCommandInContainer("whoami");
assertEquals("spring", whoami.trim(), 
"Container should run as non-root user");
}
@Test
void testNoSensitiveFilesExposed() {
// Check that sensitive files are not accessible
List<String> sensitivePaths = Arrays.asList(
"/etc/passwd",
"/etc/shadow",
"/root",
"/proc"
);
for (String path : sensitivePaths) {
String result = executeCommandInContainer(
"find " + path + " -type f -name '*' 2>/dev/null | head -5");
assertTrue(result == null || result.trim().isEmpty(),
"Sensitive path should not be accessible: " + path);
}
}
@Test
void testHealthCheckEndpoint() {
Integer port = appContainer.getMappedPort(8080);
String healthUrl = "http://localhost:" + port + "/actuator/health";
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.getForEntity(healthUrl, String.class);
assertEquals(HttpStatus.OK, response.getStatusCode());
// Verify health response structure
assertTrue(response.getBody().contains("\"status\":\"UP\""));
}
@Test
void testSecurityHeaders() {
Integer port = appContainer.getMappedPort(8080);
String testUrl = "http://localhost:" + port + "/api/public/test";
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = restTemplate.headForHeaders(testUrl);
// Verify security headers
assertNotNull(headers.get("X-Content-Type-Options"));
assertNotNull(headers.get("X-Frame-Options"));
assertNotNull(headers.get("X-XSS-Protection"));
assertEquals("DENY", headers.getFirst("X-Frame-Options"));
}
@Test
void testVulnerabilityScanResults() {
// This test would parse Anchore scan results
// and verify no critical vulnerabilities exist
File scanResults = new File("anchore_results.json");
if (scanResults.exists()) {
try {
JsonNode results = new ObjectMapper().readTree(scanResults);
JsonNode vulnerabilities = results.path("vulnerabilities");
int criticalCount = 0;
for (JsonNode vuln : vulnerabilities) {
if ("Critical".equals(vuln.path("severity").asText())) {
criticalCount++;
}
}
assertEquals(0, criticalCount, 
"No critical vulnerabilities should be present");
} catch (IOException e) {
fail("Failed to parse scan results: " + e.getMessage());
}
}
}
private String executeCommandInContainer(String command) {
try {
Container.ExecResult result = appContainer.execInContainer(
"sh", "-c", command);
return result.getStdout();
} catch (Exception e) {
return null;
}
}
}

7. GitHub Actions Security Workflow

security-compliance.yml

name: "Security Compliance Scan"
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
schedule:
- cron: '0 6 * * *'  # Daily at 6 AM
jobs:
security-compliance:
name: Security Compliance Check
runs-on: ubuntu-latest
permissions:
security-events: write
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Build secure container
run: |
docker build \
--tag ${{ github.repository }}:${{ github.sha }} \
--file Dockerfile \
--build-arg BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \
--build-arg VERSION=${{ github.sha }} \
.
- name: Run CIS benchmark scan
uses: docker://anchore/container-scan:latest
with:
image: ${{ github.repository }}:${{ github.sha }}
fail-build: true
severity-cutoff: high
- name: Run Docker Bench Security
run: |
docker run --rm --net host --pid host --userns host --cap-add audit_control \
-v /var/lib:/var/lib \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /etc:/etc --label docker_bench_security \
docker.io/docker/docker-bench-security:latest
- name: Run Hadolint for Dockerfile security
uses: hadolint/[email protected]
with:
dockerfile: Dockerfile
failure-threshold: warning
- name: Check for secrets in code
uses: gitleaks/gitleaks-action@v2
with:
config-path: .gitleaks.toml
- name: Generate Software Bill of Materials (SBOM)
uses: anchore/sbom-action@v0
with:
image: ${{ github.repository }}:${{ github.sha }}
format: cyclonedx-json
- name: Upload security artifacts
uses: actions/upload-artifact@v4
with:
name: security-artifacts
path: |
sbom-*.json
gitleaks-report.json
retention-days: 90

8. Security Monitoring and Reporting

Security Dashboard Configuration

# security-dashboard.yml
dashboard:
name: "Container Security Dashboard"
version: "1.0"
metrics:
- name: "critical_vulnerabilities"
query: "vulnerabilities{severity='critical'}"
threshold: 0
alert: true
- name: "high_vulnerabilities" 
query: "vulnerabilities{severity='high'}"
threshold: 5
alert: true
- name: "image_compliance"
query: "policy_violations_total"
threshold: 0
alert: true
alerts:
- name: "Critical Vulnerability Detected"
condition: "critical_vulnerabilities > 0"
severity: "critical"
description: "Critical security vulnerability found in container image"
- name: "High Vulnerability Threshold Exceeded"
condition: "high_vulnerabilities > 5"
severity: "high"
description: "Too many high severity vulnerabilities"
reports:
- type: "daily"
recipients: ["[email protected]"]
format: ["html", "pdf"]
- type: "weekly"
recipients: ["[email protected]"]
format: ["html"]

Security Report Generator

/**
* Security report generator for Anchore scan results.
*/
@Component
public class SecurityReportGenerator {
private final ObjectMapper objectMapper = new ObjectMapper();
/**
* Generate comprehensive security report.
*/
public SecurityReport generateReport(File scanResults) throws IOException {
JsonNode results = objectMapper.readTree(scanResults);
SecurityReport report = new SecurityReport();
report.setScanTimestamp(LocalDateTime.now());
report.setImageName(results.path("image").path("name").asText());
report.setImageDigest(results.path("image").path("digest").asText());
// Analyze vulnerabilities
analyzeVulnerabilities(results, report);
// Analyze policy compliance
analyzePolicyCompliance(results, report);
// Generate recommendations
generateRecommendations(report);
return report;
}
private void analyzeVulnerabilities(JsonNode results, SecurityReport report) {
JsonNode vulnerabilities = results.path("vulnerabilities");
VulnerabilitySummary summary = new VulnerabilitySummary();
for (JsonNode vuln : vulnerabilities) {
String severity = vuln.path("severity").asText();
switch (severity) {
case "Critical":
summary.incrementCritical();
break;
case "High":
summary.incrementHigh();
break;
case "Medium":
summary.incrementMedium();
break;
case "Low":
summary.incrementLow();
break;
}
}
report.setVulnerabilitySummary(summary);
// Calculate risk score
double riskScore = calculateRiskScore(summary);
report.setRiskScore(riskScore);
}
private void analyzePolicyCompliance(JsonNode results, SecurityReport report) {
JsonNode policyResults = results.path("policy_results");
ComplianceSummary compliance = new ComplianceSummary();
for (JsonNode policy : policyResults) {
String status = policy.path("status").asText();
if ("FAIL".equals(status)) {
compliance.incrementFailures();
} else if ("WARN".equals(status)) {
compliance.incrementWarnings();
} else {
compliance.incrementPasses();
}
}
report.setComplianceSummary(compliance);
}
private void generateRecommendations(SecurityReport report) {
List<String> recommendations = new ArrayList<>();
VulnerabilitySummary vulnSummary = report.getVulnerabilitySummary();
if (vulnSummary.getCriticalCount() > 0) {
recommendations.add("Immediate action required: Fix critical vulnerabilities");
}
if (vulnSummary.getHighCount() > 5) {
recommendations.add("Consider updating base image to reduce high severity vulnerabilities");
}
if (report.getComplianceSummary().getFailures() > 0) {
recommendations.add("Address policy compliance failures before deployment");
}
report.setRecommendations(recommendations);
}
private double calculateRiskScore(VulnerabilitySummary summary) {
return (summary.getCriticalCount() * 10.0) +
(summary.getHighCount() * 5.0) +
(summary.getMediumCount() * 2.0) +
(summary.getLowCount() * 0.5);
}
}
// Supporting classes
@Data
class SecurityReport {
private LocalDateTime scanTimestamp;
private String imageName;
private String imageDigest;
private VulnerabilitySummary vulnerabilitySummary;
private ComplianceSummary complianceSummary;
private double riskScore;
private List<String> recommendations;
}
@Data
class VulnerabilitySummary {
private int criticalCount = 0;
private int highCount = 0;
private int mediumCount = 0;
private int lowCount = 0;
public void incrementCritical() { criticalCount++; }
public void incrementHigh() { highCount++; }
public void incrementMedium() { mediumCount++; }
public void incrementLow() { lowCount++; }
}
@Data 
class ComplianceSummary {
private int passes = 0;
private int warnings = 0;
private int failures = 0;
public void incrementPasses() { passes++; }
public void incrementWarnings() { warnings++; }
public void incrementFailures() { failures++; }
}

9. Continuous Security Monitoring

real-time-security-monitor.yml

name: "Real-time Security Monitor"
on:
schedule:
- cron: '*/5 * * * *'  # Every 5 minutes
jobs:
monitor-running-containers:
name: Monitor Running Containers
runs-on: ubuntu-latest
steps:
- name: Check running containers
run: |
# Monitor for unauthorized containers
docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}"
# Check for privileged containers
docker ps --filter "label=privileged" --format "{{.Names}}"
# Monitor resource usage
docker stats --no-stream
- name: Scan for new vulnerabilities
run: |
# Check for updated vulnerability databases
curl -s https://ci-tools.anchore.io/inline_scan-v0.10.0 > inline_scan.sh
chmod +x inline_scan.sh
# Rescan latest image
./inline_scan.sh -r "myapp:latest" -t 300
- name: Alert on security events
if: failure()
uses: 8398a7/action-slack@v3
with:
status: failure
text: Security monitoring detected issues
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_SECURITY_WEBHOOK }}

Best Practices

1. Secure Base Images

# Use minimal, verified base images
FROM eclipse-temurin:17-jre-jammy
# Regularly update base images
# Use specific versions, not latest
# Verify image signatures

2. Runtime Security

// Container-aware security configuration
@Configuration
public class ContainerSecurityConfig {
@Bean
public SecurityConfigurer containerSecurity() {
return new SecurityConfigurer() {
@Override
public void configure(HttpSecurity http) throws Exception {
http.headers().httpStrictTransportSecurity()
.maxAgeInSeconds(31536000)
.includeSubDomains(true);
}
};
}
}

3. Continuous Vulnerability Management

# vulnerability-management.yml
vulnerability_management:
scan_frequency: "daily"
severity_threshold: "high"
auto_remediation: true
manual_review_required:
- "critical"
- "new_cves"
reporting:
channels:
- "security-team"
- "devops"
formats:
- "sarif"
- "html"
- "json"

Conclusion

This comprehensive Anchore Inline Scan implementation provides:

  • Secure container images with security-focused Dockerfiles
  • Automated vulnerability scanning integrated into CI/CD
  • Policy enforcement with customizable security gates
  • Comprehensive reporting with actionable insights
  • Continuous monitoring for runtime security

Key benefits:

  • Early vulnerability detection in development pipeline
  • Compliance assurance through policy enforcement
  • Risk reduction through continuous security monitoring
  • Developer empowerment with immediate security feedback
  • Audit readiness with comprehensive security reports

The setup enables organizations to maintain secure containerized Java applications while accelerating development through automated security controls and continuous compliance monitoring.

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.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper