Quay.io Security Features in Java

Overview

Quay.io is a container registry with robust security features including vulnerability scanning, image signing, access control, and security policies. This guide covers integrating Quay.io security features into Java applications.

Core Security Features

  • Vulnerability Scanning: Built-in security scanning
  • Image Signing: Content trust and verification
  • Access Control: RBAC and repository permissions
  • Security Notifications: Webhooks and alerts
  • Robot Accounts: Automated access with limited permissions

Quay.io API Integration

1. Quay.io API Client

package com.example.quay.security;
import org.springframework.http.*;
import org.springframework.web.client.RestTemplate;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.*;
@Component
public class QuayApiClient {
private final RestTemplate restTemplate;
private final ObjectMapper objectMapper;
private final String quayToken;
private final String baseUrl;
public QuayApiClient(@Value("${quay.token}") String quayToken,
@Value("${quay.base-url:https://quay.io}") String baseUrl) {
this.restTemplate = new RestTemplate();
this.objectMapper = new ObjectMapper();
this.quayToken = quayToken;
this.baseUrl = baseUrl;
}
// Repository Security Operations
public RepositorySecurityReport getRepositorySecurity(String namespace, String repository) {
HttpHeaders headers = createHeaders();
String url = String.format("%s/api/v1/repository/%s/%s", baseUrl, namespace, repository);
ResponseEntity<RepositorySecurityReport> response = restTemplate.exchange(
url, HttpMethod.GET, new HttpEntity<>(headers), RepositorySecurityReport.class);
return response.getBody();
}
public List<ImageVulnerability> getImageVulnerabilities(String namespace, String repository, String tag) {
HttpHeaders headers = createHeaders();
String url = String.format("%s/api/v1/repository/%s/%s/tag/%s/security", 
baseUrl, namespace, repository, tag);
ResponseEntity<ImageSecurityReport> response = restTemplate.exchange(
url, HttpMethod.GET, new HttpEntity<>(headers), ImageSecurityReport.class);
return response.getBody() != null ? response.getBody().getVulnerabilities() : Collections.emptyList();
}
public ScanStatus triggerImageScan(String namespace, String repository, String tag) {
HttpHeaders headers = createHeaders();
String url = String.format("%s/api/v1/repository/%s/%s/tag/%s/scan", 
baseUrl, namespace, repository, tag);
ResponseEntity<ScanStatus> response = restTemplate.exchange(
url, HttpMethod.POST, new HttpEntity<>(headers), ScanStatus.class);
return response.getBody();
}
// Robot Account Management
public RobotAccount createRobotAccount(String namespace, String robotName) {
HttpHeaders headers = createHeaders();
String url = String.format("%s/api/v1/organization/%s/robots/%s", 
baseUrl, namespace, robotName);
Map<String, Object> requestBody = new HashMap<>();
requestBody.put("description", "Robot account for automated deployments");
HttpEntity<Map<String, Object>> request = new HttpEntity<>(requestBody, headers);
ResponseEntity<RobotAccount> response = restTemplate.exchange(
url, HttpMethod.PUT, request, RobotAccount.class);
return response.getBody();
}
public void setRobotPermissions(String namespace, String robotName, 
String repository, String permission) {
HttpHeaders headers = createHeaders();
String url = String.format("%s/api/v1/repository/%s/%s/permissions/user/%s", 
baseUrl, namespace, repository, robotName);
Map<String, String> requestBody = new HashMap<>();
requestBody.put("role", permission); // read, write, admin
HttpEntity<Map<String, String>> request = new HttpEntity<>(requestBody, headers);
restTemplate.exchange(url, HttpMethod.PUT, request, Void.class);
}
// Security Notifications
public void createSecurityNotification(String namespace, String repository, 
SecurityNotificationConfig config) {
HttpHeaders headers = createHeaders();
String url = String.format("%s/api/v1/repository/%s/%s/notification/", 
baseUrl, namespace, repository);
HttpEntity<SecurityNotificationConfig> request = new HttpEntity<>(config, headers);
restTemplate.exchange(url, HttpMethod.POST, request, Void.class);
}
private HttpHeaders createHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + quayToken);
headers.set("Content-Type", "application/json");
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
return headers;
}
// Data Classes
public static class RepositorySecurityReport {
private String name;
private String namespace;
private boolean isPublic;
private SecurityScanStatus securityScanStatus;
private List<ImageTagSecurity> tags;
private VulnerabilitySummary vulnerabilitySummary;
// Getters and setters
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getNamespace() { return namespace; }
public void setNamespace(String namespace) { this.namespace = namespace; }
public boolean isPublic() { return isPublic; }
public void setPublic(boolean isPublic) { this.isPublic = isPublic; }
public SecurityScanStatus getSecurityScanStatus() { return securityScanStatus; }
public void setSecurityScanStatus(SecurityScanStatus securityScanStatus) { this.securityScanStatus = securityScanStatus; }
public List<ImageTagSecurity> getTags() { return tags; }
public void setTags(List<ImageTagSecurity> tags) { this.tags = tags; }
public VulnerabilitySummary getVulnerabilitySummary() { return vulnerabilitySummary; }
public void setVulnerabilitySummary(VulnerabilitySummary vulnerabilitySummary) { this.vulnerabilitySummary = vulnerabilitySummary; }
}
public static class ImageSecurityReport {
private List<ImageVulnerability> vulnerabilities;
private ScanStatus status;
private Date scannedDate;
// Getters and setters
public List<ImageVulnerability> getVulnerabilities() { return vulnerabilities; }
public void setVulnerabilities(List<ImageVulnerability> vulnerabilities) { this.vulnerabilities = vulnerabilities; }
public ScanStatus getStatus() { return status; }
public void setStatus(ScanStatus status) { this.status = status; }
public Date getScannedDate() { return scannedDate; }
public void setScannedDate(Date scannedDate) { this.scannedDate = scannedDate; }
}
public static class ImageVulnerability {
private String id;
private String severity;
private String packageName;
private String packageVersion;
private String fixedInVersion;
private String description;
private String link;
private List<String> references;
// Getters and setters
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getSeverity() { return severity; }
public void setSeverity(String severity) { this.severity = severity; }
public String getPackageName() { return packageName; }
public void setPackageName(String packageName) { this.packageName = packageName; }
public String getPackageVersion() { return packageVersion; }
public void setPackageVersion(String packageVersion) { this.packageVersion = packageVersion; }
public String getFixedInVersion() { return fixedInVersion; }
public void setFixedInVersion(String fixedInVersion) { this.fixedInVersion = fixedInVersion; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public String getLink() { return link; }
public void setLink(String link) { this.link = link; }
public List<String> getReferences() { return references; }
public void setReferences(List<String> references) { this.references = references; }
}
public static class SecurityScanStatus {
private String status; // scanned, scanning, failed, unsupported
private Date lastScanned;
private String scanResult;
// Getters and setters
}
public static class ImageTagSecurity {
private String name;
private VulnerabilitySummary vulnerabilitySummary;
private Date lastModified;
// Getters and setters
}
public static class VulnerabilitySummary {
private int critical;
private int high;
private int medium;
private int low;
private int unknown;
// Getters and setters
public int getCritical() { return critical; }
public void setCritical(int critical) { this.critical = critical; }
public int getHigh() { return high; }
public void setHigh(int high) { this.high = high; }
public int getMedium() { return medium; }
public void setMedium(int medium) { this.medium = medium; }
public int getLow() { return low; }
public void setLow(int low) { this.low = low; }
public int getUnknown() { return unknown; }
public void setUnknown(int unknown) { this.unknown = unknown; }
public int getTotal() {
return critical + high + medium + low + unknown;
}
}
public static class ScanStatus {
private String status;
private String message;
private Date startedAt;
private Date completedAt;
// Getters and setters
}
public static class RobotAccount {
private String name;
private String token;
private String description;
private Date created;
private List<RobotPermission> permissions;
// Getters and setters
}
public static class RobotPermission {
private String repository;
private String permission;
// Getters and setters
}
public static class SecurityNotificationConfig {
private String event; // vuln_found, repo_updated, build_failed
private String method; // webhook, email, slack
private Map<String, String> config;
private String title;
private boolean enabled;
// Getters and setters
}
}

