github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/service/proxy/server/server.go (about) 1 package server 2 3 import ( 4 "os" 5 "strings" 6 7 "github.com/go-acme/lego/v3/providers/dns/cloudflare" 8 "github.com/improbable-eng/grpc-web/go/grpcweb" 9 "github.com/tickoalcantara12/micro/v3/service" 10 bmem "github.com/tickoalcantara12/micro/v3/service/broker/memory" 11 muclient "github.com/tickoalcantara12/micro/v3/service/client" 12 log "github.com/tickoalcantara12/micro/v3/service/logger" 13 "github.com/tickoalcantara12/micro/v3/service/proxy" 14 "github.com/tickoalcantara12/micro/v3/service/proxy/grpc" 15 "github.com/tickoalcantara12/micro/v3/service/proxy/http" 16 "github.com/tickoalcantara12/micro/v3/service/proxy/mucp" 17 "github.com/tickoalcantara12/micro/v3/service/registry/noop" 18 murouter "github.com/tickoalcantara12/micro/v3/service/router" 19 "github.com/tickoalcantara12/micro/v3/service/server" 20 sgrpc "github.com/tickoalcantara12/micro/v3/service/server/grpc" 21 "github.com/tickoalcantara12/micro/v3/service/store" 22 "github.com/tickoalcantara12/micro/v3/util/acme" 23 "github.com/tickoalcantara12/micro/v3/util/acme/autocert" 24 "github.com/tickoalcantara12/micro/v3/util/acme/certmagic" 25 "github.com/tickoalcantara12/micro/v3/util/helper" 26 "github.com/tickoalcantara12/micro/v3/util/muxer" 27 "github.com/tickoalcantara12/micro/v3/util/opentelemetry" 28 "github.com/tickoalcantara12/micro/v3/util/opentelemetry/jaeger" 29 "github.com/tickoalcantara12/micro/v3/util/sync/memory" 30 "github.com/tickoalcantara12/micro/v3/util/wrapper" 31 "github.com/opentracing/opentracing-go" 32 "github.com/urfave/cli/v2" 33 ) 34 35 var ( 36 // Name of the proxy 37 Name = "proxy" 38 // The address of the proxy 39 Address = ":8081" 40 // Is gRPCWeb enabled 41 GRPCWebEnabled = false 42 // The address of the proxy 43 GRPCWebAddress = ":8082" 44 // the proxy protocol 45 Protocol = "grpc" 46 // The endpoint host to route to 47 Endpoint string 48 // ACME (Cert management) 49 ACMEProvider = "autocert" 50 ACMEChallengeProvider = "cloudflare" 51 ACMECA = acme.LetsEncryptProductionCA 52 ) 53 54 func Run(ctx *cli.Context) error { 55 if len(ctx.String("server_name")) > 0 { 56 Name = ctx.String("server_name") 57 } 58 if len(ctx.String("address")) > 0 { 59 Address = ctx.String("address") 60 } 61 if ctx.Bool("grpc-web") { 62 GRPCWebEnabled = ctx.Bool("grpcWeb") 63 } 64 if len(ctx.String("grpc-web-port")) > 0 { 65 GRPCWebAddress = ctx.String("grpcWebAddr") 66 } 67 if len(ctx.String("endpoint")) > 0 { 68 Endpoint = ctx.String("endpoint") 69 } 70 if len(ctx.String("protocol")) > 0 { 71 Protocol = ctx.String("protocol") 72 } 73 if len(ctx.String("acme_provider")) > 0 { 74 ACMEProvider = ctx.String("acme_provider") 75 } 76 77 // new service 78 service := service.New(service.Name(Name)) 79 80 // set the context 81 popts := []proxy.Option{ 82 proxy.WithRouter(murouter.DefaultRouter), 83 proxy.WithClient(muclient.DefaultClient), 84 } 85 86 // set endpoint 87 if len(Endpoint) > 0 { 88 ep := Endpoint 89 90 switch { 91 case strings.HasPrefix(Endpoint, "grpc://"): 92 ep = strings.TrimPrefix(Endpoint, "grpc://") 93 Protocol = "grpc" 94 case strings.HasPrefix(Endpoint, "http://"): 95 Protocol = "http" 96 case strings.HasPrefix(Endpoint, "mucp://"): 97 ep = strings.TrimPrefix(Endpoint, "mucp://") 98 Protocol = "mucp" 99 } 100 101 popts = append(popts, proxy.WithEndpoint(ep)) 102 } 103 104 serverOpts := []server.Option{ 105 server.Name(Name), 106 server.Address(Address), 107 server.Registry(noop.NewRegistry()), 108 server.Broker(bmem.NewBroker()), 109 } 110 111 // enable acme will create a net.Listener which 112 if ctx.Bool("enable_acme") { 113 var ap acme.Provider 114 115 switch ACMEProvider { 116 case "autocert": 117 ap = autocert.NewProvider() 118 case "certmagic": 119 if ACMEChallengeProvider != "cloudflare" { 120 log.Fatal("The only implemented DNS challenge provider is cloudflare") 121 } 122 123 apiToken := os.Getenv("CF_API_TOKEN") 124 if len(apiToken) == 0 { 125 log.Fatal("env variables CF_API_TOKEN and CF_ACCOUNT_ID must be set") 126 } 127 128 storage := certmagic.NewStorage( 129 memory.NewSync(), 130 store.DefaultStore, 131 ) 132 133 config := cloudflare.NewDefaultConfig() 134 config.AuthToken = apiToken 135 config.ZoneToken = apiToken 136 challengeProvider, err := cloudflare.NewDNSProviderConfig(config) 137 if err != nil { 138 log.Fatal(err.Error()) 139 } 140 141 // define the provider 142 ap = certmagic.NewProvider( 143 acme.AcceptToS(true), 144 acme.CA(ACMECA), 145 acme.Cache(storage), 146 acme.ChallengeProvider(challengeProvider), 147 acme.OnDemand(false), 148 ) 149 default: 150 log.Fatalf("Unsupported acme provider: %s\n", ACMEProvider) 151 } 152 153 // generate the tls config 154 config, err := ap.TLSConfig(helper.ACMEHosts(ctx)...) 155 if err != nil { 156 log.Fatalf("Failed to generate acme tls config: %v", err) 157 } 158 159 // set the tls config 160 serverOpts = append(serverOpts, server.TLSConfig(config)) 161 // enable tls will leverage tls certs and generate a tls.Config 162 } else if ctx.Bool("enable_tls") { 163 // get certificates from the context 164 config, err := helper.TLSConfig(ctx) 165 if err != nil { 166 log.Fatal(err) 167 return err 168 } 169 serverOpts = append(serverOpts, server.TLSConfig(config)) 170 } 171 172 reporterAddress := ctx.String("tracing_reporter_address") 173 if len(reporterAddress) == 0 { 174 reporterAddress = jaeger.DefaultReporterAddress 175 } 176 177 // Create a new Jaeger opentracer: 178 openTracer, traceCloser, err := jaeger.New( 179 opentelemetry.WithServiceName("proxy"), 180 opentelemetry.WithTraceReporterAddress(reporterAddress), 181 ) 182 log.Infof("Setting jaeger global tracer to %s", reporterAddress) 183 defer traceCloser.Close() // Make sure we flush any pending traces before shutdown: 184 if err != nil { 185 log.Warnf("Unable to prepare a Jaeger tracer: %s", err) 186 } else { 187 // Set the global default opentracing tracer: 188 opentracing.SetGlobalTracer(openTracer) 189 } 190 opentelemetry.DefaultOpenTracer = openTracer 191 192 // new proxy 193 var p proxy.Proxy 194 195 // set proxy 196 switch Protocol { 197 case "http": 198 p = http.NewProxy(popts...) 199 // TODO: http server 200 case "mucp": 201 p = mucp.NewProxy(popts...) 202 default: 203 // default to the grpc proxy 204 p = grpc.NewProxy(popts...) 205 } 206 207 // wrap the proxy using the proxy's authHandler 208 authOpt := server.WrapHandler(authHandler()) 209 serverOpts = append(serverOpts, authOpt) 210 serverOpts = append(serverOpts, server.WithRouter(p)) 211 serverOpts = append(serverOpts, server.WrapHandler(wrapper.OpenTraceHandler())) 212 213 if len(Endpoint) > 0 { 214 log.Infof("Proxy [%s] serving endpoint: %s", p.String(), Endpoint) 215 } else { 216 log.Infof("Proxy [%s] serving protocol: %s", p.String(), Protocol) 217 } 218 219 if GRPCWebEnabled { 220 serverOpts = append(serverOpts, sgrpc.GRPCWebPort(GRPCWebAddress)) 221 serverOpts = append(serverOpts, sgrpc.GRPCWebOptions( 222 grpcweb.WithCorsForRegisteredEndpointsOnly(false), 223 grpcweb.WithOriginFunc(func(origin string) bool { return true }))) 224 225 log.Infof("Proxy [%s] serving gRPC-Web on %s", p.String(), GRPCWebAddress) 226 } 227 228 // create a new grpc server 229 srv := sgrpc.NewServer(serverOpts...) 230 231 // create a new proxy muxer which includes the debug handler 232 muxer := muxer.New(Name, p) 233 234 // set the router 235 service.Server().Init( 236 server.WithRouter(muxer), 237 ) 238 239 // Start the proxy server 240 if err := srv.Start(); err != nil { 241 log.Fatal(err) 242 } 243 244 // Run internal service 245 if err := service.Run(); err != nil { 246 log.Fatal(err) 247 } 248 249 // Stop the server 250 if err := srv.Stop(); err != nil { 251 log.Fatal(err) 252 } 253 254 return nil 255 } 256 257 var ( 258 Flags = []cli.Flag{ 259 &cli.BoolFlag{ 260 Name: "enable_acme", 261 Usage: "Enables ACME support via Let's Encrypt. ACME hosts should also be specified.", 262 EnvVars: []string{"MICRO_PROXY_ENABLE_ACME"}, 263 }, 264 &cli.StringFlag{ 265 Name: "acme_hosts", 266 Usage: "Comma separated list of hostnames to manage ACME certs for", 267 EnvVars: []string{"MICRO_PROXY_ACME_HOSTS"}, 268 }, 269 &cli.StringFlag{ 270 Name: "acme_provider", 271 Usage: "The provider that will be used to communicate with Let's Encrypt. Valid options: autocert, certmagic", 272 EnvVars: []string{"MICRO_PROXY_ACME_PROVIDER"}, 273 }, 274 &cli.BoolFlag{ 275 Name: "enable_tls", 276 Usage: "Enable TLS support. Expects cert and key file to be specified", 277 EnvVars: []string{"MICRO_PROXY_ENABLE_TLS"}, 278 }, 279 &cli.StringFlag{ 280 Name: "tls_cert_file", 281 Usage: "Path to the TLS Certificate file", 282 EnvVars: []string{"MICRO_PROXY_TLS_CERT_FILE"}, 283 }, 284 &cli.StringFlag{ 285 Name: "tls_key_file", 286 Usage: "Path to the TLS Key file", 287 EnvVars: []string{"MICRO_PROXY_TLS_KEY_FILE"}, 288 }, 289 &cli.StringFlag{ 290 Name: "tls_client_ca_file", 291 Usage: "Path to the TLS CA file to verify clients against", 292 EnvVars: []string{"MICRO_PROXY_TLS_CLIENT_CA_FILE"}, 293 }, 294 &cli.StringFlag{ 295 Name: "address", 296 Usage: "Set the proxy http address e.g 0.0.0.0:8081", 297 EnvVars: []string{"MICRO_PROXY_ADDRESS"}, 298 }, 299 &cli.StringFlag{ 300 Name: "protocol", 301 Usage: "Set the protocol used for proxying e.g mucp, grpc, http", 302 EnvVars: []string{"MICRO_PROXY_PROTOCOL"}, 303 }, 304 &cli.StringFlag{ 305 Name: "endpoint", 306 Usage: "Set the endpoint to route to e.g greeter or localhost:9090", 307 EnvVars: []string{"MICRO_PROXY_ENDPOINT"}, 308 }, 309 &cli.BoolFlag{ 310 Name: "grpc-web", 311 Usage: "Enable the gRPCWeb server", 312 EnvVars: []string{"MICRO_PROXY_GRPC_WEB"}, 313 }, 314 &cli.StringFlag{ 315 Name: "grpc-web-addr", 316 Usage: "Set the gRPC web addr on the proxy", 317 EnvVars: []string{"MICRO_PROXY_GRPC_WEB_ADDRESS"}, 318 }, 319 } 320 )