Compare commits
1 Commits
build-test
...
go1.22
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ca8e277f9 |
@@ -22,8 +22,8 @@ local Build(mirror, go, alpine, os, arch) = {
|
|||||||
"apk update",
|
"apk update",
|
||||||
"apk add --no-cache git",
|
"apk add --no-cache git",
|
||||||
"mkdir .bin",
|
"mkdir .bin",
|
||||||
"go build -v -pgo=auto -v -trimpath -ldflags='-buildid= -bindnow' -buildmode pie -o ./.bin/go-away ./cmd/go-away",
|
"go build -v -pgo=auto -v -trimpath -ldflags=-buildid= -o ./.bin/go-away ./cmd/go-away",
|
||||||
"go build -v -trimpath -ldflags='-buildid= -bindnow' -buildmode pie -o ./.bin/test-wasm-runtime ./cmd/test-wasm-runtime",
|
"go build -v -o ./.bin/test-wasm-runtime ./cmd/test-wasm-runtime",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -138,25 +138,12 @@ local Publish(mirror, registry, repo, secret, go, alpine, os, arch, trigger, pla
|
|||||||
#
|
#
|
||||||
local containerArchitectures = ["linux/amd64", "linux/arm64", "linux/riscv64"];
|
local containerArchitectures = ["linux/amd64", "linux/arm64", "linux/riscv64"];
|
||||||
|
|
||||||
local alpineVersion = "3.21";
|
local alpineVersion = "3.20";
|
||||||
local goVersion = "1.24";
|
local goVersion = "1.22";
|
||||||
|
|
||||||
local mirror = "https://mirror.gcr.io";
|
local mirror = "https://mirror.gcr.io";
|
||||||
|
|
||||||
[
|
[
|
||||||
Build(mirror, goVersion, alpineVersion, "linux", "amd64") + {"trigger": {event: ["push", "tag"], }},
|
Build(mirror, goVersion, alpineVersion, "linux", "amd64"),
|
||||||
Build(mirror, goVersion, alpineVersion, "linux", "arm64") + {"trigger": {event: ["push", "tag"], }},
|
Build(mirror, goVersion, alpineVersion, "linux", "arm64"),
|
||||||
|
|
||||||
# Test PRs
|
|
||||||
Build(mirror, goVersion, alpineVersion, "linux", "amd64") + {"name": "test-pr", "trigger": {event: ["pull_request"], }},
|
|
||||||
|
|
||||||
# latest
|
|
||||||
Publish(mirror, "git.gammaspectra.live", "git.gammaspectra.live/git/go-away", "git", goVersion, alpineVersion, "linux", "amd64", {event: ["push"], branch: ["master"], }, containerArchitectures, {tags: ["latest"],}) + {name: "publish-latest-git"},
|
|
||||||
Publish(mirror, "codeberg.org", "codeberg.org/gone/go-away", "codeberg", goVersion, alpineVersion, "linux", "amd64", {event: ["push"], branch: ["master"], }, containerArchitectures, {tags: ["latest"],}) + {name: "publish-latest-codeberg"},
|
|
||||||
Publish(mirror, "ghcr.io", "ghcr.io/weebdatahoarder/go-away", "github", goVersion, alpineVersion, "linux", "amd64", {event: ["push"], branch: ["master"], }, containerArchitectures, {tags: ["latest"],}) + {name: "publish-latest-github"},
|
|
||||||
|
|
||||||
# modern
|
|
||||||
Publish(mirror, "git.gammaspectra.live", "git.gammaspectra.live/git/go-away", "git", goVersion, alpineVersion, "linux", "amd64", {event: ["promote", "tag"], target: ["production"], }, containerArchitectures, {auto_tag: true,}),
|
|
||||||
Publish(mirror, "codeberg.org", "codeberg.org/gone/go-away", "codeberg", goVersion, alpineVersion, "linux", "amd64", {event: ["promote", "tag"], target: ["production"], }, containerArchitectures, {auto_tag: true,}),
|
|
||||||
Publish(mirror, "ghcr.io", "ghcr.io/weebdatahoarder/go-away", "github", goVersion, alpineVersion, "linux", "amd64", {event: ["promote", "tag"], target: ["production"], }, containerArchitectures, {auto_tag: true,}),
|
|
||||||
]
|
]
|
||||||
411
.drone.yml
411
.drone.yml
@@ -5,7 +5,7 @@ environment:
|
|||||||
GOOS: linux
|
GOOS: linux
|
||||||
GOTOOLCHAIN: local
|
GOTOOLCHAIN: local
|
||||||
kind: pipeline
|
kind: pipeline
|
||||||
name: build-1.24-alpine3.21-amd64
|
name: build-1.22-alpine3.20-amd64
|
||||||
platform:
|
platform:
|
||||||
arch: amd64
|
arch: amd64
|
||||||
os: linux
|
os: linux
|
||||||
@@ -14,11 +14,9 @@ steps:
|
|||||||
- apk update
|
- apk update
|
||||||
- apk add --no-cache git
|
- apk add --no-cache git
|
||||||
- mkdir .bin
|
- mkdir .bin
|
||||||
- go build -v -pgo=auto -v -trimpath -ldflags='-buildid= -bindnow' -buildmode pie
|
- go build -v -pgo=auto -v -trimpath -ldflags=-buildid= -o ./.bin/go-away ./cmd/go-away
|
||||||
-o ./.bin/go-away ./cmd/go-away
|
- go build -v -o ./.bin/test-wasm-runtime ./cmd/test-wasm-runtime
|
||||||
- go build -v -trimpath -ldflags='-buildid= -bindnow' -buildmode pie -o ./.bin/test-wasm-runtime
|
image: golang:1.22-alpine3.20
|
||||||
./cmd/test-wasm-runtime
|
|
||||||
image: golang:1.24-alpine3.21
|
|
||||||
mirror: https://mirror.gcr.io
|
mirror: https://mirror.gcr.io
|
||||||
name: build
|
name: build
|
||||||
- commands:
|
- commands:
|
||||||
@@ -26,7 +24,7 @@ steps:
|
|||||||
--policy examples/forgejo.yml --policy-snippets examples/snippets/
|
--policy examples/forgejo.yml --policy-snippets examples/snippets/
|
||||||
depends_on:
|
depends_on:
|
||||||
- build
|
- build
|
||||||
image: alpine:3.21
|
image: alpine:3.20
|
||||||
mirror: https://mirror.gcr.io
|
mirror: https://mirror.gcr.io
|
||||||
name: check-policy-forgejo
|
name: check-policy-forgejo
|
||||||
- commands:
|
- commands:
|
||||||
@@ -34,7 +32,7 @@ steps:
|
|||||||
--policy examples/generic.yml --policy-snippets examples/snippets/
|
--policy examples/generic.yml --policy-snippets examples/snippets/
|
||||||
depends_on:
|
depends_on:
|
||||||
- build
|
- build
|
||||||
image: alpine:3.21
|
image: alpine:3.20
|
||||||
mirror: https://mirror.gcr.io
|
mirror: https://mirror.gcr.io
|
||||||
name: check-policy-generic
|
name: check-policy-generic
|
||||||
- commands:
|
- commands:
|
||||||
@@ -42,7 +40,7 @@ steps:
|
|||||||
--policy examples/spa.yml --policy-snippets examples/snippets/
|
--policy examples/spa.yml --policy-snippets examples/snippets/
|
||||||
depends_on:
|
depends_on:
|
||||||
- build
|
- build
|
||||||
image: alpine:3.21
|
image: alpine:3.20
|
||||||
mirror: https://mirror.gcr.io
|
mirror: https://mirror.gcr.io
|
||||||
name: check-policy-spa
|
name: check-policy-spa
|
||||||
- commands:
|
- commands:
|
||||||
@@ -53,7 +51,7 @@ steps:
|
|||||||
0
|
0
|
||||||
depends_on:
|
depends_on:
|
||||||
- build
|
- build
|
||||||
image: alpine:3.21
|
image: alpine:3.20
|
||||||
mirror: https://mirror.gcr.io
|
mirror: https://mirror.gcr.io
|
||||||
name: test-wasm-success
|
name: test-wasm-success
|
||||||
- commands:
|
- commands:
|
||||||
@@ -64,13 +62,9 @@ steps:
|
|||||||
1
|
1
|
||||||
depends_on:
|
depends_on:
|
||||||
- build
|
- build
|
||||||
image: alpine:3.21
|
image: alpine:3.20
|
||||||
mirror: https://mirror.gcr.io
|
mirror: https://mirror.gcr.io
|
||||||
name: test-wasm-fail
|
name: test-wasm-fail
|
||||||
trigger:
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
- tag
|
|
||||||
type: docker
|
type: docker
|
||||||
---
|
---
|
||||||
environment:
|
environment:
|
||||||
@@ -79,7 +73,7 @@ environment:
|
|||||||
GOOS: linux
|
GOOS: linux
|
||||||
GOTOOLCHAIN: local
|
GOTOOLCHAIN: local
|
||||||
kind: pipeline
|
kind: pipeline
|
||||||
name: build-1.24-alpine3.21-arm64
|
name: build-1.22-alpine3.20-arm64
|
||||||
platform:
|
platform:
|
||||||
arch: arm64
|
arch: arm64
|
||||||
os: linux
|
os: linux
|
||||||
@@ -88,11 +82,9 @@ steps:
|
|||||||
- apk update
|
- apk update
|
||||||
- apk add --no-cache git
|
- apk add --no-cache git
|
||||||
- mkdir .bin
|
- mkdir .bin
|
||||||
- go build -v -pgo=auto -v -trimpath -ldflags='-buildid= -bindnow' -buildmode pie
|
- go build -v -pgo=auto -v -trimpath -ldflags=-buildid= -o ./.bin/go-away ./cmd/go-away
|
||||||
-o ./.bin/go-away ./cmd/go-away
|
- go build -v -o ./.bin/test-wasm-runtime ./cmd/test-wasm-runtime
|
||||||
- go build -v -trimpath -ldflags='-buildid= -bindnow' -buildmode pie -o ./.bin/test-wasm-runtime
|
image: golang:1.22-alpine3.20
|
||||||
./cmd/test-wasm-runtime
|
|
||||||
image: golang:1.24-alpine3.21
|
|
||||||
mirror: https://mirror.gcr.io
|
mirror: https://mirror.gcr.io
|
||||||
name: build
|
name: build
|
||||||
- commands:
|
- commands:
|
||||||
@@ -100,7 +92,7 @@ steps:
|
|||||||
--policy examples/forgejo.yml --policy-snippets examples/snippets/
|
--policy examples/forgejo.yml --policy-snippets examples/snippets/
|
||||||
depends_on:
|
depends_on:
|
||||||
- build
|
- build
|
||||||
image: alpine:3.21
|
image: alpine:3.20
|
||||||
mirror: https://mirror.gcr.io
|
mirror: https://mirror.gcr.io
|
||||||
name: check-policy-forgejo
|
name: check-policy-forgejo
|
||||||
- commands:
|
- commands:
|
||||||
@@ -108,7 +100,7 @@ steps:
|
|||||||
--policy examples/generic.yml --policy-snippets examples/snippets/
|
--policy examples/generic.yml --policy-snippets examples/snippets/
|
||||||
depends_on:
|
depends_on:
|
||||||
- build
|
- build
|
||||||
image: alpine:3.21
|
image: alpine:3.20
|
||||||
mirror: https://mirror.gcr.io
|
mirror: https://mirror.gcr.io
|
||||||
name: check-policy-generic
|
name: check-policy-generic
|
||||||
- commands:
|
- commands:
|
||||||
@@ -116,7 +108,7 @@ steps:
|
|||||||
--policy examples/spa.yml --policy-snippets examples/snippets/
|
--policy examples/spa.yml --policy-snippets examples/snippets/
|
||||||
depends_on:
|
depends_on:
|
||||||
- build
|
- build
|
||||||
image: alpine:3.21
|
image: alpine:3.20
|
||||||
mirror: https://mirror.gcr.io
|
mirror: https://mirror.gcr.io
|
||||||
name: check-policy-spa
|
name: check-policy-spa
|
||||||
- commands:
|
- commands:
|
||||||
@@ -127,7 +119,7 @@ steps:
|
|||||||
0
|
0
|
||||||
depends_on:
|
depends_on:
|
||||||
- build
|
- build
|
||||||
image: alpine:3.21
|
image: alpine:3.20
|
||||||
mirror: https://mirror.gcr.io
|
mirror: https://mirror.gcr.io
|
||||||
name: test-wasm-success
|
name: test-wasm-success
|
||||||
- commands:
|
- commands:
|
||||||
@@ -138,377 +130,12 @@ steps:
|
|||||||
1
|
1
|
||||||
depends_on:
|
depends_on:
|
||||||
- build
|
- build
|
||||||
image: alpine:3.21
|
image: alpine:3.20
|
||||||
mirror: https://mirror.gcr.io
|
mirror: https://mirror.gcr.io
|
||||||
name: test-wasm-fail
|
name: test-wasm-fail
|
||||||
trigger:
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
- tag
|
|
||||||
type: docker
|
|
||||||
---
|
|
||||||
environment:
|
|
||||||
CGO_ENABLED: "0"
|
|
||||||
GOARCH: amd64
|
|
||||||
GOOS: linux
|
|
||||||
GOTOOLCHAIN: local
|
|
||||||
kind: pipeline
|
|
||||||
name: test-pr
|
|
||||||
platform:
|
|
||||||
arch: amd64
|
|
||||||
os: linux
|
|
||||||
steps:
|
|
||||||
- commands:
|
|
||||||
- apk update
|
|
||||||
- apk add --no-cache git
|
|
||||||
- mkdir .bin
|
|
||||||
- go build -v -pgo=auto -v -trimpath -ldflags='-buildid= -bindnow' -buildmode pie
|
|
||||||
-o ./.bin/go-away ./cmd/go-away
|
|
||||||
- go build -v -trimpath -ldflags='-buildid= -bindnow' -buildmode pie -o ./.bin/test-wasm-runtime
|
|
||||||
./cmd/test-wasm-runtime
|
|
||||||
image: golang:1.24-alpine3.21
|
|
||||||
mirror: https://mirror.gcr.io
|
|
||||||
name: build
|
|
||||||
- commands:
|
|
||||||
- ./.bin/go-away --check --slog-level DEBUG --backend example.com=http://127.0.0.1:80
|
|
||||||
--policy examples/forgejo.yml --policy-snippets examples/snippets/
|
|
||||||
depends_on:
|
|
||||||
- build
|
|
||||||
image: alpine:3.21
|
|
||||||
mirror: https://mirror.gcr.io
|
|
||||||
name: check-policy-forgejo
|
|
||||||
- commands:
|
|
||||||
- ./.bin/go-away --check --slog-level DEBUG --backend example.com=http://127.0.0.1:80
|
|
||||||
--policy examples/generic.yml --policy-snippets examples/snippets/
|
|
||||||
depends_on:
|
|
||||||
- build
|
|
||||||
image: alpine:3.21
|
|
||||||
mirror: https://mirror.gcr.io
|
|
||||||
name: check-policy-generic
|
|
||||||
- commands:
|
|
||||||
- ./.bin/go-away --check --slog-level DEBUG --backend example.com=http://127.0.0.1:80
|
|
||||||
--policy examples/spa.yml --policy-snippets examples/snippets/
|
|
||||||
depends_on:
|
|
||||||
- build
|
|
||||||
image: alpine:3.21
|
|
||||||
mirror: https://mirror.gcr.io
|
|
||||||
name: check-policy-spa
|
|
||||||
- commands:
|
|
||||||
- ./.bin/test-wasm-runtime -wasm ./embed/challenge/js-pow-sha256/runtime/runtime.wasm
|
|
||||||
-make-challenge ./embed/challenge/js-pow-sha256/test/make-challenge.json -make-challenge-out
|
|
||||||
./embed/challenge/js-pow-sha256/test/make-challenge-out.json -verify-challenge
|
|
||||||
./embed/challenge/js-pow-sha256/test/verify-challenge.json -verify-challenge-out
|
|
||||||
0
|
|
||||||
depends_on:
|
|
||||||
- build
|
|
||||||
image: alpine:3.21
|
|
||||||
mirror: https://mirror.gcr.io
|
|
||||||
name: test-wasm-success
|
|
||||||
- commands:
|
|
||||||
- ./.bin/test-wasm-runtime -wasm ./embed/challenge/js-pow-sha256/runtime/runtime.wasm
|
|
||||||
-make-challenge ./embed/challenge/js-pow-sha256/test/make-challenge.json -make-challenge-out
|
|
||||||
./embed/challenge/js-pow-sha256/test/make-challenge-out.json -verify-challenge
|
|
||||||
./embed/challenge/js-pow-sha256/test/verify-challenge-fail.json -verify-challenge-out
|
|
||||||
1
|
|
||||||
depends_on:
|
|
||||||
- build
|
|
||||||
image: alpine:3.21
|
|
||||||
mirror: https://mirror.gcr.io
|
|
||||||
name: test-wasm-fail
|
|
||||||
trigger:
|
|
||||||
event:
|
|
||||||
- pull_request
|
|
||||||
type: docker
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: publish-latest-git
|
|
||||||
platform:
|
|
||||||
arch: amd64
|
|
||||||
os: linux
|
|
||||||
steps:
|
|
||||||
- commands:
|
|
||||||
- echo '[registry."docker.io"]' > buildkitd.toml
|
|
||||||
- echo ' mirrors = ["mirror.gcr.io"]' >> buildkitd.toml
|
|
||||||
image: alpine:3.21
|
|
||||||
mirror: https://mirror.gcr.io
|
|
||||||
name: setup-buildkitd
|
|
||||||
- environment:
|
|
||||||
DOCKER_BUILDKIT: "1"
|
|
||||||
LC_ALL: C
|
|
||||||
PLUGIN_BUILDER_CONFIG: buildkitd.toml
|
|
||||||
PLUGIN_BUILDER_DRIVER: docker-container
|
|
||||||
SOURCE_DATE_EPOCH: 0
|
|
||||||
TZ: UTC
|
|
||||||
image: plugins/buildx
|
|
||||||
name: docker
|
|
||||||
privileged: true
|
|
||||||
settings:
|
|
||||||
auto_tag_suffix: alpine3.21
|
|
||||||
build_args:
|
|
||||||
from: alpine:3.21
|
|
||||||
from_builder: golang:1.24-alpine3.21
|
|
||||||
compress: true
|
|
||||||
mirror: https://mirror.gcr.io
|
|
||||||
password:
|
|
||||||
from_secret: git_password
|
|
||||||
platform:
|
|
||||||
- linux/amd64
|
|
||||||
- linux/arm64
|
|
||||||
- linux/riscv64
|
|
||||||
registry: git.gammaspectra.live
|
|
||||||
repo: git.gammaspectra.live/git/go-away
|
|
||||||
tags:
|
|
||||||
- latest
|
|
||||||
username:
|
|
||||||
from_secret: git_username
|
|
||||||
trigger:
|
|
||||||
branch:
|
|
||||||
- master
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
type: docker
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: publish-latest-codeberg
|
|
||||||
platform:
|
|
||||||
arch: amd64
|
|
||||||
os: linux
|
|
||||||
steps:
|
|
||||||
- commands:
|
|
||||||
- echo '[registry."docker.io"]' > buildkitd.toml
|
|
||||||
- echo ' mirrors = ["mirror.gcr.io"]' >> buildkitd.toml
|
|
||||||
image: alpine:3.21
|
|
||||||
mirror: https://mirror.gcr.io
|
|
||||||
name: setup-buildkitd
|
|
||||||
- environment:
|
|
||||||
DOCKER_BUILDKIT: "1"
|
|
||||||
LC_ALL: C
|
|
||||||
PLUGIN_BUILDER_CONFIG: buildkitd.toml
|
|
||||||
PLUGIN_BUILDER_DRIVER: docker-container
|
|
||||||
SOURCE_DATE_EPOCH: 0
|
|
||||||
TZ: UTC
|
|
||||||
image: plugins/buildx
|
|
||||||
name: docker
|
|
||||||
privileged: true
|
|
||||||
settings:
|
|
||||||
auto_tag_suffix: alpine3.21
|
|
||||||
build_args:
|
|
||||||
from: alpine:3.21
|
|
||||||
from_builder: golang:1.24-alpine3.21
|
|
||||||
compress: true
|
|
||||||
mirror: https://mirror.gcr.io
|
|
||||||
password:
|
|
||||||
from_secret: codeberg_password
|
|
||||||
platform:
|
|
||||||
- linux/amd64
|
|
||||||
- linux/arm64
|
|
||||||
- linux/riscv64
|
|
||||||
registry: codeberg.org
|
|
||||||
repo: codeberg.org/gone/go-away
|
|
||||||
tags:
|
|
||||||
- latest
|
|
||||||
username:
|
|
||||||
from_secret: codeberg_username
|
|
||||||
trigger:
|
|
||||||
branch:
|
|
||||||
- master
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
type: docker
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: publish-latest-github
|
|
||||||
platform:
|
|
||||||
arch: amd64
|
|
||||||
os: linux
|
|
||||||
steps:
|
|
||||||
- commands:
|
|
||||||
- echo '[registry."docker.io"]' > buildkitd.toml
|
|
||||||
- echo ' mirrors = ["mirror.gcr.io"]' >> buildkitd.toml
|
|
||||||
image: alpine:3.21
|
|
||||||
mirror: https://mirror.gcr.io
|
|
||||||
name: setup-buildkitd
|
|
||||||
- environment:
|
|
||||||
DOCKER_BUILDKIT: "1"
|
|
||||||
LC_ALL: C
|
|
||||||
PLUGIN_BUILDER_CONFIG: buildkitd.toml
|
|
||||||
PLUGIN_BUILDER_DRIVER: docker-container
|
|
||||||
SOURCE_DATE_EPOCH: 0
|
|
||||||
TZ: UTC
|
|
||||||
image: plugins/buildx
|
|
||||||
name: docker
|
|
||||||
privileged: true
|
|
||||||
settings:
|
|
||||||
auto_tag_suffix: alpine3.21
|
|
||||||
build_args:
|
|
||||||
from: alpine:3.21
|
|
||||||
from_builder: golang:1.24-alpine3.21
|
|
||||||
compress: true
|
|
||||||
mirror: https://mirror.gcr.io
|
|
||||||
password:
|
|
||||||
from_secret: github_password
|
|
||||||
platform:
|
|
||||||
- linux/amd64
|
|
||||||
- linux/arm64
|
|
||||||
- linux/riscv64
|
|
||||||
registry: ghcr.io
|
|
||||||
repo: ghcr.io/weebdatahoarder/go-away
|
|
||||||
tags:
|
|
||||||
- latest
|
|
||||||
username:
|
|
||||||
from_secret: github_username
|
|
||||||
trigger:
|
|
||||||
branch:
|
|
||||||
- master
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
type: docker
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: publish-1.24-alpine3.21-git
|
|
||||||
platform:
|
|
||||||
arch: amd64
|
|
||||||
os: linux
|
|
||||||
steps:
|
|
||||||
- commands:
|
|
||||||
- echo '[registry."docker.io"]' > buildkitd.toml
|
|
||||||
- echo ' mirrors = ["mirror.gcr.io"]' >> buildkitd.toml
|
|
||||||
image: alpine:3.21
|
|
||||||
mirror: https://mirror.gcr.io
|
|
||||||
name: setup-buildkitd
|
|
||||||
- environment:
|
|
||||||
DOCKER_BUILDKIT: "1"
|
|
||||||
LC_ALL: C
|
|
||||||
PLUGIN_BUILDER_CONFIG: buildkitd.toml
|
|
||||||
PLUGIN_BUILDER_DRIVER: docker-container
|
|
||||||
SOURCE_DATE_EPOCH: 0
|
|
||||||
TZ: UTC
|
|
||||||
image: plugins/buildx
|
|
||||||
name: docker
|
|
||||||
privileged: true
|
|
||||||
settings:
|
|
||||||
auto_tag: true
|
|
||||||
auto_tag_suffix: alpine3.21
|
|
||||||
build_args:
|
|
||||||
from: alpine:3.21
|
|
||||||
from_builder: golang:1.24-alpine3.21
|
|
||||||
compress: true
|
|
||||||
mirror: https://mirror.gcr.io
|
|
||||||
password:
|
|
||||||
from_secret: git_password
|
|
||||||
platform:
|
|
||||||
- linux/amd64
|
|
||||||
- linux/arm64
|
|
||||||
- linux/riscv64
|
|
||||||
registry: git.gammaspectra.live
|
|
||||||
repo: git.gammaspectra.live/git/go-away
|
|
||||||
username:
|
|
||||||
from_secret: git_username
|
|
||||||
trigger:
|
|
||||||
event:
|
|
||||||
- promote
|
|
||||||
- tag
|
|
||||||
target:
|
|
||||||
- production
|
|
||||||
type: docker
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: publish-1.24-alpine3.21-codeberg
|
|
||||||
platform:
|
|
||||||
arch: amd64
|
|
||||||
os: linux
|
|
||||||
steps:
|
|
||||||
- commands:
|
|
||||||
- echo '[registry."docker.io"]' > buildkitd.toml
|
|
||||||
- echo ' mirrors = ["mirror.gcr.io"]' >> buildkitd.toml
|
|
||||||
image: alpine:3.21
|
|
||||||
mirror: https://mirror.gcr.io
|
|
||||||
name: setup-buildkitd
|
|
||||||
- environment:
|
|
||||||
DOCKER_BUILDKIT: "1"
|
|
||||||
LC_ALL: C
|
|
||||||
PLUGIN_BUILDER_CONFIG: buildkitd.toml
|
|
||||||
PLUGIN_BUILDER_DRIVER: docker-container
|
|
||||||
SOURCE_DATE_EPOCH: 0
|
|
||||||
TZ: UTC
|
|
||||||
image: plugins/buildx
|
|
||||||
name: docker
|
|
||||||
privileged: true
|
|
||||||
settings:
|
|
||||||
auto_tag: true
|
|
||||||
auto_tag_suffix: alpine3.21
|
|
||||||
build_args:
|
|
||||||
from: alpine:3.21
|
|
||||||
from_builder: golang:1.24-alpine3.21
|
|
||||||
compress: true
|
|
||||||
mirror: https://mirror.gcr.io
|
|
||||||
password:
|
|
||||||
from_secret: codeberg_password
|
|
||||||
platform:
|
|
||||||
- linux/amd64
|
|
||||||
- linux/arm64
|
|
||||||
- linux/riscv64
|
|
||||||
registry: codeberg.org
|
|
||||||
repo: codeberg.org/gone/go-away
|
|
||||||
username:
|
|
||||||
from_secret: codeberg_username
|
|
||||||
trigger:
|
|
||||||
event:
|
|
||||||
- promote
|
|
||||||
- tag
|
|
||||||
target:
|
|
||||||
- production
|
|
||||||
type: docker
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: publish-1.24-alpine3.21-github
|
|
||||||
platform:
|
|
||||||
arch: amd64
|
|
||||||
os: linux
|
|
||||||
steps:
|
|
||||||
- commands:
|
|
||||||
- echo '[registry."docker.io"]' > buildkitd.toml
|
|
||||||
- echo ' mirrors = ["mirror.gcr.io"]' >> buildkitd.toml
|
|
||||||
image: alpine:3.21
|
|
||||||
mirror: https://mirror.gcr.io
|
|
||||||
name: setup-buildkitd
|
|
||||||
- environment:
|
|
||||||
DOCKER_BUILDKIT: "1"
|
|
||||||
LC_ALL: C
|
|
||||||
PLUGIN_BUILDER_CONFIG: buildkitd.toml
|
|
||||||
PLUGIN_BUILDER_DRIVER: docker-container
|
|
||||||
SOURCE_DATE_EPOCH: 0
|
|
||||||
TZ: UTC
|
|
||||||
image: plugins/buildx
|
|
||||||
name: docker
|
|
||||||
privileged: true
|
|
||||||
settings:
|
|
||||||
auto_tag: true
|
|
||||||
auto_tag_suffix: alpine3.21
|
|
||||||
build_args:
|
|
||||||
from: alpine:3.21
|
|
||||||
from_builder: golang:1.24-alpine3.21
|
|
||||||
compress: true
|
|
||||||
mirror: https://mirror.gcr.io
|
|
||||||
password:
|
|
||||||
from_secret: github_password
|
|
||||||
platform:
|
|
||||||
- linux/amd64
|
|
||||||
- linux/arm64
|
|
||||||
- linux/riscv64
|
|
||||||
registry: ghcr.io
|
|
||||||
repo: ghcr.io/weebdatahoarder/go-away
|
|
||||||
username:
|
|
||||||
from_secret: github_username
|
|
||||||
trigger:
|
|
||||||
event:
|
|
||||||
- promote
|
|
||||||
- tag
|
|
||||||
target:
|
|
||||||
- production
|
|
||||||
type: docker
|
type: docker
|
||||||
---
|
---
|
||||||
kind: signature
|
kind: signature
|
||||||
hmac: 9a3872c0b58810924c4342c9dbd338e16da20631c9a0848e3abd2bf6773f9ba6
|
hmac: 00be8252a86e2c760c07c2baa6efa55d75f46fdc22299bb3feb1b199cc54af9e
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|||||||
@@ -1,71 +0,0 @@
|
|||||||
---
|
|
||||||
name: CI/CD Pipeline
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [master, build-test]
|
|
||||||
pull_request:
|
|
||||||
release:
|
|
||||||
types: [published]
|
|
||||||
workflow_dispatch:
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
architecture: [amd64, arm64]
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v2
|
|
||||||
with:
|
|
||||||
go-version: '1.24'
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y git
|
|
||||||
- name: Build go-away
|
|
||||||
run: |
|
|
||||||
mkdir .bin
|
|
||||||
go build -v -pgo=auto -v -trimpath -ldflags='-buildid= -bindnow' -buildmode pie -o ./.bin/go-away ./cmd/go-away
|
|
||||||
go build -v -trimpath -ldflags='-buildid= -bindnow' -buildmode pie -o ./.bin/test-wasm-runtime ./cmd/test-wasm-runtime
|
|
||||||
- name: Check policy for Forgejo
|
|
||||||
run: |
|
|
||||||
./.bin/go-away --check --slog-level DEBUG --backend example.com=http://127.0.0.1:80 --policy examples/forgejo.yml --policy-snippets examples/snippets/
|
|
||||||
- name: Check policy for Generic
|
|
||||||
run: |
|
|
||||||
./.bin/go-away --check --slog-level DEBUG --backend example.com=http://127.0.0.1:80 --policy examples/generic.yml --policy-snippets examples/snippets/
|
|
||||||
- name: Check policy for SPA
|
|
||||||
run: |
|
|
||||||
./.bin/go-away --check --slog-level DEBUG --backend example.com=http://127.0.0.1:80 --policy examples/spa.yml --policy-snippets examples/snippets/
|
|
||||||
- name: Test WASM Runtime Success
|
|
||||||
run: |
|
|
||||||
./.bin/test-wasm-runtime -wasm ./embed/challenge/js-pow-sha256/runtime/runtime.wasm -make-challenge ./embed/challenge/js-pow-sha256/test/make-challenge.json -make-challenge-out ./embed/challenge/js-pow-sha256/test/make-challenge-out.json -verify-challenge ./embed/challenge/js-pow-sha256/test/verify-challenge.json -verify-challenge-out 0
|
|
||||||
- name: Test WASM Runtime Fail
|
|
||||||
run: |
|
|
||||||
./.bin/test-wasm-runtime -wasm ./embed/challenge/js-pow-sha256/runtime/runtime.wasm -make-challenge ./embed/challenge/js-pow-sha256/test/make-challenge.json -make-challenge-out ./embed/challenge/js-pow-sha256/test/make-challenge-out.json -verify-challenge ./embed/challenge/js-pow-sha256/test/verify-challenge-fail.json -verify-challenge-out 1
|
|
||||||
publish:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: build
|
|
||||||
if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/')
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
- name: Log in to Git Forge registry
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: git.projectsegfau.lt
|
|
||||||
username: ${{ secrets.GIT_USERNAME }}
|
|
||||||
password: ${{ secrets.GIT_TOKEN }}
|
|
||||||
- name: Build and push Docker images
|
|
||||||
env:
|
|
||||||
SOURCE_DATE_EPOCH: 0
|
|
||||||
TZ: UTC
|
|
||||||
run: |-
|
|
||||||
docker buildx build \
|
|
||||||
--platform linux/amd64,linux/arm64,linux/riscv64 \
|
|
||||||
--tag git.projectsegfau.lt/${{ secrets.GIT_USERNAME }}/go-away:latest \
|
|
||||||
--push \
|
|
||||||
.
|
|
||||||
17
Dockerfile
17
Dockerfile
@@ -24,26 +24,18 @@ ENV CGO_ENABLED=0
|
|||||||
ENV GOOS=${TARGETOS}
|
ENV GOOS=${TARGETOS}
|
||||||
ENV GOARCH=${TARGETARCH}
|
ENV GOARCH=${TARGETARCH}
|
||||||
ENV GOTOOLCHAIN=${GOTOOLCHAIN}
|
ENV GOTOOLCHAIN=${GOTOOLCHAIN}
|
||||||
ENV BUILDMODE=pie
|
|
||||||
|
|
||||||
# riscv64 requires GCC for pie buildmode
|
|
||||||
# see https://github.com/golang/go/issues/64875
|
|
||||||
RUN if [[ "$GOARCH" == "riscv64" ]]; then export BUILDMODE=exe; fi && \
|
|
||||||
go build -v \
|
|
||||||
-pgo=auto \
|
|
||||||
-trimpath -ldflags='-buildid= -bindnow' -buildmode $BUILDMODE \
|
|
||||||
-o "${GOBIN}/go-away" ./cmd/go-away
|
|
||||||
|
|
||||||
|
RUN go build -pgo=auto -v -trimpath -ldflags=-buildid= -o "${GOBIN}/go-away" ./cmd/go-away
|
||||||
RUN test -e "${GOBIN}/go-away"
|
RUN test -e "${GOBIN}/go-away"
|
||||||
|
|
||||||
|
|
||||||
FROM ${from}
|
FROM --platform=$TARGETPLATFORM ${from}
|
||||||
|
|
||||||
COPY --from=build /go/bin/go-away /bin/go-away
|
COPY --from=build /go/bin/go-away /bin/go-away
|
||||||
COPY examples/snippets/ /snippets/
|
COPY examples/snippets/ /snippets/
|
||||||
COPY docker-entrypoint.sh /
|
COPY docker-entrypoint.sh /
|
||||||
|
|
||||||
ENV TZ=UTC
|
ENV TZ UTC
|
||||||
|
|
||||||
ENV GOAWAY_METRICS_BIND=""
|
ENV GOAWAY_METRICS_BIND=""
|
||||||
ENV GOAWAY_DEBUG_BIND=""
|
ENV GOAWAY_DEBUG_BIND=""
|
||||||
@@ -60,6 +52,7 @@ ENV GOAWAY_CHALLENGE_TEMPLATE_LOGO=""
|
|||||||
ENV GOAWAY_SLOG_LEVEL="WARN"
|
ENV GOAWAY_SLOG_LEVEL="WARN"
|
||||||
ENV GOAWAY_CLIENT_IP_HEADER=""
|
ENV GOAWAY_CLIENT_IP_HEADER=""
|
||||||
ENV GOAWAY_BACKEND_IP_HEADER=""
|
ENV GOAWAY_BACKEND_IP_HEADER=""
|
||||||
|
ENV GOAWAY_JWT_PRIVATE_KEY_SEED=""
|
||||||
ENV GOAWAY_BACKEND=""
|
ENV GOAWAY_BACKEND=""
|
||||||
ENV GOAWAY_ACME_AUTOCERT=""
|
ENV GOAWAY_ACME_AUTOCERT=""
|
||||||
ENV GOAWAY_CACHE="/cache"
|
ENV GOAWAY_CACHE="/cache"
|
||||||
@@ -70,6 +63,6 @@ EXPOSE 8080/udp
|
|||||||
EXPOSE 9090/tcp
|
EXPOSE 9090/tcp
|
||||||
EXPOSE 6060/tcp
|
EXPOSE 6060/tcp
|
||||||
|
|
||||||
# Use GOAWAY_JWT_PRIVATE_KEY_SEED or JWT_PRIVATE_KEY_SEED secret mount to expose this value to docker
|
ENV JWT_PRIVATE_KEY_SEED="${GOAWAY_JWT_PRIVATE_KEY_SEED}"
|
||||||
|
|
||||||
ENTRYPOINT ["/docker-entrypoint.sh"]
|
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||||
|
|||||||
@@ -9,17 +9,18 @@ mkdir -p .bin/ 2>/dev/null
|
|||||||
|
|
||||||
# Setup tinygo first
|
# Setup tinygo first
|
||||||
if [[ ! -d .bin/tinygo ]]; then
|
if [[ ! -d .bin/tinygo ]]; then
|
||||||
git clone --depth=1 --branch v0.38.0 https://github.com/tinygo-org/tinygo.git .bin/tinygo
|
git clone --depth=1 --branch v0.37.0 https://github.com/tinygo-org/tinygo.git .bin/tinygo
|
||||||
pushd .bin/tinygo
|
pushd .bin/tinygo
|
||||||
git submodule update --init --recursive
|
git submodule update --init --recursive
|
||||||
|
|
||||||
go mod download -x && go mod verify
|
go mod download -x && go mod verify
|
||||||
|
|
||||||
|
make binaryen STATIC=1
|
||||||
|
make wasi-libc
|
||||||
|
|
||||||
make llvm-source
|
make llvm-source
|
||||||
make llvm-build
|
make llvm-build
|
||||||
|
|
||||||
make binaryen STATIC=1
|
|
||||||
|
|
||||||
make build/release
|
make build/release
|
||||||
else
|
else
|
||||||
pushd .bin/tinygo
|
pushd .bin/tinygo
|
||||||
|
|||||||
@@ -154,9 +154,7 @@ func main() {
|
|||||||
var seed []byte
|
var seed []byte
|
||||||
|
|
||||||
var kValue string
|
var kValue string
|
||||||
if kValue = os.Getenv("GOAWAY_JWT_PRIVATE_KEY_SEED"); kValue != "" {
|
if kValue = os.Getenv("JWT_PRIVATE_KEY_SEED"); kValue != "" {
|
||||||
// prefer first
|
|
||||||
} else if kValue = os.Getenv("JWT_PRIVATE_KEY_SEED"); kValue != "" {
|
|
||||||
|
|
||||||
} else if *jwtPrivateKeySeed != "" {
|
} else if *jwtPrivateKeySeed != "" {
|
||||||
kValue = *jwtPrivateKeySeed
|
kValue = *jwtPrivateKeySeed
|
||||||
@@ -212,7 +210,7 @@ func main() {
|
|||||||
fatal(fmt.Errorf("backend %s: failed to make reverse proxy: %w", k, err))
|
fatal(fmt.Errorf("backend %s: failed to make reverse proxy: %w", k, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
backend.ErrorLog = slog.NewLogLogger(slog.With("backend", k).Handler(), slog.LevelDebug)
|
backend.ErrorLog = slog.NewLogLogger(slog.With("backend", k).Handler(), slog.LevelError)
|
||||||
createdBackends[k] = backend
|
createdBackends[k] = backend
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,7 +291,7 @@ func main() {
|
|||||||
fatal(fmt.Errorf("failed to create server: %w", err))
|
fatal(fmt.Errorf("failed to create server: %w", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
server.ErrorLog = slog.NewLogLogger(slog.With("server", "http").Handler(), slog.LevelDebug)
|
server.ErrorLog = slog.NewLogLogger(slog.With("server", "http").Handler(), slog.LevelError)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
handler, err := loadPolicyState()
|
handler, err := loadPolicyState()
|
||||||
@@ -304,7 +302,6 @@ func main() {
|
|||||||
swap(handler)
|
swap(handler)
|
||||||
slog.Warn(
|
slog.Warn(
|
||||||
"handler configuration loaded",
|
"handler configuration loaded",
|
||||||
"key_fingerprint", hex.EncodeToString(handler.PrivateKeyFingerprint()),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// allow reloading from now on
|
// allow reloading from now on
|
||||||
@@ -339,7 +336,7 @@ func main() {
|
|||||||
debugServer := http.Server{
|
debugServer := http.Server{
|
||||||
Addr: opt.BindDebug,
|
Addr: opt.BindDebug,
|
||||||
Handler: mux,
|
Handler: mux,
|
||||||
ErrorLog: slog.NewLogLogger(slog.With("server", "debug").Handler(), slog.LevelDebug),
|
ErrorLog: slog.NewLogLogger(slog.With("server", "debug").Handler(), slog.LevelError),
|
||||||
}
|
}
|
||||||
|
|
||||||
slog.Warn(
|
slog.Warn(
|
||||||
@@ -359,7 +356,7 @@ func main() {
|
|||||||
metricsServer := http.Server{
|
metricsServer := http.Server{
|
||||||
Addr: opt.BindMetrics,
|
Addr: opt.BindMetrics,
|
||||||
Handler: mux,
|
Handler: mux,
|
||||||
ErrorLog: slog.NewLogLogger(slog.With("server", "metrics").Handler(), slog.LevelDebug),
|
ErrorLog: slog.NewLogLogger(slog.With("server", "metrics").Handler(), slog.LevelError),
|
||||||
}
|
}
|
||||||
|
|
||||||
slog.Warn(
|
slog.Warn(
|
||||||
|
|||||||
Binary file not shown.
@@ -1,62 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en" class="fixed_navbar">
|
|
||||||
<head>
|
|
||||||
<title>{{ .Title }}</title>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
{{ range .MetaTags }}
|
|
||||||
<meta {{ range $key, $value := . }}{{ $key | attr }}="{{ $value }}" {{end}}/>
|
|
||||||
{{ end }}
|
|
||||||
{{ range .LinkTags }}
|
|
||||||
<link {{ range $key, $value := . }}{{ $key | attr }}="{{ $value }}" {{end}}/>
|
|
||||||
{{ end }}
|
|
||||||
{{ range .HeaderTags }}
|
|
||||||
{{ . }}
|
|
||||||
{{ end }}
|
|
||||||
<link rel="stylesheet" type="text/css" href="/style.css?v=0.36.0">
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body class="fixed_navbar">
|
|
||||||
<!-- NAVIGATION BAR -->
|
|
||||||
<nav class="fixed_navbar">
|
|
||||||
<div id="logo">
|
|
||||||
<a id="redlib" href="/"><span id="red">red</span><span id="lib">lib.</span></a>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<!-- MAIN CONTENT -->
|
|
||||||
<main>
|
|
||||||
<div id="error">
|
|
||||||
<h1 id="status">Please wait while we verify you aren't a robot!</h1>
|
|
||||||
|
|
||||||
{{ if .Challenge }}
|
|
||||||
<h3 id="status">{{ .Strings.Get "status_loading_challenge" }} : {{.Challenge }}...</h3>
|
|
||||||
{{ else if .Error }}
|
|
||||||
<h3 id="status">{{ .Strings.Get "status_error" }} {{ .Error }}</h3>
|
|
||||||
{{ else }}
|
|
||||||
<h3 id="status">{{ .Strings.Get "status_loading" }}</h3>
|
|
||||||
{{ end }}
|
|
||||||
<details style="padding-top: 5px;">
|
|
||||||
<summary>{{ .Strings.Get "details_title" }}</summary>
|
|
||||||
|
|
||||||
{{.Strings.Get "details_text"}}
|
|
||||||
</details>
|
|
||||||
{{ if .Redirect }}
|
|
||||||
<h3><a href="{{ .Redirect }}">{{ .Strings.Get "button_refresh_page" }}</a></h3>
|
|
||||||
</div>
|
|
||||||
{{ end }}
|
|
||||||
{{if .EndTags }}
|
|
||||||
<noscript>
|
|
||||||
{{ .Strings.Get "noscript_warning" }}
|
|
||||||
</noscript>
|
|
||||||
{{end}}
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<!-- FOOTER -->
|
|
||||||
<footer>
|
|
||||||
<div class="footer-buttons">
|
|
||||||
<p><small>{{ .Strings.Get "details_contact_admin_with_request_id" }}: <em>{{ .Id }}</em></small></p>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -24,7 +24,7 @@ bind:
|
|||||||
#bind-debug: ":6060"
|
#bind-debug: ":6060"
|
||||||
|
|
||||||
# Bind the Prometheus metrics onto /metrics path on this port
|
# Bind the Prometheus metrics onto /metrics path on this port
|
||||||
#bind-metrics: ":9090"
|
#bind-metrics ":9090"
|
||||||
|
|
||||||
# These links will be shown on the presented challenge or error pages
|
# These links will be shown on the presented challenge or error pages
|
||||||
links:
|
links:
|
||||||
|
|||||||
@@ -104,15 +104,6 @@ rules:
|
|||||||
- *is-bot-yandexbot
|
- *is-bot-yandexbot
|
||||||
action: pass
|
action: pass
|
||||||
|
|
||||||
# Matches private networks and localhost.
|
|
||||||
# Uncomment this if you want to let your own tools this way
|
|
||||||
# - name: allow-private-networks
|
|
||||||
# conditions:
|
|
||||||
# # Allows localhost and private networks CIDR
|
|
||||||
# - *is-network-localhost
|
|
||||||
# - *is-network-private
|
|
||||||
# action: pass
|
|
||||||
|
|
||||||
- name: undesired-networks
|
- name: undesired-networks
|
||||||
conditions:
|
conditions:
|
||||||
- 'remoteAddress.network("huawei-cloud") || remoteAddress.network("alibaba-cloud") || remoteAddress.network("zenlayer-inc")'
|
- 'remoteAddress.network("huawei-cloud") || remoteAddress.network("alibaba-cloud") || remoteAddress.network("zenlayer-inc")'
|
||||||
@@ -296,7 +287,7 @@ rules:
|
|||||||
# Map OpenGraph or similar <meta> tags back to the reply, even if denied/challenged
|
# Map OpenGraph or similar <meta> tags back to the reply, even if denied/challenged
|
||||||
proxy-meta-tags: "true"
|
proxy-meta-tags: "true"
|
||||||
# proxy-safe-link-tags: "true"
|
# proxy-safe-link-tags: "true"
|
||||||
|
|
||||||
# Set additional response headers
|
# Set additional response headers
|
||||||
#response-headers:
|
#response-headers:
|
||||||
# X-Clacks-Overhead:
|
# X-Clacks-Overhead:
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ networks:
|
|||||||
|
|
||||||
challenges:
|
challenges:
|
||||||
# Challenges will get included from snippets
|
# Challenges will get included from snippets
|
||||||
|
|
||||||
conditions:
|
conditions:
|
||||||
# Conditions will get replaced on rules AST when found as ($condition-name)
|
# Conditions will get replaced on rules AST when found as ($condition-name)
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ conditions:
|
|||||||
# Old IE browsers
|
# Old IE browsers
|
||||||
- 'userAgent.matches("MSIE ([2-9]|10|11)\\.")'
|
- 'userAgent.matches("MSIE ([2-9]|10|11)\\.")'
|
||||||
# Old Linux browsers
|
# Old Linux browsers
|
||||||
- 'userAgent.matches("Linux i[63]86") || userAgent.matches("FreeBSD i[63]86")'
|
- 'userAgent.contains("Linux i[63]86") || userAgent.contains("FreeBSD i[63]86")'
|
||||||
# Old Windows browsers
|
# Old Windows browsers
|
||||||
- 'userAgent.matches("Windows (3|95|98|CE)") || userAgent.matches("Windows NT [1-5]\\.")'
|
- 'userAgent.matches("Windows (3|95|98|CE)") || userAgent.matches("Windows NT [1-5]\\.")'
|
||||||
# Old mobile browsers
|
# Old mobile browsers
|
||||||
@@ -60,15 +60,6 @@ rules:
|
|||||||
- *is-bot-yandexbot
|
- *is-bot-yandexbot
|
||||||
action: pass
|
action: pass
|
||||||
|
|
||||||
# Matches private networks and localhost.
|
|
||||||
# Uncomment this if you want to let your own tools this way
|
|
||||||
# - name: allow-private-networks
|
|
||||||
# conditions:
|
|
||||||
# # Allows localhost and private networks CIDR
|
|
||||||
# - *is-network-localhost
|
|
||||||
# - *is-network-private
|
|
||||||
# action: pass
|
|
||||||
|
|
||||||
- name: undesired-crawlers
|
- name: undesired-crawlers
|
||||||
conditions:
|
conditions:
|
||||||
- '($is-headless-chromium)'
|
- '($is-headless-chromium)'
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
networks:
|
|
||||||
localhost:
|
|
||||||
# localhost and loopback addresses
|
|
||||||
- prefixes:
|
|
||||||
- "127.0.0.0/8"
|
|
||||||
- "::1/128"
|
|
||||||
private:
|
|
||||||
# Private network CIDR blocks
|
|
||||||
- prefixes:
|
|
||||||
# private networks
|
|
||||||
- "10.0.0.0/8"
|
|
||||||
- "172.16.0.0/12"
|
|
||||||
- "192.168.0.0/16"
|
|
||||||
- "fc00::/7"
|
|
||||||
# CGNAT
|
|
||||||
- "100.64.0.0/10"
|
|
||||||
|
|
||||||
conditions:
|
|
||||||
is-network-localhost:
|
|
||||||
- &is-network-localhost 'remoteAddress.network("localhost")'
|
|
||||||
is-network-private:
|
|
||||||
- &is-network-private 'remoteAddress.network("private")'
|
|
||||||
36
go.mod
36
go.mod
@@ -1,14 +1,12 @@
|
|||||||
module git.gammaspectra.live/git/go-away
|
module git.gammaspectra.live/git/go-away
|
||||||
|
|
||||||
go 1.24.0
|
go 1.22.12
|
||||||
|
|
||||||
toolchain go1.24.2
|
|
||||||
|
|
||||||
require (
|
require (
|
||||||
codeberg.org/gone/http-cel v1.0.0
|
codeberg.org/gone/http-cel v1.0.0
|
||||||
codeberg.org/meta/gzipped/v2 v2.0.0-20231111234332-aa70c3194756
|
codeberg.org/meta/gzipped/v2 v2.0.0-20231111234332-aa70c3194756
|
||||||
github.com/alphadose/haxmap v1.4.1
|
github.com/alphadose/haxmap v1.4.1
|
||||||
github.com/go-jose/go-jose/v4 v4.1.0
|
github.com/go-jose/go-jose/v4 v4.0.5
|
||||||
github.com/goccy/go-yaml v1.17.1
|
github.com/goccy/go-yaml v1.17.1
|
||||||
github.com/google/cel-go v0.25.0
|
github.com/google/cel-go v0.25.0
|
||||||
github.com/itchyny/gojq v0.12.17
|
github.com/itchyny/gojq v0.12.17
|
||||||
@@ -16,7 +14,8 @@ require (
|
|||||||
github.com/prometheus/client_golang v1.22.0
|
github.com/prometheus/client_golang v1.22.0
|
||||||
github.com/tetratelabs/wazero v1.9.0
|
github.com/tetratelabs/wazero v1.9.0
|
||||||
github.com/yl2chen/cidranger v1.0.2
|
github.com/yl2chen/cidranger v1.0.2
|
||||||
golang.org/x/crypto v0.37.0
|
golang.org/x/crypto v0.33.0
|
||||||
|
golang.org/x/net v0.37.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -29,13 +28,26 @@ require (
|
|||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/prometheus/client_model v0.6.2 // indirect
|
github.com/prometheus/client_model v0.6.2 // indirect
|
||||||
github.com/prometheus/common v0.63.0 // indirect
|
github.com/prometheus/common v0.63.0 // indirect
|
||||||
github.com/prometheus/procfs v0.16.1 // indirect
|
github.com/prometheus/procfs v0.15.1 // indirect
|
||||||
github.com/stoewer/go-strcase v1.3.0 // indirect
|
github.com/stoewer/go-strcase v1.3.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
|
golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac // indirect
|
||||||
golang.org/x/net v0.39.0 // indirect
|
golang.org/x/sys v0.30.0 // indirect
|
||||||
golang.org/x/sys v0.32.0 // indirect
|
golang.org/x/text v0.22.0 // indirect
|
||||||
golang.org/x/text v0.24.0 // indirect
|
google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7 // indirect
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20250422160041-2d3770c4ea7f // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250422160041-2d3770c4ea7f // indirect
|
|
||||||
google.golang.org/protobuf v1.36.6 // indirect
|
google.golang.org/protobuf v1.36.6 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Pin latest versions to support Go 1.22 to prevent a package update from changing them
|
||||||
|
// TODO: remove this when Go 1.22+ is supported by other downstream users
|
||||||
|
replace (
|
||||||
|
github.com/go-jose/go-jose/v4 => github.com/go-jose/go-jose/v4 v4.0.5
|
||||||
|
github.com/prometheus/procfs => github.com/prometheus/procfs v0.15.1
|
||||||
|
golang.org/x/crypto => golang.org/x/crypto v0.33.0
|
||||||
|
golang.org/x/exp => golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac
|
||||||
|
golang.org/x/net => golang.org/x/net v0.35.0
|
||||||
|
golang.org/x/sys => golang.org/x/sys v0.30.0
|
||||||
|
golang.org/x/text => golang.org/x/text v0.22.0
|
||||||
|
google.golang.org/genproto/googleapis/api => google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7
|
||||||
|
google.golang.org/genproto/googleapis/rpc => google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7
|
||||||
|
)
|
||||||
|
|||||||
36
go.sum
36
go.sum
@@ -15,8 +15,8 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
|
|||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/go-jose/go-jose/v4 v4.1.0 h1:cYSYxd3pw5zd2FSXk2vGdn9igQU2PS8MuxrCOCl0FdY=
|
github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
|
||||||
github.com/go-jose/go-jose/v4 v4.1.0/go.mod h1:GG/vqmYm3Von2nYiB2vGTXzdoNKE5tix5tuc6iAd+sw=
|
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
|
||||||
github.com/goccy/go-yaml v1.17.1 h1:LI34wktB2xEE3ONG/2Ar54+/HJVBriAGJ55PHls4YuY=
|
github.com/goccy/go-yaml v1.17.1 h1:LI34wktB2xEE3ONG/2Ar54+/HJVBriAGJ55PHls4YuY=
|
||||||
github.com/goccy/go-yaml v1.17.1/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
github.com/goccy/go-yaml v1.17.1/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||||
github.com/google/cel-go v0.25.0 h1:jsFw9Fhn+3y2kBbltZR4VEz5xKkcIFRPDnuEzAGv5GY=
|
github.com/google/cel-go v0.25.0 h1:jsFw9Fhn+3y2kBbltZR4VEz5xKkcIFRPDnuEzAGv5GY=
|
||||||
@@ -45,8 +45,8 @@ github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNw
|
|||||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||||
github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k=
|
github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k=
|
||||||
github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18=
|
github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18=
|
||||||
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
|
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||||
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
|
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||||
github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
|
github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
|
||||||
github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
|
github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
@@ -62,20 +62,20 @@ github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZB
|
|||||||
github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM=
|
github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM=
|
||||||
github.com/yl2chen/cidranger v1.0.2 h1:lbOWZVCG1tCRX4u24kuM1Tb4nHqWkDxwLdoS+SevawU=
|
github.com/yl2chen/cidranger v1.0.2 h1:lbOWZVCG1tCRX4u24kuM1Tb4nHqWkDxwLdoS+SevawU=
|
||||||
github.com/yl2chen/cidranger v1.0.2/go.mod h1:9U1yz7WPYDwf0vpNWFaeRh0bjwz5RVgRy/9UEQfHl0g=
|
github.com/yl2chen/cidranger v1.0.2/go.mod h1:9U1yz7WPYDwf0vpNWFaeRh0bjwz5RVgRy/9UEQfHl0g=
|
||||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
||||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
|
golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac h1:l5+whBCLH3iH2ZNHYLbAe58bo7yrN4mVcnkHDYz5vvs=
|
||||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
|
golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac/go.mod h1:hH+7mtFmImwwcMvScyxUhjuVHR3HGaDPMn9rMSUUbxo=
|
||||||
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20250422160041-2d3770c4ea7f h1:tjZsroqekhC63+WMqzmWyW5Twj/ZfR5HAlpd5YQ1Vs0=
|
google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7 h1:YcyjlL1PRr2Q17/I0dPk2JmYS5CDXfcdb2Z3YRioEbw=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20250422160041-2d3770c4ea7f/go.mod h1:Cd8IzgPo5Akum2c9R6FsXNaZbH3Jpa2gpHlW89FqlyQ=
|
google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:OCdP9MfskevB/rbYvHTsXTtKC+3bHWajPdoKgjcYkfo=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250422160041-2d3770c4ea7f h1:N/PrbTw4kdkqNRzVfWPrBekzLuarFREcbFOiOLkXon4=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 h1:2035KHhUv+EpyB+hWgJnaWKJOdX1E95w2S8Rr4uWKTs=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250422160041-2d3770c4ea7f/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
|
||||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
|||||||
@@ -28,9 +28,7 @@ func (a Block) Handle(logger *slog.Logger, w http.ResponseWriter, r *http.Reques
|
|||||||
data := challenge.RequestDataFromContext(r.Context())
|
data := challenge.RequestDataFromContext(r.Context())
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "text/plain")
|
w.Header().Set("Content-Type", "text/plain")
|
||||||
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
|
||||||
w.Header().Set("Connection", "close")
|
w.Header().Set("Connection", "close")
|
||||||
|
|
||||||
data.ResponseHeaders(w)
|
data.ResponseHeaders(w)
|
||||||
w.WriteHeader(a.Code)
|
w.WriteHeader(a.Code)
|
||||||
_, _ = w.Write([]byte(fmt.Errorf("access blocked: blocked by administrative rule %s/%s", data.Id.String(), a.RuleHash).Error()))
|
_, _ = w.Write([]byte(fmt.Errorf("access blocked: blocked by administrative rule %s/%s", data.Id.String(), a.RuleHash).Error()))
|
||||||
|
|||||||
@@ -42,11 +42,7 @@ type CodeSettings struct {
|
|||||||
type Code int
|
type Code int
|
||||||
|
|
||||||
func (a Code) Handle(logger *slog.Logger, w http.ResponseWriter, r *http.Request, done func() (backend http.Handler)) (next bool, err error) {
|
func (a Code) Handle(logger *slog.Logger, w http.ResponseWriter, r *http.Request, done func() (backend http.Handler)) (next bool, err error) {
|
||||||
data := challenge.RequestDataFromContext(r.Context())
|
challenge.RequestDataFromContext(r.Context()).ResponseHeaders(w)
|
||||||
|
|
||||||
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
|
||||||
|
|
||||||
data.ResponseHeaders(w)
|
|
||||||
|
|
||||||
w.WriteHeader(int(a))
|
w.WriteHeader(int(a))
|
||||||
return false, nil
|
return false, nil
|
||||||
|
|||||||
@@ -33,8 +33,6 @@ func (a Drop) Handle(logger *slog.Logger, w http.ResponseWriter, r *http.Request
|
|||||||
w.Header().Set("Content-Type", "text/plain")
|
w.Header().Set("Content-Type", "text/plain")
|
||||||
w.Header().Set("Content-Length", "0")
|
w.Header().Set("Content-Length", "0")
|
||||||
w.Header().Set("Connection", "close")
|
w.Header().Set("Connection", "close")
|
||||||
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
|
||||||
|
|
||||||
w.WriteHeader(http.StatusForbidden)
|
w.WriteHeader(http.StatusForbidden)
|
||||||
|
|
||||||
return false, nil
|
return false, nil
|
||||||
|
|||||||
@@ -295,8 +295,8 @@ func (d *RequestData) EvaluateChallenges(w http.ResponseWriter, r *http.Request)
|
|||||||
challengeMap, err := d.verifyChallengeState()
|
challengeMap, err := d.verifyChallengeState()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errors.Is(err, http.ErrNoCookie) {
|
if !errors.Is(err, http.ErrNoCookie) {
|
||||||
//queue resend invalid cookie and continue
|
//clear invalid cookie and continue
|
||||||
d.challengeMapModified = true
|
utils.ClearCookie(d.cookieName, w, r)
|
||||||
}
|
}
|
||||||
challengeMap = make(TokenChallengeMap)
|
challengeMap = make(TokenChallengeMap)
|
||||||
}
|
}
|
||||||
@@ -329,9 +329,6 @@ func (d *RequestData) ResponseHeaders(w http.ResponseWriter) {
|
|||||||
//w.Header().Set("Accept-CH", "Sec-CH-UA, Sec-CH-UA-Platform")
|
//w.Header().Set("Accept-CH", "Sec-CH-UA, Sec-CH-UA-Platform")
|
||||||
//w.Header().Set("Critical-CH", "Sec-CH-UA, Sec-CH-UA-Platform")
|
//w.Header().Set("Critical-CH", "Sec-CH-UA, Sec-CH-UA-Platform")
|
||||||
|
|
||||||
// send Vary header to mark that response may vary based on Cookie values and other client headers
|
|
||||||
w.Header().Set("Vary", "Cookie, Accept, Accept-Encoding, Accept-Language, User-Agent")
|
|
||||||
|
|
||||||
if d.State.Settings().MainName != "" {
|
if d.State.Settings().MainName != "" {
|
||||||
w.Header().Add("Via", fmt.Sprintf("%s %s@%s", d.r.Proto, d.State.Settings().MainName, d.State.Settings().MainVersion))
|
w.Header().Add("Via", fmt.Sprintf("%s %s@%s", d.r.Proto, d.State.Settings().MainName, d.State.Settings().MainVersion))
|
||||||
}
|
}
|
||||||
@@ -368,7 +365,7 @@ func (d *RequestData) RequestHeaders(headers http.Header) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ja4, ok := d.fp["ja4"]; ok {
|
if ja4, ok := d.fp["fp4"]; ok {
|
||||||
headers.Set("X-TLS-Fingerprint-JA4", ja4)
|
headers.Set("X-TLS-Fingerprint-JA4", ja4)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -436,7 +433,13 @@ func (d *RequestData) verifyChallengeStateCookie(cookie *http.Cookie) (TokenChal
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *RequestData) verifyChallengeState() (state TokenChallengeMap, err error) {
|
func (d *RequestData) verifyChallengeState() (state TokenChallengeMap, err error) {
|
||||||
cookies := d.r.CookiesNamed(d.cookieName)
|
var cookies []*http.Cookie
|
||||||
|
for _, cookie := range d.r.Cookies() {
|
||||||
|
if cookie.Name == d.cookieName {
|
||||||
|
cookies = append(cookies, cookie)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if len(cookies) == 0 {
|
if len(cookies) == 0 {
|
||||||
return nil, http.ErrNoCookie
|
return nil, http.ErrNoCookie
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,9 +8,7 @@ import (
|
|||||||
"git.gammaspectra.live/git/go-away/utils"
|
"git.gammaspectra.live/git/go-away/utils"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrInvalidToken = errors.New("invalid token")
|
var ErrInvalidToken = errors.New("invalid token")
|
||||||
@@ -49,7 +47,6 @@ const (
|
|||||||
QueryArgRequestId = QueryArgPrefix + "_id"
|
QueryArgRequestId = QueryArgPrefix + "_id"
|
||||||
QueryArgChallenge = QueryArgPrefix + "_challenge"
|
QueryArgChallenge = QueryArgPrefix + "_challenge"
|
||||||
QueryArgToken = QueryArgPrefix + "_token"
|
QueryArgToken = QueryArgPrefix + "_token"
|
||||||
QueryArgBust = QueryArgPrefix + "_bust"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const MakeChallengeUrlSuffix = "/make-challenge"
|
const MakeChallengeUrlSuffix = "/make-challenge"
|
||||||
@@ -94,13 +91,12 @@ func VerifyUrl(r *http.Request, reg *Registration, token string) (*url.URL, erro
|
|||||||
uri.Path = reg.Path + VerifyChallengeUrlSuffix
|
uri.Path = reg.Path + VerifyChallengeUrlSuffix
|
||||||
|
|
||||||
data := RequestDataFromContext(r.Context())
|
data := RequestDataFromContext(r.Context())
|
||||||
values, _ := utils.ParseRawQuery(r.URL.RawQuery)
|
values := uri.Query()
|
||||||
values.Set(QueryArgRequestId, url.QueryEscape(data.Id.String()))
|
values.Set(QueryArgRequestId, data.Id.String())
|
||||||
values.Set(QueryArgRedirect, url.QueryEscape(redirectUrl.String()))
|
values.Set(QueryArgRedirect, redirectUrl.String())
|
||||||
values.Set(QueryArgToken, url.QueryEscape(token))
|
values.Set(QueryArgToken, token)
|
||||||
values.Set(QueryArgChallenge, url.QueryEscape(reg.Name))
|
values.Set(QueryArgChallenge, reg.Name)
|
||||||
values.Set(QueryArgBust, url.QueryEscape(strconv.FormatInt(time.Now().UTC().UnixMilli(), 10)))
|
uri.RawQuery = values.Encode()
|
||||||
uri.RawQuery = utils.EncodeRawQuery(values)
|
|
||||||
|
|
||||||
return uri, nil
|
return uri, nil
|
||||||
}
|
}
|
||||||
@@ -112,13 +108,13 @@ func RedirectUrl(r *http.Request, reg *Registration) (*url.URL, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
data := RequestDataFromContext(r.Context())
|
data := RequestDataFromContext(r.Context())
|
||||||
values, _ := utils.ParseRawQuery(r.URL.RawQuery)
|
values := uri.Query()
|
||||||
values.Set(QueryArgRequestId, url.QueryEscape(data.Id.String()))
|
values.Set(QueryArgRequestId, data.Id.String())
|
||||||
if ref := r.Referer(); ref != "" {
|
if ref := r.Referer(); ref != "" {
|
||||||
values.Set(QueryArgReferer, url.QueryEscape(r.Referer()))
|
values.Set(QueryArgReferer, r.Referer())
|
||||||
}
|
}
|
||||||
values.Set(QueryArgChallenge, url.QueryEscape(reg.Name))
|
values.Set(QueryArgChallenge, reg.Name)
|
||||||
uri.RawQuery = utils.EncodeRawQuery(values)
|
uri.RawQuery = values.Encode()
|
||||||
|
|
||||||
return uri, nil
|
return uri, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,13 +52,15 @@ func GetChallengeKeyForRequest(state StateInterface, reg *Registration, until ti
|
|||||||
hasher.Write([]byte{0})
|
hasher.Write([]byte{0})
|
||||||
|
|
||||||
// specific headers
|
// specific headers
|
||||||
for _, k := range reg.KeyHeaders {
|
for _, k := range []string{
|
||||||
hasher.Write([]byte(k))
|
"Accept-Language",
|
||||||
hasher.Write([]byte{0})
|
// General browser information
|
||||||
for _, v := range r.Header.Values(k) {
|
"User-Agent",
|
||||||
hasher.Write([]byte(v))
|
// TODO: not sent in preload
|
||||||
hasher.Write([]byte{1})
|
//"Sec-Ch-Ua",
|
||||||
}
|
//"Sec-Ch-Ua-Platform",
|
||||||
|
} {
|
||||||
|
hasher.Write([]byte(r.Header.Get(k)))
|
||||||
hasher.Write([]byte{0})
|
hasher.Write([]byte{0})
|
||||||
}
|
}
|
||||||
hasher.Write([]byte{0})
|
hasher.Write([]byte{0})
|
||||||
|
|||||||
@@ -44,9 +44,6 @@ func FillRegistration(state challenge.StateInterface, reg *challenge.Registratio
|
|||||||
|
|
||||||
reg.Class = challenge.ClassTransparent
|
reg.Class = challenge.ClassTransparent
|
||||||
|
|
||||||
// some of regular headers are not sent in default headers
|
|
||||||
reg.KeyHeaders = challenge.MinimalKeyHeaders
|
|
||||||
|
|
||||||
ob := challenge.NewAwaiter[string]()
|
ob := challenge.NewAwaiter[string]()
|
||||||
|
|
||||||
reg.Object = ob
|
reg.Object = ob
|
||||||
@@ -69,9 +66,9 @@ func FillRegistration(state challenge.StateInterface, reg *challenge.Registratio
|
|||||||
}
|
}
|
||||||
|
|
||||||
// remove redirect args
|
// remove redirect args
|
||||||
values, _ := utils.ParseRawQuery(uri.RawQuery)
|
values := uri.Query()
|
||||||
values.Del(challenge.QueryArgRedirect)
|
values.Del(challenge.QueryArgRedirect)
|
||||||
uri.RawQuery = utils.EncodeRawQuery(values)
|
uri.RawQuery = values.Encode()
|
||||||
|
|
||||||
// Redirect URI must be absolute to work
|
// Redirect URI must be absolute to work
|
||||||
uri.Scheme = utils.GetRequestScheme(r)
|
uri.Scheme = utils.GetRequestScheme(r)
|
||||||
@@ -101,7 +98,6 @@ func FillRegistration(state challenge.StateInterface, reg *challenge.Registratio
|
|||||||
|
|
||||||
mux.HandleFunc("GET "+reg.Path+challenge.VerifyChallengeUrlSuffix, func(w http.ResponseWriter, r *http.Request) {
|
mux.HandleFunc("GET "+reg.Path+challenge.VerifyChallengeUrlSuffix, func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", "text/css; charset=utf-8")
|
w.Header().Set("Content-Type", "text/css; charset=utf-8")
|
||||||
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
|
||||||
w.Header().Set("Content-Length", "0")
|
w.Header().Set("Content-Length", "0")
|
||||||
|
|
||||||
data := challenge.RequestDataFromContext(r.Context())
|
data := challenge.RequestDataFromContext(r.Context())
|
||||||
|
|||||||
@@ -35,24 +35,6 @@ var idCounter Id
|
|||||||
// DefaultDuration TODO: adjust
|
// DefaultDuration TODO: adjust
|
||||||
const DefaultDuration = time.Hour * 24 * 7
|
const DefaultDuration = time.Hour * 24 * 7
|
||||||
|
|
||||||
var DefaultKeyHeaders = []string{
|
|
||||||
// General browser information
|
|
||||||
"User-Agent",
|
|
||||||
// Accept headers
|
|
||||||
"Accept-Language",
|
|
||||||
"Accept-Encoding",
|
|
||||||
|
|
||||||
// NOTE: not sent in preload
|
|
||||||
"Sec-Ch-Ua",
|
|
||||||
"Sec-Ch-Ua-Platform",
|
|
||||||
}
|
|
||||||
|
|
||||||
var MinimalKeyHeaders = []string{
|
|
||||||
"Accept-Language",
|
|
||||||
// General browser information
|
|
||||||
"User-Agent",
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r Register) Create(state StateInterface, name string, pol policy.Challenge, replacer *strings.Replacer) (*Registration, Id, error) {
|
func (r Register) Create(state StateInterface, name string, pol policy.Challenge, replacer *strings.Replacer) (*Registration, Id, error) {
|
||||||
runtime, ok := Runtimes[pol.Runtime]
|
runtime, ok := Runtimes[pol.Runtime]
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -60,10 +42,9 @@ func (r Register) Create(state StateInterface, name string, pol policy.Challenge
|
|||||||
}
|
}
|
||||||
|
|
||||||
reg := &Registration{
|
reg := &Registration{
|
||||||
Name: name,
|
Name: name,
|
||||||
Path: path.Join(state.UrlPath(), "challenge", name),
|
Path: path.Join(state.UrlPath(), "challenge", name),
|
||||||
Duration: pol.Duration,
|
Duration: pol.Duration,
|
||||||
KeyHeaders: DefaultKeyHeaders,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if reg.Duration == 0 {
|
if reg.Duration == 0 {
|
||||||
@@ -145,9 +126,6 @@ type Registration struct {
|
|||||||
Verify VerifyFunc
|
Verify VerifyFunc
|
||||||
VerifyProbability float64
|
VerifyProbability float64
|
||||||
|
|
||||||
// KeyHeaders The client headers used in key generation, in this order
|
|
||||||
KeyHeaders []string
|
|
||||||
|
|
||||||
// IssueChallenge Issues a challenge to a request.
|
// IssueChallenge Issues a challenge to a request.
|
||||||
// If Class is ClassTransparent and VerifyResult is !VerifyResult.Ok(), continue with other challenges
|
// If Class is ClassTransparent and VerifyResult is !VerifyResult.Ok(), continue with other challenges
|
||||||
// TODO: have this return error as well
|
// TODO: have this return error as well
|
||||||
|
|||||||
@@ -23,13 +23,9 @@ func FillRegistrationHeader(state challenge.StateInterface, reg *challenge.Regis
|
|||||||
return challenge.VerifyResultFail
|
return challenge.VerifyResultFail
|
||||||
}
|
}
|
||||||
|
|
||||||
redirectUri, err := challenge.RedirectUrl(r, reg)
|
|
||||||
if err != nil {
|
|
||||||
return challenge.VerifyResultFail
|
|
||||||
}
|
|
||||||
// self redirect!
|
// self redirect!
|
||||||
//TODO: adjust deadline
|
//TODO: adjust deadline
|
||||||
w.Header().Set("Refresh", "2; url="+redirectUri.String())
|
w.Header().Set("Refresh", "2; url="+r.URL.String())
|
||||||
|
|
||||||
state.ChallengePage(w, r, state.Settings().ChallengeResponseCode, reg, map[string]any{
|
state.ChallengePage(w, r, state.Settings().ChallengeResponseCode, reg, map[string]any{
|
||||||
"LinkTags": []map[string]string{
|
"LinkTags": []map[string]string{
|
||||||
@@ -48,7 +44,6 @@ func FillRegistrationHeader(state challenge.StateInterface, reg *challenge.Regis
|
|||||||
mux.HandleFunc("GET "+reg.Path+challenge.VerifyChallengeUrlSuffix, challenge.VerifyHandlerFunc(state, reg, nil, func(state challenge.StateInterface, data *challenge.RequestData, w http.ResponseWriter, r *http.Request, verifyResult challenge.VerifyResult, err error, redirect string) {
|
mux.HandleFunc("GET "+reg.Path+challenge.VerifyChallengeUrlSuffix, challenge.VerifyHandlerFunc(state, reg, nil, func(state challenge.StateInterface, data *challenge.RequestData, w http.ResponseWriter, r *http.Request, verifyResult challenge.VerifyResult, err error, redirect string) {
|
||||||
//TODO: add other types inside css that need to be loaded!
|
//TODO: add other types inside css that need to be loaded!
|
||||||
w.Header().Set("Content-Type", "text/css; charset=utf-8")
|
w.Header().Set("Content-Type", "text/css; charset=utf-8")
|
||||||
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
|
||||||
w.Header().Set("Content-Length", "0")
|
w.Header().Set("Content-Length", "0")
|
||||||
|
|
||||||
data.ResponseHeaders(w)
|
data.ResponseHeaders(w)
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ func ServeChallengeScript(w http.ResponseWriter, r *http.Request, reg *Registrat
|
|||||||
//TODO: log
|
//TODO: log
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
data.ResponseHeaders(w)
|
data.ResponseHeaders(w)
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
|
|
||||||
@@ -31,7 +30,7 @@ func ServeChallengeScript(w http.ResponseWriter, r *http.Request, reg *Registrat
|
|||||||
"Id": data.Id.String(),
|
"Id": data.Id.String(),
|
||||||
"Path": reg.Path,
|
"Path": reg.Path,
|
||||||
"Parameters": paramData,
|
"Parameters": paramData,
|
||||||
"Random": utils.StaticCacheBust(),
|
"Random": utils.CacheBust(),
|
||||||
"Challenge": reg.Name,
|
"Challenge": reg.Name,
|
||||||
"ChallengeScript": script,
|
"ChallengeScript": script,
|
||||||
"Strings": data.State.Strings(),
|
"Strings": data.State.Strings(),
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ func FillJavaScriptRegistration(state challenge.StateInterface, reg *challenge.R
|
|||||||
reg.IssueChallenge = func(w http.ResponseWriter, r *http.Request, key challenge.Key, expiry time.Time) challenge.VerifyResult {
|
reg.IssueChallenge = func(w http.ResponseWriter, r *http.Request, key challenge.Key, expiry time.Time) challenge.VerifyResult {
|
||||||
state.ChallengePage(w, r, state.Settings().ChallengeResponseCode, reg, map[string]any{
|
state.ChallengePage(w, r, state.Settings().ChallengeResponseCode, reg, map[string]any{
|
||||||
"EndTags": []template.HTML{
|
"EndTags": []template.HTML{
|
||||||
template.HTML(fmt.Sprintf("<script async type=\"module\" src=\"%s?cacheBust=%s\"></script>", reg.Path+"/script.mjs", utils.StaticCacheBust())),
|
template.HTML(fmt.Sprintf("<script async type=\"module\" src=\"%s?cacheBust=%s\"></script>", reg.Path+"/script.mjs", utils.CacheBust())),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
return challenge.VerifyResultNone
|
return challenge.VerifyResultNone
|
||||||
@@ -164,9 +164,6 @@ func FillJavaScriptRegistration(state challenge.StateInterface, reg *challenge.R
|
|||||||
w.Header()[k] = v
|
w.Header()[k] = v
|
||||||
}
|
}
|
||||||
w.Header().Set("Content-Length", fmt.Sprintf("%d", len(out.Data)))
|
w.Header().Set("Content-Length", fmt.Sprintf("%d", len(out.Data)))
|
||||||
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
|
||||||
|
|
||||||
data.ResponseHeaders(w)
|
|
||||||
w.WriteHeader(out.Code)
|
w.WriteHeader(out.Code)
|
||||||
_, _ = w.Write(out.Data)
|
_, _ = w.Write(out.Data)
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -246,20 +246,19 @@ func (state *State) handleRequest(w http.ResponseWriter, r *http.Request) {
|
|||||||
if fromChallenge {
|
if fromChallenge {
|
||||||
r.Header.Del("Referer")
|
r.Header.Del("Referer")
|
||||||
}
|
}
|
||||||
|
|
||||||
q := r.URL.Query()
|
q := r.URL.Query()
|
||||||
|
|
||||||
if ref := q.Get(challenge.QueryArgReferer); ref != "" {
|
if ref := q.Get(challenge.QueryArgReferer); ref != "" {
|
||||||
r.Header.Set("Referer", ref)
|
r.Header.Set("Referer", ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
rawQ, _ := utils.ParseRawQuery(r.URL.RawQuery)
|
|
||||||
// delete query parameters that were set by go-away
|
// delete query parameters that were set by go-away
|
||||||
for k := range rawQ {
|
for k := range q {
|
||||||
if strings.HasPrefix(k, challenge.QueryArgPrefix) {
|
if strings.HasPrefix(k, challenge.QueryArgPrefix) {
|
||||||
rawQ.Del(k)
|
q.Del(k)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
r.URL.RawQuery = utils.EncodeRawQuery(rawQ)
|
r.URL.RawQuery = q.Encode()
|
||||||
|
|
||||||
data.ExtraHeaders.Set("X-Away-Rule", ruleName)
|
data.ExtraHeaders.Set("X-Away-Rule", ruleName)
|
||||||
data.ExtraHeaders.Set("X-Away-Action", string(ruleAction))
|
data.ExtraHeaders.Set("X-Away-Action", string(ruleAction))
|
||||||
|
|||||||
@@ -114,9 +114,9 @@ func NewState(p policy.Policy, opt settings.Settings, settings policy.StateSetti
|
|||||||
return nil, fmt.Errorf("error loading template %s: %w", state.opt.ChallengeTemplate, err)
|
return nil, fmt.Errorf("error loading template %s: %w", state.opt.ChallengeTemplate, err)
|
||||||
}
|
}
|
||||||
state.opt.ChallengeTemplate = name
|
state.opt.ChallengeTemplate = name
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("no template defined for %s", state.opt.ChallengeTemplate)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("no template defined for %s", state.opt.ChallengeTemplate)
|
||||||
}
|
}
|
||||||
|
|
||||||
state.networks = make(map[string]func() cidranger.Ranger)
|
state.networks = make(map[string]func() cidranger.Ranger)
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ func (state *State) ChallengePage(w http.ResponseWriter, r *http.Request, status
|
|||||||
data := challenge.RequestDataFromContext(r.Context())
|
data := challenge.RequestDataFromContext(r.Context())
|
||||||
input := make(map[string]any)
|
input := make(map[string]any)
|
||||||
input["Id"] = data.Id.String()
|
input["Id"] = data.Id.String()
|
||||||
input["Random"] = utils.StaticCacheBust()
|
input["Random"] = utils.CacheBust()
|
||||||
|
|
||||||
input["Path"] = state.UrlPath()
|
input["Path"] = state.UrlPath()
|
||||||
input["Links"] = state.opt.Links
|
input["Links"] = state.opt.Links
|
||||||
@@ -100,7 +100,6 @@ func (state *State) ChallengePage(w http.ResponseWriter, r *http.Request, status
|
|||||||
state.addCachedTags(data, r, input)
|
state.addCachedTags(data, r, input)
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
|
||||||
|
|
||||||
buf := bytes.NewBuffer(make([]byte, 0, 8192))
|
buf := bytes.NewBuffer(make([]byte, 0, 8192))
|
||||||
|
|
||||||
@@ -117,13 +116,12 @@ func (state *State) ChallengePage(w http.ResponseWriter, r *http.Request, status
|
|||||||
func (state *State) ErrorPage(w http.ResponseWriter, r *http.Request, status int, err error, redirect string) {
|
func (state *State) ErrorPage(w http.ResponseWriter, r *http.Request, status int, err error, redirect string) {
|
||||||
data := challenge.RequestDataFromContext(r.Context())
|
data := challenge.RequestDataFromContext(r.Context())
|
||||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
|
||||||
|
|
||||||
buf := bytes.NewBuffer(make([]byte, 0, 8192))
|
buf := bytes.NewBuffer(make([]byte, 0, 8192))
|
||||||
|
|
||||||
input := map[string]any{
|
input := map[string]any{
|
||||||
"Id": data.Id.String(),
|
"Id": data.Id.String(),
|
||||||
"Random": utils.StaticCacheBust(),
|
"Random": utils.CacheBust(),
|
||||||
"Error": err.Error(),
|
"Error": err.Error(),
|
||||||
"Path": state.UrlPath(),
|
"Path": state.UrlPath(),
|
||||||
"Theme": "",
|
"Theme": "",
|
||||||
|
|||||||
@@ -1,48 +1,13 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"crypto/sha256"
|
|
||||||
"crypto/tls"
|
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"slices"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
)
|
)
|
||||||
|
|
||||||
func applyTLSFingerprinter(server *http.Server) {
|
|
||||||
if server.TLSConfig == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
server.TLSConfig = server.TLSConfig.Clone()
|
|
||||||
|
|
||||||
getConfigForClient := server.TLSConfig.GetConfigForClient
|
|
||||||
|
|
||||||
if getConfigForClient == nil {
|
|
||||||
getConfigForClient = func(info *tls.ClientHelloInfo) (*tls.Config, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
server.TLSConfig.GetConfigForClient = func(clientHello *tls.ClientHelloInfo) (*tls.Config, error) {
|
|
||||||
ja3n, ja4 := buildTLSFingerprint(clientHello)
|
|
||||||
ptr := clientHello.Context().Value(tlsFingerprintKey{})
|
|
||||||
if fpPtr, ok := ptr.(*TLSFingerprint); ok && ptr != nil && fpPtr != nil {
|
|
||||||
fpPtr.ja3n.Store(&ja3n)
|
|
||||||
fpPtr.ja4.Store(&ja4)
|
|
||||||
}
|
|
||||||
return getConfigForClient(clientHello)
|
|
||||||
}
|
|
||||||
server.ConnContext = func(ctx context.Context, c net.Conn) context.Context {
|
|
||||||
return context.WithValue(ctx, tlsFingerprintKey{}, &TLSFingerprint{})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type tlsFingerprintKey struct{}
|
type tlsFingerprintKey struct{}
|
||||||
type TLSFingerprint struct {
|
type TLSFingerprint struct {
|
||||||
ja3n atomic.Pointer[TLSFingerprintJA3N]
|
ja3n atomic.Pointer[TLSFingerprintJA3N]
|
||||||
@@ -105,227 +70,6 @@ const (
|
|||||||
extensionEncryptedClientHello uint16 = 0xfe0d
|
extensionEncryptedClientHello uint16 = 0xfe0d
|
||||||
)
|
)
|
||||||
|
|
||||||
func tlsFingerprintJA3(hello *tls.ClientHelloInfo, sortExtensions bool) []byte {
|
|
||||||
buf := make([]byte, 0, 256)
|
|
||||||
|
|
||||||
{
|
|
||||||
var sslVersion uint16
|
|
||||||
var hasGrease bool
|
|
||||||
for _, v := range hello.SupportedVersions {
|
|
||||||
if v&greaseMask != greaseValue {
|
|
||||||
if v > sslVersion {
|
|
||||||
sslVersion = v
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
hasGrease = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// maximum TLS 1.2 as specified on JA3, as TLS 1.3 is put in SupportedVersions
|
|
||||||
if slices.Contains(hello.Extensions, extensionSupportedVersions) && hasGrease && sslVersion > tls.VersionTLS12 {
|
|
||||||
sslVersion = tls.VersionTLS12
|
|
||||||
}
|
|
||||||
|
|
||||||
buf = strconv.AppendUint(buf, uint64(sslVersion), 10)
|
|
||||||
buf = append(buf, ',')
|
|
||||||
}
|
|
||||||
|
|
||||||
n := 0
|
|
||||||
for _, cipher := range hello.CipherSuites {
|
|
||||||
//if !slices.Contains(greaseValues[:], cipher) {
|
|
||||||
if cipher&greaseMask != greaseValue {
|
|
||||||
buf = strconv.AppendUint(buf, uint64(cipher), 10)
|
|
||||||
buf = append(buf, '-')
|
|
||||||
n = 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buf = buf[:len(buf)-n]
|
|
||||||
buf = append(buf, ',')
|
|
||||||
n = 0
|
|
||||||
|
|
||||||
extensions := hello.Extensions
|
|
||||||
if sortExtensions {
|
|
||||||
extensions = slices.Clone(extensions)
|
|
||||||
slices.Sort(extensions)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, extension := range extensions {
|
|
||||||
if extension&greaseMask != greaseValue {
|
|
||||||
buf = strconv.AppendUint(buf, uint64(extension), 10)
|
|
||||||
buf = append(buf, '-')
|
|
||||||
n = 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buf = buf[:len(buf)-n]
|
|
||||||
buf = append(buf, ',')
|
|
||||||
n = 0
|
|
||||||
|
|
||||||
for _, curve := range hello.SupportedCurves {
|
|
||||||
if curve&greaseMask != greaseValue {
|
|
||||||
buf = strconv.AppendUint(buf, uint64(curve), 10)
|
|
||||||
buf = append(buf, '-')
|
|
||||||
n = 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buf = buf[:len(buf)-n]
|
|
||||||
buf = append(buf, ',')
|
|
||||||
n = 0
|
|
||||||
|
|
||||||
for _, point := range hello.SupportedPoints {
|
|
||||||
buf = strconv.AppendUint(buf, uint64(point), 10)
|
|
||||||
buf = append(buf, '-')
|
|
||||||
n = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
buf = buf[:len(buf)-n]
|
|
||||||
|
|
||||||
sum := md5.Sum(buf)
|
|
||||||
return sum[:]
|
|
||||||
}
|
|
||||||
|
|
||||||
func tlsFingerprintJA4(hello *tls.ClientHelloInfo) (ja4 TLSFingerprintJA4) {
|
|
||||||
buf := make([]byte, 0, 10)
|
|
||||||
|
|
||||||
// TODO: t = TLS, q = QUIC
|
|
||||||
buf = append(buf, 't')
|
|
||||||
|
|
||||||
{
|
|
||||||
var sslVersion uint16
|
|
||||||
for _, v := range hello.SupportedVersions {
|
|
||||||
if v&greaseMask != greaseValue {
|
|
||||||
if v > sslVersion {
|
|
||||||
sslVersion = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch sslVersion {
|
|
||||||
case tls.VersionSSL30:
|
|
||||||
buf = append(buf, 's', '3')
|
|
||||||
case tls.VersionTLS10:
|
|
||||||
buf = append(buf, '1', '0')
|
|
||||||
case tls.VersionTLS11:
|
|
||||||
buf = append(buf, '1', '1')
|
|
||||||
case tls.VersionTLS12:
|
|
||||||
buf = append(buf, '1', '2')
|
|
||||||
case tls.VersionTLS13:
|
|
||||||
buf = append(buf, '1', '3')
|
|
||||||
default:
|
|
||||||
sslVersion -= 0x0201
|
|
||||||
buf = strconv.AppendUint(buf, uint64(sslVersion>>8), 10)
|
|
||||||
buf = strconv.AppendUint(buf, uint64(sslVersion&0xff), 10)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if slices.Contains(hello.Extensions, extensionServerName) && hello.ServerName != "" {
|
|
||||||
buf = append(buf, 'd')
|
|
||||||
} else {
|
|
||||||
buf = append(buf, 'i')
|
|
||||||
}
|
|
||||||
|
|
||||||
ciphers := make([]uint16, 0, len(hello.CipherSuites))
|
|
||||||
for _, cipher := range hello.CipherSuites {
|
|
||||||
if cipher&greaseMask != greaseValue {
|
|
||||||
ciphers = append(ciphers, cipher)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extensionCount := 0
|
|
||||||
extensions := make([]uint16, 0, len(hello.Extensions))
|
|
||||||
for _, extension := range hello.Extensions {
|
|
||||||
if extension&greaseMask != greaseValue {
|
|
||||||
extensionCount++
|
|
||||||
if extension != extensionALPN && extension != extensionServerName {
|
|
||||||
extensions = append(extensions, extension)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
schemes := make([]tls.SignatureScheme, 0, len(hello.SignatureSchemes))
|
|
||||||
|
|
||||||
for _, scheme := range hello.SignatureSchemes {
|
|
||||||
if scheme&greaseMask != greaseValue {
|
|
||||||
schemes = append(schemes, scheme)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: maybe little endian
|
|
||||||
slices.Sort(ciphers)
|
|
||||||
slices.Sort(extensions)
|
|
||||||
//slices.Sort(schemes)
|
|
||||||
|
|
||||||
if len(ciphers) < 10 {
|
|
||||||
buf = append(buf, '0')
|
|
||||||
buf = strconv.AppendUint(buf, uint64(len(ciphers)), 10)
|
|
||||||
} else if len(ciphers) > 99 {
|
|
||||||
buf = append(buf, '9', '9')
|
|
||||||
} else {
|
|
||||||
buf = strconv.AppendUint(buf, uint64(len(ciphers)), 10)
|
|
||||||
}
|
|
||||||
|
|
||||||
if extensionCount < 10 {
|
|
||||||
buf = append(buf, '0')
|
|
||||||
buf = strconv.AppendUint(buf, uint64(extensionCount), 10)
|
|
||||||
} else if extensionCount > 99 {
|
|
||||||
buf = append(buf, '9', '9')
|
|
||||||
} else {
|
|
||||||
buf = strconv.AppendUint(buf, uint64(extensionCount), 10)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(hello.SupportedProtos) > 0 && len(hello.SupportedProtos[0]) > 1 {
|
|
||||||
buf = append(buf, hello.SupportedProtos[0][0], hello.SupportedProtos[0][len(hello.SupportedProtos[0])-1])
|
|
||||||
} else {
|
|
||||||
buf = append(buf, '0', '0')
|
|
||||||
}
|
|
||||||
|
|
||||||
copy(ja4.A[:], buf)
|
|
||||||
|
|
||||||
ja4.B = ja4SHA256(uint16SliceToHex(ciphers))
|
|
||||||
|
|
||||||
extBuf := uint16SliceToHex(extensions)
|
|
||||||
|
|
||||||
if len(schemes) > 0 {
|
|
||||||
extBuf = append(extBuf, '_')
|
|
||||||
extBuf = append(extBuf, uint16SliceToHex(schemes)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
ja4.C = ja4SHA256(extBuf)
|
|
||||||
|
|
||||||
return ja4
|
|
||||||
}
|
|
||||||
|
|
||||||
func uint16SliceToHex[T ~uint16](in []T) (out []byte) {
|
|
||||||
if len(in) == 0 {
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
out = slices.Grow(out, hex.EncodedLen(len(in)*2)+len(in))
|
|
||||||
|
|
||||||
for _, n := range in {
|
|
||||||
out = append(out, fmt.Sprintf("%04x", uint16(n))...)
|
|
||||||
out = append(out, ',')
|
|
||||||
}
|
|
||||||
out = out[:len(out)-1]
|
|
||||||
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
func ja4SHA256(buf []byte) [6]byte {
|
|
||||||
if len(buf) == 0 {
|
|
||||||
return [6]byte{0, 0, 0, 0, 0, 0}
|
|
||||||
}
|
|
||||||
sum := sha256.Sum256(buf)
|
|
||||||
|
|
||||||
return [6]byte(sum[:6])
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildTLSFingerprint(hello *tls.ClientHelloInfo) (ja3n TLSFingerprintJA3N, ja4 TLSFingerprintJA4) {
|
|
||||||
return TLSFingerprintJA3N(tlsFingerprintJA3(hello, true)), tlsFingerprintJA4(hello)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetTLSFingerprint(r *http.Request) *TLSFingerprint {
|
func GetTLSFingerprint(r *http.Request) *TLSFingerprint {
|
||||||
ptr := r.Context().Value(tlsFingerprintKey{})
|
ptr := r.Context().Value(tlsFingerprintKey{})
|
||||||
if fpPtr, ok := ptr.(*TLSFingerprint); ok && ptr != nil && fpPtr != nil {
|
if fpPtr, ok := ptr.(*TLSFingerprint); ok && ptr != nil && fpPtr != nil {
|
||||||
|
|||||||
269
utils/fingerprint_modern.go
Normal file
269
utils/fingerprint_modern.go
Normal file
@@ -0,0 +1,269 @@
|
|||||||
|
//go:build !go1.22 && !go1.23
|
||||||
|
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/md5"
|
||||||
|
"crypto/sha256"
|
||||||
|
"crypto/tls"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"slices"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func applyTLSFingerprinter(server *http.Server) {
|
||||||
|
server.TLSConfig = server.TLSConfig.Clone()
|
||||||
|
|
||||||
|
getCertificate := server.TLSConfig.GetCertificate
|
||||||
|
if getCertificate == nil {
|
||||||
|
server.TLSConfig.GetCertificate = func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||||
|
ja3n, ja4 := buildTLSFingerprint(clientHello)
|
||||||
|
ptr := clientHello.Context().Value(tlsFingerprintKey{})
|
||||||
|
if fpPtr, ok := ptr.(*TLSFingerprint); ok && ptr != nil && fpPtr != nil {
|
||||||
|
fpPtr.ja3n.Store(&ja3n)
|
||||||
|
fpPtr.ja4.Store(&ja4)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
server.TLSConfig.GetCertificate = func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||||
|
ja3n, ja4 := buildTLSFingerprint(clientHello)
|
||||||
|
ptr := clientHello.Context().Value(tlsFingerprintKey{})
|
||||||
|
if fpPtr, ok := ptr.(*TLSFingerprint); ok && ptr != nil && fpPtr != nil {
|
||||||
|
fpPtr.ja3n.Store(&ja3n)
|
||||||
|
fpPtr.ja4.Store(&ja4)
|
||||||
|
}
|
||||||
|
|
||||||
|
return getCertificate(clientHello)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
server.ConnContext = func(ctx context.Context, c net.Conn) context.Context {
|
||||||
|
return context.WithValue(ctx, tlsFingerprintKey{}, &TLSFingerprint{})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func tlsFingerprintJA3(hello *tls.ClientHelloInfo, sortExtensions bool) []byte {
|
||||||
|
buf := make([]byte, 0, 256)
|
||||||
|
|
||||||
|
{
|
||||||
|
var sslVersion uint16
|
||||||
|
var hasGrease bool
|
||||||
|
for _, v := range hello.SupportedVersions {
|
||||||
|
if v&greaseMask != greaseValue {
|
||||||
|
if v > sslVersion {
|
||||||
|
sslVersion = v
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
hasGrease = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// maximum TLS 1.2 as specified on JA3, as TLS 1.3 is put in SupportedVersions
|
||||||
|
if slices.Contains(hello.Extensions, extensionSupportedVersions) && hasGrease && sslVersion > tls.VersionTLS12 {
|
||||||
|
sslVersion = tls.VersionTLS12
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = strconv.AppendUint(buf, uint64(sslVersion), 10)
|
||||||
|
buf = append(buf, ',')
|
||||||
|
}
|
||||||
|
|
||||||
|
n := 0
|
||||||
|
for _, cipher := range hello.CipherSuites {
|
||||||
|
//if !slices.Contains(greaseValues[:], cipher) {
|
||||||
|
if cipher&greaseMask != greaseValue {
|
||||||
|
buf = strconv.AppendUint(buf, uint64(cipher), 10)
|
||||||
|
buf = append(buf, '-')
|
||||||
|
n = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = buf[:len(buf)-n]
|
||||||
|
buf = append(buf, ',')
|
||||||
|
n = 0
|
||||||
|
|
||||||
|
extensions := hello.Extensions
|
||||||
|
if sortExtensions {
|
||||||
|
extensions = slices.Clone(extensions)
|
||||||
|
slices.Sort(extensions)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, extension := range extensions {
|
||||||
|
if extension&greaseMask != greaseValue {
|
||||||
|
buf = strconv.AppendUint(buf, uint64(extension), 10)
|
||||||
|
buf = append(buf, '-')
|
||||||
|
n = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = buf[:len(buf)-n]
|
||||||
|
buf = append(buf, ',')
|
||||||
|
n = 0
|
||||||
|
|
||||||
|
for _, curve := range hello.SupportedCurves {
|
||||||
|
if curve&greaseMask != greaseValue {
|
||||||
|
buf = strconv.AppendUint(buf, uint64(curve), 10)
|
||||||
|
buf = append(buf, '-')
|
||||||
|
n = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = buf[:len(buf)-n]
|
||||||
|
buf = append(buf, ',')
|
||||||
|
n = 0
|
||||||
|
|
||||||
|
for _, point := range hello.SupportedPoints {
|
||||||
|
buf = strconv.AppendUint(buf, uint64(point), 10)
|
||||||
|
buf = append(buf, '-')
|
||||||
|
n = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = buf[:len(buf)-n]
|
||||||
|
|
||||||
|
sum := md5.Sum(buf)
|
||||||
|
return sum[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func tlsFingerprintJA4(hello *tls.ClientHelloInfo) (ja4 TLSFingerprintJA4) {
|
||||||
|
buf := make([]byte, 0, 10)
|
||||||
|
|
||||||
|
// TODO: t = TLS, q = QUIC
|
||||||
|
buf = append(buf, 't')
|
||||||
|
|
||||||
|
{
|
||||||
|
var sslVersion uint16
|
||||||
|
for _, v := range hello.SupportedVersions {
|
||||||
|
if v&greaseMask != greaseValue {
|
||||||
|
if v > sslVersion {
|
||||||
|
sslVersion = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch sslVersion {
|
||||||
|
case tls.VersionSSL30:
|
||||||
|
buf = append(buf, 's', '3')
|
||||||
|
case tls.VersionTLS10:
|
||||||
|
buf = append(buf, '1', '0')
|
||||||
|
case tls.VersionTLS11:
|
||||||
|
buf = append(buf, '1', '1')
|
||||||
|
case tls.VersionTLS12:
|
||||||
|
buf = append(buf, '1', '2')
|
||||||
|
case tls.VersionTLS13:
|
||||||
|
buf = append(buf, '1', '3')
|
||||||
|
default:
|
||||||
|
sslVersion -= 0x0201
|
||||||
|
buf = strconv.AppendUint(buf, uint64(sslVersion>>8), 10)
|
||||||
|
buf = strconv.AppendUint(buf, uint64(sslVersion&0xff), 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if slices.Contains(hello.Extensions, extensionServerName) && hello.ServerName != "" {
|
||||||
|
buf = append(buf, 'd')
|
||||||
|
} else {
|
||||||
|
buf = append(buf, 'i')
|
||||||
|
}
|
||||||
|
|
||||||
|
ciphers := make([]uint16, 0, len(hello.CipherSuites))
|
||||||
|
for _, cipher := range hello.CipherSuites {
|
||||||
|
if cipher&greaseMask != greaseValue {
|
||||||
|
ciphers = append(ciphers, cipher)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extensionCount := 0
|
||||||
|
extensions := make([]uint16, 0, len(hello.Extensions))
|
||||||
|
for _, extension := range hello.Extensions {
|
||||||
|
if extension&greaseMask != greaseValue {
|
||||||
|
extensionCount++
|
||||||
|
if extension != extensionALPN && extension != extensionServerName {
|
||||||
|
extensions = append(extensions, extension)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
schemes := make([]tls.SignatureScheme, 0, len(hello.SignatureSchemes))
|
||||||
|
|
||||||
|
for _, scheme := range hello.SignatureSchemes {
|
||||||
|
if scheme&greaseMask != greaseValue {
|
||||||
|
schemes = append(schemes, scheme)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: maybe little endian
|
||||||
|
slices.Sort(ciphers)
|
||||||
|
slices.Sort(extensions)
|
||||||
|
//slices.Sort(schemes)
|
||||||
|
|
||||||
|
if len(ciphers) < 10 {
|
||||||
|
buf = append(buf, '0')
|
||||||
|
buf = strconv.AppendUint(buf, uint64(len(ciphers)), 10)
|
||||||
|
} else if len(ciphers) > 99 {
|
||||||
|
buf = append(buf, '9', '9')
|
||||||
|
} else {
|
||||||
|
buf = strconv.AppendUint(buf, uint64(len(ciphers)), 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
if extensionCount < 10 {
|
||||||
|
buf = append(buf, '0')
|
||||||
|
buf = strconv.AppendUint(buf, uint64(extensionCount), 10)
|
||||||
|
} else if extensionCount > 99 {
|
||||||
|
buf = append(buf, '9', '9')
|
||||||
|
} else {
|
||||||
|
buf = strconv.AppendUint(buf, uint64(extensionCount), 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(hello.SupportedProtos) > 0 && len(hello.SupportedProtos[0]) > 1 {
|
||||||
|
buf = append(buf, hello.SupportedProtos[0][0], hello.SupportedProtos[0][len(hello.SupportedProtos[0])-1])
|
||||||
|
} else {
|
||||||
|
buf = append(buf, '0', '0')
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(ja4.A[:], buf)
|
||||||
|
|
||||||
|
ja4.B = ja4SHA256(uint16SliceToHex(ciphers))
|
||||||
|
|
||||||
|
extBuf := uint16SliceToHex(extensions)
|
||||||
|
|
||||||
|
if len(schemes) > 0 {
|
||||||
|
extBuf = append(extBuf, '_')
|
||||||
|
extBuf = append(extBuf, uint16SliceToHex(schemes)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
ja4.C = ja4SHA256(extBuf)
|
||||||
|
|
||||||
|
return ja4
|
||||||
|
}
|
||||||
|
|
||||||
|
func uint16SliceToHex[T ~uint16](in []T) (out []byte) {
|
||||||
|
if len(in) == 0 {
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
out = slices.Grow(out, hex.EncodedLen(len(in)*2)+len(in))
|
||||||
|
|
||||||
|
for _, n := range in {
|
||||||
|
out = append(out, fmt.Sprintf("%04x", uint16(n))...)
|
||||||
|
out = append(out, ',')
|
||||||
|
}
|
||||||
|
out = out[:len(out)-1]
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func ja4SHA256(buf []byte) [6]byte {
|
||||||
|
if len(buf) == 0 {
|
||||||
|
return [6]byte{0, 0, 0, 0, 0, 0}
|
||||||
|
}
|
||||||
|
sum := sha256.Sum256(buf)
|
||||||
|
|
||||||
|
return [6]byte(sum[:6])
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildTLSFingerprint(hello *tls.ClientHelloInfo) (ja3n TLSFingerprintJA3N, ja4 TLSFingerprintJA4) {
|
||||||
|
return TLSFingerprintJA3N(tlsFingerprintJA3(hello, true)), tlsFingerprintJA4(hello)
|
||||||
|
}
|
||||||
@@ -7,38 +7,15 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"maps"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"net/url"
|
"net/url"
|
||||||
"slices"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewServer(handler http.Handler, tlsConfig *tls.Config) *http.Server {
|
|
||||||
if tlsConfig == nil {
|
|
||||||
proto := new(http.Protocols)
|
|
||||||
proto.SetHTTP1(true)
|
|
||||||
proto.SetUnencryptedHTTP2(true)
|
|
||||||
h1s := &http.Server{
|
|
||||||
Handler: handler,
|
|
||||||
Protocols: proto,
|
|
||||||
}
|
|
||||||
|
|
||||||
return h1s
|
|
||||||
} else {
|
|
||||||
server := &http.Server{
|
|
||||||
TLSConfig: tlsConfig,
|
|
||||||
Handler: handler,
|
|
||||||
}
|
|
||||||
applyTLSFingerprinter(server)
|
|
||||||
return server
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func SelectHTTPHandler(backends map[string]http.Handler, host string) http.Handler {
|
func SelectHTTPHandler(backends map[string]http.Handler, host string) http.Handler {
|
||||||
backend, ok := backends[host]
|
backend, ok := backends[host]
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -169,51 +146,15 @@ func GetRemoteAddress(ctx context.Context) *netip.AddrPort {
|
|||||||
return &ip
|
return &ip
|
||||||
}
|
}
|
||||||
|
|
||||||
func RandomCacheBust(n int) string {
|
func CacheBust() string {
|
||||||
buf := make([]byte, n)
|
return cacheBust
|
||||||
|
}
|
||||||
|
|
||||||
|
var cacheBust string
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
|
||||||
|
buf := make([]byte, 16)
|
||||||
_, _ = rand.Read(buf)
|
_, _ = rand.Read(buf)
|
||||||
return base64.RawURLEncoding.EncodeToString(buf)
|
cacheBust = base64.RawURLEncoding.EncodeToString(buf)
|
||||||
}
|
|
||||||
|
|
||||||
var staticCacheBust = RandomCacheBust(16)
|
|
||||||
|
|
||||||
func StaticCacheBust() string {
|
|
||||||
return staticCacheBust
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseRawQuery(rawQuery string) (m url.Values, err error) {
|
|
||||||
m = make(url.Values)
|
|
||||||
for rawQuery != "" {
|
|
||||||
var key string
|
|
||||||
key, rawQuery, _ = strings.Cut(rawQuery, "&")
|
|
||||||
if strings.Contains(key, ";") {
|
|
||||||
err = fmt.Errorf("invalid semicolon separator in query")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if key == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
key, value, _ := strings.Cut(key, "=")
|
|
||||||
m[key] = append(m[key], value)
|
|
||||||
}
|
|
||||||
return m, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func EncodeRawQuery(v url.Values) string {
|
|
||||||
if len(v) == 0 {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
var buf strings.Builder
|
|
||||||
for _, k := range slices.Sorted(maps.Keys(v)) {
|
|
||||||
vs := v[k]
|
|
||||||
for _, v := range vs {
|
|
||||||
if buf.Len() > 0 {
|
|
||||||
buf.WriteByte('&')
|
|
||||||
}
|
|
||||||
buf.WriteString(k)
|
|
||||||
buf.WriteByte('=')
|
|
||||||
buf.WriteString(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return buf.String()
|
|
||||||
}
|
}
|
||||||
|
|||||||
28
utils/http_legacy.go
Normal file
28
utils/http_legacy.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
//go:build go1.22 || go1.23
|
||||||
|
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"golang.org/x/net/http2"
|
||||||
|
"golang.org/x/net/http2/h2c"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewServer(handler http.Handler, tlsConfig *tls.Config) *http.Server {
|
||||||
|
if tlsConfig == nil {
|
||||||
|
h2s := &http2.Server{}
|
||||||
|
|
||||||
|
h1s := &http.Server{
|
||||||
|
Handler: h2c.NewHandler(handler, h2s),
|
||||||
|
}
|
||||||
|
|
||||||
|
return h1s
|
||||||
|
} else {
|
||||||
|
server := &http.Server{
|
||||||
|
TLSConfig: tlsConfig,
|
||||||
|
Handler: handler,
|
||||||
|
}
|
||||||
|
return server
|
||||||
|
}
|
||||||
|
}
|
||||||
29
utils/http_modern.go
Normal file
29
utils/http_modern.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
//go:build !go1.22 && !go1.23
|
||||||
|
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewServer(handler http.Handler, tlsConfig *tls.Config) *http.Server {
|
||||||
|
if tlsConfig == nil {
|
||||||
|
proto := new(http.Protocols)
|
||||||
|
proto.SetHTTP1(true)
|
||||||
|
proto.SetUnencryptedHTTP2(true)
|
||||||
|
h1s := &http.Server{
|
||||||
|
Handler: handler,
|
||||||
|
Protocols: proto,
|
||||||
|
}
|
||||||
|
|
||||||
|
return h1s
|
||||||
|
} else {
|
||||||
|
server := &http.Server{
|
||||||
|
TLSConfig: tlsConfig,
|
||||||
|
Handler: handler,
|
||||||
|
}
|
||||||
|
applyTLSFingerprinter(server)
|
||||||
|
return server
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,7 +39,7 @@ func FetchTags(backend http.Handler, uri *url.URL, kinds ...string) (result []ht
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for n := range node.Descendants() {
|
descendants(node, func(n *html.Node) {
|
||||||
if n.Type == html.ElementNode && slices.Contains(kinds, n.Data) {
|
if n.Type == html.ElementNode && slices.Contains(kinds, n.Data) {
|
||||||
result = append(result, html.Node{
|
result = append(result, html.Node{
|
||||||
Type: n.Type,
|
Type: n.Type,
|
||||||
@@ -49,7 +49,14 @@ func FetchTags(backend http.Handler, uri *url.URL, kinds ...string) (result []ht
|
|||||||
Attr: n.Attr,
|
Attr: n.Attr,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func descendants(n *html.Node, f func(n *html.Node)) {
|
||||||
|
f(n)
|
||||||
|
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||||
|
descendants(c, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user