Secure Your Supply Chain: Tekton Chains Provenance for Java Applications

Introduction

In today's software landscape, securing the software supply chain is paramount. Tekton Chains provides a critical layer of security by generating cryptographically signed provenance metadata for your CI/CD pipelines. For Java applications, this means you can verify the origin, integrity, and build process of every artifact deployed to production. This guide explores how to implement Tekton Chains to create verifiable software supply chains for your Java workloads.


Article: Implementing Software Supply Chain Security with Tekton Chains for Java

Tekton Chains is a Kubernetes-native solution that captures, signs, and stores provenance information from Tekton pipelines. For Java applications, this enables you to answer critical questions: Who built this JAR? What source code was used? Which dependencies were included? Was the build secure?

1. Understanding Provenance for Java Applications

What is Provenance?
Provenance is verifiable information about the origin and history of software artifacts. For Java applications, this includes:

  • Source Code: Git commit hashes, repository URLs
  • Build Environment: Base images, build tools, environment variables
  • Dependencies: Maven/Gradle dependencies, their checksums
  • Build Process: Steps executed, parameters used
  • Output Artifacts: JAR/WAR files, Docker images, SBOMs

Why Java Applications Need Provenance:

  1. Security Compliance: Meet regulatory requirements (SLSA, NIST)
  2. Vulnerability Management: Trace dependencies to specific versions
  3. Audit Trail: Prove who built what and when
  4. Reproducible Builds: Verify build integrity across environments

2. Tekton Chains Architecture Overview

Java Source Code
↓
Tekton Pipeline
↓
Tekton Chains (Provenance Capture)
↓
Cryptographic Signing
↓
Storage (OCI Registry, Transparency Log)
↓
Verification & Policy Enforcement

3. Setting Up Tekton Chains

Prerequisites:

  • Kubernetes cluster (1.21+)
  • Tekton Pipelines v0.40+
  • Tekton Chains v0.15+
  • Cosign for verification
  • Sigstore for certificate transparency

Installation:

# tekton-chains.yaml
apiVersion: operator.tekton.dev/v1alpha1
kind: TektonChain
metadata:
name: chain
spec:
targetNamespace: tekton-chains
chain:
artifacts.oci.storage: oci
artifacts.oci.signer: x509
transparency.enabled: "true"
taskrun.format: in-toto
taskrun.storage: tekton

4. Java-Specific Tekton Pipeline for Provenance

Complete Java Build Pipeline:

# java-build-pipeline.yaml
apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
name: java-build-pipeline
annotations:
chains.tekton.dev/signed: "true"
spec:
workspaces:
- name: source
- name: maven-cache
- name: cosign-key
params:
- name: image-url
type: string
- name: git-revision
type: string
- name: java-version
type: string
default: "17"
tasks:
- name: fetch-sources
taskRef:
name: git-clone
workspaces:
- name: output
workspace: source
params:
- name: url
value: "$(params.git-url)"
- name: revision
value: "$(params.git-revision)"
- name: build-java
taskRef:
name: maven-build
runAfter: ["fetch-sources"]
workspaces:
- name: source
workspace: source
- name: maven-cache
workspace: maven-cache
params:
- name: JAVA_VERSION
value: "$(params.java-version)"
- name: MAVEN_GOALS
value: "package"
- name: MAVEN_OPTS
value: "-Dmaven.test.skip=false"
- name: run-tests
taskRef:
name: maven-test
runAfter: ["build-java"]
workspaces:
- name: source
workspace: source
params:
- name: MAVEN_GOALS
value: "verify"
- name: generate-sbom
taskRef:
name: syft-sbom
runAfter: ["build-java"]
workspaces:
- name: source
workspace: source
params:
- name: IMAGE
value: "$(params.image-url)"
- name: build-image
taskRef:
name: kaniko-build
runAfter: ["run-tests", "generate-sbom"]
workspaces:
- name: source
workspace: source
params:
- name: IMAGE
value: "$(params.image-url)"
- name: DOCKERFILE
value: "./Dockerfile"
- name: sign-image
taskRef:
name: cosign-sign
runAfter: ["build-image"]
workspaces:
- name: cosign-key
workspace: cosign-key
params:
- name: IMAGE
value: "$(params.image-url)"
- name: KEY
value: "cosign.key"
- name: upload-provenance
taskRef:
name: upload-sbom-provenance
runAfter: ["sign-image"]
workspaces:
- name: source
workspace: source
params:
- name: IMAGE
value: "$(params.image-url)"

Maven Build Task:

# maven-build-task.yaml
apiVersion: tekton.dev/v1
kind: Task
metadata:
name: maven-build
annotations:
chains.tekton.dev/signed: "true"
spec:
workspaces:
- name: source
- name: maven-cache
params:
- name: JAVA_VERSION
type: string
default: "17"
- name: MAVEN_GOALS
type: string
default: "package"
- name: MAVEN_OPTS
type: string
default: ""
steps:
- name: build
image: maven:3.8.6-openjdk-$(params.JAVA_VERSION)-slim
workingDir: $(workspaces.source.path)
env:
- name: MAVEN_OPTS
value: "$(params.MAVEN_OPTS) -Dmaven.repo.local=$(workspaces.maven-cache.path)/.m2/repository"
command: ["mvn"]
args:
- "$(params.MAVEN_GOALS)"
- "-DskipTests=false"
- "-Dmaven.test.failure.ignore=false"
volumeMounts:
- name: maven-cache
mountPath: "/tmp/maven-cache"
- name: record-build-info
image: alpine/git:latest
workingDir: $(workspaces.source.path)
script: |
# Capture build information for provenance
echo "GIT_COMMIT=$(git rev-parse HEAD)" >> /tekton/build-info/build.env
echo "GIT_REPO=$(git config --get remote.origin.url)" >> /tekton/build-info/build.env
echo "BUILD_TIMESTAMP=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> /tekton/build-info/build.env
# Capture Java version information
java -version 2>&1 | head -1 >> /tekton/build-info/java-version.txt
mvn -version >> /tekton/build-info/maven-version.txt

SBOM Generation Task:

# sbom-generation-task.yaml
apiVersion: tekton.dev/v1
kind: Task
metadata:
name: syft-sbom
annotations:
chains.tekton.dev/signed: "true"
spec:
workspaces:
- name: source
params:
- name: IMAGE
type: string
steps:
- name: generate-sbom
image: anchore/syft:latest
workingDir: $(workspaces.source.path)
command: ["syft"]
args:
- "packages"
- "dir:$(workspaces.source.path)"
- "--output"
- "spdx-json"
- "--file"
- "/tekton/results/sbom.spdx.json"
- name: attest-sbom
image: gcr.io/projectsigstore/cosign:v2.0.0
workingDir: $(workspaces.source.path)
command: ["cosign"]
args:
- "attest"
- "--predicate"
- "/tekton/results/sbom.spdx.json"
- "--type"
- "spdxjson"
- "$(params.IMAGE)"

5. Java-Specific Provenance Configuration

Custom Task for Java Dependency Analysis:

# java-dependency-task.yaml
apiVersion: tekton.dev/v1
kind: Task
metadata:
name: java-dependency-analysis
annotations:
chains.tekton.dev/signed: "true"
spec:
workspaces:
- name: source
steps:
- name: analyze-dependencies
image: maven:3.8.6-openjdk-17-slim
workingDir: $(workspaces.source.path)
script: |
# Generate dependency tree for provenance
mvn dependency:tree -DoutputFile=/tekton/results/dependency-tree.txt
# Generate dependency list with versions
mvn dependency:list -DincludeArtifactIds=true -DoutputFile=/tekton/results/dependencies.txt
# Generate Software Bill of Materials (SBOM)
mvn org.cyclonedx:cyclonedx-maven-plugin:2.7.4:makeAggregateBom
cp target/bom.json /tekton/results/java-sbom.json
# Capture vulnerability scan results
mvn org.owasp:dependency-check-maven:7.4.4:check -Dformat=JSON
cp target/dependency-check-report.json /tekton/results/vulnerability-scan.json
- name: record-provenance
image: alpine:latest
script: |
# Create comprehensive build provenance
cat > /tekton/results/provenance.json << EOF
{
"buildInfo": {
"javaVersion": "$(java -version 2>&1 | head -1)",
"mavenVersion": "$(mvn -version | head -1)",
"buildTimestamp": "$(date -u +'%Y-%m-%dT%H:%M:%SZ')"
},
"source": {
"gitCommit": "$(git rev-parse HEAD)",
"gitRepo": "$(git config --get remote.origin.url)",
"branch": "$(git branch --show-current)"
},
"dependencies": {
"count": $(mvn dependency:list -q -DincludeArtifactIds=true | wc -l),
"vulnerabilities": $(jq '.dependencies | length' /tekton/results/vulnerability-scan.json)
}
}
EOF

6. PipelineRun with Provenance Capture

Complete PipelineRun:

# java-pipeline-run.yaml
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
name: java-order-service-build-$(uid)
annotations:
chains.tekton.dev/signed: "true"
chains.tekton.dev/format: "in-toto"
spec:
pipelineRef:
name: java-build-pipeline
params:
- name: image-url
value: "gcr.io/my-project/order-service:$(uid)"
- name: git-revision
value: "main"
- name: java-version
value: "17"
workspaces:
- name: source
volumeClaimTemplate:
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
- name: maven-cache
persistentVolumeClaim:
claimName: maven-cache-pvc
- name: cosign-key
secret:
secretName: cosign-key

7. Tekton Chains Configuration for Java

Chains ConfigMap:

# chains-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: chains-config
namespace: tekton-chains
data:
artifacts.oci.format: "simplesigning"
artifacts.oci.storage: "oci,tekton"
artifacts.oci.signer: "x509"
artifacts.taskrun.format: "in-toto"
artifacts.taskrun.storage: "tekton"
artifacts.taskrun.signer: "x509"
transparency.enabled: "true"
# Java-specific provenance settings
artifacts.taskrun.enabled: "true"
artifacts.oci.enabled: "true"
# Storage backends
storage.tekton.host: "http://tekton-pipelines-controller:9090"
storage.oci.repository: "gcr.io/my-project/tekton-chains"
# Signing key configuration
signers.x509.identity.simple_signing: "k8s://tekton-chains/signing-secrets"

8. Verification and Policy Enforcement

Verification with Cosign:

// Java-based verification utility
public class ProvenanceVerifier {
public boolean verifyImageSignature(String imageRef) {
try {
Process process = new ProcessBuilder(
"cosign", "verify",
"--key", "cosign.pub",
imageRef
).start();
return process.waitFor() == 0;
} catch (Exception e) {
throw new RuntimeException("Failed to verify image signature", e);
}
}
public boolean verifyProvenance(String imageRef, String expectedDigest) {
try {
// Verify SLSA provenance
Process process = new ProcessBuilder(
"cosign", "verify-attestation",
"--type", "slsaprovenance",
"--key", "cosign.pub",
imageRef
).start();
if (process.waitFor() != 0) {
return false;
}
// Extract and verify provenance content
String provenance = extractProvenance(imageRef);
return verifyProvenanceContent(provenance, expectedDigest);
} catch (Exception e) {
throw new RuntimeException("Failed to verify provenance", e);
}
}
private boolean verifyProvenanceContent(String provenance, String expectedDigest) {
try {
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(provenance);
// Verify subject digest matches expected
String actualDigest = root.path("subject")
.get(0)
.path("digest")
.path("sha256")
.asText();
return expectedDigest.equals(actualDigest);
} catch (Exception e) {
return false;
}
}
}

Kubernetes Admission Controller for Verification:

# verification-policy.yaml
apiVersion: policy.sigstore.dev/v1beta1
kind: ClusterImagePolicy
metadata:
name: java-app-verification
spec:
images:
- glob: "gcr.io/my-project/order-service/**"
authorities:
- key:
data: |
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgA...
-----END PUBLIC KEY-----
attestations:
- name: slsa-provenance
predicateType: https://slsa.dev/provenance/v0.2
policy:
type: cue
data: |
predicateType: "https://slsa.dev/provenance/v0.2"
predicate:
buildDefinition:
buildType: "https://tekton.dev/chains/v2"
runDetails:
builder:
id: "https://tekton.dev/chains/v2"

9. Java Application with Built-in Provenance Verification

Spring Boot Configuration:

@Configuration
@Slf4j
public class ProvenanceConfiguration {
@Value("${app.provenance.verification.enabled:true}")
private boolean verificationEnabled;
@Bean
public ProvenanceVerifier provenanceVerifier() {
return new ProvenanceVerifier();
}
@EventListener(ApplicationReadyEvent.class)
public void verifyOnStartup() {
if (!verificationEnabled) {
log.warn("Provenance verification is disabled");
return;
}
String imageRef = System.getenv("CONTAINER_IMAGE");
if (imageRef == null) {
log.error("CONTAINER_IMAGE environment variable not set");
return;
}
try {
boolean verified = provenanceVerifier().verifyImageSignature(imageRef);
if (verified) {
log.info("✅ Container image signature verified: {}", imageRef);
} else {
log.error("❌ Container image signature verification failed: {}", imageRef);
// In production, you might want to exit here
// System.exit(1);
}
} catch (Exception e) {
log.error("Failed to verify container image signature", e);
}
}
}

Build Information Endpoint:

@RestController
@RequestMapping("/api/system")
public class BuildInfoController {
@GetMapping("/build-info")
public ResponseEntity<Map<String, Object>> getBuildInfo() {
Map<String, Object> buildInfo = new HashMap<>();
// Read from generated build-info.properties
buildInfo.put("version", getClass().getPackage().getImplementationVersion());
buildInfo.put("buildTime", getClass().getPackage().getImplementationVendor());
// Add provenance information if available
buildInfo.put("provenance", readProvenanceInfo());
return ResponseEntity.ok(buildInfo);
}
@GetMapping("/provenance")
public ResponseEntity<Map<String, Object>> getProvenance() {
try {
String provenance = Files.readString(Path.of("/tekton/provenance.json"));
ObjectMapper mapper = new ObjectMapper();
return ResponseEntity.ok(mapper.readValue(provenance, Map.class));
} catch (Exception e) {
return ResponseEntity.status(404).build();
}
}
}

10. CI/CD Integration

GitHub Actions Workflow:

