質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

DNS

DNSとは、Domain Name Systemのことで、インターネットなどのTCP/IPネットワーク上でドメイン名やホスト名と、IPアドレスとの対応づけを管理するシステムです。DNSのデータベースは、IPアドレスの4つの数字を通知するDNSサーバーで構築されており、IPアドレスをドメイン名から引き出す機能やドメイン名に関するメールサーバ情報を取り扱っています。

HTTP

HTTP(Hypertext Transfer Protocol)とはweb上でHTML等のコンテンツを交換するために使われるアプリケーション層の通信プロトコルです。

Apache

Apacheは、Apache HTTP Serverの略で、最も人気の高いWebサーバソフトウェアの一つです。安定性が高いオープンソースソフトウェアとして商用サイトから自宅サーバまで、多くのプラットフォーム向けに開発・配布されています。サーバーソフトウェアの不具合(NCSA httpd)を修正するパッチ(a patch)を集積、一つ独立したソフトウェアとして開発されました。

proxy

proxy(プロキシー)は、企業などの内部コンピュータとインターネットの中間に位置し、例えば直接インターネットに接続できない内部コンピュータの代理としてインターネットに接続する等をするシステム、もしくは代理として機能を実行するソフトウェアです。内部ネットワークへのアクセスを一元管理し、内部からの特定の種類の接続以外を遮断すること、外部からの不正アクセスを拒否することなどに用いられます。

Q&A

解決済

1回答

7238閲覧

apacheのエラーログに"DNS lookup failure" が出力される

Liastea

総合スコア16

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

DNS

DNSとは、Domain Name Systemのことで、インターネットなどのTCP/IPネットワーク上でドメイン名やホスト名と、IPアドレスとの対応づけを管理するシステムです。DNSのデータベースは、IPアドレスの4つの数字を通知するDNSサーバーで構築されており、IPアドレスをドメイン名から引き出す機能やドメイン名に関するメールサーバ情報を取り扱っています。

HTTP

HTTP(Hypertext Transfer Protocol)とはweb上でHTML等のコンテンツを交換するために使われるアプリケーション層の通信プロトコルです。

Apache

Apacheは、Apache HTTP Serverの略で、最も人気の高いWebサーバソフトウェアの一つです。安定性が高いオープンソースソフトウェアとして商用サイトから自宅サーバまで、多くのプラットフォーム向けに開発・配布されています。サーバーソフトウェアの不具合(NCSA httpd)を修正するパッチ(a patch)を集積、一つ独立したソフトウェアとして開発されました。

proxy

proxy(プロキシー)は、企業などの内部コンピュータとインターネットの中間に位置し、例えば直接インターネットに接続できない内部コンピュータの代理としてインターネットに接続する等をするシステム、もしくは代理として機能を実行するソフトウェアです。内部ネットワークへのアクセスを一元管理し、内部からの特定の種類の接続以外を遮断すること、外部からの不正アクセスを拒否することなどに用いられます。

1グッド

1クリップ

投稿2018/10/19 13:23

リバースプロキシとして使用しているapacheに出力されるDNS lookupエラーについて質問です。

前提

  • SSLのためにリバースプロキシを使用

  • リバースプロキシサーバーは CentOS 7.1 + apache(2.4.33)で構成

  • 1日あたり約70万/日のアクセスがある

  • 構成は下記のようなイメージです。

クライアント <---> インターネット <---> ファイアウォール <---> リバースプロキシ(apache)[今回の質問対象] <---> ロードバランサー <---> コンテンツサーバー(apache)

エラー発生時の状況

  • HTTP/2.0に対応させるため、apacheの設定を下記のとおり変更し、restartした。(apache event + http/2)
主なapache設定変更点 LoadModule mpm_event_module modules/mod_mpm_event.so ←追加した #LoadModule mpm_prefork_module modules/mod_mpm_prefork.so ←コメントアウトした ~~~~ Protocols h2 http/1.1 ←追加した #Protocols http/1.1 ←コメントアウトした ~~~~ 下記設定を追加 <IfModule mpm_event_module> MaxRequestWorkers 400 ServerLimit 8 ThreadLimit 50 ThreadsPerChild 50 StartServers 8 MinSpareThreads 100 MaxSpareThreads 400 MaxConnectionsPerChild 0 MaxMemFree 2048 </IfModule>
  • 設定変更後、1万~10万アクセスに一度、下記のようなapacheエラーログが出力されるようになった。
[Tue Oct 16 12:43:25.460055 2018] [proxy:error] [pid 12345:tid 140458525089774] "example.jp" [client 192.168.X.X:45678] AH00898: DNS lookup failure for: www.hogehoge.example.jp returned by /example/hogehoge/
  • その時、アクセスログでは502エラーが返っている
  • 99.99%以上のアクセスは問題なし
  • 現在は切り戻しており、再現の条件ははっきりと分かっていない。(大量のアクセスが条件?)

確認したこと

  • CPU、メモリ、I/O → 負荷はみられない
  • /var/log/messages、dmesg等のシステムログ → 特に変わったところはない
  • apcheのソース(後述)

