Contents

Create tls connect between MongoDB and Java Spring Boot application

| Ubuntu 22.04

  • List of parameters used in application.yaml
  • Parameters from mongo
  • Good connect string: mongodb+srv://<login>:<password>@<hostname>/<database-to-connect>?replicaSet=<name-of-replicaset>&readPreference=secondary&retryWrites=true&w=majority&authSource=admin&tls=true
  • Repository with all files

Run mongodb inside Docker

1
2
3
4
docker run --rm --name mongodb -p 27017:27017 \
  -e MONGO_INITDB_ROOT_USERNAME=admin \
  -e MONGO_INITDB_ROOT_PASSWORD=secret \
  mongo

Connect to admin’s db and create a db with user (auth db will be the same db):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
mongosh  "mongodb://admin:secret@localhost/admin?ssl=false"

admin> use mongo-test

mongo-test> db.createUser(
    {
      user: "admin",
      pwd: "pass",
      roles: [
         { role: "userAdmin", db: "mongo-test" },
         { role: "readWrite", db: "mongo-test" }
      ]
    }
)

Check connection to db mongo-test and get collections (it will be empty):

1
2
3
4
5
mongosh  "mongodb://admin:pass@localhost/mongo-test?ssl=false"

# Get collections from db mongo-test
mongo-test> db.getCollectionNames()
[]

SpringBoot application

Install SdkMan!

1
2
3
4
sdk version
SDKMAN!
script: 5.18.2
native: 0.4.6

Install Spring Boot, gradle and java

1
2
3
4
5
6
7
8
9
# Find
sdk list springboot
sdk list gradle
sdk list java

# Install
sdk install springboot 3.3.5
sdk install gradle 8.10.2
sdk install java 21.0.5-oracle

Generate Spring Boot application with MongoDB

List of supported dependencies:

1
2
spring init --list | grep -iE "mongo|web"
spring --help init

Initiate Spring Boot application:

1
2
3
4
5
6
7
spring init --build=gradle --type=gradle-project-kotlin \
  --language=java --boot-version=3.3.5 \
  --group-id="com.example" --artifact-id=demo --name=Demo \
  --description="Demo project for Spring Boot" \
  --packaging="jar" --java-version=21 \
  --dependencies=web,data-mongodb /tmp/mongodb-demo
Project extracted to '/tmp/mongodb-demo'

Describe connection to MongoDB:

1
2
3
4
cd /tmp/mongodb-demo
cat src/main/resources/application.properties
spring.application.name=Demo
spring.data.mongodb.uri=mongodb://admin:pass@localhost/mongo-test?ssl=false

Using the example, we will prepare the source code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
cd src/main/java/com/example/demo/

mkdir model
vi model/GroceryItem.java

mkdir repository
vi repository/CustomItemRepositoryImpl.java
vi repository/CustomItemRepository.java
vi repository/ItemRepository.java

vi DemoApplication.java

cd /tmp/mongodb-demo

Build the source code:

1
2
3
4
5
6
gradle tasks
gradle tasks --all
gradle build -x test

# Run the jar artifact
java -jar build/libs/demo-0.0.1-SNAPSHOT.jar

Check that collections and items were created:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
mongosh  "mongodb://admin:pass@localhost/mongo-test?ssl=false"
mongo-test> db.getCollectionNames()
[ 'GroceryItem' ]
mongo-test> db.GroceryItem.find()
[
  {
    _id: 'Whole Wheat Biscuit',
    name: 'Whole Wheat Biscuit',
    quantity: 5,
    category: 'munchies',
    _class: 'com.example.demo.model.GroceryItem'
  },
  {
    _id: 'Dried Red Chilli',
    name: 'Dried Whole Red Chilli',
    quantity: 2,
    category: 'spices',
    _class: 'com.example.demo.model.GroceryItem'
  },
  {
    _id: 'Pearl Millet',
    name: 'Healthy Pearl Millet',
    quantity: 1,
    category: 'millets',
    _class: 'com.example.demo.model.GroceryItem'
  },
  {
    _id: 'Cheese Crackers',
    name: 'Bonny Cheese Crackers Plain',
    quantity: 10,
    category: 'munchies',
    _class: 'com.example.demo.model.GroceryItem'
  }
]

Now, add tls beetween MongoDB and our application:

Prepare certificates

1
2
ls ts
additional.info  mongodb.crt  mongodb.csr  mongodb.jks  mongodb.key  mongodb.p12  mongodb.pem  mytruststore.jks  rootCA.key  rootCA.pem  rootCA.srl

Run MongoDB with tls

Read only owner:

1
chmod 0400 mongodb/ca.crt mongodb/mongodb.pem

