Docker: Setting up Terraform, Ansible, Nginx and Keycloak with TLS via Step-Ca
I wanted to learn more about authentication in web apps and the Keycloak project seemed an ideal open source way for hands-on experimentation. What followed was a docker project to include various components to achieve this. I have already decided on a template approach to my docker projects and various links here of other templated setups.
https://www.rjruss.info/2025/03/setting-up-sap-web-dispatcher-with-tls.html
https://www.rjruss.info/2024/11/docker-setting-up-prometheus-grafana.html
https://www.rjruss.info/2024/11/docker-setting-up-gitea-with-postgres.html
Using my docker template approach I set out for a simple Hello World type project of calling an Ansible playbook and a Terraform configuration with a very simple authenticated web app via a Keycloak setup. The authentication is where Keycloak is required. Reading further into Ansible and Terraform it would get more complicated to actually move beyond Hello World in how to control such IaC platforms via custom web calls. That didn't put me off :) I set up both to be used independently when connecting the containers themselves. The python code was “vibe coded” with the help of Gemini, and took a lot of troubleshooting and breaking down to individual steps to get it to work. E.g. At one point it was luck that I was getting the right key for the tokens and debug steps helped, but eventually I worked out how to stabilise the code with the help of Gemini. It depends on my next projects if I want to further vibe code the web calls, but the Keycloak knowledge was the thing I started the whole thing for and it proved very useful.
The following output is the end result of building the docker compose project. (Read on for the pre-reqs before even getting to this point).
Ansible output extract
..
PLAY [Hello World Playbook] ****************************************************
TASK [Print Hello World] *******************************************************
ok: [localhost] => {
"msg": "Hello, World!"
}
..
Terraform output extract
…
Plan: 1 to add, 0 to change, 0 to destroy.
Changes to Outputs:
+ hello_world = "Hello, World! from Terraform"
null_resource.hello: Creating...
null_resource.hello: Provisioning with 'local-exec'...
null_resource.hello (local-exec): Executing: ["/bin/sh" "-c" "echo Hello, world from Terraform!"]
null_resource.hello (local-exec): Hello, world from Terraform!
null_resource.hello: Creation complete after 0s [id=4932893277560461013]
…
Read previous blogs on more of the background to my use of Docker but to highlight. Again the project got complicated via some cyber security reasons around storing passwords in clear text and using TLS all over the place. Some security frameworks state that any clear password is an instant fail/breach. And personally and from security frameworks TLS is ideal in local networks.
Step 1 setup local user and directory
Creating a dedicated docker user in this example dockeruser1 - if this is changed the .env file needs to be adapted in step 2
The directory where the downloaded project is stored can be adapted as well.
The example uses /srv/docker-config
Example using the name of the downloaded zip as ansible-terraform-nginx-keycloak-postgres-stepca-docker.zip
Step 2 Adapt the .env file
Change bold entries if required and ignore any other settings in the .env file
Local docker user and group needs to match the user created in step 1
The above would be allow the services to be accessed
Via the docker user
Adapt the dockeruser1 - group dockeruser1 is used by default
Via the shared group
Adapt the group id of 5501 a new group will be created with that gid
Via a new directory/volume (it will be created if it does not exist)
Adapt the /docker1
Via the following domain, hostname and ports
rjruss.org = this needs to be adapted to the required domain
hawarch- = is the base pre-fix of all local hosts that are not part of the proxy
keycloak.${DOMAIN} = resolved via nginx settings to point to keycloak
proxy.${DOMAIN} = resolved via nginx settings to point to test httpbin site (for testing)
ansible.${DOMAIN} = resolved via nginx settings to point to Ansible
terraform.${DOMAIN} = resolved via nginx settings to point to Terraform
Adapt Step CA
AT_APP_STEP_PORT=9006 # Port used by Step CA
These parameters control the name of the STEP services (root certificate name)
AT_PROV_NAME=baseprov
AT_AUTH_NAME=basestepCA
Adapt certificate renewal process sets the certificate renewal time, useful to set the AT_RDAY as tomorrow and AT_RTIME at an appropriate renewal time
AT_RCOUNT=1
AT_RDAY=Sunday
#Initial certificate lieftime in hours
AT_APP_CERT_DUR=24h
#Initial expiry check in %
AT_APP_EXP_CHECK=80%
AT_ROOTC=GB
AT_ROOTCA_VALID=1825
AT_RSLEEP=10m
AT_RTIME="07:30:00"
AT_FIXED_CERT_RENEW_TIME="YES"
AT_BUFFER_CERT_HOURS=2
(postgres is not open to connections other than on the docker network and uses the standard postgres port 5432 inside the docker network)
Step 3 adapt the age related password files
The age (see step 4) command is used to store passwords in an encrypted file on a docker volume. These can be shared between containers via the bash scripts in the base docker container build.
These .*.info files should be deleted ***after completing the step 4 process***
#Postgres DB connection is certificate and password based (password can be used if postgres config adapted with the required password setup)
Step 4 Create Volumes, Networks and encrypted passwords
Following script installs operating system pre-req packages, volumes, networks and encrypts the passwords.
After running the script the following volumes and network will be available.
docker volume ls |grep at
local at_automateit_ansible_vol1
local at_automateit_info_vol1
local at_automateit_keycloak_vol1
local at_automateit_keys_vol1
local at_automateit_nginx_vol1
local at_automateit_postgres_vol1
local at_automateit_step_vol1
local at_automateit_terraform_vol1
docker network ls |grep at
710c2f355fcd at-automateit-net1 bridge local
**** delete these password files ****ENSURE YOU KNOW THE PASSWORDS before deleting****
ls -a .*.info
Step 5 build and run it
Network DNS resolution checks
Before building the docker compose project it is essential to check that the proxy hosts and dedicated hosts resolve as required.
**SUCCESSFUL checks
The above example is from a test server to confirm this works on another server :)
The following hosts need to resolve to make this setup work
Proxy related hosts (resolved via nginx connection)
ansible2.rjruss.org resolved
keycloak2.rjruss.org resolved
proxy2.rjruss.org resolved
terraform2.rjruss.org resolved
Direct related hosts that need to succeed on the resolution
hawarch-keycloak.rjruss.org resolved
hawarch-step.rjruss.org resolved
***WARNING checks
The warnings are for future work or maybe not used. The hosts are part of the template based approach and for direct connections to the containers
Could take a while depending on the server/computer performance! as it builds quite a but
Wait until you see the keycloak container logs indicating the health check is UP
Step 6 Run the test Ansible and Terraform scripts
Run “test-ansible.sh” and “test-terraform.sh” to confirm the setup is working as expected. The scripts download the required Step based CA root certificate locally to the docker host and then calls the containers via the nginx proxy. Take a look at the scripts for the setup.
Start a new terminal session
su - dockeruser1
cd /srv/docker-config/ansible-terraform-nginx-keycloak-postgres-stepca-docker/
#Run the Ansible test script
The script calls a python web app running on the ansible container via the nginx proxy to get the token from Keycloak. The using the token calls Ansible again to run the playbook
A successful call is shown above with the “Hello, World!” output
#Run the Terraform test script
Using the same principal as the Ansible setup but calling the Terraform container to run the configuration.
A successful call is shown above with the Hello, World! from Terraform output
Certificate Renewal Control Script(s)
The above scripts will set the certificate renewal to the following settings
#The date and time the certificate check will take place
Wake up time to check certificate : 30-08-2025 18:30:00
#The controlling scripts will sleep every 2 minutes before checking date and time for
# certificate renewal
Sleep interval : 2m
#Not used in this project. This is an option to check that the certificate has X days left
Days remaining for certificate expiry check : 1
#This is where Step CA command will check that the certificate still valid
Percentage for certificate expiry check : 80%
#Length of the renewed certificate -
Length in days a new certificate is valid for : 1
Appendix
A: General Information: Keycloak
To access the keycloak via the web admin pages you need the Step root certificate on your host computer. A helper script as follows is setup to cut and paste the command to get this on a windows computer via powershell.
Run,
./display_web_urls.sh
## automateit-step-run
## STEP powershell to trust certificate - added as a trusted root so use it on that understanding
downloading step CA certificate to /var/tmp/stepCA.pem
Successfully copied 2.56kB to /var/tmp/stepCA.pem
I have enclosed the powershell in the table to run on a windows computer via powershell prompt
Install step-ca root certificate on host windows
Answer YES at the prompt
I have a homelab Hyper-V landscape with a windows DNS host, and it is important to set up dns resolution for all the proxy hosts in this project
For my local windows based P.C I updated the host file with the proxy hosts from earlier.
My example I need the Keycloak proxy URL and Nginx Proxy port e.g.
https://keycloak2.rjruss.org:44340/
The new admin user is hard coded as newuser the password is the PW defined in the .KEYCLOAK.info file from earlier (that should have been deleted after noting down what the password was ;))
Keycloak Realms
I am no Keycloak expert and the realm approach is open to interpretation on how to set these up for any use case. So my setup is a home lab with few users, therefore I decided on a dedicated realm for each use case - it is obviously best to check out realm usage for any production based projects.
I used a naming convention to identify which Keycloak service/function was for each by noting -an- for Ansible and -tr- for Terraform. E.g. auto-an-realm is for the public based client defined in Keycloak for the Ansible. All the configuration is setup automatically via the kcadm.sh cli within the startup scripts for the Keycloak container. There is no manual intervention required to run the test Hello World web apps. Check out the scripts for these kcadm.sh commands and flow.
Screenshot shows the defined realms that are all created when starting the project for the first time. (There is a private client realm and client defined but not currently used).
Miscellaneous actions
Remove demo certs from certstore, from the example code only - if the root CA name is changed then the *basestep* match won't match any…..