Skip to content

Commit df21e99

Browse files
committed
Add Container Security Provider and Luna Security Provider frameworks
Implements two security provider frameworks for Java applications: 1. Container Security Provider (1.20.0): - Always enabled by default (no service binding required) - Provides CloudFoundryContainerProvider for Java security - Configures JAVA_OPTS for Java 8 (extension dirs) and Java 9+ (bootstrap classpath) 2. Luna Security Provider (7.4.0): - Requires Luna HSM service binding - Installs Safenet Luna client libraries and native components - Configures Chrystoki.conf for HSM server connections - Supports HA configurations with multiple server groups Files added: - src/java/frameworks/container_security_provider.go (265 lines) - src/java/frameworks/luna_security_provider.go (487 lines) Files modified: - manifest.yml: Added dependencies with verified SHA256 hashes - src/java/supply/supply.go: Registered frameworks (lines 173-175) - src/java/finalize/finalize.go: Registered frameworks (lines 168-170) - src/integration/frameworks_test.go: Added Container Security Provider test Integration test: Container Security Provider test passes (20.70s)
1 parent 705b4cc commit df21e99

File tree

6 files changed

+811
-0
lines changed

6 files changed

+811
-0
lines changed

manifest.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ default_versions:
7070
version: 14.x
7171
- name: sealights-agent
7272
version: 4.x
73+
- name: container-security-provider
74+
version: 1.x
75+
- name: luna-security-provider
76+
version: 7.x
7377

7478
url_to_dependency_map:
7579
- match: openjdk-jre-(\d+\.\d+\.\d+)
@@ -159,6 +163,12 @@ url_to_dependency_map:
159163
- match: sealights-java-(\d+\.\d+\.\d+)
160164
name: sealights-agent
161165
version: $1
166+
- match: container-security-provider-(\d+\.\d+\.\d+)
167+
name: container-security-provider
168+
version: $1
169+
- match: luna-security-provider-(\d+\.\d+\.\d+)
170+
name: luna-security-provider
171+
version: $1
162172

163173
dependency_deprecation_dates:
164174
- version_line: 8.x
@@ -615,3 +625,30 @@ dependencies:
615625
cf_stacks:
616626
- cflinuxfs4
617627
- cflinuxfs3
628+
629+
# ========================================
630+
# Container Security Provider
631+
# ========================================
632+
# Repository: https://java-buildpack.cloudfoundry.org/container-security-provider/
633+
# Note: Always enabled by default, provides container-specific security context
634+
- name: container-security-provider
635+
version: 1.20.0
636+
uri: https://java-buildpack.cloudfoundry.org/container-security-provider/container-security-provider-1.20.0-RELEASE.jar
637+
sha256: fef33f4ffec1451b97253887026ec65ad99df0d2e8f8412e50e2afe5a4f6c62d
638+
cf_stacks:
639+
- cflinuxfs4
640+
- cflinuxfs3
641+
642+
# ========================================
643+
# Luna Security Provider
644+
# ========================================
645+
# Repository: https://java-buildpack.cloudfoundry.org/luna-security-provider/
646+
# Note: Requires Luna HSM service binding in VCAP_SERVICES
647+
# Contains native libraries (libLunaAPI.so) and JARs for SafeNet Luna HSM integration
648+
- name: luna-security-provider
649+
version: 7.4.0
650+
uri: https://java-buildpack.cloudfoundry.org/luna-security-provider/LunaClient-Minimal-v7.4.0-226.x86_64.tar
651+
sha256: e024103719ffa99a011607942ecddfd91c5681113e6cea27f5514bc9fa172875
652+
cf_stacks:
653+
- cflinuxfs4
654+
- cflinuxfs3

