@@ -18,6 +18,7 @@ import (
18
18
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
19
19
"k8s.io/apimachinery/pkg/runtime"
20
20
"k8s.io/apimachinery/pkg/util/intstr"
21
+ "k8s.io/apimachinery/pkg/util/sets"
21
22
"k8s.io/apimachinery/pkg/util/validation"
22
23
"k8s.io/kubernetes/pkg/api/legacyscheme"
23
24
kapi "k8s.io/kubernetes/pkg/apis/core"
84
85
privkeyName = "router.pem"
85
86
privkeyPath = secretsPath + "/" + privkeyName
86
87
88
+ defaultMutualTLSAuth = "none"
89
+ clientCertConfigDir = "/etc/pki/tls/client-certs"
90
+ clientCertConfigCA = "ca.pem"
91
+ clientCertConfigCRL = "crl.pem"
92
+
87
93
defaultCertificatePath = path .Join (defaultCertificateDir , "tls.crt" )
88
94
)
89
95
@@ -229,6 +235,19 @@ type RouterConfig struct {
229
235
StrictSNI bool
230
236
231
237
Local bool
238
+
239
+ // MutualTLSAuth controls access to the router using a mutually agreed
240
+ // upon TLS authentication mechanism (ala client certificates).
241
+ // One of: required | optional | none - the default is none.
242
+ MutualTLSAuth string
243
+
244
+ // MutualTLSAuthCA contains the CA certificates that will be used
245
+ // to verify a client's certificate.
246
+ MutualTLSAuthCA string
247
+
248
+ // MutualTLSAuthCRL contains the certificate revocation list used to
249
+ // verify a client's certificate.
250
+ MutualTLSAuthCRL string
232
251
}
233
252
234
253
const (
@@ -257,6 +276,8 @@ func NewCmdRouter(f *clientcmd.Factory, parentName, name string, out, errout io.
257
276
StatsPort : defaultStatsPort ,
258
277
HostNetwork : true ,
259
278
HostPorts : true ,
279
+
280
+ MutualTLSAuth : defaultMutualTLSAuth ,
260
281
}
261
282
262
283
cmd := & cobra.Command {
@@ -309,15 +330,24 @@ func NewCmdRouter(f *clientcmd.Factory, parentName, name string, out, errout io.
309
330
cmd .Flags ().BoolVar (& cfg .StrictSNI , "strict-sni" , cfg .StrictSNI , "Use strict-sni bind processing (do not use default cert). Not supported for F5." )
310
331
cmd .Flags ().BoolVar (& cfg .Local , "local" , cfg .Local , "If true, do not contact the apiserver" )
311
332
333
+ cmd .Flags ().StringVar (& cfg .MutualTLSAuth , "mutual-tls-auth" , cfg .MutualTLSAuth , "Controls access to the router using mutually agreed upon TLS configuration (ala client certificates). You can choose one of 'required', 'optional', or 'none'. The default is none." )
334
+ cmd .Flags ().StringVar (& cfg .MutualTLSAuthCA , "mutual-tls-auth-ca" , cfg .MutualTLSAuthCA , "Optional path to a file containing one or more CA certificates used for mutual TLS authentication. The CA certificate[s] are used by the router to verify a client's certificate." )
335
+ cmd .Flags ().StringVar (& cfg .MutualTLSAuthCRL , "mutual-tls-auth-crl" , cfg .MutualTLSAuthCRL , "Optional path to a file containing the certificate revocation list used for mutual TLS authentication. The certificate revocation list is used by the router to verify a client's certificate." )
336
+
312
337
cfg .Action .BindForOutput (cmd .Flags ())
313
338
cmd .Flags ().String ("output-version" , "" , "The preferred API versions of the output objects" )
314
339
315
340
return cmd
316
341
}
317
342
343
+ // generateMutualTLSSecretName generates a mutual TLS auth secret name.
344
+ func generateMutualTLSSecretName (prefix string ) string {
345
+ return fmt .Sprintf ("%s-mutual-tls-auth" , prefix )
346
+ }
347
+
318
348
// generateSecretsConfig generates any Secret and Volume objects, such
319
349
// as SSH private keys, that are necessary for the router container.
320
- func generateSecretsConfig (cfg * RouterConfig , namespace string , defaultCert [] byte , certName string ) ([]* kapi.Secret , []kapi.Volume , []kapi.VolumeMount , error ) {
350
+ func generateSecretsConfig (cfg * RouterConfig , namespace string , certName string , defaultCert , mtlsAuthCA , mtlsAuthCRL [] byte ) ([]* kapi.Secret , []kapi.Volume , []kapi.VolumeMount , error ) {
321
351
var secrets []* kapi.Secret
322
352
var volumes []kapi.Volume
323
353
var mounts []kapi.VolumeMount
@@ -424,6 +454,42 @@ func generateSecretsConfig(cfg *RouterConfig, namespace string, defaultCert []by
424
454
}
425
455
mounts = append (mounts , mount )
426
456
457
+ mtlsSecretData := map [string ][]byte {}
458
+ if len (mtlsAuthCA ) > 0 {
459
+ mtlsSecretData [clientCertConfigCA ] = mtlsAuthCA
460
+ }
461
+ if len (mtlsAuthCRL ) > 0 {
462
+ mtlsSecretData [clientCertConfigCRL ] = mtlsAuthCRL
463
+ }
464
+
465
+ if len (mtlsSecretData ) > 0 {
466
+ secretName := generateMutualTLSSecretName (cfg .Name )
467
+ secret := & kapi.Secret {
468
+ ObjectMeta : metav1.ObjectMeta {
469
+ Name : secretName ,
470
+ },
471
+ Data : mtlsSecretData ,
472
+ }
473
+ secrets = append (secrets , secret )
474
+
475
+ volume := kapi.Volume {
476
+ Name : "mutual-tls-config" ,
477
+ VolumeSource : kapi.VolumeSource {
478
+ Secret : & kapi.SecretVolumeSource {
479
+ SecretName : secretName ,
480
+ },
481
+ },
482
+ }
483
+ volumes = append (volumes , volume )
484
+
485
+ mount := kapi.VolumeMount {
486
+ Name : volume .Name ,
487
+ ReadOnly : true ,
488
+ MountPath : clientCertConfigDir ,
489
+ }
490
+ mounts = append (mounts , mount )
491
+ }
492
+
427
493
return secrets , volumes , mounts , nil
428
494
}
429
495
@@ -576,6 +642,14 @@ func RunCmdRouter(f *clientcmd.Factory, cmd *cobra.Command, out, errout io.Write
576
642
if err != nil {
577
643
return fmt .Errorf ("error getting client: %v" , err )
578
644
}
645
+
646
+ if len (cfg .MutualTLSAuthCA ) > 0 || len (cfg .MutualTLSAuthCRL ) > 0 {
647
+ secretName := generateMutualTLSSecretName (cfg .Name )
648
+ if _ , err := kClient .Core ().Secrets (namespace ).Get (secretName , metav1.GetOptions {}); err == nil {
649
+ return fmt .Errorf ("router could not be created: mutual tls secret %q already exists" , secretName )
650
+ }
651
+ }
652
+
579
653
service , err := kClient .Core ().Services (namespace ).Get (name , metav1.GetOptions {})
580
654
if err != nil {
581
655
if ! generate {
@@ -628,6 +702,20 @@ func RunCmdRouter(f *clientcmd.Factory, cmd *cobra.Command, out, errout io.Write
628
702
return fmt .Errorf ("router could not be created; error reading default certificate file: %v" , err )
629
703
}
630
704
705
+ mtlsAuthOptions := []string {"required" , "optional" , "none" }
706
+ allowedMutualTLSAuthOptions := sets .NewString (mtlsAuthOptions ... )
707
+ if ! allowedMutualTLSAuthOptions .Has (cfg .MutualTLSAuth ) {
708
+ return fmt .Errorf ("invalid mutual tls auth option %v, expected one of %v" , cfg .MutualTLSAuth , mtlsAuthOptions )
709
+ }
710
+ mtlsAuthCA , err := fileutil .LoadData (cfg .MutualTLSAuthCA )
711
+ if err != nil {
712
+ return fmt .Errorf ("reading ca certificates for mutual tls auth: %v" , err )
713
+ }
714
+ mtlsAuthCRL , err := fileutil .LoadData (cfg .MutualTLSAuthCRL )
715
+ if err != nil {
716
+ return fmt .Errorf ("reading certificate revocation list for mutual tls auth: %v" , err )
717
+ }
718
+
631
719
if len (cfg .StatsPassword ) == 0 {
632
720
cfg .StatsPassword = generateStatsPassword ()
633
721
if ! cfg .Action .ShouldPrint () {
@@ -685,6 +773,17 @@ func RunCmdRouter(f *clientcmd.Factory, cmd *cobra.Command, out, errout io.Write
685
773
env ["ROUTER_METRICS_TLS_CERT_FILE" ] = "/etc/pki/tls/metrics/tls.crt"
686
774
env ["ROUTER_METRICS_TLS_KEY_FILE" ] = "/etc/pki/tls/metrics/tls.key"
687
775
}
776
+ mtlsAuth := strings .TrimSpace (cfg .MutualTLSAuth )
777
+ if len (mtlsAuth ) > 0 && mtlsAuth != defaultMutualTLSAuth {
778
+ env ["ROUTER_MUTUAL_TLS_AUTH" ] = cfg .MutualTLSAuth
779
+ if len (mtlsAuthCA ) > 0 {
780
+ env ["ROUTER_MUTUAL_TLS_AUTH_CA" ] = path .Join (clientCertConfigDir , clientCertConfigCA )
781
+ }
782
+ if len (mtlsAuthCRL ) > 0 {
783
+ env ["ROUTER_MUTUAL_TLS_AUTH_CRL" ] = path .Join (clientCertConfigDir , clientCertConfigCRL )
784
+ }
785
+ }
786
+
688
787
env .Add (secretEnv )
689
788
if len (defaultCert ) > 0 {
690
789
if cfg .SecretsAsEnv {
@@ -695,7 +794,7 @@ func RunCmdRouter(f *clientcmd.Factory, cmd *cobra.Command, out, errout io.Write
695
794
}
696
795
env .Add (app.Environment {"DEFAULT_CERTIFICATE_DIR" : defaultCertificateDir })
697
796
var certName = fmt .Sprintf ("%s-certs" , cfg .Name )
698
- secrets , volumes , mounts , err := generateSecretsConfig (cfg , namespace , defaultCert , certName )
797
+ secrets , volumes , mounts , err := generateSecretsConfig (cfg , namespace , certName , defaultCert , mtlsAuthCA , mtlsAuthCRL )
699
798
if err != nil {
700
799
return fmt .Errorf ("router could not be created: %v" , err )
701
800
}
0 commit comments