fix(server): apply the outbound address policy to remote cert pinning

The remote certificate fetch now dials through the same netsafe guard
as the REALITY target scan. A private or loopback endpoint is refused
unless the request carries allowPrivate; the inbound form asks the
operator to confirm and retries with the opt-in.
This commit is contained in:
MHSanaei
2026-10-03 13:20:00 +02:00
parent d31465e37b
commit ede275e4dc
8 changed files with 87 additions and 8 deletions
+8 -1
View File
@@ -1,6 +1,7 @@
package controller
import (
"errors"
"fmt"
"net/http"
"regexp"
@@ -10,6 +11,7 @@ import (
"github.com/mhsanaei/3x-ui/v3/internal/database/model"
"github.com/mhsanaei/3x-ui/v3/internal/logger"
"github.com/mhsanaei/3x-ui/v3/internal/util/netsafe"
"github.com/mhsanaei/3x-ui/v3/internal/web/entity"
"github.com/mhsanaei/3x-ui/v3/internal/web/global"
"github.com/mhsanaei/3x-ui/v3/internal/web/service"
@@ -467,7 +469,12 @@ func (a *ServerController) getCertHash(c *gin.Context) {
// getRemoteCertHash runs `xray tls ping` against the given server and returns
// its live certificate SHA-256 hash(es) for pinning.
func (a *ServerController) getRemoteCertHash(c *gin.Context) {
hashes, err := a.serverService.GetRemoteCertHash(c.PostForm("server"))
allowPrivate := c.PostForm("allowPrivate") == "true"
hashes, err := a.serverService.GetRemoteCertHash(c.PostForm("server"), allowPrivate)
if errors.Is(err, netsafe.ErrPrivateAddressBlocked) {
jsonMsgObj(c, "get remote cert hash", gin.H{"privateTarget": true}, err)
return
}
if err != nil {
jsonMsg(c, "get remote cert hash", err)
return