Latest commit

History

11 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

mssh

Minimal rendezvous service for reaching SSH behind NAT

This enables SSH access to machines behind NAT/firewalls using a simple rendezvous server. No complex VPN setup required, works with your ssh command out of the box.

mssh diagram

Usage

Deploy the server on a publicly reachable machine, run the agent on each host behind NAT, then connect from your machines using either the built-in Go SSH client or the ProxyCommand approach described below.

CommandPurpose
mssh serverRuns the rendezvous service on a public host
mssh agent <node-id>Keeps a connection open from a NATed host back to the server
mssh proxy <node-id> / mssh user@nodeLets you connect from your machines

Server

Run the rendezvous server:

mssh server --host 0.0.0.0 --port 8443

Production tip: Deploy behind a TLS proxy (nginx, Traefik, Caddy) with Let's Encrypt for secure public exposure.

Agent

Run on the remote host behind NAT:

# With explicit node-id
mssh agent prod-db-1 --server rendezvous.example.com:8443 --ssh-port 22
# Auto-detect node-id from primary IPv4
mssh agent --server rendezvous.example.com:8443

The agent automatically re-registers after each session ends.

Node-ID rules: May contain letters, digits, ., _, and -. If omitted, the primary IPv4 address is used.

Client

Built-in SSH client:

mssh alice@prod-db-1
# With custom server or identity
mssh alice@prod-db-1 --server other.example.net:8443 --identity ~/.ssh/prod_key

The client scans ~/.ssh/id_{ed25519,rsa,ecdsa} (with passphrase prompts) and falls back to SSH_AUTH_SOCK.

ProxyCommand integration:

ssh -o ProxyCommand="mssh proxy prod-db-1 --server rendezvous.example.com:8443" alice@localhost

To make this seamless, add it to ~/.ssh/config so ssh prod-db works without long command lines:

Hostprod-db-1HostNamelocalhostUseraliceProxyCommandmssh proxy prod-db-1 --server rendezvous.example.com:8443

Now simply run ssh prod-db and the ProxyCommand will invoke mssh proxy ... behind the scenes.

Installation

Set VERSION=vX.Y.Z to pin a specific release (default: latest).

Binary only

curl -fsSL https://raw.githubusercontent.com/eznix86/mssh/main/install/install.sh | sudo bash

The script drops the binary at /usr/local/bin/mssh (override with BIN_DIR=/path).

Server (systemd)

curl -fsSL https://raw.githubusercontent.com/eznix86/mssh/main/install/install.sh | \
sudo BIN_DIR=/usr/local/bin bash -s -- server --host 0.0.0.0 --port 8443

Agent (systemd)

curl -fsSL https://raw.githubusercontent.com/eznix86/mssh/main/install/install.sh | \
sudo bash -s -- agent --server rendezvous.example.com:8443 --ssh-port 22

if node-id is omited, it defaults to auto-detected IP. Or set unique name.

Manual Build

go build ./cmd/mssh
sudo install -m 0755 mssh /usr/local/bin/mssh

Uninstall

curl -fsSL https://raw.githubusercontent.com/eznix86/mssh/main/install/uninstall.sh | sudo bash

Removes the binary and disables all systemd units.

Configuration

Only used for mssh username@node-id

Initialize your config file:

mssh config init

This creates ~/.mssh/config.yaml:

server: rendezvous.example.com:8443identity: ~/.ssh/id_ed25519 # optional; leave blank to auto-detect keys / use ssh-agentnodes:
prod-db-1:
server: prod-rendezvous.example.com:8443identity: ~/.ssh/prod_key

Priority: CLI flags → node-specific values → top-level defaults

Security

  • TLS termination: Place the rendezvous server behind a TLS proxy (nginx/Caddy/Traefik) with Let's Encrypt
  • Optional hardening: Add mutual TLS or IP filtering at the proxy layer

License

MIT

About

Enable SSH access to machines behind NAT without a VPN

Topics

Resources

Stars

116 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Latest commit

History

11 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

mssh

Minimal rendezvous service for reaching SSH behind NAT

This enables SSH access to machines behind NAT/firewalls using a simple rendezvous server. No complex VPN setup required, works with your ssh command out of the box.

mssh diagram

Usage

Deploy the server on a publicly reachable machine, run the agent on each host behind NAT, then connect from your machines using either the built-in Go SSH client or the ProxyCommand approach described below.

CommandPurpose
mssh serverRuns the rendezvous service on a public host
mssh agent <node-id>Keeps a connection open from a NATed host back to the server
mssh proxy <node-id> / mssh user@nodeLets you connect from your machines

Server

Run the rendezvous server:

mssh server --host 0.0.0.0 --port 8443

Production tip: Deploy behind a TLS proxy (nginx, Traefik, Caddy) with Let's Encrypt for secure public exposure.

Agent

Run on the remote host behind NAT:

# With explicit node-id
mssh agent prod-db-1 --server rendezvous.example.com:8443 --ssh-port 22
# Auto-detect node-id from primary IPv4
mssh agent --server rendezvous.example.com:8443

The agent automatically re-registers after each session ends.

Node-ID rules: May contain letters, digits, ., _, and -. If omitted, the primary IPv4 address is used.

Client

Built-in SSH client:

mssh alice@prod-db-1
# With custom server or identity
mssh alice@prod-db-1 --server other.example.net:8443 --identity ~/.ssh/prod_key

The client scans ~/.ssh/id_{ed25519,rsa,ecdsa} (with passphrase prompts) and falls back to SSH_AUTH_SOCK.

ProxyCommand integration:

ssh -o ProxyCommand="mssh proxy prod-db-1 --server rendezvous.example.com:8443" alice@localhost

To make this seamless, add it to ~/.ssh/config so ssh prod-db works without long command lines:

Hostprod-db-1HostNamelocalhostUseraliceProxyCommandmssh proxy prod-db-1 --server rendezvous.example.com:8443

Now simply run ssh prod-db and the ProxyCommand will invoke mssh proxy ... behind the scenes.

Installation

Set VERSION=vX.Y.Z to pin a specific release (default: latest).

Binary only

curl -fsSL https://raw.githubusercontent.com/eznix86/mssh/main/install/install.sh | sudo bash

The script drops the binary at /usr/local/bin/mssh (override with BIN_DIR=/path).

Server (systemd)

curl -fsSL https://raw.githubusercontent.com/eznix86/mssh/main/install/install.sh | \
sudo BIN_DIR=/usr/local/bin bash -s -- server --host 0.0.0.0 --port 8443

Agent (systemd)

curl -fsSL https://raw.githubusercontent.com/eznix86/mssh/main/install/install.sh | \
sudo bash -s -- agent --server rendezvous.example.com:8443 --ssh-port 22

if node-id is omited, it defaults to auto-detected IP. Or set unique name.

Manual Build

go build ./cmd/mssh
sudo install -m 0755 mssh /usr/local/bin/mssh

Uninstall

curl -fsSL https://raw.githubusercontent.com/eznix86/mssh/main/install/uninstall.sh | sudo bash

Removes the binary and disables all systemd units.

Configuration

Only used for mssh username@node-id

Initialize your config file:

mssh config init

This creates ~/.mssh/config.yaml:

server: rendezvous.example.com:8443identity: ~/.ssh/id_ed25519 # optional; leave blank to auto-detect keys / use ssh-agentnodes:
prod-db-1:
server: prod-rendezvous.example.com:8443identity: ~/.ssh/prod_key

Priority: CLI flags → node-specific values → top-level defaults

Security

  • TLS termination: Place the rendezvous server behind a TLS proxy (nginx/Caddy/Traefik) with Let's Encrypt
  • Optional hardening: Add mutual TLS or IP filtering at the proxy layer

License

MIT

About

Enable SSH access to machines behind NAT without a VPN

Topics

Resources

