diff --git a/.air.toml b/.air.toml index 3809007..4562ee7 100644 --- a/.air.toml +++ b/.air.toml @@ -2,7 +2,7 @@ root = "." tmp_dir = "var" [build] -cmd = "go build -o ./var/main ./cmd/anubis" +cmd = "go build -o ./var/main ./cmd/nuke" bin = "./var/main" args = ["--use-remote-address"] exclude_dir = ["var", "vendor", "docs", "node_modules"] diff --git a/.devcontainer/README.md b/.devcontainer/README.md index d0243de..d798b14 100644 --- a/.devcontainer/README.md +++ b/.devcontainer/README.md @@ -1,13 +1,13 @@ -# Anubis Dev Container +# nuke Dev Container -Anubis offers a [development container](https://containers.dev/) image in order to make it easier to contribute to the project. This image is based on [Xe/devcontainer-base/go](https://github.com/Xe/devcontainer-base/tree/main/src/go), which is based on Debian Bookworm with the following customizations: +nuke offers a [development container](https://containers.dev/) image in order to make it easier to contribute to the project. This image is based on [Xe/devcontainer-base/go](https://github.com/Xe/devcontainer-base/tree/main/src/go), which is based on Debian Bookworm with the following customizations: - [Fish](https://fishshell.com/) as the shell complete with a custom theme - [Go](https://go.dev) at the most recent stable version - [Node.js](https://nodejs.org/en) at the most recent stable version - [Atuin](https://atuin.sh/) to sync shell history between your host OS and the development container -- [Docker](https://docker.com) to manage and build Anubis container images from inside the development container -- [Ko](https://ko.build/) to build production-ready Anubis container images +- [Docker](https://docker.com) to manage and build nuke container images from inside the development container +- [Ko](https://ko.build/) to build production-ready nuke container images - [Neovim](https://neovim.io/) for use with Git This development container is tested and known to work with [Visual Studio Code](https://code.visualstudio.com/). If you run into problems with it outside of VS Code, please file an issue and let us know what editor you are using. diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 0602ff1..0eda0b1 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -2,11 +2,9 @@ // README at: https://github.com/devcontainers/templates/tree/main/src/debian { "name": "Dev", - "dockerComposeFile": [ - "./docker-compose.yaml" - ], + "dockerComposeFile": ["./docker-compose.yaml"], "service": "workspace", - "workspaceFolder": "/workspace/anubis", + "workspaceFolder": "/workspace/nuke", "postStartCommand": "bash ./.devcontainer/poststart.sh", "features": { "ghcr.io/xe/devcontainer-features/ko:1.1.0": {}, @@ -31,4 +29,4 @@ } } } -} \ No newline at end of file +} diff --git a/.devcontainer/docker-compose.yaml b/.devcontainer/docker-compose.yaml index d2fbc3b..40a189e 100644 --- a/.devcontainer/docker-compose.yaml +++ b/.devcontainer/docker-compose.yaml @@ -14,12 +14,12 @@ services: # VS Code workspace service workspace: - image: ghcr.io/techarohq/anubis/devcontainer + image: git.sad.ovh/sophie/nuke/devcontainer build: context: .. dockerfile: .devcontainer/Dockerfile volumes: - - ../:/workspace/anubis:cached + - ../:/workspace/nuke:cached environment: VALKEY_URL: redis://valkey:6379/0 #entrypoint: ["/usr/bin/sleep", "infinity"] diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index d5395a3..1d9d35b 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,5 +1,5 @@ blank_issues_enabled: false contact_links: - name: Security - url: https://techaro.lol/contact - about: Do not file security reports here. Email security@techaro.lol. + url: https://sad.ovh + about: Do not file security reports here. Email sophie@sad.ovh. diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index 4557c79..ad563f5 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -7,8 +7,8 @@ alibaba alrest amazonbot anthro -anubis -anubistest +nuke +nuketest apnic APNICRANDNETAU Applebot @@ -335,15 +335,13 @@ tarrif taviso tbn tbr -techaro -techarohq +sophie +sad.ovh telegrambot templ templruntime testarea Thancred -thoth -thothmock Tik Timpibot TLog @@ -375,7 +373,6 @@ websites Webzio whois wildbase -withthothmock wolfbeast wordpress workaround diff --git a/.github/workflows/docs-deploy.yml b/.github/workflows/docs-deploy.yml index 8f739cb..fffce13 100644 --- a/.github/workflows/docs-deploy.yml +++ b/.github/workflows/docs-deploy.yml @@ -13,7 +13,7 @@ permissions: jobs: build: - if: github.repository == 'TecharoHQ/anubis' + if: github.repository == 'sophie/nuke' runs-on: ubuntu-24.04 steps: @@ -28,14 +28,14 @@ jobs: uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 with: registry: ghcr.io - username: techarohq + username: sophie password: ${{ secrets.GITHUB_TOKEN }} - name: Docker meta id: meta uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0 with: - images: ghcr.io/techarohq/anubis/docs + images: git.sad.ovh/sophie/nuke/docs tags: | type=sha,enable=true,priority=100,prefix=,suffix=,format=long main @@ -64,4 +64,4 @@ jobs: env: KUBE_CONFIG: ${{ secrets.LIMSA_LOMINSA_KUBECONFIG }} with: - args: rollout restart -n default deploy/anubis-docs + args: rollout restart -n default deploy/nuke-docs diff --git a/.github/workflows/docs-test.yml b/.github/workflows/docs-test.yml index 96b0af5..9523926 100644 --- a/.github/workflows/docs-test.yml +++ b/.github/workflows/docs-test.yml @@ -24,7 +24,7 @@ jobs: id: meta uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0 with: - images: ghcr.io/techarohq/anubis/docs + images: git.sad.ovh/sophie/nuke/docs tags: | type=sha,enable=true,priority=100,prefix=,suffix=,format=long main diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 96a101e..96f0cf4 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -12,7 +12,7 @@ permissions: jobs: go_tests: - #runs-on: alrest-techarohq + #runs-on: alrest-sophie runs-on: ubuntu-24.04 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -26,10 +26,10 @@ jobs: - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: - node-version: '24.11.0' + node-version: "24.11.0" - uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0 with: - go-version: '1.25.4' + go-version: "1.25.4" - name: Cache playwright binaries uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2 diff --git a/.github/workflows/package-builds-stable.yml b/.github/workflows/package-builds-stable.yml index a77b77f..2f411c1 100644 --- a/.github/workflows/package-builds-stable.yml +++ b/.github/workflows/package-builds-stable.yml @@ -11,7 +11,7 @@ permissions: jobs: package_builds: - #runs-on: alrest-techarohq + #runs-on: alrest-sophie runs-on: ubuntu-24.04 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -27,10 +27,10 @@ jobs: - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: - node-version: '24.11.0' + node-version: "24.11.0" - uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0 with: - go-version: '1.25.4' + go-version: "1.25.4" - name: install node deps run: | diff --git a/.github/workflows/ssh-ci-runner-cron.yml b/.github/workflows/ssh-ci-runner-cron.yml index 4fb3e5a..bbbd4cf 100644 --- a/.github/workflows/ssh-ci-runner-cron.yml +++ b/.github/workflows/ssh-ci-runner-cron.yml @@ -14,7 +14,7 @@ permissions: jobs: ssh-ci-rebuild: - if: github.repository == 'TecharoHQ/anubis' + if: github.repository == 'sophie/nuke' runs-on: ubuntu-latest steps: - name: Checkout code diff --git a/.github/workflows/ssh-ci.yml b/.github/workflows/ssh-ci.yml index bb1e5d9..951b1ad 100644 --- a/.github/workflows/ssh-ci.yml +++ b/.github/workflows/ssh-ci.yml @@ -11,8 +11,8 @@ permissions: jobs: ssh: - if: github.repository == 'TecharoHQ/anubis' - runs-on: alrest-techarohq + if: github.repository == 'sophie/nuke' + runs-on: alrest-sophie strategy: matrix: host: @@ -37,7 +37,7 @@ jobs: - uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0 with: - go-version: '1.25.4' + go-version: "1.25.4" - name: Run CI run: go run ./utils/cmd/backoff-retry bash test/ssh-ci/rigging.sh ${{ matrix.host }} diff --git a/.ko.yaml b/.ko.yaml index 35c1fa0..2e6dd82 100644 --- a/.ko.yaml +++ b/.ko.yaml @@ -1,13 +1,13 @@ defaultBaseImage: cgr.dev/chainguard/static defaultPlatforms: -- linux/arm64 -- linux/amd64 -- linux/arm/v7 + - linux/arm64 + - linux/amd64 + - linux/arm/v7 builds: -- id: anubis - main: ./cmd/anubis - ldflags: - - -s -w - - -extldflags "-static" - - -X github.com/TecharoHQ/anubis.Version={{.Env.VERSION}} + - id: nuke + main: ./cmd/nuke + ldflags: + - -s -w + - -extldflags "-static" + - -X git.sad.ovh/sophie/nuke.Version={{.Env.VERSION}} diff --git a/.vscode/launch.json b/.vscode/launch.json index f6db59f..1f2881d 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,19 +9,19 @@ "type": "go", "request": "launch", "mode": "auto", - "program": "${fileDirname}" + "program": "${fileDirname}", }, { - "name": "Anubis [dev]", + "name": "Nuke [dev]", "command": "npm run dev", "request": "launch", - "type": "node-terminal" + "type": "node-terminal", }, { "name": "Start Docs", "command": "cd docs && npm ci && npm run start", "request": "launch", - "type": "node-terminal" - } - ] -} \ No newline at end of file + "type": "node-terminal", + }, + ], +} diff --git a/Makefile b/Makefile index 534ae5b..cd3f40f 100644 --- a/Makefile +++ b/Makefile @@ -17,9 +17,9 @@ assets: deps ./xess/build.sh build: assets - $(GO) build -o ./var/anubis ./cmd/anubis + $(GO) build -o ./var/nuke ./cmd/nuke $(GO) build -o ./var/robots2policy ./cmd/robots2policy - @echo "Anubis is now built to ./var/anubis" + @echo "nuke is now built to ./var/nuke" lint: assets $(GO) vet ./... @@ -27,8 +27,8 @@ lint: assets $(GO) tool govulncheck ./... prebaked-build: - $(GO) build -o ./var/anubis -ldflags "-X 'github.com/TecharoHQ/anubis.Version=$(VERSION)'" ./cmd/anubis - $(GO) build -o ./var/robots2policy -ldflags "-X 'github.com/TecharoHQ/anubis.Version=$(VERSION)'" ./cmd/robots2policy + $(GO) build -o ./var/nuke -ldflags "-X 'git.sad.ovh/sophie/nuke.Version=$(VERSION)'" ./cmd/nuke + $(GO) build -o ./var/robots2policy -ldflags "-X 'git.sad.ovh/sophie/nuke.Version=$(VERSION)'" ./cmd/robots2policy test: assets $(GO) test ./... diff --git a/README.md b/README.md index 6de0183..c36c423 100644 --- a/README.md +++ b/README.md @@ -1,100 +1,13 @@ -# Anubis +fork of anubis & rebranded "nuke" -
-
-
-
-
-
-
-### Gold Tier
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-## Overview
-
-Anubis is a Web AI Firewall Utility that [weighs the soul of your connection](https://en.wikipedia.org/wiki/Weighing_of_souls) using one or more challenges in order to protect upstream resources from scraper bots.
-
-This program is designed to help protect the small internet from the endless storm of requests that flood in from AI companies. Anubis is as lightweight as possible to ensure that everyone can afford to protect the communities closest to them.
-
-Anubis is a bit of a nuclear response. This will result in your website being blocked from smaller scrapers and may inhibit "good bots" like the Internet Archive. You can configure [bot policy definitions](./docs/docs/admin/policies.mdx) to explicitly allowlist them and we are working on a curated set of "known good" bots to allow for a compromise between discoverability and uptime.
-
-In most cases, you should not need this and can probably get by using Cloudflare to protect a given origin. However, for circumstances where you can't or won't use Cloudflare, Anubis is there for you.
-
-If you want to try this out, visit the Anubis documentation site at [anubis.techaro.lol](https://anubis.techaro.lol).
-
-## Support
-
-If you run into any issues running Anubis, please [open an issue](https://github.com/TecharoHQ/anubis/issues/new?template=Blank+issue). Please include all the information I would need to diagnose your issue.
-
-For live chat, please join the [Patreon](https://patreon.com/cadey) and ask in the Patron discord in the channel `#anubis`.
-
-## Star History
-
-
- Last updated: June 2025
- -In common with other websites, log files are stored on the web server - saving details such as the visitor's IP address, browser type, referring - page and time of visit.
- -Cookies may be used to remember visitor preferences when interacting - with the website.
- -Where registration is required, the visitor's email and a username - will be stored on the server.
- - -``` - -If this is insufficient, please [file an issue](https://github.com/TecharoHQ/anubis/issues/new) with a link to the relevant legislation for your country so that this feature can be amended and improved. - -### No-JS Challenge - -One of the first issues in Anubis before it was moved to the [TecharoHQ org](https://github.com/TecharoHQ) was a request [to support challenging browsers without using JavaScript](https://github.com/Xe/x/issues/651). This is a pretty challenging thing to do without rethinking how Anubis works from a fundamentally low level, and with v1.20.0, [Anubis finally has support for running without client-side JavaScript](https://github.com/TecharoHQ/anubis/issues/95) thanks to the [Meta Refresh](/docs/admin/configuration/challenges/metarefresh) challenge. - -When Anubis decides it needs to send a challenge to your browser, it sends a challenge page. Historically, this challenge page is [an HTML template](https://github.com/TecharoHQ/anubis/blob/main/web/index.templ) that kicks off some JavaScript, reads the challenge information out of the page body, and then solves it as fast as possible in order to let users see the website they want to visit. - -In v1.20.0, Anubis has a challenge registry to hold [different client challenge implementations](/docs/admin/configuration/challenges/). This allows us to implement anything we want as long as it can render a page to show a challenge and then check if the result is correct. This is going to be used to implement a WebAssembly-based proof of work option (one that will be way more efficient than the existing browser JS version), but as a proof of concept I implemented a simple challenge using [HTML ``](https://en.wikipedia.org/wiki/Meta_refresh). - -In my testing, this has worked with every browser I have thrown it at (including CLI browsers, the browser embedded in emacs, etc.). The default configuration of Anubis does use the [meta refresh challenge](/docs/admin/configuration/challenges/metarefresh) for [clients with a very low suspicion](/docs/admin/configuration/thresholds), but by default clients will be sent an [easy proof of work challenge](/docs/admin/configuration/challenges/proof-of-work). - -If the false positive rate of this challenge turns out to not be very high in practice, the meta refresh challenge will be enabled by default for browsers in future versions of Anubis. - -### `robots2policy` - -Anubis was created because crawler bots don't respect [`robots.txt` files](https://www.robotstxt.org/). Administrators have been working on refining and crafting their `robots.txt` files for years, and one common comment is that people don't know where to start crafting their own rules. - -Anubis now ships with a [`robots2policy` tool](/docs/admin/robots2policy) that lets you convert your `robots.txt` file to an Anubis policy. - -```text -robots2policy -input https://github.com/robots.txt -``` - -:::note - -If you installed Anubis from [an OS package](/docs/admin/native-install), you may need to run `anubis-robots2policy` instead of `robots2policy`. - -::: - -We hope that this will help you get started with Anubis faster. We are working on a version of this that will run in the documentation via WebAssembly. - -### Open Graph configuration is being moved to the policy file - -Anubis supports reading [Open Graph tags](/docs/admin/configuration/open-graph) from target services and returning them in challenge pages. This makes the right metadata show up when linking services protected by Anubis in chat applications or on social media. - -In order to test the migration of all of the configuration to the policy file, Open Graph configuration has been moved to the policy file. For more information, please read [the Open Graph configuration options](/docs/admin/configuration/open-graph#configuration-options). - -You can also set default Open Graph tags: - -```yaml -openGraph: - enabled: true - ttl: 24h - # If set, return these opengraph values instead of looking them up with - # the target service. - # - # Correlates to properties in https://ogp.me/ - override: - # og:title is required, it is the title of the website - "og:title": "Techaro Anubis" - "og:description": >- - Anubis is a Web AI Firewall Utility that helps you fight the bots - away so that you can maintain uptime at work! - "description": >- - Anubis is a Web AI Firewall Utility that helps you fight the bots - away so that you can maintain uptime at work! -``` - -## Improvements and optimizations - -One of the biggest improvements we've made in v1.20.0 is replacing [SHA-256 with xxhash](https://github.com/TecharoHQ/anubis/pull/676). Anubis uses hashes all over the place to help with identifying clients, matching against rules when allowing traffic through, in error messages sent to users, and more. Historically these have been done with [SHA-256](https://en.wikipedia.org/wiki/SHA-2), however this has been having a mild performance impact in real-world use. As a result, we now use [xxhash](https://xxhash.com/) when possible. This makes policy matching 3x faster in some scenarios and reduces memory usage across the board. - -Anubis now uses [bart](https://pkg.go.dev/github.com/gaissmai/bart) for doing IP address matching when you specify addresses in a `remote_address` check configuration or when you are matching against [advanced checks](/docs/admin/thoth). This uses the same kind of IP address routing configuration that your OS kernel does, making it very fast to query information about IP addresses. This makes IP address range matches anywhere from 3-14 times faster depending on the number of addresses it needs to match against. For more information and benchmarks, check out [@JasonLovesDoggo](https://github.com/JasonLovesDoggo)'s PR: [perf: replace cidranger with bart for significant performance improvements #675](https://github.com/TecharoHQ/anubis/pull/675). - -## What's up next? - -v1.21.0 is already shaping up to be a massive improvement as Anubis adds [internationalization](https://en.wikipedia.org/wiki/Internationalization) support, allowing your users to see its messages in the language they're most comfortable with. - -So far Anubis supports the following languages: - -- English (Simplified and Traditional) -- French -- Portuguese (Brazil) -- Spanish - -If you want to contribute translations, please [file an issue](https://github.com/TecharoHQ/anubis/issues/new) with your language of choice or submit a pull request to [the `lib/localization/locales` folder](https://github.com/TecharoHQ/anubis/tree/main/lib/localization/locales). We are about to introduce features to the translation stack, so you may want to hold off a hot minute, but we welcome any and all contributions to making Anubis useful to a global audience. - -Other things we plan to do: - -- Move configuration to the policy file -- Support reloading the policy file at runtime without having to restart Anubis -- Detecting if a client is "brand new" -- A [Valkey](https://valkey.io/)-backed store for sharing information between instances of Anubis -- Augmenting No-JS support in the paid product -- TLS fingerprinting -- Automated testing improvements in CI (FreeBSD CI support, better automated integration/functional testing, etc.) - -## Conclusion - -I hope that these features let you get the same Anubis power you've come to know and love and increases the things you can do with it! I've been really excited to ship [thresholds](/docs/admin/configuration/thresholds) and the cloud-based services for Anubis. - -If you run into any problems, please [file an issue](https://github.com/TecharoHQ/anubis/issues/new). Otherwise, have a good day and get back to making your communities great. diff --git a/docs/blog/2025-06-27-release-1.20.0/sunburst.webp b/docs/blog/2025-06-27-release-1.20.0/sunburst.webp deleted file mode 100644 index b934a4f..0000000 Binary files a/docs/blog/2025-06-27-release-1.20.0/sunburst.webp and /dev/null differ diff --git a/docs/blog/2025-07-09-incident-report/index.mdx b/docs/blog/2025-07-09-incident-report/index.mdx deleted file mode 100644 index d42e1ed..0000000 --- a/docs/blog/2025-07-09-incident-report/index.mdx +++ /dev/null @@ -1,105 +0,0 @@ ---- -slug: incident/TI-20250709-0001 -title: "TI-20250709-0001: IPv4 traffic failures for Techaro services" -authors: [xe] -tags: [incident] -image: ./window-portal.jpg ---- - - - -Techaro services were down for IPv4 traffic on July 9th, 2025. This blogpost is a report of what happened, what actions were taken to resolve the situation, and what actions are being done in the near future to prevent this problem. Enjoy this incident report! - -{/* truncate */} - -:::note - -In other companies, this kind of documentation would be kept internal. At Techaro, we believe that you deserve radical candor and the truth. As such, we are proving our lofty words with actions by publishing details about how things go wrong publicly. - -Everything past this point follows my standard incident root cause meeting template. - -::: - -This incident report will focus on the services affected, timeline of what happened at which stage of the incident, where we got lucky, the root cause analysis, and what action items are being planned or taken to prevent this from happening in the future. - -## Timeline - -All events take place on July 9th, 2025. - -| Time (UTC) | Description | -| :--------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| 12:32 | Uptime Kuma reports that another unrelated website on the same cluster was timing out. | -| 12:33 | Uptime Kuma reports that Thoth's production endpoint is failing gRPC health checks. | -| 12:35 | Investigation begins, [announcement made on Xe's Bluesky](https://bsky.app/profile/xeiaso.net/post/3ltjtdczpwc2x) due to the impact including their personal blog. | -| 12:39 | `nginx-ingress` logs on the production cluster show IPv6 traffic but an abrupt cutoff in IPv4 traffic around 12:32 UTC. Ticket is opened with the hosting provider. | -| 12:41 | IPv4 traffic resumes long enough for Uptime Kuma to report uptime, but then immediately fails again. | -| 12:46 | IPv4 traffic resumes long enough for Uptime Kuma to report uptime, but then immediately fails again. (repeat instances of this have been scrubbed, but it happened about every 5-10 minutes) | -| 12:48 | First reply from the hosting provider. | -| 12:57 | Reply to hosting provider, ask to reboot the load balancer. | -| 13:00 | Incident responder because busy due to a meeting under the belief that the downtime was out of their control and that uptime monitoring software would let them know if it came back up. | -| 13:20 | Incident responder ended meeting and went back to monitoring downtime and preparing this document. | -| 13:34 | IPv4 traffic starts to show up in the `ingress-nginx` logs. | -| 13:35 | All services start to report healthy. Incident status changes to monitoring. | -| 13:48 | Incident closed. | -| 14:07 | Incident re-opened. Issues seem to be manifesting as BGP issues in the upstream provider. | -| 14:10 | IPv4 traffic resumes and then stops. | -| 14:18 | IPv4 traffic resumes again. Incident status changes to monitoring. | -| 14:40 | Incident closed. | - -## Services affected - -| Service name | User impact | -| :-------------------------------------------------- | :----------------- | -| [Anubis Docs](https://anubis.techaro.lol) (IPv4) | Connection timeout | -| [Anubis Docs](https://anubis.techaro.lol) (IPv6) | None | -| [Thoth](/docs/admin/thoth/) (IPv4) | Connection timeout | -| [Thoth](/docs/admin/thoth/) (IPv6) | None | -| Other websites colocated on the same cluster (IPv4) | Connection timeout | -| Other websites colocated on the same cluster (IPv6) | None | - -## Root cause analysis - -In simplify server management, Techaro runs a [Kubernetes](https://kubernetes.io/) cluster on [Vultr VKE](https://www.vultr.com/kubernetes/) (Vultr Kubernetes Engine). When you do this, Vultr needs to provision a [load balancer](https://docs.vultr.com/how-to-use-a-vultr-load-balancer-with-vke) to bridge the gap between the outside world and the Kubernetes world, kinda like this: - -```mermaid ---- -title: Overall architecture ---- - -flowchart LR - UT(User Traffic) - subgraph Provider Infrastructure - LB[Load Balancer] - end - subgraph Kubernetes - IN(ingress-nginx) - TH(Thoth) - AN(Anubis Docs) - OS(Other sites) - - IN --> TH - IN --> AN - IN --> OS - end - - UT --> LB --> IN -``` - -Techaro controls everything inside the Kubernetes side of that diagram. Anything else is out of our control. That load balancer is routed to the public internet via [Border Gateway Protocol (BGP)](https://en.wikipedia.org/wiki/Border_Gateway_Protocol). - -If there is an interruption with the BGP sessions in the upstream provider, this can manifest as things either not working or inconsistently working. This is made more difficult by the fact that the IPv4 and IPv6 internets are technically separate networks. With this in mind, it's very possible to have IPv4 traffic fail but not IPv6 traffic. - -The root cause is that the hosting provider we use for production services had flapping IPv4 BGP sessions in its Toronto region. When this happens all we can do is open a ticket and wait for it to come back up. - -## Where we got lucky - -The Uptime Kuma instance that caught this incident runs on an IPv4-only network. If it was dual stack, this would not have been caught as quickly. - -The `ingress-nginx` logs print IP addresses of remote clients to the log feed. If this was not the case, it would be much more difficult to find this error. - -## Action items - -- A single instance of downtime like this is not enough reason to move providers. Moving providers because of this is thus out of scope. -- Techaro needs a status page hosted on a different cloud provider than is used for the production cluster (`TecharoHQ/TODO#6`). -- Health checks for IPv4 and IPv6 traffic need to be created (`TecharoHQ/TODO#7`). -- Remove the requirement for [Anubis to pass Thoth health checks before it can start if Thoth is enabled](https://github.com/TecharoHQ/anubis/pull/794). diff --git a/docs/blog/2025-07-09-incident-report/window-portal.jpg b/docs/blog/2025-07-09-incident-report/window-portal.jpg deleted file mode 100644 index 04647cf..0000000 Binary files a/docs/blog/2025-07-09-incident-report/window-portal.jpg and /dev/null differ diff --git a/docs/blog/2025-07-22-release-1.21.1/anubis-i18n.webp b/docs/blog/2025-07-22-release-1.21.1/anubis-i18n.webp deleted file mode 100644 index 351d6e3..0000000 Binary files a/docs/blog/2025-07-22-release-1.21.1/anubis-i18n.webp and /dev/null differ diff --git a/docs/blog/2025-07-22-release-1.21.1/index.mdx b/docs/blog/2025-07-22-release-1.21.1/index.mdx deleted file mode 100644 index 237edf6..0000000 --- a/docs/blog/2025-07-22-release-1.21.1/index.mdx +++ /dev/null @@ -1,369 +0,0 @@ ---- -slug: release/v1.21.1 -title: Anubis v1.21.1 is now available! -authors: [xe] -tags: [release] -image: anubis-i18n.webp ---- - - - -Hey all! - -Recently we released [Anubis v1.21.1: Minfilia Warde (Echo 1)](https://github.com/TecharoHQ/anubis/releases/tag/v1.21.1). This is a fairly meaty release and like [last time](../2025-06-27-release-1.20.0/index.mdx) this blogpost will tell you what you need to know before you update. Kick back, get some popcorn and let's dig into this! - -{/* truncate */} - -In this release, Anubis becomes internationalized, gains the ability to use system load as input to issuing challenges, finally fixes the "invalid response" after "success" bug, and more! Please read these notes before upgrading as the changes are big enough that administrators should take action to ensure that the upgrade goes smoothly. - -This release is brought to you by [FreeCAD](https://www.freecad.org/), an open-source computer aided design tool that lets you design things for the real world. - -## What's in this release? - -The biggest change is that the ["invalid response" after "success" bug](https://github.com/TecharoHQ/anubis/issues/564) is now finally fixed for good by totally rewriting how [Anubis' challenge issuance flow works](#challenge-flow-v2). - -This release gives Anubis the following features: - -- [Internationalization support](#internationalization), allowing Anubis to render its messages in the human language you speak. -- Anubis now supports the [`missingHeader`](#missingHeader-function) function to assert the absence of headers in requests. -- Anubis now has the ability to [store data persistently on the server](#persistent-data-storage). -- Anubis can use [the system load average](#load-average-checks) as a factor to determine if it needs to filter traffic or not. -- Add `COOKIE_SECURE` option to set the cookie [Secure flag](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Cookies#block_access_to_your_cookies) -- Sets cookie defaults to use [SameSite: None](https://web.dev/articles/samesite-cookies-explained) -- Allow [Common Crawl](https://commoncrawl.org/) by default so scrapers have less incentive to scrape -- Add `/healthz` metrics route for use in platform-based health checks. -- Start exposing JA4H fingerprints for later use in CEL expressions. - -And this release also fixes the following bugs: - -- [Challenge issuance has been totally rewritten](#challenge-flow-v2) to finally squash the infamous ["invalid response" after "success" bug](https://github.com/TecharoHQ/anubis/issues/564) for good. -- In order to reduce confusion, the "Success" interstitial that shows up when you pass a proof of work challenge has been removed. -- Don't block Anubis starting up if [Thoth](/docs/admin/thoth/) health checks fail. -- The "Try again" button on the error page has been fixed. Previously it meant "try the solution again" instead of "try the challenge again". -- In certain cases, a user could be stuck with a test cookie that is invalid, locking them out of the service for up to half an hour. This has been fixed with better validation of this case and clearing the cookie. -- "Proof of work" has been removed from the branding due to some users having extremely negative connotations with it. - -We try to avoid introducing breaking changes as much as possible, but these are the changes that may be relevant for you as an administrator: - -- The [challenge format](#challenge-format-change) has been changed in order to account for [the new challenge issuance flow](#challenge-flow-v2). -- The [systemd service `RuntimeDirectory` has been changed](#breaking-change-systemd-runtimedirectory-change). - -### Sponsoring the project - -If you rely on Anubis to keep your website safe, please consider sponsoring the project on [GitHub Sponsors](https://github.com/sponsors/Xe) or [Patreon](https://patreon.com/cadey). Funding helps pay hosting bills and offset the time spent on making this project the best it can be. Every little bit helps and when enough money is raised, [I can make Anubis my full-time job](https://github.com/TecharoHQ/anubis/discussions/278). - -Once this pie chart is at 100%, I can start to reduce my hours at my day job as most of my needs will be met (pre-tax): - -```mermaid -pie title Funding update - "GitHub Sponsors" : 29 - "Patreon" : 14 - "Remaining" : 56 -``` - -I am waiting to hear back from NLNet on if Anubis was selected for funding or not. Let's hope it is! - -## New features - -### Internationalization - -Anubis now supports localized responses. Locales can be added in [lib/localization/locales/](https://github.com/TecharoHQ/anubis/tree/main/lib/localization/locales). This release includes support for the following languages: - -- [Brazilian Portuguese](https://github.com/TecharoHQ/anubis/pull/726) -- [Chinese (Simplified)](https://github.com/TecharoHQ/anubis/pull/774) -- [Chinese (Traditional)](https://github.com/TecharoHQ/anubis/pull/759) -- [Czech](https://github.com/TecharoHQ/anubis/pull/849) -- English -- [Estonian](https://github.com/TecharoHQ/anubis/pull/783) -- [Filipino](https://github.com/TecharoHQ/anubis/pull/775) -- [Finnish](https://github.com/TecharoHQ/anubis/pull/863) -- [French](https://github.com/TecharoHQ/anubis/pull/716) -- [German](https://github.com/TecharoHQ/anubis/pull/741) -- [Japanese](https://github.com/TecharoHQ/anubis/pull/772) -- [Icelandic](https://github.com/TecharoHQ/anubis/pull/780) -- [Italian](https://github.com/TecharoHQ/anubis/pull/778) -- [Norwegian](https://github.com/TecharoHQ/anubis/pull/855) -- [Russian](https://github.com/TecharoHQ/anubis/pull/882) -- [Spanish](https://github.com/TecharoHQ/anubis/pull/716) -- [Turkish](https://github.com/TecharoHQ/anubis/pull/751) - -If facts or local regulations demand, you can set Anubis default language with the `FORCED_LANGUAGE` environment variable or the `--forced-language` command line argument: - -```sh -FORCED_LANGUAGE=de -``` - -## Big ticket bug fixes - -These issues affect every user of Anubis. Administrators should upgrade Anubis as soon as possible to mitigate them. - -### Fix event loop thrashing when solving a proof of work challenge - -Anubis has a progress bar so that users can have something moving while it works. This gives users more confidence that something is happening and that the website is not being malicious with CPU usage. However, the way it was implemented way back in [#87](https://github.com/TecharoHQ/anubis/pull/87) had a subtle bug: - -```js -if ( - (nonce > oldNonce) | 1023 && // we've wrapped past 1024 - (nonce >> 10) % threads === threadId // and it's our turn -) { - postMessage(nonce); -} -``` - -The logic here looks fine but is subtly wrong as was reported in [#877](https://github.com/TecharoHQ/anubis/issues/877) by the main Pale Moon developer. - -For context, `nonce` is a counter that increments by the worker count every loop. This is intended to spread the load between CPU cores as such: - -| Iteration | Worker ID | Nonce | -| :-------- | :-------- | :---- | -| 1 | 0 | 0 | -| 1 | 1 | 1 | -| 2 | 0 | 2 | -| 2 | 1 | 3 | - -And so on. This makes the proof of work challenge as fast as it can possibly be so that Anubis quickly goes away and you can enjoy the service it is protecting. - -The incorrect part of this is the boolean logic, specifically the part with the bitwise or `|`. I think the intent was to use a logical or (`||`), but this had the effect of making the `postMessage` handler fire on every iteration. The intent of this snippet (as the comment clearly indicates) is to make sure that the main event loop is only updated with the worker status every 1024 iterations per worker. This had the opposite effect, causing a lot of messages to be sent from workers to the parent JavaScript context. - -This is bad for the event loop. - -Instead, I have ripped out that statement and replaced it with a much simpler increment only counter that fires every 1024 iterations. Additionally, only the first thread communicates back to the parent process. This does mean that in theory the other workers could be ahead of the first thread (posting a message out of a worker has a nonzero cost), but in practice I don't think this will be as much of an issue as the current behaviour is. - -The root cause of the stack exhaustion is likely the pressure caused by all of the postMessage futures piling up. Maybe the larger stack size in 64 bit environments is causing this to be fine there, maybe it's some combination of newer hardware in 64 bit systems making this not be as much of a problem due to it being able to handle events fast enough to keep up with the pressure. - -Either way, thanks much to [@wolfbeast](https://github.com/wolfbeast) and the Pale Moon community for finding this. This will make Anubis faster for everyone! - -### Fix potential memory leak when discovering a solution - -In some cases, the parallel solution finder in Anubis could cause all of the worker promises to leak due to the fact the promises were being improperly terminated. A recursion bomb happens in the following scenario: - -1. A worker sends a message indicating it found a solution to the proof of work challenge. -2. The `onmessage` handler for that worker calls `terminate()` -3. Inside `terminate()`, the parent process loops through all other workers and calls `w.terminate()` on them. -4. It's possible that terminating a worker could lead to the `onerror` event handler. -5. This would create a recursive loop of `onmessage` -> `terminate` -> `onerror` -> `terminate` -> `onerror` and so on. - -This infinite recursion quickly consumes all available stack space, but this has never been noticed in development because all of my computers have at least 64Gi of ram provisioned to them under the axiom paying for more ram is cheaper than paying in my time spent having to work around not having enough ram. Additionally, ia32 has a smaller base stack size, which means that they will run into this issue much sooner than users on other CPU architectures will. - -The fix adds a boolean `settled` flag to prevent termination from running more than once. - -## Expressions features - -Anubis v1.21.1 adds additional [expressions](/docs/admin/configuration/expressions) features so that you can make your request matching even more granular. - -### `missingHeader` function - -Anubis [expressions](/docs/admin/configuration/expressions) have [a few functions exposed](/docs/admin/configuration/expressions/#functions-exposed-to-anubis-expressions). Anubis v1.21.1 adds the `missingHeader` function, allowing you to assert the _absence_ of a header in requests. - -Let's say you're getting a lot of requests from clients that are pretending to be Google Chrome. Google Chrome sends a few signals to web servers, the main one of them is the [`Sec-Ch-Ua`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Sec-CH-UA). Sec-CH-UA is part of Google's [User Agent Client Hints](https://wicg.github.io/ua-client-hints/#sec-ch-ua) proposal, but it being present is a sign that the client is more likely Google Chrome than not. With the `missingHeader` function, you can write a rule to [add weight](/docs/admin/policies/#request-weight) to requests without `Sec-Ch-Ua` that claim to be Google Chrome. - -```yaml -# Adds weight clients that claim to be Google Chrome without setting Sec-Ch-Ua -- name: old-chrome - action: WEIGH - weight: - adjust: 10 - expression: - all: - - userAgent.matches("Chrome/[1-9][0-9]?\\.0\\.0\\.0") - - missingHeader(headers, "Sec-Ch-Ua") -``` - -When combined with [weight thresholds](/docs/admin/configuration/thresholds), this allows you to make requests that don't match the signature of Google Chrome more suspicious, which will make them have a more difficult challenge. - -### Load average checks - -Anubis can dynamically take action [based on the system load average](/docs/admin/configuration/expressions/#using-the-system-load-average), allowing you to write rules like this: - -```yaml -## System load based checks. -# If the system is under high load for the last minute, add weight. -- name: high-load-average - action: WEIGH - expression: load_1m >= 10.0 # make sure to end the load comparison in a .0 - weight: - adjust: 20 - -# If it is not for the last 15 minutes, remove weight. -- name: low-load-average - action: WEIGH - expression: load_15m <= 4.0 # make sure to end the load comparison in a .0 - weight: - adjust: -10 -``` - -Something to keep in mind about system load average is that it is not aware of the number of cores the system has. If you have a 16 core system that has 16 processes running but none of them is hogging the CPU, then you will get a load average below 16. If you are in doubt, make your "high load" metric at least two times the number of CPU cores and your "low load" metric at least half of the number of CPU cores. For example: - -| Kind | Core count | Load threshold | -| --------: | :--------- | :------------- | -| high load | 4 | `8.0` | -| low load | 4 | `2.0` | -| high load | 16 | `32.0` | -| low load | 16 | `8` | - -Also keep in mind that this does not account for other kinds of latency like I/O latency or downstream API response latency. A system can have its web applications unresponsive due to high latency from a MySQL server but still have that web application server report a load near or at zero. - -:::note - -This does not work if you are using Kubernetes. - -::: - -When combined with [weight thresholds](/docs/admin/configuration/thresholds), this allows you to make incoming sessions "back off" while the server is under high load. - -## Challenge flow v2 - -The main goal of Anubis is to weigh the risks of incoming requests in order to protect upstream resources against abusive clients like badly written scrapers. In order to separate "good" clients (like users wanting to learn from a website's content) from "bad" clients, Anubis issues [challenges](/docs/admin/configuration/challenges/). - -Previously the Anubis challenge flow looked like this: - -```mermaid ---- -title: Old Anubis challenge flow ---- -flowchart LR - user(User Browser) - subgraph Anubis - mIC{Challenge?} - ic(Issue Challenge) - rp(Proxy to service) - mIC -->|User needs a challenge| ic - mIC -->|User does not need a challenge| rp - end - target(Target Service) - rp --> target - user --> mIC - ic -->|Pass a challenge| user - target -->|Site data| users -``` - -In order to issue a challenge, Anubis generated a challenge string based on request metadata that we assumed wouldn't drastically change between requests, including but not limited to: - -- The client's User-Agent string. -- The client [`Accept-Language` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Accept-Language) value. -- The client's IP address. - -Anubis also didn't store any information about challenges so that it can remain lightweight and handle the onslaught of requests from scrapers. The assumption was that the challenge string function was idempotent per client across time. What actually ended up happening was something like this: - -```mermaid ---- -title: Anubis challenge string idempotency ---- -sequenceDiagram - User->>+Anubis: GET /wiki/some-page - Anubis->>+Make Challenge: Generate a challenge string - Make Challenge->>-Anubis: Challenge string: taco salad - Anubis->>-User: HTTP 401 solve a challenge - User->>+Anubis: GET internal-api/pass-challenge - Anubis->>+Make Challenge: Generate a challenge string - Make Challenge->>-Anubis: Challenge string: burrito bar - Anubis->>+User: Error: invalid response -``` - -Various attempts were made to fix this. All of these ended up failing. Many difficulties were discovered including but not limited to: - -- Removing `Accept-Language` from consideration because [Chrome randomizes the contents of `Accept-Language` to reduce fingerprinting](https://github.com/explainers-by-googlers/reduce-accept-language), a behaviour which [causes a lot of confusion](https://www.reddit.com/r/chrome/comments/nhpnez/google_chrome_is_randomly_switching_languages_on/) for users with multiple system languages selected. -- [IPv6 privacy extensions](https://www.internetsociety.org/resources/deploy360/2014/privacy-extensions-for-ipv6-slaac/) mean that each request could be coming from a different IP address (at least one legitimate user in the wild has been observed to have a different IP address per TCP session across an entire `/48`). -- Some [US mobile phone carriers make it too easy for your IP address to drastically change](https://news.ycombinator.com/item?id=32038215) without user input. -- [Happy eyeballs](https://en.wikipedia.org/wiki/Happy_Eyeballs) means that some requests can come in over IPv4 and some requests can come in over IPv6. -- To make things worse, you can't even assert that users are from the same [BGP autonomous system]({challenge}
-{combinedData}
-{renderHash()}
-