Skip to content
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -170,3 +170,4 @@ chatmail.zone
/custom/
docker-compose.yaml
.env
/traefik/data/
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

- Add configuration parameters
([#614](https://github.com/chatmail/relay/pull/614)):
- `use_foreign_cert_manager` - Use a third-party certificate manager instead of acmetool (default: `False`)
- `change_kernel_settings` - Whether to change kernel parameters during installation (default: `True`)
- `fs_inotify_max_user_instances_and_watchers` - Value for kernel parameters `fs.inotify.max_user_instances` and `fs.inotify.max_user_watches` (default: `65535`)

Expand Down
3 changes: 3 additions & 0 deletions chatmaild/src/chatmaild/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ def __init__(self, inipath, params):
)
self.mtail_address = params.get("mtail_address")
self.disable_ipv6 = params.get("disable_ipv6", "false").lower() == "true"
self.use_foreign_cert_manager = (
params.get("use_foreign_cert_manager", "false").lower() == "true"
)
self.change_kernel_settings = (
params.get("change_kernel_settings", "true").lower() == "true"
)
Expand Down
3 changes: 3 additions & 0 deletions chatmaild/src/chatmaild/ini/chatmail.ini.f
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@
# if set to "True" IPv6 is disabled
disable_ipv6 = False

# if you set "True", acmetool will not be installed and you will have to manage certificates yourself.
use_foreign_cert_manager = False

#
# Kernel settings
#
Expand Down
9 changes: 5 additions & 4 deletions cmdeploy/src/cmdeploy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -726,10 +726,11 @@ def deploy_chatmail(config_path: Path, disable_mail: bool) -> None:
deploy_iroh_relay(config)

# Deploy acmetool to have TLS certificates.
tls_domains = [mail_domain, f"mta-sts.{mail_domain}", f"www.{mail_domain}"]
deploy_acmetool(
domains=tls_domains,
)
if not config.use_foreign_cert_manager:
tls_domains = [mail_domain, f"mta-sts.{mail_domain}", f"www.{mail_domain}"]
deploy_acmetool(
domains=tls_domains,
)

apt.packages(
# required for setfacl for echobot
Expand Down
136 changes: 136 additions & 0 deletions docker/docker-compose-traefik.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
services:
chatmail:
build:
context: ./docker
dockerfile: chatmail_relay.dockerfile
tags:
- chatmail-relay:latest
image: chatmail-relay:latest
restart: unless-stopped
container_name: chatmail
depends_on:
- traefik-certs-dumper
cgroup: host # required for systemd
tty: true # required for logs
tmpfs: # required for systemd
- /tmp
- /run
- /run/lock
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
environment: #all possible variables you can check inside README and /chatmaild/src/chatmaild/ini/chatmail.ini.f
MAIL_DOMAIN: $MAIL_DOMAIN
# MAX_MESSAGE_SIZE: "50M"
# DEBUG_COMMANDS_ENABLED: "true"
# FORCE_REINIT_INI_FILE: "true"
# RECREATE_VENV: "false"
USE_FOREIGN_CERT_MANAGER: "true"
CHANGE_KERNEL_SETTINGS: "false"
PATH_TO_SSL: "${CERTS_ROOT_DIR_CONTAINER}/${MAIL_DOMAIN}"
ENABLE_CERTS_MONITORING: "true"
# CERTS_MONITORING_TIMEOUT: 60
# IS_DEVELOPMENT_INSTANCE: "true"
ports:
- "25:25"
- "587:587"
- "143:143"
- "465:465"
- "993:993"
volumes:
## system
- /sys/fs/cgroup:/sys/fs/cgroup:rw # required for systemd
- ./:/opt/chatmail
- ${CERTS_ROOT_DIR_HOST}:${CERTS_ROOT_DIR_CONTAINER}:ro

## data
- ./data/chatmail:/home
# - ./data/chatmail-dkimkeys:/etc/dkimkeys
# - ./data/chatmail-echobot:/run/echobot
# - ./data/chatmail-acme:/var/lib/acme

## custom resources
# - ./custom/www/src/index.md:/opt/chatmail/www/src/index.md

## debug
# - ./docker/files/setup_chatmail_docker.sh:/setup_chatmail_docker.sh
# - ./docker/files/entrypoint.sh:/entrypoint.sh
# - ./docker/files/update_ini.sh:/update_ini.sh

labels:
- traefik.enable=true
- traefik.http.services.chatmail-relay.loadbalancer.server.scheme=https
- traefik.http.services.chatmail-relay.loadbalancer.server.port=443
- traefik.http.services.chatmail-relay.loadbalancer.serverstransport=insecure@file
- traefik.http.routers.chatmail-relay.rule=Host(`${MAIL_DOMAIN}`) || Host(`mta-sts.${MAIL_DOMAIN}`) || Host(`www.${MAIL_DOMAIN}`)
- traefik.http.routers.chatmail-relay.service=chatmail-relay
- traefik.http.routers.chatmail-relay.tls=true
- traefik.http.routers.chatmail-relay.tls.certresolver=letsEncrypt

traefik_init:
image: alpine:latest
restart: on-failure
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
working_dir: /app
entrypoint: sh -c '
touch acme.json &&
chown 0:0 ./acme.json &&
chmod 600 ./acme.json'
volumes:
- ./traefik/data:/app

traefik:
image: traefik:v3.3
container_name: traefik
restart: unless-stopped
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
command:
- "--configFile=/config.yaml"
- "--certificatesresolvers.letsEncrypt.acme.email=${ACME_EMAIL}"
# ports:
# - "80:80"
# - "443:443"
network_mode: host
depends_on:
traefik_init:
condition: service_completed_successfully
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./traefik/config.yaml:/config.yaml
- ./traefik/data/acme.json:/acme.json
- ./traefik/dynamic-configs:/dynamic/conf

traefik-certs-dumper:
image: ldez/traefik-certs-dumper:v2.10.0
restart: unless-stopped
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
depends_on:
- traefik
entrypoint: sh -c '
apk add openssl &&
while ! [ -e /data/acme.json ]
|| ! [ `jq ".[] | .Certificates | length" /data/acme.json | jq -s "add" ` != 0 ]; do
sleep 1
; done
&& traefik-certs-dumper file --version v3 --watch --domain-subdir=true
--source /data/acme.json --dest /data/letsencrypt/certs --post-hook "sh /post-hook.sh"'
environment:
CERTS_DIR: /data/letsencrypt/certs
volumes:
- ./traefik/data/letsencrypt:/data/letsencrypt
- ./traefik/data/acme.json:/data/acme.json
- ./traefik/post-hook.sh:/post-hook.sh
4 changes: 4 additions & 0 deletions docker/example.env
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
MAIL_DOMAIN="chat.example.com"
ACME_EMAIL="my.email@gmail.com"

CERTS_ROOT_DIR_HOST="./traefik/data/letsencrypt/certs"
CERTS_ROOT_DIR_CONTAINER="/var/lib/acme/live"
13 changes: 13 additions & 0 deletions docker/files/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,19 @@ set -eo pipefail

unlink /etc/nginx/sites-enabled/default || true

if [ "${USE_FOREIGN_CERT_MANAGER,,}" == "true" ]; then
if [ ! -f "$PATH_TO_SSL/fullchain" ]; then
echo "Error: file '$PATH_TO_SSL/fullchain' does not exist. Exiting..." > /dev/stderr
sleep 2
exit 1
fi
if [ ! -f "$PATH_TO_SSL/privkey" ]; then
echo "Error: file '$PATH_TO_SSL/privkey' does not exist. Exiting..." > /dev/stderr
sleep 2
exit 1
fi
fi

SETUP_CHATMAIL_SERVICE_PATH="${SETUP_CHATMAIL_SERVICE_PATH:-/lib/systemd/system/setup_chatmail.service}"

env_vars=$(printenv | cut -d= -f1 | xargs)
Expand Down
16 changes: 15 additions & 1 deletion docs/DOCKER_INSTALLATION_EN.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,25 @@ Please substitute it with your own domain.
```

## Installation
When installing via Docker, there are several options:

1. Copy the file `./docker/docker-compose-default.yaml` to `docker-compose.yaml`. This is necessary because `docker-compose.yaml` is in `.gitignore` and won’t cause conflicts when updating the git repository.
- Use the built-in nginx and acmetool in Chatmail container to host the chat and manage certificates.
- Use third-party tools for certificate management.

For the third-party certificate manager example, traefik will be used, but you can use whatever is more convenient for you.

1. Copy the file `./docker/docker-compose-default.yaml` or `./docker/docker-compose-traefik.yaml` and rename it to `docker-compose.yaml`. This is necessary because `docker-compose.yaml` is in `.gitignore` and won’t cause conflicts when updating the git repository.

```shell
cp ./docker/docker-compose-default.yaml docker-compose.yaml
## or
# cp ./docker/docker-compose-traefik.yaml docker-compose.yaml
```

2. Copy `./docker/example.env` and rename it to `.env`. This file stores variables used in `docker-compose.yaml`.

```shell
cp ./docker/example.env .env
```

3. Configure environment variables in the `.env` file. These variables are used in the `docker-compose.yaml` file to pass repeated values.
Expand Down
14 changes: 13 additions & 1 deletion docs/DOCKER_INSTALLATION_RU.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,22 @@ Please substitute it with your own domain.
```

## Installation
При установке через docker есть несколько вариантов:
- использовать встроенный в chatmail контейнер nginx и acmetool для хостинга чата и управления сертификатами.
- использовать сторонние инструменты для менеджмента сертификатов

1. Скопировать файл `./docker/docker-compose-default.yaml` в `docker-compose.yaml`. Это нужно потому что `docker-compose.yaml` находится в `.gitignore` и не будет создавать конфликты при обновлении гит репозитория.
В качестве примера для стороннего менеджера сертификатов будет использоваться traefik, но вы можете использовать то что удобнее вам.

1. Скопировать файл `./docker/docker-compose-default.yaml` или `./docker/docker-compose-traefik.yaml` и переименовать в `docker-compose.yaml`. Это нужно потому что `docker-compose.yaml` находится в `.gitignore` и не будет создавать конфликты при обновлении гит репозитория.
```shell
cp ./docker/docker-compose-default.yaml docker-compose.yaml
## or
# cp ./docker/docker-compose-traefik.yaml docker-compose.yaml
```

2. Скопировать `./docker/example.env` и переименовать в `.env`. Здесь хранятся переменные, которые используятся в `docker-compose.yaml`.
```shell
cp ./docker/example.env .env
```

3. Настроить переменные окружения в `.env` файле. Эти переменные используются в `docker-compose.yaml` файле, чтобы передавать повторяющиеся значения.
Expand Down
33 changes: 33 additions & 0 deletions traefik/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
log:
level: TRACE

entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
permanent: true
websecure:
address: ":443"

providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
file:
directory: /dynamic/conf
watch: true

serverstransport:
insecureskipverify: true

certificatesResolvers:
letsEncrypt:
acme:
storage: /acme.json
caServer: "https://acme-v02.api.letsencrypt.org/directory"
tlschallenge: true
httpChallenge:
entryPoint: web
4 changes: 4 additions & 0 deletions traefik/dynamic-configs/insecure.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
http:
serversTransports:
insecure:
insecureSkipVerify: true
15 changes: 15 additions & 0 deletions traefik/post-hook.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
CERTS_DIR=${CERTS_DIR:-"/data/letsencrypt/certs"}

echo "CERTS_DIR: $CERTS_DIR"

for dir in "$CERTS_DIR"/*/; do
echo "Processing: $dir"
cd "$dir"
if [ -f "certificate.crt" ]; then
ln -sf certificate.crt fullchain
fi
if [ -f "privatekey.key" ]; then
ln -sf privatekey.key privkey
fi
cd -
done