@@ -18,6 +18,7 @@ import (
1818 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1919 "k8s.io/apimachinery/pkg/runtime"
2020 "k8s.io/apimachinery/pkg/util/intstr"
21+ "k8s.io/apimachinery/pkg/util/sets"
2122 "k8s.io/apimachinery/pkg/util/validation"
2223 "k8s.io/kubernetes/pkg/api/legacyscheme"
2324 kapi "k8s.io/kubernetes/pkg/apis/core"
8485 privkeyName = "router.pem"
8586 privkeyPath = secretsPath + "/" + privkeyName
8687
88+ defaultMutualTLSAuth = "none"
89+ clientCertConfigDir = "/etc/pki/tls/client-certs"
90+ clientCertConfigCA = "ca.pem"
91+ clientCertConfigCRL = "crl.pem"
92+
8793 defaultCertificatePath = path .Join (defaultCertificateDir , "tls.crt" )
8894)
8995
@@ -229,6 +235,19 @@ type RouterConfig struct {
229235 StrictSNI bool
230236
231237 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
232251}
233252
234253const (
@@ -257,6 +276,8 @@ func NewCmdRouter(f *clientcmd.Factory, parentName, name string, out, errout io.
257276 StatsPort : defaultStatsPort ,
258277 HostNetwork : true ,
259278 HostPorts : true ,
279+
280+ MutualTLSAuth : defaultMutualTLSAuth ,
260281 }
261282
262283 cmd := & cobra.Command {
@@ -309,15 +330,24 @@ func NewCmdRouter(f *clientcmd.Factory, parentName, name string, out, errout io.
309330 cmd .Flags ().BoolVar (& cfg .StrictSNI , "strict-sni" , cfg .StrictSNI , "Use strict-sni bind processing (do not use default cert). Not supported for F5." )
310331 cmd .Flags ().BoolVar (& cfg .Local , "local" , cfg .Local , "If true, do not contact the apiserver" )
311332
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+
312337 cfg .Action .BindForOutput (cmd .Flags ())
313338 cmd .Flags ().String ("output-version" , "" , "The preferred API versions of the output objects" )
314339
315340 return cmd
316341}
317342
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+
318348// generateSecretsConfig generates any Secret and Volume objects, such
319349// 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 ) {
321351 var secrets []* kapi.Secret
322352 var volumes []kapi.Volume
323353 var mounts []kapi.VolumeMount
@@ -424,6 +454,42 @@ func generateSecretsConfig(cfg *RouterConfig, namespace string, defaultCert []by
424454 }
425455 mounts = append (mounts , mount )
426456
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+
427493 return secrets , volumes , mounts , nil
428494}
429495
@@ -576,6 +642,14 @@ func RunCmdRouter(f *clientcmd.Factory, cmd *cobra.Command, out, errout io.Write
576642 if err != nil {
577643 return fmt .Errorf ("error getting client: %v" , err )
578644 }
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+
579653 service , err := kClient .Core ().Services (namespace ).Get (name , metav1.GetOptions {})
580654 if err != nil {
581655 if ! generate {
@@ -628,6 +702,20 @@ func RunCmdRouter(f *clientcmd.Factory, cmd *cobra.Command, out, errout io.Write
628702 return fmt .Errorf ("router could not be created; error reading default certificate file: %v" , err )
629703 }
630704
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+
631719 if len (cfg .StatsPassword ) == 0 {
632720 cfg .StatsPassword = generateStatsPassword ()
633721 if ! cfg .Action .ShouldPrint () {
@@ -685,6 +773,17 @@ func RunCmdRouter(f *clientcmd.Factory, cmd *cobra.Command, out, errout io.Write
685773 env ["ROUTER_METRICS_TLS_CERT_FILE" ] = "/etc/pki/tls/metrics/tls.crt"
686774 env ["ROUTER_METRICS_TLS_KEY_FILE" ] = "/etc/pki/tls/metrics/tls.key"
687775 }
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+
688787 env .Add (secretEnv )
689788 if len (defaultCert ) > 0 {
690789 if cfg .SecretsAsEnv {
@@ -695,7 +794,7 @@ func RunCmdRouter(f *clientcmd.Factory, cmd *cobra.Command, out, errout io.Write
695794 }
696795 env .Add (app.Environment {"DEFAULT_CERTIFICATE_DIR" : defaultCertificateDir })
697796 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 )
699798 if err != nil {
700799 return fmt .Errorf ("router could not be created: %v" , err )
701800 }
0 commit comments