Update to 2021-12-01 19:13

This commit is contained in:
Daniel Berteaud
2021-12-01 19:13:34 +01:00
commit 4c4556c660
2153 changed files with 60999 additions and 0 deletions

View File

@@ -0,0 +1 @@
Please use the vaultwarden role instead

View File

@@ -0,0 +1,49 @@
---
bitwarden_version: 1.20.0
bitwarden_archive_url: https://github.com/dani-garcia/bitwarden_rs/archive/{{ bitwarden_version }}.tar.gz
bitwarden_archive_sha1: 39354ae4124a95a7fcb53e81d6234c5599f609fa
bitwarden_web_version: 2.19.0
bitwarden_web_archive_url: https://github.com/dani-garcia/bw_web_builds/releases/download/v{{ bitwarden_web_version }}/bw_web_v{{ bitwarden_web_version }}.tar.gz
bitwarden_web_archive_sha1: dfb5acdad88bb6a915b7115739428278e7f3ea98
bitwarden_root_dir: /opt/bitwarden_rs
bitwarden_user: bitwarden_rs
# Database : can be sqlite or mysql
bitwarden_db_engine: sqlite
bitwarden_db_server: "{{ mysql_server | default('localhost') }}"
bitwarden_db_port: 3306
bitwarden_db_name: bitwardenrs
bitwarden_db_user: bitwardenrs
# A random one will be created if not defined
# bitwaren_db_pass: S3cr3t.
# Port on which bitwarden will bind
bitwarden_http_port: 8000
bitwarden_ws_port: 8001
# List of IP addresses (can be CIDR notation) which will be able to
# access bitwarden ports
bitwarden_src_ip: []
bitwarden_web_src_ip: []
# Public URL on which bitwarden will be accessible
bitwarden_public_url: http://{{ inventory_hostname }}:{{ bitwarden_http_port }}
# Should registration be enabled
bitwarden_registration: False
# List of domain names for which registration will be accepted
# Those domains will be accepted for registration even if bitwarden_registration is set to False
bitwarden_domains_whitelist:
- "{{ ansible_domain }}"
# Admin Token to access /admin. A random one is created if not defined
# bitwarden_admin_token: S3cr3t.
# Or you can just disable the admin token. But you have to protect /admin yourself (eg, on a reverse proxy)
bitwarden_disable_admin_token: False
# YubiKey settings
# bitwarden_yubico_client_id: XXXX
# bitwarden_yubico_secret_key: XXXX

View File

@@ -0,0 +1,5 @@
---
- name: restart bitwarden_rs
service: name=bitwarden_rs state=restarted
when: not bitwarden_started.changed

View File

@@ -0,0 +1,9 @@
---
dependencies:
- role: rust
- role: nginx
- role: repo_mariadb
when: bitwarden_db_engine == 'mysql'
- role: mysql_server
when: bitwarden_db_engine == 'mysql' and (bitwarden_db_server == 'localhost' or bitwarden_db_server == '127.0.0.1')

View File

@@ -0,0 +1,12 @@
---
- name: Compress previous version
command: tar cJf {{ bitwarden_root_dir }}/archives/{{ bitwarden_current_version }}+{{ bitwarden_web_current_version }}.txz ./
args:
warn: False
chdir: "{{ bitwarden_root_dir }}/archives/{{ bitwarden_current_version }}+{{ bitwarden_web_current_version }}"
tags: bitwarden
- name: Remove archive dir
file: path={{ bitwarden_root_dir }}/archives/{{ bitwarden_current_version }}+{{ bitwarden_web_current_version }} state=absent
tags: bitwarden

View File

@@ -0,0 +1,38 @@
---
- name: Create archive dir
file: path={{ bitwarden_root_dir }}/archives/{{ bitwarden_current_version }}+{{ bitwarden_web_current_version }} state=directory
tags: bitwarden
- name: Stop bitwarden during upgrade
service: name=bitwarden_rs state=stopped
tags: bitwarden
- name: Archive current version
synchronize:
src: "{{ bitwarden_root_dir }}/{{ item }}"
dest: "{{ bitwarden_root_dir }}/archives/{{ bitwarden_current_version }}+{{ bitwarden_web_current_version }}/"
recursive: True
delete: True
delegate_to: "{{ inventory_hostname }}"
loop:
- bitwarden_rs
- data
- etc
- web-vault
tags: bitwarden
- name: Dump the database
mysql_db:
state: dump
name: "{{ bitwarden_db_name }}"
target: "{{ bitwarden_root_dir }}/archives/{{ bitwarden_current_version }}+{{ bitwarden_web_current_version }}/{{ bitwarden_db_name }}.sql.xz"
login_host: "{{ bitwarden_db_server }}"
login_user: "{{ bitwarden_db_user }}"
login_password: "{{ bitwarden_db_pass }}"
quick: True
single_transaction: True
environment:
XZ_OPT: -T0
when: bitwarden_db_engine == 'mysql'
tags: bitwarden

View File

@@ -0,0 +1,10 @@
---
- name: Remove temp files
file: path={{ item }} state=absent
loop:
- "{{ bitwarden_root_dir }}/tmp/bitwarden_rs-{{ bitwarden_version }}"
- "{{ bitwarden_root_dir }}/tmp/bitwarden_rs-{{ bitwarden_version }}.tar.gz"
- "{{ bitwarden_root_dir }}/tmp/web-vault"
- "{{ bitwarden_root_dir }}/tmp/bw_web_v{{ bitwarden_web_version }}.tar.gz"
tags: bitwarden

View File

@@ -0,0 +1,11 @@
---
- name: Deploy configuration
template: src=bitwarden_rs.conf.j2 dest={{ bitwarden_root_dir }}/etc/bitwarden_rs.conf group={{ bitwarden_user }} mode=640
notify: restart bitwarden_rs
tags: bitwarden
- name: Deploy nginx configuration
template: src=nginx.conf.j2 dest=/etc/nginx/ansible_conf.d/31-bitwarden.conf
notify: reload nginx
tags: bitwarden

View File

@@ -0,0 +1,24 @@
---
- name: Create directories
file: path={{ bitwarden_root_dir }}/{{ item.dir }} state=directory owner={{ item.owner | default(omit) }} group={{ item.group | default(omit) }} mode={{ item.mode | default(omit) }}
loop:
- dir: /
mode: 755
- dir: etc
group: "{{ bitwarden_user }}"
mode: 750
- dir: tmp
mode: 700
- dir: meta
mode: 700
- dir: archives
mode: 700
- dir: data
owner: "{{ bitwarden_user }}"
group: "{{ bitwarden_user }}"
mode: 700
- dir: web-vault
- dir: backup
mode: 700
tags: bitwarden

View File

@@ -0,0 +1,67 @@
---
- name: Set initial install modes
block:
- set_fact: bitwarden_install_mode='none'
- set_fact: bitwarden_current_version=''
- set_fact: bitwarden_web_install_mode='none'
- set_fact: bitwarden_web_current_version=''
tags: bitwarden
- name: Check if server is installed
stat: path={{ bitwarden_root_dir }}/meta/ansible_version
register: bitwarden_version_file
tags: bitwarden
- when: bitwarden_version_file.stat.exists
block:
- name: Check installed version
slurp: src={{ bitwarden_root_dir }}/meta/ansible_version
register: bitwarden_current_version
- set_fact: bitwarden_current_version={{ bitwarden_current_version.content | b64decode | trim }}
- set_fact: bitwarden_install_mode='upgrade'
when: bitwarden_current_version != bitwarden_version
tags: bitwarden
- when: not bitwarden_version_file.stat.exists
block:
- set_fact: bitwarden_install_mode='install'
tags: bitwarden
- name: Check if web vault is installed
stat: path={{ bitwarden_root_dir }}/meta/ansible_web_version
register: bitwarden_web_version_file
tags: bitwarden
- when: bitwarden_web_version_file.stat.exists
block:
- name: Check installed version
slurp: src={{ bitwarden_root_dir }}/meta/ansible_web_version
register: bitwarden_web_current_version
- set_fact: bitwarden_web_current_version={{ bitwarden_web_current_version.content | b64decode | trim }}
- set_fact: bitwarden_web_install_mode='upgrade'
when: bitwarden_web_current_version != bitwarden_web_version
tags: bitwarden
- when: not bitwarden_web_version_file.stat.exists
block:
- set_fact: bitwarden_web_install_mode='install'
tags: bitwarden
- when: bitwarden_admin_token is not defined
name: Generate a random admin token
block:
- import_tasks: ../includes/get_rand_pass.yml
vars:
- pass_file: "{{ bitwarden_root_dir }}/meta/ansible_admin_token"
- set_fact: bitwarden_admin_token={{ rand_pass }}
tags: bitwarden
- when: bitwarden_db_pass is not defined
tags: bitwarden
block:
- import_tasks: ../includes/get_rand_pass.yml
vars:
- pass_file: "{{ bitwarden_root_dir }}/meta/ansible_dbpass"
- set_fact: bitwarden_db_pass={{ rand_pass }}

View File

@@ -0,0 +1,109 @@
---
- name: Install needed packages
yum:
name:
- openssl-devel
- gcc
- sqlite
tags: bitwarden
- name: Check if MariaDB version is set
fail: msg="Need to define mysql_mariadb_version"
when:
- bitwarden_db_engine == 'mysql'
- mysql_mariadb_version is not defined or mysql_mariadb_version == 'default'
- ansible_os_family == 'RedHat'
- ansible_distribution_major_version is version('8','<')
tags: bitwarden
- name: Install MariaDB devel package
yum:
name:
- mariadb-devel
when: bitwarden_db_engine == 'mysql'
tags: bitwarden
# With upstream MariaDB repo, /usr/lib64/libmariadb.so is in MariaDB-shared not in MariaDB-devel
- name: Install MariaDB shared libs
yum:
name:
- MariaDB-shared
when:
- bitwarden_db_engine == 'mysql'
- mysql_mariadb_version is defined
- mysql_mariadb_version != 'default'
tags: bitwarden
- when: bitwarden_install_mode != 'none'
tags: bitwarden
block:
- name: Download bitwarden
get_url:
url: "{{ bitwarden_archive_url }}"
dest: "{{ bitwarden_root_dir }}/tmp"
checksum: sha1:{{ bitwarden_archive_sha1 }}
- name: Extract bitwarden archive
unarchive:
src: "{{ bitwarden_root_dir }}/tmp/bitwarden_rs-{{ bitwarden_version }}.tar.gz"
dest: "{{ bitwarden_root_dir }}/tmp"
remote_src: True
- name: Build bitwarden
command: bash -lc 'cargo build --features={{ (bitwarden_db_engine == "mysql") | ternary("mysql","sqlite") }} --release'
args:
chdir: "{{ bitwarden_root_dir }}/tmp/bitwarden_rs-{{ bitwarden_version }}"
- name: Install binary
copy: src={{ bitwarden_root_dir }}/tmp/bitwarden_rs-{{ bitwarden_version }}/target/release/bitwarden_rs dest="{{ bitwarden_root_dir }}/" mode=755 remote_src=True
notify: restart bitwarden_rs
- when: bitwarden_web_install_mode != 'none'
tags: bitwarden
block:
- name: Download bitwarden web vault
get_url:
url: "{{ bitwarden_web_archive_url }}"
dest: "{{ bitwarden_root_dir }}/tmp"
checksum: sha1:{{ bitwarden_web_archive_sha1 }}
- name: Extract the archive
unarchive:
src: "{{ bitwarden_root_dir }}/tmp/bw_web_v{{ bitwarden_web_version }}.tar.gz"
dest: "{{ bitwarden_root_dir }}/tmp"
remote_src: True
- name: Move files to their final location
synchronize:
src: "{{ bitwarden_root_dir }}/tmp/web-vault/"
dest: "{{ bitwarden_root_dir }}/web-vault/"
recursive: True
delete: True
delegate_to: "{{ inventory_hostname }}"
- name: Install systemd unit
template: src=bitwarden_rs.service.j2 dest=/etc/systemd/system/bitwarden_rs.service
register: bitwarden_unit
tags: bitwarden
- name: Reload systemd
systemd: daemon_reload=True
when: bitwarden_unit.changed
tags: bitwarden
- name: Install pre/post backup hooks
template: src={{ item }}-backup.sh.j2 dest=/etc/backup/{{ item }}.d/bitwarden_rs.sh mode=755
loop:
- pre
- post
tags: bitwarden
- import_tasks: ../includes/webapps_create_mysql_db.yml
vars:
- db_name: "{{ bitwarden_db_name }}"
- db_user: "{{ bitwarden_db_user }}"
- db_server: "{{ bitwarden_db_server }}"
- db_pass: "{{ bitwarden_db_pass }}"
when: bitwarden_db_engine == 'mysql'
tags: bitwarden

View File

@@ -0,0 +1,9 @@
---
- name: Handle bitwarden_rs ports in the firewall
iptables_raw:
name: bitwarden_rs
state: "{{ (bitwarden_src_ip | length > 0) | ternary('present','absent') }}"
rules: "-A INPUT -m state --state NEW -m multiport -p tcp --dports {{ bitwarden_http_port }},{{ bitwarden_ws_port }} -s {{ bitwarden_src_ip | join(',') }} -j ACCEPT"
when: iptables_manage | default(True)
tags: firewall,bitwarden

View File

@@ -0,0 +1,15 @@
---
- include: user.yml
- include: directories.yml
- include: facts.yml
- include: archive_pre.yml
when: bitwarden_install_mode == 'upgrade' or bitwarden_web_install_mode == 'upgrade'
- include: install.yml
- include: conf.yml
- include: iptables.yml
- include: service.yml
- include: write_version.yml
- include: archive_post.yml
when: bitwarden_install_mode == 'upgrade' or bitwarden_web_install_mode == 'upgrade'
- include: cleanup.yml

View File

@@ -0,0 +1,6 @@
---
- name: Start and enable the service
service: name=bitwarden_rs state=started enabled=True
register: bitwarden_started
tags: bitwarden

View File

@@ -0,0 +1,5 @@
---
- name: Create bitwarden_rs user
user: name={{ bitwarden_user }} home={{ bitwarden_root_dir }} system=True
tags: bitwarden

View File

@@ -0,0 +1,10 @@
---
- name: Write versions
copy: content={{ item.version }} dest={{ bitwarden_root_dir }}/meta/{{ item.file }}
loop:
- version: "{{ bitwarden_version }}"
file: ansible_version
- version: "{{ bitwarden_web_version }}"
file: ansible_web_version
tags: bitwarden

View File

@@ -0,0 +1,28 @@
IP_HEADER=X-Forwarded-For
SIGNUPS_VERIFY=true
SIGNUPS_ALLOWED={{ bitwarden_registration | ternary('true','false') }}
{% if bitwarden_domains_whitelist | length > 0 %}
SIGNUPS_DOMAINS_WHITELIST={{ bitwarden_domains_whitelist | join(',') }}
{% endif %}
ADMIN_TOKEN={{ bitwarden_admin_token }}
DISABLE_ADMIN_TOKEN={{ bitwarden_disable_admin_token | ternary('true','false') }}
DOMAIN={{ bitwarden_public_url }}
ROCKET_ENV=prod
ROCKET_ADDRESS=0.0.0.0
ROCKET_PORT={{ bitwarden_http_port }}
WEBSOCKET_ENABLED=true
WEBSOCKET_PORT={{ bitwarden_ws_port }}
SMTP_HOST=localhost
SMTP_PORT=25
SMTP_SSL=false
SMTP_FROM=bitwarden-rs-noreply@{{ ansible_domain }}
{% if bitwarden_db_engine == 'mysql' %}
DATABASE_URL=mysql://{{ bitwarden_db_user }}:{{ bitwarden_db_pass | urlencode | regex_replace('/','%2F') }}@{{ bitwarden_db_server }}:{{ bitwarden_db_port }}/{{ bitwarden_db_name }}
ENABLE_DB_WAL=false
{% else %}
DATABASE_URL=data/db.sqlite3
{% endif %}
{% if bitwarden_yubico_client_id is defined and bitwarden_yubico_secret_key is defined %}
YUBICO_CLIENT_ID={{ bitwarden_yubico_client_id }}
YUBICO_SECRET_KEY={{ bitwarden_yubico_secret_key }}
{% endif %}

View File

@@ -0,0 +1,27 @@
[Unit]
Description=Bitwarden Server (Rust Edition)
Documentation=https://github.com/dani-garcia/bitwarden_rs
After=network.target
{% if bitwarden_db_engine == 'mysql' and (bitwarden_db_server == 'localhost' or bitwarden_db_server == '127.0.0.1') %}
After=mariadb.service
Requires=mariadb.service
{% endif %}
[Service]
User={{ bitwarden_user }}
Group={{ bitwarden_user }}
EnvironmentFile={{ bitwarden_root_dir }}/etc/bitwarden_rs.conf
ExecStart={{ bitwarden_root_dir }}/bitwarden_rs
PrivateTmp=true
PrivateDevices=true
ProtectHome=true
ProtectSystem=full
WorkingDirectory={{ bitwarden_root_dir }}
ReadWriteDirectories={{ bitwarden_root_dir }}/data
ReadOnlyDirectories={{ bitwarden_root_dir }}/etc {{ bitwarden_root_dir }}/web-vault
Restart=on-failure
StartLimitInterval=0
RestartSec=30
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,69 @@
server {
listen 443 ssl http2;
server_name {{ bitwarden_public_url | urlsplit('hostname') }};
include /etc/nginx/ansible_conf.d/acme.inc;
{% if bitwarden_cert_path is defined and bitwarden_key_path is defined %}
ssl_certificate {{ bitwarden_cert_path }};
ssl_certificate_key {{ bitwarden_key_path }};
{% elif bitwarden_letsencrypt_cert is defined and bitwarden_letsencrypt_cert == True %}
ssl_certificate /var/lib/dehydrated/certificates/certs/{{ bitwarden_public_url | urlsplit('hostname') }}/fullchain.pem;
ssl_certificate_key /var/lib/dehydrated/certificates/certs/{{ bitwarden_public_url | urlsplit('hostname') }}/privkey.pem;
{% elif bitwarden_letsencrypt_cert is string %}
ssl_certificate /var/lib/dehydrated/certificates/certs/{{ bitwarden_letsencrypt_cert }}/fullchain.pem;
ssl_certificate_key /var/lib/dehydrated/certificates/certs/{{ bitwarden_letsencrypt_cert }}/privkey.pem;
{% endif %}
root {{ bitwarden_root_dir }}/web-vault;
client_max_body_size 512M;
if ($request_method !~ ^(GET|POST|HEAD|PUT|DELETE)$ ) {
return 405;
}
location /notifications/hub {
proxy_pass http://localhost:{{ bitwarden_ws_port }};
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location /notifications/hub/negotiate {
proxy_pass http://localhost:{{ bitwarden_http_port }};
}
location @proxy {
proxy_pass http://localhost:{{ bitwarden_http_port }};
}
location / {
try_files $uri $uri/index.html @proxy;
}
add_header X-Frame-Options "DENY";
add_header X-Content-Type-Options "nosniff";
add_header X-XSS-Protection "1; mode=block";
add_header Strict-Transport-Security "$hsts_header";
# Send info about the original request to the backend
proxy_set_header X-Forwarded-For "$proxy_add_x_forwarded_for";
proxy_set_header X-Real-IP "$remote_addr";
proxy_set_header X-Forwarded-Proto "$scheme";
proxy_set_header X-Forwarded-Host "$host";
proxy_set_header Host "$host";
# Set the timeout to read responses from the backend
proxy_read_timeout 60s;
# Enable Keep Alive to the backend
proxy_socket_keepalive on;
# Disable buffering large files
proxy_max_temp_file_size 5m;
allow 127.0.0.1;
{% for ip in bitwarden_web_src_ip %}
allow {{ ip }};
{% endfor %}
deny all;
}

View File

@@ -0,0 +1,4 @@
#!/bin/bash -e
rm -f {{ bitwarden_root_dir }}/backup/*
umount /home/lbkp/bitwarden_rs

View File

@@ -0,0 +1,19 @@
#!/bin/sh
set -eo pipefail
mkdir -p /home/lbkp/bitwarden_rs/
cp {{ bitwarden_root_dir }}/data/rsa* {{ bitwarden_root_dir }}/backup/
{% if bitwarden_db_engine == 'mysql' %}
/usr/bin/mysqldump \
{% if bitwarden_db_server != 'localhost' and bitwarden_db_server != '127.0.0.1' %}
--user={{ bitwarden_db_user }} \
--password={{ bitwarden_db_pass | quote }} \
--host={{ bitwarden_db_server }} \
{% endif %}
--quick --single-transaction \
--add-drop-table {{ bitwarden_db_name }} | zstd -T0 -c > {{ bitwarden_root_dir }}/backup/{{ bitwarden_db_name }}.sql.zst
{% else %}
sqlite3 {{ bitwarden_root_dir }}/data/db.sqlite3 ".backup '{{ bitwarden_root_dir }}/backup/db.sqlite3'"
{% endif %}
mountpoint -q /home/lbkp/bitwarden_rs/ || mount -o bind,ro {{ bitwarden_root_dir }}/backup/ /home/lbkp/bitwarden_rs/

View File

@@ -0,0 +1,117 @@
---
bm_http_ports:
- 80
- 443
bm_http_src_ip:
- 0.0.0.0/0
bm_imap_ports:
- 143
- 993
bm_imap_src_ip:
- 0.0.0.0/0
bm_pop_ports:
- 110
- 995
bm_pop_src_ip:
- 0.0.0.0/0
bm_smtp_ports:
- 25
- 465
- 587
bm_smtp_src_ip:
- 0.0.0.0/0
bm_milter_ports:
- 2500
bm_milter_src:ip: []
bm_int_ports:
- 24
- 144
- 1110
- 1143
- 2000
- 2400
- 2500
- 4444
- 5280
- 5290
- 5432
- '5701:5715'
- 8021
- 8022
- 8079
- 8080
- 8082
- 8084
- 8087
- 9083
- 9086
- 9090
- 9099
- 9200
- 9300
bm_int_src_ip: []
# bm_letsencrypt_cert: bluemind.domain.tld
bm_mem_alloc_base:
bm-core:
heap: 512
direct: 512
spare: 20
bm-node:
heap: 128
direct: 128
spare: 0
bm-eas:
heap: 256
direct: 128
spare: 2
bm-mapi:
heap: 512
direct: 256
spare: 10
bm-ips:
heap: 64
direct: 64
spare: 0
bm-hps:
heap: 128
direct: 128
spare: 0
bm-lmtpd:
heap: 128
direct: 128
spare: 0
bm-locator:
heap: 64
direct: 64
spare: 0
bm-milter:
heap: 64
direct: 64
spare: 0
bm-tika:
heap: 128
direct: 128
spare: 0
bm-xmpp:
heap: 32
direct: 32
spare: 0
bm-ysnp:
heap: 64
direct: 64
spare: 0
bm-elasticsearch:
heap: 512
direct: 512
spare: 20
bm_mem_alloc: {}
bm_mem_alloc_rules: "{{ bm_mem_alloc_base | combine(bm_mem_alloc, recursive=True) }}"

View File

@@ -0,0 +1,4 @@
---
- name: restart bluemind
command: bmctl restart

View File

@@ -0,0 +1,118 @@
---
- name: Install tools
yum:
name:
- socat
tags: bm
- name: Create dehydrated hook dir
file: path=/etc/dehydrated/hooks_deploy_cert.d state=directory
tags: bm
- name: Deploy dehydrated hook
template: src=dehydrated_deploy_hook.j2 dest=/etc/dehydrated/hooks_deploy_cert.d/bluemind mode=755
tags: bm
- name: Create local conf directory
file: path=/etc/bm/local state=directory
tags: bm
- name: Configure proxy
lineinfile:
regex: '^PROXY_OPTS=.*'
line: "PROXY_OPTS=\"{{ (system_proxy is defined and system_proxy != '') | ternary('-Dhttps.proxyHost=' ~ system_proxy | urlsplit('hostname') ~ ' -Dhttps.proxyPort=' ~ system_proxy | urlsplit('port') ~ ' -Dhttp.proxyHost=' ~ system_proxy | urlsplit('hostname') ~ ' -Dhttp.proxyPort=' ~ system_proxy | urlsplit('port'),'') }}\""
path: /etc/bm/local/{{ item }}.ini
create: True
loop:
- bm-core
- bm-webserver
notify: restart bluemind
tags: bm
- name: Configure JVM options
lineinfile:
regex: '^JVM_OPTS=.*'
line: "JVM_OPTS=\"${PROXY_OPTS}\""
path: /etc/bm/local/{{ item }}.ini
insertafter: '^PROXY_OPTS=.*'
loop:
- bm-core
- bm-webserver
notify: restart bluemind
tags: bm
- name: Configure memory allocation rules
template: src=rules.json.j2 dest=/etc/bm/local/rules.json
notify: restart bluemind
tags: bm
- set_fact:
bm_restart_services: "[ 'bm-elasticsearch', 'bm-mapi' ]"
tags: bm
- name: Create systemd unit snippet dirs
file: path=/etc/systemd/system/{{ item }}.service.d state=directory
loop: "{{ bm_restart_services }}"
tags: bm
- name: Configure systemd to restart services on failure
copy:
content: |
[Service]
TimeoutSec=60
StartLimitInterval=0
RestartSec=1
Restart=on-failure
dest: /etc/systemd/system/{{ item }}.service.d/restart.conf
loop: "{{ bm_restart_services }}"
register: bm_units
notify: restart bluemind
tags: bm
- name: Reload systemd
systemd: daemon_reload=True
when: bm_units.results | selectattr('changed','equalto',True) | list | length > 0
tags: bm
- name: Handle firewall ports
iptables_raw:
name: "{{ item.name }}"
state: "{{ (item.src | length > 0) | ternary('present','absent') }}"
rules: "{% if 'tcp' in item.proto | default(['tcp']) or item.proto | default('tcp') == 'tcp' %}-A INPUT -m state --state NEW -p tcp -m multiport --dports {{ item.ports | join(',') }} -s {{ item.src | join(',') }} -j ACCEPT\n{% endif %}
{% if 'udp' in item.proto | default(['tcp']) or item.proto | default('tcp') == 'udp' %}-A INPUT -m state --state NEW -p udp -m multiport --dports {{ item.ports | join(',') }} -s {{ item.src | join(',') }} -j ACCEPT{% endif %}"
when: iptables_manage | default(True)
with_items:
- ports: "{{ bm_http_ports }}"
name: bm_http_ports
src: "{{ bm_http_src_ip }}"
- ports: "{{ bm_imap_ports }}"
name: bm_imap_ports
src: "{{ bm_imap_src_ip }}"
- ports: "{{ bm_pop_ports }}"
name: bm_pop_ports
src: "{{ bm_pop_src_ip }}"
- ports: "{{ bm_smtp_ports }}"
name: bm_smtp_ports
src: "{{ bm_smtp_src_ip }}"
- ports: "{{ bm_milter_ports }}"
name: bm_milter_ports
src: "{{ bm_milter_src_ip }}"
- ports: "{{ bm_int_ports }}"
name: bm_int_ports
src: "{{ bm_int_src_ip }}"
tags: bm,firewall
- name: Create pre/post backup hook dir
file: path=/etc/backup/{{ item }}.d state=directory mode=750
loop:
- pre
- post
tags: bm
- name: Deploy pre and post backup script
template: src={{ item }}-backup.j2 dest=/etc/backup/{{ item }}.d/bluemind mode=755
loop:
- pre
- post
tags: bm

View File

@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="CORE" class="ch.qos.logback.classic.net.SyslogAppender">
<syslogHost>localhost</syslogHost>
<port>10514</port>
<facility>DAEMON</facility>
<suffixPattern>bm-core - [%thread] %c{1} %p - %m\n</suffixPattern>
</appender>
<logger name="org.apache.directory.shared" level="ERROR" />
<root level="INFO">
<appender-ref ref="CORE" />
</root>
<appender name="XMPP" class="ch.qos.logback.classic.net.SyslogAppender">
<syslogHost>localhost</syslogHost>
<port>10514</port>
<facility>DAEMON</facility>
<suffixPattern>bm-xmpp - [%thread] %c{1} %p - %m\n</suffixPattern>
</appender>
<logger name="net.bluemind.xmpp" level="INFO" additivity="false" />
<appender-ref ref="XMPP" />
</logger>
<appender name="MAILINDEX" class="ch.qos.logback.classic.net.SyslogAppender">
<syslogHost>localhost</syslogHost>
<port>10514</port>
<facility>DAEMON</facility>
<suffixPattern>bm-mailindex - [%thread] %c{1} %p - %m\n</suffixPattern>
</appender>
<logger name="net.bluemind.index.mail" level="INFO" additivity="false" />
<appender-ref ref="MAILINDEX" />
</logger>
<appender name="SLOWRESTCALL" class="ch.qos.logback.classic.net.SyslogAppender">
<syslogHost>localhost</syslogHost>
<port>10514</port>
<facility>DAEMON</facility>
<suffixPattern>bm-slowrestcall - [%thread] %c{1} %p - %m\n</suffixPattern>
</appender>
<logger name="net.bluemind.core.rest.log.CallLogger" level="WARN" additivity="false" />
<appender-ref ref="SLOWRESTCALL" />
</logger>
<appender name="RESTSOCKJSPROXYHANDLER" class="ch.qos.logback.classic.net.SyslogAppender">
<syslogHost>localhost</syslogHost>
<port>10514</port>
<facility>DAEMON</facility>
<suffixPattern>bm-js - [%thread] %c{1} %p - %m\n</suffixPattern>
</appender>
<logger name="net.bluemind.core.rest.sockjs.vertx.RestSockJsProxyHandler" level="INFO" additivity="false" />
<appender-ref ref="RESTSOCKJSPROXYHANDLER" />
</logger>
</configuration>

View File

@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="ALL" class="ch.qos.logback.classic.net.SyslogAppender">
<syslogHost>localhost</syslogHost>
<port>10514</port>
<facility>DAEMON</facility>
<suffixPattern>bm-eas - [%thread] %c{1} %p - %m\n</suffixPattern>
</appender>
<appender name="REQUESTS" class="ch.qos.logback.classic.net.SyslogAppender">
<syslogHost>localhost</syslogHost>
<port>10514</port>
<facility>DAEMON</facility>
<suffixPattern>bm-eas-requests - [%thread] %c{1} %p - %m\n</suffixPattern>
</appender>
<appender name="SIFT" class="ch.qos.logback.classic.sift.SiftingAppender">
<!-- in the absence of the class attribute, it is assumed that the
desired discriminator type is
ch.qos.logback.classic.sift.MDCBasedDiscriminator -->
<discriminator>
<key>user</key>
<defaultValue>anonymous</defaultValue>
</discriminator>
<sift>
<appender name="FILE-${user}" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>/var/log/bm-eas/user-eas-${user}.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<maxIndex>10</maxIndex>
<FileNamePattern>/var/log/bm-eas/user-eas-${user}.log.%i.gz</FileNamePattern>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>5000KB</MaxFileSize>
</triggeringPolicy>
<encoder>
<pattern>%d [%thread] %c{1} %p - %m\n</pattern>
</encoder>
</appender>
</sift>
</appender>
<appender name="ASYNC_SIFT" class="ch.qos.logback.classic.AsyncAppender">
<queueSize>500</queueSize>
<discardingThreshold>0</discardingThreshold>
<appender-ref ref="SIFT" />
</appender>
<logger name="org.apache.directory.shared.asn1.ber" level="ERROR">
<appender-ref ref="ALL"/>
</logger>
<logger name="net.bluemind.vertx.common.request.impl.WrappedResponse" level="INFO" additivity="true">
<appender-ref ref="REQUESTS"/>
</logger>
<root level="INFO">
<appender-ref ref="ALL"/>
<appender-ref ref="ASYNC_SIFT"/>
</root>
</configuration>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="SYSLOG" class="ch.qos.logback.classic.net.SyslogAppender">
<syslogHost>localhost</syslogHost>
<port>10514</port>
<facility>DAEMON</facility>
<suffixPattern>bm-hps - [%thread] %c{1} %p - %m\n</suffixPattern>
</appender>
<root level="INFO">
<appender-ref ref="SYSLOG" />
</root>
</configuration>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="SYSLOG" class="ch.qos.logback.classic.net.SyslogAppender">
<syslogHost>localhost</syslogHost>
<port>10514</port>
<facility>DAEMON</facility>
<suffixPattern>bm-ips - [%thread] %c{1} %p - %m\n</suffixPattern>
</appender>
<root level="INFO">
<appender-ref ref="SYSLOG" />
</root>
</configuration>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="SYSLOG" class="ch.qos.logback.classic.net.SyslogAppender">
<syslogHost>localhost</syslogHost>
<port>10514</port>
<facility>DAEMON</facility>
<suffixPattern>bm-lmtp - [%thread] %c{1} %p - %m\n</suffixPattern>
</appender>
<root level="INFO">
<appender-ref ref="SYSLOG" />
</root>
</configuration>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="SYSLOG" class="ch.qos.logback.classic.net.SyslogAppender">
<syslogHost>localhost</syslogHost>
<port>10514</port>
<facility>DAEMON</facility>
<suffixPattern>bm-locator - [%thread] %c{1} %p - %m\n</suffixPattern>
</appender>
<logger name="org.apache.directory.shared.asn1.ber" level="ERROR" />
<root level="INFO">
<appender-ref ref="SYSLOG" />
</root>
</configuration>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="SYSLOG" class="ch.qos.logback.classic.net.SyslogAppender">
<syslogHost>localhost</syslogHost>
<port>10514</port>
<facility>DAEMON</facility>
<suffixPattern>bm-milter - [%thread] %c{1} %p - %m\n</suffixPattern>
</appender>
<root level="INFO">
<appender-ref ref="SYSLOG" />
</root>
</configuration>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="SYSLOG" class="ch.qos.logback.classic.net.SyslogAppender">
<syslogHost>localhost</syslogHost>
<port>10514</port>
<facility>DAEMON</facility>
<suffixPattern>bm-node - [%thread] %c{1} %p - %m\n</suffixPattern>
</appender>
<logger name="org.apache.sshd.server" level="WARN" />
<root level="INFO">
<appender-ref ref="SYSLOG" />
</root>
</configuration>

View File

@@ -0,0 +1,19 @@
[Unit]
Description=Bluemind syslog daemon
After=syslog.target
[Service]
Type=simple
ExecStart=/bin/socat -t0 -T0 -u -s udp4-recv:10514 stdout
User=bm-syslog
Group=bm-syslog
Restart=always
PrivateTmp=yes
PrivateDevices=yes
ProtectSystem=full
ProtectHome=yes
NoNewPrivileges=yes
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="SYSLOG" class="ch.qos.logback.classic.net.SyslogAppender">
<syslogHost>localhost</syslogHost>
<port>10514</port>
<facility>DAEMON</facility>
<suffixPattern>bm-locator - [%thread] %c{1} %p - %m\n</suffixPattern>
</appender>
<root level="INFO">
<appender-ref ref="SYSLOG" />
</root>
</configuration>

View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="SYSLOG" class="ch.qos.logback.classic.net.SyslogAppender">
<syslogHost>localhost</syslogHost>
<port>10514</port>
<facility>DAEMON</facility>
<suffixPattern>bm-webserver - [%thread] %c{1} %p - %m\n</suffixPattern>
</appender>
<root level="INFO">
<appender-ref ref="SYSLOG" />
</root>
<appender name="DAV" class="ch.qos.logback.classic.net.SyslogAppender">
<syslogHost>localhost</syslogHost>
<port>10514</port>
<facility>DAEMON</facility>
<suffixPattern>bm-dav - [%thread] %c{1} %p - %m\n</suffixPattern>
</appender>
<logger name="net.bluemind.dav.server" level="INFO" additivity="false">
<appender-ref ref="DAV" />
</logger>
<appender name="SETUP" class="ch.qos.logback.classic.net.SyslogAppender">
<syslogHost>localhost</syslogHost>
<port>10514</port>
<facility>DAEMON</facility>
<suffixPattern>bm-setup - [%thread] %c{1} %p - %m\n</suffixPattern>
</appender>
<logger name="net.bluemind.sw.server" level="INFO" additivity="false">
<appender-ref ref="SETUP" />
</logger>
<appender name="JSLOG" class="ch.qos.logback.classic.net.SyslogAppender">
<syslogHost>localhost</syslogHost>
<port>10514</port>
<facility>DAEMON</facility>
<suffixPattern>bm-js-errors - [%thread] %c{1} %p - %m\n</suffixPattern>
</appender>
<logger name="net.bluemind.webmodule.server.handlers.LogHandler" level="INFO" additivity="false">
<appender-ref ref="JSLOG" />
</logger>
</configuration>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="SYSLOG" class="ch.qos.logback.classic.net.SyslogAppender">
<syslogHost>localhost</syslogHost>
<port>10514</port>
<facility>DAEMON</facility>
<suffixPattern>bm-xmpp - [%thread] %c{1} %p - %m\n</suffixPattern>
</appender>
<root level="INFO">
<appender-ref ref="SYSLOG" />
</root>
</configuration>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="SYSLOG" class="ch.qos.logback.classic.net.SyslogAppender">
<syslogHost>localhost</syslogHost>
<port>10514</port>
<facility>DAEMON</facility>
<suffixPattern>bm-ysnp - [%thread] %c{1} %p - %m\n</suffixPattern>
</appender>
<logger name="org.apache.directory.shared.asn1.ber" level="ERROR" />
<root level="INFO">
<appender-ref ref="SYSLOG" />
</root>
</configuration>

View File

@@ -0,0 +1,12 @@
#!/bin/bash -e
{% if bm_letsencrypt_cert is defined %}
if [ $1 == "{{ bm_letsencrypt_cert }}" ]; then
cat /var/lib/dehydrated/certificates/certs/{{ bm_letsencrypt_cert }}/privkey.pem > /etc/ssl/certs/bm_cert.pem
cat /var/lib/dehydrated/certificates/certs/{{ bm_letsencrypt_cert }}/fullchain.pem >> /etc/ssl/certs/bm_cert.pem
chown root:root /etc/ssl/certs/bm_cert.pem
chmod 644 /etc/ssl/certs/bm_cert.pem
/bin/systemctl reload postfix
/bin/systemctl reload bm-nginx
fi
{% endif %}

View File

@@ -0,0 +1,5 @@
#!/bin/sh
set -e
rm -rf /home/lbkp/bm/*

View File

@@ -0,0 +1,17 @@
#!/bin/sh
set -eo pipefail
DEST=/home/lbkp/bm/pgsql
mkdir -p $DEST
chown postgres:postgres $DEST
chmod 700 $DEST
for DB in $(su - postgres -c "/bin/psql -d postgres -qtc 'SELECT datname from pg_database' | grep -vP '^\s+?template[01]$'")
do
su - postgres -c "/bin/pg_dump -Fp -Cc $DB" | /bin/nice -n 10 zstd -c > $DEST/$DB.sql.zst
done
su - postgres -c "/bin/pg_dumpall --globals-only" | /bin/nice -n 10 zstd -c > $DEST/pg_globals.sql.zst
su - postgres -c "/bin/pg_dumpall --schema-only" | /bin/nice -n 10 zstd -c > $DEST/pg_schema.sql.zst
cp -a /etc/bm/local /home/lbkp/bm/conf

View File

@@ -0,0 +1,11 @@
[
{% for product in bm_mem_alloc_rules.keys() | list %}
{
"product":"{{ product }}",
"defaultHeap":"{{ bm_mem_alloc_rules[product].heap }}",
"defaultDirect":"{{ bm_mem_alloc_rules[product].direct }}",
"sparePercent":{{ bm_mem_alloc_rules[product].spare }}
}{% if not loop.last %},{% endif %}
{% endfor %}
]

View File

@@ -0,0 +1,19 @@
---
bounca_version: 0.1.1
#bounca_version: master
#bounca_git_url: https://github.com/repleo/bounca.git
bounca_archive_url: https://github.com/repleo/bounca/archive/v{{ bounca_version }}.tar.gz
bounca_root_dir: /opt/bounca
bounca_port: 8084
bounca_src_ip: []
bounca_user: bounca
bounca_db_server: "{{ pg_server | default('localhost') }}"
bounca_db_name: bounca
bounca_db_user: bounca
# Will be generated if not defined
# bounca_db_pass:
# bounca_secret_key:
bounca_admin_mail: "{{ system_admin_email }}"
bounca_from_mail: bounca@{{ ansible_domain }}

View File

@@ -0,0 +1,5 @@
---
- include: ../common/handlers/main.yml
- name: restart bounca
service: name=bounca state=restarted

View File

@@ -0,0 +1,2 @@
---

View File

@@ -0,0 +1,323 @@
---
- name: Set default install mode to none
set_fact: bounca_install_mode="none"
tags: bounca
- name: Check if bounca is installed
stat: path={{ bounca_root_dir }}/meta/ansible_version
register: bounca_version_file
tags: bounca
- name: Check installed version
command: cat {{ bounca_root_dir }}/meta/ansible_version
register: bounca_current_version
changed_when: False
when: bounca_version_file.stat.exists
tags: bounca
- name: Set install mode to install
set_fact: bounca_install_mode='install'
when: not bounca_version_file.stat.exists
tags: bounca
- name: Set install mode to upgrade
set_fact: bounca_install_mode='upgrade'
when:
- bounca_version_file.stat.exists
- bounca_current_version is defined
- bounca_current_version.stdout != bounca_version
# - bounca_manage_upgrade
tags: bounca
- name: Install dependencies
yum:
name:
- python34-virtualenv
- python34-pip
- uwsgi-plugin-python3
- uwsgi-logger-systemd
- python-psycopg2
- openssl-devel
- postgresql-devel
- postgresql
- gcc
- git
tags: bounca
- name: Create user account for bounca
user:
name: bounca
system: True
shell: /sbin/nologin
home: "{{ bounca_root_dir }}"
tags: bounca
- name: Create directories
file: path={{ item.dir }} state=directory owner={{ item.owner | default(omit) }} group={{ item.group | default(omit) }} mode={{ item.mode | default(omit) }}
with_items:
- dir: "{{ bounca_root_dir }}/tmp"
- dir: "{{ bounca_root_dir }}/app"
- dir: "{{ bounca_root_dir }}/data"
mode: 700
group: "{{ bounca_user }}"
owner: "{{ bounca_user }}"
- dir: "{{ bounca_root_dir }}/meta"
mode: 700
- dir: "{{ bounca_root_dir }}/archives"
mode: 700
- dir: /etc/bounca
mode: 750
group: "{{ bounca_user }}"
tags: bounca
- name: Create archive dir
file: path={{ bounca_root_dir }}/archives/{{ bounca_current_version.stdout }} state=directory mode=700
when: bounca_install_mode == "upgrade"
tags: bounca
- name: Archive current BounCA install
synchronize:
src: "{{ bounca_root_dir }}/app"
dest: "{{ bounca_root_dir }}/archives/{{ bounca_current_version.stdout }}/app"
recursive: True
delegate_to: "{{ inventory_hostname }}"
when: bounca_install_mode == "upgrade"
tags: bounca
- name: Dump database
postgresql_db:
name: "{{ bounca_db_name }}"
state: dump
login_host: "{{ bounca_db_server }}"
login_user: sqladmin
login_password: "{{ pg_admin_pass }}"
target: "{{ bounca_root_dir }}/archives/{{ bounca_current_version.stdout }}/{{ bounca_db_name }}.sql.gz"
when: bounca_install_mode == "upgrade"
tags: bounca
- name: Compress previous version
command: tar cJf {{ bounca_root_dir }}/archives/{{ bounca_current_version.stdout }}.txz ./
environment:
XZ_OPT: -T0
args:
chdir: "{{ bounca_root_dir }}/archives/{{ bounca_current_version.stdout }}"
when: bounca_install_mode == 'upgrade'
tags: bounca
- name: Remove the archive directory
file: path={{ bounca_root_dir }}/archives/{{ bounca_current_version.stdout }} state=absent
when: bounca_install_mode == 'upgrade'
tags: bounca
- name: Download BounCA
get_url:
url: "{{ bounca_archive_url }}"
dest: "{{ bounca_root_dir }}/tmp"
when: bounca_install_mode != 'none'
tags: bounca
- name: Extract BounCA
unarchive:
src: "{{ bounca_root_dir }}/tmp/bounca-{{ bounca_version }}.tar.gz"
dest: "{{ bounca_root_dir }}/tmp"
remote_src: yes
when: bounca_install_mode != "none"
tags: bounca
- name: Move BounCA to it's directory
synchronize:
src: "{{ bounca_root_dir }}/tmp/bounca-{{ bounca_version }}/"
dest: "{{ bounca_root_dir }}/app/"
recursive: True
delete: True
when: bounca_install_mode != "none"
delegate_to: "{{ inventory_hostname }}"
tags: bounca
#- name: Clone GIT repo
# git:
# repo: "{{ bounca_git_url }}"
# dest: "{{ bounca_root_dir }}/app"
# version: "{{ bounca_version }}"
# force: True
# register: bounca_git
# tags: bounca
#
#- name: Get new git commit
# command: git rev-parse HEAD
# args:
# chdir: "{{ bounca_root_dir }}/app"
# register: bounca_git_commit
# changed_when: False
# tags: bounca
#
#- name: Set install mode to upgrade
# set_fact: bounca_install_mode='upgrade'
# when:
# - bounca_install_mode == 'none'
# - bounca_git_commit.stdout != bounca_current_version.stdout
# tags: bounca
- name: Create archive dir
file: path={{ bounca_root_dir }}/archives/{{ bounca_current_version.stdout }} state=directory mode=700
when: bounca_install_mode == "upgrade"
tags: bounca
- name: Dump database
postgresql_db:
name: "{{ bounca_db_name }}"
state: dump
login_host: "{{ bounca_db_server }}"
login_user: sqladmin
login_password: "{{ pg_admin_pass }}"
target: "{{ bounca_root_dir }}/archives/{{ bounca_current_version.stdout }}/{{ bounca_db_name }}.sql.gz"
when: bounca_install_mode == "upgrade"
tags: bounca
- name: Create the virtualenv
pip:
state: latest
virtualenv: "{{ bounca_root_dir }}"
virtualenv_command: /usr/bin/virtualenv-3
requirements: "{{ bounca_root_dir }}/app/requirements.txt"
tags: bounca
- name: Link pki to the data dir
file: src={{ bounca_root_dir }}/data dest={{ bounca_root_dir }}/app/pki state=link
tags: bounca
- name: Handle bounca ports
iptables_raw:
name: bounca_ports
state: "{{ (bounca_src_ip | length > 0) | ternary('present','absent') }}"
rules: "-A INPUT -m state --state NEW -p tcp -m multiport --dports {{ bounca_port }} -s {{ bounca_src_ip | join(',') }} -j ACCEPT"
tags: [firewall,bounca]
#- name: Install additional python module
# pip:
# state: latest
# virtualenv: "{{ bounca_root_dir }}"
# name: "{{ item }}"
# with_items:
# - django-lemonldap
# tags: bounca
- name: Generate a random pass for the database
shell: openssl rand -base64 45 > {{ bounca_root_dir }}/meta/ansible_dbpass
args:
creates: "{{ bounca_root_dir }}/meta/ansible_dbpass"
when: bounca_db_pass is not defined
tags: bounca
- name: Read database password
command: cat {{ bounca_root_dir }}/meta/ansible_dbpass
register: bounca_rand_pass
when: bounca_db_pass is not defined
changed_when: False
tags: bounca
- name: Set database pass
set_fact: bounca_db_pass={{ bounca_rand_pass.stdout }}
when: bounca_db_pass is not defined
tags: bounca
- name: Generate a random secret
shell: openssl rand -base64 45 > {{ bounca_root_dir }}/meta/ansible_secret
args:
creates: "{{ bounca_root_dir }}/meta/ansible_secret"
when: bounca_secret_key is not defined
tags: bounca
- name: Read secret_key
command: cat {{ bounca_root_dir }}/meta/ansible_secret
register: bounca_rand_secret
when: bounca_secret_key is not defined
changed_when: False
tags: bounca
- name: Set secret_key
set_fact: bounca_secret_key={{ bounca_rand_secret.stdout }}
when: bounca_secret_key is not defined
tags: bounca
- name: Create the PostgreSQL role
postgresql_user:
db: postgres
name: "{{ bounca_db_user }}"
password: "{{ bounca_db_pass }}"
login_host: "{{ bounca_db_server }}"
login_user: sqladmin
login_password: "{{ pg_admin_pass }}"
tags: bounca
- name: Create the PostgreSQL database
postgresql_db:
name: "{{ bounca_db_name }}"
encoding: UTF-8
lc_collate: C
lc_ctype: C
template: template0
owner: "{{ bounca_db_user }}"
login_host: "{{ bounca_db_server }}"
login_user: sqladmin
login_password: "{{ pg_admin_pass }}"
tags: bounca
- name: Deploy configuration
template: src={{ item.src }} dest={{ item.dest }} owner={{ item.owner | default(omit) }} group={{ item.group | default(omit) }} mode={{ item.mode | default(omit) }}
with_items:
- src: main.ini.j2
dest: /etc/bounca/main.ini
group: bounca
mode: 640
- src: uwsgi.ini.j2
dest: /etc/bounca/uwsgi.ini
group: bounca
mode: 640
notify: restart bounca
tags: bounca
#- name: Add a tmpfiles.d snippet
# copy: content="d /run/bounca 750 bounca apache" dest=/etc/tmpfiles.d/bounca.conf
# register: bounca_tmpfiles
# tags: bounca
#
#- name: Create tmpdir
# command: systemd-tmpfiles --create
# when: bounca_tmpfiles.changed
# tags: bounca
- name: Deploy BounCA unit
template: src=bounca.service.j2 dest=/etc/systemd/system/bounca.service
register: bounca_unit
tags: bounca
- name: Reload systemd
command: systemctl daemon-reload
when: bounca_unit.changed
tags: bounca
- name: Stop BounCA daemon for DB upgrade
service: name=bounca state=stopped
when: bounca_install_mode == 'upgrade'
tags: bounca
- name: Migrate BounCA DB
django_manage: command="migrate --noinput" app_path={{ bounca_root_dir }}/app virtualenv={{ bounca_root_dir }}
when: bounca_install_mode != 'none'
tags: bounca
- name: Collect static assets
django_manage: command="collectstatic --noinput" app_path={{ bounca_root_dir }}/app virtualenv={{ bounca_root_dir }}
when: bounca_install_mode != 'none'
tags: bounca
- name: Start and enable the daemon
service: name=bounca state=started enabled=True
tags: bounca
- name: Write installed version
# copy: content={{ bounca_git_commit.stdout}} dest={{ bounca_root_dir }}/meta/ansible_version
copy: content={{ bounca_version }} dest={{ bounca_root_dir }}/meta/ansible_version
tags: bounca

View File

@@ -0,0 +1,17 @@
[Unit]
Description=BounCA PKI Daemon
After=syslog.target
[Service]
Environment=PYTHONPATH=/usr/bin/python34
ExecStart=/usr/sbin/uwsgi --ini /etc/bounca/uwsgi.ini
ExecReload=/bin/kill -HUP $MAINPID
User={{ bounca_user }}
Group={{ bounca_user }}
KillSignal=SIGINT
Restart=always
Type=notify
NotifyAccess=all
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,14 @@
[database]
DATABASE_USER: {{ bounca_db_user }}
DATABASE_PASSWORD: {{ bounca_db_pass }}
DATABASE_HOST: {{ bounca_db_server }}
DATABASE_NAME: {{ bounca_db_name }}
[secrets]
SECRET_KEY: {{ bounca_secret_key }}
[email]
EMAIL_HOST: localhost
ADMIN_MAIL: {{ bounca_admin_mail }}
FROM_MAIL: {{ bounca_from_mail }}

View File

@@ -0,0 +1,17 @@
[uwsgi]
plugin = python3
thread = 4
master = 1
processes = 30
vacuum = true
http11-socket = 0.0.0.0:{{ bounca_port }}
chdir = {{ bounca_root_dir }}/app
home = {{ bounca_root_dir }}
module = bounca.wsgi
check-static = {{ bounca_root_dir }}/app/media
static-skip-ext = .php
static-skip-ext = .cgi
static-skip-ext = .py
offload-threads = 4
cache2 = name=bounca,items=200
static-cache-paths = 300

View File

@@ -0,0 +1,77 @@
---
# Install directory
mxisd_root_dir: /opt/matrix/mxisd
# User account (will be created if missing)
mxisd_user: mxisd
# GIT URI repository
mxisd_git_uri: https://github.com/kamax-io/mxisd.git
# Version to checkout
mxisd_version: v1.1.1
# Memory limit for the service, in MB
mxisd_max_mem: 2048
# TCP port on which the service will bind
mxisd_port: 8083
# List of IP address allowed to access this port
# mxisd_src_ip:
# - 0.0.0.0/0
# External server to which forward queries
# if no match are found localy
# mxisd_forwarders:
# - https://matrix.org
# - https://vector.im
# Domain of your Matrix instance. Default to synapse_server_name if present
# mxisd_matrix_domain: matrix.example.com
# mxisd_server_name: matrix-id.example.com
# mxisd_public_url: https://matrix-id.domain.com/
# Are recursive lookups allowed
# mxisd_recursive_lookups: False
# And if yes, restrict it to certain IP only
# mxisd_recursive_lookups_ip:
# - 192.168.1.0/24
# - 172.20.0.0/16
# Should we run LDAP lookups. Most settings can be taken from synapse if installed
mxisd_ldap_lookup: True
mxisd_ldap_filter: "{{ ad_auth | default(False) | ternary('(&(objectCategory=person)(objectClass=user)(primaryGroupId=513))','(objectClass=inetOrgPerson)') }}"
mxisd_ldap_tls: True # Note that this is TLS, usually on port 636. Start TLS is not supported
mxisd_ldap_server: "{{ ad_auth | default(False) | ternary(ad_realm | default(samba_realm) | default(ansible_domain) | default(ansible_domain) | lower,'ldap.example.org') }}"
mxisd_ldap_port: "{{ mxisd_ldap_tls | ternary('636','389') }}"
#mxisd_ldap_bind_dn: cn=mxisd,ou=DSA,dc=example,dc=org
#mxisd_ldap_bind_pass: secret
mxisd_ldap_base: "{{ ad_auth | default(False) | ternary('DC=' + ad_realm | default(samba_realm) | default(ansible_domain) | regex_replace('\\.',',DC='), 'dc=example,dc=org') }}"
mxisd_ldap_uid_type: uid
mxisd_ldap_uid_value: "{{ ad_auth | default(False) | ternary('samaccountname','uid') }}"
mxisd_ldap_attr_name: cn
mxisd_ldap_attr_mail:
- mail
mxisd_ldap_attr_tel:
- telephoneNumber
- mobile
- homePhone
- otherTelephone
- otherMobile
- otherHomePhone
mxisd_ldap_attr_other:
- cn
- memberOf
# Outgoing email settings
# Will use synapse settings if available
# mxisd_smtp_server: smtp.domain.com
# mxisd_smtp_port: 25
# mxisd_smtp_tls: True
# mxisd_smtp_user:
# mxisd_smtp_pass:
# mxisd_smtp_from: mxisd@domain.com
# Overwrite the DNS name of your Matrix server
mxisd_dns_overwrite:
- name: "{{ mxisd_matrix_domain | default(synapse_server_name) }}"
value: http://localhost:8008
...

View File

@@ -0,0 +1,8 @@
---
- include: ../common/handlers/main.yml
- name: restart mxisd
service: name=matrix-mxisd state=restarted
...

View File

@@ -0,0 +1,62 @@
---
- name: Install needed packages
yum:
name:
- java-1.8.0-openjdk-devel
- git
state: latest
- name: Create mxisd user account
user: name={{ mxisd_user }} home={{ mxisd_root_dir }} shell=/bin/bash state=present
- name: Create needed directories
file: path={{ mxisd_root_dir }}/{{ item.dir }} state=directory mode={{ item.mode }} group={{ mxisd_user }}
with_items:
- { dir: /, mode: 750 }
- { dir: etc, mode: 770 }
- { dir: db, mode: 770 }
- name: Clone mxisd repo
git:
depth: 1
repo: "{{ mxisd_git_uri }}"
dest: "{{ mxisd_root_dir }}/app"
version: "{{ mxisd_version }}"
become_user: "{{ mxisd_user }}"
register: mxisd_git
become: True
- name: Setup proxy settings for gradle
template: src=gradle.properties.j2 dest={{ mxisd_root_dir }}/app/gradle.properties
- name: Check if the jar already exists
stat: path={{ mxisd_root_dir }}/app/build/libs/app.jar
register: mxisd_jar
- name: Build mxisd
command: ./gradlew --no-daemon build
args:
chdir: "{{ mxisd_root_dir }}/app"
become: True
become_user: "{{ mxisd_user }}"
when: mxisd_git.changed or not mxisd_jar.stat.exists
notify: restart mxisd
- name: Handle mxisd port
iptables_raw:
name=mxisd_port
state={{ (mxisd_src_ip is defined and mxisd_src_ip | length > 0) | ternary('present','absent') }}
rules="-A INPUT -m state --state NEW -p tcp --dport {{ mxisd_port }} -s {{ mxisd_src_ip | join(',') }} -j ACCEPT"
when: iptables_manage | default(True)
- name: Deploy service config
template: src=mxisd.yaml.j2 dest={{ mxisd_root_dir }}/etc/mxisd.yaml group={{ mxisd_user }} mode=640
notify: restart mxisd
- name: Deploy systemd unit
template: src=matrix-mxisd.service.j2 dest=/etc/systemd/system/matrix-mxisd.service
notify: reload systemd
- name: Start and enable the service
service: name=matrix-mxisd state=started enabled=yes

View File

@@ -0,0 +1,6 @@
{% if system_proxy is defined and system_proxy != '' %}
systemProp.http.proxyHost={{ system_proxy | urlsplit('hostname') }}
systemProp.http.proxyPort={{ system_proxy | urlsplit('port') }}
systemProp.https.proxyHost={{ system_proxy | urlsplit('hostname') }}
systemProp.https.proxyPort={{ system_proxy | urlsplit('port') }}
{% endif %}

View File

@@ -0,0 +1,19 @@
[Unit]
Description=Matrix Identity Service Daemon
[Service]
Type=simple
User={{ mxisd_user }}
Group={{ mxisd_user }}
ExecStart=/usr/bin/java -jar {{ mxisd_root_dir }}/app/build/libs/app.jar -c {{ mxisd_root_dir }}/etc/mxisd.yaml
PrivateTmp=yes
PrivateDevices=yes
ProtectSystem=full
ProtectHome=yes
NoNewPrivileges=yes
MemoryLimit={{ mxisd_max_mem }}M
SyslogIdentifier=matrix-mxisd
Restart=on-failure
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,105 @@
matrix:
domain: '{{ mxisd_matrix_domain | default(synapse_server_name) }}'
server:
port: {{ mxisd_port }}
{% if mxisd_server_name is defined %}
name: '{{ mxisd_server_name }}'
{% endif %}
publicUrl: '{{ mxisd_public_url | default('https://' + synapse_server_name) }}'
key:
path: '{{ mxisd_root_dir }}/etc/signing.key'
lookup:
recursive:
enabled: {{ mxisd_recursive_lookups | default(True) | ternary('true','false') }}
{% if mxisd_recursive_lookups_ip is defined and mxisd_recursive_lookups_ip | length > 0 %}
allowedCidr:
{% for net in mxisd_recursive_lookups_ip %}
{% if net | ipaddr %}
- {{ net }}
{% endif %}
{% endfor %}
{% endif %}
{% if mxisd_ldap_lookup | default(synapse_ldap_auth) | default(False) %}
ldap:
enabled: True
{% if mxisd_ldap_filter is defined %}
filter: {{ mxisd_ldap_filter }}
{% elif synapse_ldap_filter is defined %}
filter: {{ synapse_ldap_filter }}
{% endif %}
connection:
tls: {{ mxisd_ldap_tls | default(True) | ternary('true','false') }}
host: {{ mxisd_ldap_server | default(synapse_ldap_uri) | regex_replace('^(ldaps?://)?(?P<host>[a-zA-Z0-9\-\.]+)(:\d+)?','\\g<host>') | default('localhost') }}
port: {{ mxisd_ldap_port | default(mxisd_ldap_tls | ternary('636','389')) }}
{% if mxisd_ldap_bind_dn is defined and mxisd_ldap_bind_pass is defined %}
bindDn: {{ mxisd_ldap_bind_dn }}
bindPassword: {{ mxisd_ldap_bind_pass }}
{% elif synapse_ldap_bind_dn is defined and synapse_ldap_bind_pass is defined %}
bindDn: {{ synapse_ldap_bind_dn }}
bindPassword: {{ synapse_ldap_bind_pass }}
{% endif %}
baseDn: {{ mxisd_ldap_base | default(synapse_ldap_user_base) }}
attribute:
uid:
type: {{ mxisd_ldap_uid_type | default('uid') }}
value: {{ mxisd_ldap_uid_value | default(ad_auth | default(False) | ternary('samaccountname','uid')) }}
name: {{ mxisd_ldap_attr_name | default(synapse_ldap_attr_name) | default('cn') }}
{% if mxisd_ldap_attr_mail is defined and mxisd_ldap_attr_mail | length > 0 %}
threepid:
email:
{% for attr in mxisd_ldap_attr_mail %}
- {{ attr }}
{% endfor %}
{% endif %}
{% if mxisd_ldap_attr_tel is defined and mxisd_ldap_attr_tel | length > 0 %}
msisdn:
{% for attr in mxisd_ldap_attr_tel %}
- {{ attr }}
{% endfor %}
{% endif %}
{% if mxisd_ldap_attr_other is defined and mxisd_ldap_attr_other | length > 0 %}
other:
{% for attr in mxisd_ldap_attr_other %}
- {{ attr }}
{% endfor %}
{% endif %}
{% endif %}
{% if mxisd_forwarders is defined and mxisd_forwarders | length > 0 %}
forward:
servers:
{% for server in mxisd_forwarders %}
- {{ server }}
{% endfor %}
{% endif %}
threepid:
medium:
email:
connectors:
smtp:
host: {{ mxisd_smtp_server | default(synapse_smtp_server) }}
port: {{ mxisd_smtp_port | default(synapse_smtp_port) }}
tls: {{ (mxisd_smtp_tls | default(synapse_smtp_tls)) | ternary('2', '1') }}
{% if mxisd_smtp_user is defined and mxisd_smtp_pass is defined %}
login: "{{ mxisd_smtp_user }}"
password: "{{ mxisd_smtp_pass }}"
{% elif synapse_smtp_user is defined and synapse_smtp_pass is defined %}
login: "{{ synapse_smtp_user }}"
password: "{{ synapse_smtp_pass }}"
{% endif %}
identity:
from: "{{ mxisd_smtp_from | default('no-reply@' + ansible_domain) }}"
storage:
backend: 'sqlite'
provider:
sqlite:
database: '{{ mxisd_root_dir }}/db/mxisd.sqlite'
{% if mxisd_dns_overwrite is defined and mxisd_dns_overwrite | length > 0 %}
dns:
overwrite:
homeserver:
client:
{% for overwrite in mxisd_dns_overwrite %}
- name: {{ overwrite.name }}
value: '{{ overwrite.value }}'
{% endfor %}
{% endif %}

View File

@@ -0,0 +1,115 @@
---
mayan_version: 4.0.11
mayan_root_dir: /opt/mayan-edms
mayan_user: mayan-edms
# Should ansible handle upgrades ? If false, only initial install will be done
mayan_manage_upgrade: True
# Can be mysql or postgresql
mayan_db_engine: postgresql
mayan_db_server: "{{ mysql_server | default('localhost') }}"
mayan_db_port: "{{ (mayan_db_engine == 'mysql') | ternary('3306','5432') }}"
mayan_db_user: mayanedms
mayan_db_name: mayanedms
# A random pass will be created if not defined
# mayan_db_pass: S3cr3t.
# URL of the redis server to use
mayan_redis_url: redis://{% if redis_pass is defined %}:{{ redis_pass }}{% endif %}127.0.0.1:6379
# ID of the redis DB mayan will use
mayan_redis_db: 0
# URL of the amqp broker
mayan_amqp_url: amqp://127.0.0.1:5672/
# Number of web workers
mayan_web_workers: 3
# Port and list of allowed IP
mayan_port: 8000
mayan_src_ip: []
# From email address
mayan_from_mail: mayan-edsm@{{ ansible_domain }}
# Main language for document
mayan_doc_lang: fra
# LDAP Auth
# Most of these settings will try to detect system auth config
# and use them. But you can override if you want
#
# This is to turn on of off LDAP auth
mayan_ldap_auth: "{{ (ad_auth | default(False) or ldap_auth | default(False)) | ternary(True,False) }}"
# URI of your LDAP server, eg ldap://ldap.example.org:389
mayan_ldap_uri: "{{ ad_auth | default(False) | ternary('ldap://' + ad_realm | default(samba_realm) | default(ansible_domain) | lower,ldap_uri) }}"
# SHould Start TLS be used ?
mayan_ldap_start_tls: True
# Base of your LDAP tree. Eg DC=example,DC=org
mayan_ldap_base: "{{ ad_auth | default(False) | ternary('DC=' + ad_realm | default(samba_realm) | default(ansible_domain) | regex_replace('\\.',',DC='), ldap_base) }}"
# If your directory only allow authenticated searches, you can define it here
# mayan_ldap_bind_dn:
# mayan_ldap_bind_pass:
#
# If set, will restrict user search in these OU. Default is to search from the base
# Eg
# mayan_ldap_user_ou:
# - OU=People,DC=example,DC=org
# - OU=Presta,DC=example,DC=org
mayan_ldap_user_ou: []
# Filter to search for users
mayan_ldap_user_filter: "{{ ad_auth | default(False) | ternary('(sAMAccountName=%(user)s)','(uid=%(user)s)') }}"
# Mapping of LDAP attributes into Django attributes
mayan_ldap_user_attr_map:
username: "{{ ad_auth | default(False) | ternary('sAMAccountName','uid') }}"
first_name: givenName
last_name: sn
email: mail
# Same for groups
mayan_ldap_group_ou: []
# How are group represented in your directory.
# See https://django-auth-ldap.readthedocs.io/en/latest/groups.html for a list of valid values
mayan_ldap_group_type: "{{ ad_auth | default(False) | ternary('NestedActiveDirectoryGroupType','PosixGroupType') }}"
# LDAP filter to search for groups
mayan_ldap_group_filter: "{{ ad_auth | default(False) | ternary('(objectClass=group)','(objectClass=posixGroup)') }}"
# Define user flags based on group membership, for example :
#
# mayan_ldap_flags_by_group:
# is_active:
# - CN=Users,DC=example,DC=org
# is_staff:
# - CN=IT,OU=Groups,DC=example,DC=org
# is_superuser:
# - CN=Role_Infra_Admin,OU=Roles,DC=example,DC=org
# - CN=Domain Admins,OU=Groups,DC=example,DC=org
mayan_ldap_flags_by_group: {}
# If defined, will either require user to be part of one of those groups,
# or forbid access to membres of those groups
# mayan_ldap_require_group:
# - CN=Admins,OU=Groups,DC=example,DC=org
# - CN=Board,OU=Groups,DC=example,DC=org
#
# mayan_ldap_deny_group:
# - CN=Guests,OU=Groups,DC=example,DC=org
# Useful to debug LDAP related issues
mayan_ldap_debug: False
# Custom settings to set in the auth.py module
# Eg
# mayan_auth_custom_conf: |
# AUTH_LDAP_USER_FLAGS_BY_GROUP = {
# 'is_active': 'CN=Role_EDMS,OU=Roles,DC=example,DC=org',
# 'is_staff': 'CN=Role_Staff,OU=Roles,DC=example,DC=org',
# 'is_superuser': 'CN=Role_Infra_Admin,OU=Roles,DC=example,DC=org',
# }
# This is a list of indexes to expose as FUSE filesystem in {{ mayan_root_dir }}/fuse
# when running the pre-backup hook
mayan_index_as_fuse:
- creation_date

View File

@@ -0,0 +1,11 @@
---
- name: restart mayan-edms
service: name={{ item }} state=restarted
loop:
- mayan-edms-web
- mayan-edms-worker-fast
- mayan-edms-worker-medium
- mayan-edms-worker-slow
- mayan-edms-beat

View File

@@ -0,0 +1,17 @@
---
dependencies:
- role: mkdir
- role: repo_remi # for gnupg1
- role: mysql_server
when:
- mayan_db_engine == 'mysql'
- mayan_db_server == '127.0.0.1' or mayan_db_server == 'localhost'
- role: postgresql_server
when:
- mayan_db_engine == 'postgresql'
- mayan_db_server == '127.0.0.1' or mayan_db_server == 'localhost'
- role: redis_server
when: mayan_redis_url | urlsplit('hostname') == '127.0.0.1' or mayan_redis_url | urlsplit('hostname') == 'localhost'
- role: rabbitmq_server
when: mayan_amqp_url | urlsplit('hostname') == '127.0.0.1' or mayan_amqp_url | urlsplit('hostname') == 'localhost'

View File

@@ -0,0 +1,10 @@
---
- name: Compress previous version
command: tar cf {{ mayan_root_dir }}/archives/{{ mayan_current_version }}.tar.zst --use-compress-program=zstd ./
environment:
ZST_CLEVEL: 10
args:
chdir: "{{ mayan_root_dir }}/archives/{{ mayan_current_version }}"
warn: False
tags: mayan

View File

@@ -0,0 +1,56 @@
---
- name: Create the archive dir
file: path={{ mayan_root_dir }}/archives/{{ mayan_current_version }} state=directory
tags: mayan
- name: Stop sevices during upgrade
service: name={{ item }} state=stopped
loop:
- mayan-edms-web
- mayan-edms-worker-fast
- mayan-edms-worker-medium
- mayan-edms-worker-slow
- mayan-edms-beat
tags: mayan
- name: Archive previous version
synchronize:
src: "{{ mayan_root_dir }}/{{ item }}"
dest: "{{ mayan_root_dir }}/archives/{{ mayan_current_version }}/"
recursive: True
delete: True
loop:
- venv
- config
delegate_to: "{{ inventory_hostname }}"
tags: mayan
- name: Dump the database
mysql_db:
state: dump
name: "{{ mayan_db_name }}"
target: "{{ root_dir }}/archives/{{ mayan_current_version }}/{{ mayan_db_name }}.sql.gz"
login_host: "{{ mayan_db_server }}"
login_user: sqladmin
login_password: "{{ mysql_admin_pass }}"
quick: True
single_transaction: True
when: mayan_db_engine == 'mysql'
tags: mayan
- name: Dump the database
command: >
/usr/pgsql-14/bin/pg_dump
--clean
--create
--host={{ mayan_db_server | quote }}
--port={{ mayan_db_port | quote }}
--username=sqladmin {{ mayan_db_name | quote }}
--file="{{ mayan_root_dir }}/archives/{{ mayan_current_version }}/{{ mayan_db_name }}.sql"
environment:
- PGPASSWORD: "{{ pg_admin_pass }}"
when: mayan_db_engine == 'postgresql'
tags: mayan

View File

@@ -0,0 +1,7 @@
---
- name: Remove temp and obsolete files
file: path={{ item }} state=absent
loop:
- "{{ mayan_root_dir }}/archive"
tags: mayan

View File

@@ -0,0 +1,9 @@
---
- name: Deploy configuration
template: src={{ item.src }} dest={{ item.dest }} group={{ mayan_user }} mode=640
loop:
- src: env.j2
dest: "{{ mayan_root_dir }}/config/.env"
notify: restart mayan-edms
tags: mayan

View File

@@ -0,0 +1,28 @@
---
- name: Create directories
file:
path: "{{ item.path }}"
state: directory
owner: "{{ item.owner | default(omit) }}"
group: "{{ item.group | default(omit) }}"
mode: "{{ item.mode | default(omit) }}"
loop:
- path: "{{ mayan_root_dir }}/meta"
mode: 700
- path: "{{ mayan_root_dir }}/tmp"
mode: 700
owner: "{{ mayan_user }}"
- path: "{{ mayan_root_dir }}/data/mayan_settings/"
mode: 700
owner: "{{ mayan_user }}"
- path: "{{ mayan_root_dir }}/archives"
mode: 700
- path: "{{ mayan_root_dir }}/backup"
mode: 700
- path: "{{ mayan_root_dir }}/config"
group: "{{ mayan_user }}"
mode: 750
- path: "{{ mayan_root_dir }}/fuse"
mode: 700
- path: "{{ mayan_root_dir }}/input"
tags: mayan

View File

@@ -0,0 +1,35 @@
---
- fail: msg="mysql_admin_pass must be set"
when: mysql_admin_pass is not defined
tags: mayan
# Ensure we have recent enough MariaDB version
- fail: msg="Require MariaDB > 10"
when:
- mayan_db_server == 'localhost' or mayan_db_server == '127.0.0.1'
- mysql_mariadb_version == 'default'
tags: mayan
- import_tasks: ../includes/webapps_set_install_mode.yml
vars:
- root_dir: "{{ mayan_root_dir }}"
- version: "{{ mayan_version }}"
tags: mayan
- block:
- set_fact: mayan_install_mode={{ (install_mode == 'upgrade' and not mayan_manage_upgrade) | ternary('none',install_mode) }}
- set_fact: mayan_current_version={{ current_version | default('') }}
tags: mayan
# Create a random pass for the DB if needed
- block:
- import_tasks: ../includes/get_rand_pass.yml
vars:
- pass_file: "{{ mayan_root_dir }}/meta/ansible_dbpass"
- complex: False
- set_fact: mayan_db_pass={{ rand_pass }}
when: mayan_db_pass is not defined
tags: mayan

View File

@@ -0,0 +1,168 @@
---
- name: Install needed tools
yum:
name:
- git
- gcc
- openssl-devel
- libffi-devel
- openldap-devel
- libjpeg-turbo-devel
- libpng-devel
- libexif
- ghostscript
- gnupg1
- graphviz
- fuse-libs
- file-libs
- libreoffice
- poppler-utils
- sane-backends
- tesseract
- tesseract-langpack-fra
- python3-devel
- python3-pip
- python3-virtualenv
- python-setuptools
- rabbitmq-server
tags: mayan
- name: Install MySQL support
yum:
name:
- mysql-devel
- MySQL-python
when: mayan_db_engine == 'mysql'
tags: mayan
# WHen using upstream MariaDB repo, we have to install MariaDB-shared
- name: Install MariaDB shared libs
yum:
name:
- MariaDB-shared
when:
- mayan_db_engine == 'mysql'
- mysql_mariadb_version is defined and mysql_mariadb_version != 'default'
tags: mayan
- name: Install PostgreSQL support
yum:
name:
- postgresql-devel
- postgresql14
- python-psycopg2
when: mayan_db_engine == 'postgresql'
tags: mayan
- name: Wipe the venv on upgrades
file: path={{ mayan_root_dir }}/venv state=absent
when: mayan_install_mode=='upgrade'
tags: mayan
- name: Create the venv dir
file: path={{ mayan_root_dir }}/venv state=directory
tags: mayan
- name: Create the virtualenv
pip:
name:
- pip
- redis==3.5.3
- python-ldap
- django_auth_ldap
- "{{ (mayan_db_engine == 'mysql') | ternary('mysql','psycopg2==2.8.6') }}"
virtualenv: "{{ mayan_root_dir }}/venv"
virtualenv_command: /usr/bin/virtualenv-3
virtualenv_python: /usr/bin/python3
tags: mayan
- name: Install mayan-edms wrapper
template: src=mayan-edms.j2 dest=/usr/local/bin/mayan-edms mode=755
tags: mayan
- name: Install Mayan EDMS
pip:
name:
- mayan-edms=={{ mayan_version }}
virtualenv: "{{ mayan_root_dir }}/venv"
virtualenv_command: /usr/bin/virtualenv-3
virtualenv_python: /usr/bin/python3
tags: mayan
- when: mayan_db_engine == 'mysql'
block:
- import_tasks: ../includes/webapps_create_mysql_db.yml
vars:
- db_name: "{{ mayan_db_name }}"
- db_user: "{{ mayan_db_user }}"
- db_server: "{{ mayan_db_server }}"
- db_pass: "{{ mayan_db_pass }}"
tags: mayan
- when: mayan_db_engine == 'postgresql'
block:
- name: Create the PostgreSQL role
postgresql_user:
db: postgres
name: "{{ mayan_db_user }}"
password: "{{ mayan_db_pass }}"
login_host: "{{ mayan_db_server }}"
login_user: sqladmin
login_password: "{{ pg_admin_pass }}"
- name: Create the PostgreSQL database
postgresql_db:
name: "{{ mayan_db_name }}"
encoding: UTF-8
template: template0
owner: "{{ mayan_db_user }}"
login_host: "{{ mayan_db_server }}"
login_user: sqladmin
login_password: "{{ pg_admin_pass }}"
tags: mayan
- name: Initialize or upgrade database
command: >-
{{ mayan_root_dir }}/venv/bin/python
{{ mayan_root_dir }}/venv/bin/mayan-edms.py
{{ (mayan_install_mode == 'install') | ternary('initialsetup','performupgrade') }}
environment:
- MAYAN_MEDIA_ROOT: "{{ mayan_root_dir }}/data"
- MAYAN_DATABASE_ENGINE: django.db.backends.{{ (mayan_db_engine == 'mysql') | ternary('mysql','postgresql') }}
- MAYAN_DATABASE_NAME: "{{ mayan_db_name }}"
- MAYAN_DATABASE_PASSWORD: "{{ mayan_db_pass }}"
- MAYAN_DATABASE_USER: "{{ mayan_db_user }}"
- MAYAN_DATABASE_HOST: "{{ mayan_db_server }}"
when: mayan_install_mode != 'none'
tags: mayan
- name: Deploy systemd units
template: src={{ item }}.j2 dest=/etc/systemd/system/{{ item }}
loop:
- mayan-edms-web.service
- mayan-edms-worker-fast.service
- mayan-edms-worker-medium.service
- mayan-edms-worker-slow.service
- mayan-edms-beat.service
register: mayan_systemd_units
notify: restart mayan-edms
tags: mayan
- name: Reload systemd
systemd: daemon_reload=True
when: mayan_systemd_units.results | selectattr('changed', 'equalto', True) | list | length > 0
tags: mayan
- name: Install pre/post backup scripts
template: src={{ item }}_backup.sh.j2 dest=/etc/backup/{{ item }}.d/mayan_edms.sh mode=750
loop:
- pre
- post
tags: mayan
- name: Deploy auth configuration
template: src=auth.py.j2 dest={{ mayan_root_dir }}/data/mayan_settings/auth.py group={{ mayan_user }} mode=640
when: mayan_ldap_auth
notify: restart mayan-edms
tags: mayan

View File

@@ -0,0 +1,9 @@
---
- name: Handle Mayan EDMS port in the firewall
iptables_raw:
name: mayan_port
state: "{{ (mayan_src_ip | length > 0) | ternary('present','absent') }}"
rules: "-A INPUT -m state --state NEW -p tcp --dport {{ mayan_port }} -s {{ mayan_src_ip | join(',') }} -j ACCEPT"
tags: firewall,mayan

View File

@@ -0,0 +1,15 @@
---
- include: user.yml
- include: directories.yml
- include: facts.yml
- include: archive_pre.yml
when: mayan_install_mode == 'upgrade'
- include: install.yml
- include: conf.yml
- include: iptables.yml
when: iptables_manage | default(True)
- include: services.yml
- include: write_version.yml
- include: archive_post.yml
when: mayan_install_mode == 'upgrade'
- include: cleanup.yml

View File

@@ -0,0 +1,11 @@
---
- name: Start and enable services
service: name={{ item }} state=started enabled=True
loop:
- mayan-edms-web
- mayan-edms-worker-fast
- mayan-edms-worker-medium
- mayan-edms-worker-slow
- mayan-edms-beat
tags: mayan

View File

@@ -0,0 +1,6 @@
---
- name: Create mayan user account
user: name={{ mayan_user }} home={{ mayan_root_dir }} system=True
tags: mayan

View File

@@ -0,0 +1,5 @@
---
- name: Write current version
copy: content={{ mayan_version }} dest={{ mayan_root_dir }}/meta/ansible_version
tags: mayan

View File

@@ -0,0 +1,84 @@
import ldap
from django_auth_ldap.config import (
LDAPSearch, LDAPSearchUnion, LDAPGroupQuery, {{ mayan_ldap_group_type }}
)
from mayan.settings.production import *
ldap.set_option(ldap.OPT_DEBUG_LEVEL, {{ mayan_ldap_debug | ternary('1','0') }})
AUTH_LDAP_ALWAYS_UPDATE_USER = True
LDAP_USER_AUTO_CREATION = True
AUTH_LDAP_START_TLS = {{ mayan_ldap_start_tls | ternary('True','False') }}
{% if mayan_ldap_bind_dn is defined and mayan_ldap_bind_pass is defined %}
AUTH_LDAP_BIND_DN = '{{ mayan_ldap_bind_dn }}'
AUTH_LDAP_BIND_PASSWORD = '{{ mayan_ldap_bind_pass }}'
{% endif %}
LDAP_BASE_DN = '{{ mayan_ldap_base }}'
AUTH_LDAP_SERVER_URI = '{{ mayan_ldap_uri }}'
{% if mayan_ldap_user_ou | length > 0 %}
AUTH_LDAP_USER_SEARCH = LDAPSearchUnion(
{% for ou in mayan_ldap_user_ou %}
LDAPSearch(
'{{ ou }}', ldap.SCOPE_SUBTREE,
'{{ mayan_ldap_user_filter }}'
),
{% endfor %}
)
{% else %}
AUTH_LDAP_USER_SEARCH = LDAPSearch(
'{{ mayan_ldap_base }}', ldap.SCOPE_SUBTREE,
'{{ mayan_ldap_user_filter }}'
)
{% endif %}
AUTH_LDAP_USER_ATTR_MAP = {
{% for attr in mayan_ldap_user_attr_map.keys() %}
'{{ attr }}': '{{ mayan_ldap_user_attr_map[attr] }}',
{% endfor %}
}
{% if mayan_ldap_group_ou | length > 0 %}
AUTH_LDAP_GROUP_SEARCH = LDAPSearchUnion(
{% for ou in mayan_ldap_group_ou %}
LDAPSearch(
'{{ ou }}', ldap.SCOPE_SUBTREE,
'{{ mayan_ldap_group_filter }}'
),
{% endfor %}
)
{% else %}
AUTH_LDAP_GROUP_SEARCH = LDAPSearch(
'{{ mayan_ldap_base }}', ldap.SCOPE_SUBTREE,
'{{ mayan_ldap_group_filter }}'
)
{% endif %}
AUTH_LDAP_GROUP_TYPE = {{ mayan_ldap_group_type }}()
AUTH_LDAP_USER_FLAGS_BY_GROUP = {
{% for key in mayan_ldap_flags_by_group.keys() %}
{% if mayan_ldap_flags_by_group[key] | length > 0 %}
'{{ key }}': (
{% for group in mayan_ldap_flags_by_group[key] %}
LDAPGroupQuery('{{ group }}') {{ '|' if not loop.last }}
{% endfor %}
),
{% endif %}
{% endfor %}
}
AUTHENTICATION_BACKENDS = (
'django_auth_ldap.backend.LDAPBackend',
'django.contrib.auth.backends.ModelBackend'
)
AUTH_LDAP_MIRROR_GROUPS = True
{% if mayan_auth_custom_conf is defined %}
{{ mayan_auth_custom_conf }}
{% endif %}

View File

@@ -0,0 +1,18 @@
MAYAN_ALLOWED_HOSTS="['*']"
PYTHONPATH="{{ mayan_root_dir }}/data/mayan_settings"
DJANGO_SETTINGS_MODULE={{ mayan_ldap_auth | ternary('auth','mayan.settings.production') }}
MAYAN_MEDIA_ROOT="{{ mayan_root_dir }}/data"
MAYAN_CELERY_RESULT_BACKEND="{{ mayan_redis_url }}/{{ mayan_redis_db }}"
MAYAN_CELERY_BROKER_URL="{{ mayan_amqp_url }}"
MAYAN_DATABASE_ENGINE="django.db.backends.{{ (mayan_db_engine == 'mysql') | ternary('mysql','postgresql') }}"
MAYAN_DATABASE_NAME={{ mayan_db_name | quote }}
MAYAN_DATABASE_PASSWORD={{ mayan_db_pass | quote }}
MAYAN_DATABASE_USER={{ mayan_db_user | quote }}
MAYAN_DATABASE_HOST={{ mayan_db_server | quote }}
MAYAN_DEFAULT_FROM_EMAIL={{ mayan_from_mail | quote }}
MAYAN_DOCUMENTS_LANGUAGE={{ mayan_doc_lang }}
MAYAN_SECURE_PROXY_SSL_HEADER="('HTTP_X_FORWARDED_PROTO', 'https')"
MAYAN_SESSION_COOKIE_NAME="mayanedmssessionid"
MAYAN_STORAGE_TEMPORARY_DIRECTORY="{{ mayan_root_dir }}/tmp"
MAYAN_TIME_ZONE="{{ system_tz | default('UTC') }}"
MAYAN_USE_X_FORWARDED_HOST="true"

View File

@@ -0,0 +1,21 @@
[Unit]
Description=Mayan EDMS celery beat service
After=redis.service {{ (pg_version is defined and pg_version != 'default') | ternary('postgresql-' + pg_version | string,'postgresql') }}.service mysql.service mariadb.service
[Service]
User={{ mayan_user }}
WorkingDirectory={{ mayan_root_dir }}
EnvironmentFile={{ mayan_root_dir }}/config/.env
ExecStart={{ mayan_root_dir }}/venv/bin/celery beat -A mayan --pidfile= -l ERROR
PrivateTmp=yes
ProtectSystem=full
ProtectHome=yes
NoNewPrivileges=yes
MemoryLimit=1024M
SyslogIdentifier=mayan-edms-beat
Restart=on-failure
StartLimitInterval=0
RestartSec=30
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,22 @@
[Unit]
Description=Mayan EDMS web service
After=redis.service {{ (pg_version is defined and pg_version != 'default') | ternary('postgresql-' + pg_version | string,'postgresql') }}.service mysql.service mariadb.service
Wants=mayan-edms-worker-fast.service mayan-edms-worker-medium.service mayan-edms-worker-slow.service mayan-edms-beat.service
[Service]
User={{ mayan_user }}
WorkingDirectory={{ mayan_root_dir }}
EnvironmentFile={{ mayan_root_dir }}/config/.env
ExecStart={{ mayan_root_dir }}/venv/bin/gunicorn -w {{ mayan_web_workers }} mayan.wsgi --max-requests 500 --max-requests-jitter 50 --worker-class sync --bind 0.0.0.0:{{ mayan_port }} --timeout 120
PrivateTmp=yes
ProtectSystem=full
ProtectHome=yes
NoNewPrivileges=yes
MemoryLimit=1024M
SyslogIdentifier=mayan-edms-web
Restart=on-failure
StartLimitInterval=0
RestartSec=30
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,22 @@
[Unit]
Description=Mayan EDMS fast celery worker
After=redis.service {{ (pg_version is defined and pg_version != 'default') | ternary('postgresql-' + pg_version | string,'postgresql') }}.service mysql.service mariadb.service rabbitmq-server.service
[Service]
User={{ mayan_user }}
WorkingDirectory={{ mayan_root_dir }}/
EnvironmentFile={{ mayan_root_dir }}/config/.env
ExecStart={{ mayan_root_dir }}/venv/bin/celery worker -A mayan -Ofair -l ERROR -Q document_states_fast,converter,sources_fast -n mayan-worker-fast.%%h --concurrency={{ ansible_processor_vcpus + 1 }}
Nice=1
PrivateTmp=yes
ProtectSystem=full
ProtectHome=yes
NoNewPrivileges=yes
MemoryLimit=2048M
SyslogIdentifier=mayan-edms-worker-fast
Restart=on-failure
StartLimitInterval=0
RestartSec=30
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,22 @@
[Unit]
Description=Mayan EDMS medium celery worker
After=redis.service {{ (pg_version is defined and pg_version != 'default') | ternary('postgresql-' + pg_version | string,'postgresql') }}.service mysql.service mariadb.service
[Service]
User={{ mayan_user }}
WorkingDirectory={{ mayan_root_dir }}/
EnvironmentFile={{ mayan_root_dir }}/config/.env
ExecStart={{ mayan_root_dir }}/venv/bin/celery worker -A mayan -Ofair -l ERROR -Q statistics,default,checkouts_periodic,indexing,signatures,documents_periodic,uploads,documents,file_metadata,metadata,sources,sources_periodic -n mayan-worker-medium.%%h --concurrency=1
Nice=18
PrivateTmp=yes
ProtectSystem=full
ProtectHome=yes
NoNewPrivileges=yes
MemoryLimit=2048M
SyslogIdentifier=mayan-edms-worker-medium
Restart=on-failure
StartLimitInterval=0
RestartSec=30
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,22 @@
[Unit]
Description=Mayan EDMS slow celery worker
After=redis.service {{ (pg_version is defined and pg_version != 'default') | ternary('postgresql-' + pg_version | string,'postgresql') }}.service mysql.service mariadb.service
[Service]
User={{ mayan_user }}
WorkingDirectory={{ mayan_root_dir }}/
EnvironmentFile={{ mayan_root_dir }}/config/.env
ExecStart={{ mayan_root_dir }}/venv/bin/celery worker -A mayan -Ofair -l ERROR -Q tools,search,parsing,document_states,mailing,ocr,storage_periodic -n mayan-worker-slow.%%h --concurrency=1
Nice=19
PrivateTmp=yes
ProtectSystem=full
ProtectHome=yes
NoNewPrivileges=yes
MemoryLimit=2048M
SyslogIdentifier=mayan-edms-worker-slow
Restart=on-failure
StartLimitInterval=0
RestartSec=30
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,6 @@
#!/bin/bash -e
set -o allexport
. {{ mayan_root_dir }}/config/.env
set +o allexport
{{ mayan_root_dir }}/venv/bin/python {{ mayan_root_dir }}/venv/bin/mayan-edms.py $@

View File

@@ -0,0 +1,7 @@
#!/bin/bash -e
rm -f {{ mayan_root_dir }}/backup/*
{% for index in mayan_index_as_fuse %}
umount {{ mayan_root_dir }}/fuse/{{ index }}
rmdir {{ mayan_root_dir }}/fuse/{{ index }}
{% endfor %}

View File

@@ -0,0 +1,29 @@
#!/bin/sh
set -eo pipefail
{% if mayan_db_engine == 'mysql' %}
/usr/bin/mysqldump --user={{ mayan_db_user | quote }} \
--password={{ mayan_db_pass | quote }} \
--host={{ mayan_db_server | quote }} \
--quick --single-transaction \
--add-drop-table {{ mayan_db_name | quote }} | zstd -c > {{ mayan_root_dir }}/backup/{{ mayan_db_name }}.sql.zst
{% else %}
PGPASSWORD={{ mayan_db_pass | quote }} /usr/pgsql-14/bin/pg_dump \
--clean \
--create \
--username={{ mayan_db_user | quote }} \
--host={{ mayan_db_server | quote }} \
{{ mayan_db_name | quote }} | \
zstd -c > "{{ mayan_root_dir }}/backup/{{ mayan_db_name }}.sql.zst"
{% endif %}
# Use FUSE to export indexes as a file tree
set -o allexport
. /opt/mayan-edms/config/.env
set +o allexport
{% for index in mayan_index_as_fuse %}
mkdir -p {{ mayan_root_dir }}/fuse/{{ index }}
{{ mayan_root_dir }}/venv/bin/python {{ mayan_root_dir }}/venv/bin/mayan-edms.py mountindex --background {{ index }} {{ mayan_root_dir }}/fuse/{{ index }}
{% endfor %}

View File

@@ -0,0 +1,79 @@
---
# List of file shares
nas_shares: []
# nas_shares:
# - name: tools
# description: IT maintenance tools
# path: /opt/shares/tools
# acl:
# read_groups:
# - 'Domain Users'
# - 'Domain Guests'
# write_groups:
# - 'Domain Admins'
# - 'Staff'
# read_users: []
# write_users: []
# protocols:
# smb:
# enabled: True
# browseable: True
# guest_ok: False
# nt_acl: True
# rsync:
# enabled: True
# read_only: True
# users:
# dani: s3cr3t.
# rv: p455phrAz
nas_root_dir: /opt/nas
nas_share_homes_defaults:
description: Répertoire personnel
recycle_bin:
enabled: True
dir: Corbeille
protocols:
smb:
enabled: True
full_audit: True
nas_share_homes_extra: {}
nas_share_homes: "{{ nas_share_homes_defaults | combine(nas_share_homes_extra) }}"
nas_default_share:
description: NAS share
name: share
manual_permissions: False
acl:
read_groups: []
write_groups: ['domain admins']
read_users: []
write_users: []
recycle_bin:
enabled: True
dir: Corbeille
protocols:
smb:
enabled: False
browseable: True
guest_ok: False
full_audit: True
nt_acl: False
rsync:
enabled: False
read_only: True
nfs:
enabled: False
root_squash: True
http:
enabled: False
indexes: False
public: False
force_ssl: True
webdav: False
nas_ad_http_auth:
ldap_url: ldap://
bind_dn: XXX
bind_pass: XXX

View File

@@ -0,0 +1,22 @@
#!/bin/bash
USER=$1
if [ -z $USER ]; then
echo "Need to get user as first argument"
exit 1
fi
getent passwd $USER >/dev/null 2>&1
if [ $? -ne 0 ]; then
echo "User $USER not found"
exit 1
fi
HOME=$(eval echo ~$USER)
if [ ! -d $HOME ]; then
echo "Creating $USER home directory ($HOME)"
umask 022
mkdir -p $HOME
GROUP=$(id -gn $USER)
chown $USER:"$GROUP" $HOME
chmod 700 $HOME
restorecon -R $HOME
fi

View File

@@ -0,0 +1,4 @@
---
- name: reload nfs
command: exportfs -ra

View File

@@ -0,0 +1,6 @@
---
dependencies:
- role: samba
- role: rsync_server
- role: nfs_server
- role: httpd_front

View File

@@ -0,0 +1,104 @@
---
- name: Build config for shares
set_fact: nas_shares_conf={{ nas_shares_conf | default([]) + [nas_default_share | combine(item,recursive=True)] }}
with_items: "{{ nas_shares }}"
tags: nas
- set_fact: nas_shares={{ nas_shares_conf | default([]) }}
tags: nas
- name: Install needed packages
yum:
name:
- rssh
tags: nas
- name: Allow every user to use rssh
file: path=/bin/rssh mode=755
tags: nas
- name: Create directories
file: path={{ nas_root_dir }}/{{ item[1] }}/{{ item[0].name }} state=directory
with_nested:
- "{{ nas_shares }}"
- [data,meta]
tags: nas
- name: Create rsync system user
user:
name: rsync
system: True
shell: /sbin/nologin
tags: nas
- name: Deploy samba shares config
template: src=smb.conf.j2 dest=/etc/samba/smb.conf.d/shares.conf
notify: reload samba
tags: nas
- name: Deploy NFS exports
template: src=exports.j2 dest=/etc/exports.d/shares.exports
notify: reload nfs
tags: nas
- name: Deploy rsyncd shares config
template: src=rsyncd.conf.j2 dest=/etc/rsyncd.conf.d/shares.conf
tags: nas
- name: Deploy rsync auth files
template: src=rsync.secrets.j2 dest={{ nas_root_dir }}/meta/{{ item.name }}/rsync.secrets owner=root group=root mode=600
with_items: "{{ nas_shares }}"
tags: nas
- name: Deploy httpd conf
template: src={{ item.src }} dest={{ item.dest }} mode={{ item.mode | default(omit) }}
loop:
- src: httpd.conf.j2
dest: /etc/httpd/ansible_conf.d/50-shares.conf
mode: 640
- src: mod_dav.conf.j2
dest: /etc/httpd/ansible_conf.modules.d/30-mod_dav.conf
- src: mod_authnz_external.conf.j2
dest: /etc/httpd/ansible_conf.modules.d/30-mod_authnz_external.conf
notify:
- reload httpd
tags: nas
- name: Allow http to use PAM auth
seboolean: name=httpd_mod_auth_pam state=True persistent=True
when: ansible_selinux.status == 'enabled'
tags: nas
- name: Deploy setfacl script
template: src=setfacl.sh.j2 dest={{ nas_root_dir }}/meta/{{ item.name }}/setfacl.sh mode=755
with_items: "{{ nas_shares }}"
register: nas_acl
tags: nas
- name: Reset acls
command: "{{ nas_root_dir }}/meta/{{ item.item.name }}/setfacl.sh"
when: item.changed
with_items: "{{ nas_acl.results }}"
tags: nas
- name: Set SELinux content
sefcontext:
target: "{{ nas_root_dir }}/data(/.*)?"
setype: public_content_rw_t
state: present
when: ansible_selinux.status == 'enabled'
tags: nas
- name: Set SEbool
seboolean: name={{ item }} state=True persistent=True
with_items:
- samba_enable_home_dirs
- samba_create_home_dirs
- samba_export_all_rw
tags: nas
- name: Deploy scripts
copy: src={{ item }} dest=/var/lib/samba/scripts/{{ item }}
with_items:
- mkhomedir
tags: nas

View File

@@ -0,0 +1,7 @@
{% for share in nas_shares %}
{% if share.protocols.nfs.enabled %}
{{ share.path | default(nas_root_dir + '/data/' + share.name) }} *(rw,{{ share.protocols.nfs.root_squash | ternary('','no_') }}root_squash)
{% else %}
# NFS not enabled for share {{ share.name }}
{% endif %}
{% endfor %}

View File

@@ -0,0 +1,51 @@
{% for share in nas_shares %}
{% if share.protocols.http.enabled %}
Alias /{{ share.name }} {{ share.path | default(nas_root_dir + '/data/' + share.name) }}
RewriteEngine On
{% if share.protocols.http.force_ssl %}
RewriteCond %{HTTPS} =off
RewriteRule ^/{{ share.name }}(/.*|$) https://%{HTTP_HOST}/{{ share.name }}$1
{% endif %}
<Directory {{ share.path | default(nas_root_dir + '/data/' + share.name) }}>
Options None
Options +FollowSymlinks
{% if share.protocols.http.force_ssl %}
SSLRequireSSL On
{% endif %}
{% if share.protocols.http.indexes %}
Options +Indexes
{% endif %}
{% if share.protocols.http.webdav %}
Dav On
{% endif %}
{% if not share.protocols.http.public %}
AuthType Basic
AuthName "Authenicated zone"
AuthBasicProvider external
AuthExternal pwauth
# Read only access
<Limit GET PROPFIND OPTIONS LOCK UNLOCK REPORT>
{% for user in share.acl.read_users %}
Require user {{ user }}
{% endfor %}
{% for group in share.acl.read_groups %}
Require unix-group {{ group }}
{% endfor %}
</Limit>
# Write access through webdav always requires authentication
<LimitExcept GET PROPFIND OPTIONS LOCK UNLOCK REPORT>
{% for user in share.acl.write_users %}
Require user {{ user }}
{% endfor %}
{% for group in share.acl.write_groups %}
Require unix-group {{ group }}
{% endfor %}
</LimitExcept>
{% endif %}
</Directory>
{% endif %}
{% endfor %}

View File

@@ -0,0 +1,3 @@
LoadModule authnz_external_module modules/mod_authnz_external.so
AddExternalAuth pwauth /usr/sbin/pwauth
SetExternalAuthMethod pwauth pipe

View File

@@ -0,0 +1,2 @@
LoadModule dav_module modules/mod_dav.so
LoadModule dav_fs_module modules/mod_dav_fs.so

View File

@@ -0,0 +1,6 @@
{% if item.protocols.rsync.enabled and item.protocols.rsync.users is defined and item.protocols.rsync.users.keys() | list | length > 0 %}
{% for user in item.protocols.rsync.users.keys() | list %}
{{ user }}:{{ item.protocols.rsync.users[user] }}
{% endfor %}
{% endif %}

View File

@@ -0,0 +1,18 @@
{% for share in nas_shares %}
{% if share.protocols.rsync.enabled %}
[{{ share.name }}]
path = {{ share.path | default(nas_root_dir + '/data/' + share.name) }}
comment = {{ share.description }}
uid = rsync
gid = rsync
read only = {{ share.protocols.rsync.read_only | ternary('yes','no') }}
{% if share.protocols.rsync.users is defined and share.protocols.rsync.users.keys() | list | length > 0 %}
auth users = {{ share.protocols.rsync.users.keys() | list | join(' ') }}
secrets file = {{ nas_root_dir }}/meta/{{ share.name }}/rsync.secrets
{% endif %}
{% else %}
# Rsync access is disabled for {{ share.name }}
{% endif %}
{% endfor %}

View File

@@ -0,0 +1,42 @@
#!/bin/bash -e
WRITE_USERS=''
READ_USERS=''
WRITE_GROUPS=''
READ_GROUPS=''
{% if item.acl.read_users | length > 0 %}
for U in '{{ item.acl.read_users | join("' '") }}'; do
getent passwd "$U" > /dev/null 2>&1 && READ_USERS=$READ_USERS",u:$U:rX,d:u:$U:rX"
done
{% endif %}
{% if item.acl.write_users | length > 0 %}
for U in '{{ item.acl.write_users | join("' '") }}'; do
getent passwd "$U" > /dev/null 2>&1 && WRITE_USERS=$WRITE_USERS",u:$U:rwX,d:u:$U:rwX"
done
{% endif %}
{% if item.acl.read_groups | length > 0 %}
for G in '{{ item.acl.read_groups | join("' '") }}'; do
getent group "$G" > /dev/null 2>&1 && READ_GROUPS=$READ_GROUPS",g:$G:rX,d:g:$G:rX"
done
{% endif %}
{% if item.acl.write_groups | length > 0 %}
for G in '{{ item.acl.write_groups | join("' '") }}'; do
getent group "$G" > /dev/null 2>&1 && WRITE_GROUPS=$WRITE_GROUPS",g:$G:rwX,d:g:$G:rwX"
done
{% endif %}
chmod 770 {{ item.path | default(nas_root_dir + '/data/' + item.name) }}
chmod 700 {{ item.path | default(nas_root_dir + '/meta/' + item.name) }}
chown root:root {{ item.path | default(nas_root_dir + '/data/' + item.name) }}
{% if not item.protocols.smb.nt_acl and not item.manual_permissions %}
setfacl -R --remove-all --remove-default --physical {{ item.path | default(nas_root_dir + '/data/' + item.name) }}
setfacl -R --remove-all --remove-default --physical {{ item.path | default(nas_root_dir + '/data/' + item.name) }}
setfacl -R --physical -m g::---"$READ_USERS$WRITE_USERS$READ_GROUPS$WRITE_GROUPS" -- {{ item.path | default(nas_root_dir + '/data/' + item.name) }}
{% if item.protocols.rsync.enabled %}
setfacl -R --physical -m u:rsync:{{ item.protocols.rsync.read_only | ternary('rX','rwX') }},d:u:rsync:{{ item.protocols.rsync.read_only | ternary('rX','rwX') }} -- {{ item.path | default(nas_root_dir + '/data/' + item.name) }}
{% endif %}
{% endif %}
setfacl -R -m mask::rwX,d:mask:rwX -- {{ item.path | default(nas_root_dir + '/data/' + item.name) }}
{% if ansible_selinux.status == 'enabled' %}
restorecon -R {{ item.path | default(nas_root_dir + '/data/' + item.name) }}
{% endif %}

View File

@@ -0,0 +1,56 @@
{% if nas_share_homes.protocols.smb.enabled %}
[homes]
comment = {{ nas_share_homes.description }}
browseable = no
guest ok = no
read only = no
writable = yes
printable = no
root preexec = /var/lib/samba/scripts/mkhomedir %u
vfs objects = {{ nas_share_homes.recycle_bin.enabled | ternary('recycle','') }} {{ nas_share_homes.protocols.smb.full_audit | ternary('full_audit','') }}
{% if nas_share_homes.recycle_bin.enabled %}
recycle:exclude_dir = tmp,temp,cache
recycle:repository = {{ nas_share_homes.recycle_bin.dir }}
recycle:versions = no
recycle:keeptree = yes
recycle:touch = yes
recycle:exclude = *.tmp,*.temp,*.o,*.obj,~$*
{% endif %}
{% if nas_share_homes.protocols.smb.full_audit %}
full_audit:success=mkdir rmdir open opendir close closedir rename unlink
full_audit:failure=mkdir rmdir open opendir close closedir rename unlink connect disconnect
full_audit:prefix=%u|%D|%I|%M|%S
{% endif %}
{% endif %}
{% if nas_shares | length < 1 %}
# No share configured
{% else %}
{% for share in nas_shares %}
{% if share.protocols.smb.enabled %}
[{{ share.name }}]
comment = {{ share.description | default(share.name) }}
readonly = no
path = {{ share.path | default(nas_root_dir + '/data/' + share.name) }}
browseable = {{ share.protocols.smb.browseable | ternary('yes','no') }}
inherit acls = yes
guest ok = {{ share.protocols.smb.guest_ok | ternary('yes','no') }}
vfs objects = {{ share.recycle_bin.enabled | ternary('recycle','') }} {{ share.protocols.smb.full_audit | ternary('full_audit','') }} {{ share.protocols.smb.nt_acl | ternary('nfs4acl_xattr','') }}
{% if share.recycle_bin.enabled %}
recycle:repository = {{ share.recycle_bin.dir }}
recycle:versions = no
recycle:keeptree = no
recycle:touch = yes
recycle:exclude = *.tmp,*.temp,*.o,*.obj,~$*
{% endif %}
{% if share.protocols.smb.full_audit %}
full_audit:success=mkdir rmdir open opendir close closedir rename unlink
full_audit:failure=mkdir rmdir open opendir close closedir rename unlink connect disconnect
full_audit:prefix=%u|%D|%I|%M|%S
{% endif %}
{% endif %}
{% endfor %}
{% endif %}

View File

@@ -0,0 +1,18 @@
---
odoo_root_dir: /opt/odoo
odoo_version: 11.0
odoo_build: 20180301
odoo_manage_upgrade: True
odoo_archive_url: https://nightly.odoo.com/11.0/nightly/src/odoo_{{ odoo_version }}.{{ odoo_build }}.tar.gz
odoo_user: odoo
odoo_db_server: "{{ pg_server | default('localhost') }}"
odoo_db_name: odoo
odoo_db_user: odoo
# odoo_db_pass: secret
odoo_workers: 4
odoo_src_ip: []
odoo_xmlrpc_port: 8069
odoo_longpolling_port: 8072
odoo_ports: [ "{{ odoo_xmlrpc_port }}","{{ odoo_longpolling_port }}" ]
odoo_email: "{{ system_admin_email | default('admin' + ansible_domain ) }}"

View File

@@ -0,0 +1,4 @@
---
- name: restart odoo-server
service: name=odoo-server state=restarted

View File

@@ -0,0 +1,4 @@
---
dependencies:
- role: repo_scl

View File

@@ -0,0 +1,264 @@
---
- name: Set default install mode to none
set_fact: odoo_install_mode="none"
tags: odoo
- name: Check if odoo is installed
stat: path={{ odoo_root_dir }}/meta/ansible_version
register: odoo_version_file
tags: odoo
- name: Check installed version
command: cat {{ odoo_root_dir }}/meta/ansible_version
register: odoo_current_version
changed_when: False
when: odoo_version_file.stat.exists
tags: odoo
- name: Set install mode to install
set_fact: odoo_install_mode='install'
when: not odoo_version_file.stat.exists
tags: odoo
- name: Set install mode to upgrade
set_fact: odoo_install_mode='upgrade'
when:
- odoo_version_file.stat.exists
- odoo_current_version is defined
- odoo_current_version.stdout != odoo_version | string + '-' + odoo_build | string
- odoo_manage_upgrade
tags: odoo
- name: Install dependencies
yum:
name:
- rh-python36-python-virtualenv
- rh-python36-python-pip
- gcc
- libxml2-devel
- libxslt-devel
- openldap-devel
- nodejs-less
- wkhtmltopdf
- python-psycopg2
- postgresql
tags: odoo
- name: Create user account for odoo
user:
name: odoo
system: True
shell: /sbin/nologin
home: "{{ odoo_root_dir }}"
tags: odoo
- name: Create directories
file: path={{ item.path }} state=directory owner={{ item.owner | default(omit) }} group={{ item.group | default(omit) }} mode={{ item.mode | default(omit) }}
with_items:
- path: "{{ odoo_root_dir }}"
owner: "{{ odoo_user }}"
mode: 700
- path: "{{ odoo_root_dir }}/tmp"
- path: "{{ odoo_root_dir }}/meta"
mode: 700
- path: "{{ odoo_root_dir }}/etc"
group: "{{ odoo_user }}"
mode: 750
- path: "{{ odoo_root_dir }}/app"
- path: "{{ odoo_root_dir }}/db_dumps"
mode: 700
- path: "{{ odoo_root_dir }}/data"
group: "{{ odoo_user }}"
mode: 770
tags: odoo
- name: Fetch odoo sources
get_url:
url: "{{ odoo_archive_url }}"
dest: "{{ odoo_root_dir }}/tmp"
when: odoo_install_mode != "none"
tags: odoo
- name: Extract odoo archive
unarchive:
src: "{{ odoo_root_dir }}/tmp/odoo_{{ odoo_version }}.{{ odoo_build }}.tar.gz"
dest: "{{ odoo_root_dir }}/tmp"
remote_src: yes
when: odoo_install_mode != "none"
tags: odoo
- name: Create archive dir
file: path={{ odoo_root_dir }}/archives/{{ odoo_current_version.stdout }} state=directory mode=700
when: odoo_install_mode == "upgrade"
tags: odoo
- name: Stop the server during upgrade
service: name=odoo-server state=stopped
when: odoo_install_mode == "upgrade"
tags: odoo
- name: Archive current Odoo install
synchronize:
src: "{{ odoo_root_dir }}/app"
dest: "{{ odoo_root_dir }}/archives/{{ odoo_current_version.stdout }}/app"
recursive: True
delegate_to: "{{ inventory_hostname }}"
when: odoo_install_mode == "upgrade"
tags: odoo
- name: Dump database
postgresql_db:
name: "{{ odoo_db_name }}"
state: dump
login_host: "{{ odoo_db_server }}"
login_user: sqladmin
login_password: "{{ pg_admin_pass }}"
target: "{{ odoo_root_dir }}/archives/{{ odoo_current_version.stdout }}/{{ odoo_db_name }}.sql.gz"
when: odoo_install_mode == "upgrade"
tags: odoo
- name: Compress previous version
command: tar cf {{ odoo_root_dir }}/archives/{{ odoo_current_version.stdout }}.txz ./
environment:
XZ_OPT: -T0
args:
chdir: "{{ odoo_root_dir }}/archives/{{ odoo_current_version.stdout }}"
when: odoo_install_mode == 'upgrade'
tags: odoo
- name: Remove the archive directory
file: path={{ odoo_root_dir }}/archives/{{ odoo_current_version.stdout }} state=absent
tags: odoo
- name: Create the virtualenv
pip:
state: latest
virtualenv: "{{ odoo_root_dir }}"
virtualenv_command: /opt/rh/rh-python36/root/usr/bin/virtualenv
requirements: "{{ odoo_root_dir }}/tmp/odoo-{{ odoo_version }}.post{{ odoo_build }}/requirements.txt"
when: odoo_install_mode != "none"
tags: odoo
- name: Install additional python modules
pip:
name: "{{ item }}"
state: latest
virtualenv: "{{ odoo_root_dir }}"
virtualenv_command: /opt/rh/rh-python36/root/usr/bin/virtualenv
with_items:
- phonenumbers
tags: odoo
- name: Copy odoo application to its final directory
synchronize:
src: "{{ odoo_root_dir }}/tmp/odoo-{{ odoo_version }}.post{{ odoo_build }}/"
dest: "{{ odoo_root_dir }}/app/"
recursive: True
delete: True
when: odoo_install_mode != "none"
delegate_to: "{{ inventory_hostname }}"
tags: odoo
- name: Install odoo
command: "{{ odoo_root_dir }}/bin/python3 {{ odoo_root_dir }}/app/setup.py install"
args:
chdir: "{{ odoo_root_dir }}/app/"
when: odoo_install_mode != "none"
tags: odoo
- name: Generate a random pass for the database
shell: openssl rand -base64 45 > {{ odoo_root_dir }}/meta/ansible_dbpass
args:
creates: "{{ odoo_root_dir }}/meta/ansible_dbpass"
when: odoo_db_pass is not defined
tags: odoo
- name: Read database password
command: cat {{ odoo_root_dir }}/meta/ansible_dbpass
register: odoo_rand_pass
when: odoo_db_pass is not defined
changed_when: False
tags: odoo
- name: Set database pass
set_fact: odoo_db_pass={{ odoo_rand_pass.stdout }}
when: odoo_db_pass is not defined
tags: odoo
- name: Create the PostgreSQL role
postgresql_user:
db: postgres
name: "{{ odoo_db_user }}"
password: "{{ odoo_db_pass }}"
login_host: "{{ odoo_db_server }}"
login_user: sqladmin
login_password: "{{ pg_admin_pass }}"
tags: odoo
- name: Create the PostgreSQL database
postgresql_db:
name: "{{ odoo_db_name }}"
encoding: UTF-8
lc_collate: C
lc_ctype: C
template: template0
owner: "{{ odoo_db_user }}"
login_host: "{{ odoo_db_server }}"
login_user: sqladmin
login_password: "{{ pg_admin_pass }}"
tags: odoo
- name: Handle odoo ports
iptables_raw:
name: odoo_ports
state: "{{ (odoo_src_ip | length > 0) | ternary('present','absent') }}"
rules: "-A INPUT -m state --state NEW -p tcp -m multiport --dports {{ odoo_ports | join(',') }} -s {{ odoo_src_ip | join(',') }} -j ACCEPT"
tags: [firewall,odoo]
- name: Deploy server configuration
template: src=odoo-server.conf.j2 dest={{ odoo_root_dir }}/etc/odoo-server.conf group={{ odoo_user }} mode=640
notify: restart odoo-server
tags: odoo
- name: Deploy odoo service file
template: src=odoo-server.service.j2 dest=/etc/systemd/system/odoo-server.service
register: odoo_unit
notify: restart odoo-server
tags: odoo
- name: Reload systemd
command: systemctl daemon-reload
when: odoo_unit.changed
tags: odoo
- name: Update modules
command: "{{ odoo_root_dir }}/bin/python3 {{ odoo_root_dir }}/app/setup/odoo -u all --stop-after-init --syslog"
become_user: "{{ odoo_user }}"
when: odoo_install_mode == "upgrade"
tags: odoo
- name: Write version
copy: content={{ odoo_version }}-{{ odoo_build }} dest={{ odoo_root_dir }}/meta/ansible_version
when: odoo_install_mode != "none"
tags: odoo
- name: Start the service
service: name=odoo-server state=started enabled=True
tags: odoo
- name: Install pre and post backup scripts
template: src={{ item.src }} dest=/etc/backup/{{ item.dest }}/odoo.sh mode=750
with_items:
- src: pre-backup.sh.j2
dest: pre.d
- src: post-backup.sh.j2
dest: post.d
tags: odoo
- name: Remove temp files
file: path={{ item }} state=absent
with_items:
- "{{ odoo_root_dir }}/tmp/odoo_{{ odoo_version }}.{{ odoo_build }}.tar.gz"
- "{{ odoo_root_dir }}/tmp/odoo-{{ odoo_version }}.post{{ odoo_build }}"
tags: odoo

View File

@@ -0,0 +1,11 @@
[options]
db_name = {{ odoo_db_name }}
db_host = {{ odoo_db_server }}
db_user = {{ odoo_db_user }}
db_password = {{ odoo_db_pass }}
addons_path = {{ odoo_root_dir }}/app/odoo/addons
workers = {{ odoo_workers }}
http_port = {{ odoo_xmlrpc_port }}
longpolling_port = {{ odoo_longpolling_port }}
email_from = {{ odoo_email }}
smtp_server = localhost

View File

@@ -0,0 +1,17 @@
[Unit]
Description=Odoo Server
After=network.service
[Service]
Type=simple
User={{ odoo_user }}
Group={{ odoo_user }}
ExecStart={{ odoo_root_dir }}/bin/python3 {{ odoo_root_dir }}/app/setup/odoo -c {{ odoo_root_dir }}/etc/odoo-server.conf --proxy-mode --no-database-list --without-demo=ALL --data-dir={{ odoo_root_dir }}/data
PrivateTmp=yes
PrivateDevices=yes
ProtectSystem=full
ProtectHome=yes
NoNewPrivileges=yes
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,3 @@
#!/bin/sh
rm -f {{ odoo_root_dir }}/db_dumps/*

View File

@@ -0,0 +1,11 @@
#!/bin/sh
set -eo pipefail
PGPASSWORD={{ odoo_db_pass | quote }} /usr/bin/pg_dump \
--format=custom \
--clean \
--username={{ odoo_db_user }} \
--host={{ odoo_db_server }} \
--file={{ odoo_root_dir }}/db_dumps/{{ odoo_db_name }}.sqlc \
{{ odoo_db_name }}

Some files were not shown because too many files have changed in this diff Show More