forked from SW/traefik
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5a6652fc20 | ||
|
|
91b0e2e4dc | ||
|
|
554bceb75b | ||
|
|
ce76212605 | ||
|
|
d474c87cde | ||
|
|
862772230c | ||
|
|
09fa1e6546 | ||
|
|
c3125104d9 | ||
|
|
31786f7163 | ||
|
|
d7ed42d614 | ||
|
|
4c709c2fcd | ||
|
|
8dac076711 | ||
|
|
0b039499c6 | ||
|
|
2124975432 | ||
|
|
e4725619ea | ||
|
|
d24f78222c | ||
|
|
3f1925e20e |
@@ -2,19 +2,19 @@
|
||||
|
||||
set -e
|
||||
|
||||
curl -O https://dl.google.com/go/go1.12.linux-amd64.tar.gz
|
||||
curl -O https://dl.google.com/go/go1.14.linux-amd64.tar.gz
|
||||
|
||||
tar -xvf go1.12.linux-amd64.tar.gz
|
||||
rm -rf go1.12.linux-amd64.tar.gz
|
||||
tar -xvf go1.14.linux-amd64.tar.gz
|
||||
rm -rf go1.14.linux-amd64.tar.gz
|
||||
|
||||
sudo mkdir -p /usr/local/golang/1.12/go
|
||||
sudo mv go /usr/local/golang/1.12/
|
||||
sudo mkdir -p /usr/local/golang/1.14/go
|
||||
sudo mv go /usr/local/golang/1.14/
|
||||
|
||||
sudo rm /usr/local/bin/go
|
||||
sudo chmod +x /usr/local/golang/1.12/go/bin/go
|
||||
sudo ln -s /usr/local/golang/1.12/go/bin/go /usr/local/bin/go
|
||||
sudo chmod +x /usr/local/golang/1.14/go/bin/go
|
||||
sudo ln -s /usr/local/golang/1.14/go/bin/go /usr/local/bin/go
|
||||
|
||||
export GOROOT="/usr/local/golang/1.12/go"
|
||||
export GOTOOLDIR="/usr/local/golang/1.12/go/pkg/tool/linux_amd64"
|
||||
export GOROOT="/usr/local/golang/1.14/go"
|
||||
export GOTOOLDIR="/usr/local/golang/1.14/go/pkg/tool/linux_amd64"
|
||||
|
||||
go version
|
||||
|
||||
28
CHANGELOG.md
28
CHANGELOG.md
@@ -1,5 +1,33 @@
|
||||
# Change Log
|
||||
|
||||
## [v1.7.22](https://github.com/containous/traefik/tree/v1.7.22) (2020-03-09)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.7.21...v1.7.22)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[provider]** Skip redirection with invalid regex syntax. ([#6446](https://github.com/containous/traefik/pull/6446) by [ldez](https://github.com/ldez))
|
||||
- **[server]** Clear closed hijacked h2c connections ([#6357](https://github.com/containous/traefik/pull/6357) by [omerkay](https://github.com/omerkay))
|
||||
|
||||
**Documentation:**
|
||||
- **[acme]** fix: manual provider code name. ([#6456](https://github.com/containous/traefik/pull/6456) by [ldez](https://github.com/ldez))
|
||||
- **[docker]** Fix typo in user guide. ([#6386](https://github.com/containous/traefik/pull/6386) by [ldez](https://github.com/ldez))
|
||||
- **[k8s]** Complete TLS example for Kubernetes Ingress in user guide ([#6457](https://github.com/containous/traefik/pull/6457) by [rtribotte](https://github.com/rtribotte))
|
||||
- **[k8s]** Updated rbac and Daemonset example bloc ([#6375](https://github.com/containous/traefik/pull/6375) by [bjthomas1](https://github.com/bjthomas1))
|
||||
|
||||
## [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'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)
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ You need to run the `binary` target. This will create binaries for Linux platfor
|
||||
$ make binary
|
||||
docker build -t "traefik-dev:no-more-godep-ever" -f build.Dockerfile .
|
||||
Sending build context to Docker daemon 295.3 MB
|
||||
Step 0 : FROM golang:1.12-alpine
|
||||
Step 0 : FROM golang:1.14-alpine
|
||||
---> 8c6473912976
|
||||
Step 1 : RUN go get github.com/golang/dep/cmd/dep
|
||||
[...]
|
||||
|
||||
5
Gopkg.lock
generated
5
Gopkg.lock
generated
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.12-alpine
|
||||
FROM golang:1.14-alpine
|
||||
|
||||
RUN apk --update upgrade \
|
||||
&& apk --no-cache --no-progress add git mercurial bash gcc musl-dev curl tar ca-certificates tzdata \
|
||||
|
||||
@@ -212,12 +212,6 @@ func (gc *GlobalConfiguration) SetEffectiveConfiguration(configFile string) {
|
||||
}
|
||||
}
|
||||
|
||||
// Thanks to SSLv3 being enabled by mistake in golang 1.12,
|
||||
// If no minVersion is set, apply TLS1.0 as the minimum.
|
||||
if entryPoint.TLS != nil && len(entryPoint.TLS.MinVersion) == 0 {
|
||||
entryPoint.TLS.MinVersion = "VersionTLS10"
|
||||
}
|
||||
|
||||
if entryPoint.TLS != nil && entryPoint.TLS.DefaultCertificate == nil && len(entryPoint.TLS.Certificates) > 0 {
|
||||
log.Infof("No tls.defaultCertificate given for %s: using the first item in tls.certificates as a fallback.", entryPointName)
|
||||
entryPoint.TLS.DefaultCertificate = &entryPoint.TLS.Certificates[0]
|
||||
|
||||
@@ -312,9 +312,7 @@ func TestSetEffectiveConfigurationTLSMinVersion(t *testing.T) {
|
||||
expected: EntryPoint{
|
||||
Address: ":443",
|
||||
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||
TLS: &tls.TLS{
|
||||
MinVersion: "VersionTLS10",
|
||||
},
|
||||
TLS: &tls.TLS{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -313,7 +313,7 @@ For example, `CF_API_EMAIL_FILE=/run/secrets/traefik_cf-api-email` could be used
|
||||
| [Lightsail](https://aws.amazon.com/lightsail/) | `lightsail` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `DNS_ZONE` | Not tested yet |
|
||||
| [Linode](https://www.linode.com) | `linode` | `LINODE_API_KEY` | Not tested yet |
|
||||
| [Linode v4](https://www.linode.com) | `linodev4` | `LINODE_TOKEN` | Not tested yet |
|
||||
| manual | - | none, but you need to run Traefik interactively, turn on `acmeLogging` to see instructions and press <kbd>Enter</kbd>. | YES |
|
||||
| manual | `manual` | none, but you need to run Traefik interactively, turn on `acmeLogging` to see instructions and press <kbd>Enter</kbd>. | YES |
|
||||
| [MyDNS.jp](https://www.mydns.jp/) | `mydnsjp` | `MYDNSJP_MASTER_ID`, `MYDNSJP_PASSWORD` | YES |
|
||||
| [Namecheap](https://www.namecheap.com) | `namecheap` | `NAMECHEAP_API_USER`, `NAMECHEAP_API_KEY` | YES |
|
||||
| [Namesilo](https://www.namesilo.com/) | `namesilo` | `NAMESILO_API_KEY` | YES |
|
||||
|
||||
@@ -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||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||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. |
|
||||
|
||||
@@ -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||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||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. |
|
||||
|
||||
@@ -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||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||HEADER2:value2</code> |
|
||||
|
||||
### Authentication
|
||||
|
||||
|
||||
@@ -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||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||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. |
|
||||
|
||||
@@ -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||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||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. |
|
||||
|
||||
@@ -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||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||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. |
|
||||
|
||||
@@ -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||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||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. |
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -235,13 +235,11 @@ Let's take a look at the labels themselves for the `app` service, which is a HTT
|
||||
- "traefik.admin.port=9443"
|
||||
```
|
||||
|
||||
We use both `container labels` and `service labels`.
|
||||
We use both `container labels` and `segment labels`.
|
||||
|
||||
#### Container labels
|
||||
|
||||
First, we specify the `backend` name which corresponds to the actual service we're routing **to**.
|
||||
|
||||
We also tell Traefik to use the `web` network to route HTTP traffic to this container.
|
||||
We tell Traefik to use the `web` network to route HTTP traffic to this container.
|
||||
With the `traefik.enable` label, we tell Traefik to include this container in its internal configuration.
|
||||
|
||||
With the `frontend.rule` label, we tell Traefik that we want to route to this container if the incoming HTTP request contains the `Host` `app.my-awesome-app.org`.
|
||||
@@ -249,20 +247,20 @@ Essentially, this is the actual rule used for Layer-7 load balancing.
|
||||
|
||||
Finally but not unimportantly, we tell Traefik to route **to** port `9000`, since that is the actual TCP/IP port the container actually listens on.
|
||||
|
||||
### Service labels
|
||||
#### Segment labels
|
||||
|
||||
`Service labels` allow managing many routes for the same container.
|
||||
`Segment labels` allow managing many routes for the same container.
|
||||
|
||||
When both `container labels` and `service labels` are defined, `container labels` are just used as default values for missing `service labels` but no frontend/backend are going to be defined only with these labels.
|
||||
Obviously, labels `traefik.frontend.rule` and `traefik.port` described above, will only be used to complete information set in `service labels` during the container frontends/backends creation.
|
||||
When both `container labels` and `segment labels` are defined, `container labels` are just used as default values for missing `segment labels` but no frontend/backend are going to be defined only with these labels.
|
||||
Obviously, labels `traefik.frontend.rule` and `traefik.port` described above, will only be used to complete information set in `segment labels` during the container frontends/backends creation.
|
||||
|
||||
In the example, two service names are defined : `basic` and `admin`.
|
||||
In the example, two segment names are defined : `basic` and `admin`.
|
||||
They allow creating two frontends and two backends.
|
||||
|
||||
- `basic` has only one `service label` : `traefik.basic.protocol`.
|
||||
- `basic` has only one `segment label` : `traefik.basic.protocol`.
|
||||
Traefik will use values set in `traefik.frontend.rule` and `traefik.port` to create the `basic` frontend and backend.
|
||||
The frontend listens to incoming HTTP requests which contain the `Host` `app.my-awesome-app.org` and redirect them in `HTTP` to the port `9000` of the backend.
|
||||
- `admin` has all the `services labels` needed to create the `admin` frontend and backend (`traefik.admin.frontend.rule`, `traefik.admin.protocol`, `traefik.admin.port`).
|
||||
- `admin` has all the `segment labels` needed to create the `admin` frontend and backend (`traefik.admin.frontend.rule`, `traefik.admin.protocol`, `traefik.admin.port`).
|
||||
Traefik will create a frontend to listen to incoming HTTP requests which contain the `Host` `admin-app.my-awesome-app.org` and redirect them in `HTTPS` to the port `9443` of the backend.
|
||||
|
||||
#### Gotchas and tips
|
||||
|
||||
@@ -53,6 +53,12 @@ rules:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- extensions
|
||||
resources:
|
||||
- ingresses/status
|
||||
verbs:
|
||||
- update
|
||||
---
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
@@ -98,7 +104,7 @@ metadata:
|
||||
namespace: kube-system
|
||||
---
|
||||
kind: Deployment
|
||||
apiVersion: extensions/v1beta1
|
||||
apiVersion: apps/v1
|
||||
metadata:
|
||||
name: traefik-ingress-controller
|
||||
namespace: kube-system
|
||||
@@ -164,13 +170,17 @@ metadata:
|
||||
namespace: kube-system
|
||||
---
|
||||
kind: DaemonSet
|
||||
apiVersion: extensions/v1beta1
|
||||
apiVersion: apps/v1
|
||||
metadata:
|
||||
name: traefik-ingress-controller
|
||||
namespace: kube-system
|
||||
labels:
|
||||
k8s-app: traefik-ingress-lb
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
k8s-app: traefik-ingress-lb
|
||||
name: traefik-ingress-lb
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
@@ -188,6 +198,7 @@ spec:
|
||||
hostPort: 80
|
||||
- name: admin
|
||||
containerPort: 8080
|
||||
hostPort: 8080
|
||||
securityContext:
|
||||
capabilities:
|
||||
drop:
|
||||
@@ -358,7 +369,7 @@ spec:
|
||||
kubectl apply -f https://raw.githubusercontent.com/containous/traefik/v1.7/examples/k8s/ui.yaml
|
||||
```
|
||||
|
||||
Now lets setup an entry in our `/etc/hosts` file to route `traefik-ui.minikube` to our cluster.
|
||||
Now let's setup an entry in our `/etc/hosts` file to route `traefik-ui.minikube` to our cluster.
|
||||
|
||||
In production you would want to set up real DNS entries.
|
||||
You can get the IP address of your minikube instance by running `minikube ip`:
|
||||
@@ -382,6 +393,23 @@ You can add a TLS entrypoint by adding the following `args` to the container spe
|
||||
--entrypoints=Name:https Address::443 TLS
|
||||
--entrypoints=Name:http Address::80
|
||||
```
|
||||
|
||||
Now let's add the TLS port either to the deployment:
|
||||
|
||||
```
|
||||
ports:
|
||||
- name: https
|
||||
containerPort: 443
|
||||
```
|
||||
|
||||
or to the daemon set:
|
||||
|
||||
```
|
||||
ports:
|
||||
- name: https
|
||||
containerPort: 443
|
||||
hostPort: 443
|
||||
```
|
||||
|
||||
To setup an HTTPS-protected ingress, you can leverage the TLS feature of the ingress resource.
|
||||
|
||||
@@ -503,7 +531,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 +557,7 @@ spec:
|
||||
- containerPort: 80
|
||||
---
|
||||
kind: Deployment
|
||||
apiVersion: extensions/v1beta1
|
||||
apiVersion: apps/v1
|
||||
metadata:
|
||||
name: cheddar
|
||||
labels:
|
||||
@@ -555,7 +583,7 @@ spec:
|
||||
- containerPort: 80
|
||||
---
|
||||
kind: Deployment
|
||||
apiVersion: extensions/v1beta1
|
||||
apiVersion: apps/v1
|
||||
metadata:
|
||||
name: wensleydale
|
||||
labels:
|
||||
|
||||
@@ -6,7 +6,7 @@ metadata:
|
||||
namespace: kube-system
|
||||
---
|
||||
kind: Deployment
|
||||
apiVersion: extensions/v1beta1
|
||||
apiVersion: apps/v1
|
||||
metadata:
|
||||
name: traefik-ingress-controller
|
||||
namespace: kube-system
|
||||
|
||||
@@ -12,7 +12,7 @@ RUN yarn install
|
||||
RUN npm run build
|
||||
|
||||
# BUILD
|
||||
FROM golang:1.12-alpine as gobuild
|
||||
FROM golang:1.14-alpine as gobuild
|
||||
|
||||
RUN apk --update upgrade \
|
||||
&& apk --no-cache --no-progress add git mercurial bash gcc musl-dev curl tar ca-certificates tzdata \
|
||||
|
||||
28
h2c/h2c.go
28
h2c/h2c.go
@@ -57,20 +57,20 @@ func (s Server) Serve(l net.Listener) error {
|
||||
if http2VerboseLogs {
|
||||
log.Debugf("Attempting h2c with prior knowledge.")
|
||||
}
|
||||
conn, err := initH2CWithPriorKnowledge(w)
|
||||
conn, err := initH2CWithPriorKnowledge(w, s.closeHijackedConnQuietly)
|
||||
if err != nil {
|
||||
if http2VerboseLogs {
|
||||
log.Debugf("Error h2c with prior knowledge: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
defer s.closeHijackedConnQuietly(conn)
|
||||
h2cSrv := &http2.Server{}
|
||||
h2cSrv.ServeConn(conn, &http2.ServeConnOpts{Handler: originalHandler})
|
||||
return
|
||||
}
|
||||
if conn, err := h2cUpgrade(w, r); err == nil {
|
||||
defer conn.Close()
|
||||
if conn, err := h2cUpgrade(w, r, s.closeHijackedConnQuietly); err == nil {
|
||||
defer s.closeHijackedConnQuietly(conn)
|
||||
h2cSrv := &http2.Server{}
|
||||
h2cSrv.ServeConn(conn, &http2.ServeConnOpts{Handler: originalHandler})
|
||||
return
|
||||
@@ -80,12 +80,24 @@ func (s Server) Serve(l net.Listener) error {
|
||||
return s.Server.Serve(l)
|
||||
}
|
||||
|
||||
func (s Server) closeHijackedConnQuietly(conn net.Conn) {
|
||||
connStateKey := conn
|
||||
if rwConn, ok := conn.(*rwConn); ok {
|
||||
connStateKey = rwConn.Conn
|
||||
}
|
||||
|
||||
s.ConnState(connStateKey, http.StateClosed)
|
||||
if err := conn.Close(); err != nil {
|
||||
log.Debugf("Error closing hijacked connection: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// initH2CWithPriorKnowledge implements creating a h2c connection with prior
|
||||
// knowledge (Section 3.4) and creates a net.Conn suitable for http2.ServeConn.
|
||||
// All we have to do is look for the client preface that is suppose to be part
|
||||
// of the body, and reforward the client preface on the net.Conn this function
|
||||
// creates.
|
||||
func initH2CWithPriorKnowledge(w http.ResponseWriter) (net.Conn, error) {
|
||||
func initH2CWithPriorKnowledge(w http.ResponseWriter, onFailureAfterHijack func(conn net.Conn)) (net.Conn, error) {
|
||||
hijacker, ok := w.(http.Hijacker)
|
||||
if !ok {
|
||||
return nil, errors.New("hijack not supported")
|
||||
@@ -100,6 +112,7 @@ func initH2CWithPriorKnowledge(w http.ResponseWriter) (net.Conn, error) {
|
||||
buf := make([]byte, len(expectedBody))
|
||||
n, err := io.ReadFull(rw, buf)
|
||||
if err != nil {
|
||||
onFailureAfterHijack(conn)
|
||||
return nil, fmt.Errorf("fail to read body: %v", err)
|
||||
}
|
||||
|
||||
@@ -112,7 +125,7 @@ func initH2CWithPriorKnowledge(w http.ResponseWriter) (net.Conn, error) {
|
||||
return c, nil
|
||||
}
|
||||
|
||||
conn.Close()
|
||||
onFailureAfterHijack(conn)
|
||||
if http2VerboseLogs {
|
||||
log.Printf(
|
||||
"Missing the request body portion of the client preface. Wanted: %v Got: %v",
|
||||
@@ -139,7 +152,7 @@ func drainClientPreface(r io.Reader) error {
|
||||
}
|
||||
|
||||
// h2cUpgrade establishes a h2c connection using the HTTP/1 upgrade (Section 3.2).
|
||||
func h2cUpgrade(w http.ResponseWriter, r *http.Request) (net.Conn, error) {
|
||||
func h2cUpgrade(w http.ResponseWriter, r *http.Request, onFailureAfterHijack func(conn net.Conn)) (net.Conn, error) {
|
||||
if !isH2CUpgrade(r.Header) {
|
||||
return nil, errors.New("non-conforming h2c headers")
|
||||
}
|
||||
@@ -167,6 +180,7 @@ func h2cUpgrade(w http.ResponseWriter, r *http.Request) (net.Conn, error) {
|
||||
// A conforming client will now send an H2 client preface which need to drain
|
||||
// since we already sent this.
|
||||
if err := drainClientPreface(rw); err != nil {
|
||||
onFailureAfterHijack(conn)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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"),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -45,6 +45,21 @@ func GetStringValue(labels map[string]string, labelName string, defaultValue str
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// GetStringSafeValue get string value associated to a label and check if the content is "quotable".
|
||||
func GetStringSafeValue(labels map[string]string, labelName string, defaultValue string) (string, error) {
|
||||
value, ok := labels[labelName]
|
||||
if !ok || len(value) <= 0 {
|
||||
return defaultValue, nil
|
||||
}
|
||||
|
||||
_, err := strconv.Unquote(`"` + value + `"`)
|
||||
if err != nil {
|
||||
return value, err
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// GetBoolValue get bool value associated to a label
|
||||
func GetBoolValue(labels map[string]string, labelName string, defaultValue bool) bool {
|
||||
rawValue, ok := labels[labelName]
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSplitAndTrimString(t *testing.T) {
|
||||
@@ -106,6 +107,78 @@ func TestGetStringValue(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetStringSafeValue(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
labels map[string]string
|
||||
labelName string
|
||||
defaultValue string
|
||||
expected string
|
||||
expectedError bool
|
||||
}{
|
||||
{
|
||||
desc: "empty labels map",
|
||||
labelName: "foo",
|
||||
defaultValue: "default",
|
||||
expected: "default",
|
||||
},
|
||||
{
|
||||
desc: "unescaped value",
|
||||
labels: map[string]string{
|
||||
"foo": `^https?://(test\.beta\.redacted\.org|redacted\.com)/(.*)`,
|
||||
},
|
||||
labelName: "foo",
|
||||
defaultValue: "default",
|
||||
expected: `^https?://(test\.beta\.redacted\.org|redacted\.com)/(.*)`,
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "escaped value",
|
||||
labels: map[string]string{
|
||||
"foo": `^https?://(test\\.beta\\.redacted\\.org|redacted\\.com)/(.*)`,
|
||||
},
|
||||
labelName: "foo",
|
||||
defaultValue: "default",
|
||||
expected: `^https?://(test\\.beta\\.redacted\\.org|redacted\\.com)/(.*)`,
|
||||
},
|
||||
{
|
||||
desc: "empty value",
|
||||
labels: map[string]string{
|
||||
"foo": "",
|
||||
},
|
||||
labelName: "foo",
|
||||
defaultValue: "default",
|
||||
expected: "default",
|
||||
},
|
||||
{
|
||||
desc: "non existing label",
|
||||
labels: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
labelName: "fii",
|
||||
defaultValue: "default",
|
||||
expected: "default",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := GetStringSafeValue(test.labels, test.labelName, test.defaultValue)
|
||||
|
||||
if test.expectedError {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
assert.Equal(t, test.expected, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetBoolValue(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
|
||||
@@ -50,8 +50,13 @@ func GetRedirect(labels map[string]string) *types.Redirect {
|
||||
|
||||
if Has(labels, TraefikFrontendRedirectRegex) &&
|
||||
Has(labels, TraefikFrontendRedirectReplacement) {
|
||||
value, err := GetStringSafeValue(labels, TraefikFrontendRedirectRegex, "")
|
||||
if err != nil {
|
||||
log.Errorf("Invalid regex syntax: %s", value)
|
||||
return nil
|
||||
}
|
||||
return &types.Redirect{
|
||||
Regex: GetStringValue(labels, TraefikFrontendRedirectRegex, ""),
|
||||
Regex: value,
|
||||
Replacement: GetStringValue(labels, TraefikFrontendRedirectReplacement, ""),
|
||||
Permanent: permanent,
|
||||
}
|
||||
|
||||
@@ -449,12 +449,19 @@ func TestGetRedirect(t *testing.T) {
|
||||
labels map[string]string
|
||||
expected *types.Redirect
|
||||
}{
|
||||
|
||||
{
|
||||
desc: "should return nil when no redirect labels",
|
||||
labels: map[string]string{},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should return nil when regex syntax is invalid",
|
||||
labels: map[string]string{
|
||||
TraefikFrontendRedirectRegex: `^https?://(test\.beta\.redacted\.org|redacted\.com)/(.*)`,
|
||||
TraefikFrontendRedirectReplacement: "$1",
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should use only entry point tag when mix regex redirect and entry point redirect",
|
||||
labels: map[string]string{
|
||||
@@ -805,6 +812,7 @@ func TestGetAuth(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPassTLSClientCert(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
|
||||
@@ -497,6 +497,12 @@ func (s *Server) createTLSConfig(entryPointName string, tlsOption *traefiktls.TL
|
||||
config.Certificates = []tls.Certificate{}
|
||||
}
|
||||
|
||||
// workaround for users who used GODEBUG to activate TLS1.3
|
||||
if strings.Contains(os.Getenv("GODEBUG"), "tls13=1") {
|
||||
config.MaxVersion = tls.VersionTLS13
|
||||
}
|
||||
config.MaxVersion = tls.VersionTLS12
|
||||
|
||||
// Set the minimum TLS version if set in the config TOML
|
||||
if minConst, exists := traefiktls.MinVersion[s.entryPoints[entryPointName].Configuration.TLS.MinVersion]; exists {
|
||||
config.PreferServerCipherSuites = true
|
||||
@@ -505,7 +511,7 @@ func (s *Server) createTLSConfig(entryPointName string, tlsOption *traefiktls.TL
|
||||
|
||||
// Set the list of CipherSuites if set in the config TOML
|
||||
if s.entryPoints[entryPointName].Configuration.TLS.CipherSuites != nil {
|
||||
// if our list of CipherSuites is defined in the entrypoint config, we can re-initilize the suites list as empty
|
||||
// if our list of CipherSuites is defined in the entrypoint config, we can re-initialize the suites list as empty
|
||||
config.CipherSuites = make([]uint16, 0)
|
||||
for _, cipher := range s.entryPoints[entryPointName].Configuration.TLS.CipherSuites {
|
||||
if cipherConst, exists := traefiktls.CipherSuites[cipher]; exists {
|
||||
|
||||
@@ -25,32 +25,34 @@ var (
|
||||
// CipherSuites Map of TLS CipherSuites from crypto/tls
|
||||
// Available CipherSuites defined at https://golang.org/pkg/crypto/tls/#pkg-constants
|
||||
CipherSuites = map[string]uint16{
|
||||
"TLS_RSA_WITH_RC4_128_SHA": tls.TLS_RSA_WITH_RC4_128_SHA,
|
||||
"TLS_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
"TLS_RSA_WITH_AES_128_CBC_SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
"TLS_RSA_WITH_AES_256_CBC_SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
"TLS_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_RSA_WITH_AES_128_CBC_SHA256,
|
||||
"TLS_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||
"TLS_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||
"TLS_ECDHE_RSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
|
||||
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
||||
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
||||
"TLS_AES_128_GCM_SHA256": tls.TLS_AES_128_GCM_SHA256,
|
||||
"TLS_AES_256_GCM_SHA384": tls.TLS_AES_256_GCM_SHA384,
|
||||
"TLS_CHACHA20_POLY1305_SHA256": tls.TLS_CHACHA20_POLY1305_SHA256,
|
||||
"TLS_FALLBACK_SCSV": tls.TLS_FALLBACK_SCSV,
|
||||
"TLS_RSA_WITH_RC4_128_SHA": tls.TLS_RSA_WITH_RC4_128_SHA,
|
||||
"TLS_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
"TLS_RSA_WITH_AES_128_CBC_SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
"TLS_RSA_WITH_AES_256_CBC_SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
"TLS_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_RSA_WITH_AES_128_CBC_SHA256,
|
||||
"TLS_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||
"TLS_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||
"TLS_ECDHE_RSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
|
||||
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
||||
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
||||
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
"TLS_AES_128_GCM_SHA256": tls.TLS_AES_128_GCM_SHA256,
|
||||
"TLS_AES_256_GCM_SHA384": tls.TLS_AES_256_GCM_SHA384,
|
||||
"TLS_CHACHA20_POLY1305_SHA256": tls.TLS_CHACHA20_POLY1305_SHA256,
|
||||
"TLS_FALLBACK_SCSV": tls.TLS_FALLBACK_SCSV,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -120,6 +122,7 @@ func (c *Certificates) CreateTLSConfig(entryPointName string) (*tls.Config, erro
|
||||
}
|
||||
}
|
||||
}
|
||||
config.BuildNameToCertificate()
|
||||
return config, nil
|
||||
}
|
||||
|
||||
|
||||
102
tls/certificate_test.go
Normal file
102
tls/certificate_test.go
Normal 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)
|
||||
}
|
||||
24
vendor/github.com/decker502/dnspod-go/domains.go
generated
vendored
24
vendor/github.com/decker502/dnspod-go/domains.go
generated
vendored
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user