Compare commits

..

1 commit

Author SHA1 Message Date
Nebulosa
e9c39f6e42
Merge d8cb65e2b5 into d8fb09faae 2026-02-04 13:59:06 +00:00
4 changed files with 55 additions and 43 deletions

View file

@ -30,8 +30,7 @@ RUN apk add --no-cache --update \
tzdata \ tzdata \
fail2ban \ fail2ban \
bash \ bash \
curl \ curl
openssl
COPY --from=builder /app/build/ /app/ COPY --from=builder /app/build/ /app/
COPY --from=builder /app/DockerEntrypoint.sh /app/ COPY --from=builder /app/DockerEntrypoint.sh /app/

View file

@ -182,24 +182,10 @@ func (a *SUBController) ApplyCommonHeaders(
) { ) {
c.Writer.Header().Set("Subscription-Userinfo", header) c.Writer.Header().Set("Subscription-Userinfo", header)
c.Writer.Header().Set("Profile-Update-Interval", updateInterval) c.Writer.Header().Set("Profile-Update-Interval", updateInterval)
c.Writer.Header().Set("Profile-Title", "base64:"+base64.StdEncoding.EncodeToString([]byte(profileTitle)))
//Basics c.Writer.Header().Set("Support-Url", profileSupportUrl)
if profileTitle != "" { c.Writer.Header().Set("Profile-Web-Page-Url", profileUrl)
c.Writer.Header().Set("Profile-Title", "base64:"+base64.StdEncoding.EncodeToString([]byte(profileTitle))) c.Writer.Header().Set("Announce", "base64:"+base64.StdEncoding.EncodeToString([]byte(profileAnnounce)))
}
if profileSupportUrl != "" {
c.Writer.Header().Set("Support-Url", profileSupportUrl)
}
if profileUrl != "" {
c.Writer.Header().Set("Profile-Web-Page-Url", profileUrl)
}
if profileAnnounce != "" {
c.Writer.Header().Set("Announce", "base64:"+base64.StdEncoding.EncodeToString([]byte(profileAnnounce)))
}
//Advanced (Happ)
c.Writer.Header().Set("Routing-Enable", strconv.FormatBool(profileEnableRouting)) c.Writer.Header().Set("Routing-Enable", strconv.FormatBool(profileEnableRouting))
if profileRoutingRules != "" { c.Writer.Header().Set("Routing", profileRoutingRules)
c.Writer.Header().Set("Routing", profileRoutingRules)
}
} }

View file

@ -3,6 +3,7 @@ package job
import ( import (
"bufio" "bufio"
"encoding/json" "encoding/json"
"fmt"
"io" "io"
"log" "log"
"os" "os"
@ -387,7 +388,7 @@ func (j *CheckClientIpJob) updateInboundClientIps(inboundClientIps *model.Inboun
// disconnectClientTemporarily removes and re-adds a client to force disconnect old connections // disconnectClientTemporarily removes and re-adds a client to force disconnect old connections
func (j *CheckClientIpJob) disconnectClientTemporarily(inbound *model.Inbound, clientEmail string, clients []model.Client) { func (j *CheckClientIpJob) disconnectClientTemporarily(inbound *model.Inbound, clientEmail string, clients []model.Client) {
var xrayAPI xray.XrayAPI var xrayAPI xray.XrayAPI
// Get panel settings for API port // Get panel settings for API port
db := database.GetDB() db := database.GetDB()
var apiPort int var apiPort int
@ -395,7 +396,7 @@ func (j *CheckClientIpJob) disconnectClientTemporarily(inbound *model.Inbound, c
if err := db.Where("key = ?", "xrayApiPort").First(&apiPortSetting).Error; err == nil { if err := db.Where("key = ?", "xrayApiPort").First(&apiPortSetting).Error; err == nil {
apiPort, _ = strconv.Atoi(apiPortSetting.Value) apiPort, _ = strconv.Atoi(apiPortSetting.Value)
} }
if apiPort == 0 { if apiPort == 0 {
apiPort = 10085 // Default API port apiPort = 10085 // Default API port
} }

View file

@ -1056,23 +1056,35 @@ func (s *ServerService) IsValidGeofileName(filename string) bool {
} }
func (s *ServerService) UpdateGeofile(fileName string) error { func (s *ServerService) UpdateGeofile(fileName string) error {
type geofileEntry struct { files := []struct {
URL string URL string
FileName string FileName string
} }{
geofileAllowlist := map[string]geofileEntry{ {"https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat", "geoip.dat"},
"geoip.dat": {"https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat", "geoip.dat"}, {"https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat", "geosite.dat"},
"geosite.dat": {"https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat", "geosite.dat"}, {"https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geoip.dat", "geoip_IR.dat"},
"geoip_IR.dat": {"https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geoip.dat", "geoip_IR.dat"}, {"https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geosite.dat", "geosite_IR.dat"},
"geosite_IR.dat": {"https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geosite.dat", "geosite_IR.dat"}, {"https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geoip.dat", "geoip_RU.dat"},
"geoip_RU.dat": {"https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geoip.dat", "geoip_RU.dat"}, {"https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geosite.dat", "geosite_RU.dat"},
"geosite_RU.dat": {"https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geosite.dat", "geosite_RU.dat"},
} }
// Strict allowlist check to avoid writing uncontrolled files // Strict allowlist check to avoid writing uncontrolled files
if fileName != "" { if fileName != "" {
if _, ok := geofileAllowlist[fileName]; !ok { // Use the centralized validation function
return common.NewErrorf("Invalid geofile name: %q not in allowlist", fileName) if !s.IsValidGeofileName(fileName) {
return common.NewErrorf("Invalid geofile name: contains unsafe path characters: %s", fileName)
}
// Ensure the filename matches exactly one from our allowlist
isAllowed := false
for _, file := range files {
if fileName == file.FileName {
isAllowed = true
break
}
}
if !isAllowed {
return common.NewErrorf("Invalid geofile name: %s not in allowlist", fileName)
} }
} }
@ -1147,18 +1159,32 @@ func (s *ServerService) UpdateGeofile(fileName string) error {
var errorMessages []string var errorMessages []string
if fileName == "" { if fileName == "" {
// Download all geofiles for _, file := range files {
for _, entry := range geofileAllowlist { // Sanitize the filename from our allowlist as an extra precaution
destPath := filepath.Join(config.GetBinFolderPath(), entry.FileName) destPath := filepath.Join(config.GetBinFolderPath(), filepath.Base(file.FileName))
if err := downloadFile(entry.URL, destPath); err != nil { if err := downloadFile(file.URL, destPath); err != nil {
errorMessages = append(errorMessages, fmt.Sprintf("Error downloading Geofile '%s': %v", entry.FileName, err)) errorMessages = append(errorMessages, fmt.Sprintf("Error downloading Geofile '%s': %v", file.FileName, err))
} }
} }
} else { } else {
entry := geofileAllowlist[fileName] // Use filepath.Base to ensure we only get the filename component, no path traversal
destPath := filepath.Join(config.GetBinFolderPath(), entry.FileName) safeName := filepath.Base(fileName)
if err := downloadFile(entry.URL, destPath); err != nil { destPath := filepath.Join(config.GetBinFolderPath(), safeName)
errorMessages = append(errorMessages, fmt.Sprintf("Error downloading Geofile '%s': %v", entry.FileName, err))
var fileURL string
for _, file := range files {
if file.FileName == fileName {
fileURL = file.URL
break
}
}
if fileURL == "" {
errorMessages = append(errorMessages, fmt.Sprintf("File '%s' not found in the list of Geofiles", fileName))
} else {
if err := downloadFile(fileURL, destPath); err != nil {
errorMessages = append(errorMessages, fmt.Sprintf("Error downloading Geofile '%s': %v", fileName, err))
}
} }
} }