Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
version: 2

updates:
- package-ecosystem: "gomod"
directory: "/"
schedule:
interval: "daily"
open-pull-requests-limit: 1
groups:
go-dependencies:
patterns:
- "*"

- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 1
groups:
github-actions-dependencies:
patterns:
- "*"
91 changes: 91 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
name: CI

on:
push:
branches:
- main
pull_request:
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
GOLANGCI_LINT_VERSION: "v2.12.2"
YAEGI_VERSION: "v0.16.1"
GOPATH: ${{ github.workspace }}/go
REPOPATH: ${{ github.workspace }}/go/src/github.com/fosrl/badger

jobs:
ci:
name: Go CI
runs-on: ubuntu-latest

defaults:
run:
working-directory: ${{ env.REPOPATH }}

steps:
- name: Check out code
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
path: go/src/github.com/fosrl/badger
persist-credentials: false
fetch-depth: 2

- name: Set up Go
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version-file: go/src/github.com/fosrl/badger/.go-version

- name: Show Go version
run: go version

- name: Run CI checks
run: make ci

- name: Run golangci-lint
uses: golangci/golangci-lint-action@82606bf257cbaff209d206a39f5134f0cfbfd2ee # v9.2.1
continue-on-error: true
with:
version: ${{ env.GOLANGCI_LINT_VERSION }}
working-directory: ${{ env.REPOPATH }}

- name: Validate Traefik plugin metadata
run: |
test -f .traefik.yml
grep -q '^displayName:' .traefik.yml
grep -q '^type:' .traefik.yml
grep -q '^import:' .traefik.yml
grep -q '^testData:' .traefik.yml
grep -q 'github.com/fosrl/badger' .traefik.yml

yaegi:
name: Yaegi compatibility
runs-on: ubuntu-latest

defaults:
run:
working-directory: ${{ env.REPOPATH }}

steps:
- name: Check out code
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
path: go/src/github.com/fosrl/badger
persist-credentials: false
fetch-depth: 2

- name: Set up Go
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version-file: go/src/github.com/fosrl/badger/.go-version

- name: Run Yaegi compatibility test
env:
GOPATH: ${{ env.GOPATH }}
run: make yaegi-test
24 changes: 22 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,22 @@
go.sum
.DS_Store
.DS_Store
.idea
*.iml

