diff --git a/data_bags/credentials/gandi_api_5apps.json b/data_bags/credentials/gandi_api_5apps.json new file mode 100644 index 0000000..e3d7256 --- /dev/null +++ b/data_bags/credentials/gandi_api_5apps.json @@ -0,0 +1,9 @@ +{ + "id": "gandi_api_5apps", + "key": { + "encrypted_data": "+tcD9x5MkNpf2Za5iLM7oTGrmAXxuWFEbyg4xrcWypSkSTjdIncOfD1UoIoS\nGzy1\n", + "iv": "ymls2idI/PdiRZCgsulwrA==\n", + "version": 1, + "cipher": "aes-256-cbc" + } +} \ No newline at end of file diff --git a/site-cookbooks/5apps-xmpp_server/recipes/letsencrypt.rb b/site-cookbooks/5apps-xmpp_server/recipes/letsencrypt.rb index 4ff8fff..6d1d6d8 100644 --- a/site-cookbooks/5apps-xmpp_server/recipes/letsencrypt.rb +++ b/site-cookbooks/5apps-xmpp_server/recipes/letsencrypt.rb @@ -1,49 +1,22 @@ -# nginx config to generate a Let's Encrypt cert +# Generate a Let's Encrypt cert for 5apps.com, muc.5apps.com and xmpp.5apps.com include_recipe "kosmos-base::letsencrypt" -root_directory = "/var/www/xmpp.5apps.com" +gandi_api_data_bag_item = data_bag_item('credentials', 'gandi_api_5apps') -directory "#{root_directory}/.well-known" do - owner node["nginx"]["user"] - group node["nginx"]["group"] - action :create - recursive true +template "/root/letsencrypt_hook.sh" do + variables gandi_api_key: gandi_api_data_bag_item["key"] + mode 0770 end -template "#{node['nginx']['dir']}/sites-available/xmpp.5apps.com" do - source 'nginx_conf_xmpp.5apps.com.erb' - owner 'www-data' - mode 0640 - variables server_name: 'xmpp.5apps.com', - root_directory: root_directory, - ssl_cert: "/etc/letsencrypt/live/xmpp.5apps.com/fullchain.pem", - ssl_key: "/etc/letsencrypt/live/xmpp.5apps.com/privkey.pem" - notifies :reload, 'service[nginx]', :delayed -end - -# Generate a Let's Encrypt cert (only if the nginx vhost exists and no cert -# has been generated before. The renew cron will take care of renewing -execute "letsencrypt cert for xmpp.5apps.com" do - command "./certbot-auto certonly --webroot --agree-tos --email ops@5apps.com --webroot-path #{root_directory} -d xmpp.5apps.com -n" +# Generate a Let's Encrypt cert (only if no cert has been generated before). +# The renew cron will take care of renewing +execute "letsencrypt cert for 5apps xmpp" do + command "./certbot-auto certonly --manual --preferred-challenges dns --manual-public-ip-logging-ok --agree-tos --manual-auth-hook \"/root/letsencrypt_hook.sh auth\" --manual-cleanup-hook \"/root/letsencrypt_hook.sh cleanup\" --deploy-hook letsencrypt_renew_hook --email ops@5apps.com -d 5apps.com -d muc.5apps.com -d xmpp.5apps.com -n" + environment cwd "/usr/local/certbot" - only_if do - File.exist?("#{node['nginx']['dir']}/sites-enabled/xmpp.5apps.com") && - !File.exist?("/etc/letsencrypt/live/xmpp.5apps.com/fullchain.pem") + not_if do + File.exist?("/etc/prosody/certs/5apps.com.crt") end - notifies :create, "template[#{node['nginx']['dir']}/sites-available/xmpp.5apps.com]", :delayed - notifies :run, "execute[copy the tls cert to prosody folder]", :delayed end -execute "copy the tls cert to prosody folder" do - action :nothing - command <<-EOF -cp /etc/letsencrypt/live/xmpp.5apps.com/fullchain.pem /var/lib/prosody/xmpp.5apps.com.crt -cp /etc/letsencrypt/live/xmpp.5apps.com/privkey.pem /var/lib/prosody/xmpp.5apps.com.key - EOF - notifies :restart, "service[prosody]", :delayed -end - -nginx_site 'xmpp.5apps.com' do - enable true -end diff --git a/site-cookbooks/5apps-xmpp_server/templates/default/letsencrypt_hook.sh.erb b/site-cookbooks/5apps-xmpp_server/templates/default/letsencrypt_hook.sh.erb new file mode 100755 index 0000000..d0ed9dc --- /dev/null +++ b/site-cookbooks/5apps-xmpp_server/templates/default/letsencrypt_hook.sh.erb @@ -0,0 +1,63 @@ +#!/usr/bin/env bash +# + +set -euf -o pipefail + +# ************** USAGE ************** +# +# Example usage (with this hook file saved in /root/): +# +# sudo su - +# certbot certonly --manual --preferred-challenges dns --manual-public-ip-logging-ok --agree-tos -d "5apps.com" -d muc.5apps.com -d "xmpp.5apps.com" \ +# --manual-auth-hook "/root/letsencrypt_hook.sh auth" --manual-cleanup-hook "/root/letsencrypt_hook.sh cleanup" +# +# This hook requires configuration, continue reading. +# +# ************** CONFIGURATION ************** +# +# GANDI_API_KEY: Your Gandi Live API key +# +# PROVIDER_UPDATE_DELAY: +# How many seconds to wait after updating your DNS records. This may be required, +# depending on how slow your DNS host is to begin serving new DNS records after updating +# them via the API. 30 seconds is a safe default, but some providers can be very slow +# (e.g. Linode). +# +# Defaults to 30 seconds. +# +GANDI_API_KEY="<%= @gandi_api_key %>" +PROVIDER_UPDATE_DELAY=30 + +regex='.*\.(.*\..*)' +if [[ $CERTBOT_DOMAIN =~ $regex ]] +then + DOMAIN="${BASH_REMATCH[1]}" +else + DOMAIN="${CERTBOT_DOMAIN}" +fi + +# To be invoked via Certbot's --manual-auth-hook +function auth { + curl -s -D- -H "Content-Type: application/json" \ + -H "X-Api-Key: ${GANDI_API_KEY}" \ + -d "{\"rrset_name\": \"_acme-challenge.${CERTBOT_DOMAIN}.\", + \"rrset_type\": \"TXT\", + \"rrset_ttl\": 3600, + \"rrset_values\": [\"${CERTBOT_VALIDATION}\"]}" \ + "https://dns.api.gandi.net/api/v5/domains/${DOMAIN}/records" + + + sleep ${PROVIDER_UPDATE_DELAY} +} + +# To be invoked via Certbot's --manual-cleanup-hook +function cleanup { + curl -s -X DELETE -H "Content-Type: application/json" \ + -H "X-Api-Key: ${GANDI_API_KEY}" \ + https://dns.api.gandi.net/api/v5/domains/${DOMAIN}/records/_acme-challenge.${CERTBOT_DOMAIN}./TXT +} + +HANDLER=$1; shift; +if [ -n "$(type -t $HANDLER)" ] && [ "$(type -t $HANDLER)" = function ]; then + $HANDLER "$@" +fi diff --git a/site-cookbooks/5apps-xmpp_server/templates/default/nginx_conf_xmpp.5apps.com.erb b/site-cookbooks/5apps-xmpp_server/templates/default/nginx_conf_xmpp.5apps.com.erb deleted file mode 100644 index 3b41657..0000000 --- a/site-cookbooks/5apps-xmpp_server/templates/default/nginx_conf_xmpp.5apps.com.erb +++ /dev/null @@ -1,23 +0,0 @@ -server { - listen 80; # For Let's Encrypt - <% if File.exist?(@ssl_cert) && File.exist?(@ssl_key) -%> - listen 443 ssl http2; - <% end -%> - - server_name <%= @server_name %>; - - # Used by Let's Encrypt (certbot in webroot mode) - location /.well-known { - root "<%= @root_directory %>"; - } - - location / { - return 200 'Nothing to see here'; - add_header Content-Type text/plain; - } - - <% if File.exist?(@ssl_cert) && File.exist?(@ssl_key) -%> - ssl_certificate <%= @ssl_cert %>; - ssl_certificate_key <%= @ssl_key %>; - <% end -%> -} diff --git a/site-cookbooks/kosmos-base/recipes/letsencrypt.rb b/site-cookbooks/kosmos-base/recipes/letsencrypt.rb index 35b5fbb..fa7959e 100644 --- a/site-cookbooks/kosmos-base/recipes/letsencrypt.rb +++ b/site-cookbooks/kosmos-base/recipes/letsencrypt.rb @@ -2,7 +2,7 @@ # Cookbook Name:: kosmos-base # Recipe:: letsencrypt # -# Copyright 2016, Kosmos +# Copyright 2018, Kosmos # # All rights reserved - Do Not Redistribute # @@ -10,7 +10,7 @@ git "/usr/local/certbot" do repository "https://github.com/certbot/certbot" action :sync - revision "v0.25.0" + revision "v0.26.1" user "root" group "root" end @@ -21,13 +21,20 @@ letsencrypt_renew_hook = <<-EOF # Reloading nginx is enough to read the new certificates systemctl reload nginx -# Copy the prosody certificate and restart the server if it has been renewed +# Copy the prosody certificates and restart the server if it has been renewed # This is necessary because the prosody user doesn't have access to the # letsencrypt live folder -echo "${RENEWED_DOMAINS}" | grep xmpp.5apps.com +echo "${RENEWED_DOMAINS}" | grep 5apps.com if [ $? -ne 1 ]; then - cp /etc/letsencrypt/live/xmpp.5apps.com/fullchain.pem /var/lib/prosody/xmpp.5apps.com.crt - cp /etc/letsencrypt/live/xmpp.5apps.com/privkey.pem /var/lib/prosody/xmpp.5apps.com.key + cp "${RENEWED_LINEAGE}/fullchain.pem" /etc/prosody/certs/5apps.com.crt + cp "${RENEWED_LINEAGE}/privkey.pem" /etc/prosody/certs/5apps.com.key + cp "${RENEWED_LINEAGE}/fullchain.pem" /etc/prosody/certs/muc.5apps.com.crt + cp "${RENEWED_LINEAGE}/privkey.pem" /etc/prosody/certs/muc.5apps.com.key + cp "${RENEWED_LINEAGE}/fullchain.pem" /etc/prosody/certs/xmpp.5apps.com.crt + cp "${RENEWED_LINEAGE}/privkey.pem" /etc/prosody/certs/xmpp.5apps.com.key + chown prosody:prosody /etc/prosody/certs/* + chmod 600 /etc/prosody/certs/*.key + chmod 640 /etc/prosody/certs/*.crt systemctl restart prosody else exit 0 @@ -45,6 +52,6 @@ cron "renew Let's Encrypt certificates" do minute "0" hour "4" mailto "logs@5apps.com" - # The post hook is only executed if a cert has been renewed - command "/usr/local/certbot/certbot-auto renew --renew-hook letsencrypt_renew_hook -n 1> /dev/null" + # The hook is only executed if a cert has been renewed + command "/usr/local/certbot/certbot-auto renew --deploy-hook letsencrypt_renew_hook -n 1> /dev/null" end