add webhook notification events

This commit is contained in:
ugwueze-dev 2023-08-19 14:05:33 +01:00
parent 466d69eae0
commit b886843a5d
10 changed files with 145 additions and 2 deletions

BIN
cmd/frpc/frpc Executable file

Binary file not shown.

9
cmd/frpc/frpc.ini Normal file
View File

@ -0,0 +1,9 @@
[common]
server_addr = 127.0.0.1
server_port = 7000
[proxy_name]
type = tcp
local_ip = 192.168.178.192
local_port = 22
plugin = http_proxy

BIN
cmd/frps/frps Executable file

Binary file not shown.

3
cmd/frps/frps.ini Normal file
View File

@ -0,0 +1,3 @@
[common]
bind_port = 7000
webhook_url = https://webhook.site/d8593a16-2efd-4973-88fc-150e7606f957

View File

@ -62,6 +62,7 @@ var (
dashboardTLSMode bool
dashboardTLSCertFile string
dashboardTLSKeyFile string
webhookURL string
)
func init() {
@ -93,6 +94,7 @@ func init() {
rootCmd.PersistentFlags().BoolVarP(&dashboardTLSMode, "dashboard_tls_mode", "", false, "dashboard tls mode")
rootCmd.PersistentFlags().StringVarP(&dashboardTLSCertFile, "dashboard_tls_cert_file", "", "", "dashboard tls cert file")
rootCmd.PersistentFlags().StringVarP(&dashboardTLSKeyFile, "dashboard_tls_key_file", "", "", "dashboard tls key file")
rootCmd.PersistentFlags().StringVarP(&webhookURL, "webhook_url", "w", "", "webhook")
}
var rootCmd = &cobra.Command{
@ -176,6 +178,7 @@ func parseServerCommonCfgFromCmd() (cfg config.ServerCommonConf, err error) {
cfg.LogMaxDays = logMaxDays
cfg.SubDomainHost = subDomainHost
cfg.TLSOnly = tlsOnly
cfg.WebhookURL = webhookURL
// Only token authentication is supported in cmd mode
cfg.ServerConfig = auth.GetDefaultServerConf()

2
go.mod
View File

@ -54,6 +54,7 @@ require (
github.com/kr/text v0.2.0 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/parnurzeal/gorequest v0.2.16 // indirect
github.com/pion/dtls/v2 v2.2.7 // indirect
github.com/pion/logging v0.2.2 // indirect
github.com/pion/transport/v2 v2.2.1 // indirect
@ -78,6 +79,7 @@ require (
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect
moul.io/http2curl v1.0.0 // indirect
)
// TODO(fatedier): Temporary use the modified version, update to the official version after merging into the official repository.

12
go.sum
View File

@ -36,6 +36,7 @@ github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo=
github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
@ -86,6 +87,9 @@ github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/klauspost/cpuid/v2 v2.0.6 h1:dQ5ueTiftKxp0gyjKSx5+8BtPWkyQbd95m8Gys/RarI=
github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/reedsolomon v1.9.15 h1:g2erWKD2M6rgnPf89fCji6jNlhMKMdXcuNHMW1SYCIo=
@ -99,10 +103,15 @@ github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/Qd
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU=
github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM=
github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc=
github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ=
github.com/parnurzeal/gorequest v0.2.16 h1:T/5x+/4BT+nj+3eSknXmCTnEVGSzFzPGdpqmUVVZXHQ=
github.com/parnurzeal/gorequest v0.2.16/go.mod h1:3Kh2QUMJoqw3icWAecsyzkpY7UzRfDhbRdTjtNwNiUE=
github.com/pion/dtls/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8=
github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s=
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
@ -275,6 +284,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
@ -286,3 +296,5 @@ k8s.io/client-go v0.27.4 h1:vj2YTtSJ6J4KxaC88P4pMPEQECWMY8gqPqsTgUKzvjk=
k8s.io/client-go v0.27.4/go.mod h1:ragcly7lUlN0SRPk5/ZkGnDjPknzb37TICq07WhI6Xc=
k8s.io/utils v0.0.0-20230209194617-a36077c30491 h1:r0BAOLElQnnFhE/ApUsg3iHdVYYPBjNSSOMowRZxxsY=
k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
moul.io/http2curl v1.0.0 h1:6XwpyZOYsgZJrU8exnG87ncVkU1FVCcTRpwzOkTDUi8=
moul.io/http2curl v1.0.0/go.mod h1:f6cULg+e4Md/oW1cYmwW4IWQOVl2lGbmCNGOHvzX2kE=

View File

@ -194,6 +194,8 @@ type ServerCommonConf struct {
PprofEnable bool `ini:"pprof_enable" json:"pprof_enable"`
// NatHoleAnalysisDataReserveHours specifies the hours to reserve nat hole analysis data.
NatHoleAnalysisDataReserveHours int64 `ini:"nat_hole_analysis_data_reserve_hours" json:"nat_hole_analysis_data_reserve_hours"`
// WebhookURL is the endpoint we send webhook events to
WebhookURL string `ini:"webhook_url" json:"webhook_url"`
}
// GetDefaultServerConf returns a server configuration with reasonable
@ -224,6 +226,7 @@ func GetDefaultServerConf() ServerCommonConf {
HTTPPlugins: make(map[string]plugin.HTTPPluginOptions),
UDPPacketSize: 1500,
NatHoleAnalysisDataReserveHours: 7 * 24,
WebhookURL: "",
}
}

View File

@ -153,8 +153,9 @@ type Control struct {
// Server configuration information
serverCfg config.ServerCommonConf
xl *xlog.Logger
ctx context.Context
xl *xlog.Logger
ctx context.Context
webhook *Webhook
}
func NewControl(
@ -193,6 +194,7 @@ func NewControl(
serverCfg: serverCfg,
xl: xlog.FromContextSafe(ctx),
ctx: ctx,
webhook: NewWebhook(serverCfg.WebhookURL),
}
ctl.msgTransporter = transport.NewMessageTransporter(ctl.sendCh)
return ctl
@ -401,6 +403,7 @@ func (ctl *Control) stoper() {
pxy.Close()
ctl.pxyManager.Del(pxy.GetName())
metrics.Server.CloseProxy(pxy.GetName(), pxy.GetConf().GetBaseConfig().ProxyType)
ctl.webhook.OnDisconnect(ctl.loginMsg.RunID)
notifyContent := &plugin.CloseProxyContent{
User: plugin.UserInfo{
@ -496,6 +499,7 @@ func (ctl *Control) manager() {
resp.RemoteAddr = remoteAddr
xl.Info("new proxy [%s] type [%s] success", m.ProxyName, m.ProxyType)
metrics.Server.NewProxy(m.ProxyName, m.ProxyType)
ctl.webhook.OnConnect(ctl.conn, m, ctl.loginMsg)
}
ctl.sendCh <- resp
case *msg.NatHoleVisitor:
@ -624,6 +628,8 @@ func (ctl *Control) CloseProxy(closeMsg *msg.CloseProxy) (err error) {
return
}
ctl.webhook.OnDisconnect(ctl.loginMsg.RunID)
if ctl.serverCfg.MaxPortsPerClient > 0 {
ctl.portsUsedNum -= pxy.GetUsedPortsNum()
}

View File

@ -0,0 +1,105 @@
package server
import (
"encoding/json"
"fmt"
"net"
"github.com/fatedier/frp/pkg/msg"
"github.com/parnurzeal/gorequest"
)
type Data struct {
// ClientIP is the IP address of the connected proxy
ClientIP string `json:"client_ip"`
// RemotePort is the Port number of the proxy
RemotePort int `json:"remote_port"`
// LocalAddr is the IP address of this frps instance
LocalAddr string `json:"local_addr"`
// ProxyName is the name of the connected proxy
ProxyName string `json:"proxy_name"`
// ProxyType is the type of the connected proxy (tcp or ssh)
ProxyType string `json:"proxy_type"`
// Headers is a map of the connected proxy headers
Headers map[string]string `json:"headers"`
// Os is the operating system the frpc is running on
Os string `json:"os"`
// Arch is the system architecture of the frpc
Arch string `json:"arch"`
// Version is the frpc proxy version
Version string `json:"version"`
// Hostname is the name of the frpc machine
Hostname string `json:"hostname"`
}
type Payload struct {
// Event is the name of the webhook event (offline or online)
Event string `json:"event"`
// UniqueID is the ID of the proxy
UniqueID string `json:"unique_id"`
// Data is a struct containing the information of the connected proxy
Data *Data `json:"data"`
}
type Webhook struct {
endpoint string
client *gorequest.SuperAgent
connections map[string]Payload
}
// NewWebhook returns an instance of the webhook object for each connected proxy
func NewWebhook(endpoint string) *Webhook {
return &Webhook{
endpoint: endpoint,
client: gorequest.New(),
connections: map[string]Payload{},
}
}
// OnConnect is called when a proxy gets connected
func (w *Webhook) OnConnect(conn net.Conn, m *msg.NewProxy, l *msg.Login) string {
uniqueID := l.RunID
d := &Data{
ClientIP: conn.RemoteAddr().String(),
LocalAddr: conn.LocalAddr().String(),
ProxyName: m.ProxyName,
ProxyType: m.ProxyType,
RemotePort: m.RemotePort,
Headers: m.Headers,
Os: l.Os,
Arch: l.Arch,
Version: l.Version,
Hostname: l.Hostname,
}
p := Payload{
Event: "online",
UniqueID: uniqueID,
Data: d,
}
w.connections[uniqueID] = p
w.send(p)
return uniqueID
}
// OnDisconnect is called when a proxy disconnects
func (w *Webhook) OnDisconnect(id string) {
if p, ok := w.connections[id]; ok {
p.Event = "offline"
w.send(p)
}
}
func (w *Webhook) send(p Payload) {
b, err := json.Marshal(&p)
if err != nil {
fmt.Printf("error marshalling payload: %s", err.Error())
return
}
w.client.Post(w.endpoint).
Send(string(b)).
End()
}