Security Scanning Integration

2. Vulnerability Scanner Service

package com.example.quay.security;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.stream.Collectors;
@Service
public class VulnerabilityScannerService {
private final QuayApiClient quayClient;
public VulnerabilityScannerService(QuayApiClient quayClient) {
this.quayClient = quayClient;
}
public SecurityComplianceReport checkImageCompliance(String namespace, String repository, String tag) {
List<QuayApiClient.ImageVulnerability> vulnerabilities = 
quayClient.getImageVulnerabilities(namespace, repository, tag);
SecurityComplianceReport report = new SecurityComplianceReport();
report.setNamespace(namespace);
report.setRepository(repository);
report.setTag(tag);
report.setScanDate(new Date());
report.setVulnerabilities(vulnerabilities);
// Analyze vulnerabilities
analyzeVulnerabilities(vulnerabilities, report);
// Check compliance against policies
checkCompliancePolicies(report);
return report;
}
private void analyzeVulnerabilities(List<QuayApiClient.ImageVulnerability> vulnerabilities, 
SecurityComplianceReport report) {
Map<String, Long> severityCounts = vulnerabilities.stream()
.collect(Collectors.groupingBy(
QuayApiClient.ImageVulnerability::getSeverity,
Collectors.counting()
));
report.setCriticalCount(severityCounts.getOrDefault("Critical", 0L).intValue());
report.setHighCount(severityCounts.getOrDefault("High", 0L).intValue());
report.setMediumCount(severityCounts.getOrDefault("Medium", 0L).intValue());
report.setLowCount(severityCounts.getOrDefault("Low", 0L).intValue());
// Find fixable vulnerabilities
List<QuayApiClient.ImageVulnerability> fixable = vulnerabilities.stream()
.filter(v -> v.getFixedInVersion() != null && !v.getFixedInVersion().isEmpty())
.collect(Collectors.toList());
report.setFixableVulnerabilities(fixable);
// Group by package
Map<String, List<QuayApiClient.ImageVulnerability>> byPackage = vulnerabilities.stream()
.collect(Collectors.groupingBy(QuayApiClient.ImageVulnerability::getPackageName));
report.setVulnerabilitiesByPackage(byPackage);
}
private void checkCompliancePolicies(SecurityComplianceReport report) {
SecurityPolicy policy = getSecurityPolicy();
boolean compliant = true;
List<String> violations = new ArrayList<>();
if (report.getCriticalCount() > policy.getMaxCritical()) {
compliant = false;
violations.add(String.format("Critical vulnerabilities exceed limit: %d > %d", 
report.getCriticalCount(), policy.getMaxCritical()));
}
if (report.getHighCount() > policy.getMaxHigh()) {
compliant = false;
violations.add(String.format("High vulnerabilities exceed limit: %d > %d", 
report.getHighCount(), policy.getMaxHigh()));
}
if (report.getTotalVulnerabilities() > policy.getMaxTotal()) {
compliant = false;
violations.add(String.format("Total vulnerabilities exceed limit: %d > %d", 
report.getTotalVulnerabilities(), policy.getMaxTotal()));
}
report.setCompliant(compliant);
report.setPolicyViolations(violations);
}
public List<String> generateRemediationSteps(SecurityComplianceReport report) {
List<String> steps = new ArrayList<>();
if (!report.isCompliant()) {
steps.add("Security compliance check failed. Remediation required:");
// Base image upgrade
steps.add("1. Consider upgrading base image to a more secure version");
// Package updates
Map<String, String> packageUpdates = new HashMap<>();
for (QuayApiClient.ImageVulnerability vuln : report.getFixableVulnerabilities()) {
if (vuln.getFixedInVersion() != null) {
packageUpdates.put(vuln.getPackageName(), vuln.getFixedInVersion());
}
}
if (!packageUpdates.isEmpty()) {
steps.add("2. Update vulnerable packages:");
packageUpdates.forEach((pkg, version) -> 
steps.add("   - " + pkg + " → " + version));
}
// Security patches
steps.add("3. Apply security patches for unfixable vulnerabilities");
steps.add("4. Rescan image after remediation");
} else {
steps.add("Image is compliant with security policies");
}
return steps;
}
public ScanTrend analyzeScanTrends(String namespace, String repository, int days) {
// Implementation to analyze vulnerability trends over time
// This would fetch historical scan data and analyze patterns
return new ScanTrend();
}
private SecurityPolicy getSecurityPolicy() {
// Load from configuration
SecurityPolicy policy = new SecurityPolicy();
policy.setMaxCritical(0);
policy.setMaxHigh(2);
policy.setMaxMedium(10);
policy.setMaxTotal(20);
return policy;
}
// Data Classes
public static class SecurityComplianceReport {
private String namespace;
private String repository;
private String tag;
private Date scanDate;
private List<QuayApiClient.ImageVulnerability> vulnerabilities;
private int criticalCount;
private int highCount;
private int mediumCount;
private int lowCount;
private List<QuayApiClient.ImageVulnerability> fixableVulnerabilities;
private Map<String, List<QuayApiClient.ImageVulnerability>> vulnerabilitiesByPackage;
private boolean compliant;
private List<String> policyViolations;
// Getters and setters
public String getNamespace() { return namespace; }
public void setNamespace(String namespace) { this.namespace = namespace; }
public String getRepository() { return repository; }
public void setRepository(String repository) { this.repository = repository; }
public String getTag() { return tag; }
public void setTag(String tag) { this.tag = tag; }
public Date getScanDate() { return scanDate; }
public void setScanDate(Date scanDate) { this.scanDate = scanDate; }
public List<QuayApiClient.ImageVulnerability> getVulnerabilities() { return vulnerabilities; }
public void setVulnerabilities(List<QuayApiClient.ImageVulnerability> vulnerabilities) { this.vulnerabilities = vulnerabilities; }
public int getCriticalCount() { return criticalCount; }
public void setCriticalCount(int criticalCount) { this.criticalCount = criticalCount; }
public int getHighCount() { return highCount; }
public void setHighCount(int highCount) { this.highCount = highCount; }
public int getMediumCount() { return mediumCount; }
public void setMediumCount(int mediumCount) { this.mediumCount = mediumCount; }
public int getLowCount() { return lowCount; }
public void setLowCount(int lowCount) { this.lowCount = lowCount; }
public List<QuayApiClient.ImageVulnerability> getFixableVulnerabilities() { return fixableVulnerabilities; }
public void setFixableVulnerabilities(List<QuayApiClient.ImageVulnerability> fixableVulnerabilities) { this.fixableVulnerabilities = fixableVulnerabilities; }
public Map<String, List<QuayApiClient.ImageVulnerability>> getVulnerabilitiesByPackage() { return vulnerabilitiesByPackage; }
public void setVulnerabilitiesByPackage(Map<String, List<QuayApiClient.ImageVulnerability>> vulnerabilitiesByPackage) { this.vulnerabilitiesByPackage = vulnerabilitiesByPackage; }
public boolean isCompliant() { return compliant; }
public void setCompliant(boolean compliant) { this.compliant = compliant; }
public List<String> getPolicyViolations() { return policyViolations; }
public void setPolicyViolations(List<String> policyViolations) { this.policyViolations = policyViolations; }
public int getTotalVulnerabilities() {
return criticalCount + highCount + mediumCount + lowCount;
}
}
public static class SecurityPolicy {
private int maxCritical;
private int maxHigh;
private int maxMedium;
private int maxLow;
private int maxTotal;
private List<String> allowedBaseImages;
private List<String> bannedPackages;
// Getters and setters
public int getMaxCritical() { return maxCritical; }
public void setMaxCritical(int maxCritical) { this.maxCritical = maxCritical; }
public int getMaxHigh() { return maxHigh; }
public void setMaxHigh(int maxHigh) { this.maxHigh = maxHigh; }
public int getMaxMedium() { return maxMedium; }
public void setMaxMedium(int maxMedium) { this.maxMedium = maxMedium; }
public int getMaxLow() { return maxLow; }
public void setMaxLow(int maxLow) { this.maxLow = maxLow; }
public int getMaxTotal() { return maxTotal; }
public void setMaxTotal(int maxTotal) { this.maxTotal = maxTotal; }
public List<String> getAllowedBaseImages() { return allowedBaseImages; }
public void setAllowedBaseImages(List<String> allowedBaseImages) { this.allowedBaseImages = allowedBaseImages; }
public List<String> getBannedPackages() { return bannedPackages; }
public void setBannedPackages(List<String> bannedPackages) { this.bannedPackages = bannedPackages; }
}
public static class ScanTrend {
private Map<Date, Integer> vulnerabilityTrend;
private double trendSlope;
private String trendDirection; // improving, worsening, stable
// Getters and setters
}
}

Image Signing and Verification

3. Content Trust Service

package com.example.quay.security;
import org.springframework.stereotype.Service;
import java.security.*;
import java.util.Base64;
@Service
public class ContentTrustService {
private final QuayApiClient quayClient;
public ContentTrustService(QuayApiClient quayClient) {
this.quayClient = quayClient;
}
public SignatureResult signImage(String namespace, String repository, 
String tag, String digest, PrivateKey privateKey) {
try {
String contentToSign = String.format("%s/%s:%s@%s", 
namespace, repository, tag, digest);
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(privateKey);
signature.update(contentToSign.getBytes());
byte[] digitalSignature = signature.sign();
String signatureBase64 = Base64.getEncoder().encodeToString(digitalSignature);
// Store signature in Quay metadata
storeSignatureInQuay(namespace, repository, tag, digest, signatureBase64);
return new SignatureResult(true, "Image signed successfully", signatureBase64);
} catch (Exception e) {
return new SignatureResult(false, "Signing failed: " + e.getMessage(), null);
}
}
public VerificationResult verifyImage(String namespace, String repository, 
String tag, String digest, PublicKey publicKey) {
try {
// Retrieve signature from Quay
String storedSignature = retrieveSignatureFromQuay(namespace, repository, tag, digest);
if (storedSignature == null) {
return new VerificationResult(false, "No signature found for image");
}
String contentToVerify = String.format("%s/%s:%s@%s", 
namespace, repository, tag, digest);
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initVerify(publicKey);
signature.update(contentToVerify.getBytes());
byte[] signatureBytes = Base64.getDecoder().decode(storedSignature);
boolean verified = signature.verify(signatureBytes);
if (verified) {
return new VerificationResult(true, "Image signature verified");
} else {
return new VerificationResult(false, "Image signature verification failed");
}
} catch (Exception e) {
return new VerificationResult(false, "Verification failed: " + e.getMessage());
}
}
public KeyPair generateSigningKeyPair() throws NoSuchAlgorithmException {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048);
return keyGen.generateKeyPair();
}
private void storeSignatureInQuay(String namespace, String repository, 
String tag, String digest, String signature) {
// Implementation to store signature in Quay metadata
// This would use Quay's API to store the signature as image metadata
}
private String retrieveSignatureFromQuay(String namespace, String repository, 
String tag, String digest) {
// Implementation to retrieve signature from Quay metadata
// This would use Quay's API to retrieve the stored signature
return "stored-signature-base64"; // Placeholder
}
// Data Classes
public static class SignatureResult {
private final boolean success;
private final String message;
private final String signature;
public SignatureResult(boolean success, String message, String signature) {
this.success = success;
this.message = message;
this.signature = signature;
}
// Getters
public boolean isSuccess() { return success; }
public String getMessage() { return message; }
public String getSignature() { return signature; }
}
public static class VerificationResult {
private final boolean verified;
private final String message;
public VerificationResult(boolean verified, String message) {
this.verified = verified;
this.message = message;
}
// Getters
public boolean isVerified() { return verified; }
public String getMessage() { return message; }
}
}