Stars

116 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

11 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

mssh

Minimal rendezvous service for reaching SSH behind NAT

This enables SSH access to machines behind NAT/firewalls using a simple rendezvous server. No complex VPN setup required, works with your ssh command out of the box.

mssh diagram

Usage

Deploy the server on a publicly reachable machine, run the agent on each host behind NAT, then connect from your machines using either the built-in Go SSH client or the ProxyCommand approach described below.

CommandPurpose
mssh serverRuns the rendezvous service on a public host
mssh agent <node-id>Keeps a connection open from a NATed host back to the server
mssh proxy <node-id> / mssh user@nodeLets you connect from your machines

Server

Run the rendezvous server:

mssh server --host 0.0.0.0 --port 8443

Production tip: Deploy behind a TLS proxy (nginx, Traefik, Caddy) with Let's Encrypt for secure public exposure.

Agent

Run on the remote host behind NAT:

# With explicit node-id
mssh agent prod-db-1 --server rendezvous.example.com:8443 --ssh-port 22
# Auto-detect node-id from primary IPv4
mssh agent --server rendezvous.example.com:8443

The agent automatically re-registers after each session ends.

Node-ID rules: May contain letters, digits, ., _, and -. If omitted, the primary IPv4 address is used.

Client

Built-in SSH client:

mssh alice@prod-db-1
# With custom server or identity
mssh alice@prod-db-1 --server other.example.net:8443 --identity ~/.ssh/prod_key

The client scans ~/.ssh/id_{ed25519,rsa,ecdsa} (with passphrase prompts) and falls back to SSH_AUTH_SOCK.

ProxyCommand integration:

ssh -o ProxyCommand="mssh proxy prod-db-1 --server rendezvous.example.com:8443" alice@localhost

To make this seamless, add it to ~/.ssh/config so ssh prod-db works without long command lines:

Hostprod-db-1HostNamelocalhostUseraliceProxyCommandmssh proxy prod-db-1 --server rendezvous.example.com:8443

Now simply run ssh prod-db and the ProxyCommand will invoke mssh proxy ... behind the scenes.

Installation

Set VERSION=vX.Y.Z to pin a specific release (default: latest).

Binary only

curl -fsSL https://raw.githubusercontent.com/eznix86/mssh/main/install/install.sh | sudo bash

The script drops the binary at /usr/local/bin/mssh (override with BIN_DIR=/path).

Server (systemd)

curl -fsSL https://raw.githubusercontent.com/eznix86/mssh/main/install/install.sh | \
sudo BIN_DIR=/usr/local/bin bash -s -- server --host 0.0.0.0 --port 8443

Agent (systemd)

curl -fsSL https://raw.githubusercontent.com/eznix86/mssh/main/install/install.sh | \
sudo bash -s -- agent --server rendezvous.example.com:8443 --ssh-port 22

if node-id is omited, it defaults to auto-detected IP. Or set unique name.

Manual Build

go build ./cmd/mssh
sudo install -m 0755 mssh /usr/local/bin/mssh

Uninstall

curl -fsSL https://raw.githubusercontent.com/eznix86/mssh/main/install/uninstall.sh | sudo bash

Removes the binary and disables all systemd units.

Configuration

Only used for mssh username@node-id

Initialize your config file:

mssh config init

This creates ~/.mssh/config.yaml:

server: rendezvous.example.com:8443identity: ~/.ssh/id_ed25519 # optional; leave blank to auto-detect keys / use ssh-agentnodes:
prod-db-1:
server: prod-rendezvous.example.com:8443identity: ~/.ssh/prod_key

Priority: CLI flags → node-specific values → top-level defaults

Security

  • TLS termination: Place the rendezvous server behind a TLS proxy (nginx/Caddy/Traefik) with Let's Encrypt
  • Optional hardening: Add mutual TLS or IP filtering at the proxy layer

License

MIT

About

Enable SSH access to machines behind NAT without a VPN

Topics

Resources

Stars

