mirror of
https://github.com/cloudflare/cloudflared.git
synced 2025-07-27 19:59:58 +00:00
TUN-5702: Allow to deserialize config from JSON
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package ingress
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
@@ -38,6 +39,34 @@ const (
|
||||
socksProxy = "socks"
|
||||
)
|
||||
|
||||
// RemoteConfig models ingress settings that can be managed remotely, for example through the dashboard.
|
||||
type RemoteConfig struct {
|
||||
Ingress Ingress
|
||||
WarpRouting config.WarpRoutingConfig
|
||||
}
|
||||
|
||||
type remoteConfigJSON struct {
|
||||
GlobalOriginRequest config.OriginRequestConfig `json:"originRequest"`
|
||||
IngressRules []config.UnvalidatedIngressRule `json:"ingress"`
|
||||
WarpRouting config.WarpRoutingConfig `json:"warp-routing"`
|
||||
}
|
||||
|
||||
func (rc *RemoteConfig) UnmarshalJSON(b []byte) error {
|
||||
var rawConfig remoteConfigJSON
|
||||
if err := json.Unmarshal(b, &rawConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
ingress, err := validateIngress(rawConfig.IngressRules, originRequestFromConfig(rawConfig.GlobalOriginRequest))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rc.Ingress = ingress
|
||||
rc.WarpRouting = rawConfig.WarpRouting
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func originRequestFromSingeRule(c *cli.Context) OriginRequestConfig {
|
||||
var connectTimeout time.Duration = defaultConnectTimeout
|
||||
var tlsTimeout time.Duration = defaultTLSTimeout
|
||||
@@ -119,7 +148,7 @@ func originRequestFromSingeRule(c *cli.Context) OriginRequestConfig {
|
||||
}
|
||||
}
|
||||
|
||||
func originRequestFromYAML(y config.OriginRequestConfig) OriginRequestConfig {
|
||||
func originRequestFromConfig(c config.OriginRequestConfig) OriginRequestConfig {
|
||||
out := OriginRequestConfig{
|
||||
ConnectTimeout: defaultConnectTimeout,
|
||||
TLSTimeout: defaultTLSTimeout,
|
||||
@@ -128,50 +157,58 @@ func originRequestFromYAML(y config.OriginRequestConfig) OriginRequestConfig {
|
||||
KeepAliveTimeout: defaultKeepAliveTimeout,
|
||||
ProxyAddress: defaultProxyAddress,
|
||||
}
|
||||
if y.ConnectTimeout != nil {
|
||||
out.ConnectTimeout = *y.ConnectTimeout
|
||||
if c.ConnectTimeout != nil {
|
||||
out.ConnectTimeout = *c.ConnectTimeout
|
||||
}
|
||||
if y.TLSTimeout != nil {
|
||||
out.TLSTimeout = *y.TLSTimeout
|
||||
if c.TLSTimeout != nil {
|
||||
out.TLSTimeout = *c.TLSTimeout
|
||||
}
|
||||
if y.TCPKeepAlive != nil {
|
||||
out.TCPKeepAlive = *y.TCPKeepAlive
|
||||
if c.TCPKeepAlive != nil {
|
||||
out.TCPKeepAlive = *c.TCPKeepAlive
|
||||
}
|
||||
if y.NoHappyEyeballs != nil {
|
||||
out.NoHappyEyeballs = *y.NoHappyEyeballs
|
||||
if c.NoHappyEyeballs != nil {
|
||||
out.NoHappyEyeballs = *c.NoHappyEyeballs
|
||||
}
|
||||
if y.KeepAliveConnections != nil {
|
||||
out.KeepAliveConnections = *y.KeepAliveConnections
|
||||
if c.KeepAliveConnections != nil {
|
||||
out.KeepAliveConnections = *c.KeepAliveConnections
|
||||
}
|
||||
if y.KeepAliveTimeout != nil {
|
||||
out.KeepAliveTimeout = *y.KeepAliveTimeout
|
||||
if c.KeepAliveTimeout != nil {
|
||||
out.KeepAliveTimeout = *c.KeepAliveTimeout
|
||||
}
|
||||
if y.HTTPHostHeader != nil {
|
||||
out.HTTPHostHeader = *y.HTTPHostHeader
|
||||
if c.HTTPHostHeader != nil {
|
||||
out.HTTPHostHeader = *c.HTTPHostHeader
|
||||
}
|
||||
if y.OriginServerName != nil {
|
||||
out.OriginServerName = *y.OriginServerName
|
||||
if c.OriginServerName != nil {
|
||||
out.OriginServerName = *c.OriginServerName
|
||||
}
|
||||
if y.CAPool != nil {
|
||||
out.CAPool = *y.CAPool
|
||||
if c.CAPool != nil {
|
||||
out.CAPool = *c.CAPool
|
||||
}
|
||||
if y.NoTLSVerify != nil {
|
||||
out.NoTLSVerify = *y.NoTLSVerify
|
||||
if c.NoTLSVerify != nil {
|
||||
out.NoTLSVerify = *c.NoTLSVerify
|
||||
}
|
||||
if y.DisableChunkedEncoding != nil {
|
||||
out.DisableChunkedEncoding = *y.DisableChunkedEncoding
|
||||
if c.DisableChunkedEncoding != nil {
|
||||
out.DisableChunkedEncoding = *c.DisableChunkedEncoding
|
||||
}
|
||||
if y.BastionMode != nil {
|
||||
out.BastionMode = *y.BastionMode
|
||||
if c.BastionMode != nil {
|
||||
out.BastionMode = *c.BastionMode
|
||||
}
|
||||
if y.ProxyAddress != nil {
|
||||
out.ProxyAddress = *y.ProxyAddress
|
||||
if c.ProxyAddress != nil {
|
||||
out.ProxyAddress = *c.ProxyAddress
|
||||
}
|
||||
if y.ProxyPort != nil {
|
||||
out.ProxyPort = *y.ProxyPort
|
||||
if c.ProxyPort != nil {
|
||||
out.ProxyPort = *c.ProxyPort
|
||||
}
|
||||
if y.ProxyType != nil {
|
||||
out.ProxyType = *y.ProxyType
|
||||
if c.ProxyType != nil {
|
||||
out.ProxyType = *c.ProxyType
|
||||
}
|
||||
if len(c.IPRules) > 0 {
|
||||
for _, r := range c.IPRules {
|
||||
rule, err := ipaccess.NewRuleByCIDR(r.Prefix, r.Ports, r.Allow)
|
||||
if err == nil {
|
||||
out.IPRules = append(out.IPRules, rule)
|
||||
}
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
@@ -188,10 +225,10 @@ type OriginRequestConfig struct {
|
||||
TCPKeepAlive time.Duration `yaml:"tcpKeepAlive"`
|
||||
// HTTP proxy should disable "happy eyeballs" for IPv4/v6 fallback
|
||||
NoHappyEyeballs bool `yaml:"noHappyEyeballs"`
|
||||
// HTTP proxy maximum keepalive connection pool size
|
||||
KeepAliveConnections int `yaml:"keepAliveConnections"`
|
||||
// HTTP proxy timeout for closing an idle connection
|
||||
KeepAliveTimeout time.Duration `yaml:"keepAliveTimeout"`
|
||||
// HTTP proxy maximum keepalive connection pool size
|
||||
KeepAliveConnections int `yaml:"keepAliveConnections"`
|
||||
// Sets the HTTP Host header for the local webserver.
|
||||
HTTPHostHeader string `yaml:"httpHostHeader"`
|
||||
// Hostname on the origin server certificate.
|
||||
@@ -308,6 +345,19 @@ func (defaults *OriginRequestConfig) setProxyType(overrides config.OriginRequest
|
||||
}
|
||||
}
|
||||
|
||||
func (defaults *OriginRequestConfig) setIPRules(overrides config.OriginRequestConfig) {
|
||||
if val := overrides.IPRules; len(val) > 0 {
|
||||
ipAccessRule := make([]ipaccess.Rule, len(overrides.IPRules))
|
||||
for i, r := range overrides.IPRules {
|
||||
rule, err := ipaccess.NewRuleByCIDR(r.Prefix, r.Ports, r.Allow)
|
||||
if err == nil {
|
||||
ipAccessRule[i] = rule
|
||||
}
|
||||
}
|
||||
defaults.IPRules = ipAccessRule
|
||||
}
|
||||
}
|
||||
|
||||
// SetConfig gets config for the requests that cloudflared sends to origins.
|
||||
// Each field has a setter method which sets a value for the field by trying to find:
|
||||
// 1. The user config for this rule
|
||||
@@ -332,5 +382,6 @@ func setConfig(defaults OriginRequestConfig, overrides config.OriginRequestConfi
|
||||
cfg.setProxyPort(overrides)
|
||||
cfg.setProxyAddress(overrides)
|
||||
cfg.setProxyType(overrides)
|
||||
cfg.setIPRules(overrides)
|
||||
return cfg
|
||||
}
|
422
ingress/config_test.go
Normal file
422
ingress/config_test.go
Normal file
@@ -0,0 +1,422 @@
|
||||
package ingress
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/urfave/cli/v2"
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/cloudflare/cloudflared/config"
|
||||
"github.com/cloudflare/cloudflared/ipaccess"
|
||||
)
|
||||
|
||||
// Ensure that the nullable config from `config` package and the
|
||||
// non-nullable config from `ingress` package have the same number of
|
||||
// fields.
|
||||
// This test ensures that programmers didn't add a new field to
|
||||
// one struct and forget to add it to the other ;)
|
||||
func TestCorrespondingFields(t *testing.T) {
|
||||
require.Equal(
|
||||
t,
|
||||
CountFields(t, config.OriginRequestConfig{}),
|
||||
CountFields(t, OriginRequestConfig{}),
|
||||
)
|
||||
}
|
||||
|
||||
func CountFields(t *testing.T, val interface{}) int {
|
||||
b, err := yaml.Marshal(val)
|
||||
require.NoError(t, err)
|
||||
m := make(map[string]interface{}, 0)
|
||||
err = yaml.Unmarshal(b, &m)
|
||||
require.NoError(t, err)
|
||||
return len(m)
|
||||
}
|
||||
|
||||
func TestUnmarshalRemoteConfigOverridesGlobal(t *testing.T) {
|
||||
rawConfig := []byte(`
|
||||
{
|
||||
"originRequest": {
|
||||
"connectTimeout": 90,
|
||||
"noHappyEyeballs": true
|
||||
},
|
||||
"ingress": [
|
||||
{
|
||||
"hostname": "jira.cfops.com",
|
||||
"service": "http://192.16.19.1:80",
|
||||
"originRequest": {
|
||||
"noTLSVerify": true,
|
||||
"connectTimeout": 10
|
||||
}
|
||||
},
|
||||
{
|
||||
"service": "http_status:404"
|
||||
}
|
||||
],
|
||||
"warp-routing": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
`)
|
||||
var remoteConfig RemoteConfig
|
||||
err := json.Unmarshal(rawConfig, &remoteConfig)
|
||||
require.NoError(t, err)
|
||||
require.True(t, remoteConfig.Ingress.Rules[0].Config.NoTLSVerify)
|
||||
require.True(t, remoteConfig.Ingress.defaults.NoHappyEyeballs)
|
||||
}
|
||||
|
||||
func TestOriginRequestConfigOverrides(t *testing.T) {
|
||||
validate := func(ing Ingress) {
|
||||
// Rule 0 didn't override anything, so it inherits the user-specified
|
||||
// root-level configuration.
|
||||
actual0 := ing.Rules[0].Config
|
||||
expected0 := OriginRequestConfig{
|
||||
ConnectTimeout: 1 * time.Minute,
|
||||
TLSTimeout: 1 * time.Second,
|
||||
TCPKeepAlive: 1 * time.Second,
|
||||
NoHappyEyeballs: true,
|
||||
KeepAliveTimeout: 1 * time.Second,
|
||||
KeepAliveConnections: 1,
|
||||
HTTPHostHeader: "abc",
|
||||
OriginServerName: "a1",
|
||||
CAPool: "/tmp/path0",
|
||||
NoTLSVerify: true,
|
||||
DisableChunkedEncoding: true,
|
||||
BastionMode: true,
|
||||
ProxyAddress: "127.1.2.3",
|
||||
ProxyPort: uint(100),
|
||||
ProxyType: "socks5",
|
||||
IPRules: []ipaccess.Rule{
|
||||
newIPRule(t, "10.0.0.0/8", []int{80, 8080}, false),
|
||||
newIPRule(t, "fc00::/7", []int{443, 4443}, true),
|
||||
},
|
||||
}
|
||||
require.Equal(t, expected0, actual0)
|
||||
|
||||
// Rule 1 overrode all the root-level config.
|
||||
actual1 := ing.Rules[1].Config
|
||||
expected1 := OriginRequestConfig{
|
||||
ConnectTimeout: 2 * time.Minute,
|
||||
TLSTimeout: 2 * time.Second,
|
||||
TCPKeepAlive: 2 * time.Second,
|
||||
NoHappyEyeballs: false,
|
||||
KeepAliveTimeout: 2 * time.Second,
|
||||
KeepAliveConnections: 2,
|
||||
HTTPHostHeader: "def",
|
||||
OriginServerName: "b2",
|
||||
CAPool: "/tmp/path1",
|
||||
NoTLSVerify: false,
|
||||
DisableChunkedEncoding: false,
|
||||
BastionMode: false,
|
||||
ProxyAddress: "interface",
|
||||
ProxyPort: uint(200),
|
||||
ProxyType: "",
|
||||
IPRules: []ipaccess.Rule{
|
||||
newIPRule(t, "10.0.0.0/16", []int{3000, 3030}, false),
|
||||
newIPRule(t, "192.16.0.0/24", []int{5000, 5050}, true),
|
||||
},
|
||||
}
|
||||
require.Equal(t, expected1, actual1)
|
||||
}
|
||||
|
||||
rulesYAML := `
|
||||
originRequest:
|
||||
connectTimeout: 1m
|
||||
tlsTimeout: 1s
|
||||
noHappyEyeballs: true
|
||||
tcpKeepAlive: 1s
|
||||
keepAliveConnections: 1
|
||||
keepAliveTimeout: 1s
|
||||
httpHostHeader: abc
|
||||
originServerName: a1
|
||||
caPool: /tmp/path0
|
||||
noTLSVerify: true
|
||||
disableChunkedEncoding: true
|
||||
bastionMode: True
|
||||
proxyAddress: 127.1.2.3
|
||||
proxyPort: 100
|
||||
proxyType: socks5
|
||||
ipRules:
|
||||
- prefix: "10.0.0.0/8"
|
||||
ports:
|
||||
- 80
|
||||
- 8080
|
||||
allow: false
|
||||
- prefix: "fc00::/7"
|
||||
ports:
|
||||
- 443
|
||||
- 4443
|
||||
allow: true
|
||||
ingress:
|
||||
- hostname: tun.example.com
|
||||
service: https://localhost:8000
|
||||
- hostname: "*"
|
||||
service: https://localhost:8001
|
||||
originRequest:
|
||||
connectTimeout: 2m
|
||||
tlsTimeout: 2s
|
||||
noHappyEyeballs: false
|
||||
tcpKeepAlive: 2s
|
||||
keepAliveConnections: 2
|
||||
keepAliveTimeout: 2s
|
||||
httpHostHeader: def
|
||||
originServerName: b2
|
||||
caPool: /tmp/path1
|
||||
noTLSVerify: false
|
||||
disableChunkedEncoding: false
|
||||
bastionMode: false
|
||||
proxyAddress: interface
|
||||
proxyPort: 200
|
||||
proxyType: ""
|
||||
ipRules:
|
||||
- prefix: "10.0.0.0/16"
|
||||
ports:
|
||||
- 3000
|
||||
- 3030
|
||||
allow: false
|
||||
- prefix: "192.16.0.0/24"
|
||||
ports:
|
||||
- 5000
|
||||
- 5050
|
||||
allow: true
|
||||
`
|
||||
|
||||
ing, err := ParseIngress(MustReadIngress(rulesYAML))
|
||||
require.NoError(t, err)
|
||||
validate(ing)
|
||||
|
||||
rawConfig := []byte(`
|
||||
{
|
||||
"originRequest": {
|
||||
"connectTimeout": 60000000000,
|
||||
"tlsTimeout": 1000000000,
|
||||
"noHappyEyeballs": true,
|
||||
"tcpKeepAlive": 1000000000,
|
||||
"keepAliveConnections": 1,
|
||||
"keepAliveTimeout": 1000000000,
|
||||
"httpHostHeader": "abc",
|
||||
"originServerName": "a1",
|
||||
"caPool": "/tmp/path0",
|
||||
"noTLSVerify": true,
|
||||
"disableChunkedEncoding": true,
|
||||
"bastionMode": true,
|
||||
"proxyAddress": "127.1.2.3",
|
||||
"proxyPort": 100,
|
||||
"proxyType": "socks5",
|
||||
"ipRules": [
|
||||
{
|
||||
"prefix": "10.0.0.0/8",
|
||||
"ports": [80, 8080],
|
||||
"allow": false
|
||||
},
|
||||
{
|
||||
"prefix": "fc00::/7",
|
||||
"ports": [443, 4443],
|
||||
"allow": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"ingress": [
|
||||
{
|
||||
"hostname": "tun.example.com",
|
||||
"service": "https://localhost:8000"
|
||||
},
|
||||
{
|
||||
"hostname": "*",
|
||||
"service": "https://localhost:8001",
|
||||
"originRequest": {
|
||||
"connectTimeout": 120000000000,
|
||||
"tlsTimeout": 2000000000,
|
||||
"noHappyEyeballs": false,
|
||||
"tcpKeepAlive": 2000000000,
|
||||
"keepAliveConnections": 2,
|
||||
"keepAliveTimeout": 2000000000,
|
||||
"httpHostHeader": "def",
|
||||
"originServerName": "b2",
|
||||
"caPool": "/tmp/path1",
|
||||
"noTLSVerify": false,
|
||||
"disableChunkedEncoding": false,
|
||||
"bastionMode": false,
|
||||
"proxyAddress": "interface",
|
||||
"proxyPort": 200,
|
||||
"proxyType": "",
|
||||
"ipRules": [
|
||||
{
|
||||
"prefix": "10.0.0.0/16",
|
||||
"ports": [3000, 3030],
|
||||
"allow": false
|
||||
},
|
||||
{
|
||||
"prefix": "192.16.0.0/24",
|
||||
"ports": [5000, 5050],
|
||||
"allow": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"warp-routing": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
`)
|
||||
var remoteConfig RemoteConfig
|
||||
err = json.Unmarshal(rawConfig, &remoteConfig)
|
||||
require.NoError(t, err)
|
||||
validate(remoteConfig.Ingress)
|
||||
}
|
||||
|
||||
func TestOriginRequestConfigDefaults(t *testing.T) {
|
||||
validate := func(ing Ingress) {
|
||||
// Rule 0 didn't override anything, so it inherits the cloudflared defaults
|
||||
actual0 := ing.Rules[0].Config
|
||||
expected0 := OriginRequestConfig{
|
||||
ConnectTimeout: defaultConnectTimeout,
|
||||
TLSTimeout: defaultTLSTimeout,
|
||||
TCPKeepAlive: defaultTCPKeepAlive,
|
||||
KeepAliveConnections: defaultKeepAliveConnections,
|
||||
KeepAliveTimeout: defaultKeepAliveTimeout,
|
||||
ProxyAddress: defaultProxyAddress,
|
||||
}
|
||||
require.Equal(t, expected0, actual0)
|
||||
|
||||
// Rule 1 overrode all defaults.
|
||||
actual1 := ing.Rules[1].Config
|
||||
expected1 := OriginRequestConfig{
|
||||
ConnectTimeout: 2 * time.Minute,
|
||||
TLSTimeout: 2 * time.Second,
|
||||
TCPKeepAlive: 2 * time.Second,
|
||||
NoHappyEyeballs: false,
|
||||
KeepAliveTimeout: 2 * time.Second,
|
||||
KeepAliveConnections: 2,
|
||||
HTTPHostHeader: "def",
|
||||
OriginServerName: "b2",
|
||||
CAPool: "/tmp/path1",
|
||||
NoTLSVerify: false,
|
||||
DisableChunkedEncoding: false,
|
||||
BastionMode: false,
|
||||
ProxyAddress: "interface",
|
||||
ProxyPort: uint(200),
|
||||
ProxyType: "",
|
||||
IPRules: []ipaccess.Rule{
|
||||
newIPRule(t, "10.0.0.0/16", []int{3000, 3030}, false),
|
||||
newIPRule(t, "192.16.0.0/24", []int{5000, 5050}, true),
|
||||
},
|
||||
}
|
||||
require.Equal(t, expected1, actual1)
|
||||
}
|
||||
|
||||
rulesYAML := `
|
||||
ingress:
|
||||
- hostname: tun.example.com
|
||||
service: https://localhost:8000
|
||||
- hostname: "*"
|
||||
service: https://localhost:8001
|
||||
originRequest:
|
||||
connectTimeout: 2m
|
||||
tlsTimeout: 2s
|
||||
noHappyEyeballs: false
|
||||
tcpKeepAlive: 2s
|
||||
keepAliveConnections: 2
|
||||
keepAliveTimeout: 2s
|
||||
httpHostHeader: def
|
||||
originServerName: b2
|
||||
caPool: /tmp/path1
|
||||
noTLSVerify: false
|
||||
disableChunkedEncoding: false
|
||||
bastionMode: false
|
||||
proxyAddress: interface
|
||||
proxyPort: 200
|
||||
proxyType: ""
|
||||
ipRules:
|
||||
- prefix: "10.0.0.0/16"
|
||||
ports:
|
||||
- 3000
|
||||
- 3030
|
||||
allow: false
|
||||
- prefix: "192.16.0.0/24"
|
||||
ports:
|
||||
- 5000
|
||||
- 5050
|
||||
allow: true
|
||||
`
|
||||
ing, err := ParseIngress(MustReadIngress(rulesYAML))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
validate(ing)
|
||||
|
||||
rawConfig := []byte(`
|
||||
{
|
||||
"ingress": [
|
||||
{
|
||||
"hostname": "tun.example.com",
|
||||
"service": "https://localhost:8000"
|
||||
},
|
||||
{
|
||||
"hostname": "*",
|
||||
"service": "https://localhost:8001",
|
||||
"originRequest": {
|
||||
"connectTimeout": 120000000000,
|
||||
"tlsTimeout": 2000000000,
|
||||
"noHappyEyeballs": false,
|
||||
"tcpKeepAlive": 2000000000,
|
||||
"keepAliveConnections": 2,
|
||||
"keepAliveTimeout": 2000000000,
|
||||
"httpHostHeader": "def",
|
||||
"originServerName": "b2",
|
||||
"caPool": "/tmp/path1",
|
||||
"noTLSVerify": false,
|
||||
"disableChunkedEncoding": false,
|
||||
"bastionMode": false,
|
||||
"proxyAddress": "interface",
|
||||
"proxyPort": 200,
|
||||
"proxyType": "",
|
||||
"ipRules": [
|
||||
{
|
||||
"prefix": "10.0.0.0/16",
|
||||
"ports": [3000, 3030],
|
||||
"allow": false
|
||||
},
|
||||
{
|
||||
"prefix": "192.16.0.0/24",
|
||||
"ports": [5000, 5050],
|
||||
"allow": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
`)
|
||||
|
||||
var remoteConfig RemoteConfig
|
||||
err = json.Unmarshal(rawConfig, &remoteConfig)
|
||||
require.NoError(t, err)
|
||||
validate(remoteConfig.Ingress)
|
||||
}
|
||||
|
||||
func TestDefaultConfigFromCLI(t *testing.T) {
|
||||
set := flag.NewFlagSet("contrive", 0)
|
||||
c := cli.NewContext(nil, set, nil)
|
||||
|
||||
expected := OriginRequestConfig{
|
||||
ConnectTimeout: defaultConnectTimeout,
|
||||
TLSTimeout: defaultTLSTimeout,
|
||||
TCPKeepAlive: defaultTCPKeepAlive,
|
||||
KeepAliveConnections: defaultKeepAliveConnections,
|
||||
KeepAliveTimeout: defaultKeepAliveTimeout,
|
||||
ProxyAddress: defaultProxyAddress,
|
||||
}
|
||||
actual := originRequestFromSingeRule(c)
|
||||
require.Equal(t, expected, actual)
|
||||
}
|
||||
|
||||
func newIPRule(t *testing.T, prefix string, ports []int, allow bool) ipaccess.Rule {
|
||||
rule, err := ipaccess.NewRuleByCIDR(&prefix, ports, allow)
|
||||
require.NoError(t, err)
|
||||
return rule
|
||||
}
|
@@ -163,7 +163,7 @@ func (ing Ingress) CatchAll() *Rule {
|
||||
return &ing.Rules[len(ing.Rules)-1]
|
||||
}
|
||||
|
||||
func validate(ingress []config.UnvalidatedIngressRule, defaults OriginRequestConfig) (Ingress, error) {
|
||||
func validateIngress(ingress []config.UnvalidatedIngressRule, defaults OriginRequestConfig) (Ingress, error) {
|
||||
rules := make([]Rule, len(ingress))
|
||||
for i, r := range ingress {
|
||||
cfg := setConfig(defaults, r.OriginRequest)
|
||||
@@ -290,7 +290,7 @@ func ParseIngress(conf *config.Configuration) (Ingress, error) {
|
||||
if len(conf.Ingress) == 0 {
|
||||
return Ingress{}, ErrNoIngressRules
|
||||
}
|
||||
return validate(conf.Ingress, originRequestFromYAML(conf.OriginRequest))
|
||||
return validateIngress(conf.Ingress, originRequestFromConfig(conf.OriginRequest))
|
||||
}
|
||||
|
||||
func isHTTPService(url *url.URL) bool {
|
||||
|
@@ -34,7 +34,7 @@ func Test_parseIngress(t *testing.T) {
|
||||
localhost8000 := MustParseURL(t, "https://localhost:8000")
|
||||
localhost8001 := MustParseURL(t, "https://localhost:8001")
|
||||
fourOhFour := newStatusCode(404)
|
||||
defaultConfig := setConfig(originRequestFromYAML(config.OriginRequestConfig{}), config.OriginRequestConfig{})
|
||||
defaultConfig := setConfig(originRequestFromConfig(config.OriginRequestConfig{}), config.OriginRequestConfig{})
|
||||
require.Equal(t, defaultKeepAliveConnections, defaultConfig.KeepAliveConnections)
|
||||
tr := true
|
||||
type args struct {
|
||||
@@ -324,7 +324,17 @@ ingress:
|
||||
{
|
||||
Hostname: "socks.foo.com",
|
||||
Service: newSocksProxyOverWSService(accessPolicy()),
|
||||
Config: defaultConfig,
|
||||
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,
|
||||
},
|
||||
}}),
|
||||
},
|
||||
{
|
||||
Service: &fourOhFour,
|
||||
@@ -345,7 +355,7 @@ ingress:
|
||||
{
|
||||
Hostname: "bastion.foo.com",
|
||||
Service: newBastionService(),
|
||||
Config: setConfig(originRequestFromYAML(config.OriginRequestConfig{}), config.OriginRequestConfig{BastionMode: &tr}),
|
||||
Config: setConfig(originRequestFromConfig(config.OriginRequestConfig{}), config.OriginRequestConfig{BastionMode: &tr}),
|
||||
},
|
||||
{
|
||||
Service: &fourOhFour,
|
||||
@@ -365,7 +375,7 @@ ingress:
|
||||
{
|
||||
Hostname: "bastion.foo.com",
|
||||
Service: newBastionService(),
|
||||
Config: setConfig(originRequestFromYAML(config.OriginRequestConfig{}), config.OriginRequestConfig{BastionMode: &tr}),
|
||||
Config: setConfig(originRequestFromConfig(config.OriginRequestConfig{}), config.OriginRequestConfig{BastionMode: &tr}),
|
||||
},
|
||||
{
|
||||
Service: &fourOhFour,
|
||||
@@ -397,6 +407,10 @@ ingress:
|
||||
}
|
||||
}
|
||||
|
||||
func ipRulePrefix(s string) *string {
|
||||
return &s
|
||||
}
|
||||
|
||||
func TestSingleOriginSetsConfig(t *testing.T) {
|
||||
flagSet := flag.NewFlagSet(t.Name(), flag.PanicOnError)
|
||||
flagSet.Bool("hello-world", true, "")
|
||||
|
@@ -1,203 +0,0 @@
|
||||
package ingress
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/urfave/cli/v2"
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/cloudflare/cloudflared/config"
|
||||
)
|
||||
|
||||
// Ensure that the nullable config from `config` package and the
|
||||
// non-nullable config from `ingress` package have the same number of
|
||||
// fields.
|
||||
// This test ensures that programmers didn't add a new field to
|
||||
// one struct and forget to add it to the other ;)
|
||||
func TestCorrespondingFields(t *testing.T) {
|
||||
require.Equal(
|
||||
t,
|
||||
CountFields(t, config.OriginRequestConfig{}),
|
||||
CountFields(t, OriginRequestConfig{}),
|
||||
)
|
||||
}
|
||||
|
||||
func CountFields(t *testing.T, val interface{}) int {
|
||||
b, err := yaml.Marshal(val)
|
||||
require.NoError(t, err)
|
||||
m := make(map[string]interface{}, 0)
|
||||
err = yaml.Unmarshal(b, &m)
|
||||
require.NoError(t, err)
|
||||
return len(m)
|
||||
}
|
||||
|
||||
func TestOriginRequestConfigOverrides(t *testing.T) {
|
||||
rulesYAML := `
|
||||
originRequest:
|
||||
connectTimeout: 1m
|
||||
tlsTimeout: 1s
|
||||
noHappyEyeballs: true
|
||||
tcpKeepAlive: 1s
|
||||
keepAliveConnections: 1
|
||||
keepAliveTimeout: 1s
|
||||
httpHostHeader: abc
|
||||
originServerName: a1
|
||||
caPool: /tmp/path0
|
||||
noTLSVerify: true
|
||||
disableChunkedEncoding: true
|
||||
bastionMode: True
|
||||
proxyAddress: 127.1.2.3
|
||||
proxyPort: 100
|
||||
proxyType: socks5
|
||||
ingress:
|
||||
- hostname: tun.example.com
|
||||
service: https://localhost:8000
|
||||
- hostname: "*"
|
||||
service: https://localhost:8001
|
||||
originRequest:
|
||||
connectTimeout: 2m
|
||||
tlsTimeout: 2s
|
||||
noHappyEyeballs: false
|
||||
tcpKeepAlive: 2s
|
||||
keepAliveConnections: 2
|
||||
keepAliveTimeout: 2s
|
||||
httpHostHeader: def
|
||||
originServerName: b2
|
||||
caPool: /tmp/path1
|
||||
noTLSVerify: false
|
||||
disableChunkedEncoding: false
|
||||
bastionMode: false
|
||||
proxyAddress: interface
|
||||
proxyPort: 200
|
||||
proxyType: ""
|
||||
`
|
||||
ing, err := ParseIngress(MustReadIngress(rulesYAML))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// Rule 0 didn't override anything, so it inherits the user-specified
|
||||
// root-level configuration.
|
||||
actual0 := ing.Rules[0].Config
|
||||
expected0 := OriginRequestConfig{
|
||||
ConnectTimeout: 1 * time.Minute,
|
||||
TLSTimeout: 1 * time.Second,
|
||||
NoHappyEyeballs: true,
|
||||
TCPKeepAlive: 1 * time.Second,
|
||||
KeepAliveConnections: 1,
|
||||
KeepAliveTimeout: 1 * time.Second,
|
||||
HTTPHostHeader: "abc",
|
||||
OriginServerName: "a1",
|
||||
CAPool: "/tmp/path0",
|
||||
NoTLSVerify: true,
|
||||
DisableChunkedEncoding: true,
|
||||
BastionMode: true,
|
||||
ProxyAddress: "127.1.2.3",
|
||||
ProxyPort: uint(100),
|
||||
ProxyType: "socks5",
|
||||
}
|
||||
require.Equal(t, expected0, actual0)
|
||||
|
||||
// Rule 1 overrode all the root-level config.
|
||||
actual1 := ing.Rules[1].Config
|
||||
expected1 := OriginRequestConfig{
|
||||
ConnectTimeout: 2 * time.Minute,
|
||||
TLSTimeout: 2 * time.Second,
|
||||
NoHappyEyeballs: false,
|
||||
TCPKeepAlive: 2 * time.Second,
|
||||
KeepAliveConnections: 2,
|
||||
KeepAliveTimeout: 2 * time.Second,
|
||||
HTTPHostHeader: "def",
|
||||
OriginServerName: "b2",
|
||||
CAPool: "/tmp/path1",
|
||||
NoTLSVerify: false,
|
||||
DisableChunkedEncoding: false,
|
||||
BastionMode: false,
|
||||
ProxyAddress: "interface",
|
||||
ProxyPort: uint(200),
|
||||
ProxyType: "",
|
||||
}
|
||||
require.Equal(t, expected1, actual1)
|
||||
}
|
||||
|
||||
func TestOriginRequestConfigDefaults(t *testing.T) {
|
||||
rulesYAML := `
|
||||
ingress:
|
||||
- hostname: tun.example.com
|
||||
service: https://localhost:8000
|
||||
- hostname: "*"
|
||||
service: https://localhost:8001
|
||||
originRequest:
|
||||
connectTimeout: 2m
|
||||
tlsTimeout: 2s
|
||||
noHappyEyeballs: false
|
||||
tcpKeepAlive: 2s
|
||||
keepAliveConnections: 2
|
||||
keepAliveTimeout: 2s
|
||||
httpHostHeader: def
|
||||
originServerName: b2
|
||||
caPool: /tmp/path1
|
||||
noTLSVerify: false
|
||||
disableChunkedEncoding: false
|
||||
bastionMode: false
|
||||
proxyAddress: interface
|
||||
proxyPort: 200
|
||||
proxyType: ""
|
||||
`
|
||||
ing, err := ParseIngress(MustReadIngress(rulesYAML))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// Rule 0 didn't override anything, so it inherits the cloudflared defaults
|
||||
actual0 := ing.Rules[0].Config
|
||||
expected0 := OriginRequestConfig{
|
||||
ConnectTimeout: defaultConnectTimeout,
|
||||
TLSTimeout: defaultTLSTimeout,
|
||||
TCPKeepAlive: defaultTCPKeepAlive,
|
||||
KeepAliveConnections: defaultKeepAliveConnections,
|
||||
KeepAliveTimeout: defaultKeepAliveTimeout,
|
||||
ProxyAddress: defaultProxyAddress,
|
||||
}
|
||||
require.Equal(t, expected0, actual0)
|
||||
|
||||
// Rule 1 overrode all defaults.
|
||||
actual1 := ing.Rules[1].Config
|
||||
expected1 := OriginRequestConfig{
|
||||
ConnectTimeout: 2 * time.Minute,
|
||||
TLSTimeout: 2 * time.Second,
|
||||
NoHappyEyeballs: false,
|
||||
TCPKeepAlive: 2 * time.Second,
|
||||
KeepAliveConnections: 2,
|
||||
KeepAliveTimeout: 2 * time.Second,
|
||||
HTTPHostHeader: "def",
|
||||
OriginServerName: "b2",
|
||||
CAPool: "/tmp/path1",
|
||||
NoTLSVerify: false,
|
||||
DisableChunkedEncoding: false,
|
||||
BastionMode: false,
|
||||
ProxyAddress: "interface",
|
||||
ProxyPort: uint(200),
|
||||
ProxyType: "",
|
||||
}
|
||||
require.Equal(t, expected1, actual1)
|
||||
}
|
||||
|
||||
func TestDefaultConfigFromCLI(t *testing.T) {
|
||||
set := flag.NewFlagSet("contrive", 0)
|
||||
c := cli.NewContext(nil, set, nil)
|
||||
|
||||
expected := OriginRequestConfig{
|
||||
ConnectTimeout: defaultConnectTimeout,
|
||||
TLSTimeout: defaultTLSTimeout,
|
||||
TCPKeepAlive: defaultTCPKeepAlive,
|
||||
KeepAliveConnections: defaultKeepAliveConnections,
|
||||
KeepAliveTimeout: defaultKeepAliveTimeout,
|
||||
ProxyAddress: defaultProxyAddress,
|
||||
}
|
||||
actual := originRequestFromSingeRule(c)
|
||||
require.Equal(t, expected, actual)
|
||||
}
|
Reference in New Issue
Block a user