Run MongoDB without tls and initiate user and password:

1
2
3
4
5
6
mkdir -pv ./mongodb/db
docker run --rm --name mongodb -p 27017:27017 \
  -v "./mongodb/db:/data/db" \
  -e MONGO_INITDB_ROOT_USERNAME=admin \
  -e MONGO_INITDB_ROOT_PASSWORD=secret \
  mongo

Run MongoDB with tls:

1
2
3
4
5
6
7
docker run --rm --name mongodb -p 27017:27017 \
  -v "./tls/rootCA.pem:/etc/ssl/ca.crt" \
  -v "./tls/mongodb.pem:/etc/ssl/mongodb.pem" \
  -v "./mongodb/db:/data/db" \
  -e MONGO_INITDB_ROOT_USERNAME=admin \
  -e MONGO_INITDB_ROOT_PASSWORD=secret \
  mongo sh -c "mongod --bind_ip_all --tlsMode requireTLS --tlsCAFile /etc/ssl/ca.crt --tlsCertificateKeyFile /etc/ssl/mongodb.pem"

Check connect with tls:

1
mongosh --tls --tlsCAFile ./tls/rootCA.pem --tlsCertificateKeyFile ./tls/mongodb.pem "mongodb://admin:secret@localhost/admin?tls=true"

Create database and user:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
use mongo-test

db.createUser(
    {
      user: "admin",
      pwd: "pass",
      roles: [
         { role: "userAdmin", db: "mongo-test" },
         { role: "readWrite", db: "mongo-test" }
      ]
    }
)

Check connection with created auth data:

1
mongosh --tls --tlsCAFile ./tls/rootCA.pem --tlsCertificateKeyFile ./tls/mongodb.pem "mongodb://admin:pass@localhost/mongo-test?tls=true"

Build and run spring application

1
2
3
4
5
6
7
cat src/main/resources/application.properties
spring.application.name=Demo
spring.data.mongodb.uri=mongodb://admin:pass@localhost/mongo-test?tls=true

gradle build -x test
export JAVA_TOOL_OPTIONS='-Djavax.net.ssl.trustStore=tls/mytruststore.jks -Djavax.net.ssl.trustStorePassword=thebattleforarrakis -Djavax.net.ssl.keyStore=tls/mongodb.jks -Djavax.net.ssl.keyStorePassword=thebattleforarrakis'
java -jar build/libs/demo-0.0.1-SNAPSHOT.jar

Communication between containers

Create network and run MongoDB there:

1
2
3
4
5
6
7
8
docker network create mongodb
docker run --rm --name mongodb -p 27017:27017 \
  -v "./tls/rootCA.pem:/etc/ssl/ca.crt" \
  -v "./tls/mongodb.pem:/etc/ssl/mongodb.pem" \
  -v "./mongodb/db:/data/db" \
  -e MONGO_INITDB_ROOT_USERNAME=admin \
  -e MONGO_INITDB_ROOT_PASSWORD=secret --network mongodb \
  mongo sh -c "mongod --bind_ip_all --tlsMode requireTLS --tlsCAFile /etc/ssl/ca.crt --tlsCertificateKeyFile /etc/ssl/mongodb.pem"

Prepare Dockerfile

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
cat <<EOF> Dockerfile
FROM gradle:8.10-jdk21-alpine AS build

ENV GRADLE_OPTS="-Dorg.gradle.daemon=false"

WORKDIR /src
COPY . .
RUN gradle build -x test

FROM bellsoft/liberica-openjdk-alpine-musl:21

ARG APP_NAME=demo
ENV APP_NAME=\$APP_NAME
WORKDIR /opt
COPY --from=build ./src/build/libs /opt

RUN addgroup -S --gid 1001 "\$APP_NAME" \\
    && adduser -S "\$APP_NAME" --uid 1001 -G "\$APP_NAME" \\
    && chown -R "\$APP_NAME":"\$APP_NAME" /opt
USER \$APP_NAME  

EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/opt/demo-0.0.1-SNAPSHOT.jar"]
EOF

And .dockerignore:

1
2
3
cat <<EOF> .dockerignore
mongodb/db/
EOF

Change connection string:

1
2
3
cat src/main/resources/application.properties
spring.application.name=Demo
spring.data.mongodb.uri=mongodb://admin:pass@mongo.mongo/mongo-test?tls=true

Build and run container with our demo application:

1
2
3
4
docker build -t demo:0.0.1 .
docker run --name demo -v $(pwd)/tls:/tls -it --network mongodb \
  -e 'JAVA_TOOL_OPTIONS=-Djavax.net.ssl.trustStore=/tls/mytruststore.jks -Djavax.net.ssl.trustStorePassword=thebattleforarrakis -Djavax.net.ssl.keyStore=/tls/mongodb.jks -Djavax.net.ssl.keyStorePassword=thebattleforarrakis' \
  demo:0.0.1

