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  )