SSL-Offloader¶
前置き¶
この良い小さなソフトウェアについて Igor Sysoev に感謝します。これは、私にとってこの偉大なプロジェクトに何か貢献できる唯一の方法です。私達が単純に"SSL-Proxy"と呼ぶものを確立する全体像を説明しようとしました(別名、SSL/TLS/HTTPS-Offloader/-Accelerator/-Terminator/-Dispatcher/Reverse-Proxy/Loadbalancer など)。
私達の会社では、NGINXをリバースプロキシとして使い、複数のバックエンドからHTTPを使ってコンテントを受け取る時にクライアントにHTTPSを提供しています。VRRPを使って1つのクラスタに"接続"している2つのバーチャルマシーンがあり、それぞれが1つ以上のアプリケーションを持つ約80のTomcatサーバ(およびいくつかのIIS, Apache/PHP...)のためのフロントエンドとして振舞います。There were many reasons for this structure: One CI (ITIL: change item) for SSL certificates, customer demands (one URL xyz with multiple services), simple but effective failover mechanism for changes and so on.
- Update 2014, success story:
- "super-url"を使ったリバースプロキシとしてのこのNGINXセットアップは7年以上の間完璧に動作していました(この間に、ubuntuのバージョンを何回か変更しました - hardyからprecise)。今では、2つのアプリケーションだけが残され、このスキーマには含められることができません。コメントを除いた設定は約7000行です。このセットアップで週辺り約10回の変更をし、それら全てはその場で(リロードを使って)行うことができます。The security audits of these applications never showed topics like “unpatched ssl versions” again, the web-services team can concentrate on deploying applications with no need to touch an apache configuration ever again and we (the networking team) can solve every access issue by ourself.
始めに、ちょっとした警告です。
- ここで説明しようとしていることは全て最終的に"リバースプロキシ"設定のようなものです。バックエンドのアプリケーションが相対パスを使用するだけであれば、全てがうまくいくでしょう。絶対パスを使う場合、それは複雑に成りえます。正しいパスのプリフィックスを使ってデプロイすることができれば、この場合もやはり全てがうまくいきます。アプリケーションがルートパスにある場合、それが不可能になります - たとえ、戻ってくるコード(html, css, jsなど)をパースできたとしても。
- "追加設定無しで"動作する電気器具のような、"簡単な"解決法を欲しがるかも知れません。My experience with this matter is: The operation of a central entry point for many applications with all their unique characteristics is unfortunatly this complex. Don’t expect from an appliance more than save buttons and a fancy web-gui for similar settings as described here. At this point you are entering the twilight zone between configuration and programming, because your proxy is mostly an integral part of a bigger “web-program”.
Technical Data¶
サーバはとても効率的に動作します:
- Hypervisor: VMware ESXi
- VM: 4x CPU (load: ~0.15), 768 MByte RAM (used: ~12%), 4 GByte HD
- Base system: Ubuntu 12.04.5/precise
- Working horse: NGINX 1.6.2-5+precise0 (ppa:nginx/stable)
- SSL-Library: LibSSL 1.0.1-4ubuntu5.21
- Entropy-Daemon: haveged 1.1-2
- VRRP daemon: Keepalived 1:1.2.2-3ubuntu1.1
- Software watchdog: Monit 1:5.3.2-1
- NTP alternative: Chrony 1.24-3.1ubuntu1 (one socket)
考慮すべきこと¶
設定¶
ポリシーのようなもの...
- 更新する時に問題を避けるために、設定ファイルとディレクトリの配置はインストレーションの既存のものへの追加にしなければなりません。
- 各設定はユニークでなければなりません。これは一方で小さな
include
ファイルの幾つかの追加の参照と複雑な配置につながります。 if
-ステートメントは入れ子にすべきではありません。したがってincludeの中にinclude
-ステートメントを使うことは避けます。- 全てのディレクトリをincludeする場合、switchのようなファイル拡張子を使います。
/etc/nginx
の中に新しく生成されたファイル:
- conf.d
- mapping/<segment>/<application>
- sslcerts/<domain of the channel>/[<app-group>|wildcard].[crt|key]
- sites-enabled/<ip-slot>_<app-group>.<customer>.any.<segment>
- scripts
名前の慣習¶
1つの内部(ADS)ドメイン、幾つかの公のそれ、そして複雑な状況がありました:
- エントリー ポイント/チャンネル: LAN ユーザ (lan), インターネット (ext), 幾つかの種類の VPN/WAN (vn2) およびDNSを持たない古いVPN (vn1)
- カスタマー: 会社、幾つかのワークグループ、ラベル、支社、そして外部のカスタマー
- ライフ サイクル/セグメント: 開発 (dev), 統合1 と 2 (ig1/ig2), 試供品 (apv), 実演 (dem) そして製品 (prd)
- environments: real (local) and labor
私達の解決方法は、2つの大きなDNSの木から始め、アプリケーション名をDNSからパスへ移動するものでした:
http(s)://<app-group>.<customer>.<channel>.<segment>.[local|labor]/<application>/
製品システムの場合、公的なドメインにマップされていました。
Tip
- プロキシ上のDNS dispatcherとして、
pdnsd
を使います。 - avahi... のようなサービスを使いたい場合は、私達がしたように
*.local
ドメインを使う必要がないかも知れません。
アプリケーションサーバ¶
最も重要なこと...
- 異なるカスタマーに異なるテーマを使う1つの"知的な"アプリケーションがあれば、仕事を減らすことができます。
- Javaアプリケーションについて、(apache+ajpがするように)TomcatのURLについてTomcatをだますために、
Remote IP Valve
を使います。
準備
ネットワーク設定¶
ネットワーク設定を/etc/sysctl.d/10-network-security.conf
ファイルに置きました。設定の幾つかは既に下の設定の中にありました。
### http://www.cyberciti.biz/tips/linux-unix-bsd-nginx-webserver-security.html
# Avoid a smurf attack
net.ipv4.icmp_echo_ignore_broadcasts = 1
# Turn on protection for bad icmp error messages
net.ipv4.icmp_ignore_bogus_error_responses = 1
# Turn on syncookies for SYN flood attack protection
net.ipv4.tcp_syncookies = 1
# Turn on and log spoofed, source routed, and redirect packets
#net.ipv4.conf.all.log_martians = 1
#net.ipv4.conf.default.log_martians = 1
# No source routed packets here
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
# Turn on reverse path filtering
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# Make sure no one can alter the routing tables
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.secure_redirects = 0
net.ipv4.conf.default.secure_redirects = 0
# Don't act as a router
net.ipv4.ip_forward = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
# Turn on execshild
kernel.exec-shield = 1
kernel.randomize_va_space = 1
# Tuen IPv6
net.ipv6.conf.default.router_solicitations = 0
net.ipv6.conf.default.accept_ra_rtr_pref = 0
net.ipv6.conf.default.accept_ra_pinfo = 0
net.ipv6.conf.default.accept_ra_defrtr = 0
net.ipv6.conf.default.autoconf = 0
net.ipv6.conf.default.dad_transmits = 0
net.ipv6.conf.default.max_addresses = 1
# Optimization for port usefor LBs
# Increase system file descriptor limit
fs.file-max = 65535
# Allow for more PIDs (to reduce rollover problems)
# !!! may break some programs 32768
#kernel.pid_max = 65536
# Increase system IP port limits
net.ipv4.ip_local_port_range = 2000 65000
# Increase TCP max buffer size setable using setsockopt()
net.ipv4.tcp_rmem = 4096 87380 8388608
net.ipv4.tcp_wmem = 4096 87380 8388608
# Increase Linux auto tuning TCP buffer limits
# min, default, and max number of bytes to use
# set max to at least 4MB, or higher if you use very high BDP paths
# Tcp Windows etc
net.core.rmem_max = 8388608
net.core.wmem_max = 8388608
net.core.netdev_max_backlog = 5000
net.ipv4.tcp_window_scaling = 1
仮想アドレス¶
これは1つのシステムのVRRP設定 /etc/keepalived/keepalived.conf
です。2つおインスタンスの設定があります。障害時には、両方のVRRPのアドレスが、残っているシステムに所属します。2つ目のシステムのために、"state"と"priority"の値を変更します。
注意
インタフェースをプロミスカスモードに設定した後で、keepalived
を再起動する必要があります例えば、デバッグのためにtcpdumpを使う場合)。
vrrp_instance ONE {
state MASTER
priority 120
interface eth0
virtual_router_id <id-1>
advert_int 1
authentication {
auth_type pass
auth_pass <pass-1>
}
virtual_ipaddress_excluded {
<vrrp-ipv4-1>
<vrrp-ipv6-1>
}
}
vrrp_instance TWO {
state BACKUP
priority 80
interface eth0
virtual_router_id <id-2>
advert_int 1
authentication {
auth_type pass
auth_pass <pass-2>
}
virtual_ipaddress_excluded {
<vrrp-ipv4-2>
<vrrp-ipv6-2>
}
}
HTTPS アドレス¶
1つのありえる解決方法は、NAT(network address translation)ではなく、直接ルーティングを使うことです。この場合は、NGINX設定のサーバに一致するローカルIPアドレスが必要です。/etc/network/interfaces
ファイルの中に、このようにダミー(あるいはループバック)のための post-up
コマンドを追加することができます。モジュールdummy
を /etc/modules
に追加するのを忘れないでください。
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet static
...
post-up /etc/nginx/conf.d/ip-mtu.sh
iface eth0 inet6 static
...
auto dummy0
iface dummy0 inet manual
up /sbin/ip link set dummy0 up
post-up /etc/nginx/conf.d/ip-addr.sh
down /sbin/ip link set dummy0 down
参照したスクリプトは幾つかのarp発行を訂正し、ルートされたネットワークのping-pongパケットを避けるためにブラックホールルーティングを行い、もちろんネットワークアドレスを追加します。
#!/bin/bash
echo 0 > /proc/sys/net/ipv4/ip_no_pmtu_disc
echo 1 > /proc/sys/net/ipv4/tcp_mtu_probing
echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce
echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore
echo 2 > /proc/sys/net/ipv4/conf/dummy0/arp_announce
echo 1 > /proc/sys/net/ipv4/conf/dummy0/arp_ignore
ip route add blackhole <network-1>
ip route add blackhole <network-2>
...
ip route add blackhole <network-n>
ip addr add <address-1>/32 dev dummy0 label <label-1>
ip addr add <address-2>/32 dev dummy0 label <label-2>
...
ip addr add <address-x>/32 dev dummy0 label <label-x>
Maybe you need a second file for all settings with requires a working network interface (e.g. if you have to fix some MTU/MSS values, you have to route to real ips on a real interfaces).
#!/bin/bash
# VPN Networks with broken PMTU
# (ADVMSS = MTU - 40)
ip route add <host-/network-1> via <default gateway> mtu <mtu> advmss <mtu-40>
ip route add <host-/network-2> via <default gateway> mtu <mtu> advmss <mtu-40>
...
ip route add <host-/network-m> via <default gateway> mtu <mtu> advmss <mtu-40>
核となる設定¶
nginx.conf¶
デフォルトの設定ファイル /etc/nginx/nginx.conf
にあまり変更をしないことに決めました。VMは4つのコアを持ち、各コアは1つの固定のworkerを取り、NGINXは他のプロセスよりも優先度を高くしたいです。他の全ての設定はincludeされていました(includeされるファイルmime.types
はプロジェクトHTML5-Boilerplateから選ばれています)。
worker_processes 4;
worker_priority -1;
worker_rlimit_nofile 8192;
worker_cpu_affinity 0001 0010 0100 1000;
user www-data;
pid /var/run/nginx.pid;
error_log /var/log/nginx/error.log;
events {
multi_accept on;
worker_connections 4096;
}
http {
map_hash_bucket_size 128;
include /etc/nginx/mime.types;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
sslproxy.conf¶
ファイル/etc/nginx/conf.d/sslproxy.conf
は全ての重要なグローバル設定を持ちます。特に:
- error_page 404 =410 /40x.html;
- 404エラーページはieの内部ページを避けるために410として隠されるでしょう。
- proxy_intercept_errors on;
- アプリケーションサーバからの全てのエラーは対応するローカルエラーページの後ろに隠されるでしょう。
- proxy_redirect http:// $scheme://;
- アプリケーションサーバからの各HTTPリダイレクトはHTTPSにrewriteされるでしょう。
- proxy_set_header Accept-Encoding “”;
- バックエンドへのプロキシインタフェースはデータを圧縮してはいけません(lan接続)。
### global ###
server_tokens off;
server_name_in_redirect off;
ignore_invalid_headers on;
if_modified_since before;
root /etc/nginx/content/;
ssi on;
ssi_silent_errors on; # testing=off
add_header X-Frame-Options SAMEORIGIN;
add_header Strict-Transport-Security max-age=16000000;
### tcp ###
tcp_nodelay off;
tcp_nopush on;
sendfile on;
keepalive_requests 100;
### timeouts ###
resolver_timeout 6;
client_header_timeout 30;
client_body_timeout 60;
send_timeout 60;
keepalive_timeout 65 20;
### buffers ###
client_header_buffer_size 1k;
client_body_buffer_size 128k;
large_client_header_buffers 4 4k;
client_max_body_size 10m;
client_body_temp_path /var/spool/nginx/client/;
output_buffers 1 32k;
postpone_output 1460;
### errors ###
recursive_error_pages off;
error_page 400 402 403 405 406 410 411 413 416 /40x.html;
error_page 500 501 502 503 504 /50x.html;
error_page 404 =410 /40x.html;
error_page 443 =200 /test.png;
open_log_file_cache max=1024 inactive=30s min_uses=3 valid=5m;
### acl ###
allow 10.0.0.0/8;
allow 172.16.0.0/12;
allow 192.168.0.0/16;
deny all;
### ssl ###
ssl on;
#ssl_stapling on; # selfsigned=off
#ssl_stapling_verify on; # selfsigned=off
ssl_prefer_server_ciphers on;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!RC4:!3DES:!aDSS:!aNULL:!kPSK:!kSRP:!MD5:@STRENGTH:+SHA1:+kRSA;
ssl_session_cache shared:TLSSL:16m;
ssl_session_timeout 10m;
ssl_certificate sslcert/de/<company>/wildcard.crt;
ssl_certificate_key sslcert/de/<company>/wildcard.key;
### compression ###
gzip on;
gzip_disable "msie6";
gzip_vary on;
gzip_min_length 128;
gzip_buffers 128 32k;
gzip_comp_level 6;
gzip_proxied any;
gzip_types text/plain text/css text/x-component
text/xml application/xml application/xhtml+xml application/json
image/x-icon image/bmp image/svg+xml application/atom+xml
text/javascript application/javascript application/x-javascript
application/pdf application/postscript
application/rtf application/msword
application/vnd.ms-powerpoint application/vnd.ms-excel
application/vnd.ms-fontobject application/vnd.wap.wml
application/x-font-ttf application/x-font-opentype;
### proxy-global ###
#resolver <dns-proxy>; # we use "pdnsd" here
proxy_intercept_errors on; # testing=off
proxy_ignore_client_abort off;
proxy_redirect http:// $scheme://;
### proxy-header ###
proxy_hide_header Server;
proxy_hide_header X-Powered-By;
proxy_hide_header X-AspNet-Version;
proxy_set_header Accept-Encoding ""; # no backend compression
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-By $server_addr:$server_port;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Class $classification; # our internal custom header
proxy_set_header X-Forwarded-Proto $scheme;
map $scheme $msiis { http off; https on; } # compatibility
proxy_set_header Front-End-Https $msiis;
### proxy-timeouts ###
proxy_connect_timeout 6;
proxy_send_timeout 60;
proxy_read_timeout 60;
### proxy-buffers ###
proxy_buffering on;
proxy_buffer_size 8k;
proxy_buffers 256 8k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64k;
proxy_temp_path /var/spool/nginx/temp/;
logging.conf¶
幾つかの追加の情報が必要な場合、この設定ファイル/etc/nginx/conf.d/logging.conf
は記録を引き起こさなければなりません。SSLプロキシをネットワークデバイスとして定義したため、したがってアプリケーションはユーザアクセスを記録する責任があります。
log_format apache
'$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'"$http_cookie"';
log_format full
'$remote_addr $remote_user [$time_local] '
'"$host"->$proxy_host->$upstream_addr '
'"$request" $status($upstream_status) '
'$bytes_sent/$gzip_ratio($sent_http_content_type) '
'$request_time($upstream_response_time)';
log_format perf
'$request_time($upstream_response_time) '
'$bytes_sent/$gzip_ratio($sent_http_content_type) '
'$status "$upstream_addr$uri"';
log_format gzip
'$bytes_sent/$gzip_ratio($sent_http_content_type) '
'[$http_accept_encoding]"$http_user_agent"';
log_format redirect
'$time_local $redir_match $redir_action $redir_url';
#access_log off;
access_log /var/log/nginx/access.log apache;
#access_log /var/log/nginx/access-full.log full;
#access_log /var/log/nginx/access-perf.log perf;
#access_log /var/log/nginx/access-gzip.log gzip;
backend.conf¶
このファイルをIDを持つ2つのバックエンドサーバの関係を定義するために使います。NGINXの構文はこれらの定義をグローバルにするだけです。このことは、新しい1つのバックエンド/アップストリームを定義した場合に2つのファイルをtouchする必要があることを意味します。
upstream <backend-id-1> {
server <server-ip-1.1>:<internal-port>;
server <server-ip-1.2>:<internal-port> backup;
}
upstream <backend-id-2> {
server <server-ip-2.1>:<internal-port>;
server <server-ip-2.2>:<internal-port> backup;
}
...
upstream <backend-id-n> {
server <server-ip-n.1>:<internal-port>;
server <server-ip-n.2>:<internal-port> backup;
}
ssl-proxyの最近のバージョンでは、亜フィルは/etc/nginx/scripts/gen-upstream.conf
を使ってアプリケーション定義から自動的に生成されます。つまり、これで1つのpath_<context>
ファイルによって新しいサービスを追加することができます。
#!/bin/sh
START=`pwd`
cd /etc/nginx
cat mapping/dem/path_* | grep "#UP#" | cut -c "6-" > conf.d/upstream.dem-auto.conf
cat mapping/prd/path_* | grep "#UP#" | cut -c "6-" > conf.d/upstream.prd-auto.conf
nginx -t
cd $START
サーバとアプリケーション¶
簡単なアプリケーション¶
これは/etc/nginx/mapping/<segment>/<application>
にあるデフォルトのアプリケーションの例で、これはほとんどの場合に当てはまるはずです。最初の部分はバックエンド設定の自動生成のために使われるコメントです。
#UP# upstream <backend-id-n> {
#UP# server <server-ip-n.1>:<internal-port>;
#UP# server <server-ip-n.2>:<internal-port> backup;
#UP# }
location /<app-path>/ { proxy_pass http://<backend-id>; }
rootのアプリケーション¶
幾つかのwebアプリケーションはrootパスをサブディレクトリに変更することを許可しません。それらのうちの一つをサーバ設定の中のサブディレクトリを使ってアプリケーションと組み合わせて使うことができます。proxy_intercept_errors
機能を使いたい場合は、if
-ステートメントを追加する必要があります。
location / {
if (-f $request_filename) { break; }
proxy_pass http://<backend-id>;
}
アップローダー アプリケーション¶
CMSシステムの編集ページのような幾つかのアプリケーションはしばしば追加の必要があります。例えば映画あるいは大きなPDFをアップロードしたいとします。その場合アップロードの最大サイズを調整する必要があります。
location /<app-path>/ {
client_max_body_size 100m;
proxy_pass http://<backend-id>; }
長時間実行しているアプリケーション¶
アプリケーションの応答が長く掛かり(例えば、レポートの生成)、keepaliveの仕組みが利用できない(私達の場合は"BIRT"フレームワークでした)場合、タイムアウトのデフォルトの設定を、クライアントおよびサーバ側で、予定以上に使いました。
location /<app-path>/ {
send_timeout 3600;
proxy_read_timeout 3600;
proxy_pass http://<backend-id>;
}
Soap Web-Service¶
クライアントと情報を交換するデフォルトの方法のため、SOAPは タイプ500のエラーを変更しないことを必要とします。
location /<app-path>/ {
proxy_intercept_errors off;
proxy_pass http://<backend-id>;
}
簡単なサーバ¶
この例は、1つの簡単なアプリケーションを使ってインターネット(全てを許可)サーバを説明します。rewriteルールはアプリケーションディレクトリに初期リダイレクトを行います。以下のindexページはアプリケーションの責任下にあります。
server {
ssl_certificate sslcert/<dns-domain>/<subdomain>.crt;
ssl_certificate_key sslcert/<dns-dmoain>/<subdomain>.key;
listen <ip>:443; allow all;
server_name <ip> <dns>;
set $classification "<customer>.<channel>.<segment>";
rewrite ^/+$ /<app-path>/ redirect;
include mapping/<segment>/<app-path>
}
リダイレクタ¶
これは私達のセットアップの中でより複雑なうちの一つです。古い/レガシー URLをスムーズに変更する必要がある場合にのみ、これを実装してください。
Motivation / Goal:
- 新しいリンクを使って、リダイレクト、リフレッシュ、あるいはエラーページを送信します。
- DNS名あるいはDNSプラス コンテキストのために動作します(パスの最初の部分)
www.
プリフィックスの暗黙的な一致- URLとリクエストの引数のために動作します
- 引数内で %-コード をパースすることができます
redir-map.conf¶
map $redir_match $redir_target { hostnames;
#[<context>.]<hostname> #(static|refresh|redirect)@<scheme>://<target>/<context>/;
my-app-1.old-url.com redirect@https://new-url.com/my-app-1/;
.old-url.com redirect@https://new-url.com/default-app/;
redir.action¶
if ($redir_target ~* ^(.*)@(.*)) { set $redir_action $1; set $redir_url $2; }
if ($redir_action = "static") { rewrite ^ /301-static.html last; }
if ($redir_action = "refresh") { rewrite ^ /301-refresh.html last; }
if ($redir_action = "redirect") { rewrite ^ $redir_url permanent; break;
access_log /var/log/nginx/redirector.log redirect;}
リダイレクタ サーバ¶
server {
allow all;
listen 80 default; ssl off;
listen 443 default ssl;
server_name <dns-name>;
include mapping/security.ext;
location / {
# deliver local files
if (-f $request_filename) { break; }
# redirector
set $redir_host $http_host;
if ($http_host ~* ^www\.(.*)) { set $redir_host $1; }
if ($uri ~* ^/([^/]+)) { set $redir_context $1.; }
set $redir_match $redir_context$redir_host;
include mapping/redir.action;
# global https enforcement
if ($scheme = "http") {
rewrite ^ https://$http_host$request_uri permanent; }
}
location /status {
stub_status on;
allow <monitoring system>;
deny all;
}
}
リダイレクタ アプリケーション¶
この部分はとても特別です: カスタマーのほとんどがsignonページ(SSOシステム)をブックマックしているため、これをrewriteすることも注意しなければなりません。
location /<login-app>/ {
if ( $arg_<return-url> ~* ^https?(://|%3A%2F%2F)([^/%]+)(/|%2F)([^/%]*) ) {
set $redir_match $4.$2; }
include mapping/redirector.action;
proxy_pass http://<backend-id>;
}
Active-Sync ゲートウェイ¶
This is only a simple gateway (no certificates!) for several different Exchange servers. dnsエントリに対して、デバイスの幾つかの"fingerprint"を検証します。The code can be “plugged” into the context files above as a service.
location /Microsoft-Server-ActiveSync {
access_log /var/log/nginx/activesync.log;
resolver your.dns.server.ip;
# deny anonymous; deny other http methods
if ( $remote_user = "" ) { return 444; break; }
if ( $request_method !~* ^(POST|OPTIONS)$ ) { return 444; break; }
# extract domain and user-id
if ( $remote_user ~* ^(.+)\x5C(.+)$ ) { set $domain $1; set $userid $2; }
if ( $remote_user !~* ^(.+)\x5C(.+)$ ) { return 444; break; }
# replace underscores in username
if ( $userid ~* ^(.+)_(.+)$ ) { set $userdn $1x$2; }
if ( $userid !~* ^(.+)_(.+)$ ) { set $userdn $userid; }
# extract device-type and version
if ( $http_user_agent ~* ^MSFT-(.+)/(.+)\.(.+)\.(.+)$ ) { set $device MSFT$1; set $versio $2x$3x$4; }
if ( $http_user_agent ~* ^Apple-iPhone(.*)/(.+)\.(.+)$ ) { set $device iPhone; set $versio $1x$2x$3; }
if ( $http_user_agent ~* ^Apple-iPad(.+)/(.+)\.(.+)$ ) { set $device iPad; set $versio $1x$2x$3; }
if ( $http_user_agent ~* ^Apple-iPod(.+)/(.+)\.(.+)$ ) { set $device iPod; set $versio $1x$2x$3; }
if ( $http_user_agent ~* ^Android-(.+)/(.+)\.(.+)$ ) { set $device Android; set $versio $1x$2x$3; }
# always allow initial requests without arguments
set $initia $request_method:$args;
if ( $initia ~* ^OPTIONS:$ ) { set $target $domain-exchange; set $versio ok; }
if ( $versio = "" ) { return 444; break; }
# set target, if usernames match
if ( $userid = $arg_User ) { set $target $domain-$userdn-$arg_DeviceId-$device-$versio; }
# forward request
proxy_pass http://$target.your.internal.sync.domain;
}
失敗したリクエストはリゾルバ エラーとしてerror.log
の中に現れます。error.log
はrsyslogによって監視され、syslogサーバに転送されました。syslogサーバは内部ドメインをチェックし、サポートにメールを送信します。
...
# Mail-Trap: ActiveSync
$ActionExecOnlyOnceEveryInterval 300
$ActionMailTo recicpient-1@your.company
$ActionMailTo recicpient-2@your.company
:msg,contains,"your.internal.sync.domain" :ommail:;mailBody
...
リモート ログ¶
問題¶
手短に: NGINXはSyslogをサポートしません。 したがって、Syslogサポートを必要とする場合は幾つかの取りうる方法があります。
- syslog patchと一緒にNGINXをコンパイル:
私はオリジナルのパッケージを使うことが好きです...
- ファイルサポートがあるsyslog実装 (例えば "imfile"を使ったrsyslog) を使用:
error.log
については良いです。access.logをローカルに2回保存したくはないので、access.log
が消費する領域については良い考えではありません。
簡単な解決法¶
- 全ての(既存の)syslogメッセージについて/etc/rsyslog.d/remote.confファイルを生成する:
# export via udp *.notice;local0,local1,local2,local3,local4,local5,local6,local7.*;mark,cron.none @<syslog-server>
- ファイルの監視に/etc/rsyslog.d/nginx.conf ファイルを生成する。Repeat the part in the middle for every file you want to see in the syslog. 最後の行が重要です。そうでなければ、これらのメッセージを3度記録するでしょう (NGINX log, udp syslog そしてローカルsyslog):
# import-module: file $ModLoad imfile # nginx/error.log $InputFileName /var/log/nginx/error.log $InputFileTag nginx: $InputFileStateFile nginx_error.log $InputFileSeverity warning $InputFileFacility local7 $InputRunFileMonitor # send and drop :syslogtag,isequal,"nginx:" @<syslog-server> & ~
Create a script /etc/cron.daily/logfile-actions, which will be executed every day and place there the cleanup commands (eg. アクセスログについては1日、その他全てについえてゃ6ヶ月)。
chmod +x
するのを忘れないでください。This this at least process all files, which you don’t want to store local a second time. しかし、以前言ったように、1日のアクセスログの量があまり大きく無い場合にのみ動作します... そしてそれはあまりスマートではありません。#!/bin/sh find /var/log/ -name *.gz -mtime +180 -delete find /var/log/nginx/ -name access*.gz -mtime +2 -delete
索引: スクリプト¶
sync-config.sh¶
毎日の使用のための最も重要なスクリプトです。しかし、年を重ねるに連れて、とても醜いものになっています。
#!/bin/bash
case `hostname` in
"sslproxy-01" )
PEER="sslproxy-02";;
"sslproxy-02" )
PEER="sslproxy-01";;
esac
START=`pwd`
/etc/init.d/nginx reload
sleep 2
chown www-data:adm /var/log/nginx/*
/etc/init.d/keepalived reload
cd /etc/nginx/sslcert/
tar -cvjpf /etc/nginx/sslcert.tbz2 ./*
cd $START
echo "
put -P /etc/cron.daily/logfile-actions /etc/cron.daily/
put -P /etc/sysctl.d/10-network-security.conf /etc/sysctl.d/
put -P /etc/monit/monitrc /etc/monit/
put -P /etc/monit/conf.d/* /etc/monit/conf.d/
put -P /etc/keepalived/keepalived.conf /etc/keepalived/remote.conf
put -P /etc/keepalived/remote.conf /etc/keepalived/keepalived.conf
put -P /etc/nginx/sslcert.tbz2 /etc/nginx/
put -P /etc/nginx/nginx.conf /etc/nginx/
rm /etc/nginx/conf.d/*
put -P /etc/nginx/conf.d/* /etc/nginx/conf.d/
rm /etc/nginx/content/*
put -P /etc/nginx/content/* /etc/nginx/content/
rm /etc/nginx/scripts/*
put -P /etc/nginx/scripts/* /etc/nginx/scripts/
rm /etc/nginx/sites-enabled/*
put -P /etc/nginx/sites-enabled/* /etc/nginx/sites-enabled/
rm /etc/nginx/mapping/*
rm /etc/nginx/mapping/dem/*
rm /etc/nginx/mapping/prd/*
put -P /etc/nginx/mapping/* /etc/nginx/mapping/
put -P /etc/nginx/mapping/dem/* /etc/nginx/mapping/dem/
put -P /etc/nginx/mapping/prd/* /etc/nginx/mapping/prd/
rm /etc/nginx/access/*
rm /etc/nginx/access/lan/*
rm /etc/nginx/access/ext/*
rm /etc/nginx/access/vn1/*
rm /etc/nginx/access/vn2/*
put -P /etc/nginx/access/* /etc/nginx/access/
put -P /etc/nginx/access/lan/* /etc/nginx/access/lan/
put -P /etc/nginx/access/ext/* /etc/nginx/access/ext/
put -P /etc/nginx/access/vn1/* /etc/nginx/access/vn1/
put -P /etc/nginx/access/vn2/* /etc/nginx/access/vn2/
bye
" | sftp -C root@$PEER
rm /etc/nginx/sslcert.tbz2
ssh $PEER '
cd /etc/nginx/sslcert/
rm -rf ./*
tar -xvjpf /etc/nginx/sslcert.tbz2
rm /etc/nginx/sslcert.tbz2
/etc/init.d/nginx reload
chown www-data:adm /var/log/nginx/*
/etc/init.d/keepalived reload
'
exit 0
dump-config.sh¶
"正規化"設定ファイルを生成します。基本的に、include-ステートメントを評価し、空白とコメントを削除する再帰的なスクリプトです。これは、バックアップ/リストア、ssl検証などを行う私のほとんどのスクリプトの基本です。おそらく綺麗でも無く、完全でもありませんが、動作します。
#!/bin/sh
START=`pwd`
cd /etc/nginx
if [ -x $0 ]
then CMD=$0
else CMD=$START/$0
fi
if [ "$1" ]
then FILE=$1
else FILE="nginx.conf"
fi
echo "# start: $FILE"
cat $FILE | awk '{
gsub("#.*","",$0);
gsub(";",";\n",$0);
gsub("{","\n{\n",$0);
gsub("}","\n}\n",$0);
print;
}' | awk -v HK="'" -v CMD=$CMD '{
gsub("[ \t]+"," ",$0);
gsub("^[ \t]","",$0);
gsub("[ \t]$","",$0);
gsub(HK,"%%",$0);
if ($1=="include") {
sub(";$","",$2);
print CMD" "HK$2HK; }
else {
print "echo "HK$0HK; }
}' | sh | awk -v HK="'" '{
gsub("%%",HK,$0);
if ($0=="") {
pass; }
else {
print; }
}' | cat
echo "# stop: $FILE"
cd $START
#exit 0
clean-restart.sh¶
このスクリプトは幾つかのサービスを再起動し、ログファイルを削除し、大きな変更の場合にループバックアドレスを再活性化します。開発システムで特に使います。製品マシーン上では、意図しないリブート時に正しく全てのことが開始されるように、そうしないで変更を行ってから再起動します。 unexpected reboot.
#!/bin/bash
/etc/init.d/monit stop
/etc/init.d/keepalived stop
/etc/init.d/nginx stop
ifconfig -a | grep "lo:" | awk '{print "ifconfig "$1" down"}' | sh
chmod +x /etc/nginx/conf.d/ip-addr.sh
chmod -R +x /etc/nginx/scripts/*
chmod -R 600 /etc/nginx/sslcert/*
rm /var/log/monit
rm /var/log/nginx/*
# other commands, like "apt-get -y upgrade"
/etc/nginx/conf.d/ip-addr.sh
/etc/init.d/nginx start
/etc/init.d/keepalived start
/etc/init.d/monit start
exit 0
良く知られたバグ / 欲しいもののリスト¶
- インラインの include
- With an statement like
include @<identifier>
and a block likeinclude { include_name <identifier>; ... }
a seperate file for every include could be avoided. - グローバルなrewriteルール と rewriteのためのログオプション
- If you have a inverse proxy it would be the perfect place to enforce a bunch o rewrite rules globaly. Because this is an security feature, each firing of one rule should be logged in a (separate?) log.