質問内容

  • "DNS lookup failure" が出力される原因は何なのでしょうか
  • 本当に名前解決できていないのでしょうか(apacheが誤解している?)

これまで apache prefork + http/1.1 で運用しており、"DNS lookup failure" が出力されたことはありません。
ネットで色々調べたものの、類似の事象が見つけられず困り果ております。


追加情報

確認したapacheのソース(コメント一部割愛)

apacheエラーログのエラーコード(AH00898)より下記ソースの動作であることは確認しました。

err変数が途中で書き換わるとDNS lookup failureが出力される様子

proxy_util.c 2189行目 int server_port; apr_status_t err = APR_SUCCESS; apr_status_t uerr = APR_SUCCESS; const char *uds_path;
proxy_util.c 2356行目 if (err != APR_SUCCESS) { return ap_proxyerror(r, HTTP_BAD_GATEWAY, apr_pstrcat(p, "DNS lookup failure for: ", conn->hostname, NULL)); }

現状の設定だとerr変数が書き換わるのはどうもここっぽい

proxy_util.c 2306行目 if (!will_reuse) { err = apr_sockaddr_info_get(&(conn->addr), conn->hostname, APR_UNSPEC, conn->port, 0, conn->pool); }

apr_sockaddr_info_get関数のソース
httpd-2.4.XX/srclib/apr/network_io/unix/sockaddr.c にあった

sockaddr.c 605行目 APR_DECLARE(apr_status_t) apr_sockaddr_info_get(apr_sockaddr_t **sa, const char *hostname, apr_int32_t family, apr_port_t port, apr_int32_t flags, apr_pool_t *p) { apr_int32_t masked; *sa = NULL; if ((masked = flags & (APR_IPV4_ADDR_OK | APR_IPV6_ADDR_OK))) { if (!hostname || family != APR_UNSPEC || masked == (APR_IPV4_ADDR_OK | APR_IPV6_ADDR_OK)) { return APR_EINVAL; } if (flags & APR_IPV6_ADDR_OK) { return APR_ENOTIMPL; } } if (family == APR_UNSPEC) { family = APR_INET; } return find_addresses(sa, hostname, family, port, flags, p); }

returnのfind_addressesのソースを読むとcall_resolverが呼び出されているので
call_resolverのソースを見てみる

sockaddr.c 307行目 static apr_status_t call_resolver(apr_sockaddr_t **sa, const char *hostname, apr_int32_t family, apr_port_t port, apr_int32_t flags, apr_pool_t *p) { struct addrinfo hints, *ai, *ai_list; apr_sockaddr_t *prev_sa; int error; char *servname = NULL; memset(&hints, 0, sizeof(hints)); hints.ai_family = family; hints.ai_socktype = SOCK_STREAM; if (family == APR_UNSPEC) { hints.ai_flags = AI_ADDRCONFIG; } ~~~略~~~ error = getaddrinfo(hostname, servname, &hints, &ai_list); ~~~略~~~

getaddrinfoは、アドレス情報を決定するためのヒントを与えることでsockaddr型構造体を得ることができる関数らしい。
ヒントとなる情報を格納した不完全なaddrinfo型構造体を与えると、必要なメンバが全てそろったaddrinfo型構造体を返すとのこと。
第1~3引数で情報を与え、第4引数に結果(メモリアドレス)が返る。ここでは&ai_listに結果が返る

sockaddr.c 403行目 prev_sa = NULL; ai = ai_list; while (ai) { /* while more addresses to report */ apr_sockaddr_t *new_sa; if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) { if (ai->ai_family != AF_INET) { ai = ai->ai_next; continue; } new_sa = apr_pcalloc(p, sizeof(apr_sockaddr_t)); new_sa->pool = p; memcpy(&new_sa->sa, ai->ai_addr, ai->ai_addrlen); apr_sockaddr_vars_set(new_sa, ai->ai_family, port); if (!prev_sa) { /* first element in new list */ if (hostname) { new_sa->hostname = apr_pstrdup(p, hostname); } *sa = new_sa; } else { new_sa->hostname = prev_sa->hostname; prev_sa->next = new_sa; } prev_sa = new_sa; ai = ai->ai_next; } freeaddrinfo(ai_list); if (prev_sa == NULL) { return APR_EGENERAL; } return APR_SUCCESS; }

上記でやってること?
getaddrinfoから得た情報(ai)のメモリ情報をコピーして新しいソケットアドレス(new_sa)を作成
最初に見つけたソケットアドレスをsaをセット(saは後にコネクション確立する用?)
最終的にprev_sa(new_sa)に値が入っているとAPR_SUCCESSが返る

要するに新しいソケットアドレスが作成できなかったときに"DNS lookup failure"が発生する?(自信なし)

リバースプロキシのhttpd.conf (一部省略しています)

