An ACME-based certificate authority, written in Go.
Go to file
Aaron Gable 2eaa2fea64
SA: Stop storing and retrieving contacts (#8198)
Add a feature flag "IgnoreAccountContacts" which has two effects in the
SA:
- When a new account is created, don't insert any contacts provided; and
- When an account is retrieved, ignore any contacts already present.

This causes boulder to act as though all accounts have no associated
contacts, and is the first step towards being able to drop the contacts
from the database entirely.

Part of https://github.com/letsencrypt/boulder/issues/8176
2025-05-21 16:23:35 -07:00
.github Update protoc-gen-go to match updated grpc libraries (#8151) 2025-05-01 17:14:57 -07:00
akamai Update protoc-gen-go to match updated grpc libraries (#8151) 2025-05-01 17:14:57 -07:00
allowlist RA: Allow profile selection to be gated on account-based allow lists (#7959) 2025-01-24 12:27:24 -05:00
bdns Add more lints: asciicheck, bidichk, spancheck (#8182) 2025-05-13 11:56:40 -07:00
ca Remove OCSP and MustStaple support from issuance (#8181) 2025-05-16 11:51:02 -07:00
cmd cert-checker: add support for ipAddress SANs (#8188) 2025-05-16 16:22:56 -07:00
config Allow validating config.Duration type (#7564) 2024-06-27 13:45:19 -04:00
core va: Remove deprecated Domain from vapb.IsCAAValidRequest (#8193) 2025-05-16 15:21:28 -07:00
crl Update protoc-gen-go to match updated grpc libraries (#8151) 2025-05-01 17:14:57 -07:00
csr Add Identifiers to Authorization & Order structs (#7961) 2025-03-26 10:30:24 -07:00
ctpolicy Finish cleaning up unused CT config types (#8174) 2025-05-10 18:37:59 -07:00
data Fixes Mandrill UNSUB merge tag (#2524) 2017-01-25 10:53:17 -08:00
db sa: Store and manage rate limit overrides in the database (#8142) 2025-05-08 14:50:30 -04:00
docs crl-updater: split temporal/explicit sharding by serial (#7990) 2025-02-04 11:45:46 -05:00
email email-exporter: Count Pardot API errors encountered (#8175) 2025-05-12 14:43:09 -07:00
errors ProblemDetails no longer implements Error (#8078) 2025-03-28 13:36:26 -05:00
features SA: Stop storing and retrieving contacts (#8198) 2025-05-21 16:23:35 -07:00
goodkey Update go versions in CI and release (#7971) 2025-02-19 14:37:01 -08:00
grpc sa: use internal certificateStatusModel instead of core.CertificateStatus (#8159) 2025-05-12 14:53:08 -07:00
iana Require email domains end in a IANA suffix (#4037) 2019-01-28 17:05:58 -08:00
identifier policy: Support IP address identifiers (#8173) 2025-05-14 13:49:51 -07:00
issuance Remove OCSP and MustStaple support from issuance (#8181) 2025-05-16 11:51:02 -07:00
linter Make CT log selection simpler and more robust (#8152) 2025-05-01 17:24:19 -07:00
log Make log-validator take glob patterns to monitor for log files (#7172) 2023-11-27 12:48:46 -08:00
mail Remove all static minica keys (#7489) 2024-05-17 11:45:40 -07:00
metrics WFE: Track in-flight for "/" (#7759) 2024-10-18 12:59:26 -04:00
mocks Add more lints: asciicheck, bidichk, spancheck (#8182) 2025-05-13 11:56:40 -07:00
must Add must.Do utility function (#6955) 2023-06-26 14:43:30 -07:00
nonce Update protoc-gen-go to match updated grpc libraries (#8151) 2025-05-01 17:14:57 -07:00
observer boulder-observer: check certificate status via CRL too (#8186) 2025-05-20 09:24:21 -07:00
ocsp Update go versions in CI and release (#7971) 2025-02-19 14:37:01 -08:00
pkcs11helpers Shorten "identifier(s)" in variable names & function arguments (#8066) 2025-03-14 10:59:38 -07:00
policy policy: Support IP address identifiers (#8173) 2025-05-14 13:49:51 -07:00
precert Replace explicit int loops with range-over-int (#7434) 2024-04-22 10:34:51 -07:00
privatekey privatekey: emit clearer error on parse (#6620) 2023-01-30 00:12:12 -08:00
probs Include supported algs in badSignatureAlgorithm problem doc (#8170) 2025-05-07 18:29:14 -07:00
publisher Update protoc-gen-go to match updated grpc libraries (#8151) 2025-05-01 17:14:57 -07:00
ra va: Remove deprecated Domain from vapb.IsCAAValidRequest (#8193) 2025-05-16 15:21:28 -07:00
ratelimits policy: Support IP address identifiers (#8173) 2025-05-14 13:49:51 -07:00
redis Update certificate-transparency-go to get static/tiled log support (#8150) 2025-04-30 15:56:31 -07:00
revocation Disallow affiliationChanged revocation reason (#6217) 2022-07-07 10:45:36 -07:00
rocsp Remove all static minica keys (#7489) 2024-05-17 11:45:40 -07:00
sa SA: Stop storing and retrieving contacts (#8198) 2025-05-21 16:23:35 -07:00
semaphore Update to math/rand/v2 (#7657) 2024-08-12 09:17:09 -07:00
sfe sfe/unpause: Test that identifiers appear on confirmation (#8087) 2025-03-28 13:12:05 -04:00
strictyaml Strict YAML parsing (#6652) 2023-02-22 14:56:26 -05:00
test SA: Stop storing and retrieving contacts (#8198) 2025-05-21 16:23:35 -07:00
tools Build deb in docker (#8126) 2025-04-17 11:15:52 -04:00
unpause Shorten "identifier(s)" in variable names & function arguments (#8066) 2025-03-14 10:59:38 -07:00
va va: Remove deprecated Domain from vapb.IsCAAValidRequest (#8193) 2025-05-16 15:21:28 -07:00
vendor Update to zlint 3.6.6 (#8194) 2025-05-16 11:48:31 -07:00
web ProblemDetails no longer implements Error (#8078) 2025-03-28 13:36:26 -05:00
wfe2 Include Location: header in GET Order responses (#8202) 2025-05-21 16:21:54 -07:00
.dockerignore Roll forward "Run Travis tests in Docker (#1830)" (#1838) 2016-05-24 15:11:22 -07:00
.gitignore Dynamically generate grpc-creds at integration test startup (#7477) 2024-05-15 11:31:23 -04:00
.golangci.yml Add more lints: asciicheck, bidichk, spancheck (#8182) 2025-05-13 11:56:40 -07:00
.typos.toml Update CI to go1.23.7 and go1.24.1 (#8051) 2025-03-10 11:28:31 -07:00
CODEOWNERS Update CODEOWNERS (#3446) 2018-02-14 16:22:13 -05:00
LICENSE.txt Remove all stray copyright headers and appends the initial line to LICENSE.txt (#1853) 2016-05-31 12:32:04 -07:00
Makefile test: Copy challtestsrv management API from pebble (#8094) 2025-04-03 15:10:18 -04:00
README.md Remove all static minica keys (#7489) 2024-05-17 11:45:40 -07:00
docker-compose.next.yml Dynamically generate grpc-creds at integration test startup (#7477) 2024-05-15 11:31:23 -04:00
docker-compose.yml boulder-observer: check certificate status via CRL too (#8186) 2025-05-20 09:24:21 -07:00
go.mod Update to zlint 3.6.6 (#8194) 2025-05-16 11:48:31 -07:00
go.sum Update to zlint 3.6.6 (#8194) 2025-05-16 11:48:31 -07:00
start.py Dynamically generate grpc-creds at integration test startup (#7477) 2024-05-15 11:31:23 -04:00
staticcheck.conf CI: Run staticcheck standalone (#7055) 2023-08-31 21:09:40 -07:00
t.sh test: No longer accumulate orphans on each test run (#7978) 2025-01-24 13:28:58 -08:00
test.sh test-cli: Pass -v/--verbose flag to Go integration tests (#7754) 2024-10-10 15:26:15 -04:00
tn.sh test: No longer accumulate orphans on each test run (#7978) 2025-01-24 13:28:58 -08:00

README.md

Boulder - An ACME CA

Build Status

This is an implementation of an ACME-based CA. The ACME protocol allows the CA to automatically verify that an applicant for a certificate actually controls an identifier, and allows domain holders to issue and revoke certificates for their domains. Boulder is the software that runs Let's Encrypt.

Contents

Overview

Boulder is divided into the following main components:

  1. Web Front Ends (one per API version)
  2. Registration Authority
  3. Validation Authority
  4. Certificate Authority
  5. Storage Authority
  6. Publisher
  7. OCSP Responder
  8. CRL Updater

This component model lets us separate the function of the CA by security context. The Web Front End, Validation Authority, OCSP Responder and Publisher need access to the Internet, which puts them at greater risk of compromise. The Registration Authority can live without Internet connectivity, but still needs to talk to the Web Front End and Validation Authority. The Certificate Authority need only receive instructions from the Registration Authority. All components talk to the SA for storage, so most lines indicating SA RPCs are not shown here.

                            CA ---------> Publisher
                             ^
                             |
       Subscriber -> WFE --> RA --> SA --> MariaDB
                             |               ^
Subscriber server <- VA <----+               |
                                             |
          Browser -------------------> OCSP Responder

Internally, the logic of the system is based around five types of objects: accounts, authorizations, challenges, orders and certificates, mapping directly to the resources of the same name in ACME. Requests from ACME clients result in new objects and changes to objects. The Storage Authority maintains persistent copies of the current set of objects.

Boulder uses gRPC for inter-component communication. For components that you want to be remote, it is necessary to instantiate a "client" and "server" for that component. The client implements the component's Go interface, while the server has the actual logic for the component. A high level overview for this communication model can be found in the gRPC documentation.

The full details of how the various ACME operations happen in Boulder are laid out in DESIGN.md.

Setting up Boulder

Development

Boulder has a Dockerfile and uses Docker Compose to make it easy to install and set up all its dependencies. This is how the maintainers work on Boulder, and is our main recommended way to run it for development/experimentation. It is not suitable for use as a production environment.

While we aim to make Boulder easy to setup ACME client developers may find Pebble, a miniature version of Boulder, to be better suited for continuous integration and quick experimentation.

We recommend setting git's fsckObjects setting before getting a copy of Boulder to have better integrity guarantees for updates.

Clone the boulder repository:

git clone https://github.com/letsencrypt/boulder/
cd boulder

Additionally, make sure you have Docker Engine 1.13.0+ and Docker Compose 1.10.0+ installed. If you do not, you can follow Docker's installation instructions.

We recommend having at least 2GB of RAM available on your Docker host. In practice using less RAM may result in the MariaDB container failing in non-obvious ways.

To start Boulder in a Docker container, run:

docker compose up

To run our standard battery of tests (lints, unit, integration):

docker compose run --use-aliases boulder ./test.sh

To run all unit tests:

docker compose run --use-aliases boulder ./test.sh --unit

To run specific unit tests (example is of the ./va directory):

docker compose run --use-aliases boulder ./test.sh --unit --filter=./va

To run all integration tests:

docker compose run --use-aliases boulder ./test.sh --integration

To run specific integration tests (example runs TestAkamaiPurgerDrainQueueFails and TestWFECORS):

docker compose run --use-aliases boulder ./test.sh --filter TestAkamaiPurgerDrainQueueFails/TestWFECORS

To get a list of available integration tests:

docker compose run --use-aliases boulder ./test.sh --list-integration-tests

The configuration in docker-compose.yml mounts your boulder checkout at /boulder so you can edit code on your host and it will be immediately reflected inside the Docker containers run with docker compose.

If you have problems with Docker, you may want to try removing all containers and volumes.

By default, Boulder uses a fake DNS resolver that resolves all hostnames to 127.0.0.1. This is suitable for running integration tests inside the Docker container. If you want Boulder to be able to communicate with a client running on your host instead, you should find your host's Docker IP with:

ifconfig docker0 | grep "inet addr:" | cut -d: -f2 | awk '{ print $1}'

And edit docker-compose.yml to change the FAKE_DNS environment variable to match. This will cause Boulder's stubbed-out DNS resolver (sd-test-srv) to respond to all A queries with the address in FAKE_DNS.

If you use a host-based firewall (e.g. ufw or iptables) make sure you allow connections from the Docker instance to your host on the required validation ports to your ACME client.

Alternatively, you can override the docker-compose.yml default with an environmental variable using -e (replace 172.17.0.1 with the host IPv4 address found in the command above)

docker compose run --use-aliases -e FAKE_DNS=172.17.0.1 --service-ports boulder ./start.py

Running tests without the ./test.sh wrapper:

Run all unit tests

docker compose run --use-aliases boulder go test -p 1 ./...

Run unit tests for a specific directory:

docker compose run --use-aliases boulder go test <DIRECTORY>

Run integration tests (omit --filter <REGEX> to run all):

docker compose run --use-aliases boulder python3 test/integration-test.py --chisel --gotest --filter <REGEX>

Working with Certbot

Check out the Certbot client from https://github.com/certbot/certbot and follow their setup instructions. Once you've got the client set up, you'll probably want to run it against your local Boulder. There are a number of command line flags that are necessary to run the client against a local Boulder, and without root access. The simplest way to run the client locally is to use a convenient alias for certbot (certbot_test) with a custom SERVER environment variable:

SERVER=http://localhost:4001/directory certbot_test certonly --standalone -d test.example.com

Your local Boulder instance uses a fake DNS resolver that returns 127.0.0.1 for any query, so you can use any value for the -d flag. To return an answer other than 127.0.0.1 change the Boulder FAKE_DNS environment variable to another IP address.

Working with another ACME Client

Once you have followed the Boulder development environment instructions and have started the containers you will find the ACME endpoints exposed to your host at the following URLs:

  • ACME v2, HTTP: http://localhost:4001/directory
  • ACME v2, HTTPS: https://localhost:4431/directory

To access the HTTPS versions of the endpoints you will need to configure your ACME client software to use a CA truststore that contains the test/certs/ipki/minica.pem CA certificate. See test/certs/README.md for more information.

Your local Boulder instance uses a fake DNS resolver that returns 127.0.0.1 for any query, allowing you to issue certificates for any domain as if it resolved to your localhost. To return an answer other than 127.0.0.1 change the Boulder FAKE_DNS environment variable to another IP address.

Most often you will want to configure FAKE_DNS to point to your host machine where you run an ACME client.

Production

Boulder is custom built for Let's Encrypt and is intended only to support the Web PKI and the CA/Browser forum's baseline requirements. In our experience often Boulder is not the right fit for organizations that are evaluating it for production usage. In most cases a centrally managed PKI that doesn't require domain-authorization with ACME is a better choice. For this environment we recommend evaluating a project other than Boulder.

We offer a brief deployment and implementation guide that describes some of the required work and security considerations involved in using Boulder in a production environment. As-is the docker based Boulder development environment is not suitable for production usage. It uses private key material that is publicly available, exposes debug ports and is brittle to component failure.

While we are supportive of other organization's deploying Boulder in a production setting we prioritize support and development work that favors Let's Encrypt's mission. This means we may not be able to provide timely support or accept pull-requests that deviate significantly from our first line goals. If you've thoroughly evaluated the alternatives and Boulder is definitely the best fit we're happy to answer questions to the best of our ability.

Contributing

Please take a look at CONTRIBUTING.md for our guidelines on submitting patches, code review process, code of conduct, and various other tips related to working on the codebase.

Code of Conduct

The code of conduct for everyone participating in this community in any capacity is available for reference on the community forum.

License

This project is licensed under the Mozilla Public License 2.0, the full text of which can be found in the LICENSE.txt file.