Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CLOUD-616] Deploy to AWS Certificate Manager #169

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions files/default/centos-nginx-lb-pong
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;

events {
worker_connections 1024;
}

http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

access_log /var/log/nginx/access.log main;

sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;

include /etc/nginx/mime.types;
default_type application/octet-stream;

# Load modular configuration files from the /etc/nginx/conf.d directory.
# See http://nginx.org/en/docs/ngx_core_module.html#include
# for more information.
include /etc/nginx/conf.d/*.conf;

server {
listen 1337 default_server;
listen [::]:1337 default_server;
server_name _;


# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;

location / {
return 200;
}
}
}
8 changes: 8 additions & 0 deletions files/default/ubuntu-nginx-lb-pong
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# HTTP endpoint to satisfy ALB health check for routing port 80 (certbot)
server {
listen 1337 default_server;
listen [::]:1337 default_server;
location / {
return 200;
}
}
43 changes: 42 additions & 1 deletion recipes/install.rb
Original file line number Diff line number Diff line change
Expand Up @@ -161,11 +161,44 @@
end
end

package ["curl", "unzip"] do
package ["curl", "unzip", "nginx"] do
retries 10
retry_delay 30
end

systemd_unit "nginx.service" do
action [:disable, :stop]
end

case node['platform_family']
when 'debian'
remote_file "/etc/nginx/sites-available/lb-pong" do
source "ubuntu-nginx-lb-pong"
user 'root'
group 'root'
mode 0660
action :create
end

bash 'configure-lb-pong' do
user 'root'
group 'root'
code <<-EOH
set -e
rm /etc/nginx/sites-enabled/default
ln -s /etc/nginx/sites-available/lb-pong /etc/nginx/sites-enabled/lb-pong
EOH
end
when 'rhel'
remote_file "/etc/nginx/nginx.conf" do
source "centos-nginx-lb-pong"
user 'root'
group 'root'
mode 0660
action :create
end
end

bash 'install-certbot' do
user 'root'
group 'root'
Expand All @@ -187,6 +220,14 @@
mode 0664
end

remote_file "#{systemd_directory}/lb-pong.service" do
source "lb-pong.service"
user 'root'
group 'root'
mode 0664
action :create
end

if node['cloud']['init']['config']['unmanaged'].casecmp?("true")
template "#{node['cloud']['init']['install_dir']}/ec2init/unmanaged_ec2init.sh" do
source "unmanaged_ec2init.sh.erb"
Expand Down
206 changes: 137 additions & 69 deletions templates/default/deploy2glassfish_hook.sh.erb
Original file line number Diff line number Diff line change
@@ -1,89 +1,157 @@
#!/usr/bin/env bash

## DO NOT set -e
set -e

## Bash script to run after a successful renewal of Let's Encrypt certificate
## Put it in /etc/letsencrypt/renewal-hooks/deploy and it will be executed
## by certbot upon a successful renewal

KEYSTOREPW=<%= node['hopsworks']['master']['password'] %>
BASE=/etc/letsencrypt/live
GF_DOMAIN=<%= node['hopsworks']['domains_dir'] %>/<%= node['hopsworks']['domain_name'] %>
GF_USER=<%= node['hopsworks']['user'] %>
GF_GROUP=<%= node['hopsworks']['group'] %>
SPARK_CONF_DIR=<%= node['hadoop_spark']['conf_dir'] %>
cloud_provider=_CLOUD_PROVIDER_
upload_certificate_manager=_UPLOAD_CERTIFICATE_MANAGER_
certificate_arn=_CERTIFICATE_ARN_

TMP="/root/letsencrypt/tmp"
mkdir -p $TMP
_log(){
now=$(date)
echo "$now - $1 - $2"
}

DATE=$(date +%Y-%m-%d)
BACKUP_DIR="/root/letsencrypt/backup/$DATE"
mkdir -p $BACKUP_DIR
_log_info(){
_log "INFO" "$1"
}

cp -f "$GF_DOMAIN/config/keystore.jks" $BACKUP_DIR
cp -f "$GF_DOMAIN/config/cacerts.jks" $BACKUP_DIR
_log_warn(){
_log "WARN" "$1"
}

_log_error(){
_log "ERROR" "$1"
}

BASE=/etc/letsencrypt/live
# There should only be one domain name
for domain in $BASE/*; do
if [ -d $domain ] && [[ $domain =~ .*\.hopsworks\..* ]]; then
working_dir=$domain
working_domain=$(basename $domain)
fi
done
done

[[ -d $working_dir ]] || exit 2

cp -f "$GF_DOMAIN/config/keystore.jks" $TMP
cp -f "$GF_DOMAIN/config/cacerts.jks" $TMP

pushd $TMP
# Bundle private key and certificate
openssl pkcs12 -export -in ${working_dir}/cert.pem -inkey ${working_dir}/privkey.pem -out cert_and_key.p12 -name ${working_domain} -CAfile ${working_dir}/chain.pem -caname root -password pass:$KEYSTOREPW
# Remove existing/old certificate for the domain
keytool -delete -keystore keystore.jks -alias ${working_domain} -srcstorepass $KEYSTOREPW -deststorepass $KEYSTOREPW -destkeypass $KEYSTOREPW
# Import new private key and certificate bundle
keytool -importkeystore -destkeystore keystore.jks -srckeystore cert_and_key.p12 -srcstoretype PKCS12 -alias ${working_domain} -srcstorepass $KEYSTOREPW -deststorepass $KEYSTOREPW -destkeypass $KEYSTOREPW
# Remove old root certificate from trust anchor
keytool -delete -keystore keystore.jks -alias root -srcstorepass $KEYSTOREPW -deststorepass $KEYSTOREPW -destkeypass $KEYSTOREPW
# Add new root certificate as trust anchor
keytool -import -noprompt -trustcacerts -alias root -file ${working_dir}/chain.pem -keystore keystore.jks -srcstorepass $KEYSTOREPW -deststorepass $KEYSTOREPW -destkeypass $KEYSTOREPW

# Bundle for glassfish-instance
openssl pkcs12 -export -in ${working_dir}/fullchain.pem -inkey ${working_dir}/privkey.pem -out pkcs.p12 -name glassfish-instance -password pass:$KEYSTOREPW
keytool -delete -keystore keystore.jks -alias glassfish-instance -srcstorepass $KEYSTOREPW -deststorepass $KEYSTOREPW -destkeypass $KEYSTOREPW
keytool -importkeystore -destkeystore keystore.jks -srckeystore pkcs.p12 -srcstoretype PKCS12 -alias glassfish-instance -srcstorepass $KEYSTOREPW -deststorepass $KEYSTOREPW -destkeypass $KEYSTOREPW
openssl pkcs12 -export -in ${working_dir}/fullchain.pem -inkey ${working_dir}/privkey.pem -out pkcs.p12 -name s1as -password pass:$KEYSTOREPW
keytool -delete -keystore keystore.jks -alias s1as -srcstorepass $KEYSTOREPW -deststorepass $KEYSTOREPW -destkeypass $KEYSTOREPW
keytool -importkeystore -destkeystore keystore.jks -srckeystore pkcs.p12 -srcstoretype PKCS12 -alias s1as -srcstorepass $KEYSTOREPW -deststorepass $KEYSTOREPW -destkeypass $KEYSTOREPW

keytool -export -alias glassfish-instance -file glassfish-instance.cert -keystore keystore.jks -storepass $KEYSTOREPW
keytool -export -alias s1as -file s1as.cert -keystore keystore.jks -storepass $KEYSTOREPW

# Update Glassfish CA certs
keytool -delete -keystore cacerts.jks -alias s1as -storepass $KEYSTOREPW
keytool -import -noprompt -alias s1as -file s1as.cert -keystore cacerts.jks -storepass $KEYSTOREPW
keytool -delete -keystore cacerts.jks -alias glassfish-instance -storepass $KEYSTOREPW
keytool -import -noprompt -alias glassfish-instance -file glassfish-instance.cert -keystore cacerts.jks -storepass $KEYSTOREPW

# Convert cacerts to pem format
keytool -importkeystore -srckeystore cacerts.jks -destkeystore cacerts.p12 -srcstoretype jks -deststoretype pkcs12 -noprompt -srcstorepass $KEYSTOREPW -deststorepass $KEYSTOREPW
openssl pkcs12 -in cacerts.p12 -out cacerts.pem -passin pass:$KEYSTOREPW

cp -f cacerts.jks ${SPARK_CONF_DIR}/cacerts.jks
mv -f cacerts.pem ${SPARK_CONF_DIR}/cacerts.pem
mv -f keystore.jks ${GF_DOMAIN}/config/keystore.jks
mv -f cacerts.jks ${GF_DOMAIN}/config/cacerts.jks

rm -f cacerts.p12

pushd ${GF_DOMAIN}/config
chown ${GF_USER}:${GF_GROUP} keystore.jks
chmod 600 keystore.jks

chown ${GF_USER}:${GF_GROUP} cacerts.jks
chmod 655 cacerts.jks

systemctl restart glassfish-<%= node['hopsworks']['domain_name'] %>
yes | ${GF_DOMAIN}/bin/domain1_asadmin disable-secure-admin
yes | ${GF_DOMAIN}/bin/domain1_asadmin enable-secure-admin
_log_info "Let's encrypt directory: $working_dir"

_deploy_certificate_local(){
# important
set +e
KEYSTOREPW=<%= node['hopsworks']['master']['password'] %>
GF_DOMAIN_NAME=<%= node['hopsworks']['domain_name'] %>
GF_DOMAIN=<%= node['hopsworks']['domains_dir'] %>/$GF_DOMAIN_NAME
GF_USER=<%= node['hopsworks']['user'] %>
GF_GROUP=<%= node['hopsworks']['group'] %>
SPARK_CONF_DIR=<%= node['hadoop_spark']['conf_dir'] %>

TMP="/root/letsencrypt/tmp"
mkdir -p $TMP

DATE=$(date +%Y-%m-%d)
BACKUP_DIR="/root/letsencrypt/backup/$DATE"
mkdir -p $BACKUP_DIR

cp -f "$GF_DOMAIN/config/keystore.jks" $BACKUP_DIR
cp -f "$GF_DOMAIN/config/cacerts.jks" $BACKUP_DIR

cp -f "$GF_DOMAIN/config/keystore.jks" $TMP
cp -f "$GF_DOMAIN/config/cacerts.jks" $TMP

pushd $TMP
# Bundle private key and certificate
openssl pkcs12 -export -in ${working_dir}/cert.pem -inkey ${working_dir}/privkey.pem -out cert_and_key.p12 -name ${working_domain} -CAfile ${working_dir}/chain.pem -caname root -password pass:$KEYSTOREPW
# Remove existing/old certificate for the domain
keytool -delete -keystore keystore.jks -alias ${working_domain} -srcstorepass $KEYSTOREPW -deststorepass $KEYSTOREPW -destkeypass $KEYSTOREPW
# Import new private key and certificate bundle
keytool -importkeystore -destkeystore keystore.jks -srckeystore cert_and_key.p12 -srcstoretype PKCS12 -alias ${working_domain} -srcstorepass $KEYSTOREPW -deststorepass $KEYSTOREPW -destkeypass $KEYSTOREPW
# Remove old root certificate from trust anchor
keytool -delete -keystore keystore.jks -alias root -srcstorepass $KEYSTOREPW -deststorepass $KEYSTOREPW -destkeypass $KEYSTOREPW
# Add new root certificate as trust anchor
keytool -import -noprompt -trustcacerts -alias root -file ${working_dir}/chain.pem -keystore keystore.jks -srcstorepass $KEYSTOREPW -deststorepass $KEYSTOREPW -destkeypass $KEYSTOREPW

# Bundle for glassfish-instance
openssl pkcs12 -export -in ${working_dir}/fullchain.pem -inkey ${working_dir}/privkey.pem -out pkcs.p12 -name glassfish-instance -password pass:$KEYSTOREPW
keytool -delete -keystore keystore.jks -alias glassfish-instance -srcstorepass $KEYSTOREPW -deststorepass $KEYSTOREPW -destkeypass $KEYSTOREPW
keytool -importkeystore -destkeystore keystore.jks -srckeystore pkcs.p12 -srcstoretype PKCS12 -alias glassfish-instance -srcstorepass $KEYSTOREPW -deststorepass $KEYSTOREPW -destkeypass $KEYSTOREPW
openssl pkcs12 -export -in ${working_dir}/fullchain.pem -inkey ${working_dir}/privkey.pem -out pkcs.p12 -name s1as -password pass:$KEYSTOREPW
keytool -delete -keystore keystore.jks -alias s1as -srcstorepass $KEYSTOREPW -deststorepass $KEYSTOREPW -destkeypass $KEYSTOREPW
keytool -importkeystore -destkeystore keystore.jks -srckeystore pkcs.p12 -srcstoretype PKCS12 -alias s1as -srcstorepass $KEYSTOREPW -deststorepass $KEYSTOREPW -destkeypass $KEYSTOREPW

keytool -export -alias glassfish-instance -file glassfish-instance.cert -keystore keystore.jks -storepass $KEYSTOREPW
keytool -export -alias s1as -file s1as.cert -keystore keystore.jks -storepass $KEYSTOREPW

# Update Glassfish CA certs
keytool -delete -keystore cacerts.jks -alias s1as -storepass $KEYSTOREPW
keytool -import -noprompt -alias s1as -file s1as.cert -keystore cacerts.jks -storepass $KEYSTOREPW
keytool -delete -keystore cacerts.jks -alias glassfish-instance -storepass $KEYSTOREPW
keytool -import -noprompt -alias glassfish-instance -file glassfish-instance.cert -keystore cacerts.jks -storepass $KEYSTOREPW

# Convert cacerts to pem format
keytool -importkeystore -srckeystore cacerts.jks -destkeystore cacerts.p12 -srcstoretype jks -deststoretype pkcs12 -noprompt -srcstorepass $KEYSTOREPW -deststorepass $KEYSTOREPW
openssl pkcs12 -in cacerts.p12 -out cacerts.pem -passin pass:$KEYSTOREPW

cp -f cacerts.jks ${SPARK_CONF_DIR}/cacerts.jks
mv -f cacerts.pem ${SPARK_CONF_DIR}/cacerts.pem
mv -f keystore.jks ${GF_DOMAIN}/config/keystore.jks
mv -f cacerts.jks ${GF_DOMAIN}/config/cacerts.jks

rm -f cacerts.p12

pushd ${GF_DOMAIN}/config
chown ${GF_USER}:${GF_GROUP} keystore.jks
chmod 600 keystore.jks

chown ${GF_USER}:${GF_GROUP} cacerts.jks [12/475]
chmod 655 cacerts.jks

systemctl restart glassfish-$GF_DOMAIN_NAME
echo "Restarting glassfish..."
yes | ${GF_DOMAIN}/bin/domain1_asadmin disable-secure-admin
yes | ${GF_DOMAIN}/bin/domain1_asadmin enable-secure-admin
}

_deploy_aws_certificate_manager(){
_log_info "Deploying AWS certificate manager"
if [ -z "$certificate_arn" ]; then
_log_error "You have to supply AWS Certificate Manager certificate ARN"
exit 1
fi
aws_region=$(echo $certificate_arn | awk -F ":" '{ print $4}')
_log_info "AWS: Importing to $certificate_arn - Region: $aws_region"
aws --region ${aws_region} acm import-certificate \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It this the only permissions we need to add to the instance profile then?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The process is not very straightforward but I explain why on my first comment

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aha, ok, but why do we need to provide the certificate_arn to start with it?

The [Amazon Resource Name (ARN)](https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html) of an imported certificate to replace. To import a new certificate, omit this field.

https://docs.aws.amazon.com/cli/latest/reference/acm/import-certificate.html

--certificate-arn ${certificate_arn} \
--certificate fileb://${working_dir}/cert.pem \
--private-key fileb://${working_dir}/privkey.pem
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

before i forget it, we need to set the certificate-chain as well

_log_info "AWS: Successfully deployed Let's Encrypt certificate to Certificate Manager"
}

_deploy_gcp_certificate_manager(){
_log_info "Deploying GCP certificate manager"
_log_error "Not implemented yet"
exit 1
}

_deploy_azure_certificate_manager(){
_log_info "Deploying Azure certificate manager"
_log_error "Not implemented yet"
exit 1
}

if [ "$upload_certificate_manager" == "true" ]; then
_log_info "Cloud provider: $cloud_provider - certificate_arn: $certificate_arn"
if [ "$cloud_provider" == "aws" ] || [ "$cloud_provider" == "AWS" ]; then
_deploy_aws_certificate_manager
elif [ "$cloud_provider" == "gcp" ] || [ "$cloud_provider" == "GCP" ]; then
_deploy_gcp_certificate_manager
elif [ "$cloud_provider" == "azure" ] || [ "$cloud_provider" == "AZURE" ]; then
_deploy_azure_certificate_manager
fi
else
_log_info "Deploy certificate locally"
_deploy_certificate_local
fi