Merge pull request #4010 from gambol99/etcd_options

Etcd TLS Peer & CLient Auth
This commit is contained in:
k8s-ci-robot 2018-02-27 22:27:56 -08:00 committed by GitHub
commit 37d4b53d0d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 75 additions and 21 deletions

View File

@ -222,6 +222,23 @@ func (c *NodeupModelContext) UseEtcdTLS() bool {
return false
}
// UseTLSAuth checks the peer-auth is set in both cluster
// @NOTE: in retrospect i think we should have consolidated the common config in the wrapper struct; it
// feels wierd we set things like version, tls etc per cluster since they both have to be the same.
func (c *NodeupModelContext) UseTLSAuth() bool {
if !c.UseEtcdTLS() {
return false
}
for _, x := range c.Cluster.Spec.EtcdClusters {
if x.EnableTLSAuth {
return true
}
}
return false
}
// UsesCNI checks if the cluster has CNI configured
func (c *NodeupModelContext) UsesCNI() bool {
networking := c.Cluster.Spec.Networking

View File

@ -212,6 +212,7 @@ type ProtokubeFlags struct {
PeerTLSCaFile *string `json:"peer-ca,omitempty" flag:"peer-ca"`
PeerTLSCertFile *string `json:"peer-cert,omitempty" flag:"peer-cert"`
PeerTLSKeyFile *string `json:"peer-key,omitempty" flag:"peer-key"`
TLSAuth *bool `json:"tls-auth,omitempty" flag:"tls-auth"`
TLSCAFile *string `json:"tls-ca,omitempty" flag:"tls-ca"`
TLSCertFile *string `json:"tls-cert,omitempty" flag:"tls-cert"`
TLSKeyFile *string `json:"tls-key,omitempty" flag:"tls-key"`
@ -286,6 +287,10 @@ func (t *ProtokubeBuilder) ProtokubeFlags(k8sVersion semver.Version) (*Protokube
f.TLSCertFile = s(filepath.Join(t.PathSrvKubernetes(), "etcd.pem"))
f.TLSKeyFile = s(filepath.Join(t.PathSrvKubernetes(), "etcd-key.pem"))
}
if t.UseTLSAuth() {
enableAuth := true
f.TLSAuth = b(enableAuth)
}
zone := t.Cluster.Spec.DNSZone
if zone != "" {

View File

@ -308,6 +308,8 @@ type EtcdClusterSpec struct {
Members []*EtcdMemberSpec `json:"etcdMembers,omitempty"`
// EnableEtcdTLS indicates the etcd service should use TLS between peers and clients
EnableEtcdTLS bool `json:"enableEtcdTLS,omitempty"`
// EnableTLSAuth indicates client and peer TLS auth should be enforced
EnableTLSAuth bool `json:"enableTLSAuth,omitempty"`
// Version is the version of etcd to run i.e. 2.1.2, 3.0.17 etcd
Version string `json:"version,omitempty"`
// LeaderElectionTimeout is the time (in milliseconds) for an etcd leader election timeout

View File

@ -305,6 +305,8 @@ type EtcdClusterSpec struct {
Name string `json:"name,omitempty"`
// Members stores the configurations for each member of the cluster (including the data volume)
Members []*EtcdMemberSpec `json:"etcdMembers,omitempty"`
// EnableTLSAuth indicats client and peer TLS auth should be enforced
EnableTLSAuth bool `json:"enableTLSAuth,omitempty"`
// EnableEtcdTLS indicates the etcd service should use TLS between peers and clients
EnableEtcdTLS bool `json:"enableEtcdTLS,omitempty"`
// Version is the version of etcd to run i.e. 2.1.2, 3.0.17 etcd

View File

@ -1249,6 +1249,7 @@ func autoConvert_v1alpha1_EtcdClusterSpec_To_kops_EtcdClusterSpec(in *EtcdCluste
} else {
out.Members = nil
}
out.EnableTLSAuth = in.EnableTLSAuth
out.EnableEtcdTLS = in.EnableEtcdTLS
out.Version = in.Version
out.LeaderElectionTimeout = in.LeaderElectionTimeout
@ -1286,6 +1287,7 @@ func autoConvert_kops_EtcdClusterSpec_To_v1alpha1_EtcdClusterSpec(in *kops.EtcdC
out.Members = nil
}
out.EnableEtcdTLS = in.EnableEtcdTLS
out.EnableTLSAuth = in.EnableTLSAuth
out.Version = in.Version
out.LeaderElectionTimeout = in.LeaderElectionTimeout
out.HeartbeatInterval = in.HeartbeatInterval

View File

@ -305,6 +305,8 @@ type EtcdClusterSpec struct {
Members []*EtcdMemberSpec `json:"etcdMembers,omitempty"`
// EnableEtcdTLS indicates the etcd service should use TLS between peers and clients
EnableEtcdTLS bool `json:"enableEtcdTLS,omitempty"`
// EnableTLSAuth indicates client and peer TLS auth should be enforced
EnableTLSAuth bool `json:"enableTLSAuth,omitempty"`
// Version is the version of etcd to run i.e. 2.1.2, 3.0.17 etcd
Version string `json:"version,omitempty"`
// LeaderElectionTimeout is the time (in milliseconds) for an etcd leader election timeout

View File

@ -1351,6 +1351,7 @@ func autoConvert_v1alpha2_EtcdClusterSpec_To_kops_EtcdClusterSpec(in *EtcdCluste
out.Members = nil
}
out.EnableEtcdTLS = in.EnableEtcdTLS
out.EnableTLSAuth = in.EnableTLSAuth
out.Version = in.Version
out.LeaderElectionTimeout = in.LeaderElectionTimeout
out.HeartbeatInterval = in.HeartbeatInterval
@ -1387,6 +1388,7 @@ func autoConvert_kops_EtcdClusterSpec_To_v1alpha2_EtcdClusterSpec(in *kops.EtcdC
out.Members = nil
}
out.EnableEtcdTLS = in.EnableEtcdTLS
out.EnableTLSAuth = in.EnableTLSAuth
out.Version = in.Version
out.LeaderElectionTimeout = in.LeaderElectionTimeout
out.HeartbeatInterval = in.HeartbeatInterval

View File

@ -116,7 +116,7 @@ func (b *PKIModelBuilder) Build(c *fi.ModelBuilderContext) error {
Lifecycle: b.Lifecycle,
Name: fi.String("etcd"),
Subject: "cn=etcd",
Type: "server",
Type: "clientServer",
Signer: defaultCA,
})
c.AddTask(&fitasks.Keypair{

View File

@ -59,7 +59,7 @@ func main() {
// run is responsible for running the protokube service controller
func run() error {
var zones []string
var applyTaints, initializeRBAC, containerized, master bool
var applyTaints, initializeRBAC, containerized, master, tlsAuth bool
var cloud, clusterID, dnsServer, dnsProviderID, dnsInternalSuffix, gossipSecret, gossipListen string
var flagChannels, tlsCert, tlsKey, tlsCA, peerCert, peerKey, peerCA string
var etcdBackupImage, etcdBackupStore, etcdImageSource, etcdElectionTimeout, etcdHeartbeatInterval string
@ -77,6 +77,7 @@ func run() error {
flag.StringVar(&peerCA, "peer-ca", peerCA, "Path to a file containing the peer ca in PEM format")
flag.StringVar(&peerCert, "peer-cert", peerCert, "Path to a file containing the peer certificate")
flag.StringVar(&peerKey, "peer-key", peerKey, "Path to a file containing the private key for the peers")
flag.BoolVar(&tlsAuth, "tls-auth", tlsAuth, "Indicates the peers and client should enforce authentication via CA")
flag.StringVar(&tlsCA, "tls-ca", tlsCA, "Path to a file containing the ca for client certificates")
flag.StringVar(&tlsCert, "tls-cert", tlsCert, "Path to a file containing the certificate for etcd server")
flag.StringVar(&tlsKey, "tls-key", tlsKey, "Path to a file containing the private key for etcd server")
@ -311,6 +312,7 @@ func run() error {
PeerCA: peerCA,
PeerCert: peerCert,
PeerKey: peerKey,
TLSAuth: tlsAuth,
TLSCA: tlsCA,
TLSCert: tlsCert,
TLSKey: tlsKey,

View File

@ -61,6 +61,8 @@ type EtcdCluster struct {
Spec *etcd.EtcdClusterSpec
// VolumeMountPath is the mount path
VolumeMountPath string
// TLSAuth indicates we should enforce peer and client verification
TLSAuth bool
// TLSCA is the path to a client ca for etcd clients
TLSCA string
// TLSCert is the path to a client certificate for etcd
@ -104,24 +106,24 @@ func newEtcdController(kubeBoot *KubeBoot, v *Volume, spec *etcd.EtcdClusterSpec
}
cluster := &EtcdCluster{
// @TODO we need to deprecate this port and use 2379, but that would be a breaking change
CPURequest: resource.MustParse("100m"),
ClientPort: 4001,
ClusterName: "etcd-" + spec.ClusterKey,
CPURequest: resource.MustParse("100m"),
DataDirName: "data-" + spec.ClusterKey,
ElectionTimeout: kubeBoot.EtcdElectionTimeout,
HeartbeatInterval: kubeBoot.EtcdHeartbeatInterval,
ImageSource: kubeBoot.EtcdImageSource,
TLSCA: kubeBoot.TLSCA,
TLSCert: kubeBoot.TLSCert,
TLSKey: kubeBoot.TLSKey,
PeerCA: kubeBoot.PeerCA,
PeerCert: kubeBoot.PeerCert,
PeerKey: kubeBoot.PeerKey,
PeerPort: 2380,
PodName: "etcd-server-" + spec.ClusterKey,
Spec: spec,
TLSAuth: kubeBoot.TLSAuth,
TLSCA: kubeBoot.TLSCA,
TLSCert: kubeBoot.TLSCert,
TLSKey: kubeBoot.TLSKey,
VolumeMountPath: v.Mountpoint,
ElectionTimeout: kubeBoot.EtcdElectionTimeout,
HeartbeatInterval: kubeBoot.EtcdHeartbeatInterval,
}
// We used to build this through text files ... it turns out to just be more complicated than code!

View File

@ -202,6 +202,12 @@ func buildEtcdEnvironmentOptions(c *EtcdCluster) []v1.EnvVar {
if notEmpty(c.TLSKey) {
options = append(options, v1.EnvVar{Name: "ETCD_KEY_FILE", Value: c.TLSKey})
}
if c.isTLS() {
if c.TLSAuth {
options = append(options, v1.EnvVar{Name: "ETCD_CLIENT_CERT_AUTH", Value: "true"})
options = append(options, v1.EnvVar{Name: "ETCD_PEER_CLIENT_CERT_AUTH", Value: "true"})
}
}
// @step: generate the initial cluster
var hosts []string

View File

@ -58,6 +58,8 @@ type KubeBoot struct {
EtcdElectionTimeout string
// EtcdHeartbeatInterval is the heartbeat interval
EtcdHeartbeatInterval string
// TLSAuth indicates we should enforce peer and client verification
TLSAuth bool
// TLSCA is the path to a client ca for etcd
TLSCA string
// TLSCert is the path to a tls certificate for etcd
@ -73,7 +75,8 @@ type KubeBoot struct {
// Kubernetes is the context methods for kubernetes
Kubernetes *KubernetesContext
// Master indicates we are a master node
Master bool
Master bool
volumeMounter *VolumeMountController
etcdControllers map[string]*EtcdController
}

View File

@ -29,22 +29,28 @@ import (
)
var wellKnownCertificateTypes = map[string]string{
"client": "ExtKeyUsageClientAuth,KeyUsageDigitalSignature",
"server": "ExtKeyUsageServerAuth,KeyUsageDigitalSignature,KeyUsageKeyEncipherment",
"ca": "CA,KeyUsageCRLSign,KeyUsageCertSign",
"ca": "CA,KeyUsageCRLSign,KeyUsageCertSign",
"client": "ExtKeyUsageClientAuth,KeyUsageDigitalSignature",
"clientServer": "ExtKeyUsageServerAuth,KeyUsageDigitalSignature,ExtKeyUsageClientAuth,KeyUsageKeyEncipherment",
"server": "ExtKeyUsageServerAuth,KeyUsageDigitalSignature,KeyUsageKeyEncipherment",
}
//go:generate fitask -type=Keypair
type Keypair struct {
Name *string
Lifecycle *fi.Lifecycle
Subject string `json:"subject"`
Type string `json:"type"`
AlternateNames []string `json:"alternateNames"`
// Name is the name of the keypair
Name *string
// AlternateNames a list of alternative names for this certificate
AlternateNames []string `json:"alternateNames"`
// AlternateNameTasks is a collection of subtask
AlternateNameTasks []fi.Task `json:"alternateNameTasks"`
// Lifecycle is context for a task
Lifecycle *fi.Lifecycle
// Signer is the keypair to use to sign, for when we want to use an alternative CA
Signer *Keypair
// Subject is the cerificate subject
Subject string `json:"subject"`
// Type the type of certificate i.e. CA, server, client etc
Type string `json:"type"`
}
var _ fi.HasCheckExisting = &Keypair{}
@ -74,7 +80,6 @@ func (e *Keypair) Find(c *fi.Context) (*Keypair, error) {
if cert == nil {
return nil, nil
}
if key == nil {
return nil, fmt.Errorf("found cert in store, but did not find private key: %q", name)
}
@ -89,8 +94,8 @@ func (e *Keypair) Find(c *fi.Context) (*Keypair, error) {
actual := &Keypair{
Name: &name,
Subject: pkixNameToString(&cert.Subject),
AlternateNames: alternateNames,
Subject: pkixNameToString(&cert.Subject),
Type: buildTypeDescription(cert.Certificate),
}
@ -173,6 +178,8 @@ func (_ *Keypair) Render(c *fi.Context, a, e, changes *Keypair) error {
createCertificate = true
} else if changes.Subject != "" {
createCertificate = true
} else if changes.Type != "" {
createCertificate = true
} else {
glog.Warningf("Ignoring changes in key: %v", fi.DebugAsJsonString(changes))
}
@ -213,6 +220,7 @@ func (_ *Keypair) Render(c *fi.Context, a, e, changes *Keypair) error {
return nil
}
// BuildCertificateTemplate is responsible for constructing a certificate template
func (e *Keypair) BuildCertificateTemplate() (*x509.Certificate, error) {
template, err := buildCertificateTemplateForType(e.Type)
if err != nil {
@ -282,6 +290,7 @@ func buildCertificateTemplateForType(certificateType string) (*x509.Certificate,
return template, nil
}
// buildTypeDescription extracts the type based on the certificate extensions
func buildTypeDescription(cert *x509.Certificate) string {
var options []string