diff --git a/cli/config/example-infisical-relay.yaml b/cli/config/example-infisical-relay.yaml new file mode 100644 index 000000000..c913ed757 --- /dev/null +++ b/cli/config/example-infisical-relay.yaml @@ -0,0 +1,8 @@ +public_ip: 127.0.0.1 +auth_secret: example-auth-secret +realm: infisical.org +# set port 5349 for tls +# port: 5349 +# tls_private_key_path: /full-path +# tls_ca_path: /full-path +# tls_cert_path: /full-path diff --git a/cli/go.mod b/cli/go.mod index 66c4e61f8..53f348807 100644 --- a/cli/go.mod +++ b/cli/go.mod @@ -28,8 +28,9 @@ require ( github.com/rs/zerolog v1.26.1 github.com/spf13/cobra v1.6.1 github.com/spf13/viper v1.8.1 - github.com/stretchr/testify v1.9.0 + github.com/stretchr/testify v1.10.0 golang.org/x/crypto v0.35.0 + golang.org/x/sys v0.30.0 golang.org/x/term v0.29.0 gopkg.in/yaml.v2 v2.4.0 ) @@ -115,7 +116,6 @@ require ( golang.org/x/net v0.35.0 // indirect golang.org/x/oauth2 v0.21.0 // indirect golang.org/x/sync v0.11.0 // indirect - golang.org/x/sys v0.30.0 // indirect golang.org/x/text v0.22.0 // indirect golang.org/x/time v0.6.0 // indirect golang.org/x/tools v0.30.0 // indirect @@ -139,3 +139,5 @@ require ( ) replace github.com/zalando/go-keyring => github.com/Infisical/go-keyring v1.0.2 + +replace github.com/pion/turn/v4 => github.com/Infisical/turn/v4 v4.0.1 diff --git a/cli/go.sum b/cli/go.sum index e274c0d38..d87cc825a 100644 --- a/cli/go.sum +++ b/cli/go.sum @@ -49,6 +49,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Infisical/go-keyring v1.0.2 h1:dWOkI/pB/7RocfSJgGXbXxLDcVYsdslgjEPmVhb+nl8= github.com/Infisical/go-keyring v1.0.2/go.mod h1:LWOnn/sw9FxDW/0VY+jHFAfOFEe03xmwBVSfJnBowto= +github.com/Infisical/turn/v4 v4.0.1 h1:omdelNsnFfzS5cu86W5OBR68by68a8sva4ogR0lQQnw= +github.com/Infisical/turn/v4 v4.0.1/go.mod h1:pMMKP/ieNAG/fN5cZiN4SDuyKsXtNTr0ccN7IToA1zs= github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0= github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= @@ -365,8 +367,6 @@ github.com/pion/stun/v3 v3.0.0 h1:4h1gwhWLWuZWOJIJR9s2ferRO+W3zA/b6ijOI6mKzUw= github.com/pion/stun/v3 v3.0.0/go.mod h1:HvCN8txt8mwi4FBvS3EmDghW6aQJ24T+y+1TKjB5jyU= github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0= github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo= -github.com/pion/turn/v4 v4.0.0 h1:qxplo3Rxa9Yg1xXDxxH8xaqcyGUtbHYw4QSCvmFWvhM= -github.com/pion/turn/v4 v4.0.0/go.mod h1:MuPDkm15nYSklKpN8vWJ9W2M0PlyQZqYt1McGuxG7mA= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -425,8 +425,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= diff --git a/cli/packages/cmd/gateway.go b/cli/packages/cmd/gateway.go index 760fd95c4..d796ffede 100644 --- a/cli/packages/cmd/gateway.go +++ b/cli/packages/cmd/gateway.go @@ -137,15 +137,10 @@ var gatewayRelayCmd = &cobra.Command{ } func init() { - gatewayCmd.SetHelpFunc(func(command *cobra.Command, strings []string) { - command.Flags().MarkHidden("domain") - command.Parent().HelpFunc()(command, strings) - }) gatewayCmd.Flags().String("token", "", "Connect with Infisical using machine identity access token") gatewayRelayCmd.Flags().String("config", "", "Relay config yaml file path") gatewayCmd.AddCommand(gatewayRelayCmd) - rootCmd.AddCommand(gatewayCmd) } diff --git a/cli/packages/gateway/gateway.go b/cli/packages/gateway/gateway.go index 43340df38..248a9dc7b 100644 --- a/cli/packages/gateway/gateway.go +++ b/cli/packages/gateway/gateway.go @@ -176,7 +176,7 @@ func (g *Gateway) Listen(ctx context.Context) error { KeepAlivePeriod: 2 * time.Second, } - g.registerRelayIsActive(ctx, relayUdpConnection.LocalAddr().String(), errCh) + g.registerRelayIsActive(ctx, errCh) quicListener, err := quic.Listen(relayUdpConnection, tlsConfig, quicConfig) if err != nil { return fmt.Errorf("Failed to listen for QUIC: %w", err) @@ -320,39 +320,49 @@ func (g *Gateway) createPermissionForStaticIps(staticIps string) error { return nil } -func (g *Gateway) registerRelayIsActive(ctx context.Context, relayAddress string, errCh chan error) error { - ticker := time.NewTicker(10 * time.Second) +func (g *Gateway) registerRelayIsActive(ctx context.Context, errCh chan error) error { + ticker := time.NewTicker(15 * time.Second) maxFailures := 3 failures := 0 + log.Info().Msg("Starting relay connection health check") + go func() { - time.Sleep(2 * time.Second) + time.Sleep(5 * time.Second) for { select { case <-ctx.Done(): + log.Info().Msg("Stopping relay connection health check") return case <-ticker.C: - // Configure TLS to skip verification - tlsConfig := &tls.Config{ - InsecureSkipVerify: true, - NextProtos: []string{"infisical-gateway"}, - } - quicConfig := &quic.Config{ - EnableDatagrams: true, - } func() { - checkCtx, cancel := context.WithTimeout(ctx, 3*time.Second) - defer cancel() - conn, err := quic.DialAddr(checkCtx, relayAddress, tlsConfig, quicConfig) - if err != nil { + log.Debug().Msg("Performing relay connection health check") + + if g.client == nil { failures++ - log.Warn().Err(err).Int("failures", failures).Msg("Relay connection check failed") + log.Warn().Int("failures", failures).Msg("TURN client is nil") + if failures >= maxFailures { + errCh <- fmt.Errorf("relay connection check failed: TURN client is nil") + } + return + } + + // we try to refresh permissions - this is a lightweight operation + // that will fail immediately if the UDP connection is broken. good for health check + log.Debug().Msg("Refreshing TURN permissions to verify connection") + if err := g.createPermissionForStaticIps(g.config.InfisicalStaticIp); err != nil { + failures++ + log.Warn().Err(err).Int("failures", failures).Msg("Failed to refresh TURN permissions") if failures >= maxFailures { errCh <- fmt.Errorf("relay connection check failed: %w", err) } + return } - if conn != nil { - conn.CloseWithError(0, "closed") + + log.Debug().Msg("Successfully refreshed TURN permissions - connection is healthy") + if failures > 0 { + log.Info().Int("previous_failures", failures).Msg("Relay connection restored") + failures = 0 } }() } diff --git a/cli/packages/gateway/relay.go b/cli/packages/gateway/relay.go index 0db5fd387..bbd1332a4 100644 --- a/cli/packages/gateway/relay.go +++ b/cli/packages/gateway/relay.go @@ -1,3 +1,6 @@ +//go:build !windows +// +build !windows + package gateway import ( diff --git a/cli/packages/gateway/relay_windows.go b/cli/packages/gateway/relay_windows.go new file mode 100644 index 000000000..f3bf89bd0 --- /dev/null +++ b/cli/packages/gateway/relay_windows.go @@ -0,0 +1,37 @@ +//go:build windows +// +build windows + +package gateway + +import ( + "errors" +) + +var ( + errMissingTlsCert = errors.New("Missing TLS files") + errWindowsNotSupported = errors.New("Relay is not supported on Windows") +) + +type GatewayRelay struct { + Config *GatewayRelayConfig +} + +type GatewayRelayConfig struct { + PublicIP string + Port int + Realm string + AuthSecret string + RelayMinPort uint16 + RelayMaxPort uint16 + TlsCertPath string + TlsPrivateKeyPath string + TlsCaPath string +} + +func NewGatewayRelay(configFilePath string) (*GatewayRelay, error) { + return nil, errWindowsNotSupported +} + +func (g *GatewayRelay) Run() error { + return errWindowsNotSupported +}