google-site-verification: google61fe8ba583a51912.html
AppArmor Profiles in Java: Complete Implementation Guide

Introduction to AppArmor

AppArmor (Application Armor) is a Linux security module that allows system administrators to restrict programs' capabilities with per-program profiles. While Java applications don't directly interact with AppArmor, you can manage AppArmor profiles and integrate them with Java applications for enhanced security.

AppArmor Concepts for Java Applications

  • Profile Enforcement - Restricting application capabilities
  • Policy Management - Creating and managing security profiles
  • Java Native Interface (JNI) - Interacting with system-level security
  • Process Confinement - Restricting Java processes capabilities

Dependencies and Setup

Maven Configuration

<properties> <jna.version>5.13.0</jna.version> <commons-exec.version>1.3</commons-exec.version> </properties> <dependencies> <!-- JNA for native system calls --> <dependency> <groupId>net.java.dev.jna</groupId> <artifactId>jna</artifactId> <version>${jna.version}</version> </dependency> <dependency> <groupId>net.java.dev.jna</groupId> <artifactId>jna-platform</artifactId> <version>${jna.version}</version> </dependency> <!-- Apache Commons Exec for process execution --> <dependency> <groupId>org.apache.commons</groupId> artifactId>commons-exec</artifactId> <version>${commons-exec.version}</version> </dependency> <!-- Logging --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>2.0.7</version> </dependency> </dependencies>

Core AppArmor Integration

AppArmor Native Interface

package com.apparmor.integration; import com.sun.jna.Library; import com.sun.jna.Native; import com.sun.jna.Pointer; import com.sun.jna.ptr.IntByReference; public interface AppArmorJNA extends Library { AppArmorJNA INSTANCE = Native.load("apparmor", AppArmorJNA.class); int aa_change_hat(String hat, long token); int aa_change_profile(String profile); int aa_find_mountpoint(String mountpoint, Pointer buffer, IntByReference size); int aa_getcon(String[] label, String[] mode); } public class AppArmorNativeAccess { private static final AppArmorJNA appArmor = AppArmorJNA.INSTANCE; public static boolean changeProfile(String profile) { int result = appArmor.aa_change_profile(profile); return result == 0; } public static boolean changeHat(String hat, long token) { int result = appArmor.aa_change_hat(hat, token); return result == 0; } public static String getCurrentContext() { String[] label = new String[1]; String[] mode = new String[1]; int result = appArmor.aa_getcon(label, mode); if (result == 0) { return label[0] + " (" + mode[0] + ")"; } return null; } }

AppArmor Profile Manager

package com.apparmor.manager; import org.apache.commons.exec.CommandLine; import org.apache.commons.exec.DefaultExecutor; import org.apache.commons.exec.PumpStreamHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; public class AppArmorProfileManager { private static final Logger logger = LoggerFactory.getLogger(AppArmorProfileManager.class); private static final String APPARMOR_PROFILES_DIR = "/etc/apparmor.d"; private static final String APPARMOR_STATUS_CMD = "aa-status"; private static final String APPARMOR_PARSER_CMD = "apparmor_parser"; /** * Check if AppArmor is enabled on the system */ public static boolean isAppArmorEnabled() { try { String output = executeCommand(APPARMOR_STATUS_CMD); return output.contains("apparmor module is loaded") && output.contains("apparmor filesystem is mounted"); } catch (Exception e) { logger.warn("AppArmor status check failed: {}", e.getMessage()); return false; } } /** * Get loaded AppArmor profiles */ public static List<String> getLoadedProfiles() throws IOException { String output = executeCommand(APPARMOR_STATUS_CMD); List<String> profiles = new ArrayList<>(); // Parse the output to extract profile names String[] lines = output.split("\n"); boolean inProfilesSection = false; for (String line : lines) { if (line.contains("profiles are loaded")) { inProfilesSection = true; continue; } if (inProfilesSection && line.contains("profiles are in enforce mode")) { break; } if (inProfilesSection && !line.trim().isEmpty()) { String profileName = line.trim().split(" ")[0]; profiles.add(profileName); } } return profiles; } /** * Create a new AppArmor profile for a Java application */ public static boolean createJavaProfile(String profileName, String javaExecutable, String applicationPath) throws IOException { String profileContent = generateJavaProfile(profileName, javaExecutable, applicationPath); Path profilePath = Paths.get(APPARMOR_PROFILES_DIR, profileName); // Write profile to file Files.write(profilePath, profileContent.getBytes()); // Load the profile return loadProfile(profilePath.toString()); } /** * Generate AppArmor profile for Java application */ private static String generateJavaProfile(String profileName, String javaExecutable, String applicationPath) { StringBuilder profile = new StringBuilder(); profile.append("#include <tunables/global>\n\n"); profile.append("profile ").append(profileName).append(" ").append(javaExecutable).append(" {\n"); profile.append(" #include <abstractions/base>\n"); profile.append(" #include <abstractions/openssl>\n\n"); // Java executable access profile.append(" ").append(javaExecutable).append(" mr,\n\n"); // Application JAR/WAR files profile.append(" ").append(applicationPath).append("/** r,\n"); profile.append(" ").append(applicationPath).append("/**/*.jar r,\n"); profile.append(" ").append(applicationPath).append("/**/*.class r,\n\n"); // Temporary files profile.append(" /tmp/** rw,\n"); profile.append(" /var/tmp/** rw,\n\n"); // Log files profile.append(" /var/log/").append(profileName).append("/** rw,\n"); profile.append(" ").append(applicationPath).append("/logs/** rw,\n\n"); // Network access profile.append(" network inet stream,\n"); profile.append(" network inet6 stream,\n\n"); // Database access (if needed) profile.append(" /run/mysqld/mysqld.sock rw,\n\n"); // JVM required paths profile.append(" /usr/lib/jvm/** mr,\n"); profile.append(" /etc/java/** r,\n"); profile.append(" /proc/** r,\n\n"); profile.append(" # Capabilities\n"); profile.append(" capability setgid,\n"); profile.append(" capability setuid,\n\n"); profile.append(" # Deny everything else\n"); profile.append(" deny /** w,\n"); profile.append("}\n"); return profile.toString(); } /** * Load AppArmor profile */ public static boolean loadProfile(String profilePath) throws IOException { CommandLine cmd = CommandLine.parse(APPARMOR_PARSER_CMD); cmd.addArgument("-r"); // Replace existing profile cmd.addArgument(profilePath); return executeCommand(cmd) == 0; } /** * Unload AppArmor profile */ public static boolean unloadProfile(String profileName) throws IOException { CommandLine cmd = CommandLine.parse(APPARMOR_PARSER_CMD); cmd.addArgument("-R"); // Remove profile cmd.addArgument(Paths.get(APPARMOR_PROFILES_DIR, profileName).toString()); return executeCommand(cmd) == 0; } /** * Execute shell command and return output */ private static String executeCommand(String command) throws IOException { CommandLine cmd = CommandLine.parse(command); return executeCommandWithOutput(cmd); } private static String executeCommandWithOutput(CommandLine cmd) throws IOException { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); DefaultExecutor executor = new DefaultExecutor(); PumpStreamHandler streamHandler = new PumpStreamHandler(outputStream); executor.setStreamHandler(streamHandler); int exitValue = executor.execute(cmd); if (exitValue != 0) { throw new IOException("Command failed with exit code: " + exitValue); } return outputStream.toString(); } private static int executeCommand(CommandLine cmd) throws IOException { DefaultExecutor executor = new DefaultExecutor(); return executor.execute(cmd); } }

Java Application Security Wrapper

AppArmor Security Manager

package com.apparmor.security; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.security.SecureRandom; public class AppArmorSecurityManager { private static final Logger logger = LoggerFactory.getLogger(AppArmorSecurityManager.class); private final String profileName; private final String applicationName; private final Path applicationPath; private long hatToken; public AppArmorSecurityManager(String applicationName, Path applicationPath) { this.applicationName = applicationName; this.applicationPath = applicationPath; this.profileName = applicationName.replaceAll("[^a-zA-Z0-9_-]", "_"); this.hatToken = new SecureRandom().nextLong(); } /** * Initialize AppArmor security for the application */ public boolean initialize() { if (!AppArmorProfileManager.isAppArmorEnabled()) { logger.warn("AppArmor is not enabled on this system"); return false; } try { // Create and load profile String javaExecutable = System.getProperty("java.home") + "/bin/java"; boolean success = AppArmorProfileManager.createJavaProfile( profileName, javaExecutable, applicationPath.toString()); if (success) { logger.info("AppArmor profile '{}' loaded successfully", profileName); return true; } else { logger.error("Failed to load AppArmor profile '{}'", profileName); return false; } } catch (Exception e) { logger.error("Error initializing AppArmor security: {}", e.getMessage(), e); return false; } } /** * Switch to a more restrictive hat (subprofile) */ public boolean enterRestrictedMode(String hatName) { try { boolean success = AppArmorNativeAccess.changeHat(hatName, hatToken); if (success) { logger.info("Entered restricted mode with hat '{}'", hatName); } else { logger.warn("Failed to enter restricted mode with hat '{}'", hatName); } return success; } catch (Exception e) { logger.error("Error entering restricted mode: {}", e.getMessage(), e); return false; } } /** * Exit restricted mode */ public boolean exitRestrictedMode() { try { boolean success = AppArmorNativeAccess.changeHat(null, hatToken); if (success) { logger.info("Exited restricted mode"); } return success; } catch (Exception e) { logger.error("Error exiting restricted mode: {}", e.getMessage(), e); return false; } } /** * Get current AppArmor context */ public String getSecurityContext() { return AppArmorNativeAccess.getCurrentContext(); } /** * Clean up AppArmor profile */ public void cleanup() { try { AppArmorProfileManager.unloadProfile(profileName); logger.info("AppArmor profile '{}' unloaded", profileName); } catch (Exception e) { logger.error("Error cleaning up AppArmor profile: {}", e.getMessage(), e); } } /** * Create hat profile for specific operations */ public boolean createHatProfile(String hatName, String operations) { String hatProfile = generateHatProfile(hatName, operations); Path hatPath = Paths.get("/etc/apparmor.d", profileName + "." + hatName); try { Files.write(hatPath, hatProfile.getBytes()); return AppArmorProfileManager.loadProfile(hatPath.toString()); } catch (IOException e) { logger.error("Error creating hat profile: {}", e.getMessage(), e); return false; } } private String generateHatProfile(String hatName, String operations) { return String.format( "hat %s.%s {\n" + " #include <abstractions/base>\n" + " %s\n" + "}\n", profileName, hatName, operations ); } }

Spring Boot Integration

AppArmor Configuration

package com.apparmor.config; import com.apparmor.security.AppArmorSecurityManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.annotation.PreDestroy; import java.nio.file.Paths; @Configuration public class AppArmorConfig { private static final Logger logger = LoggerFactory.getLogger(AppArmorConfig.class); @Value("${app.name:myapp}") private String appName; @Value("${app.path:./}") private String appPath; @Value("${apparmor.enabled:false}") private boolean appArmorEnabled; private AppArmorSecurityManager appArmorManager; @Bean public AppArmorSecurityManager appArmorSecurityManager() { if (!appArmorEnabled) { logger.info("AppArmor security is disabled"); return null; } appArmorManager = new AppArmorSecurityManager(appName, Paths.get(appPath)); boolean initialized = appArmorManager.initialize(); if (initialized) { logger.info("AppArmor security initialized successfully"); // Create hat profiles for different operations createHatProfiles(); } else { logger.warn("AppArmor security initialization failed"); } return appArmorManager; } private void createHatProfiles() { // File operations hat appArmorManager.createHatProfile("file_ops", "/home/app/data/** rw,\n" + "deny /** w,"); // Network operations hat appArmorManager.createHatProfile("network_ops", "network inet stream,\n" + "deny /** w,"); // Database operations hat appArmorManager.createHatProfile("db_ops", "/var/run/mysqld/mysqld.sock rw,\n" + "deny /** w,"); } @PreDestroy public void cleanup() { if (appArmorManager != null) { appArmorManager.cleanup(); } } }

Secure Service Implementation

package com.apparmor.service; import com.apparmor.security.AppArmorSecurityManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @Service public class SecureFileService { private static final Logger logger = LoggerFactory.getLogger(SecureFileService.class); @Autowired(required = false) private AppArmorSecurityManager appArmorManager; /** * Securely read file with AppArmor hat */ public String readFileSecurely(String filePath) throws IOException { if (appArmorManager != null) { appArmorManager.enterRestrictedMode("file_ops"); } try { Path path = Paths.get(filePath); if (!Files.exists(path)) { throw new IOException("File not found: " + filePath); } return new String(Files.readAllBytes(path)); } finally { if (appArmorManager != null) { appArmorManager.exitRestrictedMode(); } } } /** * Securely write file with AppArmor hat */ public void writeFileSecurely(String filePath, String content) throws IOException { if (appArmorManager != null) { appArmorManager.enterRestrictedMode("file_ops"); } try { Path path = Paths.get(filePath); Files.createDirectories(path.getParent()); Files.write(path, content.getBytes()); } finally { if (appArmorManager != null) { appArmorManager.exitRestrictedMode(); } } } /** * Perform secure network operation */ public void performSecureNetworkOperation() { if (appArmorManager != null) { appArmorManager.enterRestrictedMode("network_ops"); } try { // Perform network operations here logger.info("Performing secure network operation"); } finally { if (appArmorManager != null) { appArmorManager.exitRestrictedMode(); } } } /** * Get current security context */ public String getSecurityContext() { if (appArmorManager != null) { return appArmorManager.getSecurityContext(); } return "AppArmor not enabled"; } }

REST Controller with AppArmor Security

package com.apparmor.controller; import com.apparmor.service.SecureFileService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.HashMap; import java.util.Map; @RestController @RequestMapping("/api/secure") public class SecureController { @Autowired private SecureFileService secureFileService; @GetMapping("/file") public ResponseEntity<Map<String, Object>> readFile(@RequestParam String path) { Map<String, Object> response = new HashMap<>(); try { String content = secureFileService.readFileSecurely(path); response.put("success", true); response.put("content", content); response.put("securityContext", secureFileService.getSecurityContext()); return ResponseEntity.ok(response); } catch (Exception e) { response.put("success", false); response.put("error", e.getMessage()); return ResponseEntity.badRequest().body(response); } } @PostMapping("/file") public ResponseEntity<Map<String, Object>> writeFile( @RequestParam String path, @RequestBody String content) { Map<String, Object> response = new HashMap<>(); try { secureFileService.writeFileSecurely(path, content); response.put("success", true); response.put("message", "File written successfully"); response.put("securityContext", secureFileService.getSecurityContext()); return ResponseEntity.ok(response); } catch (Exception e) { response.put("success", false); response.put("error", e.getMessage()); return ResponseEntity.badRequest().body(response); } } @GetMapping("/network") public ResponseEntity<Map<String, Object>> performNetworkOperation() { Map<String, Object> response = new HashMap<>(); try { secureFileService.performSecureNetworkOperation(); response.put("success", true); response.put("message", "Network operation completed"); response.put("securityContext", secureFileService.getSecurityContext()); return ResponseEntity.ok(response); } catch (Exception e) { response.put("success", false); response.put("error", e.getMessage()); return ResponseEntity.badRequest().body(response); } } @GetMapping("/status") public ResponseEntity<Map<String, Object>> getSecurityStatus() { Map<String, Object> response = new HashMap<>(); response.put("securityContext", secureFileService.getSecurityContext()); response.put("appArmorEnabled", secureFileService.getSecurityContext().contains("AppArmor")); return ResponseEntity.ok(response); } }

Advanced AppArmor Profile Templates

Web Application Profile Template

package com.apparmor.templates; public class AppArmorTemplates { public static String generateWebAppProfile(String profileName, String appPath, String webRoot, int port) { return String.format( "#include <tunables/global>\n\n" + "profile %s /usr/bin/java {\n" + " #include <abstractions/base>\n" + " #include <abstractions/openssl>\n" + " #include <abstractions/nameservice>\n\n" + " # Java runtime\n" + " /usr/lib/jvm/** mr,\n" + " %s/** r,\n\n" + " # Web application files\n" + " %s/** r,\n" + " %s/WEB-INF/** r,\n" + " %s/WEB-INF/lib/*.jar r,\n\n" + " # Temporary files\n" + " /tmp/** rw,\n" + " /var/tmp/** rw,\n\n" + " # Log files\n" + " /var/log/%s/** rw,\n" + " %s/logs/** rw,\n\n" + " # Network\n" + " network inet stream,\n" + " network inet6 stream,\n\n" + " # Port binding\n" + " /proc/sys/net/ipv4/ip_local_port_range r,\n" + " /proc/%s/root/proc/sys/net/ipv4/ip_local_port_range r,\n\n" + " # Database access\n" + " /run/mysqld/mysqld.sock rw,\n" + " /var/run/mysqld/mysqld.sock rw,\n\n" + " # System resources\n" + " /proc/** r,\n" + " /sys/** r,\n\n" + " # Capabilities\n" + " capability setgid,\n" + " capability setuid,\n\n" + " # Deny sensitive paths\n" + " deny /etc/shadow r,\n" + " deny /root/** rw,\n" + " deny /home/*/.** rw,\n" + "}\n", profileName, appPath, webRoot, webRoot, webRoot, profileName, appPath, profileName ); } public static String generateMicroserviceProfile(String profileName, String appPath, String[] allowedEndpoints) { StringBuilder profile = new StringBuilder(); profile.append("#include <tunables/global>\n\n"); profile.append("profile ").append(profileName).append(" /usr/bin/java {\n"); profile.append(" #include <abstractions/base>\n"); profile.append(" #include <abstractions/openssl>\n\n"); // Application files profile.append(" ").append(appPath).append("/** r,\n"); profile.append(" ").append(appPath).append("/*.jar r,\n\n"); // Network restrictions profile.append(" network inet stream,\n"); profile.append(" network inet6 stream,\n\n"); // Specific endpoint access for (String endpoint : allowedEndpoints) { profile.append(" # Access to ").append(endpoint).append("\n"); } profile.append(" # Logging\n"); profile.append(" /var/log/").append(profileName).append("/** rw,\n\n"); profile.append(" # Deny unnecessary access\n"); profile.append(" deny /etc/passwd w,\n"); profile.append(" deny /etc/shadow r,\n"); profile.append("}\n"); return profile.toString(); } }

Monitoring and Auditing

AppArmor Audit Service

package com.apparmor.audit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; @Service public class AppArmorAuditService { private static final Logger logger = LoggerFactory.getLogger(AppArmorAuditService.class); private static final String AUDIT_LOG_PATH = "/var/log/audit/audit.log"; private static final String KERNEL_LOG_PATH = "/var/log/kern.log"; /** * Monitor AppArmor denials */ @Scheduled(fixedRate = 30000) // Every 30 seconds public void monitorAppArmorDenials() { try { List<String> denials = getRecentAppArmorDenials(); if (!denials.isEmpty()) { logger.warn("Found {} AppArmor denials:", denials.size()); for (String denial : denials) { logger.warn("AppArmor Denial: {}", denial); // Send alert, log to security system, etc. } } } catch (Exception e) { logger.error("Error monitoring AppArmor denials: {}", e.getMessage()); } } /** * Get recent AppArmor denials from logs */ public List<String> getRecentAppArmorDenials() throws IOException { List<String> denials = new ArrayList<>(); // Check audit log if (Files.exists(Paths.get(AUDIT_LOG_PATH))) { denials.addAll(parseAuditLog(AUDIT_LOG_PATH)); } // Check kernel log if (Files.exists(Paths.get(KERNEL_LOG_PATH))) { denials.addAll(parseKernelLog(KERNEL_LOG_PATH)); } return denials; } private List<String> parseAuditLog(String logPath) throws IOException { List<String> denials = new ArrayList<>(); Path path = Paths.get(logPath); // Read last 1000 lines (adjust as needed) List<String> lines = Files.readAllLines(path); int start = Math.max(0, lines.size() - 1000); for (int i = start; i < lines.size(); i++) { String line = lines.get(i); if (line.contains("apparmor") && line.contains("DENIED")) { denials.add(line); } } return denials; } private List<String> parseKernelLog(String logPath) throws IOException { List<String> denials = new ArrayList<>(); Path path = Paths.get(logPath); List<String> lines = Files.readAllLines(path); int start = Math.max(0, lines.size() - 1000); for (int i = start; i < lines.size(); i++) { String line = lines.get(i); if (line.contains("apparmor") && (line.contains("denied") || line.contains("DENIED"))) { denials.add(line); } } return denials; } /** * Generate security report */ public SecurityReport generateSecurityReport() throws IOException { List<String> recentDenials = getRecentAppArmorDenials(); SecurityReport report = new SecurityReport(); report.setTimestamp(LocalDateTime.now()); report.setTotalDenials(recentDenials.size()); report.setRecentDenials(recentDenials); report.setAppArmorEnabled(AppArmorProfileManager.isAppArmorEnabled()); return report; } public static class SecurityReport { private LocalDateTime timestamp; private int totalDenials; private List<String> recentDenials; private boolean appArmorEnabled; // Getters and setters public LocalDateTime getTimestamp() { return timestamp; } public void setTimestamp(LocalDateTime timestamp) { this.timestamp = timestamp; } public int getTotalDenials() { return totalDenials; } public void setTotalDenials(int totalDenials) { this.totalDenials = totalDenials; } public List<String> getRecentDenials() { return recentDenials; } public void setRecentDenials(List<String> recentDenials) { this.recentDenials = recentDenials; } public boolean isAppArmorEnabled() { return appArmorEnabled; } public void setAppArmorEnabled(boolean appArmorEnabled) { this.appArmorEnabled = appArmorEnabled; } } }

Testing AppArmor Integration

Test Configuration

package com.apparmor.test; import com.apparmor.manager.AppArmorProfileManager; import com.apparmor.security.AppArmorSecurityManager; import org.junit.jupiter.api.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; import static org.junit.jupiter.api.Assumptions.assumeTrue; @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class AppArmorIntegrationTest { private static final Logger logger = LoggerFactory.getLogger(AppArmorIntegrationTest.class); private AppArmorSecurityManager securityManager; private static final String TEST_PROFILE = "test_java_app"; @BeforeAll void setUp() { // Only run tests if AppArmor is available assumeTrue(AppArmorProfileManager.isAppArmorEnabled(), "AppArmor is not enabled on this system"); securityManager = new AppArmorSecurityManager(TEST_PROFILE, Paths.get(".")); } @Test void testAppArmorInitialization() { boolean initialized = securityManager.initialize(); Assertions.assertTrue(initialized, "AppArmor should initialize successfully"); } @Test void testProfileLoading() throws IOException { List<String> profiles = AppArmorProfileManager.getLoadedProfiles(); Assertions.assertTrue(profiles.contains(TEST_PROFILE), "Test profile should be loaded"); } @Test void testHatOperations() { boolean hatCreated = securityManager.createHatProfile("test_hat", "/tmp/test.file rw,\ndeny /** w,"); Assertions.assertTrue(hatCreated, "Hat profile should be created"); boolean hatEntered = securityManager.enterRestrictedMode("test_hat"); Assertions.assertTrue(hatEntered, "Should enter hat successfully"); boolean hatExited = securityManager.exitRestrictedMode(); Assertions.assertTrue(hatExited, "Should exit hat successfully"); } @Test void testSecurityContext() { String context = securityManager.getSecurityContext(); Assertions.assertNotNull(context, "Security context should not be null"); logger.info("Current security context: {}", context); } @AfterAll void tearDown() { if (securityManager != null) { securityManager.cleanup(); } } }

Application Properties

Spring Boot Configuration

# AppArmor Configuration apparmor.enabled=true app.name=my_secure_java_app app.path=/opt/myapp # Logging logging.level.com.apparmor=DEBUG logging.file.path=/var/log/myapp # Security security.apparmor.audit.enabled=true security.apparmor.monitoring.interval=30000

Conclusion

This comprehensive AppArmor integration for Java provides:

  1. Native AppArmor integration using JNA
  2. Profile management for Java applications
  3. Spring Boot integration for easy configuration
  4. Hat profiles for fine-grained access control
  5. Security monitoring and auditing
  6. REST API for security operations

Key benefits:

  • Enhanced security through mandatory access control
  • Fine-grained permissions for different operations
  • Runtime security context switching
  • Comprehensive auditing and monitoring
  • Production-ready configuration templates

This implementation allows Java applications to leverage Linux AppArmor security while maintaining the flexibility and portability of Java applications.

Leave a Reply

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


Macro Nepal Helper