Skip to content

Commit 07a53b6

Browse files
authored
feat: Add initial support for SSL termination for TransportServer (#3462)
* Add support for SSL termination for TransportServer
1 parent fcf512f commit 07a53b6

18 files changed

+529
-17
lines changed

deployments/common/crds/k8s.nginx.org_transportservers.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,12 @@ spec:
7373
type: string
7474
streamSnippets:
7575
type: string
76+
tls:
77+
description: TLS defines TLS configuration for a TransportServer.
78+
type: object
79+
properties:
80+
secret:
81+
type: string
7682
upstreamParameters:
7783
description: UpstreamParameters defines parameters for an upstream.
7884
type: object

deployments/helm-chart/crds/k8s.nginx.org_transportservers.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,12 @@ spec:
7373
type: string
7474
streamSnippets:
7575
type: string
76+
tls:
77+
description: TLS defines TLS configuration for a TransportServer.
78+
type: object
79+
properties:
80+
secret:
81+
type: string
7682
upstreamParameters:
7783
description: UpstreamParameters defines parameters for an upstream.
7884
type: object

docs/content/configuration/transportserver-resource.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ The TransportServer resource defines load balancing configuration for TCP, UDP,
3131
listener:
3232
name: dns-tcp
3333
protocol: TCP
34+
tls:
35+
secret: cafe-secret
3436
upstreams:
3537
- name: dns-app
3638
service: dns-service
@@ -82,6 +84,7 @@ The TransportServer resource defines load balancing configuration for TCP, UDP,
8284
| ---| ---| ---| --- |
8385
|``listener`` | The listener on NGINX that will accept incoming connections/datagrams. | [listener](#listener) | Yes |
8486
|``host`` | The host (domain name) of the server. Must be a valid subdomain as defined in RFC 1123, such as ``my-app`` or ``hello.example.com``. Wildcard domains like ``*.example.com`` are not allowed. Required for TLS Passthrough load balancing. | ``string`` | No |
87+
|``tls`` | The TLS termination configuration. Not supported for TLS Passthrough load balancing. | [tls](#tls) | No |
8588
|``upstreams`` | A list of upstreams. | [[]upstream](#upstream) | Yes |
8689
|``upstreamParameters`` | The upstream parameters. | [upstreamParameters](#upstreamparameters) | No |
8790
|``action`` | The action to perform for a client connection/datagram. | [action](#action) | Yes |
@@ -110,6 +113,19 @@ listener:
110113
|``protocol`` | The protocol of the listener. | ``string`` | Yes |
111114
{{% /table %}}
112115

116+
### TLS
117+
118+
The tls field defines TLS configuration for a TransportServer. Please note the current implementation supports TLS termination on multiple ports, where each application owns a dedicated port - the Ingress Controller terminates TLS connections on each port, where each application uses its own cert/key, and routes connections to appropriate application (service) based on that incoming port (any TLS connection regardless of the SNI on a port will be routed to the application that corresponds to that port). An example configuration is shown below:
119+
```yaml
120+
secret: cafe-secret
121+
```
122+
123+
{{% table %}}
124+
|Field | Description | Type | Required |
125+
| ---| ---| ---| --- |
126+
|``secret`` | The name of a secret with a TLS certificate and key. The secret must belong to the same namespace as the TransportServer. The secret must be of the type ``kubernetes.io/tls`` and contain keys named ``tls.crt`` and ``tls.key`` that contain the certificate and private key as described [here](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls). | ``string`` | No |
127+
{{% /table %}}
128+
113129
### Upstream
114130

115131
The upstream defines a destination for the TransportServer. For example:

internal/configs/transportserver.go

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ import (
44
"fmt"
55
"strings"
66

7+
api_v1 "k8s.io/api/core/v1"
8+
79
"github.com/nginxinc/kubernetes-ingress/internal/configs/version2"
10+
"github.com/nginxinc/kubernetes-ingress/internal/k8s/secrets"
811
conf_v1alpha1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1alpha1"
912
)
1013

@@ -18,6 +21,7 @@ type TransportServerEx struct {
1821
PodsByIP map[string]string
1922
ExternalNameSvcs map[string]bool
2023
DisableIPV6 bool
24+
SecretRefs map[string]*secrets.SecretReference
2125
}
2226

2327
func (tsEx *TransportServerEx) String() string {
@@ -38,14 +42,20 @@ func newUpstreamNamerForTransportServer(transportServer *conf_v1alpha1.Transport
3842

3943
// generateTransportServerConfig generates a full configuration for a TransportServer.
4044
func generateTransportServerConfig(transportServerEx *TransportServerEx, listenerPort int, isPlus bool, isResolverConfigured bool) (*version2.TransportServerConfig, Warnings) {
45+
warnings := newWarnings()
46+
4147
upstreamNamer := newUpstreamNamerForTransportServer(transportServerEx.TransportServer)
4248

43-
upstreams, warnings := generateStreamUpstreams(transportServerEx, upstreamNamer, isPlus, isResolverConfigured)
49+
upstreams, w := generateStreamUpstreams(transportServerEx, upstreamNamer, isPlus, isResolverConfigured)
50+
warnings.Add(w)
4451

4552
healthCheck, match := generateTransportServerHealthCheck(transportServerEx.TransportServer.Spec.Action.Pass,
4653
upstreamNamer.GetNameForUpstream(transportServerEx.TransportServer.Spec.Action.Pass),
4754
transportServerEx.TransportServer.Spec.Upstreams)
4855

56+
sslConfig, w := generateSSLConfig(transportServerEx.TransportServer, transportServerEx.TransportServer.Spec.TLS, transportServerEx.TransportServer.Namespace, transportServerEx.SecretRefs)
57+
warnings.Add(w)
58+
4959
var proxyRequests, proxyResponses *int
5060
var connectTimeout, nextUpstreamTimeout string
5161
var nextUpstream bool
@@ -97,6 +107,7 @@ func generateTransportServerConfig(transportServerEx *TransportServerEx, listene
97107
HealthCheck: healthCheck,
98108
ServerSnippets: serverSnippets,
99109
DisableIPV6: transportServerEx.DisableIPV6,
110+
SSL: sslConfig,
100111
},
101112
Match: match,
102113
Upstreams: upstreams,
@@ -112,6 +123,39 @@ func generateUnixSocket(transportServerEx *TransportServerEx) string {
112123
return ""
113124
}
114125

126+
func generateSSLConfig(ts *conf_v1alpha1.TransportServer, tls *conf_v1alpha1.TLS, namespace string, secretRefs map[string]*secrets.SecretReference) (*version2.StreamSSL, Warnings) {
127+
if tls == nil {
128+
return &version2.StreamSSL{Enabled: false}, nil
129+
}
130+
131+
warnings := newWarnings()
132+
sslEnabled := true
133+
134+
secretRef := secretRefs[fmt.Sprintf("%s/%s", namespace, tls.Secret)]
135+
var secretType api_v1.SecretType
136+
if secretRef.Secret != nil {
137+
secretType = secretRef.Secret.Type
138+
}
139+
name := secretRef.Path
140+
if secretType != "" && secretType != api_v1.SecretTypeTLS {
141+
errMsg := fmt.Sprintf("TLS secret %s is of a wrong type '%s', must be '%s'. SSL termination will not be enabled for this server.", tls.Secret, secretType, api_v1.SecretTypeTLS)
142+
warnings.AddWarning(ts, errMsg)
143+
sslEnabled = false
144+
} else if secretRef.Error != nil {
145+
errMsg := fmt.Sprintf("TLS secret %s is invalid: %v. SSL termination will not be enabled for this server.", tls.Secret, secretRef.Error)
146+
warnings.AddWarning(ts, errMsg)
147+
sslEnabled = false
148+
}
149+
150+
ssl := version2.StreamSSL{
151+
Enabled: sslEnabled,
152+
Certificate: name,
153+
CertificateKey: name,
154+
}
155+
156+
return &ssl, warnings
157+
}
158+
115159
func generateStreamUpstreams(transportServerEx *TransportServerEx, upstreamNamer *upstreamNamer, isPlus bool, isResolverConfigured bool) ([]version2.StreamUpstream, Warnings) {
116160
warnings := newWarnings()
117161
var upstreams []version2.StreamUpstream

0 commit comments

Comments
 (0)