Jelajahi Sumber

fix(sub): emit the pinned peer cert sha256 in Clash subscriptions

The Clash stream builder computed tlsSettings["pin-sha256"] from the inbound's
pinnedPeerCertSha256, but applySecurity's tls case never copied it onto the
proxy, so it was written with no reader and silently dropped. Clash subscribers
lost certificate pinning while JSON subscribers kept it. Surface pin-sha256 on
the proxy in the tls case, matching the JSON emitter.
MHSanaei 1 hari lalu
induk
melakukan
82073c10c9
2 mengubah file dengan 22 tambahan dan 0 penghapusan
  1. 3 0
      internal/sub/clash_service.go
  2. 19 0
      internal/sub/sub_panic_test.go

+ 3 - 0
internal/sub/clash_service.go

@@ -684,6 +684,9 @@ func (s *SubClashService) applySecurity(proxy map[string]any, security string, s
 					proxy["skip-cert-verify"] = true
 					proxy["skip-cert-verify"] = true
 				}
 				}
 			}
 			}
+			if pins, ok := tlsSettings["pin-sha256"].([]any); ok && len(pins) > 0 {
+				proxy["pin-sha256"] = pins
+			}
 		}
 		}
 		return true
 		return true
 	case "reality":
 	case "reality":

+ 19 - 0
internal/sub/sub_panic_test.go

@@ -2,6 +2,7 @@ package sub
 
 
 import (
 import (
 	"fmt"
 	"fmt"
+	"strings"
 	"testing"
 	"testing"
 
 
 	"github.com/gin-gonic/gin"
 	"github.com/gin-gonic/gin"
@@ -81,3 +82,21 @@ func TestGetJsonToleratesHysteriaWithoutHysteriaSettings(t *testing.T) {
 		t.Fatal("GetJson returned empty for a hysteria inbound without hysteriaSettings")
 		t.Fatal("GetJson returned empty for a hysteria inbound without hysteriaSettings")
 	}
 	}
 }
 }
+
+// A Clash subscription must carry the pinned peer certificate SHA-256 when the
+// inbound configures one, matching the JSON subscription; dropping it silently
+// downgrades certificate pinning for Clash subscribers.
+func TestGetClashEmitsPinnedCertSha256(t *testing.T) {
+	seedSubDB(t)
+	const pin = "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"
+	stream := `{"network":"tcp","security":"tls","tlsSettings":{"serverName":"pin.sni","settings":{"pinnedPeerCertSha256":["` + pin + `"]}}}`
+	seedSubInbound(t, "pin1", "pin", 46300, 1, stream)
+
+	out, _, err := NewSubClashService(false, "", NewSubService("")).GetClash("pin1", "sub.example.com")
+	if err != nil {
+		t.Fatalf("GetClash: %v", err)
+	}
+	if !strings.Contains(out, "pin-sha256") {
+		t.Fatalf("Clash proxy dropped the pinned cert sha256:\n%s", out)
+	}
+}