How to embed CA-certificates with spring-boot:build-image? - spring-boot

I need to add self-signed certificates to a spring-boot docker image using spring-boot:build-image and paketo-buildpacks/ca-certificates but couldn't get it working.
So:
where to put the certificates to add?
in which format?
how to define paketo-buildpacks/ca-certificates bindings?
should any additional argument be provided to mvn spring-boot:build-image?
What I tried with no success so far:
update pom.xml spring-boot-maven-plugin:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<bindings>
<binding>${basedir}/bindings/ca-certificates:/platform/bindings/ca-certificates</binding>
</bindings>
</image>
</configuration>
<executions>
<execution>
<goals>
<goal>build-image</goal>
</goals>
</execution>
</executions>
</plugin>
from ${basedir} folder:
mkdir bindings
mkdir bindings/ca-certificates
echo "ca-certificates" > bindings/ca-certificates/type
cp ~/.ssh/mycert.pem bindings/ca-certificates/
mvn spring-boot:build-image
Edit: moved image bindings configuration directly under boot-plugin (rather than inside a specific execution) as suggested by #nick-valanos and solved the problem.

I got it. Maven configuration above is for maven package target, not spring-boot:build-image.
Here is the complete procedure:
create bindings/ca-certificates folder at maven project root and add to it:
type file containing just ca-certificates
CA certificatates you want to be embedded (in PEM format)
in pom.xml, add a build-image execution to spring-boot-maven-plugin with ${basedir}/bindings/ca-certificates:/platform/bindings/ca-certificates image binding as configured in my question
run mvn clean package

I kind of had the same issue and I found your post really helpful. After a bit playing around, I found that your configuration would also work with spring-boot:build-image with the following alterations:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<bindings>
<binding>${basedir}/bindings/ca-certificates:/platform/bindings/ca-certificates</binding>
</bindings>
</image>
</configuration>
</plugin>
As you can see, I added the <configuration> immediately after <plugin>. That way, it seems that it is parceable by spring-boot:build-image

Related

Paketo Buildpack for OpenTelemetry not working with maven spring boot 2.5.12 app

I'm trying to add OpenTelemetry automated instrumentation to our spring boot app but I can't get it working.
The app is deployed as a docker image and the image is created via the spring-boot-maven-plugin.
I'm following these instructions: https://github.com/paketo-buildpacks/opentelemetry
I've added an env section to the spring-boot-maven-plugin config in the pom.xml:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
<configuration>
<image>
<name>appname</name>
<env>
<BP_OPENTELEMETRY_ENABLED>true</BP_OPENTELEMETRY_ENABLED>
</env>
</image>
</configuration>
</plugin>
I'm not sure this is the correct way to enable it but I'm having a hard time determining whether that step is working or not.
The docker image is created and run with:
mvn clean spring-boot:build-image
docker compose -f app.yml up
I've added environment variables to app.yml file (hostname replaced with XXXXXX):
services:
appname:
image: appname:latest
environment:
- SPRING_PROFILES_ACTIVE=docker-local
- BPE_OTEL_TRACES_EXPORTER=zipkin
- BPE_OTEL_EXPORTER_ZIPKIN_ENDPOINT=http://XXXXXX:9411/api/v2/spans
- BPE_OTEL_SERVICE_NAME=appname
- BPE_OTEL_JAVAAGENT_ENABLED=true
I don't think these environment variables are being set, however because I don't see them when I run:
docker run --entrypoint launcher -it appname:latest bash -c set
I don't see any traces going to zipkin and I don't see anything in the logs.
Without docker, I have everything working fine.
I tried to figure out if I just need to use a more recent version of spring boot but I couldn't find a way to determine that.
I couldn't find any examples of apps that have this working.
Edit to include working solution:
Martin Theiss's solution is correct. Here is the section of the pom.xml that does everything:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
<configuration>
<image>
<name>appname</name>
<buildpacks>
<buildpack>paketo-buildpacks/java</buildpack>
<buildpack>gcr.io/paketo-buildpacks/opentelemetry</buildpack>
</buildpacks>
<env>
<BP_OPENTELEMETRY_ENABLED>true</BP_OPENTELEMETRY_ENABLED>
<BPE_OTEL_JAVAAGENT_ENABLED>true</BPE_OTEL_JAVAAGENT_ENABLED>
<BPE_OTEL_TRACES_EXPORTER>zipkin</BPE_OTEL_TRACES_EXPORTER>
<BPE_OTEL_EXPORTER_ZIPKIN_ENDPOINT>http://zipkinhost:9411/api/v2/spans</BPE_OTEL_EXPORTER_ZIPKIN_ENDPOINT>
<BPE_OTEL_SERVICE_NAME>appname</BPE_OTEL_SERVICE_NAME>
</env>
</image>
</configuration>
</plugin>
Note that this is sending traces directly to zipkin. Eventually I'll be sending traces to an opentelemetry collector.
Note also that I was wrong to try to put environment variables in the spring app.yml config file. These should be put in the pom.xml as per above.
OpenTelemetry buildpack is not contained in the buildpacks/java. You have to specify it additionally.
<image>
<buildpacks>
<buildpack>paketo-buildpacks/java</buildpack>
<buildpack>gcr.io/paketo-buildpacks/opentelemetry</buildpack>
</buildpacks>
<env>
<BP_OPENTELEMETRY_ENABLED>true</BP_OPENTELEMETRY_ENABLED>
</env>
</image>

