法律No. 139-FZ:小規模プロバイダーからの見解

数週間前、私の良き友人(都市の非常勤のシステム管理者であるが、非常に大きなプロバイダーではない)に、Roskomnadzorの同僚は、IPによって、もちろんブロックされたサイトをブロックするが、URLまたはドメイン名によって完全に、しかし禁止されているリソースのIPアドレスを変更すると、再び開き始めます。 友人が私の運命について不平を言ったと同時に、悪名高い139-FZの要件を順守しなかったためにすでに個人的に罰金を科されていたことをほのめかしました。





このプロバイダーの接続スキームは次のとおりです。





いくつかの考えと、膝のレイヤー7フィルタリングに関する情報をグーグルで調べた後、Googleはそのようなことは高価な機器の特権であり、iptablesのl7フィルターは長い間開発が停止していると語りました。 その後、私たちはプロキシの方向で考え始めました。 私たちは誰も工業規模でsquidを使用していませんでしたが、ngorxについては非常に多くの実験がありました。これはIgor Sysoevによる非常に強力な製品です。



一般的な作業スキームは次のように開発されました。





nginxの設定と禁止リソースのゾーンのリストを動的に更新する必要があることは明らかです。Roskomnadzorの要件は、1日に3回の頻度を示しています。 欠点も明らかです:HTTPS経由のアクセスは制限または許可する必要がありますが、独自の証明書を使用する必要があります。そうしないと、nginxを経由するトラフィックが暗号化され、主な機能(フィルタリング)を実行できなくなります。 2番目のオプションでは、間違った証明書に対するクライアントブラウザの不正使用は避けられません。 さらに、Googleドメインのリストのアドレスがロックに該当し、クライアントにGoogleクロムがインストールされている場合、クライアントは原則としてこれらのサイトにアクセスできません。 しかし、他の限られたオプションは、限られた時間で発明することができませんでした。 それで、私たちは何を得ました:

  1. 禁止サイトのリストをダウンロードするスクリプト(プレーンXMLは要求後にSOAPサービスによって提供されます。各プロバイダーに固有の証明書を使用して承認が必要です)。
  2. DNSサーバーのゾーンのリストをロードするスクリプト。
  3. nginxに必要な構成を作成するスクリプト。




最初のスクリプトは提供しません。プロバイダー自身が情報を取得する方法を知っているため、一般ユーザーが情報を開示することはお勧めしません。



2番目のスクリプト(PowerDNSデータベースを操作するためのSQL):



USE pdns; create temporary table if not exists dump( domain text ); TRUNCATE TABLE dump; load data local infile '/opt/zapret/dump.xml' into table dump LINES STARTING BY '<domain>' TERMINATED BY '</domain>' (@tmp) SET domain = ExtractValue(@tmp, '/'); create temporary table if not exists locked( domain varchar(767) primary key ); TRUNCATE TABLE locked; INSERT INTO locked SELECT DISTINCT domain FROM dump; create temporary table if not exists locked1( domain varchar(767) primary key ); TRUNCATE TABLE locked1; INSERT INTO locked1 SELECT * FROM locked; DELETE FROM l USING locked l INNER JOIN locked1 l1 ON l.domain=SUBSTR(l1.domain from 5); UPDATE locked SET domain=SUBSTR(LCASE(domain) FROM 5) WHERE LEFT(LCASE(domain), 4) = 'www.'; DELETE FROM locked WHERE domain LIKE '%youtube.com' OR domain LIKE '%google.com' OR domain LIKE '%google.ru'; create temporary table if not exists old_locked( id int, domain varchar(767) primary key ); TRUNCATE TABLE old_locked; INSERT INTO old_locked SELECT DISTINCT d.id, d.name as domain FROM domains d INNER JOIN records r ON d.id=r.domain_id WHERE r.content='1.2.3.4' AND d.name NOT LIKE '%provider.ru'; INSERT INTO domains (name, master, last_check, type, notified_serial, account) SELECT n.domain, NULL, NULL, 'NATIVE', NULL, NULL FROM locked n LEFT JOIN old_locked o ON n.domain=o.domain WHERE o.domain IS NULL; INSERT INTO records (domain_id, name, type, content, ttl, prio, change_date, ordername, auth) SELECT d.id AS domain_id, d.name, 'SOA' as type, 'ns.provider.ru dns.provider.ru 2014022701 28800 7200 604800 86400' as content, 86400 as ttl, 0 as prio, 1393508792 as change_date, '' as ordername, 1 as auth FROM domains d INNER JOIN locked l ON d.name=l.domain LEFT JOIN old_locked o ON d.name=o.domain WHERE o.domain IS NULL; INSERT INTO records (domain_id, name, type, content, ttl, prio, change_date, ordername, auth) SELECT d.id AS domain_id, CONCAT('*.', d.name), 'A' as type, '1.2.3.4' as content, 86400 as ttl, 0 as prio, 1393508820 as change_date, '' as ordername, 1 as auth FROM domains d INNER JOIN locked l ON d.name=l.domain LEFT JOIN old_locked o ON d.name=o.domain WHERE o.domain IS NULL; DELETE FROM r, d USING records r INNER JOIN domains d ON r.domain_id=d.id INNER JOIN old_locked o ON d.name=o.domain LEFT JOIN locked l ON o.domain=l.domain WHERE l.domain IS NULL;
      
      







