TeeHee: Essentially rewrite HellPot :^)
This commit is contained in:
parent
3024b2d0c3
commit
0a18454ea0
|
@ -17,7 +17,7 @@ Clients (hopefully bots) that disregard `robots.txt` and connect to your instanc
|
|||
|
||||
HellPot will send an infinite stream of data that is _just close enough_ to being a real website that they might just stick around until their soul is ripped apart and they cease to exist.
|
||||
|
||||
Under the hood of this eternal suffering is a markov engine that chucks bits and pieces of [The Birth of Tragedy (Hellenism and Pessimism)](https://www.gutenberg.org/files/51356/51356-h/51356-h.htm) by Friedrich Nietzsche at the client using [fasthttp](https://github.com/valyala/fasthttp), or optionally you may synchronize HellPot with your nightmares by using the `-g`/`--grimoire` flag
|
||||
Under the hood of this eternal suffering is a markov engine that chucks bits and pieces of [The Birth of Tragedy (Hellenism and Pessimism)](https://www.gutenberg.org/files/51356/51356-h/51356-h.htm) by Friedrich Nietzsche at the client~~~~ using [fasthttp](https://github.com/valyala/fasthttp), or optionally you may synchronize HellPot with your nightmares by using the `-g`/`--grimoire` flag
|
||||
|
||||
## Building From Source
|
||||
|
||||
|
|
|
@ -1,59 +1,135 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"syscall"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"time"
|
||||
|
||||
"github.com/yunginnanet/HellPot/internal/config"
|
||||
"github.com/yunginnanet/HellPot/internal/extra"
|
||||
"github.com/yunginnanet/HellPot/internal/http"
|
||||
"github.com/yunginnanet/HellPot/internal/logger"
|
||||
"github.com/yunginnanet/HellPot/internal/version"
|
||||
)
|
||||
|
||||
var (
|
||||
log zerolog.Logger
|
||||
version string // set by linker
|
||||
runningConfig *config.Parameters
|
||||
)
|
||||
|
||||
func init() {
|
||||
if version != "" {
|
||||
config.Version = version[1:]
|
||||
func writeConfig(target string) bool {
|
||||
var f *os.File
|
||||
var err error
|
||||
if f, err = os.Create(target); err != nil {
|
||||
println("failed to create config file: " + err.Error())
|
||||
return false
|
||||
}
|
||||
config.Init()
|
||||
if config.BannerOnly {
|
||||
extra.Banner()
|
||||
os.Exit(0)
|
||||
if _, err = io.Copy(f, config.Defaults.IO); err != nil {
|
||||
println("failed to write default config to file: " + err.Error())
|
||||
_ = f.Close()
|
||||
return false
|
||||
}
|
||||
|
||||
switch config.DockerLogging {
|
||||
case true:
|
||||
config.CurrentLogFile = "/dev/stdout"
|
||||
config.NoColor = true
|
||||
log = config.StartLogger(false, os.Stdout)
|
||||
default:
|
||||
log = config.StartLogger(true)
|
||||
}
|
||||
|
||||
extra.Banner()
|
||||
|
||||
log.Info().Str("caller", "config").Str("file", config.Filename).Msg(config.Filename)
|
||||
log.Info().Str("caller", "logger").Msg(config.CurrentLogFile)
|
||||
log.Debug().Str("caller", "logger").Msg("debug enabled")
|
||||
log.Trace().Str("caller", "logger").Msg("trace enabled")
|
||||
|
||||
println("wrote default config to " + target)
|
||||
runningConfig, _ = config.Setup(f)
|
||||
_ = f.Close()
|
||||
return true
|
||||
}
|
||||
|
||||
func main() {
|
||||
conf := config.CLIFlags.Lookup("config").Value
|
||||
if conf.String() == "" {
|
||||
conf = config.CLIFlags.Lookup("c").Value
|
||||
}
|
||||
|
||||
usingDefaults := true
|
||||
resolvedConf := conf.String()
|
||||
|
||||
uconf, _ := os.UserConfigDir()
|
||||
if uconf == "" && os.Getenv("HOME") != "" {
|
||||
uconf = filepath.Join(os.Getenv("HOME"), ".config")
|
||||
}
|
||||
|
||||
if resolvedConf == "" {
|
||||
for _, path := range []string{
|
||||
"/etc/HellPot/config.toml",
|
||||
"/usr/local/etc/HellPot/config.toml",
|
||||
"./config.toml",
|
||||
filepath.Join(uconf, "HellPot", "config.toml"),
|
||||
} {
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
resolvedConf = path
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var setupErr error
|
||||
var err error
|
||||
var f *os.File
|
||||
|
||||
f, err = os.Open(resolvedConf)
|
||||
if err == nil {
|
||||
runningConfig, setupErr = config.Setup(f)
|
||||
}
|
||||
switch {
|
||||
case setupErr != nil:
|
||||
println("failed to setup config: " + setupErr.Error())
|
||||
case err != nil:
|
||||
println("failed to open config file for reading: " + err.Error())
|
||||
println("trying to create it....")
|
||||
wroteOK := writeConfig(resolvedConf)
|
||||
if wroteOK {
|
||||
break
|
||||
}
|
||||
case runningConfig != nil:
|
||||
usingDefaults = false
|
||||
_ = f.Close()
|
||||
}
|
||||
|
||||
if runningConfig == nil {
|
||||
if runningConfig, err = config.Setup(nil); err != nil || runningConfig == nil {
|
||||
panic("failed to setup default config...\n" + err.Error())
|
||||
return // unreachable, but the linter doesn't seem to realize that
|
||||
}
|
||||
}
|
||||
|
||||
log, _ := logger.New(runningConfig.Logger)
|
||||
|
||||
if usingDefaults {
|
||||
log.Warn().Msg("continuing with default configuration in ")
|
||||
for i := 5; i > 0; i-- {
|
||||
print(strconv.Itoa(i))
|
||||
for i := 0; i < 5; i++ {
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
print(".")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !runningConfig.Logger.NoColor {
|
||||
extra.Banner()
|
||||
}
|
||||
|
||||
log.Info().Msg("🔥 Starting HellPot 🔥")
|
||||
log.Info().Msg("Version: " + version.Version)
|
||||
log.Info().Msg("PID: " + strconv.Itoa(os.Getpid()))
|
||||
log.Info().Msg("Using config file: " + resolvedConf)
|
||||
if usingDefaults {
|
||||
log.Warn().Msg("Using default configuration")
|
||||
}
|
||||
log.Debug().Msg("Debug logging enabled")
|
||||
log.Trace().Msg("Trace logging enabled")
|
||||
|
||||
stopChan := make(chan os.Signal, 1)
|
||||
signal.Notify(stopChan, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
go func() {
|
||||
log.Fatal().Err(http.Serve()).Msg("HTTP error")
|
||||
log.Fatal().Err(http.Serve(runningConfig)).Msg("HTTP error")
|
||||
}()
|
||||
|
||||
<-stopChan // wait for SIGINT
|
||||
log.Warn().Msg("Shutting down server...")
|
||||
|
||||
}
|
||||
|
|
6
go.mod
6
go.mod
|
@ -7,17 +7,16 @@ require (
|
|||
github.com/fasthttp/router v1.5.1
|
||||
github.com/knadh/koanf/parsers/toml v0.1.0
|
||||
github.com/knadh/koanf/providers/env v0.1.0
|
||||
github.com/knadh/koanf/providers/file v0.1.0
|
||||
github.com/knadh/koanf/providers/structs v0.1.0
|
||||
github.com/knadh/koanf/v2 v2.1.1
|
||||
github.com/rs/zerolog v1.33.0
|
||||
github.com/spf13/afero v1.11.0
|
||||
github.com/valyala/fasthttp v1.55.0
|
||||
golang.org/x/term v0.21.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/fatih/structs v1.1.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 // indirect
|
||||
github.com/klauspost/compress v1.17.9 // indirect
|
||||
github.com/knadh/koanf/maps v0.1.1 // indirect
|
||||
|
@ -29,5 +28,4 @@ require (
|
|||
github.com/savsgio/gotils v0.0.0-20240303185622-093b76447511 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
golang.org/x/sys v0.21.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
)
|
||||
|
|
13
go.sum
13
go.sum
|
@ -6,8 +6,8 @@ github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV
|
|||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/fasthttp/router v1.5.1 h1:uViy8UYYhm5npJSKEZ4b/ozM//NGzVCfJbh6VJ0VKr8=
|
||||
github.com/fasthttp/router v1.5.1/go.mod h1:WrmsLo3mrerZP2VEXRV1E8nL8ymJFYCDTr4HmnB8+Zs=
|
||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 h1:TQcrn6Wq+sKGkpyPvppOz99zsMBaUOKXq6HSv655U1c=
|
||||
github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
|
@ -19,8 +19,8 @@ github.com/knadh/koanf/parsers/toml v0.1.0 h1:S2hLqS4TgWZYj4/7mI5m1CQQcWurxUz6OD
|
|||
github.com/knadh/koanf/parsers/toml v0.1.0/go.mod h1:yUprhq6eo3GbyVXFFMdbfZSo928ksS+uo0FFqNMnO18=
|
||||
github.com/knadh/koanf/providers/env v0.1.0 h1:LqKteXqfOWyx5Ab9VfGHmjY9BvRXi+clwyZozgVRiKg=
|
||||
github.com/knadh/koanf/providers/env v0.1.0/go.mod h1:RE8K9GbACJkeEnkl8L/Qcj8p4ZyPXZIQ191HJi44ZaQ=
|
||||
github.com/knadh/koanf/providers/file v0.1.0 h1:fs6U7nrV58d3CFAFh8VTde8TM262ObYf3ODrc//Lp+c=
|
||||
github.com/knadh/koanf/providers/file v0.1.0/go.mod h1:rjJ/nHQl64iYCtAW2QQnF0eSmDEX/YZ/eNFj5yR6BvA=
|
||||
github.com/knadh/koanf/providers/structs v0.1.0 h1:wJRteCNn1qvLtE5h8KQBvLJovidSdntfdyIbbCzEyE0=
|
||||
github.com/knadh/koanf/providers/structs v0.1.0/go.mod h1:sw2YZ3txUcqA3Z27gPlmmBzWn1h8Nt9O6EP/91MkcWE=
|
||||
github.com/knadh/koanf/v2 v2.1.1 h1:/R8eXqasSTsmDCsAyYj+81Wteg8AqrV9CP6gvsTsOmM=
|
||||
github.com/knadh/koanf/v2 v2.1.1/go.mod h1:4mnTRbZCK+ALuBXHZMjDfG9y714L7TykVnZkXbMU3Es=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
|
@ -41,22 +41,17 @@ github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
|||
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||
github.com/savsgio/gotils v0.0.0-20240303185622-093b76447511 h1:KanIMPX0QdEdB4R3CiimCAbxFrhB3j7h0/OvpYGVQa8=
|
||||
github.com/savsgio/gotils v0.0.0-20240303185622-093b76447511/go.mod h1:sM7Mt7uEoCeFSCBM+qBrqvEo+/9vdmj19wzp3yzUhmg=
|
||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasthttp v1.55.0 h1:Zkefzgt6a7+bVKHnu/YaYSOPfNYNisSVBo/unVCf8k8=
|
||||
github.com/valyala/fasthttp v1.55.0/go.mod h1:NkY9JtkrpPKmgwV3HTaS2HWaJss9RSIsRVfcxxoHiOM=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
|
||||
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
nullprogram.com/x/rng v1.1.0 h1:SMU7DHaQSWtKJNTpNFIFt8Wd/KSmOuSDPXrMFp/UMro=
|
||||
|
|
|
@ -8,12 +8,8 @@ import (
|
|||
"bufio"
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"github.com/yunginnanet/HellPot/internal/config"
|
||||
)
|
||||
|
||||
var log = config.GetLogger()
|
||||
|
||||
const DefaultBuffSize = 100 * 1 << 10
|
||||
|
||||
// Heffalump represents our buffer pool and markov map from Heffalump
|
||||
|
@ -46,11 +42,11 @@ func (h *Heffalump) WriteHell(bw *bufio.Writer) (int64, error) {
|
|||
var n int64
|
||||
var err error
|
||||
|
||||
defer func() {
|
||||
/* defer func() {
|
||||
if r := recover(); r != nil {
|
||||
log.Error().Interface("caller", r).Msg("panic recovered!")
|
||||
}
|
||||
}()
|
||||
}()*/
|
||||
|
||||
buf := h.pool.Get().([]byte)
|
||||
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
var (
|
||||
forceDebug = false
|
||||
forceTrace = false
|
||||
)
|
||||
|
||||
var argBoolMap = map[string]*bool{
|
||||
"--debug": &forceDebug, "-v": &forceDebug, "--trace": &forceTrace, "-vv": &forceTrace,
|
||||
"--nocolor": &noColorForce, "--banner": &BannerOnly, "--genconfig": &GenConfig,
|
||||
}
|
||||
|
||||
// TODO: should probably just make a proper CLI with flags or something
|
||||
func argParse() {
|
||||
for i, arg := range os.Args {
|
||||
if t, ok := argBoolMap[arg]; ok {
|
||||
*t = true
|
||||
continue
|
||||
}
|
||||
switch arg {
|
||||
case "-h", "--help":
|
||||
CLI.printUsage()
|
||||
case "-c", "--config":
|
||||
if len(os.Args) < i+2 {
|
||||
println("missing config file after -c/--config")
|
||||
os.Exit(1)
|
||||
}
|
||||
loadCustomConfig(os.Args[i+1])
|
||||
case "-g", "--grimoire":
|
||||
if len(os.Args) < i+2 {
|
||||
println("missing source of suffering file after -g/--grimoire")
|
||||
os.Exit(1)
|
||||
}
|
||||
Grimoire = os.Args[i+1]
|
||||
UseCustomHeffalump = true
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
type ClientRules struct {
|
||||
// See: https://github.com/yunginnanet/HellPot/issues/23
|
||||
UseragentDisallowStrings []string `koanf:"user_agent_disallow_strings"`
|
||||
useragentDisallowStrBytes [][]byte `koanf:"-"`
|
||||
UseragentDisallowRegex []string `koanf:"user_agent_disallow_regex"`
|
||||
useragentDisallowRegex []*regexp.Regexp `koanf:"-"`
|
||||
}
|
||||
|
||||
func NewClientRules(strs []string, regex []string) (*ClientRules, error) {
|
||||
if strs == nil && regex == nil {
|
||||
return &ClientRules{}, nil
|
||||
}
|
||||
if regex == nil {
|
||||
regex = make([]string, 0)
|
||||
}
|
||||
if strs == nil {
|
||||
strs = make([]string, 0)
|
||||
}
|
||||
cr := &ClientRules{
|
||||
UseragentDisallowStrings: strs,
|
||||
UseragentDisallowRegex: regex,
|
||||
}
|
||||
return cr, cr.compile()
|
||||
}
|
||||
|
||||
func (c *ClientRules) compile() error {
|
||||
dupes := make(map[string]struct{})
|
||||
for _, v := range c.UseragentDisallowRegex {
|
||||
if v == "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := dupes[v]; ok {
|
||||
continue
|
||||
}
|
||||
dupes[v] = struct{}{}
|
||||
var compd *regexp.Regexp
|
||||
var err error
|
||||
if compd, err = regexp.Compile(v); err != nil {
|
||||
return fmt.Errorf("failed to compile regex '%s': %w", v, err)
|
||||
}
|
||||
c.useragentDisallowRegex = append(c.useragentDisallowRegex, compd)
|
||||
}
|
||||
|
||||
newStrs := make([]string, 0)
|
||||
for _, v := range c.UseragentDisallowStrings {
|
||||
if v == "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := dupes[v]; ok {
|
||||
continue
|
||||
}
|
||||
dupes[v] = struct{}{}
|
||||
newStrs = append(newStrs, v)
|
||||
}
|
||||
c.UseragentDisallowStrings = newStrs
|
||||
c.useragentDisallowStrBytes = make([][]byte, len(c.UseragentDisallowStrings))
|
||||
for i, v := range c.UseragentDisallowStrings {
|
||||
c.useragentDisallowStrBytes[i] = []byte(v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ClientRules) MatchUseragent(ua []byte) bool {
|
||||
for _, v := range c.useragentDisallowRegex {
|
||||
if v.Match(ua) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, v := range c.useragentDisallowStrBytes {
|
||||
if bytes.Contains(ua, v) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/yunginnanet/HellPot/internal/extra"
|
||||
"github.com/yunginnanet/HellPot/internal/version"
|
||||
)
|
||||
|
||||
var CLIFlags = flag.NewFlagSet("cli", flag.ExitOnError)
|
||||
|
||||
func init() {
|
||||
CLIFlags.Bool("logger-debug", false, "force debug logging")
|
||||
CLIFlags.Bool("logger-trace", false, "force trace logging")
|
||||
CLIFlags.Bool("logger-nocolor", false, "force no color logging")
|
||||
CLIFlags.String("bespoke-grimoire", "", "specify a custom file used for text generation")
|
||||
CLIFlags.Bool("banner", false, "show banner and version then exit")
|
||||
CLIFlags.Bool("genconfig", false, "write default config to stdout then exit")
|
||||
CLIFlags.Bool("h", false, "show this help and exit")
|
||||
CLIFlags.Bool("help", false, "show this help and exit")
|
||||
CLIFlags.String("c", "", "specify config file")
|
||||
CLIFlags.String("config", "", "specify config file")
|
||||
CLIFlags.String("version", "", "show version and exit")
|
||||
CLIFlags.String("v", "", "show version and exit")
|
||||
if err := CLIFlags.Parse(os.Args[1:]); err != nil {
|
||||
println(err.Error())
|
||||
// flag.ExitOnError will call os.Exit(2)
|
||||
}
|
||||
if CLIFlags.Lookup("h").Value.String() == "true" || CLIFlags.Lookup("help").Value.String() == "true" {
|
||||
CLIFlags.Usage()
|
||||
os.Exit(0)
|
||||
}
|
||||
if CLIFlags.Lookup("version").Value.String() == "true" || CLIFlags.Lookup("v").Value.String() == "true" {
|
||||
_, _ = os.Stdout.WriteString("HellPot version: " + version.Version + "\n")
|
||||
os.Exit(0)
|
||||
}
|
||||
if CLIFlags.Lookup("genconfig").Value.String() == "true" {
|
||||
if n, err := io.Copy(os.Stdout, Defaults.IO); err != nil || n == 0 {
|
||||
if err == nil {
|
||||
err = io.EOF
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
if CLIFlags.Lookup("banner").Value.String() == "true" {
|
||||
extra.Banner()
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
|
@ -1,206 +0,0 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// generic vars
|
||||
var (
|
||||
noColorForce = false
|
||||
customconfig = false
|
||||
home string
|
||||
prefConfigLocation string
|
||||
snek = viper.New()
|
||||
)
|
||||
|
||||
// exported generic vars
|
||||
var (
|
||||
// Trace is the value of our trace (extra verbose) on/off toggle as per the current configuration.
|
||||
Trace bool
|
||||
// Debug is the value of our debug (verbose) on/off toggle as per the current configuration.
|
||||
Debug bool
|
||||
// Filename returns the current location of our toml config file.
|
||||
Filename string
|
||||
// UseCustomHeffalump decides if a custom Heffalump is to be used
|
||||
UseCustomHeffalump = false
|
||||
// Grimoire returns the current location of a possible source of suffering file
|
||||
Grimoire string
|
||||
)
|
||||
|
||||
func writeConfig() {
|
||||
if _, err := os.Stat(prefConfigLocation); os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(prefConfigLocation, 0o750); err != nil {
|
||||
println("error writing new config: " + err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
Filename = prefConfigLocation + "/" + "config.toml"
|
||||
if err := snek.SafeWriteConfigAs(Filename); err != nil {
|
||||
fmt.Println("Failed to write new configuration file to '" + Filename + "': " + err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Init will initialize our toml configuration engine and define our default configuration values which can be written to a new configuration file if desired
|
||||
func Init() {
|
||||
snek.SetConfigType("toml")
|
||||
snek.SetConfigName("config")
|
||||
|
||||
argParse()
|
||||
|
||||
if customconfig {
|
||||
associateExportedVariables()
|
||||
return
|
||||
}
|
||||
|
||||
setDefaults()
|
||||
|
||||
for _, loc := range getConfigPaths() {
|
||||
snek.AddConfigPath(loc)
|
||||
}
|
||||
|
||||
if err := snek.MergeInConfig(); err != nil {
|
||||
println("Error reading configuration file: " + err.Error())
|
||||
println("Writing new configuration file...")
|
||||
writeConfig()
|
||||
}
|
||||
|
||||
if len(Filename) < 1 {
|
||||
Filename = snek.ConfigFileUsed()
|
||||
}
|
||||
|
||||
snek.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_"))
|
||||
snek.SetEnvPrefix(Title)
|
||||
|
||||
snek.AutomaticEnv()
|
||||
|
||||
associateExportedVariables()
|
||||
}
|
||||
|
||||
func getConfigPaths() (paths []string) {
|
||||
paths = append(paths, "./")
|
||||
//goland:noinspection GoBoolExpressions
|
||||
if runtime.GOOS != "windows" {
|
||||
paths = append(paths,
|
||||
prefConfigLocation, "/etc/"+Title+"/", "../", "../../")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func loadCustomConfig(path string) {
|
||||
/* #nosec */
|
||||
cf, err := os.Open(path)
|
||||
if err != nil {
|
||||
println("Error opening specified config file: " + path)
|
||||
println(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
Filename, err = filepath.Abs(path)
|
||||
if len(Filename) < 1 || err != nil {
|
||||
Filename = path
|
||||
}
|
||||
|
||||
defer func(f *os.File) {
|
||||
if fcerr := f.Close(); fcerr != nil {
|
||||
fmt.Println("failed to close file handler for config file: ", fcerr.Error())
|
||||
}
|
||||
}(cf)
|
||||
|
||||
buf, err1 := io.ReadAll(cf)
|
||||
err2 := snek.ReadConfig(bytes.NewBuffer(buf))
|
||||
|
||||
switch {
|
||||
case err1 != nil:
|
||||
fmt.Println("config file read fatal error during i/o: ", err1.Error())
|
||||
os.Exit(1)
|
||||
case err2 != nil:
|
||||
fmt.Println("config file read fatal error during parse: ", err2.Error())
|
||||
os.Exit(1)
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
customconfig = true
|
||||
}
|
||||
|
||||
func processOpts() {
|
||||
// string options and their exported variables
|
||||
stringOpt := map[string]*string{
|
||||
"http.bind_addr": &HTTPBind,
|
||||
"http.bind_port": &HTTPPort,
|
||||
"http.real_ip_header": &HeaderName,
|
||||
"logger.directory": &logDir,
|
||||
"deception.server_name": &FakeServerName,
|
||||
}
|
||||
// string slice options and their exported variables
|
||||
strSliceOpt := map[string]*[]string{
|
||||
"http.router.paths": &Paths,
|
||||
"http.uagent_string_blacklist": &UseragentBlacklistMatchers,
|
||||
}
|
||||
// bool options and their exported variables
|
||||
boolOpt := map[string]*bool{
|
||||
"performance.restrict_concurrency": &RestrictConcurrency,
|
||||
"http.use_unix_socket": &UseUnixSocket,
|
||||
"logger.debug": &Debug,
|
||||
"logger.trace": &Trace,
|
||||
"logger.nocolor": &NoColor,
|
||||
"logger.docker_logging": &DockerLogging,
|
||||
"http.router.makerobots": &MakeRobots,
|
||||
"http.router.catchall": &CatchAll,
|
||||
}
|
||||
// integer options and their exported variables
|
||||
intOpt := map[string]*int{
|
||||
"performance.max_workers": &MaxWorkers,
|
||||
}
|
||||
|
||||
for key, opt := range stringOpt {
|
||||
*opt = snek.GetString(key)
|
||||
}
|
||||
for key, opt := range strSliceOpt {
|
||||
*opt = snek.GetStringSlice(key)
|
||||
}
|
||||
for key, opt := range boolOpt {
|
||||
*opt = snek.GetBool(key)
|
||||
}
|
||||
for key, opt := range intOpt {
|
||||
*opt = snek.GetInt(key)
|
||||
}
|
||||
}
|
||||
|
||||
func associateExportedVariables() {
|
||||
processOpts()
|
||||
|
||||
if noColorForce {
|
||||
NoColor = true
|
||||
}
|
||||
|
||||
if UseUnixSocket {
|
||||
UnixSocketPath = snek.GetString("http.unix_socket_path")
|
||||
parsedPermissions, err := strconv.ParseUint(snek.GetString("http.unix_socket_permissions"), 8, 32)
|
||||
if err == nil {
|
||||
UnixSocketPermissions = uint32(parsedPermissions)
|
||||
}
|
||||
}
|
||||
|
||||
// We set exported variables here so that it tracks when accessed from other packages.
|
||||
|
||||
if Debug || forceDebug {
|
||||
zerolog.SetGlobalLevel(zerolog.DebugLevel)
|
||||
Debug = true
|
||||
}
|
||||
if Trace || forceTrace {
|
||||
zerolog.SetGlobalLevel(zerolog.TraceLevel)
|
||||
Trace = true
|
||||
}
|
||||
}
|
|
@ -1,46 +1,66 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"bytes"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
"github.com/knadh/koanf/parsers/toml"
|
||||
)
|
||||
|
||||
var Defaults = &Preset{val: defOpts}
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
if home, err = os.UserHomeDir(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if len(defOpts) == 0 {
|
||||
panic("default options map is empty")
|
||||
}
|
||||
defOpts["logger"]["directory"] = path.Join(home, ".local", "share", Title, "logs")
|
||||
prefConfigLocation = path.Join(home, ".config", Title)
|
||||
Defaults.IO = &PresetIO{p: Defaults}
|
||||
}
|
||||
|
||||
var (
|
||||
configSections = []string{"logger", "http", "performance", "deception", "ssh"}
|
||||
defNoColor = false
|
||||
)
|
||||
type Preset struct {
|
||||
val map[string]interface{}
|
||||
IO *PresetIO
|
||||
}
|
||||
|
||||
var defOpts = map[string]map[string]interface{}{
|
||||
"logger": {
|
||||
"debug": true,
|
||||
"trace": false,
|
||||
"nocolor": defNoColor,
|
||||
"use_date_filename": true,
|
||||
"docker_logging": false,
|
||||
type PresetIO struct {
|
||||
p *Preset
|
||||
buf *bytes.Buffer
|
||||
}
|
||||
|
||||
func (pre *Preset) ReadBytes() ([]byte, error) {
|
||||
return toml.Parser().Marshal(pre.val) //nolint:wrapcheck
|
||||
}
|
||||
|
||||
func (shim *PresetIO) Read(p []byte) (int, error) {
|
||||
if shim.buf.Len() > 0 {
|
||||
return shim.buf.Read(p) //nolint:wrapcheck
|
||||
}
|
||||
data, err := shim.p.ReadBytes()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if shim.buf == nil {
|
||||
shim.buf = bytes.NewBuffer(data)
|
||||
}
|
||||
return shim.buf.Read(p) //nolint:wrapcheck
|
||||
}
|
||||
|
||||
func (pre *Preset) Read() (map[string]interface{}, error) {
|
||||
return pre.val, nil
|
||||
}
|
||||
|
||||
var defOpts = map[string]interface{}{
|
||||
"logger": map[string]interface{}{
|
||||
"debug": true,
|
||||
"trace": false,
|
||||
"nocolor": runtime.GOOS == "windows",
|
||||
"use_date_filename": true,
|
||||
"docker_logging": false,
|
||||
"console_time_format": time.Kitchen,
|
||||
},
|
||||
"http": {
|
||||
"http": map[string]interface{}{
|
||||
"use_unix_socket": false,
|
||||
"unix_socket_path": "/var/run/hellpot",
|
||||
"unix_socket_permissions": "0666",
|
||||
"bind_addr": "127.0.0.1",
|
||||
"bind_port": "8080",
|
||||
"bind_port": int64(8080), //nolint:gomnd
|
||||
"real_ip_header": "X-Real-IP",
|
||||
|
||||
"router": map[string]interface{}{
|
||||
|
@ -55,52 +75,11 @@ var defOpts = map[string]map[string]interface{}{
|
|||
"Cloudflare-Traffic-Manager",
|
||||
},
|
||||
},
|
||||
"performance": {
|
||||
"performance": map[string]interface{}{
|
||||
"restrict_concurrency": false,
|
||||
"max_workers": 256,
|
||||
"max_workers": 256, //nolint:gomnd
|
||||
},
|
||||
"deception": {
|
||||
"deception": map[string]interface{}{
|
||||
"server_name": "nginx",
|
||||
},
|
||||
}
|
||||
|
||||
func gen(memfs afero.Fs) {
|
||||
target := fmt.Sprintf("%s.toml", Title)
|
||||
if err := snek.SafeWriteConfigAs("config.toml"); err != nil {
|
||||
print(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
var f afero.File
|
||||
var err error
|
||||
f, err = memfs.Open("config.toml")
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
nf, err := os.Create(target) // #nosec G304
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
if _, err = io.Copy(nf, f); err != nil {
|
||||
println(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
println("default configuration successfully written to " + target)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func setDefaults() {
|
||||
memfs := afero.NewMemMapFs()
|
||||
//goland:noinspection GoBoolExpressions
|
||||
if runtime.GOOS == "windows" {
|
||||
defNoColor = true
|
||||
}
|
||||
for _, def := range configSections {
|
||||
snek.SetDefault(def, defOpts[def])
|
||||
}
|
||||
if GenConfig {
|
||||
snek.SetFs(memfs)
|
||||
gen(memfs)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDefaults(t *testing.T) {
|
||||
t.Run("ReadBytes", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
bs, err := Defaults.ReadBytes()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(bs) == 0 {
|
||||
t.Fatal("expected non-empty byte slice")
|
||||
}
|
||||
total := 0
|
||||
for _, needle := range []string{
|
||||
"logger",
|
||||
"http",
|
||||
"performance",
|
||||
"deception",
|
||||
} {
|
||||
total += bytes.Count(bs, []byte(needle)) + 3 // name plus brackets and newline
|
||||
if !bytes.Contains(bs, []byte(needle)) {
|
||||
t.Errorf("expected %q in byte slice", needle)
|
||||
}
|
||||
}
|
||||
if len(bs) <= total {
|
||||
t.Errorf("default byte slice seems too short to contain any default values")
|
||||
}
|
||||
})
|
||||
t.Run("Read", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
m, err := Defaults.Read()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(m) == 0 {
|
||||
t.Fatal("expected non-empty map")
|
||||
}
|
||||
for _, needle := range []string{
|
||||
"logger",
|
||||
"http",
|
||||
"performance",
|
||||
"deception",
|
||||
} {
|
||||
if _, ok := m[needle]; !ok {
|
||||
t.Errorf("expected %q in map", needle)
|
||||
}
|
||||
if m[needle] == nil {
|
||||
t.Errorf("expected non-nil value for %q", needle)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"runtime/debug"
|
||||
)
|
||||
|
||||
// Title is the name of the application used throughout the configuration process.
|
||||
const Title = "HellPot"
|
||||
|
||||
var Version = "dev"
|
||||
|
||||
func init() {
|
||||
if Version != "dev" {
|
||||
return
|
||||
}
|
||||
binInfo := make(map[string]string)
|
||||
info, ok := debug.ReadBuildInfo()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
for _, v := range info.Settings {
|
||||
binInfo[v.Key] = v.Value
|
||||
}
|
||||
if gitrev, ok := binInfo["vcs.revision"]; ok {
|
||||
Version = gitrev[:7]
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
// BannerOnly when toggled causes HellPot to only print the banner and version then exit.
|
||||
BannerOnly = false
|
||||
// GenConfig when toggled causes HellPot to write its default config to the cwd and then exit.
|
||||
GenConfig = false
|
||||
// NoColor when true will disable the banner and any colored console output.
|
||||
NoColor bool
|
||||
// DockerLogging when true will disable the banner and any colored console output, as well as disable the log file.
|
||||
// Assumes NoColor == true.
|
||||
DockerLogging bool
|
||||
// MakeRobots when false will not respond to requests for robots.txt.
|
||||
MakeRobots bool
|
||||
// CatchAll when true will cause HellPot to respond to all paths.
|
||||
// Note that this will override MakeRobots.
|
||||
CatchAll bool
|
||||
// ConsoleTimeFormat sets the time format for the console. The string is passed to time.Format() down the line.
|
||||
ConsoleTimeFormat string
|
||||
)
|
||||
|
||||
// "http"
|
||||
var (
|
||||
// HTTPBind is defined via our toml configuration file. It is the address that HellPot listens on.
|
||||
HTTPBind string
|
||||
// HTTPPort is defined via our toml configuration file. It is the port that HellPot listens on.
|
||||
HTTPPort string
|
||||
// HeaderName is defined via our toml configuration file. It is the HTTP Header containing the original IP of the client,
|
||||
// in traditional reverse Proxy deployments.
|
||||
HeaderName string
|
||||
|
||||
// Paths are defined via our toml configuration file. These are the paths that HellPot will present for "robots.txt"
|
||||
// These are also the paths that HellPot will respond for. Other paths will throw a warning and will serve a 404.
|
||||
Paths []string
|
||||
|
||||
// UseUnixSocket determines if we will listen for HTTP connections on a unix socket.
|
||||
UseUnixSocket bool
|
||||
|
||||
// UnixSocketPath is defined via our toml configuration file. It is the path of the socket HellPot listens on
|
||||
// if UseUnixSocket, also defined via our toml configuration file, is set to true.
|
||||
UnixSocketPath = ""
|
||||
UnixSocketPermissions uint32
|
||||
|
||||
// UseragentBlacklistMatchers contains useragent matches checked for with strings.Contains() that
|
||||
// prevent HellPot from firing off.
|
||||
// See: https://github.com/yunginnanet/HellPot/issues/23
|
||||
UseragentBlacklistMatchers []string
|
||||
)
|
||||
|
||||
// "performance"
|
||||
var (
|
||||
RestrictConcurrency bool
|
||||
MaxWorkers int
|
||||
)
|
||||
|
||||
// "deception"
|
||||
var (
|
||||
// FakeServerName is our configured value for the "Server: " response header when serving HTTP clients
|
||||
FakeServerName string
|
||||
)
|
|
@ -1,124 +0,0 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
type help struct {
|
||||
title, version string
|
||||
usage map[int][]string
|
||||
out io.Writer
|
||||
}
|
||||
|
||||
var CLI = help{
|
||||
title: Title,
|
||||
version: Version,
|
||||
usage: map[int][]string{
|
||||
0: {0: "-c, --config", 1: "<file>", 2: "Specify config file"},
|
||||
1: {0: "--nocolor", 1: "disable color and banner"},
|
||||
2: {0: "--banner", 1: "show banner + version and exit"},
|
||||
3: {0: "--genconfig", 1: "write default config to " + Title + ".toml then exit"},
|
||||
4: {0: "-g, --grimoire", 1: "<file>", 2: "Specify a custom file used for text generation"},
|
||||
5: {0: "-h,--help", 1: "show this help and exit"},
|
||||
},
|
||||
out: os.Stdout,
|
||||
}
|
||||
|
||||
func (cli help) secondColStart(index int) (max int) {
|
||||
l := cli.firstColEnd() + 2
|
||||
if len(cli.usage[index]) > 2 && cli.usage[index][2] != "" {
|
||||
l -= len(cli.usage[index][1])
|
||||
}
|
||||
if l > max {
|
||||
max = l
|
||||
}
|
||||
return max
|
||||
}
|
||||
|
||||
func (cli help) firstColEnd() (max int) {
|
||||
for n := range cli.usage {
|
||||
l := len(cli.usage[n][0])
|
||||
if l > max {
|
||||
max = l
|
||||
}
|
||||
}
|
||||
return max
|
||||
}
|
||||
|
||||
func (cli help) stdout(s ...string) {
|
||||
for _, v := range s {
|
||||
_, _ = cli.out.Write([]byte(v))
|
||||
}
|
||||
}
|
||||
|
||||
func (cli help) lb(n int) {
|
||||
for n > 0 {
|
||||
cli.stdout("\n")
|
||||
n--
|
||||
}
|
||||
}
|
||||
|
||||
func (cli help) printUsage() {
|
||||
if !term.IsTerminal(int(os.Stdout.Fd())) {
|
||||
os.Exit(1)
|
||||
}
|
||||
cli.header()
|
||||
|
||||
for n := 0; n < len(cli.usage); n++ {
|
||||
line := &strings.Builder{}
|
||||
buf := &strings.Builder{}
|
||||
usageAt := 1
|
||||
tlen := cli.secondColStart(n)
|
||||
switch {
|
||||
case cli.usage[n][0] == "":
|
||||
cli.lb(1)
|
||||
case cli.usage[n][1] == "":
|
||||
cli.stdout(cli.usage[n][0])
|
||||
cli.lb(2)
|
||||
case len(cli.usage[n]) > 2 && cli.usage[n][2] != "":
|
||||
tlen = cli.firstColEnd() - len(cli.usage[n][1])
|
||||
usageAt = 2
|
||||
fallthrough
|
||||
default:
|
||||
buf.WriteString(cli.usage[n][0])
|
||||
}
|
||||
if tlen < 0 {
|
||||
tlen = 2
|
||||
}
|
||||
tab := strings.Repeat(" ", tlen)
|
||||
line.WriteString(" ")
|
||||
if buf.Len() < cli.firstColEnd() {
|
||||
line.WriteString(strings.Repeat(" ", cli.firstColEnd()-buf.Len()))
|
||||
}
|
||||
if usageAt == 2 {
|
||||
buf.WriteString(strings.Repeat(" ", tlen/2))
|
||||
buf.WriteString(cli.usage[n][1])
|
||||
}
|
||||
buf.WriteString(tab)
|
||||
buf.Write([]byte(" (" + cli.usage[n][usageAt] + ")"))
|
||||
buf.Write([]byte{'\n'})
|
||||
line.Write([]byte(buf.String()))
|
||||
cli.stdout(line.String())
|
||||
}
|
||||
os.Exit(0)
|
||||
|
||||
}
|
||||
|
||||
func (cli help) header() {
|
||||
cli.stdout("\n")
|
||||
s := &strings.Builder{}
|
||||
s.Write([]byte(cli.title))
|
||||
s.Write([]byte(" v["))
|
||||
s.Write([]byte(cli.version))
|
||||
s.Write([]byte("]"))
|
||||
tab := cli.firstColEnd() - (s.Len() % 2) + 1
|
||||
if tab > 0 {
|
||||
cli.stdout(strings.Repeat(" ", tab))
|
||||
}
|
||||
cli.stdout(s.String())
|
||||
cli.lb(2)
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
var (
|
||||
// CurrentLogFile is used for accessing the location of the currently used log file across packages.
|
||||
CurrentLogFile string
|
||||
logFile io.Writer
|
||||
logDir string
|
||||
logger zerolog.Logger
|
||||
)
|
||||
|
||||
func prepLogDir() {
|
||||
logDir = snek.String("logger.directory")
|
||||
if logDir == "" {
|
||||
logDir = filepath.Join(home, ".local", "share", Title, "logs")
|
||||
}
|
||||
_ = os.MkdirAll(logDir, 0750)
|
||||
}
|
||||
|
||||
// StartLogger instantiates an instance of our zerolog loggger so we can hook it in our main package.
|
||||
// While this does return a logger, it should not be used for additional retrievals of the logger. Use GetLogger().
|
||||
func StartLogger(pretty bool, targets ...io.Writer) zerolog.Logger {
|
||||
logFileName := "HellPot"
|
||||
|
||||
if snek.Bool("logger.use_date_filename") {
|
||||
tn := strings.ReplaceAll(time.Now().Format(time.RFC822), " ", "_")
|
||||
tn = strings.ReplaceAll(tn, ":", "-")
|
||||
logFileName = logFileName + "_" + tn
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
switch {
|
||||
case len(targets) > 0:
|
||||
logFile = io.MultiWriter(targets...)
|
||||
default:
|
||||
prepLogDir()
|
||||
CurrentLogFile = path.Join(logDir, logFileName+".log")
|
||||
//nolint:lll
|
||||
logFile, err = os.OpenFile(CurrentLogFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0o666) // #nosec G304 G302 -- we are not using user input to create the file
|
||||
if err != nil {
|
||||
println("cannot create log file: " + err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
var logWriter = logFile
|
||||
|
||||
if pretty {
|
||||
logWriter = zerolog.MultiLevelWriter(zerolog.ConsoleWriter{TimeFormat: ConsoleTimeFormat, NoColor: NoColor, Out: os.Stdout}, logFile)
|
||||
}
|
||||
|
||||
logger = zerolog.New(logWriter).With().Timestamp().Logger()
|
||||
return logger
|
||||
}
|
||||
|
||||
// GetLogger retrieves our global logger object.
|
||||
func GetLogger() *zerolog.Logger {
|
||||
// future logic here
|
||||
return &logger
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/knadh/koanf/v2"
|
||||
"github.com/rs/zerolog"
|
||||
|
||||
"github.com/yunginnanet/HellPot/internal/logger"
|
||||
)
|
||||
|
||||
// Parameters represents the configuration for HellPot.
|
||||
type Parameters struct {
|
||||
HTTP HTTP `koanf:"http"`
|
||||
Logger *logger.Configuration `koanf:"logger"`
|
||||
Bespoke Customization `koanf:"bespoke"`
|
||||
Perf Performance `koanf:"performance"`
|
||||
Liar Deception `koanf:"deception"`
|
||||
|
||||
source *koanf.Koanf `koanf:"-"`
|
||||
logger *zerolog.Logger
|
||||
}
|
||||
|
||||
var once = &sync.Once{}
|
||||
|
||||
func (p *Parameters) GetLogger() *zerolog.Logger {
|
||||
once.Do(func() {
|
||||
p.logger = logger.GetLoggerOnce()
|
||||
})
|
||||
return p.logger
|
||||
}
|
||||
|
||||
type Deception struct {
|
||||
// FakeServerName is our configured value for the "Server: " response header when serving HTTP clients
|
||||
FakeServerName string `koanf:"fake_server_name"`
|
||||
}
|
||||
|
||||
type Performance struct {
|
||||
ConcurrencyCap bool `koanf:"limit_concurrency"`
|
||||
MaxWorkers int `koanf:"max_workers"`
|
||||
}
|
||||
|
||||
// UnixSocket represents the configuration for the Unix socket.
|
||||
type UnixSocket struct {
|
||||
// UnixSocketPath is the path to the Unix socket that HellPot will listen on if UseUnixSocket is set to true.
|
||||
UnixSocketPath string `koanf:"unix_socket_path"`
|
||||
// UseUnixSocket determines if we will listen for HTTP connections on a unix socket.
|
||||
UseUnixSocket bool `koanf:"use_unix_socket"`
|
||||
// UnixSocketPermissions are the octal permissions for the Unix socket.
|
||||
UnixSocketPermissions uint32 `koanf:"unix_socket_permissions"`
|
||||
}
|
||||
|
||||
// Router represents the configuration for the HTTP router.
|
||||
type Router struct {
|
||||
// Paths are defined via our toml configuration file. These are the paths that HellPot will present for "robots.txt"
|
||||
// These are also the paths that HellPot will respond for. Other paths will throw a warning and will serve a 404.
|
||||
Paths []string `koanf:"paths"`
|
||||
CatchAll bool `koanf:"catchall"`
|
||||
MakeRobots bool `koanf:"makerobots"`
|
||||
ClientRules ClientRules `koanf:"client_rules"`
|
||||
}
|
||||
|
||||
// HTTP represents the configuration for the HTTP server.
|
||||
type HTTP struct {
|
||||
Bind string `koanf:"bind_addr"`
|
||||
Port int64 `koanf:"port"`
|
||||
// ProxiedIPHeader is the HTTP Header containing the original IP of the client
|
||||
// for usage by traditional reverse Proxy deployments.
|
||||
ProxiedIPHeader string `koanf:"proxied_ip_header"`
|
||||
Router Router `koanf:"router"`
|
||||
UnixSocket UnixSocket `koanf:"unix_socket"`
|
||||
ClientRules ClientRules `koanf:"client_rules"`
|
||||
Experimental DevilsPlaythings `koanf:"experimental"`
|
||||
}
|
||||
|
||||
// DevilsPlaythings - nothing to see here, move along.
|
||||
type DevilsPlaythings struct {
|
||||
// POSTMimicry when true will cause HellPot to respond to POST requests to the configured roads to hell
|
||||
// with the content of the POST request entangled within the response. (Experimental)
|
||||
POSTMimicry bool `koanf:"post_mimicry"`
|
||||
}
|
||||
|
||||
// Customization represents the configuration for the customizations.
|
||||
type Customization struct {
|
||||
CustomHeffalump bool `koanf:"custom_heffalump"`
|
||||
Grimoire string `koanf:"grimoire"`
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package config
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestCompileRules(t *testing.T) {
|
||||
if _, err := NewClientRules(nil, nil); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
rules, err := NewClientRules([]string{"test", "test"}, nil)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if len(rules.UseragentDisallowStrings) != 1 {
|
||||
t.Error("expected 1 got", len(rules.UseragentDisallowStrings))
|
||||
}
|
||||
if rules.UseragentDisallowStrings[0] != "test" {
|
||||
t.Error("expected test got", rules.UseragentDisallowStrings[0])
|
||||
}
|
||||
rules, err = NewClientRules(
|
||||
[]string{"yeeterson", "", "", "", "yeeterson", "mc", "mc", "geeterson"},
|
||||
[]string{"^y..terson$", "^mc", "^mc", "^g..ters.n$"},
|
||||
)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if len(rules.useragentDisallowRegex) != 3 {
|
||||
t.Error("expected 3 got", len(rules.useragentDisallowRegex))
|
||||
}
|
||||
if len(rules.UseragentDisallowStrings) != 3 {
|
||||
t.Error("expected 3 got", len(rules.UseragentDisallowStrings))
|
||||
}
|
||||
if !rules.MatchUseragent("yeeterson") {
|
||||
t.Error("expected true got false")
|
||||
}
|
||||
if !rules.MatchUseragent("mc") {
|
||||
t.Error("expected true got false")
|
||||
}
|
||||
if !rules.MatchUseragent("yooterson") {
|
||||
t.Error("expected true got false")
|
||||
}
|
||||
if !rules.MatchUseragent("gooters%n") {
|
||||
t.Error("expected true got false")
|
||||
}
|
||||
if rules.MatchUseragent("yootersongooterson") {
|
||||
t.Error("expected false got true")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/knadh/koanf/parsers/toml"
|
||||
"github.com/knadh/koanf/providers/env"
|
||||
"github.com/knadh/koanf/v2"
|
||||
)
|
||||
|
||||
type readerProvider struct {
|
||||
source io.Reader
|
||||
}
|
||||
|
||||
func (r *readerProvider) ReadBytes() ([]byte, error) {
|
||||
return io.ReadAll(r.source)
|
||||
}
|
||||
|
||||
func (r *readerProvider) Read() (map[string]interface{}, error) {
|
||||
b, err := r.ReadBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return toml.Parser().Unmarshal(b) //nolint:wrapcheck
|
||||
}
|
||||
|
||||
func Setup(source io.Reader) (*Parameters, error) {
|
||||
k := koanf.New(".")
|
||||
|
||||
if err := k.Load(Defaults, nil); err != nil {
|
||||
return nil, fmt.Errorf("failed to load defaults: %w", err)
|
||||
}
|
||||
|
||||
if source != nil {
|
||||
if err := k.Load(&readerProvider{source}, toml.Parser()); err != nil {
|
||||
return nil, fmt.Errorf("failed to read config: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
_ = k.Load(env.Provider("HELLPOT_", ".", func(s string) string {
|
||||
s = strings.TrimPrefix(s, "HELLPOT_")
|
||||
s = strings.ToLower(s)
|
||||
s = strings.ReplaceAll(s, "__", " ")
|
||||
s = strings.ReplaceAll(s, "_", ".")
|
||||
s = strings.ReplaceAll(s, " ", "_")
|
||||
return s
|
||||
}), nil)
|
||||
|
||||
p := &Parameters{
|
||||
source: k,
|
||||
}
|
||||
|
||||
if err := k.Unmarshal("", p); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal config: %w", err)
|
||||
}
|
||||
|
||||
p.Logger.Outputs = append(p.Logger.Outputs, os.Stdout)
|
||||
|
||||
return p, nil
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSetup(t *testing.T) {
|
||||
t.Run("Success", SetupSuccess)
|
||||
t.Run("NoFailureOnNilSource", SetupNoFailureOnNilSource)
|
||||
t.Run("FailureOnReadConfig", SetupFailureOnReadConfig)
|
||||
}
|
||||
|
||||
func SetupSuccess(t *testing.T) {
|
||||
source := bytes.NewBufferString(`
|
||||
[http]
|
||||
port = 55
|
||||
bind_addr = "5.5.5.5"
|
||||
|
||||
[http.router]
|
||||
catchall = true
|
||||
makerobots = false
|
||||
`)
|
||||
|
||||
params, err := Setup(source)
|
||||
if err != nil {
|
||||
t.Errorf("Expected no error, got %v", err)
|
||||
}
|
||||
|
||||
if params == nil {
|
||||
t.Fatal("Expected params to be not nil")
|
||||
}
|
||||
|
||||
if params.source.Get("http.port") != int64(55) {
|
||||
t.Errorf("Expected 55, got (%T) %v", params.source.Get("http.port"), params.source.Get("http.port"))
|
||||
}
|
||||
if params.HTTP.Port != int64(55) {
|
||||
t.Errorf("Expected 55, got %v", params.HTTP.Port)
|
||||
}
|
||||
if params.source.Get("http.bind_addr") != "5.5.5.5" {
|
||||
t.Errorf("Expected 5.5.5.5, got %v", params.source.Get("http.bind_addr"))
|
||||
}
|
||||
if params.HTTP.Bind != "5.5.5.5" {
|
||||
t.Errorf("Expected 5.5.5.5, got %v", params.HTTP.Bind)
|
||||
}
|
||||
if params.source.Get("http.router.catchall") != true {
|
||||
t.Errorf("Expected true, got %v", params.source.Get("http.router.catchall"))
|
||||
}
|
||||
if params.HTTP.Router.CatchAll != true {
|
||||
t.Errorf("Expected true, got %v", params.HTTP.Router.CatchAll)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func SetupNoFailureOnNilSource(t *testing.T) {
|
||||
params, err := Setup(nil)
|
||||
if err != nil {
|
||||
t.Errorf("Expected no error, got %v", err)
|
||||
}
|
||||
|
||||
if params == nil {
|
||||
t.Fatal("Expected params to be not nil")
|
||||
}
|
||||
|
||||
t.Run("DefaultsWithNilSource", func(t *testing.T) {
|
||||
for _, needle := range []string{
|
||||
"logger",
|
||||
"http",
|
||||
"performance",
|
||||
"deception",
|
||||
"http.router",
|
||||
"http.router.paths",
|
||||
} {
|
||||
if params.source.Get(needle) == nil {
|
||||
t.Errorf("Expected %q in map", needle)
|
||||
}
|
||||
}
|
||||
|
||||
// nolint:forcetypeassert
|
||||
if params.HTTP.Port != Defaults.val["http"].(map[string]interface{})["port"].(int64) {
|
||||
t.Errorf("Expected %v, got %v",
|
||||
// nolint:forcetypeassert
|
||||
Defaults.val["http"].(map[string]interface{})["port"].(int64), params.HTTP.Port,
|
||||
)
|
||||
}
|
||||
// nolint:forcetypeassert
|
||||
if params.HTTP.Bind != Defaults.val["http"].(map[string]interface{})["bind_addr"].(string) {
|
||||
t.Errorf("Expected %v, got %v",
|
||||
// nolint:forcetypeassert
|
||||
Defaults.val["http"].(map[string]interface{})["bind_addr"].(string), params.HTTP.Bind,
|
||||
)
|
||||
}
|
||||
if params.HTTP.Router.CatchAll !=
|
||||
// nolint:forcetypeassert
|
||||
Defaults.val["http"].(map[string]interface{})["router"].(map[string]interface{})["catchall"].(bool) {
|
||||
t.Errorf("Expected %v, got %v",
|
||||
// nolint:forcetypeassert
|
||||
Defaults.val["http"].(map[string]interface{})["router"].(map[string]interface{})["catchall"].(bool),
|
||||
params.HTTP.Router.CatchAll,
|
||||
)
|
||||
}
|
||||
if len(params.HTTP.Router.Paths) !=
|
||||
// nolint:forcetypeassert
|
||||
len(Defaults.val["http"].(map[string]interface{})["router"].(map[string]interface{})["paths"].([]string)) {
|
||||
t.Errorf("Expected %v, got %v",
|
||||
// nolint:forcetypeassert
|
||||
Defaults.val["http"].(map[string]interface{})["router"].(map[string]interface{})["paths"].([]string),
|
||||
params.HTTP.Router.Paths,
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func SetupFailureOnReadConfig(t *testing.T) {
|
||||
source := bytes.NewBufferString(`{eeeeeeeeeeeeeeeeeeEE: 1}`)
|
||||
|
||||
params, err := Setup(source)
|
||||
if err == nil {
|
||||
t.Error("Expected error, got nil")
|
||||
}
|
||||
|
||||
if params != nil {
|
||||
t.Error("Expected params to be nil")
|
||||
}
|
||||
}
|
|
@ -5,13 +5,12 @@ import (
|
|||
"encoding/binary"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.tcp.direct/kayos/common/squish"
|
||||
|
||||
"github.com/yunginnanet/HellPot/internal/config"
|
||||
"github.com/yunginnanet/HellPot/internal/version"
|
||||
)
|
||||
|
||||
const hellpot = "H4sIAAAAAAACA8VXvW7bQAze9QpZOGQUZNXntBD6Ahm7Gx1cx0jdRnKRKAUCdPDgQavOgB/QTxLZ1P3oRJ5Obo0CtnE5feSR30fylOhmfjv9PEtzwIXIj4dds/xw2jsequNB2gizXd3Mxad2O81PX7AAe+UNGneuR8aUOuTsqQUDXAMv1cJE5Tfbn6GaKz45kpid+lQc3zoNY5zmEUEt+jCGNZUjeYr0StZYmbwtwNavuCaUFWA8MjxVIImjNas6TPQT9Tnq4MnYJF0zkhVU4rLvqflscU/ox0Lg45qKTjoSmiLQPA+ZuTT7BbrckpfWKMkUquTErIPEYbPoKjamy6SjR0feGssPUMYTCDWEnrR8c0m7hJ2B4jekK2KUsBfa7bpTD0ftnmKPE9nN2IzcLc99vxhIUbszlwqrJoklpQWlI6AeQh9nDHXj2ldOvyat/vZdDxVfzZdbSuspRUe/+IKZtxq2GWlbZzS6jnrnDEXGCkXUGnahuTgAA+DY9HU8FUoYH3ji/q84HetDWmT/Y3ml6oX21/eCNzB46+6UuVTSQHXgGmzUTJT/zeNQ3zCvysEBuH3hER9CbhNa6FoLHSBfT2gmK/rFKCj/K1nTfcBduKHVwgjo+Y+HilXBEAqhKg1X6lQzMaIF6ZK6ipVILR0Awh16SWy9KsxvZXWbL34oGpNmMcPNdYFmiE40+qV9cg4Logjm2uXjukzK5a/kYf28WpaTn4u3zcvkfvX09GVTnuFfEYzBNujvr9+S5SafvL0Wj+uiWBSrsov/I6axmMXiLhYf40zE2TTOZnF2F2fNn2n0DpcvBxhQEAAA"
|
||||
|
@ -21,23 +20,23 @@ func rc(s []string) string {
|
|||
}
|
||||
|
||||
func process(in string) (s string) {
|
||||
var v = strings.Split(config.Version, "")
|
||||
var v = strings.Split(version.Version, "")
|
||||
var maj, min, smin = "", "", ""
|
||||
if len(config.Version) > 0 {
|
||||
if len(version.Version) > 0 {
|
||||
maj = v[0]
|
||||
}
|
||||
if len(config.Version) > 2 {
|
||||
if len(version.Version) > 2 {
|
||||
min = v[2]
|
||||
}
|
||||
if len(config.Version) > 4 {
|
||||
if len(version.Version) > 4 {
|
||||
smin = v[4]
|
||||
}
|
||||
defl8, _ := squish.UnpackStr(in)
|
||||
sp := strings.Split(defl8, "|")
|
||||
s = sp[0]
|
||||
if smin == "" || len(config.Version) == 7 || config.Version == "dev" {
|
||||
if smin == "" || len(version.Version) == 7 || version.Version == "dev" {
|
||||
s = strings.ReplaceAll(s, "$1;40m.", "$1;40m")
|
||||
if len(config.Version) == 7 || config.Version == "dev" {
|
||||
if len(version.Version) == 7 || version.Version == "dev" {
|
||||
s = strings.ReplaceAll(s, "$3;40m.", "$3;40m")
|
||||
}
|
||||
}
|
||||
|
@ -53,8 +52,8 @@ func process(in string) (s string) {
|
|||
for n := 1; n < 5; n++ {
|
||||
s = cproc(s, fmt.Sprintf("%d", n))
|
||||
}
|
||||
if len(config.Version) == 7 || config.Version == "dev" {
|
||||
maj = "[" + config.Version + "]"
|
||||
if len(version.Version) == 7 || version.Version == "dev" {
|
||||
maj = "[" + version.Version + "]"
|
||||
min = ""
|
||||
smin = ""
|
||||
}
|
||||
|
@ -72,8 +71,8 @@ func ru() uint32 {
|
|||
return binary.LittleEndian.Uint32(b)
|
||||
}
|
||||
|
||||
// printBanner prints our entropic banner
|
||||
func printBanner() {
|
||||
// Banner prints our entropic banner
|
||||
func Banner() {
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
println("\n" + process(hellpot) + "\n\n")
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
|
@ -88,13 +87,3 @@ func bannerFail(errs ...error) {
|
|||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Banner prints out our banner (using spooky magic)
|
||||
func Banner() {
|
||||
//goland:noinspection GoBoolExpressions
|
||||
if runtime.GOOS == "windows" || config.NoColor {
|
||||
_, _ = os.Stdout.Write([]byte(config.Title + " " + config.Version + "\n\n"))
|
||||
return
|
||||
}
|
||||
printBanner()
|
||||
}
|
||||
|
|
|
@ -2,32 +2,35 @@ package http
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.tcp.direct/kayos/common/pool"
|
||||
"github.com/valyala/fasthttp"
|
||||
|
||||
"github.com/yunginnanet/HellPot/internal/config"
|
||||
)
|
||||
|
||||
var strs = pool.NewStringFactory()
|
||||
|
||||
func robotsTXT(ctx *fasthttp.RequestCtx) {
|
||||
config := runningConfig.HTTP.Router
|
||||
slog := log.With().
|
||||
Str("USERAGENT", string(ctx.UserAgent())).
|
||||
Str("REMOTE_ADDR", getRealRemote(ctx)).
|
||||
Interface("URL", string(ctx.RequestURI())).Logger()
|
||||
paths := &strings.Builder{}
|
||||
paths.WriteString("User-agent: *\r\n")
|
||||
pathBuf := strs.Get()
|
||||
pathBuf.MustWriteString("User-agent: *\r\n")
|
||||
for _, p := range config.Paths {
|
||||
paths.WriteString("Disallow: ")
|
||||
paths.WriteString(p)
|
||||
paths.WriteString("\r\n")
|
||||
pathBuf.MustWriteString("Disallow: ")
|
||||
pathBuf.MustWriteString(p)
|
||||
pathBuf.MustWriteString("\r\n")
|
||||
}
|
||||
paths.WriteString("\r\n")
|
||||
pathBuf.MustWriteString("\r\n")
|
||||
paths := pathBuf.String()
|
||||
strs.MustPut(pathBuf)
|
||||
|
||||
slog.Debug().
|
||||
Strs("PATHS", config.Paths).
|
||||
Msg("SERVE_ROBOTS")
|
||||
|
||||
if _, err := fmt.Fprintf(ctx, paths.String()); err != nil {
|
||||
if _, err := fmt.Fprintf(ctx, paths); err != nil {
|
||||
slog.Error().Err(err).Msg("SERVE_ROBOTS_ERROR")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"net/http"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -20,10 +21,11 @@ import (
|
|||
var (
|
||||
log *zerolog.Logger
|
||||
hellpotHeffalump *heffalump.Heffalump
|
||||
runningConfig *config.Parameters
|
||||
)
|
||||
|
||||
func getRealRemote(ctx *fasthttp.RequestCtx) string {
|
||||
xrealip := string(ctx.Request.Header.Peek(config.HeaderName))
|
||||
xrealip := string(ctx.Request.Header.Peek(runningConfig.HTTP.ProxiedIPHeader))
|
||||
if len(xrealip) > 0 {
|
||||
return xrealip
|
||||
}
|
||||
|
@ -43,15 +45,13 @@ func hellPot(ctx *fasthttp.RequestCtx) {
|
|||
Str("REMOTE_ADDR", remoteAddr).
|
||||
Interface("URL", string(ctx.RequestURI())).Logger()
|
||||
|
||||
for _, denied := range config.UseragentBlacklistMatchers {
|
||||
if strings.Contains(string(ctx.UserAgent()), denied) {
|
||||
slog.Trace().Msg("Ignoring useragent")
|
||||
ctx.Error("Not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
if runningConfig.HTTP.ClientRules.MatchUseragent(ctx.UserAgent()) {
|
||||
slog.Trace().Msg("Ignoring useragent")
|
||||
ctx.Error("Not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
if config.Trace {
|
||||
if runningConfig.Logger.Trace {
|
||||
slog = slog.With().Str("caller", path).Logger()
|
||||
}
|
||||
|
||||
|
@ -81,31 +81,29 @@ func hellPot(ctx *fasthttp.RequestCtx) {
|
|||
}
|
||||
|
||||
func getSrv(r *router.Router) fasthttp.Server {
|
||||
if !config.RestrictConcurrency {
|
||||
config.MaxWorkers = fasthttp.DefaultConcurrency
|
||||
if !runningConfig.Perf.ConcurrencyCap {
|
||||
runningConfig.Perf.MaxWorkers = fasthttp.DefaultConcurrency
|
||||
}
|
||||
|
||||
log = config.GetLogger()
|
||||
log = runningConfig.GetLogger()
|
||||
|
||||
return fasthttp.Server{
|
||||
// User defined server name
|
||||
// Likely not useful if behind a reverse proxy without additional configuration of the proxy server.
|
||||
Name: config.FakeServerName,
|
||||
Name: runningConfig.Liar.FakeServerName,
|
||||
|
||||
/*
|
||||
from fasthttp docs: "By default request read timeout is unlimited."
|
||||
My thinking here is avoiding some sort of weird oversized GET query just in case.
|
||||
Nope.
|
||||
*/
|
||||
ReadTimeout: 5 * time.Second,
|
||||
MaxRequestBodySize: 1 * 1024 * 1024,
|
||||
|
||||
// Help curb abuse of HellPot (we've always needed this badly)
|
||||
MaxConnsPerIP: 10,
|
||||
MaxConnsPerIP: 3,
|
||||
MaxRequestsPerConn: 2,
|
||||
Concurrency: config.MaxWorkers,
|
||||
Concurrency: runningConfig.Perf.MaxWorkers,
|
||||
|
||||
// only accept GET requests
|
||||
GetOnly: true,
|
||||
// GetOnly: true,
|
||||
|
||||
// we don't care if a request ends up being handled by a different handler (in fact it probably will)
|
||||
KeepHijackedConns: true,
|
||||
|
@ -121,18 +119,19 @@ func getSrv(r *router.Router) fasthttp.Server {
|
|||
}
|
||||
|
||||
// Serve starts our HTTP server and request router
|
||||
func Serve() error {
|
||||
func Serve(config *config.Parameters) error {
|
||||
log = config.GetLogger()
|
||||
runningConfig = config
|
||||
|
||||
switch config.UseCustomHeffalump {
|
||||
switch config.Bespoke.CustomHeffalump {
|
||||
case true:
|
||||
content, err := os.ReadFile(config.Grimoire)
|
||||
content, err := os.ReadFile(config.Bespoke.Grimoire)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// Wasteful, but only done once at startup
|
||||
src := string(content)
|
||||
log.Info().Msgf("Using custom grimoire file '%s'", config.Grimoire)
|
||||
log.Info().Msgf("Using custom grimoire file '%s'", config.Bespoke.Grimoire)
|
||||
|
||||
if len(src) < 1 {
|
||||
panic("grimoire file was empty!")
|
||||
|
@ -145,16 +144,16 @@ func Serve() error {
|
|||
hellpotHeffalump = heffalump.NewDefaultHeffalump()
|
||||
}
|
||||
|
||||
l := config.HTTPBind + ":" + config.HTTPPort
|
||||
l := config.HTTP.Bind + ":" + strconv.Itoa(int(config.HTTP.Port))
|
||||
|
||||
r := router.New()
|
||||
|
||||
if config.MakeRobots && !config.CatchAll {
|
||||
if config.HTTP.Router.MakeRobots && !config.HTTP.Router.CatchAll {
|
||||
r.GET("/robots.txt", robotsTXT)
|
||||
}
|
||||
|
||||
if !config.CatchAll {
|
||||
for _, p := range config.Paths {
|
||||
if !config.HTTP.Router.CatchAll {
|
||||
for _, p := range config.HTTP.Router.Paths {
|
||||
log.Trace().Str("caller", "router").Msgf("Add route: %s", p)
|
||||
r.GET(fmt.Sprintf("/%s", p), hellPot)
|
||||
}
|
||||
|
@ -166,15 +165,15 @@ func Serve() error {
|
|||
srv := getSrv(r)
|
||||
|
||||
//goland:noinspection GoBoolExpressions
|
||||
if !config.UseUnixSocket || runtime.GOOS == "windows" {
|
||||
if !config.HTTP.UnixSocket.UseUnixSocket || runtime.GOOS == "windows" {
|
||||
log.Info().Str("caller", l).Msg("Listening and serving HTTP...")
|
||||
return srv.ListenAndServe(l)
|
||||
}
|
||||
|
||||
if len(config.UnixSocketPath) < 1 {
|
||||
if len(config.HTTP.UnixSocket.UnixSocketPath) < 1 {
|
||||
log.Fatal().Msg("unix_socket_path configuration directive appears to be empty")
|
||||
}
|
||||
|
||||
log.Info().Str("caller", config.UnixSocketPath).Msg("Listening and serving HTTP...")
|
||||
return listenOnUnixSocket(config.UnixSocketPath, r)
|
||||
log.Info().Str("caller", config.HTTP.UnixSocket.UnixSocketPath).Msg("Listening and serving HTTP...")
|
||||
return listenOnUnixSocket(config.HTTP.UnixSocket.UnixSocketPath, r)
|
||||
}
|
||||
|
|
|
@ -9,11 +9,10 @@ import (
|
|||
|
||||
"github.com/fasthttp/router"
|
||||
"github.com/valyala/fasthttp"
|
||||
|
||||
"github.com/yunginnanet/HellPot/internal/config"
|
||||
)
|
||||
|
||||
func listenOnUnixSocket(addr string, r *router.Router) error {
|
||||
config := runningConfig.HTTP.UnixSocket
|
||||
var err error
|
||||
var unixAddr *net.UnixAddr
|
||||
var unixListener *net.UnixListener
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
package logger
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
// Configuration represents the configuration for the logger.
|
||||
type Configuration struct {
|
||||
Directory string `koanf:"directory"`
|
||||
Debug bool `koanf:"debug"`
|
||||
Trace bool `koanf:"trace"`
|
||||
NoColor bool `koanf:"nocolor"`
|
||||
DockerLogging bool `koanf:"docker_logging"`
|
||||
// ConsoleTimeFormat sets the time format for the console.
|
||||
// The string is passed to time.Format() down the line.
|
||||
ConsoleTimeFormat string
|
||||
|
||||
Outputs []io.Writer `koanf:"-"`
|
||||
}
|
||||
|
||||
var once = &sync.Once{}
|
||||
|
||||
func GetLoggerOnce() *zerolog.Logger {
|
||||
var ret *zerolog.Logger
|
||||
once.Do(func() {
|
||||
ret = &_log
|
||||
})
|
||||
if ret == nil {
|
||||
panic("i said once you fool")
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
var ErrNoOutputs = errors.New("no outputs provided")
|
||||
|
||||
var _log zerolog.Logger
|
||||
|
||||
func New(conf *Configuration) (zerolog.Logger, error) {
|
||||
if len(conf.Outputs) == 0 {
|
||||
return zerolog.Logger{}, ErrNoOutputs
|
||||
}
|
||||
for i, output := range conf.Outputs {
|
||||
if output == os.Stdout || output == os.Stderr {
|
||||
cw := zerolog.ConsoleWriter{Out: output, TimeFormat: conf.ConsoleTimeFormat, NoColor: conf.NoColor}
|
||||
conf.Outputs = append(conf.Outputs[:i], conf.Outputs[i+1:]...)
|
||||
conf.Outputs = append(conf.Outputs, cw)
|
||||
}
|
||||
}
|
||||
_log = zerolog.New(zerolog.MultiLevelWriter(conf.Outputs...)).With().Timestamp().Logger()
|
||||
_log = _log.Level(zerolog.InfoLevel)
|
||||
if conf.Debug {
|
||||
_log = _log.Level(zerolog.DebugLevel)
|
||||
}
|
||||
if conf.Trace {
|
||||
_log = _log.Level(zerolog.TraceLevel)
|
||||
}
|
||||
return _log, nil
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
package hellscope
|
||||
|
||||
func Measured
|
|
@ -0,0 +1,26 @@
|
|||
package version
|
||||
|
||||
import (
|
||||
"runtime/debug"
|
||||
)
|
||||
|
||||
const HP = "HellPot"
|
||||
|
||||
var Version = "dev"
|
||||
|
||||
func init() {
|
||||
if Version != "dev" {
|
||||
return
|
||||
}
|
||||
binInfo := make(map[string]string)
|
||||
info, ok := debug.ReadBuildInfo()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
for _, v := range info.Settings {
|
||||
binInfo[v.Key] = v.Value
|
||||
}
|
||||
if gitrev, ok := binInfo["vcs.revision"]; ok {
|
||||
Version = gitrev[:7]
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue