Skip to content

Commit b1edf5b

Browse files
committed
TUN-5702: Allow to deserialize config from JSON
1 parent d07d24e commit b1edf5b

File tree

7 files changed

+649
-267
lines changed

7 files changed

+649
-267
lines changed

config/configuration.go

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -175,60 +175,62 @@ func ValidateUrl(c *cli.Context, allowURLFromArgs bool) (*url.URL, error) {
175175
}
176176

177177
type UnvalidatedIngressRule struct {
178-
Hostname string
179-
Path string
180-
Service string
181-
OriginRequest OriginRequestConfig `yaml:"originRequest"`
178+
Hostname string `json:"hostname"`
179+
Path string `json:"path"`
180+
Service string `json:"service"`
181+
OriginRequest OriginRequestConfig `yaml:"originRequest" json:"originRequest"`
182182
}
183183

184184
// OriginRequestConfig is a set of optional fields that users may set to
185185
// customize how cloudflared sends requests to origin services. It is used to set
186186
// up general config that apply to all rules, and also, specific per-rule
187187
// config.
188-
// Note: To specify a time.Duration in go-yaml, use e.g. "3s" or "24h".
188+
// Note:
189+
// - To specify a time.Duration in go-yaml, use e.g. "3s" or "24h".
190+
// - To specify a time.Duration in json, use int64 of the nanoseconds
189191
type OriginRequestConfig struct {
190192
// HTTP proxy timeout for establishing a new connection
191-
ConnectTimeout *time.Duration `yaml:"connectTimeout"`
193+
ConnectTimeout *time.Duration `yaml:"connectTimeout" json:"connectTimeout"`
192194
// HTTP proxy timeout for completing a TLS handshake
193-
TLSTimeout *time.Duration `yaml:"tlsTimeout"`
195+
TLSTimeout *time.Duration `yaml:"tlsTimeout" json:"tlsTimeout"`
194196
// HTTP proxy TCP keepalive duration
195-
TCPKeepAlive *time.Duration `yaml:"tcpKeepAlive"`
197+
TCPKeepAlive *time.Duration `yaml:"tcpKeepAlive" json:"tcpKeepAlive"`
196198
// HTTP proxy should disable "happy eyeballs" for IPv4/v6 fallback
197-
NoHappyEyeballs *bool `yaml:"noHappyEyeballs"`
199+
NoHappyEyeballs *bool `yaml:"noHappyEyeballs" json:"noHappyEyeballs"`
198200
// HTTP proxy maximum keepalive connection pool size
199-
KeepAliveConnections *int `yaml:"keepAliveConnections"`
201+
KeepAliveConnections *int `yaml:"keepAliveConnections" json:"keepAliveConnections"`
200202
// HTTP proxy timeout for closing an idle connection
201-
KeepAliveTimeout *time.Duration `yaml:"keepAliveTimeout"`
203+
KeepAliveTimeout *time.Duration `yaml:"keepAliveTimeout" json:"keepAliveTimeout"`
202204
// Sets the HTTP Host header for the local webserver.
203-
HTTPHostHeader *string `yaml:"httpHostHeader"`
205+
HTTPHostHeader *string `yaml:"httpHostHeader" json:"httpHostHeader"`
204206
// Hostname on the origin server certificate.
205-
OriginServerName *string `yaml:"originServerName"`
207+
OriginServerName *string `yaml:"originServerName" json:"originServerName"`
206208
// Path to the CA for the certificate of your origin.
207209
// This option should be used only if your certificate is not signed by Cloudflare.
208-
CAPool *string `yaml:"caPool"`
210+
CAPool *string `yaml:"caPool" json:"caPool"`
209211
// Disables TLS verification of the certificate presented by your origin.
210212
// Will allow any certificate from the origin to be accepted.
211213
// Note: The connection from your machine to Cloudflare's Edge is still encrypted.
212-
NoTLSVerify *bool `yaml:"noTLSVerify"`
214+
NoTLSVerify *bool `yaml:"noTLSVerify" json:"noTLSVerify"`
213215
// Disables chunked transfer encoding.
214216
// Useful if you are running a WSGI server.
215-
DisableChunkedEncoding *bool `yaml:"disableChunkedEncoding"`
217+
DisableChunkedEncoding *bool `yaml:"disableChunkedEncoding" json:"disableChunkedEncoding"`
216218
// Runs as jump host
217-
BastionMode *bool `yaml:"bastionMode"`
219+
BastionMode *bool `yaml:"bastionMode" json:"bastionMode"`
218220
// Listen address for the proxy.
219-
ProxyAddress *string `yaml:"proxyAddress"`
221+
ProxyAddress *string `yaml:"proxyAddress" json:"proxyAddress"`
220222
// Listen port for the proxy.
221-
ProxyPort *uint `yaml:"proxyPort"`
223+
ProxyPort *uint `yaml:"proxyPort" json:"proxyPort"`
222224
// Valid options are 'socks' or empty.
223-
ProxyType *string `yaml:"proxyType"`
225+
ProxyType *string `yaml:"proxyType" json:"proxyType"`
224226
// IP rules for the proxy service
225-
IPRules []IngressIPRule `yaml:"ipRules"`
227+
IPRules []IngressIPRule `yaml:"ipRules" json:"ipRules"`
226228
}
227229

