Import the old haproxy role.
This commit is contained in:
parent
df6bcb2b0b
commit
38e97f9d09
64
README.md
64
README.md
|
@ -1,31 +1,59 @@
|
||||||
Role Name
|
Role Name
|
||||||
=========
|
=========
|
||||||
|
|
||||||
A brief description of the role goes here.
|
A role that installs Haproxy, <https://www.haproxy.org>.
|
||||||
|
|
||||||
Requirements
|
|
||||||
------------
|
|
||||||
|
|
||||||
Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required.
|
|
||||||
|
|
||||||
Role Variables
|
Role Variables
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well.
|
The most important variables are listed below:
|
||||||
|
|
||||||
|
``` yaml
|
||||||
|
haproxy_latest_release: True
|
||||||
|
haproxy_version: 2.0
|
||||||
|
haproxy_repo_key: 'http://haproxy.debian.net/bernat.debian.org.gpg'
|
||||||
|
haproxy_debian_latest_repo: "deb http://haproxy.debian.net {{ ansible_lsb.codename }}-backports-{{ haproxy_version }} main"
|
||||||
|
haproxy_ubuntu_latest_repo: "ppa:vbernat/haproxy-{{ haproxy_version }}"
|
||||||
|
haproxy_pkg_state: latest
|
||||||
|
haproxy_enabled: True
|
||||||
|
haproxy_k_bind_non_local_ip: True
|
||||||
|
|
||||||
|
haproxy_default_port: 80
|
||||||
|
haproxy_terminate_tls: False
|
||||||
|
haproxy_ssl_port: 443
|
||||||
|
haproxy_admin_port: 8880
|
||||||
|
haproxy_admin_socket: /run/haproxy/admin.sock
|
||||||
|
|
||||||
|
haproxy_letsencrypt_managed: True
|
||||||
|
haproxy_cert_dir: '{{ pki_dir }}/haproxy'
|
||||||
|
|
||||||
|
haproxy_nagios_check: False
|
||||||
|
# It's a percentage
|
||||||
|
haproxy_nagios_check_w: 70
|
||||||
|
haproxy_nagios_check_c: 90
|
||||||
|
|
||||||
|
haproxy_check_interval: 3s
|
||||||
|
haproxy_backend_maxconn: 2048
|
||||||
|
|
||||||
|
haproxy_sysctl_conntrack_max: 131072
|
||||||
|
```
|
||||||
|
|
||||||
|
Additional tasks
|
||||||
|
------------
|
||||||
|
|
||||||
|
The user of this role will need to write a haproxy.cfg template and install it with a dedicated task. Something like
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: Configure haproxy
|
||||||
|
template: src=haproxy.cfg.j2 dest=/etc/haproxy/haproxy.cfg owner=root group=haproxy mode=0440
|
||||||
|
notify: Reload haproxy
|
||||||
|
tags: [ 'haproxy', 'haproxy_conf' ]
|
||||||
|
```
|
||||||
|
|
||||||
Dependencies
|
Dependencies
|
||||||
------------
|
------------
|
||||||
|
|
||||||
A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles.
|
* letsencrypt-acme-sh
|
||||||
|
|
||||||
Example Playbook
|
|
||||||
----------------
|
|
||||||
|
|
||||||
Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too:
|
|
||||||
|
|
||||||
- hosts: servers
|
|
||||||
roles:
|
|
||||||
- { role: username.rolename, x: 42 }
|
|
||||||
|
|
||||||
License
|
License
|
||||||
-------
|
-------
|
||||||
|
@ -35,4 +63,4 @@ EUPL-1.2
|
||||||
Author Information
|
Author Information
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
An optional section for the role authors to include contact information, or a website (HTML is not allowed).
|
Andrea Dell'Amico, <andrea.dellamico@isti.cnr.it>
|
||||||
|
|
|
@ -1,2 +1,29 @@
|
||||||
---
|
---
|
||||||
# defaults file for ansible-role-template
|
haproxy_latest_release: True
|
||||||
|
haproxy_version: 2.0
|
||||||
|
haproxy_repo_key: 'http://haproxy.debian.net/bernat.debian.org.gpg'
|
||||||
|
haproxy_debian_latest_repo: "deb http://haproxy.debian.net {{ ansible_lsb.codename }}-backports-{{ haproxy_version }} main"
|
||||||
|
haproxy_ubuntu_latest_repo: "ppa:vbernat/haproxy-{{ haproxy_version }}"
|
||||||
|
haproxy_pkg_state: latest
|
||||||
|
haproxy_enabled: True
|
||||||
|
haproxy_k_bind_non_local_ip: True
|
||||||
|
|
||||||
|
haproxy_default_port: 80
|
||||||
|
haproxy_terminate_tls: False
|
||||||
|
haproxy_ssl_port: 443
|
||||||
|
haproxy_admin_port: 8880
|
||||||
|
haproxy_admin_socket: /run/haproxy/admin.sock
|
||||||
|
|
||||||
|
haproxy_letsencrypt_managed: True
|
||||||
|
haproxy_cert_dir: '{{ pki_dir }}/haproxy'
|
||||||
|
|
||||||
|
haproxy_nagios_check: False
|
||||||
|
# It's a percentage
|
||||||
|
haproxy_nagios_check_w: 70
|
||||||
|
haproxy_nagios_check_c: 90
|
||||||
|
|
||||||
|
haproxy_check_interval: 3s
|
||||||
|
haproxy_backend_maxconn: 2048
|
||||||
|
|
||||||
|
haproxy_sysctl_conntrack_max: 131072
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,225 @@
|
||||||
|
#!/usr/bin/env perl
|
||||||
|
# vim: se et ts=4:
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright (C) 2012, Giacomo Montagner <giacomo@entirelyunlike.net>
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify it
|
||||||
|
# under the same terms as Perl 5.10.1.
|
||||||
|
# For more details, see http://dev.perl.org/licenses/artistic.html
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be
|
||||||
|
# useful, but without any warranty; without even the implied
|
||||||
|
# warranty of merchantability or fitness for a particular purpose.
|
||||||
|
#
|
||||||
|
|
||||||
|
our $VERSION = "1.0.1";
|
||||||
|
|
||||||
|
# CHANGELOG:
|
||||||
|
# 1.0.0 - first release
|
||||||
|
# 1.0.1 - fixed empty message if all proxies are OK
|
||||||
|
#
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use 5.010.001;
|
||||||
|
use File::Basename qw/basename/;
|
||||||
|
use IO::Socket::UNIX;
|
||||||
|
use Getopt::Long;
|
||||||
|
|
||||||
|
sub usage {
|
||||||
|
my $me = basename $0;
|
||||||
|
print <<EOU;
|
||||||
|
NAME
|
||||||
|
$me - check haproxy stats for errors, using UNIX socket interface
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
$me [OPTIONS]
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
Get haproxy statistics via UNIX socket and parse information searching for errors.
|
||||||
|
|
||||||
|
OPTIONS
|
||||||
|
-c, --critical
|
||||||
|
Set critical threshold for sessions number (chacks current number of sessions
|
||||||
|
against session limit, if enforced) to the specified percentage.
|
||||||
|
If no session limit (slim) was specified for the given proxy, this option has
|
||||||
|
no effect.
|
||||||
|
|
||||||
|
-d, --dump
|
||||||
|
Just dump haproxy stats and exit;
|
||||||
|
|
||||||
|
-h, --help
|
||||||
|
Print this message.
|
||||||
|
|
||||||
|
-p, --proxy
|
||||||
|
Check only named proxies, not every one. Use comma to separate proxies
|
||||||
|
in list.
|
||||||
|
|
||||||
|
-s, --sock, --socket
|
||||||
|
Use named UNIX socket instead of default (/run/haproxy/admin.sock)
|
||||||
|
|
||||||
|
-w, --warning
|
||||||
|
Set warning threshold for sessions number to the specified percentage (see -c)
|
||||||
|
|
||||||
|
CHECKS AND OUTPUT
|
||||||
|
$me checks every proxy (or the named ones, if -p was given)
|
||||||
|
for status. It returns an error if any of the checked FRONTENDs is not OPEN,
|
||||||
|
any of the checked BACKENDs is not UP, or any of the checkes servers is not UP;
|
||||||
|
$me reports any problem it found.
|
||||||
|
|
||||||
|
EXAMPLES
|
||||||
|
$me -s /var/spool/haproxy/sock
|
||||||
|
Use /var/spool/haproxy/sock to communicate with haproxy.
|
||||||
|
|
||||||
|
$me -p proxy1,proxy2 -w 60 -c 80
|
||||||
|
Check only proxies named "proxy1" and "proxy2", and set sessions number
|
||||||
|
thresholds to 60% and 80%.
|
||||||
|
|
||||||
|
AUTHOR
|
||||||
|
Written by Giacomo Montagner
|
||||||
|
|
||||||
|
REPORTING BUGS
|
||||||
|
Please report any bug to bugs\@entirelyunlike.net
|
||||||
|
|
||||||
|
COPYRIGHT
|
||||||
|
Copyright (C) 2012 Giacomo Montagner <giacomo\@entirelyunlike.net>.
|
||||||
|
$me is distributed under GPL and the Artistic License 2.0
|
||||||
|
|
||||||
|
SEE ALSO
|
||||||
|
Check out online haproxy documentation at <http://haproxy.1wt.eu/>
|
||||||
|
|
||||||
|
EOU
|
||||||
|
}
|
||||||
|
|
||||||
|
my %check_statuses = (
|
||||||
|
UNK => "unknown",
|
||||||
|
INI => "initializing",
|
||||||
|
SOCKERR => "socket error",
|
||||||
|
L4OK => "layer 4 check OK",
|
||||||
|
L4CON => "connection error",
|
||||||
|
L4TMOUT => "layer 1-4 timeout",
|
||||||
|
L6OK => "layer 6 check OK",
|
||||||
|
L6TOUT => "layer 6 (SSL) timeout",
|
||||||
|
L6RSP => "layer 6 protocol error",
|
||||||
|
L7OK => "layer 7 check OK",
|
||||||
|
L7OKC => "layer 7 conditionally OK",
|
||||||
|
L7TOUT => "layer 7 (HTTP/SMTP) timeout",
|
||||||
|
L7RSP => "layer 7 protocol error",
|
||||||
|
L7STS => "layer 7 status error",
|
||||||
|
);
|
||||||
|
|
||||||
|
my @status_names = (qw/OK WARNING CRITICAL UNKNOWN/);
|
||||||
|
|
||||||
|
# Defaults
|
||||||
|
my $swarn = 80.0;
|
||||||
|
my $scrit = 90.0;
|
||||||
|
my $sock = "/run/haproxy/admin.sock";
|
||||||
|
my $dump;
|
||||||
|
my $proxy;
|
||||||
|
my $help;
|
||||||
|
|
||||||
|
# Read command line
|
||||||
|
Getopt::Long::Configure ("bundling");
|
||||||
|
GetOptions (
|
||||||
|
"c|critical=i" => \$scrit,
|
||||||
|
"d|dump" => \$dump,
|
||||||
|
"h|help" => \$help,
|
||||||
|
"p|proxy=s" => \$proxy,
|
||||||
|
"s|sock|socket=s" => \$sock,
|
||||||
|
"w|warning=i" => \$swarn,
|
||||||
|
);
|
||||||
|
|
||||||
|
# Want help?
|
||||||
|
if ($help) {
|
||||||
|
usage;
|
||||||
|
exit 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Connect to haproxy socket and get stats
|
||||||
|
my $haproxy = new IO::Socket::UNIX (
|
||||||
|
Peer => $sock,
|
||||||
|
Type => SOCK_STREAM,
|
||||||
|
);
|
||||||
|
die "Unable to connect to haproxy socket: $@" unless $haproxy;
|
||||||
|
print $haproxy "show stat\n" or die "Print to socket failed: $!";
|
||||||
|
|
||||||
|
# Dump stats and exit if requested
|
||||||
|
if ($dump) {
|
||||||
|
while (<$haproxy>) {
|
||||||
|
print;
|
||||||
|
}
|
||||||
|
exit 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get labels from first output line and map them to their position in the line
|
||||||
|
my $labels = <$haproxy>;
|
||||||
|
chomp($labels);
|
||||||
|
$labels =~ s/^# // or die "Data format not supported.";
|
||||||
|
my @labels = split /,/, $labels;
|
||||||
|
{
|
||||||
|
no strict "refs";
|
||||||
|
my $idx = 0;
|
||||||
|
map { $$_ = $idx++ } @labels;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Variables I will use from here on:
|
||||||
|
our $pxname;
|
||||||
|
our $svname;
|
||||||
|
our $status;
|
||||||
|
|
||||||
|
my @proxies = split ',', $proxy if $proxy;
|
||||||
|
my $exitcode = 0;
|
||||||
|
my $msg;
|
||||||
|
my $checked = 0;
|
||||||
|
while (<$haproxy>) {
|
||||||
|
chomp;
|
||||||
|
next if /^[[:space:]]*$/;
|
||||||
|
my @data = split /,/, $_;
|
||||||
|
if (@proxies) { next unless grep {$data[$pxname] eq $_} @proxies; };
|
||||||
|
|
||||||
|
# Is session limit enforced?
|
||||||
|
our $slim;
|
||||||
|
if ($data[$slim]) {
|
||||||
|
# Check current session # against limit
|
||||||
|
our $scur;
|
||||||
|
my $sratio = $data[$scur]/$data[$slim];
|
||||||
|
if ($sratio >= $scrit || $sratio >= $swarn) {
|
||||||
|
$exitcode = $sratio >= $scrit ? 2 :
|
||||||
|
$exitcode < 2 ? 1 : $exitcode;
|
||||||
|
$msg .= sprintf "%s:%s sessions: %.2f%%; ", $data[$pxname], $data[$svname], $sratio;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check of BACKENDS
|
||||||
|
if ($data[$svname] eq 'BACKEND') {
|
||||||
|
if ($data[$status] ne 'UP') {
|
||||||
|
$msg .= sprintf "BACKEND: %s is %s; ", $data[$pxname], $data[$status];
|
||||||
|
$exitcode = 2;
|
||||||
|
}
|
||||||
|
# Check of FRONTENDS
|
||||||
|
} elsif ($data[$svname] eq 'FRONTEND') {
|
||||||
|
if ($data[$status] ne 'OPEN') {
|
||||||
|
$msg .= sprintf "FRONTEND: %s is %s; ", $data[$pxname], $data[$status];
|
||||||
|
$exitcode = 2;
|
||||||
|
}
|
||||||
|
# Check of servers
|
||||||
|
} else {
|
||||||
|
if ($data[$status] ne 'UP') {
|
||||||
|
next if $data[$status] eq 'no check'; # Ignore server if no check is configured to be run
|
||||||
|
$exitcode = 2;
|
||||||
|
our $check_status;
|
||||||
|
$msg .= sprintf "server: %s:%s is %s", $data[$pxname], $data[$svname], $data[$status];
|
||||||
|
$msg .= sprintf " (check status: %s)", $check_statuses{$data[$check_status]} if $check_statuses{$data[$check_status]};
|
||||||
|
$msg .= "; ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++$checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
unless ($msg) {
|
||||||
|
$msg = @proxies ? sprintf("checked proxies: %s", join ', ', sort @proxies) : "checked $checked proxies.";
|
||||||
|
}
|
||||||
|
say "Check haproxy $status_names[$exitcode] - $msg";
|
||||||
|
exit $exitcode;
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
LE_SERVICES_SCRIPT_DIR=/usr/local/lib/letsencrypt
|
||||||
|
LE_CERTS_DIR=/etc/letsencrypt/live/$HOSTNAME
|
||||||
|
LE_LOG_DIR=/var/log/letsencrypt
|
||||||
|
HAPROXY_CERTDIR=/etc/pki/haproxy
|
||||||
|
HAPROXY_CERTFILE=$HAPROXY_CERTDIR/haproxy.pem
|
||||||
|
DATE=$( date )
|
||||||
|
echo "$DATE" >> $LE_LOG_DIR/haproxy.log
|
||||||
|
|
||||||
|
if [ -f /etc/default/letsencrypt ] ; then
|
||||||
|
. /etc/default/letsencrypt
|
||||||
|
else
|
||||||
|
echo "No letsencrypt default file" >> $LE_LOG_DIR/haproxy.log
|
||||||
|
fi
|
||||||
|
|
||||||
|
[ ! -d $HAPROXY_CERTDIR ] && mkdir $HAPROXY_CERTDIR
|
||||||
|
|
||||||
|
echo "Building the new certificate file" >> $LE_LOG_DIR/haproxy.log
|
||||||
|
cat ${LE_CERTS_DIR}/{fullchain.pem,privkey.pem} > ${HAPROXY_CERTFILE}
|
||||||
|
chmod 440 ${HAPROXY_CERTFILE}
|
||||||
|
chgrp haproxy ${HAPROXY_CERTFILE}
|
||||||
|
|
||||||
|
echo "Reload the haproxy service" >> $LE_LOG_DIR/haproxy.log
|
||||||
|
service haproxy reload >/dev/null 2>&1
|
||||||
|
echo "Done." >> $LE_LOG_DIR/haproxy.log
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
|
|
@ -1,2 +1,13 @@
|
||||||
---
|
---
|
||||||
# handlers file for ansible-role-template
|
- name: Restart haproxy
|
||||||
|
service: name=haproxy state=restarted
|
||||||
|
when: haproxy_enabled
|
||||||
|
|
||||||
|
- name: Reload haproxy
|
||||||
|
service: name=haproxy state=reloaded
|
||||||
|
when: haproxy_enabled
|
||||||
|
|
||||||
|
- name: Reload rsyslog
|
||||||
|
service: name=rsyslog state=reloaded
|
||||||
|
when: haproxy_enabled
|
||||||
|
|
||||||
|
|
|
@ -1,61 +1,23 @@
|
||||||
galaxy_info:
|
galaxy_info:
|
||||||
author: your name
|
author: Andrea Dell'Amico
|
||||||
description: your description
|
description: Systems Architect
|
||||||
company: ISTI-CNR
|
company: ISTI-CNR
|
||||||
|
|
||||||
# If the issue tracker for your role is not on github, uncomment the
|
|
||||||
# next line and provide a value
|
|
||||||
issue_tracker_url: https://redmine-s2i2s.isti.cnr.it/projects/provisioning
|
issue_tracker_url: https://redmine-s2i2s.isti.cnr.it/projects/provisioning
|
||||||
|
|
||||||
# Some suggested licenses:
|
license: EUPL 1.2+
|
||||||
# - BSD (default)
|
|
||||||
# - MIT
|
|
||||||
# - GPLv2
|
|
||||||
# - GPLv3
|
|
||||||
# - Apache
|
|
||||||
# - CC-BY
|
|
||||||
license: EUPL-1.2
|
|
||||||
|
|
||||||
min_ansible_version: 2.8
|
min_ansible_version: 2.8
|
||||||
|
|
||||||
# If this a Container Enabled role, provide the minimum Ansible Container version.
|
|
||||||
# min_ansible_container_version:
|
|
||||||
|
|
||||||
# Optionally specify the branch Galaxy will use when accessing the GitHub
|
|
||||||
# repo for this role. During role install, if no tags are available,
|
|
||||||
# Galaxy will use this branch. During import Galaxy will access files on
|
|
||||||
# this branch. If Travis integration is configured, only notifications for this
|
|
||||||
# branch will be accepted. Otherwise, in all cases, the repo's default branch
|
|
||||||
# (usually master) will be used.
|
|
||||||
#github_branch:
|
|
||||||
|
|
||||||
#
|
|
||||||
# Provide a list of supported platforms, and for each platform a list of versions.
|
|
||||||
# If you don't wish to enumerate all versions for a particular platform, use 'all'.
|
|
||||||
# To view available platforms and versions (or releases), visit:
|
# To view available platforms and versions (or releases), visit:
|
||||||
# https://galaxy.ansible.com/api/v1/platforms/
|
# https://galaxy.ansible.com/api/v1/platforms/
|
||||||
#
|
#
|
||||||
# platforms:
|
platforms:
|
||||||
# - name: Fedora
|
- name: Ubuntu
|
||||||
# versions:
|
versions:
|
||||||
# - all
|
- bionic
|
||||||
# - 25
|
|
||||||
# - name: SomePlatform
|
|
||||||
# versions:
|
|
||||||
# - all
|
|
||||||
# - 1.0
|
|
||||||
# - 7
|
|
||||||
# - 99.99
|
|
||||||
|
|
||||||
galaxy_tags: []
|
galaxy_tags:
|
||||||
# List tags for your role here, one per line. A tag is a keyword that describes
|
- haproxy
|
||||||
# and categorizes the role. Users find roles by searching for tags. Be sure to
|
|
||||||
# remove the '[]' above, if you add tags to this list.
|
|
||||||
#
|
|
||||||
# NOTE: A tag is limited to a single word comprised of alphanumeric characters.
|
|
||||||
# Maximum 20 tags per role.
|
|
||||||
|
|
||||||
dependencies: []
|
dependencies: []
|
||||||
# List your role dependencies here, one per line. Be sure to remove the '[]' above,
|
|
||||||
# if you add dependencies to this list.
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
---
|
||||||
|
- block:
|
||||||
|
- name: Create the acme hooks directory if it does not yet exist
|
||||||
|
file: dest={{ letsencrypt_acme_sh_services_scripts_dir }} state=directory owner=root group=root
|
||||||
|
|
||||||
|
- name: Install a script that fix the letsencrypt certificate for haproxy and then reload the service
|
||||||
|
template: src=haproxy-letsencrypt-acme.sh.j2 dest={{ letsencrypt_acme_sh_services_scripts_dir }}/haproxy owner=root group=root mode=4555
|
||||||
|
|
||||||
|
- name: When we are going to install letsencrypt certificates, create a preliminary path and a self signed cert. Now handle the haproxy special case
|
||||||
|
shell: mkdir {{ pki_dir }}/haproxy ; cat {{ letsencrypt_acme_user_home | default(omit) }}/live/{{ ansible_fqdn }}/privkey {{ letsencrypt_acme_user_home | default(omit) }}/live/{{ ansible_fqdn }}/cert > {{ pki_dir }}/haproxy/haproxy.pem
|
||||||
|
args:
|
||||||
|
creates: '{{ pki_dir }}/haproxy/haproxy.pem'
|
||||||
|
tags: [ 'pki', 'ssl', 'letsencrypt', 'haproxy', 'letsencrypt_acme_sh' ]
|
||||||
|
|
||||||
|
when: letsencrypt_acme_sh_install
|
||||||
|
tags: [ 'haproxy', 'letsencrypt', 'letsencrypt_acme_sh' ]
|
|
@ -0,0 +1,10 @@
|
||||||
|
---
|
||||||
|
- name: Install the haproxy NRPE nagios check
|
||||||
|
copy: src=check_haproxy_stats dest={{ nagios_local_plugdir }}/check_haproxy_stats owner=root group=root mode=0555
|
||||||
|
when: haproxy_nagios_check
|
||||||
|
|
||||||
|
- name: Install the haproxy NRPE command configuration
|
||||||
|
template: src=lb.cfg.j2 dest={{ nrpe_include_dir }}/lb.cfg owner=root group=root mode=0444
|
||||||
|
notify: Reload NRPE server
|
||||||
|
when: haproxy_nagios_check
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
---
|
||||||
|
- name: Get the haproxy repo key
|
||||||
|
apt_key: url={{ haproxy_repo_key }} state=present
|
||||||
|
when: haproxy_latest_release
|
||||||
|
tags: haproxy
|
||||||
|
|
||||||
|
- name: Define the haproxy repository
|
||||||
|
apt_repository: repo='{{ haproxy_ubuntu_latest_repo }}' state=present update_cache=yes
|
||||||
|
when:
|
||||||
|
- haproxy_latest_release
|
||||||
|
- is_ubuntu
|
||||||
|
tags: haproxy
|
||||||
|
|
||||||
|
- name: Define the haproxy repository
|
||||||
|
apt_repository: repo='{{ haproxy_debian_latest_repo }}' state=present update_cache=yes
|
||||||
|
when:
|
||||||
|
- haproxy_latest_release
|
||||||
|
- is_debian
|
||||||
|
tags: haproxy
|
||||||
|
|
||||||
|
- name: Install the haproxy package
|
||||||
|
apt: name=haproxy state=present default_release={{ ansible_lsb.codename }}-backports update_cache=yes cache_valid_time=3600
|
||||||
|
when: not haproxy_latest_release
|
||||||
|
register: install_haproxy
|
||||||
|
tags: haproxy
|
||||||
|
|
||||||
|
- name: Install the haproxy package
|
||||||
|
apt: name=haproxy state=latest default_release={{ ansible_lsb.codename }}-backports-{{ haproxy_version }} update_cache=yes cache_valid_time=3600
|
||||||
|
when:
|
||||||
|
- haproxy_latest_release
|
||||||
|
- is_debian
|
||||||
|
register: install_haproxy
|
||||||
|
tags: haproxy
|
||||||
|
|
||||||
|
- name: Install the haproxy package
|
||||||
|
apt: name=haproxy state=latest update_cache=yes cache_valid_time=3600
|
||||||
|
when:
|
||||||
|
- haproxy_latest_release
|
||||||
|
- is_ubuntu
|
||||||
|
register: install_haproxy
|
||||||
|
tags: haproxy
|
||||||
|
|
||||||
|
- name: Enable kernel binding non local IP addresses
|
||||||
|
sysctl: name={{ item }} value=1 reload=yes state=present
|
||||||
|
with_items:
|
||||||
|
- net.ipv4.ip_nonlocal_bind
|
||||||
|
when: haproxy_k_bind_non_local_ip
|
||||||
|
tags: [ 'haproxy', 'haproxy_sysctl' ]
|
||||||
|
|
||||||
|
- name: Disable kernel binding non local IP addresses
|
||||||
|
sysctl: name={{ item }} value=0 reload=yes state=present
|
||||||
|
with_items:
|
||||||
|
- net.ipv4.ip_nonlocal_bind
|
||||||
|
when: not haproxy_k_bind_non_local_ip
|
||||||
|
tags: [ 'haproxy', 'haproxy_sysctl' ]
|
||||||
|
|
||||||
|
- name: Increase the connection tracking table capacity
|
||||||
|
sysctl: name={{ item }} value={{ haproxy_sysctl_conntrack_max }} reload=yes state=present
|
||||||
|
with_items:
|
||||||
|
- net.nf_conntrack_max
|
||||||
|
when: is_not_debian9
|
||||||
|
tags: [ 'haproxy', 'haproxy_sysctl' ]
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
---
|
||||||
|
- block:
|
||||||
|
- name: Install the socat binary needed to talk to the haproxy socket
|
||||||
|
apt: name=socat state=latest update_cache=yes cache_valid_time=3600
|
||||||
|
|
||||||
|
- name: Install a script that refreshes the OCSP configuration and reloads haproxy if needed
|
||||||
|
template: src=hapos-upd.j2 dest=/usr/local/bin/hapos-upd owner=root group=root mode=0755
|
||||||
|
|
||||||
|
- name: Install a cron job that refreshes the OCSP configuration
|
||||||
|
cron:
|
||||||
|
name: "Refresh the haproxy OCSP information"
|
||||||
|
user: root
|
||||||
|
special_time: daily
|
||||||
|
job: "/usr/local/bin/hapos-upd --cert {{ haproxy_cert_dir }}/haproxy.pem -v {{ letsencrypt_acme_certs_dir }}/fullchain -s {{ haproxy_admin_socket }} >/var/log/hapos-upd.log 2>&1"
|
||||||
|
|
||||||
|
tags: [ 'haproxy', 'letsencrypt', 'ssl', 'ssl_ocsp' ]
|
||||||
|
|
|
@ -1,2 +1,32 @@
|
||||||
---
|
---
|
||||||
# tasks file for ansible-role-template
|
- import_tasks: haproxy-service.yml
|
||||||
|
- import_tasks: haproxy-letsencrypt-acme-sh.yml
|
||||||
|
when:
|
||||||
|
- haproxy_letsencrypt_managed
|
||||||
|
- letsencrypt_acme_sh_install
|
||||||
|
- import_tasks: haproxy-ssl.yml
|
||||||
|
when:
|
||||||
|
- haproxy_letsencrypt_managed
|
||||||
|
|
||||||
|
- import_tasks: haproxy-nagios.yml
|
||||||
|
when:
|
||||||
|
- nagios_enabled is defined
|
||||||
|
- nagios_enabled
|
||||||
|
|
||||||
|
- name: Ensure that haproxy is enabled and started
|
||||||
|
service: name=haproxy state=restarted enabled=yes
|
||||||
|
when: haproxy_enabled
|
||||||
|
ignore_errors: True
|
||||||
|
tags: haproxy
|
||||||
|
|
||||||
|
- name: Haproxy puts a new rsyslog directive. Restart rsyslog to activate it. Reload is not sufficient
|
||||||
|
service: name=rsyslog state=restarted
|
||||||
|
when:
|
||||||
|
- haproxy_enabled
|
||||||
|
- install_haproxy is changed
|
||||||
|
tags: haproxy
|
||||||
|
|
||||||
|
- name: Ensure that haproxy is stopped and disabled if needed
|
||||||
|
service: name=haproxy state=stopped enabled=no
|
||||||
|
when: not haproxy_enabled
|
||||||
|
tags: haproxy
|
||||||
|
|
|
@ -0,0 +1,571 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# HAProxy OCSP Stapling Updater
|
||||||
|
# Copyright (c) 2015 Pier Carlo Chiodi - http://www.pierky.com
|
||||||
|
#
|
||||||
|
# https://github.com/pierky/haproxy-ocsp-stapling-updater
|
||||||
|
|
||||||
|
set -o nounset
|
||||||
|
|
||||||
|
VERSION="0.4.1-pre1"
|
||||||
|
|
||||||
|
PROGNAME="hapos-upd"
|
||||||
|
|
||||||
|
if [ -z ${OPENSSL_BIN+x} ]; then
|
||||||
|
OPENSSL_BIN="openssl"
|
||||||
|
fi
|
||||||
|
|
||||||
|
SOCAT_BIN="socat"
|
||||||
|
|
||||||
|
CERT=""
|
||||||
|
VAFILE=""
|
||||||
|
HAPROXY_ADMIN_SOCKET_DEFAULT="/run/haproxy/admin.sock"
|
||||||
|
HAPROXY_ADMIN_SOCKET="$HAPROXY_ADMIN_SOCKET_DEFAULT"
|
||||||
|
GOOD_ONLY=0
|
||||||
|
SYSLOG_PRIO=""
|
||||||
|
DEBUG=0
|
||||||
|
KEEP_TEMP=0
|
||||||
|
OCSP_URL=""
|
||||||
|
OCSP_HOST=""
|
||||||
|
VERIFY=1
|
||||||
|
TMP=""
|
||||||
|
SKIP_UPDATE=0
|
||||||
|
PARTIAL_CHAIN=""
|
||||||
|
|
||||||
|
function Quit() {
|
||||||
|
if [ $KEEP_TEMP -eq 0 ]; then
|
||||||
|
if [ -n "$TMP" ]; then
|
||||||
|
rm -r $TMP &>/dev/null
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
exit $1
|
||||||
|
}
|
||||||
|
|
||||||
|
function LogError() {
|
||||||
|
MSG="$1"
|
||||||
|
|
||||||
|
if [ -z "$SYSLOG_PRIO" ]; then
|
||||||
|
echo "$MSG" >&2
|
||||||
|
else
|
||||||
|
logger -p "$SYSLOG_PRIO" -s -- "$PROGNAME - $MSG"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$MSG" >>$TMP/log
|
||||||
|
}
|
||||||
|
|
||||||
|
function Error() {
|
||||||
|
if [ $1 -eq 9 ]; then
|
||||||
|
MSG="Error: $2"
|
||||||
|
else
|
||||||
|
MSG="Error processing '$CERT': $2"
|
||||||
|
fi
|
||||||
|
|
||||||
|
LogError "$MSG"
|
||||||
|
|
||||||
|
if [ $1 -eq 9 ]; then
|
||||||
|
echo "Run $PROGNAME -h for help" >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
Quit $1
|
||||||
|
}
|
||||||
|
|
||||||
|
function Debug() {
|
||||||
|
if [ $DEBUG -eq 1 ]; then
|
||||||
|
echo "$1"
|
||||||
|
fi
|
||||||
|
echo "$1" >>$TMP/log
|
||||||
|
}
|
||||||
|
|
||||||
|
function Trap() {
|
||||||
|
Debug "Aborting"
|
||||||
|
Quit 9
|
||||||
|
}
|
||||||
|
|
||||||
|
function Usage() {
|
||||||
|
echo "
|
||||||
|
HAProxy OCSP Stapling Updater - $VERSION
|
||||||
|
Copyright (c) 2015 Pier Carlo Chiodi - http://www.pierky.com
|
||||||
|
|
||||||
|
https://github.com/pierky/haproxy-ocsp-stapling-updater
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
$PROGNAME [options] --cert crt_full_path
|
||||||
|
|
||||||
|
This script extracts and queries the OCSP server present in a
|
||||||
|
certificate to obtain its revocation status, then updates HAProxy by
|
||||||
|
writing the '.issuer' and the '.ocsp' files and by sending it the
|
||||||
|
'set ssl ocsp-response' command through the local UNIX admin socket.
|
||||||
|
|
||||||
|
The crt_full_path argument is the full path to the certificate bundle
|
||||||
|
used in haproxy 'crt' setting. End-entity (EE) certificate plus any
|
||||||
|
intermediate CA certificates must be concatenated there.
|
||||||
|
An OCSP query is sent to the OCSP server given on the command line
|
||||||
|
(--ocsp-url and --ocsp-host argument); if these arguments are missing,
|
||||||
|
URL and Host header values are automatically extracted from the
|
||||||
|
certificate.
|
||||||
|
If the '.issuer' file already exists it's used to build the OCSP
|
||||||
|
request, otherwise the chain is extracted from crt_full_path and used
|
||||||
|
to identify the issuer.
|
||||||
|
Finally, it writes the related '.issuer' and .'ocsp' files and updates
|
||||||
|
haproxy, using 'socat' and the local UNIX socket (--socket argument,
|
||||||
|
default $HAPROXY_ADMIN_SOCKET_DEFAULT).
|
||||||
|
|
||||||
|
Exit codes:
|
||||||
|
0 OK
|
||||||
|
1 openssl certificates handling error
|
||||||
|
2 OCSP server URL not found
|
||||||
|
3 string parsing / PEM manipulation error
|
||||||
|
4 OCSP error
|
||||||
|
5 haproxy management error
|
||||||
|
9 program error (wrong arguments, missing dependencies)
|
||||||
|
|
||||||
|
Options:
|
||||||
|
|
||||||
|
-d, --debug : don't do anything, print debug messages only.
|
||||||
|
|
||||||
|
--keep-temp : keep temporary directory after exiting (for
|
||||||
|
debug purposes).
|
||||||
|
|
||||||
|
-g, --good-only : do not update haproxy if OCSP response
|
||||||
|
certificate status value is not 'good'.
|
||||||
|
|
||||||
|
-l, --syslog priority : log errors to syslog system log module.
|
||||||
|
The priority may be specified numerically
|
||||||
|
or as a facility.level pair (e.g.
|
||||||
|
local7.error).
|
||||||
|
|
||||||
|
--ocsp-url url : OCSP server URL; use this instead of the
|
||||||
|
one in the EE certificate.
|
||||||
|
|
||||||
|
--ocsp-host host : OCSP server hostname to be used in the
|
||||||
|
'Host:' header; use this instead of the one
|
||||||
|
extracted from the OCSP server URL.
|
||||||
|
|
||||||
|
--partial-chain : Allow partial certificate chain if at least one certificate
|
||||||
|
is in trusted store. Useful when validating an intermediate
|
||||||
|
certificate without the root CA.
|
||||||
|
|
||||||
|
-s, --socket file : haproxy admin socket. If omitted,
|
||||||
|
$HAPROXY_ADMIN_SOCKET_DEFAULT is used by default.
|
||||||
|
This script is distributed with only one
|
||||||
|
method to update haproxy: using 'socat'
|
||||||
|
with a local admin-level UNIX socket.
|
||||||
|
Feel free to implement other mechanisms as
|
||||||
|
needed! The right section in the code is
|
||||||
|
\"UPDATE HAPROXY\", at the end of the script.
|
||||||
|
|
||||||
|
-v, --VAfile file : same as the openssl ocsp -VAfile option
|
||||||
|
with 'file' as argument. For more details:
|
||||||
|
'man ocsp'.
|
||||||
|
If file = \"-\" then the chain extracted
|
||||||
|
from the certificate's bundle (or .issuer
|
||||||
|
file) is used (useful for OCSP responses
|
||||||
|
that don't include the signer certificate).
|
||||||
|
|
||||||
|
--noverify : Do not verify OCSP response.
|
||||||
|
|
||||||
|
-S, --skip-update : Do not notify haproxy of the new OCSP response.
|
||||||
|
|
||||||
|
-h, --help : this help."
|
||||||
|
}
|
||||||
|
|
||||||
|
trap Trap INT TERM
|
||||||
|
|
||||||
|
TMP="`mktemp -d -q -t $PROGNAME.XXXXXXXXXX`"
|
||||||
|
|
||||||
|
# COMMAND LINE PROCESSING
|
||||||
|
# ----------------------------------
|
||||||
|
|
||||||
|
while [[ $# > 0 ]]
|
||||||
|
do
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
-h|--help)
|
||||||
|
Usage
|
||||||
|
Quit 0
|
||||||
|
;;
|
||||||
|
|
||||||
|
-d|--debug)
|
||||||
|
DEBUG=1
|
||||||
|
;;
|
||||||
|
|
||||||
|
--keep-temp)
|
||||||
|
KEEP_TEMP=1
|
||||||
|
;;
|
||||||
|
|
||||||
|
-g|--good-only)
|
||||||
|
GOOD_ONLY=1
|
||||||
|
;;
|
||||||
|
|
||||||
|
--noverify)
|
||||||
|
VERIFY=0
|
||||||
|
;;
|
||||||
|
|
||||||
|
--partial-chain)
|
||||||
|
PARTIAL_CHAIN="-partial_chain"
|
||||||
|
;;
|
||||||
|
|
||||||
|
-l|--syslog)
|
||||||
|
if [ $# -le 1 ]; then
|
||||||
|
Error 9 "mandatory value is missing for $1 argument"
|
||||||
|
fi
|
||||||
|
SYSLOG_PRIO="$2"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
|
||||||
|
--ocsp-url)
|
||||||
|
if [ $# -le 1 ]; then
|
||||||
|
Error 9 "mandatory value is missing for $1 argument"
|
||||||
|
fi
|
||||||
|
OCSP_URL="$2"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
|
||||||
|
--ocsp-host)
|
||||||
|
if [ $# -le 1 ]; then
|
||||||
|
Error 9 "mandatory value is missing for $1 argument"
|
||||||
|
fi
|
||||||
|
OCSP_HOST="$2"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
|
||||||
|
-c|--cert)
|
||||||
|
if [ $# -le 1 ]; then
|
||||||
|
Error 9 "mandatory value is missing for $1 argument"
|
||||||
|
fi
|
||||||
|
CERT="$2"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
|
||||||
|
-v|--VAfile)
|
||||||
|
if [ $# -le 1 ]; then
|
||||||
|
Error 9 "mandatory value is missing for $1 argument"
|
||||||
|
fi
|
||||||
|
VAFILE="$2"
|
||||||
|
if [ "$VAFILE" == "-" ]; then
|
||||||
|
VAFILE="$TMP/chain.pem"
|
||||||
|
else
|
||||||
|
if [ ! -e "$VAFILE" ]; then
|
||||||
|
Error 9 "VAfile does not exists: $VAFILE"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
|
||||||
|
-s|--socket)
|
||||||
|
if [ $# -le 1 ]; then
|
||||||
|
Error 9 "mandatory value is missing for $1 argument"
|
||||||
|
fi
|
||||||
|
HAPROXY_ADMIN_SOCKET="$2"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
|
||||||
|
-S|--skip-update)
|
||||||
|
SKIP_UPDATE=1
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
Error 9 "unknown option: $1"
|
||||||
|
esac
|
||||||
|
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
Debug "Temporary directory: $TMP"
|
||||||
|
|
||||||
|
$OPENSSL_BIN version | grep OpenSSL &>>$TMP/log
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
Error 9 "openssl binary not found; adjust OPENSSL_BIN variable in the script"
|
||||||
|
fi
|
||||||
|
|
||||||
|
$SOCAT_BIN -V | grep socat &>>$TMP/log
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
Error 9 "socat binary not found; adjust SOCAT_BIN variable in the script"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$CERT" ]; then
|
||||||
|
Error 9 "certificate not provided (--cert argument)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# CURRENT RESPONSE EXPIRED?
|
||||||
|
# ----------------------------------
|
||||||
|
|
||||||
|
ISNEW=1
|
||||||
|
if [ -e $CERT.ocsp ]; then
|
||||||
|
ISNEW=0
|
||||||
|
Debug "An OCSP response already exists: checking its expiration."
|
||||||
|
|
||||||
|
$OPENSSL_BIN ocsp -respin $CERT.ocsp -text -noverify | \
|
||||||
|
grep "Next Update:" &>>$TMP/log
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
CURR_EXP=`$OPENSSL_BIN ocsp -respin $CERT.ocsp -text -noverify | grep "Next Update:" | cut -d ':' -f 2-`
|
||||||
|
CURR_EXP_EPOCH=`date --date="$CURR_EXP" +%s`
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
Error 3 "can't parse Next Update from current OCSP response"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $CURR_EXP_EPOCH -lt `date +%s` ]; then
|
||||||
|
Debug "Current OCSP response expiration: $CURR_EXP - expired"
|
||||||
|
LogError "current OCSP response is expired: please consider running this script more frequently"
|
||||||
|
else
|
||||||
|
Debug "Current OCSP response expiration: $CURR_EXP - NOT expired"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# EXTRACT EE CERTIFICATE INFO
|
||||||
|
# ----------------------------------
|
||||||
|
|
||||||
|
# extract EE certificate
|
||||||
|
$OPENSSL_BIN x509 -in $CERT -outform PEM -out $TMP/ee.pem &>>$TMP/log
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
Error 1 "can't extract EE certificate from $CERT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# get OCSP server URL
|
||||||
|
if [ -z "$OCSP_URL" ]; then
|
||||||
|
OCSP_URL="`$OPENSSL_BIN x509 -in $TMP/ee.pem -ocsp_uri -noout`"
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
Error 1 "can't obtain OCSP server URL from $CERT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$OCSP_URL" ]; then
|
||||||
|
Error 2 "OCSP server URL not found in the EE certificate"
|
||||||
|
fi
|
||||||
|
|
||||||
|
Debug "OCSP server URL found: $OCSP_URL"
|
||||||
|
else
|
||||||
|
Debug "Using OCSP server URL from command line: $OCSP_URL"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# check OCSP server URL format (http:// or https://)
|
||||||
|
echo "$OCSP_URL" | egrep -i "(http://|https://)" &>/dev/null
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
Error 3 "OCSP server URL not in http[s]:// format"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# get OCSP server URL host name
|
||||||
|
if [ -z "$OCSP_HOST" ]; then
|
||||||
|
OCSP_HOST="`echo "$OCSP_URL" | egrep -i "(http://|https://)" | cut -d'/' -f 3`"
|
||||||
|
|
||||||
|
if [ $? -ne 0 -o -z "$OCSP_HOST" ]; then
|
||||||
|
Error 3 "can't extract hostname from OCSP server URL $OCSP_URL"
|
||||||
|
fi
|
||||||
|
|
||||||
|
Debug "OCSP server hostname: $OCSP_HOST"
|
||||||
|
else
|
||||||
|
Debug "Using OCSP server hostname from command line: $OCSP_HOST"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# EXTRACT CHAIN INFO
|
||||||
|
# ----------------------------------
|
||||||
|
|
||||||
|
if [ -e $CERT.issuer ]; then
|
||||||
|
Debug "Using existing chain ($CERT.issuer)"
|
||||||
|
|
||||||
|
# copy .issuer file to temporary chain.pem
|
||||||
|
cp $CERT.issuer $TMP/chain.pem &>>$TMP/log
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
Error 3 "can't copy current chain from $CERT.issuer"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
Debug "Extracting chain from certificates bundle"
|
||||||
|
|
||||||
|
# get EE certificate's fingerprint
|
||||||
|
FP_EE="`$OPENSSL_BIN x509 -fingerprint -noout -in $TMP/ee.pem`"
|
||||||
|
|
||||||
|
if [ $? -ne 0 -o -z "$FP_EE" ]; then
|
||||||
|
Error 1 "can't obtain EE certificate's fingerprint"
|
||||||
|
fi
|
||||||
|
|
||||||
|
Debug "EE certificate's fingerprint: $FP_EE"
|
||||||
|
|
||||||
|
# get BEGIN CERTIFICATE and END CERTIFICATE separators
|
||||||
|
PEM_BEGIN_CERT="`head $TMP/ee.pem -n 1`"
|
||||||
|
PEM_END_CERT="`tail $TMP/ee.pem -n 1`"
|
||||||
|
|
||||||
|
# get number of certificates in the bundle file
|
||||||
|
NUM_OF_CERTS=`cat $CERT | grep -e "$PEM_BEGIN_CERT" | wc -l`
|
||||||
|
|
||||||
|
if [ $NUM_OF_CERTS -le 1 ]; then
|
||||||
|
Error 3 "can't obtain the number of certificates in the chain"
|
||||||
|
fi
|
||||||
|
|
||||||
|
Debug "$NUM_OF_CERTS certificates found in the bundle"
|
||||||
|
|
||||||
|
# save each certificate in the bundle into $TMP/chain-X.pem
|
||||||
|
cat $CERT | \
|
||||||
|
sed -n -e "/$PEM_BEGIN_CERT/,/$PEM_END_CERT/p" | \
|
||||||
|
awk "/$PEM_BEGIN_CERT/{x=\"$TMP/chain-\" ++i \".pem\";}{print > x;}" &>>$TMP/log
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
Error 3 "can't extract certificates from bundle"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# for each certificate that is extracted from the bundle check if
|
||||||
|
# it's the EE certificate, otherwise uses it to build the chain file
|
||||||
|
for c in `seq 1 $NUM_OF_CERTS`;
|
||||||
|
do
|
||||||
|
# check fingerprint of current and EE certificates
|
||||||
|
FP="`$OPENSSL_BIN x509 -fingerprint -noout -in $TMP/chain-$c.pem`"
|
||||||
|
if [ $? -ne 0 -o -z "$FP" ]; then
|
||||||
|
Error 1 "can't obtain the fingerprint of the certificate n. $c in the bundle"
|
||||||
|
else
|
||||||
|
if [ ! "$FP" == "$FP_EE" ]; then
|
||||||
|
Debug "Bundle certificate n. $c fingerprint: $FP - it's part of the chain"
|
||||||
|
|
||||||
|
# current certificate is not the same as the EE; append to the chain
|
||||||
|
cat $TMP/chain-$c.pem >> $TMP/chain.pem
|
||||||
|
else
|
||||||
|
Debug "Bundle certificate n. $c fingerprint: $FP - EE certificate"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# check if the EE certificate validates against the chain
|
||||||
|
$OPENSSL_BIN verify $PARTIAL_CHAIN -CAfile $TMP/chain.pem $TMP/ee.pem &>>$TMP/log
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
if [ -e $CERT.issuer ]; then
|
||||||
|
Error 1 "can't validate the EE certificate against the existing chain; if it has been changed recently consider removing the current $CERT.issuer file and let this script to figure out a new one"
|
||||||
|
else
|
||||||
|
Error 1 "can't validate the EE certificate against the extracted chain"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# OCSP
|
||||||
|
# ----------------------------------
|
||||||
|
|
||||||
|
# query the OCSP server and save its response
|
||||||
|
|
||||||
|
$OPENSSL_BIN version | grep "OpenSSL 1.0" &>/dev/null
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
# OpenSSL 1.0.x
|
||||||
|
|
||||||
|
$OPENSSL_BIN ocsp $PARTIAL_CHAIN -issuer $TMP/chain.pem -cert $TMP/ee.pem \
|
||||||
|
-respout $TMP/ocsp.der -noverify \
|
||||||
|
-no_nonce -url $OCSP_URL -header "Host" "$OCSP_HOST" &>>$TMP/log
|
||||||
|
else
|
||||||
|
$OPENSSL_BIN ocsp $PARTIAL_CHAIN -issuer $TMP/chain.pem -cert $TMP/ee.pem \
|
||||||
|
-respout $TMP/ocsp.der -noverify \
|
||||||
|
-no_nonce -url $OCSP_URL -header "Host=$OCSP_HOST" &>>$TMP/log
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
Error 1 "can't receive the OCSP server response"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# process the OCSP response
|
||||||
|
VERIFYOPT=""
|
||||||
|
if [ $VERIFY -eq 0 ]; then
|
||||||
|
VERIFYOPT="-noverify"
|
||||||
|
fi
|
||||||
|
if [ -z "$VAFILE" ]; then
|
||||||
|
$OPENSSL_BIN ocsp $PARTIAL_CHAIN $VERIFYOPT -issuer $TMP/chain.pem -cert $TMP/ee.pem \
|
||||||
|
-respin $TMP/ocsp.der -no_nonce -CAfile $TMP/chain.pem \
|
||||||
|
-out $TMP/ocsp.txt &>>$TMP/ocsp-verify.txt
|
||||||
|
else
|
||||||
|
$OPENSSL_BIN ocsp $PARTIAL_CHAIN $VERIFYOPT -issuer $TMP/chain.pem -cert $TMP/ee.pem \
|
||||||
|
-respin $TMP/ocsp.der -no_nonce -CAfile $TMP/chain.pem \
|
||||||
|
-VAfile $VAFILE \
|
||||||
|
-out $TMP/ocsp.txt &>>$TMP/ocsp-verify.txt
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
Error 1 "can't receive OCSP response"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $VERIFY -eq 1 ]; then
|
||||||
|
Debug "OCSP response verification results: `cat $TMP/ocsp-verify.txt`"
|
||||||
|
|
||||||
|
cat $TMP/ocsp-verify.txt | grep "Response verify OK" &>>$TMP/log
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
grep "signer certificate not found" $TMP/ocsp-verify.txt &>/dev/null
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
Error 4 "OCSP response verification failure: signer certificate not found; try with '--VAfile -' or '--VAfile OCSP-response-signing-certificate-file' arguments"
|
||||||
|
else
|
||||||
|
Error 4 "OCSP response verification failure."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
Debug "OCSP response: `cat $TMP/ocsp.txt`"
|
||||||
|
|
||||||
|
if [ $GOOD_ONLY -eq 1 ]; then
|
||||||
|
cat $TMP/ocsp.txt | head -n 1 | grep ": good" &>>$TMP/log
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
Error 4 "OCSP response, certificate status not good"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# UPDATE HAPROXY
|
||||||
|
# ----------------------------------
|
||||||
|
|
||||||
|
# Status:
|
||||||
|
# - $TMP/ocsp.der contains the OCSP response, DER format
|
||||||
|
# - $TMP/ocsp.txt contains the textual OCSP response as produced
|
||||||
|
# by openssl
|
||||||
|
# - the OCSP response has been verified against the chain or
|
||||||
|
# the --VAfile
|
||||||
|
|
||||||
|
if [ $DEBUG -eq 0 ]; then
|
||||||
|
# update .ocsp and .issuer files
|
||||||
|
|
||||||
|
cp $TMP/ocsp.der $CERT.ocsp &>>$TMP/log
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
Error 5 "can't update $CERT.ocsp file"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -e $CERT.issuer ]; then
|
||||||
|
cp $TMP/chain.pem $CERT.issuer &>>$TMP/log
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
Error 5 "can't update $CERT.issuer file"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $SKIP_UPDATE -eq 0 ]; then
|
||||||
|
if [ $ISNEW -eq 1 ]; then
|
||||||
|
# no .ocsp file found, maybe it's an initial run
|
||||||
|
Debug "Reloading haproxy."
|
||||||
|
|
||||||
|
service haproxy reload
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
Error 5 "can't reload haproxy with 'service haproxy reload'"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# update haproxy via local UNIX socket
|
||||||
|
Debug "Updating haproxy."
|
||||||
|
|
||||||
|
echo "set ssl ocsp-response `base64 -w 0 $TMP/ocsp.der`" | $SOCAT_BIN stdio $HAPROXY_ADMIN_SOCKET &>>$TMP/log
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
Error 5 "can't update haproxy ssl ocsp-response using $HAPROXY_ADMIN_SOCKET socket"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
Debug "Not notifying haproxy because skip-update is set."
|
||||||
|
fi
|
||||||
|
|
||||||
|
else
|
||||||
|
Debug "Debug mode: haproxy update skipped."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# remove temporary files and quit with success
|
||||||
|
Quit 0
|
||||||
|
|
||||||
|
# vim: set tabstop=4 shiftwidth=4 expandtab:
|
|
@ -0,0 +1,50 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
H_NAME="{{ letsencrypt_acme_sh_certs_data_prefix }}"
|
||||||
|
LE_SERVICES_SCRIPT_DIR=/usr/lib/acme/hooks
|
||||||
|
LE_CERTS_DIR=/var/lib/acme/live/$H_NAME
|
||||||
|
LE_LOG_DIR=/var/log/letsencrypt
|
||||||
|
HAPROXY_CERTDIR=/etc/pki/haproxy
|
||||||
|
HAPROXY_CERTFILE=$HAPROXY_CERTDIR/haproxy.pem
|
||||||
|
DATE=$( date )
|
||||||
|
|
||||||
|
[ ! -d $HAPROXY_CERTDIR ] && mkdir -p $HAPROXY_CERTDIR
|
||||||
|
[ ! -d $LE_LOG_DIR ] && mkdir $LE_LOG_DIR
|
||||||
|
echo "$DATE" >> $LE_LOG_DIR/haproxy.log
|
||||||
|
|
||||||
|
{% if letsencrypt_acme_install %}
|
||||||
|
LE_ENV_FILE=/etc/default/letsencrypt
|
||||||
|
{% endif %}
|
||||||
|
{% if letsencrypt_acme_sh_install %}
|
||||||
|
LE_ENV_FILE=/etc/default/acme_sh_request_env
|
||||||
|
{% endif %}
|
||||||
|
if [ -f "$LE_ENV_FILE" ] ; then
|
||||||
|
. "$LE_ENV_FILE"
|
||||||
|
else
|
||||||
|
echo "No letsencrypt default file" >> $LE_LOG_DIR/haproxy.log
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Building the new certificate file" >> $LE_LOG_DIR/haproxy.log
|
||||||
|
cat ${LE_CERTS_DIR}/{fullchain,privkey} > ${HAPROXY_CERTFILE}
|
||||||
|
chmod 440 ${HAPROXY_CERTFILE}
|
||||||
|
chgrp haproxy ${HAPROXY_CERTFILE}
|
||||||
|
|
||||||
|
echo "Reload the haproxy service" >> $LE_LOG_DIR/haproxy.log
|
||||||
|
if [ -x /bin/systemctl ] ; then
|
||||||
|
systemctl reload haproxy >> $LE_LOG_DIR/haproxy.log 2>&1
|
||||||
|
else
|
||||||
|
service haproxy reload >> $LE_LOG_DIR/haproxy.log 2>&1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Run the OCSP stapling script
|
||||||
|
if [ -x /usr/local/bin/hapos-upd ] ; then
|
||||||
|
echo "Run the OCSP stapling updater script" >> $LE_LOG_DIR/haproxy.log
|
||||||
|
/usr/local/bin/hapos-upd --cert {{ haproxy_cert_dir }}/haproxy.pem -v ${LE_CERTS_DIR}/fullchain -s {{ haproxy_admin_socket }} -v - >> $LE_LOG_DIR/haproxy.log 2>&1
|
||||||
|
else
|
||||||
|
echo "No OCPS stapling updater script" >> $LE_LOG_DIR/haproxy.log
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Done." >> $LE_LOG_DIR/haproxy.log
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
# Check the haproxy backends status
|
||||||
|
command[lb_check_bk_status]=/usr/bin/sudo {{ nagios_local_plugdir }}/check_haproxy_stats -s {{ haproxy_admin_socket }} -w {{ haproxy_nagios_check_w }} -c {{ haproxy_nagios_check_c }}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue