#author("2018-11-04T17:29:48+09:00","default:nowsky","nowsky")
*NGINX [#v73bd6c9]
同時処理速度、メモリの軽さが特徴のWebサーバ。
Webサーバ以外にもリバースプロキシとして動かす事も出来る。
ソフトウェア実装としては珍しい、UDPリバースプロキシにも対応している。
#author("2018-11-04T21:48:54+09:00","default:nowsky","nowsky")
*PowerDNS [#xe753079]
DNSゾーンを保存するバックエンドにMySQL・PostgreSQLなどのデータベースを利用出来る権威DNSサーバ。
APIも用意されており、スクリプトで直にAPIを叩いたり、WebGUIから間接的に操作する事も出来る。
BINDのテキストゾーンファイルを読み込んだりTinyDNSとの連携もでき、他の権威DNSサーバとの親和性も高い。
WebGUIでゾーン編集ができる事から、PowerDNSをHiddenPrimaryにして実クエリは別の権威DNSで処理させる構成が多い。
~
*参考サイト [#e83a40ec]
-[[nginx news:+http://nginx.org/]]
-[[Nginxをソースコードからインストール:+https://qiita.com/HayneRyo/items/eccc36302623088ad7f3]]
-[[Serving CGI Scripts With Nginx On OpenSUSE:+https://www.howtoforge.com/serving-cgi-scripts-with-nginx-on-opensuse-11.4-p2]]
*参考サイト [#rf3adb39]
-[[PowerDNS:+https://www.powerdns.com/]]
-[[PowerDNS 3.1 On Debian 7 (Wheezy):+https://stacksetup.com/DNS/PowerDNSDebian]]
~
*インストール [#jc6da6b3]
-&size(16){&font(b){1. インストール};};
nginxの全機能を利用するにはプログラムをソースコードからビルドする必要がある。
特に、リバースプロキシなど最新の実装機能を利用する場合には必須となる。
今回は機能を全部有効化してビルドするので、長くなるconfigure指定を別ファイル化しておき、
configureする時にコマンド置換(バッククォート)で読み込む。
 # vi configure.option
*構築環境 [#kd7a873d]
今回は下記構成のPowerDNSをchrootモードで構築する。
バックエンドにはMySQLを利用するので、事前にビルドしておく。
- [[Linux/Source/MySQL]]

OSはopenSUSEを利用しているがプログラムはソースからビルドしているので、
他ディストリビューションでも同様の手順で構築する事が出来る。
 OS  :openSUSE Leap 42.3
 PDNS:PowerDNS v4.1.4
 DB  :MariaDB v10.1.17
 ---
  --prefix=/usr/local/nginx-1.13.9
  --with-select_module
  --with-poll_module
  --with-threads
  --with-file-aio
  --with-http_ssl_module
  --with-http_v2_module
  --with-http_realip_module
  --with-http_addition_module
  --with-http_xslt_module
  --with-http_geoip_module
  --with-http_gunzip_module
  --with-http_gzip_static_module
  --with-http_auth_request_module
  --with-http_secure_link_module
  --with-http_degradation_module
  --with-http_slice_module
  --with-http_stub_status_module
  --with-mail
  --with-mail_ssl_module
  --with-stream
  --with-stream_ssl_module
  --with-stream_realip_module
  --with-stream_geoip_module
  --with-stream_ssl_preread_module
  --with-cpp_test_module
  --with-compat
  --with-pcre
  --with-pcre-jit
  --with-libatomic
 > PDNS Parameter
 user :pdns
 group:pdns
 ---
 > DB Parameter
 database:pdns_zone
 user    :pdns_user
 password:pdns_pass
~
*インストール [#b38ee685]
-&size(16){&font(b){1. インストール};};
最近のOSはsystemdを使っている事が多いので、systemdモジュールを有効化する。
また、今回はバックエンドにMySQLを使うのでDBモジュールもビルドする。
luaは使う時が来そうなので一応ビルド。必要なければconfigureオプションから外す。
 # zypper install lua lua-devel systemd systemd-devel
 # wget https://downloads.powerdns.com/releases/pdns-4.1.4.tar.bz2
 # tar jxvf pdns-4.1.4.tar.bz2
 # cd pdns-4.1.4
 # ./configure --prefix=/usr/local/powerdns --enable-tools --enable-systemd --with-lua --with-mysql=/usr/local/mysql
 # make
 # make install
 # ln -s /usr/local/powerdns/etc /etc/powerdns
 # cp /etc/powerdns/pdns.conf-dist /etc/powerdns/pdns.conf
 
 # wget http://nginx.org/download/nginx-1.13.9.tar.gz
 # tar zxvf nginx-1.13.9.tar.gz
 # cd nginx-1.13.9
 # ./configure `cat ../configure.option | tr '\r\n' ''`
 # make && make install
-&size(16){&font(b){2. コンフィグ作成};};
PowerDNSのコンフィグはテキストで作成する必要がある。
APIはPowerDNS単体では使わないので無効化しておき、APIを利用する外部ツールを使う時に有効化する。
DB接続は別ファイル化して必要に応じてパーミッションを絞っておく。
起動スクリプトについては、ソースコード同梱の物が上手く動かないので自作する。
 
-&size(16){&font(b){2. 起動準備};};
実行ユーザ、systemdスクリプト、ログローテート設定を準備する。
systemdスクリプトにPIDファイルを埋め込んでいる為、
後で作成するコンフィグのPID指定もsystemdに記載した物と合わせる。
 # vi /usr/lib/systemd/system/nginx.service
 ---
 [Unit]
 Description=NGINX - Web server and Reverse proxy
 Documentation=http://nginx.org/en/docs/
 After=local-fs.target remote-fs.target network.target nss-lookup.target
#region(/etc/powerdns/pdns.conf)
 8bit-dns=no                        # 8bitDNSクエリを拒否
 allow-axfr-ips=127.0.0.1,::1       # DNSゾーン転送(AXFR)の許可IP
 any-to-tcp=yes                     # TCPリモート参照に応答させる。DNSリフレクション攻撃の防止になる
 api=no                             # REST APIを無効化
 api-key=API-PASSWORD               # REST APIの事前共有キー
 cache-ttl=60                       # PowerDNSのDNSクエリ応答ローカルキャッシュ
                                    # この値はDNSゾーンのキャッシュとは違うので注意
 chroot=/var/powerdns/chroot        # PowerDNSをchroot動作させる時の起点ディレクトリ
 config-dir=/etc/powerdns           # コンフィグを読み取るディレクトリ
                                    # コンフィグ自体に設定しても読めないので、pdns.serviceにも設定
 
 [Install]
 WantedBy=multi-user.target
 disable-axfr=no                    # DNSゾーン転送を許可
 disable-syslog=no                  # ログをsyslog出力する。標準出力に出す時はyesに変更
 disable-tcp=no                     # TCPクエリにも応答させる
 dnsupdate=no                       # DNS Update(DynamicDNS)を無効化
 guardian=no                        # GuardianProcess内でのPDNS動作を無効化
                                    # 有効にした方がセキュリティが向上するが、chrootと組み合わせるとハマる
 
 include-dir=/etc/powerdns/extra    # 外部ファイルを保存するディレクトリ
 launch=gmysql                      # PowerDNSのバックエンドプログラム。2台のMySQLを参照させて冗長化も可能
 local-address=0.0.0.0              # PowerDNSでLISTENするIPv4アドレス
 local-ipv6=::                      # PowerDNSでLISTENするIPv6アドレス
 local-port=53                      # Listenする時に利用するポート番号
 
 log-dns-details=no                 # 詳細ログの出力を停止。Yesにするとログ出力が増えてパフォーマンスが下がる
 log-dns-queries=yes                # DNSクエリログを出力。 Yesにするとログ出力が増えてパフォーマンスが下がる
 log-timestamp=yes                  # ログにタイムスタンプを追加
 logging-facility=0                 # 出力ログのsyslog-facility番号。local[0-7]のみ設定可能
 loglevel=6                         # ログ出力レベル。1~3は基本的に使わない。デバッグ時は9を設定
 
 master=yes                         # PowerDNSをマスターとして動作
 slave=yes                          # PowerDNSをスレーブとして動作
 query-local-address=0.0.0.0        # PowerDNS自体が名前解決する時に利用する送信IPv4アドレス
 query-local-address6=::            # PowerDNS自体が名前解決する時に利用する送信IPv6アドレス
 query-logging=no                   # PowerDNS自体が名前解決したログ出力を無効化
 setgid=pdnsd                       # daemon実行グループ
 setuid=pdnsd                       # daemon実行ユーザ
 
 daemon=yes                         # daemonとして動作
 socket-dir=/var/run/powerdns       # PowerDNSのソケットファイルとPIDの保存先
                                    # chroot環境下ではchroot配下の絶体パスを設定
 version-string=unknown             # DNSのバージョン応答。秘匿する為にunknownに設定
 
 webserver=no                       # Webサーバ機能。REST API利用時は有効にする
 webserver-address=127.0.0.1        # WebサーバのLISTEN IPアドレス
 webserver-allow-from=127.0.0.1,::1 # Webサーバにアクセス出来るIPアドレス
 webserver-password=PASSWORD        # Webサーバにアクセスする際のパスワード
 webserver-port=8081                # Webサーバ/REST APIが待受けるポート番号
#endregion
 
#region(/etc/powerdns/extra/gmysql.conf)
 gmysql-host=localhost
 gmysql-port=3306
 gmysql-dbname=pdns_zone
 gmysql-user=pdns_user
 gmysql-password=pdns_pass
 gmysql-dnssec=no
#endregion
 
#region(/usr/lib/systemd/system/pdns.service)
 [Unit]
 Description=PowerDNS Authoritative Server
 Documentation=https://doc.powerdns.com/
 Wants=network-online.target
 After=network-online.target mysqld.service mariadb.service rsyslog.service
 
 [Service]
 Type=forking
 NotifyAccess=all
 PIDFile=/var/run/nginx.pid
 ExecStartPre=/usr/local/nginx/sbin/nginx -t -c /etc/nginx/nginx.conf
 ExecStart=/usr/local/nginx/sbin/nginx -c /etc/nginx/nginx.conf
 ExecReload=/bin/kill -s HUP $MAINPID
 ExecStop=/bin/kill -s QUIT $MAINPID
 
 ## Security Setting
 DevicePolicy=closed
 PrivateDevices=true
 ExecStart=/usr/local/powerdns/sbin/pdns_server --config-dir=/etc/powerdns
 ExecStop=/usr/local/powerdns/bin/pdns_control quit
 ExecReload=/usr/local/powerdns/bin/pdns_control reload
 ## OPTION
 Restart=on-failure
 RestartSec=1
 StartLimitInterval=0
 PrivateTmp=true
 PrivateDevices=true
 NoNewPrivileges=true
 ProtectSystem=full
 ProtectHome=true
 
 [Install]
 WantedBy=multi-user.target
#endregion
 
 # vi /etc/logrotate.d/nginx
-&size(16){&font(b){3. 起動準備};};
chrootディレクトリの作成、パーミッション設定、DBスキーマの流し込みを行う。
PowerDNSはログ出力をsyslog経由でしか出来ない為、ログを書き出せる様にsyslogサーバの設定も変更する。
ただし、chroot環境下でPowerDNSのログをsyslog出力させるには、
PowerDNSのログを&color(#ff0000){syslog側でフックさせる為に、&font(b){ソケット(ログ出力デバイス)を作成する};};必要がある。
 ・ユーザ作成と起動スクリプト設定
 ---
 /var/log/nginx/*.log {
     weekly
     compress
     rotate 4
     missingok
     ifempty
     sharedscripts
     postrotate
         systemctl restart nginx > /dev/null 2>/dev/null || true
     endscript
 }
 # groupadd pdnsd
 # useradd -g pdnsd -s /sbin/nologin -d /var/powerdns pdnsd
 # chmod 644 /usr/lib/systemd/system/pdns.service
 # chown root.root /usr/lib/systemd/system/pdns.service
 # systemctl enable pdns
 
 # ln -s /usr/local/nginx-1.13.9 /usr/local/nginx
 # ln -s /usr/local/nginx/conf /etc/nginx
 # rm /etc/nginx/koi-utf /etc/nginx/koi-win /etc/nginx/win-utf
 # mkdir /var/log/nginx
 # groupadd nginx
 # useradd -g nginx -s /sbin/nologin -d /var/nginx nginx
 # systemctl unmask nginx
 # systemctl enable nginx
 
-&size(16){&font(b){3. 基礎設定};};
nginxは利用する機能に応じてコンフィグの内容が大きく変化する。
下記では、nginxの実行ユーザなど基本的な部分のみ記載する。
 # vi /etc/nginx/nginx.conf
 ・chrootディレクトリ作成
 ---
 user       nginx;
 pid        /var/run/nginx.pid;
 error_log  /var/log/nginx/error.log;
 # mkdir -p /var/powerdns/chroot
 # mkdir -p /var/powerdns/chroot/dev
 # mkdir -p /var/powerdns/chroot/var/log
 # mkdir -p /var/powerdns/chroot/var/run/pdnsd
 # chmod 755        /var/powerdns/chroot/var/run/pdnsd
 # chown root.pdnsd /var/powerdns/chroot/var/run/pdnsd
 
 worker_processes       auto;
 worker_rlimit_nofile   4096;
 # cd /var/powerdns/chroot
 # touch                ./var/log/api
 # chmod -R 755         ./var/log
 # chown -R pdnsd.pdnsd ./var/log
 
 events {
     worker_connections 1024;
     accept_mutex_delay 50ms;
     multi_accept       on;
     use                epoll;
 }
~
*コンフィグ [#dd983aa4]
nginxは通常のWebサーバ動作や、リバースプロキシ動作など様々な用途に利用出来る。
用途に応じて必要な設定とディレクティブ指定が変わる為、使う機能に応じて内容を変更する。
同じサーバで違う処理を実装する場合、 "include" を使って設定を外出しすると整理しやすくなる。
 # cd /var/powerdns/chroot/dev
 # mknod -m 644 ./null c 1 3
 # mknod -m 644 ./zero c 1 5
 # mknod -m 644 ./full c 1 7
 # mknod -m 644 ./random c 1 8
 # mknod -m 644 ./urandom c 1 9
 

----

-&size(16){&font(b){UDPリバースプロキシ};};
UDPリバースプロキシのコンフィグは、nginxのTCPリバースプロキシ設定とほぼ同じになる。
ただし、nginxのフロントエンドとなるlistenの箇所にudpを指定する点に注意する。
 ・DBスキーマ登録
 ---
 MariaDB [(none)]> create database pdns_zone;
 MariaDB [(none)]> grant all on pdns_zone.* to pdns_user identified by 'pdns_pass';
 MariaDB [(none)]> flush privileges;
 MariaDB [(none)]> quit
 ---
 # cd ${PDNS_SRC}/modules/gmysqlbackend/
 # mysql -u pdns_user -p pdns_zone < schema.mysql.sql
 
下記はフロントエンドを「10.0.0.1」で待ち受け、
バックエンドの2IPアドレス「192.168.0.1」「192.168.0.2」へUDP53で振り分ける設定となる。
 stream {
     access_log /var/log/nginx/access.log basic;
     error_log  /var/log/nginx/error.log  info;
 ・rsyslog設定変更
 ---
 # vi /etc/rsyslog.conf
 ---
 ## PowerDNS chroot log device
 $AddUnixListenSocket    /var/powerdns/chroot/dev/log
 local0.*                /var/log/powerdns
 & stop
~
*DNSゾーン登録 [#oe42fac7]
バックエンドにMySQLを使っている場合、SQLコマンドでDNSゾーンを流し込むか、
BINDテキスト形式でゾーンを作った後に変換しながらDBに登録する方法がある。
今回はテスト用にDNSゾーンを登録する為、SQLコマンドで登録を行う。
 # vi example.com-sql
 ---
 INSERT INTO domains (name,type) values ('example.com','NATIVE');
 INSERT INTO records (domain_id,name,content,type,ttl,prio) VALUES (1,'example.com','localhost postmaster@example.com 1','SOA',86400,NULL);
 INSERT INTO records (domain_id,name,content,type,ttl,prio) VALUES (1,'example.com','ns1.example.com','NS',86400,NULL);
 INSERT INTO records (domain_id,name,content,type,ttl,prio) VALUES (1,'ns1.example.com','127.0.0.1','A',120,NULL);
 
     upstream DNS {
         hash   $remote_addr;
         server 192.168.0.1:53;
         server 192.168.0.2:53;
     }
 # mysql -u pdns_user -p pdns_zone < example.com-sql
 # dig @127.0.0.1 ns1.example.com
~
*起動テスト [#e739e825]
起動テストを行う場合、『pdns_server』コマンドをそのまま実行するとログが出てくる。
コンフィグを書き換えれば、ログを標準出力にしながらdaemon起動させる事も出来る。
 ・起動
 ---
 systemctl start pdns
 
     server {
         listen          10.0.0.1 udp;
         proxy_pass      DNS;
         proxy_timeout   1s;
         proxy_responses 1;
     }
 }
 

----

-&size(16){&font(b){HTTP Webサーバ};};
Apacheと違う所として「.htaccess」ファイルが動かない事と、
PHPを動かす時にはPHP-FPMを起動させて代理処理する点に注意する
 
下記はDocumentRootを "/var/nginx/html" とした上で、
nginxをWebサーバとして稼働させる設定例となる。
 http {
     default_type    application/octet-stream;
     include         /etc/nginx/mime.types;
     access_log      /var/log/nginx/access.log combined;
 ・停止
 ---
 systemctl stop pdns
 
     # Server Setting
     sendfile            on;
     tcp_nopush          on;
     autoindex           off;
     etag                off;
     server_tokens       off;
     keepalive_timeout   5;
 
     # Contents Compress
     gzip                on;
     gzip_http_version   1.0;
     gzip_disable        msie6;
     gzip_min_length     1024;
     gzip_types          text/plain
                         text/xml
                         text/css
                         application/xml
                         application/xhtml+xml
                         application/rss+xml
                         application/atom_xml
                         application/javascript
                         application/x-javascript
                         application/x-httpd-php;
 
     # HTTP Server
     server {
         listen      0.0.0.0:80 default_server;
         server_name _;
 
         location / {
             root    /var/nginx/html;
             index   index.html index.htm;
         }
 
         add_header  X-Frame-Options        SAMEORIGIN;
         add_header  X-XSS-Protection       "1; mode=block";
         add_header  X-Content-Type-Options nosniff;
     }
 }
 

----

-&size(16){&font(b){CGI実行環境};};
nginxの処理体系はイベント型の為、CGIを実行するには別途プログラムを噛ませる必要がある。
今回はCGI実行環境としてFastCGIを利用する。
 
下記はopenSUSE上でのCGI実行環境の構築例となる。
 # zypper install spawn-fcgi FastCGI FastCGI-devel
 # ln -s /usr/include/fastcgi/fastcgi.h     /usr/local/include/
 # ln -s /usr/include/fastcgi/fcgi_config.h /usr/local/include/
 # ln -s /usr/include/fastcgi/fcgi_stdio.h  /usr/local/include/
 # ln -s /usr/include/fastcgi/fcgiapp.h     /usr/local/include/
 # ln -s /usr/include/fastcgi/fcgimisc.h    /usr/local/include/
 # ln -s /usr/include/fastcgi/fcgio.h       /usr/local/include/
 # ln -s /usr/include/fastcgi/fcgios.h      /usr/local/include/
 
 # cd /usr/local/src
 # git clone https://github.com/gnosek/fcgiwrap `date "+%Y%m%d"`
 # ln -s `date "+%Y%m%d"` fcgiwrap
 # cd fcgiwrap
 # autoreconf -i
 # ./configure
 # make
 # make install
 
 # vi /etc/init.d/spawn-fcgi
 # chmod 755 /etc/init.d/spawn-fcgi
 # chown root.root /etc/init.d/spawn-fcgi
 # chkconfig --add spawn-fcgi
 # systemctl enable spawn-fcgi
 # systemctl start spawn-fcgi
#region(/etc/init.d/spawn-fcgi)
 #!/bin/sh
 #
 # spawn-fcgi        FastCGI rapper daemon
 #
 # chkconfig:        35 95 10
 # description:      FastCGI and SpawnFCGI rapper daemon
 #
 ### BEGIN INIT INFO
 # Provides:         spawn-fcgi
 # Description:      Start the spawn-fcgi
 # Required-Start:   $local_fs $remote_fs $network
 # Required-Stop:    $local_fs $remote_fs $network
 # Default-Start:    3 5
 # Default-Stop:     0 1 6
 ### END INIT INFO
 
 . /etc/rc.status
 rc_reset
 
 BIN="/usr/local/sbin/fcgiwrap"
 PID="/var/run/spawn-fcgi.pid"
 SOCK="/var/run/spawn-fcgi.socket"
 OPT="-M 0660"
 
 NAME=spawn-fcgi
 USER=nginx
 GROUP=nginx
 
 case "$1" in
   start)
     echo -n "Starting ${NAME}"
     /usr/bin/spawn-fcgi -u ${USER} -g ${GROUP} -s ${SOCK} -S ${OPT} -F 1 -P ${PID} -- ${BIN}
     rc_status -v
   ;;
 
   stop)
     echo -n "Shutdown ${NAME}"
     checkproc -p ${PID} ${BIN} || echo -n "- Warning: ${NAME} not running!"
     killproc -p ${PID} -TERM ${BIN}
     if [ -S ${SOCK} ]; then
       rm -f ${SOCK}
     fi
     rc_status -v
   ;;
 
   restart)
     $0 stop && sleep 3 && $0 start
     rc_status
   ;;
 
   *)
     echo "Usage: spawn-fcgi {start|stop|restart}"
     exit 1
   ;;
 esac
 
 rc_exit
#endregion
 
 # vi /etc/nginx/nginx.conf
 ・リロード
 ---
 http {
     server {
         location ~ [^/]\.(cgi|pl)($|\?|\/) {
            root            /var/nginx/html;
            fastcgi_pass    unix:/var/run/spawn-fcgi.socket;
            fastcgi_index   index.cgi,index.pl;
            include         /etc/nginx/fastcgi_params;
            fastcgi_param   PATH_INFO $fastcgi_path_info;
            fastcgi_param   PATH_TRANSLATED $document_root$fastcgi_path_info;
            fastcgi_param   SCRIPT_FILENAME $document_root$fastcgi_script_name;
         }
     }
 }
 
 systemctl reload pdns

----

-&size(16){&font(b){PHP実行環境};};
CGIと同様に、nginx上でPHPを実行する場合も別プログラムに投げる必要がある。
PHPを実行する手段は多種存在するが、今回はphp-fpmを利用手法をメモしておく。
 # zypper install php7 php7-devel php7-fpm
 # cp /etc/php7/fpm/php-fpm.conf.default /etc/php7/fpm/php-fpm.conf
 # cp /etc/php7/fpm/php-fpm.d/www.conf.default /etc/php7/fpm/php-fpm.d/www.conf
 # vi /etc/php7/fpm/php-fpm.d/www.conf
 ---
 [www]
 user  = nginx
 group = nginx
 listen = /var/run/php-fpm.socket
 listen.owner = nginx
 listen.group = nginx
 listen.mode  = 0660
 listen.allowed_clients = 127.0.0.1
 
 # /etc/init.d/php-fpm start
 # vi /etc/nginx/nginx.conf
 ---
 http {
     server {
         location ~ \.(php)$ {
             root            /var/nginx/html;
             fastcgi_pass    unix:/var/run/php-fpm.socket;
             fastcgi_index   index.php;
             include         /etc/nginx/fastcgi_params;
             fastcgi_param   PATH_INFO $fastcgi_path_info;
             fastcgi_param   PATH_TRANSLATED $document_root$fastcgi_path_info;
             fastcgi_param   SCRIPT_FILENAME $document_root$fastcgi_script_name;
         }
     }
 }