123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176 |
- package libtrust
- import (
- "crypto/tls"
- "crypto/x509"
- "fmt"
- "io/ioutil"
- "net"
- "os"
- "path"
- "sync"
- )
- // ClientKeyManager manages client keys on the filesystem
- type ClientKeyManager struct {
- key PrivateKey
- clientFile string
- clientDir string
- clientLock sync.RWMutex
- clients []PublicKey
- configLock sync.Mutex
- configs []*tls.Config
- }
- // NewClientKeyManager loads a new manager from a set of key files
- // and managed by the given private key.
- func NewClientKeyManager(trustKey PrivateKey, clientFile, clientDir string) (*ClientKeyManager, error) {
- m := &ClientKeyManager{
- key: trustKey,
- clientFile: clientFile,
- clientDir: clientDir,
- }
- if err := m.loadKeys(); err != nil {
- return nil, err
- }
- // TODO Start watching file and directory
- return m, nil
- }
- func (c *ClientKeyManager) loadKeys() (err error) {
- // Load authorized keys file
- var clients []PublicKey
- if c.clientFile != "" {
- clients, err = LoadKeySetFile(c.clientFile)
- if err != nil {
- return fmt.Errorf("unable to load authorized keys: %s", err)
- }
- }
- // Add clients from authorized keys directory
- files, err := ioutil.ReadDir(c.clientDir)
- if err != nil && !os.IsNotExist(err) {
- return fmt.Errorf("unable to open authorized keys directory: %s", err)
- }
- for _, f := range files {
- if !f.IsDir() {
- publicKey, err := LoadPublicKeyFile(path.Join(c.clientDir, f.Name()))
- if err != nil {
- return fmt.Errorf("unable to load authorized key file: %s", err)
- }
- clients = append(clients, publicKey)
- }
- }
- c.clientLock.Lock()
- c.clients = clients
- c.clientLock.Unlock()
- return nil
- }
- // RegisterTLSConfig registers a tls configuration to manager
- // such that any changes to the keys may be reflected in
- // the tls client CA pool
- func (c *ClientKeyManager) RegisterTLSConfig(tlsConfig *tls.Config) error {
- c.clientLock.RLock()
- certPool, err := GenerateCACertPool(c.key, c.clients)
- if err != nil {
- return fmt.Errorf("CA pool generation error: %s", err)
- }
- c.clientLock.RUnlock()
- tlsConfig.ClientCAs = certPool
- c.configLock.Lock()
- c.configs = append(c.configs, tlsConfig)
- c.configLock.Unlock()
- return nil
- }
- // NewIdentityAuthTLSConfig creates a tls.Config for the server to use for
- // libtrust identity authentication for the domain specified
- func NewIdentityAuthTLSConfig(trustKey PrivateKey, clients *ClientKeyManager, addr string, domain string) (*tls.Config, error) {
- tlsConfig := newTLSConfig()
- tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
- if err := clients.RegisterTLSConfig(tlsConfig); err != nil {
- return nil, err
- }
- // Generate cert
- ips, domains, err := parseAddr(addr)
- if err != nil {
- return nil, err
- }
- // add domain that it expects clients to use
- domains = append(domains, domain)
- x509Cert, err := GenerateSelfSignedServerCert(trustKey, domains, ips)
- if err != nil {
- return nil, fmt.Errorf("certificate generation error: %s", err)
- }
- tlsConfig.Certificates = []tls.Certificate{{
- Certificate: [][]byte{x509Cert.Raw},
- PrivateKey: trustKey.CryptoPrivateKey(),
- Leaf: x509Cert,
- }}
- return tlsConfig, nil
- }
- // NewCertAuthTLSConfig creates a tls.Config for the server to use for
- // certificate authentication
- func NewCertAuthTLSConfig(caPath, certPath, keyPath string) (*tls.Config, error) {
- tlsConfig := newTLSConfig()
- cert, err := tls.LoadX509KeyPair(certPath, keyPath)
- if err != nil {
- return nil, fmt.Errorf("Couldn't load X509 key pair (%s, %s): %s. Key encrypted?", certPath, keyPath, err)
- }
- tlsConfig.Certificates = []tls.Certificate{cert}
- // Verify client certificates against a CA?
- if caPath != "" {
- certPool := x509.NewCertPool()
- file, err := ioutil.ReadFile(caPath)
- if err != nil {
- return nil, fmt.Errorf("Couldn't read CA certificate: %s", err)
- }
- certPool.AppendCertsFromPEM(file)
- tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
- tlsConfig.ClientCAs = certPool
- }
- return tlsConfig, nil
- }
- func newTLSConfig() *tls.Config {
- return &tls.Config{
- NextProtos: []string{"http/1.1"},
- // Avoid fallback on insecure SSL protocols
- MinVersion: tls.VersionTLS10,
- }
- }
- // parseAddr parses an address into an array of IPs and domains
- func parseAddr(addr string) ([]net.IP, []string, error) {
- host, _, err := net.SplitHostPort(addr)
- if err != nil {
- return nil, nil, err
- }
- var domains []string
- var ips []net.IP
- ip := net.ParseIP(host)
- if ip != nil {
- ips = []net.IP{ip}
- } else {
- domains = []string{host}
- }
- return ips, domains, nil
- }
|