228230
type IngressIPRule struct {
229-
Prefix *string `yaml:"prefix"`
230-
Ports []int `yaml:"ports"`
231-
Allow bool `yaml:"allow"`
231+
Prefix *string `yaml:"prefix" json:"prefix"`
232+
Ports []int `yaml:"ports" json:"ports"`
233+
Allow bool `yaml:"allow" json:"allow"`
232234
}
233235

234236
type Configuration struct {
@@ -240,7 +242,7 @@ type Configuration struct {
240242
}
241243

242244
type WarpRoutingConfig struct {
243-
Enabled bool `yaml:"enabled"`
245+
Enabled bool `yaml:"enabled" json:"enabled"`
244246
}
245247

246248
type configFileSettings struct {

config/configuration_test.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package config
22

33
import (
4+
"encoding/json"
45
"testing"
56
"time"
67

@@ -26,6 +27,18 @@ func TestConfigFileSettings(t *testing.T) {
2627
)
2728
rawYAML := `
2829
tunnel: config-file-test
30+
originRequest:
31+
ipRules:
32+
- prefix: "10.0.0.0/8"
33+
ports:
34+
- 80
35+
- 8080
36+
allow: false
37+
- prefix: "fc00::/7"
38+
ports:
39+
- 443
40+
- 4443
41+
allow: true
2942
ingress:
3043
- hostname: tunnel1.example.com
3144
path: /id
@@ -53,6 +66,21 @@ counters:
5366
assert.Equal(t, firstIngress, config.Ingress[0])
5467
assert.Equal(t, secondIngress, config.Ingress[1])
5568
assert.Equal(t, warpRouting, config.WarpRouting)
69+
privateV4 := "10.0.0.0/8"
70+
privateV6 := "fc00::/7"
71+
ipRules := []IngressIPRule{
72+
{
73+
Prefix: &privateV4,
74+
Ports: []int{80, 8080},
75+
Allow: false,
76+
},
77+
{
78+
Prefix: &privateV6,
79+
Ports: []int{443, 4443},
80+
Allow: true,
81+
},
82+
}
83+
assert.Equal(t, ipRules, config.OriginRequest.IPRules)
5684

5785
retries, err := config.Int("retries")
5886
assert.NoError(t, err)
@@ -81,3 +109,71 @@ counters:
81109
assert.Equal(t, 456, counters[1])
82110

83111
}
112+
113+
func TestUnmarshalOriginRequestConfig(t *testing.T) {
114+
raw := []byte(`
115+
{
116+
"connectTimeout": 10000000000,
117+
"tlsTimeout": 30000000000,
118+
"tcpKeepAlive": 30000000000,
119+
"noHappyEyeballs": true,
120+
"keepAliveTimeout": 60000000000,
121+
"keepAliveConnections": 10,
122+
"httpHostHeader": "app.tunnel.com",
123+
"originServerName": "app.tunnel.com",
124+
"caPool": "/etc/capool",
125+
"noTLSVerify": true,
126+
"disableChunkedEncoding": true,
127+
"bastionMode": true,
128+
"proxyAddress": "127.0.0.3",
129+
"proxyPort": 9000,
130+
"proxyType": "socks",
131+
"ipRules": [
132+
{
133+
"prefix": "10.0.0.0/8",
134+
"ports": [80, 8080],
135+
"allow": false
136+
},
137+
{
138+
"prefix": "fc00::/7",
139+
"ports": [443, 4443],
140+
"allow": true
141+
}
142+
]
143+
}
144+
`)
145+
var config OriginRequestConfig
146+
assert.NoError(t, json.Unmarshal(raw, &config))
147+
assert.Equal(t, time.Second*10, *config.ConnectTimeout)
148+
assert.Equal(t, time.Second*30, *config.TLSTimeout)
149+
assert.Equal(t, time.Second*30, *config.TCPKeepAlive)
150+
assert.Equal(t, true, *config.NoHappyEyeballs)
151+
assert.Equal(t, time.Second*60, *config.KeepAliveTimeout)
152+
assert.Equal(t, 10, *config.KeepAliveConnections)
153+
assert.Equal(t, "app.tunnel.com", *config.HTTPHostHeader)
154+
assert.Equal(t, "app.tunnel.com", *config.OriginServerName)
155+
assert.Equal(t, "/etc/capool", *config.CAPool)
156+
assert.Equal(t, true, *config.NoTLSVerify)
157+
assert.Equal(t, true, *config.DisableChunkedEncoding)
158+
assert.Equal(t, true, *config.BastionMode)
159+
assert.Equal(t, "127.0.0.3", *config.ProxyAddress)
160+
assert.Equal(t, true, *config.NoTLSVerify)
161+
assert.Equal(t, uint(9000), *config.ProxyPort)
162+
assert.Equal(t, "socks", *config.ProxyType)
163+
164+
privateV4 := "10.0.0.0/8"
165+
privateV6 := "fc00::/7"
166+
ipRules := []IngressIPRule{
167+
{
168+
Prefix: &privateV4,
169+
Ports: []int{80, 8080},
170+
Allow: false,
171+
},
172+
{
173+
Prefix: &privateV6,
174+
Ports: []int{443, 4443},
175+
Allow: true,
176+
},
177+
}
178+
assert.Equal(t, ipRules, config.IPRules)
179+
}

ingress/origin_request_config.go renamed to ingress/config.go

Lines changed: 84 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package ingress
22

33
import (
4+
"encoding/json"
45
"time"
56

67
"github.com/urfave/cli/v2"
@@ -38,6 +39,34 @@ const (
3839
socksProxy = "socks"
3940
)
4041

42+
// RemoteConfig models ingress settings that can be managed remotely, for example through the dashboard.
43+
type RemoteConfig struct {
44+
Ingress Ingress
45+
WarpRouting config.WarpRoutingConfig
46+
}
47+
48+
type remoteConfigJSON struct {
49+
GlobalOriginRequest config.OriginRequestConfig `json:"originRequest"`
50+
IngressRules []config.UnvalidatedIngressRule `json:"ingress"`
51+
WarpRouting config.WarpRoutingConfig `json:"warp-routing"`
52+
}
53+
54+
func (rc *RemoteConfig) UnmarshalJSON(b []byte) error {
55+
var rawConfig remoteConfigJSON
56+
if err := json.Unmarshal(b, &rawConfig); err != nil {
57+
return err
58+
}
59+
ingress, err := validateIngress(rawConfig.IngressRules, originRequestFromConfig(rawConfig.GlobalOriginRequest))
60+
if err != nil {
61+
return err
62+
}
63+
64+
rc.Ingress = ingress
65+
rc.WarpRouting = rawConfig.WarpRouting
66+
67+
return nil
68+
}
69+
4170
func originRequestFromSingeRule(c *cli.Context) OriginRequestConfig {
4271
var connectTimeout time.Duration = defaultConnectTimeout
4372
var tlsTimeout time.Duration = defaultTLSTimeout
@@ -119,7 +148,7 @@ func originRequestFromSingeRule(c *cli.Context) OriginRequestConfig {
119148
}
120149
}
121150

122-
func originRequestFromYAML(y config.OriginRequestConfig) OriginRequestConfig {
151+
func originRequestFromConfig(c config.OriginRequestConfig) OriginRequestConfig {
123152
out := OriginRequestConfig{
124153
ConnectTimeout: defaultConnectTimeout,
125154
TLSTimeout: defaultTLSTimeout,
@@ -128,50 +157,58 @@ func originRequestFromYAML(y config.OriginRequestConfig) OriginRequestConfig {
128157
KeepAliveTimeout: defaultKeepAliveTimeout,
129158
ProxyAddress: defaultProxyAddress,
130159
}
131-
if y.ConnectTimeout != nil {
132-
out.ConnectTimeout = *y.ConnectTimeout
160+
if c.ConnectTimeout != nil {
161+
out.ConnectTimeout = *c.ConnectTimeout
162+
}
163+
if c.TLSTimeout != nil {
164+
out.TLSTimeout = *c.TLSTimeout
133165
}
134-
if y.TLSTimeout != nil {
135-
out.TLSTimeout = *y.TLSTimeout
166+
if c.TCPKeepAlive != nil {
167+
out.TCPKeepAlive = *c.TCPKeepAlive
136168
}
137-
if y.TCPKeepAlive != nil {
138-
out.TCPKeepAlive = *y.TCPKeepAlive
169+
if c.NoHappyEyeballs != nil {
170+
out.NoHappyEyeballs = *c.NoHappyEyeballs
139171
}
140-
if y.NoHappyEyeballs != nil {
141-
out.NoHappyEyeballs = *y.NoHappyEyeballs
172+
if c.KeepAliveConnections != nil {
173+
out.KeepAliveConnections = *c.KeepAliveConnections
142174
}
143-
if y.KeepAliveConnections != nil {
144-
out.KeepAliveConnections = *y.KeepAliveConnections
175+
if c.KeepAliveTimeout != nil {
176+
out.KeepAliveTimeout = *c.KeepAliveTimeout
145177
}
146-
if y.KeepAliveTimeout != nil {
147-
out.KeepAliveTimeout = *y.KeepAliveTimeout
178+
if c.HTTPHostHeader != nil {
179+
out.HTTPHostHeader = *c.HTTPHostHeader
148180
}
149-
if y.HTTPHostHeader != nil {
150-
out.HTTPHostHeader = *y.HTTPHostHeader
181+
if c.OriginServerName != nil {
182+
out.OriginServerName = *c.OriginServerName
151183
}
152-
if y.OriginServerName != nil {
153-
out.OriginServerName = *y.OriginServerName
184+
if c.CAPool != nil {
185+
out.CAPool = *c.CAPool
154186
}
155-
if y.CAPool != nil {
156-
out.CAPool = *y.CAPool
187+
if c.NoTLSVerify != nil {
188+
out.NoTLSVerify = *c.NoTLSVerify
157189
}
158-
if y.NoTLSVerify != nil {
159-
out.NoTLSVerify = *y.NoTLSVerify
190+
if c.DisableChunkedEncoding != nil {
191+
out.DisableChunkedEncoding = *c.DisableChunkedEncoding
160192
}
161-
if y.DisableChunkedEncoding != nil {
162-
out.DisableChunkedEncoding = *y.DisableChunkedEncoding
193+
if c.BastionMode != nil {
194+
out.BastionMode = *c.BastionMode
163195
}
164-
if y.BastionMode != nil {
165-
out.BastionMode = *y.BastionMode
196+
if c.ProxyAddress != nil {
197+
out.ProxyAddress = *c.ProxyAddress
166198
}
167-
if y.ProxyAddress != nil {
168-
out.ProxyAddress = *y.ProxyAddress
199+
if c.ProxyPort != nil {
200+
out.ProxyPort = *c.ProxyPort
169201
}
170-
if y.ProxyPort != nil {
171-
out.ProxyPort = *y.ProxyPort
202+
if c.ProxyType != nil {
203+
out.ProxyType = *c.ProxyType
172204
}
173-
if y.ProxyType != nil {
174-
out.ProxyType = *y.ProxyType
205+
if len(c.IPRules) > 0 {
206+
for _, r := range c.IPRules {
207+
rule, err := ipaccess.NewRuleByCIDR(r.Prefix, r.Ports, r.Allow)
208+
if err == nil {
209+
out.IPRules = append(out.IPRules, rule)
210+
}
211+
}
175212
}
176213
return out
177214
}
@@ -188,10 +225,10 @@ type OriginRequestConfig struct {
188225
TCPKeepAlive time.Duration `yaml:"tcpKeepAlive"`
189226
// HTTP proxy should disable "happy eyeballs" for IPv4/v6 fallback
190227
NoHappyEyeballs bool `yaml:"noHappyEyeballs"`
191-
// HTTP proxy maximum keepalive connection pool size
192-
KeepAliveConnections int `yaml:"keepAliveConnections"`
193228
// HTTP proxy timeout for closing an idle connection
194229
KeepAliveTimeout time.Duration `yaml:"keepAliveTimeout"`
230+
// HTTP proxy maximum keepalive connection pool size
231+
KeepAliveConnections int `yaml:"keepAliveConnections"`
195232
// Sets the HTTP Host header for the local webserver.
196233
HTTPHostHeader string `yaml:"httpHostHeader"`
197234
// Hostname on the origin server certificate.
@@ -308,6 +345,19 @@ func (defaults *OriginRequestConfig) setProxyType(overrides config.OriginRequest
308345
}
309346
}
310347

348+
func (defaults *OriginRequestConfig) setIPRules(overrides config.OriginRequestConfig) {
349+
if val := overrides.IPRules; len(val) > 0 {
350+
ipAccessRule := make([]ipaccess.Rule, len(overrides.IPRules))
351+
for i, r := range overrides.IPRules {
352+
rule, err := ipaccess.NewRuleByCIDR(r.Prefix, r.Ports, r.Allow)
353+
if err == nil {
354+
ipAccessRule[i] = rule
355+
}
356+
}
357+
defaults.IPRules = ipAccessRule
358+
}
359+
}
360+
311361
// SetConfig gets config for the requests that cloudflared sends to origins.
312362
// Each field has a setter method which sets a value for the field by trying to find:
313363
// 1. The user config for this rule
@@ -332,5 +382,6 @@ func setConfig(defaults OriginRequestConfig, overrides config.OriginRequestConfi
332382
cfg.setProxyPort(overrides)
333383
cfg.setProxyAddress(overrides)
334384
cfg.setProxyType(overrides)
385+
cfg.setIPRules(overrides)
335386
return cfg
336387
}

0 commit comments

Comments
 (0)