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.