Compare commits

...

8 Commits

Author SHA1 Message Date
Ludovic Fernandez
5a6652fc20 Prepare release v1.7.22 2020-03-18 15:00:07 +01:00
robotte
91b0e2e4dc Complete TLS example for Kubernetes Ingress in user guide 2020-03-09 19:16:05 +01:00
Ludovic Fernandez
554bceb75b Skip redirection with invalid regex syntax. 2020-03-09 13:24:05 +01:00
Ludovic Fernandez
ce76212605 fix: manual provider code name. 2020-03-08 12:08:03 +01:00
bjthomas1
d474c87cde Updated rbac and demonset example bloc 2020-03-02 09:48:04 +01:00
Ludovic Fernandez
862772230c Update to go1.14 2020-03-02 09:30:05 +01:00
Ludovic Fernandez
09fa1e6546 Fix typo in user guide. 2020-02-27 21:06:05 +01:00
Omer Karjevsky
c3125104d9 Clear closed hijacked h2c connections 2020-02-27 14:08:04 +01:00
17 changed files with 224 additions and 70 deletions

View File

@@ -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

View File

@@ -1,5 +1,18 @@
# 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)

View File

@@ -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
[...]

View File

@@ -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 \

View File

@@ -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]

View File

@@ -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{},
},
},
}

View File

@@ -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 |

View File

@@ -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

View File

@@ -53,6 +53,12 @@ rules:
- get
- list
- watch
- apiGroups:
- extensions
resources:
- ingresses/status
verbs:
- update
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
@@ -171,6 +177,10 @@ metadata:
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.

View File

@@ -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 \

View File

@@ -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
}

View File

@@ -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]

View File

@@ -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

View File

@@ -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,
}

View File

@@ -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

View File

@@ -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 {

View File

@@ -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,
}
)