...

Source file src/golang.conradwood.net/go-easyops/server/certificates.go

Documentation: golang.conradwood.net/go-easyops/server

     1  package server
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/tls"
     6  	"crypto/x509"
     7  	"encoding/pem"
     8  	"flag"
     9  	"fmt"
    10  	"strings"
    11  	"sync"
    12  	"time"
    13  
    14  	"golang.conradwood.net/apis/certmanager"
    15  	"golang.conradwood.net/go-easyops/authremote"
    16  )
    17  
    18  var (
    19  	BuiltinCert    []byte          // set on server startup
    20  	BuiltinKey     []byte          // set on server startup
    21  	BuiltinTLSCert tls.Certificate // set on server startup
    22  	certmap        = &certmapper{}
    23  	dynamic_certs  = flag.Bool("ge_retrieve_missing_https_certificates", true, "if true, retrieve missing https certificates")
    24  )
    25  
    26  type certmapper struct {
    27  	sync.Mutex
    28  	certmap map[string]*tls.Certificate // hostname->cert
    29  }
    30  
    31  func (cm *certmapper) ByHostname(hostname string) *tls.Certificate {
    32  	hm := strings.ToLower(hostname)
    33  	cm.Lock()
    34  	if cm.certmap == nil {
    35  		cm.certmap = make(map[string]*tls.Certificate)
    36  	}
    37  	tcert := cm.certmap[hm]
    38  	if tcert != nil {
    39  		cm.Unlock()
    40  		return tcert
    41  	}
    42  	cm.Unlock()
    43  	ctx := authremote.Context()
    44  	cert, err := certmanager.GetCertManagerClient().GetLocalCertificate(ctx, &certmanager.LocalCertificateRequest{Subject: hm})
    45  	if err != nil {
    46  		fmt.Printf("[go-easyops] certs failed to get cert for \"%s\": %s\n", hm, err)
    47  		return nil
    48  	}
    49  
    50  	cm.Lock()
    51  	tcert = cm.certmap[hm]
    52  	if tcert != nil {
    53  		cm.Unlock()
    54  		return tcert
    55  	}
    56  
    57  	// convert into tls certificate
    58  	tc, err := tls.X509KeyPair([]byte(cert.PemCertificate), []byte(cert.PemPrivateKey))
    59  	if err != nil {
    60  		fmt.Printf("[go-easyops] certs Failed to parse cert %s: %s\n", hm, err)
    61  		cm.Unlock()
    62  		return nil
    63  	}
    64  	// add the ca:
    65  	block, _ := pem.Decode([]byte(cert.PemCA))
    66  	if block == nil {
    67  		fmt.Printf("[go-easyops] certs certificate %s has no CA certificate\n", cert.Host)
    68  	} else {
    69  		xcert, xerr := x509.ParseCertificate(block.Bytes)
    70  		if xerr != nil {
    71  			fmt.Printf("[go-easyops] certs Cannot parse certificate %s: %s\n", cert.Host, err)
    72  			cm.Unlock()
    73  			return nil
    74  		}
    75  		now := time.Now()
    76  		if now.After(xcert.NotAfter) {
    77  			fmt.Printf("[go-easyops] certs certificate for \"%s\" expired on %v\n", hm, xcert.NotAfter)
    78  			cm.Unlock()
    79  			return nil
    80  		}
    81  
    82  		b := &bytes.Buffer{}
    83  		err = pem.Encode(b, block)
    84  		if err != nil {
    85  			cm.Unlock()
    86  			fmt.Printf("[go-easyops] certs cert for \"%s\" failed to encode: %s\n", hm, err)
    87  			return nil
    88  		}
    89  		tc.Certificate = append(tc.Certificate, block.Bytes)
    90  	}
    91  	fmt.Printf("[go-easyops] certs retrieved certificate for host \"%s\"\n", hm)
    92  	cm.certmap[hm] = &tc
    93  	cm.Unlock()
    94  
    95  	return &tc
    96  }
    97  
    98  func getcert(chi *tls.ClientHelloInfo) (*tls.Certificate, error) {
    99  	hostname := chi.ServerName
   100  	if hostname == "rfc-client" { // call from go-easyops
   101  		return &BuiltinTLSCert, nil
   102  	}
   103  	if !*dynamic_certs {
   104  		fmt.Printf("[go-easyops] certs not retrieving for host \"%s\", because flag -ge_retrieve_missing_https_certificates is false\n", hostname)
   105  		return &BuiltinTLSCert, nil
   106  	}
   107  	cert := certmap.ByHostname(hostname)
   108  	if cert != nil {
   109  		return cert, nil
   110  	}
   111  	fmt.Printf("[go-easyops] certs No cert for host \"%s\"\n", hostname)
   112  	return &BuiltinTLSCert, nil
   113  }
   114  

View as plain text