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

This commit is contained in:
世界 2025-02-03 09:09:41 +08:00
parent 1fe983a81b
commit 5361d2acec
No known key found for this signature in database
GPG Key ID: CD109927C34A63C4
4 changed files with 91 additions and 0 deletions

View File

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

View File

@ -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)
}
}

View File

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

View File

@ -91,6 +91,10 @@ func (e *Engine) Close() error {
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) {
if e.tlsDecryptionEnabled && metadata.ClientHello != nil {
err := e.newTLS(ctx, this, conn, metadata, onClose)