116 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

11 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

mssh

Minimal rendezvous service for reaching SSH behind NAT

This enables SSH access to machines behind NAT/firewalls using a simple rendezvous server. No complex VPN setup required, works with your ssh command out of the box.

mssh diagram

Usage

Deploy the server on a publicly reachable machine, run the agent on each host behind NAT, then connect from your machines using either the built-in Go SSH client or the ProxyCommand approach described below.

CommandPurpose
mssh serverRuns the rendezvous service on a public host
mssh agent <node-id>Keeps a connection open from a NATed host back to the server
mssh proxy <node-id> / mssh user@nodeLets you connect from your machines

Server

Run the rendezvous server:

mssh server --host 0.0.0.0 --port 8443

Production tip: Deploy behind a TLS proxy (nginx, Traefik, Caddy) with Let's Encrypt for secure public exposure.

Agent

Run on the remote host behind NAT:

# With explicit node-id
mssh agent prod-db-1 --server rendezvous.example.com:8443 --ssh-port 22
# Auto-detect node-id from primary IPv4
mssh agent --server rendezvous.example.com:8443

The agent automatically re-registers after each session ends.

Node-ID rules: May contain letters, digits, ., _, and -. If omitted, the primary IPv4 address is used.

Client

Built-in SSH client:

mssh alice@prod-db-1
# With custom server or identity
mssh alice@prod-db-1 --server other.example.net:8443 --identity ~/.ssh/prod_key

The client scans ~/.ssh/id_{ed25519,rsa,ecdsa} (with passphrase prompts) and falls back to SSH_AUTH_SOCK.

ProxyCommand integration:

ssh -o ProxyCommand="mssh proxy prod-db-1 --server rendezvous.example.com:8443" alice@localhost

To make this seamless, add it to ~/.ssh/config so ssh prod-db works without long command lines:

Hostprod-db-1HostNamelocalhostUseraliceProxyCommandmssh proxy prod-db-1 --server rendezvous.example.com:8443

Now simply run ssh prod-db and the ProxyCommand will invoke mssh proxy ... behind the scenes.

Installation

Set VERSION=vX.Y.Z to pin a specific release (default: latest).

Binary only

curl -fsSL https://raw.githubusercontent.com/eznix86/mssh/main/install/install.sh | sudo bash

The script drops the binary at /usr/local/bin/mssh (override with BIN_DIR=/path).

Server (systemd)

curl -fsSL https://raw.githubusercontent.com/eznix86/mssh/main/install/install.sh | \
sudo BIN_DIR=/usr/local/bin bash -s -- server --host 0.0.0.0 --port 8443

Agent (systemd)

curl -fsSL https://raw.githubusercontent.com/eznix86/mssh/main/install/install.sh | \
sudo bash -s -- agent --server rendezvous.example.com:8443 --ssh-port 22

if node-id is omited, it defaults to auto-detected IP. Or set unique name.

Manual Build

go build ./cmd/mssh
sudo install -m 0755 mssh /usr/local/bin/mssh

Uninstall

curl -fsSL https://raw.githubusercontent.com/eznix86/mssh/main/install/uninstall.sh | sudo bash

Removes the binary and disables all systemd units.

Configuration

Only used for mssh username@node-id

Initialize your config file:

mssh config init

This creates ~/.mssh/config.yaml:

server: rendezvous.example.com:8443identity: ~/.ssh/id_ed25519 # optional; leave blank to auto-detect keys / use ssh-agentnodes:
prod-db-1:
server: prod-rendezvous.example.com:8443identity: ~/.ssh/prod_key

Priority: CLI flags → node-specific values → top-level defaults

Security

  • TLS termination: Place the rendezvous server behind a TLS proxy (nginx/Caddy/Traefik) with Let's Encrypt
  • Optional hardening: Add mutual TLS or IP filtering at the proxy layer

License

MIT

About

Enable SSH access to machines behind NAT without a VPN

Topics

Resources

Stars

