Compare commits

...

13 Commits

Author SHA1 Message Date
Ludovic Fernandez
31786f7163 Prepare release v1.7.21 2020-02-20 18:22:04 +01:00
pierresteiner
d7ed42d614 Fix sample for ssl-header in docs 2020-02-19 17:18:04 +01:00
mpl
4c709c2fcd Improve rate-limiting doc 2020-02-07 17:26:05 +01:00
Yazan Dabain
8dac076711 Fix finding proper provided certificate when ACME is enabled 2020-02-06 10:08:04 +01:00
Julien Salleyron
0b039499c6 don't create http client in each request in forward auth 2020-02-04 18:36:04 +01:00
Steven
2124975432 Fix dnspod update. 2020-01-26 12:42:03 +01:00
Jean-Baptiste Doumenjou
e4725619ea Update the k8s api version in the documentation 2020-01-13 15:44:05 +01:00
Ludovic Fernandez
d24f78222c fix: skip ECS container when information are missing instead of panic. 2020-01-06 15:10:05 +01:00
Daniel Tomcej
3f1925e20e Add edge case for root path with rewrite-target 2019-12-19 09:24:04 +01:00
Ludovic Fernandez
a8393faf0a Prepare release v1.7.20 2019-12-10 17:50:05 +01:00
Daniel Tomcej
4b8ece5b42 Truncate key for identification in log 2019-12-09 11:50:06 +01:00
Brad Jones
7574bb9226 Add a warning note regarding optional TLS mutual auth 2019-11-27 17:18:05 +01:00
Ludovic Fernandez
f68b629469 fix: location header rewrite.
Co-authored-by: Daniel Tomcej <daniel.tomcej@gmail.com>
2019-11-18 14:28:06 +01:00
26 changed files with 276 additions and 67 deletions

View File

