Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/#118 add certificates to internet connection #119

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 47 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,7 @@ There are several docker compose files that start different services, according
### User's perspective
From a user's point of view, the application consists of the following.
Take note that the ports are defined in file *env_template.env* as external ports and can be changed if needed or desired.
- A home web site accesible via port **80**
From which all applications can be called
- A home web site accesible via port **80**, or port **443** (assuming Letsencrypt certificates were generated and configured for the Host it is running on).
- An ADempiere ZK UI accesible via path **/webui**
- An ADempiere Vue UI accesible via port **/vue**
- A Postgres database accesible e.g. by PGAdmin via port **55432**
Expand All @@ -55,33 +54,33 @@ Take note that the ports are defined in file *env_template.env* as external port

### Application Stack
The application stack consists of the following services defined in the *docker-compose files* (and retrieved on the console with **docker compose ls**); these services will eventually run as containers:
- **adempiere-site**: defines the landing page (web site) for this application
- **postgresql.service**: defines the Postgres database
- **adempiere-zk**: defines the Jetty server and the ADempiere ZK UI
- **adempiere-grpc-server**: Defines a grpc server as the backend server for Vue
- **adempiere-site**: the landing page (web site) for this application
- **postgresql.service**: the Postgres database
- **adempiere-zk**: the Jetty server and the ADempiere ZK UI
- **adempiere-grpc-server**: a grpc server as the backend server for Vue
- **adempiere.processor**: for processes that are executed outside Adempiere
- **dkron.scheduler**: a scheduler
- **grpc.proxy**: an envoy server
- **vue-ui**: defines ADempiere Vue UI
- **vue-ui**: ADempiere Vue UI
- **opensearch.node**: stores the Application Dictionary definitions
- **opensearch.setup**: configure the service *opensearch.node*
- **zookeeper**: controller for *kafka* service
- **kafka**: messaging and streaming
- **kafdrop**: a Kafka Cluster Queues Overview, Monitor and Administrator
- **dictionary.rs**:
- **keycloak**: user management on service *postgresql.service*
- **ui.gateway**:
- **ui.gateway**: manages all incoming requests and distributes them to the corresponding containers.
- **s3.storage**: for attachments
- **s3.client**: configuration of "s3-storage" service
- **s3.gateway.rs**:
- **opensearch.dashboards**: display and monitor of e.g. exported menus, smart browsers, forms, windows, processes.

Additional objects defined in the *docker-compose files*:
- `adempiere_network`: defines the subnet used in the involved Docker containers (e.g. **192.168.100.0/24**)
- `volume_postgres`: defines the mounting point of the Postgres database (typically directory **/var/lib/postgresql/data**) to a local directory on the host where the Docker container runs. This implement a persistent database.
- `volume_backups`: defines the mounting point for a backup (or restore) directory on the Docker container to a local directrory on the host where the Docker container has access.
- `adempiere_network`: the subnet used in the involved Docker containers (e.g. **192.168.100.0/24**)
- `volume_postgres`: the mounting point of the Postgres database (typically directory **/var/lib/postgresql/data**) to a local directory on the host where the Docker container runs. This implement a persistent database.
- `volume_backups`: the mounting point for a backup (or restore) directory on the Docker container to a local directrory on the host where the Docker container has access.
- `volume_persistent_files`: mounting point for the ZK container
- `volume_scheduler`: defines the mounting point for the scheduler (`TO BE IMPLEMENTED YET`)
- `volume_scheduler`: the mounting point for the scheduler (`TO BE IMPLEMENTED YET`)

### Architecture
- The application `vue` stack as graphic:
Expand Down Expand Up @@ -465,6 +464,42 @@ cd adempiere-ui-gateway/docker-compose
Connect to database via port **55432** with a DB connector, e.g. PGAdmin.
Or to the port the variable `POSTGRES_EXTERNAL_PORT` points in file `env_template.env`.

## Using Certificates
You can use whatever cerfiticate you may choose.
Here, instructions to implement Letsencrypt are documented.
If you decide to implement with other Certificate Authority (CA), you can follow the instructions, only changing Letsencrypt with the CA selected.
### Get Letsencrypt Certificate
Steps to use Letsencrypt certificates for a secure communication:
- Letsencrypt issues Certificates only to a server which owns a Domainename.
- On your registrar, make sure the Domainname you want to use points to the to the IP of the Host server.
- Install the program `certbot` on your Host to get certificates.
- The port 80 must be enabled on the Host so certbot can generate the certificates.
- Generate with certbot on the Host the `Letsencrypt certificates`. The option `--standalone` suits best when doing it manually.
- Example: `sudo certbot certonly --standalone --preferred-challenges http -d YOUR_DOMAINNAME`
- If the generation runs successsfully four files will be created: privkey.pem, fullchain.pem, chain.pem and cert.pem.
- On files `docker-compose/docker-compose-standard.yml` and `docker-compose/17d-ui_gateway_service_standard.yml`, replace YOUR_DOMAINNAME with the DNS value you have acquired the Certificate for on the Host.
- Still on files `docker-compose/docker-compose-standard.yml` and `docker-compose/17d-ui_gateway_service_standard.yml`, make sure that the files `fullchain.pem` and `privkey.pem` are mounted to the files where Letsencrypt generated the certificates.
- If you want to apply Certificates to other application stacks, you must apply the changes on the other files where the service ui.gateway is defined.
- On file `docker-compose/nginx/gateway/api_gateway.conf`, uncomment the directives concerning the certificate and replace the text YOUR_DOMAINNAME with the Domain Name reserved for the Host.
- Still on file `docker-compose/nginx/gateway/api_gateway.conf`, make sure that the variables `ssl_certificate` and `ssl_certificate_key` are mounted to the files where Letsencrypt generated the certificates.
- **Important**
- the Letsencrypt certificate generation includes cron jobs to renew the certificate validation.
- the certificate validation process runs twice a day and needs the port 80 to be unused while validating.
- this means, the Nginx server must be down for the renewal to be effective.
- so you must either synchronize the certificate validation process to be on time with a programmed downtime of the Nginx server, or you disable the certificate validation process and stop the Nginx server and run the certificate validation process manually at least one month before expiration.
- another solution would be to open another port but 80 to access Nginx, but it is up to you.
### Make Certificate Transmission Secure
Diffie-Hellman script makes the certificate transmission more secure.
- Generate on the Host a `Diffie-Hollman` key to secure the Certificate transmission.
- Usually, you generate it with the following command: `openssl dhparam -out /etc/nginx/dhparam.pem 4096`.
- It may take long to generate the key.
- On files `docker-compose/docker-compose-standard.yml` and `docker-compose/17d-ui_gateway_service_standard.yml`, make sure the Diffie-Hellman key is mounted orrectly.
- On file `docker-compose/nginx/gateway/api_gateway.conf`, make sure that the variable `ssl_dhparam` is mounted to the file where the Diffie-Hollman key was generated.
### Misc Certificates
- Enable access of Port 443 on the Host.
- You can still use HTTP protocoll, because the context for port 80 is left untouched.
- Remember that Letsencrypt certificates are issued for 90 days.

## Useful Commands
This application uses **Docker Compose** and as such, all docker and docker compose commands can be called to work wit it.
### Container Management
Expand Down
6 changes: 4 additions & 2 deletions docker-compose/.env
Original file line number Diff line number Diff line change
Expand Up @@ -250,8 +250,10 @@ ENVOY_GRPC_PROXY_REPORT_PORT=5557
NGINX_UI_GATEWAY_IMAGE="nginx:1.26.0-alpine3.19"
NGINX_UI_GATEWAY_CONTAINER_NAME="${COMPOSE_PROJECT_NAME}.nginx.ui.gateway"
NGINX_UI_GATEWAY_HOSTNAME="${CLIENT_NAME}.nginx"
NGINX_UI_GATEWAY_EXTERNAL_PORT=80
NGINX_UI_GATEWAY_INTERNAL_PORT=80
NGINX_UI_GATEWAY_HTTP_EXTERNAL_PORT=80
NGINX_UI_GATEWAY_HTTP_INTERNAL_PORT=80
NGINX_UI_GATEWAY_HTTPS_EXTERNAL_PORT=443
NGINX_UI_GATEWAY_HTTPS_INTERNAL_PORT=443
NGINX_UI_GATEWAY_VOLUME="${COMPOSE_PROJECT_NAME}.volume_nginx" # Not used yet in in docker-compose.yml.


Expand Down
13 changes: 12 additions & 1 deletion docker-compose/17d-ui_gateway_service_standard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,24 @@
adempiere.site:
condition: service_started
ports:
- ${NGINX_UI_GATEWAY_EXTERNAL_PORT}:${NGINX_UI_GATEWAY_INTERNAL_PORT}
- ${NGINX_UI_GATEWAY_HTTP_EXTERNAL_PORT}:${NGINX_UI_GATEWAY_HTTP_INTERNAL_PORT}
# Remember to open port 443 on host if you want to use HTTPS protocol
- ${NGINX_UI_GATEWAY_HTTPS_EXTERNAL_PORT}:${NGINX_UI_GATEWAY_HTTPS_INTERNAL_PORT}
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
- ./nginx/upstreams/:/etc/nginx/api_upstreams_conf.d
- ./nginx/api/:/etc/nginx/api_conf.d
- ./nginx/gateway/api_gateway.conf:/etc/nginx/api_gateway.conf
- ./nginx/api_json_errors.conf:/etc/nginx/api_json_errors.conf

# Certificate generated by Letsencypt on the Host. Only fullchain.pem and privkey.pem are needed.
# Replace YOUR_DOMAINNAME with the DNS value you have acquired the Certificate for on the Host
- /etc/letsencrypt/live/YOUR_DOMAINNAME/fullchain.pem:/etc/letsencrypt/live/YOUR_DOMAINNAME/fullchain.pem
- /etc/letsencrypt/live/YOUR_DOMAINNAME/privkey.pem:/etc/letsencrypt/live/YOUR_DOMAINNAME/privkey.pem
# Diffie-Hellman to secure Certificate transport. It must also be generated on the Host.
# Command to generate: openssl dhparam -out /etc/nginx/dhparam.pem 4096
- /etc/nginx/dhparam.pem:/etc/nginx/dhparam.pem

#- ./keys/api_keys.conf:/etc/nginx/api_keys.conf
# time zone
- ${TIMEZONE_PATH_ON_HOST}:${TIMEZONE_PATH_ON_CONTAINER}:${TIMEZONE_OPTIONS} # Map the Timezone of the host to the Timezone of the container
Expand Down
12 changes: 11 additions & 1 deletion docker-compose/docker-compose-standard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -469,12 +469,22 @@ services:
- ./nginx/api/:/etc/nginx/api_conf.d
- ./nginx/gateway/api_gateway.conf:/etc/nginx/api_gateway.conf
- ./nginx/api_json_errors.conf:/etc/nginx/api_json_errors.conf

# Certificate generated by Letsencypt on the Host. Only fullchain.pem and privkey.pem are needed.
# Replace YOUR_DOMAINNAME with the DNS value you have acquired the Certificate for on the Host
- /etc/letsencrypt/live/YOUR_DOMAINNAME/fullchain.pem:/etc/letsencrypt/live/YOUR_DOMAINNAME/fullchain.pem
- /etc/letsencrypt/live/YOUR_DOMAINNAME/privkey.pem:/etc/letsencrypt/live/YOUR_DOMAINNAME/privkey.pem
Comment on lines +475 to +476
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is necessary that the internal folder of the nginx container contains the word YOUR_DOMAINNAME? I understand that it can be replaced, but if you can use something generic (like secure_domain or certificates) so that it is established as a criterion that it will always be the internal path of the container, I think it would be more practical regardless if the host path changes.

# Diffie-Hellman to secure Certificate transport. It must also be generated on the Host.
# Command to generate: openssl dhparam -out /etc/nginx/dhparam.pem 4096
- /etc/nginx/dhparam.pem:/etc/nginx/dhparam.pem

#- ./keys/api_keys.conf:/etc/nginx/api_keys.conf
# time zone
- ${TIMEZONE_PATH_ON_HOST}:${TIMEZONE_PATH_ON_CONTAINER}:${TIMEZONE_OPTIONS} # Map the Timezone of the host to the Timezone of the container
- ${LOCALTIME_PATH_ON_HOST}:${LOCALTIME_PATH_ON_CONTAINER}:${LOCALTIME_OPTIONS} # Map the Localtime of the host to the Timezone of the container
ports:
- ${NGINX_UI_GATEWAY_EXTERNAL_PORT}:${NGINX_UI_GATEWAY_INTERNAL_PORT}
- ${NGINX_UI_GATEWAY_HTTP_EXTERNAL_PORT}:${NGINX_UI_GATEWAY_HTTP_INTERNAL_PORT}
- ${NGINX_UI_GATEWAY_HTTPS_EXTERNAL_PORT}:${NGINX_UI_GATEWAY_HTTPS_INTERNAL_PORT}
networks:
- adempiere_network

Expand Down
6 changes: 4 additions & 2 deletions docker-compose/env_template.env
Original file line number Diff line number Diff line change
Expand Up @@ -250,8 +250,10 @@ ENVOY_GRPC_PROXY_REPORT_PORT=5557
NGINX_UI_GATEWAY_IMAGE="nginx:1.26.0-alpine3.19"
NGINX_UI_GATEWAY_CONTAINER_NAME="${COMPOSE_PROJECT_NAME}.nginx.ui.gateway"
NGINX_UI_GATEWAY_HOSTNAME="${CLIENT_NAME}.nginx"
NGINX_UI_GATEWAY_EXTERNAL_PORT=80
NGINX_UI_GATEWAY_INTERNAL_PORT=80
NGINX_UI_GATEWAY_HTTP_EXTERNAL_PORT=80
NGINX_UI_GATEWAY_HTTP_INTERNAL_PORT=80
NGINX_UI_GATEWAY_HTTPS_EXTERNAL_PORT=443
NGINX_UI_GATEWAY_HTTPS_INTERNAL_PORT=443
NGINX_UI_GATEWAY_VOLUME="${COMPOSE_PROJECT_NAME}.volume_nginx" # Not used yet in in docker-compose.yml.


Expand Down
66 changes: 57 additions & 9 deletions docker-compose/nginx/gateway/api_gateway.conf
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,63 @@ server {
# listen 127.0.0.1:80;
listen localhost:80;

#listen 443 ssl;
# server_name api.adempiere.io;
# TLS config
#ssl_certificate /etc/ssl/certs/api.example.com.crt;
#ssl_certificate_key /etc/ssl/private/api.example.com.key;
#ssl_session_cache shared:SSL:10m;
#ssl_session_timeout 5m;
#ssl_ciphers HIGH:!aNULL:!MD5;
#ssl_protocols TLSv1.2 TLSv1.3;
# API definitions, one per file
include api_conf.d/*.conf;
include api_conf.d/**/*.conf;

# Error responses
error_page 404 = @400; # Treat invalid paths as bad requests
proxy_intercept_errors off; # Do not send backend errors to client
include api_json_errors.conf; # API client-friendly JSON errors
default_type application/json; # If no content-type, assume JSON
}

server {
# Context for HTTPS (port 443)
# You must first generate a certificate in the Host, for example with Letsencrypt.
# Then, you must replace the text "YOUR_DOMAINNAME" with the name the certificate was issued for and uncomment the corresponding lines below.
# The directives server_name, ssl_certificate and ssl_certificate_key must be uncommented and have the correct values for the HTTPS connection to work correctly.
# Otherwise, there will be a connection error.
# Do no forget to also open port 443 or HTTP protocol on the Host.

access_log /var/log/nginx/api_access.log main; # Each API may also log to a separate file

# Begin of Certificate suggestions by https://cipherlist.eu/
listen 443 ssl;
listen [::]:443 ssl;
listen localhost:443;

# Uncomment the following line and replace "YOUR_DOMAINNAME" with the DNS value of the HOST
# server_name YOUR_DOMAINNAME; # Here: the (sub)domain this server is referred as

# Uncomment the following line and replace "YOUR_DOMAINNAME" with the DNS value of the HOST
# ssl_certificate /etc/letsencrypt/live/YOUR_DOMAINNAME/fullchain.pem; # Generated for (sub)domain on Host and mounted here

# Uncomment the following line and replace "YOUR_DOMAINNAME" with the DNS value of the HOST
# ssl_certificate_key /etc/letsencrypt/live/YOUR_DOMAINNAME/privkey.pem; # Generated for (sub)domain on Host and mounted here

ssl_protocols TLSV1.2 TLSv1.3;# Requires nginx >= 1.13.0 else use TLSv1.2
ssl_prefer_server_ciphers on;

# Uncomment the following line once you have generated a Diffie-Hellman key
# ssl_dhparam /etc/nginx/dhparam.pem; # Diffie-Hellman generated on Host and mounted here: openssl dhparam -out /etc/nginx/dhparam.pem 4096

ssl_ciphers EECDH+CHACHA20:EECDH+AESGCM:EDH+AESGCM;
ssl_conf_command Ciphersuites TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256;
ssl_conf_command Options PrioritizeChaCha;
ssl_ecdh_curve secp384r1; # Requires nginx >= 1.1.0
ssl_session_timeout 10m;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off; # Requires nginx >= 1.5.9
ssl_stapling on; # Requires nginx >= 1.3.7
ssl_stapling_verify on; # Requires nginx => 1.3.7
resolver 8.8.8.8 8.8.4.4 valid=300s; # Google. Choose as you please.
resolver_timeout 5s;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
# End of Certificate suggestions by https://cipherlist.eu/

# API definitions, one per file
include api_conf.d/*.conf;
Expand Down