Smail/backend/hooks/plugin_egress_linux.go
smail 857491c6a6
Some checks are pending
Security Checks / security (push) Waiting to run
修复Telegram回调与沙箱推送未使用本机代理
2026-10-03 20:42:13 +08:00

104 lines
3.2 KiB
Go

//go:build linux
package hooks
import (
"context"
"encoding/json"
"errors"
"io"
"log"
"net"
"net/http"
"os"
"path/filepath"
"strings"
"time"
"git.pcux.cn/xuans/Smail/utils/credential"
)
func startPluginEgress(spec *pluginLaunchSpec, uid, gid int) error {
logicalID := ""
for _, item := range spec.procAttr.Env {
if strings.HasPrefix(item, "SMAIL_PLUGIN_ID=") {
logicalID = strings.TrimPrefix(item, "SMAIL_PLUGIN_ID=")
}
}
if logicalID == "" {
return errors.New("plugin egress identity is unavailable")
}
token, err := newPluginAuthToken()
if err != nil {
return errors.New("plugin egress authorization generation failed")
}
socketPath := filepath.Join(spec.runtimeDir, filepath.Base(credential.PluginEgressSocket))
listener, err := net.ListenUnix("unix", &net.UnixAddr{Name: socketPath, Net: "unix"})
if err != nil {
return errors.New("plugin egress listener creation failed")
}
if err := os.Chmod(socketPath, 0600); err != nil {
_ = listener.Close()
return errors.New("plugin egress socket permissions failed")
}
if err := os.Chown(socketPath, uid, gid); err != nil {
_ = listener.Close()
return errors.New("plugin egress socket identity failed")
}
ctx, cancel := context.WithCancel(context.Background())
var telegramProxy func() (string, error)
if logicalID == "telegram_push" {
telegramProxy = func() (string, error) {
instance, _, ok := pluginInstanceForStartup(logicalID)
if !ok || validatePluginRuntimeBinding(logicalID, instance) != nil {
return "", errors.New("Telegram plugin configuration is unavailable")
}
var settings struct {
Proxy string `json:"proxy"`
}
if err := json.Unmarshal(instance.Settings, &settings); err != nil {
return "", errors.New("Telegram plugin configuration is invalid")
}
return credential.NormalizeTelegramProxyURL(settings.Proxy)
}
}
server := &http.Server{
Handler: credential.NewPluginEgressHandlerWithTelegramProxy(token, func() []credential.NetworkTarget {
instance, _, ok := pluginInstanceForStartup(logicalID)
if !ok || validatePluginRuntimeBinding(logicalID, instance) != nil {
return nil
}
canNetwork := false
for _, capability := range instance.Permissions.Capabilities {
canNetwork = canNetwork || capability == "network"
}
if !canNetwork {
return nil
}
targets := make([]credential.NetworkTarget, 0, len(instance.Permissions.Network))
for _, target := range instance.Permissions.Network {
if target.Scheme == "https" {
targets = append(targets, credential.NetworkTarget{Scheme: target.Scheme, Host: target.Host, Port: target.Port})
}
}
return targets
}, telegramProxy),
BaseContext: func(net.Listener) context.Context { return ctx },
ReadHeaderTimeout: 5 * time.Second,
ReadTimeout: 30 * time.Second,
WriteTimeout: 65 * time.Second,
IdleTimeout: 5 * time.Second,
MaxHeaderBytes: 32 << 10,
ErrorLog: log.New(io.Discard, "", 0),
}
server.SetKeepAlivesEnabled(false)
spec.egressStop = func() {
cancel()
_ = listener.Close()
_ = server.Close()
}
spec.procAttr.Env = appendPluginEnv(spec.procAttr.Env, "SMAIL_PLUGIN_EGRESS_TOKEN", token)
go func() { _ = server.Serve(listener) }()
return nil
}