mirror of https://github.com/kubernetes/kops.git
Merge pull request #4010 from gambol99/etcd_options
Etcd TLS Peer & CLient Auth
This commit is contained in:
commit
37d4b53d0d
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 != "" {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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{
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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!
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue