diff --git a/integration/acme_test.go b/integration/acme_test.go new file mode 100644 index 000000000..485e42673 --- /dev/null +++ b/integration/acme_test.go @@ -0,0 +1,92 @@ +package main + +import ( + "crypto/tls" + "net/http" + "os" + "os/exec" + "time" + + "github.com/go-check/check" + + "errors" + "github.com/containous/traefik/integration/utils" + checker "github.com/vdemeester/shakers" +) + +// ACME test suites (using libcompose) +type AcmeSuite struct { + BaseSuite +} + +func (s *AcmeSuite) SetUpSuite(c *check.C) { + s.createComposeProject(c, "boulder") + s.composeProject.Start(c) + + boulderHost := s.composeProject.Container(c, "boulder").NetworkSettings.IPAddress + + // wait for boulder + err := utils.Try(120*time.Second, func() error { + resp, err := http.Get("http://" + boulderHost + ":4000/directory") + if err != nil { + return err + } + if resp.StatusCode != 200 { + return errors.New("Expected http 200 from boulder") + } + return nil + }) + + c.Assert(err, checker.IsNil) +} + +func (s *AcmeSuite) TearDownSuite(c *check.C) { + // shutdown and delete compose project + if s.composeProject != nil { + s.composeProject.Stop(c) + } +} + +func (s *AcmeSuite) TestRetrieveAcmeCertificate(c *check.C) { + boulderHost := s.composeProject.Container(c, "boulder").NetworkSettings.IPAddress + file := s.adaptFile(c, "fixtures/acme/acme.toml", struct{ BoulderHost string }{boulderHost}) + defer os.Remove(file) + cmd := exec.Command(traefikBinary, "--configFile="+file) + err := cmd.Start() + c.Assert(err, checker.IsNil) + defer cmd.Process.Kill() + + backend := startTestServer("9010", 200) + defer backend.Close() + + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + client := &http.Client{Transport: tr} + + // wait for traefik (generating acme account take some seconds) + err = utils.Try(30*time.Second, func() error { + _, err := client.Get("https://127.0.0.1:5001") + if err != nil { + return err + } + return nil + }) + c.Assert(err, checker.IsNil) + + tr = &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + ServerName: "traefik.acme.wtf", + }, + } + client = &http.Client{Transport: tr} + req, _ := http.NewRequest("GET", "https://127.0.0.1:5001/", nil) + req.Host = "traefik.acme.wtf" + req.Header.Set("Host", "traefik.acme.wtf") + req.Header.Set("Accept", "*/*") + resp, err := client.Do(req) + c.Assert(err, checker.IsNil) + // Expected a 200 + c.Assert(resp.StatusCode, checker.Equals, 200) +} diff --git a/integration/fixtures/acme/acme.toml b/integration/fixtures/acme/acme.toml new file mode 100644 index 000000000..e1e637bf0 --- /dev/null +++ b/integration/fixtures/acme/acme.toml @@ -0,0 +1,32 @@ +logLevel = "DEBUG" + +defaultEntryPoints = ["http", "https"] + +[entryPoints] + [entryPoints.http] + address = ":8080" + [entryPoints.https] + address = ":5001" + [entryPoints.https.tls] + + +[acme] +email = "test@traefik.io" +storage = "/dev/null" +entryPoint = "https" +onDemand = true +caServer = "http://{{.BoulderHost}}:4000/directory" + +[file] + +[backends] + [backends.backend] + [backends.backend.servers.server1] + url = "http://127.0.0.1:9010" + + +[frontends] + [frontends.frontend] + backend = "backend" + [frontends.frontend.routes.test] + rule = "Host:traefik.acme.wtf" diff --git a/integration/integration_test.go b/integration/integration_test.go index ec033cd56..78463f51c 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -4,6 +4,7 @@ package main import ( "fmt" "io/ioutil" + "net" "os" "os/exec" "path/filepath" @@ -34,6 +35,7 @@ func init() { check.Suite(&ConstraintSuite{}) check.Suite(&MesosSuite{}) check.Suite(&EurekaSuite{}) + check.Suite(&AcmeSuite{}) } var traefikBinary = "../dist/traefik" @@ -52,6 +54,18 @@ func (s *BaseSuite) TearDownSuite(c *check.C) { func (s *BaseSuite) createComposeProject(c *check.C, name string) { projectName := fmt.Sprintf("integration-test-%s", name) composeFile := fmt.Sprintf("resources/compose/%s.yml", name) + + addrs, err := net.InterfaceAddrs() + c.Assert(err, checker.IsNil) + for _, addr := range addrs { + ip, _, err := net.ParseCIDR(addr.String()) + c.Assert(err, checker.IsNil) + if !ip.IsLoopback() && ip.To4() != nil { + os.Setenv("DOCKER_HOST_IP", ip.String()) + break + } + } + s.composeProject = compose.CreateProject(c, projectName, composeFile) } diff --git a/integration/resources/compose/boulder.yml b/integration/resources/compose/boulder.yml new file mode 100644 index 000000000..8320c6a92 --- /dev/null +++ b/integration/resources/compose/boulder.yml @@ -0,0 +1,44 @@ +boulder: + image: containous/boulder:release + environment: + FAKE_DNS: ${DOCKER_HOST_IP} + PKCS11_PROXY_SOCKET: tcp://boulder-hsm:5657 + extra_hosts: + - le.wtf:127.0.0.1 + - boulder:127.0.0.1 + ports: + - 4000:4000 # ACME + - 4002:4002 # OCSP + - 4003:4003 # OCSP + - 4500:4500 # ct-test-srv + - 8000:8000 # debug ports + - 8001:8001 + - 8002:8002 + - 8003:8003 + - 8004:8004 + - 8055:8055 # dns-test-srv updates + - 9380:9380 # mail-test-srv + - 9381:9381 # mail-test-srv + links: + - bhsm:boulder-hsm + - bmysql:boulder-mysql + - brabbitmq:boulder-rabbitmq + +bhsm: + # To minimize the fetching of various layers this should match + # the FROM image and tag in boulder/Dockerfile + image: letsencrypt/boulder-tools:2016-11-02 + environment: + PKCS11_DAEMON_SOCKET: tcp://0.0.0.0:5657 + command: /usr/local/bin/pkcs11-daemon /usr/lib/softhsm/libsofthsm.so + expose: + - 5657 +bmysql: + image: mariadb:10.1 + environment: + MYSQL_ALLOW_EMPTY_PASSWORD: "yes" + log_driver: none +brabbitmq: + image: rabbitmq:3 + environment: + RABBITMQ_NODE_IP_ADDRESS: "0.0.0.0"