Kubernetes

Build image with tag and push into docker registry:

1
docker build --push -t tty8747/demo:0.0.1 .

Prepare secrets with passwords for truststore, keyStore and pkcs12:

1
2
3
4
5
kubectl --namespace default create secret generic jks-secret \
  --from-literal=keystore-password=Oofiewe8uv1Gahriac \
  --from-literal=truststore-password=Oofiewe8uv1Gahriac

kubectl --namespace default create secret generic pkcs12-secret --from-literal=pkcs12-password=ahwohraexi5eeN4Ahf

And create certificate and key with cert-manager:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
cat <<EOF | kubectl apply -f-
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: demo-internal
  namespace: default
spec:
  secretName: demo-internal-tls
  secretTemplate:
    labels:
      demo: secret
  privateKey:
    algorithm: RSA
    encoding: PKCS1
    size: 2048
  keystores:
    jks:
      create: true
      passwordSecretRef:
        name: jks-secret
        key: keystore-password
    pkcs12:
      create: true
      passwordSecretRef:
        name: pkcs12-secret
        key: pkcs12-password
  duration: 2160h # 90d
  renewBefore: 360h # 15d
  isCA: false
  usages:
    - server auth
    - client auth
    - digital signature
    - key encipherment
  subject:
    organizations:
      - cert-manager
  commonName: demo-internal
  dnsNames:
    - "localhost"
    - "mongodb-psmdb-db-rs01"
    - "mongodb-psmdb-db-rs01.temp"
    - "mongodb-psmdb-db-rs01.temp.svc.cluster.local"
    - "*.mongodb-psmdb-db-rs01"
    - "*.mongodb-psmdb-db-rs01.temp"
    - "*.mongodb-psmdb-db-rs01.temp.svc.cluster.local"
    - "mongodb-psmdb-db-rs01.temp.svc.clusterset.local"
    - "*.mongodb-psmdb-db-rs01.temp.svc.clusterset.local"
    - "*.temp.svc.clusterset.local"
    - "mongodb-psmdb-db-mongos"
    - "mongodb-psmdb-db-mongos.temp"
    - "mongodb-psmdb-db-mongos.temp.svc.cluster.local"
    - "*.mongodb-psmdb-db-mongos"
    - "*.mongodb-psmdb-db-mongos.temp"
    - "*.mongodb-psmdb-db-mongos.temp.svc.cluster.local"
    - "mongodb-psmdb-db-cfg"
    - "mongodb-psmdb-db-cfg.temp"
    - "mongodb-psmdb-db-cfg.temp.svc.cluster.local"
    - "*.mongodb-psmdb-db-cfg"
    - "*.mongodb-psmdb-db-cfg.temp"
    - "*.mongodb-psmdb-db-cfg.temp.svc.cluster.local"
    - "mongodb-psmdb-db-mongos.temp.svc.clusterset.local"
    - "*.mongodb-psmdb-db-mongos.temp.svc.clusterset.local"
    - "mongodb-psmdb-db-cfg.temp.svc.clusterset.local"
    - "*.mongodb-psmdb-db-cfg.temp.svc.clusterset.local"
  issuerRef:
    name: my-ca-issuer
    kind: ClusterIssuer
    group: cert-manager.io
EOF

Run pod of demo application and run

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
cat <<EOF pod.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: demo
  name: demo
spec:
  containers:
  - image: tty8747/demo:0.0.1
    name: demo
    command:
      - sh
      - -c
      - sleep 3600
    volumeMounts:
    - mountPath: /tls
      name: tls
      readOnly: true
  volumes:
  - name: tls
    secret:
      secretName: demo-internal-tls
  dnsPolicy: ClusterFirst
  restartPolicy: Never
EOF

Connect to demo pod and run:

1
2
export JAVA_TOOL_OPTIONS='-Djavax.net.ssl.trustStore=/tls/truststore.jks -Djavax.net.ssl.trustStorePassword=Oofiewe8uv1Gahriac -Djavax.net.ssl.keyStore=/tls/keystore.jks -Djavax.net.ssl.keyStorePassword=Oofiewe8uv1Gahriac -Dspring.data.mongodb.uri=mongodb+srv://admin:pass@mongodb-psmdb-db-rs01.temp.svc.csp.test.cluster/mongo-test?replicaSet=rs01&readPreference=secondary&retryWrites=true&w=majority&authSource=mongo-test&tls=true'
java -jar demo-0.0.1-SNAPSHOT.jar