Skip to content
Closed

WIP #13

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
069883c
feat: gateway v2 scaffolding
sheensantoscapadngan Aug 29, 2025
1fb0a48
misc: updated proxy to start tls server instead of tcp
sheensantoscapadngan Aug 29, 2025
cda3ac3
misc: added full server certificate chain to proxy tls
sheensantoscapadngan Aug 29, 2025
6f7eda5
misc: added log
sheensantoscapadngan Aug 29, 2025
3369207
misc: updated proxy to fetch client pem chain
sheensantoscapadngan Aug 29, 2025
97b9d17
misc: added log point
sheensantoscapadngan Aug 29, 2025
ef24451
misc: added handshake forcing
sheensantoscapadngan Aug 29, 2025
a233a3f
misc: updated gateway to fetch client certificate chain
sheensantoscapadngan Aug 31, 2025
74db2f3
misc: set target host of proxy to gateway
sheensantoscapadngan Aug 31, 2025
36c069d
feat: added TCP and HTTP forward handling to gateway
sheensantoscapadngan Sep 1, 2025
f7ed054
feat: added auth injection for k8 and platform checks
sheensantoscapadngan Sep 1, 2025
dc7a438
feat: added heartbeat
sheensantoscapadngan Sep 2, 2025
2dbb176
feat: added systemd support
sheensantoscapadngan Sep 2, 2025
085de6d
misc: added proxy name validation
sheensantoscapadngan Sep 2, 2025
9909141
misc: added proxy cert auto-renewal
sheensantoscapadngan Sep 2, 2025
6d0a021
misc: updated proxy tls server handling for cert renewal
sheensantoscapadngan Sep 2, 2025
b152338
misc: corrected client handling
sheensantoscapadngan Sep 2, 2025
3bcf34c
misc: addeed tls connection accept log
sheensantoscapadngan Sep 2, 2025
9ccf30b
misc: add connection deadline for unauthenticated requests
sheensantoscapadngan Sep 2, 2025
d39ef05
misc: finalized cert renewal interval to 10 days
sheensantoscapadngan Sep 2, 2025
6065584
misc: add cert renewal to gateway server
sheensantoscapadngan Sep 2, 2025
4e6ee38
misc: used non-standard port for proxy TLS
sheensantoscapadngan Sep 3, 2025
8eaf2a5
misc: improved security posture of proxy server
sheensantoscapadngan Sep 3, 2025
ce41396
misc: added sending of error message when multiple gateway is detected
sheensantoscapadngan Sep 3, 2025
c51d31f
Revert "misc: added sending of error message when multiple gateway is…
sheensantoscapadngan Sep 3, 2025
21d61c1
misc: only close new connection for duplicate gateway
sheensantoscapadngan Sep 3, 2025
7d2276f
misc: decreased tls deadline
sheensantoscapadngan Sep 3, 2025
e5a426d
misc: addressed greptile
sheensantoscapadngan Sep 3, 2025
fcdc145
misc: removed proxy auth logging
sheensantoscapadngan Sep 3, 2025
fc62acd
misc: updated gateway logs
sheensantoscapadngan Sep 3, 2025
144d4e7
misc: updated to use relay and connector terminologies
sheensantoscapadngan Sep 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions packages/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,11 @@ const (
operationCallRegisterGatewayIdentityV1 = "CallRegisterGatewayIdentityV1"
operationCallExchangeRelayCertV1 = "CallExchangeRelayCertV1"
operationCallGatewayHeartBeatV1 = "CallGatewayHeartBeatV1"
operationCallConnectorHeartBeat = "CallConnectorHeartBeat"
operationCallBootstrapInstance = "CallBootstrapInstance"
operationCallRegisterInstanceRelay = "CallRegisterInstanceRelay"
operationCallRegisterOrgRelay = "CallRegisterOrgRelay"
operationCallRegisterGateway = "CallRegisterGateway"
)

func CallGetEncryptedWorkspaceKey(httpClient *resty.Client, request GetEncryptedWorkspaceKeyRequest) (GetEncryptedWorkspaceKeyResponse, error) {
Expand Down Expand Up @@ -652,6 +656,23 @@ func CallGatewayHeartBeatV1(httpClient *resty.Client) error {
return nil
}

func CallConnectorHeartBeat(httpClient *resty.Client) error {
response, err := httpClient.
R().
SetHeader("User-Agent", USER_AGENT).
Post(fmt.Sprintf("%v/v1/connectors/heartbeat", config.INFISICAL_URL))

if err != nil {
return NewGenericRequestError(operationCallConnectorHeartBeat, err)
}

if response.IsError() {
return NewAPIErrorWithResponse(operationCallConnectorHeartBeat, response, nil)
}

return nil
}

func CallBootstrapInstance(httpClient *resty.Client, request BootstrapInstanceRequest) (BootstrapInstanceResponse, error) {
var resBody BootstrapInstanceResponse
response, err := httpClient.
Expand All @@ -671,3 +692,63 @@ func CallBootstrapInstance(httpClient *resty.Client, request BootstrapInstanceRe

return resBody, nil
}

func CallRegisterInstanceRelay(httpClient *resty.Client, request RegisterRelayRequest) (RegisterRelayResponse, error) {
var resBody RegisterRelayResponse
response, err := httpClient.
R().
SetResult(&resBody).
SetHeader("User-Agent", USER_AGENT).
SetBody(request).
Post(fmt.Sprintf("%v/v1/relays/register-instance-relay", config.INFISICAL_URL))

if err != nil {
return RegisterRelayResponse{}, NewGenericRequestError(operationCallRegisterInstanceRelay, err)
}

if response.IsError() {
return RegisterRelayResponse{}, NewAPIErrorWithResponse(operationCallRegisterInstanceRelay, response, nil)
}

return resBody, nil
}

func CallRegisterRelay(httpClient *resty.Client, request RegisterRelayRequest) (RegisterRelayResponse, error) {
var resBody RegisterRelayResponse
response, err := httpClient.
R().
SetResult(&resBody).
SetHeader("User-Agent", USER_AGENT).
SetBody(request).
Post(fmt.Sprintf("%v/v1/relays/register-org-relay", config.INFISICAL_URL))

if err != nil {
return RegisterRelayResponse{}, NewGenericRequestError(operationCallRegisterOrgRelay, err)
}

if response.IsError() {
return RegisterRelayResponse{}, NewAPIErrorWithResponse(operationCallRegisterOrgRelay, response, nil)
}

return resBody, nil
}

func CallRegisterConnector(httpClient *resty.Client, request RegisterConnectorRequest) (RegisterConnectorResponse, error) {
var resBody RegisterConnectorResponse
response, err := httpClient.
R().
SetResult(&resBody).
SetHeader("User-Agent", USER_AGENT).
SetBody(request).
Post(fmt.Sprintf("%v/v1/connectors", config.INFISICAL_URL))

if err != nil {
return RegisterConnectorResponse{}, NewGenericRequestError(operationCallRegisterGateway, err)
}

if response.IsError() {
return RegisterConnectorResponse{}, NewAPIErrorWithResponse(operationCallRegisterGateway, response, nil)
}

return resBody, nil
}
38 changes: 38 additions & 0 deletions packages/api/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -703,3 +703,41 @@ type BootstrapUser struct {
Username string `json:"username"`
SuperAdmin bool `json:"superAdmin"`
}

type RegisterRelayRequest struct {
IP string `json:"ip"`
Name string `json:"name"`
}

type RegisterRelayResponse struct {
PKI struct {
ServerCertificate string `json:"serverCertificate"`
ServerPrivateKey string `json:"serverPrivateKey"`
ClientCertificateChain string `json:"clientCertificateChain"`
} `json:"pki"`
SSH struct {
ServerCertificate string `json:"serverCertificate"`
ServerPrivateKey string `json:"serverPrivateKey"`
ClientCAPublicKey string `json:"clientCAPublicKey"`
} `json:"ssh"`
}

type RegisterConnectorRequest struct {
RelayName string `json:"relayName"`
Name string `json:"name"`
}

type RegisterConnectorResponse struct {
ConnectorID string `json:"connectorId"`
RelayIP string `json:"relayIp"`
PKI struct {
ServerCertificate string `json:"serverCertificate"`
ServerPrivateKey string `json:"serverPrivateKey"`
ClientCertificateChain string `json:"clientCertificateChain"`
} `json:"pki"`
SSH struct {
ClientCertificate string `json:"clientCertificate"`
ClientPrivateKey string `json:"clientPrivateKey"`
ServerCAPublicKey string `json:"serverCAPublicKey"`
} `json:"ssh"`
}
222 changes: 222 additions & 0 deletions packages/cmd/connector.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
package cmd

import (
"context"
"errors"
"fmt"
"os"
"os/signal"
"runtime"
"sync/atomic"
"syscall"
"time"

"github.com/Infisical/infisical-merge/packages/connector"
"github.com/Infisical/infisical-merge/packages/util"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
)

var connectorCmd = &cobra.Command{
Use: "connector",
Short: "Connector-related commands",
Long: "Connector-related commands for Infisical",
}

var connectorStartCmd = &cobra.Command{
Use: "start",
Short: "Start the Infisical connector component",
Long: "Start the Infisical connector component. Use 'connector install' to set up the systemd service.",
Example: "infisical connector start --relay=us-west-1 --name=my-connector --token=<token>",
DisableFlagsInUseLine: true,
Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) {

relayName, err := util.GetCmdFlagOrEnv(cmd, "relay", []string{connector.RELAY_NAME_ENV_NAME})
if err != nil {
util.HandleError(err, fmt.Sprintf("unable to get relay flag or %s env", connector.RELAY_NAME_ENV_NAME))
}

connectorName, err := util.GetCmdFlagOrEnv(cmd, "name", []string{connector.CONNECTOR_NAME_ENV_NAME})
if err != nil {
util.HandleError(err, fmt.Sprintf("unable to get name flag or %s env", connector.CONNECTOR_NAME_ENV_NAME))
}

connectorInstance, err := connector.NewConnector(&connector.ConnectorConfig{
Name: connectorName,
RelayName: relayName,
ReconnectDelay: 10 * time.Second,
})

if err != nil {
util.HandleError(err, "unable to create connector instance")
}

infisicalClient, cancelSdk, err := getInfisicalSdkInstance(cmd)
if err != nil {
util.HandleError(err, "unable to get infisical client")
}
defer cancelSdk()

var accessToken atomic.Value
accessToken.Store(infisicalClient.Auth().GetAccessToken())

if accessToken.Load().(string) == "" {
util.HandleError(errors.New("no access token found"))
}

connectorInstance.SetToken(accessToken.Load().(string))

sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)

ctx, cancelCmd := context.WithCancel(cmd.Context())
defer cancelCmd()

go func() {
<-sigCh
log.Info().Msg("Received shutdown signal, shutting down connector...")
cancelCmd()
cancelSdk()

// Give graceful shutdown 10 seconds, then force exit on second signal
select {
case <-sigCh:
log.Warn().Msg("Second signal received, force exit triggered")
os.Exit(1)
case <-time.After(10 * time.Second):
log.Info().Msg("Graceful shutdown completed")
os.Exit(0)
}
}()

// Token refresh goroutine - runs every 10 seconds
go func() {
tokenRefreshTicker := time.NewTicker(10 * time.Second)
defer tokenRefreshTicker.Stop()

for {
select {
case <-tokenRefreshTicker.C:
if ctx.Err() != nil {
return
}

newToken := infisicalClient.Auth().GetAccessToken()
if newToken != "" && newToken != accessToken.Load().(string) {
accessToken.Store(newToken)
connectorInstance.SetToken(newToken)
}

case <-ctx.Done():
return
}
}
}()

err = connectorInstance.Start(ctx)
if err != nil {
util.HandleError(err, "unable to start connector instance")
}

},
}

