From a2683187f966fd79256e4efd30fd116f7b80a7a5 Mon Sep 17 00:00:00 2001 From: moeny-matt Date: Wed, 5 Feb 2025 15:34:33 -0500 Subject: [PATCH] Docker and config --- .gitignore | 4 ++ README.md | 28 +++++++++ config/config | 138 ++++++++++++++++++++++++++++++++++++++++++++ config/users.sample | 2 + docker-compose.yaml | 70 ++++++++++++++++++++++ 5 files changed, 242 insertions(+) create mode 100644 .gitignore create mode 100644 config/config create mode 100644 config/users.sample create mode 100644 docker-compose.yaml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bb31a5a --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.DS_Store +.env +generate-bcrypt-hash.py +config/users \ No newline at end of file diff --git a/README.md b/README.md index 8c86f5c..08b756e 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,30 @@ # radicale +[Radicale](https://radicale.org/v3.html) is a small but powerful CalDAV (calendars, to-do lists) and CardDAV (contacts) server. The Docker configuration is based off of the work in [docker-radicale](https://github.com/tomsquest/docker-radicale). + +## Quick start +1. All of the necessary files to start a Radicale server are in this repo. Begin by editing the following lines in the docker-compose.yaml file to reflect your hostname and the email address you want to use for Let's Encrypt. + +``` yaml +services: + radicale: + labels: + - "traefik.http.routers.radicale.rule=Host(`radicale.moeny.ai`)" +``` + +and + +``` yaml +services: + traefik: + command: + - --certificatesresolvers.le.acme.email=radicale@moeny.ai +``` + +Make sure you also have a domain DNS A record pointed to your server's public IP for the hostname you gave to traefik. To set up your own DNS server, see our [bind9 repo](https://gitea.moeny.ai/moeny/bind9). + +2. Rename `config/users.sample` to `users` and edit the usernames and passwords it contains. Ensure that each line contains a username and bcrypt-hashed password, separated by a colon (:). + +3. Once ready, run `docker-compose up -d` to start the server. + +4. You should then be able to access Radicale at the URL you set up with traefik earlier. The usernames and passwords to log in are the ones added to the `users` file. Use the non-hashed password. \ No newline at end of file diff --git a/config/config b/config/config new file mode 100644 index 0000000..88c3793 --- /dev/null +++ b/config/config @@ -0,0 +1,138 @@ +# -*- mode: conf -*- +# vim:ft=cfg + +# Config file for Radicale - A simple calendar server +# +# Place it into /etc/radicale/config (global) +# or ~/.config/radicale/config (user) +# +# The current values are the default ones + + +[server] + +# CalDAV server hostnames separated by a comma +# IPv4 syntax: address:port +# IPv6 syntax: [address]:port +# Hostname syntax (using "getaddrinfo" to resolve to IPv4/IPv6 adress(es)): hostname:port +# For example: 0.0.0.0:9999, [::]:9999, localhost:9999 +#hosts = localhost:5232 +hosts = 0.0.0.0:5232 + +# Max parallel connections +#max_connections = 8 + +# Max size of request body (bytes) +#max_content_length = 100000000 + +# Socket timeout (seconds) +#timeout = 30 + +# SSL flag, enable HTTPS protocol +#ssl = False + +# SSL certificate path +#certificate = /etc/ssl/radicale.cert.pem + +# SSL private key +#key = /etc/ssl/radicale.key.pem + +# CA certificate for validating clients. This can be used to secure +# TCP traffic between Radicale and a reverse proxy +#certificate_authority = + + +[encoding] + +# Encoding for responding requests +#request = utf-8 + +# Encoding for storing local collections +#stock = utf-8 + + +[auth] + +# Authentication method +# Value: none | htpasswd | remote_user | http_x_remote_user +type = htpasswd + +# Htpasswd filename +htpasswd_filename = /config/users + +# Htpasswd encryption method +# Value: plain | bcrypt | md5 | sha256 | sha512 | autodetect +# bcrypt requires the installation of 'bcrypt' module. +htpasswd_encryption = bcrypt + +# Incorrect authentication delay (seconds) +#delay = 1 + +# Message displayed in the client when a password is needed +#realm = Radicale - Password Required + +# Сonvert username to lowercase, must be true for case-insensitive auth providers +#lc_username = False + + +[rights] + +# Rights backend +# Value: none | authenticated | owner_only | owner_write | from_file +#type = owner_only + +# File for rights management from_file +#file = /etc/radicale/rights + +# Permit delete of a collection (global) +#permit_delete_collection = True + + +[storage] + +# Storage backend +# Value: multifilesystem | multifilesystem_nolock +#type = multifilesystem + +# Folder for storing local collections, created if not present +#filesystem_folder = /var/lib/radicale/collections +filesystem_folder = /data/collections + +# Delete sync token that are older (seconds) +#max_sync_token_age = 2592000 + +# Command that is run after changes to storage +# Example: ([ -d .git ] || git init) && git add -A && (git diff --cached --quiet || git commit -m "Changes by \"%(user)s\"") +#hook = + + +[web] + +# Web interface backend +# Value: none | internal +#type = internal + + +[logging] + +# Threshold for the logger +# Value: debug | info | warning | error | critical +#level = info + +# Don't include passwords in logs +#mask_passwords = True + + +[headers] + +# Additional HTTP headers +#Access-Control-Allow-Origin = * + +[hook] + +# Hook types +# Value: none | rabbitmq +#type = none +#rabbitmq_endpoint = +#rabbitmq_topic = +#rabbitmq_queue_type = classic \ No newline at end of file diff --git a/config/users.sample b/config/users.sample new file mode 100644 index 0000000..886f1dd --- /dev/null +++ b/config/users.sample @@ -0,0 +1,2 @@ +john:$2b$12$l1Se4qIaRlfOnaC1pGt32uNe/Dr61r4JrZQCNnY.kTx2KgJ70GPSm +sarah:$2b$12$lKEHYHjrZ.QHpWQeB/feWe/0m4ZtckLI.cYkVOITW8/0xoLCp1/Wy \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..eaaadb2 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,70 @@ +# Can be enhanced with an additional compose file +# See also https://docs.docker.com/compose/production/#modify-your-compose-file-for-production + +services: + radicale: + image: tomsquest/docker-radicale + container_name: radicale + init: true + read_only: true + security_opt: + - no-new-privileges:true + cap_drop: + - ALL + cap_add: + - SETUID + - SETGID + - CHOWN + - KILL + deploy: + resources: + limits: + memory: 256M + pids: 50 + healthcheck: + test: curl -f http://127.0.0.1:5232 || exit 1 + interval: 30s + retries: 3 + restart: always + volumes: + - ./data:/data + - ./config:/config:ro + networks: + - proxy + labels: + - "traefik.enable=true" + - "traefik.http.routers.radicale.rule=Host(`radicale.moeny.ai`)" + - "traefik.http.routers.radicale.entrypoints=https" + - "traefik.http.routers.radicale.tls.certresolver=le" + - "traefik.http.services.radicale.loadbalancer.server.port=5232" + + traefik: + image: traefik:v2.10 + container_name: traefik + restart: always + command: + - --api.insecure=false + - --providers.docker=true + - --providers.docker.exposedbydefault=false + - --entrypoints.web.address=:80 + - --entrypoints.web.http.redirections.entryPoint.to=https + - --entrypoints.web.http.redirections.entryPoint.scheme=https + - --entrypoints.https.address=:443 + - --certificatesresolvers.le.acme.tlschallenge=true + - --certificatesresolvers.le.acme.email=radicale@moeny.ai + - --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json + ports: + - "80:80" + - "443:443" + volumes: + - traefik:/letsencrypt:rw + - /var/run/docker.sock:/var/run/docker.sock:ro + networks: + - proxy + +networks: + proxy: + driver: bridge + +volumes: + traefik: { driver: local } \ No newline at end of file