Pass dockerPublisher maven properties over command line in spring-boot:build-image

I want to integrate the spring boot maven plugins capability to build and publish an OCI Image to a remote Repository
My Goal
I want to use the following plugin configuration:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<name>${docker.image.prefix}/${project.artifactId}:${project.version}</name>
</image>
</configuration>
<executions>
<execution>
<goals>
<goal>build-image</goal>
</goals>
</execution>
</executions>
</plugin>
And now I want to pass the docker.publishRegistry variables by command line.
What I've tried so far
I've tried to pass the parameter with the -Ddocker.publishRegistry.username property but that didn't work.
When you take a look at the source code of the plugin Docker has no Parameter property assigned to it:
/**
* Alias for {#link Image#publish} to support configuration via command-line property.
*/
#Parameter(property = "spring-boot.build-image.publish", readonly = true)
Boolean publish;
/**
* Docker configuration options.
* #since 2.4.0
*/
#Parameter
private Docker docker;
https://github.com/spring-projects/spring-boot/blob/82b90d57496ba85be316b9eb88a36d81f2cc9baa/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/BuildImageMojo.java#L159
So I guess it is not possible to define this parameter by command line or is it?
Current Workaround
Currently I'm defining the properties by global maven properties and reuse them in the docker scope.
My pom.xml:
<properties>
<docker-registry>https://example.org</docker-registry>
<docker-registry-username>username</docker-registry-username>
<docker-registry-username>password</docker-registry-username>
</properties>
<!-- ... -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<name>${docker.image.prefix}/${project.artifactId}:${project.version}</name>
</image>
<docker>
<publishRegistry>
<username>${docker-registry-username}</username>
<password>${docker-registry-password}</password>
<url>${docker-registry}</url>
</publishRegistry>
</docker>
</configuration>
<executions>
<execution>
<goals>
<goal>build-image</goal>
</goals>
</execution>
</executions>
</plugin>
And I'm building with:
./mvnw -B -s \
-Dspring-boot.build-image.publish=true \
-Ddocker-registry-username="$USERNAME" \
-Ddocker-registry-password="$PASSWORD" \
-Ddocker-registry="$REGISTRY" \
clean deploy
I have not the exact solution to you question: "passing publishRegistry parameters on the command line", but If I may, I have another workaround that shields you from exposing your credential in the pom.xml.
What i have done is to put the parameters and credential in a profile in my .m2/settings.xml like this:
<profiles>
<profile>
<id>docker-io-credentials</id>
<properties>
<docker-reg>docker.io</docker-reg>
<docker-reg.user>your-user-name</docker-reg.user>
<docker-reg.pwd>your-token-or-passwd</docker-reg.pwd>
<docker-reg.url>${docker-reg}/library/${docker-reg.user}</docker-reg.url>
</properties>
</profile>
</profiles>
Then on the command-line you can simply pass the profile's name to merge the credential to the current build.
mvn clean install -Pdocker-io-credentials
You can define placeholders in the spring-boot plugin's configuration, which refer to environment variables. This will be slightly less complex, so like
...
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>main.Class</mainClass>
<image>
<name>registry-url/library/image-name:${project.version}</name>
</image>
<docker>
<publishRegistry>
<username>docker-user</username>
<password>${env.docker_registry_password}</password>
<url>https://registry-url/v1/</url>
<email>user#example.com</email>
</publishRegistry>
</docker>
</configuration>
...
See more on this topic here: https://www.baeldung.com/maven-env-variables
Just to mention that the Spring team is aware and did not consider this a bug but rather a documentation issue: https://github.com/spring-projects/spring-boot/issues/31024#issuecomment-1127905504
Very similar to what #twobiers suggested in his workaround:
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<docker>
<publishRegistry>
<url>${docker.publishRegistry.url}</url>
<username>${docker.publishRegistry.username}</username>
<password>${docker.publishRegistry.password}</password>
</publishRegistry>
</docker>
</configuration>
and then I build (and publish to Github Packages Registry) my project with:
./mvnw spring-boot:build-image \
-Ddocker.publishRegistry.username=${{ github.actor }} \
-Ddocker.publishRegistry.password=${{ secrets.GITHUB_TOKEN }} \
-Ddocker.publishRegistry.url=ghcr.io \
-Dspring-boot.build-image.publish=true \
-Dspring-boot.build-image.imageName="ghcr.io/${{ github.repository }}:latest" \
-DskipTests

How to reference a properties file on Master and Child POM's

I have a master POM file that defines two profiles: QA and Production. It uses the properties-maven-plugin to set some vars that are later used by the wildfly-maven-plugin to deploy the packages to the web server.
Something like this:
[MASTER POM]
<profile>
<id>qa</id>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>properties-maven-plugin</artifactId>
<version>1.0.0</version>
<executions>
<execution>
<phase>initialize</phase>
<goals>
<goal>read-project-properties</goal>
</goals>
<configuration>
<files>
<file>../build-qa.properties</file>
</files>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
And on the Child POMs:
<parent>
<groupId>fhng.apps</groupId>
<artifactId>fhng-build-all</artifactId>
<version>1.0</version>
</parent>
(...)
<plugin>
<groupId>org.wildfly.plugins</groupId>
<artifactId>wildfly-maven-plugin</artifactId>
<version>1.1.0.Alpha11</version>
<executions>
<execution>
<phase>install</phase>
<goals>
<goal>deploy</goal>
</goals>
</execution>
</executions>
<configuration>
<hostname>${wildfly.server}</hostname>
<port>${wildfly.port}</port>
<username>${wildfly.username}</username>
<password>${wildfly.password}</password>
<filename>myapp.war</filename>
</configuration>
</plugin>
the Master POM is located on the project root and each particular web application is located on a sub-folder. This works if I run mvn install from each particular project folder. I would very much like to run "mvn -Pqa clean install" from the master pom folder. However it fails because the master pom references ..\build-qa.properties which works from each project but obviously is invalid from the parent directory.
Is where a way to solve this? Is it possible to reference a file relative to the Master POM folder, irrespective of which particular POM is built? I understand that this approach breaks the maven premise that the parent package must not necessarily be present on our working dir.
As an alternative, is there a way to reference the properties files as an artifact of the parent package? So that maven is able to get said parent package from the repo and use a file inside it?
I would also accept a way to "skip" ou "disable" the initialize/compile/install phase on the parent pom so that it won't try to read the .properties files.
Thank you!
If in root folder you put directory .mvn/, you can refer to root folder by ${maven.multiModuleProjectDirectory}
I current case:
<file>${maven.multiModuleProjectDirectory}/build-qa.properties</file>
Ref: http://takari.io/2015/03/20/mmp.html
PS. Remember that empty folder is not comitted to git

Use Maven to Copy Files to WEB-INF Before WAR is Packaged

I have two servlet config files in my webapp, one for our normal environment (Heroku) and the other for WebLogic. I have a Maven profile for our WebLogic builds that should copy "servlet-context.xml.weblogic" to "servlet-context.xml". Everything appears to be working, except that the copy takes place AFTER the war file is built, so the correct servlet context doesn't get included in the package. What is the right build phase to use in the maven-antrun-plugin to get the copying done correctly?
Here is the relevant section of my POM.xml file:
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<phase>test</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<move
file="${project.build.directory}/${project.build.finalName}/WEB-INF/spring/appServlet/radio-context.xml.weblogic"
tofile="${project.build.directory}/${project.build.finalName}/WEB-INF/spring/appServlet/radio-context.xml"/>
</tasks>
</configuration>
</execution>
</executions>
</plugin>
This fails with the following error:
Failed to execute goal org.apache.maven.plugins:maven-antrun-plugin:1.3:run (default) on project radio: An Ant BuildException has occured: Warning: Could not find file C:\workspace\radio\target\radio-1.0.0-BUILD-SNAPSHOT\WEB-INF\spring\appServlet\radio-context.xml.weblogic to copy. -> [Help 1]
However, if I change the <phase> to package, the copy works, but after the war is built.
Any help would be appreciated.
As a reference, this page lists the Maven lifecycle phases.
The key phases you need to think about in your build are:
process-resources - where most of the files are placed into ${project.build.directory}
package - this is the phase where the WAR is built
BUT...
Looking at the documentation of the WAR plugin, the copying of WAR resources to ${project.build.directory}/${project.build.finalName} occurs in the war:war goal, which executes in the package phase also.
So let's take a step back. What you want to achieve is to use a different radio-context.xml file depending on your profile. Perhaps a better approach would be to configure the WAR plugin differently in your weblogic profile.
Have a separate webapp resource directory for your weblogic, put your custom radio-context.xml file in there, and only include that directory in the weblogic profile. e.g.
<project>
...
<profiles>
<profile>
<id>weblogic</id>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.2</version>
<configuration>
<webResources>
<resource>
<directory>src/main/webapp-weblogic</directory>
</resource>
</webResources>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
Try using process-resources, this copies your file before the war is built.

Maven and adding JARs to system scope

