cloudflare/cloudflared

Public

mirrored from https://github.com/cloudflare/cloudflaredAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
2020.2.1

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

dbconnect/cmd.go

157lines · modecode

1package dbconnect
2
3import (
4 "context"
5 "log"
6 "net"
7 "strconv"
8
9 "gopkg.in/urfave/cli.v2"
10 "gopkg.in/urfave/cli.v2/altsrc"
11)
12
13// Cmd is the entrypoint command for dbconnect.
14//
15// The tunnel package is responsible for appending this to tunnel.Commands().
16func Cmd() *cli.Command {
17 return &cli.Command{
18 Category: "Database Connect (ALPHA)",
19 Name: "db-connect",
20 Usage: "Access your SQL database from Cloudflare Workers or the browser",
21 ArgsUsage: " ",
22 Description: `
23 Creates a connection between your database and the Cloudflare edge.
24 Now you can execute SQL commands anywhere you can send HTTPS requests.
25
26 Connect your database with any of the following commands, you can also try the "playground" without a database:
27
28 cloudflared db-connect --hostname sql.mysite.com --url postgres://user:pass@localhost?sslmode=disable \
29 --auth-domain mysite.cloudflareaccess.com --application-aud my-access-policy-tag
30 cloudflared db-connect --hostname sql-dev.mysite.com --url mysql://localhost --insecure
31 cloudflared db-connect --playground
32
33 Requests should be authenticated using Cloudflare Access, learn more about how to enable it here:
34
35 https://developers.cloudflare.com/access/service-auth/service-token/
36 `,
37 Flags: []cli.Flag{
38 altsrc.NewStringFlag(&cli.StringFlag{
39 Name: "url",
40 Usage: "URL to the database (eg. postgres://user:pass@localhost?sslmode=disable)",
41 EnvVars: []string{"TUNNEL_URL"},
42 }),
43 altsrc.NewStringFlag(&cli.StringFlag{
44 Name: "hostname",
45 Usage: "Hostname to accept commands over HTTPS (eg. sql.mysite.com)",
46 EnvVars: []string{"TUNNEL_HOSTNAME"},
47 }),
48 altsrc.NewStringFlag(&cli.StringFlag{
49 Name: "auth-domain",
50 Usage: "Cloudflare Access authentication domain for your account (eg. mysite.cloudflareaccess.com)",
51 EnvVars: []string{"TUNNEL_ACCESS_AUTH_DOMAIN"},
52 }),
53 altsrc.NewStringFlag(&cli.StringFlag{
54 Name: "application-aud",
55 Usage: "Cloudflare Access application \"AUD\" to verify JWTs from requests",
56 EnvVars: []string{"TUNNEL_ACCESS_APPLICATION_AUD"},
57 }),
58 altsrc.NewBoolFlag(&cli.BoolFlag{
59 Name: "insecure",
60 Usage: "Disable authentication, the database will be open to the Internet",
61 Value: false,
62 EnvVars: []string{"TUNNEL_ACCESS_INSECURE"},
63 }),
64 altsrc.NewBoolFlag(&cli.BoolFlag{
65 Name: "playground",
66 Usage: "Run a temporary, in-memory SQLite3 database for testing",
67 Value: false,
68 EnvVars: []string{"TUNNEL_HELLO_WORLD"},
69 }),
70 altsrc.NewStringFlag(&cli.StringFlag{
71 Name: "loglevel",
72 Value: "debug", // Make it more verbose than the tunnel default 'info'.
73 EnvVars: []string{"TUNNEL_LOGLEVEL"},
74 Hidden: true,
75 }),
76 },
77 Before: CmdBefore,
78 Action: CmdAction,
79 Hidden: true,
80 }
81}
82
83// CmdBefore runs some validation checks before running the command.
84func CmdBefore(c *cli.Context) error {
85 // Show the help text is no flags are specified.
86 if c.NumFlags() == 0 {
87 return cli.ShowSubcommandHelp(c)
88 }
89
90 // Hello-world and playground are synonymous with each other,
91 // unset hello-world to prevent tunnel from initializing the hello package.
92 if c.IsSet("hello-world") {
93 c.Set("playground", "true")
94 c.Set("hello-world", "false")
95 }
96
97 // Unix-socket database urls are supported, but the logic is the same as url.
98 if c.IsSet("unix-socket") {
99 c.Set("url", c.String("unix-socket"))
100 c.Set("unix-socket", "")
101 }
102
103 // When playground mode is enabled, run with an in-memory database.
104 if c.IsSet("playground") {
105 c.Set("url", "sqlite3::memory:?cache=shared")
106 c.Set("insecure", strconv.FormatBool(!c.IsSet("auth-domain") && !c.IsSet("application-aud")))
107 }
108
109 // At this point, insecure configurations are valid.
110 if c.Bool("insecure") {
111 return nil
112 }
113
114 // Ensure that secure configurations specify a hostname, domain, and tag for JWT validation.
115 if !c.IsSet("hostname") || !c.IsSet("auth-domain") || !c.IsSet("application-aud") {
116 log.Fatal("must specify --hostname, --auth-domain, and --application-aud unless you want to run in --insecure mode")
117 }
118
119 return nil
120}
121
122// CmdAction starts the Proxy and sets the url in cli.Context to point to the Proxy address.
123func CmdAction(c *cli.Context) error {
124 // STOR-612: sync with context in tunnel daemon.
125 ctx := context.Background()
126
127 var proxy *Proxy
128 var err error
129 if c.Bool("insecure") {
130 proxy, err = NewInsecureProxy(ctx, c.String("url"))
131 } else {
132 proxy, err = NewSecureProxy(ctx, c.String("url"), c.String("auth-domain"), c.String("application-aud"))
133 }
134
135 if err != nil {
136 log.Fatal(err)
137 return err
138 }
139
140 listenerC := make(chan net.Listener)
141 defer close(listenerC)
142
143 // Since the Proxy should only talk to the tunnel daemon, find the next available
144 // localhost port and start to listen to requests.
145 go func() {
146 err := proxy.Start(ctx, "127.0.0.1:", listenerC)
147 if err != nil {
148 log.Fatal(err)
149 }
150 }()
151
152 // Block until the the Proxy is online, retreive its address, and change the url to point to it.
153 // This is effectively "handing over" control to the tunnel package so it can run the tunnel daemon.
154 c.Set("url", "https://"+(<-listenerC).Addr().String())
155
156 return nil
157}
158