Skip to content

Commit a08a703

Browse files
committed
TUN-3578: cloudflared tunnel route dns should allow wildcard subdomains
1 parent 87203bb commit a08a703

File tree

2 files changed

+76
-17
lines changed

2 files changed

+76
-17
lines changed

cmd/cloudflared/tunnel/subcommands.go

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ func buildCreateCommand() *cli.Command {
103103
Usage: "Create a new tunnel with given name",
104104
UsageText: "cloudflared tunnel [tunnel command options] create [subcommand options] NAME",
105105
Description: `Creates a tunnel, registers it with Cloudflare edge and generates credential file used to run this tunnel.
106-
Use "cloudflared tunnel route" subcommand to map a DNS name to this tunnel and "cloudflared tunnel run" to start the connection.
106+
Use "cloudflared tunnel route" subcommand to map a DNS name to this tunnel and "cloudflared tunnel run" to start the connection.
107107
108108
For example, to create a tunnel named 'my-tunnel' run:
109109
@@ -329,13 +329,13 @@ func buildRunCommand() *cli.Command {
329329
Before: SetFlagsFromConfigFile,
330330
Usage: "Proxy a local web server by running the given tunnel",
331331
UsageText: "cloudflared tunnel [tunnel command options] run [subcommand options] [TUNNEL]",
332-
Description: `Runs the tunnel identified by name or UUUD, creating highly available connections
332+
Description: `Runs the tunnel identified by name or UUUD, creating highly available connections
333333
between your server and the Cloudflare edge. You can provide name or UUID of tunnel to run either as the
334334
last command line argument or in the configuration file using "tunnel: TUNNEL".
335335
336-
This command requires the tunnel credentials file created when "cloudflared tunnel create" was run,
336+
This command requires the tunnel credentials file created when "cloudflared tunnel create" was run,
337337
however it does not need access to cert.pem from "cloudflared login" if you identify the tunnel by UUID.
338-
If you experience other problems running the tunnel, "cloudflared tunnel cleanup" may help by removing
338+
If you experience other problems running the tunnel, "cloudflared tunnel cleanup" may help by removing
339339
any old connection records.
340340
`,
341341
Flags: flags,
@@ -431,7 +431,7 @@ func dnsRouteFromArg(c *cli.Context) (tunnelstore.Route, error) {
431431
userHostname := c.Args().Get(userHostnameIndex)
432432
if userHostname == "" {
433433
return nil, cliutil.UsageError("The third argument should be the hostname")
434-
} else if !validateHostname(userHostname) {
434+
} else if !validateHostname(userHostname, true) {
435435
return nil, errors.Errorf("%s is not a valid hostname", userHostname)
436436
}
437437
return tunnelstore.NewDNSRoute(userHostname), nil
@@ -449,34 +449,38 @@ func lbRouteFromArg(c *cli.Context) (tunnelstore.Route, error) {
449449
lbName := c.Args().Get(lbNameIndex)
450450
if lbName == "" {
451451
return nil, cliutil.UsageError("The third argument should be the load balancer name")
452-
} else if !validateHostname(lbName) {
452+
} else if !validateHostname(lbName, true) {
453453
return nil, errors.Errorf("%s is not a valid load balancer name", lbName)
454454
}
455455

456456
lbPool := c.Args().Get(lbPoolIndex)
457457
if lbPool == "" {
458458
return nil, cliutil.UsageError("The fourth argument should be the pool name")
459-
} else if !validateName(lbPool) {
459+
} else if !validateName(lbPool, false) {
460460
return nil, errors.Errorf("%s is not a valid pool name", lbPool)
461461
}
462462

463463
return tunnelstore.NewLBRoute(lbName, lbPool), nil
464464
}
465465

466466
var nameRegex = regexp.MustCompile("^[_a-zA-Z0-9][-_.a-zA-Z0-9]*$")
467+
var hostNameRegex = regexp.MustCompile("^[*_a-zA-Z0-9][-_.a-zA-Z0-9]*$")
467468

468-
func validateName(s string) bool {
469+
func validateName(s string, allowWildcardSubdomain bool) bool {
470+
if allowWildcardSubdomain {
471+
return hostNameRegex.MatchString(s)
472+
}
469473
return nameRegex.MatchString(s)
470474
}
471475

472-
func validateHostname(s string) bool {
476+
func validateHostname(s string, allowWildcardSubdomain bool) bool {
473477
// Slightly stricter than PunyCodeProfile
474478
idnaProfile := idna.New(
475479
idna.ValidateLabels(true),
476480
idna.VerifyDNSLength(true))
477481

478482
puny, err := idnaProfile.ToASCII(s)
479-
return err == nil && validateName(puny)
483+
return err == nil && validateName(puny, allowWildcardSubdomain)
480484
}
481485

482486
func routeCommand(c *cli.Context) error {
@@ -535,13 +539,13 @@ func commandHelpTemplate() string {
535539
}
536540
const template = `NAME:
537541
{{.HelpName}} - {{.Usage}}
538-
542+
539543
USAGE:
540544
{{.UsageText}}
541-
545+
542546
DESCRIPTION:
543547
{{.Description}}
544-
548+
545549
TUNNEL COMMAND OPTIONS:
546550
%s
547551
SUBCOMMAND OPTIONS:

cmd/cloudflared/tunnel/subcommands_test.go

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,8 @@ import (
66
"testing"
77

88
"github.com/cloudflare/cloudflared/tunnelstore"
9-
"github.com/mitchellh/go-homedir"
10-
119
"github.com/google/uuid"
10+
"github.com/mitchellh/go-homedir"
1211
"github.com/stretchr/testify/assert"
1312
)
1413

@@ -117,8 +116,64 @@ func TestValidateName(t *testing.T) {
117116
{name: "_ab_c.-d-ef", want: true},
118117
}
119118
for _, tt := range tests {
120-
if got := validateName(tt.name); got != tt.want {
119+
if got := validateName(tt.name, false); got != tt.want {
121120
t.Errorf("validateName() = %v, want %v", got, tt.want)
122121
}
123122
}
124-
}
123+
}
124+
125+
func Test_validateHostname(t *testing.T) {
126+
type args struct {
127+
s string
128+
allowWildcardSubdomain bool
129+
}
130+
tests := []struct {
131+
name string
132+
args args
133+
want bool
134+
}{
135+
{
136+
name: "Normal",
137+
args: args{
138+
s: "example.com",
139+
allowWildcardSubdomain: true,
140+
},
141+
want: true,
142+
},
143+
{
144+
name: "wildcard subdomain for TUN-358",
145+
args: args{
146+
s: "*.ehrig.io",
147+
allowWildcardSubdomain: true,
148+
},
149+
want: true,
150+
},
151+
{
152+
name: "Misplaced wildcard",
153+
args: args{
154+
s: "subdomain.*.ehrig.io",
155+
allowWildcardSubdomain: true,
156+
},
157+
},
158+
{
159+
name: "Invalid domain",
160+
args: args{
161+
s: "..",
162+
allowWildcardSubdomain: true,
163+
},
164+
},
165+
{
166+
name: "Invalid domain",
167+
args: args{
168+
s: "..",
169+
},
170+
},
171+
}
172+
for _, tt := range tests {
173+
t.Run(tt.name, func(t *testing.T) {
174+
if got := validateHostname(tt.args.s, tt.args.allowWildcardSubdomain); got != tt.want {
175+
t.Errorf("validateHostname() = %v, want %v", got, tt.want)
176+
}
177+
})
178+
}
179+
}

0 commit comments

Comments
 (0)