name: Java Build with Provenance
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Java
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
cache: 'maven'
- name: Build with Maven
run: mvn -B package -DskipTests
- name: Generate SBOM
run: |
mvn org.cyclonedx:cyclonedx-maven-plugin:2.7.4:makeAggregateBom
cp target/bom.json sbom.json
- name: Upload Build Results
uses: actions/upload-artifact@v3
with:
name: build-artifacts
path: |
target/*.jar
sbom.json
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- name: Verify Provenance
run: |
# Verify the built image has proper provenance
cosign verify-attestation \
--type slsaprovenance \
--key cosign.pub \
$IMAGE
# Check SLSA level
slsa-verifier verify-artifact \
--provenance-path provenance.json \
--source-uri github.com/${{ github.repository }} \
target/application.jar

11. Monitoring and Auditing

Provenance Dashboard:

# provenance-monitoring.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: provenance-audit
spec:
schedule: "0 * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: audit
image: curlimages/curl:latest
command:
- /bin/sh
- -c
- |
# Check recent builds for provenance compliance
kubectl get taskruns -l chains.tekton.dev/signed=true \
-o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.metadata.creationTimestamp}{"\t"}{.status.conditions[*].type}{"\n"}{end}'
# Verify stored provenance
cosign verify-attestation --key cosign.pub gcr.io/my-project/order-service:latest

12. Best Practices for Java Applications

1. Dependency Management:

# Ensure all dependencies are captured in SBOM
- name: capture-dependencies
image: maven:3.8.6-openjdk-17-slim
script: |
# Use CycloneDX for comprehensive Java SBOM
mvn org.cyclonedx:cyclonedx-maven-plugin:makeAggregateBom

2. Reproducible Builds:

# Dockerfile for reproducible Java builds
FROM eclipse-temurin:17-jdk as builder
WORKDIR /workspace
COPY . .
RUN ./mvnw -DskipTests package
FROM eclipse-temurin:17-jre
COPY --from=builder /workspace/target/*.jar app.jar
# Add provenance metadata
LABEL org.opencontainers.image.source="https://github.com/myorg/myapp"

3. Secure Signing:

# Use KMS or cloud-based key management
spec:
params:
- name: signer
value: "kms://projects/my-project/locations/global/keyRings/tekton/cryptoKeys/signing"

Benefits for Java Applications

  1. Supply Chain Security - Verifiable build and dependency information
  2. Compliance - Meet SLSA Level 2+ requirements
  3. Audit Trail - Complete history of builds and deployments
  4. Vulnerability Management - Trace dependencies to specific versions
  5. Reproducible Builds - Verify build integrity across environments

Conclusion

Tekton Chains provides a powerful foundation for establishing software supply chain security for Java applications. By capturing cryptographically signed provenance metadata from your CI/CD pipelines, you can verify the integrity and origin of every artifact deployed to production.

For Java development teams, this means:

  • Trustworthy Builds - Verified build processes and dependencies
  • Security Compliance - Meeting organizational and regulatory requirements
  • Rapid Incident Response - Quickly identify affected components during security incidents
  • Improved Governance - Complete audit trail of software changes

Start by implementing basic provenance capture for your Java builds, then gradually add more sophisticated verification and policy enforcement as your security maturity grows.


Call to Action: Begin by installing Tekton Chains in your development cluster and configuring it to capture basic build information from your Java pipelines. Start with signing your container images and gradually add SBOM generation and vulnerability scanning to create a comprehensive software supply chain security solution.

Java Logistics, Shipping Integration & Enterprise Inventory Automation (Tracking, ERP, RFID & Billing Systems)

https://macronepal.com/blog/aftership-tracking-in-java-enterprise-package-visibility/
Explains how to integrate AfterShip tracking services into Java applications to provide real-time shipment visibility, delivery status updates, and centralized tracking across multiple courier services.

https://macronepal.com/blog/shipping-integration-using-fedex-api-with-java-for-logistics-automation/
Explains how to integrate the FedEx API into Java systems to automate shipping tasks such as creating shipments, calculating delivery costs, generating shipping labels, and tracking packages.

https://macronepal.com/blog/shipping-and-logistics-integrating-ups-apis-with-java-applications/
Explains UPS API integration in Java to enable automated shipping operations including rate calculation, shipment scheduling, tracking, and delivery confirmation management.

https://macronepal.com/blog/generating-and-reading-qr-codes-for-products-in-java/
Explains how Java applications generate and read QR codes for product identification, tracking, and authentication, supporting faster inventory handling and product verification processes.

https://macronepal.com/blog/designing-a-robust-pick-and-pack-workflow-in-java/
Explains how to design an efficient pick-and-pack workflow in Java warehouse systems, covering order processing, item selection, packaging steps, and logistics preparation to improve fulfillment efficiency.

https://macronepal.com/blog/rfid-inventory-management-system-in-java-a-complete-guide/
Explains how RFID technology integrates with Java applications to automate inventory tracking, reduce manual errors, and enable real-time stock monitoring in warehouses and retail environments.

https://macronepal.com/blog/erp-integration-with-odoo-in-java/
Explains how Java applications connect with Odoo ERP systems to synchronize inventory, orders, customer records, and financial data across enterprise systems.

https://macronepal.com/blog/automated-invoice-generation-creating-professional-excel-invoices-with-apache-poi-in-java/
Explains how to automatically generate professional Excel invoices in Java using Apache POI, enabling structured billing documents and automated financial record creation.

https://macronepal.com/blog/enterprise-financial-integration-using-quickbooks-api-in-java-applications/
Explains QuickBooks API integration in Java to automate financial workflows such as invoice management, payment tracking, accounting synchronization, and financial reporting.

Leave a Reply

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


Macro Nepal Helper