ドキュメント

Apache用モジュールをコンパイルできるようにする

  • httpd-devel (apxs) をインストールしておく。
    # yum install httpd-devel

バーチャルホスト設定

設定

  • /etc/httpd/conf.d/VirtualHosts.conf
    # Use name-based virtual hosting.
    NameVirtualHost *:80
    
    <VirtualHost *:80>
    	ServerName	www.takeash.net
    	DocumentRoot	/var/www/html/
    </VirtualHost>
    
    <VirtualHost *:80>
    	ServerName	vh1.takeash.net
    	DocumentRoot	/var/www/vh1-html/
    	<Directory "/var/www/vh1-html">
    #		AllowOverride	All
    	</Directory>
    </VirtualHost>

エラー対策

  • バーチャルホストが表示されない。
    • エラーメッセージ
      [warn] _default_ VirtualHost overlap on port 80, the first has precedence
    • 原因
      NameVirtualHost ディレクティブが設定されていない。
    • 対処
      NameVirtualHost ディレクティブを設定する。
      httpd.conf を修正するより conf.d に VirtualHosts.conf として専用のファイルを作成しておく方が忘れなくていいかも。

Digest 認証

  • 認証、承認、アクセス制御
  • mod_auth_digest
    • レルム/ユーザ/パスワード追加
      htdigest [-c] passwdfile <realm> <username>
    • /etc/httpd/conf.d/DigestAuth.conf
      <Directory "/var/www/html/Download/<realm>">
      	AllowOverride AuthConfig
      	AuthType Digest
      	AuthName "<realm>"
      	AuthUserFile /var/www/passwd/passwords_digest
      	Require user <username>
      	Options None
      	Options Indexes
      	DirectoryIndex index.html index.htm index.php
      	Order allow,deny
      	Allow from all
      </Directory>

mod_ssl

  • インストール
    # yum install mod_ssl

自己署名証明書使用

Certbot 使用

  • Certbot 使用前準備

    • ホスト名が正引きできること。(ワイルドカード不可)
    • バーチャルホストのサーバ名と要求するドメイン名のどれかが一致すること。
    • https でアクセス可能になっていること。
      • https ポート解放
        # firewall-cmd --add-service=https --permanent
  • Certbot インストール (EPEL リポジトリ)

    # yum install python-certbot-apache
  • 証明書取得
    取得に成功すると「/etc/letsencrypt/live/<ドメイン1>/」に証明書が作成される。

    # certbot --apache certonly -m <メールアドレス> -d <ドメイン1> [-d <ドメイン2> ...] --agree-tos
  • /etc/httpd/conf.d/ssl.conf (抜粋)

    Listen 443 https
    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/<ドメイン1>/cert.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/<ドメイン1>/privkey.pem
    SSLCertificateChainFile /etc/letsencrypt/live/<ドメイン1>/chain.pem
  • /etc/httpd/conf.d/VirtualHosts.conf (抜粋)
    バーチャルホスト毎に SSL 設定が必要。

    <VirtualHost *:80 *:443>
            ServerName      vh1.<ドメイン1>
            DocumentRoot    /var/www/vh1-html/
            SSLEngine on
            SSLCertificateFile /etc/letsencrypt/live/<ドメイン1>/cert.pem
            SSLCertificateKeyFile /etc/letsencrypt/live/<ドメイン1>/privkey.pem
            SSLCertificateChainFile /etc/letsencrypt/live/<ドメイン1>/chain.pem
            <Directory "/var/www/vh1-html">
                    AllowOverride   All
            </Directory>
    </VirtualHost>
  • 自動更新スクリプト /etc/cron.monthly/certbot.sh

    #!/bin/bash
    /bin/certbot renew
  • Certbot

  • Webサーバー間通信内容暗号化(Apache+mod_SSL+Certbot) - CentOSで自宅サーバー構築

