A tor server manager running multiple clients load-balanced using iptables.
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

tor_controller.pl 7.9KB

6年前
6年前
6年前
6年前
6年前
6年前
6年前
6年前
6年前
6年前
6年前
6年前
6年前
6年前
6年前
6年前
6年前
6年前
6年前
6年前
6年前
6年前
6年前
6年前
6年前
6年前
6年前
6年前
6年前
6年前
6年前
6年前
6年前
6年前
6年前
6年前
6年前
6年前
6年前
6年前
6年前
6年前
6年前
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. #!/usr/bin/perl
  2. use strict;
  3. use POSIX ":sys_wait_h";
  4. use Data::Dumper;
  5. use warnings;
  6. my %conf = (
  7. avail => 2,
  8. spare => 2,
  9. ipt_chain => "PREROUTING",
  10. ipt_rule => "-p tcp -m tcp --dport 777 -m statistic --mode random --probability %s -j REDIRECT --to-ports %s",
  11. ipt_table => 'nat',
  12. socks_offset => 9000,
  13. path => '/opt/tor',
  14. dns_check => [
  15. 'www.microsoft.com',
  16. 'www.google.com',
  17. ],
  18. proto => {
  19. ipv4 => 1,
  20. ipv6 => 1,
  21. },
  22. loopinterval => 60,
  23. );
  24. # states: startup, ready, problem, dying, killed
  25. # modes: spare, live
  26. my @var;
  27. ipt_clean();
  28. while(1) { run(); }
  29. sub run {
  30. check_processes();
  31. show_state();
  32. sleep($conf{loopinterval});
  33. }
  34. sub show_state {
  35. my $s = ipt_conn();
  36. printf("======================================\n");
  37. printf("%3s %-7s %-5s %4s %4s\n", "id", "state", "mode", "ipv4", "ipv6");
  38. for(my $id=0; defined $var[$id]; $id++) {
  39. printf("%3s %-7s %-5s %4s %4s\n",
  40. $id,
  41. $var[$id]{state},
  42. $var[$id]{mode},
  43. exists $s->{$id}{ipv4} ? $s->{$id}{ipv4} : 0,
  44. exists $s->{$id}{ipv6} ? $s->{$id}{ipv6} : 0,
  45. );
  46. }
  47. printf("======================================\n");
  48. }
  49. sub check_processes {
  50. my $inuse=0;
  51. my @spares;
  52. for(my $id=0; defined $var[$id]; $id++) {
  53. if ( $var[$id]{pid} eq waitpid($var[$id]{pid}, WNOHANG) ) {
  54. print "child $var[$id]{pid} died. resetting instance $id\n";
  55. ipt_del($id);
  56. $var[$id]{pid}=undef;
  57. $var[$id]{state}='killed';
  58. $var[$id]{mode}='none';
  59. }
  60. }
  61. for(my $id=0; defined $var[$id]; $id++) {
  62. if ( $var[$id]{state} =~ /^(startup|ready|problem)$/ ) {
  63. check_dns($id);
  64. }
  65. }
  66. for(my $id=0; defined $var[$id]; $id++) {
  67. if ( $var[$id]{state} eq "ready" && $var[$id]{mode} eq 'live' ) {
  68. $inuse++;
  69. }
  70. if ( $var[$id]{state} =~ /^(startup|ready|problem)$/ && $var[$id]{mode} eq 'spare' ) {
  71. push @spares, $id;
  72. }
  73. }
  74. print "found " . scalar(@spares) . " spare clients and $inuse live clients\n";
  75. if ( $inuse < $conf{avail} ) {
  76. print "missing live processes. looking for spares.\n";
  77. for(my $x=0; $x<($conf{avail}-$inuse); $x++) {
  78. my $new = pop @spares;
  79. if ( defined $new ) {
  80. if ( $var[$new]{state} eq 'ready' ) {
  81. print "moving spare to prod.\n";
  82. tor_prod($new);
  83. }
  84. else {
  85. push @spares, $new;
  86. }
  87. }
  88. }
  89. }
  90. if ( scalar(@spares) < $conf{spare} ) {
  91. print "missing spare processes, starting new ones.\n";
  92. for(my $x=0; $x < ($conf{spare}-scalar(@spares)); $x++) {
  93. tor_new();
  94. }
  95. }
  96. }
  97. sub check_dns {
  98. my($id) = @_;
  99. my $ok = 0;
  100. foreach my $hostname ( @{$conf{dns_check}} ) {
  101. print "Checking DNS ($hostname) for $id ";
  102. system("timeout 5 tor-resolve $hostname 127.0.0.1:$var[$id]{port} >/dev/null 2>&1");
  103. if ( $? ) {
  104. print "ERROR\n";
  105. }
  106. else {
  107. print "OK\n";
  108. $ok++;
  109. }
  110. }
  111. if ( $var[$id]{state} eq 'startup' ) {
  112. if ( $ok ) {
  113. $var[$id]{state} = 'ready';
  114. }
  115. else {
  116. $var[$id]{state} = 'problem';
  117. }
  118. }
  119. elsif ( $var[$id]{state} eq 'ready' && !$ok ) {
  120. $var[$id]{state} = 'problem';
  121. }
  122. elsif ( $var[$id]{state} eq 'problem' ) {
  123. if ( $ok ) {
  124. $var[$id]{state} = 'ready';
  125. }
  126. else {
  127. $var[$id]{state} = 'dying';
  128. ipt_del($id);
  129. }
  130. }
  131. }
  132. sub tor_new {
  133. my $next;
  134. print "initiating a new tor client\n";
  135. # try to recycle a position
  136. for(my $id=0; defined $var[$id]; $id++) {
  137. if ( $var[$id]{state} eq 'killed' ) {
  138. print "recovering id $id\n";
  139. $next = $id;
  140. last;
  141. }
  142. }
  143. # alternatively, create a new position
  144. if ( !defined $next ) {
  145. $next = @var;
  146. print "new id $next\n";
  147. }
  148. $var[$next] = {
  149. state => "startup",
  150. mode => "spare",
  151. port => $conf{socks_offset} + $next,
  152. pid => undef,
  153. };
  154. print "creating configuration file and dependencies\n";
  155. # create a config file and the required files and directories
  156. my $datadir = "$conf{path}/var/lib/$next";
  157. my %files = (
  158. config => "$conf{path}/etc/$next.conf",
  159. pidfile => "$conf{path}/run/$next.pid",
  160. socket => "$conf{path}/run/$next.socks",
  161. ctrl => "$conf{path}/run/$next.ctrl",
  162. cookie => "$conf{path}/run/$next.cookie",
  163. );
  164. system("mkdir -p $datadir");
  165. system("chmod 700 $datadir");
  166. system("chown debian-tor:debian-tor $datadir");
  167. foreach my $f ( keys %files ) {
  168. system("touch $files{$f}");
  169. system("chown debian-tor:debian-tor $files{$f}");
  170. }
  171. open(C,">$files{config}");
  172. print C <<EOF;
  173. DataDirectory $datadir
  174. PidFile $files{pidfile}
  175. RunAsDaemon 0
  176. User debian-tor
  177. ControlSocket $files{ctrl} GroupWritable RelaxDirModeCheck
  178. ControlSocketsGroupWritable 1
  179. #SocksPort unix:$files{socket} WorldWritable
  180. SocksPort 0.0.0.0:$var[$next]{port}
  181. SocksPort [::]:$var[$next]{port}
  182. CookieAuthentication 1
  183. CookieAuthFileGroupReadable 1
  184. CookieAuthFile $files{cookie}
  185. BandwidthRate 10MB
  186. Log notice syslog
  187. EOF
  188. close(C);
  189. print "forking...\n";
  190. my $pid = fork();
  191. if ( !defined $pid ) {
  192. die "Failed to fork, something has gone badly wrong!\n";
  193. }
  194. elsif ( $pid ) {
  195. print "tor client pid $pid\n";
  196. $var[$next]{pid} = $pid;
  197. }
  198. else {
  199. chdir $conf{path};
  200. exec("tor -f $files{config} >/dev/null");
  201. die "something weng really wrong, failed to exec tor";
  202. exit;
  203. }
  204. }
  205. sub tor_prod {
  206. my($id) = @_;
  207. # move process from spare to prod
  208. $var[$id]{mode} = 'live';
  209. ipt_add($var[$id]{port});
  210. }
  211. sub ipt_add {
  212. my($port) = @_;
  213. system("iptables -t $conf{ipt_table} -A $conf{ipt_chain} " . sprintf($conf{ipt_rule}, "1.0", $port)) if $conf{proto}{ipv4};
  214. system("ip6tables -t $conf{ipt_table} -A $conf{ipt_chain} " . sprintf($conf{ipt_rule}, "1.0", $port)) if $conf{proto}{ipv6};
  215. ipt_recalc();
  216. }
  217. sub ipt_del {
  218. my($port) = @_;
  219. if ( $conf{proto}{ipv4} ) {
  220. open(IPT,"iptables -t $conf{ipt_table} -L $conf{ipt_chain} -n --line-numbers |");
  221. while(<IPT>) {
  222. chomp;
  223. if ( /^(\d+) .*mode random probability .* redir ports $port$/ ) {
  224. system("iptables -t $conf{ipt_table} -D $conf{ipt_chain} $1");
  225. }
  226. }
  227. close(IPT);
  228. }
  229. if ( $conf{proto}{ipv6} ) {
  230. open(IPT,"ip6tables -t $conf{ipt_table} -L $conf{ipt_chain} -n --line-numbers |");
  231. while(<IPT>) {
  232. chomp;
  233. if ( /^(\d+) .*mode random probability .* redir ports $port$/ ) {
  234. system("ip6tables -t $conf{ipt_table} -D $conf{ipt_chain} $1");
  235. }
  236. }
  237. close(IPT);
  238. }
  239. ipt_recalc();
  240. }
  241. sub ipt_clean {
  242. system("iptables -t $conf{ipt_table} -F $conf{ipt_chain}") if $conf{proto}{ipv4};
  243. system("ip6tables -t $conf{ipt_table} -F $conf{ipt_chain}") if $conf{proto}{ipv6};
  244. }
  245. sub ipt_recalc {
  246. my @rules;
  247. if ( $conf{proto}{ipv4} ) {
  248. open(IPT,"iptables -t $conf{ipt_table} -L $conf{ipt_chain} -n --line-numbers |");
  249. while(<IPT>) {
  250. chomp;
  251. if ( /^(\d+) .*mode random probability .* redir ports (\d+)$/ ) {
  252. push @rules, [ $1, $2 ];
  253. }
  254. }
  255. close(IPT);
  256. my $nums = @rules;
  257. for(my $num = 0; defined $rules[$num]; $num++) {
  258. my($rulenum,$port)=@{$rules[$num]};
  259. my $prob = 1 / ($nums - $num);
  260. system("iptables -t $conf{ipt_table} -R $conf{ipt_chain} $rulenum " . sprintf($conf{ipt_rule}, $prob, $port));
  261. }
  262. }
  263. if ( $conf{proto}{ipv6} ) {
  264. open(IPT,"ip6tables -t $conf{ipt_table} -L $conf{ipt_chain} -n --line-numbers |");
  265. while(<IPT>) {
  266. chomp;
  267. if ( /^(\d+) .*mode random probability .* redir ports (\d+)$/ ) {
  268. push @rules, [ $1, $2 ];
  269. }
  270. }
  271. close(IPT);
  272. my $nums = @rules;
  273. for(my $num = 0; defined $rules[$num]; $num++) {
  274. my($rulenum,$port)=@{$rules[$num]};
  275. my $prob = 1 / ($nums - $num);
  276. system("ip6tables -t $conf{ipt_table} -R $conf{ipt_chain} $rulenum " . sprintf($conf{ipt_rule}, $prob, $port));
  277. }
  278. }
  279. }
  280. sub ipt_conn {
  281. my %s;
  282. if ( $conf{proto}{ipv4} ) {
  283. open(IPT,"conntrack -L -f ipv4 2>/dev/null |");
  284. while(<IPT>) {
  285. chomp;
  286. if ( / dport=777 .* sport=(\d+) / ) {
  287. my $port = $1;
  288. if ( $port >= $conf{socks_offset} ) {
  289. my $id = $port - $conf{socks_offset};
  290. $s{$id}{ipv4}++;
  291. }
  292. }
  293. }
  294. close(IPT);
  295. }
  296. if ( $conf{proto}{ipv6} ) {
  297. open(IPT,"conntrack -L -f ipv6 2>/dev/null |");
  298. while(<IPT>) {
  299. chomp;
  300. if ( / dport=777 .* sport=(\d+) / ) {
  301. my $port = $1;
  302. if ( $port >= $conf{socks_offset} ) {
  303. my $id = $port - $conf{socks_offset};
  304. $s{$id}{ipv6}++;
  305. }
  306. }
  307. }
  308. close(IPT);
  309. }
  310. return \%s;
  311. }