feat(amneziawg): support persistent keepalive ranges (#6881)

AmneziaWG clients accept a randomised PersistentKeepalive range such as 15-25,
which makes keepalive timing harder to fingerprint; the panel only stored a
single number. A client's keepalive may now be a range, validated the way
AmneziaWG parses it. The downloadable .conf and vpn:// profile carry the range,
Clash/Mihomo output gets its lower bound, and plain WireGuard stays scalar-only.
The range lives in an additive wg_keep_alive_range column, so the integer
column and an older panel binary keep working after a rollback.
This commit is contained in:
YoungReckless4
2026-10-24 07:36:49 +03:00
committed by GitHub
parent 207c705300
commit 35e09191de
59 changed files with 1871 additions and 146 deletions
+26 -8
View File
@@ -334,6 +334,17 @@ func (s *ClientService) AddInboundClient(inboundSvc *InboundService, data *model
if err := validateClientsRenewal(clients); err != nil {
return false, err
}
inbound, err := inboundSvc.GetInbound(data.Id)
if err != nil {
return false, err
}
existingClients, err := inboundSvc.GetClients(inbound)
if err != nil {
return false, err
}
if err := validateClientsKeepAliveAgainstExisting(inbound.Protocol, clients, existingClients); err != nil {
return false, err
}
var settings map[string]any
err = json.Unmarshal([]byte(data.Settings), &settings)
@@ -372,11 +383,6 @@ func (s *ClientService) AddInboundClient(inboundSvc *InboundService, data *model
return false, err
}
existingClients, err := inboundSvc.GetClients(oldInbound)
if err != nil {
return false, err
}
// A client already on this inbound is skipped instead of appended again:
// checkEmailsExistForClients exempts a matching subId so one identity can
// live on several inbounds, which let retried or raced adds duplicate the
@@ -670,7 +676,10 @@ func (s *ClientService) UpdateInboundClient(inboundSvc *InboundService, data *mo
if err := validateClientsRenewal(clients); err != nil {
return false, err
}
inbound, err := inboundSvc.GetInbound(data.Id)
if err != nil {
return false, err
}
var settings map[string]any
err = json.Unmarshal([]byte(data.Settings), &settings)
if err != nil {
@@ -691,6 +700,15 @@ func (s *ClientService) UpdateInboundClient(inboundSvc *InboundService, data *mo
if err != nil {
return false, err
}
for i, client := range clients {
identityEmail := client.Email
if i == 0 {
identityEmail = oldEmail
}
if err := validateClientKeepAliveAgainstExisting(inbound.Protocol, client, oldClients, identityEmail); err != nil {
return false, fmt.Errorf("client %q: %w", client.Email, err)
}
}
newClientId := ""
switch oldInbound.Protocol {
@@ -844,8 +862,8 @@ func (s *ClientService) UpdateInboundClient(inboundSvc *InboundService, data *mo
if clients[0].PreSharedKey != "" {
newMap["preSharedKey"] = clients[0].PreSharedKey
}
if ka := clients[0].KeepAliveSeconds(); ka > 0 {
newMap["keepAlive"] = ka
if clients[0].KeepAlive != nil {
newMap["keepAlive"] = clients[0].KeepAlive
}
if oldInbound.Protocol == model.AmneziaWG && clients[0].ForwardedPorts != "" {
newMap["forwardedPorts"] = clients[0].ForwardedPorts