jackc / pgx

PostgreSQL driver and toolkit for Go

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Connecting via SSH fails to resolve host

madisonchamberlain opened this issue · comments

Describe the bug
When connecting to redshift with the pgx driver, if we use a ssh tunnel, but specify the hostname of the connection as a private DNS name (resolvable on ssh tunnel host, but not resolvable on the wider internet), the connection attempts will fail with
lookup ****.*****.private (host) on 10.47.240.10:53: (ssh host + port) no such host. This is not the case if we use the standard dbSql library to connect.

To Reproduce

Steps to reproduce the behavior:
create a route 53 hosted zone (e.g. name.private), for some vpc-123abc, and add a host record pointing to a redshift IP, e.g. redshift.name.private → 54.151.2.2

spin up an ec2 host in the above vpc, validate you can resolve redshift.name.private.

Use these credentials to connect to redshift over pgx via connectionConfig like this

type tunnel struct {
	config *pgsshConfig
	client *ssh.Client
}

func() connectionFunction() {
	sshPort := uint16(22)
	if config.SSHTunnel.Port != 0 {
		sshPort = config.SSHTunnel.Port
	}
	tunnelHost := fmt.Sprintf("%s:%s", config.SSHTunnel.Host, fmt.Sprint(sshPort))
	tunnelConfig := ssh.ClientConfig{
		User:            utils.SSHTunnelUser(),
		Auth:            getAuthMethods(),
		HostKeyCallback: knownhosts.CertChecker.HostKeyFallback,
		Timeout:         time.Duration(config.MaxTimeoutSecs * int64(time.Second)),
	}
	tunnel := tunnel{config: &pgsshConfig{tunnelHost, tunnelConfig, mkPgxConnStr(config, tz)}}

	pgxCfg, err := pgxpool.ParseConfig(mkPgxConnStr(config, tz))
	if err != nil {
		return nil, err
	}

	sshcon, err := ssh.Dial("tcp", tunnel.config.tunnelHost, &tunnel.config.tunnelConfig)
	if err != nil {		
               return nil, err
	}


	pgxCfg.ConnConfig.DialFunc = func(ctx context.Context, network, addr string) (net.Conn, error) {
		conn, err := sshcon.Dial(network, addr)
		return conn, err
	}

	pool, err := pgxpool.ConnectConfig(ctx, pgxCfg)
        if err != nil {
	        // This is where we see the error 
        }
   ...
}

Expected behavior
The connection should work without throwing any errors

Actual behavior
No such host error

Version

  • Go: -> go version go1.20.5 darwin/amd64
  • PostgreSQL: `NA
  • pgx: github.com/jackc/pgx/v4 v4.18.1

Additional context
From looking at the lookupHost code in lookup_unix.go, it looks like the issue is that pgx tries to resolve the hostname of the server well before it tries to dial ssh which wont work if a caller would only be able to resolve the IP on the ssh host.

Related: #1661

Thank you!

From looking at the lookupHost code in lookup_unix.go, it looks like the issue is that pgx tries to resolve the hostname of the server well before it tries to dial ssh which wont work if a caller would only be able to resolve the IP on the ssh host.

That would do it. pgx doesn't actually know anything about the SSH connection. It is simply using a custom DialFunc. I believe the solution would be to use a custom LookupFunc in addition to the custom DialFunc. LookupFunc allows custom DNS resolution like DialFunc allows custom dialing. That LookupFunc could do the DNS resolution through the existing SSH connection (not exactly sure how, but it should be doable).