Skip to content

Commit

Permalink
Merge pull request #190 from engineyard/letsencrypt_recipe
Browse files Browse the repository at this point in the history
[FB-2830] Adds letsencrypt
  • Loading branch information
mushyy authored Jun 15, 2021
2 parents 4c54f94 + 5824560 commit 5f61035
Show file tree
Hide file tree
Showing 10 changed files with 152 additions and 1 deletion.
1 change: 1 addition & 0 deletions cookbooks/ey-base/metadata.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,4 @@
depends 'redis'
depends 'memcached'
depends 'sidekiq'
depends 'letsencrypt'
5 changes: 5 additions & 0 deletions cookbooks/ey-base/recipes/custom.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,8 @@
if fetch_env_var(node, "EY_SIDEKIQ_ENABLED") =~ /^TRUE$/i
include_recipe 'sidekiq'
end

if fetch_env_var(node, "EY_LETSENCRYPT_ENABLED") =~ /^TRUE$/i
include_recipe 'letsencrypt'
end

1 change: 1 addition & 0 deletions cookbooks/ey-init/recipes/integrate.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@
include_recipe "db-ssl::setup" if is_db_master
end
include_recipe "ssh_keys" # CC-691 - update ssh whitelist after takeovers
include_recipe "ey-base::custom"
36 changes: 36 additions & 0 deletions cookbooks/letsencrypt/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
**letsencrypt**
==========

This installs and sets up the optional LetsEncrypt recipe on the engineyard stack.


**Installation**
=========

**Prerequisites**

* 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)


**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

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`
* 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`




**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`


**Upcoming Changes**

Wildcard integration
Empty file.
1 change: 1 addition & 0 deletions cookbooks/letsencrypt/metadata.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
name 'letsencrypt'
48 changes: 48 additions & 0 deletions cookbooks/letsencrypt/recipes/default.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
letsencrypt = node['letsencrypt']

package "certbot" do
action :install
end

md = 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

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

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'
group 'root'
mode 0700
source "copycerts.sh.erb"
variables(
:app_name => app,
:instances => (node.cluster - node.db_slaves - node.db_master).join(' '),
:md => md
)
end

execute "push certificate" do
command "/engineyard/bin/copycerts.sh"
end

cron "renew certificates" do
command "bash -c '/engineyard/bin/copycerts.sh'"
day '1,15,29'
hour '0'
minute '0'
end
end
43 changes: 43 additions & 0 deletions cookbooks/letsencrypt/templates/copycerts.sh.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/bin/bash
instances='<%= @instances %>'
appname='<%= @app_name %>'
md='<%= @md %>'
ssh_options='ssh -i /root/.ssh/internal -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=10'


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

certbot --debug renew > /var/log/engineyard/le-renew.log

fi


function push_cert {
local target=${1}
#Pushes LE certs

rsync --copy-links -e "${ssh_options}" /etc/letsencrypt/live/${md}/fullchain.pem ${target}:/etc/nginx/ssl/${appname}/${appname}.crt

rsync --copy-links -e "${ssh_options}" /etc/letsencrypt/live/${md}/privkey.pem ${target}:/etc/nginx/ssl/${appname}/${appname}.key
${ssh_options} ${target} '/etc/init.d/nginx reload'

}



function is_up() {
eval ${ssh_options} $1 'date'
[[ $? -eq 0 ]]
}

cd ${keypath}
# copy keys to other instances
for instance in ${instances}
do
if is_up ${instance}
then
push_cert ${instance}
else
echo "Instance ${instance} is not available. Skipping."
fi
done
12 changes: 11 additions & 1 deletion cookbooks/nginx/recipes/default.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
nginx_haproxy_https_port = 8092
nginx_xlb_http_port = 8081
nginx_xlb_https_port = 8082
letsencrypt = fetch_env_var(node, 'EY_LETSENCRYPT_ENABLED') || false

Chef::Log.info "LetsEncrypt Enabled: #{letsencrypt}"

base_port = node['passenger5']['port'].to_i
stepping = 200
Expand All @@ -27,6 +30,7 @@
is_passenger = false
is_unicorn = false
is_puma = false
is_app_master = ['app_master','solo'].include?(node['dna']['instance_role']) || false


if stack.match(/nginx_passenger5/)
Expand Down Expand Up @@ -145,6 +149,7 @@
:vhost => app.vhosts.first,
:haproxy_nginx_port => nginx_haproxy_http_port,
:xlb_nginx_port => nginx_xlb_http_port,
:app_instance => is_app_master,
:upstream_port => app_base_port,
:http2 => false
})
Expand All @@ -161,6 +166,7 @@
:webroot => php_webroot,
:vhost => app.vhosts.first,
:env_name => node.engineyard.environment[:name],
:app_instance => is_app_master,
:haproxy_nginx_port => nginx_haproxy_http_port,
:xlb_nginx_port => nginx_xlb_http_port,
:http2 => false,
Expand Down Expand Up @@ -305,6 +311,10 @@
end
end

if (!letsencrypt || !File.exists?("/data/nginx/ssl/#{app.name}/#{app.name}.key"))



template "/data/nginx/ssl/#{app.name}/#{app.name}.key" do
owner node['owner_name']
group node['owner_name']
Expand All @@ -329,7 +339,7 @@
)
notifies node['nginx'][:action], resources(:service => "nginx"), :delayed
end

end
# Add Cipher chain
template "/data/nginx/servers/#{app.name}/default.ssl_cipher" do
owner node['owner_name']
Expand Down
6 changes: 6 additions & 0 deletions cookbooks/nginx/templates/default/nginx_app.conf.erb
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,12 @@ server {
# 5. Failing any caching or system maintenance, pass the request to the
# application.
#

<% if !@app_instance %>
location /.well-known {
proxy_pass http://ey-app-master:8081;
}
<% end %>
location / {
if (-f $document_root/system/maintenance.html) { return 503; }
<% if @ssl %>
Expand Down

0 comments on commit 5f61035

Please sign in to comment.