Skip to content

Instantly share code, notes, and snippets.

@nykma
Last active October 9, 2017 05:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nykma/d8492b157b00d89fde21d410824ada85 to your computer and use it in GitHub Desktop.
Save nykma/d8492b157b00d89fde21d410824ada85 to your computer and use it in GitHub Desktop.
Shadowsocks-libev-redir @ linux w/ iptables

Nykma's magic scripts

  • ss_redir_iptables:
    • Generate iptables redir rules to ss_redir
    • Auto load and unload ipset of china IPs using chnroute.txt
    • Test if an IP is in china IP set.
  • ss_ctl:
    • Switch shadowsocks profiles with systemctl --user
  • chnroute_refresh:
    • Download and parse china IPs based on APNIC record
# /usr/lib/systemd/system/chinadns.service
[Unit]
Description=ChinaDNS Service
After=network.target
[Service]
Type=simple
User=root
ExecStart=/usr/bin/chinadns -m -d -c /etc/chnroute.txt -b 0.0.0.0 -v
[Install]
WantedBy=multi-user.target
#!/bin/bash
# ~/.bin/chnroute_refresh
curl 'http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest' | grep ipv4 | grep CN | awk -F\| '{ printf("%s/%d\n", $4, 32-log($5)/log(2)) }' > /home/nykma/.config/chnroute.txt
# ~/.config/systemd/user/shadowsocks-libev-redir@.service
[Unit]
Description=Shadowsocks-Libev Client Service Redir Mode
After=network.target
[Service]
Type=simple
# CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
ExecStart=/usr/bin/ss-redir -c /etc/shadowsocks/%i.json
ExecStartPost=/usr/bin/sudo /home/nykma/.bin/ss_redir_iptables up -c %i
ExecStopPost=/usr/bin/sudo /home/nykma/.bin/ss_redir_iptables down
[Install]
WantedBy=multi-user.target
#!/usr/bin/env ruby
# ~/.bin/ss_ctl
temp = `systemctl --user status | grep 'shadowsocks-libev-redir@'`.split.find { |text| text.end_with?('.service') }
current_profile = temp.nil? ? nil : /(?<=shadowsocks-libev-redir@).*(?=\.service)/.match(temp)
target_profile = ARGV[1] || current_profile
def service_name(profile)
"shadowsocks-libev-redir@#{profile}.service"
end
def print_info(info)
print "#{info} ... "
yield
print "Complete.\n"
end
case ARGV[0]
when nil, 'status'
puts "#{service_name(current_profile)}: " + `systemctl --user status #{service_name(current_profile)} | grep 'Active'`
when 'start'
exit 'Should give different profile or using `restart`' if target_profile == current_profile
unless current_profile.nil?
print_info("Stopping #{service_name(current_profile)}") do
`systemctl --user stop #{service_name(current_profile)}`
end
end
print_info("Starting #{service_name(target_profile)}") do
`systemctl --user start #{service_name(target_profile)}`
end
when 'stop'
print_info("Stopping #{service_name(target_profile)}") do
`systemctl --user stop #{service_name(target_profile)}`
end
when 'restart'
print_info "Restarting #{service_name(current_profile)} ..." do
`systemctl --user restart #{service_name(current_profile)}`
end
else
abort 'Accepted actions: start stop status restart'
end
#!/usr/bin/env fish
# ~/.config/fish/completions
# Copyright (C) 2012-2014 Dmitry Medvinsky <me@dmedvinsky.name>. All Rights Reserved.
# This file is licensed under the GPLv2+. Please see COPYING for more information.
set PROG 'ss_ctl'
set CONFIG_PATH '/etc/shadowsocks'
function __fish_ss_ctl_needs_command
set -l cmd (commandline -opc)
if [ (count $cmd) -eq 1 -a $cmd[1] = $PROG ]
return 0
end
return 1
end
function __fish_ss_ctl_uses_command
set cmd (commandline -opc)
if [ (count $cmd) -gt 1 ]
if [ $argv[1] = $cmd[2] ]
return 0
end
end
return 1
end
function __fish_ss_ctl_print_config_files
set -l files
eval "set files '$CONFIG_PATH'/**.json"
for json in $files
echo (echo $json | grep -oh '\w*\.json\b' | sed 's/\.json\b//')
end
end
complete -c $PROG -e
complete -c $PROG -f -A -n '__fish_ss_ctl_needs_command' -a start -d 'Command: start or switch a profile'
complete -c $PROG -f -A -n '__fish_ss_ctl_needs_command' -a restart -d 'Command: restart current profile'
complete -c $PROG -f -A -n '__fish_ss_ctl_needs_command' -a status -d 'Command: show current profile\'s systemctl status'
complete -c $PROG -f -A -n '__fish_ss_ctl_needs_command' -a stop -d 'Command: stop current profile'
complete -c $PROG -f -A -n '__fish_ss_ctl_uses_command start' -x -a "(__fish_ss_ctl_print_config_files)" -d 'profile'
#!/usr/bin/env ruby
# ~/.bin/ss_redir_iptables
# Set and unset iptables for ss-redir
VERSION = '0.1.0'.freeze
CHNROUTES_FILE = '/home/nykma/.config/chnroute.txt'.freeze
CONFIG_PATH = '/etc/shadowsocks'.freeze
LOCAL_ADDR = %w(
0.0.0.0/8
10.0.0.0/8
127.0.0.0/8
169.254.0.0/16
172.16.0.0/12
192.168.0.0/16
224.0.0.0/4
240.0.0.0/4
).freeze
require 'json'
require 'slop'
REGEX_IPV4 = /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/
OPTS = Slop.parse do |o|
o.string '-h', '--host', 'Hostname'
o.string '-c', '--config', 'Specific config name'
o.separator 'other options:'
o.on '--version', 'print the version' do
puts VERSION
exit
end
end
def to_ip(target)
if target && !REGEX_IPV4.match(target.to_s)
require 'resolv'
Resolv.getaddress(target)
else
target
end
end
def current_config
current_config_path = if OPTS[:config]
"#{CONFIG_PATH}/#{OPTS[:config]}.json"
else
temp = `systemctl status | grep 'shadowsocks-libev-redir@'`.split.find do |text|
text.end_with?('.service')
end
current_profile = temp && /(?<=shadowsocks-libev-redir@).*(?=\.service)/.match(temp)
current_profile && "#{CONFIG_PATH}/#{current_profile}.json"
end
(current_config_path && JSON.parse(File.read(current_config_path))) || {}
end
def print_info(info)
print "#{info} ... "
yield
print "Done.\n"
end
case OPTS.arguments.first
when 'up'
unless (target = OPTS[:host] || to_ip(current_config['server']))
puts 'No target given. Abort.'
abort
end
puts "Target: #{target}\n"
print_info('Initializing') do
`iptables -t nat -N SHADOWSOCKS`
`ipset -N chnroute hash:net maxelem 10000`
end
print_info('Adding Shadowsocks server itself to ignore list') do
`iptables -t nat -A SHADOWSOCKS -d #{target} -j RETURN`
end
print_info('Adding local IP addresses') do
LOCAL_ADDR.each do |la|
`iptables -t nat -A SHADOWSOCKS -d #{la} -j RETURN`
end
end
print_info('Adding CHNROUTE') do
File.open(CHNROUTES_FILE, 'r').each_line do |line|
`ipset add chnroute #{line.chomp}`
end
`iptables -t nat -A SHADOWSOCKS -p tcp -m set --match-set chnroute dst -j RETURN`
end
print_info('Redirecting') do
`iptables -t nat -A SHADOWSOCKS -p tcp -j REDIRECT --to-port 1080`
`iptables -t nat -A OUTPUT -p tcp -j SHADOWSOCKS`
end
when 'down'
print_info('Cleaning iptables') do
`iptables -t nat -D OUTPUT -p tcp -j SHADOWSOCKS`
`iptables -t nat -F SHADOWSOCKS`
`iptables -t nat -X SHADOWSOCKS`
`ipset destroy chnroute`
end
when 'test'
`ipset test chnroute #{to_ip(OPTS.arguments[1])}`
else
puts OPTS
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment