Instalando davical con ansible, por supuesto a lo bestia

Davical es, según su creador, un servidor para compartir calendarios, una implementación del protocolo caldav para almacenar recursos de calendario en un servidor compartido.
Se trata de una pieza de software muy bien hecha y muy madura, que nos permite compartir nuestros calendario y agendas con nosotros mismo (crear un evento en dispositivo android y que se replique en el Thunderbir de nuestro escritorio o compartir los contactos entre todos los dispositivos) o crear recursos compartidos con terceros, por ejemplo que un asistente pueda crear una cita en el calendario de su jefe, pudiendo leer los que ha creado pero sin poder acceder a los que ha creado su jefe, o solo poder acceder a un freebusy(ver las horas libres y reservadas pero no de que), el sistema de recursos de Davical permite la creación de gran cantidad de recursos con permisos muy bien controlados.
Todo esto hace que junto con la instalación de Dbmail que hicimos en este artículo, tengamos ya los mimbres sobre los que montaremos nuestra herramienta colaborativa. Al igual que con Dbmail, vamos a hacer la instalación a lo bestia, es decir en un playbook, ya más adelante, cuando hayamos tocado el tema de roles y vaults convertiremos el playbook en un rol que habrá que empezar a tratar bien con un control de versiones, con pruebas unitarias y con código documentado, poco a poco.
Definiendo nuestra instalación:
Ya hemos tratado el tema de las plantillas y las variables, esta vez, antes de comenzar con el playbook, vamos a hacer un listado con los pasos que tenemos que dar:
- Por sanidad, actualizar el estado del servidor.
- Instalar los paquetes necesarios, en nuestro caso, los paquetes de la base de datos, de davical, apache y php5, además de sus dependencias.
- Enviar las configuraciones para apache, davical y postgresql
- Crear, la base de datos, los usuarios e importar la estructura de tablas.
Vamos al lío, primero los paquetes:
Aquí ya empezamos a ver la reusabilidad de los fragmentos de código YAML de nuestros anteriores playbooks, por lo que es recomendable realizar este tipo de acciones en roles para poder utilizar ficheros tal cual con tareas que son comunes a distintos roles.
davical.yml(1)
---
- hosts: davical
user: ansrunner
sudo: yes
vars_files:
- vars/vars_davical.yml
tasks:
- name: Actualiza cache de apt
apt: update_cache=yes cache_valid_time=3600
- name: Actualiza paquetes
apt: upgrade=yes
- name: instalar paquetes
apt: name={{ item }} state=installed allow_unauthenticated=yes
with_items:
- "apache2"
- "postgresql"
- "php5"
- "php5-pgsql"
- "php5-imap"
- "php5-curl"
- "php5-cgi"
- "libapache2-mod-php5"
- "libyaml-perl"
- "libdbd-pg-perl"
- "libdbi-perl"
- "libawl-php"
- "davical"
- "python-psycopg2"
Como vemos, salvo que el listado de paquetes es distinto, la estructura es la misma que en el caso de la instalación de dbmail.
Creando la base de datos y los usuarios:
Davical utiliza dos usuarios distintos, un usuario dba para la administración de la base de datos y un usuario app para el acceso mediante la aplicación, Creamos los dos usuarios y le damos permisos al usuario dba, los scripts de davical ya establecen los permisos para el usuario app.
davical.yml(2)
- name: verifica que la bdd esta creada
become: true
become_user: postgres
postgresql_db: state=present db={{ davical_database }} login_user=postgres
- name: verifica que el usuario dba esta creado
become: true
become_user: postgres
postgresql_user: name={{ item.username }} password={{ item.password }} state=present priv=CONNECT db={{ davical_database }} login_user=postgres
with_items:
- {username: "{{ davical_dba_username }}", password: "{{ davical_dba_password }}" }
- {username: "{{ davical_app_username }}", password: "{{ davical_app_password }}" }
- name: verifica que el usuario tiene los privilegios correctos
become: true
become_user: postgres
postgresql_user: name={{ davical_dba_username }} role_attr_flags=LOGIN login_user=postgres
- name: verifica que el usuario dba puede modificar la bdd
become: true
become_user: postgres
postgresql_privs: privs=ALL type=schema objs=public role={{ davical_dba_username }} db={{ davical_database }} login_user=postgres
Aplicando la configuración con plantillas:
Al igual que en casos anteriores, utilizamos el motor de plantillas para desplegar los ficheros de configuración:
davical.yml(3)
- name: prepara configuraciones
template: src={{ item.source }} dest={{ item.dest }}
with_items:
- {source: 'templates/davical_config_php.j2', dest: '/etc/davical/config.php'}
- {source: 'templates/apache2_sites_available_davical.j2', dest: '/etc/apache2/sites-available/davical'}
- {source: 'templates/apache2_sites_available_davical_ssl.j2', dest: '/etc/apache2/sites-available/davical-ssl'}
En este caso, vamos a detenernos a echar un vistazo a la configuración de davical:
davical_config_php.j2
<?php
#
# {{ ansible_managed }}
# Please do not edit manually
# Last modified: {{ ansible_date_time.date }}
#
$c->pg_connect[] = "dbname={{ davical_database }} user={{ davical_app_username }} host=localhost password={{ davical_app_password }}";
$c->system_name = "CalDAV Praderas";
$c->get_includes_subcollections = true;
$c->readonly_webdav_collections = true;
$c->skip_bad_event_on_import = true;
$c->admin_email ='{{ davical_admin_email }}';
$c->restrict_setup_to_admin = true;
$c->restrict_admin_domain = 'admin.{{ davical_server_name }}';
$c->enable_row_linking = true;
$c->default_privileges = array('read-free-busy', 'schedule-query-freebusy','schedule-send-reply','schedule-deliver-reply');
$c->hide_older_than = 90;
$c->default_locale = "es_ES";
$c->enable_auto_schedule = true;
$c->authenticate_hook['call'] = 'IMAP_PAM_check';
$c->authenticate_hook['config'] = array('imap_url' => '{{ '{' }}{{ davical_server_name }}{{ ':' }}993/imap/ssl/{{ '}' }}INBOX','email_base' => '{{ davical_dominio_correo }}');
include('drivers_imap_pam.php');
?>
En la configuración, que se puede ver con más detalle en la página de Davical, vemos que utilizamos un hook de autenticación para permitir que los usuarios se autentiquen mediante IMAP, esto, unido a que la configuración por defecto permite crear automáticamente la agenda y la libreta de direcciones de los usuarios autenticados, nos permitirá no tener que administrar la creación de usuarios y buzones por defecto en davical, sino que el mismo usuario del correo servirá para acceder a Caldav.
Vemos que en la sintaxis tenemos que utilizar el formato {{ '{' }}
tanto para las llaves como para los dos puntos, de lo contrario el intérprete nos daría error, ya que ambos son caracteres especiales para Jinja2.
Las variables que hemos definido para este playbook (obviamente sin el contenido) y sus plantillas son:
vars_davical.j2
davical_dba_username: "davical_dba"
davical_database: "davical"
davical_dba_password: ""
davical_app_username: "davical_app"
davical_app_password: ""
admin_password: ""
davical_admin_email: ""
davical_server_name: ""
davical_dominio_correo: ""
admin_email: ""
tls_cert_file: ""
tls_key_file: ""
tls_ca_file: ""
Configurando Apache:
Una vez copiadas las plantillas, vinculamos los archivos de site-available a site-enabled para habilitarlos y habilitamos los módulos ssl y rewrite de apache:
davical.yml(4)
- name: añade el sitio a apache2
file:
src: '/etc/apache2/sites-available/davical'
dest: '/etc/apache2/sites-enabled/010-davical.conf'
owner: root
group: root
state: link
- name: añade el sitio ssl a apache2
file:
src: '/etc/apache2/sites-available/davical-ssl'
dest: '/etc/apache2/sites-enabled/010-davical-ssl.conf'
owner: root
group: root
state: link
- name: Habilita módulos de apache
apache2_module: state: present name: {{ item }}
with_items:
- "ssl"
- "rewrite"
notify: reinicia apache
El fichero de plantilla de sitio de apache usa el modulo rewrite para convertir las llamadas del estilo nombredelsitio/usuario/calendario a llamadas al caldav.php de esta manera las URLs serán limpias y más fáciles de poner en el cliente de correo y en los dispositivos móviles, además, hacemos uso de los facts de ansible para completar campos:
apache2_sites_available_davical_ssl.j2
#
# {{ ansible_managed }}
# Please do not edit manually
# Last modified: {{ ansible_date_time.date }}
#
<VirtualHost {{ ansible_eth0.ipv4.address }}:443 >
DocumentRoot /usr/share/davical/htdocs
DirectoryIndex index.php
ServerName {{ davical_server_name }}
SSLEngine on
SSLCertificateFile {{ tls_cert_file }}
SSLCertificateKeyFile {{ tls_key_file }}
<Directory /usr/share/davical/htdocs/>
AllowOverride None
Order allow,deny
Allow from all
</Directory>
RewriteEngine On
RewriteCond %{REQUEST_URI} !^/$
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ /caldav.php/$1 [NC,L]
</VirtualHost>
Vemos como en la última tarea, hay un comando notify, esto quiere decir que, en caso de que se modifique el equipo con esta tarea, se notifica al handler indicado, en este caso, nuestro handler reinicia apache. Los handlers se sitúan en el fichero del playbook una vez terminadas las tareas.
Preparando la base de datos:
Con los paquetes instalados, los ficheros de configuración listos y apache con los sitios creados, sólo nos queda definir la base de datos, igual que en el caso de dbmail, para asegurar la idempotencia comprobaremos la existencia de las tablas en la bdd antes de comenzar y utilizamos el fichero .pgpass para almacenar las contraseñas de la base de datos, como ya lo habíamos creado con anterioridad, primero comprobamos si existe, para no seobrescribirlo.
davical.yml(5)
- name: Verificamos si existe ~/.pgpass
stat: path=~/.pgpass
register: dotpgpass
- name: Revisamos .pgpass si existe
lineinfile:
dest: ~/.pgpass
state: present
regexp: '^localhost:5432:{{ davical_database }}:{{ davical_dba_username }}:{{ davical_dba_password }}'
line: 'localhost:5432:{{ davical_database }}:{{ davical_dba_username }}:{{ davical_dba_password }}'
when: dotpgpass.stat.exists is defined and dotpgpass.stat.exists == True
- name: creamos fichero .pgpass si no existe
template: src={{ item.source }} dest={{ item.dest }} mode={{ item.mode }}
with_items:
- {source: 'templates/davical_dotpgpass.j2', dest: '~/.pgpass', mode: '0600'}
when: dotpgpass.stat.exists is defined and dotpgpass.stat.exists == False
- name: comprueba que las tablas están creadas.
command: psql -U {{ davical_dba_username }} -d {{ davical_database }} -h localhost -w -q -c "SELECT EXISTS(SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'usr');"
register: respsql
- name: Ejecuta scripts en la base de datos
command: psql -U {{ davical_dba_username }} -d {{ davical_database }} -h localhost -w -q -f {{ item }}
with_items:
- '/usr/share/awl/dba/awl-tables.sql '
- '/usr/share/awl/dba/schema-management.sql'
- '/usr/share/davical/dba/davical.sql'
when: respsql.stdout_lines[2] is defined and respsql.stdout_lines[2] | search('f')
- name: Ejecuta script de configuración de permisos
command: /usr/share/davical/dba/update-davical-database --dbname {{ davical_database }} --dbuser davical_dba --dbhost localhost --dbpass {{ davical_dba_password}} --appuser davical_app --nopatch --owner davical_dba
when: respsql.stdout_lines[2] is defined and respsql.stdout_lines[2] | search('f')
- name: Realiza la carga inicial de datos
command: psql -U {{ davical_dba_username }} -d {{ davical_database }} -h localhost -w -q -f /usr/share/davical/dba/base-data.sql
when: respsql.stdout_lines[2] is defined and respsql.stdout_lines[2] | search('f')
- name: Modifica password de administrador
command: psql -U {{ davical_dba_username }} -d {{ davical_database }} -h localhost -w -q -c "UPDATE usr SET password = '**{{ admin_password }}' WHERE user_no = 1;"
when: respsql.stdout_lines[2] is defined and respsql.stdout_lines[2] | search('f')
handlers:
- name: reinicia apache
service: name=apache2 state=restarted
Con esto, ya tenemos el servidor de davical funcionando, como en el caso anterior, me diréis que el trabajo es el mismo o más, y otra vez os doy la razón, salvo que quieras instalar un segundo servidor, en ese caso el tiempo de instalación es:
PLAY RECAP *********************************************************************
servidornuevo : ok=18 changed=12 unreachable=0 failed=0
real 1m27.068s
user 0m11.468s
sys 0m27.220s
Los ficheros: