Last active
March 21, 2016 14:34
-
-
Save cloverstd/f4fb0e8821abc3cb9fd8 to your computer and use it in GitHub Desktop.
ssbee
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"ENCRYPT_KEY": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", | |
"ENCRYPT_IV": "AAAAAAAAAAAAAAAA", | |
"WORKER_URL": "http://11.11.11.3:9998/worker", | |
"WORKER_WS_URI": "ws://11.11.11.3:9998/worker/", | |
"SUPERVISOR_PROCESS_NAME": "shadowsocks", | |
"SUPERVISOR_CTL_PORT": 4999, | |
"SUPERVISOR_CTL_HOST": "127.0.0.1", | |
"DEBUG": true | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# encoding: utf-8 | |
import json | |
import os | |
_config = { | |
"ENCRYPT_KEY": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", | |
"ENCRYPT_IV": "AAAAAAAAAAAAAAAA", | |
"WORKER_URL": "http://11.11.11.3:9998/worker", | |
"WORKER_WS_URI": "ws://11.11.11.3:9998/worker/", | |
"SUPERVISOR_PROCESS_NAME": "shadowsocks", | |
"SUPERVISOR_CTL_PORT": 4999, | |
"SUPERVISOR_CTL_HOST": "127.0.0.1", | |
"DEBUG": False | |
} | |
_node_config = { | |
"name": "franky", | |
"domain": "jp.do.hui.lu", | |
"ip": "11.11.11.3", | |
"remark": "这里是描述", | |
"location": "Japan Tokyo", | |
"method": "aes-256-cfb" | |
} | |
def read_config(_config, prompt="config"): | |
config = {} | |
for key, value in _config.items(): | |
print "Please enter {} {}(default {})".format(prompt, key, value) | |
temp = raw_input('> ') | |
if temp == '': | |
temp = value | |
if isinstance(temp, str) and temp.isdigit(): | |
temp = int(temp) | |
config[key] = temp | |
return config | |
config = read_config(_config) | |
node_config = read_config(_node_config, 'node_config') | |
print json.dumps(config, indent=4, separators=(',', ': ')) | |
print json.dumps(node_config, indent=4, separators=(',', ': ')) | |
save_path = raw_input('save_path > ') | |
if save_path == '': | |
save_path = os.path.dirname(os.path.abspath(__file__)) | |
with open(os.path.join(save_path, 'config.json'), 'w') as fp: | |
json.dump(config, fp, indent=4, separators=(',', ': ')) | |
with open(os.path.join(save_path, 'node.json'), 'w') as fp: | |
json.dump(node_config, fp, indent=4, separators=(',', ': ')) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "franky", | |
"domain": "jp.do.hui.lu", | |
"ip": "11.11.11.3", | |
"remark": "这里是描述", | |
"location": "Japan Tokyo", | |
"method": "aes-256-cfb" | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# install software | |
SS_USER=app | |
SS_APP=ssbee | |
APP_PATH=/home/$SS_USER/$SS_APP | |
sudo apt-get update -y && sudo apt-get upgrade -y | |
curl -sL https://deb.nodesource.com/setup_4.x | sudo -E bash - | |
sudo apt-get install -y python-pip supervisor fail2ban nodejs git | |
sudo useradd -m -U -s /bin/bash $SS_USER | |
# download node code | |
sudo rm -rf $APP_PATH | |
sudo -u app sh -c "git clone https://gist.github.com/f4fb0e8821abc3cb9fd8.git ${APP_PATH}" | |
sudo cp $APP_PATH/shadowsocks.json /etc/shadowsocks.json | |
sudo cp /etc/supervisor/supervisord.conf /etc/supervisor/supervisord.conf.bak | |
sudo cp $APP_PATH/supervisor.conf /etc/supervisor/supervisord.conf | |
sudo cp $APP_PATH/supervisor.worker.conf /etc/supervisor/conf.d/worker.conf | |
# iptables | |
sudo iptables -F | |
sudo iptables -A INPUT -p tcp -d 127.0.0.1 --dport 6001 -j ACCEPT | |
sudo iptables -A INPUT -p tcp -d 127.0.0.1 --dport 4999 -j ACCEPT | |
sudo iptables -A INPUT -p tcp --dport 6001 -j DROP | |
sudo iptables -A INPUT -p tcp --dport 4999 -j DROP | |
sudo sh -c "iptables-save > /etc/iptables.rules" | |
sudo -u app sh -c "python ${APP_PATH}/node.init.py" | |
sudo su - app -c "cd ${APP_PATH} && npm install" | |
sudo pip install shadowsocks | |
sudo service supervisor restart |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "ssbee", | |
"version": "1.0.0", | |
"description": "", | |
"main": "index.js", | |
"scripts": { | |
"test": "echo \"Error: no test specified\" && exit 1" | |
}, | |
"author": "", | |
"license": "MIT", | |
"dependencies": { | |
"request": "^2.69.0", | |
"systeminformation": "^2.0.5", | |
"ws": "^1.0.1", | |
"xmlrpc": "^1.3.1" | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"server": "0.0.0.0", | |
"port_password": { | |
"9999": "123456" | |
}, | |
"timeout": 60, | |
"method": "aes-256-cfb", | |
"manager_address": "127.0.0.1:6001" | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[unix_http_server] | |
file=/var/run/supervisor.sock | |
chmod=0700 | |
[supervisord] | |
logfile=/var/log/supervisor/supervisord.log | |
pidfile=/var/run/supervisord.pid | |
childlogdir=/var/log/supervisor | |
[rpcinterface:supervisor] | |
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface | |
[inet_http_server] | |
port = 127.0.0.1:4999 | |
[supervisorctl] | |
serverurl = unix:///var/run/supervisor.sock | |
[program:shadowsocks] | |
command=ssserver -c /etc/shadowsocks.json | |
autorestart=true | |
user=nobody | |
stdout_logfile=/var/log/shadowsocks.out.log | |
stderr_logfile=/var/log/shadowsocks.err.log | |
[include] | |
files = /etc/supervisor/conf.d/*.conf |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[program:worker] | |
command=node worker.js | |
numprocs=1 | |
directory=/home/app/ssbee | |
autostart=true | |
autorestart=true | |
user=app | |
stdout_logfile=/var/log/shadowsocks-%(program_name)s.out.log | |
stderr_logfile=/var/log/shadowsocks-%(program_name)s.err.log |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var WebSocket = require('ws'); | |
var request = require('request'); | |
var EventEmitter = require('events'); | |
var dgram = require('dgram'); | |
var si = require('systeminformation'); | |
var crypto = require('crypto'); | |
var xmlrpc = require('xmlrpc'); | |
var config = require('./config.json'); | |
var node_config = require('./node.json'); | |
var PORT = 6001; | |
var HOST = '127.0.0.1'; | |
var SYS_UPDATE_INTERVAL = 10 * 1000; // 10 s | |
var timeouts = {}; | |
var DEBUG = config.DEBUG; | |
var supervisor_client = xmlrpc.createClient({ | |
host: config.SUPERVISOR_CTL_HOST, | |
port: config.SUPERVISOR_CTL_PORT, | |
path: '/RPC2' | |
}); | |
var ip, iface = 'eth0'; | |
if (DEBUG) { | |
iface = 'eth1'; | |
} | |
function Encryption(key, iv) { | |
this.encrypt = function(data) { | |
var | |
encipher = crypto.createCipheriv('aes-256-cbc', key, iv), | |
encoded = encipher.update(data, 'utf-8', 'base64'); | |
encoded += encipher.final( 'base64' ); | |
return encoded; | |
}; | |
this.decrypt = function(data) { | |
var | |
decipher = crypto.createDecipheriv('aes-256-cbc', key, iv), | |
decoded = decipher.update(data, 'base64', 'utf-8'); | |
decoded += decipher.final( 'utf-8' ); | |
return decoded; | |
}; | |
} | |
var encryption = new Encryption(config.ENCRYPT_KEY, config.ENCRYPT_IV); | |
function handshake(ip, cb) { | |
function inner() { | |
var body = node_config; | |
body.code = 1; | |
body = JSON.stringify(body); | |
body = encryption.encrypt(body); | |
request.post({ | |
url: config.WORKER_URL, | |
body: body | |
}, function(error, response, body) { | |
if (!error && response.statusCode == 200){ | |
try { | |
var data = JSON.parse(body); | |
if (data.code == 1 && data.data && data.data.node_id) { | |
console.log(data.data.node_id); | |
if (typeof cb == "function") { | |
cb(data.data); | |
} | |
} else { | |
setTimeout(function() { | |
inner(); | |
console.log(data); | |
console.log('retry.'); | |
}, 2000); | |
} | |
} catch (SyntaxError) { | |
console.log('错误的消息' + body); | |
} | |
} else { | |
setTimeout(function() { | |
inner(); | |
console.log(error); | |
console.log('retry.'); | |
}, 2000); | |
} | |
}); | |
} | |
inner(); | |
} | |
function dance(node_id) { | |
var ws_uri = config.WORKER_WS_URI + node_id; | |
var ws_reconnect_time = 10; | |
var ws, e = new EventEmitter(); | |
function createWS() { | |
var ws = new WebSocket(ws_uri); | |
function reconnect() { | |
e.removeAllListeners('ss.flow'); | |
for (var key in timeouts) { | |
clearTimeout(timeouts[key]); | |
} | |
setTimeout(function() { | |
createWS(); | |
console.log('reconnect'); | |
}, ws_reconnect_time * 1000); | |
} | |
function send(data) { | |
if (ws) { | |
var message; | |
if (typeof data == 'string') { | |
message = data; | |
} else { | |
message = JSON.stringify(data); | |
} | |
ws.send(encryption.encrypt(message), function(err) { | |
if (err) { | |
e.removeAllListeners('ws.send'); | |
e.emit('ws.send', message); | |
console.log('ws closed' + err); | |
} | |
}); | |
} | |
} | |
function handle_message(data) { | |
switch (data.code) { | |
case 2: // add user | |
console.log('新用户来了'); | |
e.emit('ss.add', data); | |
break; | |
case 3: // remove user | |
console.log('要移除用户了'); | |
e.emit('ss.remove', data); | |
break; | |
case 6: // 同步 | |
console.log('同步'); | |
e.emit('ss.sync', data.data); | |
break; | |
case 7: // 同步 | |
console.log('改密码'); | |
e.emit('ss.change_password', data.data); | |
break; | |
case 40: | |
console.log('停止 supervisor shadowsocks'); | |
supervisor_client.methodCall('supervisor.stopProcess', [config.SUPERVISOR_PROCESS_NAME], function(err, res) { | |
if (err) { | |
send({ | |
code: 40, | |
err: err, | |
status: false | |
}); | |
} else { | |
send({ | |
code: 40, | |
status: true | |
}); | |
} | |
}); | |
break; | |
case 41: | |
console.log('开启 supervisor shadowsocks'); | |
supervisor_client.methodCall('supervisor.startProcess', [config.SUPERVISOR_PROCESS_NAME], function(err, res) { | |
if (err) { | |
send({ | |
code: 41, | |
err: err, | |
status: false | |
}); | |
} else { | |
send({ | |
code: 41, | |
status: true | |
}); | |
} | |
}); | |
break; | |
default: | |
console.log(data); | |
break; | |
} | |
} | |
ws.on('open', function() { | |
console.log('ws opened'); | |
e.removeAllListeners('ss.flow'); | |
e.on('ws.send', function(message) { | |
send(message); | |
}); | |
e.on('ss.flow', function(data) { | |
console.log('更新流量'); | |
send({ | |
code: 5, | |
data: { | |
node_id: node_id, | |
data: data | |
}, | |
message: '更新流量', | |
}); | |
}); | |
// 系统信息 | |
// 只更新一次的 | |
si.osInfo(function(info) { | |
send({ | |
code: 10, | |
data: info | |
}); | |
}); | |
si.cpu(function(info) { | |
send({ | |
code: 11, | |
data: info | |
}); | |
}); | |
// 系统信息 | |
// 不停刷新的 | |
(function memUpdate() { | |
// 内存 | |
si.mem(function(info) { | |
send({ | |
code: 12, | |
data: info, | |
}); | |
timeouts.mem = setTimeout(memUpdate, SYS_UPDATE_INTERVAL); | |
}); | |
})(); | |
(function fsSizeUpdate() { | |
// 硬盘容量 | |
si.fsSize(function(info) { | |
send({ | |
code: 13, | |
data: info | |
}); | |
timeouts.fsSize = setTimeout(fsSizeUpdate, SYS_UPDATE_INTERVAL); | |
}); | |
})(); | |
(function fsStatsUpdate() { | |
// 硬盘读写 | |
si.fsStats(function(info) { | |
send({ | |
code: 14, | |
data: info | |
}); | |
timeouts.fsStats = setTimeout(fsStatsUpdate, SYS_UPDATE_INTERVAL); | |
}); | |
})(); | |
(function networkStatsUpdate() { | |
// 网络读写 | |
si.networkStats(iface, function(info) { | |
send({ | |
code: 15, | |
data: info | |
}); | |
timeouts.net = setTimeout(networkStatsUpdate, SYS_UPDATE_INTERVAL); | |
}); | |
})(); | |
(function currentLoadUpdate() { | |
// 负载 | |
si.currentLoad(function(info) { | |
send({ | |
code: 16, | |
data: { | |
load: info, | |
uptime: si.time().uptime, | |
current: si.time().current | |
} | |
}); | |
timeouts.currentLoad = setTimeout(currentLoadUpdate, SYS_UPDATE_INTERVAL); | |
}); | |
})(); | |
(function fullLoadUpdate() { | |
// 负载2 | |
si.fullLoad(function(info) { | |
send({ | |
code: 17, | |
data: info | |
}); | |
timeouts.fullLoad = setTimeout(fullLoadUpdate, SYS_UPDATE_INTERVAL); | |
}); | |
})(); | |
(function ssInfoUpdate() { | |
// ss 负载 | |
si.processLoad('ssserver', function(info) { | |
send({ | |
code: 18, | |
data: info | |
}); | |
timeouts.ss = setTimeout(ssInfoUpdate, SYS_UPDATE_INTERVAL); | |
}); | |
})(); | |
(function supervisorInfoUpdate() { | |
// supervisor info 42 | |
supervisor_client.methodCall('supervisor.getSupervisorVersion', [], function(err, s_version) { | |
if (err) { | |
console.log(err); | |
} else { | |
supervisor_client.methodCall('supervisor.getState', [], function(err, res) { | |
if (err) { | |
console.log(err); | |
} else { | |
send({ | |
code: 42, | |
data: { | |
version: s_version, | |
statecode: res.statecode, | |
statename: res.statename | |
} | |
}); | |
} | |
timeouts.supervisor_info = setTimeout(supervisorInfoUpdate, SYS_UPDATE_INTERVAL); | |
}); | |
} | |
}); | |
})(); | |
(function supervisorShadowsocksUpdate() { | |
// supervisor shadowsocks 43 | |
supervisor_client.methodCall('supervisor.getProcessInfo', [config.SUPERVISOR_PROCESS_NAME], function(err, res) { | |
if (err) { | |
console.log(err); | |
} else { | |
send({ | |
code: 43, | |
data: res | |
}); | |
} | |
timeouts.supervisor_shadowsocks = setTimeout(supervisorShadowsocksUpdate, SYS_UPDATE_INTERVAL); | |
}); | |
})(); | |
}); | |
ws.on('message', function(message) { | |
// try { | |
message = encryption.decrypt(message); | |
var data = JSON.parse(message); | |
console.log(data); | |
handle_message(data); | |
// } catch (SyntaxError) { | |
console.log('错误的消息' + message); | |
// } | |
}); | |
ws.on('close', function(data) { | |
console.log('ws close, wait connect.'); | |
reconnect(); | |
}); | |
ws.on('error', function(data) { | |
console.log('ws error, wait connect.'); | |
reconnect(); | |
}); | |
} | |
var client; | |
var socket_reconnect_time = 10; | |
function createUDP() { | |
// function reconnect() { | |
// e.removeAllListeners('ss.add'); | |
// e.removeAllListeners('ss.remove'); | |
// setTimeout(function() { | |
// createUDP(); | |
// console.log('reconnect ss'); | |
// }, socket_reconnect_time * 1000); | |
// } | |
function send(message, cb) { | |
client.send(message, 0, message.length, PORT, HOST, cb); | |
} | |
client = dgram.createSocket('udp4', function(message) { | |
var data = message.toString('utf-8'); | |
if (!data) { | |
console.log(message); | |
} | |
if (data.startsWith('stat: ')) { | |
// 上报流量 | |
data = JSON.parse(data.slice(6)); | |
console.log(data); | |
e.emit('ss.flow', data); | |
} else if (data.startsWith('pong')) { | |
// 连接上了 | |
console.log('pong'); | |
} | |
e.removeAllListeners('ss.add'); | |
e.removeAllListeners('ss.remove'); | |
e.removeAllListeners('ss.sync'); | |
e.removeAllListeners('ss.change_password'); | |
e.on('ss.sync', function(users) { | |
users.forEach(function(user) { | |
e.emit('ss.add', user); | |
}); | |
}); | |
e.on('ss.change_password', function(user) { | |
var ss_info = { | |
server_port: user.port, | |
password: user.old_password | |
}; | |
send('remove: ' + JSON.stringify(ss_info), function(err, bytes) { | |
console.log('remove user'); | |
ss_info = { | |
server_port: user.port, | |
password: user.new_password | |
}; | |
send('add: ' + JSON.stringify(ss_info), function(err, bytes) { | |
console.log('add user'); | |
}); | |
}); | |
}); | |
e.on('ss.add', function(user) { | |
var ss_info = { | |
server_port: user.port, | |
password: user.password | |
}; | |
send('add: ' + JSON.stringify(ss_info), function(err, bytes) { | |
console.log('add user'); | |
}); | |
}); | |
e.on('ss.remove', function(user) { | |
var ss_info = { | |
server_port: user.port, | |
password: user.password | |
}; | |
send('remove: ' + JSON.stringify(ss_info), function(err, bytes) { | |
console.log('remove user'); | |
}); | |
}); | |
}); | |
// client.send('ping', 0, 'ping'.length, PORT, HOST, function(err, bytes) { | |
// if (err) throw err; | |
// console.log('UDP message sent ping to ' + HOST +':'+ PORT); | |
// //client.close(); | |
// }); | |
send('ping', function(err, bytes) { | |
if (err) throw err; | |
console.log('ping'); | |
}); | |
client.on('close', function() { | |
console.log('ss UDP close, wait connect.'); | |
reconnect(); | |
}); | |
client.on('error', function() { | |
console.log('ss UDP error, wait connect.'); | |
reconnect(); | |
}); | |
} | |
createWS(); | |
createUDP(); | |
} | |
si.networkInterfaces(function(info) { | |
info.forEach(function(net) { | |
if (iface == net.iface) { | |
ip = net.ip4; | |
return; | |
} | |
}); | |
console.log('Net iface: ' + iface); | |
console.log('IP: ' + ip); | |
handshake(ip, function(data) { | |
// 握手 | |
console.log(data); | |
dance(data.node_id); | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment