Skip to main content
Version: Next 🚧

Multi-Module Applications

jGuard provides module-based isolation for applications with multiple JPMS modules, each with independent security policies.

Architecture

jGuard's security model is module-based, not classloader-based:

ScenarioSupportNotes
Single app, multiple JPMS modulesFully supportedDesigned use case
Multiple apps with different module namesFully supportedEach module gets its own policy
Same module name across classloadersShared policyModules with same name share policy
Design Choice

jGuard uses JPMS modules as the security boundary. This is simpler and more predictable than classloader-based isolation. If you need per-classloader isolation, consider running separate JVMs or containers.

Auto-Discovery

jGuard automatically discovers policies from signed JARs on the module path:

┌─────────────────────────────────────────────────────────┐
│ JVM Startup with jGuard Agent │
│ │
│ 1. Scan module path for JAR files │
│ 2. Verify JAR signatures (production mode) │
│ 3. Extract META-INF/jguard/policy.bin from each JAR │
│ 4. Load and merge policies per module │
│ 5. Enforce based on caller's module identity │
└─────────────────────────────────────────────────────────┘

Development Mode

For development without JAR signing:

jguardPolicy {
allowUnsignedPolicies = true // Only for development!
discoveryMode = true
}

Or via system property:

java -Djguard.allowUnsignedPolicies=true \
-Djguard.discovery=true \
-javaagent:jguard-agent.jar \
-jar app.jar

JAR Signing for Production

Sign JARs using a keystore:

jarsigner -keystore keystore.jks -storepass changeit mymodule.jar mykey

The agent verifies signatures before loading policies. Unsigned or tampered JARs are rejected.

Module-Based Isolation

Each module has its own policy file:

app/
├── core/
│ ├── src/main/java/
│ │ ├── module-info.java
│ │ └── module-info.jguard # Core module policy
│ └── build.gradle
├── network/
│ ├── src/main/java/
│ │ ├── module-info.java
│ │ └── module-info.jguard # Network module policy
│ └── build.gradle
└── app/
├── src/main/java/
│ ├── module-info.java
│ └── module-info.jguard # App module policy
└── build.gradle

Module Isolation Guarantee

  • Module A can only use Module A's entitlements
  • Module B cannot use Module A's entitlements
  • Each module's policy is enforced independently
  • No capability inheritance across module boundaries

Delegation Pattern

Modules can delegate sensitive operations to trusted modules:

Core Module (File Access)

security module com.example.core {
entitle module to fs.read("config", "**");
entitle module to fs.read(".", "*.txt");
}

Network Module (Network Access)

security module com.example.network {
entitle module to network.outbound("*.example.com", 443);
entitle module to network.outbound("httpbin.org", "80-443");
entitle module to threads.create;
}

App Module (Orchestration)

security module com.example.app {
entitle module to threads.create;
entitle module to env.read("HOME");
entitle module to env.read("USER");
entitle module to system.property.read;
}

Usage Pattern

// In app module - cannot read files directly
// Files.readString(Path.of("config/app.conf")); // ✗ BLOCKED

// But app delegates to core module
ConfigReader reader = new ConfigReader(); // ✓ ALLOWED
String config = reader.readConfig("app.conf"); // ✓ ALLOWED (core module)

// App cannot make network calls directly
// new Socket("api.example.com", 443); // ✗ BLOCKED

// But app delegates to network module
ApiClient client = new ApiClient(); // ✓ ALLOWED
client.fetch("/data"); // ✓ ALLOWED (network module)

Use Case: Application with Plugins

┌──────────────────────────────────────────────────────┐
│ JVM │
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ com.example.app (trusted) │ │
│ │ • Full filesystem access │ │
│ │ • Network access │ │
│ │ • Thread creation │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ com.vendor.a │ │ com.vendor.b │ │
│ │ (plugin) │ │ (plugin) │ │
│ │ • fs.read only │ │ • fs.read only │ │
│ │ • No network │ │ • No network │ │
│ └─────────────────┘ └─────────────────┘ │
│ │
│ jGuard Agent - Enforces per-module policies │
└──────────────────────────────────────────────────────┘

Resource Isolation by Module

Different modules can have different access levels:

// Premium module - more capabilities
security module premium.features {
entitle module to network.outbound;
entitle module to threads.create;
entitle module to fs.read("data", "**");
entitle module to fs.write("output", "**");
}

// Basic module - restricted
security module basic.features {
entitle module to fs.read("data", "**");
// No network, no threads, no writes
}

Network Isolation by Module

// Internal module - internal network only
security module internal.services {
entitle module to network.outbound("*.internal.example.com", 443);
}

// External module - public network allowed
security module external.services {
entitle module to network.outbound;
}

// Isolated module - no network
security module isolated.processor {
// No network.outbound granted
}

External Policy Overrides

Override module policies at deployment without rebuilding:

// policies/com.vendor.plugin.jguard
security module com.vendor.plugin {
// Deny capabilities the plugin requested
deny module to network.outbound;
deny module to threads.create;
}

See External Policies for details on grant/deny semantics.

Best Practices

  1. Use distinct module names - Each module gets its own policy
  2. Separate concerns by module - Network module, storage module, etc.
  3. Apply least privilege per module - Only grant what each module needs
  4. Use delegation - Let trusted modules handle sensitive operations
  5. Sign JARs in production - Verify policy authenticity
  6. Use external policies - Override vendor policies at deployment
  7. Consider containers - For true tenant isolation, use separate JVMs/containers

Comparison with SecurityManager

AspectJava SecurityManagerjGuard
Isolation boundaryCodeSource + ProtectionDomainJPMS Module
GranularityPer-class, stack-basedPer-module, package-based
ConfigurationPolicy filesmodule-info.jguard
ComplexityHigh (stack inspection)Lower (module identity)
PerformanceHigher overheadLower overhead
StatusRemoved in JDK 24Active development