google-site-verification: google61fe8ba583a51912.html
Harbor Vulnerability Scan in Java: Comprehensive Container Security Integration

Harbor provides enterprise-grade container registry with built-in vulnerability scanning using Trivy, Clair, or other scanners. This guide covers integrating Harbor vulnerability scanning into Java applications and CI/CD pipelines.


Setup and Dependencies

1. Maven Dependencies
<!-- pom.xml --> <properties> <jib-maven-plugin.version>3.4.0</jib-maven-plugin.version> <docker-java.version>3.3.4</docker-java.version> <spring-boot.version>3.1.0</spring-boot.version> </properties> <dependencies> <!-- Spring Boot --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>${spring-boot.version}</version> </dependency> <!-- Docker Java API --> <dependency> <groupId>com.github.docker-java</groupId> <artifactId>docker-java</artifactId> <version>${docker-java.version}</version> </dependency> <!-- HTTP Client for Harbor API --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> <version>${spring-boot.version}</version> </dependency> </dependencies> <build> <plugins> <!-- Jib for 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</image> </from> <to> <image>harbor.example.com/my-project/${project.artifactId}:${project.version}</image> <auth> <username>${harbor.username}</username> <password>${harbor.password}</password> </auth> </to> <container> <ports> <port>8080</port> </ports> <environment> <SPRING_PROFILES_ACTIVE>prod</SPRING_PROFILES_ACTIVE> </environment> </container> </configuration> </plugin> </plugins> </build>
2. Dockerfile for Multi-Stage Build
# Multi-stage Dockerfile FROM eclipse-temurin:17-jdk as builder WORKDIR /app COPY . . RUN ./mvnw clean package -DskipTests FROM eclipse-temurin:17-jre as runtime RUN addgroup --system --gid 1000 appuser && \ adduser --system --uid 1000 --gid 1000 appuser USER appuser WORKDIR /app # Copy the application JAR COPY --from=builder /app/target/*.jar app.jar # Security hardening RUN java -Djarmode=layertools -jar app.jar extract # Create non-root user and set permissions USER root RUN chown -R appuser:appuser /app USER appuser # Health check HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD curl -f http://localhost:8080/actuator/health || exit 1 EXPOSE 8080 ENTRYPOINT ["java", "-jar", "/app/app.jar"]
3. Environment Setup
#!/bin/bash # setup-harbor.sh export HARBOR_URL=https://harbor.example.com export HARBOR_USERNAME=admin export HARBOR_PASSWORD=password export HARBOR_PROJECT=my-project export HARBOR_SCANNER=trivy # Configure Docker to use Harbor docker login $HARBOR_URL -u $HARBOR_USERNAME -p $HARBOR_PASSWORD echo "Harbor environment configured"

Harbor API Integration

