Peter Kurfer
49e58ac2e4
- move to Gitlab - make code better testable - create app abstraction for server - cleanup
165 lines
3.8 KiB
Go
165 lines
3.8 KiB
Go
package cert
|
|
|
|
import (
|
|
"crypto/ecdsa"
|
|
"crypto/elliptic"
|
|
"crypto/rand"
|
|
"crypto/tls"
|
|
"crypto/x509"
|
|
"net"
|
|
|
|
"gitlab.com/inetmock/inetmock/pkg/config"
|
|
"gitlab.com/inetmock/inetmock/pkg/logging"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
const (
|
|
ipv4Loopback = "127.0.0.1"
|
|
)
|
|
|
|
var (
|
|
defaultKeyProvider = func(options config.CertOptions) func() (key interface{}, err error) {
|
|
return func() (key interface{}, err error) {
|
|
return privateKeyForCurve(options)
|
|
}
|
|
}
|
|
)
|
|
|
|
type KeyProvider func() (key interface{}, err error)
|
|
|
|
type Store interface {
|
|
CACert() *tls.Certificate
|
|
GetCertificate(serverName string, ip string) (*tls.Certificate, error)
|
|
TLSConfig() *tls.Config
|
|
}
|
|
|
|
func NewDefaultStore(
|
|
config config.Config,
|
|
logger logging.Logger,
|
|
) (Store, error) {
|
|
timeSource := NewTimeSource()
|
|
return NewStore(
|
|
config.TLSConfig(),
|
|
NewFileSystemCache(config.TLSConfig().CertCachePath, timeSource),
|
|
NewDefaultGenerator(config.TLSConfig()),
|
|
logger,
|
|
)
|
|
}
|
|
|
|
func NewStore(
|
|
options config.CertOptions,
|
|
cache Cache,
|
|
generator Generator,
|
|
logger logging.Logger,
|
|
) (Store, error) {
|
|
store := &store{
|
|
options: options,
|
|
cache: cache,
|
|
generator: generator,
|
|
logger: logger,
|
|
}
|
|
|
|
if err := store.loadCACert(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return store, nil
|
|
}
|
|
|
|
type store struct {
|
|
options config.CertOptions
|
|
caCert *tls.Certificate
|
|
cache Cache
|
|
timeSource TimeSource
|
|
generator Generator
|
|
logger logging.Logger
|
|
}
|
|
|
|
func (s *store) TLSConfig() *tls.Config {
|
|
rootCaPool := x509.NewCertPool()
|
|
rootCaPubKey, _ := x509.ParseCertificate(s.caCert.Certificate[0])
|
|
rootCaPool.AddCert(rootCaPubKey)
|
|
|
|
suites := make([]uint16, 0)
|
|
|
|
for _, suite := range tls.CipherSuites() {
|
|
suites = append(suites, suite.ID)
|
|
}
|
|
|
|
if s.options.IncludeInsecureCipherSuites {
|
|
for _, suite := range tls.InsecureCipherSuites() {
|
|
suites = append(suites, suite.ID)
|
|
}
|
|
}
|
|
|
|
return &tls.Config{
|
|
CipherSuites: suites,
|
|
MinVersion: s.options.MinTLSVersion.TLSVersion(),
|
|
CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256},
|
|
PreferServerCipherSuites: true,
|
|
GetCertificate: func(info *tls.ClientHelloInfo) (cert *tls.Certificate, err error) {
|
|
var localIp string
|
|
if localIp, err = extractIPFromAddress(info.Conn.LocalAddr().String()); err != nil {
|
|
localIp = ipv4Loopback
|
|
}
|
|
|
|
if cert, err = s.GetCertificate(info.ServerName, localIp); err != nil {
|
|
s.logger.Error(
|
|
"error while resolving certificate",
|
|
zap.String("serverName", info.ServerName),
|
|
zap.String("localAddr", localIp),
|
|
zap.Error(err),
|
|
)
|
|
}
|
|
|
|
return
|
|
},
|
|
RootCAs: rootCaPool,
|
|
}
|
|
}
|
|
|
|
func (s *store) loadCACert() (err error) {
|
|
pemCrt := NewPEM(nil)
|
|
if err = pemCrt.ReadFrom(s.options.RootCACert.PublicKeyPath, s.options.RootCACert.PrivateKeyPath); err != nil {
|
|
return
|
|
}
|
|
s.caCert = pemCrt.Cert()
|
|
return
|
|
}
|
|
|
|
func (s *store) CACert() *tls.Certificate {
|
|
return s.caCert
|
|
}
|
|
|
|
func (s *store) GetCertificate(serverName string, ip string) (cert *tls.Certificate, err error) {
|
|
if crt, ok := s.cache.Get(serverName); ok {
|
|
return crt, nil
|
|
}
|
|
|
|
if cert, err = s.generator.ServerCert(GenerationOptions{
|
|
CommonName: serverName,
|
|
DNSNames: []string{serverName},
|
|
IPAddresses: []net.IP{net.ParseIP(ip)},
|
|
}, s.caCert); err == nil {
|
|
s.cache.Put(cert)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func privateKeyForCurve(options config.CertOptions) (privateKey interface{}, err error) {
|
|
switch options.Curve {
|
|
case config.CurveTypeP224:
|
|
privateKey, err = ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
|
|
case config.CurveTypeP256:
|
|
privateKey, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
|
case config.CurveTypeP384:
|
|
privateKey, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
|
|
case config.CurveTypeP521:
|
|
privateKey, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
|
|
default:
|
|
privateKey, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
|
}
|
|
|
|
return
|
|
}
|