Skip to content

Commit

Permalink
switch local SOCKS proxy to remote SSH tunnels
Browse files Browse the repository at this point in the history
  • Loading branch information
ab77 committed Aug 25, 2018
1 parent 54926d9 commit 918435b
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 66 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<img align="left" src="https://raw.githubusercontent.com/ab77/proxy-socks/master/assets/app-icon/png/48.png"> This [Electron](https://electronjs.org/) desktop app starts a [SOCKS server](https://github.com/mscdex/socksv5) locally on `proxyPort` and forwards an available port on a remote [Linux box](#server-config) to a locally available TCP port over [SSH](https://github.com/mscdex/ssh2).

SOCKS clients connecting to `proxyPort` will be proxied via the remote server. Similarly, remote connections will be proxied out the local WAN interface of the machine running the app.
Clients connecting to `squidPort` or `socksPort` on the machine running the app, will be tunneled to `proxyRemote` on remote server. Similarly, remote connections will be proxied out the local WAN interface of the machine running the app. It is up to you to handle incoming client connections on the remote server using HAProxy, Squid, etc.


# instructions
Expand All @@ -32,7 +32,9 @@ cat << EOF > ~/.proxy-socks/config.json
"host": "{proxy-concentrator}",
"port": 22,
"privateKey": "${HOME}/.proxy-socks/id_rsa",
"proxyPort": 1080
"squidPort": 3128,
"socksPort": 1080,
"proxyRemote": "172.20.0.10"
}
EOF
```
Expand Down
203 changes: 140 additions & 63 deletions main.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
require('string.format')

const net = require('net')
const assert = require('assert')
const fs = require('fs')
const path = require('path')
Expand All @@ -17,12 +18,15 @@ const socks = require('socksv5')
const homedir = require('os').homedir()
const debug = /--debug/.test(process.argv[2])
const default_ssh_port = 22
const default_proxy_port = 1080
const default_proxy_remote = '172.20.0.10' // e.g. Docker container running Squid, etc.
const default_socks_port = 1080
const default_squid_port = 3128

let server
let client
let conn
let proxy
let socks_proxy
let squid_proxy
let mainWindow
let trayIcon
let trayMenu
Expand All @@ -34,7 +38,7 @@ if (resourcesPath.endsWith('app.asar')) {
extraResources = path.resolve(resourcesPath, '..', 'extra')
}

var iShouldQuit = app.makeSingleInstance( () => {
let iShouldQuit = app.makeSingleInstance( () => {
if (mainWindow) {
if (mainWindow.isMinimized())
mainWindow.restore()
Expand All @@ -58,15 +62,35 @@ if (!fs.existsSync(dir)) {
let configPath = path.resolve(dir)
let configFile = path.join(configPath, 'config.json')

let proxy_port
let proxy_remote
try {
proxy_port = require(configFile).proxyPort
assert.notStrictEqual(proxy_port, undefined)
proxy_remote = require(configFile).proxyRemote
assert.notStrictEqual(proxy_remote, undefined)
} catch (err) {
console.log(err)
proxy_port = default_proxy_port
proxy_remote = default_proxy_remote
}
console.log(proxy_port)
console.log(proxy_remote)

let squid_port
try {
squid_port = require(configFile).squidPort
assert.notStrictEqual(squid_port, undefined)
} catch (err) {
console.log(err)
squid_port = default_squid_port
}
console.log(squid_port)

let socks_port
try {
socks_port = require(configFile).socksPort
assert.notStrictEqual(socks_port, undefined)
} catch (err) {
console.log(err)
socks_port = default_socks_port
}
console.log(socks_port)

let privateKeyPath
try {
Expand Down Expand Up @@ -184,41 +208,15 @@ function createWindow () {
})
}

app.once('ready', () => {
require('update-electron-app')()
createWindow()
trayIcon = new Tray(path.resolve(
path.normalize(
path.join(__dirname, '/assets/app-icon/png/16.png')
)
))
trayMenu = Menu.buildFromTemplate([
{
label: 'Show',
click: () => {
mainWindow.show()
mainWindow.focus()
}
},
{
label: 'Quit',
click: () => {
app.isQuiting = true
app.quit()
}
}
])
trayIcon.setContextMenu(trayMenu)
trayIcon.setToolTip(app.getName())
})

function save_config() {
let app_config = {
username: ssh_config.username,
host: ssh_config.host,
port: ssh_config.port,
privateKey: privateKeyPath,
proxyPort: proxy_port
squidPort: squid_port,
socksPort: socks_port,
proxyRemote: proxy_remote
}
if (!fs.existsSync(configPath)) {
fs.mkdirSync(configPath)
Expand All @@ -242,7 +240,7 @@ function forward_port(event, rport, lport) {
save_config()
})
}).on('tcp connection', function(info, accept, deny) {
var stream = accept()
let stream = accept()
stream.pause()
stream.on('error', function(err) {
console.log(err)
Expand All @@ -265,6 +263,34 @@ function forward_port(event, rport, lport) {
}
}

app.once('ready', () => {
require('update-electron-app')()
createWindow()
trayIcon = new Tray(path.resolve(
path.normalize(
path.join(__dirname, '/assets/app-icon/png/16.png')
)
))
trayMenu = Menu.buildFromTemplate([
{
label: 'Show',
click: () => {
mainWindow.show()
mainWindow.focus()
}
},
{
label: 'Quit',
click: () => {
app.isQuiting = true
app.quit()
}
}
])
trayIcon.setContextMenu(trayMenu)
trayIcon.setToolTip(app.getName())
})

ipc.on('connect-input', (event, message) => {
try {
ssh_config.username = message.split(':')[0].split('@')[0]
Expand Down Expand Up @@ -298,6 +324,7 @@ ipc.on('passphrase-input', (event, message) => {
}
})

// SOCKS proxy (inbound) listing on random local port
ipc.on('start-client', (event) => {
if (ssh_config.username && ssh_config.host && ssh_config.port && ssh_config.privateKey) {
if (client === undefined ) {
Expand Down Expand Up @@ -336,51 +363,101 @@ ipc.on('stop-client', (event) => {
})

ipc.on('start-server', (event) => {
if (proxy === undefined ) {
event.sender.send('starting-server', 'Starting proxy...')
proxy = socks.createServer(function(info, accept, deny) {
let proxy_ports = []
// SSH tunnel localhost:3128 => docker0:3128
if (squid_proxy === undefined ) {
event.sender.send('starting-server', 'Starting HTTP(S) proxy tunnel...')
squid_proxy = net.createServer(function(sock) {
if (debug) {
console.log(info, accept, deny);
console.log(sock)
}
let conn = new Client()
conn.on('ready', function() {
conn.forwardOut(info.srcAddr, info.srcPort, info.dstAddr, info.dstPort, function(err, stream) {
conn.forwardOut(sock.remoteAddress, sock.remotePort, proxy_remote, squid_port, function(err, stream) {
if (err) {
conn.end()
return deny()
}
let socket
if (socket = accept(true)) {
stream.pipe(socket).on('error', function (err) {
deny()
}).on('close', function (err) {
conn.end();
}).pipe(stream).on('error', function (err) {
deny()
}).on('close', function() {
conn.end()
})
} else
if (debug) {
console.log(stream)
}
stream.pipe(sock).on('error', function (err) {
deny()
}).on('close', function (err) {
conn.end();
}).pipe(stream).on('error', function (err) {
deny()
}).on('close', function() {
conn.end()
})
})
}).on('error', function(err) {
deny()
}).connect(ssh_config)
}).listen(squid_port, '::', () => {
if (!proxy_ports.indexOf(squid_port) > -1) { proxy_ports.push(squid_port) }
message = 'Proxy tunnel(s) localhost: ' + proxy_ports
event.sender.send('started-server', message)
})
} else {
event.sender.send('started-server', 'Already running')
}

// SSH tunnel localhost:3128 => docker0:3128
if (socks_proxy === undefined ) {
event.sender.send('starting-server', 'Starting SOCKS proxy tunnel...')
socks_proxy = net.createServer(function(sock) {
if (debug) {
console.log(sock)
}
let conn = new Client()
conn.on('ready', function() {
conn.forwardOut(sock.remoteAddress, sock.remotePort, proxy_remote, socks_port, function(err, stream) {
if (err) {
conn.end()
return deny()
}
if (debug) {
console.log(stream)
}
stream.pipe(sock).on('error', function (err) {
deny()
}).on('close', function (err) {
conn.end();
}).pipe(stream).on('error', function (err) {
deny()
}).on('close', function() {
conn.end()
})
})
}).on('error', function(err) {
deny()
}).connect(ssh_config)
}).listen(proxy_port, '::', () => {
message = 'SOCKS proxy running on localhost:' + proxy_port
}).listen(socks_port, '::', () => {
if (!proxy_ports.indexOf(socks_port) > -1) { proxy_ports.push(socks_port) }
message = 'Proxy tunnel(s) localhost: ' + proxy_ports
event.sender.send('started-server', message)
}).useAuth(socks.auth.None())
})
} else {
event.sender.send('started-server', 'Already running')
}
})

ipc.on('stop-server', (event) => {
if (proxy !== undefined ) {
event.sender.send('stopping-server', 'Stopping proxy...')
proxy.close()
proxy = undefined
event.sender.send('stopped-server', 'Proxy stopped')
if (squid_proxy !== undefined ) {
event.sender.send('stopping-server', 'Stopping tunnel...')
squid_proxy.close()
squid_proxy = undefined
event.sender.send('stopped-server', 'Tunnel stopped')
} else {
event.sender.send('stopped-server', 'Not running')
}

if (socks_proxy !== undefined ) {
event.sender.send('stopping-server', 'Stopping tunnel...')
socks_proxy.close()
socks_proxy = undefined
event.sender.send('stopped-server', 'Tunnel stopped')
} else {
event.sender.send('stopped-server', 'Not running')
}
Expand Down
2 changes: 1 addition & 1 deletion renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ ipc.on('starting-server', function (event, message) {

ipc.on('started-server', function (event, message) {
document.getElementById('server-info').innerHTML = message
if (message.toLowerCase().startsWith('socks proxy running on')) {
if (message.toLowerCase().startsWith('proxy tunnel')) {
stopServerBtn.disabled = false
} else {
startServerBtn.disabled = false
Expand Down

0 comments on commit 918435b

Please sign in to comment.