このスクリプトを実行した後、忘れずに実行する必要があります

 # pdnssec rectify-all-zones
      
      





powerdnsが変更を認識できるようにします。



3番目のスクリプト(ブロックリストの作成):

 <?php $xml = simplexml_load_file ('/opt/zapret/dump.xml'); $dirty = array(); $excl = array(); $excl[] = 'youtube.com'; $excl[] = 'google.ru'; $excl[] = 'google.com'; $excl[] = 'badsite.org'; foreach($xml as $node) { if( strlen( (string)$node->domain )>0 ) { $parsed = parse_url((string)$node->url); if( $parsed!=false ) { if( isset($parsed['path']) ) { if( isset($parsed['scheme']) ) $scheme = $parsed['scheme'] . "://"; else $scheme = "http://"; if( isset($parsed['port']) ) { $port = ':' . $parsed['port']; if( $scheme=="https://" ) $port = $port . " ssl"; } else { if( $scheme=="https://" ) $port = ":443 ssl"; else $port = ":80"; } $port = $port . ";"; $domain = (string)$node->domain; if( strcmp(strtolower(substr($domain, 0, 4)), 'www.') == 0 ) $domain = substr($domain, 4); if( isset($parsed['query']) ) $que = $parsed['query']; else $que = ''; $que = str_replace('\\E', '\\E\\\\E\\Q', $que); $que = '\\Q' . $que . '\\E'; if ( strcmp($que, '\\Q\\E')==0 ) $que = ''; $path = $parsed['path']; $path = str_replace('\\E', '\\E\\\\E\\Q', $path); if ( strcmp($path, '/')<>0 ) { $path = '\\Q' . $path . '\\E'; if ( strcmp($path, '\\Q\\E')==0 ) $path = ''; } $keys = preg_grep('/' . $domain . '/', $excl); if( count($keys)<1 ) $dirty[] = array('domain'=>$domain, 'url'=>(string)$node->url, 'loc'=>$path, 'query'=>$que, 'port'=>$port, 'scheme'=>$scheme); } } } } // ..   ,     $dirty[] = array('domain'=>'badsite.org', 'url'=>'badsite.org', 'loc'=>'/', 'query'=>'', 'port'=>':80;', 'scheme'=>'http://'); $sort_func = function($obj_1, $obj_2) { return strnatcasecmp($obj_1['domain'] . $obj_1['url'], $obj_2['domain'] . $obj_2['url']); }; $domains = array_unique($dirty, SORT_REGULAR); usort($domains, $sort_func); $old_domain = ""; $old_loc = ""; $wasroot = false; $alldomain = false; $allloc = false; foreach($domains as $node) { $domain = $node['domain']; $url = $node['url']; $loc = $node['loc']; $query = $node['query']; $port = $node['port']; $scheme = $node['scheme']; // echo "\n1. Root " . (string)$wasroot . "; alldomain " . (string)$alldomain . "; alloc " . (string) $allloc . "; loc '" . $loc . "'; query '" . $query . "'\n"; if( strcmp($domain, $old_domain) ) { loc_close( $old_loc, ($alldomain || $wasroot || $allloc)); dom_close( $old_domain, $wasroot ); dom_open( $domain, $port, $scheme ); $old_loc = ''; $alldomain = false; $wasroot = false; } if( !$alldomain ){ if( strcmp($loc, $old_loc) ) { loc_close( $old_loc, ($alldomain || $allloc) ); loc_open( $loc ); $allloc = false; } if( strlen($loc)<2 && strlen($query)>0 ) $wasroot = true; if( strlen($loc)<2 && strlen($query)<1 ) { $alldomain = true; $wasroot = true; } if( strlen($query)<1 ) $allloc = true; if( !$allloc ) args_check( $query ); } // echo "\n2. Root " . (string)$wasroot . "; alldomain " . (string)$alldomain . "; alloc " . (string) $allloc . "; loc '" . $loc . "'; query '" . $query . "'\n"; $old_loc = $loc; $old_domain = $domain; } loc_close( $loc, ($alldomain || $wasroot || $allloc) ); dom_close( $domain, $wasroot ); function loc_close( $_loc, $_alldomain ) { if (strlen($_loc)>0 ) { if( $_alldomain ) { ?> return 301 http://eais.rkn.gov.ru/; <?php } else { //if ( strcmp($_loc, '/')==0 ) { ?> include /etc/nginx/proxy_params; if ($args = '') { proxy_pass $scheme://$host$uri; } if ($args != '') { proxy_pass $scheme://$host$uri?$args; } <?php } echo " } # location\n"; } } function dom_close( $_dom, $_wasroot ) { if( strlen($_dom)>0 ) { if( !$_wasroot ) { ?> location / { include /etc/nginx/proxy_params; if ($args = '') { proxy_pass $scheme://$host$uri; } if ($args != '') { proxy_pass $scheme://$host$uri?$args; } } #root location <?php } echo "} #domain\n"; } } function loc_open( $_loc ) { ?> location ~* <?php echo $_loc . "* {\n"; ?> <?php } function dom_open( $_domain, $_port, $_scheme ) { ?> server { listen 1.2.3.4<?php echo $_port; if( strcmp( $_scheme, 'https:\/\/' )==0 ) echo ' ssl'; ?> server_name <?php echo $_domain . " " . "*." . $_domain . ";\n"; } function args_check( $_query ) { if ( strlen($_query)>0 ) { echo "\t if (\$args ~* \""; if(strlen($_query)>0) echo $_query; echo "*\") {\n"; echo "\t\treturn 301 http://eais.rkn.gov.ru/;\n"; echo "\t } #args\n"; } else echo "\treturn 301 http://eais.rkn.gov.ru/;\n"; } ?>
      
      







3番目のスクリプトの結果に基づいて、nginxの構成を取得します。これは、受け取ったドメイン名をプロキシします。 アドレスがブロックされた場合、アドレスeais.rkn.gov.ruへの無条件リダイレクト(301)が実行されます -禁止サイトの登録。

ロックには次の3つのタイプがあります。



1.ドメイン全体。 そのようなサイトの場合、次のエントリを取得します。

 server { listen 1.2.3.4:80; server_name badsite.org *.badsite.org; location ~* /* { return 301 http://eais.rkn.gov.ru/; } # location } #domain
      
      







2.ドメイン内の定義済みURL。 この場合、別のエントリを取得します。

 server { listen 1.2.3.4:80; server_name badsite.hk *.badsite.hk; location ~* \Q/h/\E* { return 301 http://eais.rkn.gov.ru/; } # location location ~* \Q/h/res/214.html\E* { return 301 http://eais.rkn.gov.ru/; } # location location / { include /etc/nginx/proxy_params; if ($args = '') { proxy_pass $scheme://$host$uri; } if ($args != '') { proxy_pass $scheme://$host$uri?$args; } } #root location } #domain
      
      







3.特定のURLの特定の引数(たとえば、PHPBBの特定の投稿):

 server { listen 1.2.3.4:80; server_name badsite.com *.badsite.com; location ~* \Q/forum/viewforum.php\E* { if ($args ~* "\Qf=6\E*") { return 301 http://eais.rkn.gov.ru/; } #args if ($args ~* "\Qf=6&start=25\E*") { return 301 http://eais.rkn.gov.ru/; } #args include /etc/nginx/proxy_params; if ($args = '') { proxy_pass $scheme://$host$uri; } if ($args != '') { proxy_pass $scheme://$host$uri?$args; } } # location location / { include /etc/nginx/proxy_params; if ($args = '') { proxy_pass $scheme://$host$uri; } if ($args != '') { proxy_pass $scheme://$host$uri?$args; } } #root location } #domain<
      
      







そしてもちろん、デフォルトでは、すべてのリクエストが対応するアドレスに転送されます(念のため):

 server { listen 1.2.3.4:80 default_server; location / { include /etc/nginx/proxy_params; if ($args = '') { proxy_pass $scheme://$host$uri; } if ($args != '') { proxy_pass $scheme://$host$uri?$args; } } } server { listen 1.2.3.4:443 ssl default_server; location / { include /etc/nginx/proxy_params; if ($args = '') { proxy_pass $scheme://$host$uri; } if ($args != '') { proxy_pass $scheme://$host$uri?$args; } } }
      
      







構成を生成した後、言うことを忘れないでください

 # service nginx reload
      
      





これは、nginxに古いプールを穏やかに消して構成をリロードするように指示します。



youtube.comのクリップがこのリストに追加された1週間前に、nginxを使用したシステムの強度がテストされました。 メモリ消費の増加に加えて、副作用は認められませんでした。 クライアントとのキープアライブ接続を切断することで、メモリ消費を克服することができました。 もちろん、ユーザーにとってはあまり便利ではありませんでした。youtube.comでの動画の表示とダウンロードは一般に機能しましたが、多くの動画はhttpsを使用して他のページに埋め込まれ、ブラウザは偽造証明書でそれらを表示したくありませんでした プロバイダーのリーダーシップの意図的な決定により、ドメインgoogle.com、google.ru、youtube.comが例外のリストに追加され、サイトの1つが「反対の例外」のリストに含まれました。全体をブロックするという長年の決定がありますが、このレジストリにはロードされていません禁止されているURLは2つだけです。

一般に、このソリューションは、ロシアの法律の厳しい条件で働き続けたい小規模プロバイダーにとって非常に有効であることが証明されました。



All Articles