├── .gitignore ├── LICENSE ├── README.md ├── ssadmin.sh ├── sscounter.sh ├── sshelp ├── sslib.sh └── ssmlt.template /.gitignore: -------------------------------------------------------------------------------- 1 | *.json 2 | ssusers 3 | *.log 4 | sstraffic 5 | tmp 6 | *.swp 7 | *~ 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 hellofwy 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ss-bash 2 | ======= 3 | 4 | Shadowsocks流量管理脚本 5 | 6 | * 目前只支持python版Shadowsocks 7 | * 目前只支持统计ipv4流量 8 | 9 | [英文版](https://github.com/hellofwy/ss-bash/tree/en)请见`en`分支,感谢[@Yaoshicn](https://github.com/Yaoshicn)的贡献。 10 | 11 | 12 | [使用说明][User Manual] 13 | 14 | 15 | [User Manual]: https://github.com/hellofwy/ss-bash/wiki 16 | -------------------------------------------------------------------------------- /ssadmin.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (c) 2014 hellofwy 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | SOURCE="${BASH_SOURCE[0]}" 24 | while [ -h "$SOURCE" ]; do 25 | DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" 26 | SOURCE="$(readlink "$SOURCE")" 27 | [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" 28 | done 29 | DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" 30 | 31 | . $DIR/sslib.sh 32 | 33 | usage () { 34 | cat $DIR/sshelp 35 | } 36 | wrong_para_prompt() { 37 | echo "参数输入错误!" 38 | echo "查看帮助:ssadmin.sh -h" 39 | } 40 | 41 | #根据用户文件生成ssserver配置文件 42 | create_json () { 43 | echo '{' > $JSON_FILE.tmp 44 | sed -E 's/(.*)/ \1/' $DIR/ssmlt.template >> $JSON_FILE.tmp 45 | awk ' 46 | BEGIN { 47 | i=1; 48 | printf(" \"port_password\": {\n"); 49 | } 50 | ! /^#|^\s*$/ { 51 | port=$1; 52 | pw=$2; 53 | ports[i++] = port; 54 | pass[port]=pw; 55 | } 56 | END { 57 | for(j=1;j> $JSON_FILE.tmp 66 | echo '}' >> $JSON_FILE.tmp 67 | mv $JSON_FILE.tmp $JSON_FILE 68 | 69 | } 70 | 71 | run_ssserver () { 72 | $SSSERVER -qq -c $JSON_FILE 2>/dev/null >/dev/null & 73 | echo $! > $SSSERVER_PID 74 | } 75 | 76 | check_ssserver () { 77 | if [ -e $SSSERVER_PID ]; then 78 | ps $(cat $SSSERVER_PID) 2>/dev/null | grep $SSSERVER_NAME 2>/dev/null 79 | return $? 80 | else 81 | return 1 82 | fi 83 | } 84 | 85 | check_sscounter () { 86 | if [ -e $SSCOUNTER_PID ]; then 87 | ps $(cat $SSCOUNTER_PID) 2>/dev/null | grep sscounter 2>/dev/null 88 | return $? 89 | else 90 | return 1 91 | fi 92 | } 93 | 94 | start_ss () { 95 | if [ ! -e $USER_FILE ]; then 96 | echo "还没有用户,请先添加一个用户" 97 | return 1 98 | fi 99 | if [ -e $SSSERVER_PID ]; then 100 | if check_ssserver; then 101 | echo 'ss服务已启动,同一实例不能启动多次!' 102 | return 1 103 | else 104 | rm $SSSERVER_PID 105 | fi 106 | fi 107 | create_json 108 | 109 | if [ -e $SSCOUNTER_PID ]; then 110 | if check_sscounter ; then 111 | kill `cat $SSCOUNTER_PID` 112 | else 113 | rm $SSCOUNTER_PID 114 | fi 115 | fi 116 | 117 | echo 'sscounter.sh启动中...' 118 | ( $DIR/sscounter.sh ) & 119 | echo $! > $SSCOUNTER_PID 120 | if check_sscounter; then 121 | echo 'sscounter.sh已启动' 122 | else 123 | echo 'sscounter.sh启动失败' 124 | return 1 125 | fi 126 | 127 | echo 'ssserver启动中...' 128 | run_ssserver 129 | sleep 1 130 | if check_ssserver; then 131 | echo 'ssserver已启动' 132 | else 133 | echo 'ssserver启动失败' 134 | return 1 135 | fi 136 | } 137 | 138 | stop_ss () { 139 | if check_ssserver; then 140 | kill `cat $SSSERVER_PID` 141 | rm $SSSERVER_PID 142 | del_ipt_chains 2> /dev/null 143 | echo 'ssserver已关闭' 144 | else 145 | echo 'ssserver未启动' 146 | fi 147 | if check_sscounter; then 148 | kill `cat $SSCOUNTER_PID` 149 | rm $SSCOUNTER_PID 150 | echo 'sscounter.sh已关闭' 151 | else 152 | echo 'sscounter.sh未启动' 153 | fi 154 | } 155 | 156 | restart_ss () { 157 | stop_ss 158 | start_ss 159 | } 160 | 161 | soft_restart_ss () { 162 | if check_ssserver; then 163 | kill -s SIGQUIT `cat $SSSERVER_PID` 164 | echo 'ssserver已关闭' 165 | kill `cat $SSCOUNTER_PID` 166 | echo 'sscounter.sh已关闭' 167 | rm $SSSERVER_PID $SSCOUNTER_PID 168 | del_ipt_chains 2> /dev/null 169 | start_ss 170 | else 171 | echo 'ssserver未启动' 172 | fi 173 | } 174 | 175 | status_ss () { 176 | if check_ssserver; then 177 | echo 'ssserver正在运行' 178 | else 179 | echo 'ssserver未启动' 180 | fi 181 | if check_sscounter; then 182 | echo 'sscounter.sh正在运行' 183 | else 184 | echo 'sscounter.sh未启动' 185 | fi 186 | } 187 | 188 | bytes2gb () { 189 | TLIMIT=$1 190 | echo "$TLIMIT" | 191 | sed -E 's/[kK][bB]?/ * 1024/' | 192 | sed -E 's/[mM][bB]?/ * 1024 * 1024/' | 193 | sed -E 's/[gG][bB]?/ * 1024 * 1024 * 1024/' | 194 | sed -E 's/[tT][bB]?/ * 1024 * 1024 * 1024 * 1024/' | 195 | bc | 196 | awk '{printf("%.0f", $1)}' 197 | } 198 | check_port_range () { 199 | PORT=$1 200 | if (( ($PORT > 0) && ($PORT <= 65535 ) )); then 201 | return 0 202 | else 203 | return 1 204 | fi 205 | } 206 | add_user () { 207 | if [ "$#" -ne 3 ]; then 208 | wrong_para_prompt; 209 | return 1 210 | fi 211 | PORT=$1 212 | if check_port_range $PORT; then 213 | : 214 | else 215 | wrong_para_prompt; 216 | return 1 217 | fi 218 | PWORD=$2 219 | TLIMIT=$3 220 | TLIMIT=`bytes2gb $TLIMIT` 221 | if [ ! -e $USER_FILE ]; then 222 | echo "\ 223 | # 以空格、制表符分隔 224 | # 端口 密码 流量限制 225 | # 2345 abcde 1000000" > $USER_FILE; 226 | fi 227 | cat $USER_FILE | 228 | awk ' 229 | { 230 | if($1=='$PORT') exit 1 231 | }' 232 | if [ $? -eq 0 ]; then 233 | echo "\ 234 | $PORT $PWORD $TLIMIT" >> $USER_FILE; 235 | else 236 | echo "用户已存在!" 237 | return 1 238 | fi 239 | # 重新生成配置文件,并加载 240 | if [ -e $SSSERVER_PID ]; then 241 | create_json 242 | kill -s SIGQUIT `cat $SSSERVER_PID` 243 | add_rules $PORT 244 | run_ssserver 245 | fi 246 | # 更新流量记录文件 247 | update_or_create_traffic_file_from_users 248 | calc_remaining 249 | } 250 | 251 | del_user () { 252 | if [ "$#" -ne 1 ]; then 253 | wrong_para_prompt; 254 | return 1 255 | fi 256 | PORT=$1 257 | if check_port_range $PORT; then 258 | : 259 | else 260 | wrong_para_prompt; 261 | return 1 262 | fi 263 | if [ -e $USER_FILE ]; then 264 | sed -i '/^\s*'$PORT'\s/ d' $USER_FILE 265 | fi 266 | # 重新生成配置文件,并加载 267 | if [ -e $SSSERVER_PID ]; then 268 | create_json 269 | kill -s SIGQUIT `cat $SSSERVER_PID` 270 | del_rules $PORT 2>/dev/null 271 | del_reject_rules $PORT 2>/dev/null 272 | run_ssserver 273 | fi 274 | # 更新流量记录文件 275 | update_or_create_traffic_file_from_users 276 | calc_remaining 277 | } 278 | 279 | change_user () { 280 | if [ "$#" -ne 3 ]; then 281 | wrong_para_prompt; 282 | return 1 283 | fi 284 | PORT=$1 285 | if check_port_range $PORT; then 286 | : 287 | else 288 | wrong_para_prompt; 289 | return 1 290 | fi 291 | PWORD=$2 292 | TLIMIT=$3 293 | TLIMIT=`bytes2gb $TLIMIT` 294 | if [ ! -e $USER_FILE ]; then 295 | echo "目前还无用户,请先添加用户" 296 | return 1 297 | fi 298 | if grep -q "^\s*$PORT\s" $USER_FILE; then 299 | cat $USER_FILE | 300 | awk ' 301 | { 302 | if($1=='$PORT') { 303 | printf("'$PORT' '$PWORD' '$TLIMIT'\n"); 304 | } else { 305 | print $0 306 | } 307 | }' > $USER_FILE.tmp; 308 | mv $USER_FILE.tmp $USER_FILE 309 | # 重新生成配置文件,并加载 310 | if [ -e $SSSERVER_PID ]; then 311 | create_json 312 | kill -s SIGQUIT `cat $SSSERVER_PID` 313 | add_rules $PORT 314 | run_ssserver 315 | fi 316 | # 更新流量记录文件 317 | update_or_create_traffic_file_from_users 318 | calc_remaining 319 | else 320 | echo "此用户不存在!" 321 | return 1 322 | fi 323 | } 324 | 325 | change_passwd () { 326 | if [ "$#" -ne 2 ]; then 327 | wrong_para_prompt; 328 | return 1 329 | fi 330 | PORT=$1 331 | if check_port_range $PORT; then 332 | : 333 | else 334 | wrong_para_prompt; 335 | return 1 336 | fi 337 | PWORD=$2 338 | if [ ! -e $USER_FILE ]; then 339 | echo "目前还无用户,请先添加用户" 340 | return 1 341 | fi 342 | if grep -q "^\s*$PORT\s" $USER_FILE; then 343 | cat $USER_FILE | 344 | awk ' 345 | { 346 | if($1=='$PORT') { 347 | printf("'$PORT' '$PWORD' %s\n", $3); 348 | } else { 349 | print $0 350 | } 351 | }' > $USER_FILE.tmp; 352 | mv $USER_FILE.tmp $USER_FILE 353 | # 重新生成配置文件,并加载 354 | if [ -e $SSSERVER_PID ]; then 355 | create_json 356 | kill -s SIGQUIT `cat $SSSERVER_PID` 357 | add_rules $PORT 358 | run_ssserver 359 | fi 360 | # 更新流量记录文件 361 | update_or_create_traffic_file_from_users 362 | calc_remaining 363 | else 364 | echo "此用户不存在!" 365 | return 1 366 | fi 367 | } 368 | 369 | change_limit () { 370 | if [ "$#" -ne 2 ]; then 371 | wrong_para_prompt; 372 | return 1 373 | fi 374 | PORT=$1 375 | if check_port_range $PORT; then 376 | : 377 | else 378 | wrong_para_prompt; 379 | return 1 380 | fi 381 | TLIMIT=$2 382 | TLIMIT=`bytes2gb $TLIMIT` 383 | if [ ! -e $USER_FILE ]; then 384 | echo "目前还无用户,请先添加用户" 385 | return 1 386 | fi 387 | if grep -q "^\s*$PORT\s" $USER_FILE; then 388 | cat $USER_FILE | 389 | awk ' 390 | { 391 | if($1=='$PORT') { 392 | printf("'$PORT' %s '$TLIMIT'\n", $2); 393 | } else { 394 | print $0 395 | } 396 | }' > $USER_FILE.tmp; 397 | mv $USER_FILE.tmp $USER_FILE 398 | # 更新流量记录文件 399 | update_or_create_traffic_file_from_users 400 | calc_remaining 401 | else 402 | echo "此用户不存在!" 403 | return 1 404 | fi 405 | } 406 | 407 | change_all_limit () { 408 | if [ "$#" -ne 1 ]; then 409 | wrong_para_prompt; 410 | return 1 411 | fi 412 | TLIMIT=$1 413 | TLIMIT=`bytes2gb $TLIMIT` 414 | if [ ! -e $USER_FILE ]; then 415 | echo "目前还无用户,请先添加用户" 416 | return 1 417 | fi 418 | cat $USER_FILE | 419 | awk ' 420 | { 421 | if($0 !~ /^#/ && $0 !~ /^\s.*$/) { 422 | printf("%s %s '$TLIMIT'\n", $1, $2); 423 | } else { 424 | print $0 425 | } 426 | }' > $USER_FILE.tmp; 427 | mv $USER_FILE.tmp $USER_FILE 428 | # 更新流量记录文件 429 | update_or_create_traffic_file_from_users 430 | calc_remaining 431 | } 432 | 433 | show_user () { 434 | if [ $# -eq 0 ]; then 435 | cat $TRAFFIC_FILE; 436 | else 437 | if [ "$#" -ne 1 ]; then 438 | wrong_para_prompt; 439 | return 1 440 | fi 441 | PORT=$1 442 | if check_port_range $PORT; then 443 | : 444 | else 445 | wrong_para_prompt; 446 | return 1 447 | fi 448 | res=`grep "^\s*$PORT\s" $TRAFFIC_FILE` 449 | if [ -z "$res" ]; then 450 | echo "此用户不存在!" 451 | else 452 | head -n1 $TRAFFIC_FILE 453 | echo "$res" 454 | fi 455 | fi 456 | } 457 | 458 | show_passwd () { 459 | if [ $# -eq 0 ]; then 460 | cat $USER_FILE; 461 | else 462 | if [ "$#" -ne 1 ]; then 463 | wrong_para_prompt; 464 | return 1 465 | fi 466 | PORT=$1 467 | if check_port_range $PORT; then 468 | : 469 | else 470 | wrong_para_prompt; 471 | return 1 472 | fi 473 | res=`grep "^\s*$PORT\s" $USER_FILE` 474 | if [ -z "$res" ]; then 475 | echo "此用户不存在!" 476 | else 477 | head -n2 $USER_FILE 478 | echo "$res" 479 | fi 480 | fi 481 | } 482 | reset_limit () { 483 | if [ ! -e $USER_FILE ]; then 484 | echo "目前还无用户,请先添加用户" 485 | return 1 486 | fi 487 | if [ $# -eq 0 ]; then 488 | cat $USER_FILE | 489 | awk ' 490 | { 491 | if($0 !~ /^#/ && $0 !~ /^\s.*$/) { 492 | printf("%s %s 0\n", $1, $2); 493 | } else { 494 | print $0 495 | } 496 | }' > $USER_FILE.tmp; 497 | mv $USER_FILE.tmp $USER_FILE 498 | # 更新流量记录文件 499 | update_or_create_traffic_file_from_users 500 | calc_remaining 501 | else 502 | if [ "$#" -ne 1 ]; then 503 | wrong_para_prompt; 504 | return 1 505 | fi 506 | PORT=$1 507 | if check_port_range $PORT; then 508 | : 509 | else 510 | wrong_para_prompt; 511 | return 1 512 | fi 513 | if grep -q "^\s*$PORT\s" $USER_FILE; then 514 | cat $USER_FILE | 515 | awk ' 516 | { 517 | if($1=='$PORT') { 518 | printf("'$PORT' %s 0\n", $2); 519 | } else { 520 | print $0 521 | } 522 | }' > $USER_FILE.tmp; 523 | mv $USER_FILE.tmp $USER_FILE 524 | # 更新流量记录文件 525 | update_or_create_traffic_file_from_users 526 | calc_remaining 527 | else 528 | echo "此用户不存在!" 529 | return 1 530 | fi 531 | fi 532 | } 533 | 534 | reset_used () { 535 | if [ ! -e $USER_FILE ]; then 536 | echo "目前还无用户,请先添加用户" 537 | return 1 538 | fi 539 | while [ -e $TRAFFIC_LOG.lock ]; do 540 | sleep 1 541 | done 542 | touch $TRAFFIC_LOG.lock 543 | if [ $# -eq 0 ]; then 544 | cat $TRAFFIC_LOG | 545 | awk ' 546 | { 547 | if($0 !~ /^#/ && $0 !~ /^\s.*$/) { 548 | printf("%-5d\t0\n", $1); 549 | } else { 550 | print $0 551 | } 552 | }' > $TRAFFIC_LOG.tmp; 553 | mv $TRAFFIC_LOG.tmp $TRAFFIC_LOG 554 | else 555 | if [ "$#" -ne 1 ]; then 556 | wrong_para_prompt; 557 | return 1 558 | fi 559 | PORT=$1 560 | if check_port_range $PORT; then 561 | : 562 | else 563 | wrong_para_prompt; 564 | return 1 565 | fi 566 | if grep -q "^\s*$PORT\s" $USER_FILE; then 567 | cat $TRAFFIC_LOG | 568 | awk ' 569 | { 570 | if($1=='$PORT') { 571 | printf("%-5d\t0\n", '$PORT'); 572 | } else { 573 | print $0 574 | } 575 | }' > $TRAFFIC_LOG.tmp; 576 | mv $TRAFFIC_LOG.tmp $TRAFFIC_LOG 577 | else 578 | echo "此用户不存在!" 579 | rm $TRAFFIC_LOG.lock 580 | return 1 581 | fi 582 | fi 583 | rm $TRAFFIC_LOG.lock 584 | # 更新流量记录文件 585 | calc_remaining 586 | } 587 | 588 | if [ "$#" -eq 0 ]; then 589 | usage 590 | exit 0 591 | fi 592 | case $1 in 593 | -h|h|help ) 594 | usage 595 | exit 0; 596 | ;; 597 | -v|v|version ) 598 | echo 'ss-bash Version 1.0-beta.3, 2014-12-3, Copyright (c) 2014 hellofwy' 599 | exit 0; 600 | ;; 601 | esac 602 | if [ "$EUID" -ne 0 ]; then 603 | echo "必需以root身份运行,请使用sudo等命令" 604 | exit 1; 605 | fi 606 | if type $SSSERVER 2>&1 >/dev/null; then 607 | : 608 | else 609 | echo "无法找到ssserver程序,请在sslib.sh中指定其路径" 610 | exit 1; 611 | fi 612 | case $1 in 613 | add ) 614 | shift 615 | add_user $1 $2 $3 616 | ;; 617 | del ) 618 | shift 619 | del_user $1 620 | ;; 621 | show ) 622 | shift 623 | show_user $1 624 | ;; 625 | showpw ) 626 | shift 627 | show_passwd $1 628 | ;; 629 | change ) 630 | shift 631 | change_user $1 $2 $3 632 | ;; 633 | cpw ) 634 | shift 635 | change_passwd $1 $2 636 | ;; 637 | clim ) 638 | shift 639 | change_limit $1 $2 640 | ;; 641 | rlim ) 642 | shift 643 | if [ $# -eq 0 ]; then 644 | echo "请指定用户端口号" 645 | exit 1 646 | else 647 | reset_limit $1 648 | fi 649 | ;; 650 | change_all_limit ) 651 | shift 652 | change_all_limit $1 653 | ;; 654 | reset_all_limit ) 655 | shift 656 | reset_limit 657 | ;; 658 | rused ) 659 | shift 660 | if [ $# -eq 0 ]; then 661 | echo "请指定用户端口号" 662 | exit 1 663 | else 664 | reset_used $1 665 | fi 666 | ;; 667 | reset_all_used ) 668 | shift 669 | reset_used 670 | ;; 671 | start ) 672 | start_ss 673 | ;; 674 | stop ) 675 | stop_ss 676 | ;; 677 | restart ) 678 | restart_ss 679 | ;; 680 | status ) 681 | status_ss 682 | ;; 683 | soft_restart ) 684 | soft_restart_ss 685 | ;; 686 | lrules ) 687 | list_rules 688 | ;; 689 | * ) 690 | usage 691 | ;; 692 | esac 693 | 694 | -------------------------------------------------------------------------------- /sscounter.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (c) 2014 hellofwy 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | SOURCE="${BASH_SOURCE[0]}" 24 | while [ -h "$SOURCE" ]; do 25 | DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" 26 | SOURCE="$(readlink "$SOURCE")" 27 | [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" 28 | done 29 | DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" 30 | 31 | . $DIR/sslib.sh 32 | 33 | update_or_create_traffic_file_from_users 34 | init_ipt_chains 35 | add_new_rules 36 | calc_remaining 37 | > $PORTS_ALREADY_BAN 38 | check_traffic_against_limits 39 | 40 | ISFIRST=1; 41 | while true; do 42 | # 是否第一次运行,第一次则生成临时流量记录 43 | if [ $ISFIRST -eq 1 ]; then 44 | get_traffic_from_iptables_first_time 45 | ISFIRST=0; 46 | sleep $INTERVEL 47 | continue 48 | else 49 | #计算每个时间间隔内的流量使用量 50 | get_traffic_from_iptables_now 51 | calc_traffic_between_intervel 52 | fi 53 | # 将流量记录添加到文件中 54 | update_traffic_record 55 | # 验证流量是否超过预设值 56 | calc_remaining 57 | check_traffic_against_limits 58 | sleep $INTERVEL 59 | done 60 | -------------------------------------------------------------------------------- /sshelp: -------------------------------------------------------------------------------- 1 | 用法: 2 | 显示版本: 3 | ssadmin.sh -v|v|version 4 | 显示帮助: 5 | ssadmin.sh [-h|h|help] 6 | 启动ss: 7 | ssadmin.sh start 8 | 停止ss: 9 | ssadmin.sh stop 10 | 查看ss状态: 11 | ssadmin.sh status 12 | 重启ss: 13 | ssadmin.sh restart 14 | 软重启ss: 15 | ssadmin.sh soft_restart 16 | 在不影响现有连接的情况下重启ss服务。用于ss服务参数修改, 17 | 和手动直接修改配置文件后,重启ss服务。 18 | 添加用户: 19 | ssadmin.sh add port passwd limit 20 | port:端口号, 0 /dev/null 60 | iptables -N $SS_IN_RULES 61 | iptables -N $SS_OUT_RULES 62 | iptables -A INPUT -j $SS_IN_RULES 63 | iptables -A OUTPUT -j $SS_OUT_RULES 64 | } 65 | 66 | add_rules () { 67 | PORT=$1; 68 | iptables -A $SS_IN_RULES -p tcp --dport $PORT -j ACCEPT 69 | iptables -A $SS_OUT_RULES -p tcp --sport $PORT -j ACCEPT 70 | iptables -A $SS_IN_RULES -p udp --dport $PORT -j ACCEPT 71 | iptables -A $SS_OUT_RULES -p udp --sport $PORT -j ACCEPT 72 | } 73 | 74 | add_reject_rules () { 75 | PORT=$1; 76 | iptables -A $SS_IN_RULES -p tcp --dport $PORT -j REJECT 77 | iptables -A $SS_OUT_RULES -p tcp --sport $PORT -j REJECT 78 | iptables -A $SS_IN_RULES -p udp --dport $PORT -j REJECT 79 | iptables -A $SS_OUT_RULES -p udp --sport $PORT -j REJECT 80 | } 81 | 82 | del_rules () { 83 | PORT=$1; 84 | iptables -D $SS_IN_RULES -p tcp --dport $PORT -j ACCEPT 85 | iptables -D $SS_OUT_RULES -p tcp --sport $PORT -j ACCEPT 86 | iptables -D $SS_IN_RULES -p udp --dport $PORT -j ACCEPT 87 | iptables -D $SS_OUT_RULES -p udp --sport $PORT -j ACCEPT 88 | } 89 | 90 | del_reject_rules () { 91 | PORT=$1; 92 | iptables -D $SS_IN_RULES -p tcp --dport $PORT -j REJECT 93 | iptables -D $SS_OUT_RULES -p tcp --sport $PORT -j REJECT 94 | iptables -D $SS_IN_RULES -p udp --dport $PORT -j REJECT 95 | iptables -D $SS_OUT_RULES -p udp --sport $PORT -j REJECT 96 | } 97 | 98 | list_rules () { 99 | iptables -vnx -L $SS_IN_RULES 100 | iptables -vnx -L $SS_OUT_RULES 101 | } 102 | 103 | add_new_rules () { 104 | ports=`awk ' 105 | { 106 | if($0 !~ /^#|^\s*$/) print $1 107 | } 108 | ' $USER_FILE` 109 | for port in $ports 110 | do 111 | add_rules $port 112 | done 113 | } 114 | 115 | update_or_create_traffic_file_from_users () { 116 | #根据用户文件生成或更新流量记录 117 | while [ -e $TRAFFIC_LOG.lock ]; do 118 | sleep 1 119 | done 120 | touch $TRAFFIC_LOG.lock 121 | 122 | if [ ! -f $TRAFFIC_LOG ]; then 123 | awk '{if($1 > 0) printf("%-5d\t0\n", $1)}' $USER_FILE > $TRAFFIC_LOG 124 | else 125 | awk ' 126 | BEGIN { 127 | i=1; 128 | } 129 | { 130 | if(FILENAME=="'$USER_FILE'"){ 131 | if($0 !~ /^#|^\s*$/){ 132 | port=$1; 133 | user[i++]=port; 134 | } 135 | } 136 | if(FILENAME=="'$TRAFFIC_LOG'"){ 137 | uport=$1; 138 | utra=$2; 139 | uta[uport]=utra; 140 | } 141 | } 142 | END { 143 | for(j=1;j0) { 146 | printf("'$TRA_FORMAT'", port, uta[port]) 147 | } else { 148 | printf("%-5d\t0\n", port) 149 | } 150 | } 151 | }' $USER_FILE $TRAFFIC_LOG > $TRAFFIC_LOG.tmp 152 | mv -f $TRAFFIC_LOG.tmp $TRAFFIC_LOG 153 | fi 154 | 155 | rm $TRAFFIC_LOG.lock 156 | } 157 | 158 | calc_remaining () { 159 | while [ -e $TRAFFIC_FILE.lock ]; do 160 | sleep 1 161 | done 162 | touch $TRAFFIC_FILE.lock 163 | awk ' 164 | function print_in_gb(bytes) { 165 | tb=bytes/(1024*1024*1024*1024*1.0); 166 | if(tb>=1||tb<=-1) { 167 | printf("(%.2fTB)", tb); 168 | } else { 169 | gb=bytes/(1024*1024*1024*1.0); 170 | if(gb>=1||gb<=-1) { 171 | printf("(%.2fGB)", gb); 172 | } else { 173 | mb=bytes/(1024*1024*1.0); 174 | if(mb>=1||mb<=-1) { 175 | printf("(%.2fMB)", mb); 176 | } else { 177 | kb=bytes/(1024*1.0); 178 | printf("(%.2fKB)", kb); 179 | } 180 | } 181 | } 182 | } 183 | BEGIN { 184 | i=1; 185 | totallim=0; 186 | totalused=0; 187 | totalrem=0; 188 | } 189 | { 190 | if(FILENAME=="'$USER_FILE'"){ 191 | if($0 !~ /^#|^\s*$/){ 192 | port=$1; 193 | user[i++]=port; 194 | limit=$3; 195 | limits[port]=limit 196 | } 197 | } 198 | if(FILENAME=="'$TRAFFIC_LOG'"){ 199 | uport=$1; 200 | utra=$2; 201 | uta[uport]=utra; 202 | } 203 | } 204 | END { 205 | printf("# port limit(in_TB/GB/MB/KB) used(in_TB/GB/MB/KB) remaining(in_TB/GB/MB/KB)\n"); 206 | for(j=1;j $TRAFFIC_FILE.tmp 240 | mv $TRAFFIC_FILE.tmp $TRAFFIC_FILE 241 | rm $TRAFFIC_FILE.lock 242 | } 243 | 244 | check_traffic_against_limits () { 245 | #根据用户文件查看流量是否超限 246 | ports_2ban=`awk ' 247 | BEGIN { 248 | i=1; 249 | } 250 | { 251 | if(FILENAME=="'$USER_FILE'"){ 252 | if($0 !~ /^#|^\s*$/){ 253 | port=$1; 254 | user[i++]=port; 255 | limit=$3; 256 | limits[port]=limit 257 | } 258 | } 259 | if(FILENAME=="'$TRAFFIC_LOG'"){ 260 | uport=$1; 261 | utra=$2; 262 | uta[uport]=utra; 263 | } 264 | } 265 | END { 266 | for(j=1;j> $PORTS_ALREADY_BAN 279 | fi 280 | done 281 | } 282 | get_traffic_from_iptables () { 283 | echo "$(iptables -nvx -L $SS_IN_RULES)" "$(iptables -nvx -L $SS_OUT_RULES)" | 284 | sed -nr '/ [sd]pt:[0-9]{1,5}$/ s/[sd]pt:([0-9]{1,5})/\1/p' | 285 | awk ' 286 | { 287 | trans=$2; 288 | port=$NF; 289 | tr[port]+=trans; 290 | } 291 | END { 292 | for(port in tr) { 293 | printf("'$TRA_FORMAT'", port, tr[port]) 294 | } 295 | } 296 | ' 297 | } 298 | 299 | get_traffic_from_iptables_first_time () { 300 | get_traffic_from_iptables > $IPT_TRA_LOG 301 | } 302 | 303 | get_traffic_from_iptables_now () { 304 | get_traffic_from_iptables > $IPT_TRA_LOG.tmp 305 | } 306 | 307 | calc_traffic_between_intervel () { 308 | awk ' 309 | { 310 | if(FILENAME=="'$IPT_TRA_LOG.tmp'") { 311 | port=$1; 312 | tras=$2; 313 | tr[port]=tras; 314 | } 315 | if(FILENAME=="'$IPT_TRA_LOG'") { 316 | port=$1; 317 | tras=$2; 318 | pretr[port]=tras; 319 | } 320 | } 321 | END { 322 | for(port in tr) { 323 | min_tras=tr[port]-pretr[port]; 324 | if(min_tras<0) min_tras=0; 325 | printf("'$TRA_FORMAT'", port, min_tras); 326 | } 327 | } 328 | ' $IPT_TRA_LOG.tmp $IPT_TRA_LOG > $MIN_TRA_LOG 329 | mv $IPT_TRA_LOG.tmp $IPT_TRA_LOG 330 | } 331 | update_traffic_record () { 332 | while [ -e $TRAFFIC_LOG.lock ]; do 333 | sleep 1 334 | done 335 | touch $TRAFFIC_LOG.lock 336 | awk ' 337 | BEGIN { 338 | i=1; 339 | } 340 | { 341 | if(FILENAME=="'$MIN_TRA_LOG'"){ 342 | trans=$2; 343 | port=$1; 344 | ta[port]+=trans; 345 | } 346 | if(FILENAME=="'$TRAFFIC_LOG'"){ 347 | uport=$1; 348 | utra=$2; 349 | uta[uport]=utra; 350 | useq[i++]=uport; 351 | } 352 | } 353 | END { 354 | for (j=1;j $TRAFFIC_LOG.tmp 359 | mv $TRAFFIC_LOG.tmp $TRAFFIC_LOG 360 | rm $TRAFFIC_LOG.lock 361 | } 362 | -------------------------------------------------------------------------------- /ssmlt.template: -------------------------------------------------------------------------------- 1 | "server": "0.0.0.0", 2 | "timeout": 60, 3 | "method": "aes-256-cfb", 4 | --------------------------------------------------------------------------------