Add ACME/Let’s Encrypt integration tests
Thx @gwallet for the help.
This commit is contained in:
parent
e1ed8b71f6
commit
599c95e5f6
4 changed files with 182 additions and 0 deletions
92
integration/acme_test.go
Normal file
92
integration/acme_test.go
Normal file
|
@ -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)
|
||||||
|
}
|
32
integration/fixtures/acme/acme.toml
Normal file
32
integration/fixtures/acme/acme.toml
Normal file
|
@ -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"
|
|
@ -4,6 +4,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
@ -34,6 +35,7 @@ func init() {
|
||||||
check.Suite(&ConstraintSuite{})
|
check.Suite(&ConstraintSuite{})
|
||||||
check.Suite(&MesosSuite{})
|
check.Suite(&MesosSuite{})
|
||||||
check.Suite(&EurekaSuite{})
|
check.Suite(&EurekaSuite{})
|
||||||
|
check.Suite(&AcmeSuite{})
|
||||||
}
|
}
|
||||||
|
|
||||||
var traefikBinary = "../dist/traefik"
|
var traefikBinary = "../dist/traefik"
|
||||||
|
@ -52,6 +54,18 @@ func (s *BaseSuite) TearDownSuite(c *check.C) {
|
||||||
func (s *BaseSuite) createComposeProject(c *check.C, name string) {
|
func (s *BaseSuite) createComposeProject(c *check.C, name string) {
|
||||||
projectName := fmt.Sprintf("integration-test-%s", name)
|
projectName := fmt.Sprintf("integration-test-%s", name)
|
||||||
composeFile := fmt.Sprintf("resources/compose/%s.yml", 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)
|
s.composeProject = compose.CreateProject(c, projectName, composeFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
44
integration/resources/compose/boulder.yml
Normal file
44
integration/resources/compose/boulder.yml
Normal file
|
@ -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"
|
Loading…
Reference in a new issue