Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SSH Tunnel support #246

Merged
merged 11 commits into from
Jan 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,6 @@ dblab
assets/.DS_Store

.DS_Store

# sshdb pkg testing
pkg/sshdb/testdata/known_hosts
66 changes: 54 additions & 12 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,19 +75,61 @@ Run the integration test with make too, but make sure the database containers ar
make int-test
```

This runs the tests. You can check all the options with `help` command.
## SSH Tunnel

There is an special compose file that spins up an ssh server, to test the ssh tunnel and work with it. The compose file also provides postgres and mysql containers but they are not exposed to the localhost. The sshd server is the intermediary between the client and those containers.

Run the command below to spin up the ssh server and the databases containers behind it.

```bash
make up-ssh
```

To connect to the databases, the make file provides a new series of targets addding the ssh related parameters:

```bash
make run-ssh
```

The command above, is the equivalent of this command:

```bash
dblab --host postgres --user postgres --pass password --schema public --ssl disable --port 5432 --driver postgres --limit 50 --ssh-host localhost --ssh-port 2222 --ssh-user root --ssh-pass root
```

You can check all the options with `help` command.

```bash
Usage:
test Runs the tests
unit-test Runs the tests with the short flag
int-test Runs the integration tests
linter Runs the colangci-lint command
test-all Runs the integration testing bash script with different database docker image versions
docker-build Builds de Docker image
build Builds the Go program
run Runs the application
up Runs all the containers listed in the docker-compose.yml file
down Shut down all the containers listed in the docker-compose.yml file
help Prints this help message
test Runs the tests
unit-test Runs the tests with the short flag
int-test Runs the integration tests
linter Runs the golangci-lint command
test-all Runs the integration testing bash script with different database docker image versions
docker-build Builds de Docker image
build Builds the Go program
run Runs the application
run-ssh Runs the application through a ssh tunnel
run-ssh-key Runs the application through a ssh tunnel using a private key file
run-mysql Runs the application with a connection to mysql
run-mysql-ssh Runs the application through a ssh tunnel
run-mysql-socket Runs the application with a connection to mysql through a socket file. In this example the socke file is located in /var/lib/mysql/mysql.sock.
run-postgres-socket Runs the application with a connection to mysql through a socket file. In this example the socke file is located in /var/lib/mysql/mysql.sock.
run-oracle Runs the application making a connection to the Oracle database
run-sql-server Runs the application making a connection to the SQL Server database
run-mysql-socket-url Runs the application with a connection to mysql through a socket file. In this example the socke file is located in /var/lib/mysql/mysql.sock.
run-sqlite3 Runs the application with a connection to sqlite3
run-sqlite3-url Runs the application with a connection string to sqlite3
run-url Runs the app passing the url as parameter
run-url-ssh Runs the application through a ssh tunnel providing the url as parameter
run-mysql-url Runs the app passing the url as parameter
run-mysql-url-ssh Runs the app passing the url as parameter through a ssh tunnel providing the url as parameter
run-config Runs the client using the config file.
up Runs all the containers listed in the docker-compose.yml file
up-ssh Runs all the containers listed in the docker-compose.ssh.yml file to test the ssh tunnel
down Shut down all the containers listed in the docker-compose.yml file
stop-ssh Shut down all the containers listed in the docker-compose.ssh.yml file
form Runs the application with no arguments
create Creates golang-migrate migration files
help Prints this help message
```
36 changes: 35 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,25 @@ build:
run: build
./dblab --host localhost --user postgres --db users --pass password --schema public --ssl disable --port 5432 --driver postgres --limit 50

.PHONY: run-ssh
## run-ssh: Runs the application through a ssh tunnel
run-ssh: build
./dblab --host postgres --user postgres --pass password --schema public --ssl disable --port 5432 --driver postgres --limit 50 --ssh-host localhost --ssh-port 2222 --ssh-user root --ssh-pass root

.PHONY: run-ssh-key
## run-ssh-key: Runs the application through a ssh tunnel using a private key file
run-ssh-key: build
./dblab --host postgres --user postgres --pass password --schema public --ssl disable --port 5432 --driver postgres --limit 50 --ssh-host localhost --ssh-port 2222 --ssh-user root --ssh-key my_ssh_key

.PHONY: run-mysql
## run-mysql: Runs the application with a connection to mysql
run-mysql: build
./dblab --host localhost --user myuser --db mydb --pass 5@klkbN#ABC --ssl enable --port 3306 --driver mysql

.PHONY: run-mysql-ssh
## run-mysql-ssh: Runs the application through a ssh tunnel
run-mysql-ssh: build
./dblab --host mysql --user myuser --db mydb --pass 5@klkbN#ABC --ssl enable --port 3306 --driver mysql --limit 50 --ssh-host localhost --ssh-port 2222 --ssh-user root --ssh-pass root

.PHONY: run-mysql-socket
## run-mysql-socket: Runs the application with a connection to mysql through a socket file. In this example the socke file is located in /var/lib/mysql/mysql.sock.
Expand Down Expand Up @@ -87,10 +101,20 @@ run-sqlite3-url: build
run-url: build
./dblab --url postgres://postgres:password@localhost:5432/users?sslmode=disable

.PHONY: run-url-ssh
## run-url-ssh: Runs the application through a ssh tunnel providing the url as parameter
run-url-ssh: build
./dblab --url postgres://postgres:password@postgres:5432/users?sslmode=disable --schema public --ssh-host localhost --ssh-port 2222 --ssh-user root --ssh-pass root

.PHONY: run-mysql-url
## run-mysql-url: Runs the app passing the url as parameter
run-mysql-url: build
./dblab --url "mysql://myuser:5@klkbN#ABC@tcp(localhost:3306)/mydb"
./dblab --url "mysql://myuser:5@klkbN#ABC@tcp(localhost:3306)/mydb"

.PHONY: run-mysql-url-ssh
## run-mysql-url-ssh: Runs the app passing the url as parameter through a ssh tunnel providing the url as parameter
run-mysql-url-ssh: build
./dblab --url "mysql://myuser:5@klkbN#ABC@mysql+tcp(mysql:3306)/mydb" --driver mysql --ssh-host localhost --ssh-port 2222 --ssh-user root --ssh-pass root

.PHONY: run-config
## run-config: Runs the client using the config file.
Expand All @@ -102,11 +126,21 @@ run-config: build
up:
docker compose up --build -d

.PHONY: up-ssh
## up-ssh: Runs all the containers listed in the docker-compose.ssh.yml file to test the ssh tunnel
up-ssh:
docker compose -f docker-compose.ssh.yml up -d

.PHONY: down
## down: Shut down all the containers listed in the docker-compose.yml file
down:
docker compose down

.PHONY: stop-ssh
## stop-ssh: Shut down all the containers listed in the docker-compose.ssh.yml file
stop-ssh:
docker compose -f docker-compose.ssh.yml down

.PHONY: form
## form: Runs the application with no arguments
form: build
Expand Down
72 changes: 72 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ __Interactive client for PostgreSQL, MySQL, SQLite3, Oracle and SQL Server.__
- [Automated installation/update](#automated-installationupdate)
- [Help Command](#help)
- [Usage](#usage)
- [SSH Tunnel](#ssh-tunnel)
- [Configuration](#configuration)
- [Navigation](#navigation)
- [Key Bindings](#key-bindings)
Expand Down Expand Up @@ -103,6 +104,12 @@ Flags:
--port string Server port
--schema string Database schema (postgres only)
--socket string Path to a Unix socket file
--ssh-host string SSH Server Hostname/IP
--ssh-key string File with private key for SSH authentication
--ssh-key-pass string Supports connections with protected private keys with passphrase
--ssh-pass string SSH Password (Empty string for no password)
--ssh-port string SSH Port
--ssh-user string SSH User
--ssl string SSL mode
--ssl-verify string [enable|disable] or [true|false] enable ssl verify for the server
--sslcert string This parameter specifies the file name of the client SSL certificate, replacing the default ~/.postgresql/postgresql.crt
Expand Down Expand Up @@ -170,6 +177,59 @@ Now, it is possible to ensure SSL connections with `PostgreSQL` databases. SSL r
dblab --host db-postgresql-nyc3-56456-do-user-foo-0.fake.db.ondigitalocean.com --user myuser --db users --pass password --schema myschema --port 5432 --driver postgres --limit 50 --ssl require --sslrootcert ~/Downloads/foo.crt
```

### SSH Tunnel

Now, it's possible to connect to Postgres or MySQL (more to come later) databases on a server via SSH using password or a ssh key files.

To do so, 6 new flags has been added to the dblab command:

| Flag | Description |
|----------------------|-------------------------------------------------------------------|
| --ssh-host | SSH Server Hostname/IP |
| --ssh-port | SSH Port |
| --ssh-user | SSH User |
| --ssh-pass | SSH Password (Empty string for no password) |
| --ssh-key | File with private key for SSH authentication |
| --ssh-key-pass | Passphrase for protected private key files |

#### Examples

Postgres connection via ssh tunnel using password:

```{ .sh .copy }
dblab --host localhost --user postgres --pass password --schema public --ssl disable --port 5432 --driver postgres --limit 50 --ssh-host example.com --ssh-port 22 --ssh-user root --ssh-pass root
```

Postgres connection via ssh tunnel using ssh private key file:

```{ .sh .copy }
dblab --host localhost --user postgres --pass password --schema public --ssl disable --port 5432 --driver postgres --limit 50 --ssh-host example.com --ssh-port 22 --ssh-user root --ssh-key my_ssh_key --ssh-key-pass password
```

Postgres connection using the url parameter via ssh tunnel using password:

```{ .sh .copy }
dblab --url postgres://postgres:password@localhost:5432/users?sslmode=disable --schema public --ssh-host example.com --ssh-port 22 --ssh-user root --ssh-pass root
```

MySQL connection via ssh tunnel using password:

```{ .sh .copy }
dblab --host localhost --user myuser --db mydb --pass 5@klkbN#ABC --ssl enable --port 3306 --driver mysql --limit 50 --ssh-host example.com --ssh-port 22 --ssh-user root --ssh-pass root
```

MySQL connection via ssh tunnel using ssh private key file:

```{ .sh .copy }
dblab --host localhost --user postgres --pass password --ssl enable --port 3306 --driver mysql --limit 50 --ssh-host example.com --ssh-port 22 --ssh-user root --ssh-key my_ssh_key --ssh-key-pass passphrase
```

MySQL connection using the url parameter via ssh tunnel using password:

```{ .sh .copy }
dblab --url "mysql://myuser:5@klkbN#ABC@mysql+tcp(localhost:3306)/mydb" --driver mysql --ssh-host example.com --ssh-port 22 --ssh-user root --ssh-pass root
```

### Configuration

Enter previous flags every time is tedious, so `dblab` provides a couple of flags to help with it: `--config` and `--cfg-name`.
Expand Down Expand Up @@ -236,6 +296,18 @@ database:
db: "msdb"
password: "5@klkbN#ABC"
user: "SA"
- name: "ssh-tunnel"
host: "localhost"
port: 5432
db: "users"
password: "password"
user: "postgres"
schema: "public"
driver: "postgres"
ssh-host: "example.com"
ssh-port: 22
ssh-user: "ssh-user"
ssh-pass: "password"
# should be greater than 0, otherwise the app will error out
limit: 50
```
Expand Down
25 changes: 25 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ var (
sslkey string
sslpassword string
sslrootcert string
// SSH Tunnel.
sshHost string
sshPort string
sshUser string
sshPass string
sshKey string
sshKeyPassphrase string
// oracle specific.
traceFile string
sslVerify string
Expand Down Expand Up @@ -77,6 +84,12 @@ func NewRootCmd() *cobra.Command {
Encrypt: encrypt,
TrustServerCertificate: trustServerCertificate,
ConnectionTimeout: connectionTimeout,
SSHHost: sshHost,
SSHPort: sshPort,
SSHUser: sshUser,
SSHPass: sshPass,
SSHKeyFile: sshKey,
SSHKeyPassphrase: sshKeyPassphrase,
}

if form.IsEmpty(opts) {
Expand Down Expand Up @@ -174,4 +187,16 @@ func init() {
StringVarP(&trustServerCertificate, "trust-server-certificate", "", "", "[false|true] server certificate is checked or not")
rootCmd.Flags().
StringVarP(&connectionTimeout, "timeout", "", "", "in seconds (default is 0 for no timeout), set to 0 for no timeout. Recommended to set to 0 and use context to manage query and connection timeouts")
rootCmd.Flags().StringVarP(&sshHost, "ssh-host", "", "", "SSH Server Hostname/IP")
rootCmd.Flags().StringVarP(&sshPort, "ssh-port", "", "", "SSH Port")
rootCmd.Flags().StringVarP(&sshUser, "ssh-user", "", "", "SSH User")
rootCmd.Flags().
StringVarP(&sshPass, "ssh-pass", "", "", "SSH Password (Empty string for no password)")
rootCmd.Flags().
StringVarP(&sshKey, "ssh-key", "", "", "File with private key for SSH authentication")
rootCmd.Flags().
StringVarP(&sshKeyPassphrase, "ssh-key-pass", "", "", "Supports connections with protected private keys with passphrase")

// rootCmd.Flags().
// StringVarP(&sshKeyAlgo, "ssh-key-algo", "", "", "Publick Key Algorithm")
}
75 changes: 75 additions & 0 deletions docker-compose.ssh.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
services:
ssh:
image: rastasheep/ubuntu-sshd:latest
container_name: test-ssh
ports:
- "2222:22"
environment:
- ROOT_PASSWORD=root
networks:
- private-network
- public-network

postgres:
image: postgres:15
container_name: test-postgres
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=password
- POSTGRES_DB=users
networks:
- private-network

mysql:
image: mysql:8.0
environment:
MYSQL_USER: myuser
MYSQL_PASSWORD: 5@klkbN#ABC
MYSQL_ROOT_PASSWORD: myuser
MYSQL_DATABASE: mydb
networks:
- private-network

dblab:
build:
context: .
target: builder
volumes:
- ./:/src/app:z
depends_on:
- postgres
environment:
- DB_HOST=postgres
- DB_USER=postgres
- DB_PASSWORD=password
- DB_NAME=users
- DB_PORT=5432
- DB_DRIVER=postgres
- DB_SCHEMA=public
entrypoint: ["/bin/bash", "./scripts/entrypoint.dev.sh"]
networks:
- private-network

dblab-mysql:
build:
context: .
target: builder
volumes:
- ./:/src/app:z
depends_on:
- mysql
environment:
- DB_HOST=mysql
- DB_USER=myuser
- DB_PASSWORD=5@klkbN#ABC
- DB_NAME=mydb
- DB_PORT=3306
- DB_DRIVER=mysql
entrypoint: ["/bin/bash", "./scripts/entrypoint-mysql.dev.sh"]
networks:
- private-network

networks:
private-network:
internal: true # Makes this network inaccessible from outside Docker
public-network:
2 changes: 0 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
version: "3.8"

services:
postgres:
image: postgres:12.1-alpine
Expand Down
Loading
Loading