Merge pull request #41484 from deads2k/kubeadm-01-add-front-proxy

Automatic merge from submit-queue (batch tested with PRs 41505, 41484, 41544, 41514, 41022)

add front proxy to kubeadm created kube-apiservers

The front proxy authenticator configuration has been in a release or two.  It allows a front proxy (secured by mutual TLS auth) to provide user information for a request.  The kube-aggregator uses this to securely terminate authentication (has to terminate TLS and thus client-certs) and communicate user info to backing API servers.

Since the kube-apiserver always verifies the front-proxy via a client certificate, this isn't open for abuse unless you already have access to either the signing key or client cert which kubeadm creates locally.  If you got there, you already owned the box.  Therefore, this adds the authenticator unconditionally.

@luxas Are there e2e tests for `kubeadm`?
@liggitt @kubernetes/sig-auth-misc
This commit is contained in:
Kubernetes Submit Queue 2017-02-16 14:28:16 -08:00 committed by GitHub
commit 30ce5d7244
4 changed files with 95 additions and 0 deletions

View File

@ -38,6 +38,14 @@ const (
ServiceAccountPublicKeyName = "sa.pub"
ServiceAccountPrivateKeyName = "sa.key"
FrontProxyCACertAndKeyBaseName = "front-proxy-ca"
FrontProxyCACertName = "front-proxy-ca.crt"
FrontProxyCAKeyName = "front-proxy-ca.key"
FrontProxyClientCertAndKeyBaseName = "front-proxy-client"
FrontProxyClientCertName = "front-proxy-client.crt"
FrontProxyClientKeyName = "front-proxy-client.key"
// TODO: These constants should actually come from pkg/kubeapiserver/authorizer, but we can't vendor that package in now
// because of all the other sub-packages that would get vendored. To fix this, a pkg/kubeapiserver/authorizer/modes package
// or similar should exist that only has these constants; then we can vendor it.

View File

@ -318,6 +318,13 @@ func getAPIServerCommand(cfg *kubeadmapi.MasterConfiguration, selfHosted bool) [
"--allow-privileged",
"--storage-backend=etcd3",
"--kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname",
// add options to configure the front proxy. Without the generated client cert, this will never be useable
// so add it unconditionally with recommended values
"--requestheader-username-headers=X-Remote-User",
"--requestheader-group-headers=X-Remote-Group",
"--requestheader-extra-headers-prefix=X-Remote-Extra-",
"--requestheader-client-ca-file="+getCertFilePath(kubeadmconstants.FrontProxyCACertName),
"--requestheader-allowed-names=front-proxy-client",
)
if cfg.AuthorizationMode != "" {

View File

@ -383,6 +383,11 @@ func TestGetAPIServerCommand(t *testing.T) {
"--allow-privileged",
"--storage-backend=etcd3",
"--kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname",
"--requestheader-username-headers=X-Remote-User",
"--requestheader-group-headers=X-Remote-Group",
"--requestheader-extra-headers-prefix=X-Remote-Extra-",
"--requestheader-client-ca-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/front-proxy-ca.crt",
"--requestheader-allowed-names=front-proxy-client",
"--etcd-servers=http://127.0.0.1:2379",
},
},
@ -407,6 +412,11 @@ func TestGetAPIServerCommand(t *testing.T) {
"--allow-privileged",
"--storage-backend=etcd3",
"--kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname",
"--requestheader-username-headers=X-Remote-User",
"--requestheader-group-headers=X-Remote-Group",
"--requestheader-extra-headers-prefix=X-Remote-Extra-",
"--requestheader-client-ca-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/front-proxy-ca.crt",
"--requestheader-allowed-names=front-proxy-client",
"--advertise-address=foo",
"--etcd-servers=http://127.0.0.1:2379",
},
@ -433,6 +443,11 @@ func TestGetAPIServerCommand(t *testing.T) {
"--allow-privileged",
"--storage-backend=etcd3",
"--kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname",
"--requestheader-username-headers=X-Remote-User",
"--requestheader-group-headers=X-Remote-Group",
"--requestheader-extra-headers-prefix=X-Remote-Extra-",
"--requestheader-client-ca-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/front-proxy-ca.crt",
"--requestheader-allowed-names=front-proxy-client",
"--etcd-servers=http://127.0.0.1:2379",
"--etcd-certfile=fiz",
"--etcd-keyfile=faz",

View File

@ -192,6 +192,71 @@ func CreatePKIAssets(cfg *kubeadmapi.MasterConfiguration, pkiDir string) error {
fmt.Println("[certificates] Generated service account token signing public key.")
}
// front proxy CA and client certs are used to secure a front proxy authenticator which is used to assert identity
// without the client cert, you cannot make use of the front proxy and the kube-aggregator uses this connection
// so we generate and enable it unconditionally
// This is a separte CA, so that front proxy identities cannot hit the API and normal client certs cannot be used
// as front proxies.
var frontProxyCACert *x509.Certificate
var frontProxyCAKey *rsa.PrivateKey
// If at least one of them exists, we should try to load them
// In the case that only one exists, there will most likely be an error anyway
if pkiutil.CertOrKeyExist(pkiDir, kubeadmconstants.FrontProxyCACertAndKeyBaseName) {
// Try to load front-proxy-ca.crt and front-proxy-ca.key from the PKI directory
frontProxyCACert, frontProxyCAKey, err = pkiutil.TryLoadCertAndKeyFromDisk(pkiDir, kubeadmconstants.FrontProxyCACertAndKeyBaseName)
if err != nil || frontProxyCACert == nil || frontProxyCAKey == nil {
return fmt.Errorf("certificate and/or key existed but they could not be loaded properly")
}
// The certificate and key could be loaded, but the certificate is not a CA
if !frontProxyCACert.IsCA {
return fmt.Errorf("certificate and key could be loaded but the certificate is not a CA")
}
fmt.Println("[certificates] Using the existing front-proxy CA certificate and key.")
} else {
// The certificate and the key did NOT exist, let's generate them now
frontProxyCACert, frontProxyCAKey, err = pkiutil.NewCertificateAuthority()
if err != nil {
return fmt.Errorf("failure while generating front-proxy CA certificate and key [%v]", err)
}
if err = pkiutil.WriteCertAndKey(pkiDir, kubeadmconstants.FrontProxyCACertAndKeyBaseName, frontProxyCACert, frontProxyCAKey); err != nil {
return fmt.Errorf("failure while saving front-proxy CA certificate and key [%v]", err)
}
fmt.Println("[certificates] Generated front-proxy CA certificate and key.")
}
// At this point we have a front proxy CA signing key. We can use that create the front proxy client cert if
// it doesn't already exist.
// If at least one of them exists, we should try to load them
// In the case that only one exists, there will most likely be an error anyway
if pkiutil.CertOrKeyExist(pkiDir, kubeadmconstants.FrontProxyClientCertAndKeyBaseName) {
// Try to load apiserver-kubelet-client.crt and apiserver-kubelet-client.key from the PKI directory
apiCert, apiKey, err := pkiutil.TryLoadCertAndKeyFromDisk(pkiDir, kubeadmconstants.FrontProxyClientCertAndKeyBaseName)
if err != nil || apiCert == nil || apiKey == nil {
return fmt.Errorf("certificate and/or key existed but they could not be loaded properly")
}
fmt.Println("[certificates] Using the existing front-proxy client certificate and key.")
} else {
// The certificate and the key did NOT exist, let's generate them now
// TODO: Add a test case to verify that this cert has the x509.ExtKeyUsageClientAuth flag
config := certutil.Config{
CommonName: "front-proxy-client",
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
}
apiClientCert, apiClientKey, err := pkiutil.NewCertAndKey(frontProxyCACert, frontProxyCAKey, config)
if err != nil {
return fmt.Errorf("failure while creating front-proxy client key and certificate [%v]", err)
}
if err = pkiutil.WriteCertAndKey(pkiDir, kubeadmconstants.FrontProxyClientCertAndKeyBaseName, apiClientCert, apiClientKey); err != nil {
return fmt.Errorf("failure while saving front-proxy client certificate and key [%v]", err)
}
fmt.Println("[certificates] Generated front-proxy client certificate and key.")
}
fmt.Printf("[certificates] Valid certificates and keys now exist in %q\n", pkiDir)
return nil