github.com/lmb/consul@v1.4.1/connect/proxy/proxy.go (about) 1 package proxy 2 3 import ( 4 "crypto/x509" 5 "log" 6 7 "github.com/hashicorp/consul/api" 8 "github.com/hashicorp/consul/connect" 9 "github.com/hashicorp/consul/lib" 10 ) 11 12 // Proxy implements the built-in connect proxy. 13 type Proxy struct { 14 client *api.Client 15 cfgWatcher ConfigWatcher 16 stopChan chan struct{} 17 logger *log.Logger 18 service *connect.Service 19 } 20 21 // New returns a proxy with the given configuration source. 22 // 23 // The ConfigWatcher can be used to update the configuration of the proxy. 24 // Whenever a new configuration is detected, the proxy will reconfigure itself. 25 func New(client *api.Client, cw ConfigWatcher, logger *log.Logger) (*Proxy, error) { 26 return &Proxy{ 27 client: client, 28 cfgWatcher: cw, 29 stopChan: make(chan struct{}), 30 logger: logger, 31 }, nil 32 } 33 34 // Serve the proxy instance until a fatal error occurs or proxy is closed. 35 func (p *Proxy) Serve() error { 36 var cfg *Config 37 38 // failCh is used to stop Serve and return an error from another goroutine we 39 // spawn. 40 failCh := make(chan error, 1) 41 42 // Watch for config changes (initial setup happens on first "change") 43 for { 44 select { 45 case err := <-failCh: 46 // don't log here, we can log with better context at the point where we 47 // write the err to the chan 48 return err 49 50 case newCfg := <-p.cfgWatcher.Watch(): 51 p.logger.Printf("[DEBUG] got new config") 52 53 if cfg == nil { 54 // Initial setup 55 56 // Setup telemetry if configured 57 _, err := lib.InitTelemetry(newCfg.Telemetry) 58 if err != nil { 59 p.logger.Printf("[ERR] proxy telemetry config error: %s", err) 60 } 61 62 // Setup Service instance now we know target ID etc 63 service, err := newCfg.Service(p.client, p.logger) 64 if err != nil { 65 return err 66 } 67 p.service = service 68 69 go func() { 70 <-service.ReadyWait() 71 p.logger.Printf("[INFO] Proxy loaded config and ready to serve") 72 tcfg := service.ServerTLSConfig() 73 cert, _ := tcfg.GetCertificate(nil) 74 leaf, _ := x509.ParseCertificate(cert.Certificate[0]) 75 p.logger.Printf("[INFO] TLS Identity: %s", leaf.URIs[0]) 76 roots, err := connect.CommonNamesFromCertPool(tcfg.RootCAs) 77 if err != nil { 78 p.logger.Printf("[ERR] Failed to parse root subjects: %s", err) 79 } else { 80 p.logger.Printf("[INFO] TLS Roots : %v", roots) 81 } 82 83 // Only start a listener if we have a port set. This allows 84 // the configuration to disable our public listener. 85 if newCfg.PublicListener.BindPort != 0 { 86 newCfg.PublicListener.applyDefaults() 87 l := NewPublicListener(p.service, newCfg.PublicListener, p.logger) 88 err = p.startListener("public listener", l) 89 if err != nil { 90 // This should probably be fatal. 91 p.logger.Printf("[ERR] failed to start public listener: %s", err) 92 failCh <- err 93 } 94 95 } 96 }() 97 } 98 99 // TODO(banks) update/remove upstreams properly based on a diff with current. Can 100 // store a map of uc.String() to Listener here and then use it to only 101 // start one of each and stop/modify if changes occur. 102 for _, uc := range newCfg.Upstreams { 103 uc.applyDefaults() 104 105 if uc.LocalBindPort < 1 { 106 p.logger.Printf("[ERR] upstream %s has no local_bind_port. "+ 107 "Can't start upstream.", uc.String()) 108 continue 109 } 110 111 l := NewUpstreamListener(p.service, p.client, uc, p.logger) 112 err := p.startListener(uc.String(), l) 113 if err != nil { 114 p.logger.Printf("[ERR] failed to start upstream %s: %s", uc.String(), 115 err) 116 } 117 } 118 cfg = newCfg 119 120 case <-p.stopChan: 121 return nil 122 } 123 } 124 } 125 126 // startPublicListener is run from the internal state machine loop 127 func (p *Proxy) startListener(name string, l *Listener) error { 128 p.logger.Printf("[INFO] %s starting on %s", name, l.BindAddr()) 129 go func() { 130 err := l.Serve() 131 if err != nil { 132 p.logger.Printf("[ERR] %s stopped with error: %s", name, err) 133 return 134 } 135 p.logger.Printf("[INFO] %s stopped", name) 136 }() 137 138 go func() { 139 <-p.stopChan 140 l.Close() 141 142 }() 143 144 return nil 145 } 146 147 // Close stops the proxy and terminates all active connections. It must be 148 // called only once. 149 func (p *Proxy) Close() { 150 close(p.stopChan) 151 if p.service != nil { 152 p.service.Close() 153 } 154 }