I have a JAR in my Android project and I want it to be added to final APK.
Okay, here I go:
<dependency>
<groupId>com.loopj.android.http</groupId>
<artifactId>android-async-http</artifactId>
<version>1.3.2</version>
<type>jar</type>
<scope>system</scope>
<systemPath>${project.basedir}/libs/android-async-http-1.3.2.jar</systemPath>
</dependency>
But when I am running mvn package I am getting a warning:
[WARNING] Some problems were encountered while building the effective model for **apk:1.0
[WARNING] 'dependencies.dependency.systemPath' for com.loopj.android.http:android-async-http:jar should not point at files within the project directory, ${project.basedir}/libs/android-async-http-1.3.2.jar will be unresolvable by dependent projects # line 36, column 25
And in the final APK there are no JARs.
How do I fix that?
I don't know the real reason but Maven pushes developers to install all libraries (custom too) into some maven repositories, so scope:system is not well liked, A simple workaround is to use maven-install-plugin
follow the usage:
write your dependency in this way
<dependency>
<groupId>com.mylib</groupId>
<artifactId>mylib-core</artifactId>
<version>0.0.1</version>
</dependency>
then, add maven-install-plugin
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
<executions>
<execution>
<id>install-external</id>
<phase>clean</phase>
<configuration>
<file>${basedir}/lib/mylib-core-0.0.1.jar</file>
<repositoryLayout>default</repositoryLayout>
<groupId>com.mylib</groupId>
<artifactId>mylib-core</artifactId>
<version>0.0.1</version>
<packaging>jar</packaging>
<generatePom>true</generatePom>
</configuration>
<goals>
<goal>install-file</goal>
</goals>
</execution>
</executions>
</plugin>
pay attention to phase:clean, to install your custom library into your repository, you have to run mvn clean and then mvn install
You will need to add the jar to your local maven repository. Alternatively (better option) specify the proper repository (if one exists) so it can be automatically downloaded by maven
In either case, remove the <systemPath> tag from the dependency
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<includeSystemScope>true</includeSystemScope>
</configuration>
</plugin>
Try this.
System scope was only designed to deal with 'system' files; files sitting in some fixed location. Files in /usr/lib, or ${java.home} (e.g. tools.jar). It wasn't designed to support miscellaneous .jar files in your project.
The authors intentionally refused to make the pathname expansions work right for that to discourage you. As a result, in the short term you can use install:install-file to install into the local repo, and then some day use a repo manager to share.
Try this configuration. It worked for me:
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>2.4</version>
<configuration>
<warSourceDirectory>mywebRoot</warSourceDirectory>
<warSourceExcludes>source\**,build\**,dist\**,WEB-INF\lib\*,
WEB-INF\classes\**,build.*
</warSourceExcludes>
<webXml>myproject/source/deploiement/web.xml</webXml>
<webResources>
<resource>
<directory>mywebRoot/WEB-INF/lib</directory>
<targetPath>WEB-INF/lib</targetPath>
<includes>
<include>mySystemJar1.jar.jar</include>
<include>mySystemJar2.jar</include>
</includes>
</resource>
</webResources>
</configuration>
</plugin>
Use a repository manager and install this kind of jars into it. That solves your problems at all and for all computers in your network.
mvn install:install-file -DgroupId=com.paic.maven -DartifactId=tplconfig-maven-plugin -Dversion=1.0 -Dpackaging=jar -Dfile=tplconfig-maven-plugin-1.0.jar -DgeneratePom=true
Install the jar to local repository.
Thanks to Ging3r i got solution:
follow these steps:
don't use in dependency tag. Use following in dependencies tag in pom.xml file::
<dependency>
<groupId>com.netsuite.suitetalk.proxy.v2019_1</groupId>
<artifactId>suitetalk-axis-proxy-v2019_1</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.netsuite.suitetalk.client.v2019_1</groupId>
<artifactId>suitetalk-client-v2019_1</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>com.netsuite.suitetalk.client.common</groupId>
<artifactId>suitetalk-client-common</artifactId>
<version>1.0.0</version>
</dependency>
use following code in plugins tag in pom.xml file:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
<executions>
<execution>
<id>suitetalk-proxy</id>
<phase>clean</phase>
<configuration>
<file>${basedir}/lib/suitetalk-axis-proxy-v2019_1-1.0.0.jar</file>
<repositoryLayout>default</repositoryLayout>
<groupId>com.netsuite.suitetalk.proxy.v2019_1</groupId>
<artifactId>suitetalk-axis-proxy-v2019_1</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<generatePom>true</generatePom>
</configuration>
<goals>
<goal>install-file</goal>
</goals>
</execution>
<execution>
<id>suitetalk-client</id>
<phase>clean</phase>
<configuration>
<file>${basedir}/lib/suitetalk-client-v2019_1-2.0.0.jar</file>
<repositoryLayout>default</repositoryLayout>
<groupId>com.netsuite.suitetalk.client.v2019_1</groupId>
<artifactId>suitetalk-client-v2019_1</artifactId>
<version>2.0.0</version>
<packaging>jar</packaging>
<generatePom>true</generatePom>
</configuration>
<goals>
<goal>install-file</goal>
</goals>
</execution>
<execution>
<id>suitetalk-client-common</id>
<phase>clean</phase>
<configuration>
<file>${basedir}/lib/suitetalk-client-common-1.0.0.jar</file>
<repositoryLayout>default</repositoryLayout>
<groupId>com.netsuite.suitetalk.client.common</groupId>
<artifactId>suitetalk-client-common</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<generatePom>true</generatePom>
</configuration>
<goals>
<goal>install-file</goal>
</goals>
</execution>
</executions>
</plugin>
I am including 3 jars from lib folder:
Finally, use mvn clean and then mvn install or 'mvn clean install' and just run jar file from target folder or the path where install(see mvn install log):
java -jar abc.jar
note: Remember one thing if you are working at jenkins then first use mvn clean and then mvn clean install command work for you because with previous code mvn clean install command store cache for dependency.
Following this thread I was able to configure the install plugin to load my custom jar, but the plugin was not seeing my configuration when running a mvn install
I'm using the base maven-install-plugin:2.5.2 using the maven:3.6.3-jdk-8 docker image.
I don't fully understand this note in the documentation (at the end of the section), but it seems that you can give the phase goal an execution id forcing it to use your configuration:
Note: Configurations inside the element used to differ from those that are outside in that they could not be used from a direct command line invocation because they were only applied when the lifecycle phase they were bound to was invoked. So you had to move a configuration section outside of the executions section to apply it globally to all invocations of the plugin. Since Maven 3.3.1 this is not the case anymore as you can specify on the command line the execution id for direct plugin goal invocation. Hence if you want to run the above plugin and it's specific execution1's configuration from the command-line, you can execute:
mvn myqyeryplugin:queryMojo#execution1
My final working docker command:
docker run -it --rm --name parser -v "$(shell pwd)":/usr/src/parser -w /usr/src/parser maven:3.6.3-jdk-8 mvn -X install:install-file#install-my-jar-file
Where install-my-jar-file is my executions id <execution><id>install-my-jar-file</id>...

Resources