Certbot 使用(ワイルドカード, 手動)

  • Certbot インストール (EPEL リポジトリ)

    # yum -y install yum-utils
    # yum-config-manager --enable rhui-REGION-rhel-server-extras rhui-REGION-rhel-server-optional
    # yum install certbot-apache
  • 証明書取得(手動)
    途中HTTPへのテキストファイルの配置とDNSへのTXTレコードの追加を指示されるので、追加してからEnterを押して先へ進む。
    取得に成功すると「/etc/letsencrypt/live/<ドメイン>/」に証明書が作成される。

    # certbot certonly --manual --server https://acme-v02.api.letsencrypt.org/directory -d "*.example.com" -d example.com
  • /etc/httpd/conf.d/ssl.conf (抜粋)

    Listen 443 https
    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/<ドメイン>/cert.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/<ドメイン>/privkey.pem
    SSLCertificateChainFile /etc/letsencrypt/live/<ドメイン>/chain.pem
  • /etc/httpd/conf.d/VirtualHosts.conf (抜粋)
    バーチャルホスト毎に SSL 設定が必要。

    <VirtualHost *:80 *:443>
            ServerName      vh1.<ドメイン1>
            DocumentRoot    /var/www/vh1-html/
            SSLEngine on
            SSLCertificateFile /etc/letsencrypt/live/<ドメイン1>/cert.pem
            SSLCertificateKeyFile /etc/letsencrypt/live/<ドメイン1>/privkey.pem
            SSLCertificateChainFile /etc/letsencrypt/live/<ドメイン1>/chain.pem
            <Directory "/var/www/vh1-html">
                    AllowOverride   All
            </Directory>
    </VirtualHost>
  • apache 再起動

    # systemctl restart httpd
  • 動作確認

    $ openssl s_client -connect <ホスト>:443
    
    • 設定失敗 CONNECTED(00000003) 140139752064912:error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol:s23_clnt.c:794: --- no peer certificate available --- No client certificate CA names sent --- SSL handshake has read 7 bytes and written 289 bytes --- New, (NONE), Cipher is (NONE) Secure Renegotiation IS NOT supported Compression: NONE Expansion: NONE No ALPN negotiated SSL-Session: Protocol : TLSv1.2 Cipher : 0000 Session-ID: Session-ID-ctx: Master-Key: Key-Arg : None Krb5 Principal: None PSK identity: None PSK identity hint: None Start Time: 1535884386 Timeout : 300 (sec) Verify return code: 0 (ok) ---
    • 設定成功 CONNECTED(00000003) depth=2 O = Digital Signature Trust Co., CN = DST Root CA X3 verify return:1 depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3 verify return:1 depth=0 CN = *.<ドメイン1> verify return:1 --- Certificate chain 0 s:/CN=*.<ドメイン1> i:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3 1 s:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3 i:/O=Digital Signature Trust Co./CN=DST Root CA X3 --- Server certificate -----BEGIN CERTIFICATE----- MIIGETCCBPmgAwIBAgISA3VBvI0cSyzAQGtpIaQKQRZxMA0GCSqGSIb3DQEBCwUA ...
  • 証明書更新
    「--manual」で取得した場合は「renew」による自動更新ができないので、既存の証明書を削除し同名で取得し直す。

    # certbot delete
  • 証明書の有効期限を表示
    getExpireDate.sh

    #!/bin/bash
    
    CommonName=example.net
    
    NotAfter=`openssl x509 -noout -dates -in /etc/letsencrypt/live/${CommonName}/fullchain.pem | \
      grep notAfter | \
      sed -e "s/notAfter=//" | \
      date -f - --iso-8601`
    echo ${NotAfter}