116 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Latest commit

History

11 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

mssh

Minimal rendezvous service for reaching SSH behind NAT

This enables SSH access to machines behind NAT/firewalls using a simple rendezvous server. No complex VPN setup required, works with your ssh command out of the box.

mssh diagram

Usage

Deploy the server on a publicly reachable machine, run the agent on each host behind NAT, then connect from your machines using either the built-in Go SSH client or the ProxyCommand approach described below.

CommandPurpose
mssh serverRuns the rendezvous service on a public host
mssh agent <node-id>Keeps a connection open from a NATed host back to the server
mssh proxy <node-id> / mssh user@nodeLets you connect from your machines

Server

Run the rendezvous server:

mssh server --host 0.0.0.0 --port 8443

Production tip: Deploy behind a TLS proxy (nginx, Traefik, Caddy) with Let's Encrypt for secure public exposure.

Agent

Run on the remote host behind NAT:

# With explicit node-id
mssh agent prod-db-1 --server rendezvous.example.com:8443 --ssh-port 22
# Auto-detect node-id from primary IPv4
mssh agent --server rendezvous.example.com:8443

The agent automatically re-registers after each session ends.

Node-ID rules: May contain letters, digits, ., _, and -. If omitted, the primary IPv4 address is used.

Client

Built-in SSH client:

mssh alice@prod-db-1
# With custom server or identity
mssh alice@prod-db-1 --server other.example.net:8443 --identity ~/.ssh/prod_key

The client scans ~/.ssh/id_{ed25519,rsa,ecdsa} (with passphrase prompts) and falls back to SSH_AUTH_SOCK.

ProxyCommand integration:

ssh -o ProxyCommand="mssh proxy prod-db-1 --server rendezvous.example.com:8443" alice@localhost

To make this seamless, add it to ~/.ssh/config so ssh prod-db works without long command lines:

Hostprod-db-1HostNamelocalhostUseraliceProxyCommandmssh proxy prod-db-1 --server rendezvous.example.com:8443

Now simply run ssh prod-db and the ProxyCommand will invoke mssh proxy ... behind the scenes.

Installation

Set VERSION=vX.Y.Z to pin a specific release (default: latest).

Binary only

curl -fsSL https://raw.githubusercontent.com/eznix86/mssh/main/install/install.sh | sudo bash

The script drops the binary at /usr/local/bin/mssh (override with BIN_DIR=/path).

Server (systemd)

curl -fsSL https://raw.githubusercontent.com/eznix86/mssh/main/install/install.sh | \
sudo BIN_DIR=/usr/local/bin bash -s -- server --host 0.0.0.0 --port 8443

Agent (systemd)

curl -fsSL https://raw.githubusercontent.com/eznix86/mssh/main/install/install.sh | \
sudo bash -s -- agent --server rendezvous.example.com:8443 --ssh-port 22

if node-id is omited, it defaults to auto-detected IP. Or set unique name.

Manual Build

go build ./cmd/mssh
sudo install -m 0755 mssh /usr/local/bin/mssh

Uninstall

curl -fsSL https://raw.githubusercontent.com/eznix86/mssh/main/install/uninstall.sh | sudo bash

Removes the binary and disables all systemd units.

Configuration

Only used for mssh username@node-id

Initialize your config file:

mssh config init

This creates ~/.mssh/config.yaml:

server: rendezvous.example.com:8443identity: ~/.ssh/id_ed25519 # optional; leave blank to auto-detect keys / use ssh-agentnodes:
prod-db-1:
server: prod-rendezvous.example.com:8443identity: ~/.ssh/prod_key

Priority: CLI flags → node-specific values → top-level defaults

Security

  • TLS termination: Place the rendezvous server behind a TLS proxy (nginx/Caddy/Traefik) with Let's Encrypt
  • Optional hardening: Add mutual TLS or IP filtering at the proxy layer

License

MIT

About

Enable SSH access to machines behind NAT without a VPN

Topics

Resources

Stars