Access Control and RBAC

4. Access Management Service

package com.example.quay.security;
import org.springframework.stereotype.Service;
import java.util.*;
@Service
public class AccessManagementService {
private final QuayApiClient quayClient;
public AccessManagementService(QuayApiClient quayClient) {
this.quayClient = quayClient;
}
public RepositoryAccessReport analyzeRepositoryAccess(String namespace, String repository) {
RepositoryAccessReport report = new RepositoryAccessReport();
report.setNamespace(namespace);
report.setRepository(repository);
report.setAnalysisDate(new Date());
// Analyze user permissions
analyzeUserPermissions(namespace, repository, report);
// Analyze robot account permissions
analyzeRobotPermissions(namespace, repository, report);
// Check for security risks
checkSecurityRisks(report);
return report;
}
private void analyzeUserPermissions(String namespace, String repository, 
RepositoryAccessReport report) {
// Implementation to analyze user permissions
// This would fetch user permissions and identify potential risks
List<PermissionFinding> findings = new ArrayList<>();
// Example checks
if (hasTooManyAdmins(report)) {
findings.add(new PermissionFinding(
"TOO_MANY_ADMINS", 
"High", 
"Repository has more than 2 users with admin permissions"
));
}
if (hasExternalUsersWithWriteAccess(report)) {
findings.add(new PermissionFinding(
"EXTERNAL_WRITE_ACCESS", 
"Medium", 
"External users have write access to repository"
));
}
report.setPermissionFindings(findings);
}
private void analyzeRobotPermissions(String namespace, String repository, 
RepositoryAccessReport report) {
// Analyze robot account permissions and usage
List<RobotFinding> robotFindings = new ArrayList<>();
// Check for overly permissive robot accounts
if (hasOverlyPermissiveRobots(report)) {
robotFindings.add(new RobotFinding(
"OVERLY_PERMISSIVE_ROBOT",
"High",
"Robot account has admin permissions without justification"
));
}
// Check for unused robot accounts
if (hasUnusedRobots(report)) {
robotFindings.add(new RobotFinding(
"UNUSED_ROBOT",
"Low",
"Robot account hasn't been used in 90 days"
));
}
report.setRobotFindings(robotFindings);
}
private void checkSecurityRisks(RepositoryAccessReport report) {
List<SecurityRisk> risks = new ArrayList<>();
// Public repository with sensitive data
if (isPublicWithSensitiveData(report)) {
risks.add(new SecurityRisk(
"PUBLIC_SENSITIVE_DATA",
"Critical",
"Public repository may contain sensitive data"
));
}
// No 2FA enforcement
if (!has2FAEnforcement(report)) {
risks.add(new SecurityRisk(
"NO_2FA_ENFORCEMENT",
"Medium",
"Two-factor authentication not enforced for organization"
));
}
report.setSecurityRisks(risks);
}
public AccessComplianceReport checkAccessCompliance(String namespace) {
AccessComplianceReport report = new AccessComplianceReport();
report.setNamespace(namespace);
report.setCheckDate(new Date());
// Check organization-wide compliance
checkOrganizationCompliance(namespace, report);
// Check repository-specific compliance
checkRepositoryCompliance(namespace, report);
return report;
}
private void checkOrganizationCompliance(String namespace, AccessComplianceReport report) {
List<ComplianceFinding> findings = new ArrayList<>();
// Check if 2FA is enabled
if (!is2FAEnabled(namespace)) {
findings.add(new ComplianceFinding(
"2FA_NOT_ENABLED",
"Organization does not enforce two-factor authentication",
"HIGH"
));
}
// Check team structure
if (!hasProperTeamStructure(namespace)) {
findings.add(new ComplianceFinding(
"IMPROPER_TEAM_STRUCTURE",
"Organization lacks proper team hierarchy and access control",
"MEDIUM"
));
}
report.setOrganizationFindings(findings);
}
private void checkRepositoryCompliance(String namespace, AccessComplianceReport report) {
List<RepositoryCompliance> repoCompliance = new ArrayList<>();
// Check each repository in the namespace
// This would iterate through all repositories and check their compliance
RepositoryCompliance comp = new RepositoryCompliance();
comp.setRepositoryName("example-repo");
comp.setCompliant(true);
comp.setFindings(Collections.emptyList());
repoCompliance.add(comp);
report.setRepositoryCompliance(repoCompliance);
}
// Helper methods (simplified implementations)
private boolean hasTooManyAdmins(RepositoryAccessReport report) { return false; }
private boolean hasExternalUsersWithWriteAccess(RepositoryAccessReport report) { return false; }
private boolean hasOverlyPermissiveRobots(RepositoryAccessReport report) { return false; }
private boolean hasUnusedRobots(RepositoryAccessReport report) { return false; }
private boolean isPublicWithSensitiveData(RepositoryAccessReport report) { return false; }
private boolean has2FAEnforcement(RepositoryAccessReport report) { return false; }
private boolean is2FAEnabled(String namespace) { return true; }
private boolean hasProperTeamStructure(String namespace) { return true; }
// Data Classes
public static class RepositoryAccessReport {
private String namespace;
private String repository;
private Date analysisDate;
private List<PermissionFinding> permissionFindings;
private List<RobotFinding> robotFindings;
private List<SecurityRisk> securityRisks;
// Getters and setters
public String getNamespace() { return namespace; }
public void setNamespace(String namespace) { this.namespace = namespace; }
public String getRepository() { return repository; }
public void setRepository(String repository) { this.repository = repository; }
public Date getAnalysisDate() { return analysisDate; }
public void setAnalysisDate(Date analysisDate) { this.analysisDate = analysisDate; }
public List<PermissionFinding> getPermissionFindings() { return permissionFindings; }
public void setPermissionFindings(List<PermissionFinding> permissionFindings) { this.permissionFindings = permissionFindings; }
public List<RobotFinding> getRobotFindings() { return robotFindings; }
public void setRobotFindings(List<RobotFinding> robotFindings) { this.robotFindings = robotFindings; }
public List<SecurityRisk> getSecurityRisks() { return securityRisks; }
public void setSecurityRisks(List<SecurityRisk> securityRisks) { this.securityRisks = securityRisks; }
}
public static class PermissionFinding {
private String code;
private String severity;
private String description;
public PermissionFinding(String code, String severity, String description) {
this.code = code;
this.severity = severity;
this.description = description;
}
// Getters
public String getCode() { return code; }
public String getSeverity() { return severity; }
public String getDescription() { return description; }
}
public static class RobotFinding {
private String code;
private String severity;
private String description;
public RobotFinding(String code, String severity, String description) {
this.code = code;
this.severity = severity;
this.description = description;
}
// Getters
public String getCode() { return code; }
public String getSeverity() { return severity; }
public String getDescription() { return description; }
}
public static class SecurityRisk {
private String code;
private String severity;
private String description;
public SecurityRisk(String code, String severity, String description) {
this.code = code;
this.severity = severity;
this.description = description;
}
// Getters
public String getCode() { return code; }
public String getSeverity() { return severity; }
public String getDescription() { return description; }
}
public static class AccessComplianceReport {
private String namespace;
private Date checkDate;
private List<ComplianceFinding> organizationFindings;
private List<RepositoryCompliance> repositoryCompliance;
// Getters and setters
public String getNamespace() { return namespace; }
public void setNamespace(String namespace) { this.namespace = namespace; }
public Date getCheckDate() { return checkDate; }
public void setCheckDate(Date checkDate) { this.checkDate = checkDate; }
public List<ComplianceFinding> getOrganizationFindings() { return organizationFindings; }
public void setOrganizationFindings(List<ComplianceFinding> organizationFindings) { this.organizationFindings = organizationFindings; }
public List<RepositoryCompliance> getRepositoryCompliance() { return repositoryCompliance; }
public void setRepositoryCompliance(List<RepositoryCompliance> repositoryCompliance) { this.repositoryCompliance = repositoryCompliance; }
}
public static class ComplianceFinding {
private String code;
private String description;
private String severity;
public ComplianceFinding(String code, String description, String severity) {
this.code = code;
this.description = description;
this.severity = severity;
}
// Getters
public String getCode() { return code; }
public String getDescription() { return description; }
public String getSeverity() { return severity; }
}
public static class RepositoryCompliance {
private String repositoryName;
private boolean compliant;
private List<ComplianceFinding> findings;
// Getters and setters
public String getRepositoryName() { return repositoryName; }
public void setRepositoryName(String repositoryName) { this.repositoryName = repositoryName; }
public boolean isCompliant() { return compliant; }
public void setCompliant(boolean compliant) { this.compliant = compliant; }
public List<ComplianceFinding> getFindings() { return findings; }
public void setFindings(List<ComplianceFinding> findings) { this.findings = findings; }
}
}

