Production hardening: host-aware install, ownership contracts, firewall safety

This commit is contained in:
2026-04-30 03:26:14 +05:00
parent 0545990968
commit c10ab1fafd
25 changed files with 190 additions and 262 deletions
+2 -2
View File
@@ -23,7 +23,7 @@ func LogSystem(c *gin.Context) {
return
}
numLine := 0
if logSystemDto.NumLine != nil || *logSystemDto.NumLine > 0 {
if logSystemDto.NumLine != nil && *logSystemDto.NumLine > 0 {
numLine = *logSystemDto.NumLine
}
logLines, total, err := util.ReadLinesFromBottom(constant.SystemLogPath, numLine)
@@ -62,7 +62,7 @@ func LogHysteria2(c *gin.Context) {
return
}
numLine := 0
if logSystemDto.NumLine != nil || *logSystemDto.NumLine > 0 {
if logSystemDto.NumLine != nil && *logSystemDto.NumLine > 0 {
numLine = *logSystemDto.NumLine
}
logLines, total, err := util.ReadLinesFromBottom(constant.Hysteria2LogPath, numLine)
-229
View File
@@ -1,229 +0,0 @@
package proxy
import (
"bufio"
"errors"
"fmt"
"github.com/sirupsen/logrus"
"gopkg.in/natefinch/lumberjack.v2"
"hy2xs-admin/model/constant"
"io"
"os/exec"
"sync"
"syscall"
"time"
)
var logger logrus.Logger
func initLogger() {
logger.SetOutput(&lumberjack.Logger{
Filename: constant.Hysteria2LogPath,
MaxSize: 1,
MaxBackups: 2,
MaxAge: 30,
Compress: true,
LocalTime: true,
})
logger.SetFormatter(&logrus.JSONFormatter{TimestampFormat: "2006-01-02 15:04:05"})
logger.SetLevel(logrus.InfoLevel)
}
func init() {
initLogger()
}
type process struct {
mutex *sync.Mutex
cmd *exec.Cmd
}
func (p *process) isRunning() bool {
return p.cmd != nil && p.cmd.Process != nil && p.cmd.ProcessState == nil
}
func (p *process) start(name string, arg ...string) error {
if !p.mutex.TryLock() {
logrus.Errorf("start cmd err: lock not acquired")
return errors.New("start cmd err")
}
defer p.mutex.Unlock()
if p.isRunning() {
return nil
}
cmd := exec.Command(name, arg...)
if cmd.Err != nil {
logrus.Errorf("cmd err: %v", cmd.Err)
return errors.New("cmd err")
}
// Получение stdout и stderr команды
stdout, err := cmd.StdoutPipe()
if err != nil {
logrus.Errorf("Error obtaining stdout: %v", err)
return err
}
stderr, err := cmd.StderrPipe()
if err != nil {
logrus.Errorf("Error obtaining stderr: %v", err)
return err
}
if err := cmd.Start(); err != nil {
logrus.Errorf("cmd start err: %v", err)
return errors.New("cmd start err")
}
p.cmd = cmd
go p.handleLogs(stdout, stderr)
return nil
}
func (p *process) stop() error {
if !p.mutex.TryLock() {
return errors.New("cmd stop err: lock not acquired")
}
if !p.isRunning() {
p.mutex.Unlock()
return nil
}
cmd := p.cmd
p.mutex.Unlock()
done := make(chan error, 1)
go func() {
done <- cmd.Wait()
}()
if err := cmd.Process.Signal(syscall.SIGTERM); err != nil {
logrus.Warnf("send SIGTERM failed: %v", err)
}
timer := time.NewTimer(3 * time.Second)
defer timer.Stop()
select {
case err := <-done:
if normalizeExitErr(err, syscall.SIGTERM) != nil {
return fmt.Errorf("process exit failed: %w", err)
}
case <-timer.C:
if err := cmd.Process.Kill(); err != nil {
return fmt.Errorf("SIGKILL failed: %w", err)
}
err := <-done
if normalizeExitErr(err, syscall.SIGKILL) != nil {
return fmt.Errorf("process killed but exit abnormal: %w", err)
}
}
p.mutex.Lock()
p.cmd = nil
p.mutex.Unlock()
return nil
}
func normalizeExitErr(err error, allowedSignals ...syscall.Signal) error {
if err == nil {
return nil
}
exitErr, ok := err.(*exec.ExitError)
if !ok {
return err
}
status, ok := exitErr.Sys().(syscall.WaitStatus)
if !ok {
return err
}
if status.Signaled() {
sig := status.Signal()
for _, s := range allowedSignals {
if sig == s {
return nil
}
}
}
return err
}
func (p *process) release() error {
if !p.mutex.TryLock() {
logrus.Errorf("cmd release err: lock not acquired")
return errors.New("cmd release err")
}
defer p.mutex.Unlock()
if !p.isRunning() {
return nil
}
if err := p.cmd.Process.Release(); err != nil {
logrus.Errorf("cmd release err: %v", err)
return errors.New("cmd release err")
}
p.cmd = nil
return nil
}
func (p *process) handleLogs(stdout, stderr io.ReadCloser) {
// Логи
stdoutChan := make(chan string)
stderrChan := make(chan string)
go func() {
scanner := bufio.NewScanner(stdout)
for scanner.Scan() {
stdoutChan <- scanner.Text()
}
if err := scanner.Err(); err != nil {
logrus.Errorf("Error reading stdout: %v", err)
}
close(stdoutChan)
}()
go func() {
scanner := bufio.NewScanner(stderr)
for scanner.Scan() {
stderrChan <- scanner.Text()
}
if err := scanner.Err(); err != nil {
logrus.Errorf("Error reading stderr: %v", err)
}
close(stderrChan)
}()
for {
select {
case line, ok := <-stdoutChan:
if !ok {
stdoutChan = nil
} else {
logger.Infof(line)
}
case line, ok := <-stderrChan:
if !ok {
stderrChan = nil
} else {
logger.Errorf(line)
}
}
// Когда оба channel закрыты, выходим из цикла
if stdoutChan == nil && stderrChan == nil {
break
}
}
}