Merge branch 'unstable' into stable

This commit is contained in:
Jérémy Lecour 2017-11-19 23:43:11 +01:00 committed by Jérémy Lecour
commit 9038beefd1
261 changed files with 5754 additions and 1001 deletions

View file

@ -1,3 +0,0 @@
---
admin_users: {}
admin_users_group: adm

View file

@ -1,11 +0,0 @@
---
- include: user.yml
- include: profile.yml
- include: ssh.yml
- include: sudo.yml
- meta: flush_handlers

View file

@ -1,16 +0,0 @@
---
- fail:
msg: only compatible with Debian >= 8
when:
- ansible_distribution != "Debian" or ansible_distribution_major_version | version_compare('8', '<')
- debug:
msg: "Warning: empty 'admin_users' variable, tasks will be skipped!"
when: admin_users == {}
- include: admin_user.yml
vars:
user: "{{ item.value }}"
with_dict: "{{ admin_users }}"
when: admin_users != {}

View file

@ -1,15 +0,0 @@
---
- name: is evomaintenance installed?
stat:
path: "/usr/share/scripts/evomaintenance.sh"
register: evomaintenance_script
check_mode: no
- name: "Add evomaintenance trap for '{{ user.name }}'"
lineinfile:
state: present
dest: '/home/{{ user.name }}/.profile'
insertafter: EOF
line: 'trap "sudo /usr/share/scripts/evomaintenance.sh" 0'
when: evomaintenance_script.stat.exists

View file

@ -1,48 +0,0 @@
---
- name: "Verify Evolinux sudoers file presence (jessie)"
template:
src: sudoers_jessie.j2
dest: /etc/sudoers.d/evolinux
force: no
validate: '/usr/sbin/visudo -cf %s'
register: copy_sudoers_evolinux
when: ansible_distribution_release == "jessie"
- name: "Verify Evolinux sudoers file presence (Debian 9 or later)"
template:
src: sudoers_stretch.j2
dest: /etc/sudoers.d/evolinux
force: no
validate: '/usr/sbin/visudo -cf %s'
register: copy_sudoers_evolinux
when: ansible_distribution_major_version | version_compare('9', '>=')
- name: "Verify Evolinux sudoers file permissions"
file:
path: /etc/sudoers.d/evolinux
mode: "0440"
state: file
- name: "Add user in sudoers file for '{{ user.name }}' (jessie)"
replace:
dest: /etc/sudoers.d/evolinux
regexp: '^(User_Alias\s+ADMINS\s+=((?!{{ user.name }}).)*)$'
replace: '\1,{{ user.name }}'
validate: '/usr/sbin/visudo -cf %s'
when:
- ansible_distribution_release == "jessie"
- not copy_sudoers_evolinux.changed
- name: "Create evolinux-sudo group (Debian 9 or later)"
group:
name: evolinux-sudo
system: yes
when: ansible_distribution_major_version | version_compare('9', '>=')
- name: "Add user to evolinux-sudo group (Debian 9 or later)"
user:
name: '{{ user.name }}'
groups: 'evolinux-sudo'
append: yes
when: ansible_distribution_major_version | version_compare('9', '>=')

5
amavis/handlers/main.yml Normal file
View file

@ -0,0 +1,5 @@
---
- name: restart amavis
service:
name: amavis
state: restarted

19
amavis/tasks/main.yml Normal file
View file

@ -0,0 +1,19 @@
---
- name: install Amavis
apt:
name: "{{ item }}"
state: present
with_items:
- postgrey
- amavisd-new
tags:
- amavis
- name: configure Amavis
template:
src: amavis.conf.j2
dest: /etc/amavis/conf.d/49-evolinux-defaults.conf
mode: "0644"
notify: restart amavis
tags:
- amavis

View file

@ -0,0 +1,57 @@
use strict;
## Liste des domaines considérés comme locaux
#@local_domains_acl = qw(.);
@local_domains_acl = (".example.net","example.com");
# On customise la ligne ajoutée dans les entêtes
$X_HEADER_LINE = "by Amavis at $mydomain";
# On precise les FROM pour etre (bugs dans certaines version d'Amavis)
$mailfrom_notify_admin = "postmaster\@$mydomain";
$mailfrom_notify_recip = "postmaster\@$mydomain";
$mailfrom_notify_spamadmin = "postmaster\@$mydomain";
# Notifications de fichiers bannis / virus
$virus_admin = "postmaster\@$mydomain";
# Ne pas recevoir des notifications pour les mails UNCHECKED
delete $admin_maps_by_ccat{&CC_UNCHECKED};
# Que faire avec les messages détectés
$final_virus_destiny = D_DISCARD;
$final_banned_destiny = D_BOUNCE;
$final_spam_destiny = D_BOUNCE;
$final_bad_header_destiny = D_PASS;
# Pour recevoir des bounces (mails originals) des fichiers bloqués / virus
#$banned_quarantine_to = "banned\@$mydomain";
#$virus_quarantine_to = "virus\@$mydomain";
# Note tueuse
$sa_tag2_level_deflt = 6.31;
# Pour un comportement "normal" de SA
$sa_tag_level_deflt = -1999;
$sa_kill_level_deflt = 1999;
$sa_dsn_cutoff_level = -99;
$sa_spam_subject_tag = '[SPAM]';
# log
$log_level = 2;
# En fonction besoin/ressources, on a juste le nbre de process
$max_servers = 2;
$enable_ldap = 1;
$default_ldap = {
hostname => '127.0.0.1', tls => 0,
base => '{{ ldap_suffix }}', scope => 'sub',
query_filter => '(&(mailacceptinggeneralid=%m)(isActive=TRUE))'
};
# Activer l'antivirus et antivirus
@bypass_virus_checks_maps = (
\%bypass_virus_checks, \@bypass_virus_checks_acl, \$bypass_virus_checks_re);
@bypass_spam_checks_maps = (
\%bypass_spam_checks, \@bypass_spam_checks_acl, \$bypass_spam_checks_re);
1; # ensure a defined return

60
amazon-ec2/README Normal file
View file

@ -0,0 +1,60 @@
# amazon-ec2
Manage Amazon EC2 instances.
This role is intended to be called before any other role to setup and start EC2
instances.
## Dependencies
You should first ensure that you have `python-boto` package installed on your
machine and an Amazon security access key pair created for your account.
## Tasks
By default, this role does nothing (no `main.yml` file).
* `setup.yml`: create a security group and ssh keys
* `create-instance.yml`: create new EC2 instances
* `post-install.yml`: remove admin user created on Debian instances
## Variables
- `aws_access_key` and `aws_secret_key`: your AWS credentials
- `aws_region`: where to create instances. Default: ca-central-1
- `ec2_public_ip`: assign public elastic IP address. Default: False
- `ec2_instance_count`: how many instance to launch. Default: 1
- `ec2_security_group: EC2 security group to use. See
ec2_evolinux_security_group in `defaults/main.yml` to define your own.
Default: ec2_evolinux_security_group
- `ec2_base_ami`: EC2 image to use. Default is to use Debian official ones,
depending on the region
- `ec2_instance_type`: EC2 instance type to use
- `ssh_pubkey_file`: SSH public key file to push to AWS. Do not try to put
your ED25519 key here, AWS does not support it. Default: ~/.ssh/id_rsa.pub
- `ec2_keyname: a name to give to your public key on AWS. Default is to use
$USER environment variable.
## Examples
In your main evolinux playbook put this play before Evolinux one:
```
---
- name: Prepare Amazon EC2 instance
hosts: localhost
gather_facts: False
vars:
aws_access_key:
aws_secret_key:
# Any other variable you want to set.
tasks:
- include_role:
name: amazon-ec2
tasks_from: create-instance.yml
```
See amazon-ec2-evolinux.yml for an almost ready-to-use playbook to set up
Amazon EC2 instances running Evolinux.

View file

@ -0,0 +1,62 @@
---
- name: Prepare Amazon EC2 instance
hosts: localhost
gather_facts: False
vars:
aws_access_key:
aws_secret_key:
aws_region: ca-central-1
tasks:
- include_role:
name: amazon-ec2
tasks_from: setup.yml
- include_role:
name: amazon-ec2
tasks_from: create-instance.yml
- name: Install Evolinux
hosts: launched-instances
become: yes
vars_files:
- 'vars/secrets.yml'
vars:
admin_users: "{{ admin_users }}"
minifirewall_trusted_ips: "{{ trusted_ips }}"
fail2ban_ignore_ips: "{{ trusted_ips }}"
evolinux_hostname:
evolinux_domain:
evolinux_fqdn:
evolinux_internal_hostname:
minifirewall_public_ports_tcp: [80, 443]
minifirewall_public_ports_udp: []
minifirewall_semipublic_ports_tcp: [22]
nagios_nrpe_allowed_hosts: "{{ trusted_ips }}"
roles:
- etc-git
- evolinux-base
- admin-users
- munin
- minifirewall
- fail2ban
- nagios-nrpe
- listupgrade
- evomaintenance
- evocheck
- packweb-apache
- mysql
post_tasks:
- include_role:
name: etc-git
tasks_from: commit.yml
vars:
commit_message: "Ansible post-run Evolinux playbook"
- include_role:
name: evocheck
tasks_from: exec.yml

View file

@ -0,0 +1,135 @@
---
aws_region: ca-central-1
ec2_public_ip: False
ec2_instance_count: 1
ec2_security_group: "{{ ec2_evolinux_security_group }}"
ec2_base_ami: "{{ ec2_debian_base_ami[aws_region] }}"
ec2_instance_type: t2.micro
# Note: Do not try to put your ED25519 key here, AWS does not support it...
ssh_pubkey_file: ~/.ssh/id_rsa.pub
ec2_keyname: "{{ lookup('env', 'USER') }}"
# From https://wiki.debian.org/Cloud/AmazonEC2Image/Stretch
ec2_debian_base_ami:
ap-northeast-1: ami-032dd665
ap-northeast-2: ami-e174ac8f
ap-south-1: ami-6e7a3e01
ap-southeast-1: ami-41365b22
ap-southeast-2: ami-51f61333
ca-central-1: ami-18239d7c
eu-central-1: ami-11bb0e7e
eu-west-1: ami-d037cda9
eu-west-2: ami-ece3f388
sa-east-1: ami-a24635ce
us-east-1: ami-ac5e55d7
us-east-2: ami-9fbb98fa
us-west-1: ami-560c3836
us-west-2: ami-fa18f282
ec2_evolinux_security_group:
name: evolinux-default
description: Evolinux default security group
rules:
- proto: icmp
cidr_ip: 0.0.0.0/0
from_port: -1
to_port: -1
- proto: tcp
from_port: 22
to_port: 22
cidr_ip: 0.0.0.0/0
- proto: tcp
from_port: 5666
to_port: 5666
cidr_ip: 0.0.0.0/0
- proto: tcp
from_port: 53
to_port: 53
cidr_ip: 0.0.0.0/0
- proto: udp
from_port: 53
to_port: 53
cidr_ip: 0.0.0.0/0
- proto: tcp
from_port: 389
to_port: 389
cidr_ip: 0.0.0.0/0
- proto: tcp
from_port: 636
to_port: 636
cidr_ip: 0.0.0.0/0
- proto: tcp
from_port: 143
to_port: 143
cidr_ip: 0.0.0.0/0
- proto: tcp
from_port: 993
to_port: 993
cidr_ip: 0.0.0.0/0
- proto: tcp
from_port: 110
to_port: 110
cidr_ip: 0.0.0.0/0
- proto: tcp
from_port: 995
to_port: 995
cidr_ip: 0.0.0.0/0
- proto: tcp
from_port: 25
to_port: 25
cidr_ip: 0.0.0.0/0
- proto: tcp
from_port: 80
to_port: 80
cidr_ip: 0.0.0.0/0
- proto: tcp
from_port: 443
to_port: 443
cidr_ip: 0.0.0.0/0
- proto: tcp
from_port: 21
to_port: 21
cidr_ip: 0.0.0.0/0
- proto: tcp
from_port: 20
to_port: 20
cidr_ip: 0.0.0.0/0
- proto: tcp
from_port: 5001
to_port: 5001
cidr_ip: 0.0.0.0/0
- proto: tcp
from_port: 465
to_port: 465
cidr_ip: 0.0.0.0/0
- proto: tcp
from_port: 587
to_port: 587
cidr_ip: 0.0.0.0/0
- proto: tcp
from_port: 8181
to_port: 8181
cidr_ip: 0.0.0.0/0
- proto: tcp
from_port: 8282
to_port: 8282
cidr_ip: 0.0.0.0/0
- proto: tcp
from_port: 9091
to_port: 9091
cidr_ip: 0.0.0.0/0
- proto: tcp
from_port: 2222
to_port: 2222
cidr_ip: 0.0.0.0/0
- proto: tcp
from_port: 2223
to_port: 2223
cidr_ip: 0.0.0.0/0
- proto: udp
from_port: 123
to_port: 123
cidr_ip: 0.0.0.0/0
rules_egress:
- proto: all
cidr_ip: 0.0.0.0/0

View file

@ -0,0 +1,36 @@
---
- name: Launch new instance(s)
ec2:
state: present
aws_access_key: "{{aws_access_key}}"
aws_secret_key: "{{aws_secret_key}}"
region: "{{aws_region}}"
image: "{{ec2_base_ami}}"
instance_type: "{{ec2_instance_type}}"
count: "{{ec2_instance_count}}"
assign_public_ip: "{{ec2_public_ip}}"
group: "{{ec2_security_group.name}}"
key_name: "{{ec2_keyname}}"
wait: yes
register: ec2
- name: Add newly created instance(s) to inventory
add_host:
hostname: "{{item.public_dns_name}}"
groupname: launched-instances
ansible_user: admin
ansible_ssh_common_args: "-o StrictHostKeyChecking=no"
with_items: "{{ec2.instances}}"
- debug:
msg: "Your newly created instance is reachable at: {{item.public_dns_name}}"
with_items: "{{ec2.instances}}"
- name: Wait for SSH to come up on all instances (give up after 2m)
wait_for:
state: started
host: "{{item.public_dns_name}}"
port: 22
timeout: 120
with_items: "{{ec2.instances}}"

View file

@ -0,0 +1,5 @@
---
- name: Remove admin user
user:
name: admin
state: absent

View file

@ -0,0 +1,20 @@
---
- name: Create default security group
ec2_group:
name: "{{ec2_security_group.name}}"
state: present
aws_access_key: "{{aws_access_key}}"
aws_secret_key: "{{aws_secret_key}}"
region: "{{aws_region}}"
description: "{{ec2_security_group.description}}"
rules: "{{ec2_security_group.rules}}"
- name: Create key pair
ec2_key:
name: "{{ec2_keyname}}"
state: present
aws_access_key: "{{aws_access_key}}"
aws_secret_key: "{{aws_secret_key}}"
region: "{{aws_region}}"
key_material: "{{item}}"
with_file: "{{ssh_pubkey_file}}"

View file

