Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge v1.6.6 into v1.7 #3802

Merged
merged 10 commits into from
Aug 21, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
# Change Log

## [v1.6.6](https://github.com/containous/traefik/tree/v1.6.6) (2018-08-20)
[All Commits](https://github.com/containous/traefik/compare/v1.6.5...v1.6.6)

**Bug fixes:**
- **[acme]** Avoid duplicated ACME resolution ([#3751](https://github.com/containous/traefik/pull/3751) by [nmengin](https://github.com/nmengin))
- **[api]** Remove TLS in API ([#3788](https://github.com/containous/traefik/pull/3788) by [Juliens](https://github.com/Juliens))
- **[cluster]** Remove unusable `--cluster` flag ([#3616](https://github.com/containous/traefik/pull/3616) by [dtomcej](https://github.com/dtomcej))
- **[ecs]** Fix bad condition in ECS provider ([#3609](https://github.com/containous/traefik/pull/3609) by [mmatur](https://github.com/mmatur))
- Set keepalive on TCP socket so idleTimeout works ([#3740](https://github.com/containous/traefik/pull/3740) by [ajardan](https://github.com/ajardan))

**Documentation:**
- A tiny rewording on the documentation API's page ([#3794](https://github.com/containous/traefik/pull/3794) by [dduportal](https://github.com/dduportal))
- Adding warnings and solution about the configuration exposure ([#3790](https://github.com/containous/traefik/pull/3790) by [dduportal](https://github.com/dduportal))
- Fix path to the debug pprof API ([#3608](https://github.com/containous/traefik/pull/3608) by [multani](https://github.com/multani))

**Misc:**
- **[oxy,websocket]** Update oxy dependency ([#3777](https://github.com/containous/traefik/pull/3777) by [Juliens](https://github.com/Juliens))

## [v1.7.0-rc3](https://github.com/containous/traefik/tree/v1.7.0-rc3) (2018-08-01)
[All Commits](https://github.com/containous/traefik/compare/v1.7.0-rc2...v1.7.0-rc3)

Expand Down
4 changes: 2 additions & 2 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

39 changes: 39 additions & 0 deletions acme/acme.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"net/url"
"reflect"
"strings"
"sync"
"time"

"github.com/BurntSushi/ty/fun"
Expand Down Expand Up @@ -64,6 +65,8 @@ type ACME struct {
jobs *channels.InfiniteChannel
TLSConfig *tls.Config `description:"TLS config in case wildcard certs are used"`
dynamicCerts *safe.Safe
resolvingDomains map[string]struct{}
resolvingDomainsMutex sync.RWMutex
}

func (a *ACME) init() error {
Expand All @@ -76,6 +79,10 @@ func (a *ACME) init() error {
}

a.jobs = channels.NewInfiniteChannel()

// Init the currently resolved domain map
a.resolvingDomains = make(map[string]struct{})

return nil
}

Expand Down Expand Up @@ -537,6 +544,10 @@ func (a *ACME) LoadCertificateForDomains(domains []string) {
if len(uncheckedDomains) == 0 {
return
}

a.addResolvingDomains(uncheckedDomains)
defer a.removeResolvingDomains(uncheckedDomains)

certificate, err := a.getDomainsCertificates(uncheckedDomains)
if err != nil {
log.Errorf("Error getting ACME certificates %+v : %v", uncheckedDomains, err)
Expand Down Expand Up @@ -568,6 +579,24 @@ func (a *ACME) LoadCertificateForDomains(domains []string) {
}
}

func (a *ACME) addResolvingDomains(resolvingDomains []string) {
a.resolvingDomainsMutex.Lock()
defer a.resolvingDomainsMutex.Unlock()

for _, domain := range resolvingDomains {
a.resolvingDomains[domain] = struct{}{}
}
}

func (a *ACME) removeResolvingDomains(resolvingDomains []string) {
a.resolvingDomainsMutex.Lock()
defer a.resolvingDomainsMutex.Unlock()

for _, domain := range resolvingDomains {
delete(a.resolvingDomains, domain)
}
}

// Get provided certificate which check a domains list (Main and SANs)
// from static and dynamic provided certificates
func (a *ACME) getProvidedCertificate(domains string) *tls.Certificate {
Expand Down Expand Up @@ -603,6 +632,9 @@ func searchProvidedCertificateForDomains(domain string, certs map[string]*tls.Ce
// Get provided certificate which check a domains list (Main and SANs)
// from static and dynamic provided certificates
func (a *ACME) getUncheckedDomains(domains []string, account *Account) []string {
a.resolvingDomainsMutex.RLock()
defer a.resolvingDomainsMutex.RUnlock()

log.Debugf("Looking for provided certificate to validate %s...", domains)
allCerts := make(map[string]*tls.Certificate)

Expand All @@ -625,6 +657,13 @@ func (a *ACME) getUncheckedDomains(domains []string, account *Account) []string
}
}

// Get currently resolved domains
for domain := range a.resolvingDomains {
if _, ok := allCerts[domain]; !ok {
allCerts[domain] = &tls.Certificate{}
}
}

// Get Configuration Domains
for i := 0; i < len(a.Domains); i++ {
allCerts[a.Domains[i].Main] = &tls.Certificate{}
Expand Down
10 changes: 8 additions & 2 deletions acme/acme_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -331,9 +331,12 @@ func TestAcme_getUncheckedCertificates(t *testing.T) {
mm["*.containo.us"] = &tls.Certificate{}
mm["traefik.acme.io"] = &tls.Certificate{}

a := ACME{TLSConfig: &tls.Config{NameToCertificate: mm}}
dm := make(map[string]struct{})
dm["*.traefik.wtf"] = struct{}{}

a := ACME{TLSConfig: &tls.Config{NameToCertificate: mm}, resolvingDomains: dm}

domains := []string{"traefik.containo.us", "trae.containo.us"}
domains := []string{"traefik.containo.us", "trae.containo.us", "foo.traefik.wtf"}
uncheckedDomains := a.getUncheckedDomains(domains, nil)
assert.Empty(t, uncheckedDomains)
domains = []string{"traefik.acme.io", "trae.acme.io"}
Expand All @@ -351,6 +354,9 @@ func TestAcme_getUncheckedCertificates(t *testing.T) {
account := Account{DomainsCertificate: domainsCertificates}
uncheckedDomains = a.getUncheckedDomains(domains, &account)
assert.Empty(t, uncheckedDomains)
domains = []string{"traefik.containo.us", "trae.containo.us", "traefik.wtf"}
uncheckedDomains = a.getUncheckedDomains(domains, nil)
assert.Len(t, uncheckedDomains, 1)
}

func TestAcme_getProvidedCertificate(t *testing.T) {
Expand Down
21 changes: 20 additions & 1 deletion docs/configuration/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

```toml
# API definition
# Warning: Enabling API will expose Træfik's configuration.
# It is not recommended in production,
# unless secured by authentication and authorizations
[api]
# Name of the related entry point
#
Expand All @@ -12,7 +15,7 @@
#
entryPoint = "traefik"

# Enabled Dashboard
# Enable Dashboard
#
# Optional
# Default: true
Expand All @@ -38,6 +41,22 @@ For more customization, see [entry points](/configuration/entrypoints/) document

![Web UI Health](/img/traefik-health.png)

## Security

Enabling the API will expose all configuration elements,
including sensitive data.

It is not recommended in production,
unless secured by authentication and authorizations.

A good sane default (but not exhaustive) set of recommendations
would be to apply the following protection mechanism:

* _At application level:_ enabling HTTP [Basic Authentication](#authentication)
* _At transport level:_ NOT exposing publicly the API's port,
keeping it restricted over internal networks
(restricted networks as in https://en.wikipedia.org/wiki/Principle_of_least_privilege).

## API

| Path | Method | Description |
Expand Down
9 changes: 9 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ services:
- /var/run/docker.sock:/var/run/docker.sock # So that Traefik can listen to the Docker events
```

!!! warning
Enabling the Web UI with the `--api` flag might exposes configuration elements. You can read more about this on the [API/Dashboard's Security section](/configuration/api#security).


**That's it. Now you can launch Træfik!**

Start your `reverse-proxy` with the following command:
Expand Down Expand Up @@ -199,3 +203,8 @@ Using the tiny Docker image:
```shell
docker run -d -p 8080:8080 -p 80:80 -v $PWD/traefik.toml:/etc/traefik/traefik.toml traefik
```

## Security

We want to keep Træfik safe for everyone.
If you've discovered a security vulnerability in Træfik, we appreciate your help in disclosing it to us in a responsible manner, using [this form](https://security.traefik.io).
2 changes: 1 addition & 1 deletion docs/user-guide/docker-and-lets-encrypt.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ In addition, we want to use Let's Encrypt to automatically generate and renew SS

## Setting Up

In order for this to work, you'll need a server with a public IP address, with Docker installed on it.
In order for this to work, you'll need a server with a public IP address, with Docker and docker-compose installed on it.

In this example, we're using the fictitious domain _my-awesome-app.org_.

Expand Down
2 changes: 1 addition & 1 deletion examples/acme/manage_acme_docker_environment.sh
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ start_boulder() {
# Script usage
show_usage() {
echo
echo "USAGE : manage_acme_docker_environment.sh [--start|--stop|--restart]"
echo "USAGE : manage_acme_docker_environment.sh [--dev|--start|--stop|--restart]"
echo
}

Expand Down
7 changes: 2 additions & 5 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,11 @@ theme:
include_sidebar: true
favicon: img/traefik.icon.png
logo: img/traefik.logo.png
palette:
primary: 'blue'
accent: 'light blue'
feature:
tabs: false
palette:
primary: 'cyan'
accent: 'cyan'
feature:
tabs: false
i18n:
prev: 'Previous'
next: 'Next'
Expand Down
36 changes: 35 additions & 1 deletion provider/acme/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ type Provider struct {
clientMutex sync.Mutex
configFromListenerChan chan types.Configuration
pool *safe.Pool
resolvingDomains map[string]struct{}
resolvingDomainsMutex sync.RWMutex
}

// Certificate is a struct which contains all data needed from an ACME certificate
Expand Down Expand Up @@ -144,6 +146,9 @@ func (p *Provider) Init(_ types.Constraints) error {
return fmt.Errorf("unable to get ACME certificates : %v", err)
}

// Init the currently resolved domain map
p.resolvingDomains = make(map[string]struct{})

return nil
}

Expand Down Expand Up @@ -373,6 +378,9 @@ func (p *Provider) resolveCertificate(domain types.Domain, domainFromConfigurati
return nil, nil
}

p.addResolvingDomains(uncheckedDomains)
defer p.removeResolvingDomains(uncheckedDomains)

log.Debugf("Loading ACME certificates %+v...", uncheckedDomains)

client, err := p.getClient()
Expand Down Expand Up @@ -410,6 +418,24 @@ func (p *Provider) resolveCertificate(domain types.Domain, domainFromConfigurati
return certificate, nil
}

func (p *Provider) removeResolvingDomains(resolvingDomains []string) {
p.resolvingDomainsMutex.Lock()
defer p.resolvingDomainsMutex.Unlock()

for _, domain := range resolvingDomains {
delete(p.resolvingDomains, domain)
}
}

func (p *Provider) addResolvingDomains(resolvingDomains []string) {
p.resolvingDomainsMutex.Lock()
defer p.resolvingDomainsMutex.Unlock()

for _, domain := range resolvingDomains {
p.resolvingDomains[domain] = struct{}{}
}
}

func (p *Provider) useCertificateWithRetry(domains []string) bool {
// Check if we can use the retry mechanism only if we use the DNS Challenge and if is there are at least 2 domains to check
if p.DNSChallenge != nil && len(domains) > 1 {
Expand Down Expand Up @@ -636,6 +662,9 @@ func (p *Provider) renewCertificates() {
// Get provided certificate which check a domains list (Main and SANs)
// from static and dynamic provided certificates
func (p *Provider) getUncheckedDomains(domainsToCheck []string, checkConfigurationDomains bool) []string {
p.resolvingDomainsMutex.RLock()
defer p.resolvingDomainsMutex.RUnlock()

log.Debugf("Looking for provided certificate(s) to validate %q...", domainsToCheck)

allDomains := p.certificateStore.GetAllDomains()
Expand All @@ -645,6 +674,11 @@ func (p *Provider) getUncheckedDomains(domainsToCheck []string, checkConfigurati
allDomains = append(allDomains, strings.Join(certificate.Domain.ToStrArray(), ","))
}

// Get currently resolved domains
for domain := range p.resolvingDomains {
allDomains = append(allDomains, domain)
}

// Get Configuration Domains
if checkConfigurationDomains {
for i := 0; i < len(p.Domains); i++ {
Expand All @@ -664,7 +698,7 @@ func searchUncheckedDomains(domainsToCheck []string, existentDomains []string) [
}

if len(uncheckedDomains) == 0 {
log.Debugf("No ACME certificate to generate for domains %q.", domainsToCheck)
log.Debugf("No ACME certificate generation required for domains %q.", domainsToCheck)
} else {
log.Debugf("Domains %q need ACME certificates generation for domains %q.", domainsToCheck, strings.Join(uncheckedDomains, ","))
}
Expand Down
Loading