var connectorInstallCmd = &cobra.Command{
Use: "install",
Short: "Install and enable systemd service for the connector (requires sudo)",
Long: "Install and enable systemd service for the connector. Must be run with sudo on Linux.",
Example: "sudo infisical connector install --token=<token> --domain=<domain> --name=<name> --relay=<relay-name>",
DisableFlagsInUseLine: true,
Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) {
if runtime.GOOS != "linux" {
util.HandleError(fmt.Errorf("systemd service installation is only supported on Linux"))
}

if os.Geteuid() != 0 {
util.HandleError(fmt.Errorf("systemd service installation requires root/sudo privileges"))
}

token, err := util.GetInfisicalToken(cmd)
if err != nil {
util.HandleError(err, "Unable to parse flag")
}

if token == nil {
util.HandleError(errors.New("Token not found"))
}

domain, err := cmd.Flags().GetString("domain")
if err != nil {
util.HandleError(err, "Unable to parse domain flag")
}

connectorName, err := cmd.Flags().GetString("name")
if err != nil {
util.HandleError(err, "Unable to parse name flag")
}
if connectorName == "" {
util.HandleError(errors.New("Connector name is required"))
}

relayName, err := cmd.Flags().GetString("relay")
if err != nil {
util.HandleError(err, "Unable to parse relay flag")
}
if relayName == "" {
util.HandleError(errors.New("Relay name is required"))
}

err = connector.InstallConnectorSystemdService(token.Token, domain, connectorName, relayName)
if err != nil {
util.HandleError(err, "Unable to install systemd service")
}
},
}

var connectorUninstallCmd = &cobra.Command{
Use: "uninstall",
Short: "Uninstall and remove systemd service for the connector (requires sudo)",
Long: "Uninstall and remove systemd service for the connector. Must be run with sudo on Linux.",
Example: "sudo infisical connector uninstall",
DisableFlagsInUseLine: true,
Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) {
if runtime.GOOS != "linux" {
util.HandleError(fmt.Errorf("systemd service installation is only supported on Linux"))
}

if os.Geteuid() != 0 {
util.HandleError(fmt.Errorf("systemd service installation requires root/sudo privileges"))
}

if err := connector.UninstallConnectorSystemdService(); err != nil {
util.HandleError(err, "Failed to uninstall systemd service")
}
},
}

func init() {
connectorStartCmd.Flags().String("relay", "", "The name of the relay to connect to")
connectorStartCmd.Flags().String("name", "", "The name of the connector")
connectorStartCmd.Flags().String("token", "", "connect with Infisical using machine identity access token. if not provided, you must set the auth-method flag")
connectorStartCmd.Flags().String("auth-method", "", "login method [universal-auth, kubernetes, azure, gcp-id-token, gcp-iam, aws-iam, oidc-auth]. if not provided, you must set the token flag")
connectorStartCmd.Flags().String("client-id", "", "client id for universal auth")
connectorStartCmd.Flags().String("client-secret", "", "client secret for universal auth")
connectorStartCmd.Flags().String("machine-identity-id", "", "machine identity id for kubernetes, azure, gcp-id-token, gcp-iam, and aws-iam auth methods")
connectorStartCmd.Flags().String("service-account-token-path", "", "service account token path for kubernetes auth")
connectorStartCmd.Flags().String("service-account-key-file-path", "", "service account key file path for GCP IAM auth")
connectorStartCmd.Flags().String("jwt", "", "JWT for jwt-based auth methods [oidc-auth, jwt-auth]")

connectorInstallCmd.Flags().String("token", "", "Connect with Infisical using machine identity access token")
connectorInstallCmd.Flags().String("domain", "", "Domain of your self-hosted Infisical instance")
connectorInstallCmd.Flags().String("name", "", "The name of the connector")
connectorInstallCmd.Flags().String("relay", "", "The name of the relay")

connectorCmd.AddCommand(connectorStartCmd)
connectorCmd.AddCommand(connectorInstallCmd)
connectorCmd.AddCommand(connectorUninstallCmd)

rootCmd.AddCommand(connectorCmd)
}
Loading
Loading