1
mirror of https://github.com/xddxdd/bird-lg-go synced 2025-10-17 22:42:12 +02:00

56 Commits

Author SHA1 Message Date
Lan Tian
47c66b125c release: v1.1.1 2022-12-18 16:26:47 -06:00
Yuhui Xu
9e17b116f1 frontend: refactor bgpmap and fix node colors (#67)
* frontend: refactor bgpmap and fix node colors

* frontend: alternative way to test bgpmap
2022-12-07 16:30:19 -06:00
Lan Tian
335ad40634 release: v1.1.0 2022-11-27 17:22:21 -06:00
dependabot[bot]
6ec0f2e7a6 build(deps): bump github.com/spf13/viper from 1.13.0 to 1.14.0 in /proxy (#65)
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.13.0 to 1.14.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.13.0...v1.14.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>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-07 20:26:44 -06:00
dependabot[bot]
4b73cf0fcb build(deps): bump github.com/spf13/viper in /frontend (#64)
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.13.0 to 1.14.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.13.0...v1.14.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>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-07 20:26:37 -06:00
Klara Modin
3b1d001543 frontend: sortable tables in summary (#61)
Adapted from https://stackoverflow.com/a/57080195.
Additions:
1. sortTable accepts a secondary column and sorting direction. The 'Name'
   column (number 0) is always used for the secondary
2. use classes 'ascSorted' and 'descSorted' to toggle between ascending and
   descending order
3. in the table functions, use .innerHTML instead of .innnerText so the
   link to detailed protocol information does not get lost. Also
   preserve .classList
2022-09-18 15:58:20 -05:00
dependabot[bot]
675cb26ed1 build(deps): bump github.com/spf13/viper from 1.12.0 to 1.13.0 in /proxy (#63)
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.12.0 to 1.13.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.12.0...v1.13.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>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-14 12:22:03 -05:00
dependabot[bot]
556d3e50d3 build(deps): bump github.com/spf13/viper in /frontend (#62)
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.12.0 to 1.13.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.12.0...v1.13.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>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-14 12:21:56 -05:00
Lan Tian
06796f546e general: remove failing docker image build tasks for PRs 2022-08-25 11:42:41 -05:00
Potat0
d029d6684c Fix the wrong order of examples (#60) 2022-08-17 11:44:35 -05:00
日下部 詩
5ce0f55f35 1. support local whois. 2 add some useful bird command (#59) 2022-08-11 22:34:39 -04:00
Lan Tian
890ab51b07 release: v1.0.0 2022-08-05 22:36:43 -04:00
Lan Tian
8e4a35cc8c general: fix GOARCH for ARM 32 bit 2022-08-05 22:33:33 -04:00
Lan Tian
97f3c6088f general: add GitHub actions for releasing binaries 2022-08-05 22:29:30 -04:00
Yuhui Xu
982326a678 frontend: fix XSS (#57) (#58) 2022-08-05 21:59:18 -04:00
Tristan Heaven
4b3980f6bd Fix navbar_brand_url config (#56) 2022-07-28 09:54:39 -04:00
Yuhui Xu
6f6b2bd283 general: support reading config files (#55) 2022-07-08 23:13:10 -04:00
Yuhui Xu
892a7bee22 frontend: support listening on unix socket (#54) 2022-07-08 21:14:11 -04:00
Nicolas Lorin
348295b9aa frontend: add the abilities to customized timeout time (#51)
* main.go: add timeout setting

* lgproxy.go: use timeout setting when querying server

* README.md: add new timeout setting
2022-02-08 02:29:05 -06:00
Kioubit
950c018b18 Confirm that bird access was restricted (#49)
Co-authored-by: Kioubit <kioubit@localhost.invalid>
2022-01-18 03:03:09 -06:00
herver
26efeb4996 Add name filter feature (#48)
This uses a RE2 regexp to hide protocols which name matches the expression
2022-01-18 03:01:57 -06:00
Lan Tian
5a5dfbc93f general: add docker hub links to readme 2022-01-09 01:51:36 -06:00
Lan Tian
f60a292129 general: build docker images with correct arch label 2022-01-09 01:45:00 -06:00
James Lu
e7f6026854 Add BIRDLG_TRACEROUTE_RAW option to leave traceroute output in the default format (#47) 2022-01-09 00:14:31 -06:00
Lan Tian
a4e0f4c193 frontend: skip network related tests when unavailable
Fix #46
2022-01-09 00:10:14 -06:00
日下部 詩
af5b653326 BIRDLG_BGPMAP_INFO 選項 (#44)
* BIRDLG_BGPMAP_INFO

update the paramater

description fix for bgpmap_test

singleline and multiline

* Static file instead of jsdelivr; favicon.ico

Co-authored-by: testscript <testscript@example.com>
2021-12-20 03:35:43 -06:00
日下部 詩
58847759b3 一些小改動 (#42)
* 1. remove ":" at the start of port assignement. 2. use BIRDLG_PROXY_PORT at proxy. 3. add custom URL to brand

* goto / if only one server

* add BIRDLG_TRACEROUTE_BIN
2021-11-09 12:27:02 -06:00
Yuhui Xu
6481e7cc8d frontend: fix uninitialized buffer (#41) 2021-09-26 13:26:07 -05:00
James Lu
2166d73b3d frontend: add filtering by protocol type to summary tables (#40)
* frontend: add option to filter by protocol type

Closes #33.

* frontend: use case insensitive comparisons for protocol filter
2021-09-07 15:17:16 -05:00
Yuhui Xu
a64d839e2c frontend: limit fetched response size to 64KB (#39) 2021-09-02 20:21:28 -05:00
James Lu
1a3c618522 frontend: BGPmap improvements (#36)
* bgpmap: Compact nexthop info into an edge label

* bgpmap: parse and show non-BGP routes

* bgpmap: Misc tweaks

- Show the protocol name instead of the ASN in edge labels
- Correctly draw only the primary path if there are multiple routes to the first neighbour ASN in a path
- Use a smaller font size for edge labels

* bgpmap_test: update to match new changes

* bgpmap: Split route info on all (non-empty) rta_dest_names values
2021-08-31 09:44:14 -05:00
Yuhui Xu
fbd190628c general: add go 1.16 requirement to readme [skip ci] (#37) 2021-08-30 23:46:14 -05:00
Yuhui Xu
823b639245 frontend: also filter whois privacy redacted lines (#34) 2021-08-28 22:02:03 -05:00
Yuhui Xu
b0c0e5442d frontend: set lgproxy request timeout to 120s (#31)
lgproxy traceroutecan be really slow if dns resolve doesnt work well.
2021-08-04 00:30:38 +08:00
Yuhui Xu
4e4ce89418 frontend: set timeout longer for lgproxy requests (#30) 2021-08-03 11:46:05 +08:00
Yuhui Xu
234aadadd9 frontend: specify timeout for requests (#29) 2021-08-02 18:46:43 +08:00
Lan Tian
bee26f421c frontend: resolve asn in dns/whois/fail order & fix tests 2021-07-31 17:11:20 +08:00
Yuhui Xu
2e0cb131ca Merge pull request #27 from miegl/master
frontend: optional ASN resolution using whois
2021-07-31 17:02:25 +08:00
Josef Miegl
4c248c638a frontend: optional asn resolution using whois 2021-07-30 16:54:41 +02:00
Yuhui Xu
3550362a4d Merge pull request #26 from sesa-me/master
Refactor to use go:embed
2021-07-14 22:13:36 +08:00
Simon Marsh
256a80646f - Refactor file embedding to use Go 1.16 embed functionality.
- Remove references to previous bindata packages from build scripts and docs
2021-07-13 11:54:35 +01:00
Yuhui Xu
03c42eb1e8 Merge pull request #25 from outloudvi/patch-1
doc: add --net-specific-mode
2021-07-03 22:20:56 +08:00
Outvi V
aea85e774c doc: add --net-specific-mode 2021-07-03 12:33:49 +08:00
Yuhui Xu
80d9351a58 Merge pull request #24 from xddxdd/lantian-dev
frontend: allow long lines if result is short
2021-06-21 01:01:37 +08:00
Lan Tian
5e0bc081e6 frontend: allow long lines if result is short 2021-06-21 00:57:26 +08:00
Yuhui Xu
4d53d1f095 Merge pull request #23 from xddxdd/lantian-dev
frontend: change behavior of whois shorten mode
2021-06-21 00:49:37 +08:00
Lan Tian
5883015294 frontend: change behavior of whois shorten mode 2021-06-21 00:44:44 +08:00
Yuhui Xu
80e66a7a81 Merge pull request #22 from xddxdd/lantian-dev
frontend: add generic whois shorten mode
2021-06-20 02:24:49 +08:00
Lan Tian
41329da7cb frontend: add generic whois shorten mode 2021-06-20 02:20:18 +08:00
Yuhui Xu
8e56705205 Merge pull request #21 from xddxdd/lantian-dev
frontend: fix typo
2021-06-19 16:42:01 +08:00
Lan Tian
6a8b3a0e55 frontend: fix typo 2021-06-19 16:36:18 +08:00
Yuhui Xu
83ab403706 Merge pull request #20 from xddxdd/lantian-dev
frontend: limit telegram commands by bot name
2021-06-19 16:27:17 +08:00
Lan Tian
7c7814cc7b frontend: limit telegram commands by bot name 2021-06-19 16:23:43 +08:00
Yuhui Xu
8598060cc0 Merge pull request #19 from xddxdd/lantian-dev
Add instruction for create Dockerfile
2021-05-21 21:11:00 +08:00
Lan Tian
bda06ddd5e Add instruction for create Dockerfile 2021-05-21 21:09:50 +08:00
Yuhui Xu
f404072ab8 Merge pull request #18 from xddxdd/dependabot/add-v2-config-file
Upgrade to GitHub-native Dependabot
2021-05-01 23:21:14 +08:00
49 changed files with 3617 additions and 603 deletions

View File

@@ -4,16 +4,19 @@ workflows:
docker:
jobs:
- build
- deploy:
- docker-frontend-deploy:
context:
- docker
requires:
- build
filters:
branches:
only: master
- docker-proxy-deploy:
context:
- docker
requires:
- build
matrix:
parameters:
program: [frontend, proxy]
# latest is amd64 arch + push to default latest tag
image_arch: [latest, i386, arm32v7, arm64v8, ppc64le, s390x]
filters:
branches:
only: master
@@ -21,41 +24,104 @@ workflows:
jobs:
build:
docker:
- image: circleci/golang:1.15
working_directory: /go/src/github.com/xddxdd/bird-lg-go
- image: cimg/go:1.17
working_directory: /home/circleci/go/src/github.com/xddxdd/bird-lg-go
steps:
- checkout
- run: go get -v -t -d ./...
- run: go get -u github.com/kevinburke/go-bindata/...
- run: cd frontend && go generate
- run: go test -v ./...
deploy:
docker:
- image: circleci/golang:1.15
working_directory: /go/src/github.com/xddxdd/bird-lg-go
parameters:
image_arch:
type: string
program:
type: string
steps:
- checkout
- setup_remote_docker:
version: 19.03.13
- run:
name: Install GPP
- run:
name: Test frontend
command: |
sudo apt-get update && sudo apt-get install -y gpp
export GO111MODULE=on
cd frontend
go get -v -t -d ./...
go test -v ./...
- run:
name: Test proxy
command: |
export GO111MODULE=on
cd proxy
go get -v -t -d ./...
go test -v ./...
docker-frontend-deploy:
machine:
image: ubuntu-2004:202111-02
environment:
BUILDX_PLATFORMS: linux/amd64,linux/arm64,linux/386,linux/arm/v7
steps:
- checkout
- run:
name: Install buildx
command: |
BUILDX_BINARY_URL="https://github.com/docker/buildx/releases/download/v0.7.1/buildx-v0.7.1.linux-amd64"
curl --output docker-buildx \
--silent --show-error --location --fail --retry 3 \
"$BUILDX_BINARY_URL"
mkdir -p ~/.docker/cli-plugins
mv docker-buildx ~/.docker/cli-plugins/
chmod a+x ~/.docker/cli-plugins/docker-buildx
docker buildx install
# Run binfmt
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
- run:
name: Build Docker image
environment:
IMAGE_ARCH: << parameters.image_arch >>
PROGRAM: << parameters.program >>
BUILD_ID: << pipeline.number >>
command: |
make -f Makefile.docker _crossbuild
echo $DOCKER_PASSWORD | docker login -u $DOCKER_USERNAME --password-stdin
make -f Makefile.docker \
DOCKER_USERNAME=$DOCKER_USERNAME \
BUILD_ID=circleci-build$BUILD_ID \
$PROGRAM/$IMAGE_ARCH
docker buildx create --name mybuilder --use
docker buildx build \
--platform $BUILDX_PLATFORMS \
-t $DOCKER_USERNAME/bird-lg-go:circleci-build$BUILD_ID \
--progress plain \
--push frontend
docker buildx build \
--platform $BUILDX_PLATFORMS \
-t $DOCKER_USERNAME/bird-lg-go:latest \
--progress plain \
--push frontend
docker-proxy-deploy:
machine:
image: ubuntu-2004:202111-02
environment:
BUILDX_PLATFORMS: linux/amd64,linux/arm64,linux/386,linux/arm/v7
steps:
- checkout
- run:
name: Install buildx
command: |
BUILDX_BINARY_URL="https://github.com/docker/buildx/releases/download/v0.7.1/buildx-v0.7.1.linux-amd64"
curl --output docker-buildx \
--silent --show-error --location --fail --retry 3 \
"$BUILDX_BINARY_URL"
mkdir -p ~/.docker/cli-plugins
mv docker-buildx ~/.docker/cli-plugins/
chmod a+x ~/.docker/cli-plugins/docker-buildx
docker buildx install
# Run binfmt
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
- run:
name: Build Docker image
environment:
BUILD_ID: << pipeline.number >>
command: |
echo $DOCKER_PASSWORD | docker login -u $DOCKER_USERNAME --password-stdin
docker buildx create --name mybuilder --use
docker buildx build \
--platform $BUILDX_PLATFORMS \
-t $DOCKER_USERNAME/bird-lgproxy-go:circleci-build$BUILD_ID \
--push proxy
docker buildx build \
--platform $BUILDX_PLATFORMS \
-t $DOCKER_USERNAME/bird-lgproxy-go:latest \
--progress plain \
--push proxy

35
.github/workflows/release.yaml vendored Normal file
View File

@@ -0,0 +1,35 @@
on:
release:
types: [created]
jobs:
releases-matrix:
name: Release Go Binary
runs-on: ubuntu-latest
strategy:
matrix:
goos: [linux, windows, darwin]
goarch: ["386", amd64, "arm", arm64]
exclude:
- goarch: "386"
goos: darwin
- goarch: "arm"
goos: darwin
- goarch: "arm"
goos: windows
steps:
- uses: actions/checkout@v3
- uses: wangyoucao577/go-release-action@v1.30
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
goos: ${{ matrix.goos }}
goarch: ${{ matrix.goarch }}
project_path: "./frontend"
binary_name: "bird-lg-go"
- uses: wangyoucao577/go-release-action@v1.30
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
goos: ${{ matrix.goos }}
goarch: ${{ matrix.goarch }}
project_path: "./proxy"
binary_name: "bird-lgproxy-go"

View File

@@ -9,5 +9,5 @@ proxy:
all: frontend proxy
install:
install -m 755 frontend/frontend /usr/local/bin/frontend
install -m 755 proxy/proxy /usr/local/bin/proxy
install -m 755 frontend/frontend /usr/local/bin/bird-lg-go
install -m 755 proxy/proxy /usr/local/bin/bird-lgproxy-go

View File

@@ -1,77 +0,0 @@
# Basic definitions
DOCKER_USERNAME := xddxdd
ARCHITECTURES := amd64 i386 arm32v7 arm64v8 ppc64le s390x
IMAGES := frontend proxy
# General Purpose Preprocessor config
GPP_INCLUDE_DIR := include
GPP_FLAGS_U := "" "" "(" "," ")" "(" ")" "\#" ""
GPP_FLAGS_M := "\#" "\n" " " " " "\n" "(" ")"
GPP_FLAGS_EXTRA := +c "\\\n" ""
GPP_FLAGS := -I ${GPP_INCLUDE_DIR} --nostdinc -U ${GPP_FLAGS_U} -M ${GPP_FLAGS_M} ${GPP_FLAGS_EXTRA}
BUILD_ID ?= $(shell date +%Y%m%d%H%M)
define create-image-arch-target
frontend/Dockerfile.$1: frontend/template.Dockerfile
@gpp ${GPP_FLAGS} -D ARCH_$(shell echo $1 | tr a-z A-Z) -o frontend/Dockerfile.$1 frontend/template.Dockerfile || rm -rf frontend/Dockerfile.$1
frontend/$1: frontend/Dockerfile.$1
@if [ -f frontend/Dockerfile.$1 ]; then \
docker build --pull --no-cache -t ${DOCKER_USERNAME}/bird-lg-go:$1-${BUILD_ID} -f frontend/Dockerfile.$1 frontend || exit 1; \
docker push ${DOCKER_USERNAME}/bird-lg-go:$1-${BUILD_ID} || exit 1; \
docker tag ${DOCKER_USERNAME}/bird-lg-go:$1-${BUILD_ID} ${DOCKER_USERNAME}/bird-lg-go:$1 || exit 1; \
docker push ${DOCKER_USERNAME}/bird-lg-go:$1 || exit 1; \
else \
echo "Dockerfile generation failed, see error above"; \
exit 1; \
fi
proxy/Dockerfile.$1: proxy/template.Dockerfile
@gpp ${GPP_FLAGS} -D ARCH_$(shell echo $1 | tr a-z A-Z) -o proxy/Dockerfile.$1 proxy/template.Dockerfile || rm -rf proxy/Dockerfile.$1
proxy/$1: proxy/Dockerfile.$1
@if [ -f proxy/Dockerfile.$1 ]; then \
docker build --pull --no-cache -t ${DOCKER_USERNAME}/bird-lgproxy-go:$1-${BUILD_ID} -f proxy/Dockerfile.$1 proxy || exit 1; \
docker push ${DOCKER_USERNAME}/bird-lgproxy-go:$1-${BUILD_ID} || exit 1; \
docker tag ${DOCKER_USERNAME}/bird-lgproxy-go:$1-${BUILD_ID} ${DOCKER_USERNAME}/bird-lgproxy-go:$1 || exit 1; \
docker push ${DOCKER_USERNAME}/bird-lgproxy-go:$1 || exit 1; \
else \
echo "Dockerfile generation failed, see error above"; \
exit 1; \
fi
endef
$(foreach arch,${ARCHITECTURES},$(eval $(call create-image-arch-target,$(arch))))
frontend:$(foreach arch,latest ${ARCHITECTURES},frontend/${arch})
frontend/latest: frontend/amd64
@docker tag ${DOCKER_USERNAME}/bird-lg-go:amd64-${BUILD_ID} ${DOCKER_USERNAME}/bird-lg-go:${BUILD_ID} || exit 1
@docker push ${DOCKER_USERNAME}/bird-lg-go:${BUILD_ID} || exit 1
@docker tag ${DOCKER_USERNAME}/bird-lg-go:amd64-${BUILD_ID} ${DOCKER_USERNAME}/bird-lg-go:latest || exit 1
@docker push ${DOCKER_USERNAME}/bird-lg-go:latest || exit 1
proxy:$(foreach arch,latest ${ARCHITECTURES},proxy/${arch})
proxy/latest: proxy/amd64
@docker tag ${DOCKER_USERNAME}/bird-lgproxy-go:amd64-${BUILD_ID} ${DOCKER_USERNAME}/bird-lgproxy-go:${BUILD_ID} || exit 1
@docker push ${DOCKER_USERNAME}/bird-lgproxy-go:${BUILD_ID} || exit 1
@docker tag ${DOCKER_USERNAME}/bird-lgproxy-go:amd64-${BUILD_ID} ${DOCKER_USERNAME}/bird-lgproxy-go:latest || exit 1
@docker push ${DOCKER_USERNAME}/bird-lgproxy-go:latest || exit 1
.DEFAULT_GOAL := images
.DELETE_ON_ERROR:
.SECONDARY:
# Target to enable multiarch support
_crossbuild:
@docker run --rm --privileged multiarch/qemu-user-static --reset -p yes >/dev/null
dockerfiles: $(foreach image,${IMAGES},$(foreach arch,${ARCHITECTURES},$(image)/Dockerfile.$(arch)))
images: $(foreach image,${IMAGES},$(image))
clean:
@rm -rf */Dockerfile.{$(shell echo ${ARCHITECTURES} | sed "s/ /,/g")}

199
README.md

File diff suppressed because it is too large Load Diff

1
RELEASE Normal file
View File

@@ -0,0 +1 @@
v1.1.1

View File

@@ -153,9 +153,11 @@ Request:
```json
{
"servers": [],
"type": "server_list",
"args": ""
"servers": [
"alpha"
],
"type": "bird",
"args": "show status"
}
```
@@ -179,11 +181,9 @@ Request:
```json
{
"servers": [
"alpha"
],
"type": "bird",
"args": "show status"
"servers": [],
"type": "server_list",
"args": ""
}
```

22
docs/Telegram.md Normal file
View File

@@ -0,0 +1,22 @@
# Telegram Bot Webhook
The frontend can act as a Telegram Bot webhook endpoint, to add BGP route/traceroute/whois lookup functionality to your tech group.
There is no configuration necessary on the frontend, just start it up normally.
Set your Telegram Bot webhook URL to `https://your.frontend.com/telegram/alpha+beta+gamma`, where `alpha+beta+gamma` is the list of servers to be queried on Telegram commands, separated by `+`.
You may omit `alpha+beta+gamma` to use all your servers, but it is not recommended when you have lots of servers, or the message would be too long and hard to read.
## Example of setting the webhook
```bash
curl "https://api.telegram.org/bot${BOT_TOKEN}/setWebhook?url=https://your.frontend.com:5000/telegram/alpha+beta+gamma"
```
## Supported commands
- `path`: Show bird's ASN path to target IP
- `route`: Show bird's preferred route to target IP
- `trace`: Traceroute to target IP/domain
- `whois`: Whois query

11
frontend/Dockerfile Normal file
View File

@@ -0,0 +1,11 @@
FROM golang:buster AS step_0
ENV CGO_ENABLED=0 GO111MODULE=on
WORKDIR /root
COPY . .
RUN go build -ldflags "-w -s" -o /frontend
################################################################################
FROM scratch AS step_1
COPY --from=step_0 /frontend /
ENTRYPOINT ["/frontend"]

View File

@@ -1,5 +1,3 @@
.PHONY: all
all:
go get -u github.com/kevinburke/go-bindata/...
go generate
go build -ldflags "-w -s" -o frontend

BIN
frontend/assets/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,73 @@
// adapted from https://stackoverflow.com/a/57080195
document.querySelectorAll('table.sortable')
.forEach((table)=> {
table.querySelectorAll('th')
.forEach((element, columnNo) => {
element.addEventListener('click', event => {
if(element.classList.contains('ascSorted')) {
dir = -1;
element.classList.remove('ascSorted');
element.classList.add('descSorted');
element.innerText = element.innerText.slice(0,-2) + " ↓";
} else if(element.classList.contains('descSorted')) {
dir = 1;
element.classList.remove('descSorted');
element.classList.add('ascSorted');
element.innerText = element.innerText.slice(0,-2) + " ↑";
} else {
dir = 1;
element.classList.add('ascSorted');
element.innerText += " ↑";
}
sortTable(table, columnNo, 0, dir, 1);
});
});
});
function sortTable(table, priCol, secCol, priDir, secDir) {
const tableBody = table.querySelector('tbody');
const tableData = table2data(tableBody);
tableData.sort((a, b) => {
if(a[priCol] === b[priCol]) {
if(a[secCol] > b[secCol]) {
return secDir;
} else {
return -secDir;
}
} else if(a[priCol] > b[priCol]) {
return priDir;
} else {
return -priDir;
}
});
data2table(tableBody, tableData);
}
function table2data(tableBody) {
const tableData = [];
tableBody.querySelectorAll('tr')
.forEach(row => {
const rowData = [];
row.querySelectorAll('td')
.forEach(cell => {
rowData.push(cell.innerHTML);
});
rowData.classList = row.classList.toString();
tableData.push(rowData);
});
return tableData;
}
function data2table(tableBody, tableData) {
tableBody.querySelectorAll('tr')
.forEach((row, i) => {
const rowData = tableData[i];
row.classList = rowData.classList;
row.querySelectorAll('td')
.forEach((cell, j) => {
cell.innerHTML = rowData[j];
});
tableData.push(rowData);
});
}

View File

@@ -2,11 +2,11 @@
<div id="bgpmap">
</div>
<script src="https://cdn.jsdelivr.net/npm/viz.js@2.1.2/viz.min.js" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/viz.js@2.1.2/lite.render.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>
var viz = new Viz();
viz.renderSVGElement(`{{ .Result }}`)
viz.renderSVGElement(atob({{ .Result }}))
.then(element => {
document.getElementById("bgpmap").appendChild(element);
})

View File

@@ -1,18 +1,19 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<link rel="icon" href="/favicon.ico" type="image/x-icon" />
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<meta name="renderer" content="webkit">
<title>{{ html .Title }}</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/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" integrity="sha256-VoFZSlmyTXsegReQCNmbXrS4hBBUl/cexZvPmPWoJsY=" crossorigin="anonymous">
<meta name="robots" content="noindex, nofollow">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="/">{{ .Brand }}</a>
<a class="navbar-brand" href="{{ .BrandURL }}">{{ .Brand }}</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
@@ -28,13 +29,24 @@
{{ end }}
<ul class="navbar-nav mr-auto">
<li class="nav-item">
{{ if eq .AllServersURLCustom "all" }}
<a class="nav-link{{ if .AllServersLinkActive }} active{{ end }}"
href="/{{ $option }}/{{ .AllServersURL }}/{{ $target }}"> All Servers </a>
href="/{{ $option }}/{{ .AllServersURL }}/{{ $target }}"> {{ .AllServerTitle }} </a>
{{ else }}
<a class="nav-link active"
href="{{ .AllServersURLCustom }}"> {{ .AllServerTitle }} </a>
{{ end }}
</li>
{{ range $k, $v := .ServersEscaped }}
{{ $length := len .Servers }}
{{ range $k, $v := .Servers }}
<li class="nav-item">
{{ if gt $length 1 }}
<a class="nav-link{{ if eq $server $v }} active{{ end }}"
href="/{{ $option }}/{{ $v }}/{{ $target }}">{{ html (index $.ServersDisplay $k) }}</a>
{{ else }}
<a class="nav-link{{ if eq $server $v }} active{{ end }}"
href="/">{{ html (index $.ServersDisplay $k) }}</a>
{{ end }}
</li>
{{ end }}
</ul>
@@ -62,8 +74,9 @@
{{ .Content }}
</div>
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.1/dist/js/bootstrap.min.js" integrity="sha256-0IiaoZCI++9oAAvmCb5Y0r93XkuhvJpRalZLffQXLok=" crossorigin="anonymous"></script>
<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/bootstrap@4.5.1/dist/js/bootstrap.min.js" integrity="sha256-0IiaoZCI++9oAAvmCb5Y0r93XkuhvJpRalZLffQXLok=" crossorigin="anonymous"></script>
<script src="/static/sortTable.js"></script>
<script>
function goto() {

View File

@@ -1,6 +1,6 @@
{{ $ServerName := urlquery .ServerName }}
<table class="table table-striped table-bordered table-sm">
<table class="table table-striped table-bordered table-sm sortable">
<thead>
{{ range .Header }}
<th scope="col">{{ html . }}</th>

File diff suppressed because it is too large Load Diff

View File

@@ -5,8 +5,32 @@ import (
"testing"
)
func TestGetASNRepresentation(t *testing.T) {
func contains(s []string, str string) bool {
for _, v := range s {
if v == str {
return true
}
}
return false
}
func TestGetASNRepresentationDNS(t *testing.T) {
checkNetwork(t)
setting.dnsInterface = "asn.cymru.com"
setting.whoisServer = ""
result := getASNRepresentation("6939")
if !strings.Contains(result, "HURRICANE") {
t.Errorf("Lookup AS6939 failed, got %s", result)
}
}
func TestGetASNRepresentationWhois(t *testing.T) {
checkNetwork(t)
setting.dnsInterface = ""
setting.whoisServer = "whois.arin.net"
result := getASNRepresentation("6939")
if !strings.Contains(result, "HURRICANE") {
t.Errorf("Lookup AS6939 failed, got %s", result)
@@ -15,12 +39,14 @@ func TestGetASNRepresentation(t *testing.T) {
func TestGetASNRepresentationFallback(t *testing.T) {
setting.dnsInterface = ""
setting.whoisServer = ""
result := getASNRepresentation("6939")
if result != "AS6939" {
t.Errorf("Lookup AS6939 failed, got %s", result)
}
}
// Broken due to random order of attributes
func TestBirdRouteToGraphviz(t *testing.T) {
setting.dnsInterface = ""
@@ -33,14 +59,13 @@ func TestBirdRouteToGraphviz(t *testing.T) {
BGP.as_path: 4242422601
BGP.next_hop: 172.18.0.2`
expectedResult := `digraph {
"Nexthop:\n172.18.0.2" -> "AS4242422601" [color=red];
"Nexthop:\n172.18.0.2" [shape=diamond];
"AS4242422601" -> "Target: 192.168.0.1" [color=red];
"Target: 192.168.0.1" [color=red,shape=diamond];
"alpha" [color=blue,shape=box];
"alpha" -> "Nexthop:\n172.18.0.2" [color=red];
}`
expectedLinesInResult := []string{
`"AS4242422601" [`,
`"AS4242422601" -> "Target: 192.168.0.1" [`,
`"Target: 192.168.0.1" [`,
`"alpha" [`,
`"alpha" -> "AS4242422601" [`,
}
result := birdRouteToGraphviz([]string{
"alpha",
@@ -48,9 +73,10 @@ func TestBirdRouteToGraphviz(t *testing.T) {
fakeResult,
}, "192.168.0.1")
for _, line := range strings.Split(result, "\n") {
if !strings.Contains(expectedResult, line) {
t.Errorf("Unexpected line in result: %s", line)
for _, line := range expectedLinesInResult {
if !strings.Contains(result, line) {
t.Errorf("Expected line in result not found: %s", line)
}
}
}

View File

@@ -49,3 +49,53 @@ func dn42WhoisFilter(whois string) string {
return commandResult
}
}
/* experimental, behavior may change */
func shortenWhoisFilter(whois string) string {
commandResult := ""
commandResultLonger := ""
lines := 0
linesLonger := 0
skippedLines := 0
skippedLinesLonger := 0
for _, s := range strings.Split(whois, "\n") {
s = strings.TrimSpace(s)
shouldSkip := false
shouldSkip = shouldSkip || len(s) == 0
shouldSkip = shouldSkip || len(s) > 0 && s[0] == '#'
shouldSkip = shouldSkip || strings.Contains(strings.ToUpper(s), "REDACTED FOR PRIVACY")
if shouldSkip {
skippedLinesLonger++
continue
}
commandResultLonger += s + "\n"
linesLonger++
shouldSkip = shouldSkip || len(s) > 80
shouldSkip = shouldSkip || !strings.Contains(s, ":")
shouldSkip = shouldSkip || strings.Index(s, ":") > 20
if shouldSkip {
skippedLines++
continue
}
commandResult += s + "\n"
lines++
}
if lines < 5 {
commandResult = commandResultLonger
skippedLines = skippedLinesLonger
}
if skippedLines > 0 {
return commandResult + fmt.Sprintf("\n%d line(s) skipped.\n", skippedLines)
} else {
return commandResult
}
}

View File

@@ -1,9 +1,10 @@
module github.com/xddxdd/bird-lg-go/frontend
go 1.15
go 1.16
require (
github.com/elazarl/go-bindata-assetfs v1.0.1
github.com/felixge/httpsnoop v1.0.3 // indirect
github.com/gorilla/handlers v1.5.1
github.com/kevinburke/go-bindata v3.22.0+incompatible // indirect
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.14.0
)

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,12 @@
package main
import (
"io/ioutil"
"io"
"net/http"
"net/url"
"strconv"
"strings"
"time"
)
type channelData struct {
@@ -46,13 +47,20 @@ func batchRequest(servers []string, endpoint string, command string) []string {
}
url := "http://" + hostname + ":" + strconv.Itoa(setting.proxyPort) + "/" + url.PathEscape(endpoint) + "?q=" + url.QueryEscape(command)
go func(url string, i int) {
response, err := http.Get(url)
client := http.Client{Timeout: time.Duration(setting.timeOut) * time.Second}
response, err := client.Get(url)
if err != nil {
ch <- channelData{i, "request failed: " + err.Error() + "\n"}
return
}
text, _ := ioutil.ReadAll(response.Body)
ch <- channelData{i, string(text)}
buf := make([]byte, 65536)
n, err := io.ReadFull(response.Body, buf)
if err != nil && err != io.ErrUnexpectedEOF {
ch <- channelData{i, err.Error()}
} else {
ch <- channelData{i, string(buf[:n])}
}
}(url, i)
}
}

View File

@@ -1,15 +1,11 @@
package main
import (
"flag"
"net"
"os"
"strconv"
"strings"
)
// binary data
//go:generate go-bindata -prefix bindata -o bindata.go bindata/...
type settingType struct {
servers []string
serversDisplay []string
@@ -21,93 +17,40 @@ type settingType struct {
netSpecificMode string
titleBrand string
navBarBrand string
navBarBrandURL string
navBarAllServer string
navBarAllURL string
bgpmapInfo string
telegramBotName string
protocolFilter []string
nameFilter string
timeOut int
}
var setting settingType
func main() {
var settingDefault = settingType{
servers: []string{""},
proxyPort: 8000,
whoisServer: "whois.verisign-grs.com",
listen: ":5000",
dnsInterface: "asn.cymru.com",
titleBrand: "Bird-lg Go",
navBarBrand: "Bird-lg Go",
}
if env := os.Getenv("BIRDLG_SERVERS"); env != "" {
settingDefault.servers = strings.Split(env, ",")
}
if env := os.Getenv("BIRDLG_DOMAIN"); env != "" {
settingDefault.domain = env
}
if env := os.Getenv("BIRDLG_PROXY_PORT"); env != "" {
var err error
if settingDefault.proxyPort, err = strconv.Atoi(env); err != nil {
panic(err)
}
}
if env := os.Getenv("BIRDLG_WHOIS"); env != "" {
settingDefault.whoisServer = env
}
if env := os.Getenv("BIRDLG_LISTEN"); env != "" {
settingDefault.listen = env
}
if env := os.Getenv("BIRDLG_DNS_INTERFACE"); env != "" {
settingDefault.dnsInterface = env
}
if env := os.Getenv("BIRDLG_NET_SPECIFIC_MODE"); env != "" {
settingDefault.netSpecificMode = env
}
if env := os.Getenv("BIRDLG_TITLE_BRAND"); env != "" {
settingDefault.titleBrand = env
settingDefault.navBarBrand = env
}
if env := os.Getenv("BIRDLG_NAVBAR_BRAND"); env != "" {
settingDefault.navBarBrand = env
}
serversPtr := flag.String("servers", strings.Join(settingDefault.servers, ","), "server name prefixes, separated by comma")
domainPtr := flag.String("domain", settingDefault.domain, "server name domain suffixes")
proxyPortPtr := flag.Int("proxy-port", settingDefault.proxyPort, "port bird-lgproxy is running on")
whoisPtr := flag.String("whois", settingDefault.whoisServer, "whois server for queries")
listenPtr := flag.String("listen", settingDefault.listen, "address bird-lg is listening on")
dnsInterfacePtr := flag.String("dns-interface", settingDefault.dnsInterface, "dns zone to query ASN information")
netSpecificModePtr := flag.String("net-specific-mode", settingDefault.netSpecificMode, "network specific operation mode, [(none)|dn42]")
titleBrandPtr := flag.String("title-brand", settingDefault.titleBrand, "prefix of page titles in browser tabs")
navBarBrandPtr := flag.String("navbar-brand", settingDefault.navBarBrand, "brand to show in the navigation bar")
flag.Parse()
if *serversPtr == "" {
panic("no server set")
}
servers := strings.Split(*serversPtr, ",")
serversDisplay := strings.Split(*serversPtr, ",")
// Split server names of the form "DisplayName<Hostname>"
for i, server := range servers {
pos := strings.Index(server, "<")
if pos != -1 {
serversDisplay[i] = server[0:pos]
servers[i] = server[pos+1:len(server)-1]
}
}
setting = settingType{
servers,
serversDisplay,
*domainPtr,
*proxyPortPtr,
*whoisPtr,
*listenPtr,
*dnsInterfacePtr,
strings.ToLower(*netSpecificModePtr),
*titleBrandPtr,
*navBarBrandPtr,
}
parseSettings()
ImportTemplates()
webServerStart()
var l net.Listener
var err error
if strings.HasPrefix(setting.listen, "/") {
// Delete existing socket file, ignore errors (will fail later anyway)
os.Remove(setting.listen)
l, err = net.Listen("unix", setting.listen)
} else {
listenAddr := setting.listen
if !strings.Contains(listenAddr, ":") {
listenAddr = ":" + listenAddr
}
l, err = net.Listen("tcp", listenAddr)
}
if err != nil {
panic(err)
}
webServerStart(l)
}

30
frontend/network_test.go Normal file
View File

@@ -0,0 +1,30 @@
package main
import (
"net"
"testing"
"time"
)
const (
NETWORK_UNKNOWN = 0
NETWORK_DOWN = 1
NETWORK_UP = 2
)
var networkState int = NETWORK_UNKNOWN
func checkNetwork(t *testing.T) {
if networkState == NETWORK_UNKNOWN {
conn, err := net.DialTimeout("tcp", "8.8.8.8:53", 1*time.Second)
if err != nil {
networkState = NETWORK_DOWN
} else {
networkState = NETWORK_UP
conn.Close()
}
}
if networkState == NETWORK_DOWN {
t.Skipf("Test skipped for network error")
}
}

View File

@@ -4,28 +4,35 @@ import (
"bytes"
"errors"
"fmt"
"html/template"
"net/http"
"net/url"
"regexp"
"sort"
"strings"
"text/template"
)
// static options map
var optionsMap = map[string]string{
"summary": "show protocols",
"detail": "show protocols all",
"route": "show route for ...",
"route_all": "show route for ... all",
"route_bgpmap": "show route for ... (bgpmap)",
"route_where": "show route where net ~ [ ... ]",
"route_where_all": "show route where net ~ [ ... ] all",
"route_where_bgpmap": "show route where net ~ [ ... ] (bgpmap)",
"route_generic": "show route ...",
"generic": "show ...",
"whois": "whois ...",
"traceroute": "traceroute ...",
"summary": "show protocols",
"detail": "show protocols all ...",
"route_from_protocol": "show route protocol ...",
"route_from_protocol_all": "show route protocol ... all",
"route_from_protocol_all_primary": "show route protocol ... all primary",
"route_filtered_from_protocol": "show route filtered protocol ...",
"route_filtered_from_protocol_all": "show route filtered protocol ... all",
"route_from_origin": "show route where bgp_path.last = ...",
"route_from_origin_all": "show route where bgp_path.last = ... all",
"route_from_origin_all_primary": "show route where bgp_path.last = ... all primary",
"route": "show route for ...",
"route_all": "show route for ... all",
"route_bgpmap": "show route for ... (bgpmap)",
"route_where": "show route where net ~ [ ... ]",
"route_where_all": "show route where net ~ [ ... ] all",
"route_where_bgpmap": "show route where net ~ [ ... ] (bgpmap)",
"route_generic": "show route ...",
"generic": "show ...",
"whois": "whois ...",
"traceroute": "traceroute ...",
}
// pre-compiled regexp and constant statemap for summary rendering
@@ -38,7 +45,7 @@ var summaryStateMap = map[string]string{
}
// render the page template
func renderPageTemplate(w http.ResponseWriter, r *http.Request, title string, content string) {
func renderPageTemplate(w http.ResponseWriter, r *http.Request, title string, content template.HTML) {
path := r.URL.Path[1:]
split := strings.SplitN(path, "/", 3)
@@ -48,33 +55,30 @@ func renderPageTemplate(w http.ResponseWriter, r *http.Request, title string, co
// Use a default URL if the request URL is too short
// The URL is for return to summary page
if len(split) < 2 {
path = "summary/" + url.PathEscape(strings.Join(setting.servers, "+")) + "/"
path = "summary/" + strings.Join(setting.servers, "+") + "/"
} else if len(split) == 2 {
path += "/"
}
split = strings.SplitN(path, "/", 3)
serversEscaped := make([]string, len(setting.servers))
for i, v := range setting.servers {
serversEscaped[i] = url.PathEscape(v)
}
args := TemplatePage{
Options: optionsMap,
Servers: setting.servers,
ServersEscaped: serversEscaped,
ServersDisplay: setting.serversDisplay,
AllServersLinkActive: strings.ToLower(split[1]) == strings.ToLower(strings.Join(setting.servers, "+")),
AllServersURL: url.PathEscape(strings.Join(setting.servers, "+")),
AllServersLinkActive: strings.EqualFold(split[1], strings.Join(setting.servers, "+")),
AllServersURL: strings.Join(setting.servers, "+"),
AllServerTitle: setting.navBarAllServer,
AllServersURLCustom: setting.navBarAllURL,
IsWhois: isWhois,
WhoisTarget: whoisTarget,
URLOption: strings.ToLower(split[0]),
URLServer: url.PathEscape(strings.ToLower(split[1])),
URLServer: strings.ToLower(split[1]),
URLCommand: split[2],
Title: setting.titleBrand + title,
Brand: setting.navBarBrand,
BrandURL: setting.navBarBrandURL,
Content: content,
}
@@ -88,7 +92,7 @@ func renderPageTemplate(w http.ResponseWriter, r *http.Request, title string, co
// Write the given text to http response, and add whois links for
// ASNs and IP addresses
func smartFormatter(s string) string {
func smartFormatter(s string) template.HTML {
var result string
result += "<pre>"
s = template.HTMLEscapeString(s)
@@ -105,7 +109,7 @@ func smartFormatter(s string) string {
result += lineFormatted + "\n"
}
result += "</pre>"
return result
return template.HTML(result)
}
// Parse bird show protocols result
@@ -130,6 +134,9 @@ func summaryParse(data string, serverName string) (TemplateSummary, error) {
args.Header = append(args.Header, col)
}
// Build regexp for nameFilter
nameFilterRegexp := regexp.MustCompile(setting.nameFilter)
// sort the remaining rows
rows := lines[1:]
sort.Strings(rows)
@@ -153,9 +160,24 @@ func summaryParse(data string, serverName string) (TemplateSummary, error) {
if len(lineSplitted) >= 2 {
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])
@@ -179,11 +201,11 @@ func summaryParse(data string, serverName string) (TemplateSummary, error) {
}
// Output a table for the summary page
func summaryTable(data string, serverName string) string {
func summaryTable(data string, serverName string) template.HTML {
result, err := summaryParse(data, serverName)
if err != nil {
return "<pre>" + template.HTMLEscapeString(err.Error()) + "</pre>"
return template.HTML("<pre>" + template.HTMLEscapeString(err.Error()) + "</pre>")
}
// render the summary template
@@ -194,5 +216,5 @@ func summaryTable(data string, serverName string) string {
fmt.Println("Error rendering summary:", err.Error())
}
return buffer.String()
return template.HTML(buffer.String())
}

Some files were not shown because too many files have changed in this diff Show More