CI/CD Integration

5. Security Gate Service

package com.example.quay.security;
import org.springframework.stereotype.Service;
import java.util.*;
@Service
public class SecurityGateService {
private final VulnerabilityScannerService vulnerabilityScanner;
private final ContentTrustService contentTrustService;
private final AccessManagementService accessManagementService;
public SecurityGateService(VulnerabilityScannerService vulnerabilityScanner,
ContentTrustService contentTrustService,
AccessManagementService accessManagementService) {
this.vulnerabilityScanner = vulnerabilityScanner;
this.contentTrustService = contentTrustService;
this.accessManagementService = accessManagementService;
}
public DeploymentApproval checkDeploymentApproval(String namespace, String repository, 
String tag, String digest) {
DeploymentApproval approval = new DeploymentApproval();
approval.setNamespace(namespace);
approval.setRepository(repository);
approval.setTag(tag);
approval.setDigest(digest);
approval.setCheckTime(new Date());
// Check vulnerability compliance
VulnerabilityScannerService.SecurityComplianceReport vulnReport = 
vulnerabilityScanner.checkImageCompliance(namespace, repository, tag);
approval.setVulnerabilityReport(vulnReport);
// Check image signature
ContentTrustService.VerificationResult signatureResult = 
contentTrustService.verifyImage(namespace, repository, tag, digest, getPublicKey());
approval.setSignatureVerified(signatureResult.isVerified());
approval.setSignatureVerificationMessage(signatureResult.getMessage());
// Check access compliance
AccessManagementService.RepositoryAccessReport accessReport = 
accessManagementService.analyzeRepositoryAccess(namespace, repository);
approval.setAccessReport(accessReport);
// Make approval decision
makeApprovalDecision(approval);
return approval;
}
private void makeApprovalDecision(DeploymentApproval approval) {
boolean approved = true;
List<String> rejectionReasons = new ArrayList<>();
// Check vulnerability compliance
if (!approval.getVulnerabilityReport().isCompliant()) {
approved = false;
rejectionReasons.add("Image has critical/high vulnerabilities");
}
// Check signature verification
if (!approval.isSignatureVerified()) {
approved = false;
rejectionReasons.add("Image signature verification failed");
}
// Check access security
if (hasCriticalSecurityRisks(approval.getAccessReport())) {
approved = false;
rejectionReasons.add("Repository has critical security risks");
}
approval.setApproved(approved);
approval.setRejectionReasons(rejectionReasons);
if (approved) {
approval.setApprovalMessage("Image meets all security requirements for deployment");
} else {
approval.setApprovalMessage("Image rejected due to security policy violations");
}
}
public SecurityGateReport generateSecurityReport(String namespace, String repository) {
SecurityGateReport report = new SecurityGateReport();
report.setNamespace(namespace);
report.setRepository(repository);
report.setGenerationDate(new Date());
// Generate comprehensive security report
generateVulnerabilitySummary(namespace, repository, report);
generateAccessSummary(namespace, repository, report);
generateComplianceSummary(namespace, repository, report);
return report;
}
private void generateVulnerabilitySummary(String namespace, String repository, 
SecurityGateReport report) {
// Implementation to generate vulnerability summary
// This would analyze all image tags and their vulnerability status
}
private void generateAccessSummary(String namespace, String repository, 
SecurityGateReport report) {
// Implementation to generate access control summary
// This would analyze user and robot account permissions
}
private void generateComplianceSummary(String namespace, String repository, 
SecurityGateReport report) {
// Implementation to generate compliance summary
// This would check against organizational security policies
}
private PublicKey getPublicKey() {
// Implementation to retrieve the public key for signature verification
// This could be from a key store, configuration, or environment variable
return null; // Placeholder
}
private boolean hasCriticalSecurityRisks(AccessManagementService.RepositoryAccessReport report) {
return report.getSecurityRisks().stream()
.anyMatch(risk -> "Critical".equals(risk.getSeverity()));
}
// Data Classes
public static class DeploymentApproval {
private String namespace;
private String repository;
private String tag;
private String digest;
private Date checkTime;
private VulnerabilityScannerService.SecurityComplianceReport vulnerabilityReport;
private boolean signatureVerified;
private String signatureVerificationMessage;
private AccessManagementService.RepositoryAccessReport accessReport;
private boolean approved;
private List<String> rejectionReasons;
private String approvalMessage;
// Getters and setters
public String getNamespace() { return namespace; }
public void setNamespace(String namespace) { this.namespace = namespace; }
public String getRepository() { return repository; }
public void setRepository(String repository) { this.repository = repository; }
public String getTag() { return tag; }
public void setTag(String tag) { this.tag = tag; }
public String getDigest() { return digest; }
public void setDigest(String digest) { this.digest = digest; }
public Date getCheckTime() { return checkTime; }
public void setCheckTime(Date checkTime) { this.checkTime = checkTime; }
public VulnerabilityScannerService.SecurityComplianceReport getVulnerabilityReport() { return vulnerabilityReport; }
public void setVulnerabilityReport(VulnerabilityScannerService.SecurityComplianceReport vulnerabilityReport) { this.vulnerabilityReport = vulnerabilityReport; }
public boolean isSignatureVerified() { return signatureVerified; }
public void setSignatureVerified(boolean signatureVerified) { this.signatureVerified = signatureVerified; }
public String getSignatureVerificationMessage() { return signatureVerificationMessage; }
public void setSignatureVerificationMessage(String signatureVerificationMessage) { this.signatureVerificationMessage = signatureVerificationMessage; }
public AccessManagementService.RepositoryAccessReport getAccessReport() { return accessReport; }
public void setAccessReport(AccessManagementService.RepositoryAccessReport accessReport) { this.accessReport = accessReport; }
public boolean isApproved() { return approved; }
public void setApproved(boolean approved) { this.approved = approved; }
public List<String> getRejectionReasons() { return rejectionReasons; }
public void setRejectionReasons(List<String> rejectionReasons) { this.rejectionReasons = rejectionReasons; }
public String getApprovalMessage() { return approvalMessage; }
public void setApprovalMessage(String approvalMessage) { this.approvalMessage = approvalMessage; }
}
public static class SecurityGateReport {
private String namespace;
private String repository;
private Date generationDate;
private VulnerabilitySummary vulnerabilitySummary;
private AccessSummary accessSummary;
private ComplianceSummary complianceSummary;
private List<SecurityRecommendation> recommendations;
// Getters and setters
public String getNamespace() { return namespace; }
public void setNamespace(String namespace) { this.namespace = namespace; }
public String getRepository() { return repository; }
public void setRepository(String repository) { this.repository = repository; }
public Date getGenerationDate() { return generationDate; }
public void setGenerationDate(Date generationDate) { this.generationDate = generationDate; }
public VulnerabilitySummary getVulnerabilitySummary() { return vulnerabilitySummary; }
public void setVulnerabilitySummary(VulnerabilitySummary vulnerabilitySummary) { this.vulnerabilitySummary = vulnerabilitySummary; }
public AccessSummary getAccessSummary() { return accessSummary; }
public void setAccessSummary(AccessSummary accessSummary) { this.accessSummary = accessSummary; }
public ComplianceSummary getComplianceSummary() { return complianceSummary; }
public void setComplianceSummary(ComplianceSummary complianceSummary) { this.complianceSummary = complianceSummary; }
public List<SecurityRecommendation> getRecommendations() { return recommendations; }
public void setRecommendations(List<SecurityRecommendation> recommendations) { this.recommendations = recommendations; }
}
public static class VulnerabilitySummary {
private int totalImages;
private int compliantImages;
private int nonCompliantImages;
private Map<String, Integer> severityDistribution;
private List<String> mostVulnerableImages;
// Getters and setters
}
public static class AccessSummary {
private int totalUsers;
private int totalRobots;
private int adminUsers;
private int writeUsers;
private int readUsers;
private List<String> securityFindings;
// Getters and setters
}
public static class ComplianceSummary {
private boolean organizationCompliant;
private int compliantRepositories;
private int nonCompliantRepositories;
private List<String> complianceIssues;
// Getters and setters
}
public static class SecurityRecommendation {
private String category;
private String description;
private String priority; // HIGH, MEDIUM, LOW
private String action;
// Getters and setters
}
}

Configuration

6. Spring Boot Configuration

package com.example.quay.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class QuaySecurityConfig {
@Value("${quay.security.token}")
private String quayToken;
@Value("${quay.security.base-url:https://quay.io}")
private String quayBaseUrl;
@Value("${quay.security.policy.max-critical:0}")
private int maxCriticalVulnerabilities;
@Value("${quay.security.policy.max-high:2}")
private int maxHighVulnerabilities;
@Value("${quay.security.policy.max-total:20}")
private int maxTotalVulnerabilities;
@Bean
public QuayApiClient quayApiClient() {
return new QuayApiClient(quayToken, quayBaseUrl);
}
@Bean
public VulnerabilityScannerService.SecurityPolicy securityPolicy() {
VulnerabilityScannerService.SecurityPolicy policy = 
new VulnerabilityScannerService.SecurityPolicy();
policy.setMaxCritical(maxCriticalVulnerabilities);
policy.setMaxHigh(maxHighVulnerabilities);
policy.setMaxTotal(maxTotalVulnerabilities);
return policy;
}
}
// application.yml
quay:
security:
token: ${QUAY_SECURITY_TOKEN}
base-url: https://quay.io
policy:
max-critical: 0
max-high: 2
max-medium: 10
max-total: 20
signing:
key-algorithm: RSA
key-size: 2048
security:
gate:
require-signature: true
block-critical-vulns: true
enforce-access-control: true
logging:
level:
com.example.quay.security: DEBUG

This comprehensive Quay.io security integration provides:

  1. Vulnerability Management - Scanning, analysis, and compliance checking
  2. Content Trust - Image signing and verification
  3. Access Control - RBAC analysis and security auditing
  4. Security Gates - Automated deployment approvals
  5. Compliance Reporting - Comprehensive security reporting

The system can be integrated into your CI/CD pipeline to enforce security policies and ensure container images meet organizational security standards before deployment.

Leave a Reply

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


Macro Nepal Helper