Skip to content

Commit 9952e6b

Browse files
committed
Move to a passthrough route to support mTLS
This change moves us to a passthrough route that can support the mTLS requirements of the request header identity provider. 1. Sync router certs from openshift-config-managed to openshift-authentication namespace 2. Mount (copied) router certs into deployment 3. SNI: wire each key, value pair in router certs secret to a domain and the mount path for the combined TLS cert and key data. Signed-off-by: Monis Khan <[email protected]>
1 parent 78dd53b commit 9952e6b

File tree

5 files changed

+66
-13
lines changed

5 files changed

+66
-13
lines changed

pkg/operator2/deployment.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ func (c *authOperator) getGeneration() int64 {
2828
func defaultDeployment(
2929
operatorConfig *operatorv1.Authentication,
3030
syncData *configSyncData,
31+
routerSecret *corev1.Secret,
3132
resourceVersions ...string,
3233
) *appsv1.Deployment {
3334
replicas := int32(1) // TODO configurable?
@@ -63,6 +64,12 @@ func defaultDeployment(
6364
path: serviceCAMount,
6465
keys: []string{serviceCAKey},
6566
},
67+
{
68+
name: routerCertsLocalName,
69+
configmap: false,
70+
path: routerCertsLocalMount,
71+
keys: sets.StringKeySet(routerSecret.Data).List(),
72+
},
6673
} {
6774
v, m := data.split()
6875
volumes = append(volumes, v)

pkg/operator2/oauth.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ func init() {
3434
func (c *authOperator) handleOAuthConfig(
3535
operatorConfig *operatorv1.Authentication,
3636
route *routev1.Route,
37+
routerSecret *corev1.Secret,
3738
service *corev1.Service,
3839
consoleConfig *configv1.Console,
3940
) (
@@ -100,13 +101,14 @@ func (c *authOperator) handleOAuthConfig(
100101
ServingInfo: configv1.ServingInfo{
101102
BindAddress: fmt.Sprintf("0.0.0.0:%d", containerPort),
102103
BindNetwork: "tcp4",
103-
// we have valid serving certs provided by service-ca so that we can use reencrypt routes
104+
// we have valid serving certs provided by service-ca
105+
// this is our main server cert which is used if SNI does not match
104106
CertInfo: configv1.CertInfo{
105107
CertFile: servingCertPathCert,
106108
KeyFile: servingCertPathKey,
107109
},
108110
ClientCA: "", // I think this can be left unset
109-
NamedCertificates: nil,
111+
NamedCertificates: routerSecretToSNI(routerSecret),
110112
MinTLSVersion: crypto.TLSVersionToNameOrDie(crypto.DefaultTLSVersion()),
111113
CipherSuites: crypto.CipherSuitesToNamesOrDie(crypto.DefaultCiphers()),
112114
},

pkg/operator2/operator.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ const (
6767
cliConfigMount = systemConfigPathConfigMaps + "/" + cliConfigNameAndKey
6868
cliConfigPath = cliConfigMount + "/" + cliConfigNameAndKey
6969

70+
routerCertsSharedName = "router-ca"
71+
routerCertsLocalName = systemConfigPrefix + "router-certs"
72+
routerCertsLocalMount = systemConfigPathSecrets + "/" + routerCertsLocalName
73+
7074
userConfigPath = "/var/config/user"
7175

7276
servicePort = 443
@@ -174,11 +178,11 @@ func (c *authOperator) handleSync(operatorConfig *operatorv1.Authentication) err
174178
// we get resource versions so that if either changes, we redeploy our payload
175179
resourceVersions := []string{operatorConfig.GetResourceVersion()}
176180

177-
route, err := c.handleRoute()
181+
route, routerSecret, err := c.handleRoute()
178182
if err != nil {
179183
return err
180184
}
181-
resourceVersions = append(resourceVersions, route.GetResourceVersion())
185+
resourceVersions = append(resourceVersions, route.GetResourceVersion(), routerSecret.GetResourceVersion())
182186

183187
serviceCA, err := c.handleServiceCA()
184188
if err != nil {
@@ -217,7 +221,7 @@ func (c *authOperator) handleSync(operatorConfig *operatorv1.Authentication) err
217221
consoleConfig := c.handleConsoleConfig()
218222
resourceVersions = append(resourceVersions, consoleConfig.GetResourceVersion())
219223

220-
oauthConfig, expectedCLIconfig, syncData, err := c.handleOAuthConfig(operatorConfig, route, service, consoleConfig)
224+
oauthConfig, expectedCLIconfig, syncData, err := c.handleOAuthConfig(operatorConfig, route, routerSecret, service, consoleConfig)
221225
if err != nil {
222226
return err
223227
}
@@ -241,6 +245,7 @@ func (c *authOperator) handleSync(operatorConfig *operatorv1.Authentication) err
241245
expectedDeployment := defaultDeployment(
242246
operatorConfig,
243247
syncData,
248+
routerSecret,
244249
resourceVersions...,
245250
)
246251
// TODO add support for spec.operandSpecs.unsupportedResourcePatches, like:

pkg/operator2/route.go

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,30 @@ package operator2
22

33
import (
44
"fmt"
5+
"strings"
56

67
"github.com/golang/glog"
78

9+
corev1 "k8s.io/api/core/v1"
810
"k8s.io/apimachinery/pkg/api/errors"
911
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1012
"k8s.io/apimachinery/pkg/util/intstr"
1113

14+
configv1 "github.com/openshift/api/config/v1"
1215
routev1 "github.com/openshift/api/route/v1"
1316
)
1417

15-
func (c *authOperator) handleRoute() (*routev1.Route, error) {
18+
func (c *authOperator) handleRoute() (*routev1.Route, *corev1.Secret, error) {
1619
route, err := c.route.Get(targetName, metav1.GetOptions{})
1720
if errors.IsNotFound(err) {
1821
route, err = c.route.Create(defaultRoute())
1922
}
2023
if err != nil {
21-
return nil, err
24+
return nil, nil, err
2225
}
2326

2427
if len(route.Spec.Host) == 0 {
25-
return nil, fmt.Errorf("route has no host: %#v", route)
28+
return nil, nil, fmt.Errorf("route has no host: %#v", route)
2629
}
2730

2831
if err := isValidRoute(route); err != nil {
@@ -32,14 +35,20 @@ func (c *authOperator) handleRoute() (*routev1.Route, error) {
3235
if err := c.route.Delete(route.Name, opts); err != nil && !errors.IsNotFound(err) {
3336
glog.Infof("failed to delete invalid route: %v", err)
3437
}
35-
return nil, err
38+
return nil, nil, err
3639
}
3740

38-
return route, nil
41+
routerSecret, err := c.secrets.Secrets(targetName).Get(routerCertsLocalName, metav1.GetOptions{})
42+
if err != nil {
43+
return nil, nil, err
44+
}
45+
46+
return route, routerSecret, nil
3947
}
4048

4149
func isValidRoute(route *routev1.Route) error {
4250
// TODO: return all errors at once
51+
// TODO error when fields that should be empty are set
4352

4453
// get the expected settings from the default route
4554
expectedRoute := defaultRoute()
@@ -64,7 +73,7 @@ func isValidRoute(route *routev1.Route) error {
6473
return fmt.Errorf("route contains wrong TLS termination - '%s' is required: %#v", expTLSTermination, route)
6574
}
6675

67-
if route.Spec.TLS.InsecureEdgeTerminationPolicy != routev1.InsecureEdgeTerminationPolicyRedirect {
76+
if route.Spec.TLS.InsecureEdgeTerminationPolicy != expInsecureEdgeTerminationPolicy {
6877
return fmt.Errorf("route contains wrong insecure termination policy - '%s' is required: %#v", expInsecureEdgeTerminationPolicy, route)
6978
}
7079

@@ -83,9 +92,32 @@ func defaultRoute() *routev1.Route {
8392
TargetPort: intstr.FromInt(containerPort),
8493
},
8594
TLS: &routev1.TLSConfig{
86-
Termination: routev1.TLSTerminationReencrypt,
95+
Termination: routev1.TLSTerminationPassthrough,
8796
InsecureEdgeTerminationPolicy: routev1.InsecureEdgeTerminationPolicyRedirect,
8897
},
8998
},
9099
}
91100
}
101+
102+
func routerSecretToSNI(routerSecret *corev1.Secret) []configv1.NamedCertificate {
103+
var out []configv1.NamedCertificate
104+
for key := range routerSecret.Data {
105+
out = append(out, configv1.NamedCertificate{
106+
Names: []string{routerSecretKeyToName(key)}, // workaround the lack of *.domain.tld as valid key
107+
CertInfo: configv1.CertInfo{ // the cert and key are appended together
108+
CertFile: routerCertsLocalMount + "/" + key,
109+
KeyFile: routerCertsLocalMount + "/" + key,
110+
},
111+
})
112+
}
113+
return out
114+
}
115+
116+
func routerSecretKeyToName(key string) string {
117+
switch {
118+
case strings.HasPrefix(key, "."): // .domain.tld means *.domain.tld
119+
return "*" + key
120+
default:
121+
return key // domain.tld is good as-is
122+
}
123+
}

pkg/operator2/starter.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ func RunOperator(ctx *controllercmd.ControllerContext) error {
112112
v1helpers.EnsureOperatorConfigExists(dynamicClient, []byte(resource), gvr)
113113
}
114114

115-
resourceSyncerInformers := v1helpers.NewKubeInformersForNamespaces(kubeClient, targetName, userConfigNamespace)
115+
resourceSyncerInformers := v1helpers.NewKubeInformersForNamespaces(kubeClient, targetName, userConfigNamespace, machineConfigNamespace)
116116

117117
operatorClient := &OperatorClient{
118118
authOperatorConfigInformers,
@@ -127,6 +127,13 @@ func RunOperator(ctx *controllercmd.ControllerContext) error {
127127
ctx.EventRecorder,
128128
)
129129

130+
if err := resourceSyncer.SyncSecret(
131+
resourcesynccontroller.ResourceLocation{Namespace: targetName, Name: routerCertsLocalName},
132+
resourcesynccontroller.ResourceLocation{Namespace: machineConfigNamespace, Name: routerCertsSharedName},
133+
); err != nil {
134+
return err
135+
}
136+
130137
operator := NewAuthenticationOperator(
131138
*operatorClient,
132139
kubeInformersNamespaced,

0 commit comments

Comments
 (0)