116 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

11 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

mssh

Minimal rendezvous service for reaching SSH behind NAT

This enables SSH access to machines behind NAT/firewalls using a simple rendezvous server. No complex VPN setup required, works with your ssh command out of the box.

mssh diagram

Usage

Deploy the server on a publicly reachable machine, run the agent on each host behind NAT, then connect from your machines using either the built-in Go SSH client or the ProxyCommand approach described below.

CommandPurpose
mssh serverRuns the rendezvous service on a public host
mssh agent <node-id>Keeps a connection open from a NATed host back to the server
mssh proxy <node-id> / mssh user@nodeLets you connect from your machines

Server

Run the rendezvous server:

mssh server --host 0.0.0.0 --port 8443

Production tip: Deploy behind a TLS proxy (nginx, Traefik, Caddy) with Let's Encrypt for secure public exposure.

Agent

Run on the remote host behind NAT:

# With explicit node-id
mssh agent prod-db-1 --server rendezvous.example.com:8443 --ssh-port 22
# Auto-detect node-id from primary IPv4
mssh agent --server rendezvous.example.com:8443

The agent automatically re-registers after each session ends.

Node-ID rules: May contain letters, digits, ., _, and -. If omitted, the primary IPv4 address is used.

Client

Built-in SSH client:

mssh alice@prod-db-1
# With custom server or identity
mssh alice@prod-db-1 --server other.example.net:8443 --identity ~/.ssh/prod_key

The client scans ~/.ssh/id_{ed25519,rsa,ecdsa} (with passphrase prompts) and falls back to SSH_AUTH_SOCK.

ProxyCommand integration:

ssh -o ProxyCommand="mssh proxy prod-db-1 --server rendezvous.example.com:8443" alice@localhost

To make this seamless, add it to ~/.ssh/config so ssh prod-db works without long command lines:

Hostprod-db-1HostNamelocalhostUseraliceProxyCommandmssh proxy prod-db-1 --server rendezvous.example.com:8443

Now simply run ssh prod-db and the ProxyCommand will invoke mssh proxy ... behind the scenes.

Installation

Set VERSION=vX.Y.Z to pin a specific release (default: latest).

Binary only

curl -fsSL https://raw.githubusercontent.com/eznix86/mssh/main/install/install.sh | sudo bash

The script drops the binary at /usr/local/bin/mssh (override with BIN_DIR=/path).

Server (systemd)

curl -fsSL https://raw.githubusercontent.com/eznix86/mssh/main/install/install.sh | \
sudo BIN_DIR=/usr/local/bin bash -s -- server --host 0.0.0.0 --port 8443

Agent (systemd)

curl -fsSL https://raw.githubusercontent.com/eznix86/mssh/main/install/install.sh | \
sudo bash -s -- agent --server rendezvous.example.com:8443 --ssh-port 22

if node-id is omited, it defaults to auto-detected IP. Or set unique name.

Manual Build

go build ./cmd/mssh
sudo install -m 0755 mssh /usr/local/bin/mssh

Uninstall

curl -fsSL https://raw.githubusercontent.com/eznix86/mssh/main/install/uninstall.sh | sudo bash

Removes the binary and disables all systemd units.

Configuration

Only used for mssh username@node-id

Initialize your config file:

mssh config init

This creates ~/.mssh/config.yaml:

server: rendezvous.example.com:8443identity: ~/.ssh/id_ed25519 # optional; leave blank to auto-detect keys / use ssh-agentnodes:
prod-db-1:
server: prod-rendezvous.example.com:8443identity: ~/.ssh/prod_key

Priority: CLI flags → node-specific values → top-level defaults

Security

  • TLS termination: Place the rendezvous server behind a TLS proxy (nginx/Caddy/Traefik) with Let's Encrypt
  • Optional hardening: Add mutual TLS or IP filtering at the proxy layer

License

MIT

About

Enable SSH access to machines behind NAT without a VPN

Topics

Resources

Stars

116 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

