mirror of
https://github.com/TecharoHQ/anubis.git
synced 2026-04-05 16:28:17 +00:00
Compare commits
20 Commits
v1.25.0
...
Xe/express
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b6c361c294 | ||
|
|
e9e602976f | ||
|
|
33bb5803a8 | ||
|
|
ada7b3a179 | ||
|
|
dfa7025afe | ||
|
|
884af5fd4c | ||
|
|
3fb8fa2009 | ||
|
|
b43df36f7d | ||
|
|
fd058964fa | ||
|
|
fb20b36b18 | ||
|
|
84cba05167 | ||
|
|
9f988578a4 | ||
|
|
ea4e5751ab | ||
|
|
4e1db3842e | ||
|
|
029c79ba28 | ||
|
|
9f8ede7fe3 | ||
|
|
80bd7c563b | ||
|
|
92a3e5ba81 | ||
|
|
65cbc6922c | ||
|
|
eae3a7b5e4 |
6
data/apps/allow-api-routes.yaml
Normal file
6
data/apps/allow-api-routes.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
- name: allow-api-routes
|
||||||
|
action: ALLOW
|
||||||
|
expression:
|
||||||
|
all:
|
||||||
|
- '!(method == "HEAD" || method == "GET")'
|
||||||
|
- path.startsWith("/api/")
|
||||||
@@ -12,6 +12,12 @@
|
|||||||
{
|
{
|
||||||
"import": "(data)/bots/us-ai-scraper.yaml"
|
"import": "(data)/bots/us-ai-scraper.yaml"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"import": "(data)/bots/aggressive-brazilian-scrapers.yaml"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"import": "(data)/clients/curl-impersonate.yaml"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"import": "(data)/crawlers/googlebot.yaml"
|
"import": "(data)/crawlers/googlebot.yaml"
|
||||||
},
|
},
|
||||||
@@ -46,4 +52,4 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"dnsbl": false
|
"dnsbl": false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ bots:
|
|||||||
- import: (data)/bots/cloudflare-workers.yaml
|
- import: (data)/bots/cloudflare-workers.yaml
|
||||||
- import: (data)/bots/headless-browsers.yaml
|
- import: (data)/bots/headless-browsers.yaml
|
||||||
- import: (data)/bots/us-ai-scraper.yaml
|
- import: (data)/bots/us-ai-scraper.yaml
|
||||||
|
- import: (data)/bots/aggressive-brazilian-scrapers.yaml
|
||||||
|
- import: (data)/clients/curl-impersonate.yaml
|
||||||
|
|
||||||
# Search engines to allow
|
# Search engines to allow
|
||||||
- import: (data)/crawlers/googlebot.yaml
|
- import: (data)/crawlers/googlebot.yaml
|
||||||
@@ -41,10 +43,11 @@ bots:
|
|||||||
# report_as: 4 # lie to the operator
|
# report_as: 4 # lie to the operator
|
||||||
# algorithm: slow # intentionally waste CPU cycles and time
|
# algorithm: slow # intentionally waste CPU cycles and time
|
||||||
|
|
||||||
# Generic catchall rule
|
# Challenge clients with "Mozilla" or "Opera" in their user-agent string
|
||||||
- name: generic-browser
|
#- import: (data)/common/legacy-challenge-everything.yaml
|
||||||
user_agent_regex: >-
|
|
||||||
Mozilla|Opera
|
- name: reject-browsers
|
||||||
action: CHALLENGE
|
action: DENY
|
||||||
|
expression: userAgent.isBrowserLike()
|
||||||
|
|
||||||
dnsbl: false
|
dnsbl: false
|
||||||
|
|||||||
28
data/bots/aggressive-brazilian-scrapers.yaml
Normal file
28
data/bots/aggressive-brazilian-scrapers.yaml
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
- name: deny-aggressive-brazilian-scrapers
|
||||||
|
action: DENY
|
||||||
|
expression:
|
||||||
|
any:
|
||||||
|
# Internet Explorer should be out of support
|
||||||
|
- userAgent.contains("MSIE")
|
||||||
|
# Trident is the Internet Explorer browser engine
|
||||||
|
- userAgent.contains("Trident")
|
||||||
|
# Opera is a fork of chrome now
|
||||||
|
- userAgent.contains("Presto")
|
||||||
|
# Windows CE is discontinued
|
||||||
|
- userAgent.contains("Windows CE")
|
||||||
|
# Windows 95 is discontinued
|
||||||
|
- userAgent.contains("Windows 95")
|
||||||
|
# Windows 98 is discontinued
|
||||||
|
- userAgent.contains("Windows 98")
|
||||||
|
# Windows 9.x is discontinued
|
||||||
|
- userAgent.contains("Win 9x")
|
||||||
|
# Amazon does not have an Alexa Toolbar.
|
||||||
|
- userAgent.contains("Alexa Toolbar")
|
||||||
|
- name: challenge-aggressive-brazilian-scrapers
|
||||||
|
action: CHALLENGE
|
||||||
|
expression:
|
||||||
|
any:
|
||||||
|
# This is not released, even Windows 11 calls itself Windows 10
|
||||||
|
- userAgent.contains("Windows NT 11.0")
|
||||||
|
# iPods are not in common use
|
||||||
|
- userAgent.contains("iPod")
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
- name: cloudflare-workers
|
- name: cloudflare-workers
|
||||||
headers_regex:
|
expression: '"Cf-Worker" in headers'
|
||||||
CF-Worker: .*
|
action: CHALLENGE
|
||||||
action: DENY
|
|
||||||
9
data/bots/irc-bots/archlinux-phrik.yaml
Normal file
9
data/bots/irc-bots/archlinux-phrik.yaml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# phrik in the Arch Linux IRC channels
|
||||||
|
- name: archlinux-phrik
|
||||||
|
action: ALLOW
|
||||||
|
expression:
|
||||||
|
all:
|
||||||
|
- remoteAddress == "159.69.213.214"
|
||||||
|
- userAgent == "Mozilla/5.0 (compatible; utils.web Limnoria module)"
|
||||||
|
- '"X-Http-Version" in headers'
|
||||||
|
- headers["X-Http-Version"] == "HTTP/1.1"
|
||||||
9
data/bots/irc-bots/gentoo-chat.yaml
Normal file
9
data/bots/irc-bots/gentoo-chat.yaml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# chat in the gentoo IRC channels
|
||||||
|
- name: gentoo-chat
|
||||||
|
action: ALLOW
|
||||||
|
expression:
|
||||||
|
all:
|
||||||
|
- remoteAddress == "45.76.166.57"
|
||||||
|
- userAgent == "Mozilla/5.0 (Linux x86_64; rv:76.0) Gecko/20100101 Firefox/76.0"
|
||||||
|
- '"X-Http-Version" in headers'
|
||||||
|
- headers["X-Http-Version"] == "HTTP/1.1"
|
||||||
32
data/clients/curl-impersonate.yaml
Normal file
32
data/clients/curl-impersonate.yaml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
- name: curl-impersonate
|
||||||
|
action: CHALLENGE
|
||||||
|
expression:
|
||||||
|
any:
|
||||||
|
- >
|
||||||
|
"Sec-Ch-Ua" in headers && headers["Sec-Ch-Ua"] == '" Not A;Brand";v="99", "Chromium";v="100", "Google Chrome";v="100"'
|
||||||
|
- >
|
||||||
|
"Sec-Ch-Ua" in headers && headers["Sec-Ch-Ua"] == '" Not A;Brand";v="99", "Chromium";v="101", "Google Chrome";v="101"'
|
||||||
|
- >
|
||||||
|
"Sec-Ch-Ua" in headers && headers["Sec-Ch-Ua"] == '"Chromium";v="104", " Not A;Brand";v="99", "Google Chrome";v="104"'
|
||||||
|
- >
|
||||||
|
"Sec-Ch-Ua" in headers && headers["Sec-Ch-Ua"] == '"Google Chrome";v="107", "Chromium";v="107", "Not=A?Brand";v="24"'
|
||||||
|
- >
|
||||||
|
"Sec-Ch-Ua" in headers && headers["Sec-Ch-Ua"] == '"Chromium";v="110", "Not A(Brand";v="24", "Google Chrome";v="110"'
|
||||||
|
- >
|
||||||
|
"Sec-Ch-Ua" in headers && headers["Sec-Ch-Ua"] == '"Chromium";v="116", "Not)A;Brand";v="24", "Google Chrome";v="116"'
|
||||||
|
- >
|
||||||
|
"Sec-Ch-Ua" in headers && headers["Sec-Ch-Ua"] == '"Google Chrome";v="119", "Chromium";v="119", "Not?A_Brand";v="24"'
|
||||||
|
- >
|
||||||
|
"Sec-Ch-Ua" in headers && headers["Sec-Ch-Ua"] == '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"'
|
||||||
|
- >
|
||||||
|
"Sec-Ch-Ua" in headers && headers["Sec-Ch-Ua"] == '"Google Chrome";v="123", "Not:A-Brand";v="8", "Chromium";v="123"'
|
||||||
|
- >
|
||||||
|
"Sec-Ch-Ua" in headers && headers["Sec-Ch-Ua"] == '"Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"'
|
||||||
|
- >
|
||||||
|
"Sec-Ch-Ua" in headers && headers["Sec-Ch-Ua"] == '"Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"'
|
||||||
|
- >
|
||||||
|
"Sec-Ch-Ua" in headers && headers["Sec-Ch-Ua"] == '"Not(A:Brand";v="99", "Google Chrome";v="133", "Chromium";v="133"'
|
||||||
|
- >
|
||||||
|
"Sec-Ch-Ua" in headers && headers["Sec-Ch-Ua"] == '" Not A;Brand";v="99", "Chromium";v="101", "Microsoft Edge";v="101"'
|
||||||
|
- >
|
||||||
|
"Sec-Ch-Ua" in headers && headers["Sec-Ch-Ua"] == '" Not A;Brand";v="99", "Chromium";v="99", "Microsoft Edge";v="99"'
|
||||||
14
data/clients/git.yaml
Normal file
14
data/clients/git.yaml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
- name: allow-git-clients
|
||||||
|
action: ALLOW
|
||||||
|
expression:
|
||||||
|
all:
|
||||||
|
- >
|
||||||
|
(
|
||||||
|
userAgent.startsWith("git/") ||
|
||||||
|
userAgent.contains("libgit") ||
|
||||||
|
userAgent.startsWith("go-git") ||
|
||||||
|
userAgent.startsWith("JGit/") ||
|
||||||
|
userAgent.startsWith("JGit-")
|
||||||
|
)
|
||||||
|
- '"Git-Protocol" in headers'
|
||||||
|
- headers["Git-Protocol"] == "version=2"
|
||||||
7
data/clients/go-get.yaml
Normal file
7
data/clients/go-get.yaml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
- name: go-get
|
||||||
|
action: ALLOW
|
||||||
|
expression:
|
||||||
|
all:
|
||||||
|
- userAgent.startsWith("Go-http-client/")
|
||||||
|
- '"go-get" in query'
|
||||||
|
- query["go-get"] == "1"
|
||||||
6
data/common/allow-api-like.yaml
Normal file
6
data/common/allow-api-like.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
- name: allow-api-routes
|
||||||
|
action: ALLOW
|
||||||
|
expression:
|
||||||
|
all:
|
||||||
|
- '!(method == "HEAD" || method == "GET")'
|
||||||
|
- path.startsWith("/api/")
|
||||||
10
data/common/challenge-browser-like.yaml
Normal file
10
data/common/challenge-browser-like.yaml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# Challenge anything with HTTP/1.1 that claims to be a browser
|
||||||
|
- name: challenge-lies-browser-but-http-1.1
|
||||||
|
action: CHALLENGE
|
||||||
|
expression:
|
||||||
|
all:
|
||||||
|
- '"X-Http-Version" in headers'
|
||||||
|
- headers["X-Http-Version"] == "HTTP/1.1"
|
||||||
|
- '"X-Forwarded-Proto" in headers'
|
||||||
|
- headers["X-Forwarded-Proto"] == "https"
|
||||||
|
- userAgent.isBrowserLike()
|
||||||
@@ -7,4 +7,7 @@
|
|||||||
action: ALLOW
|
action: ALLOW
|
||||||
- name: robots-txt
|
- name: robots-txt
|
||||||
path_regex: ^/robots.txt$
|
path_regex: ^/robots.txt$
|
||||||
|
action: ALLOW
|
||||||
|
- name: sitemap
|
||||||
|
path_regex: ^/sitemap.xml$
|
||||||
action: ALLOW
|
action: ALLOW
|
||||||
4
data/common/legacy-challenge-everything.yaml
Normal file
4
data/common/legacy-challenge-everything.yaml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# Generic catchall rule
|
||||||
|
- name: generic-browser
|
||||||
|
expression: userAgent.isBrowserLike()
|
||||||
|
action: CHALLENGE
|
||||||
3
data/common/rfc-violations.yaml
Normal file
3
data/common/rfc-violations.yaml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
- name: no-user-agent-string
|
||||||
|
expression: userAgent == ""
|
||||||
|
action: DENY
|
||||||
@@ -3,6 +3,6 @@ package data
|
|||||||
import "embed"
|
import "embed"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
//go:embed botPolicies.yaml botPolicies.json apps bots common crawlers
|
//go:embed botPolicies.yaml botPolicies.json apps bots clients common crawlers
|
||||||
BotPolicies embed.FS
|
BotPolicies embed.FS
|
||||||
)
|
)
|
||||||
|
|||||||
25
docs/docs/admin/configuration/expressions.mdx
Normal file
25
docs/docs/admin/configuration/expressions.mdx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# Expression-based rule matching
|
||||||
|
|
||||||
|
- Anubis offers the ability to use [Common Expression Language (CEL)](https://cel.dev) for advanced rule matching
|
||||||
|
- A brief summary of CEL
|
||||||
|
- Imagine the rule as the contents of a function body in programming or the WHERE clause in SQL
|
||||||
|
- This is an advanced feature and it is easy to get yourself into trouble with it
|
||||||
|
- Link to the spec, mention docs are WIP
|
||||||
|
- Variables exposed to Anubis expressions
|
||||||
|
- `remoteAddress` -> string IP of client
|
||||||
|
- `host` -> string HTTP/TLS hostname
|
||||||
|
- `method` -> string HTTP method
|
||||||
|
- `userAgent` -> string User-Agent header
|
||||||
|
- `path` -> string HTTP request path
|
||||||
|
- `query` -> map[string]string URL key values
|
||||||
|
- `headers` -> map[string]string HTTP request headers
|
||||||
|
- Load average:
|
||||||
|
- `load_1m` -> system load in the last minute
|
||||||
|
- `load_5m` -> system load in the last 5 minutes
|
||||||
|
- `load_15m` -> system load in the last 15 minutes
|
||||||
|
- Functions exposed to Anubis expressions
|
||||||
|
- `userAgent.isBrowserLike` -> returns true if the userAgent is like a browser
|
||||||
|
- Life advice
|
||||||
|
- When in doubt, throw a CHALLENGE over a DENY. CHALLENGE makes it more easy to renege
|
||||||
|
- Example usage
|
||||||
|
- [How to make Anubis much less aggressive](../less-aggressive.mdx)
|
||||||
@@ -91,7 +91,8 @@ Assuming you are protecting `anubistest.techaro.lol`, you need the following ser
|
|||||||
# These headers need to be set or else Anubis will
|
# These headers need to be set or else Anubis will
|
||||||
# throw an "admin misconfiguration" error.
|
# throw an "admin misconfiguration" error.
|
||||||
RequestHeader set "X-Real-Ip" expr=%{REMOTE_ADDR}
|
RequestHeader set "X-Real-Ip" expr=%{REMOTE_ADDR}
|
||||||
RequestHeader set X-Forwarded-Proto "https"
|
RequestHeader set "X-Forwarded-Proto" "https"
|
||||||
|
RequestHeader set "X-Http-Version" "%{SERVER_PROTOCOL}s"
|
||||||
|
|
||||||
ProxyPreserveHost On
|
ProxyPreserveHost On
|
||||||
|
|
||||||
|
|||||||
@@ -59,8 +59,14 @@ server {
|
|||||||
listen [::]:443 ssl http2;
|
listen [::]:443 ssl http2;
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
|
# Anubis needs these headers to understand the connection
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Http-Version $server_protocol;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_set_header X-Request-Id $request_id;
|
||||||
|
proxy_set_header X-Forwarded-Host $host;
|
||||||
proxy_pass http://anubis;
|
proxy_pass http://anubis;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
97
docs/docs/admin/less-aggressive.mdx
Normal file
97
docs/docs/admin/less-aggressive.mdx
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
# How to make Anubis much less aggressive
|
||||||
|
|
||||||
|
Out of the box, Anubis has fairly paranoid defaults. It's designed to stop the bleeding now, so it defaults to a global "challenge everything" rule. This does work, but comes at significant user experience cost if users disable JavaScript or run plugins that interfere with JavaScript execution.
|
||||||
|
|
||||||
|
Anubis ships with a rule named `challenge-lies-browser-but-http-1.1` that changes the default behavior to fire much less often. This works on top of [expression support](./configuration/expressions.mdx) to allow you to block the worst of the bad while leaving normal users able to access the website. This requires integration with your HTTP load balancer.
|
||||||
|
|
||||||
|
You can import this rule by replacing the `generic-browser` rule with the following:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- import: (data)/common/challenge-browser-like.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
## The new rule
|
||||||
|
|
||||||
|
Previously Anubis aggressively challenged everything that had "Mozilla" in its User-Agent string. The rule has been amended to this set of heuristics:
|
||||||
|
|
||||||
|
1. If the request headers contain `X-Http-Protocol`
|
||||||
|
1. AND if the request header `X-Http-Protocol` is `HTTP/1.1`
|
||||||
|
1. AND if the request headers contain `X-Forwarded-Proto`
|
||||||
|
1. AND if the request header `X-Forwarded-Proto` is `https`
|
||||||
|
1. AND if the request's User-Agent string is similar to that of a browser
|
||||||
|
1. THEN throw a challenge.
|
||||||
|
|
||||||
|
This means that users that are using up to date browsers will automatically get through without having to pass a challenge.
|
||||||
|
|
||||||
|
## Apache
|
||||||
|
|
||||||
|
Ensure [`mod_http2`](https://httpd.apache.org/docs/2.4/mod/mod_http2.html) is loaded.
|
||||||
|
|
||||||
|
Make sure that your HTTPS VirtualHost has the right settings for Anubis in place:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Enable HTTP/2 support so Anubis can issues challenges for HTTP/1.1 clients
|
||||||
|
Protocols h2 http/1.1
|
||||||
|
|
||||||
|
# These headers need to be set or else Anubis will
|
||||||
|
# throw an "admin misconfiguration" error.
|
||||||
|
# diff-add
|
||||||
|
RequestHeader set "X-Real-Ip" expr=%{REMOTE_ADDR}
|
||||||
|
# diff-add
|
||||||
|
RequestHeader set "X-Forwarded-Proto" "https"
|
||||||
|
# diff-add
|
||||||
|
RequestHeader set "X-Http-Version" "%{SERVER_PROTOCOL}s"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Caddy
|
||||||
|
|
||||||
|
Make sure that your [`reverse_proxy` has the right headers configured](https://caddyserver.com/docs/caddyfile/directives/reverse_proxy#headers):
|
||||||
|
|
||||||
|
```python
|
||||||
|
ellenjoe.int.within.lgbt {
|
||||||
|
# ...
|
||||||
|
# diff-remove
|
||||||
|
reverse_proxy http://localhost:3000
|
||||||
|
# diff-add
|
||||||
|
reverse_proxy http://localhost:3000 {
|
||||||
|
# diff-add
|
||||||
|
header_up X-Real-Ip {remote_host}
|
||||||
|
# diff-add
|
||||||
|
header_up X-Http-Version {http.request.proto}
|
||||||
|
# diff-add
|
||||||
|
}
|
||||||
|
# ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## ingress-nginx
|
||||||
|
|
||||||
|
Edit your `ingress-nginx-controller` ConfigMap:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
data:
|
||||||
|
# ...
|
||||||
|
# diff-add
|
||||||
|
location-snippet: |
|
||||||
|
# diff-add
|
||||||
|
proxy_set_header X-Http-Version $server_protocol;
|
||||||
|
# diff-add
|
||||||
|
proxy_set_header X-Tls-Version $ssl_protocol;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Nginx
|
||||||
|
|
||||||
|
Edit your `server` blocks to add the following headers:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# diff-add
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
# diff-add
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
# diff-add
|
||||||
|
proxy_set_header X-Http-Version $server_protocol;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Traefik
|
||||||
|
|
||||||
|
This configuration is not currently supported with Traefik. A Traefik plugin is needed to add the right header.
|
||||||
18
go.mod
18
go.mod
@@ -6,46 +6,58 @@ require (
|
|||||||
github.com/a-h/templ v0.3.857
|
github.com/a-h/templ v0.3.857
|
||||||
github.com/facebookgo/flagenv v0.0.0-20160425205200-fcd59fca7456
|
github.com/facebookgo/flagenv v0.0.0-20160425205200-fcd59fca7456
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.2
|
github.com/golang-jwt/jwt/v5 v5.2.2
|
||||||
|
github.com/google/cel-go v0.25.0
|
||||||
github.com/playwright-community/playwright-go v0.5101.0
|
github.com/playwright-community/playwright-go v0.5101.0
|
||||||
github.com/prometheus/client_golang v1.22.0
|
github.com/prometheus/client_golang v1.22.0
|
||||||
github.com/sebest/xff v0.0.0-20210106013422-671bd2870b3a
|
github.com/sebest/xff v0.0.0-20210106013422-671bd2870b3a
|
||||||
|
github.com/shirou/gopsutil/v4 v4.25.3
|
||||||
github.com/yl2chen/cidranger v1.0.2
|
github.com/yl2chen/cidranger v1.0.2
|
||||||
golang.org/x/net v0.39.0
|
golang.org/x/net v0.39.0
|
||||||
|
k8s.io/apimachinery v0.32.3
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
cel.dev/expr v0.23.1 // indirect
|
||||||
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect
|
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect
|
||||||
github.com/a-h/parse v0.0.0-20250122154542-74294addb73e // indirect
|
github.com/a-h/parse v0.0.0-20250122154542-74294addb73e // indirect
|
||||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
github.com/andybalholm/brotli v1.1.0 // indirect
|
||||||
|
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/cli/browser v1.3.0 // indirect
|
github.com/cli/browser v1.3.0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
|
||||||
github.com/deckarep/golang-set/v2 v2.6.0 // indirect
|
github.com/deckarep/golang-set/v2 v2.6.0 // indirect
|
||||||
|
github.com/ebitengine/purego v0.8.2 // indirect
|
||||||
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 // indirect
|
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 // indirect
|
||||||
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect
|
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect
|
||||||
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 // indirect
|
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 // indirect
|
||||||
github.com/fatih/color v1.16.0 // indirect
|
github.com/fatih/color v1.16.0 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||||
github.com/go-jose/go-jose/v3 v3.0.4 // indirect
|
github.com/go-jose/go-jose/v3 v3.0.4 // indirect
|
||||||
|
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||||
github.com/go-stack/stack v1.8.1 // indirect
|
github.com/go-stack/stack v1.8.1 // indirect
|
||||||
|
github.com/kr/text v0.2.0 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/natefinch/atomic v1.0.1 // indirect
|
github.com/natefinch/atomic v1.0.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||||
github.com/prometheus/client_model v0.6.1 // indirect
|
github.com/prometheus/client_model v0.6.1 // indirect
|
||||||
github.com/prometheus/common v0.62.0 // indirect
|
github.com/prometheus/common v0.62.0 // indirect
|
||||||
github.com/prometheus/procfs v0.15.1 // indirect
|
github.com/prometheus/procfs v0.15.1 // indirect
|
||||||
|
github.com/stoewer/go-strcase v1.2.0 // indirect
|
||||||
|
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
|
||||||
golang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678 // indirect
|
golang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678 // indirect
|
||||||
golang.org/x/mod v0.24.0 // indirect
|
golang.org/x/mod v0.24.0 // indirect
|
||||||
golang.org/x/sync v0.13.0 // indirect
|
golang.org/x/sync v0.13.0 // indirect
|
||||||
golang.org/x/sys v0.32.0 // indirect
|
golang.org/x/sys v0.32.0 // indirect
|
||||||
|
golang.org/x/text v0.24.0 // indirect
|
||||||
golang.org/x/tools v0.32.0 // indirect
|
golang.org/x/tools v0.32.0 // indirect
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7 // indirect
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 // indirect
|
||||||
google.golang.org/protobuf v1.36.5 // indirect
|
google.golang.org/protobuf v1.36.5 // indirect
|
||||||
honnef.co/go/tools v0.6.1 // indirect
|
honnef.co/go/tools v0.6.1 // indirect
|
||||||
k8s.io/apimachinery v0.32.3 // indirect
|
|
||||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
|
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
|
||||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
42
go.sum
42
go.sum
@@ -1,3 +1,5 @@
|
|||||||
|
cel.dev/expr v0.23.1 h1:K4KOtPCJQjVggkARsjG9RWXP6O4R73aHeJMa/dmCQQg=
|
||||||
|
cel.dev/expr v0.23.1/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
|
||||||
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs=
|
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs=
|
||||||
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||||
github.com/a-h/parse v0.0.0-20250122154542-74294addb73e h1:HjVbSQHy+dnlS6C3XajZ69NYAb5jbGNfHanvm1+iYlo=
|
github.com/a-h/parse v0.0.0-20250122154542-74294addb73e h1:HjVbSQHy+dnlS6C3XajZ69NYAb5jbGNfHanvm1+iYlo=
|
||||||
@@ -6,6 +8,8 @@ github.com/a-h/templ v0.3.857 h1:6EqcJuGZW4OL+2iZ3MD+NnIcG7nGkaQeF2Zq5kf9ZGg=
|
|||||||
github.com/a-h/templ v0.3.857/go.mod h1:qhrhAkRFubE7khxLZHsBFHfX+gWwVNKbzKeF9GlPV4M=
|
github.com/a-h/templ v0.3.857/go.mod h1:qhrhAkRFubE7khxLZHsBFHfX+gWwVNKbzKeF9GlPV4M=
|
||||||
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
||||||
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
||||||
|
github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
|
||||||
|
github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||||
@@ -14,11 +18,14 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF
|
|||||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/cli/browser v1.3.0 h1:LejqCrpWr+1pRqmEPDGnTZOjsMe7sehifLynZJuqJpo=
|
github.com/cli/browser v1.3.0 h1:LejqCrpWr+1pRqmEPDGnTZOjsMe7sehifLynZJuqJpo=
|
||||||
github.com/cli/browser v1.3.0/go.mod h1:HH8s+fOAxjhQoBUAsKuPCbqUuxZDhQ2/aD+SzsEfBTk=
|
github.com/cli/browser v1.3.0/go.mod h1:HH8s+fOAxjhQoBUAsKuPCbqUuxZDhQ2/aD+SzsEfBTk=
|
||||||
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
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.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM=
|
github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM=
|
||||||
github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
|
github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
|
||||||
|
github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
|
||||||
|
github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||||
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 h1:0JZ+dUmQeA8IIVUMzysrX4/AKuQwWhV2dYQuPZdvdSQ=
|
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 h1:0JZ+dUmQeA8IIVUMzysrX4/AKuQwWhV2dYQuPZdvdSQ=
|
||||||
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64=
|
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64=
|
||||||
github.com/facebookgo/flagenv v0.0.0-20160425205200-fcd59fca7456 h1:CkmB2l68uhvRlwOTPrwnuitSxi/S3Cg4L5QYOcL9MBc=
|
github.com/facebookgo/flagenv v0.0.0-20160425205200-fcd59fca7456 h1:CkmB2l68uhvRlwOTPrwnuitSxi/S3Cg4L5QYOcL9MBc=
|
||||||
@@ -33,15 +40,23 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
|
|||||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||||
github.com/go-jose/go-jose/v3 v3.0.4 h1:Wp5HA7bLQcKnf6YYao/4kpRpVMp/yf6+pJKV8WFSaNY=
|
github.com/go-jose/go-jose/v3 v3.0.4 h1:Wp5HA7bLQcKnf6YYao/4kpRpVMp/yf6+pJKV8WFSaNY=
|
||||||
github.com/go-jose/go-jose/v3 v3.0.4/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
|
github.com/go-jose/go-jose/v3 v3.0.4/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
|
||||||
|
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||||
|
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||||
github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw=
|
github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw=
|
||||||
github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=
|
github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
|
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||||
|
github.com/google/cel-go v0.25.0 h1:jsFw9Fhn+3y2kBbltZR4VEz5xKkcIFRPDnuEzAGv5GY=
|
||||||
|
github.com/google/cel-go v0.25.0/go.mod h1:hjEb6r5SuOSlhCHmFoLzu8HGCERvIsDAbxDAyNU/MmI=
|
||||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||||
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
@@ -60,6 +75,8 @@ github.com/playwright-community/playwright-go v0.5101.0/go.mod h1:kBNWs/w2aJ2ZUp
|
|||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||||
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||||
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
|
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
|
||||||
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
|
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
|
||||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||||
@@ -68,19 +85,30 @@ github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ
|
|||||||
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
|
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
|
||||||
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||||
|
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||||
|
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||||
github.com/sebest/xff v0.0.0-20210106013422-671bd2870b3a h1:iLcLb5Fwwz7g/DLK89F+uQBDeAhHhwdzB5fSlVdhGcM=
|
github.com/sebest/xff v0.0.0-20210106013422-671bd2870b3a h1:iLcLb5Fwwz7g/DLK89F+uQBDeAhHhwdzB5fSlVdhGcM=
|
||||||
github.com/sebest/xff v0.0.0-20210106013422-671bd2870b3a/go.mod h1:wozgYq9WEBQBaIJe4YZ0qTSFAMxmcwBhQH0fO0R34Z0=
|
github.com/sebest/xff v0.0.0-20210106013422-671bd2870b3a/go.mod h1:wozgYq9WEBQBaIJe4YZ0qTSFAMxmcwBhQH0fO0R34Z0=
|
||||||
|
github.com/shirou/gopsutil/v4 v4.25.3 h1:SeA68lsu8gLggyMbmCn8cmp97V1TI9ld9sVzAUcKcKE=
|
||||||
|
github.com/shirou/gopsutil/v4 v4.25.3/go.mod h1:xbuxyoZj+UsgnZrENu3lQivsngRR5BdjbJwf2fv4szA=
|
||||||
|
github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU=
|
||||||
|
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
|
||||||
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=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
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=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
|
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||||
|
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||||
|
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
|
||||||
|
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
|
||||||
golang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678 h1:1P7xPZEwZMoBoz0Yze5Nx2/4pxj6nw9ZqHWXqP0iRgQ=
|
golang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678 h1:1P7xPZEwZMoBoz0Yze5Nx2/4pxj6nw9ZqHWXqP0iRgQ=
|
||||||
golang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
golang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
@@ -97,12 +125,12 @@ golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
|||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
|
||||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
|
||||||
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
||||||
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
@@ -124,18 +152,24 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
|||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
|
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||||
|
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU=
|
|
||||||
golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ=
|
|
||||||
golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU=
|
golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU=
|
||||||
golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s=
|
golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7 h1:YcyjlL1PRr2Q17/I0dPk2JmYS5CDXfcdb2Z3YRioEbw=
|
||||||
|
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-20240826202546-f6391c0de4c7 h1:2035KHhUv+EpyB+hWgJnaWKJOdX1E95w2S8Rr4uWKTs=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
|
||||||
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
||||||
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||||
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=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/TecharoHQ/anubis/data"
|
"github.com/TecharoHQ/anubis/data"
|
||||||
"github.com/TecharoHQ/anubis/internal"
|
"github.com/TecharoHQ/anubis/internal"
|
||||||
"github.com/TecharoHQ/anubis/lib/policy"
|
"github.com/TecharoHQ/anubis/lib/policy"
|
||||||
|
"github.com/TecharoHQ/anubis/lib/policy/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
func loadPolicies(t *testing.T, fname string) *policy.ParsedConfig {
|
func loadPolicies(t *testing.T, fname string) *policy.ParsedConfig {
|
||||||
@@ -83,7 +84,7 @@ func TestCVE2025_24369(t *testing.T) {
|
|||||||
Next: http.NewServeMux(),
|
Next: http.NewServeMux(),
|
||||||
Policy: pol,
|
Policy: pol,
|
||||||
|
|
||||||
CookieDomain: "local.cetacean.club",
|
CookieDomain: ".local.cetacean.club",
|
||||||
CookiePartitioned: true,
|
CookiePartitioned: true,
|
||||||
CookieName: t.Name(),
|
CookieName: t.Name(),
|
||||||
})
|
})
|
||||||
@@ -393,3 +394,42 @@ func TestBasePrefix(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCloudflareWorkersRule(t *testing.T) {
|
||||||
|
for _, variant := range []string{"cel", "header"} {
|
||||||
|
t.Run(variant, func(t *testing.T) {
|
||||||
|
pol := loadPolicies(t, "./testdata/cloudflare-workers-"+variant+".yaml")
|
||||||
|
|
||||||
|
h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fmt.Fprintln(w, "OK")
|
||||||
|
})
|
||||||
|
|
||||||
|
s, err := New(Options{
|
||||||
|
Next: h,
|
||||||
|
Policy: pol,
|
||||||
|
ServeRobotsTXT: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("can't construct libanubis.Server: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("no-cf-worker-header", func(t *testing.T) {
|
||||||
|
req, err := http.NewRequest(http.MethodGet, "/", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Add("X-Real-Ip", "127.0.0.1")
|
||||||
|
|
||||||
|
cr, _, err := s.check(req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cr.Rule != config.RuleAllow {
|
||||||
|
t.Errorf("rule is wrong, wanted %s, got: %s", config.RuleAllow, cr.Rule)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
114
lib/policy/celchecker.go
Normal file
114
lib/policy/celchecker.go
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
package policy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/TecharoHQ/anubis/internal"
|
||||||
|
"github.com/TecharoHQ/anubis/lib/policy/config"
|
||||||
|
"github.com/TecharoHQ/anubis/lib/policy/expressions"
|
||||||
|
"github.com/google/cel-go/cel"
|
||||||
|
"github.com/google/cel-go/common/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CELChecker struct {
|
||||||
|
src string
|
||||||
|
program cel.Program
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCELChecker(cfg *config.ExpressionOrList) (*CELChecker, error) {
|
||||||
|
env, err := expressions.NewEnvironment()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var src string
|
||||||
|
var ast *cel.Ast
|
||||||
|
|
||||||
|
if cfg.Expression != "" {
|
||||||
|
src = cfg.Expression
|
||||||
|
var iss *cel.Issues
|
||||||
|
interm, iss := env.Compile(src)
|
||||||
|
if iss != nil {
|
||||||
|
return nil, iss.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
ast, iss = env.Check(interm)
|
||||||
|
if iss != nil {
|
||||||
|
return nil, iss.Err()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cfg.All) != 0 {
|
||||||
|
ast, err = expressions.Join(env, expressions.JoinAnd, cfg.All...)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cfg.Any) != 0 {
|
||||||
|
ast, err = expressions.Join(env, expressions.JoinOr, cfg.Any...)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
program, err := expressions.Compile(env, ast)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("can't compile CEL program: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &CELChecker{
|
||||||
|
src: src,
|
||||||
|
program: program,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cc *CELChecker) Hash() string {
|
||||||
|
return internal.SHA256sum(cc.src)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cc *CELChecker) Check(r *http.Request) (bool, error) {
|
||||||
|
result, _, err := cc.program.ContextEval(r.Context(), &CELRequest{r})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if val, ok := result.(types.Bool); ok {
|
||||||
|
return bool(val), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type CELRequest struct {
|
||||||
|
*http.Request
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cr *CELRequest) Parent() cel.Activation { return nil }
|
||||||
|
|
||||||
|
func (cr *CELRequest) ResolveName(name string) (any, bool) {
|
||||||
|
switch name {
|
||||||
|
case "remoteAddress":
|
||||||
|
return cr.Header.Get("X-Real-Ip"), true
|
||||||
|
case "host":
|
||||||
|
return cr.Host, true
|
||||||
|
case "method":
|
||||||
|
return cr.Method, true
|
||||||
|
case "userAgent":
|
||||||
|
return cr.UserAgent(), true
|
||||||
|
case "path":
|
||||||
|
return cr.URL.Path, true
|
||||||
|
case "query":
|
||||||
|
return expressions.URLValues{Values: cr.URL.Query()}, true
|
||||||
|
case "headers":
|
||||||
|
return expressions.HTTPHeaders{Header: cr.Header}, true
|
||||||
|
case "load_1m":
|
||||||
|
return expressions.Load1(), true
|
||||||
|
case "load_5m":
|
||||||
|
return expressions.Load5(), true
|
||||||
|
case "load_15m":
|
||||||
|
return expressions.Load15(), true
|
||||||
|
default:
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -53,9 +53,11 @@ type BotConfig struct {
|
|||||||
UserAgentRegex *string `json:"user_agent_regex"`
|
UserAgentRegex *string `json:"user_agent_regex"`
|
||||||
PathRegex *string `json:"path_regex"`
|
PathRegex *string `json:"path_regex"`
|
||||||
HeadersRegex map[string]string `json:"headers_regex"`
|
HeadersRegex map[string]string `json:"headers_regex"`
|
||||||
Action Rule `json:"action"`
|
|
||||||
RemoteAddr []string `json:"remote_addresses"`
|
RemoteAddr []string `json:"remote_addresses"`
|
||||||
Challenge *ChallengeRules `json:"challenge,omitempty"`
|
Expression *ExpressionOrList `json:"expression"`
|
||||||
|
|
||||||
|
Action Rule `json:"action"`
|
||||||
|
Challenge *ChallengeRules `json:"challenge,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b BotConfig) Zero() bool {
|
func (b BotConfig) Zero() bool {
|
||||||
@@ -83,7 +85,12 @@ func (b BotConfig) Valid() error {
|
|||||||
errs = append(errs, ErrBotMustHaveName)
|
errs = append(errs, ErrBotMustHaveName)
|
||||||
}
|
}
|
||||||
|
|
||||||
if b.UserAgentRegex == nil && b.PathRegex == nil && len(b.RemoteAddr) == 0 && len(b.HeadersRegex) == 0 {
|
allFieldsEmpty := b.UserAgentRegex == nil &&
|
||||||
|
b.PathRegex == nil &&
|
||||||
|
len(b.RemoteAddr) == 0 &&
|
||||||
|
len(b.HeadersRegex) == 0
|
||||||
|
|
||||||
|
if allFieldsEmpty && b.Expression == nil {
|
||||||
errs = append(errs, ErrBotMustHaveUserAgentOrPath)
|
errs = append(errs, ErrBotMustHaveUserAgentOrPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,6 +142,12 @@ func (b BotConfig) Valid() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if b.Expression != nil {
|
||||||
|
if err := b.Expression.Valid(); err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch b.Action {
|
switch b.Action {
|
||||||
case RuleAllow, RuleBenchmark, RuleChallenge, RuleDeny:
|
case RuleAllow, RuleBenchmark, RuleChallenge, RuleDeny:
|
||||||
// okay
|
// okay
|
||||||
|
|||||||
62
lib/policy/config/expressionorlist.go
Normal file
62
lib/policy/config/expressionorlist.go
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"slices"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrExpressionOrListMustBeStringOrObject = errors.New("config: this must be a string or an object")
|
||||||
|
ErrExpressionEmpty = errors.New("config: this expression is empty")
|
||||||
|
ErrExpressionCantHaveBoth = errors.New("config: expression block can't contain multiple expression types")
|
||||||
|
)
|
||||||
|
|
||||||
|
type ExpressionOrList struct {
|
||||||
|
Expression string `json:"-"`
|
||||||
|
All []string `json:"all"`
|
||||||
|
Any []string `json:"any"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (eol ExpressionOrList) Equal(rhs *ExpressionOrList) bool {
|
||||||
|
if eol.Expression != rhs.Expression {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !slices.Equal(eol.All, rhs.All) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !slices.Equal(eol.Any, rhs.Any) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (eol *ExpressionOrList) UnmarshalJSON(data []byte) error {
|
||||||
|
switch string(data[0]) {
|
||||||
|
case `"`: // string
|
||||||
|
return json.Unmarshal(data, &eol.Expression)
|
||||||
|
case "{": // object
|
||||||
|
type RawExpressionOrList ExpressionOrList
|
||||||
|
var val RawExpressionOrList
|
||||||
|
if err := json.Unmarshal(data, &val); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
eol.All = val.All
|
||||||
|
eol.Any = val.Any
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrExpressionOrListMustBeStringOrObject
|
||||||
|
}
|
||||||
|
|
||||||
|
func (eol *ExpressionOrList) Valid() error {
|
||||||
|
if len(eol.All) != 0 && len(eol.Any) != 0 {
|
||||||
|
return ErrExpressionCantHaveBoth
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
73
lib/policy/config/expressionorlist_test.go
Normal file
73
lib/policy/config/expressionorlist_test.go
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestExpressionOrListUnmarshal(t *testing.T) {
|
||||||
|
for _, tt := range []struct {
|
||||||
|
name string
|
||||||
|
inp string
|
||||||
|
err error
|
||||||
|
validErr error
|
||||||
|
result *ExpressionOrList
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "simple",
|
||||||
|
inp: `"\"User-Agent\" in headers"`,
|
||||||
|
result: &ExpressionOrList{
|
||||||
|
Expression: `"User-Agent" in headers`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "object-and",
|
||||||
|
inp: `{
|
||||||
|
"all": ["\"User-Agent\" in headers"]
|
||||||
|
}`,
|
||||||
|
result: &ExpressionOrList{
|
||||||
|
All: []string{
|
||||||
|
`"User-Agent" in headers`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "object-or",
|
||||||
|
inp: `{
|
||||||
|
"any": ["\"User-Agent\" in headers"]
|
||||||
|
}`,
|
||||||
|
result: &ExpressionOrList{
|
||||||
|
Any: []string{
|
||||||
|
`"User-Agent" in headers`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "both-or-and",
|
||||||
|
inp: `{
|
||||||
|
"all": ["\"User-Agent\" in headers"],
|
||||||
|
"any": ["\"User-Agent\" in headers"]
|
||||||
|
}`,
|
||||||
|
validErr: ErrExpressionCantHaveBoth,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
var eol ExpressionOrList
|
||||||
|
|
||||||
|
if err := json.Unmarshal([]byte(tt.inp), &eol); !errors.Is(err, tt.err) {
|
||||||
|
t.Errorf("wanted unmarshal error: %v but got: %v", tt.err, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tt.result != nil && !eol.Equal(tt.result) {
|
||||||
|
t.Logf("wanted: %#v", tt.result)
|
||||||
|
t.Logf("got: %#v", &eol)
|
||||||
|
t.Fatal("parsed expression is not what was expected")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := eol.Valid(); !errors.Is(err, tt.validErr) {
|
||||||
|
t.Errorf("wanted validation error: %v but got: %v", tt.err, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
17
lib/policy/config/testdata/bad/multiple_expression_types.json
vendored
Normal file
17
lib/policy/config/testdata/bad/multiple_expression_types.json
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"bots": [
|
||||||
|
{
|
||||||
|
"name": "multiple-expression-types",
|
||||||
|
"action": "ALLOW",
|
||||||
|
"expression": {
|
||||||
|
"all": [
|
||||||
|
"userAgent.startsWith(\"git/\") || userAgent.contains(\"libgit\")",
|
||||||
|
"\"Git-Protocol\" in headers && headers[\"Git-Protocol\"] == \"version=2\"\n"
|
||||||
|
],
|
||||||
|
"any": [
|
||||||
|
"userAgent.startsWith(\"evilbot/\")"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
10
lib/policy/config/testdata/bad/multiple_expression_types.yaml
vendored
Normal file
10
lib/policy/config/testdata/bad/multiple_expression_types.yaml
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
bots:
|
||||||
|
- name: multiple-expression-types
|
||||||
|
action: ALLOW
|
||||||
|
expression:
|
||||||
|
all:
|
||||||
|
- userAgent.startsWith("git/") || userAgent.contains("libgit")
|
||||||
|
- >
|
||||||
|
"Git-Protocol" in headers && headers["Git-Protocol"] == "version=2"
|
||||||
|
any:
|
||||||
|
- userAgent.startsWith("evilbot/")
|
||||||
14
lib/policy/config/testdata/good/git_client.json
vendored
Normal file
14
lib/policy/config/testdata/good/git_client.json
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"bots": [
|
||||||
|
{
|
||||||
|
"name": "allow-git-clients",
|
||||||
|
"action": "ALLOW",
|
||||||
|
"expression": {
|
||||||
|
"all": [
|
||||||
|
"userAgent.startsWith(\"git/\") || userAgent.contains(\"libgit\")",
|
||||||
|
"\"Git-Protocol\" in headers && headers[\"Git-Protocol\"] == \"version=2\""
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
8
lib/policy/config/testdata/good/git_client.yaml
vendored
Normal file
8
lib/policy/config/testdata/good/git_client.yaml
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
bots:
|
||||||
|
- name: allow-git-clients
|
||||||
|
action: ALLOW
|
||||||
|
expression:
|
||||||
|
all:
|
||||||
|
- userAgent.startsWith("git/") || userAgent.contains("libgit")
|
||||||
|
- >
|
||||||
|
"Git-Protocol" in headers && headers["Git-Protocol"] == "version=2"
|
||||||
3
lib/policy/expressions/README.md
Normal file
3
lib/policy/expressions/README.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Expressions support
|
||||||
|
|
||||||
|
The expressions support is based on ideas from [go-away](https://git.gammaspectra.live/git/go-away) but with different opinions about how things should be done.
|
||||||
81
lib/policy/expressions/environment.go
Normal file
81
lib/policy/expressions/environment.go
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
package expressions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/google/cel-go/cel"
|
||||||
|
"github.com/google/cel-go/common/types"
|
||||||
|
"github.com/google/cel-go/common/types/ref"
|
||||||
|
"github.com/google/cel-go/ext"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewEnvironment creates a new CEL environment, this is the set of
|
||||||
|
// variables and functions that are passed into the CEL scope so that
|
||||||
|
// Anubis can fail loudly and early when something is invalid instead
|
||||||
|
// of blowing up at runtime.
|
||||||
|
func NewEnvironment() (*cel.Env, error) {
|
||||||
|
return cel.NewEnv(
|
||||||
|
ext.Strings(
|
||||||
|
ext.StringsLocale("en_US"),
|
||||||
|
ext.StringsValidateFormatCalls(true),
|
||||||
|
),
|
||||||
|
|
||||||
|
// default all timestamps to UTC
|
||||||
|
cel.DefaultUTCTimeZone(true),
|
||||||
|
|
||||||
|
// Variables exposed to CEL programs:
|
||||||
|
|
||||||
|
// Request metadata
|
||||||
|
cel.Variable("remoteAddress", cel.StringType),
|
||||||
|
cel.Variable("host", cel.StringType),
|
||||||
|
cel.Variable("method", cel.StringType),
|
||||||
|
cel.Variable("userAgent", cel.StringType),
|
||||||
|
cel.Variable("path", cel.StringType),
|
||||||
|
cel.Variable("query", cel.MapType(cel.StringType, cel.StringType)),
|
||||||
|
cel.Variable("headers", cel.MapType(cel.StringType, cel.StringType)),
|
||||||
|
|
||||||
|
// System load metadata
|
||||||
|
cel.Variable("load_1m", cel.DoubleType),
|
||||||
|
cel.Variable("load_5m", cel.DoubleType),
|
||||||
|
cel.Variable("load_15m", cel.DoubleType),
|
||||||
|
|
||||||
|
// Functions exposed to CEL programs:
|
||||||
|
|
||||||
|
// userAgent.isBrowserLike() method, used to detect if a user agent is likely a browser
|
||||||
|
// based on shibboleth words in the User-Agent string.
|
||||||
|
cel.Function("isBrowserLike",
|
||||||
|
cel.MemberOverload("userAgent_isBrowserLike_string",
|
||||||
|
[]*cel.Type{cel.StringType},
|
||||||
|
cel.BoolType,
|
||||||
|
cel.UnaryBinding(func(userAgentVal ref.Val) ref.Val {
|
||||||
|
var userAgent string
|
||||||
|
switch v := userAgentVal.Value().(type) {
|
||||||
|
case string:
|
||||||
|
userAgent = v
|
||||||
|
default:
|
||||||
|
return types.NewErr("invalid type %T", userAgentVal)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case strings.Contains(userAgent, "Mozilla"), strings.Contains(userAgent, "Opera"), strings.Contains(userAgent, "Gecko"), strings.Contains(userAgent, "WebKit"), strings.Contains(userAgent, "Apple"), strings.Contains(userAgent, "Chrome"), strings.Contains(userAgent, "Windows"), strings.Contains(userAgent, "Linux"):
|
||||||
|
return types.Bool(true)
|
||||||
|
default:
|
||||||
|
return types.Bool(false)
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile takes CEL environment and syntax tree then emits an optimized
|
||||||
|
// Program for execution.
|
||||||
|
func Compile(env *cel.Env, ast *cel.Ast) (cel.Program, error) {
|
||||||
|
return env.Program(
|
||||||
|
ast,
|
||||||
|
cel.EvalOptions(
|
||||||
|
// optimize regular expressions right now instead of on the fly
|
||||||
|
cel.OptOptimize,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
75
lib/policy/expressions/http_headers.go
Normal file
75
lib/policy/expressions/http_headers.go
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
package expressions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/google/cel-go/common/types"
|
||||||
|
"github.com/google/cel-go/common/types/ref"
|
||||||
|
"github.com/google/cel-go/common/types/traits"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HTTPHeaders is a type wrapper to expose HTTP headers into CEL programs.
|
||||||
|
type HTTPHeaders struct {
|
||||||
|
http.Header
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h HTTPHeaders) ConvertToNative(typeDesc reflect.Type) (any, error) {
|
||||||
|
return nil, ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h HTTPHeaders) ConvertToType(typeVal ref.Type) ref.Val {
|
||||||
|
switch typeVal {
|
||||||
|
case types.MapType:
|
||||||
|
return h
|
||||||
|
case types.TypeType:
|
||||||
|
return types.MapType
|
||||||
|
}
|
||||||
|
|
||||||
|
return types.NewErr("can't convert from %q to %q", types.MapType, typeVal)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h HTTPHeaders) Equal(other ref.Val) ref.Val {
|
||||||
|
return types.Bool(false) // We don't want to compare header maps
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h HTTPHeaders) Type() ref.Type {
|
||||||
|
return types.MapType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h HTTPHeaders) Value() any { return h }
|
||||||
|
|
||||||
|
func (h HTTPHeaders) Find(key ref.Val) (ref.Val, bool) {
|
||||||
|
k, ok := key.(types.String)
|
||||||
|
if !ok {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := h.Header[string(k)]; !ok {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return types.String(strings.Join(h.Header.Values(string(k)), ",")), true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h HTTPHeaders) Contains(key ref.Val) ref.Val {
|
||||||
|
_, ok := h.Find(key)
|
||||||
|
return types.Bool(ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h HTTPHeaders) Get(key ref.Val) ref.Val {
|
||||||
|
result, ok := h.Find(key)
|
||||||
|
if !ok {
|
||||||
|
return types.ValOrErr(result, "no such key: %v", key)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h HTTPHeaders) Iterator() traits.Iterator { panic("TODO(Xe): implement me") }
|
||||||
|
|
||||||
|
func (h HTTPHeaders) IsZeroValue() bool {
|
||||||
|
return len(h.Header) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h HTTPHeaders) Size() ref.Val { return types.Int(len(h.Header)) }
|
||||||
52
lib/policy/expressions/http_headers_test.go
Normal file
52
lib/policy/expressions/http_headers_test.go
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package expressions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/cel-go/common/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHTTPHeaders(t *testing.T) {
|
||||||
|
headers := HTTPHeaders{
|
||||||
|
Header: http.Header{
|
||||||
|
"Content-Type": {"application/json"},
|
||||||
|
"Cf-Worker": {"true"},
|
||||||
|
"User-Agent": {"Go-http-client/2"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("contains-existing-header", func(t *testing.T) {
|
||||||
|
resp := headers.Contains(types.String("User-Agent"))
|
||||||
|
if !bool(resp.(types.Bool)) {
|
||||||
|
t.Fatal("headers does not contain User-Agent")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("not-contains-missing-header", func(t *testing.T) {
|
||||||
|
resp := headers.Contains(types.String("Xxx-Random-Header"))
|
||||||
|
if bool(resp.(types.Bool)) {
|
||||||
|
t.Fatal("headers does not contain User-Agent")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("get-existing-header", func(t *testing.T) {
|
||||||
|
val := headers.Get(types.String("User-Agent"))
|
||||||
|
switch val.(type) {
|
||||||
|
case types.String:
|
||||||
|
// ok
|
||||||
|
default:
|
||||||
|
t.Fatalf("result was wrong type %T", val)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("not-get-missing-header", func(t *testing.T) {
|
||||||
|
val := headers.Get(types.String("Xxx-Random-Header"))
|
||||||
|
switch val.(type) {
|
||||||
|
case *types.Err:
|
||||||
|
// ok
|
||||||
|
default:
|
||||||
|
t.Fatalf("result was wrong type %T", val)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
104
lib/policy/expressions/join.go
Normal file
104
lib/policy/expressions/join.go
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
package expressions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/google/cel-go/cel"
|
||||||
|
)
|
||||||
|
|
||||||
|
// JoinOperator is a type wrapper for and/or operators.
|
||||||
|
//
|
||||||
|
// This is a separate type so that validation can be done at the type level.
|
||||||
|
type JoinOperator string
|
||||||
|
|
||||||
|
// Possible values for JoinOperator
|
||||||
|
const (
|
||||||
|
JoinAnd JoinOperator = "&&"
|
||||||
|
JoinOr JoinOperator = "||"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Valid ensures that JoinOperator is semantically valid.
|
||||||
|
func (jo JoinOperator) Valid() error {
|
||||||
|
switch jo {
|
||||||
|
case JoinAnd, JoinOr:
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return ErrWrongJoinOperator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrWrongJoinOperator = errors.New("expressions: invalid join operator")
|
||||||
|
ErrNoExpressions = errors.New("expressions: cannot join zero expressions")
|
||||||
|
ErrCantCompile = errors.New("expressions: can't compile one expression")
|
||||||
|
)
|
||||||
|
|
||||||
|
// JoinClauses joins a list of compiled clauses into one big if statement.
|
||||||
|
//
|
||||||
|
// Imagine the following two clauses:
|
||||||
|
//
|
||||||
|
// ball.color == "red"
|
||||||
|
// ball.shape == "round"
|
||||||
|
//
|
||||||
|
// JoinClauses would emit one "joined" clause such as:
|
||||||
|
//
|
||||||
|
// ( ball.color == "red" ) && ( ball.shape == "round" )
|
||||||
|
func JoinClauses(env *cel.Env, operator JoinOperator, clauses ...*cel.Ast) (*cel.Ast, error) {
|
||||||
|
if err := operator.Valid(); err != nil {
|
||||||
|
return nil, fmt.Errorf("%w: wanted && or ||, got: %q", err, operator)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch len(clauses) {
|
||||||
|
case 0:
|
||||||
|
return nil, ErrNoExpressions
|
||||||
|
case 1:
|
||||||
|
return clauses[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var exprs []string
|
||||||
|
var errs []error
|
||||||
|
|
||||||
|
for _, clause := range clauses {
|
||||||
|
clauseStr, err := cel.AstToString(clause)
|
||||||
|
if err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
exprs = append(exprs, "( "+clauseStr+" )")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(errs) != 0 {
|
||||||
|
return nil, fmt.Errorf("errors while decompiling statements: %w", errors.Join(errs...))
|
||||||
|
}
|
||||||
|
|
||||||
|
statement := strings.Join(exprs, " "+string(operator)+" ")
|
||||||
|
result, iss := env.Compile(statement)
|
||||||
|
if iss != nil {
|
||||||
|
return nil, iss.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Join(env *cel.Env, operator JoinOperator, clauses ...string) (*cel.Ast, error) {
|
||||||
|
var statements []*cel.Ast
|
||||||
|
var errs []error
|
||||||
|
|
||||||
|
for _, clause := range clauses {
|
||||||
|
stmt, iss := env.Compile(clause)
|
||||||
|
if iss != nil && iss.Err() != nil {
|
||||||
|
errs = append(errs, fmt.Errorf("%w: %q gave: %w", ErrCantCompile, clause, iss.Err()))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
statements = append(statements, stmt)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(errs) != 0 {
|
||||||
|
return nil, fmt.Errorf("errors while joining clauses: %w", errors.Join(errs...))
|
||||||
|
}
|
||||||
|
|
||||||
|
return JoinClauses(env, operator, statements...)
|
||||||
|
}
|
||||||
90
lib/policy/expressions/join_test.go
Normal file
90
lib/policy/expressions/join_test.go
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
package expressions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/cel-go/cel"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestJoin(t *testing.T) {
|
||||||
|
env, err := NewEnvironment()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range []struct {
|
||||||
|
name string
|
||||||
|
clauses []string
|
||||||
|
op JoinOperator
|
||||||
|
err error
|
||||||
|
resultStr string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "no-clauses",
|
||||||
|
clauses: []string{},
|
||||||
|
op: JoinAnd,
|
||||||
|
err: ErrNoExpressions,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "one-clause-identity",
|
||||||
|
clauses: []string{`remoteAddress == "8.8.8.8"`},
|
||||||
|
op: JoinAnd,
|
||||||
|
err: nil,
|
||||||
|
resultStr: `remoteAddress == "8.8.8.8"`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multi-clause-and",
|
||||||
|
clauses: []string{
|
||||||
|
`remoteAddress == "8.8.8.8"`,
|
||||||
|
`host == "anubis.techaro.lol"`,
|
||||||
|
},
|
||||||
|
op: JoinAnd,
|
||||||
|
err: nil,
|
||||||
|
resultStr: `remoteAddress == "8.8.8.8" && host == "anubis.techaro.lol"`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multi-clause-or",
|
||||||
|
clauses: []string{
|
||||||
|
`remoteAddress == "8.8.8.8"`,
|
||||||
|
`host == "anubis.techaro.lol"`,
|
||||||
|
},
|
||||||
|
op: JoinOr,
|
||||||
|
err: nil,
|
||||||
|
resultStr: `remoteAddress == "8.8.8.8" || host == "anubis.techaro.lol"`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "git-user-agent",
|
||||||
|
clauses: []string{
|
||||||
|
`userAgent.startsWith("git/") || userAgent.contains("libgit")`,
|
||||||
|
`"Git-Protocol" in headers && headers["Git-Protocol"] == "version=2"`,
|
||||||
|
},
|
||||||
|
op: JoinAnd,
|
||||||
|
err: nil,
|
||||||
|
resultStr: `(userAgent.startsWith("git/") || userAgent.contains("libgit")) && "Git-Protocol" in headers &&
|
||||||
|
headers["Git-Protocol"] == "version=2"`,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
result, err := Join(env, tt.op, tt.clauses...)
|
||||||
|
if !errors.Is(err, tt.err) {
|
||||||
|
t.Errorf("wanted error %v but got: %v", tt.err, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tt.err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
program, err := cel.AstToString(result)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("can't decompile program: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tt.resultStr != program {
|
||||||
|
t.Logf("wanted: %s", tt.resultStr)
|
||||||
|
t.Logf("got: %s", program)
|
||||||
|
t.Error("program did not compile as expected")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
67
lib/policy/expressions/loadavg.go
Normal file
67
lib/policy/expressions/loadavg.go
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
package expressions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log/slog"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/shirou/gopsutil/v4/load"
|
||||||
|
)
|
||||||
|
|
||||||
|
type loadAvg struct {
|
||||||
|
lock sync.RWMutex
|
||||||
|
data *load.AvgStat
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *loadAvg) updateThread(ctx context.Context) {
|
||||||
|
ticker := time.NewTicker(15 * time.Second)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ticker.C:
|
||||||
|
l.update()
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *loadAvg) update() {
|
||||||
|
l.lock.Lock()
|
||||||
|
defer l.lock.Unlock()
|
||||||
|
|
||||||
|
var err error
|
||||||
|
l.data, err = load.Avg()
|
||||||
|
if err != nil {
|
||||||
|
slog.Debug("can't get load average", "err", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
globalLoadAvg *loadAvg
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
globalLoadAvg = &loadAvg{}
|
||||||
|
go globalLoadAvg.updateThread(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
|
func Load1() float64 {
|
||||||
|
globalLoadAvg.lock.RLock()
|
||||||
|
defer globalLoadAvg.lock.RUnlock()
|
||||||
|
return globalLoadAvg.data.Load1
|
||||||
|
}
|
||||||
|
|
||||||
|
func Load5() float64 {
|
||||||
|
globalLoadAvg.lock.RLock()
|
||||||
|
defer globalLoadAvg.lock.RUnlock()
|
||||||
|
return globalLoadAvg.data.Load5
|
||||||
|
}
|
||||||
|
|
||||||
|
func Load15() float64 {
|
||||||
|
globalLoadAvg.lock.RLock()
|
||||||
|
defer globalLoadAvg.lock.RUnlock()
|
||||||
|
return globalLoadAvg.data.Load15
|
||||||
|
}
|
||||||
78
lib/policy/expressions/url_values.go
Normal file
78
lib/policy/expressions/url_values.go
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
package expressions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/url"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/google/cel-go/common/types"
|
||||||
|
"github.com/google/cel-go/common/types/ref"
|
||||||
|
"github.com/google/cel-go/common/types/traits"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrNotImplemented = errors.New("expressions: not implemented")
|
||||||
|
|
||||||
|
// URLValues is a type wrapper to expose url.Values into CEL programs.
|
||||||
|
type URLValues struct {
|
||||||
|
url.Values
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u URLValues) ConvertToNative(typeDesc reflect.Type) (any, error) {
|
||||||
|
return nil, ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u URLValues) ConvertToType(typeVal ref.Type) ref.Val {
|
||||||
|
switch typeVal {
|
||||||
|
case types.MapType:
|
||||||
|
return u
|
||||||
|
case types.TypeType:
|
||||||
|
return types.MapType
|
||||||
|
}
|
||||||
|
|
||||||
|
return types.NewErr("can't convert from %q to %q", types.MapType, typeVal)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u URLValues) Equal(other ref.Val) ref.Val {
|
||||||
|
return types.Bool(false) // We don't want to compare header maps
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u URLValues) Type() ref.Type {
|
||||||
|
return types.MapType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u URLValues) Value() any { return u }
|
||||||
|
|
||||||
|
func (u URLValues) Find(key ref.Val) (ref.Val, bool) {
|
||||||
|
k, ok := key.(types.String)
|
||||||
|
if !ok {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := u.Values[string(k)]; !ok {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return types.String(strings.Join(u.Values[string(k)], ",")), true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u URLValues) Contains(key ref.Val) ref.Val {
|
||||||
|
_, ok := u.Find(key)
|
||||||
|
return types.Bool(ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u URLValues) Get(key ref.Val) ref.Val {
|
||||||
|
result, ok := u.Find(key)
|
||||||
|
if !ok {
|
||||||
|
return types.ValOrErr(result, "no such key: %v", key)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u URLValues) Iterator() traits.Iterator { panic("TODO(Xe): implement me") }
|
||||||
|
|
||||||
|
func (u URLValues) IsZeroValue() bool {
|
||||||
|
return len(u.Values) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u URLValues) Size() ref.Val { return types.Int(len(u.Values)) }
|
||||||
50
lib/policy/expressions/url_values_test.go
Normal file
50
lib/policy/expressions/url_values_test.go
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package expressions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/cel-go/common/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestURLValues(t *testing.T) {
|
||||||
|
headers := URLValues{
|
||||||
|
Values: url.Values{
|
||||||
|
"format": {"json"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("contains-existing-key", func(t *testing.T) {
|
||||||
|
resp := headers.Contains(types.String("format"))
|
||||||
|
if !bool(resp.(types.Bool)) {
|
||||||
|
t.Fatal("headers does not contain User-Agent")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("not-contains-missing-key", func(t *testing.T) {
|
||||||
|
resp := headers.Contains(types.String("not-there"))
|
||||||
|
if bool(resp.(types.Bool)) {
|
||||||
|
t.Fatal("headers does not contain User-Agent")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("get-existing-key", func(t *testing.T) {
|
||||||
|
val := headers.Get(types.String("format"))
|
||||||
|
switch val.(type) {
|
||||||
|
case types.String:
|
||||||
|
// ok
|
||||||
|
default:
|
||||||
|
t.Fatalf("result was wrong type %T", val)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("not-get-missing-key", func(t *testing.T) {
|
||||||
|
val := headers.Get(types.String("not-there"))
|
||||||
|
switch val.(type) {
|
||||||
|
case *types.Err:
|
||||||
|
// ok
|
||||||
|
default:
|
||||||
|
t.Fatalf("result was wrong type %T", val)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -92,6 +92,15 @@ func ParseConfig(fin io.Reader, fname string, defaultDifficulty int) (*ParsedCon
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if b.Expression != nil {
|
||||||
|
c, err := NewCELChecker(b.Expression)
|
||||||
|
if err != nil {
|
||||||
|
validationErrs = append(validationErrs, fmt.Errorf("while processing rule %s expressions: %w", b.Name, err))
|
||||||
|
} else {
|
||||||
|
cl = append(cl, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if b.Challenge == nil {
|
if b.Challenge == nil {
|
||||||
parsedBot.Challenge = &config.ChallengeRules{
|
parsedBot.Challenge = &config.ChallengeRules{
|
||||||
Difficulty: defaultDifficulty,
|
Difficulty: defaultDifficulty,
|
||||||
|
|||||||
4
lib/testdata/cloudflare-workers-cel.yaml
vendored
Normal file
4
lib/testdata/cloudflare-workers-cel.yaml
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
bots:
|
||||||
|
- name: cloudflare-workers
|
||||||
|
expression: '"Cf-Worker" in headers'
|
||||||
|
action: DENY
|
||||||
5
lib/testdata/cloudflare-workers-header.yaml
vendored
Normal file
5
lib/testdata/cloudflare-workers-header.yaml
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
bots:
|
||||||
|
- name: cloudflare-workers
|
||||||
|
headers_regex:
|
||||||
|
CF-Worker: .*
|
||||||
|
action: DENY
|
||||||
2
test/anubis_configs/less_paranoid.yaml
Normal file
2
test/anubis_configs/less_paranoid.yaml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
bots:
|
||||||
|
- import: (data)/common/challenge-browser-like.yaml
|
||||||
15
test/apache/Dockerfile
Normal file
15
test/apache/Dockerfile
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
FROM httpd:2.4
|
||||||
|
|
||||||
|
RUN sed -i \
|
||||||
|
-e 's/^#\(LoadModule .*mod_ssl.so\)/\1/' \
|
||||||
|
-e 's/^#\(LoadModule .*mod_rewrite.so\)/\1/' \
|
||||||
|
-e 's/^#\(LoadModule .*mod_proxy.so\)/\1/' \
|
||||||
|
-e 's/^#\(LoadModule .*mod_proxy_http.so\)/\1/' \
|
||||||
|
-e 's/^#\(LoadModule .*mod_socache_shmcb.so\)/\1/' \
|
||||||
|
-e 's/^#\(LoadModule .*mod_http2.so\)/\1/' \
|
||||||
|
conf/httpd.conf
|
||||||
|
RUN echo '' >> conf/httpd.conf \
|
||||||
|
&& echo 'IncludeOptional conf.d/*.conf' >> conf/httpd.conf
|
||||||
|
|
||||||
|
COPY conf.d ./conf.d
|
||||||
|
COPY snippets /etc/httpd/snippets
|
||||||
15
test/apache/conf.d/http.conf
Normal file
15
test/apache/conf.d/http.conf
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<VirtualHost *:80>
|
||||||
|
ServerAdmin your@email.here
|
||||||
|
ServerName httpd.local.cetacean.club
|
||||||
|
DocumentRoot /var/www/httpd.local.cetacean.club
|
||||||
|
|
||||||
|
Include /etc/httpd/snippets/proxy-headers.conf
|
||||||
|
|
||||||
|
ProxyPreserveHost On
|
||||||
|
|
||||||
|
ProxyRequests Off
|
||||||
|
ProxyVia Off
|
||||||
|
|
||||||
|
ProxyPass / http://httpdebug:3000/
|
||||||
|
ProxyPassReverse / http://httpdebug:3000/
|
||||||
|
</VirtualHost>
|
||||||
22
test/apache/conf.d/https.conf
Normal file
22
test/apache/conf.d/https.conf
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<IfModule mod_ssl.c>
|
||||||
|
<VirtualHost *:443>
|
||||||
|
ServerAdmin me@xeiaso.net
|
||||||
|
ServerName httpd.local.cetacean.club
|
||||||
|
DocumentRoot /var/www/httpd.local.cetacean.club
|
||||||
|
Protocols h2 http/1.1
|
||||||
|
|
||||||
|
SSLCertificateFile /etc/techaro/pki/httpd.local.cetacean.club/cert.pem
|
||||||
|
SSLCertificateKeyFile /etc/techaro/pki/httpd.local.cetacean.club/key.pem
|
||||||
|
Include /etc/httpd/snippets/options-ssl-apache.conf
|
||||||
|
|
||||||
|
Include /etc/httpd/snippets/proxy-headers.conf
|
||||||
|
|
||||||
|
ProxyPreserveHost On
|
||||||
|
|
||||||
|
ProxyRequests Off
|
||||||
|
ProxyVia Off
|
||||||
|
|
||||||
|
ProxyPass / http://anubis:3000
|
||||||
|
ProxyPassReverse / http://anubis:3000
|
||||||
|
</VirtualHost>
|
||||||
|
</IfModule>
|
||||||
1
test/apache/conf.d/listen-443-https.conf
Normal file
1
test/apache/conf.d/listen-443-https.conf
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Listen 443 https
|
||||||
23
test/apache/docker-compose.yaml
Normal file
23
test/apache/docker-compose.yaml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
services:
|
||||||
|
httpd:
|
||||||
|
image: xxxtest/httpd
|
||||||
|
build: .
|
||||||
|
volumes:
|
||||||
|
- "../shared/www:/var/www/httpd.local.cetacean.club"
|
||||||
|
- "../pki/httpd.local.cetacean.club:/etc/techaro/pki/httpd.local.cetacean.club/"
|
||||||
|
ports:
|
||||||
|
- 8080:80
|
||||||
|
- 8443:443
|
||||||
|
|
||||||
|
anubis:
|
||||||
|
image: git.xeserv.us/techaro/anubis:cel
|
||||||
|
environment:
|
||||||
|
BIND: ":3000"
|
||||||
|
TARGET: http://httpdebug:3000
|
||||||
|
POLICY_FNAME: /etc/techaro/anubis/less_paranoid.yaml
|
||||||
|
volumes:
|
||||||
|
- ../anubis_configs:/etc/techaro/anubis
|
||||||
|
|
||||||
|
httpdebug:
|
||||||
|
image: ghcr.io/xe/x/httpdebug
|
||||||
|
pull_policy: always
|
||||||
13
test/apache/snippets/options-ssl-apache.conf
Normal file
13
test/apache/snippets/options-ssl-apache.conf
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
SSLEngine on
|
||||||
|
|
||||||
|
# Intermediate configuration, tweak to your needs
|
||||||
|
SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
|
||||||
|
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
|
||||||
|
SSLHonorCipherOrder off
|
||||||
|
SSLSessionTickets off
|
||||||
|
|
||||||
|
SSLOptions +StrictRequire
|
||||||
|
|
||||||
|
# Add vhost name to log entries:
|
||||||
|
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" vhost_combined
|
||||||
|
LogFormat "%v %h %l %u %t \"%r\" %>s %b" vhost_common
|
||||||
3
test/apache/snippets/proxy-headers.conf
Normal file
3
test/apache/snippets/proxy-headers.conf
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
RequestHeader set "X-Real-Ip" expr=%{REMOTE_ADDR}
|
||||||
|
RequestHeader set "X-Forwarded-Proto" "https"
|
||||||
|
RequestHeader set "X-Http-Version" "%{SERVER_PROTOCOL}s"
|
||||||
22
test/apache/start.sh
Executable file
22
test/apache/start.sh
Executable file
@@ -0,0 +1,22 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# If the transient local TLS certificate doesn't exist, mint a new one
|
||||||
|
if [ ! -f ../pki/httpd.local.cetacean.club/cert.pem ]; then
|
||||||
|
# Subshell to contain the directory change
|
||||||
|
(
|
||||||
|
cd ../pki \
|
||||||
|
&& mkdir -p httpd.local.cetacean.club \
|
||||||
|
&& \
|
||||||
|
# Try using https://github.com/FiloSottile/mkcert for better DevEx,
|
||||||
|
# but fall back to using https://github.com/jsha/minica in case
|
||||||
|
# you don't have that installed.
|
||||||
|
(
|
||||||
|
mkcert \
|
||||||
|
--cert-file ./httpd.local.cetacean.club/cert.pem \
|
||||||
|
--key-file ./httpd.local.cetacean.club/key.pem httpd.local.cetacean.club \
|
||||||
|
|| go tool minica -domains httpd.local.cetacean.club
|
||||||
|
)
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker compose up --build
|
||||||
16
test/caddy/Caddyfile
Normal file
16
test/caddy/Caddyfile
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
:80 {
|
||||||
|
reverse_proxy http://anubis:3000 {
|
||||||
|
header_up X-Real-Ip {remote_host}
|
||||||
|
header_up X-Http-Version {http.request.proto}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:443 {
|
||||||
|
tls /etc/techaro/pki/caddy.local.cetacean.club/cert.pem /etc/techaro/pki/caddy.local.cetacean.club/key.pem
|
||||||
|
|
||||||
|
reverse_proxy http://anubis:3000 {
|
||||||
|
header_up X-Real-Ip {remote_host}
|
||||||
|
header_up X-Http-Version {http.request.proto}
|
||||||
|
header_up X-Tls-Version {http.request.tls.version}
|
||||||
|
}
|
||||||
|
}
|
||||||
9
test/caddy/Dockerfile
Normal file
9
test/caddy/Dockerfile
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# FROM caddy:2.10.0-builder AS builder
|
||||||
|
|
||||||
|
# RUN xcaddy build \
|
||||||
|
# --with github.com/lolPants/caddy-requestid
|
||||||
|
|
||||||
|
FROM caddy:2.10.0 AS run
|
||||||
|
|
||||||
|
# COPY --from=builder /usr/bin/caddy /usr/bin/caddy
|
||||||
|
COPY Caddyfile /etc/caddy/Caddyfile
|
||||||
22
test/caddy/docker-compose.yaml
Normal file
22
test/caddy/docker-compose.yaml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
services:
|
||||||
|
caddy:
|
||||||
|
image: xxxtest/caddy
|
||||||
|
build: .
|
||||||
|
ports:
|
||||||
|
- 8080:80
|
||||||
|
- 8443:443
|
||||||
|
volumes:
|
||||||
|
- "../pki/caddy.local.cetacean.club:/etc/techaro/pki/caddy.local.cetacean.club/"
|
||||||
|
|
||||||
|
anubis:
|
||||||
|
image: git.xeserv.us/techaro/anubis:cel
|
||||||
|
environment:
|
||||||
|
BIND: ":3000"
|
||||||
|
TARGET: http://httpdebug:3000
|
||||||
|
POLICY_FNAME: /etc/techaro/anubis/less_paranoid.yaml
|
||||||
|
volumes:
|
||||||
|
- ../anubis_configs:/etc/techaro/anubis
|
||||||
|
|
||||||
|
httpdebug:
|
||||||
|
image: ghcr.io/xe/x/httpdebug
|
||||||
|
pull_policy: always
|
||||||
22
test/caddy/start.sh
Executable file
22
test/caddy/start.sh
Executable file
@@ -0,0 +1,22 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# If the transient local TLS certificate doesn't exist, mint a new one
|
||||||
|
if [ ! -f ../pki/caddy.local.cetacean.club/cert.pem ]; then
|
||||||
|
# Subshell to contain the directory change
|
||||||
|
(
|
||||||
|
cd ../pki \
|
||||||
|
&& mkdir -p caddy.local.cetacean.club \
|
||||||
|
&& \
|
||||||
|
# Try using https://github.com/FiloSottile/mkcert for better DevEx,
|
||||||
|
# but fall back to using https://github.com/jsha/minica in case
|
||||||
|
# you don't have that installed.
|
||||||
|
(
|
||||||
|
mkcert \
|
||||||
|
--cert-file ./caddy.local.cetacean.club/cert.pem \
|
||||||
|
--key-file ./caddy.local.cetacean.club/key.pem caddy.local.cetacean.club \
|
||||||
|
|| go tool minica -domains caddy.local.cetacean.club
|
||||||
|
)
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker compose up --build
|
||||||
4
test/nginx/Dockerfile
Normal file
4
test/nginx/Dockerfile
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
FROM nginx
|
||||||
|
|
||||||
|
COPY conf.d/ /etc/nginx/conf.d/
|
||||||
|
COPY snippets /etc/nginx/snippets
|
||||||
10
test/nginx/conf.d/http.conf
Normal file
10
test/nginx/conf.d/http.conf
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
listen [::]:80;
|
||||||
|
server_name nginx.local.cetacean.club;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://anubis:3000;
|
||||||
|
include snippets/proxy_params;
|
||||||
|
}
|
||||||
|
}
|
||||||
14
test/nginx/conf.d/https.conf
Normal file
14
test/nginx/conf.d/https.conf
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
server {
|
||||||
|
listen 443 ssl http2;
|
||||||
|
listen [::]:443 ssl http2;
|
||||||
|
server_name nginx.local.cetacean.club;
|
||||||
|
|
||||||
|
ssl_certificate /etc/techaro/pki/nginx.local.cetacean.club/cert.pem;
|
||||||
|
ssl_certificate_key /etc/techaro/pki/nginx.local.cetacean.club/key.pem;
|
||||||
|
include snippets/ssl_params;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://anubis:3000;
|
||||||
|
include snippets/proxy_params;
|
||||||
|
}
|
||||||
|
}
|
||||||
22
test/nginx/docker-compose.yaml
Normal file
22
test/nginx/docker-compose.yaml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
services:
|
||||||
|
httpd:
|
||||||
|
image: xxxtest/nginx
|
||||||
|
build: .
|
||||||
|
volumes:
|
||||||
|
- "../pki/nginx.local.cetacean.club:/etc/techaro/pki/nginx.local.cetacean.club/"
|
||||||
|
ports:
|
||||||
|
- 8080:80
|
||||||
|
- 8443:443
|
||||||
|
|
||||||
|
anubis:
|
||||||
|
image: git.xeserv.us/techaro/anubis:cel
|
||||||
|
environment:
|
||||||
|
BIND: ":3000"
|
||||||
|
TARGET: http://httpdebug:3000
|
||||||
|
POLICY_FNAME: /etc/techaro/anubis/less_paranoid.yaml
|
||||||
|
volumes:
|
||||||
|
- ../anubis_configs:/etc/techaro/anubis
|
||||||
|
|
||||||
|
httpdebug:
|
||||||
|
image: ghcr.io/xe/x/httpdebug
|
||||||
|
pull_policy: always
|
||||||
7
test/nginx/snippets/proxy_params
Normal file
7
test/nginx/snippets/proxy_params
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Http-Version $server_protocol;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_set_header X-Request-Id $request_id;
|
||||||
|
proxy_set_header X-Forwarded-Host $host;
|
||||||
11
test/nginx/snippets/ssl_params
Normal file
11
test/nginx/snippets/ssl_params
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
ssl_protocols TLSv1.3;
|
||||||
|
ssl_prefer_server_ciphers on;
|
||||||
|
ssl_ciphers EECDH+AESGCM:EDH+AESGCM;
|
||||||
|
ssl_ecdh_curve secp384r1;
|
||||||
|
ssl_session_timeout 10m;
|
||||||
|
ssl_session_cache shared:SSL:10m;
|
||||||
|
ssl_session_tickets off;
|
||||||
|
ssl_stapling on;
|
||||||
|
ssl_stapling_verify on;
|
||||||
|
resolver 8.8.8.8 8.8.4.4 valid=300s;
|
||||||
|
resolver_timeout 5s;
|
||||||
22
test/nginx/start.sh
Executable file
22
test/nginx/start.sh
Executable file
@@ -0,0 +1,22 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# If the transient local TLS certificate doesn't exist, mint a new one
|
||||||
|
if [ ! -f ../pki/nginx.local.cetacean.club/cert.pem ]; then
|
||||||
|
# Subshell to contain the directory change
|
||||||
|
(
|
||||||
|
cd ../pki \
|
||||||
|
&& mkdir -p nginx.local.cetacean.club \
|
||||||
|
&& \
|
||||||
|
# Try using https://github.com/FiloSottile/mkcert for better DevEx,
|
||||||
|
# but fall back to using https://github.com/jsha/minica in case
|
||||||
|
# you don't have that installed.
|
||||||
|
(
|
||||||
|
mkcert \
|
||||||
|
--cert-file ./nginx.local.cetacean.club/cert.pem \
|
||||||
|
--key-file ./nginx.local.cetacean.club/key.pem nginx.local.cetacean.club \
|
||||||
|
|| go tool minica -domains nginx.local.cetacean.club
|
||||||
|
)
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker compose up --build
|
||||||
Reference in New Issue
Block a user