1
mirror of https://github.com/xddxdd/bird-lg-go synced 2025-10-21 01:42:15 +02:00

93 Commits

Author SHA1 Message Date
Lan Tian
bb933954a9 release: v1.3.12.1 2025-10-13 19:35:11 -07:00
Lan Tian
4898a095b1 ci: update go release action dependency 2025-10-13 19:34:55 -07:00
Lan Tian
76fa6c6d06 ci: disable CGO when building release binaries 2025-10-13 19:34:23 -07:00
Lan Tian
ae6460d2d3 release: v1.3.12 2025-10-04 13:41:36 -07:00
Ljcbaby
d312f7de1b fix: " cuase unexpected TEXT but ' not 2025-10-04 13:40:45 -07:00
Ljcbaby
1097e69070 fix: " cuase unexpected TEXT but ' not 2025-10-04 13:40:45 -07:00
Lan Tian
4a8c752157 release: v1.3.11 2025-09-27 01:54:01 -07:00
Lan Tian
5ad6a4d35c frontend: fix race condition when setting up web server 2025-09-27 01:50:53 -07:00
Ljcbaby
5422c8fd8c refactor: deplcate Handle register 2025-09-27 01:50:53 -07:00
Ljcbaby
5042980d79 frontend multi listen 2025-09-27 01:50:53 -07:00
Ljcbaby
7884531a24 proxy multi listen 2025-09-27 01:50:53 -07:00
dependabot[bot]
cc804e81b6 Merge pull request #129 from xddxdd/dependabot/go_modules/frontend/github.com/spf13/viper-1.21.0 2025-09-09 00:07:01 +00:00
dependabot[bot]
c9bab2ae2b Merge pull request #130 from xddxdd/dependabot/go_modules/proxy/github.com/spf13/viper-1.21.0 2025-09-09 00:05:57 +00:00
dependabot[bot]
b9a4f95978 build(deps): bump github.com/spf13/viper from 1.19.0 to 1.21.0 in /proxy
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.19.0 to 1.21.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.19.0...v1.21.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/viper
  dependency-version: 1.21.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-09 00:04:35 +00:00
dependabot[bot]
7cd69746df build(deps): bump github.com/spf13/viper in /frontend
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.19.0 to 1.21.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.19.0...v1.21.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/viper
  dependency-version: 1.21.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-09 00:04:30 +00:00