@@ -1,5 +1,30 @@
# Change Log
## [v1.7.21](https://github.com/containous/traefik/tree/v1.7.21) (2020-02-20)
[All Commits](https://github.com/containous/traefik/compare/v1.7.20...v1.7.21)
**Bug fixes:**
- **[acme]** Fix dnspod update. ([#6240](https://github.com/containous/traefik/pull/6240) by [iineva](https://github.com/iineva))
- **[acme]** Fix finding proper provided certificate when ACME is enabled ([#5873](https://github.com/containous/traefik/pull/5873) by [yazd](https://github.com/yazd))
- **[authentication,middleware]** don&#39;t create http client in each request in forward auth ([#6273](https://github.com/containous/traefik/pull/6273) by [juliens](https://github.com/juliens))
- **[ecs]** fix: skip ECS container when information are missing instead of panic. ([#6071](https://github.com/containous/traefik/pull/6071) by [ldez](https://github.com/ldez))
- **[k8s]** Add edge case for root path with rewrite-target ([#6005](https://github.com/containous/traefik/pull/6005) by [dtomcej](https://github.com/dtomcej))
**Documentation:**
- **[k8s,k8s/ingress]** Update the k8s api version in the documentation ([#6162](https://github.com/containous/traefik/pull/6162) by [jbdoumenjou](https://github.com/jbdoumenjou))
- **[middleware]** Improve rate-limiting doc ([#6277](https://github.com/containous/traefik/pull/6277) by [mpl](https://github.com/mpl))
- **[provider]** Fix sample for ssl-header in docs ([#6337](https://github.com/containous/traefik/pull/6337) by [pierresteiner](https://github.com/pierresteiner))
## [v1.7.20](https://github.com/containous/traefik/tree/v1.7.20) (2019-12-09)
[All Commits](https://github.com/containous/traefik/compare/v1.7.19...v1.7.20)
**Bug fixes:**
- **[acme]** Truncate key for identification in log ([#5941](https://github.com/containous/traefik/pull/5941) by [dtomcej](https://github.com/dtomcej))
- **[middleware]** fix: location header rewrite. ([#5857](https://github.com/containous/traefik/pull/5857) by [ldez](https://github.com/ldez))
**Documentation:**
- Add a warning note regarding optional TLS mutual auth ([#5434](https://github.com/containous/traefik/pull/5434) by [bradjones1](https://github.com/bradjones1))
## [v1.7.19](https://github.com/containous/traefik/tree/v1.7.19) (2019-10-25)
[All Commits](https://github.com/containous/traefik/compare/v1.7.18...v1.7.19)

11
Gopkg.lock generated
View File

@@ -477,12 +477,11 @@
version = "v1.1.0"
[[projects]]
digest = "1:fa62421bd924623ac10a160686cc55d529f7274b2caedf7d2c607d14bc50c118"
digest = "1:295f7aed734ab28daff7c0df7db42bcccf7a043d4e0a5daf5768129346b25f23"
name = "github.com/decker502/dnspod-go"
packages = ["."]
pruneopts = "NUT"
revision = "71fbbdbdf1a7eeac949586de15bf96d416d3dd63"
version = "v0.2.0"
revision = "071171b22a9b65e4f544b61c143befd54a08a64e"
[[projects]]
digest = "1:7a6852b35eb5bbc184561443762d225116ae630c26a7c4d90546619f1e7d2ad2"
@@ -1915,12 +1914,12 @@
revision = "50716a0a853771bb36bfce61a45cdefdb98c2e6e"
[[projects]]
branch = "v1"
digest = "1:819d4566276aed820b412b7e72683edfe99f53d2ac54e5b13eda197b523a369b"
digest = "1:649756d307b6d8ddb369d1cca0465b679aa7d1a956ddfa8eb18f8072a1a2b7a4"
name = "github.com/unrolled/secure"
packages = ["."]
pruneopts = "NUT"
revision = "232c938a6a69cfd83e26e2bfe100a20486d3a9a0"
revision = "996bc0cd7e5be6e6a1c5f34b0259bc47c8bcfbc9"
version = "v1.0.5"
[[projects]]
digest = "1:e84e99d5f369afaa9a5c41f55b57fa03047ecd3bac2a65861607882693ceea81"

View File

@@ -167,8 +167,8 @@
version = "1.1.0"
[[constraint]]
branch = "v1"
name = "github.com/unrolled/secure"
version = "1.0.5"
[[constraint]]
name = "github.com/vdemeester/shakers"
@@ -272,3 +272,7 @@
[[override]]
name = "contrib.go.opencensus.io/exporter/ocagent"
version = "0.4.12"
[[override]]
name = "github.com/decker502/dnspod-go"
revision = "071171b22a9b65e4f544b61c143befd54a08a64e"

View File

@@ -111,7 +111,12 @@ func (a *Account) GetPrivateKey() crypto.PrivateKey {
return privateKey
}
log.Errorf("Cannot unmarshall private key %+v", a.PrivateKey)
keySnippet := ""
if len(a.PrivateKey) >= 16 {
keySnippet = string(a.PrivateKey[:16])
}
log.Errorf("Cannot unmarshall private key beginning with %s", keySnippet)
return nil
}

View File

@@ -224,7 +224,7 @@ If you need to support multiple frontends for a service, for example when having
| `<prefix>.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
| `<prefix>.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
| `<prefix>.frontend.headers.SSLForceHost=true` | If `SSLForceHost` is `true` and `SSLHost` is set, requests will be forced to use `SSLHost` even the ones that are already using SSL. Default is false. |
| `<prefix>.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`).<br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `<prefix>.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-Proto:https`).<br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `<prefix>.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
| `<prefix>.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
| `<prefix>.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |

View File

@@ -412,7 +412,7 @@ It also means that Traefik will manipulate only one backend, not one backend per
| `traefik.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
| `traefik.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
| `traefik.frontend.headers.SSLForceHost=true` | If `SSLForceHost` is `true` and `SSLHost` is set, requests will be forced to use `SSLHost` even the ones that are already using SSL. Default is false. |
| `traefik.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`).<br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `traefik.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-Proto:https`).<br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `traefik.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
| `traefik.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
| `traefik.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |

View File

@@ -341,7 +341,7 @@ The following security annotations are applicable on the Ingress object:
| `ingress.kubernetes.io/ssl-temporary-redirect: "true"` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
| `ingress.kubernetes.io/ssl-host: HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
| `ingress.kubernetes.io/ssl-force-host: "true"` | If `SSLForceHost` is `true` and `SSLHost` is set, requests will be forced to use `SSLHost` even the ones that are already using SSL. Default is false. |
| `ingress.kubernetes.io/ssl-proxy-headers: EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`). Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `ingress.kubernetes.io/ssl-proxy-headers: EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-Proto:https`). Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
### Authentication

View File

@@ -303,7 +303,7 @@ The following labels can be defined on Marathon applications. They adjust the be
| `traefik.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
| `traefik.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
| `traefik.frontend.headers.SSLForceHost=true` | If `SSLForceHost` is `true` and `SSLHost` is set, requests will be forced to use `SSLHost` even the ones that are already using SSL. Default is false. |
| `traefik.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`).<br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `traefik.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-Proto:https`).<br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `traefik.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
| `traefik.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
| `traefik.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |

View File

@@ -215,7 +215,7 @@ The following labels can be defined on Mesos tasks. They adjust the behavior for
| `traefik.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
| `traefik.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
| `traefik.frontend.headers.SSLForceHost=true` | If `SSLForceHost` is `true` and `SSLHost` is set, requests will be forced to use `SSLHost` even the ones that are already using SSL. Default is false. |
| `traefik.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`).<br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `traefik.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-Proto:https`).<br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `traefik.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
| `traefik.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
| `traefik.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |

View File

@@ -250,7 +250,7 @@ Labels can be used on task containers to override default behavior:
| `traefik.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
| `traefik.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
| `traefik.frontend.headers.SSLForceHost=true` | If `SSLForceHost` is `true` and `SSLHost` is set, requests will be forced to use `SSLHost` even the ones that are already using SSL. Default is false. |
| `traefik.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`).<br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `traefik.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-Proto:https`).<br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `traefik.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
| `traefik.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
| `traefik.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |

View File

@@ -145,7 +145,7 @@ Labels, set through extensions or the property manager, can be used on services
| `traefik.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
| `traefik.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
| `traefik.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
| `traefik.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`).<br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `traefik.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-Proto:https`).<br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `traefik.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
| `traefik.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
| `traefik.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |

View File

@@ -249,8 +249,14 @@ Multiple sets of rates can be added to each frontend, but the time periods must
```
In the above example, frontend1 is configured to limit requests by the client's ip address.
An average of 100 requests every 10 seconds is allowed and an average of 5 requests every 3 seconds.
These can "burst" up to 200 and 10 in each period respectively.
A sustained rate of 100 requests every 10 seconds (10 req/s) is allowed for rateset1, and 5 requests every 3 seconds (~1.67 req/s) for rateset2.
In addition, these can "burst" up to 200 and 10 in each period respectively.
Another way to describe the above parameters, is to use the [leaky bucket](https://en.wikipedia.org/wiki/Leaky_bucket) analogy:
for rateset1, the size of the bucket is 200 drops, and it is leaking at a rate of 10 drop/s.
If the incoming rate of drops falling into the bucket gets high enough that the bucket gets filled,
any subsequent drop overflows out of the bucket (i.e. the request is discarded).
This situation holds until the incoming rate gets low enough again, and remains that way, for the water level in the bucket to go down.
Valid values for `extractorfunc` are:
* `client.ip`

View File

@@ -239,11 +239,14 @@ TLS Mutual Authentication can be `optional` or not.
* If `optional = true`, if a certificate is provided, verifies if it is signed by a specified Certificate Authority (CA). Otherwise proceeds without any certificate.
* If `optional = false`, Traefik will only accept clients that present a certificate signed by a specified Certificate Authority (CA).
!!! warning
While the TLS [1.1](https://tools.ietf.org/html/rfc4346#section-7.4.6) and [1.2](https://tools.ietf.org/html/rfc5246#section-7.4.6) RFCs specify that clients should proceed with handshaking by sending an empty list should they have no certs for the CAs specified by the server, not all do so in practice.
Use this feature with caution should you require maximum compatibility with a wide variety of client user agents which may not strictly implement these specs.
`ClientCAFiles` can be configured with multiple `CA:s` in the same file or use multiple files containing one or several `CA:s`.
The `CA:s` has to be in PEM format.
By default, `ClientCAFiles` is not optional, all clients will be required to present a valid cert.
The requirement will apply to all server certs in the entrypoint.
By default, `ClientCAFiles` is not optional, all clients will be required to present a valid cert. The requirement will apply to all server certs in the entrypoint.
In the example below both `snitest.com` and `snitest.org` will require client certs

View File

@@ -98,7 +98,7 @@ metadata:
namespace: kube-system
---
kind: Deployment
apiVersion: extensions/v1beta1
apiVersion: apps/v1
metadata:
name: traefik-ingress-controller
namespace: kube-system
@@ -164,7 +164,7 @@ metadata:
namespace: kube-system
---
kind: DaemonSet
apiVersion: extensions/v1beta1
apiVersion: apps/v1
metadata:
name: traefik-ingress-controller
namespace: kube-system
@@ -503,7 +503,7 @@ First lets start by launching the pods for the cheese websites.
```yaml
---
kind: Deployment
apiVersion: extensions/v1beta1
apiVersion: apps/v1
metadata:
name: stilton
labels:
@@ -529,7 +529,7 @@ spec:
- containerPort: 80
---
kind: Deployment
apiVersion: extensions/v1beta1
apiVersion: apps/v1
metadata:
name: cheddar
labels:
@@ -555,7 +555,7 @@ spec:
- containerPort: 80
---
kind: Deployment
apiVersion: extensions/v1beta1
apiVersion: apps/v1
metadata:
name: wensleydale
labels:

View File

@@ -6,7 +6,7 @@ metadata:
namespace: kube-system
---
kind: Deployment
apiVersion: extensions/v1beta1
apiVersion: apps/v1
metadata:
name: traefik-ingress-controller
namespace: kube-system

View File

@@ -3,8 +3,10 @@ package auth
import (
"fmt"
"io/ioutil"
"net"
"net/http"
"strings"
"time"
goauth "github.com/abbot/go-http-auth"
"github.com/containous/traefik/log"
@@ -61,7 +63,10 @@ func NewAuthenticator(authConfig *types.Auth, tracingMiddleware *tracing.Tracing
tracingAuth.name = "Auth Digest"
tracingAuth.clientSpanKind = false
} else if authConfig.Forward != nil {
tracingAuth.handler = createAuthForwardHandler(authConfig)
tracingAuth.handler, err = createAuthForwardHandler(authConfig)
if err != nil {
return nil, err
}
tracingAuth.name = "Auth Forward"
tracingAuth.clientSpanKind = true
}
@@ -74,10 +79,38 @@ func NewAuthenticator(authConfig *types.Auth, tracingMiddleware *tracing.Tracing
return authenticator, nil
}
func createAuthForwardHandler(authConfig *types.Auth) negroni.HandlerFunc {
func createAuthForwardHandler(authConfig *types.Auth) (negroni.HandlerFunc, error) {
// Ensure our request client does not follow redirects
client := http.Client{
CheckRedirect: func(r *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
Timeout: 30 * time.Second,
}
if authConfig.Forward.TLS != nil {
tlsConfig, err := authConfig.Forward.TLS.CreateTLSConfig()
if err != nil {
return nil, err
}
client.Transport = &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
DualStack: true,
}).DialContext,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
TLSClientConfig: tlsConfig,
}
}
return negroni.HandlerFunc(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
Forward(authConfig.Forward, w, r, next)
})
Forward(authConfig.Forward, client, w, r, next)
}), nil
}
func createAuthDigestHandler(digestAuth *goauth.DigestAuth, authConfig *types.Auth) negroni.HandlerFunc {
return negroni.HandlerFunc(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {

View File

@@ -18,28 +18,8 @@ const (
xForwardedMethod = "X-Forwarded-Method"
)
// Forward the authentication to a external server
func Forward(config *types.Forward, w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
// Ensure our request client does not follow redirects
httpClient := http.Client{
CheckRedirect: func(r *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
if config.TLS != nil {
tlsConfig, err := config.TLS.CreateTLSConfig()
if err != nil {
tracing.SetErrorAndDebugLog(r, "Unable to configure TLS to call %s. Cause %s", config.Address, err)
w.WriteHeader(http.StatusInternalServerError)
return
}
httpClient.Transport = &http.Transport{
TLSClientConfig: tlsConfig,
}
}
// Forward the authentication to an external server
func Forward(config *types.Forward, httpClient http.Client, w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
forwardReq, err := http.NewRequest(http.MethodGet, config.Address, http.NoBody)
tracing.LogRequest(tracing.GetSpan(r), forwardReq)
if err != nil {

View File

@@ -57,7 +57,12 @@ func (a *Account) GetPrivateKey() crypto.PrivateKey {
return privateKey
}
log.Errorf("Cannot unmarshal private key %+v", a.PrivateKey)
keySnippet := ""
if len(a.PrivateKey) >= 16 {
keySnippet = string(a.PrivateKey[:16])
}
log.Errorf("Cannot unmarshall private key beginning with %s", keySnippet)
return nil
}

View File

@@ -309,6 +309,11 @@ func (p *Provider) listInstances(ctx context.Context, client *awsClient) ([]ecsI
state: aws.StringValue(task.LastStatus),
}
} else {
if containerInstance == nil {
log.Errorf("Unable to find container instance information for %s", aws.StringValue(container.Name))
continue
}
var ports []portMapping
for _, mapping := range container.NetworkBindings {
if mapping != nil {

View File

@@ -161,6 +161,23 @@ spec:
servicePort: 80
path: /api
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
ingress.kubernetes.io/rewrite-target: /app
namespace: testing
spec:
rules:
- host: rewritetargetrootpath
http:
paths:
- backend:
serviceName: service1
servicePort: 80
path: /
---
apiVersion: extensions/v1beta1
kind: Ingress

View File

@@ -674,6 +674,12 @@ func getRuleForPath(pa extensionsv1beta1.HTTPIngressPath, i *extensionsv1beta1.I
return "", fmt.Errorf("rewrite-target must not be used together with annotation %q", annotationKubernetesRuleType)
}
rewriteTargetRule := fmt.Sprintf("ReplacePathRegex: ^%s(.*) %s$1", pa.Path, strings.TrimRight(rewriteTarget, "/"))
if pa.Path == "/" {
// If path = /, then just append the cap group, as if we don't cap the path as part of the regex,
// then when we strip the right / from the rewrite target, it ends up being missed, as removed but never returned
// this only happens when path = / because it is the only case where TrimRight will catch a leading /.
rewriteTargetRule = fmt.Sprintf("ReplacePathRegex: ^(.*) %s$1", strings.TrimRight(rewriteTarget, "/"))
}
rules = append(rules, rewriteTargetRule)
}

View File

@@ -415,6 +415,12 @@ func TestProvider_loadIngresses(t *testing.T) {
server("http://example.com", weight(1))),
lbMethod("wrr"),
),
backend("rewritetargetrootpath/",
servers(
server("http://example.com", weight(1)),
server("http://example.com", weight(1))),
lbMethod("wrr"),
),
backend("error-pages/errorpages",
servers(
server("http://example.com", weight(1)),
@@ -523,6 +529,12 @@ func TestProvider_loadIngresses(t *testing.T) {
route("/whitelist-source-range", "PathPrefix:/whitelist-source-range"),
route("test", "Host:test")),
),
frontend("rewritetargetrootpath/",
passHostHeader(),
routes(
route("/", "PathPrefix:/;ReplacePathRegex: ^(.*) /app$1"),
route("rewritetargetrootpath", "Host:rewritetargetrootpath")),
),
frontend("rewrite/api",
passHostHeader(),
routes(
@@ -596,7 +608,7 @@ func TestProvider_loadIngresses(t *testing.T) {
passHostHeader(),
redirectRegex("root2/$", "root2/root2"),
routes(
route("/", "PathPrefix:/;ReplacePathRegex: ^/(.*) /abc$1"),
route("/", "PathPrefix:/;ReplacePathRegex: ^(.*) /abc$1"),
route("root2", "Host:root2"),
),
),

View File

@@ -120,6 +120,7 @@ func (c *Certificates) CreateTLSConfig(entryPointName string) (*tls.Config, erro
}
}
}
config.BuildNameToCertificate()
return config, nil
}

102
tls/certificate_test.go Normal file
View File

@@ -0,0 +1,102 @@
package tls
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"math/big"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestCertificates_CreateTLSConfig(t *testing.T) {
var cert Certificates
certificate1, err := generateCertificate("test1.traefik.com")
require.NoError(t, err)
require.NotNil(t, certificate1)
cert = append(cert, *certificate1)
certificate2, err := generateCertificate("test2.traefik.com")
require.NoError(t, err)
require.NotNil(t, certificate2)
cert = append(cert, *certificate2)
config, err := cert.CreateTLSConfig("http")
require.NoError(t, err)
assert.Len(t, config.NameToCertificate, 2)
assert.Contains(t, config.NameToCertificate, "test1.traefik.com")
assert.Contains(t, config.NameToCertificate, "test2.traefik.com")
}
func generateCertificate(domain string) (*Certificate, error) {
certPEM, keyPEM, err := keyPair(domain, time.Time{})
if err != nil {
return nil, err
}
return &Certificate{
CertFile: FileOrContent(certPEM),
KeyFile: FileOrContent(keyPEM),
}, nil
}
// keyPair generates cert and key files
func keyPair(domain string, expiration time.Time) ([]byte, []byte, error) {
rsaPrivKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, nil, err
}
keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(rsaPrivKey)})
certPEM, err := PemCert(rsaPrivKey, domain, expiration)
if err != nil {
return nil, nil, err
}
return certPEM, keyPEM, nil
}
// PemCert generates PEM cert file
func PemCert(privKey *rsa.PrivateKey, domain string, expiration time.Time) ([]byte, error) {
derBytes, err := derCert(privKey, expiration, domain)
if err != nil {
return nil, err
}
return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}), nil
}
func derCert(privKey *rsa.PrivateKey, expiration time.Time, domain string) ([]byte, error) {
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
if err != nil {
return nil, err
}
if expiration.IsZero() {
expiration = time.Now().Add(365 * (24 * time.Hour))
}
template := x509.Certificate{
SerialNumber: serialNumber,
Subject: pkix.Name{
CommonName: domain,
},
NotBefore: time.Now(),
NotAfter: expiration,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageKeyAgreement | x509.KeyUsageDataEncipherment,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
DNSNames: []string{domain},
}
return x509.CreateCertificate(rand.Reader, &template, &template, &privKey.PublicKey, privKey)
}

View File

@@ -16,18 +16,18 @@ type DomainsService struct {
}
type DomainInfo struct {
DomainTotal int `json:"domain_total,omitempty"`
AllTotal int `json:"all_total,omitempty"`
MineTotal int `json:"mine_total,omitempty"`
ShareTotal string `json:"share_total,omitempty"`
VipTotal int `json:"vip_total,omitempty"`
IsMarkTotal int `json:"ismark_total,omitempty"`
PauseTotal int `json:"pause_total,omitempty"`
ErrorTotal int `json:"error_total,omitempty"`
LockTotal int `json:"lock_total,omitempty"`
SpamTotal int `json:"spam_total,omitempty"`
VipExpire int `json:"vip_expire,omitempty"`
ShareOutTotal int `json:"share_out_total,omitempty"`
DomainTotal json.Number `json:"domain_total,omitempty"`
AllTotal json.Number `json:"all_total,omitempty"`
MineTotal json.Number `json:"mine_total,omitempty"`
ShareTotal json.Number `json:"share_total,omitempty"`
VipTotal json.Number `json:"vip_total,omitempty"`
IsMarkTotal json.Number `json:"ismark_total,omitempty"`
PauseTotal json.Number `json:"pause_total,omitempty"`
ErrorTotal json.Number `json:"error_total,omitempty"`
LockTotal json.Number `json:"lock_total,omitempty"`
SpamTotal json.Number `json:"spam_total,omitempty"`
VipExpire json.Number `json:"vip_expire,omitempty"`
ShareOutTotal json.Number `json:"share_out_total,omitempty"`
}
type Domain struct {

View File

@@ -437,9 +437,15 @@ func (s *Secure) isSSL(r *http.Request) bool {
// Used by http.ReverseProxy.
func (s *Secure) ModifyResponseHeaders(res *http.Response) error {
if res != nil && res.Request != nil {
// Fix Location response header http to https when SSL is enabled.
// Fix Location response header http to https:
// When SSL is enabled,
// And SSLHost is defined,
// And the response location header includes the SSLHost as the domain with a trailing slash,
// Or an exact match to the SSLHost.
location := res.Header.Get("Location")
if s.isSSL(res.Request) && strings.Contains(location, "http:") {
if s.isSSL(res.Request) &&
len(s.opt.SSLHost) > 0 &&
(strings.HasPrefix(location, fmt.Sprintf("http://%s/", s.opt.SSLHost)) || location == fmt.Sprintf("http://%s", s.opt.SSLHost)) {
location = strings.Replace(location, "http:", "https:", 1)
res.Header.Set("Location", location)
}