diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a49855a1..9d438cdb0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,41 @@ # Change Log +## [v1.5.0-rc2](https://github.com/containous/traefik/tree/v1.5.0-rc2) (2017-12-06) +[All Commits](https://github.com/containous/traefik/compare/v1.5.0-rc1...v1.5.0-rc2) + +**Bug fixes:** +- **[acme]** Modify the ACME renewing logs level ([#2520](https://github.com/containous/traefik/pull/2520) by [nmengin](https://github.com/nmengin)) +- **[api]** Fix pprof route order. ([#2523](https://github.com/containous/traefik/pull/2523) by [timoreimann](https://github.com/timoreimann)) +- **[docker,k8s]** Change custom headers separator ([#2509](https://github.com/containous/traefik/pull/2509) by [ldez](https://github.com/ldez)) +- **[docker,k8s]** Fix Labels/annotation logs and values. ([#2488](https://github.com/containous/traefik/pull/2488) by [ldez](https://github.com/ldez)) +- **[docker]** Quote template strings ([#2496](https://github.com/containous/traefik/pull/2496) by [dtomcej](https://github.com/dtomcej)) +- **[docker]** Fix empty IP for backend when dnsrr in Docker swarm mode ([#2490](https://github.com/containous/traefik/pull/2490) by [mmatur](https://github.com/mmatur)) +- **[healthcheck]** Fix healthcheck when web is not specified ([#2529](https://github.com/containous/traefik/pull/2529) by [Juliens](https://github.com/Juliens)) +- **[k8s]** Reduce logs with new Kubernetes security annotations ([#2506](https://github.com/containous/traefik/pull/2506) by [ldez](https://github.com/ldez)) +- **[metrics]** Do not ignore web params when web.metrics.prometheus is set ([#2499](https://github.com/containous/traefik/pull/2499) by [Juliens](https://github.com/Juliens)) +- **[metrics]** Fix metrics problem on multiple entrypoints ([#2492](https://github.com/containous/traefik/pull/2492) by [Juliens](https://github.com/Juliens)) +- Close ring buffer used in throttling function. ([#2532](https://github.com/containous/traefik/pull/2532) by [timoreimann](https://github.com/timoreimann)) +- Fix wrong default entrypoint and non-existing entrypoint issue ([#2501](https://github.com/containous/traefik/pull/2501) by [Juliens](https://github.com/Juliens)) + +**Documentation:** +- **[consul]** Improve Consul documentation ([#2485](https://github.com/containous/traefik/pull/2485) by [mmatur](https://github.com/mmatur)) +- **[docker]** Fix Docker labels documentation render. ([#2505](https://github.com/containous/traefik/pull/2505) by [ldez](https://github.com/ldez)) +- **[k8s]** Add note to Kubernetes RBAC docs about RoleBindings and namespaces ([#2498](https://github.com/containous/traefik/pull/2498) by [jmara](https://github.com/jmara)) + +**Misc:** +- Merge v1.4.5 into v1.5 ([#2530](https://github.com/containous/traefik/pull/2530) by [mmatur](https://github.com/mmatur)) + +## [v1.4.5](https://github.com/containous/traefik/tree/v1.4.5) (2017-12-05) +[All Commits](https://github.com/containous/traefik/compare/v1.4.4...v1.4.5) + +**Bug fixes:** +- **[docker]** Fix empty ip when container is stopped ([#2478](https://github.com/containous/traefik/pull/2478) by [mmatur](https://github.com/mmatur)) +- **[k8s]** Fix kubernetes path prefix rule with rewrite-target ([#2461](https://github.com/containous/traefik/pull/2461) by [cheungpat](https://github.com/cheungpat)) + +**Documentation:** +- **[file]** Emphasize the necessity of enabling file backend ([#2483](https://github.com/containous/traefik/pull/2483) by [mvasin](https://github.com/mvasin)) +- Add link to future 1.5 documentation. ([#2477](https://github.com/containous/traefik/pull/2477) by [ldez](https://github.com/ldez)) + ## [v1.5.0-rc1](https://github.com/containous/traefik/tree/v1.5.0-rc1) (2017-11-28) [All Commits](https://github.com/containous/traefik/compare/v1.4.0-rc1...v1.5.0-rc1) diff --git a/acme/acme.go b/acme/acme.go index 06e5c116f..ebb60aa3e 100644 --- a/acme/acme.go +++ b/acme/acme.go @@ -394,44 +394,27 @@ func (a *ACME) retrieveCertificates() { func (a *ACME) renewCertificates() { a.jobs.In() <- func() { - log.Debug("Testing certificate renew...") + log.Info("Testing certificate renew...") account := a.store.Get().(*Account) for _, certificateResource := range account.DomainsCertificate.Certs { if certificateResource.needRenew() { - log.Debugf("Renewing certificate %+v", certificateResource.Domains) - renewedCert, err := a.client.RenewCertificate(acme.CertificateResource{ - Domain: certificateResource.Certificate.Domain, - CertURL: certificateResource.Certificate.CertURL, - CertStableURL: certificateResource.Certificate.CertStableURL, - PrivateKey: certificateResource.Certificate.PrivateKey, - Certificate: certificateResource.Certificate.Certificate, - }, true, OSCPMustStaple) + log.Infof("Renewing certificate from LE : %+v", certificateResource.Domains) + renewedACMECert, err := a.renewACMECertificate(certificateResource) if err != nil { - log.Errorf("Error renewing certificate: %v", err) + log.Errorf("Error renewing certificate from LE: %v", err) continue } - log.Debugf("Renewed certificate %+v", certificateResource.Domains) - renewedACMECert := &Certificate{ - Domain: renewedCert.Domain, - CertURL: renewedCert.CertURL, - CertStableURL: renewedCert.CertStableURL, - PrivateKey: renewedCert.PrivateKey, - Certificate: renewedCert.Certificate, + operation := func() error { + return a.storeRenewedCertificate(account, certificateResource, renewedACMECert) } - transaction, object, err := a.store.Begin() + notify := func(err error, time time.Duration) { + log.Warnf("Renewed certificate storage error: %v, retrying in %s", err, time) + } + ebo := backoff.NewExponentialBackOff() + ebo.MaxElapsedTime = 60 * time.Second + err = backoff.RetryNotify(safe.OperationWithRecover(operation), ebo, notify) if err != nil { - log.Errorf("Error renewing certificate: %v", err) - continue - } - account = object.(*Account) - err = account.DomainsCertificate.renewCertificates(renewedACMECert, certificateResource.Domains) - if err != nil { - log.Errorf("Error renewing certificate: %v", err) - continue - } - - if err = transaction.Commit(account); err != nil { - log.Errorf("Error Saving ACME account %+v: %s", account, err.Error()) + log.Errorf("Datastore cannot sync: %v", err) continue } } @@ -439,6 +422,56 @@ func (a *ACME) renewCertificates() { } } +func (a *ACME) renewACMECertificate(certificateResource *DomainsCertificate) (*Certificate, error) { + renewedCert, err := a.client.RenewCertificate(acme.CertificateResource{ + Domain: certificateResource.Certificate.Domain, + CertURL: certificateResource.Certificate.CertURL, + CertStableURL: certificateResource.Certificate.CertStableURL, + PrivateKey: certificateResource.Certificate.PrivateKey, + Certificate: certificateResource.Certificate.Certificate, + }, true, OSCPMustStaple) + if err != nil { + return nil, err + } + log.Infof("Renewed certificate from LE: %+v", certificateResource.Domains) + return &Certificate{ + Domain: renewedCert.Domain, + CertURL: renewedCert.CertURL, + CertStableURL: renewedCert.CertStableURL, + PrivateKey: renewedCert.PrivateKey, + Certificate: renewedCert.Certificate, + }, nil +} + +func (a *ACME) storeRenewedCertificate(account *Account, certificateResource *DomainsCertificate, renewedACMECert *Certificate) error { + transaction, object, err := a.store.Begin() + if err != nil { + return fmt.Errorf("error during transaction initialization for renewing certificate: %v", err) + } + + log.Infof("Renewing certificate in data store : %+v ", certificateResource.Domains) + account = object.(*Account) + err = account.DomainsCertificate.renewCertificates(renewedACMECert, certificateResource.Domains) + if err != nil { + return fmt.Errorf("error renewing certificate in datastore: %v ", err) + } + + log.Infof("Commit certificate renewed in data store : %+v", certificateResource.Domains) + if err = transaction.Commit(account); err != nil { + return fmt.Errorf("error saving ACME account %+v: %v", account, err) + } + + oldAccount := a.store.Get().(*Account) + for _, oldCertificateResource := range oldAccount.DomainsCertificate.Certs { + if oldCertificateResource.Domains.Main == certificateResource.Domains.Main && strings.Join(oldCertificateResource.Domains.SANs, ",") == strings.Join(certificateResource.Domains.SANs, ",") && certificateResource.Certificate != renewedACMECert { + return fmt.Errorf("renewed certificate not stored: %+v", certificateResource.Domains) + } + } + + log.Infof("Certificate successfully renewed in data store: %+v", certificateResource.Domains) + return nil +} + func dnsOverrideDelay(delay int) error { var err error if delay > 0 { @@ -448,7 +481,7 @@ func dnsOverrideDelay(delay int) error { return true, nil } } else if delay < 0 { - err = fmt.Errorf("Invalid negative DelayDontCheckDNS: %d", delay) + err = fmt.Errorf("invalid negative DelayDontCheckDNS: %d", delay) } return err } diff --git a/api/debug.go b/api/debug.go index 53cc1ccf8..4ddca7c28 100644 --- a/api/debug.go +++ b/api/debug.go @@ -38,9 +38,9 @@ func (g DebugHandler) AddRoutes(router *mux.Router) { fmt.Fprint(w, "\n}\n") }) - router.Methods(http.MethodGet).PathPrefix("/debug/pprof/").HandlerFunc(pprof.Index) router.Methods(http.MethodGet).PathPrefix("/debug/pprof/cmdline").HandlerFunc(pprof.Cmdline) router.Methods(http.MethodGet).PathPrefix("/debug/pprof/profile").HandlerFunc(pprof.Profile) router.Methods(http.MethodGet).PathPrefix("/debug/pprof/symbol").HandlerFunc(pprof.Symbol) router.Methods(http.MethodGet).PathPrefix("/debug/pprof/trace").HandlerFunc(pprof.Trace) + router.Methods(http.MethodGet).PathPrefix("/debug/pprof/").HandlerFunc(pprof.Index) } diff --git a/cmd/traefik/healthcheck.go b/cmd/traefik/healthcheck.go index 876d0d04f..9f926dd6f 100644 --- a/cmd/traefik/healthcheck.go +++ b/cmd/traefik/healthcheck.go @@ -64,6 +64,9 @@ func healthCheck(globalConfiguration configuration.GlobalConfiguration) (*http.R } client.Transport = tr } - - return client.Head(protocol + "://" + pingEntryPoint.Address + globalConfiguration.Web.Path + "ping") + path := "/" + if globalConfiguration.Web != nil { + path = globalConfiguration.Web.Path + } + return client.Head(protocol + "://" + pingEntryPoint.Address + path + "ping") } diff --git a/docs/basics.md b/docs/basics.md index ffe71ec84..76fc323c3 100644 --- a/docs/basics.md +++ b/docs/basics.md @@ -620,7 +620,7 @@ This command allows to check the health of Traefik. Its exit status is `0` if Tr This can be used with Docker [HEALTHCHECK](https://docs.docker.com/engine/reference/builder/#healthcheck) instruction or any other health check orchestration mechanism. !!! note - The [`web` provider](/configuration/backends/web) must be enabled to allow `/ping` calls by the `healthcheck` command. + The [`ping`](/configuration/ping) must be enabled to allow the `healthcheck` command to call `/ping`. ```bash traefik healthcheck diff --git a/docs/configuration/backends/file.md b/docs/configuration/backends/file.md index ff77385ed..2621a5695 100644 --- a/docs/configuration/backends/file.md +++ b/docs/configuration/backends/file.md @@ -8,6 +8,8 @@ You have three choices: - [Rules in a Separate File](/configuration/backends/file/#rules-in-a-separate-file) - [Multiple `.toml` Files](/configuration/backends/file/#multiple-toml-files) +To enable the file backend, you must either pass the `--file` option to the Træfik binary or put the `[file]` section (with or without inner settings) in the configuration file. + The configuration file allows managing both backends/frontends and HTTPS certificates (which are not [Let's Encrypt](https://letsencrypt.org) certificates generated through Træfik). ## Simple @@ -25,8 +27,8 @@ defaultEntryPoints = ["http", "https"] [entryPoints.https] address = ":443" [entryPoints.https.tls] - [[entryPoints.https.tls.certificates]] - certFile = "integration/fixtures/https/snitest.org.cert" + [[entryPoints.https.tls.certificates]] + certFile = "integration/fixtures/https/snitest.org.cert" keyFile = "integration/fixtures/https/snitest.org.key" [file] @@ -153,15 +155,16 @@ filename = "rules.toml" entrypoints = ["http", "https"] # overrides defaultEntryPoints backend = "backend2" rule = "Path:/test" + # HTTPS certificate [[tlsConfiguration]] -entryPoints = ["https"] + entryPoints = ["https"] [tlsConfiguration.certificate] certFile = "integration/fixtures/https/snitest.com.cert" keyFile = "integration/fixtures/https/snitest.com.key" [[tlsConfiguration]] -entryPoints = ["https"] + entryPoints = ["https"] [[tlsConfiguration.certificates]] certFile = "integration/fixtures/https/snitest.org.cert" keyFile = "integration/fixtures/https/snitest.org.key" diff --git a/docs/user-guide/kubernetes.md b/docs/user-guide/kubernetes.md index c5fa9b3ec..b146515ed 100644 --- a/docs/user-guide/kubernetes.md +++ b/docs/user-guide/kubernetes.md @@ -21,6 +21,9 @@ If your cluster is configured with RBAC, you will need to authorize Træfik to u RoleBindings per namespace enable to restrict granted permissions to the very namespaces only that Træfik is watching over, thereby following the least-privileges principle. This is the preferred approach if Træfik is not supposed to watch all namespaces, and the set of namespaces does not change dynamically. Otherwise, a single ClusterRoleBinding must be employed. +!!! note + RoleBindings per namespace are available in Træfik 1.5 and later. Please use ClusterRoleBindings for older versions. + For the sake of simplicity, this guide will use a ClusterRoleBinding: ```yaml @@ -75,7 +78,7 @@ For namespaced restrictions, one RoleBinding is required per watched namespace a It is possible to use Træfik with a [Deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) or a [DaemonSet](https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/) object, whereas both options have their own pros and cons: - + - The scalability is much better when using a Deployment, because you will have a Single-Pod-per-Node model when using the DeaemonSet. - It is possible to exclusively run a Service on a dedicated set of machines using taints and tolerations with a DaemonSet. - On the other hand the DaemonSet allows you to access any Node directly on Port 80 and 443, where you have to setup a [Service](https://kubernetes.io/docs/concepts/services-networking/service/) object with a Deployment. diff --git a/server/server.go b/server/server.go index 1300fddb7..729955fe3 100644 --- a/server/server.go +++ b/server/server.go @@ -374,6 +374,7 @@ func (s *Server) preLoadConfiguration(configMsg types.ConfigMessage) { // it will publish the last of the newly received configurations. func throttleProviderConfigReload(throttle time.Duration, publish chan<- types.ConfigMessage, in <-chan types.ConfigMessage, stop chan bool) { ring := channels.NewRingChannel(1) + defer ring.Close() safe.Go(func() { for {