mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
refactor(tls): clarify & simplify tls configuration
This commit is contained in:
parent
68305181f9
commit
63fa35c99f
@ -393,24 +393,16 @@ func (c *Config) Reset() error {
|
|||||||
|
|
||||||
// Sanitize cleans the input fields.
|
// Sanitize cleans the input fields.
|
||||||
func (c *Config) Sanitize() error {
|
func (c *Config) Sanitize() error {
|
||||||
tlsConfig, err := c.TLSConfig()
|
var err error
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
peerTlsConfig, err := c.PeerTLSConfig()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sanitize the URLs first.
|
// Sanitize the URLs first.
|
||||||
if c.Addr, err = sanitizeURL(c.Addr, tlsConfig.Scheme); err != nil {
|
if c.Addr, err = sanitizeURL(c.Addr, c.EtcdTLSInfo().Scheme()); err != nil {
|
||||||
return fmt.Errorf("Advertised URL: %s", err)
|
return fmt.Errorf("Advertised URL: %s", err)
|
||||||
}
|
}
|
||||||
if c.BindAddr, err = sanitizeBindAddr(c.BindAddr, c.Addr); err != nil {
|
if c.BindAddr, err = sanitizeBindAddr(c.BindAddr, c.Addr); err != nil {
|
||||||
return fmt.Errorf("Listen Host: %s", err)
|
return fmt.Errorf("Listen Host: %s", err)
|
||||||
}
|
}
|
||||||
if c.Peer.Addr, err = sanitizeURL(c.Peer.Addr, peerTlsConfig.Scheme); err != nil {
|
if c.Peer.Addr, err = sanitizeURL(c.Peer.Addr, c.PeerTLSInfo().Scheme()); err != nil {
|
||||||
return fmt.Errorf("Peer Advertised URL: %s", err)
|
return fmt.Errorf("Peer Advertised URL: %s", err)
|
||||||
}
|
}
|
||||||
if c.Peer.BindAddr, err = sanitizeBindAddr(c.Peer.BindAddr, c.Peer.Addr); err != nil {
|
if c.Peer.BindAddr, err = sanitizeBindAddr(c.Peer.BindAddr, c.Peer.Addr); err != nil {
|
||||||
@ -430,34 +422,24 @@ func (c *Config) Sanitize() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TLSInfo retrieves a TLSInfo object for the client server.
|
// EtcdTLSInfo retrieves a TLSInfo object for the etcd server
|
||||||
func (c *Config) TLSInfo() server.TLSInfo {
|
func (c *Config) EtcdTLSInfo() server.TLSInfo {
|
||||||
return server.TLSInfo{
|
return server.TLSInfo{
|
||||||
CAFile: c.CAFile,
|
CAFile: c.CAFile,
|
||||||
CertFile: c.CertFile,
|
CertFile: c.CertFile,
|
||||||
KeyFile: c.KeyFile,
|
KeyFile: c.KeyFile,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClientTLSConfig generates the TLS configuration for the client server.
|
// PeerRaftInfo retrieves a TLSInfo object for the peer server.
|
||||||
func (c *Config) TLSConfig() (server.TLSConfig, error) {
|
|
||||||
return c.TLSInfo().Config()
|
|
||||||
}
|
|
||||||
|
|
||||||
// PeerTLSInfo retrieves a TLSInfo object for the peer server.
|
|
||||||
func (c *Config) PeerTLSInfo() server.TLSInfo {
|
func (c *Config) PeerTLSInfo() server.TLSInfo {
|
||||||
return server.TLSInfo{
|
return server.TLSInfo{
|
||||||
CAFile: c.Peer.CAFile,
|
CAFile: c.Peer.CAFile,
|
||||||
CertFile: c.Peer.CertFile,
|
CertFile: c.Peer.CertFile,
|
||||||
KeyFile: c.Peer.KeyFile,
|
KeyFile: c.Peer.KeyFile,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PeerTLSConfig generates the TLS configuration for the peer server.
|
|
||||||
func (c *Config) PeerTLSConfig() (server.TLSConfig, error) {
|
|
||||||
return c.PeerTLSInfo().Config()
|
|
||||||
}
|
|
||||||
|
|
||||||
// MetricsBucketName generates the name that should be used for a
|
// MetricsBucketName generates the name that should be used for a
|
||||||
// corresponding MetricsBucket object
|
// corresponding MetricsBucket object
|
||||||
func (c *Config) MetricsBucketName() string {
|
func (c *Config) MetricsBucketName() string {
|
||||||
|
62
etcd.go
62
etcd.go
@ -79,16 +79,6 @@ func main() {
|
|||||||
log.Warnf("All cached configuration is now ignored. The file %s can be removed.", info)
|
log.Warnf("All cached configuration is now ignored. The file %s can be removed.", info)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve TLS configuration.
|
|
||||||
tlsConfig, err := config.TLSInfo().Config()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Client TLS:", err)
|
|
||||||
}
|
|
||||||
peerTLSConfig, err := config.PeerTLSInfo().Config()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Peer TLS:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var mbName string
|
var mbName string
|
||||||
if config.Trace() {
|
if config.Trace() {
|
||||||
mbName = config.MetricsBucketName()
|
mbName = config.MetricsBucketName()
|
||||||
@ -124,10 +114,10 @@ func main() {
|
|||||||
dialTimeout := (3 * heartbeatTimeout) + electionTimeout
|
dialTimeout := (3 * heartbeatTimeout) + electionTimeout
|
||||||
responseHeaderTimeout := (3 * heartbeatTimeout) + electionTimeout
|
responseHeaderTimeout := (3 * heartbeatTimeout) + electionTimeout
|
||||||
|
|
||||||
// Create peer server.
|
// Create peer server
|
||||||
psConfig := server.PeerServerConfig{
|
psConfig := server.PeerServerConfig{
|
||||||
Name: config.Name,
|
Name: config.Name,
|
||||||
Scheme: peerTLSConfig.Scheme,
|
Scheme: config.PeerTLSInfo().Scheme(),
|
||||||
URL: config.Peer.Addr,
|
URL: config.Peer.Addr,
|
||||||
SnapshotCount: config.SnapshotCount,
|
SnapshotCount: config.SnapshotCount,
|
||||||
MaxClusterSize: config.MaxClusterSize,
|
MaxClusterSize: config.MaxClusterSize,
|
||||||
@ -137,18 +127,30 @@ func main() {
|
|||||||
|
|
||||||
var psListener net.Listener
|
var psListener net.Listener
|
||||||
if psConfig.Scheme == "https" {
|
if psConfig.Scheme == "https" {
|
||||||
psListener, err = server.NewTLSListener(&tlsConfig.Server, config.Peer.BindAddr, config.PeerTLSInfo().CertFile, config.PeerTLSInfo().KeyFile)
|
peerServerTLSConfig, err := config.PeerTLSInfo().ServerConfig()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("peer server TLS error: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
psListener, err = server.NewTLSListener(config.Peer.BindAddr, peerServerTLSConfig)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Failed to create peer listener: ", err)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
psListener, err = server.NewListener(config.Peer.BindAddr)
|
psListener, err = server.NewListener(config.Peer.BindAddr)
|
||||||
}
|
if err != nil {
|
||||||
if err != nil {
|
log.Fatal("Failed to create peer listener: ", err)
|
||||||
panic(err)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create Raft transporter and server
|
// Create raft transporter and server
|
||||||
raftTransporter := server.NewTransporter(followersStats, serverStats, registry, heartbeatTimeout, dialTimeout, responseHeaderTimeout)
|
raftTransporter := server.NewTransporter(followersStats, serverStats, registry, heartbeatTimeout, dialTimeout, responseHeaderTimeout)
|
||||||
if psConfig.Scheme == "https" {
|
if psConfig.Scheme == "https" {
|
||||||
raftTransporter.SetTLSConfig(peerTLSConfig.Client)
|
raftClientTLSConfig, err := config.PeerTLSInfo().ClientConfig()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("raft client TLS error: ", err)
|
||||||
|
}
|
||||||
|
raftTransporter.SetTLSConfig(*raftClientTLSConfig)
|
||||||
}
|
}
|
||||||
raftServer, err := raft.NewServer(config.Name, config.DataDir, raftTransporter, store, ps, "")
|
raftServer, err := raft.NewServer(config.Name, config.DataDir, raftTransporter, store, ps, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -158,7 +160,7 @@ func main() {
|
|||||||
raftServer.SetHeartbeatTimeout(heartbeatTimeout)
|
raftServer.SetHeartbeatTimeout(heartbeatTimeout)
|
||||||
ps.SetRaftServer(raftServer)
|
ps.SetRaftServer(raftServer)
|
||||||
|
|
||||||
// Create client server.
|
// Create etcd server
|
||||||
s := server.New(config.Name, config.Addr, ps, registry, store, &mb)
|
s := server.New(config.Name, config.Addr, ps, registry, store, &mb)
|
||||||
|
|
||||||
if config.Trace() {
|
if config.Trace() {
|
||||||
@ -166,22 +168,28 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var sListener net.Listener
|
var sListener net.Listener
|
||||||
if tlsConfig.Scheme == "https" {
|
if config.EtcdTLSInfo().Scheme() == "https" {
|
||||||
sListener, err = server.NewTLSListener(&tlsConfig.Server, config.BindAddr, config.TLSInfo().CertFile, config.TLSInfo().KeyFile)
|
etcdServerTLSConfig, err := config.EtcdTLSInfo().ServerConfig()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("etcd TLS error: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sListener, err = server.NewTLSListener(config.BindAddr, etcdServerTLSConfig)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Failed to create TLS etcd listener: ", err)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
sListener, err = server.NewListener(config.BindAddr)
|
sListener, err = server.NewListener(config.BindAddr)
|
||||||
}
|
if err != nil {
|
||||||
if err != nil {
|
log.Fatal("Failed to create etcd listener: ", err)
|
||||||
panic(err)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ps.SetServer(s)
|
ps.SetServer(s)
|
||||||
|
|
||||||
ps.Start(config.Snapshot, config.Peers)
|
ps.Start(config.Snapshot, config.Peers)
|
||||||
|
|
||||||
// Run peer server in separate thread while the client server blocks.
|
|
||||||
go func() {
|
go func() {
|
||||||
log.Infof("raft server [name %s, listen on %s, advertised url %s]", ps.Config.Name, psListener.Addr(), ps.Config.URL)
|
log.Infof("peer server [name %s, listen on %s, advertised url %s]", ps.Config.Name, psListener.Addr(), ps.Config.URL)
|
||||||
sHTTP := &ehttp.CORSHandler{ps.HTTPHandler(), corsInfo}
|
sHTTP := &ehttp.CORSHandler{ps.HTTPHandler(), corsInfo}
|
||||||
log.Fatal(http.Serve(psListener, sHTTP))
|
log.Fatal(http.Serve(psListener, sHTTP))
|
||||||
}()
|
}()
|
||||||
|
@ -16,28 +16,15 @@ func NewListener(addr string) (net.Listener, error) {
|
|||||||
return l, nil
|
return l, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTLSListener(config *tls.Config, addr, certFile, keyFile string) (net.Listener, error) {
|
func NewTLSListener(addr string, cfg *tls.Config) (net.Listener, error) {
|
||||||
if addr == "" {
|
if addr == "" {
|
||||||
addr = ":https"
|
addr = ":https"
|
||||||
}
|
}
|
||||||
|
|
||||||
if config == nil {
|
|
||||||
config = &tls.Config{}
|
|
||||||
}
|
|
||||||
|
|
||||||
config.NextProtos = []string{"http/1.1"}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
config.Certificates = make([]tls.Certificate, 1)
|
|
||||||
config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
conn, err := net.Listen("tcp", addr)
|
conn, err := net.Listen("tcp", addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return tls.NewListener(conn, config), nil
|
return tls.NewListener(conn, cfg), nil
|
||||||
}
|
}
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TLSConfig holds the TLS configuration.
|
|
||||||
type TLSConfig struct {
|
|
||||||
Scheme string // http or https
|
|
||||||
Server tls.Config // Used by the Raft or etcd Server transporter.
|
|
||||||
Client tls.Config // Used by the Raft peer client.
|
|
||||||
}
|
|
@ -15,62 +15,88 @@ type TLSInfo struct {
|
|||||||
CAFile string `json:"CAFile"`
|
CAFile string `json:"CAFile"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generates a TLS configuration from the given files.
|
func (info TLSInfo) Scheme() string {
|
||||||
func (info TLSInfo) Config() (TLSConfig, error) {
|
if info.KeyFile != "" && info.CertFile != "" {
|
||||||
var t TLSConfig
|
return "https"
|
||||||
t.Scheme = "http"
|
} else {
|
||||||
|
return "http"
|
||||||
// If the user do not specify key file, cert file and CA file, the type will be HTTP
|
|
||||||
if info.KeyFile == "" && info.CertFile == "" && info.CAFile == "" {
|
|
||||||
return t, nil
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generates a tls.Config object for a server from the given files.
|
||||||
|
func (info TLSInfo) ServerConfig() (*tls.Config, error) {
|
||||||
// Both the key and cert must be present.
|
// Both the key and cert must be present.
|
||||||
if info.KeyFile == "" || info.CertFile == "" {
|
if info.KeyFile == "" || info.CertFile == "" {
|
||||||
return t, fmt.Errorf("KeyFile and CertFile must both be present[key: %v, cert: %v]", info.KeyFile, info.CertFile)
|
return nil, fmt.Errorf("KeyFile and CertFile must both be present[key: %v, cert: %v]", info.KeyFile, info.CertFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
var cfg tls.Config
|
||||||
|
|
||||||
|
tlsCert, err := tls.LoadX509KeyPair(info.CertFile, info.KeyFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.Certificates = []tls.Certificate{tlsCert}
|
||||||
|
|
||||||
|
if info.CAFile != "" {
|
||||||
|
cfg.ClientAuth = tls.RequireAndVerifyClientCert
|
||||||
|
cp, err := newCertPool(info.CAFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.RootCAs = cp
|
||||||
|
cfg.ClientCAs = cp
|
||||||
|
} else {
|
||||||
|
cfg.ClientAuth = tls.NoClientCert
|
||||||
|
}
|
||||||
|
|
||||||
|
return &cfg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generates a tls.Config object for a client from the given files.
|
||||||
|
func (info TLSInfo) ClientConfig() (*tls.Config, error) {
|
||||||
|
var cfg tls.Config
|
||||||
|
|
||||||
|
if info.KeyFile == "" || info.CertFile == "" {
|
||||||
|
return &cfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
tlsCert, err := tls.LoadX509KeyPair(info.CertFile, info.KeyFile)
|
tlsCert, err := tls.LoadX509KeyPair(info.CertFile, info.KeyFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return t, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Scheme = "https"
|
cfg.Certificates = []tls.Certificate{tlsCert}
|
||||||
t.Server.ClientAuth, t.Server.ClientCAs, err = newCertPool(info.CAFile)
|
|
||||||
if err != nil {
|
if info.CAFile != "" {
|
||||||
return t, err
|
cp, err := newCertPool(info.CAFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.RootCAs = cp
|
||||||
}
|
}
|
||||||
|
|
||||||
// The client should trust the RootCA that the Server uses since
|
return &cfg, nil
|
||||||
// everyone is a peer in the network.
|
|
||||||
t.Client.Certificates = []tls.Certificate{tlsCert}
|
|
||||||
t.Client.RootCAs = t.Server.ClientCAs
|
|
||||||
|
|
||||||
return t, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// newCertPool creates x509 certPool and corresponding Auth Type.
|
// newCertPool creates x509 certPool with provided CA file
|
||||||
// If the given CAfile is valid, add the cert into the pool and verify the clients'
|
func newCertPool(CAFile string) (*x509.CertPool, error) {
|
||||||
// certs against the cert in the pool.
|
|
||||||
// If the given CAfile is empty, do not verify the clients' cert.
|
|
||||||
// If the given CAfile is not valid, fatal.
|
|
||||||
func newCertPool(CAFile string) (tls.ClientAuthType, *x509.CertPool, error) {
|
|
||||||
if CAFile == "" {
|
|
||||||
return tls.NoClientCert, nil, nil
|
|
||||||
}
|
|
||||||
pemByte, err := ioutil.ReadFile(CAFile)
|
pemByte, err := ioutil.ReadFile(CAFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
block, pemByte := pem.Decode(pemByte)
|
block, pemByte := pem.Decode(pemByte)
|
||||||
cert, err := x509.ParseCertificate(block.Bytes)
|
cert, err := x509.ParseCertificate(block.Bytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
certPool := x509.NewCertPool()
|
certPool := x509.NewCertPool()
|
||||||
certPool.AddCert(cert)
|
certPool.AddCert(cert)
|
||||||
|
|
||||||
return tls.RequireAndVerifyClientCert, certPool, nil
|
return certPool, nil
|
||||||
}
|
}
|
||||||
|
@ -162,6 +162,8 @@ func startServer(extra []string) (*os.Process, error) {
|
|||||||
cmd := []string{"etcd", "-f", "-data-dir=/tmp/node1", "-name=node1"}
|
cmd := []string{"etcd", "-f", "-data-dir=/tmp/node1", "-name=node1"}
|
||||||
cmd = append(cmd, extra...)
|
cmd = append(cmd, extra...)
|
||||||
|
|
||||||
|
println(strings.Join(cmd, " "))
|
||||||
|
|
||||||
return os.StartProcess(EtcdBinPath, cmd, procAttr)
|
return os.StartProcess(EtcdBinPath, cmd, procAttr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user