@ -10,8 +10,8 @@ Everything is in the `tasks/main.yml` file for now.
Main variables are :
* `apache_private_ipaddr_whitelist_present` : list of IP addresses to have in the private whitelist ;
* `apache_private_ipaddr_whitelist_absent` : list of IP addresses **not** to have in the whitelist;
* `apache_ipaddr_whitelist_present` : list of IP addresses to have in the private whitelist ;
* `apache_ipaddr_whitelist_absent` : list of IP addresses **not** to have in the whitelist;
* `apache_private_htpasswd_present` : list of users to have in the private htpasswd ;
* `apache_private_htpasswd_absent` : list of users to **not** have in the private htpasswd.
* `log2mail_alert_email`: email address to send Log2mail messages to (default: `general_alert_email`).

View file

@ -1,6 +1,9 @@
---
apache_private_ipaddr_whitelist_present: []
apache_private_ipaddr_whitelist_absent: []
evolix_trusted_ips: []
additional_trusted_ips: []
# Let's merge evolix_trusted_ips with additional_trusted_ips
apache_ipaddr_whitelist_present: "{{ evolix_trusted_ips | union(additional_trusted_ips) | unique }}"
apache_ipaddr_whitelist_absent: []
apache_private_htpasswd_present: []
apache_private_htpasswd_absent: []

View file

@ -0,0 +1,18 @@
#!/bin/bash
set -e
DIR="/var/log/apache-status"
URL="http://127.0.0.1/server-status"
TS=`date +%Y%m%d%H%M%S`
FILE="${DIR}/${TS}.html"
mkdir -p "${DIR}"
wget -q -O "${FILE}" "${URL}"
chmod 640 "${FILE}"
find "${DIR}" -type f -mtime +1 -delete
exit 0

View file

@ -2,7 +2,7 @@
- name: Init ipaddr_whitelist.conf file
copy:
src: private_ipaddr_whitelist.conf
src: ipaddr_whitelist.conf
dest: /etc/apache2/ipaddr_whitelist.conf
owner: root
group: root
@ -16,7 +16,7 @@
dest: /etc/apache2/ipaddr_whitelist.conf
line: "Require ip {{ item }}"
state: present
with_items: "{{ apache_private_ipaddr_whitelist_present }}"
with_items: "{{ apache_ipaddr_whitelist_present }}"
notify: reload apache
tags:
- apache
@ -26,7 +26,7 @@
dest: /etc/apache2/ipaddr_whitelist.conf
line: "Require ip {{ item }}"
state: absent
with_items: "{{ apache_private_ipaddr_whitelist_absent }}"
with_items: "{{ apache_ipaddr_whitelist_absent }}"
notify: reload apache
tags:
- apache

View file

@ -38,6 +38,10 @@
- expires
- headers
- cgi
- ssl
- include
- negotiation
- alias
notify: reload apache
tags:
- apache
@ -127,30 +131,17 @@
tags:
- apache
- name: Stat /default index
stat:
path: /var/www/index.html
register: _default_index
check_mode: no
- include_role:
name: remount-usr
tags:
- apache
# - block:
# - name: generate random string for serverstatus suffix
# command: "apg -a 1 -M N -n 1"
# changed_when: False
# register: _random_serverstatus_suffix
#
# - name: overwrite apache_serverstatus_suffix
# set_fact:
# apache_serverstatus_suffix: "{{ _random_serverstatus_suffix.stdout }}"
# when: apache_serverstatus_suffix == ""
#
# - name: replace server-status suffix in default site index
# replace:
# dest: /var/www/index.html
# regexp: '__SERVERSTATUS_SUFFIX__'
# replace: "{{ apache_serverstatus_suffix }}"
- name: "Install save_apache_status.sh"
copy:
src: save_apache_status.sh
dest: /usr/share/scripts/save_apache_status.sh
mode: "0755"
force: no
- include: log2mail.yml
when: apache_log2mail_include

View file

@ -1,23 +1,55 @@
---
- name: munin-node and core plugins are installed
- name: "Install munin-node and core plugins packages"
apt:
name: "{{ item }}"
state: installed
with_items:
- munin-node
- munin-plugins-core
tags:
- apache
- munin
- name: enable munin plugins
- name: "Enable Munin plugins"
file:
src: "/usr/share/munin/plugins/{{ item }}"
dest: "/etc/munin/plugins/{{ item }}"
state: link
with_items:
- apache_accesses
- apache_processes
- apache_volume
- apache_accesses
- apache_processes
- apache_volume
notify: restart munin-node
tags:
- apache
- munin
- apache
- munin
- name: "Install fcgi packages for Munin graphs"
apt:
name: "{{ item }}"
state: installed
with_items:
- libapache2-mod-fcgid
- libcgi-fast-perl
notify: reload apache
tags:
- apache
- munin
- name: "Enable libapache2-mod-fcgid"
command: a2enmod fcgid
register: cmd_enable_fcgid
changed_when: "'Module fcgid already enabled' not in cmd_enable_fcgid.stdout"
notify: restart apache
tags:
- apache
- munin
- name: "Apache has access to /var/log/munin/"
file:
path: /var/log/munin/
group: www-data
tags:
- apache
- munin

View file

@ -19,11 +19,13 @@
Require all denied
Include /etc/apache2/ipaddr_whitelist.conf
</Directory>
<Directory /usr/lib/munin/cgi/>
Options -Indexes
# munin-cgi-graph, used for zooming on graphs.
ScriptAlias /munin-cgi/munin-cgi-graph /usr/lib/munin/cgi/munin-cgi-graph
<Location /munin-cgi/munin-cgi-graph>
Options +ExecCGI
Require all denied
Include /etc/apache2/ipaddr_whitelist.conf
</Directory>
</Location>
# For CGI Scripts. We need to set Directory directive as ScriptAlias take precedence.
ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/

View file

@ -11,6 +11,7 @@ Tasks are extracted in several files, included in `tasks/main.yml` :
## Available variables
* `apt_config` : customize apt configuration (default: `True`) ;
* `apt_install_basics` : change basic sources components (default: `True`) ;
* `apt_basics_components` : basic sources components (default: `main`) ;
* `apt_install_backports` : install backports sources (default: `False`) ;

View file

@ -1,4 +1,10 @@
---
apt_config: True
apt_evolinux_config: True
apt_hooks: True
apt_remove_aptitude: True
apt_upgrade: False
apt_install_basics: True
apt_basics_components: "main"

View file

@ -1,4 +0,0 @@
---
- name: apt update
apt:
update_cache: yes

View file

@ -13,7 +13,7 @@
dest: /etc/apt/sources.list.d/backports.list
force: yes
mode: "0640"
notify: apt update
register: apt_backports_list
tags:
- apt
@ -23,11 +23,13 @@
dest: /etc/apt/preferences.d/0-backports-defaults
force: yes
mode: "0640"
notify: apt update
register: apt_backports_config
tags:
- apt
- name: Intermediate flush of handlers
meta: flush_handlers
- name: Apt update
apt:
update_cache: yes
when: apt_backports_list | changed or apt_backports_config | changed
tags:
- apt

View file

@ -6,7 +6,7 @@
dest: /etc/apt/sources.list
mode: "0644"
force: yes
notify: apt update
register: apt_basic_list
tags:
- apt
@ -20,7 +20,9 @@
- /etc/apt/sources.list.d/debian-update.list
when: apt_clean_gandi_sourceslist
- name: Intermediate flush of handlers
meta: flush_handlers
- name: Apt update
apt:
update_cache: yes
when: apt_basic_list | changed
tags:
- apt

View file

@ -1,24 +1,19 @@
---
- include_role:
name: apt
vars:
apt_install_basics: "{{ evolinux_apt_replace_default_sources }}"
apt_install_evolix_public: "{{ evolinux_apt_public_sources }}"
- name: Setting apt config
- name: Evolinux config for APT
lineinfile:
dest: /etc/apt/apt.conf.d/z-evolinux.conf
line: "{{ item }}"
line: "{{ item.line }}"
regexp: "{{ item.regexp }}"
create: yes
state: present
mode: "0640"
with_items:
- "APT::Install-Recommends \"false\";"
- "APT::Install-Suggests \"false\";"
when: evolinux_apt_conf
- { line: "APT::Install-Recommends \"false\";", regexp: 'APT::Install-Recommends' }
- { line: "APT::Install-Suggests \"false\";", regexp: 'APT::Install-Suggests' }
when: apt_evolinux_config
- name: DPKg invoke hooks
- name: DPkg invoke hooks
lineinfile:
dest: /etc/apt/apt.conf.d/z-evolinux.conf
line: "{{ item }}"
@ -30,13 +25,13 @@
- "DPkg::Pre-Invoke { \"df /usr | grep -q /usr && mount -oremount,rw /usr || true\"; };"
- "DPkg::Post-Invoke { \"df /tmp | grep -q /tmp && mount -oremount /tmp || true\"; };"
- "DPkg::Post-Invoke { \"df /usr | grep -q /usr && mount -oremount /usr || true\"; };"
when: evolinux_apt_hooks
when: apt_hooks
- name: Remove Aptitude
apt:
name: aptitude
state: absent
when: evolinux_apt_remove_aptitude
when: apt_remove_aptitude
- name: Updating APT cache
apt:
@ -46,6 +41,4 @@
- name: Upgrading system
apt:
upgrade: dist
when: evolinux_apt_upgrade
- meta: flush_handlers
when: apt_upgrade

View file

@ -19,11 +19,13 @@
dest: /etc/apt/sources.list.d/evolix_public.list
force: yes
mode: "0640"
notify: apt update
register: apt_evolix_public
tags:
- apt
- name: Intermediate flush of handlers
meta: flush_handlers
- name: Apt update
apt:
update_cache: yes
when: apt_evolix_public | changed
tags:
- apt

View file

@ -7,6 +7,12 @@
tags:
- apt
- name: Custom configuration
include: config.yml
when: apt_config
tags:
- apt
- name: Install basics repositories
include: basics.yml
when: apt_install_basics
@ -19,9 +25,6 @@
tags:
- apt
- debug:
var: apt_install_evolix_public
- name: Install Evolix Public APT repository
include: evolix_public.yml
when: apt_install_evolix_public

5
clamav/handlers/main.yml Normal file
View file

@ -0,0 +1,5 @@
---
- name: restart clamav
service:
name: clamav-daemon
state: restarted

3
clamav/meta/main.yml Normal file
View file

@ -0,0 +1,3 @@
---
dependencies:
- { role: amavis }

111
clamav/tasks/main.yml Normal file
View file

@ -0,0 +1,111 @@
---
- name: configure clamav-daemon
debconf:
name: clamav-daemon
question: "{{ item.key }}"
value: "{{ item.value }}"
vtype: "{{ item.type }}"
with_items:
- { key: 'clamav-daemon/debconf', type: 'boolean', value: 'true' }
- { key: 'clamav-daemon/MaxHTMLNormalize', type: 'string', value: '10M' }
- { key: 'clamav-daemon/StatsPEDisabled', type: 'boolean', value: 'true' }
- { key: 'clamav-daemon/FollowDirectorySymlinks', type: 'boolean', value: 'false' }
- { key: 'clamav-daemon/StreamMaxLength', type: 'string', value: '25' }
- { key: 'clamav-daemon/ReadTimeout', type: 'string', value: '180' }
- { key: 'clamav-daemon/StatsEnabled', type: 'boolean', value: 'false' }
- { key: 'clamav-daemon/MaxConnectionQueueLength', type: 'string', value: '15' }
- { key: 'clamav-daemon/LogRotate', type: 'boolean', value: 'true' }
- { key: 'clamav-daemon/AllowAllMatchScan', type: 'boolean', value: 'true' }
- { key: 'clamav-daemon/ScanOnAccess', type: 'boolean', value: 'false' }
- { key: 'clamav-daemon/LogFile', type: 'string', value: '/var/log/clamav/clamav.log' }
- { key: 'clamav-daemon/ScanMail', type: 'boolean', value: 'true' }
- { key: 'clamav-daemon/BytecodeTimeout', type: 'string', value: '60000' }
- { key: 'clamav-daemon/LogTime', type: 'boolean', value: 'true' }
- { key: 'clamav-daemon/OnAccessMaxFileSize', type: 'string', value: '5M' }
- { key: 'clamav-daemon/TcpOrLocal', type: 'select', value: 'UNIX' }
- { key: 'clamav-daemon/MaxEmbeddedPE', type: 'string', value: '10M' }
- { key: 'clamav-daemon/FixStaleSocket', type: 'boolean', value: 'true' }
- { key: 'clamav-daemon/User', type: 'string', value: 'clamav' }
- { key: 'clamav-daemon/BytecodeSecurity', type: 'select', value: 'TrustSigned' }
- { key: 'clamav-daemon/ScanSWF', type: 'boolean', value: 'true' }
- { key: 'clamav-daemon/MaxDirectoryRecursion', type: 'string', value: '0' }
- { key: 'clamav-daemon/MaxThreads', type: 'string', value: '12' }
- { key: 'clamav-daemon/LocalSocketGroup', type: 'string', value: 'clamav' }
- { key: 'clamav-daemon/MaxScriptNormalize', type: 'string', value: '5M' }
- { key: 'clamav-daemon/ForceToDisk', type: 'boolean', value: 'false' }
- { key: 'clamav-daemon/StatsHostID', type: 'string', value: 'auto' }
- { key: 'clamav-daemon/FollowFileSymlinks', type: 'boolean', value: 'false' }
- { key: 'clamav-daemon/TCPSocket', type: 'string', value: '3310' }
- { key: 'clamav-daemon/TCPAddr', type: 'string', value: 'any' }
- { key: 'clamav-daemon/DisableCertCheck', type: 'boolean', value: 'false' }
- { key: 'clamav-daemon/SelfCheck', type: 'string', value: '3600' }
- { key: 'clamav-daemon/LocalSocket', type: 'string', value: '/var/run/clamav/clamd.ctl' }
- { key: 'clamav-daemon/LocalSocketMode', type: 'string', value: '666' }
- { key: 'clamav-daemon/StatsTimeout', type: 'string', value: '10' }
- { key: 'clamav-daemon/MaxZipTypeRcg', type: 'string', value: '1M' }
- { key: 'clamav-daemon/MaxHTMLNoTags', type: 'string', value: '2M' }
- { key: 'clamav-daemon/LogSyslog', type: 'boolean', value: 'false' }
- { key: 'clamav-daemon/AddGroups', type: 'string', value: '' }
- { key: 'clamav-daemon/Bytecode', type: 'boolean', value: 'true' }
- { key: 'clamav-daemon/ScanArchive', type: 'boolean', value: 'true' }
tags:
- clamav
- name: configure clamav-freshclam
debconf:
name: clamav-freshclam
question: "{{ item.key }}"
value: "{{ item.value }}"
vtype: "{{ item.type }}"
with_items:
- { key: 'clamav-freshclam/autoupdate_freshclam', type: 'select', value: 'daemon' }
- { key: 'clamav-freshclam/proxy_user', type: 'string', value: '' }
- { key: 'clamav-freshclam/NotifyClamd', type: 'boolean', value: 'true' }
- { key: 'clamav-freshclam/local_mirror', type: 'select', value: 'db.fr.clamav.net' }
- { key: 'clamav-freshclam/http_proxy', type: 'string', value: '' }
- { key: 'clamav-freshclam/LogRotate', type: 'boolean', value: 'true' }
- { key: 'clamav-freshclam/Bytecode', type: 'boolean', value: 'true' }
- { key: 'clamav-freshclam/update_interval', type: 'string', value: '24' }
- { key: 'clamav-freshclam/SafeBrowsing', type: 'boolean', value: 'false' }
- { key: 'clamav-freshclam/PrivateMirror', type: 'string', value: '' }
- { key: 'clamav-freshclam/internet_interface', type: 'string', value: '' }
tags:
- clamav
- name: install ClamAV
apt:
name: "{{ item }}"
state: present
with_items:
- clamav-daemon
- clamav
- clamdscan
- clamav-freshclam
- arc
- arj
- zoo
- pax
- bzip2
- cabextract
- rpm
- lzop
- razor
tags:
- clamav
- name: add clamav user to amavis group
user:
name: clamav
groups: amavis
append: True
tags:
- clamav
- name: allow supplementary groups
replace:
dest: /etc/clamav/clamd.conf
regexp: 'AllowSupplementaryGroups false'
replace: 'AllowSupplementaryGroups true'
notify: restart clamav
tags:
- clamav

36
dovecot/.kitchen.yml Normal file
View file

@ -0,0 +1,36 @@
---
driver:
name: docker
privileged: true
use_sudo: false
provisioner:
name: ansible_playbook
hosts: test-kitchen
roles_path: ../
ansible_verbose: true
require_ansible_source: false
require_chef_for_busser: false
idempotency_test: true
platforms:
- name: debian
driver_config:
image: evolix/ansible:2.2.1
verifier:
name: serverspec
suites:
- name: default
provisioner:
name: ansible_playbook
playbook: ./tests/test.yml
verifier:
patterns:
- nginx/tests/spec/memcached_spec.rb
bundler_path: '/usr/local/bin'
rspec_path: '/usr/local/bin'
transport:
max_ssh_sessions: 6

11
dovecot/README.md Normal file
View file

@ -0,0 +1,11 @@
# Dovecot
Installation and basic configuration of dovecot
## Tasks
Minimal configuration is in `tasks/main.yml`
## Available variables
The full list of variables (with default values) can be found in `defaults/main.yml`.

View file

@ -0,0 +1,2 @@
---
dovecot_foo: bar

126
dovecot/files/munin_plugin Executable file
View file

@ -0,0 +1,126 @@
#! /bin/bash
#
# Munin Plugin
# to count logins to your dovecot mailserver
#
# Created by Dominik Schulz <lkml@ds.gauner.org>
# http://developer.gauner.org/munin/
# Contributions by:
# - Stephane Enten <tuf@delyth.net>
# - Steve Schnepp <steve.schnepp@pwkf.org>
#
# Parameters understood:
#
# config (required)
# autoconf (optional - used by munin-config)
#
# Config variables:
#
# logfile - Where to find the syslog file
#
# Add the following line to a file in /etc/munin/plugin-conf.d:
# env.logfile /var/log/your/logfile.log
#
# Magic markers (optional - used by munin-config and installation scripts):
#
#%# family=auto
#%# capabilities=autoconf
######################
# Configuration
######################
EXPR_BIN=/usr/bin/expr
LOGFILE=${logfile:-/var/log/mail.log}
######################
if [ "$1" = "autoconf" ]; then
echo yes
exit 0
fi
if [ "$1" = "config" ]; then
echo 'graph_title Dovecot Logins'
echo 'graph_category Mail'
echo 'graph_args --base 1000 -l 0'
echo 'graph_vlabel Login Counters'
for t in Total TLS SSL IMAP POP3
do
field=$(echo $t | tr '[:upper:]' '[:lower:]')
echo "login_$field.label $t Logins"
echo "login_$field.type DERIVE"
echo "login_$field.min 0"
done
echo 'connected.label Connected Users'
exit 0
fi
######################
# Total Logins
######################
echo -en "login_total.value "
VALUE=$(egrep -c '[dovecot]?.*Login' $LOGFILE)
if [ ! -z "$VALUE" ]; then
echo "$VALUE"
else
echo "0"
fi
echo -n
######################
# Connected Users
######################
DISCONNECTS=$(egrep -c '[dovecot]?.*Disconnected' $LOGFILE)
CONNECTS=$(egrep -c '[dovecot]?.*Login' $LOGFILE)
VALUE=$($EXPR_BIN $CONNECTS - $DISCONNECTS)
if [ -z "$VALUE" ] || [ "$VALUE" -lt 0 ]; then
VALUE=0
fi
echo -en "connected.value "
echo $VALUE
echo -n
######################
# TLS Logins
######################
echo -en "login_tls.value "
VALUE=$(egrep -c '[dovecot]?.*Login.*TLS' $LOGFILE)
if [ ! -z "$VALUE" ]; then
echo "$VALUE"
else
echo "0"
fi
echo -n
######################
# SSL Logins
######################
echo -en "login_ssl.value "
VALUE=$(egrep -c '[dovecot]?.*Login.*SSL' $LOGFILE)
if [ ! -z "$VALUE" ]; then
echo "$VALUE"
else
echo "0"
fi
echo -n
######################
# IMAP Logins
######################
echo -en "login_imap.value "
VALUE=$(egrep -c '[dovecot]?.*imap.*Login' $LOGFILE)
if [ ! -z "$VALUE" ]; then
echo "$VALUE"
else
echo "0"
fi
echo -n
######################
# POP3 Logins
######################
echo -en "login_pop3.value "
VALUE=$(egrep -c '[dovecot]?.*pop3.*Login' $LOGFILE)
if [ ! -z "$VALUE" ]; then
echo "$VALUE"
else
echo "0"
fi
echo -n

10
dovecot/handlers/main.yml Normal file
View file

@ -0,0 +1,10 @@
---
- name: restart dovecot
service:
name: dovecot
state: restarted
- name: reload dovecot
service:
name: dovecot
state: reloaded

68
dovecot/tasks/main.yml Normal file
View file

@ -0,0 +1,68 @@
- name: ensure packages are installed
apt:
name: '{{ item }}'
state: present
with_items:
- dovecot-ldap
- dovecot-imapd
- dovecot-pop3d
- dovecot-sieve
- dovecot-managesieved
tags:
- dovecot
- name: disable pam auth
replace:
dest: /etc/dovecot/conf.d/10-auth.conf
regexp: "[^#]!include auth-system.conf.ext"
replace: "#!include auth-system.conf.ext"
tags:
- dovecot
- name: update ldap auth
lineinfile:
dest: /etc/dovecot/dovecot-ldap.conf.ext
line: "{{ item.key }} = {{ item.value }}"
regexp: "^#*{{ item.key }}"
state: present
with_items:
- { key: 'hosts', value: '127.0.0.1' }
- { key: 'auth_bind', value: 'yes' }
- { key: 'ldap_version', value: 3 }
- { key: 'base', value: "{{ ldap_suffix }}" }
- { key: 'user_attrs', value: 'homeDirectory=home' }
- { key: 'user_filter', value: '(&(isActive=TRUE)(uid=%u))' }
- { key: 'pass_attrs', value: 'uid=user,userPassword=password' }
when: ldap_suffix is defined
notify: reload dovecot
tags:
- dovecot
- name: create vmail group
group:
name: vmail
gid: 5000
tags:
- dovecot
- name: create vmail user
user:
name: vmail
group: vmail
uid: 5000
shell: /bin/false
tags:
- dovecot
- name: deploy evolix config
template:
src: z-evolinux-defaults.conf.j2
dest: /etc/dovecot/conf.d/z-evolinux-defaults.conf
mode: "0644"
notify: reload dovecot
tags:
- dovecot
- include: munin.yml
tags:
- dovecot

20
dovecot/tasks/munin.yml Normal file
View file

@ -0,0 +1,20 @@
---
- name: is Munin present ?
stat:
path: /etc/munin/plugin-conf.d/munin-node
check_mode: no
register: munin_node_plugins_config
- block:
- name: Install munin plugin
copy:
src: munin_plugin
dest: /etc/munin/plugins/dovecot
mode: "0755"
# TODO : add in /etc/munin/plugin-conf.d/munin-node
# [dovecot]
# group adm
when: munin_node_plugins_config.stat.exists

View file

@ -0,0 +1,36 @@
# {{ ansible_managed }}
# Autorise les mécanismes PLAIN/LOGIN même sans SSL/TLS
disable_plaintext_auth = no
auth_mechanisms = plain login
# Authentification LDAP + intégration avec Postfix pour l'auth SMTP
!include auth-ldap.conf.ext
service auth {
unix_listener auth-userdb {
mode = 0600
user = vmail
group = vmail
}
unix_listener /var/spool/postfix/private/auth-client {
mode = 0666
user = postfix
group = postfix
}
}
# Stockage des emails dans /home/mail avec UID/GID 5000/5000
mail_location = maildir:/home/vmail/%d/%n
mail_uid = 5000
mail_gid = 5000
# Activation Sieve
protocol lda {
mail_plugins = sieve
}
# Optimisations
service login {
process_limit = 256
}
mail_max_userip_connections = 42

View file

@ -5,23 +5,3 @@ Install tools to setup DRBD replication accross servers.
## Tasks
Everything is in the `tasks/main.yml` file.
## Available variables
The variable `admin_users` must be a "dict" of one or more users :
```
admin_users:
foo:
name: foo
uid: 1001
fullname: 'Mr Foo'
password_hash: 'sdfgsdfgsdfgsdfg'
ssh_key: 'ssh-rsa AZERTYXYZ'
bar:
name: bar
uid: 1002
fullname: 'Mr Bar'
password_hash: 'gsdfgsdfgsdfgsdf'
ssh_key: 'ssh-rsa QWERTYUIOP'
```

View file

@ -8,23 +8,8 @@
tags:
- drbd
- name: Check if /usr is a partition
shell: "mount | grep 'on /usr type'"
args:
warn: no
changed_when: False
failed_when: False
register: usr_partition
check_mode: no
tags:
- drbd
- name: Mount /usr in rw
command: mount -o remount,rw /usr
args:
warn: no
changed_when: False
when: usr_partition.rc == 0 and nagios_plugins_dir.stat.exists
- include_role:
name: remount-usr
tags:
- drbd

View file

@ -8,6 +8,7 @@ elasticsearch_custom_tmpdir: Null
elasticsearch_default_tmpdir: /var/lib/elasticsearch/tmp
elasticsearch_jvm_xms: 2g
elasticsearch_jvm_xmx: 2g
elasticsearch_log_rotate_days: 365
elasticsearch_curator: False

View file

@ -13,6 +13,7 @@ galaxy_info:
- name: Debian
versions:
- jessie
- stretch
galaxy_tags: []
# List tags for your role here, one per line. A tag is
@ -23,4 +24,5 @@ galaxy_info:
# NOTE: A tag is limited to a single word comprised of
# alphanumeric characters. Maximum 20 tags per role.
dependencies: []
dependencies:
- java8

View file

@ -3,6 +3,7 @@
- name: Curator sources list is available
apt_repository:
repo: "deb http://packages.elastic.co/curator/4/debian stable main"
filename: elastic
update_cache: yes
state: present
tags:

View file

@ -7,16 +7,16 @@
register: elasticsearch_custom_datadir_test
check_mode: no
- name: "read the real datadir"
command: readlink -f /var/lib/elasticsearch
changed_when: false
register: elasticsearch_current_real_datadir_test
check_mode: no
tags:
- elasticsearch
when: elasticsearch_custom_datadir
when:
- elasticsearch_custom_datadir != ''
- elasticsearch_custom_datadir != None
- block:
- name: elasticsearch is stopped
@ -40,5 +40,9 @@
name: elasticsearch
state: started
tags:
- elasticsearch
when: elasticsearch_custom_datadir and elasticsearch_custom_datadir != elasticsearch_current_real_datadir_test.stdout and not elasticsearch_custom_datadir_test.stat.exists
- elasticsearch
when:
- elasticsearch_custom_datadir != ''
- elasticsearch_custom_datadir != None
- elasticsearch_custom_datadir != elasticsearch_current_real_datadir_test.stdout
- not elasticsearch_custom_datadir_test.stat.exists

View file

@ -0,0 +1,9 @@
---
- name: "log rotation script"
template:
src: rotate_elasticsearch_logs.j2
dest: /etc/cron.daily/rotate_elasticsearch_logs
owner: root
group: root
mode: "0750"

View file

@ -10,6 +10,8 @@
- include: tmpdir.yml
- include: logs.yml
- include: plugin_head.yml
when: elasticsearch_plugin_head

View file

@ -1,12 +1,5 @@
---
- name: install java8
include_role:
name: java8
tags:
- elasticsearch
- packages
- name: APT https transport is enabled
apt:
name: apt-transport-https
@ -27,7 +20,7 @@
- name: Elastic sources list is available
apt_repository:
repo: "deb https://artifacts.elastic.co/packages/5.x/apt stable main"
filename: elastic.list
filename: elastic
state: present
update_cache: yes
tags:

View file

@ -11,7 +11,7 @@
- block:
- name: Head repository is checked-out
git:
repo: "git://github.com/mobz/elasticsearch-head.git"
repo: "https://github.com/mobz/elasticsearch-head.git"
dest: "{{ elasticsearch_plugin_head_clone_dir }}"
clone: yes
tags:
@ -53,3 +53,21 @@
- restart elasticsearch
tags:
- elasticsearch
- name: Install systemd unit
template:
src: elasticsearch-head.service.j2
dest: /etc/systemd/system/elasticsearch-head.service
tags:
- elasticsearch
- systemd
- name: Enable systemd unit
systemd:
name: elasticsearch-head
daemon_reload: yes
enabled: yes
state: started
tags:
- elasticsearch
- systemd

View file

@ -28,4 +28,4 @@
- restart elasticsearch
tags:
- elasticsearch
when: elasticsearch_custom_tmpdir or fstab_tmp_noexec | success
when: (elasticsearch_custom_tmpdir != '' and elasticsearch_custom_tmpdir != None) or fstab_tmp_noexec | success

View file

@ -0,0 +1,14 @@
[Service]
Type=simple
ExecStart=/usr/bin/npm run start
User={{ elasticsearch_plugin_head_owner }}
Group={{ elasticsearch_plugin_head_group }}
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=elasticsearch-head
Restart=always
WorkingDirectory={{ elasticsearch_plugin_head_clone_dir }}
Environment=NODE_ENV=production
[Install]
WantedBy=multi-user.target

View file

@ -0,0 +1,9 @@
#!/bin/sh
# {{ ansible_managed }}
LOG_DIR=/var/log/elasticsearch
USER=elasticsearch
MAX_AGE={{ elasticsearch_log_rotate_days | mandatory }}
find ${LOG_DIR} -type f -user ${USER} -name "*.log.????-??-??" -exec gzip --best {} \;
find ${LOG_DIR} -type f -user ${USER} -name "*.log.????-??-??.gz" -mtime +${MAX_AGE} -delete

View file

@ -5,6 +5,7 @@ evoacme_dhparam_size: 2048
evoacme_acme_dir: /var/lib/letsencrypt
evoacme_csr_dir: /etc/ssl/requests
evoacme_crt_dir: /etc/letsencrypt
evoacme_hooks_dir: "{{ evoacme_crt_dir }}/hooks"
evoacme_log_dir: /var/log/evoacme
evoacme_ssl_minday: 30
evoacme_ssl_ct: 'FR'

View file

@ -7,8 +7,8 @@
#
[ -f /etc/default/evoacme ] && . /etc/default/evoacme
[ -z "${CRT_DIR}" ] && CRT_DIR='/etc/letsencrypt'
CRT_DIR="${CRT_DIR:-'/etc/letsencrypt'}"
find "${CRT_DIR}" -maxdepth 1 -mindepth 1 -type d ! -path "*accounts" -exec basename {} \; | while read vhost; do
evoacme "$vhost"
done
export QUIET=1
find "${CRT_DIR}" -maxdepth 1 -mindepth 1 -type d ! -path "*accounts" ! -path "*hooks" -printf "%f\n" | xargs -n1 evoacme

View file

@ -1,4 +1,4 @@
#!/bin/sh
#!/bin/bash
#
# evoacme is a shell script to manage Let's Encrypt certificate with
# certbot tool but with a dedicated user (no-root) and from a csr
@ -7,78 +7,287 @@
# Licence: AGPLv3
#
set -e
set -u
usage() {
echo "Usage: $0 NAME"
echo ""
echo "NAME must be correspond to :"
echo "- a CSR in ${CSR_DIR}/NAME.csr"
echo "- a KEY in ${SSL_KEY_DIR}/NAME.key"
echo ""
cat <<EOT
Usage: ${PROGNAME} NAME
NAME must be correspond to :
- a CSR in ${CSR_DIR}/NAME.csr
- a KEY in ${SSL_KEY_DIR}/NAME.key
If env variable TEST=1, certbot is run in staging mode
If env variable DRY_RUN=1, certbot is run in dry-run mode
If env variable QUIET=1, no message is output
If env variable VERBOSE=1, debug messages are output
EOT
}
mkconf_apache() {
[ -f "/etc/apache2/ssl/${vhost}.conf" ] && sed -i "s~^SSLCertificateFile.*$~SSLCertificateFile $CRT_DIR/${vhost}/live/fullchain.pem~" "/etc/apache2/ssl/${vhost}.conf"
apache2ctl -t 2>/dev/null && service apache2 reload
log() {
if [ "${QUIET}" != "1" ]; then
echo "${PROGNAME}: $1"
fi
}
debug() {
if [ "${VERBOSE}" = "1" ] && [ "${QUIET}" != "1" ]; then
>&2 echo "${PROGNAME}: $1"
fi
}
error() {
>&2 echo "${PROGNAME}: $1"
[ "$1" = "invalid argument(s)" ] && >&2 usage
exit 1
}
mkconf_nginx() {
[ -f "/etc/nginx/ssl/${vhost}.conf" ] && sed -i "s~^ssl_certificate[^_].*$~ssl_certificate $CRT_DIR/${vhost}/live/fullchain.pem;~" "/etc/nginx/ssl/${vhost}.conf"
nginx -t 2>/dev/null && service nginx reload
}
sed_cert_path_for_apache() {
local vhost=$1
local vhost_full_path="/etc/apache2/ssl/${vhost}.conf"
local cert_path=$2
mkconf_haproxy() {
mkdir -p /etc/ssl/haproxy -m 700
cat "$CRT_DIR/${vhost}/live/fullchain.pem" "$SSL_KEY_DIR/${vhost}.key" > "/etc/ssl/haproxy/${vhost}.pem"
[ -f "$DH_DIR/${vhost}.pem" ] && cat "$DH_DIR/${vhost}.pem" >> "/etc/ssl/haproxy/${vhost}.pem"
haproxy -c -f /etc/haproxy/haproxy.cfg >/dev/null && service haproxy reload
[ ! -r "${vhost_full_path}" ] && return 0
local search="^SSLCertificateFile.*$"
local replace="SSLCertificateFile ${cert_path}"
if ! $(grep -qE "${search}" "${vhost_full_path}"); then
[ -w "${vhost_full_path}" ] || error "File ${vhost_full_path} is not writable"
sed -i "s~^${search}~${replace}~" "${vhost_full_path}"
debug "Config in ${vhost_full_path} has been updated"
$(command -v apache2ctl) -t
fi
}
sed_cert_path_for_nginx() {
local vhost=$1
local vhost_full_path="/etc/nginx/ssl/${vhost}.conf"
local cert_path=$2
[ ! -r "${vhost_full_path}" ] && return 0
local search="^ssl_certificate[^_].*$"
local replace="ssl_certificate ${cert_path};"
if ! $(grep -qE "${search}" "${vhost_full_path}"); then
[ -w "${vhost_full_path}" ] || error "File ${vhost_full_path} is not writable"
sed -i "s~${search}~${replace}~" "${vhost_full_path}"
debug "Config in ${vhost_full_path} has been updated"
$(command -v nginx) -t
fi
}
x509_verify() {
local file="$1"
[ -r "$file" ] || error "File ${file} not found"
"${OPENSSL_BIN}" x509 -noout -modulus -in "$file" >/dev/null
}
x509_enddate() {
local file="$1"
[ -r "$file" ] || error "File ${file} not found"
"${OPENSSL_BIN}" x509 -noout -enddate -in "$file"
}
csr_verify() {
local file="$1"
[ -r "$file" ] || error "File ${file} not found"
"${OPENSSL_BIN}" req -noout -modulus -in "$file" >/dev/null
}
main() {
[ -f /etc/default/evoacme ] && . /etc/default/evoacme
[ -z "${SSL_KEY_DIR}" ] && SSL_KEY_DIR='/etc/ssl/private'
[ -z "${CRT_DIR}" ] && CRT_DIR='/etc/letsencrypt'
[ -z "${CSR_DIR}" ] && CSR_DIR='/etc/ssl/requests'
[ -z "${SELF_SIGNED_DIR}" ] && SELF_SIGNED_DIR='/etc/ssl/self-signed'
[ -z "${DH_DIR}" ] && DH_DIR='/etc/ssl/dhparam'
[ -z "${LOG_DIR}" ] && LOG_DIR='/var/log/evoacme'
[ "$#" -ne 1 ] && usage && exit 1
# check arguments
[ "$#" -eq 1 ] || error "invalid argument(s)"
vhost=$(basename "$1" .conf)
[ "$1" = "-h" ] || [ "$1" = "--help" ] && usage && exit 0
# Check master status for evoadmin-cluster
if [ -f "/home/${vhost}/state" ]; then
grep -q "STATE=master" "/home/${vhost}/state" || exit 0
fi
mkdir -p "${ACME_DIR}"
chown acme: "${ACME_DIR}"
[ -w "${ACME_DIR}" ] || error "Directory ${ACME_DIR} is not writable"
SSL_EMAIL=$(grep emailAddress "${CRT_DIR}/openssl.cnf"|cut -d'=' -f2|xargs)
if [ -n "$SSL_EMAIL" ]; then
emailopt="-m $SSL_EMAIL"
else
emailopt="--register-unsafely-without-email"
fi
DATE=$(date "+%Y%m%d")
if [ -h "$CRT_DIR/${vhost}/live" ]; then
crt_end_date=$(openssl x509 -noout -enddate -in "$CRT_DIR/${vhost}/live/cert.crt"|sed -e "s/.*=//")
date_crt=$(date -ud "$crt_end_date" +"%s")
date_today=$(date +'%s')
date_diff=$(((date_crt - date_today) / (60*60*24)))
[ "$date_diff" -ge "$SSL_MINDAY" ] && exit 0
fi
rm -rf "$CRT_DIR/${vhost}/${DATE}"
mkdir -pm 755 "$CRT_DIR/${vhost}/${DATE}"
chown -R acme: "$CRT_DIR/${vhost}"
sudo -u acme certbot certonly --quiet --webroot --csr "$CSR_DIR/${vhost}.csr" --webroot-path "$ACME_DIR" -n --agree-tos --cert-path="$CRT_DIR/${vhost}/${DATE}/cert.crt" --fullchain-path="$CRT_DIR/${vhost}/${DATE}/fullchain.pem" --chain-path="$CRT_DIR/${vhost}/${DATE}/chain.pem" "$emailopt" --logs-dir "$LOG_DIR" 2>&1 | grep -v "certbot.crypto_util"
if [ -f "$CRT_DIR/${vhost}/${DATE}/fullchain.pem" ]; then
rm -f "$CRT_DIR/${vhost}/live"
ln -s "$CRT_DIR/${vhost}/${DATE}" "$CRT_DIR/${vhost}/live"
which apache2ctl >/dev/null && mkconf_apache
which nginx >/dev/null && mkconf_nginx
which haproxy >/dev/null && mkconf_haproxy
else
rmdir "$CRT_DIR/${vhost}/${DATE}"
fi
[ -d "${CSR_DIR}" ] || error "Directory ${CSR_DIR} is not found"
mkdir -p "${CRT_DIR}"
chown acme: "${CRT_DIR}"
[ -w "${CRT_DIR}" ] || error "Directory ${CRT_DIR} is not writable"
mkdir -p "${LOG_DIR}"
chown acme: "${LOG_DIR}"
[ -w "${LOG_DIR}" ] || error "Directory ${LOG_DIR} is not writable"
mkdir -p "${HOOKS_DIR}"
chown acme: "${HOOKS_DIR}"
[ -d "${HOOKS_DIR}" ] || error "Directory ${HOOKS_DIR} is not found"
readonly VHOST=$(basename "$1" .conf)
# check for important programs
readonly OPENSSL_BIN=$(command -v openssl) || error "openssl command not installed"
readonly CERTBOT_BIN=$(command -v certbot) || error "certbot command not installed"
# double check for directories
[ -d "${ACME_DIR}" ] || error "${ACME_DIR} is not a directory"
[ -d "${CSR_DIR}" ] || error "${CSR_DIR} is not a directory"
[ -d "${LOG_DIR}" ] || error "${LOG_DIR} is not a directory"
#### CSR VALIDATION
# verify .csr file
readonly CSR_FILE="${CSR_DIR}/${VHOST}.csr"
debug "Using CSR file: ${CSR_FILE}"
[ -f "${CSR_FILE}" ] || error "${CSR_FILE} absent"
[ -r "${CSR_FILE}" ] || error "${CSR_FILE} is not readable"
csr_verify "${CSR_FILE}" || error "${CSR_FILE} is invalid"
# Hook for evoadmin-web in cluster mode : check master status
local evoadmin_state_file="/home/${VHOST}/state"
[ -r "${evoadmin_state_file}" ] \
&& grep -q "STATE=slave" "${evoadmin_state_file}" \
&& debug "We are slave of this evoadmin cluster. Quit!" \
&& exit 0
#### INIT OR RENEW?
readonly LIVE_DIR="${CRT_DIR}/${VHOST}/live"
readonly LIVE_CERT="${LIVE_DIR}/cert.crt"
readonly LIVE_FULLCHAIN="${LIVE_DIR}/fullchain.pem"
readonly LIVE_CHAIN="${LIVE_DIR}/chain.pem"
# If live symlink already exists, it's not our first time...
if [ -h "${LIVE_DIR}" ]; then
# we have a live symlink
# let's see if there is a cert to renew
x509_verify "${LIVE_CERT}" || error "${LIVE_CERT} is invalid"
# Verify if our certificate will expire
crt_end_date=$(x509_enddate "${LIVE_CERT}" | cut -d= -f2)
date_renew=$(date -ud "${crt_end_date} - ${SSL_MINDAY} days" +"%s")
date_today=$(date +'%s')
if [ "${date_today}" -lt "${date_renew}" ]; then
debug "Cert ${LIVE_CERT} expires at ${crt_end_date} => more than ${SSL_MINDAY} days: kthxbye."
exit 0
fi
fi
#### CERTIFICATE CREATION WITH CERTBOT
local iteration=$(date "+%Y%m%d%H%M%S")
[ -n "${iteration}" ] || error "invalid iteration (${iteration})"
readonly NEW_DIR="${CRT_DIR}/${VHOST}/${iteration}"
[ -d "${NEW_DIR}" ] && error "${NEW_DIR} directory already exists, remove it manually."
mkdir -p "${NEW_DIR}"
chmod -R 0700 "${CRT_DIR}"
chown -R acme: "${CRT_DIR}"
debug "New cert will be created in ${NEW_DIR}"
readonly NEW_CERT="${NEW_DIR}/cert.crt"
readonly NEW_FULLCHAIN="${NEW_DIR}/fullchain.pem"
readonly NEW_CHAIN="${NEW_DIR}/chain.pem"
local CERTBOT_MODE=""
[ "${TEST}" = "1" ] && CERTBOT_MODE="${CERTBOT_MODE} --test-cert"
[ "${QUIET}" = "1" ] && CERTBOT_MODE="${CERTBOT_MODE} --quiet"
[ "${DRY_RUN}" = "1" ] && CERTBOT_MODE="${CERTBOT_MODE} --dry-run"
local CERTBOT_REGISTRATION="--agree-tos"
if [ -n "${SSL_EMAIL}" ]; then
debug "Registering at certbot with ${SSL_EMAIL} as email"
CERTBOT_REGISTRATION="${CERTBOT_REGISTRATION} -m ${SSL_EMAIL}"
else
debug "Registering at certbot without email"
CERTBOT_REGISTRATION="${CERTBOT_REGISTRATION} --register-unsafely-without-email"
fi
# Permissions checks for acme user
sudo -u acme test -r "${CSR_FILE}" || error "File ${CSR_FILE} is not readable by user 'acme'"
sudo -u acme test -w "${NEW_DIR}" || error "Directory ${NEW_DIR} is not writable by user 'acme'"
# create a certificate with certbot
sudo -u acme \
"${CERTBOT_BIN}" \
certonly \
${CERTBOT_MODE} \
${CERTBOT_REGISTRATION} \
--non-interactive \
--webroot \
--csr "${CSR_FILE}" \
--webroot-path "${ACME_DIR}" \
--cert-path "${NEW_CERT}" \
--fullchain-path "${NEW_FULLCHAIN}" \
--chain-path "${NEW_CHAIN}" \
--logs-dir "$LOG_DIR" \
2>&1 \
| grep -v "certbot.crypto_util"
if [ "${DRY_RUN}" = "1" ]; then
debug "In dry-run mode, we stop here. Bye"
exit 0
fi
# verify if all is right
x509_verify "${NEW_CERT}" || error "${NEW_CERT} is invalid"
x509_verify "${NEW_FULLCHAIN}" || error "${NEW_FULLCHAIN} is invalid"
x509_verify "${NEW_CHAIN}" || error "${NEW_CHAIN} is invalid"
log "New certificate available at ${NEW_CERT}"
#### CERTIFICATE ACTIVATION
# link dance
if [ -h "${LIVE_DIR}" ]; then
rm "${LIVE_DIR}"
debug "Remove ${LIVE_DIR} link"
fi
ln -s "${NEW_DIR}" "${LIVE_DIR}"
debug "Link ${NEW_DIR} to ${LIVE_DIR}"
# verify final path
x509_verify "${LIVE_CERT}" || error "${LIVE_CERT} is invalid"
# update Apache
sed_cert_path_for_apache "${VHOST}" "${LIVE_FULLCHAIN}"
# update Nginx
sed_cert_path_for_nginx "${VHOST}" "${LIVE_FULLCHAIN}"
#### EXECUTE HOOKS
#
# executable scripts placed in ${HOOKS_DIR}
# are executed, unless their name ends with ".disabled"
export EVOACME_VHOST_NAME="${VHOST}"
export EVOACME_CERT="${LIVE_CERT}"
export EVOACME_CHAIN="${LIVE_CHAIN}"
export EVOACME_FULLCHAIN="${LIVE_FULLCHAIN}"
# search for files in hooks directory
for hook in $(find ${HOOKS_DIR} -type f); do
# keep only executables files, not containing a "."
if [ -x "${hook}" ] && (basename "${hook}" | grep -vqF "."); then
debug "Executing ${hook}"
${hook}
fi
done
}
main "$@"
readonly PROGNAME=$(basename "$0")
readonly PROGDIR=$(realpath -m $(dirname "$0"))
readonly ARGS=$@
readonly VERBOSE=${VERBOSE:-"0"}
readonly QUIET=${QUIET:-"0"}
readonly TEST=${TEST:-"0"}
readonly DRY_RUN=${DRY_RUN:-"0"}
# Read configuration file, if it exists
[ -r /etc/default/evoacme ] && . /etc/default/evoacme
# Default value for main variables
readonly SSL_KEY_DIR=${SSL_KEY_DIR:-"/etc/ssl/private"}
readonly ACME_DIR=${ACME_DIR:-"/var/lib/letsencrypt"}
readonly CSR_DIR=${CSR_DIR:-"/etc/ssl/requests"}
readonly CRT_DIR=${CRT_DIR:-"/etc/letsencrypt"}
readonly LOG_DIR=${LOG_DIR:-"/var/log/evoacme"}
readonly HOOKS_DIR=${HOOKS_DIR:-"${CRT_DIR}/hooks"}
readonly SSL_MINDAY=${SSL_MINDAY:-"30"}
readonly SSL_EMAIL=${SSL_EMAIL:-""}
main ${ARGS}

View file

@ -0,0 +1,28 @@
#!/bin/sh
readonly PROGNAME=$(basename "$0")
readonly ARGS=$@
readonly VERBOSE=${VERBOSE:-"0"}
readonly QUIET=${QUIET:-"0"}
error() {
>&2 echo "${PROGNAME}: $1"
exit 1
}
debug() {
if [ "${VERBOSE}" = "1" ] && [ "${QUIET}" != "1" ]; then
>&2 echo "${PROGNAME}: $1"
fi
}
if [ -n "$(pidof apache2)" ]; then
if $($(command -v apache2ctl) -t 2> /dev/null); then
debug "Apache detected... reloading"
service apache2 reload
else
error " Apache config is broken, you must fix it !"
fi
else
debug "Apache is not running. Skip."
fi

View file

@ -0,0 +1,32 @@
#!/bin/sh
readonly PROGNAME=$(basename "$0")
readonly ARGS=$@
readonly VERBOSE=${VERBOSE:-"0"}
readonly QUIET=${QUIET:-"0"}
error() {
>&2 echo "${PROGNAME}: $1"
exit 1
}
debug() {
if [ "${VERBOSE}" = "1" ] && [ "${QUIET}" != "1" ]; then
>&2 echo "${PROGNAME}: $1"
fi
}
if [ -n "$(pidof dovecot)" ]; then
if $($(command -v doveconf) > /dev/null); then
if $($(command -v doveconf)|grep -E "^ssl_cert[^_]"|grep -q "letsencrypt"); then
debug "Dovecot detected... reloading"
service dovecot reload
else
debug "Dovecot doesn't use Let's Encrypt certificate. Skip."
fi
else
error "Dovecot config is broken, you must fix it !"
fi
else
debug "Dovecot is not running. Skip."
fi

View file

@ -0,0 +1,28 @@
#!/bin/sh
readonly PROGNAME=$(basename "$0")
readonly ARGS=$@
readonly VERBOSE=${VERBOSE:-"0"}
readonly QUIET=${QUIET:-"0"}
error() {
>&2 echo "${PROGNAME}: $1"
exit 1
}
debug() {
if [ "${VERBOSE}" = "1" ] && [ "${QUIET}" != "1" ]; then
>&2 echo "${PROGNAME}: $1"
fi
}
if [ -n "$(pidof nginx)" ]; then
if $($(command -v nginx) -t 2> /dev/null); then
debug "Nginx detected... reloading"
service nginx reload
else
error "Nginx config is broken, you must fix it !"
fi
else
debug "Nginx is not running. Skip."
fi

View file

@ -0,0 +1,32 @@
#!/bin/sh
readonly PROGNAME=$(basename "$0")
readonly ARGS=$@
readonly VERBOSE=${VERBOSE:-"0"}
readonly QUIET=${QUIET:-"0"}
error() {
>&2 echo "${PROGNAME}: $1"
exit 1
}
debug() {
if [ "${VERBOSE}" = "1" ] && [ "${QUIET}" != "1" ]; then
>&2 echo "${PROGNAME}: $1"
fi
}
if [ -n "$(pidof master)" ]; then
if $($(command -v postconf) > /dev/null); then
if $($(command -v postconf)|grep -E "^smtpd_tls_cert_file"|grep -q "letsencrypt"); then
debug "Postfix detected... reloading"
service postfix reload
else
debug "Postfix doesn't use Let's Encrypt certificate. Skip."
fi
else
error "Postfix config is broken, you must fix it !"
fi
else
debug "Postfix is not running. Skip."
fi

View file

@ -1,151 +1,248 @@
#!/bin/sh
#!/bin/bash
#
# make-csr is a shell script designed to automatically generate a
# make-csr is a shell script designed to automatically generate a
# certificate signing request (CSR) from an Apache or a Nginx vhost
#
# Author: Victor Laborie <vlaborie@evolix.fr>
# Licence: AGPLv3
#
get_domains() {
echo "$vhostfile"|grep -q nginx
if [ "$?" -eq 0 ]; then
domains=$(grep -oE "^( )*[^#]+" "$vhostfile" |grep -oE "[^\$]server_name.*;$"|sed 's/server_name//'|tr -d ';'|sed 's/\s\{1,\}//'|sed 's/\s\{1,\}/\n/g'|sort|uniq)
fi
echo "$vhostfile" |grep -q apache2
if [ "$?" -eq 0 ]; then
domains=$(grep -oE "^( )*[^#]+" "$vhostfile" |grep -oE "(ServerName|ServerAlias).*"|sed 's/ServerName//'|sed 's/ServerAlias//'|sed 's/\s\{1,\}//'|sort|uniq)
fi
valid_domains=""
nb=0
echo "Valid(s) domain(s) in $vhost :"
for domain in $domains; do
real_ip=$(dig +short "$domain"|grep -oE "([0-9]+\.){3}[0-9]+")
for ip in $(echo "$SRV_IP"|xargs -n1); do
if [ "${ip}" = "${real_ip}" ]; then
valid_domains="$valid_domains $domain"
nb=$(( nb + 1 ))
echo "* $domain -> $real_ip"
fi
done
done
if [ "$nb" -eq 0 ]; then
nb=$(echo "$domains"|wc -l)
echo "* No valid domain found"
echo "All following(s) domain(s) will be used for CSR creation :"
for domain in $domains; do
echo "* $domain"
done
else
domains="$valid_domains"
fi
domains=$(echo "$domains"|xargs -n1)
set -u
usage() {
cat <<EOT
Usage: ${PROGNAME} VHOST DOMAIN...
VHOST must correspond to an Apache or Nginx enabled VHost
If VHOST ends with ".conf" it is stripped,
then files are seached at those paths:
- /etc/apache2/sites-enables/VHOST.conf
- /etc/nginx/sites-enabled/VHOST.conf
- /etc/nginx/sites-enabled/VHOST
DOMAIN... is a list of domains for the CSR (passed as arguments or input)
If env variable VERBOSE=1, debug messages are sent to stderr
EOT
}
debug() {
if [ "${VERBOSE}" = 1 ]; then
>&2 echo "${PROGNAME}: $1"
fi
}
error() {
>&2 echo "${PROGNAME}: $1"
exit 1
}
default_key_size() {
grep default_bits "${SSL_CONFIG_FILE}" | cut -d'=' -f2 | xargs
}
sed_selfsigned_cert_path_for_apache() {
local apache_ssl_vhost_path="$1"
mkdir -p $(dirname "${apache_ssl_vhost_path}")
if [ ! -f "${apache_ssl_vhost_path}" ]; then
cat > "${apache_ssl_vhost_path}" <<EOF
SSLEngine On
SSLCertificateFile ${SELF_SIGNED_FILE}
SSLCertificateKeyFile ${SSL_KEY_FILE}
EOF
debug "SSL config added in ${apache_ssl_vhost_path}"
else
local search="^SSLCertificateFile.*$"
local replace="SSLCertificateFile ${SELF_SIGNED_FILE}"
sed -i "s~${search}~${replace}~" "${apache_ssl_vhost_path}"
debug "SSL config updated in ${apache_ssl_vhost_path}"
fi
}
sed_selfsigned_cert_path_for_nginx() {
local nginx_ssl_vhost_path="$1"
mkdir -p $(dirname "${nginx_ssl_vhost_path}")
if [ ! -f "${nginx_ssl_vhost_path}" ]; then
cat > "${nginx_ssl_vhost_path}" <<EOF
ssl_certificate ${SELF_SIGNED_FILE};
ssl_certificate_key ${SSL_KEY_FILE};
EOF
debug "SSL config added in ${nginx_ssl_vhost_path}"
else
local search="^ssl_certificate[^_].*$"
local replace="ssl_certificate ${SELF_SIGNED_FILE};"
sed -i "s~${search}~${replace}~" "${nginx_ssl_vhost_path}"
debug "SSL config updated in ${nginx_ssl_vhost_path}"
fi
}
openssl_selfsigned() {
local csr="$1"
local key="$2"
local crt="$3"
local crt_dir=$(dirname ${crt})
[ -r "${csr}" ] || error "File ${csr} is not readable"
[ -r "${key}" ] || error "File ${key} is not readable"
[ -w "${crt_dir}" ] || error "Directory ${crt_dir} is not writable"
"${OPENSSL_BIN}" x509 -req -sha256 -days 365 -in "${csr}" -signkey "${key}" -out "${crt}" 2> /dev/null
[ -r "${crt}" ] || error "Something went wrong, ${crt} has not been generated"
}
openssl_key(){
local key="$1"
local key_dir=$(dirname "${key}")
local size="$2"
[ -w "${key_dir}" ] || error "Directory ${key_dir} is not writable"
"${OPENSSL_BIN}" genrsa -out "${key}" "${size}" 2> /dev/null
[ -r "${key}" ] || error "Something went wrong, ${key} has not been generated"
}
openssl_csr() {
local csr="$1"
local csr_dir=$(dirname "${csr}")
local key="$2"
local cfg="$3"
[ -w "${csr_dir}" ] || error "Directory ${csr_dir} is not writable"
if $(grep -q "DNS:" "${cfg}"); then
# CSR with SAN
"${OPENSSL_BIN}" req -new -sha256 -key "${key}" -reqexts SAN -config "${cfg}" -out "${csr}"
else
# Single domain CSR
"${OPENSSL_BIN}" req -new -sha256 -key "${key}" -config "${cfg}" -out "${csr}"
fi
[ -r "${csr}" ] || error "Something went wrong, ${csr} has not been generated"
}
make_key() {
openssl genrsa -out "$SSL_KEY_DIR/${vhost}.key" "$SSL_KEY_SIZE" 2>/dev/null
chown root: "$SSL_KEY_DIR/${vhost}.key"
chmod 600 "$SSL_KEY_DIR/${vhost}.key"
local key="$1"
local size="$2"
openssl_key "${key}" "${size}"
debug "Private key stored at ${key}"
chown root: "${key}"
chmod 600 "${key}"
}
make_csr() {
domains="$1"
nb=$(echo "$domains"|wc -l)
config_file="/tmp/make-csr-${vhost}.conf"
local domains=$@
local nb=$#
local config_file="/tmp/make-csr-${VHOST}.conf"
local san=""
mkdir -p "$CSR_DIR" -m 0755
if [ "$nb" -eq 1 ]; then
cat /etc/letsencrypt/openssl.cnf - > "$config_file" <<EOF
mkdir -p -m 0755 "${CSR_DIR}" || error "Unable to mkdir ${CSR_DIR}"
if [ "${nb}" -eq 1 ]; then
cat "${SSL_CONFIG_FILE}" - > "${config_file}" <<EOF
CN=$domains
EOF
openssl req -new -sha256 -key "$SSL_KEY_DIR/${vhost}.key" -config "$config_file" -out "$CSR_DIR/${vhost}.csr"
elif [ "$nb" -gt 1 ]; then
san=''
for domain in $domains
do
san="$san,DNS:$domain"
done
san=$(echo "$san"|sed 's/,//')
cat /etc/letsencrypt/openssl.cnf - > "$config_file" <<EOF
elif [ "${nb}" -gt 1 ]; then
for domain in ${domains}; do
san="${san},DNS:${domain}"
done
san=$(echo "${san}" | sed 's/^,//')
cat "${SSL_CONFIG_FILE}" - > "${config_file}" <<EOF
[SAN]
subjectAltName=$san
subjectAltName=${san}
EOF
openssl req -new -sha256 -key "$SSL_KEY_DIR/${vhost}.key" -reqexts SAN -config "$config_file" > "$CSR_DIR/${vhost}.csr"
fi
if [ -f "$CSR_DIR/${vhost}.csr" ]; then
chmod 644 "$CSR_DIR/${vhost}.csr"
mkdir -p "$SELF_SIGNED_DIR" -m 0755
openssl x509 -req -sha256 -days 365 -in "$CSR_DIR/${vhost}.csr" -signkey "$SSL_KEY_DIR/${vhost}.key" -out "$SELF_SIGNED_DIR/${vhost}.pem"
[ -f "$SELF_SIGNED_DIR/${vhost}.pem" ] && chmod 644 "$SELF_SIGNED_DIR/${vhost}.pem"
fi
}
fi
openssl_csr "${CSR_FILE}" "${SSL_KEY_FILE}" "${config_file}"
debug "CSR stored at ${CSR_FILE}"
mkconf_apache() {
mkdir -p /etc/apache2/ssl
if [ ! -f "/etc/apache2/ssl/${vhost}.conf" ]; then
cat > "/etc/apache2/ssl/${vhost}.conf" <<EOF
SSLEngine On
SSLCertificateFile $SELF_SIGNED_DIR/${vhost}.pem
SSLCertificateKeyFile $SSL_KEY_DIR/${vhost}.key
EOF
else
sed -i "s~^SSLCertificateFile.*$~SSLCertificateFile $SELF_SIGNED_DIR/${vhost}.pem~" "/etc/apache2/ssl/${vhost}.conf"
fi
}
if [ -r "${CSR_FILE}" ]; then
chmod 644 "${CSR_FILE}"
mkdir -p -m 0755 "${SELF_SIGNED_DIR}"
mkconf_nginx() {
mkdir -p /etc/nginx/ssl
if [ ! -f "/etc/nginx/ssl/${vhost}.conf" ]; then
cat > "/etc/nginx/ssl/${vhost}.conf" <<EOF
ssl_certificate $SELF_SIGNED_DIR/${vhost}.pem;
ssl_certificate_key $SSL_KEY_DIR/${vhost}.key;
EOF
else
sed -i "s~^ssl_certificate[^_].*$~ssl_certificate $SELF_SIGNED_DIR/${vhost}.pem;~" "/etc/nginx/ssl/${vhost}.conf"
fi
openssl_selfsigned "${CSR_FILE}" "${SSL_KEY_FILE}" "${SELF_SIGNED_FILE}"
[ -r "${SELF_SIGNED_FILE}" ] && chmod 644 "${SELF_SIGNED_FILE}"
debug "Self-signed certificate stored at ${SELF_SIGNED_FILE}"
fi
}
main() {
if [ "$#" -ne 1 ]; then
echo "You need to provide one argument !" >&2
exit 1
fi
vhost=$(basename "$1" .conf)
local_ip=$(ip a|grep brd|cut -d'/' -f1|grep -oE "([0-9]+\.){3}[0-9]+")
if [ -t 0 ]; then
# We have STDIN, so we should have at least 2 arguments
if [ "$#" -lt 2 ]; then
>&2 echo "invalid arguments"
>&2 usage
exit 1
fi
# read VHOST from first argument
VHOST="$1"
# remove the first argument
shift
# read domains from remaining arguments
DOMAINS=$@
else
# We don't have STDIN, so we should have only 1 argument
if [ "$#" != 1 ]; then
>&2 echo "invalid arguments"
>&2 usage
exit 1
fi
# read VHOST from first argument
VHOST="$1"
# read domains from input
DOMAINS=
while read -r line ; do
DOMAINS="${DOMAINS} ${line}"
done
# trim the string to remove leading/trailing spaces
DOMAINS=$(echo "${DOMAINS}" | xargs)
fi
readonly VHOST
readonly DOMAINS
[ -f /etc/default/evoacme ] && . /etc/default/evoacme
[ -z "${SSL_KEY_DIR}" ] && SSL_KEY_DIR='/etc/ssl/private'
[ -z "${CSR_DIR}" ] && CSR_DIR='/etc/ssl/requests'
[ -z "${CRT_DIR}" ] && CRT_DIR='/etc/letsencrypt'
[ -z "${SELF_SIGNED_DIR}" ] && SELF_SIGNED_DIR='/etc/ssl/self-signed'
SSL_KEY_SIZE=$(grep default_bits /etc/letsencrypt/openssl.cnf|cut -d'=' -f2|xargs)
[ -n "${SRV_IP}" ] && SRV_IP="${SRV_IP} $local_ip" || SRV_IP="$local_ip"
vhostfile=$(ls "/etc/nginx/sites-enabled/${vhost}" "/etc/nginx/sites-enabled/${vhost}.conf" "/etc/apache2/sites-enabled/${vhost}" "/etc/apache2/sites-enabled/${vhost}.conf" 2>/dev/null|head -n1)
if [ ! -h "$vhostfile" ]; then
echo "$vhost is not a valid virtualhost !" >&2
exit 1
fi
mkdir -p "${CSR_DIR}"
chown root: "${CSR_DIR}"
[ -w "${CSR_DIR}" ] || error "Directory ${CSR_DIR} is not writable"
if [ -f "$SSL_KEY_DIR/${vhost}.key" ]; then
echo "$vhost key already exist, overwrite it ? (y)"
read REPLY
[ "$REPLY" = "Y" ] || [ "$REPLY" = "y" ] || exit 0
rm -f "/etc/apache2/ssl/${vhost}.conf /etc/nginx/ssl/${vhost}.conf"
[ -h "${CRT_DIR}/${vhost}/live" ] && rm "${CRT_DIR}/${vhost}/live"
fi
mkdir -p "${SELF_SIGNED_DIR}"
chown root: "${SELF_SIGNED_DIR}"
[ -w "${SELF_SIGNED_DIR}" ] || error "Directory ${SELF_SIGNED_DIR} is not writable"
get_domains
make_key
make_csr "$domains"
which apache2ctl >/dev/null && mkconf_apache
which nginx >/dev/null && mkconf_nginx
mkdir -p "${SSL_KEY_DIR}"
chown root: "${SSL_KEY_DIR}"
[ -w "${SSL_KEY_DIR}" ] || error "Directory ${SSL_KEY_DIR} is not writable"
[ -r "${SSL_CONFIG_FILE}" ] || error "File ${SSL_CONFIG_FILE} is not readable"
# check for important programs
readonly OPENSSL_BIN=$(command -v openssl) || error "openssl command not installed"
readonly SELF_SIGNED_FILE="${SELF_SIGNED_DIR}/${VHOST}.pem"
readonly SSL_KEY_FILE="${SSL_KEY_DIR}/${VHOST}.key"
readonly CSR_FILE="${CSR_DIR}/${VHOST}.csr"
make_key "${SSL_KEY_FILE}" "${SSL_KEY_SIZE}"
make_csr ${DOMAINS}
command -v apache2ctl >/dev/null && sed_selfsigned_cert_path_for_apache "/etc/apache2/ssl/${VHOST}.conf"
command -v nginx >/dev/null && sed_selfsigned_cert_path_for_nginx "/etc/nginx/ssl/${VHOST}.conf"
}
main "$@"
readonly PROGNAME=$(basename "$0")
readonly PROGDIR=$(realpath -m $(dirname "$0"))
readonly ARGS=$@
readonly VERBOSE=${VERBOSE:-"0"}
# Read configuration file, if it exists
[ -r /etc/default/evoacme ] && . /etc/default/evoacme
# Default value for main variables
readonly CSR_DIR=${CSR_DIR:-'/etc/ssl/requests'}
readonly SSL_CONFIG_FILE=${SSL_CONFIG_FILE:-"/etc/letsencrypt/openssl.cnf"}
readonly SELF_SIGNED_DIR=${SELF_SIGNED_DIR:-'/etc/ssl/self-signed'}
readonly SSL_KEY_DIR=${SSL_KEY_DIR:-'/etc/ssl/private'}
readonly SSL_KEY_SIZE=${SSL_KEY_SIZE:-$(default_key_size)}
main ${ARGS}

153
evoacme/files/vhost-domains.sh Executable file
View file

@ -0,0 +1,153 @@
#!/bin/bash
#
# make-csr is a shell script designed to automatically generate a
# certificate signing request (CSR) from an Apache or a Nginx vhost
#
# Author: Victor Laborie <vlaborie@evolix.fr>
# Licence: AGPLv3
#
set -u
usage() {
cat <<EOT
Usage: ${PROGNAME} VHOST
VHOST must correspond to an Apache or Nginx enabled VHost
If VHOST ends with ".conf" it is stripped,
then files are seached at those paths:
- /etc/apache2/sites-enables/VHOST.conf
- /etc/nginx/sites-enabled/VHOST.conf
- /etc/nginx/sites-enabled/VHOST
If env variable VERBOSE=1, debug messages are sent to stderr
EOT
}
debug() {
if [ "${VERBOSE}" = 1 ]; then
>&2 echo "${PROGNAME}: $1"
fi
}
error() {
>&2 echo "${PROGNAME}: $1"
exit 1
}
real_ip_for_domain() {
dig +short "$1" | grep -oE "([0-9]+\.){3}[0-9]+"
}
local_ip() {
ip a | grep brd | cut -d'/' -f1 | grep -oE "([0-9]+\.){3}[0-9]+"
}
nginx_domains() {
local vhost_file="$1"
grep -oE "^( )*[^#]+" "${vhost_file}" \
| grep -oE "[^\$]server_name.*;$" \
| sed 's/server_name//' \
| tr -d ';' \
| sed 's/\s\{1,\}//' \
| sed 's/\s\{1,\}/\n/g' \
| sort \
| uniq
}
apache_domains() {
local vhost_file="$1"
grep -oE "^( )*[^#]+" "${vhost_file}" \
| grep -oE "(ServerName|ServerAlias).*" \
| sed 's/ServerName//' \
| sed 's/ServerAlias//' \
| sed 's/\s\{1,\}//' \
| sort \
| uniq
}
get_domains() {
local vhost_file="$1"
local ips="$2"
local domains=""
local valid_domains=""
local nb=0
if $(echo "${vhost_file}" | grep -q nginx); then
debug "Nginx vhost file used"
domains=$(nginx_domains "${vhost_file}")
fi
if $(echo "${vhost_file}" | grep -q apache2); then
debug "Apache vhost file used"
domains=$(apache_domains "${vhost_file}")
fi
debug "Valid(s) domain(s) in ${vhost_file} :"
for domain in ${domains}; do
real_ip=$(real_ip_for_domain "${domain}")
for ip in $(echo "${ips}" | xargs -n1); do
if [ "${ip}" = "${real_ip}" ]; then
valid_domains="${valid_domains} ${domain}"
nb=$(( nb + 1 ))
debug "* ${domain} -> ${real_ip}"
fi
done
done
if [ "${nb}" -eq 0 ]; then
nb=$(echo "${domains}" | wc -l)
debug "* No valid domain found"
debug "All following(s) domain(s) will be used for CSR creation :"
for domain in ${domains}; do
debug "* ${domain}"
done
else
domains="${valid_domains}"
fi
echo "${domains}" | xargs -n 1
}
first_vhost_file_found() {
local vhost_name="$1"
ls "/etc/nginx/sites-enabled/${vhost_name}" \
"/etc/nginx/sites-enabled/${vhost_name}.conf" \
"/etc/apache2/sites-enabled/${vhost_name}.conf" \
2>/dev/null \
| head -n 1
}
main() {
if [ "$#" != 1 ]; then
>&2 usage
exit 1
fi
if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
usage
exit 0
fi
local vhost_name=$(basename "$1" .conf)
local vhost_file=$(first_vhost_file_found "${vhost_name}")
if [ ! -h "${vhost_file}" ]; then
>&2 echo "No virtualhost has been found for '${vhost_name}'."
exit 1
fi
local ips=$(local_ip)
if [ -n "${SRV_IP}" ]; then
ips="${ips} ${SRV_IP}"
fi
get_domains "${vhost_file}" "${ips}"
}
readonly PROGNAME=$(basename "$0")
readonly PROGDIR=$(realpath -m $(dirname "$0"))
readonly ARGS=$@
readonly VERBOSE=${VERBOSE:-"0"}
readonly SRV_IP=${SRV_IP:-""}
main $ARGS

View file

@ -22,6 +22,14 @@
group: acme
state: directory
- name: "Fix hooks directory permissions"
file:
path: "{{ evoacme_hooks_dir }}"
mode: "0700"
owner: acme
group: acme
state: directory
- name: Fix log dir's right
file:
path: "{{ evoacme_log_dir }}"

View file

@ -20,28 +20,26 @@
name: certbot
state: latest
- name: Check if /usr is a partition
shell: "mount | grep 'on /usr type'"
args:
warn: no
changed_when: False
failed_when: False
check_mode: no
register: usr_partition
- name: Mount /usr in rw
command: mount -o remount,rw /usr
args:
warn: no
changed_when: False
when: usr_partition.rc == 0
- include_role:
name: remount-usr
- name: Remove certbot symlink for apt install
file:
path: /usr/local/bin/certbot
state: absent
- name: stat /etc/cron.d/certbot
stat:
path: /etc/cron.d/certbot
register: etc_cron_d_certbot
- name: Rename certbot dpkg cron to .disabled
copy:
remote_src: True
src: /etc/cron.d/certbot
dest: /etc/cron.d/certbot.disabled
when: etc_cron_d_certbot.stat.exists
- name: Remove certbot dpkg cron
file:
path: /etc/cron.d/certbot

View file

@ -1,6 +1,6 @@
---
- ini_file:
dest: /etc/letsencrypt/openssl.cnf
dest: "{{ evoacme_crt_dir }}/openssl.cnf"
section: 'req'
option: "{{ item.name }}"
value: "{{ item.var }}"

View file

@ -0,0 +1,14 @@
---
- name: "Search for {{ hook_name }} hook"
command: "find {{ evoacme_hooks_dir }} -type f \\( -name '{{ hook_name }}' -o -name '{{ hook_name }}.*' \\)"
check_mode: no
changed_when: False
register: _find_hook
- name: "Copy {{ hook_name }} hook if missing"
copy:
src: "hooks/{{ hook_name }}"
dest: "{{ evoacme_hooks_dir }}/{{ hook_name }}"
mode: "0750"
when: _find_hook.stdout == ""

View file

@ -9,11 +9,30 @@
- include: acme.yml
- include: evoacme_hook.yml
vars:
hook_name: "{{ item }}"
with_items:
- reload_apache
- reload_nginx
- reload_dovecot
- reload_postfix
- include: conf.yml
- include: scripts.yml
- include: webserver.yml
- name: Determine Apache presence
stat:
path: /etc/apache2/apache2.conf
check_mode: no
register: sta
- name: Determine Nginx presence
stat:
path: /etc/nginx/nginx.conf
check_mode: no
register: stn
- include: apache.yml
when: sta.stat.isreg is defined and sta.stat.isreg

View file

@ -9,15 +9,23 @@
- name: Copy make-csr.sh script
copy:
src: files/make-csr.sh
src: make-csr.sh
dest: /usr/local/sbin/make-csr
owner: root
group: root
mode: "0755"
- name: Copy vhost-domains.sh script
copy:
src: vhost-domains.sh
dest: /usr/local/sbin/vhost-domains
owner: root
group: root
mode: "0755"
- name: Copy evoacme script
copy:
src: files/evoacme.sh
src: evoacme.sh
dest: /usr/local/sbin/evoacme
owner: root
group: root

View file

@ -1,12 +0,0 @@
---
- name: Determine Nginx presence
stat:
path: /etc/nginx/nginx.conf
check_mode: no
register: stn
- name: Determine Apache presence
stat:
path: /etc/apache2/apache2.conf
check_mode: no
register: sta

View file

@ -1,5 +1,9 @@
location ~ /.well-known/acme-challenge {
{% if ansible_distribution_major_version > 8 %}
alias {{ evoacme_acme_dir }}/;
{% else %}
alias {{ evoacme_acme_dir }}/.well-known/acme-challenge;
{% endif %}
try_files $uri =404;
allow all;
}

View file

@ -1,5 +1,6 @@
---
- include: remount_usr_rw.yml
- include_role:
name: remount-usr
when: evocheck_bin_dir | search ("/usr")
- name: Scripts dir is present

View file

@ -1,15 +0,0 @@
---
- name: Get mount options for partitions
shell: "mount | grep 'on /usr type'"
args:
warn: no
register: mount
changed_when: False
failed_when: False
when: not ansible_check_mode
- name: Remount /usr if it is a partition and it is not mounted in rw
command: "mount -o remount,rw /usr"
when: mount.rc == 0 and not mount.stdout_lines.0 | search("rw")
args:
warn: no

View file

@ -36,6 +36,7 @@ Main variables are:
* `evolinux_additional_packages`: optional additional packages to install (default: `[]`)
* `evolinux_postfix_purge_exim`: purge Exim packages (default: `True`) ;
* `evolinux_ssh_password_auth_addresses`: list of addresses that can authenticate with a password (default: `[]`)
* `evolinux_ssh_disable_root`: disable SSH access for root (default: `True`)
* `evolinux_ssh_disable_root`: disable SSH access for root (default: `False`)
* `evolinux_ssh_allow_current_user`: don't lock yourself out (default: `False`)
The full list of variables (with default values) can be found in `defaults/main.yml`.

View file

@ -10,6 +10,21 @@ logcheck_alert_email: Null
raid_alert_email: Null
postfix_alias_email: Null
# apt
evolinux_apt_include: True
evolinux_apt_conf: True
evolinux_apt_hooks: True
evolinux_apt_replace_default_sources: True
evolinux_apt_public_sources: True
evolinux_apt_upgrade: True
evolinux_apt_remove_aptitude: True
# etc-git
evolinux_etcgit_include: True
# hostname
evolinux_hostname_include: True
@ -31,17 +46,6 @@ evolinux_kernel_disable_tcp_timestamps: True
evolinux_kernel_reduce_swapiness: True
evolinux_kernel_cve20165696: True
# apt
evolinux_apt_include: True
evolinux_apt_conf: True
evolinux_apt_hooks: True
evolinux_apt_replace_default_sources: True
evolinux_apt_public_sources: True
evolinux_apt_upgrade: True
evolinux_apt_remove_aptitude: True
# fstab
evolinux_fstab_include: True
@ -96,6 +100,27 @@ evolinux_system_alert5_init: True
evolinux_system_alert5_enable: True
evolinux_system_eni_auto: True
# evomaintenance
evolinux_evomaintenance_include: True
# ssh
evolinux_ssh_include: True
evolix_trusted_ips: []
additional_trusted_ips: []
# Let's merge evolix_trusted_ips with additional_trusted_ips
evolinux_ssh_password_auth_addresses: "{{ evolix_trusted_ips | union(additional_trusted_ips) | unique }}"
evolinux_ssh_match_address: True
evolinux_ssh_disable_acceptenv: True
evolinux_ssh_allow_current_user: False
### disabled because of a memory leak
# # evolinux users
#
# evolinux_users_include: True
# root
evolinux_root_include: True
@ -108,15 +133,7 @@ evolinux_root_gitconfig: True
evolinux_root_bash_history_appendonly: True
evolinux_root_vim_default: True
evolinux_root_vim_conf: True
# ssh
evolinux_ssh_include: True
evolinux_ssh_password_auth_addresses: []
evolinux_ssh_match_address: True
evolinux_ssh_disable_root: True
evolinux_ssh_disable_acceptenv: True
evolinux_root_disable_ssh: False
# postfix
@ -152,3 +169,31 @@ evolinux_hardware_include: True
evolinux_provider_online_include: False
evolinux_provider_orange_fce_include: False
# log2mail
evolinux_log2mail_include: True
# Minifirewall
evolinux_minifirewall_include: True
# Munin
evolinux_munin_include: True
# Nagios/NRPE
evolinux_nagios_nrpe_include: True
# fail2ban
evolinux_fail2ban_include: True
# Listupgrade
evolinux_listupgrade_include: True
# Generate ldif
evolinux_generateldif_include: True

View file

@ -0,0 +1,9 @@
[Unit]
Description=Evolix alert5 script
[Service]
Type=oneshot
ExecStart=/usr/share/scripts/alert5.sh
[Install]
WantedBy=multi-user.target

View file

@ -0,0 +1,31 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1.4.12 (GNU/Linux)
mQENBFHwGLoBCADGXHFostxbz4UzGFYtmox4pvyN1gMhq2KCuQ6f+FESa4HTd9L6
XVhXWPCad3cdxBIls+41+AdZTWxWMu7DUdy8nMU1Ikfw6JeHcSx97G5BdxBVMjK4
iMGfPdLfDgWf4BQ2h0dnTEWobt31WaqgNiNjNrKktqbymmF94pwYkwL53ydIA4zl
8ZQRZooFigkS9WdoKjh30Pv/SWakILSLcSQFHK0dvSkeGd1NxT9dMNPAXXqLom4+
7kCc0s04sS+0DwW16b0Hpb46mtsR9kzOnrE/Smj24uOGzNZen0oCc2Y7bfZlyaN+
RlTkWEze7lemc4Byup/QWkhT0Er8F8uxexy5ABEBAAG0PEhXUmFpZCAoaHR0cDov
L2h3cmFpZC5sZS12ZXJ0Lm5ldCkgPHJvb3RAaHdyYWlkLmxlLXZlcnQubmV0PokB
OAQTAQIAIgUCUfAYugIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQYAUh
DiOz07Rc4Af+N3dEZZHzLNVTjQ0+fCyeg8/flWOkR8DhP10cyoJhSHFTZRdXVshn
kP4VmmUycVeURh76DmrIRe/9Oyca6aGXccRMqvq+HMgBPVwD5qNhcJPIuzqEvmlO
6UIeW2ydil/v1pWu740fGntyFRQcsfqjReVPXw9K588F7MDMyL+31vLm6aorLSzR
hvLhOmGisTs0wg2Oz9f4muauRy6cpQPw/Zi/P/F4WkQYscbHrSbhszj6OIg/vftR
UbZ7QB26/+40B0ag4JzLpmj3scFxf/WdUl5LXazqhsbkurk7huV41BNKXi1+BS3c
x6pFzWEHpiuG1j7U/nScGzEQpsMlUW9D+rkBDQRR8Bi6AQgAuhH1H0VLwcROI/5n
9yTxSbTIZbyhUan3raAbit3pgo0zLagfUtp3vULVnm5ISqQcYFGLZoE1MUkmjGOL
38W0lsIiZTaKOKXxBbLlPhhrvlXnNWAG/S1wnq7K+DV179KCTkUzaLRDbHvv999j
9odBRtAkiTnCfHTMCN4AhydEejNxtlzJo4E5FecH4reimLI5euUdTltgCjixrbsa
KbQftYpSMdXnLy2+00QZoXu0U/h4WZcMhOSEEiyGP9BY6m5G76n03HIeQ6eALDFu
ryAgO+SB9rBrm/VN0kR/TZq0iA3uzLHC7zCw2aImipkr+rIuJOku0wH9MyowBbia
bQtnCQARAQABiQEfBBgBAgAJBQJR8Bi6AhsMAAoJEGAFIQ4js9O0d5YH/3fNQgsC
LvD0g2wdoksv5bG9CUOi9Bs0JHqI0LhXmPvMsbDojZ+zZle7KWNfK2227mWhmoG1
WLujJSmTtxhEO1fXIdYjlDfk2uLJKuFi2wQX9n8dFDUmKY3CUJgeVZof1uQ/5C3D
O06CcuOtf2d/+iijuW112aV1q1hoQqw71ojTET0iIV6lD/0i1eEBSSe1Ohb9yTGR
VxTVrB78zU9hih4/Oq8wJT/Fv25aO1MDSc26CXAg0JA6IWvKal3BSPNhtz4L4FIg
lXleArf9oJqxDO3TsV5zcLyxsIuRuxyP0+AKdSQUqv0dFi4Jf79OmvOmgwydhHjY
+f7quLbwiiDmPbU=
=Yv6D
-----END PGP PUBLIC KEY BLOCK-----

View file

@ -12,6 +12,7 @@ galaxy_info:
- name: Debian
versions:
- jessie
- stretch
dependencies: []
# List your role dependencies here, one per line.

View file

@ -16,7 +16,7 @@
replace: '\1{{ evolinux_fstab_home_options | mandatory }}\3'
notify: remount /home
when:
- "' /home ' in fstab_content.stdout"
- fstab_content.stdout | regex_search('\s/home\s')
- evolinux_fstab_home
- name: /tmp partition is customized
@ -25,7 +25,7 @@
regexp: '([^#]\s+/tmp\s+\S+\s+)([a-z,]+)(\s+)'
replace: '\1{{ evolinux_fstab_tmp_options | mandatory }}\3'
when:
- "' /tmp ' in fstab_content.stdout"
- fstab_content.stdout | regex_search('\s/tmp\s')
- evolinux_fstab_tmp
- name: /usr partition is customized
@ -34,7 +34,7 @@
regexp: '([^#]\s+/usr\s+\S+\s+)([a-z,]+)(\s+)'
replace: '\1{{ evolinux_fstab_usr_options | mandatory }}\3'
when:
- "' /usr ' in fstab_content.stdout"
- fstab_content.stdout | regex_search('\s/usr\s')
- evolinux_fstab_usr
- name: /var partition is customized
@ -44,7 +44,7 @@
replace: '\1{{ evolinux_fstab_var_options | mandatory }}\3'
notify: remount /var
when:
- "' /var ' in fstab_content.stdout"
- fstab_content.stdout | regex_search('\s/var\s')
- evolinux_fstab_var
- name: /var/tmp is created

View file

@ -9,7 +9,7 @@
- name: Check if Broadcom NetXtreme II device is present
shell: "lspci | grep -q 'NetXtreme II'"
check_mode: no
register: broadcom
register: broadcom_netextreme_search
failed_when: False
changed_when: False
@ -22,7 +22,7 @@
tasks_from: basics.yml
vars:
apt_basics_components: "main contrib non-free"
when: broadcom|success
when: broadcom_netextreme_search.rc == 0
## RAID
@ -53,6 +53,18 @@
when: "'Hewlett-Packard Company Smart Array' in raidmodel.stdout"
- block:
- name: Add HW tool GPG key
apt_key:
# url: https://hwraid.le-vert.net/debian/hwraid.le-vert.net.gpg.key
data: "{{ lookup('file', 'hwraid.le-vert.net.gpg.key') }}"
when: ansible_distribution_release == "stretch"
- name: Add HW tool repository
apt_repository:
repo: 'deb http://hwraid.le-vert.net/debian stretch main'
state: present
when: ansible_distribution_release == "stretch"
- name: Install packages for DELL/LSI hardware
apt:
name: "{{ item }}"

View file

@ -5,6 +5,19 @@
when:
- ansible_distribution != "Debian" or ansible_distribution_major_version | version_compare('8', '<')
- name: Apt configuration
include_role:
name: apt
vars:
apt_install_basics: "{{ evolinux_apt_replace_default_sources }}"
apt_install_evolix_public: "{{ evolinux_apt_public_sources }}"
when: evolinux_apt_include
- name: /etc versioning with Git
include_role:
name: etc-git
when: evolinux_etcgit_include
- name: Hostname
include: hostname.yml
when: evolinux_hostname_include
@ -13,10 +26,6 @@
include: kernel.yml
when: evolinux_kernel_include
- name: Apt configuration and packages install
include: apt.yml
when: evolinux_apt_include
- name: Fstab configuration
include: fstab.yml
when: evolinux_fstab_include
@ -29,14 +38,25 @@
include: system.yml
when: evolinux_system_include
- name: Root user configuration
include: root.yml
when: evolinux_root_include
- name: Evomaintenance
include_role:
name: evomaintenance
when: evolinux_evomaintenance_include
- name: SSH configuration
include: ssh.yml
when: evolinux_ssh_include
### disabled because of a memory leak
# - name: Create evolinux users
# include_role:
# name: evolinux-users
# when: evolinux_users_include
- name: Root user configuration
include: root.yml
when: evolinux_root_include
- name: Postfix
include: postfix.yml
when: evolinux_postfix_include
@ -63,4 +83,34 @@
- name: Override Logmail service
include: log2mail.yml
when: evolinux_packages_serveur_base
when: evolinux_log2mail_include
- name: Minifirewall
include_role:
name: minifirewall
when: evolinux_minifirewall_include
- name: Munin
include_role:
name: munin
when: evolinux_munin_include
- name: Nagios/NRPE
include_role:
name: nagios-nrpe
when: evolinux_nagios_nrpe_include
- name: fail2ban
include_role:
name: fail2ban
when: evolinux_fail2ban_include
- name: Listupgrade
include_role:
name: listupgrade
when: evolinux_listupgrade_include
- name: Generate ldif script
include_role:
name: generate-ldif
when: evolinux_generateldif_include

View file

@ -13,6 +13,7 @@
- apg
- conntrack
- logrotate
- bash-completion
- ssl-cert
- ca-certificates
when: evolinux_packages_system

View file

@ -1,6 +1,6 @@
---
- name: packages are installed
- name: Postfix packages are installed
apt:
name: "{{ item }}"
state: present

View file

@ -1,15 +0,0 @@
---
- name: Get mount options for partitions
shell: "mount | grep 'on /usr type'"
args:
warn: no
register: mount
changed_when: False
failed_when: False
when: not ansible_check_mode
- name: Remount /usr if it is a partition and it is not mounted in rw
command: "mount -o remount,rw /usr"
when: mount.rc == 0 and not mount.stdout_lines.0 | search("rw")
args:
warn: no

View file

@ -80,4 +80,23 @@
- "set shiftwidth=4"
when: evolinux_root_vim_conf
- name: disable SSH access for root
replace:
dest: /etc/ssh/sshd_config
regexp: '^PermitRootLogin (yes|without-password|prohibit-password)'
replace: "PermitRootLogin no"
validate: '/usr/sbin/sshd -T -f %s'
notify: reload sshd
when: evolinux_root_disable_ssh
### Disabled : it seems useless and too dangerous for now
# - name: remove root from AllowUsers directive
# replace:
# dest: /etc/ssh/sshd_config
# regexp: '^(AllowUsers ((?!root(?:@\S+)?).)*)(\sroot(?:@\S+)?|root(?:@\S+)?\s)(.*)$'
# replace: '\1\4'
# validate: '/usr/sbin/sshd -T -f %s'
# notify: reload sshd
# when: evolinux_root_disable_ssh
- meta: flush_handlers

View file

@ -3,61 +3,45 @@
msg: "Warning: empty 'evolinux_ssh_password_auth_addresses' variable, tasks will be skipped!"
when: evolinux_ssh_password_auth_addresses == []
- name: Security directives for Evolinux
# From 'man sshd_config' :
# « If all of the criteria on the Match line are satisfied, the keywords
# on the following lines override those set in the global section of the config
# file, until either another Match line or the end of the file.
# If a keyword appears in multiple Match blocks that are satisfied,
# only the first instance of the keyword is applied. »
#
# We want to allow any user from a list of IP addresses to login with password,
# but users of the "evolix" group can't login with password from other IP addresses
- name: Security directives for Evolinux (Debian 9 or later)"
blockinfile:
dest: /etc/ssh/sshd_config
block: |
Match Group evolinux-sudo
PasswordAuthentication no
Match Address {{ evolinux_ssh_password_auth_addresses | join(',') }}
PasswordAuthentication yes
Match Group evolix
PasswordAuthentication no
marker: "# {mark} EVOLINUX PASSWORD RESTRICTIONS"
insertafter: EOF
validate: '/usr/sbin/sshd -T -f %s'
notify: reload sshd
when: not evolinux_ssh_password_auth_addresses == []
when:
- evolinux_ssh_password_auth_addresses != []
- ansible_distribution_major_version | version_compare('9', '>=')
# - name: verify Match Address directive
# command: "grep 'Match Address' /etc/ssh/sshd_config"
# changed_when: False
# failed_when: False
# check_mode: no
# register: grep_matchaddress_ssh
#
# - name: Add Match Address sshd directive
# lineinfile:
# dest: /etc/ssh/sshd_config
# line: "\nMatch Address {{ evolinux_ssh_password_auth_addresses | join(',') }}\n PasswordAuthentication yes"
# insertafter: '# +ForceCommand cvs server'
# validate: '/usr/sbin/sshd -T -f %s'
# notify: reload sshd
# when: evolinux_ssh_match_address and grep_matchaddress_ssh.rc != 0 and evolinux_ssh_password_auth_addresses != []
#
# - name: Modify Match Address sshd directive
# replace:
# dest: /etc/ssh/sshd_config
# regexp: '^(Match Address ((?!{{ item }}).)*)$'
# replace: '\1,{{ item }}'
# validate: '/usr/sbin/sshd -T -f %s'
# with_items: "{{ evolinux_ssh_password_auth_addresses }}"
# notify: reload sshd
# when: evolinux_ssh_match_address and grep_matchaddress_ssh.rc == 0
#
# - name: Add Match Group sudo without password
# lineinfile:
# dest: /etc/ssh/sshd_config
# line: "\nMatch Group sudo\n PasswordAuthentication no"
# insertbefore: '^Match Address'
# validate: '/usr/sbin/sshd -T -f %s'
# notify: reload sshd
- name: disable SSH access for root
replace:
- name: Security directives for Evolinux (Jessie)
blockinfile:
dest: /etc/ssh/sshd_config
regexp: '^PermitRootLogin (yes|without-password)'
replace: "PermitRootLogin no"
block: |
Match Address {{ evolinux_ssh_password_auth_addresses | join(',') }}
PasswordAuthentication yes
marker: "# {mark} EVOLINUX PASSWORD RESTRICTIONS BY ADDRESS"
insertafter: EOF
validate: '/usr/sbin/sshd -T -f %s'
notify: reload sshd
when: evolinux_ssh_disable_root
when:
- evolinux_ssh_password_auth_addresses != []
- ansible_distribution_release == "jessie"
# We disable AcceptEnv because it can be a security issue, but also because we
# do not want clients to push their environment variables like LANG.
@ -77,4 +61,38 @@
notify: reload sshd
when: ansible_distribution_major_version | version_compare('9', '>=')
- name: "Get current user"
command: logname
register: logname
check_mode: no
changed_when: False
when: evolinux_ssh_allow_current_user
# we must double-escape caracters, because python
- name: verify AllowUsers directive
shell: "grep -E '^AllowUsers' /etc/ssh/sshd_config"
changed_when: False
failed_when: False
register: grep_allowusers_ssh
check_mode: no
when: evolinux_ssh_allow_current_user
- name: "Add AllowUsers sshd directive for current user"
lineinfile:
dest: /etc/ssh/sshd_config
line: "\nAllowUsers {{ logname.stdout }}"
insertafter: 'Subsystem'
validate: '/usr/sbin/sshd -T -f %s'
notify: reload sshd
when: evolinux_ssh_allow_current_user and grep_allowusers_ssh.rc != 0
- name: "Modify AllowUsers sshd directive for current user"
replace:
dest: /etc/ssh/sshd_config
regexp: '^(AllowUsers ((?!{{ logname.stdout }}).)*)$'
replace: '\1 {{ logname.stdout }}'
validate: '/usr/sbin/sshd -T -f %s'
notify: reload sshd
when: evolinux_ssh_allow_current_user and grep_allowusers_ssh.rc == 0
- meta: flush_handlers

View file

@ -33,7 +33,8 @@
# TODO : find a way to force the console-data configuration
# non-interactively (like tzdata ↑)
- include: remount_usr_rw.yml
- include_role:
name: remount-usr
- name: Ensure automagic vim conf is disabled
lineinfile:
@ -59,7 +60,7 @@
- name: Set /etc/adduser.conf DIR_MODE to 0700
replace:
dest: /etc/adduser.conf
regexp: "^DIR_MODE=.*$"
regexp: "^DIR_MODE=0755$"
replace: "DIR_MODE=0700"
when: evolinux_system_dirmode_adduser
@ -116,29 +117,56 @@
## alert5
- name: Install alert5 init script
- name: Install alert5 init script (jessie/stretch)
template:
src: system/init_alert5.j2
src: system/alert5.sysvinit.j2
dest: /etc/init.d/alert5
force: no
mode: "0755"
when: evolinux_system_alert5_init
when:
- evolinux_system_alert5_init
- ansible_distribution_release == "jessie" or ansible_distribution_release == "stretch"
#TODO: switch service/systemd modules with Ansible 2.2+
- name: Enable alert5 init script
- name: Enable alert5 init script (jessie/stretch)
service:
name: alert5
enabled: yes
when: evolinux_system_alert5_init and evolinux_system_alert5_enable
when:
- evolinux_system_alert5_init
- evolinux_system_alert5_enable
- ansible_distribution_release == "jessie" or ansible_distribution_release == "stretch"
# - name: Enable alert5 init script
# systemd:
# name: alert5
# daemon_reload: yes
# enabled: yes
# when: evolinux_system_alert5_init and evolinux_system_alert5_enable
- name: Install alert5 init script (buster)
template:
src: system/alert5.sh.j2
dest: /usr/share/scripts/alert5.sh
force: no
mode: "0755"
when:
- evolinux_system_alert5_init
- ansible_distribution_major_version | version_compare('10', '>=')
- name: Install alert5 service (buster)
copy:
src: alert5.service
dest: /etc/systemd/system/alert5.service
force: yes
mode: "0755"
when:
- evolinux_system_alert5_init
- ansible_distribution_major_version | version_compare('10', '>=')
- name: Enable alert5 init script (buster)
systemd:
name: alert5
daemon_reload: yes
enabled: yes
when:
- evolinux_system_alert5_init
- evolinux_system_alert5_enable
- ansible_distribution_major_version | version_compare('10', '>=')
## network interfaces

View file

@ -0,0 +1,7 @@
#!/bin/sh
## sends a mail when booting
date | mail -s'boot/reboot' {{ reboot_alert_email or general_alert_email | mandatory }}
## starts the firewall
#/etc/init.d/minifirewall start

View file

@ -31,7 +31,7 @@ suites:
playbook: ./tests/test.yml
verifier:
patterns:
- admin-users/tests/spec/admin-users_spec.rb
- evolinux-users/tests/spec/evolinux-users_spec.rb
bundler_path: '/usr/local/bin'
rspec_path: '/usr/local/bin'

View file

@ -1,6 +1,6 @@
# admin-users
# evolinux-users
Creates admin users accounts, based on a configuration data structure.
Creates evolinux users accounts, based on a configuration data structure.
## Tasks
@ -8,20 +8,26 @@ Everything is in the `tasks/main.yml` file.
## Available variables
The variable `admin_users` must be a "dict" of one or more users :
The variable `evolinux_users` must be a "dict" of one or more users :
```
admin_users:
evolinux_users:
foo:
name: foo
uid: 1001
fullname: 'Mr Foo'
groups: "baz"
password_hash: 'sdfgsdfgsdfgsdfg'
ssh_key: 'ssh-rsa AZERTYXYZ'
bar:
name: bar
uid: 1002
fullname: 'Mr Bar'
groups:
- "baz"
- "qux"
password_hash: 'gsdfgsdfgsdfgsdf'
ssh_key: 'ssh-rsa QWERTYUIOP'
ssh_keys:
- 'ssh-rsa QWERTYUIOP'
- 'ssh-ed25519 QWERTYUIOP'
```

View file

@ -0,0 +1,4 @@
---
evolinux_users: {}
evolinux_sudo_group: "evolinux-sudo"
evolinux_root_disable_ssh: True

View file

@ -1,6 +1,6 @@
galaxy_info:
author: Evolix
description: Creates admin users accounts.
description: Creates evolinux users accounts.
issue_tracker_url: https://forge.evolix.org/projects/ansible-roles/issues

View file

@ -35,19 +35,22 @@
update_password: on_create
when: loginisbusy.rc != 0 and uidisbusy.rc == 0
- name: "Create {{ admin_users_group }} group (Debian 9 or later)"
- name: "Create secondary groups"
group:
name: "{{ admin_users_group }}"
when: ansible_distribution_major_version | version_compare('9', '>=')
name: "{{ group }}"
with_items: "{{ user.groups }}"
loop_control:
loop_var: group
when: user.groups is defined
- name: "Add user to {{ admin_users_group }} group (Debian 9 or later)"
- name: "Add user '{{ user.name }}' to secondary groups"
user:
name: '{{ user.name }}'
groups: '{{ admin_users_group }}'
groups: "{{ user.groups }}"
append: yes
when: ansible_distribution_major_version | version_compare('9', '>=')
when: user.groups is defined
- name: "Fix perms on homedirectory for '{{ user.name }}'"
- name: "Fix perms on home directory for '{{ user.name }}'"
file:
name: '/home/{{ user.name }}'
mode: "0700"

View file

@ -0,0 +1,20 @@
---
- fail:
msg: only compatible with Debian >= 8
when:
- ansible_distribution != "Debian" or ansible_distribution_major_version | version_compare('8', '<')
- debug:
msg: "Warning: empty 'evolinux_users' variable, tasks will be skipped!"
when: evolinux_users == {}
- name: Create user accounts
include: user.yml
vars:
user: "{{ item.value }}"
with_dict: "{{ evolinux_users }}"
when: evolinux_users != {}
- include: root_disable_ssh.yml
when: evolinux_root_disable_ssh

View file

@ -0,0 +1,16 @@
---
- name: search profile for presence of evomaintenance
command: 'grep -q "trap.*sudo.*evomaintenance.sh"'
changed_when: False
failed_when: False
register: grep_profile_evomaintenance
# Don't add the trap if it is present or commented
- name: "Add evomaintenance trap for '{{ user.name }}'"
lineinfile:
state: present
dest: '/home/{{ user.name }}/.profile'
insertafter: EOF
line: 'trap "sudo /usr/share/scripts/evomaintenance.sh" 0'
when: grep_profile_evomaintenance.rc != 0

View file

@ -0,0 +1,17 @@
---
- name: disable root login
replace:
dest: /etc/ssh/sshd_config
regexp: '^PermitRootLogin (yes|without-password|prohibit-password)'
replace: "PermitRootLogin no"
notify: reload sshd
### Disabled : it seems useless and too dangerous for now
# - name: remove root from AllowUsers directive
# replace:
# dest: /etc/ssh/sshd_config
# regexp: '^(AllowUsers ((?!root(?:@\S+)?).)*)(\sroot(?:@\S+)?|root(?:@\S+)?\s)(.*)$'
# replace: '\1\4'
# validate: '/usr/sbin/sshd -T -f %s'
# notify: reload sshd

View file

@ -14,10 +14,21 @@
user: "{{ user.name }}"
key: "{{ user.ssh_key }}"
state: present
when: user.ssh_key is defined
- name: "Add user's SSH public keys for '{{ user.name }}'"
authorized_key:
user: "{{ user.name }}"
key: "{{ ssk_key }}"
state: present
with_items: "{{ user.ssh_keys }}"
loop_control:
loop_var: ssk_key
when: user.ssh_keys is defined
# we must double-escape caracters, because python
- name: verify AllowUsers directive
shell: "egrep '^AllowUsers' /etc/ssh/sshd_config"
shell: "grep -E '^AllowUsers' /etc/ssh/sshd_config"
changed_when: False
failed_when: False
register: grep_allowusers_ssh
@ -35,32 +46,37 @@
- name: "Modify AllowUsers sshd directive for '{{ user.name }}'"
replace:
dest: /etc/ssh/sshd_config
regexp: '^(AllowUsers ((?!{{ user.name }}).)*)$'
regexp: '^(AllowUsers ((?!\b{{ user.name }}\b).)*)$'
replace: '\1 {{ user.name }}'
validate: '/usr/sbin/sshd -T -f %s'
notify: reload sshd
when: grep_allowusers_ssh.rc == 0
- name: verify Match User directive
- name: "verify Match User directive"
command: "grep 'Match User' /etc/ssh/sshd_config"
changed_when: False
failed_when: False
register: grep_matchuser_ssh
check_mode: no
- name: "Add Match User sshd directive for '{{ user.name }}'"
- name: "Add Match User sshd directive for '{{ user.name }}' (Jessie)"
lineinfile:
dest: /etc/ssh/sshd_config
line: "\nMatch User {{ user.name }}\n PasswordAuthentication no"
insertafter: "# END EVOLINUX PASSWORD RESTRICTIONS BY ADDRESS"
validate: '/usr/sbin/sshd -T -f %s'
notify: reload sshd
when: grep_matchuser_ssh.rc != 0
when:
- ansible_distribution_release == "jessie"
- grep_matchuser_ssh.rc != 0
- name: "Modify Match User's sshd directive for '{{ user.name }}'"
- name: "Modify Match User's sshd directive for '{{ user.name }}' (Jessie)"
replace:
dest: /etc/ssh/sshd_config
regexp: '^(Match User ((?!{{ user.name }}).)*)$'
replace: '\1,{{ user.name }}'
validate: '/usr/sbin/sshd -T -f %s'
notify: reload sshd
when: grep_matchuser_ssh.rc == 0
when:
- ansible_distribution_release == "jessie"
- grep_matchuser_ssh.rc == 0

View file

@ -0,0 +1,18 @@
---
- name: "Verify Evolinux sudoers file presence (jessie)"
template:
src: sudoers_jessie.j2
dest: /etc/sudoers.d/evolinux
force: no
mode: "0440"
validate: '/usr/sbin/visudo -cf %s'
register: copy_sudoers_evolinux
- name: "Add user in sudoers file for '{{ user.name }}' (jessie)"
replace:
dest: /etc/sudoers.d/evolinux
regexp: '^(User_Alias\s+ADMINS\s+=((?!{{ user.name }}).)*)$'
replace: '\1,{{ user.name }}'
validate: '/usr/sbin/visudo -cf %s'
when: not copy_sudoers_evolinux.changed

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