Azure Container Registry (ACR) provides robust security policies and features to secure your Java container images. These policies help enforce security standards, scan for vulnerabilities, and ensure compliance throughout the container lifecycle.
ACR Security Overview
ACR security policies encompass:
- Image Vulnerability Scanning: Automated security scanning
- Content Trust: Image signing and verification
- Network Security: Private endpoints and firewall rules
- Access Control: RBAC and managed identities
- Compliance Policies: Enforcement of security standards
ACR Security Policy Types
1. Vulnerability Scanning Policies
Example 1: ACR Task for Security Scanning
# acr-security-scan.yaml
version: v1.1.0
steps:
- id: build-java-app
cmd: docker build -t {{.Run.Registry}}/java-app:{{.Run.ID}} .
workingDir: src
when: ["-"]
- id: security-scan
cmd: |
echo "Running security scan on Java image..."
# Use Trivy for vulnerability scanning
trivy image --exit-code 1 --severity HIGH,CRITICAL {{.Run.Registry}}/java-app:{{.Run.ID}}
when: ["-"]
- id: push-image
cmd: docker push {{.Run.Registry}}/java-app:{{.Run.ID}}
when: ["security-scan"]
Example 2: Azure Pipeline with Security Gates
# azure-pipeline-security.yml trigger: branches: include: - main - develop variables: imageRepository: 'java-app' dockerfilePath: '**/Dockerfile' tag: '$(Build.BuildId)' stages: - stage: Build displayName: Build and Scan jobs: - job: Build displayName: Build Java App steps: - task: Maven@3 inputs: mavenPomFile: 'pom.xml' goals: 'clean package' options: '-DskipTests' - task: Docker@2 displayName: Build Container Image inputs: command: build dockerfile: '**/Dockerfile' tags: | $(tag) latest - task: ContainerSecurityScan@1 displayName: 'Security Scan' inputs: imageName: '$(imageRepository):$(tag)' failOnHighSeverityVulnerabilities: true failOnMediumSeverityVulnerabilities: false - task: AzureContainerRegistry@1 displayName: Push to ACR inputs: command: push imageName: '$(imageRepository):$(tag)'
2. Content Trust Policies
Example 3: Docker Content Trust Configuration
# Secure Dockerfile for Java Application FROM eclipse-temurin:17-jre-jammy # Security best practices RUN groupadd -r javaapp && useradd -r -g javaapp javaapp # Create non-root user USER javaapp # Copy application COPY --chown=javaapp:javaapp target/*.jar app.jar # Security scanning labels LABEL org.opencontainers.image.source="https://github.com/company/java-app" LABEL security.scan.enabled="true" LABEL security.policy.version="1.0" # 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.jar"]
Example 4: Notary Configuration for Image Signing
#!/bin/bash # sign-image.sh #!/bin/bash IMAGE_NAME="myacr.azurecr.io/java-app" IMAGE_TAG="1.0.0" # Build image docker build -t $IMAGE_NAME:$IMAGE_TAG . # Sign the image export DOCKER_CONTENT_TRUST=1 export DOCKER_CONTENT_TRUST_SERVER=https://myacr.azurecr.io docker trust sign $IMAGE_NAME:$IMAGE_TAG # Verify signature docker trust inspect --pretty $IMAGE_NAME:$IMAGE_TAG
ACR Policy as Code
Example 5: Azure Policy for ACR Compliance
{
"mode": "All",
"policyRule": {
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.ContainerRegistry/registries"
},
{
"field": "Microsoft.ContainerRegistry/registries/policies.trustPolicy.status",
"notEquals": "enabled"
}
]
},
"then": {
"effect": "audit"
}
},
"parameters": {}
}
Example 6: ACR Task with Security Enforcement
# acr-task-security.yaml
version: v1.1.0
stepTimeout: 3600
volumes:
- name: security-reports
path: /reports
steps:
- id: build
cmd: docker build -t {{.Values.IMAGE_NAME}}:{{.Run.ID}} .
workingDir: {{.Values.WORKING_DIR}}
- id: trivy-scan
cmd: |
trivy image \
--format json \
--output /reports/trivy-report.json \
--exit-code 0 \
{{.Values.IMAGE_NAME}}:{{.Run.ID}}
when: ["-"]
- id: check-vulnerabilities
cmd: |
# Check for critical vulnerabilities
CRITICAL_COUNT=$(jq '.Results[].Vulnerabilities[]? | select(.Severity == "CRITICAL") | .VulnerabilityID' /reports/trivy-report.json | wc -l)
if [ $CRITICAL_COUNT -gt 0 ]; then
echo "CRITICAL vulnerabilities found: $CRITICAL_COUNT"
exit 1
fi
# Check for high severity vulnerabilities
HIGH_COUNT=$(jq '.Results[].Vulnerabilities[]? | select(.Severity == "HIGH") | .VulnerabilityID' /reports/trivy-report.json | wc -l)
if [ $HIGH_COUNT -gt 10 ]; then
echo "Too many HIGH severity vulnerabilities: $HIGH_COUNT"
exit 1
fi
when: ["trivy-scan"]
- id: push
cmd: docker push {{.Values.IMAGE_NAME}}:{{.Run.ID}}
when: ["check-vulnerabilities"]
Network Security Policies
Example 7: ACR Network Security Configuration
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"acrName": {
"type": "string",
"defaultValue": "mysecureacr"
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]"
}
},
"resources": [
{
"type": "Microsoft.ContainerRegistry/registries",
"apiVersion": "2019-12-01-preview",
"name": "[parameters('acrName')]",
"location": "[parameters('location')]",
"sku": {
"name": "Premium"
},
"properties": {
"adminUserEnabled": false,
"policies": {
"quarantinePolicy": {
"status": "enabled"
},
"trustPolicy": {
"type": "Notary",
"status": "enabled"
},
"retentionPolicy": {
"days": 30,
"status": "enabled"
}
},
"networkRuleSet": {
"defaultAction": "Deny",
"virtualNetworkRules": [
{
"action": "Allow",
"virtualNetworkResourceId": "[resourceId('Microsoft.Network/virtualNetworks/subnets', 'vnet-prod', 'subnet-acr')]"
}
],
"ipRules": [
{
"action": "Allow",
"value": "192.168.1.1/32"
}
]
}
}
}
]
}
Identity and Access Management
Example 8: Azure RBAC for ACR
// Java application using Managed Identity to access ACR
@Configuration
public class AcrSecurityConfig {
@Value("${azure.acr.server}")
private String acrServer;
@Bean
public DockerClient dockerClient() {
return DefaultDockerClient.builder()
.uri(URI.create("unix:///var/run/docker.sock"))
.dockerCertificates(new DockerCertificates(Paths.get("/certs")))
.build();
}
@Bean
public AcrTokenProvider acrTokenProvider() {
return new ManagedIdentityAcrTokenProvider();
}
}
// Token provider using Managed Identity
@Component
public class ManagedIdentityAcrTokenProvider {
private final TokenCredential credential;
public ManagedIdentityAcrTokenProvider() {
this.credential = new DefaultAzureCredentialBuilder().build();
}
public String getAcrToken() {
try {
AccessToken token = credential.getToken(
new TokenRequestContext()
.addScopes("https://management.azure.com/.default"))
.block();
return token.getToken();
} catch (Exception e) {
throw new RuntimeException("Failed to get ACR token", e);
}
}
}
Example 9: ACR Access Key Rotation
// ACR key rotation service
@Service
public class AcrKeyRotationService {
private final ContainerRegistryManagementClient registryClient;
public AcrKeyRotationService() {
this.registryClient = new ContainerRegistryManagementClientBuilder()
.credential(new DefaultAzureCredentialBuilder().build())
.buildClient();
}
public void rotateAcrPassword(String resourceGroup, String registryName) {
// Generate new passwords
RegistryListCredentials credentials =
registryClient.registries().listCredentials(resourceGroup, registryName);
// Update application configuration with new password
updateApplicationSecrets(credentials.passwords().get(0).value());
// Regenerate passwords
registryClient.registries().regenerateCredential(
resourceGroup,
registryName,
PasswordName.PASSWORD
);
}
private void updateApplicationSecrets(String newPassword) {
// Update in Azure Key Vault or application configuration
SecretClient secretClient = new SecretClientBuilder()
.vaultUrl("https://my-vault.vault.azure.net")
.credential(new DefaultAzureCredentialBuilder().build())
.buildClient();
secretClient.setSecret(new KeyVaultSecret("acr-password", newPassword));
}
}
Compliance and Governance
Example 10: ACR Compliance Policies
{
"properties": {
"policy": {
"quarantinePolicy": {
"status": "enabled"
},
"trustPolicy": {
"type": "Notary",
"status": "enabled"
},
"retentionPolicy": {
"days": 7,
"status": "enabled"
},
"exportPolicy": {
"status": "enabled"
},
"azurePolicy": {
"status": "enabled"
}
}
}
}
Example 11: Azure Policy for ACR Image Compliance
{
"mode": "All",
"policyRule": {
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.ContainerRegistry/registries"
},
{
"field": "Microsoft.ContainerRegistry/registries/policies.retentionPolicy.status",
"notEquals": "enabled"
}
]
},
"then": {
"effect": "deny"
}
},
"parameters": {}
}
Security Monitoring and Alerting
Example 12: ACR Security Monitoring
// Security event monitoring service
@Service
public class AcrSecurityMonitor {
private final LogAnalyticsClient logAnalyticsClient;
public AcrSecurityMonitor() {
this.logAnalyticsClient = new LogAnalyticsClientBuilder()
.credential(new DefaultAzureCredentialBuilder().build())
.buildClient();
}
public void monitorAcrEvents() {
String query = """
AzureActivity
| where ResourceProvider == "MICROSOFT.CONTAINERREGISTRY"
| where OperationNameValue endswith "WRITE"
| where ActivityStatusValue == "Success"
| project TimeGenerated, Caller, OperationName, ResourceGroup, Resource
| order by TimeGenerated desc
""";
QueryResults response = logAnalyticsClient.queryWorkspace(
"workspace-id",
query,
Duration.ofHours(24)
);
processSecurityEvents(response);
}
private void processSecurityEvents(QueryResults results) {
results.getTables().forEach(table -> {
table.getRows().forEach(row -> {
String operation = row.get(2).toString();
if (isSensitiveOperation(operation)) {
sendSecurityAlert(row);
}
});
});
}
private boolean isSensitiveOperation(String operation) {
return operation.contains("Delete") ||
operation.contains("RegenerateCredential") ||
operation.contains("UpdatePolicies");
}
private void sendSecurityAlert(List<Object> eventData) {
// Send alert to security team
System.out.println("SECURITY ALERT: Sensitive ACR operation detected: " + eventData);
}
}
Automated Security Workflows
Example 13: Complete Security Pipeline
# azure-pipelines-security-full.yml name: Java App Security Pipeline variables: acrName: 'mysecureacr' imageName: 'java-app' tag: '$(Build.BuildId)' stages: - stage: Security_Scan displayName: Security Scanning jobs: - job: Code_Analysis steps: - task: SonarCloudPrepare@1 inputs: SonarCloud: 'sonarcloud' organization: 'my-organization' scannerMode: 'MSBuild' projectKey: 'java-app-security' - task: Maven@3 inputs: mavenPomFile: 'pom.xml' goals: 'clean compile' options: '-Dsonar.coverage.exclusions="**/test/**"' - task: SonarCloudAnalyze@1 - job: Container_Security steps: - task: Docker@2 inputs: command: 'build' dockerfile: '**/Dockerfile' tags: '$(tag)' - task: ContainerSecurityScan@1 inputs: imageName: '$(acrName).azurecr.io/$(imageName):$(tag)' failOnHighSeverityVulnerabilities: true failOnMediumSeverityVulnerabilities: false customScanOptions: '--ignore-unfixed' - task: AzureContainerRegistry@1 inputs: command: 'push' imageName: '$(acrName).azurecr.io/$(imageName):$(tag)' - stage: Compliance_Check displayName: Compliance Validation dependsOn: Security_Scan jobs: - job: Policy_Validation steps: - task: AzureCLI@2 inputs: azureSubscription: 'azure-connection' scriptType: 'bash' scriptLocation: 'inlineScript' inlineScript: | # Check ACR policies az acr show --name $(acrName) --query policies # Validate image signatures az acr repository show --name $(acrName) --image $(imageName):$(tag) # Check vulnerability scan results az acr task run --name security-scan --registry $(acrName)
Best Practices for Java Applications
Example 14: Secure Java Dockerfile
# Multi-stage build for security FROM eclipse-temurin:17-jdk-jammy as builder # Security: Use package cache and clean in same layer RUN apt-get update && \ apt-get install -y --no-install-recommends \ maven \ && rm -rf /var/lib/apt/lists/* WORKDIR /app COPY pom.xml . COPY src ./src # Build application RUN mvn clean package -DskipTests # Runtime stage FROM eclipse-temurin:17-jre-jammy # Security enhancements RUN groupadd -r javaapp && useradd -r -g javaapp javaapp && \ apt-get update && \ apt-get install -y --no-install-recommends \ curl \ && rm -rf /var/lib/apt/lists/* USER javaapp # Copy from builder stage COPY --from=builder --chown=javaapp:javaapp /app/target/*.jar app.jar # Security labels LABEL org.opencontainers.image.title="Java Application" \ org.opencontainers.image.description="Secure Java Application" \ org.opencontainers.image.vendor="My Company" \ security.scan.policy="high" \ maintainer="[email protected]" # JVM security settings ENV JAVA_OPTS="-Djava.security.egd=file:/dev/./urandom -Dfile.encoding=UTF-8" # Health check HEALTHCHECK --interval=30s --timeout=3s --start-period=60s --retries=3 \ CMD curl -f http://localhost:8080/actuator/health || exit 1 EXPOSE 8080 ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar /app.jar"]
Conclusion
ACR security policies provide comprehensive protection for Java container images:
Key Security Features:
- Vulnerability Scanning: Integrated security scanning
- Content Trust: Image signing and verification
- Network Security: Private endpoint support
- Access Control: Fine-grained RBAC
- Compliance: Policy enforcement and auditing
Implementation Strategy:
- Enable Security Features: Turn on vulnerability scanning and content trust
- Configure Network Security: Use private endpoints and firewall rules
- Implement RBAC: Use managed identities and least privilege
- Automate Security Scans: Integrate into CI/CD pipelines
- Monitor and Alert: Set up security monitoring
Best Practices for Java:
- Use multi-stage Docker builds
- Run as non-root user
- Regularly update base images
- Implement health checks
- Use JVM security settings
- Scan for vulnerabilities in CI/CD
By implementing these ACR security policies, organizations can ensure their Java applications are secure, compliant, and protected throughout the container lifecycle.
Next Steps: Start by enabling ACR security features, implement security scanning in your pipelines, configure network security, and establish regular security reviews and updates.