config_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. package ingress
  2. import (
  3. "encoding/json"
  4. "flag"
  5. "testing"
  6. "time"
  7. "github.com/stretchr/testify/require"
  8. "github.com/urfave/cli/v2"
  9. yaml "gopkg.in/yaml.v3"
  10. "github.com/cloudflare/cloudflared/config"
  11. "github.com/cloudflare/cloudflared/ipaccess"
  12. )
  13. // Ensure that the nullable config from `config` package and the
  14. // non-nullable config from `ingress` package have the same number of
  15. // fields.
  16. // This test ensures that programmers didn't add a new field to
  17. // one struct and forget to add it to the other ;)
  18. func TestCorrespondingFields(t *testing.T) {
  19. require.Equal(
  20. t,
  21. CountFields(t, config.OriginRequestConfig{}),
  22. CountFields(t, OriginRequestConfig{}),
  23. )
  24. }
  25. func CountFields(t *testing.T, val interface{}) int {
  26. b, err := yaml.Marshal(val)
  27. require.NoError(t, err)
  28. m := make(map[string]interface{}, 0)
  29. err = yaml.Unmarshal(b, &m)
  30. require.NoError(t, err)
  31. return len(m)
  32. }
  33. func TestUnmarshalRemoteConfigOverridesGlobal(t *testing.T) {
  34. rawConfig := []byte(`
  35. {
  36. "originRequest": {
  37. "connectTimeout": 90,
  38. "noHappyEyeballs": true
  39. },
  40. "ingress": [
  41. {
  42. "hostname": "jira.cfops.com",
  43. "service": "http://192.16.19.1:80",
  44. "originRequest": {
  45. "noTLSVerify": true,
  46. "connectTimeout": 10
  47. }
  48. },
  49. {
  50. "service": "http_status:404"
  51. }
  52. ],
  53. "warp-routing": {
  54. "enabled": true
  55. }
  56. }
  57. `)
  58. var remoteConfig RemoteConfig
  59. err := json.Unmarshal(rawConfig, &remoteConfig)
  60. require.NoError(t, err)
  61. require.True(t, remoteConfig.Ingress.Rules[0].Config.NoTLSVerify)
  62. require.True(t, remoteConfig.Ingress.Defaults.NoHappyEyeballs)
  63. }
  64. func TestOriginRequestConfigOverrides(t *testing.T) {
  65. validate := func(ing Ingress) {
  66. // Rule 0 didn't override anything, so it inherits the user-specified
  67. // root-level configuration.
  68. actual0 := ing.Rules[0].Config
  69. expected0 := OriginRequestConfig{
  70. ConnectTimeout: config.CustomDuration{Duration: 1 * time.Minute},
  71. TLSTimeout: config.CustomDuration{Duration: 1 * time.Second},
  72. TCPKeepAlive: config.CustomDuration{Duration: 1 * time.Second},
  73. NoHappyEyeballs: true,
  74. KeepAliveTimeout: config.CustomDuration{Duration: 1 * time.Second},
  75. KeepAliveConnections: 1,
  76. HTTPHostHeader: "abc",
  77. OriginServerName: "a1",
  78. CAPool: "/tmp/path0",
  79. NoTLSVerify: true,
  80. DisableChunkedEncoding: true,
  81. BastionMode: true,
  82. ProxyAddress: "127.1.2.3",
  83. ProxyPort: uint(100),
  84. ProxyType: "socks5",
  85. IPRules: []ipaccess.Rule{
  86. newIPRule(t, "10.0.0.0/8", []int{80, 8080}, false),
  87. newIPRule(t, "fc00::/7", []int{443, 4443}, true),
  88. },
  89. }
  90. require.Equal(t, expected0, actual0)
  91. // Rule 1 overrode all the root-level config.
  92. actual1 := ing.Rules[1].Config
  93. expected1 := OriginRequestConfig{
  94. ConnectTimeout: config.CustomDuration{Duration: 2 * time.Minute},
  95. TLSTimeout: config.CustomDuration{Duration: 2 * time.Second},
  96. TCPKeepAlive: config.CustomDuration{Duration: 2 * time.Second},
  97. NoHappyEyeballs: false,
  98. KeepAliveTimeout: config.CustomDuration{Duration: 2 * time.Second},
  99. KeepAliveConnections: 2,
  100. HTTPHostHeader: "def",
  101. OriginServerName: "b2",
  102. CAPool: "/tmp/path1",
  103. NoTLSVerify: false,
  104. DisableChunkedEncoding: false,
  105. BastionMode: false,
  106. ProxyAddress: "interface",
  107. ProxyPort: uint(200),
  108. ProxyType: "",
  109. IPRules: []ipaccess.Rule{
  110. newIPRule(t, "10.0.0.0/16", []int{3000, 3030}, false),
  111. newIPRule(t, "192.16.0.0/24", []int{5000, 5050}, true),
  112. },
  113. }
  114. require.Equal(t, expected1, actual1)
  115. }
  116. rulesYAML := `
  117. originRequest:
  118. connectTimeout: 1m
  119. tlsTimeout: 1s
  120. noHappyEyeballs: true
  121. tcpKeepAlive: 1s
  122. keepAliveConnections: 1
  123. keepAliveTimeout: 1s
  124. httpHostHeader: abc
  125. originServerName: a1
  126. caPool: /tmp/path0
  127. noTLSVerify: true
  128. disableChunkedEncoding: true
  129. bastionMode: True
  130. proxyAddress: 127.1.2.3
  131. proxyPort: 100
  132. proxyType: socks5
  133. ipRules:
  134. - prefix: "10.0.0.0/8"
  135. ports:
  136. - 80
  137. - 8080
  138. allow: false
  139. - prefix: "fc00::/7"
  140. ports:
  141. - 443
  142. - 4443
  143. allow: true
  144. ingress:
  145. - hostname: tun.example.com
  146. service: https://localhost:8000
  147. - hostname: "*"
  148. service: https://localhost:8001
  149. originRequest:
  150. connectTimeout: 2m
  151. tlsTimeout: 2s
  152. noHappyEyeballs: false
  153. tcpKeepAlive: 2s
  154. keepAliveConnections: 2
  155. keepAliveTimeout: 2s
  156. httpHostHeader: def
  157. originServerName: b2
  158. caPool: /tmp/path1
  159. noTLSVerify: false
  160. disableChunkedEncoding: false
  161. bastionMode: false
  162. proxyAddress: interface
  163. proxyPort: 200
  164. proxyType: ""
  165. ipRules:
  166. - prefix: "10.0.0.0/16"
  167. ports:
  168. - 3000
  169. - 3030
  170. allow: false
  171. - prefix: "192.16.0.0/24"
  172. ports:
  173. - 5000
  174. - 5050
  175. allow: true
  176. `
  177. ing, err := ParseIngress(MustReadIngress(rulesYAML))
  178. require.NoError(t, err)
  179. validate(ing)
  180. rawConfig := []byte(`
  181. {
  182. "originRequest": {
  183. "connectTimeout": 60,
  184. "tlsTimeout": 1,
  185. "noHappyEyeballs": true,
  186. "tcpKeepAlive": 1,
  187. "keepAliveConnections": 1,
  188. "keepAliveTimeout": 1,
  189. "httpHostHeader": "abc",
  190. "originServerName": "a1",
  191. "caPool": "/tmp/path0",
  192. "noTLSVerify": true,
  193. "disableChunkedEncoding": true,
  194. "bastionMode": true,
  195. "proxyAddress": "127.1.2.3",
  196. "proxyPort": 100,
  197. "proxyType": "socks5",
  198. "ipRules": [
  199. {
  200. "prefix": "10.0.0.0/8",
  201. "ports": [80, 8080],
  202. "allow": false
  203. },
  204. {
  205. "prefix": "fc00::/7",
  206. "ports": [443, 4443],
  207. "allow": true
  208. }
  209. ]
  210. },
  211. "ingress": [
  212. {
  213. "hostname": "tun.example.com",
  214. "service": "https://localhost:8000"
  215. },
  216. {
  217. "hostname": "*",
  218. "service": "https://localhost:8001",
  219. "originRequest": {
  220. "connectTimeout": 120,
  221. "tlsTimeout": 2,
  222. "noHappyEyeballs": false,
  223. "tcpKeepAlive": 2,
  224. "keepAliveConnections": 2,
  225. "keepAliveTimeout": 2,
  226. "httpHostHeader": "def",
  227. "originServerName": "b2",
  228. "caPool": "/tmp/path1",
  229. "noTLSVerify": false,
  230. "disableChunkedEncoding": false,
  231. "bastionMode": false,
  232. "proxyAddress": "interface",
  233. "proxyPort": 200,
  234. "proxyType": "",
  235. "ipRules": [
  236. {
  237. "prefix": "10.0.0.0/16",
  238. "ports": [3000, 3030],
  239. "allow": false
  240. },
  241. {
  242. "prefix": "192.16.0.0/24",
  243. "ports": [5000, 5050],
  244. "allow": true
  245. }
  246. ]
  247. }
  248. }
  249. ],
  250. "warp-routing": {
  251. "enabled": true
  252. }
  253. }
  254. `)
  255. var remoteConfig RemoteConfig
  256. err = json.Unmarshal(rawConfig, &remoteConfig)
  257. require.NoError(t, err)
  258. validate(remoteConfig.Ingress)
  259. }
  260. func TestOriginRequestConfigDefaults(t *testing.T) {
  261. validate := func(ing Ingress) {
  262. // Rule 0 didn't override anything, so it inherits the cloudflared defaults
  263. actual0 := ing.Rules[0].Config
  264. expected0 := OriginRequestConfig{
  265. ConnectTimeout: defaultHTTPConnectTimeout,
  266. TLSTimeout: defaultTLSTimeout,
  267. TCPKeepAlive: defaultTCPKeepAlive,
  268. KeepAliveConnections: defaultKeepAliveConnections,
  269. KeepAliveTimeout: defaultKeepAliveTimeout,
  270. ProxyAddress: defaultProxyAddress,
  271. }
  272. require.Equal(t, expected0, actual0)
  273. // Rule 1 overrode all defaults.
  274. actual1 := ing.Rules[1].Config
  275. expected1 := OriginRequestConfig{
  276. ConnectTimeout: config.CustomDuration{Duration: 2 * time.Minute},
  277. TLSTimeout: config.CustomDuration{Duration: 2 * time.Second},
  278. TCPKeepAlive: config.CustomDuration{Duration: 2 * time.Second},
  279. NoHappyEyeballs: false,
  280. KeepAliveTimeout: config.CustomDuration{Duration: 2 * time.Second},
  281. KeepAliveConnections: 2,
  282. HTTPHostHeader: "def",
  283. OriginServerName: "b2",
  284. CAPool: "/tmp/path1",
  285. NoTLSVerify: false,
  286. DisableChunkedEncoding: false,
  287. BastionMode: false,
  288. ProxyAddress: "interface",
  289. ProxyPort: uint(200),
  290. ProxyType: "",
  291. IPRules: []ipaccess.Rule{
  292. newIPRule(t, "10.0.0.0/16", []int{3000, 3030}, false),
  293. newIPRule(t, "192.16.0.0/24", []int{5000, 5050}, true),
  294. },
  295. }
  296. require.Equal(t, expected1, actual1)
  297. }
  298. rulesYAML := `
  299. ingress:
  300. - hostname: tun.example.com
  301. service: https://localhost:8000
  302. - hostname: "*"
  303. service: https://localhost:8001
  304. originRequest:
  305. connectTimeout: 2m
  306. tlsTimeout: 2s
  307. noHappyEyeballs: false
  308. tcpKeepAlive: 2s
  309. keepAliveConnections: 2
  310. keepAliveTimeout: 2s
  311. httpHostHeader: def
  312. originServerName: b2
  313. caPool: /tmp/path1
  314. noTLSVerify: false
  315. disableChunkedEncoding: false
  316. bastionMode: false
  317. proxyAddress: interface
  318. proxyPort: 200
  319. proxyType: ""
  320. ipRules:
  321. - prefix: "10.0.0.0/16"
  322. ports:
  323. - 3000
  324. - 3030
  325. allow: false
  326. - prefix: "192.16.0.0/24"
  327. ports:
  328. - 5000
  329. - 5050
  330. allow: true
  331. `
  332. ing, err := ParseIngress(MustReadIngress(rulesYAML))
  333. if err != nil {
  334. t.Error(err)
  335. }
  336. validate(ing)
  337. rawConfig := []byte(`
  338. {
  339. "ingress": [
  340. {
  341. "hostname": "tun.example.com",
  342. "service": "https://localhost:8000"
  343. },
  344. {
  345. "hostname": "*",
  346. "service": "https://localhost:8001",
  347. "originRequest": {
  348. "connectTimeout": 120,
  349. "tlsTimeout": 2,
  350. "noHappyEyeballs": false,
  351. "tcpKeepAlive": 2,
  352. "keepAliveConnections": 2,
  353. "keepAliveTimeout": 2,
  354. "httpHostHeader": "def",
  355. "originServerName": "b2",
  356. "caPool": "/tmp/path1",
  357. "noTLSVerify": false,
  358. "disableChunkedEncoding": false,
  359. "bastionMode": false,
  360. "proxyAddress": "interface",
  361. "proxyPort": 200,
  362. "proxyType": "",
  363. "ipRules": [
  364. {
  365. "prefix": "10.0.0.0/16",
  366. "ports": [3000, 3030],
  367. "allow": false
  368. },
  369. {
  370. "prefix": "192.16.0.0/24",
  371. "ports": [5000, 5050],
  372. "allow": true
  373. }
  374. ]
  375. }
  376. }
  377. ]
  378. }
  379. `)
  380. var remoteConfig RemoteConfig
  381. err = json.Unmarshal(rawConfig, &remoteConfig)
  382. require.NoError(t, err)
  383. validate(remoteConfig.Ingress)
  384. }
  385. func TestDefaultConfigFromCLI(t *testing.T) {
  386. set := flag.NewFlagSet("contrive", 0)
  387. c := cli.NewContext(nil, set, nil)
  388. expected := OriginRequestConfig{
  389. ConnectTimeout: defaultHTTPConnectTimeout,
  390. TLSTimeout: defaultTLSTimeout,
  391. TCPKeepAlive: defaultTCPKeepAlive,
  392. KeepAliveConnections: defaultKeepAliveConnections,
  393. KeepAliveTimeout: defaultKeepAliveTimeout,
  394. ProxyAddress: defaultProxyAddress,
  395. }
  396. actual := originRequestFromSingleRule(c)
  397. require.Equal(t, expected, actual)
  398. }
  399. func newIPRule(t *testing.T, prefix string, ports []int, allow bool) ipaccess.Rule {
  400. rule, err := ipaccess.NewRuleByCIDR(&prefix, ports, allow)
  401. require.NoError(t, err)
  402. return rule
  403. }