src/integration/frameworks_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -896,6 +896,20 @@ func testFrameworks(platform switchblade.Platform, fixtures string) func(*testin
896896
Expect(deployment.ExternalURL).NotTo(BeEmpty())
897897
})
898898
})
899+
900+
context("with Container Security Provider", func() {
901+
it("detects and configures Container Security Provider", func() {
902+
deployment, logs, err := platform.Deploy.
903+
WithEnv(map[string]string{
904+
"BP_JAVA_VERSION": "11",
905+
}).
906+
Execute(name, filepath.Join(fixtures, "container_spring_boot_staged"))
907+
Expect(err).NotTo(HaveOccurred(), logs.String)
908+
909+
Expect(logs.String()).To(ContainSubstring("Container Security Provider"))
910+
Expect(deployment.ExternalURL).NotTo(BeEmpty())
911+
})
912+
})
899913
})
900914
}
901915
}

src/java/finalize/finalize.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,10 @@ func (f *Finalizer) finalizeFrameworks() error {
165165
// mTLS Support (Priority 1)
166166
registry.Register(frameworks.NewClientCertificateMapperFramework(ctx))
167167

168+
// Security Providers (Priority 1)
169+
registry.Register(frameworks.NewContainerSecurityProviderFramework(ctx))
170+
registry.Register(frameworks.NewLunaSecurityProviderFramework(ctx))
171+
168172
// Development Tools (Priority 1)
169173
registry.Register(frameworks.NewDebugFramework(ctx))
170174
registry.Register(frameworks.NewJmxFramework(ctx))
Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
package frameworks
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"path/filepath"
7+
"strconv"
8+
)
9+
10+
// ContainerSecurityProviderFramework implements container-based security provider support
11+
// This framework provides CloudFoundryContainerProvider for Java security integration
12+
type ContainerSecurityProviderFramework struct {
13+
context *Context
14+
}
15+
16+
// NewContainerSecurityProviderFramework creates a new container security provider framework instance
17+
func NewContainerSecurityProviderFramework(ctx *Context) *ContainerSecurityProviderFramework {
18+
return &ContainerSecurityProviderFramework{context: ctx}
19+
}
20+
21+
// Detect checks if container security provider should be included
22+
// Enabled by default, can be disabled via configuration
23+
func (c *ContainerSecurityProviderFramework) Detect() (string, error) {
24+
// Enabled by default to provide container-based security
25+
return "Container Security Provider", nil
26+
}
27+
28+
// Supply installs the container security provider JAR
29+
func (c *ContainerSecurityProviderFramework) Supply() error {
30+
c.context.Log.BeginStep("Installing Container Security Provider")
31+
32+
// Get container-security-provider dependency from manifest
33+
dep, err := c.context.Manifest.DefaultVersion("container-security-provider")
34+
if err != nil {
35+
return fmt.Errorf("unable to determine Container Security Provider version: %w", err)
36+
}
37+
38+
// Install container security provider JAR
39+
providerDir := filepath.Join(c.context.Stager.DepDir(), "container_security_provider")
40+
if err := c.context.Installer.InstallDependency(dep, providerDir); err != nil {
41+
return fmt.Errorf("failed to install Container Security Provider: %w", err)
42+
}
43+
44+
c.context.Log.Info("Installed Container Security Provider version %s", dep.Version)
45+
return nil
46+
}
47+
48+
// Finalize configures the container security provider for runtime
49+
func (c *ContainerSecurityProviderFramework) Finalize() error {
50+
// Find the installed JAR
51+
providerDir := filepath.Join(c.context.Stager.DepDir(), "container_security_provider")
52+
jarPattern := filepath.Join(providerDir, "container-security-provider-*.jar")
53+
54+
matches, err := filepath.Glob(jarPattern)
55+
if err != nil || len(matches) == 0 {
56+
// JAR not found, might not have been installed
57+
return nil
58+
}
59+
60+
jarPath := matches[0]
61+
62+
// Detect Java version to determine extension mechanism
63+
// Java 9+ uses root libraries (-Xbootclasspath/a), Java 8 uses extension directories
64+
javaVersion, err := c.getJavaMajorVersion()
65+
if err != nil {
66+
c.context.Log.Warning("Unable to detect Java version, assuming Java 8: %s", err.Error())
67+
javaVersion = 8
68+
}
69+
70+
// Add to JAVA_OPTS
71+
javaOpts := ""
72+
if javaVersion >= 9 {
73+
// Java 9+: Add to bootstrap classpath via -Xbootclasspath/a
74+
javaOpts = fmt.Sprintf("-Xbootclasspath/a:%s", jarPath)
75+
} else {
76+
// Java 8: Use extension directory
77+
javaOpts = fmt.Sprintf("-Djava.ext.dirs=%s:$JAVA_HOME/jre/lib/ext:$JAVA_HOME/lib/ext", providerDir)
78+
}
79+
80+
// Add security provider to java.security.properties
81+
// Insert at position 1 (after default providers)
82+
securityProvider := "-Djava.security.properties=" + filepath.Join(c.context.Stager.DepDir(), "container_security_provider", "java.security")
83+
javaOpts += " " + securityProvider
84+
85+
// Write security properties file
86+
if err := c.writeSecurityProperties(); err != nil {
87+
return fmt.Errorf("failed to write security properties: %w", err)
88+
}
89+
90+
// Add key manager and trust manager configuration if specified
91+
keyManagerEnabled := c.getKeyManagerEnabled()
92+
if keyManagerEnabled != "" {
93+
javaOpts += fmt.Sprintf(" -Dorg.cloudfoundry.security.keymanager.enabled=%s", keyManagerEnabled)
94+
}
95+
96+
trustManagerEnabled := c.getTrustManagerEnabled()
97+
if trustManagerEnabled != "" {
98+
javaOpts += fmt.Sprintf(" -Dorg.cloudfoundry.security.trustmanager.enabled=%s", trustManagerEnabled)
99+
}
100+
101+
// Write JAVA_OPTS to environment
102+
existingOpts := os.Getenv("JAVA_OPTS")
103+
if existingOpts != "" {
104+
javaOpts = existingOpts + " " + javaOpts
105+
}
106+
107+
if err := c.context.Stager.WriteEnvFile("JAVA_OPTS", javaOpts); err != nil {
108+
return fmt.Errorf("failed to set JAVA_OPTS for Container Security Provider: %w", err)
109+
}
110+
111+
return nil
112+
}
113+
114+
// writeSecurityProperties writes the java.security properties file with CloudFoundryContainerProvider
115+
func (c *ContainerSecurityProviderFramework) writeSecurityProperties() error {
116+
providerDir := filepath.Join(c.context.Stager.DepDir(), "container_security_provider")
117+
securityFile := filepath.Join(providerDir, "java.security")
118+
119+
// Write security provider configuration
120+
// Insert CloudFoundryContainerProvider at position 1
121+
content := "security.provider.1=org.cloudfoundry.security.CloudFoundryContainerProvider\n"
122+
123+
if err := os.WriteFile(securityFile, []byte(content), 0644); err != nil {
124+
return fmt.Errorf("failed to write security properties file: %w", err)
125+
}
126+
127+
return nil
128+
}
129+
130+
// getJavaMajorVersion detects the Java major version from JAVA_HOME
131+
func (c *ContainerSecurityProviderFramework) getJavaMajorVersion() (int, error) {
132+
// Check if JAVA_HOME is set
133+
javaHome := os.Getenv("JAVA_HOME")
134+
if javaHome == "" {
135+
return 0, fmt.Errorf("JAVA_HOME not set")
136+
}
137+
138+
// Read release file
139+
releaseFile := filepath.Join(javaHome, "release")
140+
content, err := os.ReadFile(releaseFile)
141+
if err != nil {
142+
return 0, fmt.Errorf("failed to read release file: %w", err)
143+
}
144+
145+
// Parse JAVA_VERSION from release file
146+
version := parseJavaVersion(string(content))
147+
if version == 0 {
148+
return 0, fmt.Errorf("unable to parse Java version")
149+
}
150+
151+
return version, nil
152+
}
153+
154+
// getKeyManagerEnabled returns the key_manager_enabled configuration value
155+
func (c *ContainerSecurityProviderFramework) getKeyManagerEnabled() string {
156+
config := os.Getenv("JBP_CONFIG_CONTAINER_SECURITY_PROVIDER")
157+
if config == "" {
158+
return ""
159+
}
160+
161+
// Parse configuration for key_manager_enabled
162+
// Format: {key_manager_enabled: true} or {'key_manager_enabled': 'true'}
163+
if contains(config, "key_manager_enabled") {
164+
if contains(config, "true") {
165+
return "true"
166+
}
167+
if contains(config, "false") {
168+
return "false"
169+
}
170+
}
171+
172+
return ""
173+
}
174+
175+
// getTrustManagerEnabled returns the trust_manager_enabled configuration value
176+
func (c *ContainerSecurityProviderFramework) getTrustManagerEnabled() string {
177+
config := os.Getenv("JBP_CONFIG_CONTAINER_SECURITY_PROVIDER")
178+
if config == "" {
179+
return ""
180+
}
181+
182+
// Parse configuration for trust_manager_enabled
183+
// Format: {trust_manager_enabled: true} or {'trust_manager_enabled': 'true'}
184+
if contains(config, "trust_manager_enabled") {
185+
if contains(config, "true") {
186+
return "true"
187+
}
188+
if contains(config, "false") {
189+
return "false"
190+
}
191+
}
192+
193+
return ""
194+
}
195+
196+
// parseJavaVersion parses Java major version from release file content
197+
func parseJavaVersion(content string) int {
198+
// Look for JAVA_VERSION="1.8.0_..." or JAVA_VERSION="11.0...."
199+
lines := splitByNewline(content)
200+
for _, line := range lines {
201+
if contains(line, "JAVA_VERSION=") {
202+
// Extract version string
203+
start := stringIndexOf(line, "\"")
204+
if start == -1 {
205+
continue
206+
}
207+
end := stringIndexOf(line[start+1:], "\"")
208+
if end == -1 {
209+
continue
210+
}
211+
version := line[start+1 : start+1+end]
212+
213+
// Parse major version
214+
if stringStartsWith(version, "1.8") {
215+
return 8
216+
}
217+
if stringStartsWith(version, "1.7") {
218+
return 7
219+
}
220+
221+
// Java 9+ format: "11.0.1" or "17.0.1"
222+
dotIndex := stringIndexOf(version, ".")
223+
if dotIndex > 0 {
224+
major := version[:dotIndex]
225+
if majorVersion, err := strconv.Atoi(major); err == nil {
226+
return majorVersion
227+
}
228+
}
229+
}
230+
}
231+
232+
return 0
233+
}
234+
235+
// Helper functions for string manipulation
236+
func splitByNewline(s string) []string {
237+
var lines []string
238+
start := 0
239+
for i := 0; i < len(s); i++ {
240+
if s[i] == '\n' {
241+
lines = append(lines, s[start:i])
242+
start = i + 1
243+
}
244+
}
245+
if start < len(s) {
246+
lines = append(lines, s[start:])
247+
}
248+
return lines
249+
}
250+
251+
func stringIndexOf(s, substr string) int {
252+
for i := 0; i <= len(s)-len(substr); i++ {
253+
if s[i:i+len(substr)] == substr {
254+
return i
255+
}
256+
}
257+
return -1
258+
}
259+
260+
func stringStartsWith(s, prefix string) bool {
261+
if len(s) < len(prefix) {
262+
return false
263+
}
264+
return s[:len(prefix)] == prefix
265+
}

0 commit comments

Comments
 (0)