tlsrp

TLS reverse proxy
git clone git://git.rr3.xyz/tlsrp
Log | Files | Refs | README | LICENSE

config.go (2880B)


      1 package main
      2 
      3 import (
      4 	"crypto/tls"
      5 	"crypto/x509"
      6 	"fmt"
      7 	"io"
      8 	"os"
      9 	"strings"
     10 )
     11 
     12 var lookupSinkChan chan lookupSinkMsg
     13 var lookupCertChan chan lookupCertMsg
     14 
     15 func init() {
     16 	lookupSinkChan = make(chan lookupSinkMsg, 16)
     17 	lookupCertChan = make(chan lookupCertMsg, 16)
     18 }
     19 
     20 type sink struct {
     21 	pattern pattern
     22 	network string
     23 	address string
     24 }
     25 
     26 type cert struct {
     27 	pattern pattern
     28 	crtPath string
     29 	keyPath string
     30 	cert    *tls.Certificate
     31 }
     32 
     33 type lookupSinkMsg struct {
     34 	hostname hostname
     35 	reply    chan<- *sink
     36 }
     37 
     38 type lookupCertMsg struct {
     39 	hostname hostname
     40 	reply    chan<- *cert
     41 }
     42 
     43 func lookupSink(hostname hostname) (*sink, error) {
     44 	reply := make(chan *sink, 1)
     45 	lookupSinkChan <- lookupSinkMsg{
     46 		hostname: hostname,
     47 		reply:    reply,
     48 	}
     49 
     50 	sink, ok := <-reply
     51 	if !ok {
     52 		return nil, fmt.Errorf("no sink for hostname %s", hostname)
     53 	}
     54 
     55 	return sink, nil
     56 }
     57 
     58 func lookupCert(hostname hostname) (*cert, error) {
     59 	reply := make(chan *cert, 1)
     60 	lookupCertChan <- lookupCertMsg{
     61 		hostname: hostname,
     62 		reply:    reply,
     63 	}
     64 
     65 	cert, ok := <-reply
     66 	if !ok {
     67 		return nil, fmt.Errorf("no cert for hostname %s", hostname)
     68 	}
     69 
     70 	return cert, nil
     71 }
     72 
     73 type config struct {
     74 	sinks []*sink
     75 	certs []*cert
     76 }
     77 
     78 func loadTLSCert(crtPath, keyPath string) (*tls.Certificate, error) {
     79 	tlsCert, err := tls.LoadX509KeyPair(crtPath, keyPath)
     80 	if err != nil {
     81 		return nil, err
     82 	}
     83 
     84 	// Parsing the leaf certificate in advance is recommended by crypto/tls to
     85 	// "reduce per-handshake processing".
     86 	tlsCert.Leaf, err = x509.ParseCertificate(tlsCert.Certificate[0])
     87 	if err != nil {
     88 		return nil, err
     89 	}
     90 
     91 	return &tlsCert, nil
     92 }
     93 
     94 func loadConfig(path string) (*config, error) {
     95 	file, err := os.Open(path)
     96 	if err != nil {
     97 		return nil, err
     98 	}
     99 	defer file.Close()
    100 
    101 	data, err := io.ReadAll(file)
    102 	if err != nil {
    103 		return nil, err
    104 	}
    105 
    106 	var cfg config
    107 
    108 	lines := strings.Split(string(data), "\n")
    109 	for _, line := range lines {
    110 		fields := strings.Fields(line)
    111 		if len(fields) == 0 {
    112 			continue // Empty line
    113 		}
    114 		if len(fields) < 3 {
    115 			return nil, fmt.Errorf("illegal config: line with fewer than 3 fields")
    116 		}
    117 
    118 		switch fields[0] {
    119 		case "sink":
    120 			pattern, err := parsePattern(fields[3:])
    121 			if err != nil {
    122 				return nil, err
    123 			}
    124 
    125 			sink := sink{
    126 				pattern: pattern,
    127 				network: fields[1],
    128 				address: fields[2],
    129 			}
    130 			cfg.sinks = append(cfg.sinks, &sink)
    131 
    132 		case "cert":
    133 			crtPath := fields[1]
    134 			keyPath := fields[2]
    135 
    136 			tlsCert, err := loadTLSCert(crtPath, keyPath)
    137 			if err != nil {
    138 				return nil, err
    139 			}
    140 
    141 			pattern, err := parsePattern(fields[3:])
    142 			if err != nil {
    143 				return nil, err
    144 			}
    145 
    146 			cert := cert{
    147 				pattern: pattern,
    148 				crtPath: crtPath,
    149 				keyPath: keyPath,
    150 				cert:    tlsCert,
    151 			}
    152 			cfg.certs = append(cfg.certs, &cert)
    153 
    154 		default:
    155 			return nil, fmt.Errorf("illegal config: expected \"sink\" or \"cert\" as first field")
    156 		}
    157 	}
    158 
    159 	return &cfg, nil
    160 }