Fix Docker setup.

Add an easy script to build and run the Docker instance.
Update some out-of-date information in the README.
Add goose to the Docker image.
Remove unnecessary go install step from Dockerfile.

Allow dns-test-srv to return a hardcoded address other than localhost. This was
preventing a Dockerized Boulder from answering requests from a letsencrypt
client on the host.

Change allowLoopbackAddresses to allowRestrictedAddresses and make it cover all
the private IPv4 ranges. The host IP in Docker is commonly in the 172.* range.

Fix a couple of references to lets-encrypt-preview.

This was inspired by investigation into https://github.com/letsencrypt/boulder/issues/756.
To try and reproduce, I tried running Boulder inside a container, and found some
broken things.
This commit is contained in:
Jacob Hoffman-Andrews 2015-09-08 23:23:02 -04:00
parent 566a8e4189
commit 19c68a01e0
5 changed files with 79 additions and 60 deletions

View File

@ -33,7 +33,7 @@ EXPOSE 4000
ENV BOULDER_CONFIG /go/src/github.com/letsencrypt/boulder/test/boulder-config.json
# Get the Let's Encrypt client
RUN git clone https://www.github.com/letsencrypt/lets-encrypt-preview.git /letsencrypt
RUN git clone https://www.github.com/letsencrypt/letsencrypt.git /letsencrypt
WORKDIR /letsencrypt
RUN ./bootstrap/debian.sh && \
apt-get clean && \
@ -43,20 +43,13 @@ RUN ./bootstrap/debian.sh && \
RUN virtualenv --no-site-packages -p python2 venv && \
./venv/bin/pip install -r requirements.txt -e acme -e .[dev,docs,testing] -e letsencrypt-apache -e letsencrypt-nginx
RUN go get bitbucket.org/liamstask/goose/cmd/goose
ENV LETSENCRYPT_PATH /letsencrypt
# Copy in the Boulder sources
COPY . /go/src/github.com/letsencrypt/boulder
# Build Boulder
RUN go install \
github.com/letsencrypt/boulder/cmd/activity-monitor \
github.com/letsencrypt/boulder/cmd/boulder-ca \
github.com/letsencrypt/boulder/cmd/boulder-ra \
github.com/letsencrypt/boulder/cmd/boulder-sa \
github.com/letsencrypt/boulder/cmd/boulder-va \
github.com/letsencrypt/boulder/cmd/boulder-wfe
WORKDIR /go/src/github.com/letsencrypt/boulder
CMD ["bash", "-c", "service mysql start && \
service rsyslog start && \

View File

@ -7,49 +7,30 @@ This is an initial implementation of an ACME-based CA. The [ACME protocol](https
[![Build Status](https://travis-ci.org/letsencrypt/boulder.svg)](https://travis-ci.org/letsencrypt/boulder)
[![Coverage Status](https://coveralls.io/repos/letsencrypt/boulder/badge.svg)](https://coveralls.io/r/letsencrypt/boulder)
Docker
Quickstart
------
Boulder is available as a [Docker image from Quay.io](https://quay.io/repository/letsencrypt/boulder). The Docker image expects the `config.json` file to be located at `/boulder/config.json` within the container.
Boulder has a Dockerfile to make it easy to install and set up all its
dependencies. This approach is most suitable if you just need to set up Boulder
for the purpose of testing client software against it. To start Boulder
in a Docker container, run:
(Note: You can override the `config.json` location by specifying a different BOULDER_CONFIG environment variable, such as with `-e BOULDER_CONFIG=mypath/myfile.config`.)
./test/run-docker.sh
There are no default commands; you must choose one of the executables from the `cmd` path.
There are several tags available:
- `stable` is maintained by the Let's Encrypt team as a fairly stable copy of Boulder.
- `latest` is a more recent build of Boulder. It may lag behind the `master` ref, as automated builds are being reworked.
- Tags for individual short-format git refs, representing those builds.
A quick-start method for running a Boulder instance is to use one of the example configurations:
docker run -i --name=boulder --read-only=true --rm=true -p 4000:4000 quay.io/letsencrypt/boulder:latest
Alternatively, to run all services locally, using AMQP to pass messages between them, you can use:
```
> python start.py
# start.py will use the configuration specified by BOULDER_CONFIG or test/boulder-config.json
```
To run a single module, specifying the AMQP server, you might use something more like:
```
> docker run --name=boulder --read-only=true --rm=true -v $(pwd)/.boulder-config:/boulder:ro quay.io/letsencrypt/boulder:stable boulder-ra
```
Quickstart
Slow start
----------
Boulder requires an installation of RabbitMQ, libtool-ltdl, and
This approach is better if you intend to develop on Boulder frequently, because
it's challenging to develop inside the Docker container.
Boulder requires an installation of RabbitMQ, libtool-ltdl, goose, and
MariaDB 10 to work correctly. On Ubuntu and CentOS, you may have to
install RabbitMQ from https://rabbitmq.com/download.html to get a
recent version.
Also, Boulder requires Go 1.5. As of September 2015 this version is not yet
available in OS repostiories, so you will have to install from https://golang.org/dl/.
Ubuntu:
sudo apt-get install libltdl3-dev mariadb-server rabbitmq-server
@ -69,19 +50,22 @@ or
(On OS X, using port, you will have to add `CGO_CFLAGS="-I/opt/local/include" CGO_LDFLAGS="-L/opt/local/lib"` to your environment or `go` invocations.)
```
> go get bitbucket.org/liamstask/goose/cmd/goose
> go get github.com/letsencrypt/boulder/ # Ignore errors about no buildable files
> cd $GOPATH/src/github.com/letsencrypt/boulder
> ./test/create_db.sh
# This starts each Boulder component with test configs. Ctrl-C kills all.
> ./start.py
# Run tests
> ./test.sh
```
The databases that boulder requires to operate in development and
testing can be created using test/create\_db.sh. It uses the root
MariaDB user, so if you have disabled that account you may have to
adjust the file or recreate the commands.
Note: create\_db.sh it uses the root MariaDB user, so if you
have disabled that account you may have to adjust the file or
recreate the commands.
You can also check out the official client from
https://github.com/letsencrypt/lets-encrypt-preview/ and follow the setup
https://github.com/letsencrypt/letsencrypt/ and follow the setup
instructions there.
Component Model
@ -130,7 +114,7 @@ and to [avoid insecure fallback in go
get](https://github.com/golang/go/issues/9637).
Local development also requires a RabbitMQ installation and MariaDB
10 installation. MariaDB should be run on port 3306 for the
10 or MySQL installation (see above). MariaDB should be run on port 3306 for the
default integration tests.
To update the Go dependencies:

View File

@ -45,9 +45,9 @@ var (
// DNSResolverImpl represents a client that talks to an external resolver
type DNSResolverImpl struct {
DNSClient *dns.Client
Servers []string
allowLoopbackAddresses bool
DNSClient *dns.Client
Servers []string
allowRestrictedAddresses bool
}
// NewDNSResolverImpl constructs a new DNS resolver object that utilizes the
@ -59,9 +59,9 @@ func NewDNSResolverImpl(dialTimeout time.Duration, servers []string) *DNSResolve
dnsClient.DialTimeout = dialTimeout
return &DNSResolverImpl{
DNSClient: dnsClient,
Servers: servers,
allowLoopbackAddresses: false,
DNSClient: dnsClient,
Servers: servers,
allowRestrictedAddresses: false,
}
}
@ -70,7 +70,7 @@ func NewDNSResolverImpl(dialTimeout time.Duration, servers []string) *DNSResolve
// This constructor should *only* be called from tests (unit or integration).
func NewTestDNSResolverImpl(dialTimeout time.Duration, servers []string) *DNSResolverImpl {
resolver := NewDNSResolverImpl(dialTimeout, servers)
resolver.allowLoopbackAddresses = true
resolver.allowRestrictedAddresses = true
return resolver
}
@ -120,8 +120,8 @@ func (dnsResolver *DNSResolverImpl) LookupTXT(hostname string) ([]string, time.D
return txt, rtt, err
}
func isPrivateV4(ip net.IP, allowLoopback bool) bool {
return rfc1918_10.Contains(ip) || rfc1918_172_16.Contains(ip) || rfc1918_192_168.Contains(ip) || (!allowLoopback && rfc5735_127.Contains(ip))
func isPrivateV4(ip net.IP) bool {
return rfc1918_10.Contains(ip) || rfc1918_172_16.Contains(ip) || rfc1918_192_168.Contains(ip) || rfc5735_127.Contains(ip)
}
// LookupHost sends a DNS query to find all A records associated with the provided
@ -141,7 +141,7 @@ func (dnsResolver *DNSResolverImpl) LookupHost(hostname string) ([]net.IP, time.
for _, answer := range r.Answer {
if answer.Header().Rrtype == dns.TypeA {
if a, ok := answer.(*dns.A); ok && a.A.To4() != nil && !isPrivateV4(a.A, dnsResolver.allowLoopbackAddresses) {
if a, ok := answer.(*dns.A); ok && a.A.To4() != nil && (!isPrivateV4(a.A) || dnsResolver.allowRestrictedAddresses) {
addrs = append(addrs, a.A)
}
}

View File

@ -8,6 +8,7 @@ package main
import (
"fmt"
"net"
"os"
"time"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/miekg/dns"
@ -19,6 +20,14 @@ func dnsHandler(w dns.ResponseWriter, r *dns.Msg) {
m.SetReply(r)
m.Compress = false
// Normally this test DNS server will return 127.0.0.1 for everything.
// However, in some situations (for instance Docker), it's useful to return a
// different hardcoded host. You can do so by setting the FAKE_DNS environment
// variable.
fakeDNS := os.Getenv("FAKE_DNS")
if fakeDNS == "" {
fakeDNS = "127.0.0.1"
}
for _, q := range r.Question {
fmt.Printf("dns-srv: Query -- [%s] %s\n", q.Name, dns.TypeToString[q.Qtype])
switch q.Qtype {
@ -30,7 +39,7 @@ func dnsHandler(w dns.ResponseWriter, r *dns.Msg) {
Class: dns.ClassINET,
Ttl: 0,
}
record.A = net.ParseIP("127.0.0.1")
record.A = net.ParseIP(fakeDNS)
m.Answer = append(m.Answer, record)
case dns.TypeMX:

33
test/run-docker.sh Executable file
View File

@ -0,0 +1,33 @@
#!/bin/bash
#
# Build and run a docker image for Boulder. This is suitable for running
# repeatedly during development because Docker will cache the image it builds,
# and will only re-do the minimum necessary.
#
# NOTE: Currently we're not able to effectively cache the DB setup steps,
# because setting up the DB depends on source files in the Boulder repo. So any
# time source files change, Docker treats that as potentially invalidating the
# steps that came after the COPY. In theory we could add a step that copies only
# the files necessary to do the migrations, run them, and then copy the rest of
# the source.
set -o errexit
cd $(dirname $0)/..
# In order to talk to a letsencrypt client running on the host, the fake DNS
# client used in Boulder's start.py needs to know what the host's IP is from the
# perspective of the container. We try to figure it out automatically. If you'd
# like your Boulder instance to always talk to some other host, you can set
# FAKE_DNS to that host's IP address.
if [ -z "${FAKE_DNS}" ] ; then
FAKE_DNS=$(ifconfig docker0 | sed -n 's/ *inet addr:\([0-9.]\+\).*/\1/p')
fi
docker build --tag boulder .
# The -i command makes the instance interactive, so you can kill start.py with Ctrl-C.
docker run \
--interactive \
--tty \
--rm=true \
--publish 4000-4001:4000-4001 \
--publish 8000-8100:8000-8100 \
--env FAKE_DNS="${FAKE_DNS}" \
boulder