nuke/internal/honeypot/naive/naive.go
fucksophie 896858e027
Some checks failed
Docker image builds / build (push) Waiting to run
Asset Build Verification / asset_verification (push) Has been cancelled
Docs deploy / build (push) Has been cancelled
Go Mod Tidy Check / go_mod_tidy_check (push) Has been cancelled
Go / go_tests (push) Has been cancelled
Package builds (unstable) / package_builds (push) Has been cancelled
Smoke tests / smoke-test (default-config-macro) (push) Has been cancelled
Smoke tests / smoke-test (docker-registry) (push) Has been cancelled
Smoke tests / smoke-test (double_slash) (push) Has been cancelled
Smoke tests / smoke-test (forced-language) (push) Has been cancelled
Smoke tests / smoke-test (git-clone) (push) Has been cancelled
Smoke tests / smoke-test (git-push) (push) Has been cancelled
Smoke tests / smoke-test (healthcheck) (push) Has been cancelled
Smoke tests / smoke-test (i18n) (push) Has been cancelled
Smoke tests / smoke-test (log-file) (push) Has been cancelled
Smoke tests / smoke-test (nginx) (push) Has been cancelled
Smoke tests / smoke-test (palemoon/amd64) (push) Has been cancelled
Smoke tests / smoke-test (robots_txt) (push) Has been cancelled
Check Spelling / Check Spelling (push) Has been cancelled
SSH CI / ssh (aarch64-16k) (push) Has been cancelled
SSH CI / ssh (aarch64-4k) (push) Has been cancelled
SSH CI / ssh (ppc64le) (push) Has been cancelled
SSH CI / ssh (riscv64) (push) Has been cancelled
zizmor / zizmor latest via PyPI (push) Has been cancelled
jane remover
2026-02-07 13:08:47 +02:00

205 lines
5 KiB
Go

package naive
import (
"context"
_ "embed"
"fmt"
"log/slog"
"math/rand/v2"
"net/http"
"net/netip"
"time"
"git.sad.ovh/sophie/nuke/internal"
"git.sad.ovh/sophie/nuke/internal/honeypot"
"git.sad.ovh/sophie/nuke/lib/policy/checker"
"git.sad.ovh/sophie/nuke/lib/store"
"github.com/a-h/templ"
"github.com/google/uuid"
"github.com/nikandfor/spintax"
)
//go:generate go tool github.com/a-h/templ/cmd/templ generate
// XXX(Xe): All of this was generated by ChatGPT, GLM 4.6, and GPT-OSS 120b. This is pseudoprofound bullshit in spintax[1] format so that the bullshit generator can emit plausibly human-authored text while being very computationally cheap.
//
// It feels somewhat poetic to use spammer technology in Anubis.
//
// [1]: https://outboundly.ai/blogs/what-is-spintax-and-how-to-use-it/
//
//go:embed spintext.txt
var spintext string
//go:embed titles.txt
var titles string
//go:embed affirmations.txt
var affirmations string
func New(st store.Interface, lg *slog.Logger) (*Impl, error) {
affirmation, err := spintax.Parse(affirmations)
if err != nil {
return nil, fmt.Errorf("can't parse affirmations: %w", err)
}
body, err := spintax.Parse(spintext)
if err != nil {
return nil, fmt.Errorf("can't parse bodies: %w", err)
}
title, err := spintax.Parse(titles)
if err != nil {
return nil, fmt.Errorf("can't parse titles: %w", err)
}
lg.Debug("initialized basic bullshit generator", "affirmations", affirmation.Count(), "bodies", body.Count(), "titles", title.Count())
return &Impl{
st: st,
infos: store.JSON[honeypot.Info]{Underlying: st, Prefix: "honeypot:info"},
uaWeight: store.JSON[int]{Underlying: st, Prefix: "honeypot:user-agent"},
networkWeight: store.JSON[int]{Underlying: st, Prefix: "honeypot:network"},
affirmation: affirmation,
body: body,
title: title,
lg: lg.With("component", "honeypot/naive"),
}, nil
}
type Impl struct {
st store.Interface
infos store.JSON[honeypot.Info]
uaWeight store.JSON[int]
networkWeight store.JSON[int]
lg *slog.Logger
affirmation, body, title spintax.Spintax
}
func (i *Impl) incrementUA(ctx context.Context, userAgent string) int {
result, _ := i.uaWeight.Get(ctx, internal.SHA256sum(userAgent))
result++
i.uaWeight.Set(ctx, internal.SHA256sum(userAgent), result, time.Hour)
return result
}
func (i *Impl) incrementNetwork(ctx context.Context, network string) int {
result, _ := i.networkWeight.Get(ctx, internal.SHA256sum(network))
result++
i.networkWeight.Set(ctx, internal.SHA256sum(network), result, time.Hour)
return result
}
func (i *Impl) CheckUA() checker.Impl {
return checker.Func(func(r *http.Request) (bool, error) {
result, _ := i.uaWeight.Get(r.Context(), internal.SHA256sum(r.UserAgent()))
if result >= 25 {
return true, nil
}
return false, nil
})
}
func (i *Impl) CheckNetwork() checker.Impl {
return checker.Func(func(r *http.Request) (bool, error) {
result, _ := i.uaWeight.Get(r.Context(), internal.SHA256sum(r.UserAgent()))
if result >= 25 {
return true, nil
}
return false, nil
})
}
func (i *Impl) Hash() string {
return internal.SHA256sum("naive honeypot")
}
func (i *Impl) makeAffirmations() []string {
count := rand.IntN(5) + 1
var result []string
for j := 0; j < count; j++ {
result = append(result, i.affirmation.Spin())
}
return result
}
func (i *Impl) makeSpins() []string {
count := rand.IntN(5) + 1
var result []string
for j := 0; j < count; j++ {
result = append(result, i.body.Spin())
}
return result
}
func (i *Impl) makeTitle() string {
return i.title.Spin()
}
func (i *Impl) ServeHTTP(w http.ResponseWriter, r *http.Request) {
t0 := time.Now()
lg := internal.GetRequestLogger(i.lg, r)
id := r.PathValue("id")
if id == "" {
id = uuid.NewString()
}
realIP, _ := internal.RealIP(r)
if !realIP.IsValid() {
realIP = netip.MustParseAddr(r.Header.Get("X-Real-Ip"))
}
network, ok := internal.ClampIP(realIP)
if !ok {
lg.Error("clampIP failed", "output", network, "ok", ok)
http.Error(w, "The cake is a lie", http.StatusTeapot)
return
}
networkCount := i.incrementNetwork(r.Context(), network.String())
uaCount := i.incrementUA(r.Context(), r.UserAgent())
stage := r.PathValue("stage")
if stage == "init" {
lg.Debug("found new entrance point", "id", id, "stage", stage, "userAgent", r.UserAgent(), "clampedIP", network)
} else {
switch {
case networkCount%256 == 0, uaCount%256 == 0:
lg.Warn("found possible crawler", "id", id, "network", network)
}
}
spins := i.makeSpins()
affirmations := i.makeAffirmations()
title := i.makeTitle()
var links []link
for _, affirmation := range affirmations {
links = append(links, link{
href: uuid.NewString(),
body: affirmation,
})
}
templ.Handler(
base(title, i.maze(spins, links)),
templ.WithStreaming(),
templ.WithStatus(http.StatusOK),
).ServeHTTP(w, r)
t1 := time.Since(t0)
honeypot.Timings.WithLabelValues("naive").Observe(float64(t1.Milliseconds()))
}
type link struct {
href string
body string
}