ACME TLS ALPN
This commit is contained in:
parent
17ad5153b8
commit
139f280f35
258 changed files with 25528 additions and 1516 deletions
|
@ -4,8 +4,10 @@ import (
|
|||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/containous/traefik/integration/try"
|
||||
|
@ -13,268 +15,360 @@ import (
|
|||
"github.com/containous/traefik/testhelpers"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/go-check/check"
|
||||
"github.com/miekg/dns"
|
||||
checker "github.com/vdemeester/shakers"
|
||||
)
|
||||
|
||||
// ACME test suites (using libcompose)
|
||||
type AcmeSuite struct {
|
||||
BaseSuite
|
||||
boulderIP string
|
||||
pebbleIP string
|
||||
fakeDNSServer *dns.Server
|
||||
}
|
||||
|
||||
// Acme tests configuration
|
||||
type AcmeTestCase struct {
|
||||
configuration acme.Configuration
|
||||
type acmeTestCase struct {
|
||||
template templateModel
|
||||
traefikConfFilePath string
|
||||
expectedDomain string
|
||||
expectedCommonName string
|
||||
expectedAlgorithm x509.PublicKeyAlgorithm
|
||||
}
|
||||
|
||||
type templateModel struct {
|
||||
PortHTTP string
|
||||
PortHTTPS string
|
||||
Acme acme.Configuration
|
||||
}
|
||||
|
||||
const (
|
||||
// Domain to check
|
||||
acmeDomain = "traefik.acme.wtf"
|
||||
|
||||
// Wildcard domain to check
|
||||
wildcardDomain = "*.acme.wtf"
|
||||
|
||||
// Traefik default certificate
|
||||
traefikDefaultDomain = "TRAEFIK DEFAULT CERT"
|
||||
)
|
||||
|
||||
func (s *AcmeSuite) getAcmeURL() string {
|
||||
return fmt.Sprintf("http://%s:4001/directory", s.boulderIP)
|
||||
return fmt.Sprintf("https://%s:14000/dir", s.pebbleIP)
|
||||
}
|
||||
|
||||
func setupPebbleRootCA() (*http.Transport, error) {
|
||||
path, err := filepath.Abs("fixtures/acme/ssl/pebble.minica.pem")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
os.Setenv("LEGO_CA_CERTIFICATES", path)
|
||||
os.Setenv("LEGO_CA_SERVER_NAME", "pebble")
|
||||
|
||||
customCAs, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
certPool := x509.NewCertPool()
|
||||
if ok := certPool.AppendCertsFromPEM(customCAs); !ok {
|
||||
return nil, fmt.Errorf("error creating x509 cert pool from %q: %v", path, err)
|
||||
}
|
||||
|
||||
return &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
ServerName: "pebble",
|
||||
RootCAs: certPool,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *AcmeSuite) SetUpSuite(c *check.C) {
|
||||
s.createComposeProject(c, "boulder")
|
||||
s.createComposeProject(c, "peddle")
|
||||
s.composeProject.Start(c)
|
||||
|
||||
s.boulderIP = s.composeProject.Container(c, "boulder").NetworkSettings.IPAddress
|
||||
s.fakeDNSServer = startFakeDNSServer()
|
||||
|
||||
// wait for boulder
|
||||
err := try.GetRequest(s.getAcmeURL(), 120*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||
s.pebbleIP = s.composeProject.Container(c, "pebble").NetworkSettings.IPAddress
|
||||
|
||||
pebbleTransport, err := setupPebbleRootCA()
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
// wait for peddle
|
||||
req := testhelpers.MustNewRequest(http.MethodGet, s.getAcmeURL(), nil)
|
||||
|
||||
client := &http.Client{
|
||||
Transport: pebbleTransport,
|
||||
}
|
||||
|
||||
err = try.Do(5*time.Second, func() error {
|
||||
resp, errGet := client.Do(req)
|
||||
if errGet != nil {
|
||||
return errGet
|
||||
}
|
||||
return try.StatusCodeIs(http.StatusOK)(resp)
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
func (s *AcmeSuite) TearDownSuite(c *check.C) {
|
||||
err := s.fakeDNSServer.Shutdown()
|
||||
if err != nil {
|
||||
c.Log(err)
|
||||
}
|
||||
|
||||
// shutdown and delete compose project
|
||||
if s.composeProject != nil {
|
||||
s.composeProject.Stop(c)
|
||||
}
|
||||
}
|
||||
|
||||
// Test ACME provider with certificate at start
|
||||
func (s *AcmeSuite) TestACMEProviderAtStart(c *check.C) {
|
||||
testCase := AcmeTestCase{
|
||||
func (s *AcmeSuite) TestHTTP01DomainsAtStart(c *check.C) {
|
||||
testCase := acmeTestCase{
|
||||
traefikConfFilePath: "fixtures/acme/acme-base.toml",
|
||||
configuration: acme.Configuration{
|
||||
CAServer: s.getAcmeURL(),
|
||||
HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"},
|
||||
OnHostRule: true,
|
||||
Domains: types.Domains{types.Domain{
|
||||
Main: "traefik.acme.wtf",
|
||||
}},
|
||||
template: templateModel{
|
||||
Acme: acme.Configuration{
|
||||
HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"},
|
||||
Domains: types.Domains{types.Domain{
|
||||
Main: "traefik.acme.wtf",
|
||||
}},
|
||||
},
|
||||
},
|
||||
expectedDomain: acmeDomain,
|
||||
expectedAlgorithm: x509.RSA,
|
||||
expectedCommonName: acmeDomain,
|
||||
expectedAlgorithm: x509.RSA,
|
||||
}
|
||||
|
||||
s.retrieveAcmeCertificate(c, testCase)
|
||||
}
|
||||
|
||||
// Test ACME provider with certificate at start
|
||||
func (s *AcmeSuite) TestACMEProviderAtStartInSAN(c *check.C) {
|
||||
testCase := AcmeTestCase{
|
||||
func (s *AcmeSuite) TestHTTP01DomainsInSANAtStart(c *check.C) {
|
||||
testCase := acmeTestCase{
|
||||
traefikConfFilePath: "fixtures/acme/acme-base.toml",
|
||||
configuration: acme.Configuration{
|
||||
CAServer: s.getAcmeURL(),
|
||||
HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"},
|
||||
Domains: types.Domains{types.Domain{
|
||||
Main: "acme.wtf",
|
||||
SANs: []string{"traefik.acme.wtf"},
|
||||
}},
|
||||
template: templateModel{
|
||||
Acme: acme.Configuration{
|
||||
HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"},
|
||||
Domains: types.Domains{types.Domain{
|
||||
Main: "acme.wtf",
|
||||
SANs: []string{"traefik.acme.wtf"},
|
||||
}},
|
||||
},
|
||||
},
|
||||
expectedDomain: "acme.wtf",
|
||||
expectedAlgorithm: x509.RSA,
|
||||
expectedCommonName: "acme.wtf",
|
||||
expectedAlgorithm: x509.RSA,
|
||||
}
|
||||
|
||||
s.retrieveAcmeCertificate(c, testCase)
|
||||
}
|
||||
|
||||
// Test ACME provider with certificate at start
|
||||
func (s *AcmeSuite) TestACMEProviderOnHost(c *check.C) {
|
||||
testCase := AcmeTestCase{
|
||||
func (s *AcmeSuite) TestHTTP01OnHostRule(c *check.C) {
|
||||
testCase := acmeTestCase{
|
||||
traefikConfFilePath: "fixtures/acme/acme-base.toml",
|
||||
configuration: acme.Configuration{
|
||||
HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"},
|
||||
CAServer: s.getAcmeURL(),
|
||||
OnHostRule: true,
|
||||
template: templateModel{
|
||||
Acme: acme.Configuration{
|
||||
HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"},
|
||||
OnHostRule: true,
|
||||
},
|
||||
},
|
||||
expectedDomain: acmeDomain,
|
||||
expectedAlgorithm: x509.RSA,
|
||||
expectedCommonName: acmeDomain,
|
||||
expectedAlgorithm: x509.RSA,
|
||||
}
|
||||
|
||||
s.retrieveAcmeCertificate(c, testCase)
|
||||
}
|
||||
|
||||
// Test ACME provider with certificate at start ECDSA algo
|
||||
func (s *AcmeSuite) TestACMEProviderOnHostECDSA(c *check.C) {
|
||||
testCase := AcmeTestCase{
|
||||
func (s *AcmeSuite) TestHTTP01OnHostRuleECDSA(c *check.C) {
|
||||
testCase := acmeTestCase{
|
||||
traefikConfFilePath: "fixtures/acme/acme-base.toml",
|
||||
configuration: acme.Configuration{
|
||||
CAServer: s.getAcmeURL(),
|
||||
HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"},
|
||||
OnHostRule: true,
|
||||
KeyType: "EC384",
|
||||
template: templateModel{
|
||||
Acme: acme.Configuration{
|
||||
HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"},
|
||||
OnHostRule: true,
|
||||
KeyType: "EC384",
|
||||
},
|
||||
},
|
||||
expectedDomain: acmeDomain,
|
||||
expectedAlgorithm: x509.ECDSA,
|
||||
expectedCommonName: acmeDomain,
|
||||
expectedAlgorithm: x509.ECDSA,
|
||||
}
|
||||
|
||||
s.retrieveAcmeCertificate(c, testCase)
|
||||
}
|
||||
|
||||
// Test ACME provider with certificate at start invalid algo default RSA
|
||||
func (s *AcmeSuite) TestACMEProviderOnHostInvalidAlgo(c *check.C) {
|
||||
testCase := AcmeTestCase{
|
||||
func (s *AcmeSuite) TestHTTP01OnHostRuleInvalidAlgo(c *check.C) {
|
||||
testCase := acmeTestCase{
|
||||
traefikConfFilePath: "fixtures/acme/acme-base.toml",
|
||||
configuration: acme.Configuration{
|
||||
CAServer: s.getAcmeURL(),
|
||||
HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"},
|
||||
OnHostRule: true,
|
||||
KeyType: "INVALID",
|
||||
template: templateModel{
|
||||
Acme: acme.Configuration{
|
||||
HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"},
|
||||
OnHostRule: true,
|
||||
KeyType: "INVALID",
|
||||
},
|
||||
},
|
||||
expectedDomain: acmeDomain,
|
||||
expectedAlgorithm: x509.RSA,
|
||||
expectedCommonName: acmeDomain,
|
||||
expectedAlgorithm: x509.RSA,
|
||||
}
|
||||
|
||||
s.retrieveAcmeCertificate(c, testCase)
|
||||
}
|
||||
|
||||
// Test ACME provider with certificate at start and no ACME challenge
|
||||
func (s *AcmeSuite) TestACMEProviderOnHostWithNoACMEChallenge(c *check.C) {
|
||||
testCase := AcmeTestCase{
|
||||
traefikConfFilePath: "fixtures/acme/acme-base.toml",
|
||||
configuration: acme.Configuration{
|
||||
CAServer: s.getAcmeURL(),
|
||||
OnHostRule: true,
|
||||
},
|
||||
expectedDomain: traefikDefaultDomain,
|
||||
expectedAlgorithm: x509.RSA,
|
||||
}
|
||||
|
||||
s.retrieveAcmeCertificate(c, testCase)
|
||||
}
|
||||
|
||||
// Test OnDemand option with none provided certificate and challenge HTTP-01
|
||||
func (s *AcmeSuite) TestOnDemandRetrieveAcmeCertificateHTTP01(c *check.C) {
|
||||
testCase := AcmeTestCase{
|
||||
traefikConfFilePath: "fixtures/acme/acme-base.toml",
|
||||
configuration: acme.Configuration{
|
||||
CAServer: s.getAcmeURL(),
|
||||
HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"},
|
||||
OnDemand: true,
|
||||
},
|
||||
expectedDomain: acmeDomain,
|
||||
expectedAlgorithm: x509.RSA,
|
||||
}
|
||||
|
||||
s.retrieveAcmeCertificate(c, testCase)
|
||||
}
|
||||
|
||||
// Test OnHostRule option with none provided certificate and challenge HTTP-01
|
||||
func (s *AcmeSuite) TestOnHostRuleRetrieveAcmeCertificateHTTP01(c *check.C) {
|
||||
testCase := AcmeTestCase{
|
||||
traefikConfFilePath: "fixtures/acme/acme-base.toml",
|
||||
configuration: acme.Configuration{
|
||||
CAServer: s.getAcmeURL(),
|
||||
HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"},
|
||||
OnHostRule: true,
|
||||
},
|
||||
expectedDomain: acmeDomain,
|
||||
expectedAlgorithm: x509.RSA,
|
||||
}
|
||||
|
||||
s.retrieveAcmeCertificate(c, testCase)
|
||||
}
|
||||
|
||||
// Test OnHostRule option with none provided certificate and challenge HTTP-01 and web path
|
||||
func (s *AcmeSuite) TestOnHostRuleRetrieveAcmeCertificateHTTP01WithPath(c *check.C) {
|
||||
testCase := AcmeTestCase{
|
||||
func (s *AcmeSuite) TestHTTP01OnHostRuleWithPath(c *check.C) {
|
||||
testCase := acmeTestCase{
|
||||
traefikConfFilePath: "fixtures/acme/acme_http01_web_path.toml",
|
||||
configuration: acme.Configuration{
|
||||
CAServer: s.getAcmeURL(),
|
||||
HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"},
|
||||
OnHostRule: true,
|
||||
template: templateModel{
|
||||
Acme: acme.Configuration{
|
||||
HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"},
|
||||
OnHostRule: true,
|
||||
},
|
||||
},
|
||||
expectedDomain: acmeDomain,
|
||||
expectedAlgorithm: x509.RSA,
|
||||
expectedCommonName: acmeDomain,
|
||||
expectedAlgorithm: x509.RSA,
|
||||
}
|
||||
|
||||
s.retrieveAcmeCertificate(c, testCase)
|
||||
}
|
||||
|
||||
// Test OnDemand option with a wildcard provided certificate
|
||||
func (s *AcmeSuite) TestOnDemandRetrieveAcmeCertificateWithWildcard(c *check.C) {
|
||||
func (s *AcmeSuite) TestHTTP01OnHostRuleStaticCertificatesWithWildcard(c *check.C) {
|
||||
testCase := acmeTestCase{
|
||||
traefikConfFilePath: "fixtures/acme/acme_tls.toml",
|
||||
template: templateModel{
|
||||
Acme: acme.Configuration{
|
||||
HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"},
|
||||
OnHostRule: true,
|
||||
},
|
||||
},
|
||||
expectedCommonName: wildcardDomain,
|
||||
expectedAlgorithm: x509.RSA,
|
||||
}
|
||||
|
||||
s.retrieveAcmeCertificate(c, testCase)
|
||||
}
|
||||
|
||||
func (s *AcmeSuite) TestHTTP01OnHostRuleDynamicCertificatesWithWildcard(c *check.C) {
|
||||
testCase := acmeTestCase{
|
||||
traefikConfFilePath: "fixtures/acme/acme_tls_dynamic.toml",
|
||||
template: templateModel{
|
||||
Acme: acme.Configuration{
|
||||
HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"},
|
||||
OnHostRule: true,
|
||||
},
|
||||
},
|
||||
expectedCommonName: wildcardDomain,
|
||||
expectedAlgorithm: x509.RSA,
|
||||
}
|
||||
|
||||
s.retrieveAcmeCertificate(c, testCase)
|
||||
}
|
||||
|
||||
func (s *AcmeSuite) TestHTTP01OnDemand(c *check.C) {
|
||||
testCase := acmeTestCase{
|
||||
traefikConfFilePath: "fixtures/acme/acme-base.toml",
|
||||
template: templateModel{
|
||||
Acme: acme.Configuration{
|
||||
HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"},
|
||||
OnDemand: true,
|
||||
},
|
||||
},
|
||||
expectedCommonName: acmeDomain,
|
||||
expectedAlgorithm: x509.RSA,
|
||||
}
|
||||
|
||||
s.retrieveAcmeCertificate(c, testCase)
|
||||
}
|
||||
|
||||
func (s *AcmeSuite) TestHTTP01OnDemandStaticCertificatesWithWildcard(c *check.C) {
|
||||
// FIXME flaky
|
||||
c.Skip("Flaky behavior will be fixed in the next PR")
|
||||
testCase := AcmeTestCase{
|
||||
testCase := acmeTestCase{
|
||||
traefikConfFilePath: "fixtures/acme/acme_tls.toml",
|
||||
configuration: acme.Configuration{
|
||||
CAServer: s.getAcmeURL(),
|
||||
HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"},
|
||||
OnDemand: true,
|
||||
template: templateModel{
|
||||
Acme: acme.Configuration{
|
||||
HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"},
|
||||
OnDemand: true,
|
||||
},
|
||||
},
|
||||
expectedDomain: wildcardDomain,
|
||||
expectedAlgorithm: x509.RSA,
|
||||
expectedCommonName: wildcardDomain,
|
||||
expectedAlgorithm: x509.RSA,
|
||||
}
|
||||
|
||||
s.retrieveAcmeCertificate(c, testCase)
|
||||
}
|
||||
|
||||
// Test onHostRule option with a wildcard provided certificate
|
||||
func (s *AcmeSuite) TestOnHostRuleRetrieveAcmeCertificateWithWildcard(c *check.C) {
|
||||
testCase := AcmeTestCase{
|
||||
traefikConfFilePath: "fixtures/acme/acme_tls.toml",
|
||||
configuration: acme.Configuration{
|
||||
CAServer: s.getAcmeURL(),
|
||||
HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"},
|
||||
OnHostRule: true,
|
||||
},
|
||||
expectedDomain: wildcardDomain,
|
||||
expectedAlgorithm: x509.RSA,
|
||||
}
|
||||
|
||||
s.retrieveAcmeCertificate(c, testCase)
|
||||
}
|
||||
|
||||
// Test OnDemand option with a wildcard provided certificate
|
||||
func (s *AcmeSuite) TestOnDemandRetrieveAcmeCertificateWithDynamicWildcard(c *check.C) {
|
||||
testCase := AcmeTestCase{
|
||||
func (s *AcmeSuite) TestHTTP01OnDemandDynamicCertificatesWithWildcard(c *check.C) {
|
||||
testCase := acmeTestCase{
|
||||
traefikConfFilePath: "fixtures/acme/acme_tls_dynamic.toml",
|
||||
configuration: acme.Configuration{
|
||||
CAServer: s.getAcmeURL(),
|
||||
HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"},
|
||||
OnDemand: true,
|
||||
template: templateModel{
|
||||
Acme: acme.Configuration{
|
||||
HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"},
|
||||
OnDemand: true,
|
||||
},
|
||||
},
|
||||
expectedDomain: wildcardDomain,
|
||||
expectedAlgorithm: x509.RSA,
|
||||
expectedCommonName: wildcardDomain,
|
||||
expectedAlgorithm: x509.RSA,
|
||||
}
|
||||
|
||||
s.retrieveAcmeCertificate(c, testCase)
|
||||
}
|
||||
|
||||
// Test onHostRule option with a wildcard provided certificate
|
||||
func (s *AcmeSuite) TestOnHostRuleRetrieveAcmeCertificateWithDynamicWildcard(c *check.C) {
|
||||
testCase := AcmeTestCase{
|
||||
traefikConfFilePath: "fixtures/acme/acme_tls_dynamic.toml",
|
||||
configuration: acme.Configuration{
|
||||
CAServer: s.getAcmeURL(),
|
||||
HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"},
|
||||
OnHostRule: true,
|
||||
func (s *AcmeSuite) TestTLSALPN01OnHostRule(c *check.C) {
|
||||
testCase := acmeTestCase{
|
||||
traefikConfFilePath: "fixtures/acme/acme-base.toml",
|
||||
template: templateModel{
|
||||
Acme: acme.Configuration{
|
||||
TLSChallenge: &acme.TLSChallenge{},
|
||||
OnHostRule: true,
|
||||
},
|
||||
},
|
||||
expectedDomain: wildcardDomain,
|
||||
expectedAlgorithm: x509.RSA,
|
||||
expectedCommonName: acmeDomain,
|
||||
expectedAlgorithm: x509.RSA,
|
||||
}
|
||||
|
||||
s.retrieveAcmeCertificate(c, testCase)
|
||||
}
|
||||
|
||||
func (s *AcmeSuite) TestTLSALPN01OnDemand(c *check.C) {
|
||||
testCase := acmeTestCase{
|
||||
traefikConfFilePath: "fixtures/acme/acme-base.toml",
|
||||
template: templateModel{
|
||||
Acme: acme.Configuration{
|
||||
TLSChallenge: &acme.TLSChallenge{},
|
||||
OnDemand: true,
|
||||
},
|
||||
},
|
||||
expectedCommonName: acmeDomain,
|
||||
expectedAlgorithm: x509.RSA,
|
||||
}
|
||||
|
||||
s.retrieveAcmeCertificate(c, testCase)
|
||||
}
|
||||
|
||||
func (s *AcmeSuite) TestTLSALPN01DomainsAtStart(c *check.C) {
|
||||
testCase := acmeTestCase{
|
||||
traefikConfFilePath: "fixtures/acme/acme-base.toml",
|
||||
template: templateModel{
|
||||
Acme: acme.Configuration{
|
||||
TLSChallenge: &acme.TLSChallenge{},
|
||||
Domains: types.Domains{types.Domain{
|
||||
Main: "traefik.acme.wtf",
|
||||
}},
|
||||
},
|
||||
},
|
||||
expectedCommonName: acmeDomain,
|
||||
expectedAlgorithm: x509.RSA,
|
||||
}
|
||||
|
||||
s.retrieveAcmeCertificate(c, testCase)
|
||||
}
|
||||
|
||||
func (s *AcmeSuite) TestTLSALPN01DomainsInSANAtStart(c *check.C) {
|
||||
testCase := acmeTestCase{
|
||||
traefikConfFilePath: "fixtures/acme/acme-base.toml",
|
||||
template: templateModel{
|
||||
Acme: acme.Configuration{
|
||||
TLSChallenge: &acme.TLSChallenge{},
|
||||
Domains: types.Domains{types.Domain{
|
||||
Main: "acme.wtf",
|
||||
SANs: []string{"traefik.acme.wtf"},
|
||||
}},
|
||||
},
|
||||
},
|
||||
expectedCommonName: "acme.wtf",
|
||||
expectedAlgorithm: x509.RSA,
|
||||
}
|
||||
|
||||
s.retrieveAcmeCertificate(c, testCase)
|
||||
|
@ -282,10 +376,12 @@ func (s *AcmeSuite) TestOnHostRuleRetrieveAcmeCertificateWithDynamicWildcard(c *
|
|||
|
||||
// Test Let's encrypt down
|
||||
func (s *AcmeSuite) TestNoValidLetsEncryptServer(c *check.C) {
|
||||
file := s.adaptFile(c, "fixtures/acme/acme-base.toml", acme.Configuration{
|
||||
CAServer: "http://wrongurl:4001/directory",
|
||||
HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"},
|
||||
OnHostRule: true,
|
||||
file := s.adaptFile(c, "fixtures/acme/acme-base.toml", templateModel{
|
||||
Acme: acme.Configuration{
|
||||
CAServer: "http://wrongurl:4001/directory",
|
||||
HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"},
|
||||
OnHostRule: true,
|
||||
},
|
||||
})
|
||||
defer os.Remove(file)
|
||||
|
||||
|
@ -301,8 +397,20 @@ func (s *AcmeSuite) TestNoValidLetsEncryptServer(c *check.C) {
|
|||
}
|
||||
|
||||
// Doing an HTTPS request and test the response certificate
|
||||
func (s *AcmeSuite) retrieveAcmeCertificate(c *check.C, testCase AcmeTestCase) {
|
||||
file := s.adaptFile(c, testCase.traefikConfFilePath, testCase.configuration)
|
||||
func (s *AcmeSuite) retrieveAcmeCertificate(c *check.C, testCase acmeTestCase) {
|
||||
if len(testCase.template.PortHTTP) == 0 {
|
||||
testCase.template.PortHTTP = ":5002"
|
||||
}
|
||||
|
||||
if len(testCase.template.PortHTTPS) == 0 {
|
||||
testCase.template.PortHTTPS = ":5001"
|
||||
}
|
||||
|
||||
if len(testCase.template.Acme.CAServer) == 0 {
|
||||
testCase.template.Acme.CAServer = s.getAcmeURL()
|
||||
}
|
||||
|
||||
file := s.adaptFile(c, testCase.traefikConfFilePath, testCase.template)
|
||||
defer os.Remove(file)
|
||||
|
||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
||||
|
@ -357,8 +465,8 @@ func (s *AcmeSuite) retrieveAcmeCertificate(c *check.C, testCase AcmeTestCase) {
|
|||
}
|
||||
|
||||
cn := resp.TLS.PeerCertificates[0].Subject.CommonName
|
||||
if cn != testCase.expectedDomain {
|
||||
return fmt.Errorf("domain %s found instead of %s", cn, testCase.expectedDomain)
|
||||
if cn != testCase.expectedCommonName {
|
||||
return fmt.Errorf("domain %s found instead of %s", cn, testCase.expectedCommonName)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -367,6 +475,6 @@ func (s *AcmeSuite) retrieveAcmeCertificate(c *check.C, testCase AcmeTestCase) {
|
|||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(resp.StatusCode, checker.Equals, http.StatusOK)
|
||||
// Check Domain into response certificate
|
||||
c.Assert(resp.TLS.PeerCertificates[0].Subject.CommonName, checker.Equals, testCase.expectedDomain)
|
||||
c.Assert(resp.TLS.PeerCertificates[0].Subject.CommonName, checker.Equals, testCase.expectedCommonName)
|
||||
c.Assert(resp.TLS.PeerCertificates[0].PublicKeyAlgorithm, checker.Equals, testCase.expectedAlgorithm)
|
||||
}
|
||||
|
|
114
integration/fake_dns_server.go
Normal file
114
integration/fake_dns_server.go
Normal file
|
@ -0,0 +1,114 @@
|
|||
package integration
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
type handler struct{}
|
||||
|
||||
// ServeDNS a fake DNS server
|
||||
// Simplified version of the Challenge Test Server from Boulder
|
||||
// https://github.com/letsencrypt/boulder/blob/a6597b9f120207eff192c3e4107a7e49972a0250/test/challtestsrv/dnsone.go#L40
|
||||
func (s *handler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
|
||||
m := new(dns.Msg)
|
||||
m.SetReply(r)
|
||||
m.Compress = false
|
||||
|
||||
fakeDNS := os.Getenv("DOCKER_HOST_IP")
|
||||
if fakeDNS == "" {
|
||||
fakeDNS = "127.0.0.1"
|
||||
}
|
||||
for _, q := range r.Question {
|
||||
log.Printf("Query -- [%s] %s", q.Name, dns.TypeToString[q.Qtype])
|
||||
|
||||
switch q.Qtype {
|
||||
case dns.TypeA:
|
||||
record := new(dns.A)
|
||||
record.Hdr = dns.RR_Header{
|
||||
Name: q.Name,
|
||||
Rrtype: dns.TypeA,
|
||||
Class: dns.ClassINET,
|
||||
Ttl: 0,
|
||||
}
|
||||
record.A = net.ParseIP(fakeDNS)
|
||||
|
||||
m.Answer = append(m.Answer, record)
|
||||
case dns.TypeCAA:
|
||||
addCAARecord := true
|
||||
|
||||
var value string
|
||||
switch q.Name {
|
||||
case "bad-caa-reserved.com.":
|
||||
value = "sad-hacker-ca.invalid"
|
||||
case "good-caa-reserved.com.":
|
||||
value = "happy-hacker-ca.invalid"
|
||||
case "accounturi.good-caa-reserved.com.":
|
||||
uri := os.Getenv("ACCOUNT_URI")
|
||||
value = fmt.Sprintf("happy-hacker-ca.invalid; accounturi=%s", uri)
|
||||
case "recheck.good-caa-reserved.com.":
|
||||
// Allow issuance when we're running in the past
|
||||
// (under FAKECLOCK), otherwise deny issuance.
|
||||
if os.Getenv("FAKECLOCK") != "" {
|
||||
value = "happy-hacker-ca.invalid"
|
||||
} else {
|
||||
value = "sad-hacker-ca.invalid"
|
||||
}
|
||||
case "dns-01-only.good-caa-reserved.com.":
|
||||
value = "happy-hacker-ca.invalid; validationmethods=dns-01"
|
||||
case "http-01-only.good-caa-reserved.com.":
|
||||
value = "happy-hacker-ca.invalid; validationmethods=http-01"
|
||||
case "dns-01-or-http-01.good-caa-reserved.com.":
|
||||
value = "happy-hacker-ca.invalid; validationmethods=dns-01,http-01"
|
||||
default:
|
||||
addCAARecord = false
|
||||
}
|
||||
if addCAARecord {
|
||||
record := new(dns.CAA)
|
||||
record.Hdr = dns.RR_Header{
|
||||
Name: q.Name,
|
||||
Rrtype: dns.TypeCAA,
|
||||
Class: dns.ClassINET,
|
||||
Ttl: 0,
|
||||
}
|
||||
record.Tag = "issue"
|
||||
record.Value = value
|
||||
m.Answer = append(m.Answer, record)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auth := new(dns.SOA)
|
||||
auth.Hdr = dns.RR_Header{Name: "boulder.invalid.", Rrtype: dns.TypeSOA, Class: dns.ClassINET, Ttl: 0}
|
||||
auth.Ns = "ns.boulder.invalid."
|
||||
auth.Mbox = "master.boulder.invalid."
|
||||
auth.Serial = 1
|
||||
auth.Refresh = 1
|
||||
auth.Retry = 1
|
||||
auth.Expire = 1
|
||||
auth.Minttl = 1
|
||||
m.Ns = append(m.Ns, auth)
|
||||
|
||||
w.WriteMsg(m)
|
||||
}
|
||||
|
||||
func startFakeDNSServer() *dns.Server {
|
||||
srv := &dns.Server{
|
||||
Addr: ":5053",
|
||||
Net: "udp",
|
||||
Handler: &handler{},
|
||||
}
|
||||
|
||||
go func() {
|
||||
log.Infof("Start a fake DNS server.")
|
||||
if err := srv.ListenAndServe(); err != nil {
|
||||
log.Fatalf("Failed to set udp listener %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
return srv
|
||||
}
|
|
@ -4,9 +4,9 @@ defaultEntryPoints = ["http", "https"]
|
|||
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":5002"
|
||||
address = "{{ .PortHTTP }}"
|
||||
[entryPoints.https]
|
||||
address = ":5001"
|
||||
address = "{{ .PortHTTPS }}"
|
||||
[entryPoints.https.tls]
|
||||
|
||||
[acme]
|
||||
|
@ -14,17 +14,21 @@ defaultEntryPoints = ["http", "https"]
|
|||
storage = "/tmp/acme.json"
|
||||
entryPoint = "https"
|
||||
acmeLogging = true
|
||||
onDemand = {{ .OnDemand }}
|
||||
onHostRule = {{ .OnHostRule }}
|
||||
keyType = "{{ .KeyType }}"
|
||||
caServer = "{{ .CAServer }}"
|
||||
onDemand = {{ .Acme.OnDemand }}
|
||||
onHostRule = {{ .Acme.OnHostRule }}
|
||||
keyType = "{{ .Acme.KeyType }}"
|
||||
caServer = "{{ .Acme.CAServer }}"
|
||||
|
||||
{{if .HTTPChallenge }}
|
||||
{{if .Acme.HTTPChallenge }}
|
||||
[acme.httpChallenge]
|
||||
entryPoint = "{{ .HTTPChallenge.EntryPoint }}"
|
||||
entryPoint = "{{ .Acme.HTTPChallenge.EntryPoint }}"
|
||||
{{end}}
|
||||
|
||||
{{range .Domains}}
|
||||
{{if .Acme.TLSChallenge }}
|
||||
[acme.tlsChallenge]
|
||||
{{end}}
|
||||
|
||||
{{range .Acme.Domains}}
|
||||
[[acme.domains]]
|
||||
main = "{{ .Main }}"
|
||||
sans = [{{range .SANs }}
|
||||
|
|
|
@ -4,9 +4,9 @@ defaultEntryPoints = ["http", "https"]
|
|||
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":5002"
|
||||
address = "{{ .PortHTTP }}"
|
||||
[entryPoints.https]
|
||||
address = ":5001"
|
||||
address = "{{ .PortHTTPS }}"
|
||||
[entryPoints.https.tls]
|
||||
|
||||
[acme]
|
||||
|
@ -14,17 +14,17 @@ defaultEntryPoints = ["http", "https"]
|
|||
storage = "/tmp/acme.json"
|
||||
entryPoint = "https"
|
||||
acmeLogging = true
|
||||
onDemand = {{ .OnDemand }}
|
||||
onHostRule = {{ .OnHostRule }}
|
||||
keyType = "{{ .KeyType }}"
|
||||
caServer = "{{ .CAServer }}"
|
||||
onDemand = {{ .Acme.OnDemand }}
|
||||
onHostRule = {{ .Acme.OnHostRule }}
|
||||
keyType = "{{ .Acme.KeyType }}"
|
||||
caServer = "{{ .Acme.CAServer }}"
|
||||
|
||||
{{if .HTTPChallenge }}
|
||||
{{if .Acme.HTTPChallenge }}
|
||||
[acme.httpChallenge]
|
||||
entryPoint = "{{ .HTTPChallenge.EntryPoint }}"
|
||||
entryPoint = "{{ .Acme.HTTPChallenge.EntryPoint }}"
|
||||
{{end}}
|
||||
|
||||
{{range .Domains}}
|
||||
{{range .Acme.Domains}}
|
||||
[[acme.domains]]
|
||||
main = "{{ .Main }}"
|
||||
sans = [{{range .SANs }}
|
||||
|
|
|
@ -4,9 +4,9 @@ defaultEntryPoints = ["http", "https"]
|
|||
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":5002"
|
||||
address = "{{ .PortHTTP }}"
|
||||
[entryPoints.https]
|
||||
address = ":5001"
|
||||
address = "{{ .PortHTTPS }}"
|
||||
[entryPoints.https.tls]
|
||||
[[entryPoints.https.tls.certificates]]
|
||||
certFile = "fixtures/acme/ssl/wildcard.crt"
|
||||
|
@ -17,17 +17,17 @@ defaultEntryPoints = ["http", "https"]
|
|||
storage = "/tmp/acme.json"
|
||||
entryPoint = "https"
|
||||
acmeLogging = true
|
||||
onDemand = {{ .OnDemand }}
|
||||
onHostRule = {{ .OnHostRule }}
|
||||
keyType = "{{ .KeyType }}"
|
||||
caServer = "{{ .CAServer }}"
|
||||
onDemand = {{ .Acme.OnDemand }}
|
||||
onHostRule = {{ .Acme.OnHostRule }}
|
||||
keyType = "{{ .Acme.KeyType }}"
|
||||
caServer = "{{ .Acme.CAServer }}"
|
||||
|
||||
{{if .HTTPChallenge }}
|
||||
{{if .Acme.HTTPChallenge }}
|
||||
[acme.httpChallenge]
|
||||
entryPoint = "{{ .HTTPChallenge.EntryPoint }}"
|
||||
entryPoint = "{{ .Acme.HTTPChallenge.EntryPoint }}"
|
||||
{{end}}
|
||||
|
||||
{{range .Domains}}
|
||||
{{range .Acme.Domains}}
|
||||
[[acme.domains]]
|
||||
main = "{{ .Main }}"
|
||||
sans = [{{range .SANs }}
|
||||
|
|
|
@ -4,28 +4,27 @@ defaultEntryPoints = ["http", "https"]
|
|||
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":5002"
|
||||
address = "{{ .PortHTTP }}"
|
||||
[entryPoints.https]
|
||||
address = ":5001"
|
||||
address = "{{ .PortHTTPS }}"
|
||||
[entryPoints.https.tls]
|
||||
|
||||
|
||||
[acme]
|
||||
email = "test@traefik.io"
|
||||
storage = "/tmp/acme.json"
|
||||
entryPoint = "https"
|
||||
acmeLogging = true
|
||||
onDemand = {{ .OnDemand }}
|
||||
onHostRule = {{ .OnHostRule }}
|
||||
keyType = "{{ .KeyType }}"
|
||||
caServer = "{{ .CAServer }}"
|
||||
onDemand = {{ .Acme.OnDemand }}
|
||||
onHostRule = {{ .Acme.OnHostRule }}
|
||||
keyType = "{{ .Acme.KeyType }}"
|
||||
caServer = "{{ .Acme.CAServer }}"
|
||||
|
||||
{{if .HTTPChallenge }}
|
||||
{{if .Acme.HTTPChallenge }}
|
||||
[acme.httpChallenge]
|
||||
entryPoint = "{{ .HTTPChallenge.EntryPoint }}"
|
||||
entryPoint = "{{ .Acme.HTTPChallenge.EntryPoint }}"
|
||||
{{end}}
|
||||
|
||||
{{range .Domains}}
|
||||
{{range .Acme.Domains}}
|
||||
[[acme.domains]]
|
||||
main = "{{ .Main }}"
|
||||
sans = [{{range .SANs }}
|
||||
|
|
19
integration/fixtures/acme/ssl/pebble.minica.pem
Normal file
19
integration/fixtures/acme/ssl/pebble.minica.pem
Normal file
|
@ -0,0 +1,19 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDCTCCAfGgAwIBAgIIJOLbes8sTr4wDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE
|
||||
AxMVbWluaWNhIHJvb3QgY2EgMjRlMmRiMCAXDTE3MTIwNjE5NDIxMFoYDzIxMTcx
|
||||
MjA2MTk0MjEwWjAgMR4wHAYDVQQDExVtaW5pY2Egcm9vdCBjYSAyNGUyZGIwggEi
|
||||
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5WgZNoVJandj43kkLyU50vzCZ
|
||||
alozvdRo3OFiKoDtmqKPNWRNO2hC9AUNxTDJco51Yc42u/WV3fPbbhSznTiOOVtn
|
||||
Ajm6iq4I5nZYltGGZetGDOQWr78y2gWY+SG078MuOO2hyDIiKtVc3xiXYA+8Hluu
|
||||
9F8KbqSS1h55yxZ9b87eKR+B0zu2ahzBCIHKmKWgc6N13l7aDxxY3D6uq8gtJRU0
|
||||
toumyLbdzGcupVvjbjDP11nl07RESDWBLG1/g3ktJvqIa4BWgU2HMh4rND6y8OD3
|
||||
Hy3H8MY6CElL+MOCbFJjWqhtOxeFyZZV9q3kYnk9CAuQJKMEGuN4GU6tzhW1AgMB
|
||||
AAGjRTBDMA4GA1UdDwEB/wQEAwIChDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYB
|
||||
BQUHAwIwEgYDVR0TAQH/BAgwBgEB/wIBADANBgkqhkiG9w0BAQsFAAOCAQEAF85v
|
||||
d40HK1ouDAtWeO1PbnWfGEmC5Xa478s9ddOd9Clvp2McYzNlAFfM7kdcj6xeiNhF
|
||||
WPIfaGAi/QdURSL/6C1KsVDqlFBlTs9zYfh2g0UXGvJtj1maeih7zxFLvet+fqll
|
||||
xseM4P9EVJaQxwuK/F78YBt0tCNfivC6JNZMgxKF59h0FBpH70ytUSHXdz7FKwix
|
||||
Mfn3qEb9BXSk0Q3prNV5sOV3vgjEtB4THfDxSz9z3+DepVnW3vbbqwEbkXdk3j82
|
||||
2muVldgOUgTwK8eT+XdofVdntzU/kzygSAtAQwLJfn51fS1GvEcYGBc1bDryIqmF
|
||||
p9BI7gVKtWSZYegicA==
|
||||
-----END CERTIFICATE-----
|
|
@ -1,49 +0,0 @@
|
|||
boulder:
|
||||
image: containous/boulder:containous-acmev2
|
||||
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
|
||||
- 4001:4001 # ACMEv2
|
||||
- 4002:4002 # OCSP
|
||||
- 4003:4003 # OCSP
|
||||
- 4430:4430 # ACME via HTTPS
|
||||
- 4431:4431 # ACMEv2 via HTTPS
|
||||
- 4500:4500 # ct-test-srv
|
||||
- 6000:6000 # gsb-test-srv
|
||||
- 8000:8000 # debug ports
|
||||
- 8001:8001
|
||||
- 8002:8002
|
||||
- 8003:8003
|
||||
- 8004:8004
|
||||
- 8005:8005
|
||||
- 8006:8006
|
||||
- 8008:8008
|
||||
- 8009:8009
|
||||
- 8010:8010
|
||||
- 8055:8055 # dns-test-srv updates
|
||||
- 9380:9380 # mail-test-srv
|
||||
- 9381:9381 # mail-test-srv
|
||||
links:
|
||||
- bhsm:boulder-hsm
|
||||
- bmysql:boulder-mysql
|
||||
|
||||
bhsm:
|
||||
# To minimize the fetching of various layers this should match
|
||||
# the FROM image and tag in boulder/Dockerfile
|
||||
image: letsencrypt/boulder-tools:2018-03-07
|
||||
environment:
|
||||
PKCS11_DAEMON_SOCKET: tcp://0.0.0.0:5657
|
||||
command: /usr/local/bin/pkcs11-daemon /usr/lib/softhsm/libsofthsm2.so
|
||||
expose:
|
||||
- 5657
|
||||
bmysql:
|
||||
image: mariadb:10.1
|
||||
environment:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
|
||||
command: mysqld --bind-address=0.0.0.0
|
||||
log_driver: none
|
10
integration/resources/compose/peddle.yml
Normal file
10
integration/resources/compose/peddle.yml
Normal file
|
@ -0,0 +1,10 @@
|
|||
pebble:
|
||||
image: ldez/pebble
|
||||
command: --dnsserver ${DOCKER_HOST_IP}:5053
|
||||
ports:
|
||||
- 14000:14000
|
||||
environment:
|
||||
# https://github.com/letsencrypt/pebble#testing-at-full-speed
|
||||
- PEBBLE_VA_NOSLEEP=1
|
||||
# https://github.com/letsencrypt/pebble#invalid-anti-replay-nonce-errors
|
||||
- PEBBLE_WFE_NONCEREJECT=0
|
Loading…
Add table
Add a link
Reference in a new issue