Certbot 使用(ワイルドカード, 自動)

  • Certbot, DNS Plugin インストール (EPEL リポジトリ)

    # yum -y install yum-utils
    # yum-config-manager --enable rhui-REGION-rhel-server-extras rhui-REGION-rhel-server-optional
    # yum install certbot-apache python2-certbot-dns-rfc2136
  • BIND 用認証キーの作成
    Kcertbot-key.+165+43987.key, Kcertbot-key.+165+43987.private の2つのファイルが作成される。

    # cd /etc/named/
    # dnssec-keygen -a HMAC-SHA512 -b 512 -n HOST certbot-key
  • 認証ファイル /etc/named/certbot_rfc2136.ini , ファイルモード 600

    # Target DNS server
    dns_rfc2136_server = 127.0.0.1
    # Target DNS port
    dns_rfc2136_port = 53
    # TSIG key name
    dns_rfc2136_name = certbot-key.
    # TSIG key secret
    dns_rfc2136_secret = <Kcertbot-key.+165+43987.key のハッシュ値>
    # TSIG key algorithm
    dns_rfc2136_algorithm = HMAC-SHA512
  • /etc/named.conf に追加

    key "certbot-key." {
      algorithm hmac-sha512;
      secret "<Kcertbot-key.+165+43987.key のハッシュ値>";
    };
    
    view "internal" {
      match-clients { localhost; localnets; };
      match-destinations { localhost; localnets; };
      recursion yes;
    
      zone "." IN {
        type hint;
        file "named.ca";
      };
    
      include "/etc/named.rfc1912.zones";
      include "/etc/named.root.key";
      include "/etc/named/<ドメイン>.lan.zone";
    };
    
    view "external" {
      match-clients { any; };
      match-destinations { any; };
      recursion no;
      include "/etc/named/<ドメイン>.wan.zone";
      include "/etc/named/_acme-challenge.<ドメイン>.wan.zone";
    };
  • /etc/named/<ドメイン>.lan.zone

    zone "<ドメイン>" {
      type master;
      file "<ドメイン>.lan.db";
      update-policy {
        grant certbot-key. name _acme-challenge.<ドメイン>. txt;
      };
    };
  • /etc/named/<ドメイン>.wan.zone

    zone "<ドメイン>" {
      type master;
      file "<ドメイン>.wan.db";
      allow-query { any; };
      update-policy {
        grant certbot-key. name _acme-challenge.<ドメイン>. txt;
      };
    };
  • /etc/named/_acme-challenge.<ドメイン>.wan.zone

    zone "_acme-challenge.<ドメイン>" {
      type master;
      file "_acme-challenge.<ドメイン>.wan.db";
      allow-query { any; };
      update-policy {
        grant certbot-key. name _acme-challenge.<ドメイン>. txt;
      };
    };
  • /var/named/<ドメイン>.wan.db

    $TTL    86400
    @       IN SOA  ns1.<ドメイン>.        root.<ドメイン>. (
                            2018090500 ; Serial
                            28800      ; Refresh
                            14400      ; Retry
                            2592000    ; Expire
                            86400      ; Minimum
                    )
            IN NS   ns1.<ドメイン>.
            IN MX 10 mail.<ドメイン>.
    @       IN A    <グローバル IP アドレス>
    ns1     IN A    <グローバル IP アドレス>
    www     IN A    <グローバル IP アドレス>
    mail    IN A    <グローバル IP アドレス>
    _acme-challenge IN NS ns1.<ドメイン>.
    *       IN A    <グローバル IP アドレス>
    <ドメイン>.    IN TXT "v=spf1 a mx ~all"
  • /var/named/_acme-challenge.<ドメイン>.wan.db

    $TTL    86400
    @       IN SOA  ns1.<ドメイン>.  root.<ドメイン>. (
                            2018090500 ; Serial
                            1h      ; Refresh
                            15m     ; Retry
                            30d     ; Expire
                            1h      ; Minimum
                    )
            IN NS   ns1.<ドメイン>.
  • <ドメイン>.jnl: create: permission denied(/var/named/data/named.run) 対策

    # chmod 770 /var/named/
    # setsebool -P named_write_master_zones 1
  • 証明書取得

    # certbot certonly \
      --dns-rfc2136 \
      --dns-rfc2136-credentials /etc/named/certbot_rfc2136.ini \
      -d "*.<ドメイン>" -d <ドメイン>

mod_security

インストール

  • EPELリポジトリ
    # yum install mod_security mod_security_crs

設定

  • /etc/httpd/conf.d/mod_security.conf (抜粋)

    # Maximum request body size we will
    # accept for buffering
    #SecRequestBodyLimit 131072
    SecRequestBodyLimit 5242880
    SecRequestBodyNoFilesLimit 51200
  • /etc/httpd/modsecurity.d/modsecurity_localrules.conf

    # Drop your local rules in here.
    
    # White List IP
    SecRule REMOTE_ADDR "@pmFromFile /etc/httpd/modsecurity.d/whitelist_ip.txt" \
            "phase:1,id:'1000001',nolog,allow,ctl:ruleEngine=Off,ctl:auditEngine=Off"
    
    # White List URI
    SecRule REQUEST_URI "@pmFromFile /etc/httpd/modsecurity.d/whitelist_uri.txt" \
            "phase:1,id:'1000002',nolog,allow,ctl:ruleEngine=Off,ctl:auditEngine=Off"
    
    # White List URI 2
    SecRule REQUEST_URI "@rx ^\/Etc\/" \
            "phase:1,id:'1000003',nolog,allow,ctl:ruleEngine=Off,ctl:auditEngine=Off"
    
     # White List Sub-Domain
     SecRule REQUEST_HEADERS:Host "@pmFromFile /etc/httpd/modsecurity.d/whitelist_subdomain.txt" \
             "phase:1,id:'1000004',nolog,allow,ctl:ruleEngine=Off,ctl:auditEngine=Off"
    
    # ZmEu Attack / phpMyAdmin
    SecRule REQUEST_URI "@rx (?i)\/(php-?My-?Admin[^\/]*|mysqlmanager|myadmin|pma2005|pma\/scripts|w00tw00t[^\/]+)\/" \
            "severity:alert,id:'0000013',deny,log,status:400,msg:'Unacceptable folder.',severity:'2'"
    • mod_security-2.7.1 でエラーが出るから適当にid追加したけど、idの振り方のルールってどこにあるのかな?
      ModSecurity: No action id present within the rule
  • /etc/httpd/modsecurity.d/whitelist_ip.txt
    mod_security による制限を行わない IP アドレスを列挙する。
    コメントは行頭から「#」で始める。

    # localhost
    127.0.0.1
    
    # example.com
    xxx.xxx.xxx.xxx
    
    # example.net
    yyy.yyy.yyy.yyy
  • /etc/httpd/modsecurity.d/whitelist_uri.txt
    mod_security による制限を行わない URI を列挙する。

    /cgi-bin/etc/PrintEnv.cgi
    /cgi-bin/etc/PrintEnv_txt.cgi
    /cgi-bin/etc/index.cgi
    /cgi-bin/etc/testCGI.cgi
  • /etc/httpd/modsecurity.d/whitelist_subdomain.txt
    mod_security による制限を行わないホスト名を列挙する。

    # WebApp1
    vh1.takeash.net
  • /etc/httpd/modsecurity.d/activated_rules/modsecurity_crs_20_protocol_violations.conf

    • id:958291 "Range: 0-", mp4 等のストリーミングや分割ダウンロードが行われるファイルのダウンロードで引っかかる。

リンク

mod_geoip

インストール

  • EPELリポジトリ
    yum install mod_geoip

設定

  • /etc/cron.monthly/updateGeoIP
    データベースの自動更新スクリプト
    wget http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz
    gunzip GeoIP.dat.gz
    mv -f GeoIP.dat /usr/share/GeoIP/GeoIP.dat
    /sbin/restorecon -v /usr/share/GeoIP/GeoIP.dat
  • GeoIP が表示されない場合は、SELinux のラベルを確認。
    # ls -Z /usr/share/GeoIP/
    Good) unconfined_u:object_r:usr_t:s0
    NG)   unconfined_u:object_r:admin_home_t:s0
    ラベルが正しくない場合は下記コマンドで修正する。
    # sealert -a /var/log/audit/audit.log
    # /sbin/restorecon -v /usr/share/GeoIP/GeoIP.dat
    ラベル修正後、httpd を再起動すること。
    # service httpd restart

リンク

Apacheでhttp-equiv属性値を反映させる

Apache に DoS 攻撃対策 mod_evasive

cgi-bin ディレクトリでファイル名を省略したときに index.cgi を実行する設定

  • httpd.conf に ScriptAliasMatch を追加する。

  • 修正前

    ScriptAlias /cgi-bin/ /var/www/cgi-bin/
  • 修正後

    ScriptAliasMatch ^/cgi-bin/(.*)\.cgi /var/www/cgi-bin/$1.cgi
    ScriptAliasMatch ^/cgi-bin/(.*)/? /var/www/cgi-bin/$1/index.cgi
    ScriptAliasMatch ^/cgi-bin$ /var/www/cgi-bin/index.cgi
    ScriptAlias /cgi-bin/ /var/www/cgi-bin/
  • index.cgi の例

    #!/usr/local/bin/perl
    
    use strict;
    use warnings;
    use utf8;
    use CGI::Pretty;
    
    my $q = new CGI;
    my $host = $q->url(-base => 1);
    print $q->redirect( $host . '/' );
    
    # EOF
  • Apacheで、http://hoge.hoge/cgi-bin/foo/にアクセスできるようにする方法

Apache で外部からの直リンクを禁止する

Basic認証にタイムアウトを設定する