This commit is contained in:
2026-05-29 17:05:44 -03:00
parent e857bdba67
commit 26777dbb89

53
main.go
View File

@@ -485,6 +485,7 @@ func (a *App) handleDragonCreate(w http.ResponseWriter, r *http.Request) {
Password string `json:"password"`
UUID string `json:"uuid"`
Days int `json:"days"`
Hours int `json:"hours"`
Minutes int `json:"minutes"`
MaxConnections int `json:"max_connections"`
WithXray bool `json:"with_xray"`
@@ -497,6 +498,9 @@ func (a *App) handleDragonCreate(w http.ResponseWriter, r *http.Request) {
if p.Minutes > 0 {
t := time.Now().Add(time.Duration(p.Minutes) * time.Minute)
exp = &t
} else if p.Hours > 0 {
t := time.Now().Add(time.Duration(p.Hours) * time.Hour)
exp = &t
} else if p.Days > 0 {
t := time.Now().AddDate(0, 0, p.Days)
exp = &t
@@ -571,15 +575,12 @@ func (a *App) createSSH(username, password string, limit int, expiresAt *time.Ti
}
args := []string{"-M", "-s", "/bin/false", "-p", hash}
if expiresAt != nil {
linuxExpiry := *expiresAt
if linuxExpiry.Before(time.Now().Add(24 * time.Hour)) {
linuxExpiry = time.Now().AddDate(0, 0, 2)
}
linuxExpiry := linuxExpiryForAccount(*expiresAt)
args = append(args, "-e", linuxExpiry.Format("2006-01-02"))
}
args = append(args, username)
if out, err := exec.Command("useradd", args...).CombinedOutput(); err != nil {
return fmt.Errorf("useradd: %v: %s", err, strings.TrimSpace(string(out)))
if err := runUserAdd(args); err != nil {
return err
}
if err := writeCompatUserFiles(username, password, limit); err != nil {
log.Printf("compat files: %v", err)
@@ -595,6 +596,46 @@ func (a *App) createSSH(username, password string, limit int, expiresAt *time.Ti
return a.store.saveLocked()
}
func linuxExpiryForAccount(expiresAt time.Time) time.Time {
// Linux account expiry is date-only and cannot safely represent accounts that
// expire in minutes or hours. For those short tests, keep the system account
// valid for seven days and let the bridge SQLite expiry loop delete it at the
// exact minute/hour.
if expiresAt.Before(time.Now().Add(24 * time.Hour)) {
return time.Now().AddDate(0, 0, 7)
}
return expiresAt
}
func runUserAdd(args []string) error {
out, err := exec.Command("useradd", args...).CombinedOutput()
if err == nil {
return nil
}
firstErr := fmt.Errorf("useradd: %v: %s", err, strings.TrimSpace(string(out)))
text := strings.ToLower(string(out))
nameRejected := strings.Contains(text, "invalid user name") ||
strings.Contains(text, "invalid username") ||
strings.Contains(text, "bad name") ||
strings.Contains(text, "does not match")
if !nameRejected {
return firstErr
}
// DragonCore test usernames can start with numbers, for example 820etl.
// Some Linux distributions reject those by default. Debian/Ubuntu shadow-utils
// accept --badname; a few adduser wrappers use --force-badname. Try both as a
// compatibility fallback, keeping the same password, shell and expiry args.
for _, opt := range []string{"--badname", "--force-badname"} {
retryArgs := append([]string{opt}, args...)
out, err = exec.Command("useradd", retryArgs...).CombinedOutput()
if err == nil {
return nil
}
}
return firstErr
}
func (a *App) deleteSSH(username, uuid string) error {
if username == "" {
return fmt.Errorf("username required")