paketo-buildpacks / libjvm

A library and helper applications that form the basis for building the different Paketo-style JVM-providing buildpacks
Apache License 2.0
19 stars 20 forks source link

Supply a custom Java Security File #259

Closed tuckeremulls closed 1 year ago

tuckeremulls commented 1 year ago

Hello team, I am looking for a way to set the networkaddress.cache.ttl java security property for a Spring Boot app that we containerize with buidpacks without setting it in application code.

Unfortunately this property cannot just be set by passing it as a JVM argument through the $JAVA_TOOL_OPTIONS environment variable supplied by bellsoft-liberica. It must be set in the java.security file. Is there any way to supply a custom file with properties that can override the java.security file of the jdk that bellsoft-liberica builds into the image? Does this functionality already exist or would it be a new feature?

Describe the Enhancement

Some method of supplying a file to override certain properties of the jdk's java.security file.

Possible Solution

Provided through an environment variable possibly for ease of access.

Motivation

This specific property networkaddress.cache.ttl can be altered to disable DNS caching, so that if DNS rules change, an application will be able to pick up the new resolutions rather than using an older incorrect one. Beyond just this property, there are many others that can be customized in the java.security file that may serve important usage to developers containerizing their apps.

dmikusa commented 1 year ago

Sorry for the delay. I had to look some of this up because we don't have it documented. Apologizes for that.

  1. If your DNS configuration is set to a link local unicast IP then it will automatically disable DNS caching. The helper, which runs before your app starts up at runtime, will check this and adjust the java.security file setting networkaddress.cache.ttl to 0. It does not support arbitrarily disabling DNS caching, but that is possibly something we could add.

  2. There is support in the buildpack for adding additional security providers. Not what you're looking for here, but it's an advanced feature that one might need to write to java.security file to accomplish and is already supported in the buildpack.

  3. The buildpack is setting an env variable called JAVA_SECURITY_PROPERTIES that points to the java.security file which is configured. This file is appended to the default settings file provided by the JVM as is the JVM's default behavior, so the buildpack makes it an empty file at build-time. Some other buildpack features like 1.) above may write to this file but it's often empty. We then set -Djava.security.properties=/same/path to point to the same file as JAVA_SECURITY_PROPERTIES. You could try adding another -Djava.security.properties=/your/file argument to JAVA_TOOL_OPTIONS. I didn't try this, so it may not work but often the JVM will take the last property (it seems to read left-to-right) to be set when there are multiple of the same property set, so it's possible that your value may override the buildpack just because the JVM reads it last. It's also possible, again, I didn't check that the JVM might support this property being set multiple times (since it is appending its contents to the default security file), although I'm skeptical of that. Anyway, if you have a chance to try this and report your findings we can go from there.


My initial thought is to not allow generically modifying the security file, at least not having a way in the buildpack to do it, but that we could enable specific use cases like disabling DNS, adding security providers, or whatever other concrete use cases come up. My rationale is a.) it's easy to mess things up in this file and that can result in extra support burden for buildpacks b.) it's nice to have this capability as a developer, but if you switch perspective to a sysadmin/infosec point of view, maybe it's not so great, and having controlled/limited access is more appealing. Anyway, I'm certainly open to hearing more on this topic though.

gudatcomputers commented 1 year ago

Sorry for the delay. I had to look some of this up because we don't have it documented. Apologizes for that.

1. If your DNS configuration is set to a [link local unicast IP](https://en.wikipedia.org/wiki/Link-local_address) then it will automatically disable DNS caching. The helper, which runs before your app starts up at runtime, will check this and [adjust the `java.security` file setting `networkaddress.cache.ttl` to 0](https://github.com/paketo-buildpacks/libjvm/blob/main/helper/link_local_dns.go#L56-L59). It does not support arbitrarily disabling DNS caching, but that is possibly something we could add.

2. There is support in the buildpack for adding additional security providers. Not what you're looking for here, but it's an advanced feature that one might need to write to `java.security` file to accomplish and is already supported in the buildpack.

3. The buildpack is setting an env variable called `JAVA_SECURITY_PROPERTIES` that points to the `java.security` file which is configured. This file is appended to the default settings file provided by the JVM as is the JVM's default behavior, so the buildpack makes it an empty file at build-time. Some other buildpack features like 1.) above may write to this file but it's often empty. We then set `-Djava.security.properties=/same/path` to point to the same file as `JAVA_SECURITY_PROPERTIES`. You could try adding another `-Djava.security.properties=/your/file` argument to `JAVA_TOOL_OPTIONS`. I didn't try this, so it may not work but often the JVM will take the last property (it seems to read left-to-right) to be set when there are multiple of the same property set, so it's possible that your value may override the buildpack just because the JVM reads it last. It's also possible, again, I didn't check that the JVM might support this property being set multiple times (since it is appending its contents to the default security file), although I'm skeptical of that. Anyway, if you have a chance to try this and report your findings we can go from there.

My initial thought is to not allow generically modifying the security file, at least not having a way in the buildpack to do it, but that we could enable specific use cases like disabling DNS, adding security providers, or whatever other concrete use cases come up. My rationale is a.) it's easy to mess things up in this file and that can result in extra support burden for buildpacks b.) it's nice to have this capability as a developer, but if you switch perspective to a sysadmin/infosec point of view, maybe it's not so great, and having controlled/limited access is more appealing. Anyway, I'm certainly open to hearing more on this topic though.

