Skip to content

Commit bf99851

Browse files
authored
Initial AWS Lambda support (#1)
* Initial AWS Lambda support
1 parent 29e9985 commit bf99851

6 files changed

Lines changed: 257 additions & 0 deletions

File tree

.gitignore

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Editors
2+
# =======
3+
.*code
4+
.idea
5+
*.todo
6+
*.history
7+
8+
# Build artifacts
9+
# ===============
10+
certbot_build
11+
certbot_build_aws
12+
build_artifacts
13+
14+
# Misc files
15+
# ==========
16+
*.DS_Store
17+

.travis.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
dist: cosmic
2+
language: bash
3+
services:
4+
- docker
5+
6+
cache:
7+
directories:
8+
- "/home/travis/.python_cache/"
9+
10+
env:
11+
global:
12+
- CERTBOT_VERSION=0.32.0 # Bump this version for new builds
13+
14+
jobs:
15+
include:
16+
- stage: build
17+
script:
18+
- ". build.sh; run"
19+
20+
deploy:
21+
provider: releases
22+
api_key:
23+
secure: RRreMcvDnaqpNGJwhktPso7kuyxTpRO6xZC5I0aob9W06o1IESZZN8JaiUiP2Ya3j58h97uIL+iYPIeXhNNn2LdpH6/b2I6Ei73htkpluTFF54Xws+8cjz5tkAgzgskjrFI80xOpa323+bdlLMz3ndnrxyUMUphWOAshq13L1rCWeCor48BUC0h09GSkaPLFlfSEu/AWLl3HLmzHcePJs1G7UQmxgR+zdXoVu5FBRcGHeDyiQrI9k06+rS8ue8hcbvgTr9/9D9z/3ESA4nYoyeDbXckwWjw4hdggTA5dTrTk3+l0AZNjD0oLa/NDdJQ/Jaal92woW0LHgoysM3V3rVSh9OXva7peUgkOH51geA/rFURK3i2/QY46+cKWB8VqUMjU/GQlB8B6h/sU8aAW/P2twVQ8CsjSyhln1CtGCS2OLPY9y7/sW3ygoisyyTjW7lTGC5uJ55AKBNtZrvsIyeGgaAWOOjrSFKRvAxxr+xUNcwKVnG0qb/Qj80Ch1WtwUSaJCJLF5jGSnu+9mYUWb72AZ/MQ4lkKZAmOeO5d86VN2URxWREckVlCOATWZ7Ehjf++55SXB4HBqaWXRq7P64SFcJ65JlTByCzNjCLEXbHXA6Na52vhGi/yWRgt6A6325Owx9lCOlr3lw24/yYPPyzHGBOcbGk5ERNjRu9bh1M=
24+
skip_cleanup: true
25+
file: build_artefacts/*
26+
on:
27+
tags: true

README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
[![Build Status](https://travis-ci.org/aries1980/serverless-certbot.svg?branch=master)](https://travis-ci.org/aries1980/serverless-certbot)
2+
3+
## The Problem
4+
5+
> Run Certbot on the first of every month and upload the certificates to a cloud stora,ge.
6+
7+
SSL/TLS certificates is a must for any external communication. Not just because the served traffic is a secret, but also
8+
to hide the endpoint that is serving. Also it is a must for SEO, HTTP/2, but it is a good practice in general.
9+
10+
Company-provided TLS certificates are expensive, thank God we have the free [Let's Encrypt](https://letsencrypt.org)
11+
and [Certbot](https://certbot.eff.org)! Because the letsencrypted TLS certificates have to be renewed in every 90
12+
days the least. So let's say, this task should run monthly. Because it is written in Python, it would make sense to
13+
run it as a serverless application. Unfortunately Certbot saves the certificates on a local storage, so an extra code
14+
needs to bundled to handle the serverless requests and
15+
16+
17+
## The Solution
18+
19+
- Have a code that glues the serverless event and Certbot. Luckly there is a [code snippet for AWS Lambda](https://github.com/rog2/certbot-lambda/blob/master/main.py)
20+
what I borrowed. Cheers, mate!
21+
22+
The process is inspired by [Deploying EFF's Certbot in AWS Lambda](https://arkadiyt.com/2018/01/26/deploying-effs-certbot-in-aws-lambda/).
23+
24+
- Create a build pipeline
25+
- Installs a given version of Certbot into a Python virtual environment.
26+
- Adds the snippet
27+
- Zips it up
28+
- Provide a release artifact on Github.
29+
30+
## Usage
31+
32+
- Copy the certbot-lambda-[certbot_version].zip into AWS S3. (More cloud provider support is planned. Feel free to create a PR!)
33+
- Terraform the serverless application.
34+

aws/README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
This snippet is proudly borrowed from [https://github.com/rog2/certbot-lambda](https://github.com/rog2/certbot-lambda/blob/master/main.py):
2+
3+
# Running Certbot on AWS Lambda.
4+
5+
Inspired by [Deploying EFF's Certbot in AWS Lambda](https://arkadiyt.com/2018/01/26/deploying-effs-certbot-in-aws-lambda/).
6+
7+
## Features
8+
9+
- Supports wildcard certificates (Let's Encrypt ACME v2).
10+
- Uploads certificates to specified Amazon S3 bucket.
11+
- Works with CloudWatch Scheduled Events for certificate renewal.
12+
13+
## Sample Event
14+
15+
```json
16+
{
17+
"domains": "*.foobar.com,foobar.com",
18+
"email": "[EMAIL]",
19+
"s3_bucket": "[BUCKET]",
20+
"s3_prefix": "[KEY_PREFIX]"
21+
}
22+
```

aws/main.py

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
#!/usr/bin/env python3
2+
3+
import os
4+
import shutil
5+
import boto3
6+
import certbot.main
7+
8+
# Let’s Encrypt acme-v02 server that supports wildcard certificates
9+
CERTBOT_SERVER = 'https://acme-v02.api.letsencrypt.org/directory'
10+
11+
# Temp dir of Lambda runtime
12+
CERTBOT_DIR = '/tmp/certbot'
13+
14+
15+
def rm_tmp_dir():
16+
if os.path.exists(CERTBOT_DIR):
17+
try:
18+
shutil.rmtree(CERTBOT_DIR)
19+
except NotADirectoryError:
20+
os.remove(CERTBOT_DIR)
21+
22+
23+
def obtain_certs(email, domains):
24+
certbot_args = [
25+
# Override directory paths so script doesn't have to be run as root
26+
'--config-dir', CERTBOT_DIR,
27+
'--work-dir', CERTBOT_DIR,
28+
'--logs-dir', CERTBOT_DIR,
29+
30+
# Obtain a cert but don't install it
31+
'certonly',
32+
33+
# Run in non-interactive mode
34+
'--non-interactive',
35+
36+
# Agree to the terms of service
37+
'--agree-tos',
38+
39+
# Email of domain administrator
40+
'--email', email,
41+
42+
# Use dns challenge with route53
43+
'--dns-route53',
44+
'--preferred-challenges', 'dns-01',
45+
46+
# Use this server instead of default acme-v01
47+
'--server', CERTBOT_SERVER,
48+
49+
# Domains to provision certs for (comma separated)
50+
'--domains', domains,
51+
]
52+
return certbot.main.main(certbot_args)
53+
54+
55+
# /tmp/certbot
56+
# ├── live
57+
# │ └── [domain]
58+
# │ ├── README
59+
# │ ├── cert.pem
60+
# │ ├── chain.pem
61+
# │ ├── fullchain.pem
62+
# │ └── privkey.pem
63+
def upload_certs(s3_bucket, s3_prefix):
64+
client = boto3.client('s3')
65+
cert_dir = os.path.join(CERTBOT_DIR, 'live')
66+
for dirpath, _dirnames, filenames in os.walk(cert_dir):
67+
for filename in filenames:
68+
local_path = os.path.join(dirpath, filename)
69+
relative_path = os.path.relpath(local_path, cert_dir)
70+
s3_key = os.path.join(s3_prefix, relative_path)
71+
print(f'Uploading: {local_path} => s3://{s3_bucket}/{s3_key}')
72+
client.upload_file(local_path, s3_bucket, s3_key)
73+
74+
75+
def guarded_handler(event, context):
76+
# Input parameters
77+
email = event['email']
78+
domains = event['domains']
79+
s3_bucket = event['s3_bucket'] # The S3 bucket to publish certificates
80+
s3_prefix = event['s3_prefix'] # The S3 key prefix to publish certificates
81+
82+
obtain_certs(email, domains)
83+
upload_certs(s3_bucket, s3_prefix)
84+
85+
return 'Certificates obtained and uploaded successfully.'
86+
87+
88+
def lambda_handler(event, context):
89+
try:
90+
rm_tmp_dir()
91+
return guarded_handler(event, context)
92+
finally:
93+
rm_tmp_dir()
94+

build.sh

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#!/usr/bin/env bash
2+
# vim: et sr sw=2 ts=2 smartindent:
3+
4+
set -xe
5+
6+
##
7+
# Sets up the environment, sets constants and variables.
8+
##
9+
setenv() {
10+
AWS_PYTHON_VERSION="${AWS_PYTHON_VERSION:-"3.7"}"
11+
CERTBOT_VERSION="${CERTBOT_VERSION:-"0.32.0"}"
12+
WORKSPACE=$(pwd)
13+
14+
[ ! -d .python_cache ] && mkdir .python_cache
15+
[ ! -d .build_artifacts ] && mkdir .build_artifacts
16+
17+
return 0
18+
}
19+
20+
##
21+
# Downloads a given version of Certbot
22+
##
23+
build_for_aws() {
24+
local certbot_filename="certbot-awslambda-${1}.zip"
25+
local certbot_version=$1
26+
local aws_python_version=$2
27+
28+
docker run \
29+
--rm \
30+
-v ${WORKSPACE}/.python_cache:/root/.cache/pip \
31+
-v ${WORKSPACE}:/workspace \
32+
-w /workspace \
33+
python:${aws_python_version}-slim \
34+
bash -c -e -x " \
35+
pip install virtualenv && \
36+
virtualenv venv && \
37+
source venv/bin/activate && \
38+
pip install certbot==${certbot_version} certbot-dns-route53==${certbot_version}
39+
"
40+
41+
sudo chown -R $UID venv
42+
cd venv/lib/python${aws_python_version}/site-packages
43+
cp ${WORKSPACE}/aws/main.py .
44+
zip -q -r -9 $WORKSPACE/.build_artifacts/${certbot_filename} .
45+
}
46+
47+
##
48+
# Removes intermediary build artifacts.
49+
##
50+
cleanup() {
51+
rm -rf venv
52+
rm -rf build_artifacts
53+
}
54+
55+
##
56+
# Run this function to run the build.
57+
##
58+
run() {
59+
setenv
60+
build_for_aws ${CERTBOT_VERSION} ${AWS_PYTHON_VERSION}
61+
cleanup
62+
}
63+

0 commit comments

Comments
 (0)