Skip to content

Commit

Permalink
Merge pull request #215 from engineyard/letsencrypt
Browse files Browse the repository at this point in the history
[FB-2830] Adds wildcard letsencrypt
  • Loading branch information
mushyy authored Sep 16, 2021
2 parents e7cb0ce + d67a692 commit 05f2503
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 13 deletions.
52 changes: 47 additions & 5 deletions cookbooks/letsencrypt/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,68 @@ This installs and sets up the optional LetsEncrypt recipe on the engineyard stac

* Have a [certificate](https://support.cloud.engineyard.com/hc/en-us/articles/205407488-Obtain-and-Install-SSL-Certificates-for-Applications#topic8) applied to the [environment](https://support.cloud.engineyard.com/hc/en-us/articles/205407488-Obtain-and-Install-SSL-Certificates-for-Applications#topic12)

* Have the application deployed successfully

**Environment Variables**

There are several environmental variables needed to be used with the LetsEncrypt recipe. To enable the recipe set `EY_LETSENCRYPT_ENABLED` to `TRUE`. This will install certbot on all application instances


**SAN Certificate Setup**

To automatically create a certificate or SAN certiciate:

* Make sure a certificate is applied to the environment using the documentation above. This certificate will be for configuration reasons only and not seen by visitors, so can be a self-signed one, with a name that highlights letsencrypt's use, e.g. `using-letsencrypt`
* Set the environmental variable `EY_LE_MAIN_DOMAIN` as your main domain e.g. `Engineyard.com`
* Make sure a certificate is applied to the environment using the documentation above. This certificate will be for configuration reasons only and not seen by visitors, so can be a self-signed one, with a name that highlights letsencrypts use, e.g. `using-letsencrypt`
* Set the environment variable `EY_LE_DOMAINS` with any additional domains seperated with a space. The main domain **must** come first e.g. `Engineyard.com www.Engineyard.com Example.com`
*


**Wildcard Certificate Setup**

To automatically create a wildcard certificate

* Make sure a certificate is applied to the environment using the documentation above. This certificate will be for configuration reasons only and not seen by visitors, so can be a self-signed one, with a name that highlights letsencrypts use, e.g. `using-letsencrypt`
* Set the environment variable `EY_LE_DOMAINS` with the domain you wish to set up e.g. `*.engineyard.com`
* Set the environment variable `EY_LE_USE_WILDCARD` to `TRUE`
* Set the environment variable `EY_LE_DNS_TYPE` to one of the supported DNS providers listed below
* Set the environemnt variable `EY_LE_DNS_AUTH_INFO` to the API information for said DNS provider. The formatting can be found under the third party documentation link


**Notes**

* If you're using a multi-application environment setup set the variable `EY_LE_MAIN_APP_NAME` to the application you wish to use LetsEncrypt with
* `www` is not included so you may wish to use `www.example.com` and `example.com`

* `www` is not included so you may wish to use `www.example.com` and `example.com` if you're using a SAN certificate

* Route53 provider (AWS). Requires the custom variable `AWS_CONFIG_FILE` to be set as `/opt/.letsencrypt-secrets`


**Supported DNS Providers**

* cloudflare - https://certbot-dns-cloudflare.readthedocs.io/en/stable/

* cloudxns - https://certbot-dns-cloudxns.readthedocs.io/en/stable/

* digitalocean - https://certbot-dns-digitalocean.readthedocs.io/en/stable/

* dnsimple - https://certbot-dns-dnsimple.readthedocs.io/en/stable/

* dnsmadeeasy - https://certbot-dns-dnsmadeeasy.readthedocs.io/en/stable/

* gehirn - https://certbot-dns-gehirn.readthedocs.io/en/stable/

* google - https://certbot-dns-google.readthedocs.io/en/stable/

* linode - https://certbot-dns-linode.readthedocs.io/en/stable/

* luadns - https://certbot-dns-luadns.readthedocs.io/en/stable/

* nsone - https://certbot-dns-nsone.readthedocs.io/en/stable/

* ovh - https://certbot-dns-ovh.readthedocs.io/en/stable/

* rfc2136 - https://certbot-dns-rfc2136.readthedocs.io/en/stable/

**Upcoming Changes**
* route53 - https://certbot-dns-route53.readthedocs.io/en/stable/

Wildcard integration
* sakuracloud - https://certbot-dns-sakuracloud.readthedocs.io/en/stable/
70 changes: 62 additions & 8 deletions cookbooks/letsencrypt/recipes/default.rb
Original file line number Diff line number Diff line change
@@ -1,27 +1,68 @@
letsencrypt = node['letsencrypt']

package "certbot" do
package "python3-pip" do
action :install
end

md = fetch_env_var(node, 'EY_LE_MAIN_DOMAIN').downcase
execute "ensure pip3 is up-to-date" do
command "pip3 install pip --upgrade"
end

execute "install certbot" do
command "pip3 install certbot"
end

md = fetch_env_var(node, 'EY_LE_DOMAINS').split[0] #= fetch_env_var(node, 'EY_LE_MAIN_DOMAIN').downcase
domain = fetch_env_var(node, 'EY_LE_DOMAINS').nil? || (fetch_env_var(node, 'EY_LE_DOMAINS').gsub(" "," -d ")).downcase
app = fetch_env_var(node, 'EY_LE_MAIN_APP_NAME') || node['dna']['applications'].keys.first
wc = fetch_env_var(node, 'EY_LE_USE_WILDCARD') || false
type = fetch_env_var(node, 'EY_LE_DNS_TYPE') || ""

Chef::Log.info "LetsEncrypt Widlcard: #{wc}"

if Dir.exist?("/data/#{app}/current") && ['solo', 'app_master'].include?(node['dna']['instance_role'])

execute "force start haproxy / nginx" do
command "/etc/init.d/haproxy start /etc/init.d/nginx start"
end

if !wc
if wc

execute "Install plugin for certbot" do
command "pip3 install certbot-dns-#{type}"
end

managed_template "/opt/.letsencrypt-secrets" do
mode 0700
source "letsencrypt.secrets.erb"
variables(
:data => fetch_env_var(node, 'EY_LE_DNS_AUTH_INFO')
)
end

case type
when /route53/
dns_type = ""
else
dns_type = "--dns-#{type}-credentials /opt/.letsencrypt-secrets"
end

execute "Issue certiciate initially" do
command ". /data/#{app}/shared/config/env.cloud && certbot certonly --dns-#{type} #{dns_type} -d #{domain} --non-interactive --agree-tos --register-unsafely-without-email --dry-run"
not_if { File.exist?("/etc/letsencrypt/live/#{md}/privkey.pem") }
end

execute "issue certificate" do
command "certbot certonly --noninteractive --agree-tos --register-unsafely-without-email -d #{domain} --webroot -w /data/#{app}/current/public/"
not_if { ::Dir.exist? ("/etc/letsencrypt/live/#{md}/") }
end
end

if !wc

execute "issue certificate" do
command "certbot certonly --noninteractive --agree-tos --register-unsafely-without-email -d #{domain} --webroot -w /data/#{app}/current/public/"
not_if { ::Dir.exist? ("/etc/letsencrypt/live/#{md}/") }
end
end



managed_template "/engineyard/bin/copycerts.sh" do
owner 'root'
Expand All @@ -30,11 +71,24 @@
source "copycerts.sh.erb"
variables(
:app_name => app,
:instances => (node.cluster - node.db_slaves - node.db_master).join(' '),
:instances => (node.cluster - node.db_slaves - node.db_master - node.util_servers).join(' '),
:md => md
)
end

managed_template "/engineyard/bin/check_le.sh" do
owner 'deploy'
group 'deploy'
mode 0700
source 'check_le.sh.erb'
variables(
:md => md
)
end
execute "update collectd" do
command 'sed -i \'81 i \ \ Exec \"deploy\" \"/engineyard/bin/check_le.sh\"\' /etc/engineyard/collectd.conf && /etc/init.d/collectd restart'
end

execute "push certificate" do
command "/engineyard/bin/copycerts.sh"
end
Expand Down
Binary file not shown.
40 changes: 40 additions & 0 deletions cookbooks/letsencrypt/templates/check_le.sh.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/bin/bash

umask 022

# make tmp dir
status_dir="/tmp/check_le_status"
mkdir -p "${status_dir}"

# alert
alert ()
{
# params
device="${1}"; severity="${2}"
timestamp=$(date '+%s')

# load previous status
status_file="${status_dir}/le-status"
previous_severity=$(cat "${status_file}") 2>/dev/null

# send notification
if [[ $severity != $previous_severity ]]; then
case "${severity}" in
OKAY) message="Certificate is valid" ;;
FAILURE) message="Certificate will expire in 7 days or less. Something has gone wrong." ;;
esac

echo "PUTNOTIF Type=custom-certificate Time=${timestamp} Severity=${severity} Message=\"raw_message: ${message}\""
fi

# write current status to status file
echo "${severity}" > "${status_file}"
}



if [[ $(sudo openssl x509 -in /etc/letsencrypt/live/<%= @md %>/cert.pem -checkend 604800 -noout |grep "Certificate will expire" --ignore-case) ]]; then
alert "Certificate is invalid" "FAILURE"
else
alert "Certificate is valid" "OKAY"
fi
1 change: 1 addition & 0 deletions cookbooks/letsencrypt/templates/copycerts.sh.erb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ appname='<%= @app_name %>'
md='<%= @md %>'
ssh_options='ssh -i /root/.ssh/internal -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=10'

source /data/${appname}/shared/config/env.coud

if [[ $(find "/etc/letsencrypt/live/${md}/fullchain.pem" -mtime +30 -print) ]]; then

Expand Down
2 changes: 2 additions & 0 deletions cookbooks/letsencrypt/templates/letsencrypt.secrets.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Configured letsencrypt secrets for automated renewal
<%= @data %>

0 comments on commit 05f2503

Please sign in to comment.