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 лет назад
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. }