go-away

Self-hosted abuse detection and rule enforcement against low-effort mass AI scraping and bots.

Build Status Go Reference

This documentation is a work in progress. For now, see policy examples under examples/.

This Go package can be used as a command on git.gammaspectra.live/git/go-away/cmd/go-away or a library under git.gammaspectra.live/git/go-away/lib

Support

If you have some suggestion or issue, feel free to open a New Issue on the repository.

Pull Requests are encouraged and desired.

For real-time chat and other support join IRC on ##go-away on Libera.Chat. The channel may not be monitored at all times, feel free to ping the operators there.

Example policies

Forgejo

The policy file at examples/forgejo.yml provides a ready template to be used on your own Forgejo instance.

Important notes:

  • Edit the homesite rule, as it's targeted to common users or orgs on the instance. A better regex might be possible in the future.
  • Edit the http-cookie-check challenge, as this will fetch the listed backend with the given session cookie to check for user login.
  • Adjust the desired blocked networks or others. A template list of network ranges is provided, feel free to remove these if not needed.
  • Check the conditions and base rules to change your challenges offered and other ordering.
  • By default Googlebot / Bingbot / DuckDuckBot / Kagibot / Qwantbot / Yandexbot are allowed by useragent and network ranges.

Generic

The policy file at examples/generic.yml provides a baseline to place on any site, that can be modified to fit your needs.

Important notes:

  • Edit the homesite rule, as it's targeted to pages you always want to have available, like landing pages.
  • Edit the is-static-asset condition or the allow-static-resources rule to allow static file access as necessary.
  • If you have an API, add a PASS rule targeting it.
  • Check the conditions and base rules to change your challenges offered and other ordering.
  • Add or modify rules to target specific pages on your site as desired.
  • By default Googlebot / Bingbot / DuckDuckBot / Kagibot / Qwantbot / Yandexbot are allowed by useragent and network ranges.

Setup

It is recommended to have another reverse proxy above (for example Caddy, nginx, HAProxy) to handle HTTPs or similar.

go-away for now only accepts plaintext connections, although it can take HTTP/2 / h2c connections if desired over the same port.

Binary / Go

Requires Go 1.24+. Builds statically without CGo usage.

git clone https://git.gammaspectra.live/git/go-away.git && cd go-away

CGO_ENABLED=0 go build -pgo=auto -v -trimpath -o ./go-away ./cmd/go-away

# Run on port 8080, forwarding matching requests on git.example.com to http://forgejo:3000
./go-away --bind :8080 \
--backend git.example.com=http://forgejo:3000 \
--policy examples/forgejo.yml \
--challenge-template forgejo --challenge-template-theme forgejo-dark

Dockerfile

Available under Dockerfile. See the docker compose below for the environment variables.

docker compose

Example follows a hypothetical Forgejo server running on http://forgejo:3000 serving git.example.com

networks:
  forgejo:
    external: false
    
volumes:
  goaway_cache:
    
services:
  go-away:
    image: git.gammaspectra.live/git/go-away:latest
    restart: always
    ports:
      - "3000:8080"
    networks:
      - forgejo
    depends_on:
      - forgejo
    volumes:
      - "goaway_cache:/cache"
      - "./examples/forgejo.yml:/policy.yml:ro"
    environment:
      #GOAWAY_BIND: ":8080"
      # Supported tcp, unix, and proxy (for enabling PROXY module for request unwrapping)
      #GOAWAY_BIND_NETWORK: "tcp"
      #GOAWAY_SOCKET_MODE: "0770"
      
      # set to letsencrypt or other directory URL to enable HTTPS. Above ports will be TLS only.
      # enables request JA3N / JA4 client TLS fingerprinting
      # TLS fingerprints are served on X-TLS-Fingerprint-JA3N and X-TLS-Fingerprint-JA4 headers
      # TLS fingerprints can be matched against on CEL conditions
      #GOAWAY_ACME_AUTOCERT: ""
      
      # Cache path for several services like certificates and caching network ranges
      # Can be semi-ephemeral, recommended to be mapped to a permanent volume
      #GOAWAY_CACHE="/cache"
      
      # default is WARN, set to INFO to also see challenge successes and others
      #GOAWAY_SLOG_LEVEL: "INFO"
      
      # this value is used to sign cookies and challenges. by default a new one is generated each time
      # set to generate to create one, then set the same value across all your instances
      #GOAWAY_JWT_PRIVATE_KEY_SEED: ""
      
      # HTTP header that the client ip will be fetched from
      # Defaults to the connection ip itself, if set here make sure your upstream proxy sets this properly
      # Usually X-Forwarded-For is a good pick
      # Not necessary with GOAWAY_BIND_NETWORK: proxy
      GOAWAY_CLIENT_IP_HEADER: "X-Real-Ip"
      
      # HTTP header that go-away will set the obtained ip will be set to
      # If left empty, the header on GOAWAY_CLIENT_IP_HEADER will be left as-is
      #GOAWAY_BACKEND_IP_HEADER: ""
      
      GOAWAY_POLICY: "/policy.yml"
      
      # Template, and theme for the template to pick. defaults to an anubis-like one
      # An file path can be specified. See embed/templates for a few examples
      GOAWAY_CHALLENGE_TEMPLATE: forgejo
      GOAWAY_CHALLENGE_TEMPLATE_THEME: forgejo-dark
      
      # specify a DNSBL for usage in conditions. Defaults to DroneBL 
      # GOAWAY_DNSBL: "dnsbl.dronebl.org"
      
      GOAWAY_BACKEND: "git.example.com=http://forgejo:3000"
      
    # additional backends can be specified via more command arguments  
    # command: ["--backend", "ci.example.com=http://ci:3000"]

  forgejo:
    # etc.

Development

Compiling WASM runtime challenge modules

Custom WASM runtime modules follow the WASI wasip1 preview syscall API.

It is recommended using TinyGo to compile / refresh modules, and some function helpers are provided.

If you want to use a different language or compiler, enable wasip1 and the following interface must be exported:

// Allocation is a combination of pointer location in WASM memory and size of it
type Allocation uint64

func (p Allocation) Pointer() uint32 {
	return uint32(p >> 32)
}
func (p Allocation) Size() uint32 {
	return uint32(p)
}


// MakeChallenge MakeChallengeInput / MakeChallengeOutput are valid JSON.
// See lib/challenge/interface.go for a definition
func MakeChallenge(in Allocation[MakeChallengeInput]) Allocation[MakeChallengeOutput]

// VerifyChallenge VerifyChallengeInput is valid JSON.
// See lib/challenge/interface.go for a definition
func VerifyChallenge(in Allocation[VerifyChallengeInput]) VerifyChallengeOutput

func malloc(size uint32) uintptr
func free(size uintptr)

Modules will be recreated for each call, so there is no state leftover

Description
Self-hosted abuse detection and rule enforcement against low-effort mass AI scraping and bots.
Readme MIT 1,017 KiB
Languages
Go 94.3%
JavaScript 2.8%
Shell 1.2%
CSS 0.9%
Dockerfile 0.8%