AAA is an ACME Agent for AWS environment. All information is stored on S3 with SSE-KMS. This design allows us to run ACME agent in stateless (e.g. AWS Lambda).
AAA works well with DNS-01 on LE's production environment. It means you can issue certificates without HTTP servers.
- ✔️ New Registration
- ✔️ New Authorization
- ✔️ dns-01 with Route53
- ✔️ Create CSR with SAN (Subject Alternative Name)
- ✔️ Issue certificates
- ✔️ Store data on S3 with SSE-KMS
- ✔️ Renewal management by utilizing S3
- ✔️ AWS Lambda build
- ✔️ authz, cert
- ✔️ automatic certificates renewal
- ✔️ Confirmed that it works well with ELB
- ✔️ Integrate with Apex.
aaa is still in beta so no pre-built binaries are available.
go get -u github.com/nabeken/aaa
AAA
requires:
- AWS KMS Encryption Key for encrypting all data in the S3 bucket and token in the Lambda Function
- A dedicated S3 bucket that holds
/aaa-data/*
: will be used for all generated secrets and certificates so under this prefix MUST be encrypted
To protect you from uploading data without encryption, I highly recommend you to add a bucket policy like this:
{
"Version": "2012-10-17",
"Id": "PutObjPolicy",
"Statement": [
{
"Sid": "DenyUnEncryptedObjectUploads",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::YourBucket/aaa-data/*",
"Condition": {
"StringNotEquals": {
"s3:x-amz-server-side-encryption": "aws:kms"
}
}
}
]
}
To issue the certificate, you must:
- register your account key pair
- authorize your domain with solving the challenge
Finally, you are able to request ACME server to issue your certificates.
In default, ACME API endpoint in aaa
points to LE's staging environment.
After you grasp how aaa
works, you can point the endpoint to LE's production environment.
export AAA_DIRECTORY_URL=https://acme-v01.api.letsencrypt.org/directory
aaa reg --email you@example.com --s3-bucket YourBucket --s3-kms-key xxxx
Please agree with TOS found at https://letsencrypt.org/documents/LE-SA-v1.0.1-July-27-2015.pdf
aaa
prints the message that you must agree TOS to proceed. After you read it and agree with it:
aaa reg --email you@example.com --s3-bucket YourBucket --s3-kms-key xxxx --agree https://letsencrypt.org/documents/LE-SA-v1.0.1-July-27-2015.pdf
aaa
implements DNS-01 solver. You must provide Route53 environment.
aaa authz --email you@example.com --s3-bucket YourBucket --s3-kms-key xxxx --domain le-test-01.example.com --challenge dns-01
Bonus: You authorize more domains, you will get a certificate that has SAN for your domains.
aaa authz --email you@example.com --s3-bucket YourBucket --s3-kms-key xxxx --domain le-test-02.example.com --challenge dns-01
Let's issue a certifiate for two domains le-test-0[12].example.com
. If you don't want to issue a certificate with SAN, just drop --domain
argument.
aaa cert --email you@example.com --s3-bucket YourBucket --s3-kms-key xxxx --cn le-test-01.example.com --domain le-test-02.example.com --create-key
Please note --create-key
is required for the first time since you don't have a private key for the certificate.
To renew the cert, you can still reuse the private key with omitting --create-key
or re-generate it with --create-key
.
To show all accounts and certificates, you can use ls
subcommand like this:
aaa ls --s3-bucket YourBucket --s3-kms-key xxxx | jq -r .
[
{
"email": "letest-stag@example.com",
"domain": "le-test-dns-01.example.com",
"authorization": {
"expires": "2016-11-06T16:57:22Z"
},
"certificate": {
"not_before": "2016-01-11T16:02:00Z",
"not_after": "2016-04-10T16:02:00Z",
"san": [
"le-test-dns-01.example.com"
]
}
}
]
Please note that information is encoded in JSON. This information will be used for certificate renewal management and it allows another processes to consume the info easily.
If the authorization is still available, you can just issue the certificate.
Otherwise, you need to begin with authorization by using authz
subcommand. See the above.Authorization
section.
aaa cert --email you@example.com --s3-bucket YourBucket --s3-kms-key xxxx --cn le-test-01.example.com --domain le-test-02.example.com
If you want to regenerate the private key, add --create-key
flag too:
aaa cert --email you@example.com --s3-bucket YourBucket --s3-kms-key xxxx --create-key --cn le-test-01.example.com --domain le-test-02.example.com
Create an R/O IAM role/user for a specific prefix like /aaa-data/foobar@example.com/domain/le-test.example.com
like this:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt1453475376000",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": [
"arn:aws:s3:::example-bucket/aaa-data/foobar@example.com/domain/le-test.example.com/*"
]
},
{
"Sid": "Stmt1453475425000",
"Effect": "Allow",
"Action": [
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::example-bucket"
]
}
]
}
And add this user to KMS users.
Do aaa sync
with the prefix periodically:
aaa sync --email foobar@example.com --domain le-test.example.com --s3-bucket example-bucket
Profit!
Or
- Create an R/O IAM role/user for a specific prefix like
/aaa-data/foobar@example.com/domain/le-test.example.com
- When automatic renewal process puts new certificate on S3, S3 notifications will be generated
- Respond with the notification with Lambda Function and update the certificate
- Profit!
We integrate aaa
with Slack's Slash Commands by Apex. To do this, we need:
- AWS API Gateway that invokes...
- AWS Lambda Function
aaa_dispatcher
synchronously that invokes ... - AWS Lambda Function
aaa_executor
asynchronously
We should respond to Slash Commands within 3 seconds so we have to defer to aaa_executor
asynchronously.
Steps: (assumes that you have already the KMS key and the S3 bucket)
- Create a Slash Command
/letsencrypt
in Slack but let the endpoint URL be empty - Create a IAM role for the lambda functions
- Create a Apex configuration
project.json
- Deploy the lambda functions by apex
- Create a API Gateway
aaa-dispatcher-gateway
and deploy it - Update the Slash Command to fill the endpoint URL that points to the
aaa-dispatcher-gateway
Here we go!
Please see https://api.slack.com/slash-commands for detail. Here, token
parameter is needed.
We need a IAM role for Lambda environment.
Please create a IAM role aaa-lambda
. It should have the following managed policies:
AmazonS3FullAccess
AmazonRoute53FullAccess
CloudWatchLogsFullAccesss
AWSLambdaRole
IAMFullAccess
and the following inline policy aaa-kms
:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"kms:Decrypt"
],
"Resource": [
"<KMS_KEY_ARN>"
]
}
]
}
# Copy sample
cp project.json.example project.json
# Fill it
$EDITOR project.json
# dryrun
apex deploy -D
# If everything is okay, then shipit
apex deploy
Create an API named aaa-dispatcher-gateway
.
We really don't care about how resources are located but we need a resource that accepts POST
method and some configurations for the resource:
- Integration Request
- Integration type: Lambda Function
- Lambda Region: (Select your region)
- Lambda Function:
aaa-dispatcher
- Mapping Templates: Set
Content-Type: application/x-www-form-urlencoded
and create mapping tables that converts POST form request into JSON. You can find some examples on the Internet.
Then, deploy API. You will get a invoke URL for the resource.
Finally, you can fill the URL in Integration Settings
.
You can invoke aaa_scheduler
lambda function by CloudWatch Events.
The scheduler will invoke the executor lambda function when a domain requires authz or cert renewal 30 days before it expires.
- github.com/aws/aws-sdk-go
- github.com/lestrrat/go-jwx
- github.com/tent/http-link-go
- github.com/jessevdk/go-flags
- Integrate S3 Event Notifications ...
- To automate the installation of certificates (e.g. ELB)