Spring Boot : Enabling HTTPS (SSL) Using Cloudflare Certificate
In this blog post, I will explain how to enable HTTPS (SSL) in Spring Boot applications using Cloudflare SSL certificate.
For enabling SSL in Spring Boot applications we can use self signed certificate if you are running application in local development machine but in production you have to use certificate issued by Certificate Authority. In this tutorial I am going to use free SSL certificate provided by Cloudflare.
In my previous blog post, I have deployed Spring Boot application on Oracle cloud. In this blog post I am going to enable HTTPS (SSL) on same application.
Prerequisite
- Own a domain address ( You can get free domain from Freenom)
- Adding domain to Cloudflare
In my previous blog post, I have mapped the server IP address to “ap.fullstackdev.tk” address.
For same domain address we will enable the https (SSL).
Getting SSL Certificate from Cloudflare
- Log into your Cloudflare account. From the home page click on the domain for which you want to download the certificate.
2.From the left side menu, click on the SSL/TLS -> Origin Server
3.Click on the “Create Certificate” button.
4. We can go with default options on “Origin Certification Installation” screen and click on the “Create” button.
The certificate will work for domain and all sub domains ( as *.domain option is used).
5. Copy the generated certificate and private key into a file separately and save them as .pem and .key files.
Now we have the Cloudflare certificate. Copy these file to Oracle server. You can use software like WinSCP to copy files to server.
7. Click on SSL/TLS link on the left side menu and enable “Full” or “Full (Strict)” option
Converting Certificate to PKCS12 Format
The certificate we copied to oracle server are in .pem format. Java applications requires SSL certificate in PKCS12 format. We use package called openssl to convert certificate from pem to pkcs12 format.
Ubuntu distribution installed on Oracle server come with OpenSSL pre installed.
From your terminal enter following command and enter password when asked.
openssl pkcs12 -export -in fullstackdev.tk.pem -out keystore.p12 -inkey fullstackdev.tk.key --name fullstackdev
Code language: Java (java)
Enabling SSL in Spring Boot
Now let’s enable SSL in spring boot application.
We can use following properties in application.properties and enable https (SSL) in spring appplication.
server.port=8443
security.require-ssl=true
server.ssl.key-store=/home/sbuser/certs/keystore.p12
server.ssl.key-store-password=certificatepassword
server.ssl.keyStoreType=PKCS12
Code language: Java (java)
In Spring Boot applications, SSL is generally enabled on port 8443.
In general SSL is enabled when deployed to dev,test and production servers. So we make use of Spring Boot profile feature and create application-prod.properties and place SSL related properties and enable prod profile while running application.
Making Certificate Available
As we are running application with docker.
We make Spring Boot application access certificate in 2 ways.
- Place certificate in resources folder in project
- Copy certs using docker volume
In this blog, I am going to show you, how to make certificate available to application using docker volumes.
We need to make some changes to Dockerfile to access certificates using docker volume as we are running application with non root user.
Following is the docker file we were using to build image
FROM adoptopenjdk/openjdk11:x86_64-alpine-jdk-11.0.14.1_1-slim
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
RUN addgroup -S springboot && adduser -S sbuser -G springboot
USER sbuser
EXPOSE 8080
ENTRYPOINT ["java","-jar","/app.jar"]
Code language: Java (java)
As it is bad practice to run the docker container with root user, we were creating user called “sbuser” and adding him to group called “springboot“
As we are running with non root user, when files/folders are mapped using docker volumes application will not able to access the files/folders as the mapped folders have different owner permission.
for example, in oracle server “ubuntu” is the user name we are using to log into server so files placed in the server will have “ubuntu” as the owner “ubuntu” as group owner
-rw------- 1 ubuntu ubuntu 3020 Jan 6 05:45 keystore.p12
Code language: Java (java)
As we are running container with user “sbuser“, application will not be able to access the file we will get “Permission Denied ” error with docker volumes.
To over come above problem we will assign uid and gid to user and group while creating the user and group and also create folder under home folder of user while building docker image.
FROM adoptopenjdk/openjdk11:x86_64-alpine-jdk-11.0.14.1_1-slim
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
RUN addgroup -S --g 1024 springboot && adduser -u 1024 -S sbuser -G springboot
USER sbuser
RUN mkdir /home/sbuser/certs
EXPOSE 8080
ENTRYPOINT ["java","-jar","/app.jar"]
Code language: Java (java)
In above docker file we are creating “certs ” folder under “sbuser” home . We will map certificates from host machine to this directory.
In server I have copied the certificates to “certs” folder.
Using below command I will set group ownership and of certs directory and files inside it to 1024. 1024 is the group id we have used in the docker file
sudo chown -R :1024
Code language: Java (java)
Note
When you change ownership to groupid , Ubuntu does not check whether that groupid exists in the system.
File permissions looks like below
$ ls -la
total 28
drwxrwxr-x 2 ubuntu 1024 4096 Jan 6 05:57 .
drwxr-x--- 13 ubuntu ubuntu 4096 Jan 4 11:28 ..
-rw------x 1 ubuntu 1024 3020 Jan 2 11:44 keystore.p12
Code language: Java (java)
If the group does not have read permission to certificate file, provide read permission
chmod 641 keystore.p12
Code language: Java (java)
File permissions should look like below
-rw-r----x 1 ubuntu 1024 3020 Jan 2 11:44 keystore.p12
Code language: Java (java)
Now we will map certs folder from host to docker container and enable “prod” profile while running spring boot application.
As we are running application on port 8443, we need to open the port in Oracle cloud VNIC and allow the traffic on port using IP tables of server .You can refer to the my previous blog post for instructions.
We use following docker compose file
version: '3.3'
services:
springboot-app:
container_name: 'springboot-app'
image: 'sureshgkhyd/springboot-flyway-dbmigration'
restart: always
ports:
- 8443:8443
environment:
- SPRING_PROFILES_ACTIVE=prod
volumes:
- /home/ubuntu/certs:/home/sbuser/certs
depends_on:
- "postgres"
postgres:
container_name: 'postgresdb'
image: 'postgres:13.2'
volumes: # volume
- ./postgres/postgres-data:/var/lib/postgresql/data
ports:
- 5432:5432
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: admin
POSTGRES_DB: Test
Code language: Java (java)
Now start the application
docker-compose up
Code language: Java (java)
Spring boot application should start on the port 8443.
[ main] org.hibernate.dialect.Dialect : HHH000400: Using dialect: org.hibernate.dialect.PostgreSQLDialect
[ main] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
[ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
springboot-app | WARN 1 --- [ main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
[ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8443 (https) with context path ''
[ main] e.SpringbootFlywayDbmigrationApplication : Started SpringbootFlywayDbmigrationApplication in 36.379 seconds (JVM running for 39.786)
Code language: Java (java)
Now let’s test the application with Rest client.
Trouble Shooting
Most probably you might face issues with “Permission Denied” error due to permission issues with docker volumes while running Spring boot application with SSL certificate.
Follow the instruction as outlined in the blog post. If you are still facing the problem,
Run the application without SSL, and log into docker container.
docker exec -it springboot-app sh
Code language: Java (java)
Navigate to the /home/sbuser/certs folder check permissions on the files.
The group should point to “springboot” which was created as part of docker file.
In host machine we assigned gid 1024 as group owner, as gid matching with springboot (1024) docker container displays the group name.
$ ls -la
-rw-r----x 1 1001 springbo 3020 Jan 2 11:44 keystore.p12
Code language: Java (java)
References
https://medium.com/@nielssj/docker-volumes-and-file-system-permissions-772c1aee23ca