TUN-3578: cloudflared tunnel route dns should allow wildcard subdomains
This commit is contained in:
parent
87203bbe25
commit
a08a7030d1
|
@ -103,7 +103,7 @@ func buildCreateCommand() *cli.Command {
|
||||||
Usage: "Create a new tunnel with given name",
|
Usage: "Create a new tunnel with given name",
|
||||||
UsageText: "cloudflared tunnel [tunnel command options] create [subcommand options] NAME",
|
UsageText: "cloudflared tunnel [tunnel command options] create [subcommand options] NAME",
|
||||||
Description: `Creates a tunnel, registers it with Cloudflare edge and generates credential file used to run this tunnel.
|
Description: `Creates a tunnel, registers it with Cloudflare edge and generates credential file used to run this tunnel.
|
||||||
Use "cloudflared tunnel route" subcommand to map a DNS name to this tunnel and "cloudflared tunnel run" to start the connection.
|
Use "cloudflared tunnel route" subcommand to map a DNS name to this tunnel and "cloudflared tunnel run" to start the connection.
|
||||||
|
|
||||||
For example, to create a tunnel named 'my-tunnel' run:
|
For example, to create a tunnel named 'my-tunnel' run:
|
||||||
|
|
||||||
|
@ -329,13 +329,13 @@ func buildRunCommand() *cli.Command {
|
||||||
Before: SetFlagsFromConfigFile,
|
Before: SetFlagsFromConfigFile,
|
||||||
Usage: "Proxy a local web server by running the given tunnel",
|
Usage: "Proxy a local web server by running the given tunnel",
|
||||||
UsageText: "cloudflared tunnel [tunnel command options] run [subcommand options] [TUNNEL]",
|
UsageText: "cloudflared tunnel [tunnel command options] run [subcommand options] [TUNNEL]",
|
||||||
Description: `Runs the tunnel identified by name or UUUD, creating highly available connections
|
Description: `Runs the tunnel identified by name or UUUD, creating highly available connections
|
||||||
between your server and the Cloudflare edge. You can provide name or UUID of tunnel to run either as the
|
between your server and the Cloudflare edge. You can provide name or UUID of tunnel to run either as the
|
||||||
last command line argument or in the configuration file using "tunnel: TUNNEL".
|
last command line argument or in the configuration file using "tunnel: TUNNEL".
|
||||||
|
|
||||||
This command requires the tunnel credentials file created when "cloudflared tunnel create" was run,
|
This command requires the tunnel credentials file created when "cloudflared tunnel create" was run,
|
||||||
however it does not need access to cert.pem from "cloudflared login" if you identify the tunnel by UUID.
|
however it does not need access to cert.pem from "cloudflared login" if you identify the tunnel by UUID.
|
||||||
If you experience other problems running the tunnel, "cloudflared tunnel cleanup" may help by removing
|
If you experience other problems running the tunnel, "cloudflared tunnel cleanup" may help by removing
|
||||||
any old connection records.
|
any old connection records.
|
||||||
`,
|
`,
|
||||||
Flags: flags,
|
Flags: flags,
|
||||||
|
@ -431,7 +431,7 @@ func dnsRouteFromArg(c *cli.Context) (tunnelstore.Route, error) {
|
||||||
userHostname := c.Args().Get(userHostnameIndex)
|
userHostname := c.Args().Get(userHostnameIndex)
|
||||||
if userHostname == "" {
|
if userHostname == "" {
|
||||||
return nil, cliutil.UsageError("The third argument should be the hostname")
|
return nil, cliutil.UsageError("The third argument should be the hostname")
|
||||||
} else if !validateHostname(userHostname) {
|
} else if !validateHostname(userHostname, true) {
|
||||||
return nil, errors.Errorf("%s is not a valid hostname", userHostname)
|
return nil, errors.Errorf("%s is not a valid hostname", userHostname)
|
||||||
}
|
}
|
||||||
return tunnelstore.NewDNSRoute(userHostname), nil
|
return tunnelstore.NewDNSRoute(userHostname), nil
|
||||||
|
@ -449,14 +449,14 @@ func lbRouteFromArg(c *cli.Context) (tunnelstore.Route, error) {
|
||||||
lbName := c.Args().Get(lbNameIndex)
|
lbName := c.Args().Get(lbNameIndex)
|
||||||
if lbName == "" {
|
if lbName == "" {
|
||||||
return nil, cliutil.UsageError("The third argument should be the load balancer name")
|
return nil, cliutil.UsageError("The third argument should be the load balancer name")
|
||||||
} else if !validateHostname(lbName) {
|
} else if !validateHostname(lbName, true) {
|
||||||
return nil, errors.Errorf("%s is not a valid load balancer name", lbName)
|
return nil, errors.Errorf("%s is not a valid load balancer name", lbName)
|
||||||
}
|
}
|
||||||
|
|
||||||
lbPool := c.Args().Get(lbPoolIndex)
|
lbPool := c.Args().Get(lbPoolIndex)
|
||||||
if lbPool == "" {
|
if lbPool == "" {
|
||||||
return nil, cliutil.UsageError("The fourth argument should be the pool name")
|
return nil, cliutil.UsageError("The fourth argument should be the pool name")
|
||||||
} else if !validateName(lbPool) {
|
} else if !validateName(lbPool, false) {
|
||||||
return nil, errors.Errorf("%s is not a valid pool name", lbPool)
|
return nil, errors.Errorf("%s is not a valid pool name", lbPool)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -464,19 +464,23 @@ func lbRouteFromArg(c *cli.Context) (tunnelstore.Route, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var nameRegex = regexp.MustCompile("^[_a-zA-Z0-9][-_.a-zA-Z0-9]*$")
|
var nameRegex = regexp.MustCompile("^[_a-zA-Z0-9][-_.a-zA-Z0-9]*$")
|
||||||
|
var hostNameRegex = regexp.MustCompile("^[*_a-zA-Z0-9][-_.a-zA-Z0-9]*$")
|
||||||
|
|
||||||
func validateName(s string) bool {
|
func validateName(s string, allowWildcardSubdomain bool) bool {
|
||||||
|
if allowWildcardSubdomain {
|
||||||
|
return hostNameRegex.MatchString(s)
|
||||||
|
}
|
||||||
return nameRegex.MatchString(s)
|
return nameRegex.MatchString(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateHostname(s string) bool {
|
func validateHostname(s string, allowWildcardSubdomain bool) bool {
|
||||||
// Slightly stricter than PunyCodeProfile
|
// Slightly stricter than PunyCodeProfile
|
||||||
idnaProfile := idna.New(
|
idnaProfile := idna.New(
|
||||||
idna.ValidateLabels(true),
|
idna.ValidateLabels(true),
|
||||||
idna.VerifyDNSLength(true))
|
idna.VerifyDNSLength(true))
|
||||||
|
|
||||||
puny, err := idnaProfile.ToASCII(s)
|
puny, err := idnaProfile.ToASCII(s)
|
||||||
return err == nil && validateName(puny)
|
return err == nil && validateName(puny, allowWildcardSubdomain)
|
||||||
}
|
}
|
||||||
|
|
||||||
func routeCommand(c *cli.Context) error {
|
func routeCommand(c *cli.Context) error {
|
||||||
|
@ -535,13 +539,13 @@ func commandHelpTemplate() string {
|
||||||
}
|
}
|
||||||
const template = `NAME:
|
const template = `NAME:
|
||||||
{{.HelpName}} - {{.Usage}}
|
{{.HelpName}} - {{.Usage}}
|
||||||
|
|
||||||
USAGE:
|
USAGE:
|
||||||
{{.UsageText}}
|
{{.UsageText}}
|
||||||
|
|
||||||
DESCRIPTION:
|
DESCRIPTION:
|
||||||
{{.Description}}
|
{{.Description}}
|
||||||
|
|
||||||
TUNNEL COMMAND OPTIONS:
|
TUNNEL COMMAND OPTIONS:
|
||||||
%s
|
%s
|
||||||
SUBCOMMAND OPTIONS:
|
SUBCOMMAND OPTIONS:
|
||||||
|
|
|
@ -6,9 +6,8 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/cloudflare/cloudflared/tunnelstore"
|
"github.com/cloudflare/cloudflared/tunnelstore"
|
||||||
"github.com/mitchellh/go-homedir"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
"github.com/mitchellh/go-homedir"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -117,8 +116,64 @@ func TestValidateName(t *testing.T) {
|
||||||
{name: "_ab_c.-d-ef", want: true},
|
{name: "_ab_c.-d-ef", want: true},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
if got := validateName(tt.name); got != tt.want {
|
if got := validateName(tt.name, false); got != tt.want {
|
||||||
t.Errorf("validateName() = %v, want %v", got, tt.want)
|
t.Errorf("validateName() = %v, want %v", got, tt.want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_validateHostname(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
s string
|
||||||
|
allowWildcardSubdomain bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Normal",
|
||||||
|
args: args{
|
||||||
|
s: "example.com",
|
||||||
|
allowWildcardSubdomain: true,
|
||||||
|
},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "wildcard subdomain for TUN-358",
|
||||||
|
args: args{
|
||||||
|
s: "*.ehrig.io",
|
||||||
|
allowWildcardSubdomain: true,
|
||||||
|
},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Misplaced wildcard",
|
||||||
|
args: args{
|
||||||
|
s: "subdomain.*.ehrig.io",
|
||||||
|
allowWildcardSubdomain: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid domain",
|
||||||
|
args: args{
|
||||||
|
s: "..",
|
||||||
|
allowWildcardSubdomain: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid domain",
|
||||||
|
args: args{
|
||||||
|
s: "..",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := validateHostname(tt.args.s, tt.args.allowWildcardSubdomain); got != tt.want {
|
||||||
|
t.Errorf("validateHostname() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue