From 249b342de9e5f391ec8ef80a010bd0908c7b86f3 Mon Sep 17 00:00:00 2001 From: Steve Swor Date: Mon, 16 Aug 2021 14:02:28 +1000 Subject: [PATCH] Support connecting through a socks proxy --- README.md | 6 ++++++ docs/index.md | 27 +++++++++++++++++++++++++++ go.mod | 1 + redshift/config.go | 4 ++-- redshift/proxy_driver.go | 35 +++++++++++++++++++++++++++++++++++ templates/index.md.tmpl | 27 +++++++++++++++++++++++++++ 6 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 redshift/proxy_driver.go diff --git a/README.md b/README.md index a01ea73..9fd456e 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,12 @@ REDSHIFT_PASSWORD= make testacc ``` +If your cluster is only accessible from within the VPC, you can connect via a socks proxy: +```sh +ALL_PROXY=socks5[h]://[:@][:] +NO_PROXY=127.0.0.1,192.168.0.0/24,*.example.com,localhost +``` + ## Documentation Documentation is generated with diff --git a/docs/index.md b/docs/index.md index 9981231..3d3cdba 100644 --- a/docs/index.md +++ b/docs/index.md @@ -60,3 +60,30 @@ Optional: - **auto_create_user** (Boolean) Create a database user with the name specified for the user if one does not exist. - **db_groups** (Set of String) A list of the names of existing database groups that the user will join for the current session, in addition to any group memberships for an existing user. If not specified, a new user is added only to PUBLIC. - **duration_seconds** (Number) The number of seconds until the returned temporary password expires. + +## Proxy Support + +If your Redshift cluster is only accessible from within a VPC, you can use the `ALL_PROXY` (`all_proxy`) +and `NO_PROXY` (`no_proxy`) environment variables to configure the provider to access Redshift through +a SOCKS5 proxy + +``` +ALL_PROXY=socks5[h]://[:@][:] +NO_PROXY=#.... +``` + +### `ALL_PROXY` URL parameters +* scheme: can be either `socks5` or `socks5h`. The `h` is optional and [does not change the behavior](https://cs.opensource.google/go/x/net/+/3a7c4785:proxy/proxy.go;l=92). +* `socks5-username` - optional username for authenticating to the socks proxy +* `socks5-password` - optional password for authenticating to the socks proxy +* `socks5-hostname` - the socks proxy hostname or IP address +* `socks5-port` - optional port number for the socks proxy. Default port is [1080](https://cs.opensource.google/go/x/net/+/3a7c4785:proxy/proxy.go;l=96) + +### `NO_PROXY` Format + +`NO_PROXY`/`no_proxy` is an optional environment variable which bypasses `ALL_PROXY`/`all_proxy` +for specific hosts. This is a [comma-separated string, where each value is one of](https://cs.opensource.google/go/x/net/+/3a7c4785:proxy/per_host.go;l=92-96): +* IP address (e.g. `127.0.0.1`) +* CIDR range (e.g. `192.168.0.0/24`) +* zone (e.g. `*.example.com`) +* hostname (e.g. `localhost`) diff --git a/go.mod b/go.mod index df06430..9e077f3 100644 --- a/go.mod +++ b/go.mod @@ -10,4 +10,5 @@ require ( github.com/hashicorp/terraform-plugin-docs v0.4.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.6.1 github.com/lib/pq v1.10.2 + golang.org/x/net v0.0.0-20210326060303-6b1517762897 ) diff --git a/redshift/config.go b/redshift/config.go index 06a77d2..9519fc0 100644 --- a/redshift/config.go +++ b/redshift/config.go @@ -58,7 +58,7 @@ func (c *Client) Connect() (*DBConnection, error) { dsn := c.config.connStr(c.databaseName) conn, found := dbRegistry[dsn] if !found { - db, err := sql.Open("postgres", dsn) + db, err := sql.Open(proxyDriverName, dsn) if err != nil { return nil, fmt.Errorf("Error connecting to PostgreSQL server %s: %w", c.config.Host, err) } @@ -118,7 +118,7 @@ func (c *Config) Client() (*Client, error) { c.Port, c.Database) - db, err := sql.Open("postgres", conninfo) + db, err := sql.Open(proxyDriverName, conninfo) if err != nil { db.Close() return nil, err diff --git a/redshift/proxy_driver.go b/redshift/proxy_driver.go new file mode 100644 index 0000000..01aac2f --- /dev/null +++ b/redshift/proxy_driver.go @@ -0,0 +1,35 @@ +package redshift + +import ( + "context" + "database/sql" + "database/sql/driver" + "net" + "time" + + "github.com/lib/pq" + "golang.org/x/net/proxy" +) + +const proxyDriverName = "postgresql-proxy" + +type proxyDriver struct{} + +func (d proxyDriver) Open(name string) (driver.Conn, error) { + return pq.DialOpen(d, name) +} + +func (d proxyDriver) Dial(network, address string) (net.Conn, error) { + dialer := proxy.FromEnvironment() + return dialer.Dial(network, address) +} + +func (d proxyDriver) DialTimeout(network, address string, timeout time.Duration) (net.Conn, error) { + ctx, cancel := context.WithTimeout(context.TODO(), timeout) + defer cancel() + return proxy.Dial(ctx, network, address) +} + +func init() { + sql.Register(proxyDriverName, proxyDriver{}) +} diff --git a/templates/index.md.tmpl b/templates/index.md.tmpl index 54eda9c..75e0fe1 100644 --- a/templates/index.md.tmpl +++ b/templates/index.md.tmpl @@ -21,3 +21,30 @@ The Redshift provider provides configuration management resources for {{ tffile "examples/provider/provider_using_temporary_credentials.tf" }} {{ .SchemaMarkdown | trimspace }} + +## Proxy Support + +If your Redshift cluster is only accessible from within a VPC, you can use the `ALL_PROXY` (`all_proxy`) +and `NO_PROXY` (`no_proxy`) environment variables to configure the provider to access Redshift through +a SOCKS5 proxy + +``` +ALL_PROXY=socks5[h]://[:@][:] +NO_PROXY=#.... +``` + +### `ALL_PROXY` URL parameters +* scheme: can be either `socks5` or `socks5h`. The `h` is optional and [does not change the behavior](https://cs.opensource.google/go/x/net/+/3a7c4785:proxy/proxy.go;l=92). +* `socks5-username` - optional username for authenticating to the socks proxy +* `socks5-password` - optional password for authenticating to the socks proxy +* `socks5-hostname` - the socks proxy hostname or IP address +* `socks5-port` - optional port number for the socks proxy. Default port is [1080](https://cs.opensource.google/go/x/net/+/3a7c4785:proxy/proxy.go;l=96) + +### `NO_PROXY` Format + +`NO_PROXY`/`no_proxy` is an optional environment variable which bypasses `ALL_PROXY`/`all_proxy` +for specific hosts. This is a [comma-separated string, where each value is one of](https://cs.opensource.google/go/x/net/+/3a7c4785:proxy/per_host.go;l=92-96): +* IP address (e.g. `127.0.0.1`) +* CIDR range (e.g. `192.168.0.0/24`) +* zone (e.g. `*.example.com`) +* hostname (e.g. `localhost`)