1. Harbor Client Service
package com.example.security.harbor; import org.springframework.http.*; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import org.springframework.web.reactive.function.client.WebClient; import reactor.core.publisher.Mono; import java.util.*; @Service public class HarborClientService { private final String HARBOR_BASE_URL; private final WebClient webClient; private final RestTemplate restTemplate; public HarborClientService(@Value("${harbor.url}") String harborUrl) { this.HARBOR_BASE_URL = harborUrl; this.webClient = WebClient.builder() .baseUrl(harborUrl) .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .build(); this.restTemplate = new RestTemplate(); } public HarborProject createProject(String projectName, String credentials) { String url = HARBOR_BASE_URL + "/api/v2.0/projects"; HarborProject project = new HarborProject(); project.setProject_name(projectName); project.setMetadata(new HashMap<>()); project.getMetadata().put("auto_scan", "true"); project.getMetadata().put("enable_content_trust", "true"); project.getMetadata().put("prevent_vul", "true"); project.getMetadata().put("severity", "high"); project.getMetadata().put("reuse_sys_cve_allowlist", "true"); HttpHeaders headers = createHeaders(credentials); HttpEntity<HarborProject> entity = new HttpEntity<>(project, headers); ResponseEntity<HarborProject> response = restTemplate.exchange( url, HttpMethod.POST, entity, HarborProject.class); return response.getBody(); } public List<HarborRepository> getRepositories(String projectName, String credentials) { String url = String.format("%s/api/v2.0/projects/%s/repositories", HARBOR_BASE_URL, projectName); HttpHeaders headers = createHeaders(credentials); HttpEntity<String> entity = new HttpEntity<>(headers); ResponseEntity<HarborRepository[]> response = restTemplate.exchange( url, HttpMethod.GET, entity, HarborRepository[].class); return Arrays.asList(response.getBody()); } public HarborArtifact getArtifact(String projectName, String repositoryName, String reference, String credentials) { String url = String.format("%s/api/v2.0/projects/%s/repositories/%s/artifacts/%s", HARBOR_BASE_URL, projectName, repositoryName, reference); HttpHeaders headers = createHeaders(credentials); HttpEntity<String> entity = new HttpEntity<>(headers); ResponseEntity<HarborArtifact> response = restTemplate.exchange( url, HttpMethod.GET, entity, HarborArtifact.class); return response.getBody(); } public HarborScanReport getVulnerabilityScanReport(String projectName, String repositoryName, String reference, String credentials) { String url = String.format("%s/api/v2.0/projects/%s/repositories/%s/artifacts/%s/additions/vulnerabilities", HARBOR_BASE_URL, projectName, repositoryName, reference); HttpHeaders headers = createHeaders(credentials); HttpEntity<String> entity = new HttpEntity<>(headers); ResponseEntity<HarborScanReport> response = restTemplate.exchange( url, HttpMethod.GET, entity, HarborScanReport.class); return response.getBody(); } public Mono<Void> triggerImageScan(String projectName, String repositoryName, String reference, String credentials) { String url = String.format("%s/api/v2.0/projects/%s/repositories/%s/artifacts/%s/scan", HARBOR_BASE_URL, projectName, repositoryName, reference); return webClient.post() .uri(url) .headers(headers -> headers.setBasicAuth(credentials)) .retrieve() .bodyToMono(Void.class); } public HarborScanOverview getScanOverview(String projectName, String repositoryName, String reference, String credentials) { String url = String.format("%s/api/v2.0/projects/%s/repositories/%s/artifacts/%s?page=1&page_size=10&with_tag=true&with_label=false&with_scan_overview=true&with_signature=false&with_immutable_status=false", HARBOR_BASE_URL, projectName, repositoryName, reference); HttpHeaders headers = createHeaders(credentials); HttpEntity<String> entity = new HttpEntity<>(headers); ResponseEntity<HarborArtifact> response = restTemplate.exchange( url, HttpMethod.GET, entity, HarborArtifact.class); return response.getBody().getScan_overview(); } public List<HarborWebhook> getWebhooks(String projectName, String credentials) { String url = String.format("%s/api/v2.0/projects/%s/webhook/policies", HARBOR_BASE_URL, projectName); HttpHeaders headers = createHeaders(credentials); HttpEntity<String> entity = new HttpEntity<>(headers); ResponseEntity<HarborWebhook[]> response = restTemplate.exchange( url, HttpMethod.GET, entity, HarborWebhook[].class); return Arrays.asList(response.getBody()); } public HarborWebhook createWebhook(String projectName, HarborWebhook webhook, String credentials) { String url = String.format("%s/api/v2.0/projects/%s/webhook/policies", HARBOR_BASE_URL, projectName); HttpHeaders headers = createHeaders(credentials); HttpEntity<HarborWebhook> entity = new HttpEntity<>(webhook, headers); ResponseEntity<HarborWebhook> response = restTemplate.exchange( url, HttpMethod.POST, entity, HarborWebhook.class); return response.getBody(); } private HttpHeaders createHeaders(String credentials) { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); if (credentials != null && !credentials.isEmpty()) { String auth = "Basic " + Base64.getEncoder() .encodeToString(credentials.getBytes()); headers.set("Authorization", auth); } return headers; } // DTO classes for Harbor API public static class HarborProject { private String project_name; private Map<String, String> metadata; public String getProject_name() { return project_name; } public void setProject_name(String project_name) { this.project_name = project_name; } public Map<String, String> getMetadata() { return metadata; } public void setMetadata(Map<String, String> metadata) { this.metadata = metadata; } } public static class HarborRepository { private String name; private int artifact_count; private String creation_time; private String update_time; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getArtifact_count() { return artifact_count; } public void setArtifact_count(int artifact_count) { this.artifact_count = artifact_count; } public String getCreation_time() { return creation_time; } public void setCreation_time(String creation_time) { this.creation_time = creation_time; } public String getUpdate_time() { return update_time; } public void setUpdate_time(String update_time) { this.update_time = update_time; } } public static class HarborArtifact { private String digest; private String size; private String push_time; private List<HarborTag> tags; private HarborScanOverview scan_overview; public String getDigest() { return digest; } public void setDigest(String digest) { this.digest = digest; } public String getSize() { return size; } public void setSize(String size) { this.size = size; } public String getPush_time() { return push_time; } public void setPush_time(String push_time) { this.push_time = push_time; } public List<HarborTag> getTags() { return tags; } public void setTags(List<HarborTag> tags) { this.tags = tags; } public HarborScanOverview getScan_overview() { return scan_overview; } public void setScan_overview(HarborScanOverview scan_overview) { this.scan_overview = scan_overview; } } public static class HarborTag { private String name; private String push_time; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPush_time() { return push_time; } public void setPush_time(String push_time) { this.push_time = push_time; } } public static class HarborScanOverview { private Map<String, HarborScanSummary> details; public Map<String, HarborScanSummary> getDetails() { return details; } public void setDetails(Map<String, HarborScanSummary> details) { this.details = details; } } public static class HarborScanSummary { private int total; private int fixable; private String summary; private String severity; private String scan_status; private String end_time; public int getTotal() { return total; } public void setTotal(int total) { this.total = total; } public int getFixable() { return fixable; } public void setFixable(int fixable) { this.fixable = fixable; } public String getSummary() { return summary; } public void setSummary(String summary) { this.summary = summary; } public String getSeverity() { return severity; } public void setSeverity(String severity) { this.severity = severity; } public String getScan_status() { return scan_status; } public void setScan_status(String scan_status) { this.scan_status = scan_status; } public String getEnd_time() { return end_time; } public void setEnd_time(String end_time) { this.end_time = end_time; } } public static class HarborScanReport { private List<HarborVulnerability> vulnerabilities; public List<HarborVulnerability> getVulnerabilities() { return vulnerabilities; } public void setVulnerabilities(List<HarborVulnerability> vulnerabilities) { this.vulnerabilities = vulnerabilities; } } public static class HarborVulnerability { private String id; private String package; private String version; private String fix_version; private String severity; private String description; private List<String> links; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getPackage() { return package; } public void setPackage(String package) { this.package = package; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public String getFix_version() { return fix_version; } public void setFix_version(String fix_version) { this.fix_version = fix_version; } public String getSeverity() { return severity; } public void setSeverity(String severity) { this.severity = severity; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public List<String> getLinks() { return links; } public void setLinks(List<String> links) { this.links = links; } } public static class HarborWebhook { private String name; private String description; private List<HarborWebhookTarget> targets; private List<String> event_types; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return name; } public void setDescription(String description) { this.description = description; } public List<HarborWebhookTarget> getTargets() { return targets; } public void setTargets(List<HarborWebhookTarget> targets) { this.targets = targets; } public List<String> getEvent_types() { return event_types; } public void setEvent_types(List<String> event_types) { this.event_types = event_types; } } public static class HarborWebhookTarget { private String type; private String address; private String auth_header; public String getType() { return type; } public void setType(String type) { this.type = type; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public String getAuth_header() { return auth_header; } public void setAuth_header(String auth_header) { this.auth_header = auth_header; } } }
2. Vulnerability Analysis Service
package com.example.security.harbor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import java.util.*; @Service public class VulnerabilityAnalysisService { private static final Logger logger = LoggerFactory.getLogger(VulnerabilityAnalysisService.class); private final HarborClientService harborClient; private final SecurityAlertService alertService; public VulnerabilityAnalysisService(HarborClientService harborClient, SecurityAlertService alertService) { this.harborClient = harborClient; this.alertService = alertService; } public VulnerabilityReport analyzeImage(String projectName, String repositoryName, String tag, String credentials) { VulnerabilityReport report = new VulnerabilityReport(); report.setImageName(repositoryName + ":" + tag); report.setAnalysisTime(new Date()); try { // Get scan overview HarborClientService.HarborScanOverview scanOverview = harborClient.getScanOverview(projectName, repositoryName, tag, credentials); if (scanOverview != null && scanOverview.getDetails() != null) { for (Map.Entry<String, HarborClientService.HarborScanSummary> entry : scanOverview.getDetails().entrySet()) { HarborClientService.HarborScanSummary summary = entry.getValue(); report.setTotalVulnerabilities(summary.getTotal()); report.setFixableVulnerabilities(summary.getFixable()); report.setHighestSeverity(summary.getSeverity()); report.setScanStatus(summary.getScan_status()); // Get detailed vulnerability report HarborClientService.HarborScanReport scanReport = harborClient.getVulnerabilityScanReport(projectName, repositoryName, tag, credentials); if (scanReport != null && scanReport.getVulnerabilities() != null) { processVulnerabilities(scanReport.getVulnerabilities(), report); } } } report.setSuccessful(true); } catch (Exception e) { logger.error("Failed to analyze image vulnerabilities: {}", repositoryName + ":" + tag, e); report.setSuccessful(false); report.setErrorMessage(e.getMessage()); } return report; } @Scheduled(cron = "0 0 6 * * ?") // Daily at 6 AM public void performDailyVulnerabilityScan() { logger.info("Starting daily vulnerability scan for all images"); String credentials = getHarborCredentials(); String projectName = System.getenv("HARBOR_PROJECT") ?? "default"; try { List<HarborClientService.HarborRepository> repositories = harborClient.getRepositories(projectName, credentials); for (HarborClientService.HarborRepository repo : repositories) { scanRepositoryImages(projectName, repo.getName(), credentials); } } catch (Exception e) { logger.error("Daily vulnerability scan failed", e); alertService.sendAlert("Daily vulnerability scan failed", e.getMessage()); } } public ComplianceCheckResult checkCompliance(String projectName, String repositoryName, String tag, CompliancePolicy policy, String credentials) { ComplianceCheckResult result = new ComplianceCheckResult(); result.setImageName(repositoryName + ":" + tag); result.setPolicyName(policy.getName()); VulnerabilityReport vulnReport = analyzeImage(projectName, repositoryName, tag, credentials); if (!vulnReport.isSuccessful()) { result.setCompliant(false); result.setReason("Failed to analyze vulnerabilities"); return result; } // Check against policy if (vulnReport.getTotalVulnerabilities() > policy.getMaxTotalVulnerabilities()) { result.setCompliant(false); result.setReason("Exceeds maximum total vulnerabilities"); result.setViolations(vulnReport.getCriticalVulnerabilities() + vulnReport.getHighVulnerabilities()); return result; } if (vulnReport.getCriticalVulnerabilities() > policy.getMaxCriticalVulnerabilities()) { result.setCompliant(false); result.setReason("Exceeds maximum critical vulnerabilities"); result.setViolations(vulnReport.getCriticalVulnerabilities()); return result; } if (vulnReport.getHighVulnerabilities() > policy.getMaxHighVulnerabilities()) { result.setCompliant(false); result.setReason("Exceeds maximum high vulnerabilities"); result.setViolations(vulnReport.getHighVulnerabilities()); return result; } result.setCompliant(true); result.setReason("Compliant with security policy"); return result; } private void scanRepositoryImages(String projectName, String repositoryName, String credentials) { try { // Get artifact details to find tags HarborClientService.HarborArtifact artifact = harborClient.getArtifact(projectName, repositoryName, "latest", credentials); if (artifact != null && artifact.getTags() != null) { for (HarborClientService.HarborTag tag : artifact.getTags()) { VulnerabilityReport report = analyzeImage(projectName, repositoryName, tag.getName(), credentials); if (!report.isSuccessful()) { logger.warn("Failed to scan {}:{}", repositoryName, tag.getName()); continue; } // Check if we need to alert if (report.getCriticalVulnerabilities() > 0 || report.getHighVulnerabilities() > 5) { alertService.sendAlert( "High vulnerability count in " + repositoryName + ":" + tag.getName(), String.format("Critical: %d, High: %d, Total: %d", report.getCriticalVulnerabilities(), report.getHighVulnerabilities(), report.getTotalVulnerabilities()) ); } } } } catch (Exception e) { logger.error("Failed to scan repository: {}", repositoryName, e); } } private void processVulnerabilities(List<HarborClientService.HarborVulnerability> vulnerabilities, VulnerabilityReport report) { int critical = 0; int high = 0; int medium = 0; int low = 0; int unknown = 0; List<Vulnerability> criticalVulns = new ArrayList<>(); List<Vulnerability> highVulns = new ArrayList<>(); for (HarborClientService.HarborVulnerability vuln : vulnerabilities) { switch (vuln.getSeverity().toLowerCase()) { case "critical": critical++; criticalVulns.add(convertToVulnerability(vuln)); break; case "high": high++; highVulns.add(convertToVulnerability(vuln)); break; case "medium": medium++; break; case "low": low++; break; default: unknown++; break; } } report.setCriticalVulnerabilities(critical); report.setHighVulnerabilities(high); report.setMediumVulnerabilities(medium); report.setLowVulnerabilities(low); report.setUnknownVulnerabilities(unknown); report.setCriticalVulnerabilityList(criticalVulns); report.setHighVulnerabilityList(highVulns); } private Vulnerability convertToVulnerability(HarborClientService.HarborVulnerability harborVuln) { Vulnerability vuln = new Vulnerability(); vuln.setId(harborVuln.getId()); vuln.setPackageName(harborVuln.getPackage()); vuln.setVersion(harborVuln.getVersion()); vuln.setFixedVersion(harborVuln.getFix_version()); vuln.setSeverity(harborVuln.getSeverity()); vuln.setDescription(harborVuln.getDescription()); return vuln; } private String getHarborCredentials() { String username = System.getenv("HARBOR_USERNAME"); String password = System.getenv("HARBOR_PASSWORD"); return username + ":" + password; } // DTO classes public static class VulnerabilityReport { private String imageName; private Date analysisTime; private boolean successful; private String errorMessage; private int totalVulnerabilities; private int fixableVulnerabilities; private int criticalVulnerabilities; private int highVulnerabilities; private int mediumVulnerabilities; private int lowVulnerabilities; private int unknownVulnerabilities; private String highestSeverity; private String scanStatus; private List<Vulnerability> criticalVulnerabilityList; private List<Vulnerability> highVulnerabilityList; // Getters and setters public String getImageName() { return imageName; } public void setImageName(String imageName) { this.imageName = imageName; } public Date getAnalysisTime() { return analysisTime; } public void setAnalysisTime(Date analysisTime) { this.analysisTime = analysisTime; } public boolean isSuccessful() { return successful; } public void setSuccessful(boolean successful) { this.successful = successful; } public String getErrorMessage() { return errorMessage; } public void setErrorMessage(String errorMessage) { this.errorMessage = errorMessage; } public int getTotalVulnerabilities() { return totalVulnerabilities; } public void setTotalVulnerabilities(int totalVulnerabilities) { this.totalVulnerabilities = totalVulnerabilities; } public int getFixableVulnerabilities() { return fixableVulnerabilities; } public void setFixableVulnerabilities(int fixableVulnerabilities) { this.fixableVulnerabilities = fixableVulnerabilities; } public int getCriticalVulnerabilities() { return criticalVulnerabilities; } public void setCriticalVulnerabilities(int criticalVulnerabilities) { this.criticalVulnerabilities = criticalVulnerabilities; } public int getHighVulnerabilities() { return highVulnerabilities; } public void setHighVulnerabilities(int highVulnerabilities) { this.highVulnerabilities = highVulnerabilities; } public int getMediumVulnerabilities() { return mediumVulnerabilities; } public void setMediumVulnerabilities(int mediumVulnerabilities) { this.mediumVulnerabilities = mediumVulnerabilities; } public int getLowVulnerabilities() { return lowVulnerabilities; } public void setLowVulnerabilities(int lowVulnerabilities) { this.lowVulnerabilities = lowVulnerabilities; } public int getUnknownVulnerabilities() { return unknownVulnerabilities; } public void setUnknownVulnerabilities(int unknownVulnerabilities) { this.unknownVulnerabilities = unknownVulnerabilities; } public String getHighestSeverity() { return highestSeverity; } public void setHighestSeverity(String highestSeverity) { this.highestSeverity = highestSeverity; } public String getScanStatus() { return scanStatus; } public void setScanStatus(String scanStatus) { this.scanStatus = scanStatus; } public List<Vulnerability> getCriticalVulnerabilityList() { return criticalVulnerabilityList; } public void setCriticalVulnerabilityList(List<Vulnerability> criticalVulnerabilityList) { this.criticalVulnerabilityList = criticalVulnerabilityList; } public List<Vulnerability> getHighVulnerabilityList() { return highVulnerabilityList; } public void setHighVulnerabilityList(List<Vulnerability> highVulnerabilityList) { this.highVulnerabilityList = highVulnerabilityList; } } public static class Vulnerability { private String id; private String packageName; private String version; private String fixedVersion; private String severity; private String description; // Getters and setters public String getId() { return id; } public void setId(String id) { this.id = id; } public String getPackageName() { return packageName; } public void setPackageName(String packageName) { this.packageName = packageName; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public String getFixedVersion() { return fixedVersion; } public void setFixedVersion(String fixedVersion) { this.fixedVersion = fixedVersion; } public String getSeverity() { return severity; } public void setSeverity(String severity) { this.severity = severity; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } } public static class CompliancePolicy { private String name; private int maxTotalVulnerabilities; private int maxCriticalVulnerabilities; private int maxHighVulnerabilities; private boolean allowUnknownSeverity; // Getters and setters public String getName() { return name; } public void setName(String name) { this.name = name; } public int getMaxTotalVulnerabilities() { return maxTotalVulnerabilities; } public void setMaxTotalVulnerabilities(int maxTotalVulnerabilities) { this.maxTotalVulnerabilities = maxTotalVulnerabilities; } public int getMaxCriticalVulnerabilities() { return maxCriticalVulnerabilities; } public void setMaxCriticalVulnerabilities(int maxCriticalVulnerabilities) { this.maxCriticalVulnerabilities = maxCriticalVulnerabilities; } public int getMaxHighVulnerabilities() { return maxHighVulnerabilities; } public void setMaxHighVulnerabilities(int maxHighVulnerabilities) { this.maxHighVulnerabilities = maxHighVulnerabilities; } public boolean isAllowUnknownSeverity() { return allowUnknownSeverity; } public void setAllowUnknownSeverity(boolean allowUnknownSeverity) { this.allowUnknownSeverity = allowUnknownSeverity; } } public static class ComplianceCheckResult { private String imageName; private String policyName; private boolean compliant; private String reason; private int violations; // Getters and setters public String getImageName() { return imageName; } public void setImageName(String imageName) { this.imageName = imageName; } public String getPolicyName() { return policyName; } public void setPolicyName(String policyName) { this.policyName = policyName; } public boolean isCompliant() { return compliant; } public void setCompliant(boolean compliant) { this.compliant = compliant; } public String getReason() { return reason; } public void setReason(String reason) { this.reason = reason; } public int getViolations() { return violations; } public void setViolations(int violations) { this.violations = violations; } } }
3. Security Alert Service
package com.example.security.harbor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.mail.SimpleMailMessage; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import java.util.HashMap; import java.util.Map; @Service public class SecurityAlertService { private static final Logger logger = LoggerFactory.getLogger(SecurityAlertService.class); private final JavaMailSender mailSender; private final RestTemplate restTemplate; @Value("${security.alerts.email.recipient:[email protected]}") private String alertEmailRecipient; @Value("${security.alerts.slack.webhook:}") private String slackWebhookUrl; @Value("${security.alerts.teams.webhook:}") private String teamsWebhookUrl; public SecurityAlertService(JavaMailSender mailSender, RestTemplate restTemplate) { this.mailSender = mailSender; this.restTemplate = restTemplate; } public void sendAlert(String subject, String message) { sendEmailAlert(subject, message); sendSlackAlert(subject, message); sendTeamsAlert(subject, message); logSecurityAlert(subject, message); } public void sendCriticalAlert(String subject, String message) { String criticalSubject = "🚨 CRITICAL: " + subject; sendEmailAlert(criticalSubject, message); sendSlackAlert(criticalSubject, message); sendTeamsAlert(criticalSubject, message); logSecurityAlert(criticalSubject, message); } private void sendEmailAlert(String subject, String message) { try { SimpleMailMessage mailMessage = new SimpleMailMessage(); mailMessage.setTo(alertEmailRecipient); mailMessage.setSubject("[Harbor Security] " + subject); mailMessage.setText(message); mailSender.send(mailMessage); logger.info("Security alert email sent: {}", subject); } catch (Exception e) { logger.error("Failed to send security alert email", e); } } private void sendSlackAlert(String subject, String message) { if (slackWebhookUrl == null || slackWebhookUrl.isEmpty()) { return; } try { Map<String, Object> slackMessage = new HashMap<>(); slackMessage.put("text", "🔒 *Harbor Security Alert*\n*" + subject + "*\n" + message); restTemplate.postForEntity(slackWebhookUrl, slackMessage, String.class); logger.info("Security alert sent to Slack: {}", subject); } catch (Exception e) { logger.error("Failed to send security alert to Slack", e); } } private void sendTeamsAlert(String subject, String message) { if (teamsWebhookUrl == null || teamsWebhookUrl.isEmpty()) { return; } try { Map<String, Object> teamsMessage = new HashMap<>(); teamsMessage.put("@type", "MessageCard"); teamsMessage.put("@context", "http://schema.org/extensions"); teamsMessage.put("themeColor", "FF0000"); teamsMessage.put("summary", subject); teamsMessage.put("sections", new Object[]{ Map.of( "activityTitle", "Harbor Security Alert", "activitySubtitle", subject, "text", message, "markdown", true ) }); restTemplate.postForEntity(teamsWebhookUrl, teamsMessage, String.class); logger.info("Security alert sent to Teams: {}", subject); } catch (Exception e) { logger.error("Failed to send security alert to Teams", e); } } private void logSecurityAlert(String subject, String message) { logger.warn("SECURITY ALERT - {}: {}", subject, message); } }

CI/CD Integration

1. GitHub Actions Workflow
# .github/workflows/harbor-scan.yml name: Harbor Container Security Scan on: push: branches: [ main, develop ] tags: [ 'v*' ] pull_request: branches: [ main ] schedule: - cron: '0 6 * * 1' # Weekly Monday at 6 AM jobs: build-and-scan: name: Build and Security Scan runs-on: ubuntu-latest permissions: contents: read packages: 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 with Maven run: mvn clean package -DskipTests - name: Build Docker image run: | docker build -t harbor.example.com/my-project/app:${{ github.sha }} . docker tag harbor.example.com/my-project/app:${{ github.sha }} harbor.example.com/my-project/app:latest - name: Log in to Harbor run: | docker login harbor.example.com -u ${{ secrets.HARBOR_USERNAME }} -p ${{ secrets.HARBOR_PASSWORD }} - name: Push to Harbor run: | docker push harbor.example.com/my-project/app:${{ github.sha }} docker push harbor.example.com/my-project/app:latest - name: Trigger Harbor Scan run: | curl -X POST \ -u ${{ secrets.HARBOR_USERNAME }}:${{ secrets.HARBOR_PASSWORD }} \ "https://harbor.example.com/api/v2.0/projects/my-project/repositories/app/artifacts/${{ github.sha }}/scan" - name: Wait for Scan Completion run: | sleep 30 # Wait for scan to complete curl -X GET \ -u ${{ secrets.HARBOR_USERNAME }}:${{ secrets.HARBOR_PASSWORD }} \ "https://harbor.example.com/api/v2.0/projects/my-project/repositories/app/artifacts/${{ github.sha }}?with_scan_overview=true" > scan-result.json - name: Check Scan Results run: | TOTAL_VULNS=$(jq '.scan_overview.details."application/vnd.security.vulnerability.report; version=1.1".summary.total' scan-result.json) CRITICAL_VULNS=$(jq '.scan_overview.details."application/vnd.security.vulnerability.report; version=1.1".summary.severity' scan-result.json) if [ "$TOTAL_VULNS" -gt "10" ] || [ "$CRITICAL_VULNS" = "Critical" ]; then echo "❌ Security vulnerabilities detected" exit 1 else echo "✅ Security scan passed" fi - name: Upload Scan Report uses: actions/upload-artifact@v3 with: name: security-scan-report path: scan-result.json
2. Jenkins Pipeline
// Jenkinsfile pipeline { agent any environment { HARBOR_URL = 'https://harbor.example.com' HARBOR_PROJECT = 'my-project' HARBOR_CREDENTIALS = credentials('harbor-credentials') } stages { stage('Checkout') { steps { checkout scm } } stage('Build') { steps { sh 'mvn clean package -DskipTests' } } stage('Build Docker Image') { steps { script { docker.build("${HARBOR_URL}/${HARBOR_PROJECT}/app:${env.BUILD_ID}") } } } stage('Push to Harbor') { steps { script { docker.withRegistry(HARBOR_URL, 'harbor-credentials') { docker.image("${HARBOR_URL}/${HARBOR_PROJECT}/app:${env.BUILD_ID}").push() docker.image("${HARBOR_URL}/${HARBOR_PROJECT}/app:${env.BUILD_ID}").push('latest') } } } } stage('Security Scan') { steps { script { // Trigger Harbor scan sh """ curl -X POST \ -u ${HARBOR_CREDENTIALS} \ "${HARBOR_URL}/api/v2.0/projects/${HARBOR_PROJECT}/repositories/app/artifacts/${env.BUILD_ID}/scan" """ // Wait for scan completion sleep time: 30, unit: 'SECONDS' // Get scan results sh """ curl -X GET \ -u ${HARBOR_CREDENTIALS} \ "${HARBOR_URL}/api/v2.0/projects/${HARBOR_PROJECT}/repositories/app/artifacts/${env.BUILD_ID}?with_scan_overview=true" > scan-result.json """ // Parse and evaluate results def scanResult = readJSON file: 'scan-result.json' def scanSummary = scanResult.scan_overview.details.'application/vnd.security.vulnerability.report; version=1.1'.summary if (scanSummary.total > 10 || scanSummary.severity == 'Critical') { error "Security vulnerabilities detected: ${scanSummary.total} total, ${scanSummary.severity} severity" } } } } stage('Deploy') { when { expression { currentBuild.result == null || currentBuild.result == 'SUCCESS' } } steps { echo 'Deploying to production...' // Add deployment steps here } } } post { always { archiveArtifacts artifacts: 'scan-result.json', fingerprint: true } failure { emailext ( subject: "Security Scan Failed: ${env.JOB_NAME}", body: "Container security vulnerabilities detected in build ${env.BUILD_URL}", to: "[email protected]" ) } } }
3. Webhook Controller for Harbor Events
package com.example.security.harbor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.*; import java.util.Map; @RestController @RequestMapping("/api/webhooks/harbor") public class HarborWebhookController { private static final Logger logger = LoggerFactory.getLogger(HarborWebhookController.class); private final VulnerabilityAnalysisService vulnerabilityService; private final SecurityAlertService alertService; public HarborWebhookController(VulnerabilityAnalysisService vulnerabilityService, SecurityAlertService alertService) { this.vulnerabilityService = vulnerabilityService; this.alertService = alertService; } @PostMapping("/scan-completed") public void handleScanCompleted(@RequestBody HarborWebhookPayload payload) { logger.info("Received scan completed webhook for {}", payload.getEvent_data().getResources()[0].getResource_url()); try { // Extract image information from payload String resourceUrl = payload.getEvent_data().getResources()[0].getResource_url(); String[] parts = resourceUrl.split("/"); String projectName = parts[parts.length - 3]; String repositoryName = parts[parts.length - 2]; String tag = parts[parts.length - 1]; // Analyze the scan results String credentials = getHarborCredentials(); VulnerabilityAnalysisService.VulnerabilityReport report = vulnerabilityService.analyzeImage(projectName, repositoryName, tag, credentials); if (!report.isSuccessful()) { logger.error("Failed to analyze scan results for {}", resourceUrl); return; } // Check compliance VulnerabilityAnalysisService.CompliancePolicy policy = createDefaultPolicy(); VulnerabilityAnalysisService.ComplianceCheckResult compliance = vulnerabilityService.checkCompliance(projectName, repositoryName, tag, policy, credentials); if (!compliance.isCompliant()) { alertService.sendAlert( "Non-compliant image detected: " + resourceUrl, "Reason: " + compliance.getReason() + ", Violations: " + compliance.getViolations() ); } logger.info("Scan analysis completed for {}: {}", resourceUrl, compliance.isCompliant() ? "COMPLIANT" : "NON-COMPLIANT"); } catch (Exception e) { logger.error("Failed to process scan completed webhook", e); } } @PostMapping("/image-pushed") public void handleImagePushed(@RequestBody HarborWebhookPayload payload) { logger.info("Received image pushed webhook for {}", payload.getEvent_data().getResources()[0].getResource_url()); // Trigger automatic scan for newly pushed images String resourceUrl = payload.getEvent_data().getResources()[0].getResource_url(); logger.info("New image pushed: {}", resourceUrl); } @PostMapping("/image-deleted") public void handleImageDeleted(@RequestBody HarborWebhookPayload payload) { logger.info("Received image deleted webhook for {}", payload.getEvent_data().getResources()[0].getResource_url()); // Clean up any related records String resourceUrl = payload.getEvent_data().getResources()[0].getResource_url(); logger.info("Image deleted: {}", resourceUrl); } private String getHarborCredentials() { String username = System.getenv("HARBOR_USERNAME"); String password = System.getenv("HARBOR_PASSWORD"); return username + ":" + password; } private VulnerabilityAnalysisService.CompliancePolicy createDefaultPolicy() { VulnerabilityAnalysisService.CompliancePolicy policy = new VulnerabilityAnalysisService.CompliancePolicy(); policy.setName("default-policy"); policy.setMaxTotalVulnerabilities(50); policy.setMaxCriticalVulnerabilities(0); policy.setMaxHighVulnerabilities(5); policy.setAllowUnknownSeverity(false); return policy; } // Webhook payload DTO public static class HarborWebhookPayload { private String type; private String occur_at; private String operator; private EventData event_data; public String getType() { return type; } public void setType(String type) { this.type = type; } public String getOccur_at() { return occur_at; } public void setOccur_at(String occur_at) { this.occur_at = occur_at; } public String getOperator() { return operator; } public void setOperator(String operator) { this.operator = operator; } public EventData getEvent_data() { return event_data; } public void setEvent_data(EventData event_data) { this.event_data = event_data; } } public static class EventData { private Resource[] resources; private String repository; public Resource[] getResources() { return resources; } public void setResources(Resource[] resources) { this.resources = resources; } public String getRepository() { return repository; } public void setRepository(String repository) { this.repository = repository; } } public static class Resource { private String digest; private String tag; private String resource_url; public String getDigest() { return digest; } public void setDigest(String digest) { this.digest = digest; } public String getTag() { return tag; } public void setTag(String tag) { this.tag = tag; } public String getResource_url() { return resource_url; } public void setResource_url(String resource_url) { this.resource_url = resource_url; } } }

Best Practices

  1. Image Security:
  • Use minimal base images
  • Regular base image updates
  • Multi-stage builds to reduce attack surface
  • Non-root user execution
  1. Scanning Strategy:
  • Scan on every push
  • Regular scheduled scans
  • Block deployment on critical vulnerabilities
  • Automated fix suggestions
  1. Compliance:
  • Define clear security policies
  • Regular compliance audits
  • Automated policy enforcement
  • Executive reporting
  1. Integration:
  • Webhook-based automation
  • CI/CD pipeline integration
  • Real-time alerting
  • Centralized dashboard
# Useful Harbor CLI commands harbor-cli project create my-project --public=false harbor-cli repository list --project my-project harbor-cli artifact scan my-project/app:latest harbor-cli vulnerability list my-project/app:latest

Conclusion

Harbor Vulnerability Scanning for Java provides:

  • Comprehensive container security scanning
  • Integration with CI/CD pipelines
  • Policy-based compliance checking
  • Real-time monitoring and alerting
  • Centralized management of container images

By implementing the patterns and configurations shown above, you can establish a robust container security practice, ensure compliance with security policies, identify and remediate vulnerabilities early, and maintain a secure software supply chain for your Java applications.

Secure Java Dependency Management, Vulnerability Scanning & Software Supply Chain Protection (SBOM, SCA, CI Security & License Compliance)

https://macronepal.com/blog/github-code-scanning-in-java-complete-guide/
Explains GitHub Code Scanning for Java using tools like CodeQL to automatically analyze source code and detect security vulnerabilities directly inside CI/CD pipelines before deployment.

https://macronepal.com/blog/license-compliance-in-java-comprehensive-guide/
Explains software license compliance in Java projects, ensuring dependencies follow legal requirements (MIT, Apache, GPL, etc.) and preventing license violations in enterprise software.

https://macronepal.com/blog/container-security-for-java-uncovering-vulnerabilities-with-grype/
Explains using Grype to scan Java container images and filesystems for known CVEs in OS packages and application dependencies to improve container security.

https://macronepal.com/blog/syft-sbom-generation-in-java-comprehensive-software-bill-of-materials-for-jvm-applications/
Explains using Syft to generate SBOMs (Software Bill of Materials) for Java applications, listing all dependencies, libraries, and components for supply chain transparency.

https://macronepal.com/blog/comprehensive-dependency-analysis-generating-and-scanning-sboms-with-trivy-for-java/
Explains using Trivy to generate SBOMs and scan Java dependencies and container images for vulnerabilities, integrating security checks into CI/CD pipelines.

https://macronepal.com/blog/dependabot-for-java-in-java/
Explains GitHub Dependabot for Java projects, which automatically detects vulnerable dependencies and creates pull requests to update them securely.

https://macronepal.com/blog/parasoft-jtest-in-java-comprehensive-guide-to-code-analysis-and-testing/
Explains Parasoft Jtest, a static analysis and testing tool for Java that helps detect bugs, security issues, and code quality problems early in development.

https://macronepal.com/blog/snyk-open-source-in-java-comprehensive-dependency-vulnerability-management-2/
Explains Snyk Open Source for Java, which continuously scans dependencies for vulnerabilities and provides automated fix suggestions and monitoring.

https://macronepal.com/blog/owasp-dependency-check-in-java-complete-vulnerability-scanning-guide/
Explains OWASP Dependency-Check, which scans Java dependencies against the National Vulnerability Database (NVD) to detect known security vulnerabilities.

https://macronepal.com/blog/securing-your-dependencies-a-java-developers-guide-to-whitesource-mend-bolt/
Explains Mend (WhiteSource) Bolt for Java, a dependency management and SCA tool that provides vulnerability detection, license compliance, and security policy enforcement in enterprise environments.

Leave a Reply

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


Macro Nepal Helper