/
proxy.go
326 lines (283 loc) · 8.39 KB
/
proxy.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
// Package proxy is a cli proxy
package proxy
import (
"os"
"strings"
"github.com/go-acme/lego/v3/providers/dns/cloudflare"
"github.com/micro/cli/v2"
"github.com/micro/go-micro/v2"
"github.com/micro/go-micro/v2/api/server/acme"
"github.com/micro/go-micro/v2/api/server/acme/autocert"
"github.com/micro/go-micro/v2/api/server/acme/certmagic"
"github.com/micro/go-micro/v2/auth"
bmem "github.com/micro/go-micro/v2/broker/memory"
"github.com/micro/go-micro/v2/client"
mucli "github.com/micro/go-micro/v2/client"
"github.com/micro/go-micro/v2/config/cmd"
log "github.com/micro/go-micro/v2/logger"
"github.com/micro/go-micro/v2/proxy"
//"github.com/micro/go-micro/v2/proxy/grpc"
"github.com/micro/go-micro/v2/proxy/http"
"github.com/micro/go-micro/v2/proxy/mucp"
"github.com/micro/go-micro/v2/registry"
rmem "github.com/micro/go-micro/v2/registry/memory"
"github.com/micro/go-micro/v2/router"
rs "github.com/micro/go-micro/v2/router/service"
"github.com/micro/go-micro/v2/server"
sgrpc "github.com/micro/go-micro/v2/server/grpc"
"github.com/micro/go-micro/v2/sync/memory"
"github.com/micro/go-micro/v2/util/mux"
"github.com/micro/go-micro/v2/util/wrapper"
"github.com/gzl-tommy/micro/v2/internal/helper"
)
var (
// Name of the proxy
Name = "go.micro.proxy"
// The address of the proxy
Address = ":8081"
// the proxy protocol
Protocol = "grpc"
// The endpoint host to route to
Endpoint string
// ACME (Cert management)
ACMEProvider = "autocert"
ACMEChallengeProvider = "cloudflare"
ACMECA = acme.LetsEncryptProductionCA
)
func Run(ctx *cli.Context, srvOpts ...micro.Option) {
log.Init(log.WithFields(map[string]interface{}{"service": "proxy"}))
// because MICRO_PROXY_ADDRESS is used internally by the go-micro/client
// we need to unset it so we don't end up calling ourselves infinitely
os.Unsetenv("MICRO_PROXY_ADDRESS")
if len(ctx.String("server_name")) > 0 {
Name = ctx.String("server_name")
}
if len(ctx.String("address")) > 0 {
Address = ctx.String("address")
}
if len(ctx.String("endpoint")) > 0 {
Endpoint = ctx.String("endpoint")
}
if len(ctx.String("protocol")) > 0 {
Protocol = ctx.String("protocol")
}
if len(ctx.String("acme_provider")) > 0 {
ACMEProvider = ctx.String("acme_provider")
}
// Init plugins
for _, p := range Plugins() {
p.Init(ctx)
}
// service opts
srvOpts = append(srvOpts, micro.Name(Name))
// new service
service := micro.NewService(srvOpts...)
// set the context
var popts []proxy.Option
// create new router
var r router.Router
routerName := ctx.String("router")
routerAddr := ctx.String("router_address")
ropts := []router.Option{
router.Id(server.DefaultId),
router.Client(client.DefaultClient),
router.Address(routerAddr),
router.Registry(registry.DefaultRegistry),
}
// check if we need to use the router service
switch {
case routerName == "go.micro.router":
r = rs.NewRouter(ropts...)
case routerName == "service":
r = rs.NewRouter(ropts...)
case len(routerAddr) > 0:
r = rs.NewRouter(ropts...)
default:
r = router.NewRouter(ropts...)
}
// start the router
if err := r.Start(); err != nil {
log.Errorf("Proxy error starting router: %s", err)
os.Exit(1)
}
// append router to proxy opts
popts = append(popts, proxy.WithRouter(r))
// new proxy
var p proxy.Proxy
// setup the default server
var srv server.Server
// set endpoint
if len(Endpoint) > 0 {
switch {
case strings.HasPrefix(Endpoint, "grpc://"):
ep := strings.TrimPrefix(Endpoint, "grpc://")
popts = append(popts, proxy.WithEndpoint(ep))
Protocol = "grpc"
case strings.HasPrefix(Endpoint, "http://"):
// TODO: strip prefix?
popts = append(popts, proxy.WithEndpoint(Endpoint))
Protocol = "http"
default:
// TODO: strip prefix?
popts = append(popts, proxy.WithEndpoint(Endpoint))
Protocol = "mucp"
}
}
serverOpts := []server.Option{
server.Address(Address),
server.Registry(rmem.NewRegistry()),
server.Broker(bmem.NewBroker()),
}
// enable acme will create a net.Listener which
if ctx.Bool("enable_acme") {
var ap acme.Provider
switch ACMEProvider {
case "autocert":
ap = autocert.NewProvider()
case "certmagic":
if ACMEChallengeProvider != "cloudflare" {
log.Fatal("The only implemented DNS challenge provider is cloudflare")
}
apiToken := os.Getenv("CF_API_TOKEN")
if len(apiToken) == 0 {
log.Fatal("env variables CF_API_TOKEN and CF_ACCOUNT_ID must be set")
}
storage := certmagic.NewStorage(
memory.NewSync(),
service.Options().Store,
)
config := cloudflare.NewDefaultConfig()
config.AuthToken = apiToken
config.ZoneToken = apiToken
challengeProvider, err := cloudflare.NewDNSProviderConfig(config)
if err != nil {
log.Fatal(err.Error())
}
// define the provider
ap = certmagic.NewProvider(
acme.AcceptToS(true),
acme.CA(ACMECA),
acme.Cache(storage),
acme.ChallengeProvider(challengeProvider),
acme.OnDemand(false),
)
default:
log.Fatalf("Unsupported acme provider: %s\n", ACMEProvider)
}
// generate the tls config
config, err := ap.TLSConfig(helper.ACMEHosts(ctx)...)
if err != nil {
log.Fatalf("Failed to generate acme tls config: %v", err)
}
// set the tls config
serverOpts = append(serverOpts, server.TLSConfig(config))
// enable tls will leverage tls certs and generate a tls.Config
} else if ctx.Bool("enable_tls") {
// get certificates from the context
config, err := helper.TLSConfig(ctx)
if err != nil {
log.Fatal(err)
return
}
serverOpts = append(serverOpts, server.TLSConfig(config))
}
// add auth wrapper to server
var authOpts []auth.Option
if ctx.IsSet("auth_public_key") {
authOpts = append(authOpts, auth.PublicKey(ctx.String("auth_public_key")))
}
if ctx.IsSet("auth_private_key") {
authOpts = append(authOpts, auth.PublicKey(ctx.String("auth_private_key")))
}
a := *cmd.DefaultOptions().Auth
a.Init(authOpts...)
authFn := func() auth.Auth { return a }
authOpt := server.WrapHandler(wrapper.AuthHandler(authFn))
serverOpts = append(serverOpts, authOpt)
// set proxy
switch Protocol {
case "http":
p = http.NewProxy(popts...)
serverOpts = append(serverOpts, server.WithRouter(p))
// TODO: http server
srv = server.NewServer(serverOpts...)
case "mucp":
popts = append(popts, proxy.WithClient(mucli.NewClient()))
p = mucp.NewProxy(popts...)
serverOpts = append(serverOpts, server.WithRouter(p))
srv = server.NewServer(serverOpts...)
default:
p = mucp.NewProxy(popts...)
serverOpts = append(serverOpts, server.WithRouter(p))
srv = sgrpc.NewServer(serverOpts...)
}
if len(Endpoint) > 0 {
log.Infof("Proxy [%s] serving endpoint: %s", p.String(), Endpoint)
} else {
log.Infof("Proxy [%s] serving protocol: %s", p.String(), Protocol)
}
// create a new proxy muxer which includes the debug handler
muxer := mux.New(Name, p)
// set the router
service.Server().Init(
server.WithRouter(muxer),
)
// Start the proxy server
if err := srv.Start(); err != nil {
log.Fatal(err)
}
// Run internal service
if err := service.Run(); err != nil {
log.Fatal(err)
}
// Stop the server
if err := srv.Stop(); err != nil {
log.Fatal(err)
}
}
func Commands(options ...micro.Option) []*cli.Command {
command := &cli.Command{
Name: "proxy",
Usage: "Run the service proxy",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "router",
Usage: "Set the router to use e.g default, go.micro.router",
EnvVars: []string{"MICRO_ROUTER"},
},
&cli.StringFlag{
Name: "router_address",
Usage: "Set the router address",
EnvVars: []string{"MICRO_ROUTER_ADDRESS"},
},
&cli.StringFlag{
Name: "address",
Usage: "Set the proxy http address e.g 0.0.0.0:8081",
EnvVars: []string{"MICRO_PROXY_ADDRESS"},
},
&cli.StringFlag{
Name: "protocol",
Usage: "Set the protocol used for proxying e.g mucp, grpc, http",
EnvVars: []string{"MICRO_PROXY_PROTOCOL"},
},
&cli.StringFlag{
Name: "endpoint",
Usage: "Set the endpoint to route to e.g greeter or localhost:9090",
EnvVars: []string{"MICRO_PROXY_ENDPOINT"},
},
},
Action: func(ctx *cli.Context) error {
Run(ctx, options...)
return nil
},
}
for _, p := range Plugins() {
if cmds := p.Commands(); len(cmds) > 0 {
command.Subcommands = append(command.Subcommands, cmds...)
}
if flags := p.Flags(); len(flags) > 0 {
command.Flags = append(command.Flags, flags...)
}
}
return []*cli.Command{command}
}