dependabot[bot]
e719859d68 Merge pull request #128 from xddxdd/dependabot/go_modules/proxy/github.com/spf13/pflag-1.0.10 2025-09-03 22:06:35 +00:00
dependabot[bot]
5d06affefc build(deps): bump github.com/spf13/pflag from 1.0.8 to 1.0.10 in /proxy
Bumps [github.com/spf13/pflag](https://github.com/spf13/pflag) from 1.0.8 to 1.0.10.
- [Release notes](https://github.com/spf13/pflag/releases)
- [Commits](https://github.com/spf13/pflag/compare/v1.0.8...v1.0.10)

---
updated-dependencies:
- dependency-name: github.com/spf13/pflag
  dependency-version: 1.0.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-03 22:05:01 +00:00
dependabot[bot]
060fe9bf8e Merge pull request #127 from xddxdd/dependabot/go_modules/frontend/github.com/spf13/pflag-1.0.10 2025-09-03 19:13:13 +00:00
dependabot[bot]
f23d36f357 build(deps): bump github.com/spf13/pflag in /frontend
Bumps [github.com/spf13/pflag](https://github.com/spf13/pflag) from 1.0.8 to 1.0.10.
- [Release notes](https://github.com/spf13/pflag/releases)
- [Commits](https://github.com/spf13/pflag/compare/v1.0.8...v1.0.10)

---
updated-dependencies:
- dependency-name: github.com/spf13/pflag
  dependency-version: 1.0.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-03 19:11:24 +00:00
dependabot[bot]
1dbc0fccd2 Merge pull request #126 from xddxdd/dependabot/go_modules/frontend/github.com/spf13/pflag-1.0.8 2025-09-01 00:08:36 +00:00
dependabot[bot]
b2d64d19e3 build(deps): bump github.com/spf13/pflag in /frontend
Bumps [github.com/spf13/pflag](https://github.com/spf13/pflag) from 1.0.7 to 1.0.8.
- [Release notes](https://github.com/spf13/pflag/releases)
- [Commits](https://github.com/spf13/pflag/compare/v1.0.7...v1.0.8)

---
updated-dependencies:
- dependency-name: github.com/spf13/pflag
  dependency-version: 1.0.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-01 00:06:28 +00:00
dependabot[bot]
4dee4b0806 Merge pull request #125 from xddxdd/dependabot/go_modules/proxy/github.com/spf13/pflag-1.0.8 2025-09-01 00:06:18 +00:00
dependabot[bot]
cb279e0459 build(deps): bump github.com/spf13/pflag from 1.0.7 to 1.0.8 in /proxy
Bumps [github.com/spf13/pflag](https://github.com/spf13/pflag) from 1.0.7 to 1.0.8.
- [Release notes](https://github.com/spf13/pflag/releases)
- [Commits](https://github.com/spf13/pflag/compare/v1.0.7...v1.0.8)

---
updated-dependencies:
- dependency-name: github.com/spf13/pflag
  dependency-version: 1.0.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-01 00:04:16 +00:00
dependabot[bot]
31ba36beaf Merge pull request #123 from xddxdd/dependabot/go_modules/frontend/github.com/jarcoal/httpmock-1.4.1 2025-08-20 05:14:21 +00:00
dependabot[bot]
5ab3b95d64 build(deps): bump github.com/jarcoal/httpmock in /frontend
Bumps [github.com/jarcoal/httpmock](https://github.com/jarcoal/httpmock) from 1.4.0 to 1.4.1.
- [Release notes](https://github.com/jarcoal/httpmock/releases)
- [Commits](https://github.com/jarcoal/httpmock/compare/v1.4.0...v1.4.1)

---
updated-dependencies:
- dependency-name: github.com/jarcoal/httpmock
  dependency-version: 1.4.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-20 05:12:43 +00:00
dependabot[bot]
7bf654f35f Merge pull request #122 from xddxdd/dependabot/go_modules/proxy/github.com/spf13/pflag-1.0.7 2025-07-17 00:25:25 +00:00
dependabot[bot]
5b33629a9d build(deps): bump github.com/spf13/pflag from 1.0.6 to 1.0.7 in /proxy
Bumps [github.com/spf13/pflag](https://github.com/spf13/pflag) from 1.0.6 to 1.0.7.
- [Release notes](https://github.com/spf13/pflag/releases)
- [Commits](https://github.com/spf13/pflag/compare/v1.0.6...v1.0.7)

---
updated-dependencies:
- dependency-name: github.com/spf13/pflag
  dependency-version: 1.0.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-17 00:23:21 +00:00
dependabot[bot]
0868c5d42c Merge pull request #121 from xddxdd/dependabot/go_modules/frontend/github.com/spf13/pflag-1.0.7 2025-07-17 00:09:29 +00:00
dependabot[bot]
bc61579e6a build(deps): bump github.com/spf13/pflag in /frontend
Bumps [github.com/spf13/pflag](https://github.com/spf13/pflag) from 1.0.6 to 1.0.7.
- [Release notes](https://github.com/spf13/pflag/releases)
- [Commits](https://github.com/spf13/pflag/compare/v1.0.6...v1.0.7)

---
updated-dependencies:
- dependency-name: github.com/spf13/pflag
  dependency-version: 1.0.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-17 00:07:25 +00:00
Lan Tian
e9750a8278 release: v1.3.10 2025-07-02 19:58:51 -07:00
Lan Tian
d40dd3a4d3 frontend: handle protocol names with dash 2025-07-01 17:45:12 -07:00
Lan Tian
ffdeeac06e frontend: refactor summary table parsing 2025-07-01 00:55:15 -07:00
Lan Tian
7eb4d75bbf frontend: handle protocol names starting with number 2025-07-01 00:04:03 -07:00
Lan Tian
6e5e190d32 frontend: try fix TestWhoisConnectionError on darwin 2025-06-08 22:01:39 -07:00
Lan Tian
1b2573d87c release: v1.3.9 2025-04-23 21:13:36 -07:00
Lan Tian
0d5337508b frontend: update API test to adapt to changes in #118 2025-04-23 21:12:40 -07:00
Mxmilu666
b9094d3d6c fix(template): typo 2025-04-23 21:08:40 -07:00
Mxmilu666
ec7f348418 feat(api): return data from available services even if one backend fails 2025-04-23 21:08:40 -07:00
dependabot[bot]
a632739443 Merge pull request #117 from xddxdd/dependabot/go_modules/proxy/github.com/magiconair/properties-1.8.10 2025-04-10 00:55:19 +00:00
dependabot[bot]
a9e278357a build(deps): bump github.com/magiconair/properties in /proxy
Bumps [github.com/magiconair/properties](https://github.com/magiconair/properties) from 1.8.9 to 1.8.10.
- [Release notes](https://github.com/magiconair/properties/releases)
- [Commits](https://github.com/magiconair/properties/compare/v1.8.9...v1.8.10)

---
updated-dependencies:
- dependency-name: github.com/magiconair/properties
  dependency-version: 1.8.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-10 00:53:10 +00:00
dependabot[bot]
e4c00c897f Merge pull request #116 from xddxdd/dependabot/go_modules/frontend/github.com/magiconair/properties-1.8.10 2025-04-10 00:22:47 +00:00
dependabot[bot]
4df3918b35 build(deps): bump github.com/magiconair/properties in /frontend
Bumps [github.com/magiconair/properties](https://github.com/magiconair/properties) from 1.8.9 to 1.8.10.
- [Release notes](https://github.com/magiconair/properties/releases)
- [Commits](https://github.com/magiconair/properties/compare/v1.8.9...v1.8.10)

---
updated-dependencies:
- dependency-name: github.com/magiconair/properties
  dependency-version: 1.8.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-10 00:20:55 +00:00
dependabot[bot]
45dc24470d Merge pull request #115 from xddxdd/dependabot/go_modules/frontend/github.com/jarcoal/httpmock-1.4.0 2025-04-07 00:54:10 +00:00
dependabot[bot]
55ea5c3b28 build(deps): bump github.com/jarcoal/httpmock in /frontend
Bumps [github.com/jarcoal/httpmock](https://github.com/jarcoal/httpmock) from 1.3.1 to 1.4.0.
- [Release notes](https://github.com/jarcoal/httpmock/releases)
- [Commits](https://github.com/jarcoal/httpmock/compare/v1.3.1...v1.4.0)

---
updated-dependencies:
- dependency-name: github.com/jarcoal/httpmock
  dependency-version: 1.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-07 00:52:24 +00:00
dependabot[bot]
7eb44c3828 Merge pull request #110 from xddxdd/dependabot/go_modules/proxy/github.com/spf13/pflag-1.0.6 2025-01-30 00:16:58 +00:00
dependabot[bot]
124fdedbda build(deps): bump github.com/spf13/pflag from 1.0.5 to 1.0.6 in /proxy
Bumps [github.com/spf13/pflag](https://github.com/spf13/pflag) from 1.0.5 to 1.0.6.
- [Release notes](https://github.com/spf13/pflag/releases)
- [Commits](https://github.com/spf13/pflag/compare/v1.0.5...v1.0.6)

---
updated-dependencies:
- dependency-name: github.com/spf13/pflag
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-30 00:14:41 +00:00
dependabot[bot]
e6a98358b5 Merge pull request #109 from xddxdd/dependabot/go_modules/frontend/github.com/spf13/pflag-1.0.6 2025-01-30 00:04:45 +00:00
dependabot[bot]
761eb2160a build(deps): bump github.com/spf13/pflag in /frontend
Bumps [github.com/spf13/pflag](https://github.com/spf13/pflag) from 1.0.5 to 1.0.6.
- [Release notes](https://github.com/spf13/pflag/releases)
- [Commits](https://github.com/spf13/pflag/compare/v1.0.5...v1.0.6)

---
updated-dependencies:
- dependency-name: github.com/spf13/pflag
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-30 00:02:32 +00:00
dependabot[bot]
cc2a146a88 Merge pull request #108 from xddxdd/dependabot/go_modules/frontend/github.com/magiconair/properties-1.8.9 2024-12-09 00:51:25 +00:00
dependabot[bot]
3db9454350 build(deps): bump github.com/magiconair/properties in /frontend
Bumps [github.com/magiconair/properties](https://github.com/magiconair/properties) from 1.8.7 to 1.8.9.
- [Release notes](https://github.com/magiconair/properties/releases)
- [Commits](https://github.com/magiconair/properties/compare/v1.8.7...v1.8.9)

---
updated-dependencies:
- dependency-name: github.com/magiconair/properties
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-09 00:50:06 +00:00
dependabot[bot]
c30bed112c Merge pull request #107 from xddxdd/dependabot/go_modules/proxy/github.com/magiconair/properties-1.8.9 2024-12-09 00:18:14 +00:00
dependabot[bot]
af5ab3c78f build(deps): bump github.com/magiconair/properties in /proxy
Bumps [github.com/magiconair/properties](https://github.com/magiconair/properties) from 1.8.7 to 1.8.9.
- [Release notes](https://github.com/magiconair/properties/releases)
- [Commits](https://github.com/magiconair/properties/compare/v1.8.7...v1.8.9)

---
updated-dependencies:
- dependency-name: github.com/magiconair/properties
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-09 00:16:23 +00:00
Marc 'risson' Schmitt
0fdde8afc7 frontend: allow webserver to trust proxy headers (#106)
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2024-07-14 15:50:18 -07:00
Lan Tian
39a129db9d release: v1.3.8 2024-07-01 21:31:55 -07:00
Lan Tian
0dd1c07b66 frontend: disable escaping of special HTML chars for BGPMap graph 2024-07-01 21:17:43 -07:00
Lan Tian
f0f072c4a6 frontend: handle UTF-8 characters in GraphViz graphs 2024-06-30 13:04:15 -07:00
dependabot[bot]
657565857b Merge pull request #104 from xddxdd/dependabot/go_modules/proxy/github.com/spf13/viper-1.19.0 2024-06-03 00:40:25 +00:00
dependabot[bot]
7ac2158e70 build(deps): bump github.com/spf13/viper from 1.18.2 to 1.19.0 in /proxy
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.18.2 to 1.19.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.18.2...v1.19.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/viper
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-03 00:38:37 +00:00
dependabot[bot]
5c433bc27a Merge pull request #103 from xddxdd/dependabot/go_modules/frontend/github.com/spf13/viper-1.19.0 2024-06-03 00:35:26 +00:00
dependabot[bot]
1b0b923da9 build(deps): bump github.com/spf13/viper in /frontend
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.18.2 to 1.19.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.18.2...v1.19.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/viper
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-03 00:32:44 +00:00
Lan Tian
01438edaef release: v1.3.7.1 2024-03-12 18:05:50 -07:00
Lan Tian
90f36610dc ci: fix failing whois test 2024-03-12 18:03:23 -07:00
Lan Tian
6174208d07 ci: fix typo in release workflow 2024-03-12 17:56:52 -07:00
Lan Tian
76174cdc08 release: v1.3.7 2024-03-12 17:47:29 -07:00
Lan Tian
088bb6fe5a readme: add mention of MTR based lgproxy docker image 2024-03-12 17:46:56 -07:00
Lan Tian
3951eed011 ci: fix dockerfile path for proxy image with mtr 2024-03-09 12:21:00 -08:00
Lan Tian
91c0a8962b ci: also test mtr binary in proxy image 2024-03-09 12:09:19 -08:00
Lan Tian
5f7850a903 ci: also build docker image with mtr on regular commits 2024-03-09 12:05:20 -08:00
Lan Tian
6a78cf2e80 proxy: add docker image with mtr 2024-03-09 12:04:05 -08:00
Lan Tian
5b5a44bcb6 release: v1.3.6 2024-02-25 19:47:39 -08:00
Marc 'risson' Schmitt
ac31862237 frontend: show dynamic BGP sessions without any color (#98)
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2024-02-18 22:11:20 -08:00
Lan Tian
86129190ab release: v1.3.5 2024-01-01 14:43:25 -08:00
Lan Tian
ff55064a20 frontend: adjust navbar display of many servers 2023-12-31 16:31:56 -08:00
dependabot[bot]
dbb02c04ed Merge pull request #95 from xddxdd/dependabot/go_modules/proxy/github.com/spf13/viper-1.18.2 2023-12-19 00:50:09 +00:00
dependabot[bot]
c2b7de2e17 build(deps): bump github.com/spf13/viper from 1.18.1 to 1.18.2 in /proxy
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.18.1 to 1.18.2.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.18.1...v1.18.2)

---
updated-dependencies:
- dependency-name: github.com/spf13/viper
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-19 00:48:47 +00:00
dependabot[bot]
c1b578e8db Merge pull request #94 from xddxdd/dependabot/go_modules/frontend/github.com/spf13/viper-1.18.2 2023-12-19 00:28:10 +00:00
dependabot[bot]
7b0e5689d4 build(deps): bump github.com/spf13/viper in /frontend
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.18.1 to 1.18.2.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.18.1...v1.18.2)

---
updated-dependencies:
- dependency-name: github.com/spf13/viper
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-19 00:26:13 +00:00
dependabot[bot]
3c46bda49d Merge pull request #93 from xddxdd/dependabot/go_modules/frontend/github.com/spf13/viper-1.18.1 2023-12-11 00:45:58 +00:00
dependabot[bot]
32e00d2ce3 build(deps): bump github.com/spf13/viper in /frontend
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.18.0 to 1.18.1.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.18.0...v1.18.1)

---
updated-dependencies:
- dependency-name: github.com/spf13/viper
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-11 00:44:41 +00:00
dependabot[bot]
a19750cdef Merge pull request #92 from xddxdd/dependabot/go_modules/proxy/github.com/spf13/viper-1.18.1 2023-12-11 00:04:27 +00:00
dependabot[bot]
7f1cdaa4ee build(deps): bump github.com/spf13/viper from 1.18.0 to 1.18.1 in /proxy
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.18.0 to 1.18.1.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.18.0...v1.18.1)

---
updated-dependencies:
- dependency-name: github.com/spf13/viper
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-11 00:03:09 +00:00
Lan Tian
2d2193041e release: v1.3.4 2023-12-06 23:54:47 -08:00
dependabot[bot]
aad8ee98d7 Merge pull request #91 from xddxdd/dependabot/go_modules/frontend/github.com/spf13/viper-1.18.0 2023-12-07 00:58:49 +00:00
dependabot[bot]
00b5c12787 build(deps): bump github.com/spf13/viper in /frontend
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.17.0 to 1.18.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.17.0...v1.18.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/viper
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-07 00:57:01 +00:00
dependabot[bot]
55a1eb54fd Merge pull request #90 from xddxdd/dependabot/go_modules/proxy/github.com/spf13/viper-1.18.0 2023-12-07 00:33:12 +00:00
dependabot[bot]
0594edc69d build(deps): bump github.com/spf13/viper from 1.17.0 to 1.18.0 in /proxy
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.17.0 to 1.18.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.17.0...v1.18.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/viper
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-07 00:31:37 +00:00
Lan Tian
38bf6aba09 frontend: fix unit tests 2023-11-25 10:47:37 -08:00
Lan Tian
d261c22235 frontend: add connection timeout 2023-11-25 10:30:13 -08:00
dependabot[bot]
19aa8c77c5 Merge pull request #88 from xddxdd/dependabot/go_modules/frontend/github.com/gorilla/handlers-1.5.2 2023-11-06 01:01:43 +00:00
dependabot[bot]
fe07ebb5a5 build(deps): bump github.com/gorilla/handlers in /frontend
Bumps [github.com/gorilla/handlers](https://github.com/gorilla/handlers) from 1.5.1 to 1.5.2.
- [Release notes](https://github.com/gorilla/handlers/releases)
- [Commits](https://github.com/gorilla/handlers/compare/v1.5.1...v1.5.2)

---
updated-dependencies:
- dependency-name: github.com/gorilla/handlers
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-06 01:00:08 +00:00
dependabot[bot]
66547ebfa9 Merge pull request #87 from xddxdd/dependabot/go_modules/proxy/github.com/gorilla/handlers-1.5.2 2023-11-06 00:24:24 +00:00
dependabot[bot]
d253e4311b build(deps): bump github.com/gorilla/handlers in /proxy
Bumps [github.com/gorilla/handlers](https://github.com/gorilla/handlers) from 1.5.1 to 1.5.2.
- [Release notes](https://github.com/gorilla/handlers/releases)
- [Commits](https://github.com/gorilla/handlers/compare/v1.5.1...v1.5.2)

---
updated-dependencies:
- dependency-name: github.com/gorilla/handlers
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-06 00:22:00 +00:00
Lan Tian
026498ba2f general: auto merge updates from dependabot 2023-10-24 23:32:55 -07:00
28 changed files with 544 additions and 4350 deletions

16
.github/workflows/auto-merge.yml vendored Normal file
View File

@@ -0,0 +1,16 @@
name: auto-merge
on:
pull_request_target:
jobs:
auto-merge:
name: Dependabot Auto Merge
runs-on: ubuntu-latest
if: github.actor == 'dependabot[bot]' || github.actor == 'dependabot-preview[bot]'
steps:
- uses: actions/checkout@v2
- uses: ahmadnassri/action-dependabot-auto-merge@v2
with:
target: minor
github-token: ${{ secrets.AUTOMERGE_TOKEN }}

View File

@@ -47,7 +47,7 @@ jobs:
- name: Test whois binary in frontend image - name: Test whois binary in frontend image
run: | run: |
docker build -t local/frontend frontend/ docker build -t local/frontend frontend/
docker run --rm --net host --entrypoint whois local/frontend github.com || exit 1 docker run --rm --net host --entrypoint whois local/frontend -I github.com || exit 1
docker run --rm --net host --entrypoint whois local/frontend -h whois.ripe.net github.com || exit 1 docker run --rm --net host --entrypoint whois local/frontend -h whois.ripe.net github.com || exit 1
docker run --rm --net host --entrypoint whois local/frontend -h whois.ripe.net:43 github.com || exit 1 docker run --rm --net host --entrypoint whois local/frontend -h whois.ripe.net:43 github.com || exit 1
@@ -57,6 +57,12 @@ jobs:
docker run --rm --net host --entrypoint traceroute local/proxy 127.0.0.1 || exit 1 docker run --rm --net host --entrypoint traceroute local/proxy 127.0.0.1 || exit 1
docker run --rm --net host --entrypoint traceroute local/proxy ::1 || exit 1 docker run --rm --net host --entrypoint traceroute local/proxy ::1 || exit 1
- name: Test mtr binary in proxy image
run: |
docker build -t local/proxy:mtr -f proxy/Dockerfile.mtr proxy/
docker run --rm --net host --entrypoint mtr local/proxy:mtr -w -c1 -Z1 -G1 -b 127.0.0.1 || exit 1
docker run --rm --net host --entrypoint mtr local/proxy:mtr -w -c1 -Z1 -G1 -b ::1 || exit 1
docker-develop: docker-develop:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: needs:
@@ -106,3 +112,16 @@ jobs:
xddxdd/bird-lgproxy-go:develop-${{ github.sha }} xddxdd/bird-lgproxy-go:develop-${{ github.sha }}
ghcr.io/xddxdd/bird-lg-go:proxy-develop ghcr.io/xddxdd/bird-lg-go:proxy-develop
ghcr.io/xddxdd/bird-lg-go:proxy-develop-${{ github.sha }} ghcr.io/xddxdd/bird-lg-go:proxy-develop-${{ github.sha }}
- name: Build proxy docker image
uses: docker/build-push-action@v4
with:
context: '{{defaultContext}}:proxy'
file: 'Dockerfile.mtr'
platforms: linux/amd64,linux/arm64,linux/386,linux/arm/v7
push: true
tags: |
xddxdd/bird-lgproxy-go:develop-mtr
xddxdd/bird-lgproxy-go:develop-${{ github.sha }}-mtr
ghcr.io/xddxdd/bird-lg-go:proxy-develop-mtr
ghcr.io/xddxdd/bird-lg-go:proxy-develop-${{ github.sha }}-mtr

View File

@@ -23,22 +23,26 @@ jobs:
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Release frontend - name: Release frontend
uses: wangyoucao577/go-release-action@v1.40 uses: wangyoucao577/go-release-action@v1.53
with: with:
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
goos: ${{ matrix.goos }} goos: ${{ matrix.goos }}
goarch: ${{ matrix.goarch }} goarch: ${{ matrix.goarch }}
project_path: "./frontend" project_path: "./frontend"
binary_name: "bird-lg-go" binary_name: "bird-lg-go"
pre_command: |
export CGO_ENABLED=0
- name: Release proxy - name: Release proxy
uses: wangyoucao577/go-release-action@v1.40 uses: wangyoucao577/go-release-action@v1.53
with: with:
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
goos: ${{ matrix.goos }} goos: ${{ matrix.goos }}
goarch: ${{ matrix.goarch }} goarch: ${{ matrix.goarch }}
project_path: "./proxy" project_path: "./proxy"
binary_name: "bird-lgproxy-go" binary_name: "bird-lgproxy-go"
pre_command: |
export CGO_ENABLED=0
docker-release: docker-release:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -85,3 +89,16 @@ jobs:
xddxdd/bird-lgproxy-go:${{ github.event.release.tag_name }} xddxdd/bird-lgproxy-go:${{ github.event.release.tag_name }}
ghcr.io/xddxdd/bird-lg-go:proxy ghcr.io/xddxdd/bird-lg-go:proxy
ghcr.io/xddxdd/bird-lg-go:proxy-${{ github.event.release.tag_name }} ghcr.io/xddxdd/bird-lg-go:proxy-${{ github.event.release.tag_name }}
- name: Build proxy docker image
uses: docker/build-push-action@v4
with:
context: '{{defaultContext}}:proxy'
file: 'Dockerfile.mtr'
platforms: linux/amd64,linux/arm64,linux/386,linux/arm/v7
push: true
tags: |
xddxdd/bird-lgproxy-go:latest-mtr
xddxdd/bird-lgproxy-go:${{ github.event.release.tag_name }}-mtr
ghcr.io/xddxdd/bird-lg-go:proxy-mtr
ghcr.io/xddxdd/bird-lg-go:proxy-${{ github.event.release.tag_name }}-mtr

4
.gitignore vendored
View File

@@ -20,7 +20,3 @@ proxy/proxy
# don't include generated bindata file # don't include generated bindata file
frontend/bindata.go frontend/bindata.go
# don't include generated Dockerfiles
frontend/Dockerfile.*
proxy/Dockerfile.*

View File

@@ -167,6 +167,7 @@ Example: the following docker-compose.yml entry does the same as above, but by s
services: services:
bird-lgproxy: bird-lgproxy:
# Use xddxdd/bird-lgproxy-go:develop for the latest build from master branch # Use xddxdd/bird-lgproxy-go:develop for the latest build from master branch
# Use xddxdd/bird-lgproxy-go:latest-mtr to use MTR instead of Traceroute
image: xddxdd/bird-lgproxy-go:latest image: xddxdd/bird-lgproxy-go:latest
container_name: bird-lgproxy container_name: bird-lgproxy
restart: always restart: always

View File

@@ -1 +1 @@
v1.3.3 v1.3.12.1

View File

@@ -20,6 +20,7 @@ type apiGenericResultPair struct {
type apiSummaryResultPair struct { type apiSummaryResultPair struct {
Server string `json:"server"` Server string `json:"server"`
Data []SummaryRowData `json:"data"` Data []SummaryRowData `json:"data"`
Error string `json:"error,omitempty"`
} }
type apiResponse struct { type apiResponse struct {
@@ -70,9 +71,12 @@ func apiSummaryHandler(request apiRequest) apiResponse {
for i, result := range results { for i, result := range results {
parsedSummary, err := summaryParse(result, request.Servers[i]) parsedSummary, err := summaryParse(result, request.Servers[i])
if err != nil { if err != nil {
return apiResponse{ response.Result = append(response.Result, &apiSummaryResultPair{
Error: err.Error(), Server: request.Servers[i],
} Data: []SummaryRowData{},
Error: err.Error(),
})
continue
} }
response.Result = append(response.Result, &apiSummaryResultPair{ response.Result = append(response.Result, &apiSummaryResultPair{

View File

@@ -73,13 +73,14 @@ func TestApiSummaryHandler(t *testing.T) {
summary := response.Result[0].(*apiSummaryResultPair) summary := response.Result[0].(*apiSummaryResultPair)
assert.Equal(t, summary.Server, "alpha") assert.Equal(t, summary.Server, "alpha")
assert.Equal(t, len(summary.Data), 7)
// Protocol list will be sorted // Protocol list will be sorted
assert.Equal(t, summary.Data[1].Name, "device1") assert.Equal(t, summary.Data[0].Name, "device1")
assert.Equal(t, summary.Data[1].Proto, "Device") assert.Equal(t, summary.Data[0].Proto, "Device")
assert.Equal(t, summary.Data[1].Table, "---") assert.Equal(t, summary.Data[0].Table, "---")
assert.Equal(t, summary.Data[1].State, "up") assert.Equal(t, summary.Data[0].State, "up")
assert.Equal(t, summary.Data[1].Since, "2021-08-27") assert.Equal(t, summary.Data[0].Since, "2021-08-27")
assert.Equal(t, summary.Data[1].Info, "") assert.Equal(t, summary.Data[0].Info, "")
} }
func TestApiSummaryHandlerError(t *testing.T) { func TestApiSummaryHandlerError(t *testing.T) {
@@ -100,7 +101,10 @@ func TestApiSummaryHandlerError(t *testing.T) {
} }
response := apiSummaryHandler(request) response := apiSummaryHandler(request)
assert.Equal(t, response.Error, "Mock backend error") assert.Equal(t, response.Error, "")
summary := response.Result[0].(*apiSummaryResultPair)
assert.Equal(t, summary.Error, "Mock backend error")
} }
func TestApiWhoisHandler(t *testing.T) { func TestApiWhoisHandler(t *testing.T) {

View File

@@ -5,8 +5,19 @@
<script src="/static/jsdelivr/npm/viz.js@2.1.2/viz.min.js" crossorigin="anonymous"></script> <script src="/static/jsdelivr/npm/viz.js@2.1.2/viz.min.js" crossorigin="anonymous"></script>
<script src="/static/jsdelivr/npm/viz.js@2.1.2/lite.render.js" crossorigin="anonymous"></script> <script src="/static/jsdelivr/npm/viz.js@2.1.2/lite.render.js" crossorigin="anonymous"></script>
<script> <script>
function decodeBase64(base64) {
const text = atob(base64);
const length = text.length;
const bytes = new Uint8Array(length);
for (let i = 0; i < length; i++) {
bytes[i] = text.charCodeAt(i);
}
const decoder = new TextDecoder();
return decoder.decode(bytes);
}
var viz = new Viz(); var viz = new Viz();
viz.renderSVGElement(atob({{ .Result }})) viz.renderSVGElement(decodeBase64({{ .Result }}))
.then(element => { .then(element => {
document.getElementById("bgpmap").appendChild(element); document.getElementById("bgpmap").appendChild(element);
}) })

View File

@@ -7,7 +7,20 @@
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<meta name="renderer" content="webkit"> <meta name="renderer" content="webkit">
<title>{{ html .Title }}</title> <title>{{ html .Title }}</title>
<link rel="stylesheet" href="/static/jsdelivr/npm/bootstrap@4.5.1/dist/css/bootstrap.min.css" integrity="sha256-VoFZSlmyTXsegReQCNmbXrS4hBBUl/cexZvPmPWoJsY=" crossorigin="anonymous"> <link rel="stylesheet" href="/static/jsdelivr/npm/bootstrap@4.5.1/dist/css/bootstrap.min.css" crossorigin="anonymous">
<style>
.navbar-nav {
flex-wrap: wrap;
}
@media (min-width: 768px) {
.navbar form {
min-width: 400px;
}
.nav-link {
padding: 0.2rem 0.5rem !important;
}
}
</style>
<meta name="robots" content="noindex, nofollow"> <meta name="robots" content="noindex, nofollow">
</head> </head>
<body> <body>
@@ -74,8 +87,8 @@
{{ .Content }} {{ .Content }}
</div> </div>
<script src="/static/jsdelivr/npm/jquery@3.5.1/dist/jquery.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script> <script src="/static/jsdelivr/npm/jquery@3.5.1/dist/jquery.min.js" crossorigin="anonymous"></script>
<script src="/static/jsdelivr/npm/bootstrap@4.5.1/dist/js/bootstrap.min.js" integrity="sha256-0IiaoZCI++9oAAvmCb5Y0r93XkuhvJpRalZLffQXLok=" crossorigin="anonymous"></script> <script src="/static/jsdelivr/npm/bootstrap@4.5.1/dist/js/bootstrap.min.js" crossorigin="anonymous"></script>
<script src="/static/sortTable.js"></script> <script src="/static/sortTable.js"></script>
<script> <script>

View File

@@ -1,6 +1,7 @@
package main package main
import ( import (
"bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"strings" "strings"
@@ -69,11 +70,15 @@ func (graph *RouteGraph) attrsToString(attrs RouteAttrs) string {
} }
func (graph *RouteGraph) escape(s string) string { func (graph *RouteGraph) escape(s string) string {
result, err := json.Marshal(s) buffer := &bytes.Buffer{}
encoder := json.NewEncoder(buffer)
encoder.SetEscapeHTML(false)
err := encoder.Encode(s)
if err != nil { if err != nil {
return err.Error() return err.Error()
} else { } else {
return string(result) return string(buffer.Bytes())
} }
} }

View File

@@ -33,7 +33,7 @@ func TestBirdRouteToGraphvizXSS(t *testing.T) {
fakeResult, fakeResult,
}, fakeResult) }, fakeResult)
if strings.Contains(result, "<script>") { if strings.Contains(result, fakeResult) {
t.Errorf("XSS injection succeeded: %s", result) t.Errorf("XSS injection succeeded: %s", result)
} }
} }

View File

@@ -1,33 +1,27 @@
module github.com/xddxdd/bird-lg-go/frontend module github.com/xddxdd/bird-lg-go/frontend
go 1.17 go 1.23.0
require ( require (
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/gorilla/handlers v1.5.1 github.com/gorilla/handlers v1.5.2
github.com/jarcoal/httpmock v1.3.1 github.com/jarcoal/httpmock v1.4.1
github.com/magiconair/properties v1.8.7 github.com/magiconair/properties v1.8.10
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.10
github.com/spf13/viper v1.17.0 github.com/spf13/viper v1.21.0
) )
require ( require (
github.com/felixge/httpsnoop v1.0.3 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/sagikazarmark/locafero v0.11.0 // indirect
github.com/sagikazarmark/locafero v0.3.0 // indirect github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/spf13/afero v1.15.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/cast v1.10.0 // indirect
github.com/spf13/afero v1.10.0 // indirect
github.com/spf13/cast v1.5.1 // indirect
github.com/subosito/gotenv v1.6.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect
go.uber.org/atomic v1.9.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect
go.uber.org/multierr v1.9.0 // indirect golang.org/x/sys v0.29.0 // indirect
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect golang.org/x/text v0.28.0 // indirect
golang.org/x/sys v0.12.0 // indirect
golang.org/x/text v0.13.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
) )

File diff suppressed because it is too large Load Diff

View File

@@ -2,11 +2,14 @@ package main
import ( import (
"io" "io"
"net"
"net/http" "net/http"
"net/url" "net/url"
"strconv" "strconv"
"strings" "strings"
"time" "time"
"github.com/jarcoal/httpmock"
) )
type channelData struct { type channelData struct {
@@ -14,6 +17,29 @@ type channelData struct {
data string data string
} }
func createConnectionTimeoutRoundTripper(timeout int) http.RoundTripper {
context := net.Dialer{
Timeout: time.Duration(timeout) * time.Second,
}
// Prefer httpmock's transport if activated, so unit tests can work
if http.DefaultTransport == httpmock.DefaultTransport {
return httpmock.DefaultTransport
}
return &http.Transport{
DialContext: context.DialContext,
// Default options from transport.go
Proxy: http.ProxyFromEnvironment,
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
}
// Send commands to lgproxy instances in parallel, and retrieve their responses // Send commands to lgproxy instances in parallel, and retrieve their responses
func batchRequest(servers []string, endpoint string, command string) []string { func batchRequest(servers []string, endpoint string, command string) []string {
// Channel and array for storing responses // Channel and array for storing responses
@@ -47,7 +73,10 @@ func batchRequest(servers []string, endpoint string, command string) []string {
} }
url := "http://" + hostname + ":" + strconv.Itoa(setting.proxyPort) + "/" + url.PathEscape(endpoint) + "?q=" + url.QueryEscape(command) url := "http://" + hostname + ":" + strconv.Itoa(setting.proxyPort) + "/" + url.PathEscape(endpoint) + "?q=" + url.QueryEscape(command)
go func(url string, i int) { go func(url string, i int) {
client := http.Client{Timeout: time.Duration(setting.timeOut) * time.Second} client := http.Client{
Transport: createConnectionTimeoutRoundTripper(setting.connectionTimeOut),
Timeout: time.Duration(setting.timeOut) * time.Second,
}
response, err := client.Get(url) response, err := client.Get(url)
if err != nil { if err != nil {
ch <- channelData{i, "request failed: " + err.Error() + "\n"} ch <- channelData{i, "request failed: " + err.Error() + "\n"}

View File

@@ -7,24 +7,26 @@ import (
) )
type settingType struct { type settingType struct {
servers []string servers []string
serversDisplay []string serversDisplay []string
domain string domain string
proxyPort int proxyPort int
whoisServer string whoisServer string
listen string listen []string
dnsInterface string dnsInterface string
netSpecificMode string netSpecificMode string
titleBrand string titleBrand string
navBarBrand string navBarBrand string
navBarBrandURL string navBarBrandURL string
navBarAllServer string navBarAllServer string
navBarAllURL string navBarAllURL string
bgpmapInfo string bgpmapInfo string
telegramBotName string telegramBotName string
protocolFilter []string protocolFilter []string
nameFilter string nameFilter string
timeOut int timeOut int
connectionTimeOut int
trustProxyHeaders bool
} }
var setting settingType var setting settingType
@@ -33,24 +35,29 @@ func main() {
parseSettings() parseSettings()
ImportTemplates() ImportTemplates()
var l net.Listener for _, listenAddr := range setting.listen {
var err error go func(listenAddr string) {
var l net.Listener
var err error
if strings.HasPrefix(setting.listen, "/") { if strings.HasPrefix(listenAddr, "/") {
// Delete existing socket file, ignore errors (will fail later anyway) // Delete existing socket file, ignore errors (will fail later anyway)
os.Remove(setting.listen) os.Remove(listenAddr)
l, err = net.Listen("unix", setting.listen) l, err = net.Listen("unix", listenAddr)
} else { } else {
listenAddr := setting.listen if !strings.Contains(listenAddr, ":") {
if !strings.Contains(listenAddr, ":") { listenAddr = ":" + listenAddr
listenAddr = ":" + listenAddr }
} l, err = net.Listen("tcp", listenAddr)
l, err = net.Listen("tcp", listenAddr) }
if err != nil {
panic(err)
}
webServerStart(l)
}(listenAddr)
} }
if err != nil { select {}
panic(err)
}
webServerStart(l)
} }

View File

@@ -35,15 +35,6 @@ var optionsMap = map[string]string{
"traceroute": "traceroute ...", "traceroute": "traceroute ...",
} }
// pre-compiled regexp and constant statemap for summary rendering
var splitSummaryLine = regexp.MustCompile(`(\w+)(\s+)(\w+)(\s+)([\w-]+)(\s+)(\w+)(\s+)([0-9\-\. :]+)(.*)`)
var summaryStateMap = map[string]string{
"up": "success",
"down": "secondary",
"start": "danger",
"passive": "info",
}
// render the page template // render the page template
func renderPageTemplate(w http.ResponseWriter, r *http.Request, title string, content template.HTML) { func renderPageTemplate(w http.ResponseWriter, r *http.Request, title string, content template.HTML) {
path := r.URL.Path[1:] path := r.URL.Path[1:]
@@ -143,58 +134,23 @@ func summaryParse(data string, serverName string) (TemplateSummary, error) {
// parse each line // parse each line
for _, line := range rows { for _, line := range rows {
row := SummaryRowDataFromLine(line)
// Ignore empty lines if row == nil {
line = strings.TrimSpace(line)
if len(line) == 0 {
continue continue
} }
// Parse a total of 6 columns from bird summary // Filter row name
lineSplitted := splitSummaryLine.FindStringSubmatch(line) if setting.nameFilter != "" && nameFilterRegexp.MatchString(row.Name) {
if lineSplitted == nil {
continue continue
} }
var row SummaryRowData // Filter away unwanted protocol types, if setting.protocolFilter is non-empty
if len(setting.protocolFilter) > 0 && !row.ProtocolMatches(setting.protocolFilter) {
if len(lineSplitted) >= 2 { continue
row.Name = strings.TrimSpace(lineSplitted[1])
if setting.nameFilter != "" && nameFilterRegexp.MatchString(row.Name) {
continue
}
}
if len(lineSplitted) >= 4 {
row.Proto = strings.TrimSpace(lineSplitted[3])
// Filter away unwanted protocol types, if setting.protocolFilter is non-empty
found := false
for _, protocol := range setting.protocolFilter {
if strings.EqualFold(row.Proto, protocol) {
found = true
break
}
}
if len(setting.protocolFilter) > 0 && !found {
continue
}
}
if len(lineSplitted) >= 6 {
row.Table = strings.TrimSpace(lineSplitted[5])
}
if len(lineSplitted) >= 8 {
row.State = strings.TrimSpace(lineSplitted[7])
row.MappedState = summaryStateMap[row.State]
}
if len(lineSplitted) >= 10 {
row.Since = strings.TrimSpace(lineSplitted[9])
}
if len(lineSplitted) >= 11 {
row.Info = strings.TrimSpace(lineSplitted[10])
} }
// add to the result // add to the result
args.Rows = append(args.Rows, row) args.Rows = append(args.Rows, *row)
} }
return args, nil return args, nil

View File

@@ -8,8 +8,7 @@ import (
"testing" "testing"
) )
const BirdSummaryData = `BIRD 2.0.8 ready. const BirdSummaryData = `Name Proto Table State Since Info
Name Proto Table State Since Info
static1 Static master4 up 2021-08-27 static1 Static master4 up 2021-08-27
static2 Static master6 up 2021-08-27 static2 Static master6 up 2021-08-27
device1 Device --- up 2021-08-27 device1 Device --- up 2021-08-27

View File

@@ -9,23 +9,25 @@ import (
) )
type viperSettingType struct { type viperSettingType struct {
Servers string `mapstructure:"servers"` Servers string `mapstructure:"servers"`
Domain string `mapstructure:"domain"` Domain string `mapstructure:"domain"`
ProxyPort int `mapstructure:"proxy_port"` ProxyPort int `mapstructure:"proxy_port"`
WhoisServer string `mapstructure:"whois"` WhoisServer string `mapstructure:"whois"`
Listen string `mapstructure:"listen"` Listen []string `mapstructure:"listen"`
DNSInterface string `mapstructure:"dns_interface"` DNSInterface string `mapstructure:"dns_interface"`
NetSpecificMode string `mapstructure:"net_specific_mode"` NetSpecificMode string `mapstructure:"net_specific_mode"`
TitleBrand string `mapstructure:"title_brand"` TitleBrand string `mapstructure:"title_brand"`
NavBarBrand string `mapstructure:"navbar_brand"` NavBarBrand string `mapstructure:"navbar_brand"`
NavBarBrandURL string `mapstructure:"navbar_brand_url"` NavBarBrandURL string `mapstructure:"navbar_brand_url"`
NavBarAllServer string `mapstructure:"navbar_all_servers"` NavBarAllServer string `mapstructure:"navbar_all_servers"`
NavBarAllURL string `mapstructure:"navbar_all_url"` NavBarAllURL string `mapstructure:"navbar_all_url"`
BgpmapInfo string `mapstructure:"bgpmap_info"` BgpmapInfo string `mapstructure:"bgpmap_info"`
TelegramBotName string `mapstructure:"telegram_bot_name"` TelegramBotName string `mapstructure:"telegram_bot_name"`
ProtocolFilter string `mapstructure:"protocol_filter"` ProtocolFilter string `mapstructure:"protocol_filter"`
NameFilter string `mapstructure:"name_filter"` NameFilter string `mapstructure:"name_filter"`
TimeOut int `mapstructure:"timeout"` TimeOut int `mapstructure:"timeout"`
ConnectionTimeOut int `mapstructure:"connection_timeout"`
TrustProxyHeaders bool `mapstructure:"trust_proxy_headers"`
} }
// Parse settings with viper, and convert to legacy setting format // Parse settings with viper, and convert to legacy setting format
@@ -50,7 +52,7 @@ func parseSettings() {
pflag.String("whois", "whois.verisign-grs.com", "whois server for queries") pflag.String("whois", "whois.verisign-grs.com", "whois server for queries")
viper.BindPFlag("whois", pflag.Lookup("whois")) viper.BindPFlag("whois", pflag.Lookup("whois"))
pflag.String("listen", "5000", "address or unix socket bird-lg is listening on") pflag.StringSlice("listen", []string{"5000"}, "address or unix socket bird-lg is listening on")
viper.BindPFlag("listen", pflag.Lookup("listen")) viper.BindPFlag("listen", pflag.Lookup("listen"))
pflag.String("dns-interface", "asn.cymru.com", "dns zone to query ASN information") pflag.String("dns-interface", "asn.cymru.com", "dns zone to query ASN information")
@@ -87,9 +89,15 @@ func parseSettings() {
pflag.String("name-filter", "", "protocol name regex to hide in summary tables (RE2 syntax); defaults to none if not set") pflag.String("name-filter", "", "protocol name regex to hide in summary tables (RE2 syntax); defaults to none if not set")
viper.BindPFlag("name_filter", pflag.Lookup("name-filter")) viper.BindPFlag("name_filter", pflag.Lookup("name-filter"))
pflag.Int("time-out", 120, "time before request timed out, in seconds; defaults to 120 if not set") pflag.Int("time-out", 120, "time before backend HTTP request times out, in seconds; defaults to 120 if not set")
viper.BindPFlag("timeout", pflag.Lookup("time-out")) viper.BindPFlag("timeout", pflag.Lookup("time-out"))
pflag.Int("connection-time-out", 5, "time before backend TCP connection times out, in seconds; defaults to 5 if not set")
viper.BindPFlag("connection_timeout", pflag.Lookup("connection-time-out"))
pflag.Bool("trust-proxy-headers", false, "Trust X-Forwared-For, X-Real-IP, X-Forwarded-Proto, X-Forwarded-Scheme and X-Forwarded-Host sent by the client")
viper.BindPFlag("trust_proxy_headers", pflag.Lookup("trust-proxy-headers"))
pflag.Parse() pflag.Parse()
if err := viper.ReadInConfig(); err != nil { if err := viper.ReadInConfig(); err != nil {
@@ -139,6 +147,8 @@ func parseSettings() {
setting.nameFilter = viperSettings.NameFilter setting.nameFilter = viperSettings.NameFilter
setting.timeOut = viperSettings.TimeOut setting.timeOut = viperSettings.TimeOut
setting.connectionTimeOut = viperSettings.ConnectionTimeOut
setting.trustProxyHeaders = viperSettings.TrustProxyHeaders
fmt.Printf("%#v\n", setting) fmt.Printf("%#v\n", setting)
} }

View File

@@ -3,11 +3,13 @@ package main
import ( import (
"embed" "embed"
"html/template" "html/template"
"net/url" "net/url"
"regexp"
"strings" "strings"
) )
// import templates and other assets // import templates and other assets
//
//go:embed assets //go:embed assets
var assets embed.FS var assets embed.FS
@@ -64,6 +66,47 @@ func (r SummaryRowData) NameContains(prefix string) bool {
return strings.Contains(r.Name, prefix) return strings.Contains(r.Name, prefix)
} }
func (r SummaryRowData) ProtocolMatches(protocols []string) bool {
for _, protocol := range protocols {
if strings.EqualFold(r.Proto, protocol) {
return true
}
}
return false
}
// pre-compiled regexp and constant statemap for summary rendering
var splitSummaryLine = regexp.MustCompile(`^([\w-]+)\s+(\w+)\s+([\w-]+)\s+(\w+)\s+([0-9\-\. :]+)(.*)$`)
var summaryStateMap = map[string]string{
"up": "success",
"down": "secondary",
"start": "danger",
"passive": "info",
}
func SummaryRowDataFromLine(line string) *SummaryRowData {
lineSplitted := splitSummaryLine.FindStringSubmatch(line)
if lineSplitted == nil {
return nil
}
var row SummaryRowData
row.Name = strings.TrimSpace(lineSplitted[1])
row.Proto = strings.TrimSpace(lineSplitted[2])
row.Table = strings.TrimSpace(lineSplitted[3])
row.State = strings.TrimSpace(lineSplitted[4])
row.Since = strings.TrimSpace(lineSplitted[5])
row.Info = strings.TrimSpace(lineSplitted[6])
if strings.Contains(row.Info, "Passive") {
row.MappedState = summaryStateMap["passive"]
} else {
row.MappedState = summaryStateMap[row.State]
}
return &row
}
type TemplateSummary struct { type TemplateSummary struct {
ServerName string ServerName string
Raw string Raw string
@@ -108,7 +151,7 @@ var requiredTemplates = [...]string{
// define functions to be made available in templates // define functions to be made available in templates
var funcMap = template.FuncMap{ var funcMap = template.FuncMap{
"pathescape": url.PathEscape, "pathescape": url.PathEscape,
} }
// import templates from embedded assets // import templates from embedded assets

View File

@@ -23,3 +23,67 @@ func TestSummaryRowDataNameContains(t *testing.T) {
assert.Equal(t, data.NameContains("oc"), true) assert.Equal(t, data.NameContains("oc"), true)
assert.Equal(t, data.NameContains("no"), false) assert.Equal(t, data.NameContains("no"), false)
} }
func TestSummaryRowDataFromLine(t *testing.T) {
data := SummaryRowDataFromLine("sys_device Device --- up 2025-06-27 21:23:08")
assert.Equal(t, data.Name, "sys_device")
assert.Equal(t, data.Proto, "Device")
assert.Equal(t, data.Table, "---")
assert.Equal(t, data.State, "up")
assert.Equal(t, data.Since, "2025-06-27 21:23:08")
}
func TestSummaryRowDataFromLineNumeric(t *testing.T) {
data := SummaryRowDataFromLine("12345 Device --- up 2025-06-27 21:23:08")
assert.Equal(t, data.Name, "12345")
assert.Equal(t, data.Proto, "Device")
assert.Equal(t, data.Table, "---")
assert.Equal(t, data.State, "up")
assert.Equal(t, data.Since, "2025-06-27 21:23:08")
}
func TestSummaryRowDataFromLinePipe(t *testing.T) {
data := SummaryRowDataFromLine("pipe Pipe --- up 2025-06-27 21:23:08 master4 <=> pipe_v4")
assert.Equal(t, data.Name, "pipe")
assert.Equal(t, data.Proto, "Pipe")
assert.Equal(t, data.Table, "---")
assert.Equal(t, data.State, "up")
assert.Equal(t, data.Since, "2025-06-27 21:23:08")
assert.Equal(t, data.Info, "master4 <=> pipe_v4")
}
func TestSummaryRowDataFromLineBGP(t *testing.T) {
data := SummaryRowDataFromLine("bgp BGP --- up 2025-06-30 20:45:33 Established")
assert.Equal(t, data.Name, "bgp")
assert.Equal(t, data.Proto, "BGP")
assert.Equal(t, data.Table, "---")
assert.Equal(t, data.State, "up")
assert.Equal(t, data.Since, "2025-06-30 20:45:33")
assert.Equal(t, data.Info, "Established")
}
func TestSummaryRowDataFromLineBGPPassive(t *testing.T) {
data := SummaryRowDataFromLine("passive BGP --- start 2025-06-27 21:23:08 Passive")
assert.Equal(t, data.Name, "passive")
assert.Equal(t, data.Proto, "BGP")
assert.Equal(t, data.Table, "---")
assert.Equal(t, data.State, "start")
assert.Equal(t, data.Since, "2025-06-27 21:23:08")
assert.Equal(t, data.Info, "Passive")
}
func TestSummaryRowDataFromLineWithDash(t *testing.T) {
data := SummaryRowDataFromLine("ibgp_test-01 BGP --- up 07:16:51.656 Established")
assert.Equal(t, data.Name, "ibgp_test-01")
assert.Equal(t, data.Proto, "BGP")
assert.Equal(t, data.Table, "---")
assert.Equal(t, data.State, "up")
assert.Equal(t, data.Since, "07:16:51.656")
assert.Equal(t, data.Info, "Established")
}

View File

@@ -12,19 +12,20 @@ import (
"net/url" "net/url"
"os" "os"
"strings" "strings"
"sync/atomic"
"github.com/gorilla/handlers" "github.com/gorilla/handlers"
) )
var primitiveMap = map[string]string{ var primitiveMap = map[string]string{
"summary": "show protocols", "summary": "show protocols",
"detail": "show protocols all %s", "detail": "show protocols all '%s'",
"route_from_protocol": "show route protocol %s", "route_from_protocol": "show route protocol '%s'",
"route_from_protocol_all": "show route protocol %s all", "route_from_protocol_all": "show route protocol '%s' all",
"route_from_protocol_primary": "show route protocol %s primary", "route_from_protocol_primary": "show route protocol '%s' primary",
"route_from_protocol_all_primary": "show route protocol %s all primary", "route_from_protocol_all_primary": "show route protocol '%s' all primary",
"route_filtered_from_protocol": "show route filtered protocol %s", "route_filtered_from_protocol": "show route filtered protocol '%s'",
"route_filtered_from_protocol_all": "show route filtered protocol %s all", "route_filtered_from_protocol_all": "show route filtered protocol '%s' all",
"route_from_origin": "show route where bgp_path.last = %s", "route_from_origin": "show route where bgp_path.last = %s",
"route_from_origin_all": "show route where bgp_path.last = %s all", "route_from_origin_all": "show route where bgp_path.last = %s all",
"route_from_origin_primary": "show route where bgp_path.last = %s primary", "route_from_origin_primary": "show route where bgp_path.last = %s primary",
@@ -39,8 +40,10 @@ var primitiveMap = map[string]string{
"traceroute": "%s", "traceroute": "%s",
} }
var webServerPrepared uint32 = 0
// serve up a generic error // serve up a generic error
func serverError(w http.ResponseWriter, r *http.Request) { func serverError(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("500 Internal Server Error")) w.Write([]byte("500 Internal Server Error"))
} }
@@ -75,7 +78,6 @@ func webHandlerWhois(w http.ResponseWriter, r *http.Request) {
// serve up results from bird // serve up results from bird
func webBackendCommunicator(endpoint string, command string) func(w http.ResponseWriter, r *http.Request) { func webBackendCommunicator(endpoint string, command string) func(w http.ResponseWriter, r *http.Request) {
backendCommandPrimitive, commandPresent := primitiveMap[command] backendCommandPrimitive, commandPresent := primitiveMap[command]
if !commandPresent { if !commandPresent {
panic("invalid command: " + command) panic("invalid command: " + command)
@@ -193,12 +195,11 @@ func webHandlerBGPMap(endpoint string, command string) func(w http.ResponseWrite
} }
} }
// set up routing paths and start webserver // set up routing paths
func webServerStart(l net.Listener) { func webServerPrepare() {
// redirect main page to all server summary // redirect main page to all server summary
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/summary/"+url.PathEscape(strings.Join(setting.servers, "+")), 302) http.Redirect(w, r, "/summary/"+url.PathEscape(strings.Join(setting.servers, "+")), http.StatusFound)
}) })
// serve static pages using embedded assets from template.go // serve static pages using embedded assets from template.go
@@ -237,7 +238,19 @@ func webServerStart(l net.Listener) {
http.HandleFunc("/whois/", webHandlerWhois) http.HandleFunc("/whois/", webHandlerWhois)
http.HandleFunc("/api/", apiHandler) http.HandleFunc("/api/", apiHandler)
http.HandleFunc("/telegram/", webHandlerTelegramBot) http.HandleFunc("/telegram/", webHandlerTelegramBot)
}
// Start HTTP server
http.Serve(l, handlers.LoggingHandler(os.Stdout, http.DefaultServeMux)) // start webserver
func webServerStart(l net.Listener) {
if atomic.SwapUint32(&webServerPrepared, 1) == 0 {
webServerPrepare()
}
var handler http.Handler
handler = http.DefaultServeMux
if setting.trustProxyHeaders {
handler = handlers.ProxyHeaders(handler)
}
handler = handlers.LoggingHandler(os.Stdout, handler)
http.Serve(l, handler)
} }

View File

@@ -88,7 +88,7 @@ func TestWhoisWithoutServer(t *testing.T) {
} }
func TestWhoisConnectionError(t *testing.T) { func TestWhoisConnectionError(t *testing.T) {
setting.whoisServer = "127.0.0.1:0" setting.whoisServer = "127.0.0.1:1"
result := whois("AS6939") result := whois("AS6939")
if !strings.Contains(result, "connect: connection refused") { if !strings.Contains(result, "connect: connection refused") {
t.Errorf("Whois AS6939 without server produced output, got %s", result) t.Errorf("Whois AS6939 without server produced output, got %s", result)

31
proxy/Dockerfile.mtr Normal file
View File

@@ -0,0 +1,31 @@
FROM golang AS step_0
ENV CGO_ENABLED=0 GO111MODULE=on
WORKDIR /root
COPY . .
RUN go build -ldflags "-w -s" -o /proxy
################################################################################
FROM alpine:edge AS step_1
WORKDIR /root
RUN apk add --no-cache build-base linux-headers
RUN wget https://www.bitwizard.nl/mtr/files/mtr-0.94.tar.gz \
-O mtr-0.94.tar.gz
RUN tar xvf mtr-0.94.tar.gz \
&& cd mtr-0.94 \
&& ./configure --without-gtk --without-ncurses --without-jansson --without-ipinfo --disable-bash-completion \
&& make -j4 LDFLAGS="-static" \
&& strip /root/mtr-0.94/mtr \
&& strip /root/mtr-0.94/mtr-packet
################################################################################
FROM scratch AS step_2
ENV PATH=/
COPY --from=step_0 /proxy /
COPY --from=step_1 /root/mtr-0.94/mtr /
COPY --from=step_1 /root/mtr-0.94/mtr-packet /
ENTRYPOINT ["/proxy"]

View File

@@ -1,32 +1,26 @@
module github.com/xddxdd/bird-lg-go/proxy module github.com/xddxdd/bird-lg-go/proxy
go 1.17 go 1.23.0
require ( require (
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/gorilla/handlers v1.5.1 github.com/gorilla/handlers v1.5.2
github.com/magiconair/properties v1.8.7 github.com/magiconair/properties v1.8.10
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.10
github.com/spf13/viper v1.17.0 github.com/spf13/viper v1.21.0
) )
require ( require (
github.com/felixge/httpsnoop v1.0.3 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/sagikazarmark/locafero v0.11.0 // indirect
github.com/sagikazarmark/locafero v0.3.0 // indirect github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/spf13/afero v1.15.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/cast v1.10.0 // indirect
github.com/spf13/afero v1.10.0 // indirect
github.com/spf13/cast v1.5.1 // indirect
github.com/subosito/gotenv v1.6.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect
go.uber.org/atomic v1.9.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect
go.uber.org/multierr v1.9.0 // indirect golang.org/x/sys v0.29.0 // indirect
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect golang.org/x/text v0.28.0 // indirect
golang.org/x/sys v0.12.0 // indirect
golang.org/x/text v0.13.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
) )

File diff suppressed because it is too large Load Diff

View File

@@ -62,7 +62,7 @@ func accessHandler(next http.Handler) http.Handler {
type settingType struct { type settingType struct {
birdSocket string birdSocket string
listen string listen []string
allowedNets []*net.IPNet allowedNets []*net.IPNet
tr_bin string tr_bin string
tr_flags []string tr_flags []string
@@ -76,32 +76,40 @@ func main() {
parseSettings() parseSettings()
tracerouteAutodetect() tracerouteAutodetect()
fmt.Printf("Listening on %s...\n", setting.listen) mux := http.NewServeMux()
var l net.Listener // Prepare HTTP server
var err error mux.HandleFunc("/", invalidHandler)
mux.HandleFunc("/bird", birdHandler)
mux.HandleFunc("/bird6", birdHandler)
mux.HandleFunc("/traceroute", tracerouteHandler)
mux.HandleFunc("/traceroute6", tracerouteHandler)
if strings.HasPrefix(setting.listen, "/") { for _, listenAddr := range setting.listen {
// Delete existing socket file, ignore errors (will fail later anyway) go func(addr string) {
os.Remove(setting.listen) fmt.Printf("Listening on %s...\n", addr)
l, err = net.Listen("unix", setting.listen)
} else { var l net.Listener
listenAddr := setting.listen var err error
if !strings.Contains(listenAddr, ":") {
listenAddr = ":" + listenAddr if strings.HasPrefix(addr, "/") {
} // Delete existing socket file, ignore errors (will fail later anyway)
l, err = net.Listen("tcp", listenAddr) os.Remove(addr)
l, err = net.Listen("unix", addr)
} else {
if !strings.Contains(addr, ":") {
addr = ":" + addr
}
l, err = net.Listen("tcp", addr)
}
if err != nil {
panic(err)
}
http.Serve(l, handlers.LoggingHandler(os.Stdout, accessHandler(mux)))
}(listenAddr)
} }
if err != nil { select {}
panic(err)
}
// Start HTTP server
http.HandleFunc("/", invalidHandler)
http.HandleFunc("/bird", birdHandler)
http.HandleFunc("/bird6", birdHandler)
http.HandleFunc("/traceroute", tracerouteHandler)
http.HandleFunc("/traceroute6", tracerouteHandler)
http.Serve(l, handlers.LoggingHandler(os.Stdout, accessHandler(http.DefaultServeMux)))
} }

View File

@@ -11,12 +11,12 @@ import (
) )
type viperSettingType struct { type viperSettingType struct {
BirdSocket string `mapstructure:"bird_socket"` BirdSocket string `mapstructure:"bird_socket"`
Listen string `mapstructure:"listen"` Listen []string `mapstructure:"listen"`
AllowedNets string `mapstructure:"allowed_ips"` AllowedNets string `mapstructure:"allowed_ips"`
TracerouteBin string `mapstructure:"traceroute_bin"` TracerouteBin string `mapstructure:"traceroute_bin"`
TracerouteFlags string `mapstructure:"traceroute_flags"` TracerouteFlags string `mapstructure:"traceroute_flags"`
TracerouteRaw bool `mapstructure:"traceroute_raw"` TracerouteRaw bool `mapstructure:"traceroute_raw"`
} }
// Parse settings with viper, and convert to legacy setting format // Parse settings with viper, and convert to legacy setting format
@@ -37,7 +37,7 @@ func parseSettings() {
pflag.String("bird", "/var/run/bird/bird.ctl", "socket file for bird, set either in parameter or environment variable BIRD_SOCKET") pflag.String("bird", "/var/run/bird/bird.ctl", "socket file for bird, set either in parameter or environment variable BIRD_SOCKET")
viper.BindPFlag("bird_socket", pflag.Lookup("bird")) viper.BindPFlag("bird_socket", pflag.Lookup("bird"))
pflag.String("listen", "8000", "listen address, set either in parameter or environment variable BIRDLG_PROXY_PORT") pflag.StringSlice("listen", []string{"8000"}, "listen address, set either in parameter or environment variable BIRDLG_PROXY_PORT")
viper.BindPFlag("listen", pflag.Lookup("listen")) viper.BindPFlag("listen", pflag.Lookup("listen"))
pflag.String("allowed", "", "IPs or networks allowed to access this proxy, separated by commas. Don't set to allow all IPs.") pflag.String("allowed", "", "IPs or networks allowed to access this proxy, separated by commas. Don't set to allow all IPs.")