# Server Base ServerRoot "/usr/local/apache2" PidFile "/var/run/httpd.pid" DefaultRuntimeDir "/var/run/apache2" Listen 80 Listen 443 UseCanonicalName Off HostnameLookups On ServerTokens Prod ServerSignature Off Timeout 120 KeepAlive On MaxKeepAliveRequests 50 KeepAliveTimeout 2 TraceEnable Off FileETag MTime Size ServerAdmin root@localhost ServerName localhost User www Group www DocumentRoot "/var/www" # Load Module LoadModule authz_host_module modules/mod_authz_host.so LoadModule authz_core_module modules/mod_authz_core.so LoadModule socache_shmcb_module modules/mod_socache_shmcb.so LoadModule socache_dbm_module modules/mod_socache_dbm.so LoadModule reqtimeout_module modules/mod_reqtimeout.so LoadModule http2_module modules/mod_http2.so LoadModule log_config_module modules/mod_log_config.so LoadModule env_module modules/mod_env.so LoadModule proxy_module modules/mod_proxy.so LoadModule proxy_http_module modules/mod_proxy_http.so LoadModule ssl_module modules/mod_ssl.so LoadModule mpm_event_module modules/mod_mpm_event.so #LoadModule mpm_prefork_module modules/mod_mpm_prefork.so LoadModule unixd_module modules/mod_unixd.so LoadModule dir_module modules/mod_dir.so ## prefork MPM <IfModule mpm_prefork_module> ServerLimit 500 StartServers 50 MinSpareServers 50 MaxSpareServers 50 MaxClients 400 MaxRequestsPerChild 1000 </IfModule> # Event MPM <IfModule mpm_event_module> MaxRequestWorkers 400 ServerLimit 8 ThreadLimit 50 ThreadsPerChild 50 StartServers 8 MinSpareThreads 100 MaxSpareThreads 400 MaxConnectionsPerChild 0 MaxMemFree 2048 </IfModule> # Proxy ProxyRequests Off ProxyVia Off ProxyPreserveHost On # HTTP2 <IfModule http2_module> ProtocolsHonorOrder On Protocols h2 http/1.1 #Protocols http/1.1 LogLevel http2:info H2MaxWorkerIdleSeconds 120 H2ModernTLSOnly On H2Push On H2MaxSessionStreams 30 H2SessionExtraFiles 30 H2TLSWarmUpSize 65535 </IfModule> # SSL SSLRandomSeed startup builtin SSLRandomSeed connect builtin SSLProtocol -All +TLSv1.2 +TLSv1.1 +TLSv1 SSLCipherSuite (割愛) SSLHonorCipherOrder On SSLCompression Off SSLSessionCache dbm:/hoge/hoge Mutex file:/var/tmp SSLPassPhraseDialog exec:/hoge/hoge SSLSessionCacheTimeout 300 SSLSessionTickets Off SSLUseStapling On SSLStaplingResponderTimeout 5 SSLStaplingReturnResponderErrors Off SSLStaplingCache shmcb:/var/run/apache2/ocsp(128000) # configure <VirtualHost 192.168.1.10> SSLEngine On ServerName sslproxy.example.jp SSLCertificateKeyFile /hoge/hoge.key SSLCertificateFile /hoge/hoge.crt </VirtualHost> <VirtualHost 192.168.1.11> SSLEngine On ServerName hoge.example.jp SSLCertificateKeyFile /hoge/hoge.key SSLCertificateFile /hoge/hoge.crt <IfDefine !AP22> ProxyPass / http://www.hogehoge.example.jp/ </IfDefine> <IfDefine AP22> ProxyPass / http://www.hogehoge.example.jp/ disablereuse=On </IfDefine> </VirtualHost>
set0gut1👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

TaichiYanagiya

2018/10/21 14:50

/var/log/messages には何かエラーは出ていませんでしょうか。ファイルディスクリプターが足りないとか。
Liastea

2018/10/22 02:24

コメントありがとうございます。 該当時間の/var/log/messagesには何も出力されていませんでした。 ulimitやnf_conntrackの上限に達しているわけではなさそうです。
guest

回答1

0

自己解決

答えに辿り着くまでに相当時間がかかりましたが、自己解決しました。
結論から言うとglibcのバグでした。

検証中、テストプログラムをコンパイルするためにgccをyumでインストールした後、
不具合が発生しなくなったのをきっかけに気づくことができました。

問題のバージョンは glibc-2.17-78 で、
glibc-2.17-105 以上にアップデートすることによって修正できることを確認しました。

対象のバグは、おそらくですが下記と予想しています。

https://access.redhat.com/security/cve/cve-2013-7423

It was discovered that, under certain circumstances, glibc's getaddrinfo() function would send DNS queries to random file descriptors.

https://jvndb.jvn.jp/ja/contents/2013/JVNDB-2013-006731.html

第三者により、大量のリクエストを介して、getaddrinfo関数の呼び出しを誘発されることで、DNS クエリを意図しない場所に送信される可能性があります。

上記不具合は glibc-2.17-90 で修正されています。
http://rpm.pbone.net/index.php3/stat/22/idpl/33992424/com/changelog.html

apacheのsockaddr.cでも使用されていたgetaddrinfo関連の修正なのでほぼ間違いないかなと思っています。

以上、この記録が誰かの役に立つことを祈っています。

投稿2018/10/23 10:29

Liastea

総合スコア16

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問