vendor/*
bin/*
dist/*
# Yaegi / Traefik local-plugin runtime work directory.
plugins-local/*

# Go coverage directories (GOCOVERDIR)
.cover/
coverage.out
coverage.txt
*.cover.out
*_coverage.out
unit.out
int.out
nohup.out

*.test
*.exe
2 changes: 1 addition & 1 deletion .go-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.25
1.26.4
156 changes: 156 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
# yaml-language-server: $schema=https://golangci-lint.run/jsonschema/golangci.jsonschema.json

version: "2"
run:
timeout: 3m

linters:
default: standard
enable:
# Existing / default-critical checks.
- govet
- staticcheck
- ineffassign
- unused
- misspell

# Error handling / correctness.
- errcheck
- errorlint
- errname
- nilerr
- nilnesserr
- unconvert
- unparam
- wastedassign

# HTTP / middleware safety.
- bodyclose
- noctx
# - canonicalheader # Too noisy, especially for tests.

# Security.
- gosec
- bidichk

# Maintainability, but with relaxed thresholds.
- revive
- gocyclo
- funlen
- goconst
- gocritic

# Dependency/import hygiene.
- depguard
- gomoddirectives

# Comments / TODO handling.
- godox

settings:
govet:
enable-all: true
disable:
# Too noisy
- fieldalignment

misspell:
locale: US

gocyclo:
min-complexity: 20

funlen:
lines: -1
statements: 80

goconst:
# Avoid noisy suggestions for small repeated strings.
min-len: 5
min-occurrences: 4

godox:
keywords:
- FIXME
- BUG

depguard:
rules:
main:
files:
- $all
- "!$test"
allow:
- $gostd
- github.com/fosrl/badger/ips
- github.com/fosrl/badger/version

tests:
files:
- $test
allow:
- $gostd
- github.com/fosrl/badger
- github.com/fosrl/badger/ips
- github.com/fosrl/badger/version

revive:
rules:
- name: blank-imports
- name: context-as-argument
- name: context-keys-type
- name: dot-imports
- name: error-return
- name: error-strings
- name: error-naming
- name: errorf
- name: exported
disabled: true
- name: if-return
- name: increment-decrement
- name: indent-error-flow
- name: package-comments
disabled: true
- name: range
- name: receiver-naming
- name: redefines-builtin-id
- name: struct-tag
- name: superfluous-else
- name: time-naming
- name: unreachable-code
- name: unused-parameter
disabled: true
- name: var-declaration
- name: var-naming

exclusions:
# Keep default golangci-lint exclusions enabled, but add repo-specific rules.
generated: strict
presets:
- comments
- common-false-positives
- legacy
- std-error-handling

rules:
# Test files are allowed to be longer and more repetitive.
- path: '(.+)_test\.go'
linters:
- funlen
- goconst
- dupl
- noctx

# Cloudflare IP list is intentionally a large literal allowlist.
- path: '^ips/ips\.go$'
linters:
- goconst
- funlen
- revive

issues:
max-issues-per-linter: 0
max-same-issues: 0

output:
show-stats: true
51 changes: 51 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
YAEGI_VERSION ?= v0.16.1
GOVULNCHECK_VERSION ?= v1.3.0

.PHONY: fmt tidy vet lint test vulncheck vendor yaegi-test ci ci-full clean

fmt:
gofmt -w .

tidy:
go mod tidy

vet:
go vet ./...

lint:
golangci-lint run

test:
go test -race -cover ./...

vulncheck:
go install golang.org/x/vuln/cmd/govulncheck@$(GOVULNCHECK_VERSION)
govulncheck ./...

vendor:
go mod vendor

# Yaegi compatibility check. Vendors dependencies first so Yaegi can resolve
# local sub-packages (e.g. github.com/fosrl/badger/ips) without needing a
# plugins-local copy layout. Must be run from a GOPATH-compatible directory
# (go/src/github.com/fosrl/badger) for local sub-package resolution.
yaegi-test: vendor
go run github.com/traefik/yaegi/cmd/yaegi@$(YAEGI_VERSION) test -v .

# Reproduce the CI checks locally (excluding yaegi and lint).
ci:
test -z "$$(gofmt -l .)"
go mod tidy
git diff --exit-code -- go.mod go.sum
go vet ./...
go test -race -cover ./...
go install golang.org/x/vuln/cmd/govulncheck@$(GOVULNCHECK_VERSION)
govulncheck ./...

# Full CI including lint and yaegi compatibility check.
ci-full: ci
$(MAKE) lint
$(MAKE) yaegi-test

clean:
rm -rf vendor
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,46 @@
# Pangolin Middleware: Badger

[![GitHub Release](https://img.shields.io/github/release/fosrl/badger?sort=semver)](https://github.com/fosrl/badger/releases)
[![Go Version](https://img.shields.io/github/go-mod/go-version/fosrl/badger)](https://github.com/fosrl/badger/blob/main/go.mod)
[![CI](https://github.com/fosrl/badger/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/fosrl/badger/actions/workflows/ci.yml)
[![Go Report Card](https://goreportcard.com/badge/github.com/fosrl/badger)](https://goreportcard.com/report/github.com/fosrl/badger)
[![License](https://img.shields.io/github/license/fosrl/badger)](https://github.com/fosrl/badger/blob/main/LICENSE)
[![Traefik Plugin](https://img.shields.io/badge/Traefik-Plugin-24A1C1)](https://plugins.traefik.io/)
[![Pangolin](https://img.shields.io/badge/Pangolin-Middleware-blue)](https://github.com/fosrl/pangolin)

Badger is a middleware plugin designed to work with Traefik in conjunction with [Pangolin](https://github.com/fosrl/pangolin), an identity-aware reverse proxy and zero-trust VPN. Badger acts as an authentication bouncer, ensuring only authenticated and authorized requests are allowed through the proxy.

> [!NOTE]
> Badger can also be used standalone for IP handling (Cloudflare and custom proxy support) without Pangolin. Simply set `disableForwardAuth: true` in your configuration. See the [Disabling Forward Auth](#disabling-forward-auth) section below for details.

This plugin is **required** to be installed alongside [Pangolin](https://github.com/fosrl/pangolin) to enforce secure authentication and session management.

## What Badger does

Badger runs as a Traefik middleware in front of protected services.

For each request, Badger can:

1. determine the real client IP from Cloudflare or a trusted upstream proxy,
2. normalize `X-Real-IP` and `X-Forwarded-For` for downstream services,
3. call the Pangolin API to verify authentication and resource access,
4. allow, block, or redirect the request based on the verification result.

## Modes

### Pangolin authentication mode

This is the default mode. Badger validates incoming requests against the
Pangolin API before forwarding them to the upstream service.

### IP handling only mode

Set `disableForwardAuth: true` to disable Pangolin authentication and only use
Badger for real-client-IP handling.

Use this only when authentication is handled elsewhere or when you explicitly
want Badger to act only as an IP normalization middleware.

## Installation

Badger is automatically installed with Pangolin. Learn how to install Pangolin in the [Pangolin Documentation](https://docs.pangolin.net/self-host/quick-install).
Expand Down
File renamed without changes.
Loading
Loading