Article
Tetragon is a powerful runtime security observability and enforcement tool that leverages eBPF (extended Berkeley Packet Filter) to provide deep visibility into application behavior and enforce security policies at the kernel level. This guide covers everything from basic setup to advanced security policies for Java applications.
Why Tetragon for Java Security?
- Runtime Protection: Real-time security monitoring and enforcement
- eBPF Powered: Kernel-level visibility without application changes
- Deep Observability: Process execution, file access, network activity monitoring
- Policy Enforcement: Flexible security policies using Kubernetes CRDs
- Low Overhead: Efficient monitoring with minimal performance impact
- Cloud Native: Designed for Kubernetes and containerized environments
Project Setup and Dependencies
Maven Dependencies:
<properties> <jackson.version>2.15.2</jackson.version> <kubernetes.client.version>6.7.2</kubernetes.client.version> <micrometer.version>1.11.5</micrometer.version> </properties> <dependencies> <!-- Kubernetes Client for Tetragon CRDs --> <dependency> <groupId>io.kubernetes</groupId> <artifactId>client-java</artifactId> <version>${kubernetes.client.version}</version> </dependency> <!-- JSON Processing --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>${jackson.version}</version> </dependency> <!-- Metrics --> <dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-core</artifactId> <version>${micrometer.version}</version> </dependency> <!-- HTTP Client for Tetragon API --> <dependency> <groupId>org.apache.httpcomponents.client5</groupId> <artifactId>httpclient5</artifactId> <version>5.2.1</version> </dependency> </dependencies> 1. Tetragon Configuration for Java
tetragon-config.yaml:
apiVersion: cilium.io/v1alpha1 kind: TracingPolicy metadata: name: "java-app-security" spec: # Monitor Java process execution kprobes: - call: "execve" syscall: true args: - index: 0 type: "string" - index: 1 type: "string" selectors: - matchBinaries: - operator: "In" values: - "/usr/bin/java" - "/opt/java/bin/java" - operator: "In" values: - "/app/java-app" # Monitor file operations in Java app kprobes: - call: "security_file_open" syscall: true args: - index: 0 type: "file" - index: 1 type: "int" selectors: - matchBinaries: - operator: "In" values: - "/usr/bin/java" matchArgs: - index: 0 operator: "Prefix" values: - "/etc/" - "/var/run/" - "/tmp/" # Network connectivity monitoring kprobes: - call: "tcp_connect" syscall: false return: false args: - index: 0 type: "sock" selectors: - matchBinaries: - operator: "In" values: - "/usr/bin/java" # System call monitoring for Java processes tracepoints: - subsystem: "raw_syscalls" event: "sys_enter" args: - index: 0 type: "int64" selectors: - matchBinaries: - operator: "In" values: - "/usr/bin/java" matchArgs: - index: 0 operator: "Equal" values: - "56" # connect - "42" # connect - "257" # openat # Java-specific system calls kprobes: - call: "do_sys_open" syscall: true args: - index: 0 type: "int" - index: 1 type: "string" selectors: - matchBinaries: - operator: "In" values: - "/usr/bin/java" matchArgs: - index: 1 operator: "Postfix" values: - ".jar" - ".class" - ".properties"
2. Java Security Agent Integration
Tetragon Security Agent:
package com.example.security.tetragon; import com.fasterxml.jackson.databind.ObjectMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import java.io.*; import java.net.HttpURLConnection; import java.net.URL; import java.nio.file.*; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @Service public class TetragonSecurityAgent { private static final Logger logger = LoggerFactory.getLogger(TetragonSecurityAgent.class); private final ObjectMapper objectMapper; private final TetragonConfig config; private final ScheduledExecutorService scheduler; private final Map<String, SecurityEvent> activeEvents; private final SecurityPolicyEngine policyEngine; // Tetragon API endpoints private static final String TETRAGON_API_BASE = "http://tetragon:54321"; private static final String EVENTS_ENDPOINT = "/v1/events"; private static final String METRICS_ENDPOINT = "/v1/metrics"; public TetragonSecurityAgent(TetragonConfig config) { this.objectMapper = new ObjectMapper(); this.config = config; this.scheduler = Executors.newScheduledThreadPool(3); this.activeEvents = new ConcurrentHashMap<>(); this.policyEngine = new SecurityPolicyEngine(); initializeAgent(); } private void initializeAgent() { // Start monitoring Tetragon events scheduler.scheduleAtFixedRate(this::fetchTetragonEvents, 0, 1, TimeUnit.SECONDS); // Process security events scheduler.scheduleAtFixedRate(this::processSecurityEvents, 5, 5, TimeUnit.SECONDS); // Report metrics scheduler.scheduleAtFixedRate(this::reportMetrics, 30, 30, TimeUnit.SECONDS); logger.info("Tetragon Security Agent initialized"); } public void fetchTetragonEvents() { try { URL url = new URL(TETRAGON_API_BASE + EVENTS_ENDPOINT); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); connection.setConnectTimeout(5000); connection.setReadTimeout(10000); if (connection.getResponseCode() == 200) { try (InputStream inputStream = connection.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { String line; while ((line = reader.readLine()) != null) { processTetragonEvent(line); } } } } catch (Exception e) { logger.warn("Failed to fetch Tetragon events: {}", e.getMessage()); } } private void processTetragonEvent(String eventJson) { try { TetragonEvent event = objectMapper.readValue(eventJson, TetragonEvent.class); // Filter events for our Java application if (isRelevantJavaEvent(event)) { SecurityEvent securityEvent = convertToSecurityEvent(event); // Check against security policies PolicyViolation violation = policyEngine.evaluate(securityEvent); if (violation != null) { handlePolicyViolation(violation, securityEvent); } // Store for further analysis activeEvents.put(securityEvent.getId(), securityEvent); } } catch (Exception e) { logger.error("Failed to process Tetragon event: {}", e.getMessage(), e); } } private boolean isRelevantJavaEvent(TetragonEvent event) { // Filter events for Java processes and our application return event.getProcess() != null && (event.getProcess().getBinary().contains("java") || event.getProcess().getPod() != null && event.getProcess().getPod().getName().contains(config.getAppName())); } private SecurityEvent convertToSecurityEvent(TetragonEvent tetragonEvent) { SecurityEvent event = new SecurityEvent(); event.setId(UUID.randomUUID().toString()); event.setTimestamp(new Date()); event.setEventType(tetragonEvent.getType()); if (tetragonEvent.getProcess() != null) { event.setProcessName(tetragonEvent.getProcess().getBinary()); event.setProcessId(tetragonEvent.getProcess().getPid()); event.setCommandLine(String.join(" ", tetragonEvent.getProcess().getArguments())); } // Extract event-specific data extractEventData(event, tetragonEvent); return event; } private void extractEventData(SecurityEvent event, TetragonEvent tetragonEvent) { switch (tetragonEvent.getType()) { case "PROCESS_EXEC": event.setDescription("Process execution: " + event.getProcessName()); break; case "FILE_OPEN": if (tetragonEvent.getFile() != null) { event.setDescription("File access: " + tetragonEvent.getFile().getPath()); event.setFilePath(tetragonEvent.getFile().getPath()); } break; case "NETWORK_CONNECT": if (tetragonEvent.getNetwork() != null) { event.setDescription("Network connection: " + tetragonEvent.getNetwork().getDestinationIp() + ":" + tetragonEvent.getNetwork().getDestinationPort()); event.setDestinationIp(tetragonEvent.getNetwork().getDestinationIp()); event.setDestinationPort(tetragonEvent.getNetwork().getDestinationPort()); } break; case "SYSCALL": event.setDescription("System call: " + tetragonEvent.getSyscall()); break; } } private void processSecurityEvents() { Iterator<Map.Entry<String, SecurityEvent>> iterator = activeEvents.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry<String, SecurityEvent> entry = iterator.next(); SecurityEvent event = entry.getValue(); // Remove old events if (isEventExpired(event)) { iterator.remove(); continue; } // Analyze event patterns analyzeEventPatterns(event); } } private void analyzeEventPatterns(SecurityEvent event) { // Detect suspicious patterns if (isSuspiciousFileAccess(event)) { logger.warn("Suspicious file access detected: {}", event.getDescription()); triggerAlert(event, "SUSPICIOUS_FILE_ACCESS"); } if (isSuspiciousNetworkConnection(event)) { logger.warn("Suspicious network connection detected: {}", event.getDescription()); triggerAlert(event, "SUSPICIOUS_NETWORK_CONNECTION"); } if (isPrivilegeEscalationAttempt(event)) { logger.warn("Possible privilege escalation attempt: {}", event.getDescription()); triggerAlert(event, "PRIVILEGE_ESCALATION_ATTEMPT"); } } private boolean isSuspiciousFileAccess(SecurityEvent event) { if (event.getFilePath() == null) return false; List<String> sensitivePaths = Arrays.asList( "/etc/passwd", "/etc/shadow", "/etc/kubernetes", "/var/run/secrets/kubernetes.io", "/proc/self" ); return sensitivePaths.stream().anyMatch(event.getFilePath()::contains); } private boolean isSuspiciousNetworkConnection(SecurityEvent event) { if (event.getDestinationIp() == null) return false; // Check for connections to suspicious IP ranges List<String> suspiciousRanges = Arrays.asList( "10.0.0.0/8", "192.168.0.0/16", "172.16.0.0/12" ); // In production, use proper IP range checking return event.getDestinationIp().startsWith("10.") || event.getDestinationIp().startsWith("192.168."); } private boolean isPrivilegeEscalationAttempt(SecurityEvent event) { return "setuid".equals(event.getEventType()) || "setgid".equals(event.getEventType()) || (event.getCommandLine() != null && (event.getCommandLine().contains("chmod") || event.getCommandLine().contains("sudo"))); } private void handlePolicyViolation(PolicyViolation violation, SecurityEvent event) { logger.error("Policy violation detected: {} - {}", violation.getPolicyName(), event.getDescription()); // Take action based on violation severity switch (violation.getSeverity()) { case CRITICAL: triggerIncidentResponse(event, violation); break; case HIGH: triggerAlert(event, violation.getPolicyName()); break; case MEDIUM: logger.warn("Policy violation: {}", violation.getPolicyName()); break; } // Report to security monitoring reportViolation(violation, event); } private void triggerAlert(SecurityEvent event, String alertType) { SecurityAlert alert = new SecurityAlert(); alert.setId(UUID.randomUUID().toString()); alert.setTimestamp(new Date()); alert.setAlertType(alertType); alert.setEvent(event); alert.setSeverity(Severity.HIGH); // Send to alerting system sendAlert(alert); } private void triggerIncidentResponse(SecurityEvent event, PolicyViolation violation) { logger.error("🚨 CRITICAL SECURITY INCIDENT: {}", violation.getPolicyName()); // Immediate response actions // 1. Isolate the pod/container // 2. Trigger forensic data collection // 3. Notify security team IncidentResponse response = new IncidentResponse(); response.setIncidentId(UUID.randomUUID().toString()); response.setTimestamp(new Date()); response.setEvent(event); response.setViolation(violation); response.setActionsTaken(Arrays.asList("POD_ISOLATION", "FORENSIC_COLLECTION")); executeIncidentResponse(response); } private void reportMetrics() { // Report security metrics to monitoring system Map<String, Object> metrics = new HashMap<>(); metrics.put("active_events", activeEvents.size()); metrics.put("policy_violations", policyEngine.getViolationCount()); metrics.put("security_alerts", policyEngine.getAlertCount()); // Send metrics to monitoring backend sendMetrics(metrics); } // Getters for monitoring public int getActiveEventCount() { return activeEvents.size(); } public List<SecurityEvent> getRecentEvents(int count) { return activeEvents.values().stream() .sorted(Comparator.comparing(SecurityEvent::getTimestamp).reversed()) .limit(count) .collect(ArrayList::new, ArrayList::add, ArrayList::addAll); } // Cleanup @PreDestroy public void shutdown() { scheduler.shutdown(); try { if (!scheduler.awaitTermination(10, TimeUnit.SECONDS)) { scheduler.shutdownNow(); } } catch (InterruptedException e) { scheduler.shutdownNow(); Thread.currentThread().interrupt(); } } // Placeholder implementations private void sendAlert(SecurityAlert alert) { // Implementation to send alert to security monitoring system logger.info("Security alert triggered: {}", alert.getAlertType()); } private void executeIncidentResponse(IncidentResponse response) { // Implementation for incident response actions logger.info("Incident response executed: {}", response.getIncidentId()); } private void sendMetrics(Map<String, Object> metrics) { // Implementation to send metrics to monitoring backend } private void reportViolation(PolicyViolation violation, SecurityEvent event) { // Implementation to report policy violations } private boolean isEventExpired(SecurityEvent event) { return System.currentTimeMillis() - event.getTimestamp().getTime() > 300000; // 5 minutes } } 3. Security Policy Engine
Policy Engine Implementation:
package com.example.security.tetragon; import org.springframework.stereotype.Component; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; import java.util.regex.Pattern; @Component public class SecurityPolicyEngine { private final Map<String, SecurityPolicy> policies; private final AtomicLong violationCount; private final AtomicLong alertCount; public SecurityPolicyEngine() { this.policies = new ConcurrentHashMap<>(); this.violationCount = new AtomicLong(0); this.alertCount = new AtomicLong(0); initializeDefaultPolicies(); } private void initializeDefaultPolicies() { // File access policies policies.put("RESTRICTED_FILE_ACCESS", SecurityPolicy.builder() .name("RESTRICTED_FILE_ACCESS") .description("Prevent access to sensitive files") .severity(Severity.HIGH) .condition(event -> event.getFilePath() != null && (event.getFilePath().contains("/etc/passwd") || event.getFilePath().contains("/etc/shadow") || event.getFilePath().contains("/proc/kcore"))) .build()); // Network policies policies.put("UNAUTHORIZED_NETWORK_ACCESS", SecurityPolicy.builder() .name("UNAUTHORIZED_NETWORK_ACCESS") .description("Prevent unauthorized network connections") .severity(Severity.MEDIUM) .condition(event -> event.getDestinationIp() != null && isPrivateIp(event.getDestinationIp()) && !isAllowedInternalService(event.getDestinationIp(), event.getDestinationPort())) .build()); // Process execution policies policies.put("UNAUTHORIZED_PROCESS_EXECUTION", SecurityPolicy.builder() .name("UNAUTHORIZED_PROCESS_EXECUTION") .description("Prevent execution of unauthorized processes") .severity(Severity.CRITICAL) .condition(event -> event.getEventType().equals("PROCESS_EXEC") && (event.getProcessName().contains("sh") || event.getProcessName().contains("bash") || event.getProcessName().contains("curl") || event.getProcessName().contains("wget"))) .build()); // System call policies policies.put("DANGEROUS_SYSCALLS", SecurityPolicy.builder() .name("DANGEROUS_SYSCALLS") .description("Monitor dangerous system calls") .severity(Severity.HIGH) .condition(event -> event.getEventType().equals("SYSCALL") && (event.getDescription().contains("ptrace") || event.getDescription().contains("keyctl") || event.getDescription().contains("bpf"))) .build()); } public PolicyViolation evaluate(SecurityEvent event) { for (SecurityPolicy policy : policies.values()) { if (policy.getCondition().test(event)) { violationCount.incrementAndGet(); return new PolicyViolation(policy, event); } } return null; } public void addPolicy(SecurityPolicy policy) { policies.put(policy.getName(), policy); } public void removePolicy(String policyName) { policies.remove(policyName); } public List<SecurityPolicy> getPolicies() { return new ArrayList<>(policies.values()); } public long getViolationCount() { return violationCount.get(); } public long getAlertCount() { return alertCount.get(); } public void incrementAlertCount() { alertCount.incrementAndGet(); } private boolean isPrivateIp(String ip) { return ip.startsWith("10.") || ip.startsWith("192.168.") || (ip.startsWith("172.") && isInPrivateRange(ip)); } private boolean isInPrivateRange(String ip) { // Check for 172.16.0.0 - 172.31.255.255 if (ip.startsWith("172.")) { String[] parts = ip.split("\\."); if (parts.length >= 2) { int secondOctet = Integer.parseInt(parts[1]); return secondOctet >= 16 && secondOctet <= 31; } } return false; } private boolean isAllowedInternalService(String ip, Integer port) { // Define allowed internal services Map<String, List<Integer>> allowedServices = Map.of( "10.0.0.10", Arrays.asList(53, 443), // DNS and HTTPS "10.0.0.20", Arrays.asList(9092) // Kafka ); List<Integer> allowedPorts = allowedServices.get(ip); return allowedPorts != null && allowedPorts.contains(port); } } // Policy classes class SecurityPolicy { private String name; private String description; private Severity severity; private java.util.function.Predicate<SecurityEvent> condition; // Builder pattern public static Builder builder() { return new Builder(); } public static class Builder { private SecurityPolicy policy = new SecurityPolicy(); public Builder name(String name) { policy.name = name; return this; } public Builder description(String description) { policy.description = description; return this; } public Builder severity(Severity severity) { policy.severity = severity; return this; } public Builder condition(java.util.function.Predicate<SecurityEvent> condition) { policy.condition = condition; return this; } public SecurityPolicy build() { return policy; } } // Getters public String getName() { return name; } public String getDescription() { return description; } public Severity getSeverity() { return severity; } public java.util.function.Predicate<SecurityEvent> getCondition() { return condition; } } class PolicyViolation { private String id; private Date timestamp; private SecurityPolicy policy; private SecurityEvent event; public PolicyViolation(SecurityPolicy policy, SecurityEvent event) { this.id = UUID.randomUUID().toString(); this.timestamp = new Date(); this.policy = policy; this.event = event; } // Getters public String getId() { return id; } public Date getTimestamp() { return timestamp; } public SecurityPolicy getPolicy() { return policy; } public SecurityEvent getEvent() { return event; } public String getPolicyName() { return policy.getName(); } public Severity getSeverity() { return policy.getSeverity(); } } enum Severity { LOW, MEDIUM, HIGH, CRITICAL } 4. Tetragon Event Models
Event Data Classes:
package com.example.security.tetragon; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.List; import java.util.Map; @JsonIgnoreProperties(ignoreUnknown = true) public class TetragonEvent { @JsonProperty("process") private ProcessInfo process; @JsonProperty("file") private FileInfo file; @JsonProperty("network") private NetworkInfo network; @JsonProperty("syscall") private String syscall; @JsonProperty("type") private String type; @JsonProperty("time") private String timestamp; // Getters and setters public ProcessInfo getProcess() { return process; } public void setProcess(ProcessInfo process) { this.process = process; } public FileInfo getFile() { return file; } public void setFile(FileInfo file) { this.file = file; } public NetworkInfo getNetwork() { return network; } public void setNetwork(NetworkInfo network) { this.network = network; } public String getSyscall() { return syscall; } public void setSyscall(String syscall) { this.syscall = syscall; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getTimestamp() { return timestamp; } public void setTimestamp(String timestamp) { this.timestamp = timestamp; } } @JsonIgnoreProperties(ignoreUnknown = true) class ProcessInfo { @JsonProperty("binary") private String binary; @JsonProperty("arguments") private List<String> arguments; @JsonProperty("pid") private Long pid; @JsonProperty("pod") private PodInfo pod; // Getters and setters public String getBinary() { return binary; } public void setBinary(String binary) { this.binary = binary; } public List<String> getArguments() { return arguments; } public void setArguments(List<String> arguments) { this.arguments = arguments; } public Long getPid() { return pid; } public void setPid(Long pid) { this.pid = pid; } public PodInfo getPod() { return pod; } public void setPod(PodInfo pod) { this.pod = pod; } } @JsonIgnoreProperties(ignoreUnknown = true) class PodInfo { @JsonProperty("name") private String name; @JsonProperty("namespace") private String namespace; @JsonProperty("labels") private Map<String, String> labels; // 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 Map<String, String> getLabels() { return labels; } public void setLabels(Map<String, String> labels) { this.labels = labels; } } @JsonIgnoreProperties(ignoreUnknown = true) class FileInfo { @JsonProperty("path") private String path; // Getters and setters public String getPath() { return path; } public void setPath(String path) { this.path = path; } } @JsonIgnoreProperties(ignoreUnknown = true) class NetworkInfo { @JsonProperty("destination_ip") private String destinationIp; @JsonProperty("destination_port") private Integer destinationPort; @JsonProperty("protocol") private String protocol; // Getters and setters public String getDestinationIp() { return destinationIp; } public void setDestinationIp(String destinationIp) { this.destinationIp = destinationIp; } public Integer getDestinationPort() { return destinationPort; } public void setDestinationPort(Integer destinationPort) { this.destinationPort = destinationPort; } public String getProtocol() { return protocol; } public void setProtocol(String protocol) { this.protocol = protocol; } } class SecurityEvent { private String id; private Date timestamp; private String eventType; private String processName; private Long processId; private String commandLine; private String description; private String filePath; private String destinationIp; private Integer destinationPort; // Getters and setters public String getId() { return id; } public void setId(String id) { this.id = id; } public Date getTimestamp() { return timestamp; } public void setTimestamp(Date timestamp) { this.timestamp = timestamp; } public String getEventType() { return eventType; } public void setEventType(String eventType) { this.eventType = eventType; } public String getProcessName() { return processName; } public void setProcessName(String processName) { this.processName = processName; } public Long getProcessId() { return processId; } public void setProcessId(Long processId) { this.processId = processId; } public String getCommandLine() { return commandLine; } public void setCommandLine(String commandLine) { this.commandLine = commandLine; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getFilePath() { return filePath; } public void setFilePath(String filePath) { this.filePath = filePath; } public String getDestinationIp() { return destinationIp; } public void setDestinationIp(String destinationIp) { this.destinationIp = destinationIp; } public Integer getDestinationPort() { return destinationPort; } public void setDestinationPort(Integer destinationPort) { this.destinationPort = destinationPort; } } class SecurityAlert { private String id; private Date timestamp; private String alertType; private Severity severity; private SecurityEvent event; // Getters and setters public String getId() { return id; } public void setId(String id) { this.id = id; } public Date getTimestamp() { return timestamp; } public void setTimestamp(Date timestamp) { this.timestamp = timestamp; } public String getAlertType() { return alertType; } public void setAlertType(String alertType) { this.alertType = alertType; } public Severity getSeverity() { return severity; } public void setSeverity(Severity severity) { this.severity = severity; } public SecurityEvent getEvent() { return event; } public void setEvent(SecurityEvent event) { this.event = event; } } class IncidentResponse { private String incidentId; private Date timestamp; private SecurityEvent event; private PolicyViolation violation; private List<String> actionsTaken; // Getters and setters public String getIncidentId() { return incidentId; } public void setIncidentId(String incidentId) { this.incidentId = incidentId; } public Date getTimestamp() { return timestamp; } public void setTimestamp(Date timestamp) { this.timestamp = timestamp; } public SecurityEvent getEvent() { return event; } public void setEvent(SecurityEvent event) { this.event = event; } public PolicyViolation getViolation() { return violation; } public void setViolation(PolicyViolation violation) { this.violation = violation; } public List<String> getActionsTaken() { return actionsTaken; } public void setActionsTaken(List<String> actionsTaken) { this.actionsTaken = actionsTaken; } } 5. Kubernetes Deployment with Tetragon
Security-Enhanced Deployment:
apiVersion: apps/v1 kind: Deployment metadata: name: order-service labels: app: order-service security-tier: high spec: replicas: 3 selector: matchLabels: app: order-service template: metadata: labels: app: order-service security-tier: high annotations: # Tetragon annotations for enhanced monitoring tetragon.cilium.io/tracing: "enabled" tetragon.cilium.io/tracing-policy: "java-app-security" # Security context for the pod seccomp.security.alpha.kubernetes.io/pod: "runtime/default" apparmor.security.beta.kubernetes.io/pod: "runtime/default" spec: # Security context for the pod securityContext: runAsNonRoot: true runAsUser: 1000 runAsGroup: 1000 fsGroup: 1000 seccompProfile: type: RuntimeDefault serviceAccountName: order-service containers: - name: order-service image: gcr.io/my-project/order-service:latest # Security context for the container securityContext: allowPrivilegeEscalation: false capabilities: drop: - ALL readOnlyRootFilesystem: true runAsNonRoot: true runAsUser: 1000 privileged: false ports: - containerPort: 8080 protocol: TCP env: - name: JAVA_TOOL_OPTIONS value: "-javaagent:/app/security/tetragon-agent.jar -Dsecurity.tetragon.enabled=true" - name: TETRAGON_ENDPOINT value: "http://tetragon.tetragon.svc.cluster.local:54321" - name: APP_NAME value: "order-service" resources: requests: memory: "512Mi" cpu: "250m" limits: memory: "1Gi" cpu: "500m" livenessProbe: httpGet: path: /actuator/health port: 8080 initialDelaySeconds: 30 periodSeconds: 10 readinessProbe: httpGet: path: /actuator/health port: 8080 initialDelaySeconds: 20 periodSeconds: 5 # Volume mounts for security volumeMounts: - name: security-agent mountPath: /app/security readOnly: true - name: tmp mountPath: /tmp - name: logs mountPath: /app/logs # Security agent sidecar - name: tetragon-agent image: gcr.io/my-project/tetragon-java-agent:latest securityContext: allowPrivilegeEscalation: false capabilities: drop: - ALL readOnlyRootFilesystem: true runAsNonRoot: true runAsUser: 1001 volumeMounts: - name: security-agent mountPath: /app/security - name: tetragon-socket mountPath: /var/run/tetragon resources: requests: memory: "128Mi" cpu: "100m" limits: memory: "256Mi" cpu: "200m" # Volumes volumes: - name: security-agent emptyDir: {} - name: tmp emptyDir: medium: Memory - name: logs emptyDir: {} - name: tetragon-socket hostPath: path: /var/run/tetragon type: Directory 6. REST API for Security Management
Security Management Controller:
package com.example.security.controller; import com.example.security.tetragon.*; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.List; import java.util.Map; @RestController @RequestMapping("/api/security") public class SecurityController { private final TetragonSecurityAgent securityAgent; private final SecurityPolicyEngine policyEngine; public SecurityController(TetragonSecurityAgent securityAgent, SecurityPolicyEngine policyEngine) { this.securityAgent = securityAgent; this.policyEngine = policyEngine; } @GetMapping("/events") public ResponseEntity<List<SecurityEvent>> getRecentEvents( @RequestParam(defaultValue = "50") int count) { List<SecurityEvent> events = securityAgent.getRecentEvents(count); return ResponseEntity.ok(events); } @GetMapping("/metrics") public ResponseEntity<Map<String, Object>> getSecurityMetrics() { Map<String, Object> metrics = Map.of( "activeEvents", securityAgent.getActiveEventCount(), "policyViolations", policyEngine.getViolationCount(), "securityAlerts", policyEngine.getAlertCount(), "policiesCount", policyEngine.getPolicies().size() ); return ResponseEntity.ok(metrics); } @GetMapping("/policies") public ResponseEntity<List<SecurityPolicy>> getPolicies() { List<SecurityPolicy> policies = policyEngine.getPolicies(); return ResponseEntity.ok(policies); } @PostMapping("/policies") public ResponseEntity<String> addPolicy(@RequestBody SecurityPolicy policy) { policyEngine.addPolicy(policy); return ResponseEntity.ok("Policy added successfully"); } @DeleteMapping("/policies/{policyName}") public ResponseEntity<String> removePolicy(@PathVariable String policyName) { policyEngine.removePolicy(policyName); return ResponseEntity.ok("Policy removed successfully"); } @PostMapping("/alerts/test") public ResponseEntity<String> testAlert() { // Create a test security event SecurityEvent testEvent = new SecurityEvent(); testEvent.setId(UUID.randomUUID().toString()); testEvent.setTimestamp(new Date()); testEvent.setEventType("TEST_ALERT"); testEvent.setProcessName("java"); testEvent.setDescription("Test security alert"); // Trigger alert securityAgent.triggerAlert(testEvent, "TEST_ALERT"); return ResponseEntity.ok("Test alert triggered"); } @GetMapping("/status") public ResponseEntity<Map<String, Object>> getSecurityStatus() { Map<String, Object> status = Map.of( "agentStatus", "ACTIVE", "eventProcessing", "ENABLED", "policyEnforcement", "ENABLED", "lastEventProcessed", new Date(), "health", "HEALTHY" ); return ResponseEntity.ok(status); } } 7. Configuration Classes
Application Configuration:
# application.yml security: tetragon: enabled: true endpoint: ${TETRAGON_ENDPOINT:http://tetragon:54321} app-name: ${APP_NAME:order-service} policies: enabled: true auto-reload: true monitoring: enabled: true metrics-interval: 30s alerts: enabled: true critical-severity: true slack-webhook: ${SLACK_SECURITY_WEBHOOK} logging: level: com.example.security.tetragon: INFO management: endpoints: web: exposure: include: health,metrics,info,security endpoint: health: show-details: always security: enabled: true Configuration Class:
package com.example.security.tetragon; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; @Configuration @ConfigurationProperties(prefix = "security.tetragon") public class TetragonConfig { private boolean enabled = true; private String endpoint = "http://tetragon:54321"; private String appName = "unknown-app"; private Policies policies = new Policies(); private Monitoring monitoring = new Monitoring(); private Alerts alerts = new Alerts(); // Getters and setters public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } public String getEndpoint() { return endpoint; } public void setEndpoint(String endpoint) { this.endpoint = endpoint; } public String getAppName() { return appName; } public void setAppName(String appName) { this.appName = appName; } public Policies getPolicies() { return policies; } public void setPolicies(Policies policies) { this.policies = policies; } public Monitoring getMonitoring() { return monitoring; } public void setMonitoring(Monitoring monitoring) { this.monitoring = monitoring; } public Alerts getAlerts() { return alerts; } public void setAlerts(Alerts alerts) { this.alerts = alerts; } public static class Policies { private boolean enabled = true; private boolean autoReload = true; // Getters and setters public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } public boolean isAutoReload() { return autoReload; } public void setAutoReload(boolean autoReload) { this.autoReload = autoReload; } } public static class Monitoring { private boolean enabled = true; private String metricsInterval = "30s"; // Getters and setters public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } public String getMetricsInterval() { return metricsInterval; } public void setMetricsInterval(String metricsInterval) { this.metricsInterval = metricsInterval; } } public static class Alerts { private boolean enabled = true; private boolean criticalSeverity = true; private String slackWebhook; // Getters and setters public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } public boolean isCriticalSeverity() { return criticalSeverity; } public void setCriticalSeverity(boolean criticalSeverity) { this.criticalSeverity = criticalSeverity; } public String getSlackWebhook() { return slackWebhook; } public void setSlackWebhook(String slackWebhook) { this.slackWebhook = slackWebhook; } } } 8. Best Practices and Security Hardening
Security Hardening Tips:
- Minimal Base Images:
FROM eclipse-temurin:17-jre-alpine # Remove unnecessary packages RUN apk del --no-cache \ openssl \ && rm -rf /var/cache/apk/* # Use non-root user RUN adduser -D -u 1000 javaapp USER javaapp
- Read-only Filesystem:
securityContext: readOnlyRootFilesystem: true runAsNonRoot: true runAsUser: 1000
- Network Policies:
apiVersion: cilium.io/v1 kind: CiliumNetworkPolicy metadata: name: order-service-network-policy spec: endpointSelector: matchLabels: app: order-service egress: - toEntities: - world toPorts: - ports: - port: "443" protocol: TCP - port: "80" protocol: TCP - toEndpoints: - matchLabels: app: database toPorts: - ports: - port: "5432" protocol: TCP
Conclusion
Tetragon provides powerful runtime security for Java applications with:
- Deep Observability: Kernel-level visibility into application behavior
- Real-time Protection: Immediate detection of security threats
- Policy Enforcement: Flexible security policies using Kubernetes CRDs
- Low Overhead: Efficient eBPF-based monitoring
- Cloud Native: Designed for Kubernetes environments
By implementing the patterns shown above, you can establish a robust runtime security posture that detects threats in real-time, enforces security policies, and provides comprehensive visibility into your Java application's behavior.