mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-11-12 16:15:02 +03:00
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:
@@ -0,0 +1,38 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/mhsanaei/3x-ui/v3/internal/util/netsafe"
|
||||
)
|
||||
|
||||
func TestGetRemoteCertHashGuardsPrivateTargets(t *testing.T) {
|
||||
srv := httptest.NewTLSServer(http.HandlerFunc(func(http.ResponseWriter, *http.Request) {}))
|
||||
defer srv.Close()
|
||||
target := strings.TrimPrefix(srv.URL, "https://")
|
||||
sum := sha256.Sum256(srv.Certificate().Raw)
|
||||
want := hex.EncodeToString(sum[:])
|
||||
|
||||
t.Run("loopback refused without opt-in", func(t *testing.T) {
|
||||
hashes, err := (&ServerService{}).GetRemoteCertHash(target, false)
|
||||
if !errors.Is(err, netsafe.ErrPrivateAddressBlocked) {
|
||||
t.Fatalf("GetRemoteCertHash(%s) = %v, %v; want ErrPrivateAddressBlocked", target, hashes, err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("loopback read with opt-in", func(t *testing.T) {
|
||||
hashes, err := (&ServerService{}).GetRemoteCertHash(target, true)
|
||||
if err != nil {
|
||||
t.Fatalf("GetRemoteCertHash(%s, allowPrivate): %v", target, err)
|
||||
}
|
||||
if len(hashes) != 1 || hashes[0] != want {
|
||||
t.Fatalf("hashes = %v, want [%s]", hashes, want)
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user