TUN-3451: Cloudflared tunnel ingress command

This commit is contained in:
Adam Chalmers 2020-10-08 13:00:32 -05:00
parent 407c9550d7
commit 86a7af3dc4
3 changed files with 54 additions and 17 deletions

View File

@ -182,8 +182,7 @@ func Commands() []*cli.Command {
subcommands = append(subcommands, buildRunCommand()) subcommands = append(subcommands, buildRunCommand())
subcommands = append(subcommands, buildCleanupCommand()) subcommands = append(subcommands, buildCleanupCommand())
subcommands = append(subcommands, buildRouteCommand()) subcommands = append(subcommands, buildRouteCommand())
subcommands = append(subcommands, buildValidateCommand()) subcommands = append(subcommands, buildIngressSubcommand())
subcommands = append(subcommands, buildRuleCommand())
cmds = append(cmds, buildTunnelCommand(subcommands)) cmds = append(cmds, buildTunnelCommand(subcommands))
@ -221,6 +220,38 @@ func buildTunnelCommand(subcommands []*cli.Command) *cli.Command {
} }
} }
func buildIngressSubcommand() *cli.Command {
return &cli.Command{
Name: "ingress",
Category: "Tunnel",
Usage: "Validate and test cloudflared tunnel's ingress configuration",
Hidden: true,
Description: `
Cloudflared lets you route traffic from the internet to multiple different addresses on your
origin. Multiple-origin routing is configured by a set of rules. Each rule matches traffic
by its hostname or path, and routes it to an address. These rules are configured under the
'ingress' key of your config.yaml, for example:
ingress:
- hostname: www.example.com
service: https://localhost:8000
- hostname: *.example.xyz
path: /[a-zA-Z]+.html
service: https://localhost:8001
- hostname: *
service: https://localhost:8002
To ensure cloudflared can route all incoming requests, the last rule must be a catch-all
rule that matches all traffic. You can validate these rules with the 'ingress validate'
command, and test which rule matches a particular URL with 'ingress rule <URL>'.
Multiple-origin routing is incompatible with the --url flag.
`,
Subcommands: []*cli.Command{buildValidateCommand(), buildRuleCommand()},
Flags: tunnelFlags(false),
}
}
func TunnelCommand(c *cli.Context) error { func TunnelCommand(c *cli.Context) error {
if name := c.String("name"); name != "" { // Start a named tunnel if name := c.String("name"); name != "" { // Start a named tunnel
return adhocNamedTunnel(c, name) return adhocNamedTunnel(c, name)

View File

@ -9,7 +9,6 @@ import (
"github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil" "github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil"
"github.com/cloudflare/cloudflared/cmd/cloudflared/config" "github.com/cloudflare/cloudflared/cmd/cloudflared/config"
"github.com/cloudflare/cloudflared/logger"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
@ -91,6 +90,9 @@ func (ing ingress) validate() ([]rule, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if service.Scheme == "" || service.Hostname() == "" {
return nil, fmt.Errorf("The service %s must have a scheme and a hostname", r.Service)
}
// Ensure that there are no wildcards anywhere except the first character // Ensure that there are no wildcards anywhere except the first character
// of the hostname. // of the hostname.
@ -148,32 +150,28 @@ func parseIngress(rawYAML []byte) ([]rule, error) {
return ing.validate() return ing.validate()
} }
func ingressContext(c *cli.Context) ([]rule, *logger.OutputWriter, error) { func ingressContext(c *cli.Context) ([]rule, error) {
log, err := createLogger(c, false, false)
if err != nil {
return nil, nil, err
}
configFilePath := c.String("config") configFilePath := c.String("config")
if configFilePath == "" { if configFilePath == "" {
return nil, nil, config.ErrNoConfigFile return nil, config.ErrNoConfigFile
} }
log.Infof("Validating %s", configFilePath) fmt.Printf("Reading from config file %s\n", configFilePath)
configBytes, err := ioutil.ReadFile(configFilePath) configBytes, err := ioutil.ReadFile(configFilePath)
if err != nil { if err != nil {
return nil, nil, err return nil, err
} }
rules, err := parseIngress(configBytes) rules, err := parseIngress(configBytes)
return rules, log, err return rules, err
} }
// Validates the ingress rules in the cloudflared config file // Validates the ingress rules in the cloudflared config file
func validateCommand(c *cli.Context) error { func validateCommand(c *cli.Context) error {
_, log, err := ingressContext(c) _, err := ingressContext(c)
if err != nil { if err != nil {
log.Error(err.Error()) fmt.Println(err.Error())
return errors.New("Validation failed") return errors.New("Validation failed")
} }
log.Infof("OK") fmt.Println("OK")
return nil return nil
} }
@ -189,7 +187,7 @@ func buildValidateCommand() *cli.Command {
// Checks which ingress rule matches the given URL. // Checks which ingress rule matches the given URL.
func ruleCommand(c *cli.Context) error { func ruleCommand(c *cli.Context) error {
rules, log, err := ingressContext(c) rules, err := ingressContext(c)
if err != nil { if err != nil {
return err return err
} }
@ -206,7 +204,7 @@ func ruleCommand(c *cli.Context) error {
} }
for i, r := range rules { for i, r := range rules {
if r.matches(requestURL) { if r.matches(requestURL) {
log.Infof("Matched rule #%d", i+1) fmt.Printf("Matched rule #%d\n", i+1)
fmt.Println(r.String()) fmt.Println(r.String())
return nil return nil
} }

View File

@ -127,6 +127,14 @@ ingress:
service: https://localhost:8000 service: https://localhost:8000
path: "*/subpath2" path: "*/subpath2"
- service: https://localhost:8001 - service: https://localhost:8001
`},
wantErr: true,
},
{
name: "Service must have a scheme",
args: args{rawYAML: `
ingress:
- service: localhost:8000
`}, `},
wantErr: true, wantErr: true,
}, },