Reverse ssh tunnel
Forwards traffic from public to local developer's machine. Think of self-hosted alternative to ngrok, localtunnel etc, but without traffic limitations.
You will need public VPS with dns name. Reverse dns from your hosting provider should serve the purpose as well.
$ npm install @eu-ge-ne/tunnel
Assuming you already have VPS with Ubuntu installed, with <vps-dns-name>
dns name.
Disable password authentication for SSH logins and enable public key auth:
-
On local machine:
ssh-keygen -b 4096
File where keys will be saved:
id_rsa_proxy
Leave passphrase blank. -
On VPS:
mkdir -p ~/.ssh && sudo chmod -R 700 ~/.ssh/
-
From local machine copy public key (
id_rsa_proxy.pub
) to VPS:scp id_rsa_proxy.pub <user>@<vps-dns-name>:~/.ssh/authorized_keys
-
Set permissions for the public key directory and the key file itself:
sudo chmod -R 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys
-
Set SSH daemon options (
/etc/ssh/sshd_config
):PermitRootLogin no PasswordAuthentication no
-
Restart the SSH service to load the new configuration:
sudo systemctl restart sshd
sudo apt update
sudo apt install nginx
/etc/nginx/sites-available/default
example:
server {
server_name <vps-dns-name>;
location / {
proxy_pass http://127.0.0.1:8888/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_redirect off;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
sudo add-apt-repository ppa:certbot/certbot
sudo apt install python-certbot-nginx
sudo certbot --nginx -d <vps-dns-name>
Final /etc/nginx/sites-available/default
example:
server {
server_name <vps-dns-name>;
location / {
proxy_pass http://127.0.0.1:8888/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_redirect off;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/<vps-dns-name>/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/<vps-dns-name>/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
if ($host = <vps-dns-name>) {
return 301 https://$host$request_uri;
} # managed by Certbot
server_name <vps-dns-name>;
listen 80;
return 404; # managed by Certbot
}
import { Tunnel, Options } from "@eu-ge-ne/tunnel";
const options: Options = { /* ... */ };
const tunnel = new Tunnel(options);
Options declaration:
export type Options = {
/** Hostname or IP address of the server */
host: string;
/** SSH port of the server. Default = 22 */
port?: number;
/** SSH Username for authentication */
username?: string;
/** Password for password-based user authentication */
password?: string;
/** Buffer or string that contains a private key for either key-based or hostbased user authentication (OpenSSH format) */
privateKey?: Buffer | string;
/** The remote addr to bind on the server */
remoteHost: string;
/** The remote port to bind on the server */
remotePort: number;
/** The local addr to bind */
localHost: string;
/** The local port to bind */
localPort: number;
/** How long (in milliseconds) to wait for connection */
connectTimeout?: number;
/** How often (in milliseconds) to send SSH-level keepalive packets to the server. Set to 0 to disable */
keepaliveInterval?: number;
/** How often (in milliseconds) to check state of the tunnel and reconnect if disconnected */
checkInterval?: number;
};
await tunnel.start();
await tunnel.stop();
import { Status } from "@eu-ge-ne/tunnel";
const status: Status = tunnel.status();
Status declaration:
export type Status = {
/** State of the tunnel */
state: keyof typeof State;
/** How many times disconnect occurred */
disconnects: number;
/** Number of active connections */
connections: number;
}
enum State {
Stopped,
Disconnected,
Disconnecting,
Connecting,
Connected,
}
Events declaration:
type Events = {
/** Emitted on every tunnel state chane */
state: (state: keyof typeof State) => void;
/** Emitted on remote socket end */
end: () => void;
/** Emitted on remote socket close */
close: (hadError: boolean) => void;
/** Emitted on remote socket timeout */
timeout: () => void;
/** Emitted when any error occurs */
error: (message: string, data?: { err: Error }) => void;
}