Tetragon Security in Java: Complete Runtime Security & eBPF Guide


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:

  1. 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
  1. Read-only Filesystem:
securityContext:
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
  1. 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:

  1. Deep Observability: Kernel-level visibility into application behavior
  2. Real-time Protection: Immediate detection of security threats
  3. Policy Enforcement: Flexible security policies using Kubernetes CRDs
  4. Low Overhead: Efficient eBPF-based monitoring
  5. 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.

Leave a Reply

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


Macro Nepal Helper