feat: add radicale
This commit is contained in:
parent
9c7903ba01
commit
8886e06a3d
8 changed files with 575 additions and 2 deletions
12
deploy.yaml
12
deploy.yaml
|
|
@ -2,5 +2,13 @@
|
|||
- hosts: jellyfin
|
||||
roles:
|
||||
- acme.sh
|
||||
- nginx
|
||||
- jellyfin
|
||||
- role: nginx
|
||||
nginx_dependent_service: jellyfin
|
||||
- role: jellyfin
|
||||
|
||||
- hosts: calendar
|
||||
roles:
|
||||
- acme.sh
|
||||
- radicale
|
||||
- role: nginx
|
||||
nginx_dependent_service: radicale
|
||||
|
|
|
|||
|
|
@ -3,3 +3,6 @@ proxmox_vms:
|
|||
jellyfin:
|
||||
ansible_host: 10.0.1.8
|
||||
ansible_user: root
|
||||
calendar:
|
||||
ansible_host: 10.0.1.20
|
||||
ansible_user: root
|
||||
|
|
|
|||
73
main.tofu
73
main.tofu
|
|
@ -51,6 +51,7 @@ resource "proxmox_virtual_environment_file" "cloud_config" {
|
|||
file_name = "user-data-cloud-config.yaml"
|
||||
}
|
||||
}
|
||||
|
||||
resource "proxmox_virtual_environment_vm" "jellyfin" {
|
||||
node_name = "homelab-one"
|
||||
name = "jellyfin"
|
||||
|
|
@ -124,4 +125,76 @@ resource "proxmox_virtual_environment_vm" "jellyfin" {
|
|||
}
|
||||
}
|
||||
|
||||
resource "proxmox_virtual_environment_vm" "calendar" {
|
||||
node_name = "homelab-one"
|
||||
name = "calendar"
|
||||
acpi = true
|
||||
bios = "ovmf"
|
||||
boot_order = ["scsi0"]
|
||||
machine = "q35"
|
||||
stop_on_destroy = true
|
||||
scsi_hardware = "virtio-scsi-single"
|
||||
|
||||
operating_system {
|
||||
type = "l26"
|
||||
}
|
||||
|
||||
agent {
|
||||
enabled = true
|
||||
trim = true
|
||||
}
|
||||
|
||||
efi_disk {
|
||||
datastore_id = "spinny-zfs"
|
||||
file_format = "raw"
|
||||
type = "4m"
|
||||
}
|
||||
|
||||
serial_device {}
|
||||
|
||||
vga {
|
||||
type = "virtio"
|
||||
}
|
||||
|
||||
tpm_state {
|
||||
datastore_id = "spinny-zfs"
|
||||
version = "v2.0"
|
||||
}
|
||||
|
||||
cpu {
|
||||
cores = 4
|
||||
sockets = 1
|
||||
type = "host"
|
||||
}
|
||||
|
||||
memory {
|
||||
dedicated = 1024
|
||||
}
|
||||
|
||||
initialization {
|
||||
datastore_id = "spinny-zfs"
|
||||
user_data_file_id = proxmox_virtual_environment_file.cloud_config.id
|
||||
}
|
||||
|
||||
# boot disk
|
||||
disk {
|
||||
cache = "none"
|
||||
datastore_id = "spinny-zfs"
|
||||
discard = "on"
|
||||
file_id = "local:iso/Fedora-Cloud-Base-UEFI-UKI-42-1.1.x86_64.img"
|
||||
interface = "scsi0"
|
||||
iothread = true
|
||||
replicate = false
|
||||
size = 32
|
||||
}
|
||||
|
||||
network_device {
|
||||
bridge = "vmbr2"
|
||||
vlan_id = 100
|
||||
enabled = true
|
||||
firewall = true
|
||||
mac_address = "BC:24:11:21:6E:61"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
363
roles/radicale/files/config
Normal file
363
roles/radicale/files/config
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
# -*- 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)
|
||||
# In case of using a reverse proxy in front of check also there related option
|
||||
#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 =
|
||||
|
||||
# SSL protocol, secure configuration: ALL -SSLv3 -TLSv1 -TLSv1.1
|
||||
#protocol = (default)
|
||||
|
||||
# SSL ciphersuite, secure configuration: DHE:ECDHE:-NULL:-SHA (see also "man openssl-ciphers")
|
||||
#ciphersuite = (default)
|
||||
|
||||
# script name to strip from URI if called by reverse proxy
|
||||
#script_name = (default taken from HTTP_X_SCRIPT_NAME or SCRIPT_NAME)
|
||||
|
||||
|
||||
[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 | dovecot | ldap | oauth2 | pam | denyall
|
||||
#type = denyall
|
||||
type = htpasswd
|
||||
htpasswd_filename = /config/users
|
||||
htpasswd_encryption = bcrypt
|
||||
|
||||
# Cache logins for until expiration time
|
||||
#cache_logins = false
|
||||
|
||||
# Expiration time for caching successful logins in seconds
|
||||
#cache_successful_logins_expiry = 15
|
||||
|
||||
## Expiration time of caching failed logins in seconds
|
||||
#cache_failed_logins_expiry = 90
|
||||
|
||||
# URI to the LDAP server
|
||||
#ldap_uri = ldap://localhost
|
||||
|
||||
# Base DN of the LDAP server to search for user accounts
|
||||
#ldap_base = ##BASE_DN##
|
||||
|
||||
# Reader DN of the LDAP server; (needs read access to users and - if defined - groups)
|
||||
#ldap_reader_dn = CN=ldapreader,CN=Users,##BASE_DN##
|
||||
|
||||
# Password of the reader DN (better: use 'ldap_secret_file'!)
|
||||
#ldap_secret = ldapreader-secret
|
||||
|
||||
# Path to the file containing the password of the reader DN
|
||||
#ldap_secret_file = /run/secrets/ldap_password
|
||||
|
||||
# Filter to search for the LDAP entry of the user to authenticate. It must contain '{0}' as placeholder for the login name.
|
||||
#ldap_filter = (&(objectClass=person)(uid={0}))
|
||||
|
||||
# Attribute holding the value to be used as username after authentication
|
||||
#ldap_user_attribute = cn
|
||||
|
||||
# Use ssl on the LDAP connection (DEPRECATED - use 'ldap_security'!)
|
||||
#ldap_use_ssl = False
|
||||
|
||||
# Encryption mode to be used. Default: none; one of: none, tls, starttls
|
||||
#ldap_security = none
|
||||
|
||||
# Certificate verification mode for tls & starttls. Default: REQUIRED; one of NONE, OPTIONAL, REQUIRED
|
||||
#ldap_ssl_verify_mode = REQUIRED
|
||||
|
||||
# Path to the CA file in PEM format to certify the server certificate
|
||||
#ldap_ssl_ca_file =
|
||||
|
||||
# Attribute in the user's LDAP entry to read the group memberships from; default: not set
|
||||
#ldap_groups_attribute = memberOf
|
||||
|
||||
# Attribute in the group entries to read the group's members from, e.g. member; default: not set
|
||||
#ldap_group_members_attribute = member
|
||||
|
||||
# Base DN to search for groups; only if it differs from 'ldap_base' and if 'ldap_group_members_attribute' is set
|
||||
#ldap_group_base = ##GROUP_BASE_DN##
|
||||
|
||||
# Search filter to search for groups having the user DN found as member; only if 'ldap_group_members_attribute' is set
|
||||
#ldap_group_filter = (objectclass=groupOfNames)
|
||||
|
||||
# Quirks for Authentik LDAP server: ignore modifyTimestamp and createTimestamp attributes
|
||||
#ldap_ignore_attribute_create_modify_timestamp = false
|
||||
|
||||
# Connection type for dovecot authentication (AF_UNIX|AF_INET|AF_INET6)
|
||||
# Note: credentials are transmitted in cleartext
|
||||
#dovecot_connection_type = AF_UNIX
|
||||
|
||||
# The path to the Dovecot client authentication socket (eg. /run/dovecot/auth-client on Fedora). Radicale must have read / write access to the socket.
|
||||
#dovecot_socket = /var/run/dovecot/auth-client
|
||||
|
||||
# Host of via network exposed dovecot socket
|
||||
#dovecot_host = localhost
|
||||
|
||||
# Port of via network exposed dovecot socket
|
||||
#dovecot_port = 12345
|
||||
|
||||
# Remote address source for authentication mechanisms (such as dovecot)
|
||||
# that are passed this information.
|
||||
#remote_ip_source = REMOTE_ADDR
|
||||
|
||||
# IMAP server hostname
|
||||
# Syntax: address | address:port | [address]:port | imap.server.tld
|
||||
#imap_host = localhost
|
||||
|
||||
# Secure the IMAP connection
|
||||
# Value: tls | starttls | none
|
||||
#imap_security = tls
|
||||
|
||||
# OAuth2 token endpoint URL
|
||||
#oauth2_token_endpoint = <URL>
|
||||
|
||||
# PAM service
|
||||
#pam_serivce = radicale
|
||||
|
||||
# PAM group user should be member of
|
||||
#pam_group_membership =
|
||||
|
||||
# Htpasswd filename
|
||||
#htpasswd_filename = /etc/radicale/users
|
||||
|
||||
# Htpasswd encryption method
|
||||
# Value: plain | bcrypt | md5 | sha256 | sha512 | argon2 | autodetect
|
||||
# bcrypt requires the installation of 'bcrypt' module.
|
||||
# argon2 requires the installation of 'argon2-cffi' module.
|
||||
#htpasswd_encryption = autodetect
|
||||
|
||||
# Enable caching of htpasswd file based on size and mtime_ns
|
||||
#htpasswd_cache = False
|
||||
|
||||
# Incorrect authentication delay (seconds)
|
||||
#delay = 1
|
||||
|
||||
# Message displayed in the client when a password is needed
|
||||
#realm = Radicale - Password Required
|
||||
|
||||
# Convert username to lowercase, must be true for case-insensitive auth providers
|
||||
#lc_username = False
|
||||
|
||||
# Strip domain name from username
|
||||
#strip_domain = False
|
||||
|
||||
|
||||
[rights]
|
||||
|
||||
# Rights backend
|
||||
# Value: 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
|
||||
|
||||
# Permit overwrite of a collection (global)
|
||||
#permit_overwrite_collection = True
|
||||
|
||||
# URL Decode the given username (when URL-encoded by the client - useful for iOS devices when using email address)
|
||||
# urldecode_username = False
|
||||
|
||||
[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
|
||||
|
||||
# Folder for storing cache of local collections, created if not present
|
||||
# Note: only used in case of use_cache_subfolder_* options are active
|
||||
# Note: can be used on multi-instance setup to cache files on local node (see below)
|
||||
#filesystem_cache_folder = (filesystem_folder)
|
||||
|
||||
# Use subfolder 'collection-cache' for 'item' cache file structure instead of inside collection folder
|
||||
# Note: can be used on multi-instance setup to cache 'item' on local node
|
||||
#use_cache_subfolder_for_item = False
|
||||
|
||||
# Use subfolder 'collection-cache' for 'history' cache file structure instead of inside collection folder
|
||||
# Note: use only on single-instance setup, will break consistency with client in multi-instance setup
|
||||
#use_cache_subfolder_for_history = False
|
||||
|
||||
# Use subfolder 'collection-cache' for 'sync-token' cache file structure instead of inside collection folder
|
||||
# Note: use only on single-instance setup, will break consistency with client in multi-instance setup
|
||||
#use_cache_subfolder_for_synctoken = False
|
||||
|
||||
# Use last modifiction time (nanoseconds) and size (bytes) for 'item' cache instead of SHA256 (improves speed)
|
||||
# Note: check used filesystem mtime precision before enabling
|
||||
# Note: conversion is done on access, bulk conversion can be done offline using storage verification option: radicale --verify-storage
|
||||
#use_mtime_and_size_for_item_cache = False
|
||||
|
||||
# Use configured umask for folder creation (not applicable for OS Windows)
|
||||
# Useful value: 0077 | 0027 | 0007 | 0022
|
||||
#folder_umask = (system default, usual 0022)
|
||||
|
||||
# Delete sync token that are older (seconds)
|
||||
#max_sync_token_age = 2592000
|
||||
|
||||
# Skip broken item instead of triggering an exception
|
||||
#skip_broken_item = True
|
||||
|
||||
# Command that is run after changes to storage, default is emtpy
|
||||
# Supported placeholders:
|
||||
# %(user)s: logged-in user
|
||||
# %(cwd)s : current working directory
|
||||
# %(path)s: full path of item
|
||||
# %(to_path)s: full path of destination item (only set on MOVE request)
|
||||
# %(request)s: request method
|
||||
# Command will be executed with base directory defined in filesystem_folder
|
||||
# For "git" check DOCUMENTATION.md for bootstrap instructions
|
||||
# Example(test): echo \"user=%(user)s path=%(path)s cwd=%(cwd)s\"
|
||||
# Example(test/json): echo \"hook-json {'user':'%(user)s', 'cwd':'%(cwd)s', 'path':'%(path)s', 'request':'%(request)s', 'to_path':'%(to_path)s'}\"
|
||||
# Example(git): git add -A && (git diff --cached --quiet || git commit -m "Changes by \"%(user)s\"")
|
||||
#hook =
|
||||
|
||||
# Create predefined user collections
|
||||
#
|
||||
# json format:
|
||||
#
|
||||
# {
|
||||
# "def-addressbook": {
|
||||
# "D:displayname": "Personal Address Book",
|
||||
# "tag": "VADDRESSBOOK"
|
||||
# },
|
||||
# "def-calendar": {
|
||||
# "C:supported-calendar-component-set": "VEVENT,VJOURNAL,VTODO",
|
||||
# "D:displayname": "Personal Calendar",
|
||||
# "tag": "VCALENDAR"
|
||||
# }
|
||||
# }
|
||||
#
|
||||
#predefined_collections =
|
||||
|
||||
|
||||
[web]
|
||||
|
||||
# Web interface backend
|
||||
# Value: none | internal
|
||||
#type = internal
|
||||
|
||||
|
||||
[logging]
|
||||
|
||||
# Threshold for the logger
|
||||
# Value: debug | info | warning | error | critical
|
||||
#level = info
|
||||
|
||||
# do not filter debug messages starting with 'TRACE'
|
||||
#trace_on_debug = False
|
||||
|
||||
# filter debug messages starting with 'TRACE/<TOKEN>'
|
||||
#trace_filter = ""
|
||||
|
||||
# Don't include passwords in logs
|
||||
#mask_passwords = True
|
||||
|
||||
# Log bad PUT request content
|
||||
#bad_put_request_content = False
|
||||
|
||||
# Log backtrace on level=debug
|
||||
#backtrace_on_debug = False
|
||||
|
||||
# Log request header on level=debug
|
||||
#request_header_on_debug = False
|
||||
|
||||
# Log request content on level=debug
|
||||
#request_content_on_debug = False
|
||||
|
||||
# Log response content on level=debug
|
||||
#response_content_on_debug = False
|
||||
|
||||
# Log rights rule which doesn't match on level=debug
|
||||
#rights_rule_doesnt_match_on_debug = False
|
||||
|
||||
# Log storage cache actions on level=debug
|
||||
#storage_cache_actions_on_debug = False
|
||||
|
||||
[headers]
|
||||
|
||||
# Additional HTTP headers
|
||||
#Access-Control-Allow-Origin = *
|
||||
|
||||
|
||||
[hook]
|
||||
|
||||
# Hook types
|
||||
# Value: none | rabbitmq | email
|
||||
#type = none
|
||||
|
||||
# dry-run (do not really trigger hook action)
|
||||
#dryrun = False
|
||||
|
||||
# hook: rabbitmq
|
||||
#rabbitmq_endpoint =
|
||||
#rabbitmq_topic =
|
||||
#rabbitmq_queue_type = classic
|
||||
|
||||
# hook: email
|
||||
#smtp_server = localhost
|
||||
#smtp_port = 25
|
||||
#smtp_security = starttls
|
||||
#smtp_ssl_verify_mode = REQUIRED
|
||||
#smtp_username =
|
||||
#smtp_password =
|
||||
#from_email =
|
||||
#mass_email = False
|
||||
#new_or_added_to_event_template =
|
||||
#deleted_or_removed_from_event_template =
|
||||
#updated_event_template =
|
||||
|
||||
|
||||
[reporting]
|
||||
|
||||
# When returning a free-busy report, limit the number of returned
|
||||
# occurences per event to prevent DoS attacks.
|
||||
#max_freebusy_occurrence = 10000
|
||||
38
roles/radicale/files/radicale.conf
Normal file
38
roles/radicale/files/radicale.conf
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
server
|
||||
{
|
||||
listen 8080;
|
||||
listen [::]:8080;
|
||||
server_name calendar.homelab0ne.xyz;
|
||||
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
|
||||
server
|
||||
{
|
||||
|
||||
# listen 8443 ssl proxy_protocol;
|
||||
listen 8443 ssl;
|
||||
# deny all;
|
||||
# listen [::]:8443 ssl;
|
||||
# listen [::]:8444 ssl proxy_protocol;
|
||||
http2 on;
|
||||
server_name calendar.homelab0ne.xyz;
|
||||
client_max_body_size 20M;
|
||||
ssl_certificate /etc/nginx/ssl/cert.pem;
|
||||
ssl_certificate_key /etc/nginx/ssl/key.pem;
|
||||
ssl_protocols TLSv1.3 TLSv1.2;
|
||||
|
||||
location /
|
||||
{
|
||||
proxy_pass http://radicale:5232;
|
||||
proxy_set_header Host $host;
|
||||
# proxy_set_header X-Real-IP $proxy_protocol_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Protocol $scheme;
|
||||
proxy_set_header X-Forwarded-Host $http_host;
|
||||
proxy_buffering off;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
24
roles/radicale/files/radicale.container
Normal file
24
roles/radicale/files/radicale.container
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
[Unit]
|
||||
Description=radicale.container
|
||||
|
||||
[Container]
|
||||
ContainerName=radicale
|
||||
RunInit=true
|
||||
DropCapability=ALL
|
||||
AddCapability=SETUID SETGID CHOWN KILL
|
||||
Image=docker.io/tomsquest/docker-radicale
|
||||
Network=frontend.network
|
||||
Volume=/srv/radicale/config:/config:Z,ro
|
||||
Volume=/srv/radicale/data:/data:Z
|
||||
#PodmanArgs=--runtime runsc --security-opt label:disable
|
||||
#Label=disable
|
||||
AutoUpdate=registry
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target default.target
|
||||
|
||||
[Service]
|
||||
TasksMax=50
|
||||
MemoryHigh=256M
|
||||
Restart=always
|
||||
|
||||
1
roles/radicale/files/users
Normal file
1
roles/radicale/files/users
Normal file
|
|
@ -0,0 +1 @@
|
|||
calendar:$2y$10$geRB3AZiWrODsNiTMXBZA.b//5nwVoIN/tTQ6NB.bYqnz4y97C6pW
|
||||
63
roles/radicale/tasks/main.yaml
Normal file
63
roles/radicale/tasks/main.yaml
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
- name: Create radicale dir
|
||||
ansible.builtin.file:
|
||||
path: /srv/radicale
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Create config dir if it doesn't exist
|
||||
ansible.builtin.file:
|
||||
path: /srv/radicale/config
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Create data dir if it doesn't exist
|
||||
ansible.builtin.file:
|
||||
path: /srv/radicale/data
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Copy over radicale.container file
|
||||
ansible.builtin.copy:
|
||||
src: ./files/radicale.container
|
||||
dest: /etc/containers/systemd/radicale.container
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
|
||||
- name: Copy over radicale config
|
||||
ansible.builtin.copy:
|
||||
src: ./files/config
|
||||
dest: /srv/radicale/config/config
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
|
||||
- name: Copy over radicale user config
|
||||
ansible.builtin.copy:
|
||||
src: ./files/users
|
||||
dest: /srv/radicale/config/users
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
|
||||
- name: Copy over radicale nginx config
|
||||
ansible.builtin.copy:
|
||||
src: ./files/radicale.conf
|
||||
dest: /srv/nginx/conf.d/radicale.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
|
||||
- name: Run systemctl daemon-reload
|
||||
ansible.builtin.systemd_service:
|
||||
daemon_reload: true
|
||||
|
||||
- name: Start radicale container
|
||||
ansible.builtin.systemd_service:
|
||||
name: radicale.service
|
||||
state: restarted
|
||||
|
||||
- name: Restart nginx
|
||||
ansible.builtin.systemd_service:
|
||||
name: nginx.service
|
||||
state: restarted
|
||||
Loading…
Add table
Add a link
Reference in a new issue