finish rtt enrollment

This commit is contained in:
Shelikhoo
2025-11-10 12:33:16 +00:00
committed by Xiaokang Wang (Shelikhoo)
parent 85fd420745
commit 9aeffbef6b
9 changed files with 113 additions and 13 deletions
@@ -88,3 +88,7 @@ type ConnectionEnrollmentConfirmationServerInstanceConfig struct {
type ConnectionEnrollmentConfirmationServerInstanceConfigReceiver interface {
OnConnectionEnrollmentConfirmationServerInstanceConfigReady(ConnectionEnrollmentConfirmationServerInstanceConfig)
}
type ConnectionLoopbackPrevention struct {
Key string
}
@@ -0,0 +1,19 @@
package mirrorcommon
import (
"context"
"github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror"
)
func SetLoopbackProtectionFlagForContext(ctx context.Context, enrollmentID []byte) context.Context {
loopbackProtectionKey := tlsmirror.ConnectionLoopbackPrevention{Key: string(enrollmentID)}
return context.WithValue(ctx, loopbackProtectionKey, true)
}
func IsLoopbackProtectionEnabled(ctx context.Context, enrollmentID []byte) bool {
loopbackProtectionKey := tlsmirror.ConnectionLoopbackPrevention{Key: string(enrollmentID)}
val := ctx.Value(loopbackProtectionKey)
enabled, ok := val.(bool)
return ok && enabled
}
@@ -4,11 +4,13 @@ import (
"context"
"net"
"github.com/v2fly/v2ray-core/v5/common"
"github.com/v2fly/v2ray-core/v5/common/environment"
"github.com/v2fly/v2ray-core/v5/common/environment/envctx"
v2net "github.com/v2fly/v2ray-core/v5/common/net"
"github.com/v2fly/v2ray-core/v5/common/serial"
"github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror"
"github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror/mirrorcommon"
"github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror/mirrorenrollment/httpenrollmentconfirmation"
)
@@ -78,7 +80,9 @@ func (c *EnrollmentConfirmationClient) init() error {
return nil, newError("failed to parse destination address").Base(err).AtError()
}
dest.Network = v2net.Network_TCP
return dialer(c.ctx, dest, c.config.PrimaryEgressOutbound)
loopbackProtectedCtx := mirrorcommon.SetLoopbackProtectionFlagForContext(c.ctx, c.serverIdentity)
return dialer(loopbackProtectedCtx, dest, c.config.PrimaryEgressOutbound)
}, c.serverIdentity)
if err != nil {
return newError("failed to create HTTP round tripper for enrollment confirmation").Base(err).AtError()
@@ -93,7 +97,14 @@ func (c *EnrollmentConfirmationClient) init() error {
if err != nil {
return newError("failed to get instance of bootstrap enrollment confirmation config").Base(err).AtError()
}
enrollmentConfirmation, ok := enrollment.(tlsmirror.ConnectionEnrollmentConfirmation)
loopbackProtectedCtx := mirrorcommon.SetLoopbackProtectionFlagForContext(c.ctx, c.serverIdentity)
enrollmentInst, err := common.CreateObject(loopbackProtectedCtx, enrollment)
if err != nil {
return newError("failed to create bootstrap enrollment confirmation config").Base(err).AtError()
}
enrollmentConfirmation, ok := enrollmentInst.(tlsmirror.ConnectionEnrollmentConfirmation)
if !ok {
return newError("bootstrap enrollment confirmation config is not a valid ConnectionEnrollmentConfirmation")
@@ -37,10 +37,14 @@ func (c *client) VerifyConnectionEnrollment(req *tlsmirror.EnrollmentConfirmatio
return nil, newError("failed to create HTTP request").Base(err)
}
httpResp, err := c.httpRoundTripper.RoundTrip(httpReq)
defer func() {
if httpResp != nil && httpResp.Body != nil {
httpResp.Body.Close()
}
}()
if err != nil {
return nil, newError("failed to send HTTP request").Base(err)
}
defer httpResp.Body.Close()
if httpResp.StatusCode != http.StatusOK {
return nil, newError("unexpected HTTP response status: ", httpResp.StatusCode)
}
@@ -52,17 +52,19 @@ func (c *clientRoundtripper) RoundTrip(request *http.Request) (*http.Response, e
resp, err := c.currentConn.RoundTrip(request)
if err != nil {
defer func() {
c.currentConnLock.RUnlock()
c.currentConnLock.Lock()
defer c.currentConnLock.Unlock()
if c.currentConn != nil {
c.currentConnInnerConn.Close()
c.currentConnInnerConn = nil
c.currentConn = nil
}
c.currentConnLock.Unlock()
c.currentConnLock.RLock()
}()
return nil, newError("unable to roundtrip for enrollment verification").Base(err)
}
return resp, err
return resp, nil
}
func (c *clientRoundtripper) createNewConnection() error {
@@ -26,11 +26,16 @@ func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) {
return nil, newError("config cannot be nil")
}
rttClientI, err := serial.GetInstanceOf(config.RoundTripperClient)
rttClientConfig, err := serial.GetInstanceOf(config.RoundTripperClient)
if err != nil {
return nil, newError("failed to get instance of RoundTripperClient").Base(err)
}
rttClientI, err := common.CreateObject(ctx, rttClientConfig)
if err != nil {
return nil, newError("failed to create RoundTripperClient").Base(err)
}
rttClient, ok := rttClientI.(request.RoundTripperClient)
if !ok {
return nil, newError("RoundTripperClient is not a valid request.RoundTripperClient")
@@ -4,6 +4,7 @@ import (
"context"
"net"
"github.com/v2fly/v2ray-core/v5/common/serial"
"google.golang.org/protobuf/proto"
"github.com/v2fly/v2ray-core/v5/common"
@@ -14,12 +15,16 @@ import (
"github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror"
)
func NewServer(ctx context.Context, config *ServerConfig) *Server {
func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) {
s := &Server{
ctx: ctx,
config: config,
}
return s
if err := s.init(); err != nil {
return nil, newError("failed to initialize RoundTripperEnrollmentConfirmation server").Base(err).AtError()
}
return s, nil
}
type Server struct {
@@ -29,6 +34,10 @@ type Server struct {
rttServer request.RoundTripperServer
}
func (s *Server) OnConnectionEnrollmentConfirmationServerInstanceConfigReady(config tlsmirror.ConnectionEnrollmentConfirmationServerInstanceConfig) {
s.enrollmentProcessor = config.EnrollmentProcessor
}
func (s *Server) Listen(ctx context.Context) (v2net.Listener, error) {
transportEnvironment := envctx.EnvironmentFromContext(s.ctx).(environment.TransportEnvironment)
listener := transportEnvironment.Listener()
@@ -75,8 +84,35 @@ func (s *Server) AutoImplListener() request.Listener {
return s
}
func (s *Server) init() error {
if s.config == nil {
return newError("nil ServerConfig")
}
if s.config.RoundTripperServer == nil {
return newError("nil RoundTripperServer in ServerConfig")
}
RoundTripperServerConfig, err := serial.GetInstanceOf(s.config.RoundTripperServer)
if err != nil {
return newError("failed to get instance of RoundTripperServer").Base(err).AtError()
}
RoundTripperServerObj, err := common.CreateObject(s.ctx, RoundTripperServerConfig)
if err != nil {
return newError("failed to create RoundTripperServer").Base(err).AtError()
}
RoundTripperServerTyped, ok := RoundTripperServerObj.(request.RoundTripperServer)
if !ok {
return newError("RoundTripperServer is not a valid request.RoundTripperServer")
}
s.rttServer = RoundTripperServerTyped
s.rttServer.OnTransportServerAssemblyReady(s)
if err := s.rttServer.Start(); err != nil {
return newError("failed to start RoundTripperServer").Base(err).AtError()
}
return nil
}
func init() {
common.Must(common.RegisterConfig((*ServerConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
return NewServer(ctx, config.(*ServerConfig)), nil
return NewServer(ctx, config.(*ServerConfig))
}))
}
@@ -4,6 +4,7 @@ import (
"context"
"net"
"github.com/v2fly/v2ray-core/v5/common"
"github.com/v2fly/v2ray-core/v5/common/serial"
"github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror"
"github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror/mirrorenrollment/httpenrollmentconfirmation"
@@ -36,6 +37,10 @@ func NewEnrollmentConfirmationServer(ctx context.Context, config *Config, enroll
primaryIngressConnectionHandler: primaryIngressConnectionHandler,
}
if err = s.init(); err != nil {
return nil, newError("failed to initialize enrollment confirmation server").Base(err).AtError()
}
return s, nil
}
@@ -59,21 +64,25 @@ func (s *EnrollmentConfirmationServer) HandlePrimaryIngressConnection(ctx contex
}
func (s *EnrollmentConfirmationServer) init() error {
for _, handler := range s.config.BootstrapEgressConfig {
for _, handler := range s.config.BootstrapIngressConfig {
bootstrapEnrollmentHandler, err := serial.GetInstanceOf(handler)
if err != nil {
return newError("failed to get instance of bootstrap enrollment handler").Base(err).AtError()
}
bootstrapEnrollmentHandlerTyped, ok := bootstrapEnrollmentHandler.(tlsmirror.ConnectionEnrollmentConfirmationServerInstanceConfigReceiver)
bootstrapEnrollmentHandlerObj, err := common.CreateObject(s.ctx, bootstrapEnrollmentHandler)
if err != nil {
return newError("failed to create bootstrap enrollment handler").Base(err).AtError()
}
bootstrapEnrollmentHandlerObjTyped, ok := bootstrapEnrollmentHandlerObj.(tlsmirror.ConnectionEnrollmentConfirmationServerInstanceConfigReceiver)
if !ok {
return newError("bootstrap enrollment handler is not a valid ConnectionEnrollmentConfirmationServerInstanceConfigReceiver")
}
bootstrapEnrollmentHandlerTyped.OnConnectionEnrollmentConfirmationServerInstanceConfigReady(
bootstrapEnrollmentHandlerObjTyped.OnConnectionEnrollmentConfirmationServerInstanceConfigReady(
tlsmirror.ConnectionEnrollmentConfirmationServerInstanceConfig{
EnrollmentProcessor: s.enrollmentProcessor,
})
s.bootstrapIngressConnectionHandlers = append(s.bootstrapIngressConnectionHandlers, bootstrapEnrollmentHandlerTyped)
s.bootstrapIngressConnectionHandlers = append(s.bootstrapIngressConnectionHandlers, bootstrapEnrollmentHandlerObjTyped)
}
return nil
}
@@ -7,6 +7,7 @@ import (
"time"
"github.com/golang/protobuf/proto"
"github.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror/mirrorcommon"
core "github.com/v2fly/v2ray-core/v5"
"github.com/v2fly/v2ray-core/v5/common"
@@ -58,6 +59,8 @@ type persistentMirrorTLSDialer struct {
explicitNonceCiphersuiteLookup *ciphersuiteLookuper
enrollmentConfirmationClient *mirrorenrollment.EnrollmentConfirmationClient
enrollmentServerIdentifier []byte
}
func (d *persistentMirrorTLSDialer) init(ctx context.Context, config *Config) error {
@@ -147,6 +150,7 @@ func (d *persistentMirrorTLSDialer) init(ctx context.Context, config *Config) er
if err != nil {
return newError("failed to derive enrollment server identifier").Base(err).AtError()
}
d.enrollmentServerIdentifier = enrollmentServerIdentifier
d.enrollmentConfirmationClient, err = mirrorenrollment.NewEnrollmentConfirmationClient(d.ctx, d.config.ConnectionEnrolment, enrollmentServerIdentifier)
if err != nil {
return newError("failed to create enrollment confirmation client").Base(err).AtError()
@@ -259,6 +263,12 @@ func (d *persistentMirrorTLSDialer) handleIncomingReadyConnection(conn internet.
func (d *persistentMirrorTLSDialer) Dial(ctx context.Context,
dest net.Destination, settings *internet.MemoryStreamConfig,
) (internet.Connection, error) {
if d.enrollmentServerIdentifier != nil && len(d.enrollmentServerIdentifier) > 0 {
if mirrorcommon.IsLoopbackProtectionEnabled(ctx, d.enrollmentServerIdentifier) {
return nil, newError("loopback protection: refusing to dial to self")
}
}
var recvConn net.Conn
select {
case conn := <-d.incomingConnections: