platform: modularize api/gui, add docs-tests-web foundation, and refresh root config
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
transportcfg "selective-vpn-api/app/transportcfg"
|
||||
)
|
||||
|
||||
const (
|
||||
transportSingBoxTemplateUnitName = "singbox@.service"
|
||||
transportSingBoxInstanceDropIn = "10-selective-vpn.conf"
|
||||
transportSingBoxTemplateMarker = "# SVPN_TEMPLATE=singbox-instance"
|
||||
transportSingBoxInstanceDropInTag = "# SVPN_INSTANCE_DROPIN=singbox"
|
||||
)
|
||||
|
||||
func transportSystemdSingBoxUsesTemplateInstance(client TransportClient, unit string) bool {
|
||||
if client.Kind != TransportClientSingBox {
|
||||
return false
|
||||
}
|
||||
u := strings.TrimSpace(unit)
|
||||
if u == "" || !strings.HasSuffix(strings.ToLower(u), ".service") {
|
||||
return false
|
||||
}
|
||||
at := strings.IndexByte(u, '@')
|
||||
return at > 0
|
||||
}
|
||||
|
||||
func transportSystemdTemplateUnitForInstance(unit string) string {
|
||||
u := strings.TrimSpace(unit)
|
||||
if !strings.HasSuffix(strings.ToLower(u), ".service") {
|
||||
return ""
|
||||
}
|
||||
at := strings.IndexByte(u, '@')
|
||||
if at <= 0 {
|
||||
return ""
|
||||
}
|
||||
return u[:at] + "@.service"
|
||||
}
|
||||
|
||||
func transportSystemdUnitDropInDir(unit string) string {
|
||||
path := transportSystemdUnitPath(strings.TrimSpace(unit))
|
||||
return path + ".d"
|
||||
}
|
||||
|
||||
func transportSystemdUnitDropInPath(unit, fileName string) string {
|
||||
name := strings.TrimSpace(fileName)
|
||||
if name == "" {
|
||||
name = transportSingBoxInstanceDropIn
|
||||
}
|
||||
return filepath.Join(transportSystemdUnitDropInDir(unit), name)
|
||||
}
|
||||
|
||||
func writeTransportSystemdDropInFile(unit, fileName, content string) error {
|
||||
path := transportSystemdUnitDropInPath(unit, fileName)
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
tmp := path + ".tmp"
|
||||
if err := os.WriteFile(tmp, []byte(content), 0o644); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.Rename(tmp, path)
|
||||
}
|
||||
|
||||
func writeTransportSingBoxTemplateArtifacts(
|
||||
client TransportClient,
|
||||
in transportSystemdProvisionInputs,
|
||||
) error {
|
||||
templateUnit := transportSystemdTemplateUnitForInstance(in.unit)
|
||||
if templateUnit == "" {
|
||||
templateUnit = transportSingBoxTemplateUnitName
|
||||
}
|
||||
if !validSystemdUnitName(templateUnit) {
|
||||
return fmt.Errorf("invalid template unit: %s", templateUnit)
|
||||
}
|
||||
|
||||
templateTuning := transportSystemdServiceTuningFromConfig(nil, "")
|
||||
templateHardening := transportSystemdHardeningFromConfig(nil, "")
|
||||
templateContent := renderTransportSystemdUnit(
|
||||
TransportClient{ID: "%i", Kind: TransportClientSingBox},
|
||||
templateUnit,
|
||||
"/bin/true",
|
||||
false,
|
||||
"",
|
||||
templateTuning,
|
||||
templateHardening,
|
||||
)
|
||||
templateContent = transportSingBoxTemplateMarker + "\n" + templateContent
|
||||
if err := writeTransportSystemdUnitFile(templateUnit, templateContent); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
netnsEnabled := transportNetnsEnabled(client)
|
||||
netnsName := ""
|
||||
if netnsEnabled {
|
||||
netnsName = transportNetnsName(client)
|
||||
}
|
||||
configPath := transportSingBoxConfigPath(client)
|
||||
dropIn := renderTransportSingBoxInstanceDropIn(client, in, configPath, netnsEnabled, netnsName)
|
||||
return writeTransportSystemdDropInFile(in.unit, transportSingBoxInstanceDropIn, dropIn)
|
||||
}
|
||||
|
||||
func renderTransportSingBoxInstanceDropIn(
|
||||
client TransportClient,
|
||||
in transportSystemdProvisionInputs,
|
||||
configPath string,
|
||||
netnsEnabled bool,
|
||||
netnsName string,
|
||||
) string {
|
||||
b := strings.Builder{}
|
||||
b.WriteString(transportSingBoxInstanceDropInTag + "\n")
|
||||
b.WriteString("[Unit]\n")
|
||||
b.WriteString("StartLimitIntervalSec=" + fmt.Sprintf("%d", in.primaryTuning.StartLimitIntervalSec) + "\n")
|
||||
b.WriteString("StartLimitBurst=" + fmt.Sprintf("%d", in.primaryTuning.StartLimitBurst) + "\n")
|
||||
b.WriteString("\n[Service]\n")
|
||||
b.WriteString("WorkingDirectory=" + stateDir + "\n")
|
||||
b.WriteString("Restart=" + in.primaryTuning.RestartPolicy + "\n")
|
||||
b.WriteString("RestartSec=" + fmt.Sprintf("%d", in.primaryTuning.RestartSec) + "\n")
|
||||
b.WriteString("Environment=SVPN_TRANSPORT_ID=" + strings.TrimSpace(client.ID) + "\n")
|
||||
b.WriteString("Environment=SVPN_TRANSPORT_KIND=" + strings.TrimSpace(string(client.Kind)) + "\n")
|
||||
if strings.TrimSpace(configPath) != "" {
|
||||
b.WriteString("Environment=SVPN_CONFIG_PATH=" + strings.TrimSpace(configPath) + "\n")
|
||||
}
|
||||
if netnsEnabled {
|
||||
b.WriteString("Environment=SVPN_NETNS_ENABLED=1\n")
|
||||
if strings.TrimSpace(netnsName) != "" {
|
||||
b.WriteString("Environment=SVPN_NETNS_NAME=" + strings.TrimSpace(netnsName) + "\n")
|
||||
}
|
||||
} else {
|
||||
b.WriteString("Environment=SVPN_NETNS_ENABLED=0\n")
|
||||
}
|
||||
b.WriteString("ExecStart=\n")
|
||||
b.WriteString("ExecStart=" + transportcfg.SystemdShellExec(in.primaryCmd, shellQuoteArg) + "\n")
|
||||
b.WriteString("ExecStop=\n")
|
||||
b.WriteString("ExecStop=/bin/kill -TERM $MAINPID\n")
|
||||
b.WriteString("TimeoutStartSec=" + fmt.Sprintf("%d", in.primaryTuning.TimeoutStartSec) + "\n")
|
||||
b.WriteString("TimeoutStopSec=" + fmt.Sprintf("%d", in.primaryTuning.TimeoutStopSec) + "\n")
|
||||
if in.primaryTuning.WatchdogSec > 0 {
|
||||
b.WriteString("WatchdogSec=" + fmt.Sprintf("%d", in.primaryTuning.WatchdogSec) + "\n")
|
||||
b.WriteString("NotifyAccess=main\n")
|
||||
}
|
||||
if in.primaryHardening.Enabled {
|
||||
b.WriteString("NoNewPrivileges=" + transportSystemdBool(in.primaryHardening.NoNewPrivileges) + "\n")
|
||||
b.WriteString("PrivateTmp=" + transportSystemdBool(in.primaryHardening.PrivateTmp) + "\n")
|
||||
b.WriteString("ProtectSystem=" + in.primaryHardening.ProtectSystem + "\n")
|
||||
b.WriteString("ProtectHome=" + in.primaryHardening.ProtectHome + "\n")
|
||||
b.WriteString("ProtectControlGroups=" + transportSystemdBool(in.primaryHardening.ProtectControlGroups) + "\n")
|
||||
b.WriteString("ProtectKernelModules=" + transportSystemdBool(in.primaryHardening.ProtectKernelModules) + "\n")
|
||||
b.WriteString("ProtectKernelTunables=" + transportSystemdBool(in.primaryHardening.ProtectKernelTunables) + "\n")
|
||||
b.WriteString("RestrictSUIDSGID=" + transportSystemdBool(in.primaryHardening.RestrictSUIDSGID) + "\n")
|
||||
b.WriteString("LockPersonality=" + transportSystemdBool(in.primaryHardening.LockPersonality) + "\n")
|
||||
if in.primaryHardening.PrivateDevices {
|
||||
b.WriteString("PrivateDevices=yes\n")
|
||||
}
|
||||
b.WriteString("UMask=" + in.primaryHardening.UMask + "\n")
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func transportSystemdBool(v bool) string {
|
||||
if v {
|
||||
return "yes"
|
||||
}
|
||||
return "no"
|
||||
}
|
||||
Reference in New Issue
Block a user