Add option to select algorithm to generate ACME certificates
This commit is contained in:
parent
e691168cdc
commit
68cc826519
12 changed files with 179 additions and 23 deletions
|
@ -14,6 +14,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containous/traefik/log"
|
"github.com/containous/traefik/log"
|
||||||
|
acmeprovider "github.com/containous/traefik/provider/acme"
|
||||||
"github.com/containous/traefik/types"
|
"github.com/containous/traefik/types"
|
||||||
acme "github.com/xenolf/lego/acmev2"
|
acme "github.com/xenolf/lego/acmev2"
|
||||||
)
|
)
|
||||||
|
@ -23,6 +24,7 @@ type Account struct {
|
||||||
Email string
|
Email string
|
||||||
Registration *acme.RegistrationResource
|
Registration *acme.RegistrationResource
|
||||||
PrivateKey []byte
|
PrivateKey []byte
|
||||||
|
KeyType acme.KeyType
|
||||||
DomainsCertificate DomainsCertificates
|
DomainsCertificate DomainsCertificates
|
||||||
ChallengeCerts map[string]*ChallengeCert
|
ChallengeCerts map[string]*ChallengeCert
|
||||||
HTTPChallenge map[string]map[string][]byte
|
HTTPChallenge map[string]map[string][]byte
|
||||||
|
@ -63,7 +65,9 @@ func (a *Account) Init() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAccount creates an account
|
// NewAccount creates an account
|
||||||
func NewAccount(email string, certs []*DomainsCertificate) (*Account, error) {
|
func NewAccount(email string, certs []*DomainsCertificate, keyTypeValue string) (*Account, error) {
|
||||||
|
keyType := acmeprovider.GetKeyType(keyTypeValue)
|
||||||
|
|
||||||
// Create a user. New accounts need an email and private key to start
|
// Create a user. New accounts need an email and private key to start
|
||||||
privateKey, err := rsa.GenerateKey(rand.Reader, 4096)
|
privateKey, err := rsa.GenerateKey(rand.Reader, 4096)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -79,6 +83,7 @@ func NewAccount(email string, certs []*DomainsCertificate) (*Account, error) {
|
||||||
return &Account{
|
return &Account{
|
||||||
Email: email,
|
Email: email,
|
||||||
PrivateKey: x509.MarshalPKCS1PrivateKey(privateKey),
|
PrivateKey: x509.MarshalPKCS1PrivateKey(privateKey),
|
||||||
|
KeyType: keyType,
|
||||||
DomainsCertificate: DomainsCertificates{Certs: domainsCerts.Certs},
|
DomainsCertificate: DomainsCertificates{Certs: domainsCerts.Certs},
|
||||||
ChallengeCerts: map[string]*ChallengeCert{}}, nil
|
ChallengeCerts: map[string]*ChallengeCert{}}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,7 @@ type ACME struct {
|
||||||
OnHostRule bool `description:"Enable certificate generation on frontends Host rules."`
|
OnHostRule bool `description:"Enable certificate generation on frontends Host rules."`
|
||||||
CAServer string `description:"CA server to use."`
|
CAServer string `description:"CA server to use."`
|
||||||
EntryPoint string `description:"Entrypoint to proxy acme challenge to."`
|
EntryPoint string `description:"Entrypoint to proxy acme challenge to."`
|
||||||
|
KeyType string `description:"KeyType used for generating certificate private key. Allow value 'EC256', 'EC384', 'RSA2048', 'RSA4096', 'RSA8192'. Default to 'RSA4096'"`
|
||||||
DNSChallenge *acmeprovider.DNSChallenge `description:"Activate DNS-01 Challenge"`
|
DNSChallenge *acmeprovider.DNSChallenge `description:"Activate DNS-01 Challenge"`
|
||||||
HTTPChallenge *acmeprovider.HTTPChallenge `description:"Activate HTTP-01 Challenge"`
|
HTTPChallenge *acmeprovider.HTTPChallenge `description:"Activate HTTP-01 Challenge"`
|
||||||
DNSProvider string `description:"(Deprecated) Activate DNS-01 Challenge"` // Deprecated
|
DNSProvider string `description:"(Deprecated) Activate DNS-01 Challenge"` // Deprecated
|
||||||
|
@ -186,7 +187,7 @@ func (a *ACME) leadershipListener(elected bool) error {
|
||||||
domainsCerts = account.DomainsCertificate
|
domainsCerts = account.DomainsCertificate
|
||||||
}
|
}
|
||||||
|
|
||||||
account, err = NewAccount(a.Email, domainsCerts.Certs)
|
account, err = NewAccount(a.Email, domainsCerts.Certs, a.KeyType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -395,7 +396,7 @@ func (a *ACME) buildACMEClient(account *Account) (*acme.Client, error) {
|
||||||
if len(a.CAServer) > 0 {
|
if len(a.CAServer) > 0 {
|
||||||
caServer = a.CAServer
|
caServer = a.CAServer
|
||||||
}
|
}
|
||||||
client, err := acme.NewClient(caServer, account, acme.RSA4096)
|
client, err := acme.NewClient(caServer, account, account.KeyType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,6 +64,7 @@ func RemoveAccountV1Values(account *Account) error {
|
||||||
account.Email = ""
|
account.Email = ""
|
||||||
account.Registration = nil
|
account.Registration = nil
|
||||||
account.PrivateKey = nil
|
account.PrivateKey = nil
|
||||||
|
account.KeyType = "RSA4096"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -113,6 +114,7 @@ func ConvertToNewFormat(fileName string) {
|
||||||
PrivateKey: account.PrivateKey,
|
PrivateKey: account.PrivateKey,
|
||||||
Registration: account.Registration,
|
Registration: account.Registration,
|
||||||
Email: account.Email,
|
Email: account.Email,
|
||||||
|
KeyType: account.KeyType,
|
||||||
}
|
}
|
||||||
|
|
||||||
var newCertificates []*acme.Certificate
|
var newCertificates []*acme.Certificate
|
||||||
|
@ -167,6 +169,7 @@ func FromNewToOldFormat(fileName string) (*Account, error) {
|
||||||
PrivateKey: storeAccount.PrivateKey,
|
PrivateKey: storeAccount.PrivateKey,
|
||||||
Registration: storeAccount.Registration,
|
Registration: storeAccount.Registration,
|
||||||
DomainsCertificate: DomainsCertificates{},
|
DomainsCertificate: DomainsCertificates{},
|
||||||
|
KeyType: storeAccount.KeyType,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -381,6 +381,7 @@ func (gc *GlobalConfiguration) InitACMEProvider() *acmeprovider.Provider {
|
||||||
if gc.Cluster == nil {
|
if gc.Cluster == nil {
|
||||||
provider := &acmeprovider.Provider{}
|
provider := &acmeprovider.Provider{}
|
||||||
provider.Configuration = &acmeprovider.Configuration{
|
provider.Configuration = &acmeprovider.Configuration{
|
||||||
|
KeyType: gc.ACME.KeyType,
|
||||||
OnHostRule: gc.ACME.OnHostRule,
|
OnHostRule: gc.ACME.OnHostRule,
|
||||||
OnDemand: gc.ACME.OnDemand,
|
OnDemand: gc.ACME.OnDemand,
|
||||||
Email: gc.ACME.Email,
|
Email: gc.ACME.Email,
|
||||||
|
|
|
@ -86,6 +86,15 @@ entryPoint = "https"
|
||||||
#
|
#
|
||||||
# caServer = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
# caServer = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||||
|
|
||||||
|
# KeyType to use.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: "RSA4096"
|
||||||
|
#
|
||||||
|
# Available values : "EC256", "EC384", "RSA2048", "RSA4096", "RSA8192"
|
||||||
|
#
|
||||||
|
# KeyType = "RSA4096"
|
||||||
|
|
||||||
# Domains list.
|
# Domains list.
|
||||||
# Only domains defined here can generate wildcard certificates.
|
# Only domains defined here can generate wildcard certificates.
|
||||||
#
|
#
|
||||||
|
|
|
@ -9,7 +9,7 @@ readonly doc_file=$basedir"/docker-compose.yml"
|
||||||
down_environment() {
|
down_environment() {
|
||||||
echo "STOP Docker environment"
|
echo "STOP Docker environment"
|
||||||
! docker-compose -f $doc_file down -v &>/dev/null && \
|
! docker-compose -f $doc_file down -v &>/dev/null && \
|
||||||
echo "[ERROR] Impossible to stop the Docker environment" && exit 11
|
echo "[ERROR] Unable to stop the Docker environment" && exit 11
|
||||||
}
|
}
|
||||||
|
|
||||||
# Create and start Docker-compose environment or subpart of its services (if services are listed)
|
# Create and start Docker-compose environment or subpart of its services (if services are listed)
|
||||||
|
@ -17,7 +17,7 @@ down_environment() {
|
||||||
up_environment() {
|
up_environment() {
|
||||||
echo "START Docker environment"
|
echo "START Docker environment"
|
||||||
! docker-compose -f $doc_file up -d $@ &>/dev/null && \
|
! docker-compose -f $doc_file up -d $@ &>/dev/null && \
|
||||||
echo "[ERROR] Impossible to start Docker environment" && exit 21
|
echo "[ERROR] Unable to start Docker environment" && exit 21
|
||||||
}
|
}
|
||||||
|
|
||||||
# Init the environment : get IP address and create needed files
|
# Init the environment : get IP address and create needed files
|
||||||
|
@ -40,7 +40,7 @@ start_boulder() {
|
||||||
sleep 5
|
sleep 5
|
||||||
let waiting_counter-=1
|
let waiting_counter-=1
|
||||||
if [[ $waiting_counter -eq 0 ]]; then
|
if [[ $waiting_counter -eq 0 ]]; then
|
||||||
echo "[ERROR] Impossible to start boulder container in the allowed time, the Docker environment will be stopped"
|
echo "[ERROR] Unable to start boulder container in the allowed time, the Docker environment will be stopped"
|
||||||
down_environment
|
down_environment
|
||||||
exit 41
|
exit 41
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -2,6 +2,7 @@ package integration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
@ -24,6 +25,7 @@ type AcmeTestCase struct {
|
||||||
onDemand bool
|
onDemand bool
|
||||||
traefikConfFilePath string
|
traefikConfFilePath string
|
||||||
domainToCheck string
|
domainToCheck string
|
||||||
|
algorithm x509.PublicKeyAlgorithm
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -60,7 +62,8 @@ func (s *AcmeSuite) TestACMEProviderAtStart(c *check.C) {
|
||||||
testCase := AcmeTestCase{
|
testCase := AcmeTestCase{
|
||||||
traefikConfFilePath: "fixtures/provideracme/acme.toml",
|
traefikConfFilePath: "fixtures/provideracme/acme.toml",
|
||||||
onDemand: false,
|
onDemand: false,
|
||||||
domainToCheck: acmeDomain}
|
domainToCheck: acmeDomain,
|
||||||
|
algorithm: x509.RSA}
|
||||||
|
|
||||||
s.retrieveAcmeCertificate(c, testCase)
|
s.retrieveAcmeCertificate(c, testCase)
|
||||||
}
|
}
|
||||||
|
@ -70,7 +73,8 @@ func (s *AcmeSuite) TestACMEProviderAtStartInSAN(c *check.C) {
|
||||||
testCase := AcmeTestCase{
|
testCase := AcmeTestCase{
|
||||||
traefikConfFilePath: "fixtures/provideracme/acme_insan.toml",
|
traefikConfFilePath: "fixtures/provideracme/acme_insan.toml",
|
||||||
onDemand: false,
|
onDemand: false,
|
||||||
domainToCheck: "acme.wtf"}
|
domainToCheck: "acme.wtf",
|
||||||
|
algorithm: x509.RSA}
|
||||||
|
|
||||||
s.retrieveAcmeCertificate(c, testCase)
|
s.retrieveAcmeCertificate(c, testCase)
|
||||||
}
|
}
|
||||||
|
@ -80,7 +84,30 @@ func (s *AcmeSuite) TestACMEProviderOnHost(c *check.C) {
|
||||||
testCase := AcmeTestCase{
|
testCase := AcmeTestCase{
|
||||||
traefikConfFilePath: "fixtures/provideracme/acme_onhost.toml",
|
traefikConfFilePath: "fixtures/provideracme/acme_onhost.toml",
|
||||||
onDemand: false,
|
onDemand: false,
|
||||||
domainToCheck: acmeDomain}
|
domainToCheck: acmeDomain,
|
||||||
|
algorithm: x509.RSA}
|
||||||
|
|
||||||
|
s.retrieveAcmeCertificate(c, testCase)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test ACME provider with certificate at start ECDSA algo
|
||||||
|
func (s *AcmeSuite) TestACMEProviderOnHostECDSA(c *check.C) {
|
||||||
|
testCase := AcmeTestCase{
|
||||||
|
traefikConfFilePath: "fixtures/provideracme/acme_onhost_ecdsa.toml",
|
||||||
|
onDemand: false,
|
||||||
|
domainToCheck: acmeDomain,
|
||||||
|
algorithm: 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{
|
||||||
|
traefikConfFilePath: "fixtures/provideracme/acme_onhost_invalid_algo.toml",
|
||||||
|
onDemand: false,
|
||||||
|
domainToCheck: acmeDomain,
|
||||||
|
algorithm: x509.RSA}
|
||||||
|
|
||||||
s.retrieveAcmeCertificate(c, testCase)
|
s.retrieveAcmeCertificate(c, testCase)
|
||||||
}
|
}
|
||||||
|
@ -90,7 +117,8 @@ func (s *AcmeSuite) TestACMEProviderOnHostWithNoACMEChallenge(c *check.C) {
|
||||||
testCase := AcmeTestCase{
|
testCase := AcmeTestCase{
|
||||||
traefikConfFilePath: "fixtures/acme/no_challenge_acme.toml",
|
traefikConfFilePath: "fixtures/acme/no_challenge_acme.toml",
|
||||||
onDemand: false,
|
onDemand: false,
|
||||||
domainToCheck: traefikDefaultDomain}
|
domainToCheck: traefikDefaultDomain,
|
||||||
|
algorithm: x509.RSA}
|
||||||
|
|
||||||
s.retrieveAcmeCertificate(c, testCase)
|
s.retrieveAcmeCertificate(c, testCase)
|
||||||
}
|
}
|
||||||
|
@ -100,7 +128,8 @@ func (s *AcmeSuite) TestOnDemandRetrieveAcmeCertificateHTTP01(c *check.C) {
|
||||||
testCase := AcmeTestCase{
|
testCase := AcmeTestCase{
|
||||||
traefikConfFilePath: "fixtures/acme/acme_http01.toml",
|
traefikConfFilePath: "fixtures/acme/acme_http01.toml",
|
||||||
onDemand: true,
|
onDemand: true,
|
||||||
domainToCheck: acmeDomain}
|
domainToCheck: acmeDomain,
|
||||||
|
algorithm: x509.RSA}
|
||||||
|
|
||||||
s.retrieveAcmeCertificate(c, testCase)
|
s.retrieveAcmeCertificate(c, testCase)
|
||||||
}
|
}
|
||||||
|
@ -110,7 +139,8 @@ func (s *AcmeSuite) TestOnHostRuleRetrieveAcmeCertificateHTTP01(c *check.C) {
|
||||||
testCase := AcmeTestCase{
|
testCase := AcmeTestCase{
|
||||||
traefikConfFilePath: "fixtures/acme/acme_http01.toml",
|
traefikConfFilePath: "fixtures/acme/acme_http01.toml",
|
||||||
onDemand: false,
|
onDemand: false,
|
||||||
domainToCheck: acmeDomain}
|
domainToCheck: acmeDomain,
|
||||||
|
algorithm: x509.RSA}
|
||||||
|
|
||||||
s.retrieveAcmeCertificate(c, testCase)
|
s.retrieveAcmeCertificate(c, testCase)
|
||||||
}
|
}
|
||||||
|
@ -120,7 +150,8 @@ func (s *AcmeSuite) TestOnHostRuleRetrieveAcmeCertificateHTTP01WithPath(c *check
|
||||||
testCase := AcmeTestCase{
|
testCase := AcmeTestCase{
|
||||||
traefikConfFilePath: "fixtures/acme/acme_http01_web.toml",
|
traefikConfFilePath: "fixtures/acme/acme_http01_web.toml",
|
||||||
onDemand: false,
|
onDemand: false,
|
||||||
domainToCheck: acmeDomain}
|
domainToCheck: acmeDomain,
|
||||||
|
algorithm: x509.RSA}
|
||||||
|
|
||||||
s.retrieveAcmeCertificate(c, testCase)
|
s.retrieveAcmeCertificate(c, testCase)
|
||||||
}
|
}
|
||||||
|
@ -130,7 +161,8 @@ func (s *AcmeSuite) TestOnDemandRetrieveAcmeCertificateWithWildcard(c *check.C)
|
||||||
testCase := AcmeTestCase{
|
testCase := AcmeTestCase{
|
||||||
traefikConfFilePath: "fixtures/acme/acme_provided.toml",
|
traefikConfFilePath: "fixtures/acme/acme_provided.toml",
|
||||||
onDemand: true,
|
onDemand: true,
|
||||||
domainToCheck: wildcardDomain}
|
domainToCheck: wildcardDomain,
|
||||||
|
algorithm: x509.RSA}
|
||||||
|
|
||||||
s.retrieveAcmeCertificate(c, testCase)
|
s.retrieveAcmeCertificate(c, testCase)
|
||||||
}
|
}
|
||||||
|
@ -140,7 +172,8 @@ func (s *AcmeSuite) TestOnHostRuleRetrieveAcmeCertificateWithWildcard(c *check.C
|
||||||
testCase := AcmeTestCase{
|
testCase := AcmeTestCase{
|
||||||
traefikConfFilePath: "fixtures/acme/acme_provided.toml",
|
traefikConfFilePath: "fixtures/acme/acme_provided.toml",
|
||||||
onDemand: false,
|
onDemand: false,
|
||||||
domainToCheck: wildcardDomain}
|
domainToCheck: wildcardDomain,
|
||||||
|
algorithm: x509.RSA}
|
||||||
|
|
||||||
s.retrieveAcmeCertificate(c, testCase)
|
s.retrieveAcmeCertificate(c, testCase)
|
||||||
}
|
}
|
||||||
|
@ -150,7 +183,8 @@ func (s *AcmeSuite) TestOnDemandRetrieveAcmeCertificateWithDynamicWildcard(c *ch
|
||||||
testCase := AcmeTestCase{
|
testCase := AcmeTestCase{
|
||||||
traefikConfFilePath: "fixtures/acme/acme_provided_dynamic.toml",
|
traefikConfFilePath: "fixtures/acme/acme_provided_dynamic.toml",
|
||||||
onDemand: true,
|
onDemand: true,
|
||||||
domainToCheck: wildcardDomain}
|
domainToCheck: wildcardDomain,
|
||||||
|
algorithm: x509.RSA}
|
||||||
|
|
||||||
s.retrieveAcmeCertificate(c, testCase)
|
s.retrieveAcmeCertificate(c, testCase)
|
||||||
}
|
}
|
||||||
|
@ -160,7 +194,8 @@ func (s *AcmeSuite) TestOnHostRuleRetrieveAcmeCertificateWithDynamicWildcard(c *
|
||||||
testCase := AcmeTestCase{
|
testCase := AcmeTestCase{
|
||||||
traefikConfFilePath: "fixtures/acme/acme_provided_dynamic.toml",
|
traefikConfFilePath: "fixtures/acme/acme_provided_dynamic.toml",
|
||||||
onDemand: false,
|
onDemand: false,
|
||||||
domainToCheck: wildcardDomain}
|
domainToCheck: wildcardDomain,
|
||||||
|
algorithm: x509.RSA}
|
||||||
|
|
||||||
s.retrieveAcmeCertificate(c, testCase)
|
s.retrieveAcmeCertificate(c, testCase)
|
||||||
}
|
}
|
||||||
|
@ -181,8 +216,9 @@ func (s *AcmeSuite) TestNoValidLetsEncryptServer(c *check.C) {
|
||||||
// Doing an HTTPS request and test the response certificate
|
// Doing an HTTPS request and test the response certificate
|
||||||
func (s *AcmeSuite) retrieveAcmeCertificate(c *check.C, testCase AcmeTestCase) {
|
func (s *AcmeSuite) retrieveAcmeCertificate(c *check.C, testCase AcmeTestCase) {
|
||||||
file := s.adaptFile(c, testCase.traefikConfFilePath, struct {
|
file := s.adaptFile(c, testCase.traefikConfFilePath, struct {
|
||||||
BoulderHost string
|
BoulderHost string
|
||||||
OnDemand, OnHostRule bool
|
OnDemand bool
|
||||||
|
OnHostRule bool
|
||||||
}{
|
}{
|
||||||
BoulderHost: s.boulderIP,
|
BoulderHost: s.boulderIP,
|
||||||
OnDemand: testCase.onDemand,
|
OnDemand: testCase.onDemand,
|
||||||
|
@ -251,4 +287,5 @@ func (s *AcmeSuite) retrieveAcmeCertificate(c *check.C, testCase AcmeTestCase) {
|
||||||
c.Assert(resp.StatusCode, checker.Equals, http.StatusOK)
|
c.Assert(resp.StatusCode, checker.Equals, http.StatusOK)
|
||||||
// Check Domain into response certificate
|
// Check Domain into response certificate
|
||||||
c.Assert(resp.TLS.PeerCertificates[0].Subject.CommonName, checker.Equals, testCase.domainToCheck)
|
c.Assert(resp.TLS.PeerCertificates[0].Subject.CommonName, checker.Equals, testCase.domainToCheck)
|
||||||
|
c.Assert(resp.TLS.PeerCertificates[0].PublicKeyAlgorithm, checker.Equals, testCase.algorithm)
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ defaultEntryPoints = ["http", "https"]
|
||||||
|
|
||||||
[acme]
|
[acme]
|
||||||
email = "test@traefik.io"
|
email = "test@traefik.io"
|
||||||
storage = "/tmp/acme.jsonl"
|
storage = "/tmp/acme.json"
|
||||||
entryPoint = "https"
|
entryPoint = "https"
|
||||||
onDemand = {{.OnDemand}}
|
onDemand = {{.OnDemand}}
|
||||||
onHostRule = {{.OnHostRule}}
|
onHostRule = {{.OnHostRule}}
|
||||||
|
|
38
integration/fixtures/provideracme/acme_onhost_ecdsa.toml
Normal file
38
integration/fixtures/provideracme/acme_onhost_ecdsa.toml
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
logLevel = "DEBUG"
|
||||||
|
|
||||||
|
defaultEntryPoints = ["http", "https"]
|
||||||
|
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.http]
|
||||||
|
address = ":5002"
|
||||||
|
[entryPoints.https]
|
||||||
|
address = ":5001"
|
||||||
|
[entryPoints.https.tls]
|
||||||
|
|
||||||
|
|
||||||
|
[acme]
|
||||||
|
email = "test@traefik.io"
|
||||||
|
storage = "/tmp/acme.json"
|
||||||
|
entryPoint = "https"
|
||||||
|
onDemand = {{.OnDemand}}
|
||||||
|
onHostRule = {{.OnHostRule}}
|
||||||
|
caServer = "http://{{.BoulderHost}}:4001/directory"
|
||||||
|
keyType = "EC384"
|
||||||
|
[acme.httpChallenge]
|
||||||
|
entryPoint="http"
|
||||||
|
|
||||||
|
[api]
|
||||||
|
|
||||||
|
[file]
|
||||||
|
|
||||||
|
[backends]
|
||||||
|
[backends.backend]
|
||||||
|
[backends.backend.servers.server1]
|
||||||
|
url = "http://127.0.0.1:9010"
|
||||||
|
weight = 1
|
||||||
|
|
||||||
|
[frontends]
|
||||||
|
[frontends.frontend]
|
||||||
|
backend = "backend"
|
||||||
|
[frontends.frontend.routes.test]
|
||||||
|
rule = "Host:traefik.acme.wtf"
|
|
@ -0,0 +1,38 @@
|
||||||
|
logLevel = "DEBUG"
|
||||||
|
|
||||||
|
defaultEntryPoints = ["http", "https"]
|
||||||
|
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.http]
|
||||||
|
address = ":5002"
|
||||||
|
[entryPoints.https]
|
||||||
|
address = ":5001"
|
||||||
|
[entryPoints.https.tls]
|
||||||
|
|
||||||
|
|
||||||
|
[acme]
|
||||||
|
email = "test@traefik.io"
|
||||||
|
storage = "/tmp/acme.json"
|
||||||
|
entryPoint = "https"
|
||||||
|
onDemand = {{.OnDemand}}
|
||||||
|
onHostRule = {{.OnHostRule}}
|
||||||
|
caServer = "http://{{.BoulderHost}}:4001/directory"
|
||||||
|
keyType = "INVALID"
|
||||||
|
[acme.httpChallenge]
|
||||||
|
entryPoint="http"
|
||||||
|
|
||||||
|
[api]
|
||||||
|
|
||||||
|
[file]
|
||||||
|
|
||||||
|
[backends]
|
||||||
|
[backends.backend]
|
||||||
|
[backends.backend.servers.server1]
|
||||||
|
url = "http://127.0.0.1:9010"
|
||||||
|
weight = 1
|
||||||
|
|
||||||
|
[frontends]
|
||||||
|
[frontends.frontend]
|
||||||
|
backend = "backend"
|
||||||
|
[frontends.frontend.routes.test]
|
||||||
|
rule = "Host:traefik.acme.wtf"
|
|
@ -15,6 +15,7 @@ type Account struct {
|
||||||
Email string
|
Email string
|
||||||
Registration *acme.RegistrationResource
|
Registration *acme.RegistrationResource
|
||||||
PrivateKey []byte
|
PrivateKey []byte
|
||||||
|
KeyType acme.KeyType
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -23,7 +24,9 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewAccount creates an account
|
// NewAccount creates an account
|
||||||
func NewAccount(email string) (*Account, error) {
|
func NewAccount(email string, keyTypeValue string) (*Account, error) {
|
||||||
|
keyType := GetKeyType(keyTypeValue)
|
||||||
|
|
||||||
// Create a user. New accounts need an email and private key to start
|
// Create a user. New accounts need an email and private key to start
|
||||||
privateKey, err := rsa.GenerateKey(rand.Reader, 4096)
|
privateKey, err := rsa.GenerateKey(rand.Reader, 4096)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -33,6 +36,7 @@ func NewAccount(email string) (*Account, error) {
|
||||||
return &Account{
|
return &Account{
|
||||||
Email: email,
|
Email: email,
|
||||||
PrivateKey: x509.MarshalPKCS1PrivateKey(privateKey),
|
PrivateKey: x509.MarshalPKCS1PrivateKey(privateKey),
|
||||||
|
KeyType: keyType,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,3 +59,22 @@ func (a *Account) GetPrivateKey() crypto.PrivateKey {
|
||||||
log.Errorf("Cannot unmarshal private key %+v", a.PrivateKey)
|
log.Errorf("Cannot unmarshal private key %+v", a.PrivateKey)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetKeyType used to determine which algo to used
|
||||||
|
func GetKeyType(value string) acme.KeyType {
|
||||||
|
switch value {
|
||||||
|
case "EC256":
|
||||||
|
return acme.EC256
|
||||||
|
case "EC384":
|
||||||
|
return acme.EC384
|
||||||
|
case "RSA2048":
|
||||||
|
return acme.RSA2048
|
||||||
|
case "RSA4096":
|
||||||
|
return acme.RSA4096
|
||||||
|
case "RSA8192":
|
||||||
|
return acme.RSA8192
|
||||||
|
default:
|
||||||
|
log.Warnf("Unable to determine key type value %s. Use %s as default value", value, acme.RSA4096)
|
||||||
|
return acme.RSA4096
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@ type Configuration struct {
|
||||||
CAServer string `description:"CA server to use."`
|
CAServer string `description:"CA server to use."`
|
||||||
Storage string `description:"Storage to use."`
|
Storage string `description:"Storage to use."`
|
||||||
EntryPoint string `description:"EntryPoint to use."`
|
EntryPoint string `description:"EntryPoint to use."`
|
||||||
|
KeyType string `description:"KeyType used for generating certificate private key. Allow value 'EC256', 'EC384', 'RSA2048', 'RSA4096', 'RSA8192'. Default to 'RSA4096'"`
|
||||||
OnHostRule bool `description:"Enable certificate generation on frontends Host rules."`
|
OnHostRule bool `description:"Enable certificate generation on frontends Host rules."`
|
||||||
OnDemand bool `description:"Enable on demand certificate generation. This will request a certificate from Let's Encrypt during the first TLS handshake for a hostname that does not yet have a certificate."` // Deprecated
|
OnDemand bool `description:"Enable on demand certificate generation. This will request a certificate from Let's Encrypt during the first TLS handshake for a hostname that does not yet have a certificate."` // Deprecated
|
||||||
DNSChallenge *DNSChallenge `description:"Activate DNS-01 Challenge"`
|
DNSChallenge *DNSChallenge `description:"Activate DNS-01 Challenge"`
|
||||||
|
@ -116,7 +117,7 @@ func (p *Provider) init() error {
|
||||||
func (p *Provider) initAccount() (*Account, error) {
|
func (p *Provider) initAccount() (*Account, error) {
|
||||||
if p.account == nil || len(p.account.Email) == 0 {
|
if p.account == nil || len(p.account.Email) == 0 {
|
||||||
var err error
|
var err error
|
||||||
p.account, err = NewAccount(p.Email)
|
p.account, err = NewAccount(p.Email, p.KeyType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -246,7 +247,7 @@ func (p *Provider) getClient() (*acme.Client, error) {
|
||||||
caServer = p.CAServer
|
caServer = p.CAServer
|
||||||
}
|
}
|
||||||
log.Debugf(caServer)
|
log.Debugf(caServer)
|
||||||
client, err := acme.NewClient(caServer, account, acme.RSA4096)
|
client, err := acme.NewClient(caServer, account, account.KeyType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue