init radicale role
This commit is contained in:
commit
8e985a84c0
33
README.md
Normal file
33
README.md
Normal file
@ -0,0 +1,33 @@
|
||||
# Anarcho-Tech NYC: Radicale [![Build Status](https://travis-ci.org/AnarchoTechNYC/ansible-role-radicale.svg?branch=master)](https://travis-ci.org/AnarchoTechNYC/ansible-role-radicale)
|
||||
|
||||
An [Ansible role](https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html) for installing a [Radicale](http://radicale.org/) server. Notably, this role has been tested with [Raspbian](https://www.raspbian.org/) on [Raspberry Pi](https://www.raspberrypi.org/) hardware. This role's purpose is to make it simple to install a CalDAV and CardDAV server.
|
||||
|
||||
# Configuring Radicale
|
||||
|
||||
To configure your Radicale server instance, use the `radicale_config` dictionary. The keys in this dictionary map nearly one-to-one to the configuration directives described in [Radicale's Configuration documentation page](https://radicale.org/configuration/). Configuration directive groups are their own dictionaries, and directives that can accept more than one value are specified as a list.
|
||||
|
||||
Some examples may prove helpful:
|
||||
|
||||
1. Simple Radicale server with default for all values:
|
||||
```yaml
|
||||
radicale_config:
|
||||
```
|
||||
1. Simple Radicale server bound to the local host only and listening on the alternative HTTP port:
|
||||
```yaml
|
||||
radicale_config:
|
||||
server:
|
||||
hosts:
|
||||
- addr: 127.0.0.1
|
||||
port: 8080
|
||||
```
|
||||
|
||||
See the comments in the [`defaults/main.yaml`](defaults/main.yaml) file for additional details.
|
||||
|
||||
# Adding or removing Radicale user accounts
|
||||
|
||||
The `radicale_users` variable is a list containing dictionaries for each user account. Each user account dictionary in the list can have the following keys:
|
||||
|
||||
* `name`: The name of the user account. This key is required.
|
||||
* `password`: The password for this user account. It is recommended to encrypt this value with Ansible Vault. If this is omitted, the `bcrypt_hash` key is required.
|
||||
* `bcrypt_hash`: Instead of supplying a password, you can supply a bcrypt hash of the password in `passlib` format. If this is omitted, the `password` key is required.
|
||||
* `state`: Whether the user should exist (`present`) or not (`absent`). This key is optional.
|
65
defaults/main.yaml
Normal file
65
defaults/main.yaml
Normal file
@ -0,0 +1,65 @@
|
||||
---
|
||||
radicale_server_username: radicale
|
||||
radicale_server_home_dir: "/var/lib/{{ radicale_server_username }}"
|
||||
radicale_service_state: started
|
||||
|
||||
# See https://radicale.org/configuration/
|
||||
radicale_config:
|
||||
server:
|
||||
hosts:
|
||||
- addr: 0.0.0.0
|
||||
port: 5232
|
||||
#daemon: true
|
||||
#pid: /var/run/radicale/radicale.pid
|
||||
#max_connections: 20
|
||||
#max_connections: 100000000
|
||||
#timeout: 30
|
||||
dns_lookup: false
|
||||
#realm: Radicale Realm
|
||||
# Consider TLS directives carefully before activating them.
|
||||
#ssl: true
|
||||
#certificate: "/etc/ssl/radicale.cert.pem"
|
||||
#key: "/etc/ssl/radicale.key.pem"
|
||||
#certificate_authority:
|
||||
#protocol: PROTOCOL_TLSv1_2
|
||||
#ciphers:
|
||||
#encoding:
|
||||
#request: utf-8
|
||||
#stock: utf-8
|
||||
auth:
|
||||
type: htpasswd
|
||||
htpasswd_filename: "{{ radicale_server_home_dir }}/users.htpasswd"
|
||||
htpasswd_encryption: bcrypt
|
||||
delay: 1
|
||||
rights:
|
||||
type: from_file
|
||||
file: "{{ radicale_server_home_dir }}/rights.conf"
|
||||
storage:
|
||||
type: multifilesystem
|
||||
filesystem_folder: "{{ radicale_server_home_dir }}/collections"
|
||||
filesystem_locking: true
|
||||
filesystem_fsync: true
|
||||
# For an example of the `hook` directive in use, see
|
||||
# http://radicale.org/versioning/
|
||||
#hook:
|
||||
#web:
|
||||
#type: internal
|
||||
#headers:
|
||||
#X-Extra-HTTP-Header: foo
|
||||
#X-Another-Header: bar
|
||||
#logging:
|
||||
#debug: false
|
||||
#mask_passwords: true
|
||||
#full_environment: false
|
||||
#config: "/etc/radicale/log.conf"
|
||||
|
||||
# List of Radicale user information as a dictionary.
|
||||
radicale_users:
|
||||
- name: admin # The username.
|
||||
password: admin # Their password. This should probably be vault-encrypted.
|
||||
# As an alternative to a password, you can specify a bcrypt hash.
|
||||
# Create this hash using the standard `htpasswd` utility, then
|
||||
# paste it here. This method allows a user to generate a password
|
||||
# for their account themselves, and then send you the hash rather
|
||||
# than the plaintext.
|
||||
#bcrypt_hash: "$2y$05$t31SnKFWj9UcMr5Y96cl3uBFkdhelqkZn77TnquIeVb9sriEByUPK"
|
29
files/rights.conf
Normal file
29
files/rights.conf
Normal file
@ -0,0 +1,29 @@
|
||||
################################################
|
||||
# Radicale user rights configuration file. #
|
||||
# #
|
||||
# See http://radicale.org/rights/ for details. #
|
||||
################################################
|
||||
|
||||
## The user "admin" can read and write any collection.
|
||||
#[admin]
|
||||
#user = admin
|
||||
#collection = .*
|
||||
#permission = rw
|
||||
|
||||
# Authenticated users can list (discover) their own collections.
|
||||
[owner-discover]
|
||||
user = .+
|
||||
collection = ^%(login)s$
|
||||
permission = rw
|
||||
|
||||
# Authenticated users can read and write their own collections.
|
||||
[owner-write]
|
||||
user = .+
|
||||
collection = ^%(login)s/.*
|
||||
permission = rw
|
||||
|
||||
# Everyone can read the root collection
|
||||
[read]
|
||||
user = .*
|
||||
collection =
|
||||
permission = r
|
9
handlers/main.yaml
Normal file
9
handlers/main.yaml
Normal file
@ -0,0 +1,9 @@
|
||||
---
|
||||
- name: Reload systemd.
|
||||
systemd:
|
||||
daemon_reload: true
|
||||
|
||||
- name: Restart Radicale.
|
||||
service:
|
||||
name: radicale
|
||||
state: restarted
|
22
meta/main.yaml
Normal file
22
meta/main.yaml
Normal file
@ -0,0 +1,22 @@
|
||||
---
|
||||
dependencies: []
|
||||
galaxy_info:
|
||||
role_name: radicale
|
||||
author: AnarchoTechNYC
|
||||
description: Provision a Radicale CalDAV/CardDAV server in a number of small- to medium-sized deployments.
|
||||
company: Eat The Rich, Inc.
|
||||
license: AGPL-3.0-or-later # SPDX tag from https://spdx.org/licenses/
|
||||
min_ansible_version: 2.7
|
||||
platforms:
|
||||
- name: Raspbian
|
||||
versions:
|
||||
- all
|
||||
- name: Debian
|
||||
versions:
|
||||
- 9
|
||||
galaxy_tags:
|
||||
- CalDAV
|
||||
- CardDAV
|
||||
- calendar
|
||||
- contacts
|
||||
- addressbook
|
114
tasks/main.yaml
Normal file
114
tasks/main.yaml
Normal file
@ -0,0 +1,114 @@
|
||||
---
|
||||
- name: Install Radicale package dependencies.
|
||||
apt:
|
||||
name: "{{ packages }}"
|
||||
vars:
|
||||
packages:
|
||||
- python3
|
||||
- python3-pip
|
||||
- python3-setuptools
|
||||
- apache2-utils
|
||||
# These three are for Ansible itself to run on the managed host.
|
||||
- python-setuptools
|
||||
- python-passlib
|
||||
- python-bcrypt
|
||||
|
||||
- name: Install Radicale Python dependencies.
|
||||
pip:
|
||||
executable: pip3 # Radicale requires Python 3.3 or greater.
|
||||
name: "{{ item }}"
|
||||
state: present
|
||||
loop:
|
||||
- passlib
|
||||
- bcrypt
|
||||
|
||||
- name: Create Radicale system user.
|
||||
user:
|
||||
name: "{{ radicale_server_username }}"
|
||||
system: true
|
||||
home: "{{ radicale_server_home_dir }}"
|
||||
shell: "/bin/false"
|
||||
state: present
|
||||
|
||||
- name: Install Radicale.
|
||||
pip:
|
||||
executable: pip3 # Radicale requires Python 3.3 or greater.
|
||||
name: radicale
|
||||
state: present
|
||||
|
||||
- name: Create Radicale configuration directory.
|
||||
file:
|
||||
path: /etc/radicale
|
||||
state: directory
|
||||
|
||||
- name: Write Radicale configuration file.
|
||||
template:
|
||||
src: etc/radicale/config.j2
|
||||
dest: /etc/radicale/config
|
||||
notify:
|
||||
- Restart Radicale.
|
||||
|
||||
- name: Write Radicale user rights configuration.
|
||||
copy:
|
||||
src: rights.conf
|
||||
dest: "{{ radicale_server_home_dir }}/rights.conf"
|
||||
owner: "{{ radicale_server_username }}"
|
||||
group: "{{ radicale_server_username }}"
|
||||
mode: "400"
|
||||
notify:
|
||||
- Restart Radicale.
|
||||
|
||||
- name: Ensure Radicale user accounts are defined.
|
||||
when:
|
||||
- radicale_config.auth is defined
|
||||
- radicale_config.auth.type is defined
|
||||
- radicale_config.auth.type == "htpasswd"
|
||||
block:
|
||||
- name: Ensure Radicale htpasswd file exists.
|
||||
file:
|
||||
path: "{{ radicale_config.auth.htpasswd_filename | default('/var/lib/radicale/users.htpasswd') }}"
|
||||
state: touch
|
||||
access_time: preserve
|
||||
modification_time: preserve
|
||||
|
||||
- name: Set Radicale user with password.
|
||||
when: item.password is defined
|
||||
no_log: true
|
||||
htpasswd:
|
||||
path: "{{ radicale_config.auth.htpasswd_filename | default('/var/lib/radicale/users.htpasswd') }}"
|
||||
name: "{{ item.name }}"
|
||||
password: "{{ item.password }}"
|
||||
state: "{{ item.state | default('present') }}"
|
||||
crypt_scheme: "bcrypt"
|
||||
mode: "600"
|
||||
owner: "{{ radicale_server_username }}"
|
||||
group: "{{ radicale_server_username }}"
|
||||
loop: "{{ radicale_users }}"
|
||||
|
||||
- name: Set Radicale user with password hash.
|
||||
when: item.bcrypt_hash is defined
|
||||
no_log: true
|
||||
lineinfile:
|
||||
path: "{{ radicale_config.auth.htpasswd_filename | default('/var/lib/radicale/users.htpasswd') }}"
|
||||
line: "{{ item.name }}:{{ item.bcrypt_hash }}"
|
||||
state: "{{ item.state | default('present') }}"
|
||||
mode: "600"
|
||||
owner: "{{ radicale_server_username }}"
|
||||
group: "{{ radicale_server_username }}"
|
||||
loop: "{{ radicale_users }}"
|
||||
|
||||
- name: Create systemd service unit.
|
||||
template:
|
||||
src: radicale.service.j2
|
||||
dest: /etc/systemd/system/radicale.service
|
||||
# TODO:
|
||||
#validate: "systemd-analyze verify %s"
|
||||
notify:
|
||||
- Reload systemd.
|
||||
- Restart Radicale.
|
||||
|
||||
- name: Start and enable Radicale service.
|
||||
service:
|
||||
name: radicale
|
||||
state: "{{ radicale_service_state }}"
|
||||
enabled: true
|
141
templates/etc/radicale/config.j2
Normal file
141
templates/etc/radicale/config.j2
Normal file
@ -0,0 +1,141 @@
|
||||
# Radicale configuration file.
|
||||
#
|
||||
# See http://radicale.org/configuration/ for more details and
|
||||
# descriptions of additional available configuration directives.
|
||||
|
||||
{% if radicale_config.server is defined %}
|
||||
[server]
|
||||
{% if radicale_config.server.hosts is defined %}
|
||||
hosts = {% for host in radicale_config.server.hosts %}
|
||||
{{ host.addr | default('0.0.0.0') }}:{{ host.port | default('5232') }}{% if not loop.last %},{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% endif %}
|
||||
{% if radicale_config.server.daemon is defined %}
|
||||
daemon = {{ radicale_config.server.daemon }}
|
||||
{% endif %}
|
||||
{% if radicale_config.server.pid is defined %}
|
||||
pid = {{ radicale_config.server.pid }}
|
||||
{% endif %}
|
||||
{% if radicale_config.server.max_connections is defined %}
|
||||
max_connections = {{ radicale_config.server.max_connections | default(20) | int }}
|
||||
{% endif %}
|
||||
{% if radicale_config.server.max_content_length is defined %}
|
||||
max_content_length = {{ radicale_config.server.max_content_length | default(100000000) | int }}
|
||||
{% endif %}
|
||||
{% if radicale_config.server.timeout is defined %}
|
||||
timeout = {{ radicale_config.server.timeout | default(30) | int }}
|
||||
{% endif %}
|
||||
{% if radicale_config.server.dns_lookup is defined %}
|
||||
dns_lookup = {{ radicale_config.server.dns_lookup | default(true) }}
|
||||
{% endif %}
|
||||
{% if radicale_config.server.realm is defined %}
|
||||
realm = {{ radicale_config.server.realm | default('Radicale - Password Required') }}
|
||||
{% endif %}
|
||||
{% if radicale_config.server.ssl is defined %}
|
||||
ssl = {{ radicale_config.server.ssl | default('false') }}
|
||||
{% endif %}
|
||||
{% if radicale_config.server.certificate is defined %}
|
||||
certificate = {{ radicale_config.server.certificate | default('/etc/ssl/radicale.cert.pem') }}
|
||||
{% endif %}
|
||||
{% if radicale_config.server.key is defined %}
|
||||
key = {{ radicale_config.server.key | default('/etc/ssl/radicale.key.pem') }}
|
||||
{% endif %}
|
||||
{% if radicale_config.server.certificate_authority is defined %}
|
||||
certificate_authority = {{ radicale_config.server.certificate_authority }}
|
||||
{% endif %}
|
||||
{% if radicale_config.server.protocol is defined %}
|
||||
protocol = {{ radicale_config.server.protocol | default('PROTOCOL_TLSv1_2') }}
|
||||
{% endif %}
|
||||
{% if radicale_config.server.ciphers is defined %}
|
||||
ciphers = {{ radicale_config.server.ciphers }}
|
||||
{% endif %}
|
||||
{% endif %}{# END if radicale_config.server is defined #}
|
||||
{% if radicale_config.encoding is defined %}
|
||||
|
||||
[encoding]
|
||||
{% if radicale_config.encoding.request is defined %}
|
||||
request = {{ radicale_config.encoding.request | default('utf-8') }}
|
||||
{% endif %}
|
||||
{% if radicale_config.encoding.stock is defined %}
|
||||
stock = {{ radicale_config.encoding.stock | default('utf-8') }}
|
||||
{% endif %}
|
||||
{% endif %}{# END if radicale_config.encoding is defined #}
|
||||
{% if radicale_config.auth is defined %}
|
||||
|
||||
[auth]
|
||||
{% if radicale_config.auth.type is defined %}
|
||||
type = {{ radicale_config.auth.type }}
|
||||
{% endif %}
|
||||
{% if radicale_config.auth.htpasswd_filename is defined %}
|
||||
htpasswd_filename = {{ radicale_config.auth.htpasswd_filename }}
|
||||
{% endif %}
|
||||
{% if radicale_config.auth.htpasswd_encryption is defined %}
|
||||
htpasswd_encryption = {{ radicale_config.auth.htpasswd_encryption }}
|
||||
{% endif %}
|
||||
{% if radicale_config.auth.delay is defined %}
|
||||
delay = {{ radicale_config.auth.delay | default('1') }}
|
||||
{% endif %}
|
||||
{% endif %}{# END if radicale_config.auth is defined #}
|
||||
{% if radicale_config.rights is defined %}
|
||||
|
||||
[rights]
|
||||
{% if radicale_config.rights.type is defined %}
|
||||
type = {{ radicale_config.rights.type | default('owner_only') }}
|
||||
{% endif %}
|
||||
{% if radicale_config.rights is defined and radicale_config.rights.type == "from_file" %}
|
||||
file = {{ radicale_config.rights.file }}
|
||||
{% endif %}
|
||||
{% endif %}{# END if radicale_config.rights is defined #}
|
||||
{% if radicale_config.storage is defined %}
|
||||
|
||||
[storage]
|
||||
{% if radicale_config.storage.type is defined %}
|
||||
type = {{ radicale_config.storage.type | default('multifilesystem') }}
|
||||
{% endif %}
|
||||
{% if radicale_config.storage.filesystem_folder is defined %}
|
||||
filesystem_folder = {{ radicale_config.storage.filesystem_folder | default('/var/lib/radicale/collections') }}
|
||||
{% endif %}
|
||||
{% if radicale_config.storage.filesystem_locking is defined %}
|
||||
filesystem_locking = {{ radicale_config.storage.filesystem_locking | default(true) }}
|
||||
{% endif %}
|
||||
{% if radicale_config.storage.max_sync_token_age is defined %}
|
||||
max_sync_token_age = {{ radicale_config.storage.max_sync_token_age | default(2592000) | int }}
|
||||
{% endif %}
|
||||
{% if radicale_config.storage.filesystem_fsync is defined %}
|
||||
filesystem_fsync = {{ radicale_config.storage.filesystem_fsync | default(true) }}
|
||||
{% endif %}
|
||||
{% if radicale_config.storage.hook is defined %}
|
||||
hook = {{ radicale_config.storage.hook }}
|
||||
{% endif %}
|
||||
{% endif %}{# END if radicale_config.storage is defined #}
|
||||
{% if radicale_config.web is defined %}
|
||||
|
||||
[web]
|
||||
{% if radicale_config.web.type is defined %}
|
||||
type = {{ radicale_config.web.type | default('internal') }}
|
||||
{% endif %}
|
||||
{% endif %}{# END if radicale_config.web is defined #}
|
||||
{% if radicale_config.headers is defined %}
|
||||
|
||||
[headers]
|
||||
{% for k in radicale_config.headers %}
|
||||
{{ k }} = {{ radicale_config.headers[k] }}
|
||||
{% endfor %}
|
||||
{% endif %}{# END if radicale_config.headers is defined #}
|
||||
{% if radicale_config.logging is defined %}
|
||||
|
||||
[logging]
|
||||
{% if radicale_config.logging.debug is defined %}
|
||||
debug = {{ radicale_config.logging.debug | default(false) }}
|
||||
{% endif %}
|
||||
{% if radicale_config.logging.mask_passwords is defined %}
|
||||
mask_passwords = {{ radicale_config.logging.mask_passwords | default(true) }}
|
||||
{% endif %}
|
||||
{% if radicale_config.logging.full_environment %}
|
||||
full_environment = {{ radicale_config.logging.full_environment | default(false) }}
|
||||
{% endif %}
|
||||
{% if radicale_config.logging.config %}
|
||||
config = {{ radicale_config.logging.config }}
|
||||
{% endif %}
|
||||
{% endif %}{# END if radicale_config.logging is defined #}
|
6
tests/test.yaml
Normal file
6
tests/test.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
# This Ansible playbook runs test plays to ensure the role works.
|
||||
---
|
||||
- name: Test role.
|
||||
hosts: localhost
|
||||
roles:
|
||||
- ansible-role-radicale
|
Loading…
Reference in New Issue
Block a user