11 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

mssh

Minimal rendezvous service for reaching SSH behind NAT

This enables SSH access to machines behind NAT/firewalls using a simple rendezvous server. No complex VPN setup required, works with your ssh command out of the box.

mssh diagram

Usage

Deploy the server on a publicly reachable machine, run the agent on each host behind NAT, then connect from your machines using either the built-in Go SSH client or the ProxyCommand approach described below.

CommandPurpose
mssh serverRuns the rendezvous service on a public host
mssh agent <node-id>Keeps a connection open from a NATed host back to the server
mssh proxy <node-id> / mssh user@nodeLets you connect from your machines

Server

Run the rendezvous server:

mssh server --host 0.0.0.0 --port 8443

Production tip: Deploy behind a TLS proxy (nginx, Traefik, Caddy) with Let's Encrypt for secure public exposure.

Agent

Run on the remote host behind NAT:

# With explicit node-id
mssh agent prod-db-1 --server rendezvous.example.com:8443 --ssh-port 22
# Auto-detect node-id from primary IPv4
mssh agent --server rendezvous.example.com:8443

The agent automatically re-registers after each session ends.

Node-ID rules: May contain letters, digits, ., _, and -. If omitted, the primary IPv4 address is used.

Client

Built-in SSH client:

mssh alice@prod-db-1
# With custom server or identity
mssh alice@prod-db-1 --server other.example.net:8443 --identity ~/.ssh/prod_key

The client scans ~/.ssh/id_{ed25519,rsa,ecdsa} (with passphrase prompts) and falls back to SSH_AUTH_SOCK.

ProxyCommand integration:

ssh -o ProxyCommand="mssh proxy prod-db-1 --server rendezvous.example.com:8443" alice@localhost

To make this seamless, add it to ~/.ssh/config so ssh prod-db works without long command lines:

Hostprod-db-1HostNamelocalhostUseraliceProxyCommandmssh proxy prod-db-1 --server rendezvous.example.com:8443

Now simply run ssh prod-db and the ProxyCommand will invoke mssh proxy ... behind the scenes.

Installation

Set VERSION=vX.Y.Z to pin a specific release (default: latest).

Binary only

curl -fsSL https://raw.githubusercontent.com/eznix86/mssh/main/install/install.sh | sudo bash

The script drops the binary at /usr/local/bin/mssh (override with BIN_DIR=/path).

Server (systemd)

curl -fsSL https://raw.githubusercontent.com/eznix86/mssh/main/install/install.sh | \
sudo BIN_DIR=/usr/local/bin bash -s -- server --host 0.0.0.0 --port 8443

Agent (systemd)

curl -fsSL https://raw.githubusercontent.com/eznix86/mssh/main/install/install.sh | \
sudo bash -s -- agent --server rendezvous.example.com:8443 --ssh-port 22

if node-id is omited, it defaults to auto-detected IP. Or set unique name.

Manual Build

go build ./cmd/mssh
sudo install -m 0755 mssh /usr/local/bin/mssh

Uninstall

curl -fsSL https://raw.githubusercontent.com/eznix86/mssh/main/install/uninstall.sh | sudo bash

Removes the binary and disables all systemd units.

Configuration

Only used for mssh username@node-id

Initialize your config file:

mssh config init

This creates ~/.mssh/config.yaml:

server: rendezvous.example.com:8443identity: ~/.ssh/id_ed25519 # optional; leave blank to auto-detect keys / use ssh-agentnodes:
prod-db-1:
server: prod-rendezvous.example.com:8443identity: ~/.ssh/prod_key

Priority: CLI flags → node-specific values → top-level defaults

Security

  • TLS termination: Place the rendezvous server behind a TLS proxy (nginx/Caddy/Traefik) with Let's Encrypt
  • Optional hardening: Add mutual TLS or IP filtering at the proxy layer

License

MIT

About

Enable SSH access to machines behind NAT without a VPN

Topics

Resources

Stars

116 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Latest commit

