Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
bafflingbug committed Nov 25, 2017
0 parents commit 421c0e9
Show file tree
Hide file tree
Showing 12 changed files with 249 additions and 0 deletions.
68 changes: 68 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# ShadowsocksR-support
一个在服务器端生成ShadowsockR服务器订阅的简单实现

### 设计目的
- 防止因为DNS解析导致SSR服务器ip被BAN
- 降低因服务器新增/迁移,SSR设置更新等的工作量

### 部署方式
- 主服务器的部署
1. 拷贝php目录下文件到主服务器
2. 修改`config.json`配置文件
3. 配置Nginx/apache等Web服务器
- SSR服务器的部署
1. 将python目录下的拷贝到部署SSR的服务器
2. 修改`config.json`配置文件
3. 添加开机启动(必须设置时间延迟,否则会导致网络错误)
1. 打开`/etc/rc.d/rc.local`
2. 添加
```
(
sleep 120
nohup python /项目路径/python/main.py > /项目路径/python/log.out 2>&1
)&
```
4. 添加定时任务
1. 控制台输入`crontab -e`
2. 添加
```
0 * * * * python /项目路径/python/main.py &
```
### config.json配置文件
- 主服务器config.json
```
{
"group": "PowerByBafflingBUG", //SSR GROUP
"token": "password" //验证,用于确认权限(弱验证)
}
```
- SSR服务器config.json
```
{
"ss-config-file": ["ss_config1","ss_config2"], //SS服务器的配置文件路径
"ssr-config-file": ["ssr_config1","ssr_config2"], //SSR服务器的配置文件路径
"main-server": "ssr.example.com", //主服务器的域名/IP
"host": "0.0.0.0", //当前服务器的外网IP
"token": "password", //验证,需与主服务器一致
"ssl": false //主服务器是否使用SSL(https)
}
```
### SSR服务器订阅地址
**http(s)://[main-server]/?token=[token]**
示例:*http://example.com/?token=password*
### 运行流程
1. SSR服务器每小时检测本地各配置文件(包括SS配置文件,SSR配置文件,SSRS配置文件)是否被更新
2. - 若有更新 向主服务器推送新的SSR链接
- 若无更新 向主服务器发送SSR服务器在线确认
> - 若主服务器不存在当前服务器的记录 重新推送SSR链接到主服务器
3. 主服务器记录各SSR服务器的推送/确认时间,且抛弃3小时未确认的服务器的SSR链接
### 写在最后
- 这个一个个人项目,没有做很多的兼容性判断。可能会在其他环境下无法运行
- python的运行环境是2.7
- 选择python和php的原因是我现在使用的两台服务器的运行环境
18 changes: 18 additions & 0 deletions php/active.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php
date_default_timezone_set('Asia/Shanghai');
$json = file_get_contents('./config.json');
$config = json_decode($json, true);
if ($config["token"] !== $_POST["token"]){
echo "error:1";
}else{
$json = file_get_contents('./ssrURL.json');
$url = json_decode($json, true);
if(array_key_exists("lasttime", $url[$_POST["host"]])){
$url[$_POST["host"]]["lasttime"] = date("y-m-d H:i:s");
$json = json_encode($url);
file_put_contents('./ssrURL.json', $json);
}else{
echo "error:2";
}
}
?>
4 changes: 4 additions & 0 deletions php/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"group": "PowerByBafflingBUG",
"token": "password"
}
5 changes: 5 additions & 0 deletions php/group.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php
$json = file_get_contents('./config.json');
$config = json_decode($json, true);
echo array_key_exists("group", $config)?$config["group"]:"PowerByBafflingBUG";
?>
18 changes: 18 additions & 0 deletions php/index.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php
date_default_timezone_set('Asia/Shanghai');
$json = file_get_contents('./config.json');
$config = json_decode($json, true);
if ($config["token"] !== $_GET["token"]){
echo "error:1";
}else{
$json = file_get_contents('./ssrURL.json');
$url = json_decode($json, true);
$ssr = "";
foreach($url as $host => $value){
if((strtotime(date("y-m-d H:i:s"))-strtotime($value["lasttime"]))/3600 < 3.0){
$ssr .= $value["ssr"];
}
}
echo base64_encode($ssr);
}
?>
18 changes: 18 additions & 0 deletions php/post.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php
date_default_timezone_set('Asia/Shanghai');
$json = file_get_contents('./config.json');
$config = json_decode($json, true);
if ($config["token"] !== $_POST["token"]){
echo "error:1";
}else{
$json = file_get_contents('./ssrURL.json');
$url = json_decode($json, true);
echo "1";
$url[$_POST["host"]] = array(
"ssr" => $_POST["ssr"],
"lasttime" => date("y-m-d H:i:s")
);
$json = json_encode($url);
file_put_contents('./ssrURL.json', $json);
}
?>
1 change: 1 addition & 0 deletions php/ssrURL.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
1 change: 1 addition & 0 deletions python/MD5.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
8 changes: 8 additions & 0 deletions python/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"ss-config-file": ["ss_config1","ss_config2"],
"ssr-config-file": ["ssr_config1","ssr_config2"],
"main-server": "ssr.example.com",
"host": "0.0.0.0",
"token": "password",
"ssl": false
}
46 changes: 46 additions & 0 deletions python/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import json
import os
from network import *
from ssrs import *

if __name__ == "__main__":
update = False
dir_path = os.path.dirname(os.path.realpath(__file__))
try:
MD5_file = open(dir_path + '/MD5.json', 'r')
fileMD5 = json.load(MD5_file)
except:
fileMD5 = {}
update = True
finally:
MD5_file.close()
update = new_config(dir_path + '/config.json', fileMD5, update)
config_file = open(dir_path + '/config.json', 'r')
config = json.load(config_file)
config_file.close()
group = getgroup('http://' if not config['ssl'] else 'https://' + config['main-server'] + '/group.php')
if 'group' not in fileMD5.keys() or group != fileMD5['group']:
print(group)
update = True
fileMD5['group'] = group
ssr_url = ''
for ss_config_file_name in config['ss-config-file']:
update = new_config(ss_config_file_name, fileMD5, update)
ssr_url += ss2URL(ss_config_file_name, config, group)
for ssr_config_file_name in config['ssr-config-file']:
update = new_config(ssr_config_file_name, fileMD5, update)
ssr_url += ssr2URL(ssr_config_file_name, config, group)
if update:
print(ssr_url)
MD5_file = open(dir_path + '/MD5.json', 'w')
json.dump(fileMD5, MD5_file)
MD5_file.close()
res = post('http://' if not config['ssl'] else 'https://' + config['main-server'] + '/post.php', ssr_url,
config['token'], config['host'])
else:
state = active('http://' if not config['ssl'] else 'https://' + config['main-server'] + '/active.php', config['token'], config['host'])
if state.find('error:2') >= 0:
MD5_file = open(dir_path + '/MD5.json', 'w')
MD5_file.truncate()
MD5_file.close()
os.system('python ' + dir_path + '/ssrs.py')
21 changes: 21 additions & 0 deletions python/network.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import urllib
import urllib2


def getgroup(url):
res = urllib2.urlopen(url)
return str(res.read())


def post(url, data, token, host):
parm = urllib.urlencode({'ssr': data, "token": token, "host": host})
req = urllib2.Request(url, parm)
res = urllib2.urlopen(req)
return str(res.read())


def active(url, token, host):
parm = urllib.urlencode({"token": token, "host": host})
req = urllib2.Request(url, parm)
res = urllib2.urlopen(req)
return str(res.read())
41 changes: 41 additions & 0 deletions python/ssrs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import base64
import json
import hashlib
from network import *

def new_config(file, fileMD5, update):
config_file = open(file, 'rb')
md5 = hashlib.md5(config_file.read()).hexdigest()
config_file.close()
if file in fileMD5.keys() and fileMD5[file] == md5:
return update
fileMD5[file] = md5
return True


def ssr2URL(file, config, group):
ssr_config_file = open(file)
ssr_config = json.load(ssr_config_file)
param_str = "obfsparam=" + base64.urlsafe_b64encode(ssr_config['obfs_param'].encode() if not ssr_config['obfs_param'] is None else ''.encode()).decode().rstrip('=')
if 'protocol_param' in ssr_config.keys() and (not ssr_config['protocol_param'] == ""):
param_str += '&protoparam=' + base64.urlsafe_b64encode(ssr_config['protocol_param'].encode()).decode().rstrip('=')
if 'remarks' in ssr_config.keys() and (not ssr_config['remarks'] == ""):
param_str += '&remarks=' + base64.urlsafe_b64encode(ssr_config['remarks'].encode()).decode().rstrip('=')
param_str += '&group=' + base64.urlsafe_b64encode(group.encode()).decode().rstrip('=')
main_part = config["host"] + ':' + str(ssr_config['server_port']) + ':' + ssr_config['protocol'] + ':' + ssr_config['method'] + ':' + ssr_config['obfs'] + ':' + base64.urlsafe_b64encode(ssr_config['password'].encode()).decode().rstrip('=')
ssr_config_file.close()
b64 = base64.urlsafe_b64encode((main_part + '/?' + param_str).encode()).decode().rstrip('=')
return 'ssr://' + b64 + '\n'


def ss2URL(file, config, group):
ss_config_file = open(file)
ss_config = json.load(ss_config_file)
param_str = "obfsparam=" + ''
if 'remarks' in ss_config.keys() and (not ss_config['remarks'] == ""):
param_str += '&remarks=' + base64.urlsafe_b64encode(ss_config['remarks'].encode()).decode().rstrip('=')
param_str += '&group=' + base64.urlsafe_b64encode(group.encode()).decode().rstrip('=')
main_part = config["host"] + ':' + str(ss_config['server_port']) + ':' + 'origin' + ':' + ss_config['method'] + ':' + 'plain' + ':' + base64.urlsafe_b64encode(ss_config['password'].encode()).decode().rstrip('=')
ss_config_file.close()
b64 = base64.urlsafe_b64encode((main_part + '/?' + param_str).encode()).decode().rstrip('=')
return 'ssr://' + b64 + '\n'

0 comments on commit 421c0e9

Please sign in to comment.