2020-10-09 00:12:29 +00:00
package ingress
2020-10-06 17:12:52 +00:00
import (
2020-11-11 13:14:51 +00:00
"flag"
2020-11-04 15:44:15 +00:00
"fmt"
2021-01-17 20:22:53 +00:00
"net/http"
2020-10-06 17:12:52 +00:00
"net/url"
2020-11-04 15:44:15 +00:00
"regexp"
2020-10-06 17:12:52 +00:00
"testing"
2020-11-11 13:14:51 +00:00
"time"
2020-10-06 17:12:52 +00:00
2020-11-04 15:44:15 +00:00
"github.com/stretchr/testify/assert"
2020-10-06 17:12:52 +00:00
"github.com/stretchr/testify/require"
2020-11-11 13:14:51 +00:00
"github.com/urfave/cli/v2"
2022-05-30 15:19:51 +00:00
yaml "gopkg.in/yaml.v3"
2020-10-20 17:00:34 +00:00
2021-03-08 16:46:23 +00:00
"github.com/cloudflare/cloudflared/config"
2021-03-01 22:26:37 +00:00
"github.com/cloudflare/cloudflared/ipaccess"
2020-11-11 13:14:51 +00:00
"github.com/cloudflare/cloudflared/tlsconfig"
2020-10-06 17:12:52 +00:00
)
2020-10-15 21:41:03 +00:00
func TestParseUnixSocket ( t * testing . T ) {
rawYAML := `
ingress :
- service : unix : / tmp / echo . sock
`
2020-10-30 21:37:40 +00:00
ing , err := ParseIngress ( MustReadIngress ( rawYAML ) )
2020-10-15 21:41:03 +00:00
require . NoError ( t , err )
2022-02-28 20:07:47 +00:00
s , ok := ing . Rules [ 0 ] . Service . ( * unixSocketPath )
2020-10-15 21:41:03 +00:00
require . True ( t , ok )
2022-02-28 20:07:47 +00:00
require . Equal ( t , "http" , s . scheme )
}
func TestParseUnixSocketTLS ( t * testing . T ) {
rawYAML := `
ingress :
- service : unix + tls : / tmp / echo . sock
`
ing , err := ParseIngress ( MustReadIngress ( rawYAML ) )
require . NoError ( t , err )
s , ok := ing . Rules [ 0 ] . Service . ( * unixSocketPath )
require . True ( t , ok )
require . Equal ( t , "https" , s . scheme )
2020-10-15 21:41:03 +00:00
}
2023-12-14 16:29:40 +00:00
func TestParseIngressNilConfig ( t * testing . T ) {
_ , err := ParseIngress ( nil )
require . Error ( t , err )
}
2023-03-09 23:23:11 +00:00
func TestParseIngress ( t * testing . T ) {
2020-10-20 14:29:13 +00:00
localhost8000 := MustParseURL ( t , "https://localhost:8000" )
localhost8001 := MustParseURL ( t , "https://localhost:8001" )
2020-11-04 18:22:21 +00:00
fourOhFour := newStatusCode ( 404 )
2022-01-28 14:37:17 +00:00
defaultConfig := setConfig ( originRequestFromConfig ( config . OriginRequestConfig { } ) , config . OriginRequestConfig { } )
2020-10-15 21:41:03 +00:00
require . Equal ( t , defaultKeepAliveConnections , defaultConfig . KeepAliveConnections )
2020-11-15 18:47:51 +00:00
tr := true
2020-10-20 14:29:13 +00:00
type args struct {
rawYAML string
}
tests := [ ] struct {
name string
args args
2020-10-15 21:41:03 +00:00
want [ ] Rule
2020-10-20 14:29:13 +00:00
wantErr bool
} {
{
name : "Empty file" ,
args : args { rawYAML : "" } ,
wantErr : true ,
} ,
{
name : "Multiple rules" ,
args : args { rawYAML : `
ingress :
- hostname : tunnel1 . example . com
service : https : //localhost:8000
- hostname : "*"
service : https : //localhost:8001
` } ,
2020-10-15 21:41:03 +00:00
want : [ ] Rule {
2020-10-20 14:29:13 +00:00
{
Hostname : "tunnel1.example.com" ,
2020-12-09 21:46:53 +00:00
Service : & httpService { url : localhost8000 } ,
2020-10-15 21:41:03 +00:00
Config : defaultConfig ,
2020-10-20 14:29:13 +00:00
} ,
{
Hostname : "*" ,
2020-12-09 21:46:53 +00:00
Service : & httpService { url : localhost8001 } ,
2020-10-15 21:41:03 +00:00
Config : defaultConfig ,
2020-10-20 14:29:13 +00:00
} ,
2020-10-15 21:41:03 +00:00
} ,
2020-10-20 14:29:13 +00:00
} ,
{
name : "Extra keys" ,
args : args { rawYAML : `
ingress :
- hostname : "*"
service : https : //localhost:8000
extraKey : extraValue
` } ,
2020-10-15 21:41:03 +00:00
want : [ ] Rule {
2020-10-20 14:29:13 +00:00
{
Hostname : "*" ,
2020-12-09 21:46:53 +00:00
Service : & httpService { url : localhost8000 } ,
Config : defaultConfig ,
} ,
} ,
} ,
{
name : "ws service" ,
args : args { rawYAML : `
ingress :
- hostname : "*"
service : wss : //localhost:8000
` } ,
want : [ ] Rule {
{
Hostname : "*" ,
Service : & httpService { url : MustParseURL ( t , "wss://localhost:8000" ) } ,
2020-10-15 21:41:03 +00:00
Config : defaultConfig ,
2020-10-20 14:29:13 +00:00
} ,
2020-10-15 21:41:03 +00:00
} ,
2020-10-20 14:29:13 +00:00
} ,
{
name : "Hostname can be omitted" ,
args : args { rawYAML : `
ingress :
- service : https : //localhost:8000
` } ,
2020-10-15 21:41:03 +00:00
want : [ ] Rule {
2020-10-20 14:29:13 +00:00
{
2020-12-09 21:46:53 +00:00
Service : & httpService { url : localhost8000 } ,
2020-10-15 21:41:03 +00:00
Config : defaultConfig ,
2020-10-20 14:29:13 +00:00
} ,
2020-10-15 21:41:03 +00:00
} ,
2020-10-20 14:29:13 +00:00
} ,
2022-09-22 23:19:06 +00:00
{
name : "Unicode domain" ,
args : args { rawYAML : `
ingress :
- hostname : môô . cloudflare . com
service : https : //localhost:8000
- service : https : //localhost:8001
` } ,
want : [ ] Rule {
{
Hostname : "môô.cloudflare.com" ,
punycodeHostname : "xn--m-xgaa.cloudflare.com" ,
Service : & httpService { url : localhost8000 } ,
Config : defaultConfig ,
} ,
{
Service : & httpService { url : localhost8001 } ,
Config : defaultConfig ,
} ,
} ,
} ,
{
name : "Invalid unicode domain" ,
args : args { rawYAML : fmt . Sprintf ( `
ingress :
- hostname : % s
service : https : //localhost:8000
` , string ( rune ( 0xd8f3 ) ) + ".cloudflare.com" ) } ,
wantErr : true ,
} ,
2020-10-20 14:29:13 +00:00
{
name : "Invalid service" ,
args : args { rawYAML : `
ingress :
- hostname : "*"
service : https : //local host:8000
` } ,
wantErr : true ,
} ,
{
name : "Last rule isn't catchall" ,
args : args { rawYAML : `
ingress :
- hostname : example . com
service : https : //localhost:8000
` } ,
wantErr : true ,
} ,
{
name : "First rule is catchall" ,
args : args { rawYAML : `
ingress :
- service : https : //localhost:8000
- hostname : example . com
service : https : //localhost:8000
` } ,
wantErr : true ,
} ,
{
name : "Catch-all rule can't have a path" ,
args : args { rawYAML : `
ingress :
- service : https : //localhost:8001
path : / subpath1 / ( . * ) / subpath2
` } ,
wantErr : true ,
} ,
{
name : "Invalid regex" ,
args : args { rawYAML : `
ingress :
- hostname : example . com
service : https : //localhost:8000
path : "*/subpath2"
- service : https : //localhost:8001
` } ,
wantErr : true ,
} ,
{
name : "Service must have a scheme" ,
args : args { rawYAML : `
ingress :
- service : localhost : 8000
` } ,
wantErr : true ,
} ,
{
name : "Wildcard not at start" ,
args : args { rawYAML : `
ingress :
- hostname : "test.*.example.com"
service : https : //localhost:8000
` } ,
wantErr : true ,
} ,
{
name : "Service can't have a path" ,
args : args { rawYAML : `
ingress :
- service : https : //localhost:8000/static/
` } ,
wantErr : true ,
} ,
2020-10-30 21:37:40 +00:00
{
name : "Invalid HTTP status" ,
args : args { rawYAML : `
ingress :
- service : http_status : asdf
2022-09-01 21:20:22 +00:00
` } ,
wantErr : true ,
} ,
{
name : "Invalid HTTP status code" ,
args : args { rawYAML : `
ingress :
- service : http_status : 8080
2020-10-30 21:37:40 +00:00
` } ,
wantErr : true ,
} ,
2020-11-04 18:22:21 +00:00
{
name : "Valid HTTP status" ,
args : args { rawYAML : `
ingress :
- service : http_status : 404
` } ,
want : [ ] Rule {
{
Hostname : "" ,
Service : & fourOhFour ,
Config : defaultConfig ,
} ,
} ,
} ,
2020-10-30 21:37:40 +00:00
{
name : "Valid hello world service" ,
args : args { rawYAML : `
ingress :
- service : hello_world
` } ,
want : [ ] Rule {
{
Hostname : "" ,
Service : new ( helloWorld ) ,
Config : defaultConfig ,
} ,
} ,
} ,
2020-12-09 21:46:53 +00:00
{
name : "TCP services" ,
args : args { rawYAML : `
ingress :
- hostname : tcp . foo . com
service : tcp : //127.0.0.1
- hostname : tcp2 . foo . com
service : tcp : //localhost:8000
- service : http_status : 404
` } ,
want : [ ] Rule {
{
Hostname : "tcp.foo.com" ,
2021-02-05 13:01:53 +00:00
Service : newTCPOverWSService ( MustParseURL ( t , "tcp://127.0.0.1:7864" ) ) ,
2020-12-09 21:46:53 +00:00
Config : defaultConfig ,
} ,
{
Hostname : "tcp2.foo.com" ,
2021-02-05 13:01:53 +00:00
Service : newTCPOverWSService ( MustParseURL ( t , "tcp://localhost:8000" ) ) ,
2020-12-09 21:46:53 +00:00
Config : defaultConfig ,
} ,
{
Service : & fourOhFour ,
Config : defaultConfig ,
} ,
} ,
} ,
{
name : "SSH services" ,
args : args { rawYAML : `
ingress :
- service : ssh : //127.0.0.1
` } ,
want : [ ] Rule {
{
2021-02-05 13:01:53 +00:00
Service : newTCPOverWSService ( MustParseURL ( t , "ssh://127.0.0.1:22" ) ) ,
2020-12-09 21:46:53 +00:00
Config : defaultConfig ,
} ,
} ,
} ,
{
name : "RDP services" ,
args : args { rawYAML : `
ingress :
- service : rdp : //127.0.0.1
` } ,
want : [ ] Rule {
{
2021-02-05 13:01:53 +00:00
Service : newTCPOverWSService ( MustParseURL ( t , "rdp://127.0.0.1:3389" ) ) ,
2020-12-09 21:46:53 +00:00
Config : defaultConfig ,
} ,
} ,
} ,
{
name : "SMB services" ,
args : args { rawYAML : `
ingress :
- service : smb : //127.0.0.1
` } ,
want : [ ] Rule {
{
2021-02-05 13:01:53 +00:00
Service : newTCPOverWSService ( MustParseURL ( t , "smb://127.0.0.1:445" ) ) ,
2020-12-09 21:46:53 +00:00
Config : defaultConfig ,
} ,
} ,
} ,
{
name : "Other TCP services" ,
args : args { rawYAML : `
ingress :
- service : ftp : //127.0.0.1
` } ,
want : [ ] Rule {
{
2021-02-05 13:01:53 +00:00
Service : newTCPOverWSService ( MustParseURL ( t , "ftp://127.0.0.1" ) ) ,
2020-12-09 21:46:53 +00:00
Config : defaultConfig ,
} ,
} ,
} ,
2021-03-01 22:26:37 +00:00
{
name : "SOCKS services" ,
args : args { rawYAML : `
ingress :
- hostname : socks . foo . com
service : socks - proxy
originRequest :
ipRules :
- prefix : 1.1 .1 .0 / 24
ports : [ 80 , 443 ]
allow : true
- prefix : 0.0 .0 .0 / 0
allow : false
- service : http_status : 404
` } ,
want : [ ] Rule {
{
Hostname : "socks.foo.com" ,
Service : newSocksProxyOverWSService ( accessPolicy ( ) ) ,
2022-01-28 14:37:17 +00:00
Config : setConfig ( originRequestFromConfig ( config . OriginRequestConfig { } ) , config . OriginRequestConfig { IPRules : [ ] config . IngressIPRule {
{
Prefix : ipRulePrefix ( "1.1.1.0/24" ) ,
Ports : [ ] int { 80 , 443 } ,
Allow : true ,
} ,
{
Prefix : ipRulePrefix ( "0.0.0.0/0" ) ,
Allow : false ,
} ,
} } ) ,
2021-03-01 22:26:37 +00:00
} ,
{
Service : & fourOhFour ,
Config : defaultConfig ,
} ,
} ,
} ,
2020-11-15 18:47:51 +00:00
{
name : "URL isn't necessary if using bastion" ,
args : args { rawYAML : `
ingress :
- hostname : bastion . foo . com
originRequest :
bastionMode : true
- service : http_status : 404
` } ,
want : [ ] Rule {
{
Hostname : "bastion.foo.com" ,
2021-02-05 13:01:53 +00:00
Service : newBastionService ( ) ,
2022-01-28 14:37:17 +00:00
Config : setConfig ( originRequestFromConfig ( config . OriginRequestConfig { } ) , config . OriginRequestConfig { BastionMode : & tr } ) ,
2020-11-15 18:47:51 +00:00
} ,
{
Service : & fourOhFour ,
Config : defaultConfig ,
} ,
} ,
} ,
{
name : "Bastion service" ,
args : args { rawYAML : `
ingress :
- hostname : bastion . foo . com
service : bastion
- service : http_status : 404
` } ,
want : [ ] Rule {
{
Hostname : "bastion.foo.com" ,
2021-02-05 13:01:53 +00:00
Service : newBastionService ( ) ,
2022-01-28 14:37:17 +00:00
Config : setConfig ( originRequestFromConfig ( config . OriginRequestConfig { } ) , config . OriginRequestConfig { BastionMode : & tr } ) ,
2020-11-15 18:47:51 +00:00
} ,
{
Service : & fourOhFour ,
Config : defaultConfig ,
} ,
} ,
} ,
2024-05-09 19:07:59 +00:00
{
name : "Bastion mode turned on with with custom service" ,
args : args { rawYAML : `
ingress :
- hostname : bastiondest . foo . com
service : http : //localhost:9000
originRequest :
bastionMode : true
- service : http_status : 404
` } ,
want : [ ] Rule {
{
Hostname : "bastiondest.foo.com" ,
Service : newBastionServiceWithDest ( MustParseURL ( t , "http://localhost:9000" ) ) ,
Config : setConfig ( originRequestFromConfig ( config . OriginRequestConfig { } ) , config . OriginRequestConfig { BastionMode : & tr } ) ,
} ,
{
Service : & fourOhFour ,
Config : defaultConfig ,
} ,
} ,
} ,
{
name : "TCP service with Bastion mode turned off" ,
args : args { rawYAML : `
ingress :
- hostname : tcp . foo . com
service : tcp : //localhost:9000
originRequest :
bastionMode : false
- service : http_status : 404
` } ,
want : [ ] Rule {
{
Hostname : "tcp.foo.com" ,
Service : newTCPOverWSService ( MustParseURL ( t , "tcp://localhost:9000" ) ) ,
Config : defaultConfig ,
} ,
{
Service : & fourOhFour ,
Config : defaultConfig ,
} ,
} ,
} ,
2020-11-04 15:44:15 +00:00
{
name : "Hostname contains port" ,
args : args { rawYAML : `
ingress :
- hostname : "test.example.com:443"
service : https : //localhost:8000
- hostname : "*"
service : https : //localhost:8001
` } ,
wantErr : true ,
} ,
2020-10-20 14:29:13 +00:00
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
2020-10-30 21:37:40 +00:00
got , err := ParseIngress ( MustReadIngress ( tt . args . rawYAML ) )
2020-10-20 14:29:13 +00:00
if ( err != nil ) != tt . wantErr {
2020-10-30 21:37:40 +00:00
t . Errorf ( "ParseIngress() error = %v, wantErr %v" , err , tt . wantErr )
2020-10-20 14:29:13 +00:00
return
}
2020-10-30 21:37:40 +00:00
require . Equal ( t , tt . want , got . Rules )
2020-10-20 14:29:13 +00:00
} )
}
}
2020-10-07 21:34:53 +00:00
2022-01-28 14:37:17 +00:00
func ipRulePrefix ( s string ) * string {
return & s
}
2020-11-11 13:14:51 +00:00
func TestSingleOriginSetsConfig ( t * testing . T ) {
flagSet := flag . NewFlagSet ( t . Name ( ) , flag . PanicOnError )
flagSet . Bool ( "hello-world" , true , "" )
flagSet . Duration ( ProxyConnectTimeoutFlag , time . Second , "" )
flagSet . Duration ( ProxyTLSTimeoutFlag , time . Second , "" )
2021-05-06 20:10:47 +00:00
flagSet . Duration ( ProxyTCPKeepAliveFlag , time . Second , "" )
2020-11-11 13:14:51 +00:00
flagSet . Bool ( ProxyNoHappyEyeballsFlag , true , "" )
flagSet . Int ( ProxyKeepAliveConnectionsFlag , 10 , "" )
flagSet . Duration ( ProxyKeepAliveTimeoutFlag , time . Second , "" )
flagSet . String ( HTTPHostHeaderFlag , "example.com:8080" , "" )
flagSet . String ( OriginServerNameFlag , "example.com" , "" )
flagSet . String ( tlsconfig . OriginCAPoolFlag , "/etc/certs/ca.pem" , "" )
flagSet . Bool ( NoTLSVerifyFlag , true , "" )
flagSet . Bool ( NoChunkedEncodingFlag , true , "" )
flagSet . Bool ( config . BastionFlag , true , "" )
flagSet . String ( ProxyAddressFlag , "localhost:8080" , "" )
flagSet . Uint ( ProxyPortFlag , 8080 , "" )
flagSet . Bool ( Socks5Flag , true , "" )
cliCtx := cli . NewContext ( cli . NewApp ( ) , flagSet , nil )
err := cliCtx . Set ( "hello-world" , "true" )
require . NoError ( t , err )
err = cliCtx . Set ( ProxyConnectTimeoutFlag , "1s" )
require . NoError ( t , err )
err = cliCtx . Set ( ProxyTLSTimeoutFlag , "1s" )
require . NoError ( t , err )
2021-05-06 20:10:47 +00:00
err = cliCtx . Set ( ProxyTCPKeepAliveFlag , "1s" )
2020-11-11 13:14:51 +00:00
require . NoError ( t , err )
err = cliCtx . Set ( ProxyNoHappyEyeballsFlag , "true" )
require . NoError ( t , err )
err = cliCtx . Set ( ProxyKeepAliveConnectionsFlag , "10" )
require . NoError ( t , err )
err = cliCtx . Set ( ProxyKeepAliveTimeoutFlag , "1s" )
require . NoError ( t , err )
err = cliCtx . Set ( HTTPHostHeaderFlag , "example.com:8080" )
require . NoError ( t , err )
err = cliCtx . Set ( OriginServerNameFlag , "example.com" )
require . NoError ( t , err )
err = cliCtx . Set ( tlsconfig . OriginCAPoolFlag , "/etc/certs/ca.pem" )
require . NoError ( t , err )
err = cliCtx . Set ( NoTLSVerifyFlag , "true" )
require . NoError ( t , err )
err = cliCtx . Set ( NoChunkedEncodingFlag , "true" )
require . NoError ( t , err )
err = cliCtx . Set ( config . BastionFlag , "true" )
require . NoError ( t , err )
err = cliCtx . Set ( ProxyAddressFlag , "localhost:8080" )
require . NoError ( t , err )
err = cliCtx . Set ( ProxyPortFlag , "8080" )
require . NoError ( t , err )
err = cliCtx . Set ( Socks5Flag , "true" )
require . NoError ( t , err )
allowURLFromArgs := false
require . NoError ( t , err )
2023-03-09 23:23:11 +00:00
ingress , err := parseCLIIngress ( cliCtx , allowURLFromArgs )
2020-11-11 13:14:51 +00:00
require . NoError ( t , err )
2022-03-14 17:51:10 +00:00
assert . Equal ( t , config . CustomDuration { Duration : time . Second } , ingress . Rules [ 0 ] . Config . ConnectTimeout )
assert . Equal ( t , config . CustomDuration { Duration : time . Second } , ingress . Rules [ 0 ] . Config . TLSTimeout )
assert . Equal ( t , config . CustomDuration { Duration : time . Second } , ingress . Rules [ 0 ] . Config . TCPKeepAlive )
2020-11-11 13:14:51 +00:00
assert . True ( t , ingress . Rules [ 0 ] . Config . NoHappyEyeballs )
assert . Equal ( t , 10 , ingress . Rules [ 0 ] . Config . KeepAliveConnections )
2022-03-14 17:51:10 +00:00
assert . Equal ( t , config . CustomDuration { Duration : time . Second } , ingress . Rules [ 0 ] . Config . KeepAliveTimeout )
2020-11-11 13:14:51 +00:00
assert . Equal ( t , "example.com:8080" , ingress . Rules [ 0 ] . Config . HTTPHostHeader )
assert . Equal ( t , "example.com" , ingress . Rules [ 0 ] . Config . OriginServerName )
assert . Equal ( t , "/etc/certs/ca.pem" , ingress . Rules [ 0 ] . Config . CAPool )
assert . True ( t , ingress . Rules [ 0 ] . Config . NoTLSVerify )
assert . True ( t , ingress . Rules [ 0 ] . Config . DisableChunkedEncoding )
assert . True ( t , ingress . Rules [ 0 ] . Config . BastionMode )
assert . Equal ( t , "localhost:8080" , ingress . Rules [ 0 ] . Config . ProxyAddress )
assert . Equal ( t , uint ( 8080 ) , ingress . Rules [ 0 ] . Config . ProxyPort )
assert . Equal ( t , socksProxy , ingress . Rules [ 0 ] . Config . ProxyType )
}
2023-03-09 23:23:11 +00:00
func TestSingleOriginServices ( t * testing . T ) {
host := "://localhost:8080"
httpURL := urlMustParse ( "http" + host )
tcpURL := urlMustParse ( "tcp" + host )
unix := "unix://service"
newCli := func ( params ... string ) * cli . Context {
flagSet := flag . NewFlagSet ( t . Name ( ) , flag . PanicOnError )
flagSet . Bool ( "hello-world" , false , "" )
flagSet . Bool ( "bastion" , false , "" )
flagSet . String ( "url" , "" , "" )
flagSet . String ( "unix-socket" , "" , "" )
cliCtx := cli . NewContext ( cli . NewApp ( ) , flagSet , nil )
for i := 0 ; i < len ( params ) ; i += 2 {
cliCtx . Set ( params [ i ] , params [ i + 1 ] )
}
return cliCtx
}
tests := [ ] struct {
name string
cli * cli . Context
expectedService OriginService
err error
} {
{
name : "Valid hello-world" ,
cli : newCli ( "hello-world" , "true" ) ,
expectedService : & helloWorld { } ,
} ,
{
name : "Valid bastion" ,
cli : newCli ( "bastion" , "true" ) ,
expectedService : newBastionService ( ) ,
} ,
{
name : "Valid http url" ,
cli : newCli ( "url" , httpURL . String ( ) ) ,
expectedService : & httpService { url : httpURL } ,
} ,
{
name : "Valid tcp url" ,
cli : newCli ( "url" , tcpURL . String ( ) ) ,
expectedService : newTCPOverWSService ( tcpURL ) ,
} ,
{
name : "Valid unix-socket" ,
cli : newCli ( "unix-socket" , unix ) ,
expectedService : & unixSocketPath { path : unix , scheme : "http" } ,
} ,
{
name : "No origins defined" ,
cli : newCli ( ) ,
err : ErrNoIngressRulesCLI ,
} ,
}
for _ , test := range tests {
t . Run ( test . name , func ( t * testing . T ) {
ingress , err := parseCLIIngress ( test . cli , false )
require . Equal ( t , err , test . err )
if test . err != nil {
return
}
require . Equal ( t , 1 , len ( ingress . Rules ) )
rule := ingress . Rules [ 0 ]
require . Equal ( t , test . expectedService , rule . Service )
} )
}
}
func urlMustParse ( s string ) * url . URL {
u , err := url . Parse ( s )
if err != nil {
panic ( err )
}
return u
}
func TestSingleOriginServices_URL ( t * testing . T ) {
host := "://localhost:8080"
newCli := func ( param string , value string ) * cli . Context {
flagSet := flag . NewFlagSet ( t . Name ( ) , flag . PanicOnError )
flagSet . String ( "url" , "" , "" )
cliCtx := cli . NewContext ( cli . NewApp ( ) , flagSet , nil )
cliCtx . Set ( param , value )
return cliCtx
}
httpTests := [ ] string { "http" , "https" }
for _ , test := range httpTests {
t . Run ( test , func ( t * testing . T ) {
url := urlMustParse ( test + host )
ingress , err := parseCLIIngress ( newCli ( "url" , url . String ( ) ) , false )
require . NoError ( t , err )
require . Equal ( t , 1 , len ( ingress . Rules ) )
rule := ingress . Rules [ 0 ]
require . Equal ( t , & httpService { url : url } , rule . Service )
} )
}
tcpTests := [ ] string { "ssh" , "rdp" , "smb" , "tcp" }
for _ , test := range tcpTests {
t . Run ( test , func ( t * testing . T ) {
url := urlMustParse ( test + host )
ingress , err := parseCLIIngress ( newCli ( "url" , url . String ( ) ) , false )
require . NoError ( t , err )
require . Equal ( t , 1 , len ( ingress . Rules ) )
rule := ingress . Rules [ 0 ]
require . Equal ( t , newTCPOverWSService ( url ) , rule . Service )
} )
}
}
2020-11-04 15:44:15 +00:00
func TestFindMatchingRule ( t * testing . T ) {
2024-05-09 19:07:59 +00:00
2020-11-04 15:44:15 +00:00
ingress := Ingress {
Rules : [ ] Rule {
{
Hostname : "tunnel-a.example.com" ,
Path : nil ,
} ,
{
Hostname : "tunnel-b.example.com" ,
2022-03-14 17:51:10 +00:00
Path : MustParsePath ( t , "/health" ) ,
2020-11-04 15:44:15 +00:00
} ,
2024-05-09 19:07:59 +00:00
{
Hostname : "tunnel-d.example.com" ,
Path : nil ,
Config : OriginRequestConfig {
BastionMode : true ,
} ,
} ,
2020-11-04 15:44:15 +00:00
{
Hostname : "*" ,
} ,
} ,
}
tests := [ ] struct {
2024-05-09 19:07:59 +00:00
host string
path string
cfJumpDestinationHeader string
req * http . Request
wantRuleIndex int
2020-11-04 15:44:15 +00:00
} {
{
2024-05-09 19:07:59 +00:00
host : "tunnel-a.example.com" ,
path : "/" ,
cfJumpDestinationHeader : "" ,
wantRuleIndex : 0 ,
} ,
{
host : "tunnel-a.example.com" ,
path : "/pages/about" ,
cfJumpDestinationHeader : "" ,
wantRuleIndex : 0 ,
} ,
{
host : "tunnel-a.example.com:443" ,
path : "/pages/about" ,
cfJumpDestinationHeader : "" ,
wantRuleIndex : 0 ,
2020-11-04 15:44:15 +00:00
} ,
{
2024-05-09 19:07:59 +00:00
host : "tunnel-b.example.com" ,
path : "/health" ,
cfJumpDestinationHeader : "" ,
wantRuleIndex : 1 ,
2020-11-04 15:44:15 +00:00
} ,
{
2024-05-09 19:07:59 +00:00
host : "tunnel-b.example.com" ,
path : "/index.html" ,
cfJumpDestinationHeader : "" ,
wantRuleIndex : 3 ,
2020-11-04 15:44:15 +00:00
} ,
{
2024-05-09 19:07:59 +00:00
host : "tunnel-d.example.com" ,
path : "/" ,
cfJumpDestinationHeader : "https://tunnel-d.example.com" ,
wantRuleIndex : 2 ,
2020-11-04 15:44:15 +00:00
} ,
{
2024-05-09 19:07:59 +00:00
host : "tunnel-d.example.com" ,
path : "/" ,
cfJumpDestinationHeader : "https://tunnel-d.example.com" ,
wantRuleIndex : 2 ,
2020-11-04 15:44:15 +00:00
} ,
{
2024-05-09 19:07:59 +00:00
host : "tunnel-c.example.com" ,
path : "/" ,
cfJumpDestinationHeader : "" ,
wantRuleIndex : 3 ,
2020-11-04 15:44:15 +00:00
} ,
}
2021-01-17 20:22:53 +00:00
for _ , test := range tests {
2024-05-09 19:07:59 +00:00
_ , ruleIndex := ingress . FindMatchingRule ( test . host , test . path , test . cfJumpDestinationHeader )
2021-01-17 20:22:53 +00:00
assert . Equal ( t , test . wantRuleIndex , ruleIndex , fmt . Sprintf ( "Expect host=%s, path=%s to match rule %d, got %d" , test . host , test . path , test . wantRuleIndex , ruleIndex ) )
2020-11-04 15:44:15 +00:00
}
}
2020-12-09 21:46:53 +00:00
func TestIsHTTPService ( t * testing . T ) {
tests := [ ] struct {
url * url . URL
isHTTP bool
} {
{
url : MustParseURL ( t , "http://localhost" ) ,
isHTTP : true ,
} ,
{
url : MustParseURL ( t , "https://127.0.0.1:8000" ) ,
isHTTP : true ,
} ,
{
url : MustParseURL ( t , "ws://localhost" ) ,
isHTTP : true ,
} ,
{
url : MustParseURL ( t , "wss://localhost:8000" ) ,
isHTTP : true ,
} ,
{
url : MustParseURL ( t , "tcp://localhost:9000" ) ,
isHTTP : false ,
} ,
}
for _ , test := range tests {
assert . Equal ( t , test . isHTTP , isHTTPService ( test . url ) )
}
}
2022-03-14 17:51:10 +00:00
func MustParsePath ( t * testing . T , path string ) * Regexp {
2020-11-04 15:44:15 +00:00
regexp , err := regexp . Compile ( path )
assert . NoError ( t , err )
2022-03-14 17:51:10 +00:00
return & Regexp { Regexp : regexp }
2020-11-04 15:44:15 +00:00
}
2020-10-20 14:29:13 +00:00
func MustParseURL ( t * testing . T , rawURL string ) * url . URL {
2020-10-07 21:34:53 +00:00
u , err := url . Parse ( rawURL )
require . NoError ( t , err )
return u
}
2021-03-01 22:26:37 +00:00
func accessPolicy ( ) * ipaccess . Policy {
cidr1 := "1.1.1.0/24"
cidr2 := "0.0.0.0/0"
rule1 , _ := ipaccess . NewRuleByCIDR ( & cidr1 , [ ] int { 80 , 443 } , true )
rule2 , _ := ipaccess . NewRuleByCIDR ( & cidr2 , nil , false )
rules := [ ] ipaccess . Rule { rule1 , rule2 }
accessPolicy , _ := ipaccess . NewPolicy ( false , rules )
return accessPolicy
}
2020-10-20 14:29:13 +00:00
func BenchmarkFindMatch ( b * testing . B ) {
rulesYAML := `
ingress :
- hostname : tunnel1 . example . com
service : https : //localhost:8000
- hostname : tunnel2 . example . com
service : https : //localhost:8001
- hostname : "*"
service : https : //localhost:8002
`
2020-10-30 21:37:40 +00:00
ing , err := ParseIngress ( MustReadIngress ( rulesYAML ) )
2020-10-20 14:29:13 +00:00
if err != nil {
b . Error ( err )
}
2021-01-17 20:22:53 +00:00
2020-10-20 14:29:13 +00:00
for n := 0 ; n < b . N ; n ++ {
2024-05-09 19:07:59 +00:00
ing . FindMatchingRule ( "tunnel1.example.com" , "" , "" )
ing . FindMatchingRule ( "tunnel2.example.com" , "" , "" )
ing . FindMatchingRule ( "tunnel3.example.com" , "" , "" )
2020-10-20 14:29:13 +00:00
}
}
2022-11-09 12:12:37 +00:00
func TestParseAccessConfig ( t * testing . T ) {
tests := [ ] struct {
name string
cfg config . AccessConfig
expectError bool
} {
{
name : "Config required with teamName only" ,
cfg : config . AccessConfig { Required : true , TeamName : "team" } ,
expectError : false ,
} ,
{
name : "required false" ,
cfg : config . AccessConfig { Required : false } ,
expectError : false ,
} ,
{
name : "required true but empty config" ,
cfg : config . AccessConfig { Required : true } ,
expectError : false ,
} ,
{
name : "complete config" ,
cfg : config . AccessConfig { Required : true , TeamName : "team" , AudTag : [ ] string { "a" } } ,
expectError : false ,
} ,
{
name : "required true with audTags but no teamName" ,
cfg : config . AccessConfig { Required : true , AudTag : [ ] string { "a" } } ,
expectError : true ,
} ,
}
for _ , test := range tests {
t . Run ( test . name , func ( t * testing . T ) {
err := validateAccessConfiguration ( & test . cfg )
require . Equal ( t , err != nil , test . expectError )
} )
}
}
2020-10-20 17:00:34 +00:00
func MustReadIngress ( s string ) * config . Configuration {
var conf config . Configuration
err := yaml . Unmarshal ( [ ] byte ( s ) , & conf )
2020-10-20 14:29:13 +00:00
if err != nil {
panic ( err )
}
2020-10-20 17:00:34 +00:00
return & conf
2020-10-20 14:29:13 +00:00
}