History

11 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

mssh

Minimal rendezvous service for reaching SSH behind NAT

This enables SSH access to machines behind NAT/firewalls using a simple rendezvous server. No complex VPN setup required, works with your ssh command out of the box.

mssh diagram

Usage

Deploy the server on a publicly reachable machine, run the agent on each host behind NAT, then connect from your machines using either the built-in Go SSH client or the ProxyCommand approach described below.

CommandPurpose
mssh serverRuns the rendezvous service on a public host
mssh agent <node-id>Keeps a connection open from a NATed host back to the server
mssh proxy <node-id> / mssh user@nodeLets you connect from your machines

Server

Run the rendezvous server:

mssh server --host 0.0.0.0 --port 8443

Production tip: Deploy behind a TLS proxy (nginx, Traefik, Caddy) with Let's Encrypt for secure public exposure.

Agent

Run on the remote host behind NAT:

# With explicit node-id
mssh agent prod-db-1 --server rendezvous.example.com:8443 --ssh-port 22
# Auto-detect node-id from primary IPv4
mssh agent --server rendezvous.example.com:8443

The agent automatically re-registers after each session ends.

Node-ID rules: May contain letters, digits, ., _, and -. If omitted, the primary IPv4 address is used.

Client

Built-in SSH client:

mssh alice@prod-db-1
# With custom server or identity
mssh alice@prod-db-1 --server other.example.net:8443 --identity ~/.ssh/prod_key

The client scans ~/.ssh/id_{ed25519,rsa,ecdsa} (with passphrase prompts) and falls back to SSH_AUTH_SOCK.

ProxyCommand integration:

ssh -o ProxyCommand="mssh proxy prod-db-1 --server rendezvous.example.com:8443" alice@localhost

To make this seamless, add it to ~/.ssh/config so ssh prod-db works without long command lines:

Hostprod-db-1HostNamelocalhostUseraliceProxyCommandmssh proxy prod-db-1 --server rendezvous.example.com:8443

Now simply run ssh prod-db and the ProxyCommand will invoke mssh proxy ... behind the scenes.

Installation

Set VERSION=vX.Y.Z to pin a specific release (default: latest).

Binary only

curl -fsSL https://raw.githubusercontent.com/eznix86/mssh/main/install/install.sh | sudo bash

The script drops the binary at /usr/local/bin/mssh (override with BIN_DIR=/path).

Server (systemd)

curl -fsSL https://raw.githubusercontent.com/eznix86/mssh/main/install/install.sh | \
sudo BIN_DIR=/usr/local/bin bash -s -- server --host 0.0.0.0 --port 8443

Agent (systemd)

curl -fsSL https://raw.githubusercontent.com/eznix86/mssh/main/install/install.sh | \
sudo bash -s -- agent --server rendezvous.example.com:8443 --ssh-port 22

if node-id is omited, it defaults to auto-detected IP. Or set unique name.

Manual Build

go build ./cmd/mssh
sudo install -m 0755 mssh /usr/local/bin/mssh

Uninstall

curl -fsSL https://raw.githubusercontent.com/eznix86/mssh/main/install/uninstall.sh | sudo bash

Removes the binary and disables all systemd units.

Configuration

Only used for mssh username@node-id

Initialize your config file:

mssh config init

This creates ~/.mssh/config.yaml:

server: rendezvous.example.com:8443identity: ~/.ssh/id_ed25519 # optional; leave blank to auto-detect keys / use ssh-agentnodes:
prod-db-1:
server: prod-rendezvous.example.com:8443identity: ~/.ssh/prod_key

Priority: CLI flags → node-specific values → top-level defaults

Security

  • TLS termination: Place the rendezvous server behind a TLS proxy (nginx/Caddy/Traefik) with Let's Encrypt
  • Optional hardening: Add mutual TLS or IP filtering at the proxy layer

License

MIT

About

Enable SSH access to machines behind NAT without a VPN

Topics

Resources

Stars

116 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages