refresh back
This commit is contained in:
116
back/cmd/knock-local/main.go
Normal file
116
back/cmd/knock-local/main.go
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"port-knocker/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Request struct {
|
||||||
|
Targets []string `json:"targets"`
|
||||||
|
Delay string `json:"delay"`
|
||||||
|
Verbose bool `json:"verbose"`
|
||||||
|
Gateway string `json:"gateway"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Response struct {
|
||||||
|
Success bool `json:"success"`
|
||||||
|
Error string `json:"error,omitempty"`
|
||||||
|
Message string `json:"message,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseTargets(in []string, delayStr string, gateway string) (*internal.Config, error) {
|
||||||
|
cfg := &internal.Config{Targets: []internal.Target{}}
|
||||||
|
d := time.Second
|
||||||
|
if strings.TrimSpace(delayStr) != "" {
|
||||||
|
dur, err := time.ParseDuration(delayStr)
|
||||||
|
if err == nil {
|
||||||
|
d = dur
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, t := range in {
|
||||||
|
t = strings.TrimSpace(t)
|
||||||
|
if t == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
parts := strings.Split(t, ":")
|
||||||
|
if len(parts) < 3 {
|
||||||
|
return nil, fmt.Errorf("invalid target: %s", t)
|
||||||
|
}
|
||||||
|
protocol := strings.ToLower(parts[0])
|
||||||
|
host := parts[1]
|
||||||
|
portStr := parts[2]
|
||||||
|
gw := ""
|
||||||
|
if len(parts) >= 4 && strings.TrimSpace(parts[3]) != "" {
|
||||||
|
gw = strings.TrimSpace(parts[3])
|
||||||
|
} else if strings.TrimSpace(gateway) != "" {
|
||||||
|
gw = strings.TrimSpace(gateway)
|
||||||
|
}
|
||||||
|
// single port
|
||||||
|
var port int
|
||||||
|
fmt.Sscanf(portStr, "%d", &port)
|
||||||
|
cfg.Targets = append(cfg.Targets, internal.Target{
|
||||||
|
Host: host,
|
||||||
|
Ports: []int{port},
|
||||||
|
Protocol: protocol,
|
||||||
|
Delay: internal.Duration(d),
|
||||||
|
WaitConnection: false,
|
||||||
|
Gateway: gw,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return cfg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readAllStdin() ([]byte, error) {
|
||||||
|
reader := bufio.NewReader(os.Stdin)
|
||||||
|
var b []byte
|
||||||
|
for {
|
||||||
|
chunk, isPrefix, err := reader.ReadLine()
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
b = append(b, chunk...)
|
||||||
|
if !isPrefix {
|
||||||
|
b = append(b, '\n')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Read JSON from stdin
|
||||||
|
data, err := readAllStdin()
|
||||||
|
if err != nil || len(strings.TrimSpace(string(data))) == 0 {
|
||||||
|
// fallback to args? not required; expect stdin
|
||||||
|
}
|
||||||
|
var req Request
|
||||||
|
if err := json.Unmarshal(data, &req); err != nil {
|
||||||
|
_ = json.NewEncoder(os.Stdout).Encode(Response{Success: false, Error: fmt.Sprintf("invalid json: %v", err)})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg, err := parseTargets(req.Targets, req.Delay, req.Gateway)
|
||||||
|
if err != nil {
|
||||||
|
_ = json.NewEncoder(os.Stdout).Encode(Response{Success: false, Error: err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pk := internal.NewPortKnocker()
|
||||||
|
// Всегда без лишних логов на stdout (stdout должен содержать только JSON ответ)
|
||||||
|
verbose := false
|
||||||
|
if err := pk.ExecuteWithConfig(cfg, verbose, false); err != nil {
|
||||||
|
_ = json.NewEncoder(os.Stdout).Encode(Response{Success: false, Error: err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = json.NewEncoder(os.Stdout).Encode(Response{Success: true, Message: "ok"})
|
||||||
|
}
|
@@ -24,10 +24,13 @@ func setupKnockRoutes(api *gin.RouterGroup) {
|
|||||||
ConfigYaml string `json:"config_yaml"`
|
ConfigYaml string `json:"config_yaml"`
|
||||||
}
|
}
|
||||||
if err := c.BindJSON(&req); err != nil {
|
if err := c.BindJSON(&req); err != nil {
|
||||||
|
// fmt.Printf("bad json: %v\n", err)
|
||||||
c.JSON(400, gin.H{"error": fmt.Sprintf("bad json: %v", err)})
|
c.JSON(400, gin.H{"error": fmt.Sprintf("bad json: %v", err)})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fmt.Printf("req: %+v\n", req)
|
||||||
|
|
||||||
knocker := internal.NewPortKnocker()
|
knocker := internal.NewPortKnocker()
|
||||||
|
|
||||||
// Определяем режим: inline или YAML
|
// Определяем режим: inline или YAML
|
||||||
@@ -39,6 +42,8 @@ func setupKnockRoutes(api *gin.RouterGroup) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.Printf("config: %+v\n", config)
|
||||||
|
|
||||||
// Применяем дополнительные параметры из запроса
|
// Применяем дополнительные параметры из запроса
|
||||||
if req.Gateway != "" {
|
if req.Gateway != "" {
|
||||||
for i := range config.Targets {
|
for i := range config.Targets {
|
||||||
@@ -47,7 +52,8 @@ func setupKnockRoutes(api *gin.RouterGroup) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := knocker.ExecuteWithConfig(config, req.Verbose, req.WaitConnection); err != nil {
|
if err := knocker.ExecuteWithConfig(config, req.Verbose, req.WaitConnection); err != nil {
|
||||||
c.JSON(400, gin.H{"error": err.Error()})
|
fmt.Printf("error: %v\n", err)
|
||||||
|
c.JSON(400, gin.H{"status": "error","error": err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.JSON(200, gin.H{"status": "ok"})
|
c.JSON(200, gin.H{"status": "ok"})
|
||||||
@@ -101,7 +107,6 @@ func parseInlineTargetsWithWait(targets, delay string, waitConnection bool, gate
|
|||||||
host := strings.TrimSpace(parts[1])
|
host := strings.TrimSpace(parts[1])
|
||||||
portStr := strings.TrimSpace(parts[2])
|
portStr := strings.TrimSpace(parts[2])
|
||||||
|
|
||||||
|
|
||||||
if len(parts) == 4 {
|
if len(parts) == 4 {
|
||||||
gateway = strings.TrimSpace(parts[3])
|
gateway = strings.TrimSpace(parts[3])
|
||||||
}
|
}
|
||||||
|
@@ -100,13 +100,17 @@ func parseInlineTargets(targetsStr, delayStr string) (*internal.Config, error) {
|
|||||||
|
|
||||||
// Разбираем формат [proto]:[host]:[port]
|
// Разбираем формат [proto]:[host]:[port]
|
||||||
parts := strings.Split(targetStr, ":")
|
parts := strings.Split(targetStr, ":")
|
||||||
if len(parts) != 3 {
|
if len(parts) != 3 && len(parts) != 4 {
|
||||||
return nil, fmt.Errorf("неверный формат цели '%s', ожидается [proto]:[host]:[port]", targetStr)
|
return nil, fmt.Errorf("неверный формат цели '%s', ожидается [proto]:[host]:[port] или [proto]:[host]:[port]:[gateway]", targetStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
protocol := strings.TrimSpace(parts[0])
|
protocol := strings.TrimSpace(parts[0])
|
||||||
host := strings.TrimSpace(parts[1])
|
host := strings.TrimSpace(parts[1])
|
||||||
portStr := strings.TrimSpace(parts[2])
|
portStr := strings.TrimSpace(parts[2])
|
||||||
|
gateway := ""
|
||||||
|
if len(parts) == 4 {
|
||||||
|
gateway = strings.TrimSpace(parts[3])
|
||||||
|
}
|
||||||
|
|
||||||
// Проверяем протокол
|
// Проверяем протокол
|
||||||
if protocol != "tcp" && protocol != "udp" {
|
if protocol != "tcp" && protocol != "udp" {
|
||||||
@@ -130,7 +134,7 @@ func parseInlineTargets(targetsStr, delayStr string) (*internal.Config, error) {
|
|||||||
Protocol: protocol,
|
Protocol: protocol,
|
||||||
Delay: internal.Duration(delay),
|
Delay: internal.Duration(delay),
|
||||||
WaitConnection: false,
|
WaitConnection: false,
|
||||||
Gateway: "",
|
Gateway: gateway,
|
||||||
}
|
}
|
||||||
|
|
||||||
config.Targets = append(config.Targets, target)
|
config.Targets = append(config.Targets, target)
|
||||||
|
@@ -43,11 +43,16 @@ func runServe(cmd *cobra.Command, args []string) error {
|
|||||||
port = "8888"
|
port = "8888"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
host := os.Getenv("GO_KNOCKER_SERVE_HOST")
|
||||||
|
if strings.TrimSpace(port) == "" {
|
||||||
|
host = ""
|
||||||
|
}
|
||||||
|
|
||||||
r := gin.Default()
|
r := gin.Default()
|
||||||
|
|
||||||
// CORS: разрешаем для локальной разработки
|
// CORS: разрешаем для локальной разработки
|
||||||
r.Use(cors.New(cors.Config{
|
r.Use(cors.New(cors.Config{
|
||||||
AllowOrigins: []string{"http://localhost:4200", "http://127.0.0.1:8888", "http://localhost:8888"},
|
AllowOrigins: []string{"http://localhost:4200", "http://127.0.0.1:8888", "http://localhost:" + port},
|
||||||
AllowMethods: []string{"GET", "POST", "OPTIONS"},
|
AllowMethods: []string{"GET", "POST", "OPTIONS"},
|
||||||
AllowHeaders: []string{"Authorization", "Content-Type"},
|
AllowHeaders: []string{"Authorization", "Content-Type"},
|
||||||
AllowCredentials: true,
|
AllowCredentials: true,
|
||||||
@@ -65,6 +70,6 @@ func runServe(cmd *cobra.Command, args []string) error {
|
|||||||
setupCryptoRoutes(api, passHash)
|
setupCryptoRoutes(api, passHash)
|
||||||
setupStaticRoutes(r, embeddedFS)
|
setupStaticRoutes(r, embeddedFS)
|
||||||
|
|
||||||
fmt.Printf("Serving on :%s\n", port)
|
fmt.Printf("Serving on %s:%s\n", host, port)
|
||||||
return r.Run(":" + port)
|
return r.Run(host + ":" + port)
|
||||||
}
|
}
|
||||||
|
@@ -6,7 +6,6 @@ require (
|
|||||||
github.com/gin-contrib/cors v1.7.2
|
github.com/gin-contrib/cors v1.7.2
|
||||||
github.com/gin-gonic/gin v1.10.0
|
github.com/gin-gonic/gin v1.10.0
|
||||||
github.com/spf13/cobra v1.8.0
|
github.com/spf13/cobra v1.8.0
|
||||||
golang.org/x/sys v0.20.0
|
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -36,6 +35,7 @@ require (
|
|||||||
golang.org/x/arch v0.8.0 // indirect
|
golang.org/x/arch v0.8.0 // indirect
|
||||||
golang.org/x/crypto v0.23.0 // indirect
|
golang.org/x/crypto v0.23.0 // indirect
|
||||||
golang.org/x/net v0.25.0 // indirect
|
golang.org/x/net v0.25.0 // indirect
|
||||||
|
golang.org/x/sys v0.20.0
|
||||||
golang.org/x/text v0.15.0 // indirect
|
golang.org/x/text v0.15.0 // indirect
|
||||||
google.golang.org/protobuf v1.34.1 // indirect
|
google.golang.org/protobuf v1.34.1 // indirect
|
||||||
)
|
)
|
||||||
|
@@ -7,7 +7,7 @@ import (
|
|||||||
"port-knocker/cmd"
|
"port-knocker/cmd"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Version и BuildTime устанавливаются при сборке через ldflags
|
||||||
var (
|
var (
|
||||||
Version = "v1.0.10"
|
Version = "v1.0.10"
|
||||||
BuildTime = "unknown"
|
BuildTime = "unknown"
|
||||||
|
@@ -112,7 +112,6 @@ git push origin main
|
|||||||
# Сборка бинарников
|
# Сборка бинарников
|
||||||
log_info "Собираем бинарники для всех платформ..."
|
log_info "Собираем бинарники для всех платформ..."
|
||||||
export VERSION_NUM="${VERSION#v}"
|
export VERSION_NUM="${VERSION#v}"
|
||||||
# shellcheck disable=SC2155
|
|
||||||
export BUILD_TIME=$(date -u '+%Y-%m-%d_%H:%M:%S')
|
export BUILD_TIME=$(date -u '+%Y-%m-%d_%H:%M:%S')
|
||||||
|
|
||||||
# Функция сборки для платформы
|
# Функция сборки для платформы
|
||||||
@@ -156,7 +155,6 @@ log_info "Создаем Git тег..."
|
|||||||
# Читаем release-notes.md и сохраняем содержимое в переменную NOTES
|
# Читаем release-notes.md и сохраняем содержимое в переменную NOTES
|
||||||
NOTES=$(cat docs/scripts/release-notes.md)
|
NOTES=$(cat docs/scripts/release-notes.md)
|
||||||
# Заменяем все переменные вида $VERSION в NOTES на их значения
|
# Заменяем все переменные вида $VERSION в NOTES на их значения
|
||||||
# shellcheck disable=SC2001
|
|
||||||
NOTES=$(echo "$NOTES" | sed "s/\\\$VERSION/$VERSION/g")
|
NOTES=$(echo "$NOTES" | sed "s/\\\$VERSION/$VERSION/g")
|
||||||
|
|
||||||
git tag -a "$VERSION" -m "$NOTES"
|
git tag -a "$VERSION" -m "$NOTES"
|
||||||
|
Reference in New Issue
Block a user