jackc / tern

The SQL Fan's Migrator
MIT License
988 stars 71 forks source link

Tern is unable to find host accessible from SSH host #108

Open jackHedaya opened 1 month ago

jackHedaya commented 1 month ago

Hi

Hope all is well!

I'm running the following command:

 tern migrate --conn-string ${CONN_STR} --password ${PASSWORD} --migrations ./db/migrations --ssh-host ${BASTION_PUBLIC_IP} --ssh-user ec2-user

and am getting the following:

Unable to connect to PostgreSQL:
  failed to connect to `host=... user=... database=...`: hostname resolving error (lookup <hostname>: no such host)

I am able to SSH into the host and then perform the DNS lookup successfully.

I suspect tern is performing the DNS lookup locally (where the host is inaccessible) as opposed to on the ssh machine (where the host is accessible).

jackc commented 1 month ago

tern doesn't directly do the DNS lookup. It calls https://pkg.go.dev/golang.org/x/crypto/ssh#Client.Dial.

However, it's possible that the underlying pgx connection is the culprit. It tries to resolve DNS before dialing because one hostname may resolve into multiple IP addresses and we may do some sort of fallback with them. It tries to match psql / libpq behavior. I suspect the same thing would happen if manually tunneling with psql and openssh.

I'm not totally sure what the proper behavior would be. I guess it would be something like replacing https://pkg.go.dev/github.com/jackc/pgx/v5@v5.7.1/pgconn#Config the LookupFunc in the connection config with something that did the resolution remotely. But I'm not sure how to do a DNS lookup from the remote system.

jackHedaya commented 1 month ago

Hmm I'm also having trouble finding a method of performing the DNS lookup on the remote machine in an OS agnostic manner.

I did find a solution that works for my case:

Wondering if this could be generalized.

jackc commented 1 month ago

I don't know. It would seem that sshd is doing the lookup there. Not sure how to hook into that.

smainz commented 1 week ago

Why don't you use an environment var for the IP in your connect string and do the lookup via ssh like this:

IP_OF_DB_SERVER=$(ssh user@bastion dig +short <hostname_of_db_server> | head -n1)

This will do an IP lookup on the bastion host and store the result in IP_OF_DB_SERVER on your local workstation. Now this can be used to construct the connect string or even easier set --host ${IP_OF_DB_SERVER} --port ...