فهرست منبع

mitm: Add `/mitm/mobileconfig` and `/mitm/certificate` clash api endpoints

世界 9 ماه پیش
والد
کامیت
5361d2acec
4فایلهای تغییر یافته به همراه91 افزوده شده و 0 حذف شده
  1. 2 0
      adapter/mitm.go
  2. 84 0
      experimental/clashapi/mitm.go
  3. 1 0
      experimental/clashapi/server.go
  4. 4 0
      mitm/engine.go

+ 2 - 0
adapter/mitm.go

@@ -2,6 +2,7 @@ package adapter
 
 
 import (
 import (
 	"context"
 	"context"
+	"crypto/x509"
 	"net"
 	"net"
 
 
 	N "github.com/sagernet/sing/common/network"
 	N "github.com/sagernet/sing/common/network"
@@ -9,5 +10,6 @@ import (
 
 
 type MITMEngine interface {
 type MITMEngine interface {
 	Lifecycle
 	Lifecycle
+	ExportCertificate() *x509.Certificate
 	NewConnection(ctx context.Context, this N.Dialer, conn net.Conn, metadata InboundContext, onClose N.CloseHandlerFunc)
 	NewConnection(ctx context.Context, this N.Dialer, conn net.Conn, metadata InboundContext, onClose N.CloseHandlerFunc)
 }
 }

+ 84 - 0
experimental/clashapi/mitm.go

@@ -0,0 +1,84 @@
+package clashapi
+
+import (
+	"context"
+	"net/http"
+
+	"github.com/sagernet/sing-box/adapter"
+	"github.com/sagernet/sing/common"
+	"github.com/sagernet/sing/service"
+
+	"github.com/go-chi/chi/v5"
+	"github.com/go-chi/render"
+	"github.com/gofrs/uuid/v5"
+	"howett.net/plist"
+)
+
+func mitmRouter(ctx context.Context) http.Handler {
+	r := chi.NewRouter()
+	r.Get("/mobileconfig", getMobileConfig(ctx))
+	r.Get("/certificate", getCertificate(ctx))
+	return r
+}
+
+func getMobileConfig(ctx context.Context) http.HandlerFunc {
+	return func(writer http.ResponseWriter, request *http.Request) {
+		engine := service.FromContext[adapter.MITMEngine](ctx)
+		if engine == nil {
+			http.NotFound(writer, request)
+			render.PlainText(writer, request, "MITM not enabled")
+			return
+		}
+		certificate := engine.ExportCertificate()
+		if certificate == nil {
+			http.NotFound(writer, request)
+			render.PlainText(writer, request, "Certificate not configured")
+			return
+		}
+		writer.Header().Set("Content-Type", "application/x-apple-aspen-config")
+		uuidGen := common.Must1(uuid.NewV4()).String()
+		mobileConfig := map[string]interface{}{
+			"PayloadContent": []interface{}{
+				map[string]interface{}{
+					"PayloadCertificateFileName": "Certificate.cer",
+					"PayloadContent":             certificate.Raw,
+					"PayloadDescription":         "Adds a root certificate",
+					"PayloadDisplayName":         certificate.Subject.CommonName,
+					"PayloadIdentifier":          "com.apple.security.root." + uuidGen,
+					"PayloadType":                "com.apple.security.root",
+					"PayloadUUID":                uuidGen,
+					"PayloadVersion":             1,
+				},
+			},
+			"PayloadDisplayName":       certificate.Subject.CommonName,
+			"PayloadIdentifier":        "io.nekohasekai.sfa.ca.profile." + uuidGen,
+			"PayloadRemovalDisallowed": false,
+			"PayloadType":              "Configuration",
+			"PayloadUUID":              uuidGen,
+			"PayloadVersion":           1,
+		}
+		encoder := plist.NewEncoder(writer)
+		encoder.Indent("\t")
+		encoder.Encode(mobileConfig)
+	}
+}
+
+func getCertificate(ctx context.Context) http.HandlerFunc {
+	return func(writer http.ResponseWriter, request *http.Request) {
+		engine := service.FromContext[adapter.MITMEngine](ctx)
+		if engine == nil {
+			http.NotFound(writer, request)
+			render.PlainText(writer, request, "MITM not enabled")
+			return
+		}
+		certificate := engine.ExportCertificate()
+		if certificate == nil {
+			http.NotFound(writer, request)
+			render.PlainText(writer, request, "Certificate not configured")
+			return
+		}
+		writer.Header().Set("Content-Type", "application/x-x509-ca-cert")
+		writer.Header().Set("Content-Disposition", "attachment; filename=Certificate.crt")
+		writer.Write(certificate.Raw)
+	}
+}

+ 1 - 0
experimental/clashapi/server.go

@@ -124,6 +124,7 @@ func NewServer(ctx context.Context, logFactory log.ObservableFactory, options op
 		r.Mount("/profile", profileRouter())
 		r.Mount("/profile", profileRouter())
 		r.Mount("/cache", cacheRouter(ctx))
 		r.Mount("/cache", cacheRouter(ctx))
 		r.Mount("/dns", dnsRouter(s.dnsRouter))
 		r.Mount("/dns", dnsRouter(s.dnsRouter))
+		r.Mount("/mitm", mitmRouter(ctx))
 
 
 		s.setupMetaAPI(r)
 		s.setupMetaAPI(r)
 	})
 	})

+ 4 - 0
mitm/engine.go

@@ -91,6 +91,10 @@ func (e *Engine) Close() error {
 	return nil
 	return nil
 }
 }
 
 
+func (e *Engine) ExportCertificate() *x509.Certificate {
+	return e.tlsCertificate
+}
+
 func (e *Engine) NewConnection(ctx context.Context, this N.Dialer, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
 func (e *Engine) NewConnection(ctx context.Context, this N.Dialer, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
 	if e.tlsDecryptionEnabled && metadata.ClientHello != nil {
 	if e.tlsDecryptionEnabled && metadata.ClientHello != nil {
 		err := e.newTLS(ctx, this, conn, metadata, onClose)
 		err := e.newTLS(ctx, this, conn, metadata, onClose)