Hey, developer here... I'll say that our concrete use case is configuring our workload to run with BouncyCastle on the JCA/JSSE side. Without a facility to hook this up we have a much harder time moving toward FIPS compliance

gudatcomputers commented 1 year ago

There is support in the buildpack for adding additional security providers. Not what you're looking for here, but it's an advanced feature that one might need to write to java.security file to accomplish and is already supported in the buildpack.

@dmikusa since it's not well documented can you point me in the direction of the code responsible for this? I'd like to see how we might accomplish

dmikusa commented 1 year ago

Hey, developer here... I'll say that our concrete use case is configuring our workload to run with BouncyCastle on the JCA/JSSE side. Without a facility to hook this up we have a much harder time moving toward FIPS compliance

You're in luck. The buildpack can do this right now. It is not documented, apologize for that. We've had a story to document for a while and it keeps slipping.

SECURITY_PROVIDERS is the way to add something like Bouncy Castle. See code here.

gudatcomputers commented 1 year ago

Hey, developer here... I'll say that our concrete use case is configuring our workload to run with BouncyCastle on the JCA/JSSE side. Without a facility to hook this up we have a much harder time moving toward FIPS compliance

You're in luck. The buildpack can do this right now. It is not documented, apologize for that. We've had a story to document for a while and it keeps slipping.

SECURITY_PROVIDERS is the way to add something like Bouncy Castle. See code here.

super helpful thanks.. I think the code will break for my specific use case because it expects SECURITY_PROVIDERS to be space delimited. and we need to set 2=org.bouncycastle.jsse.provider.BouncyCastleJsseProvider fips:BCFIPS which would break and not properly set fips:BCFIPS unless I'm not reading properly. A potential solution is to swap to a comma delimiter. I realize that's a breaking change. Would you be open to this kind of PR?

This is under the assumption that a SECURITY_PROVIDERS value should look like 1=myFirstProvider 2=mySecondPRovider effectively taking the lines from a java.security.properties file and putting them into a single value with space delimiters

dmikusa commented 1 year ago

This is under the assumption that a SECURITY_PROVIDERS value should look like 1=myFirstProvider 2=mySecondPRovider effectively taking the lines from a java.security.properties file and putting them into a single value with space delimiters

It is space-delimited, but it should be <position>|<provider>, at least last time I looked.

For example, if you set security providers: 1|FOXTROT|2|DELTA ECHO

You end up with the following java.security file:

security.provider.1=FOXTROT
security.provider.2=DELTA
security.provider.3=ECHO

I think the code will break for my specific use case because it expects SECURITY_PROVIDERS to be space delimited. and we need to set 2=org.bouncycastle.jsse.provider.BouncyCastleJsseProvider fips:BCFIPS which would break and not properly set fips:BCFIPS unless I'm not reading properly

I think you're right. I was digging through my notes, cause I've set Bouncy Castle up with this before and this is what I had previously done.

  1. In my application, I added src/main/resources/java-security.properties. In it put the following, but adjust to your needs.

    security.provider.1=org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider
    security.provider.2=com.sun.net.ssl.internal.ssl.Provider BCFIPS
    security.provider.3=sun.security.provider.Sun 
  2. Add the Bouncy Castle FIPS JAR to my project.

    <dependency>
      <groupId>org.bouncycastle</groupId>
      <artifactId>bc-fips</artifactId>
      <version>1.0.2.1</version>
    </dependency>
  3. (Optional) Check it's all working. Run ./mvnw package. The JAR will be packaged within your app.

  4. Build an image. Run pack build maven -e BPE_DELIM_JAVA_TOOL_OPTIONS=' ' -e BPE_APPEND_JAVA_TOOL_OPTIONS='-Djava.security.properties=/workspace/BOOT-INF/classes/java-security.properties'.

    The second two arguments will set a JAVA_TOOL_OPTIONS env variable on the image. It appends -Djava.security.properties and points to the file we created in the first step. This isn't the normal way you'd do this, but there is a specific reason why we have to at the moment to make this work (I couldn't find what I was referencing in my notes, but it might be your point above about space delimiters).

  5. Run with docker run --rm -it -p 8080:8080 apps/maven.

A potential solution is to swap to a comma delimiter. I realize that's a breaking change. Would you be open to this kind of PR?

I don't think we could change from space to comma as that could break apps out there, but we could accept a PR that makes the delimiter configurable with a space being the default. You could then override that with a comma. I think that would be sufficient to make things work with SECURITY_PROVIDERS.

tuckeremulls commented 1 year ago
  1. The buildpack is setting an env variable called JAVA_SECURITY_PROPERTIES that points to the java.security file which is configured. This file is appended to the default settings file provided by the JVM as is the JVM's default behavior, so the buildpack makes it an empty file at build-time. Some other buildpack features like 1.) above may write to this file but it's often empty. We then set -Djava.security.properties=/same/path to point to the same file as JAVA_SECURITY_PROPERTIES. You could try adding another -Djava.security.properties=/your/file argument to JAVA_TOOL_OPTIONS.

I want to attempt this but just am not sure what filepath I would provide to -Djava.security.properties. Let's say I place a java.security file in src/main/resources, then what would the filepath to that file be since buildpacks is running the packaged jar in a container?

gudatcomputers commented 1 year ago
  1. The buildpack is setting an env variable called JAVA_SECURITY_PROPERTIES that points to the java.security file which is configured. This file is appended to the default settings file provided by the JVM as is the JVM's default behavior, so the buildpack makes it an empty file at build-time. Some other buildpack features like 1.) above may write to this file but it's often empty. We then set -Djava.security.properties=/same/path to point to the same file as JAVA_SECURITY_PROPERTIES. You could try adding another -Djava.security.properties=/your/file argument to JAVA_TOOL_OPTIONS.

I want to attempt this but just am not sure what filepath I would provide to -Djava.security.properties. Let's say I place a java.security file in src/main/resources, then what would the filepath to that file be since buildpacks is running the packaged jar in a container?

So this is what I'm doing today, and it is HIGHLY dependent on how your deployed environment works. because that path to the java.security file is going to be a relative path. That is relative to the pwd of the process that is running your app. In my case it was a directory above where I expected... so I had to know the relative path on disk and set it at build time based on environment variables only available on CI. I can confirm this works in terms of enabling the custom java.security file

dmikusa commented 1 year ago

I want to attempt this but just am not sure what filepath I would provide to -Djava.security.properties. Let's say I place a java.security file in src/main/resources, then what would the filepath to that file be since buildpacks is running the packaged jar in a container?

@tuckeremulls See this note https://github.com/paketo-buildpacks/libjvm/issues/259#issuecomment-1458664736 it's doing exactly that. Step 4.) is where it sets the path. It's using /workspace/... which is where your app files end up in the run image. Just note that the remainder of the path is the location of the file within the app's JAR not your source code.

Relative paths would work as well. The working directory for an app is going to be /workspace/.

tuckeremulls commented 1 year ago

@tuckeremulls See this note #259 (comment) it's doing exactly that. Step 4.) is where it sets the path. It's using /workspace/... which is where your app files end up in the run image. Just note that the remainder of the path is the location of the file within the app's JAR not your source code.

Relative paths would work as well. The working directory for an app is going to be /workspace/.

I can confirm that this works when using the maven build-image goal and you have a custom security file in src/main/resources! I supply a custom java security file by specifying:

<env>
    <BPE_APPEND_JAVA_TOOL_OPTIONS>-Djava.security.properties=/workspace/BOOT-INF/classes/ENTER_YOUR_CUSTOM_JAVA_SECURITY_FILENAME</BPE_APPEND_JAVA_TOOL_OPTIONS>
    <BPE_DELIM_JAVA_TOOL_OPTIONS xml:space="preserve"> </BPE_DELIM_JAVA_TOOL_OPTIONS>
</env>

in the spring-boot-maven-plugin configuration in the pom.