mirror of
https://github.com/Gouryella/drip.git
synced 2026-02-23 12:53:43 +00:00
feat(server): Adds server configuration management commands and metric monitoring functionality.
- Add a new `server config` command to display server configuration. - Supports displaying the full token via the --full flag. - Add the metrics-token configuration option for monitoring access control. - Integrate Prometheus metrics monitoring system - Add the /metrics endpoint to provide monitoring data in Prometheus format. - Add detailed metric collection for tunnels, connections, traffic, etc. - Add a link to the metrics endpoint on the homepage refactor: Refactor the token display logic to support full display options. - Refactor the token mask logic in the configuration display - Supports controlling the token display method via the configFull flag. build: Update dependency versions - Updated github.com/spf13/cobra from v1.10.1 to v1.10.2 - Updated golang.org/x/crypto from v0.45.0 to v0.46.0 - Updated golang.org/x/net from v0.47.0 to v0.48.0 - Update golang.org/x/sys from v0.38.0 to v0.39.0 - Added several new indirect dependency packages, including Prometheus-related components. - Update the versions of several existing dependency packages.
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -53,4 +53,5 @@ certs/
|
||||
.drip-server.env
|
||||
benchmark-results/
|
||||
drip-linux-amd64
|
||||
./drip
|
||||
./drip
|
||||
drip
|
||||
|
||||
34
go.mod
34
go.mod
@@ -7,29 +7,41 @@ require (
|
||||
github.com/goccy/go-json v0.10.5
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/hashicorp/yamux v0.1.2
|
||||
github.com/spf13/cobra v1.10.1
|
||||
github.com/spf13/cobra v1.10.2
|
||||
go.uber.org/zap v1.27.1
|
||||
golang.org/x/crypto v0.45.0
|
||||
golang.org/x/net v0.47.0
|
||||
golang.org/x/sys v0.38.0
|
||||
golang.org/x/crypto v0.46.0
|
||||
golang.org/x/net v0.48.0
|
||||
golang.org/x/sys v0.39.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
|
||||
github.com/charmbracelet/x/ansi v0.8.0 // indirect
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
|
||||
github.com/charmbracelet/x/term v0.2.1 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/charmbracelet/colorprofile v0.4.1 // indirect
|
||||
github.com/charmbracelet/x/ansi v0.11.3 // indirect
|
||||
github.com/charmbracelet/x/cellbuf v0.0.14 // indirect
|
||||
github.com/charmbracelet/x/term v0.2.2 // indirect
|
||||
github.com/clipperhouse/displaywidth v0.6.1 // indirect
|
||||
github.com/clipperhouse/stringish v0.1.1 // indirect
|
||||
github.com/clipperhouse/uax29/v2 v2.3.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.19 // indirect
|
||||
github.com/muesli/termenv v0.16.0 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/prometheus/client_golang v1.23.2 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.67.4 // indirect
|
||||
github.com/prometheus/procfs v0.19.2 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
github.com/stretchr/testify v1.11.1 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/text v0.31.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||
golang.org/x/text v0.32.0 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
)
|
||||
|
||||
56
go.sum
56
go.sum
@@ -1,15 +1,33 @@
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
|
||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=
|
||||
github.com/charmbracelet/colorprofile v0.4.1 h1:a1lO03qTrSIRaK8c3JRxJDZOvhvIeSco3ej+ngLk1kk=
|
||||
github.com/charmbracelet/colorprofile v0.4.1/go.mod h1:U1d9Dljmdf9DLegaJ0nGZNJvoXAhayhmidOdcBwAvKk=
|
||||
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
|
||||
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
|
||||
github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE=
|
||||
github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q=
|
||||
github.com/charmbracelet/x/ansi v0.11.3 h1:6DcVaqWI82BBVM/atTyq6yBoRLZFBsnoDoX9GCu2YOI=
|
||||
github.com/charmbracelet/x/ansi v0.11.3/go.mod h1:yI7Zslym9tCJcedxz5+WBq+eUGMJT0bM06Fqy1/Y4dI=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.14 h1:iUEMryGyFTelKW3THW4+FfPgi4fkmKnnaLOXuc+/Kj4=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.14/go.mod h1:P447lJl49ywBbil/KjCk2HexGh4tEY9LH0/1QrZZ9rA=
|
||||
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
|
||||
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
|
||||
github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
|
||||
github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
|
||||
github.com/clipperhouse/displaywidth v0.6.1 h1:/zMlAezfDzT2xy6acHBzwIfyu2ic0hgkT83UX5EY2gY=
|
||||
github.com/clipperhouse/displaywidth v0.6.1/go.mod h1:R+kHuzaYWFkTm7xoMmK1lFydbci4X2CicfbGstSGg0o=
|
||||
github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
|
||||
github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
|
||||
github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4=
|
||||
github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -23,20 +41,40 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
|
||||
github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
|
||||
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
|
||||
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
|
||||
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
|
||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||
github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
|
||||
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
|
||||
github.com/prometheus/common v0.67.4 h1:yR3NqWO1/UyO1w2PhUvXlGQs/PtFmoveVO0KZ4+Lvsc=
|
||||
github.com/prometheus/common v0.67.4/go.mod h1:gP0fq6YjjNCLssJCQp0yk4M8W6ikLURwkdd/YKtTbyI=
|
||||
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
|
||||
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
|
||||
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
|
||||
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
|
||||
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
|
||||
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
||||
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
@@ -50,17 +88,35 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
|
||||
go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
|
||||
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
|
||||
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
|
||||
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
||||
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
|
||||
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E=
|
||||
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
||||
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
||||
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
||||
google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
|
||||
google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
|
||||
@@ -117,16 +117,17 @@ func runConfigShow(_ *cobra.Command, _ []string) error {
|
||||
|
||||
var displayToken string
|
||||
if cfg.Token != "" {
|
||||
tokenLen := len(cfg.Token)
|
||||
if tokenLen <= 3 {
|
||||
// For very short tokens, just show asterisks
|
||||
displayToken = "***"
|
||||
} else if tokenLen > 10 {
|
||||
// For long tokens, show first 3 and last 3 characters
|
||||
displayToken = cfg.Token[:3] + "***" + cfg.Token[tokenLen-3:]
|
||||
if configFull {
|
||||
displayToken = cfg.Token
|
||||
} else {
|
||||
// For medium tokens (4-10 chars), show first 3 characters
|
||||
displayToken = cfg.Token[:3] + "***"
|
||||
tokenLen := len(cfg.Token)
|
||||
if tokenLen <= 3 {
|
||||
displayToken = "***"
|
||||
} else if tokenLen > 10 {
|
||||
displayToken = cfg.Token[:3] + "***" + cfg.Token[tokenLen-3:]
|
||||
} else {
|
||||
displayToken = cfg.Token[:3] + "***"
|
||||
}
|
||||
}
|
||||
} else {
|
||||
displayToken = ""
|
||||
|
||||
@@ -21,16 +21,17 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
serverPort int
|
||||
serverPublicPort int
|
||||
serverDomain string
|
||||
serverAuthToken string
|
||||
serverDebug bool
|
||||
serverTCPPortMin int
|
||||
serverTCPPortMax int
|
||||
serverTLSCert string
|
||||
serverTLSKey string
|
||||
serverPprofPort int
|
||||
serverPort int
|
||||
serverPublicPort int
|
||||
serverDomain string
|
||||
serverAuthToken string
|
||||
serverMetricsToken string
|
||||
serverDebug bool
|
||||
serverTCPPortMin int
|
||||
serverTCPPortMax int
|
||||
serverTLSCert string
|
||||
serverTLSKey string
|
||||
serverPprofPort int
|
||||
)
|
||||
|
||||
var serverCmd = &cobra.Command{
|
||||
@@ -48,6 +49,7 @@ func init() {
|
||||
serverCmd.Flags().IntVar(&serverPublicPort, "public-port", getEnvInt("DRIP_PUBLIC_PORT", 0), "Public port to display in URLs (env: DRIP_PUBLIC_PORT)")
|
||||
serverCmd.Flags().StringVarP(&serverDomain, "domain", "d", getEnvString("DRIP_DOMAIN", constants.DefaultDomain), "Server domain (env: DRIP_DOMAIN)")
|
||||
serverCmd.Flags().StringVarP(&serverAuthToken, "token", "t", getEnvString("DRIP_TOKEN", ""), "Authentication token (env: DRIP_TOKEN)")
|
||||
serverCmd.Flags().StringVar(&serverMetricsToken, "metrics-token", getEnvString("DRIP_METRICS_TOKEN", ""), "Metrics and stats token (env: DRIP_METRICS_TOKEN)")
|
||||
serverCmd.Flags().BoolVar(&serverDebug, "debug", false, "Enable debug logging")
|
||||
serverCmd.Flags().IntVar(&serverTCPPortMin, "tcp-port-min", getEnvInt("DRIP_TCP_PORT_MIN", constants.DefaultTCPPortMin), "Minimum TCP tunnel port (env: DRIP_TCP_PORT_MIN)")
|
||||
serverCmd.Flags().IntVar(&serverTCPPortMax, "tcp-port-max", getEnvInt("DRIP_TCP_PORT_MAX", constants.DefaultTCPPortMax), "Maximum TCP tunnel port (env: DRIP_TCP_PORT_MAX)")
|
||||
@@ -130,7 +132,7 @@ func runServer(_ *cobra.Command, _ []string) error {
|
||||
|
||||
listenAddr := fmt.Sprintf("0.0.0.0:%d", serverPort)
|
||||
|
||||
httpHandler := proxy.NewHandler(tunnelManager, logger, serverDomain, serverAuthToken)
|
||||
httpHandler := proxy.NewHandler(tunnelManager, logger, serverDomain, serverAuthToken, serverMetricsToken)
|
||||
|
||||
listener := tcp.NewListener(listenAddr, tlsConfig, serverAuthToken, tunnelManager, logger, portAllocator, serverDomain, displayPort, httpHandler)
|
||||
|
||||
|
||||
176
internal/client/cli/server_config.go
Normal file
176
internal/client/cli/server_config.go
Normal file
@@ -0,0 +1,176 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"drip/internal/shared/constants"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
serverConfigFull bool
|
||||
)
|
||||
|
||||
var serverConfigCmd = &cobra.Command{
|
||||
Use: "config",
|
||||
Short: "Manage server configuration",
|
||||
Long: "Display and manage Drip server configuration",
|
||||
}
|
||||
|
||||
var serverConfigShowCmd = &cobra.Command{
|
||||
Use: "show",
|
||||
Short: "Show server configuration",
|
||||
Long: "Display the current Drip server configuration from environment variables and flags",
|
||||
RunE: runServerConfigShow,
|
||||
}
|
||||
|
||||
func init() {
|
||||
serverConfigCmd.AddCommand(serverConfigShowCmd)
|
||||
serverConfigShowCmd.Flags().BoolVar(&serverConfigFull, "full", false, "Show full tokens (not masked)")
|
||||
|
||||
serverCmd.AddCommand(serverConfigCmd)
|
||||
}
|
||||
|
||||
func runServerConfigShow(_ *cobra.Command, _ []string) error {
|
||||
// Read configuration from environment variables and defaults
|
||||
port := getEnvInt("DRIP_PORT", 8443)
|
||||
publicPort := getEnvInt("DRIP_PUBLIC_PORT", 0)
|
||||
domain := getEnvString("DRIP_DOMAIN", constants.DefaultDomain)
|
||||
token := getEnvString("DRIP_TOKEN", "")
|
||||
metricsToken := getEnvString("DRIP_METRICS_TOKEN", "")
|
||||
tlsCert := getEnvString("DRIP_TLS_CERT", "")
|
||||
tlsKey := getEnvString("DRIP_TLS_KEY", "")
|
||||
tcpPortMin := getEnvInt("DRIP_TCP_PORT_MIN", constants.DefaultTCPPortMin)
|
||||
tcpPortMax := getEnvInt("DRIP_TCP_PORT_MAX", constants.DefaultTCPPortMax)
|
||||
pprofPort := getEnvInt("DRIP_PPROF_PORT", 0)
|
||||
|
||||
if publicPort == 0 {
|
||||
publicPort = port
|
||||
}
|
||||
|
||||
// Mask tokens if not showing full
|
||||
displayToken := maskToken(token, serverConfigFull)
|
||||
displayMetricsToken := maskToken(metricsToken, serverConfigFull)
|
||||
|
||||
// Print configuration
|
||||
fmt.Println()
|
||||
fmt.Println("╔══════════════════════════════════════════════════════════════╗")
|
||||
fmt.Println("║ Drip Server Configuration ║")
|
||||
fmt.Println("╚══════════════════════════════════════════════════════════════╝")
|
||||
fmt.Println()
|
||||
|
||||
// Server settings
|
||||
fmt.Println("📡 Server Settings:")
|
||||
fmt.Printf(" Domain: %s\n", colorValue(domain))
|
||||
fmt.Printf(" Port: %s\n", colorValue(fmt.Sprintf("%d", port)))
|
||||
if publicPort != port {
|
||||
fmt.Printf(" Public Port: %s\n", colorValue(fmt.Sprintf("%d", publicPort)))
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
// Authentication
|
||||
fmt.Println("🔐 Authentication:")
|
||||
if token != "" {
|
||||
fmt.Printf(" Auth Token: %s\n", colorValue(displayToken))
|
||||
} else {
|
||||
fmt.Printf(" Auth Token: %s\n", colorWarning("(not set)"))
|
||||
}
|
||||
if metricsToken != "" {
|
||||
fmt.Printf(" Metrics Token: %s\n", colorValue(displayMetricsToken))
|
||||
} else {
|
||||
fmt.Printf(" Metrics Token: %s\n", colorWarning("(not set)"))
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
// TLS Configuration
|
||||
fmt.Println("🔒 TLS Configuration:")
|
||||
if tlsCert != "" {
|
||||
certStatus := checkFileExists(tlsCert)
|
||||
fmt.Printf(" Certificate: %s %s\n", colorValue(tlsCert), certStatus)
|
||||
} else {
|
||||
fmt.Printf(" Certificate: %s\n", colorError("(not set)"))
|
||||
}
|
||||
if tlsKey != "" {
|
||||
keyStatus := checkFileExists(tlsKey)
|
||||
fmt.Printf(" Private Key: %s %s\n", colorValue(tlsKey), keyStatus)
|
||||
} else {
|
||||
fmt.Printf(" Private Key: %s\n", colorError("(not set)"))
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
// TCP Tunnel Ports
|
||||
fmt.Println("🌐 TCP Tunnel Port Range:")
|
||||
fmt.Printf(" Min Port: %s\n", colorValue(fmt.Sprintf("%d", tcpPortMin)))
|
||||
fmt.Printf(" Max Port: %s\n", colorValue(fmt.Sprintf("%d", tcpPortMax)))
|
||||
fmt.Printf(" Available Ports: %s\n", colorValue(fmt.Sprintf("%d", tcpPortMax-tcpPortMin+1)))
|
||||
fmt.Println()
|
||||
|
||||
// Performance
|
||||
if pprofPort > 0 {
|
||||
fmt.Println("⚡ Performance:")
|
||||
fmt.Printf(" Pprof Port: %s\n", colorValue(fmt.Sprintf("%d", pprofPort)))
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
// Configuration sources
|
||||
fmt.Println("📋 Configuration Sources:")
|
||||
fmt.Println(" Environment variables (DRIP_*)")
|
||||
fmt.Println(" Command-line flags")
|
||||
fmt.Println(" Config file: /etc/drip/server.env")
|
||||
fmt.Println()
|
||||
|
||||
// Endpoints
|
||||
fmt.Println("🔗 Server Endpoints:")
|
||||
fmt.Printf(" Main: https://%s:%d\n", domain, publicPort)
|
||||
fmt.Printf(" Health: https://%s:%d/health\n", domain, publicPort)
|
||||
fmt.Printf(" Stats: https://%s:%d/stats\n", domain, publicPort)
|
||||
fmt.Printf(" Metrics: https://%s:%d/metrics\n", domain, publicPort)
|
||||
fmt.Println()
|
||||
|
||||
if !serverConfigFull && (token != "" || metricsToken != "") {
|
||||
fmt.Println("💡 Tip: Use --full flag to show complete tokens")
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func maskToken(token string, showFull bool) string {
|
||||
if token == "" {
|
||||
return ""
|
||||
}
|
||||
if showFull {
|
||||
return token
|
||||
}
|
||||
|
||||
tokenLen := len(token)
|
||||
if tokenLen <= 8 {
|
||||
return "***"
|
||||
}
|
||||
// Show first 4 and last 4 characters
|
||||
return token[:4] + "***" + token[tokenLen-4:]
|
||||
}
|
||||
|
||||
func checkFileExists(path string) string {
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
return colorSuccess("✓")
|
||||
}
|
||||
return colorError("✗ (not found)")
|
||||
}
|
||||
|
||||
func colorValue(s string) string {
|
||||
return fmt.Sprintf("\033[36m%s\033[0m", s) // Cyan
|
||||
}
|
||||
|
||||
func colorSuccess(s string) string {
|
||||
return fmt.Sprintf("\033[32m%s\033[0m", s) // Green
|
||||
}
|
||||
|
||||
func colorWarning(s string) string {
|
||||
return fmt.Sprintf("\033[33m%s\033[0m", s) // Yellow
|
||||
}
|
||||
|
||||
func colorError(s string) string {
|
||||
return fmt.Sprintf("\033[31m%s\033[0m", s) // Red
|
||||
}
|
||||
106
internal/server/metrics/metrics.go
Normal file
106
internal/server/metrics/metrics.go
Normal file
@@ -0,0 +1,106 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
)
|
||||
|
||||
var (
|
||||
// Tunnel metrics
|
||||
TunnelCount = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "drip_tunnel_count",
|
||||
Help: "Current number of active tunnels",
|
||||
})
|
||||
|
||||
TunnelRegistrations = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "drip_tunnel_registrations_total",
|
||||
Help: "Total number of tunnel registrations",
|
||||
})
|
||||
|
||||
TunnelRegistrationFailures = promauto.NewCounterVec(prometheus.CounterOpts{
|
||||
Name: "drip_tunnel_registration_failures_total",
|
||||
Help: "Total number of failed tunnel registrations",
|
||||
}, []string{"reason"})
|
||||
|
||||
TunnelsByIP = promauto.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Name: "drip_tunnels_by_ip",
|
||||
Help: "Number of tunnels per client IP",
|
||||
}, []string{"ip"})
|
||||
|
||||
// Connection metrics
|
||||
ActiveConnections = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "drip_active_connections",
|
||||
Help: "Current number of active TCP connections",
|
||||
})
|
||||
|
||||
TotalConnections = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "drip_connections_total",
|
||||
Help: "Total number of connections handled",
|
||||
})
|
||||
|
||||
// Traffic metrics
|
||||
BytesReceived = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "drip_bytes_received_total",
|
||||
Help: "Total bytes received",
|
||||
})
|
||||
|
||||
BytesSent = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "drip_bytes_sent_total",
|
||||
Help: "Total bytes sent",
|
||||
})
|
||||
|
||||
RequestsTotal = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "drip_requests_total",
|
||||
Help: "Total number of HTTP requests handled",
|
||||
})
|
||||
|
||||
// Per-tunnel metrics
|
||||
TunnelBytesReceived = promauto.NewCounterVec(prometheus.CounterOpts{
|
||||
Name: "drip_tunnel_bytes_received_total",
|
||||
Help: "Total bytes received per tunnel",
|
||||
}, []string{"tunnel_id", "subdomain", "type"})
|
||||
|
||||
TunnelBytesSent = promauto.NewCounterVec(prometheus.CounterOpts{
|
||||
Name: "drip_tunnel_bytes_sent_total",
|
||||
Help: "Total bytes sent per tunnel",
|
||||
}, []string{"tunnel_id", "subdomain", "type"})
|
||||
|
||||
TunnelActiveConnections = promauto.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Name: "drip_tunnel_active_connections",
|
||||
Help: "Current number of active connections per tunnel",
|
||||
}, []string{"tunnel_id", "subdomain", "type"})
|
||||
|
||||
// Rate limiting metrics
|
||||
RateLimitRejections = promauto.NewCounterVec(prometheus.CounterOpts{
|
||||
Name: "drip_rate_limit_rejections_total",
|
||||
Help: "Total number of rate limit rejections",
|
||||
}, []string{"type", "ip"})
|
||||
|
||||
// System metrics
|
||||
PanicTotal = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "drip_panic_total",
|
||||
Help: "Total number of panics recovered",
|
||||
})
|
||||
|
||||
WorkerPoolSize = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "drip_worker_pool_size",
|
||||
Help: "Current worker pool size",
|
||||
})
|
||||
|
||||
WorkerPoolActiveWorkers = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "drip_worker_pool_active_workers",
|
||||
Help: "Current number of active workers",
|
||||
})
|
||||
|
||||
// HTTP proxy metrics
|
||||
HTTPRequestDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{
|
||||
Name: "drip_http_request_duration_seconds",
|
||||
Help: "HTTP request duration in seconds",
|
||||
Buckets: prometheus.DefBuckets,
|
||||
}, []string{"method", "status"})
|
||||
|
||||
HTTPRequestsInFlight = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "drip_http_requests_in_flight",
|
||||
Help: "Current number of HTTP requests being processed",
|
||||
})
|
||||
)
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"drip/internal/shared/pool"
|
||||
"drip/internal/shared/protocol"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
@@ -33,18 +34,20 @@ var bufioReaderPool = sync.Pool{
|
||||
const openStreamTimeout = 3 * time.Second
|
||||
|
||||
type Handler struct {
|
||||
manager *tunnel.Manager
|
||||
logger *zap.Logger
|
||||
domain string
|
||||
authToken string
|
||||
manager *tunnel.Manager
|
||||
logger *zap.Logger
|
||||
domain string
|
||||
authToken string
|
||||
metricsToken string
|
||||
}
|
||||
|
||||
func NewHandler(manager *tunnel.Manager, logger *zap.Logger, domain string, authToken string) *Handler {
|
||||
func NewHandler(manager *tunnel.Manager, logger *zap.Logger, domain string, authToken string, metricsToken string) *Handler {
|
||||
return &Handler{
|
||||
manager: manager,
|
||||
logger: logger,
|
||||
domain: domain,
|
||||
authToken: authToken,
|
||||
manager: manager,
|
||||
logger: logger,
|
||||
domain: domain,
|
||||
authToken: authToken,
|
||||
metricsToken: metricsToken,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +60,10 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
h.serveStats(w, r)
|
||||
return
|
||||
}
|
||||
if r.URL.Path == "/metrics" {
|
||||
h.serveMetrics(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
subdomain := h.extractSubdomain(r.Host)
|
||||
if subdomain == "" {
|
||||
@@ -346,7 +353,7 @@ func (h *Handler) serveHomePage(w http.ResponseWriter, r *http.Request) {
|
||||
<code>drip http 3000</code><br><br>
|
||||
<code>drip https 443</code><br><br>
|
||||
<code>drip tcp 5432</code>
|
||||
<p><a href="/health">Health Check</a> | <a href="/stats">Statistics</a></p>
|
||||
<p><a href="/health">Health Check</a> | <a href="/stats">Statistics</a> | <a href="/metrics">Prometheus Metrics</a></p>
|
||||
</body>
|
||||
</html>`
|
||||
|
||||
@@ -375,7 +382,7 @@ func (h *Handler) serveHealth(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func (h *Handler) serveStats(w http.ResponseWriter, r *http.Request) {
|
||||
if h.authToken != "" {
|
||||
if h.metricsToken != "" {
|
||||
// Only accept token via Authorization header (Bearer token)
|
||||
// URL query parameters are insecure (logged, cached, visible in browser history)
|
||||
var token string
|
||||
@@ -384,9 +391,9 @@ func (h *Handler) serveStats(w http.ResponseWriter, r *http.Request) {
|
||||
token = strings.TrimPrefix(authHeader, "Bearer ")
|
||||
}
|
||||
|
||||
if token != h.authToken {
|
||||
if token != h.metricsToken {
|
||||
w.Header().Set("WWW-Authenticate", `Bearer realm="stats"`)
|
||||
http.Error(w, "Unauthorized: provide token via 'Authorization: Bearer <token>' header", http.StatusUnauthorized)
|
||||
http.Error(w, "Unauthorized: provide metrics token via 'Authorization: Bearer <token>' header", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -426,6 +433,26 @@ func (h *Handler) serveStats(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write(data)
|
||||
}
|
||||
|
||||
func (h *Handler) serveMetrics(w http.ResponseWriter, r *http.Request) {
|
||||
if h.metricsToken != "" {
|
||||
// Only accept token via Authorization header (Bearer token)
|
||||
var token string
|
||||
authHeader := r.Header.Get("Authorization")
|
||||
if strings.HasPrefix(authHeader, "Bearer ") {
|
||||
token = strings.TrimPrefix(authHeader, "Bearer ")
|
||||
}
|
||||
|
||||
if token != h.metricsToken {
|
||||
w.Header().Set("WWW-Authenticate", `Bearer realm="metrics"`)
|
||||
http.Error(w, "Unauthorized: provide metrics token via 'Authorization: Bearer <token>' header", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Serve Prometheus metrics
|
||||
promhttp.Handler().ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
type bufferedReadWriteCloser struct {
|
||||
*bufio.Reader
|
||||
net.Conn
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"drip/internal/server/metrics"
|
||||
"drip/internal/server/tunnel"
|
||||
"drip/internal/shared/pool"
|
||||
"drip/internal/shared/recovery"
|
||||
@@ -56,6 +57,9 @@ func NewListener(address string, tlsConfig *tls.Config, authToken string, manage
|
||||
panicMetrics := recovery.NewPanicMetrics(logger, nil)
|
||||
recoverer := recovery.NewRecoverer(logger, panicMetrics)
|
||||
|
||||
// Initialize worker pool metrics
|
||||
metrics.WorkerPoolSize.Set(float64(workers))
|
||||
|
||||
return &Listener{
|
||||
address: address,
|
||||
tlsConfig: tlsConfig,
|
||||
@@ -237,11 +241,17 @@ func (l *Listener) handleConnection(netConn net.Conn) {
|
||||
l.connections[connID] = conn
|
||||
l.connMu.Unlock()
|
||||
|
||||
// Update connection metrics
|
||||
metrics.TotalConnections.Inc()
|
||||
metrics.ActiveConnections.Inc()
|
||||
|
||||
defer func() {
|
||||
l.connMu.Lock()
|
||||
delete(l.connections, connID)
|
||||
l.connMu.Unlock()
|
||||
|
||||
metrics.ActiveConnections.Dec()
|
||||
|
||||
if !conn.IsHandedOff() {
|
||||
netConn.Close()
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"drip/internal/server/metrics"
|
||||
"drip/internal/shared/protocol"
|
||||
"github.com/gorilla/websocket"
|
||||
"go.uber.org/zap"
|
||||
@@ -144,6 +145,8 @@ func (c *Connection) AddBytesIn(n int64) {
|
||||
return
|
||||
}
|
||||
c.bytesIn.Add(n)
|
||||
metrics.BytesReceived.Add(float64(n))
|
||||
metrics.TunnelBytesReceived.WithLabelValues(c.Subdomain, c.Subdomain, c.GetTunnelType().String()).Add(float64(n))
|
||||
}
|
||||
|
||||
func (c *Connection) AddBytesOut(n int64) {
|
||||
@@ -151,6 +154,8 @@ func (c *Connection) AddBytesOut(n int64) {
|
||||
return
|
||||
}
|
||||
c.bytesOut.Add(n)
|
||||
metrics.BytesSent.Add(float64(n))
|
||||
metrics.TunnelBytesSent.WithLabelValues(c.Subdomain, c.Subdomain, c.GetTunnelType().String()).Add(float64(n))
|
||||
}
|
||||
|
||||
func (c *Connection) GetBytesIn() int64 {
|
||||
@@ -163,12 +168,14 @@ func (c *Connection) GetBytesOut() int64 {
|
||||
|
||||
func (c *Connection) IncActiveConnections() {
|
||||
c.activeConnections.Add(1)
|
||||
metrics.TunnelActiveConnections.WithLabelValues(c.Subdomain, c.Subdomain, c.GetTunnelType().String()).Inc()
|
||||
}
|
||||
|
||||
func (c *Connection) DecActiveConnections() {
|
||||
if v := c.activeConnections.Add(-1); v < 0 {
|
||||
c.activeConnections.Store(0)
|
||||
}
|
||||
metrics.TunnelActiveConnections.WithLabelValues(c.Subdomain, c.Subdomain, c.GetTunnelType().String()).Dec()
|
||||
}
|
||||
|
||||
func (c *Connection) GetActiveConnections() int64 {
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"drip/internal/server/metrics"
|
||||
"drip/internal/shared/utils"
|
||||
"github.com/gorilla/websocket"
|
||||
"go.uber.org/zap"
|
||||
@@ -176,6 +177,7 @@ func (m *Manager) RegisterWithIP(conn *websocket.Conn, customSubdomain string, r
|
||||
zap.Int64("current", current),
|
||||
zap.Int("max", m.maxTunnels),
|
||||
)
|
||||
metrics.TunnelRegistrationFailures.WithLabelValues("max_tunnels").Inc()
|
||||
return "", ErrTooManyTunnels
|
||||
}
|
||||
if m.tunnelCount.CompareAndSwap(current, current+1) {
|
||||
@@ -199,6 +201,8 @@ func (m *Manager) RegisterWithIP(conn *websocket.Conn, customSubdomain string, r
|
||||
zap.String("ip", remoteIP),
|
||||
zap.Int("limit", m.rateLimit),
|
||||
)
|
||||
metrics.RateLimitRejections.WithLabelValues("registration", remoteIP).Inc()
|
||||
metrics.TunnelRegistrationFailures.WithLabelValues("rate_limit").Inc()
|
||||
return "", ErrRateLimitExceeded
|
||||
}
|
||||
|
||||
@@ -211,11 +215,13 @@ func (m *Manager) RegisterWithIP(conn *websocket.Conn, customSubdomain string, r
|
||||
zap.Int("current", currentPerIP),
|
||||
zap.Int("max", m.maxTunnelsPerIP),
|
||||
)
|
||||
metrics.TunnelRegistrationFailures.WithLabelValues("max_per_ip").Inc()
|
||||
return "", ErrTooManyPerIP
|
||||
}
|
||||
|
||||
// Reserve per-IP slot while still holding the lock
|
||||
m.tunnelsByIP[remoteIP]++
|
||||
metrics.TunnelsByIP.WithLabelValues(remoteIP).Set(float64(m.tunnelsByIP[remoteIP]))
|
||||
m.ipMu.Unlock()
|
||||
}
|
||||
|
||||
@@ -293,6 +299,10 @@ func (m *Manager) RegisterWithIP(conn *websocket.Conn, customSubdomain string, r
|
||||
zap.Int64("total_tunnels", m.tunnelCount.Load()),
|
||||
)
|
||||
|
||||
// Update Prometheus metrics
|
||||
metrics.TunnelRegistrations.Inc()
|
||||
metrics.TunnelCount.Set(float64(m.tunnelCount.Load()))
|
||||
|
||||
return subdomain, nil
|
||||
}
|
||||
|
||||
@@ -321,6 +331,9 @@ func (m *Manager) Unregister(subdomain string) {
|
||||
m.tunnelsByIP[remoteIP]--
|
||||
if m.tunnelsByIP[remoteIP] == 0 {
|
||||
delete(m.tunnelsByIP, remoteIP)
|
||||
metrics.TunnelsByIP.DeleteLabelValues(remoteIP)
|
||||
} else {
|
||||
metrics.TunnelsByIP.WithLabelValues(remoteIP).Set(float64(m.tunnelsByIP[remoteIP]))
|
||||
}
|
||||
}
|
||||
m.ipMu.Unlock()
|
||||
@@ -330,6 +343,9 @@ func (m *Manager) Unregister(subdomain string) {
|
||||
zap.String("subdomain", subdomain),
|
||||
zap.Int64("total_tunnels", m.tunnelCount.Load()),
|
||||
)
|
||||
|
||||
// Update Prometheus metrics
|
||||
metrics.TunnelCount.Set(float64(m.tunnelCount.Load()))
|
||||
}
|
||||
|
||||
// Get retrieves a tunnel connection by subdomain
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"drip/internal/server/metrics"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
@@ -39,6 +40,7 @@ func NewPanicMetrics(logger *zap.Logger, alerter Alerter) *PanicMetrics {
|
||||
|
||||
func (pm *PanicMetrics) RecordPanic(location string, panicValue interface{}) {
|
||||
atomic.AddUint64(&pm.totalPanics, 1)
|
||||
metrics.PanicTotal.Inc()
|
||||
|
||||
pm.mu.Lock()
|
||||
|
||||
|
||||
@@ -59,8 +59,9 @@ MSG_EN=(
|
||||
["enter_domain"]="Enter your domain (e.g., tunnel.example.com)"
|
||||
["domain_required"]="Domain is required"
|
||||
["enter_port"]="Enter server port"
|
||||
["enter_token"]="Enter authentication token (leave empty to generate)"
|
||||
["token_generated"]="Token generated"
|
||||
["enter_token"]="Enter authentication token (leave empty to auto-generate)"
|
||||
["token_generated"]="Authentication token generated"
|
||||
["metrics_token_generated"]="Metrics token generated"
|
||||
["enter_cert_path"]="Enter TLS certificate path (public key)"
|
||||
["enter_key_path"]="Enter TLS private key path"
|
||||
["cert_not_found"]="Certificate file not found"
|
||||
@@ -94,7 +95,8 @@ MSG_EN=(
|
||||
["install_complete"]="Installation completed!"
|
||||
["client_info"]="Client connection info"
|
||||
["server_addr"]="Server"
|
||||
["token_label"]="Token"
|
||||
["token_label"]="Auth Token"
|
||||
["metrics_token_label"]="Metrics Token"
|
||||
["service_commands"]="Service management commands"
|
||||
["cmd_start"]="Start service"
|
||||
["cmd_stop"]="Stop service"
|
||||
@@ -149,7 +151,8 @@ MSG_ZH=(
|
||||
["domain_required"]="域名是必填项"
|
||||
["enter_port"]="输入服务器端口"
|
||||
["enter_token"]="输入认证令牌(留空自动生成)"
|
||||
["token_generated"]="令牌已生成"
|
||||
["token_generated"]="认证令牌已生成"
|
||||
["metrics_token_generated"]="监控令牌已生成"
|
||||
["enter_cert_path"]="输入 TLS 证书路径(公钥)"
|
||||
["enter_key_path"]="输入 TLS 私钥路径"
|
||||
["cert_not_found"]="证书文件未找到"
|
||||
@@ -183,7 +186,8 @@ MSG_ZH=(
|
||||
["install_complete"]="安装完成!"
|
||||
["client_info"]="客户端连接信息"
|
||||
["server_addr"]="服务器"
|
||||
["token_label"]="令牌"
|
||||
["token_label"]="认证令牌"
|
||||
["metrics_token_label"]="监控令牌"
|
||||
["service_commands"]="服务管理命令"
|
||||
["cmd_start"]="启动服务"
|
||||
["cmd_stop"]="停止服务"
|
||||
@@ -546,7 +550,7 @@ install_binary() {
|
||||
# Configuration
|
||||
# ============================================================================
|
||||
generate_token() {
|
||||
# Generate a random 32-character token
|
||||
# Generate a random 32-character token (16 bytes = 32 hex chars)
|
||||
if command -v openssl &> /dev/null; then
|
||||
openssl rand -hex 16
|
||||
else
|
||||
@@ -583,7 +587,7 @@ configure_server() {
|
||||
read -p "$(msg enter_tcp_max) [$DEFAULT_TCP_PORT_MAX]: " TCP_PORT_MAX < /dev/tty
|
||||
TCP_PORT_MAX="${TCP_PORT_MAX:-$DEFAULT_TCP_PORT_MAX}"
|
||||
|
||||
# Token
|
||||
# Authentication token (user can provide or auto-generate)
|
||||
echo ""
|
||||
read -p "$(msg enter_token): " TOKEN < /dev/tty
|
||||
if [[ -z "$TOKEN" ]]; then
|
||||
@@ -591,6 +595,10 @@ configure_server() {
|
||||
print_success "$(msg token_generated): $TOKEN"
|
||||
fi
|
||||
|
||||
# Metrics token (always auto-generated)
|
||||
METRICS_TOKEN=$(generate_token)
|
||||
print_success "$(msg metrics_token_generated): $METRICS_TOKEN"
|
||||
|
||||
# TLS certificate selection
|
||||
print_panel "$(msg cert_option_title)" \
|
||||
"${GREEN}1)${NC} $(msg cert_option_certbot)" \
|
||||
@@ -946,6 +954,7 @@ DRIP_PORT=${PORT}
|
||||
DRIP_PUBLIC_PORT=${PUBLIC_PORT}
|
||||
DRIP_DOMAIN=${DOMAIN}
|
||||
DRIP_TOKEN=${TOKEN}
|
||||
DRIP_METRICS_TOKEN=${METRICS_TOKEN}
|
||||
|
||||
# TLS certificate paths
|
||||
DRIP_TLS_CERT=${CERT_PATH}
|
||||
@@ -995,8 +1004,9 @@ show_completion() {
|
||||
print_panel "$(msg install_complete)"
|
||||
|
||||
echo -e "${CYAN}$(msg client_info):${NC}"
|
||||
echo -e " ${BOLD}$(msg server_addr):${NC} ${DOMAIN}:${PORT}"
|
||||
echo -e " ${BOLD}$(msg token_label):${NC} ${TOKEN}"
|
||||
echo -e " ${BOLD}$(msg server_addr):${NC} ${DOMAIN}:${PORT}"
|
||||
echo -e " ${BOLD}$(msg token_label):${NC} ${TOKEN}"
|
||||
echo -e " ${BOLD}$(msg metrics_token_label):${NC} ${METRICS_TOKEN}"
|
||||
echo ""
|
||||
|
||||
echo -e "${CYAN}$(msg service_commands):${NC}"
|
||||
|
||||
Reference in New Issue
Block a user