☕️

networkaddress.cache.ttlを試す

2022/11/06に公開

networkaddress.cache.ttlとは

名前解決して得たアドレスをキャッシュする秒数を設定するためのセキュリティプロパティです。

デフォルト値は次の通りとなっています。

  • セキュリティマネージャーがインストールされている場合は-1(永久)
  • セキュリティマネージャーがインストールされていない場合は実装依存(Liberica JDKは30秒)

これを設定する方法は2つあって、1つは$JAVA_HOME/conf/security/java.securityへ記載する方法、もう1つはJavaアプリケーションの中でjava.security.Security#setPropertyを呼び出す方法です。

なお、最初の名前解決時に設定が読み込まれて固定されるのでjava.security.Security#setPropertyを使用する場合は最初の名前解決前に設定する必要があります。

今回は後者を試します。

動作確認する構成

Javaアプリケーション、PostgreSQL(2台)、DNSサーバーを用意してJavaアプリケーションからPostgreSQLを名前解決してアクセスします。

その際、名前に対応するPostgreSQLを切り替えて規定の秒数後にJavaアプリケーションが切り替え後のアドレスを正しく認識するのか、つまりnetworkaddress.cache.ttlが効いているのかを確認します。

環境全体はDockerを、DNSサーバーはConsulを、アプリケーションのフレームワークはSpring Bootを使用します。

アプリケーションの説明

動作確認用のアプリケーションは次のリポジトリに置いてあります。

メインクラスで環境変数NETWORKADDRESS_CACHE_TTLを読み取ってnullでなければそれをそのままセキュリティプロパティnetworkaddress.cache.ttlへ設定しています。

また、DBアクセスする際はHikariCPは使用せずDriverManagerで都度DB接続しています。

動作確認の準備

まずJavaアプリケーションが1台目のPostgreSQLへ繋がる状態(構成図の1つめの状態)を作ります。

最初にネットワークを作成します。

docker network create --subnet 172.16.0.0/24 demo-nw

次にConsulを起動します。

docker run -d --name dns --network demo-nw --ip 172.16.0.10 \
    -p 8500:8500 -p 8600:53/udp consul:1.13 \
    agent -dev -client=0.0.0.0 -dns-port=53

1台目のPostgreSQLを起動します。

docker run -d --name db1 --network demo-nw --ip 172.16.0.20 \
    -e POSTGRES_PASSWORD=password postgres:15

テーブルを作成してデータを投入します。

docker exec db1 psql -U postgres -c "create table demo ( \
    id integer primary key, name varchar(10)); \
    insert into demo (id, name) values (1, 'DB1')"

2台目のPostgreSQLも準備します。

docker run -d --name db2 --network demo-nw --ip 172.16.0.30 \
    -e POSTGRES_PASSWORD=password postgres:15

テーブルを作成してデータを投入します。
1台目とはdemoテーブルのnameカラムに設定する値が異なります。
これでどちらのPostgreSQLへ繋がっているのかを確認します。

docker exec db2 psql -U postgres -c "create table demo ( \
    id integer primary key, name varchar(10)); \
    insert into demo (id, name) values (1, 'DB2')"

1台目のPostgreSQLのアドレスをConsulへ登録します。

docker exec dns consul services register -address=172.16.0.20 -name=db
digで確認

登録されたアドレスをdigで確認します。

dig @127.0.0.1 -p 8600 db.service.dc1.consul

次のような内容が返されるはずです。

; <<>> DiG 9.10.6 <<>> @127.0.0.1 -p 8600 db.service.dc1.consul
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 10550
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;db.service.dc1.consul.         IN      A

;; ANSWER SECTION:
db.service.dc1.consul.  0       IN      A       172.16.0.20

;; Query time: 2 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: Sun Nov 06 07:35:51 JST 2022
;; MSG SIZE  rcvd: 66

アプリケーションをビルドします。

./mvnw spring-boot:build-image

アプリケーションを実行します。
networkaddress.cache.ttlは10秒に設定しました。

docker run -d --name app --network demo-nw --ip 172.16.0.40 \
    --dns 172.16.0.10 -p 8080:8080 \
    -e NETWORKADDRESS_CACHE_TTL=10 \
    -e JAVA_OPTS='-Duser.timezone=Asia/Tokyo' \
    networkaddress-cache-ttl-example:0.0.1-SNAPSHOT

curlでアプリケーションへアクセスして確認してみましょう。

curl -s localhost:8080

次のような内容が返されるはずです。

{"name":"DB1","dateTime":"2022-11-06T16:16:25"}

以上で準備が整いました。

動作確認

次のようにして動作確認します。

  • 1秒毎にcurlを行う
  • 任意のタイミングでDNSサーバー(Consul)へ登録しているPostgreSQLのアドレスを変更する
  • 10秒後にPostgreSQLから取得する値が変化する、つまり名前解決のキャッシュが切れて新しく名前解決していることを確認する

まず新しくコンソールを立ち上げて次のコマンドで1秒毎にcurlを行います。

while true; do curl -s -w '\n' localhost:8080; sleep 1; done

次に任意のタイミングでDNSサーバー(Consul)へ登録されているPostgreSQLのアドレスを1台目から2台目へ変更します。

docker exec dns consul services deregister -id=db
docker exec dns consul services register -address=172.16.0.30 -name=db

curlを行なっているコンソールを見て取得している値が"DB1"から"DB2"へ変化することを確認します。

{"name":"DB1","dateTime":"2022-11-06T16:18:00"}
{"name":"DB1","dateTime":"2022-11-06T16:18:02"}
{"name":"DB1","dateTime":"2022-11-06T16:18:03"}
{"name":"DB1","dateTime":"2022-11-06T16:18:04"}
{"name":"DB1","dateTime":"2022-11-06T16:18:05"}
{"name":"DB1","dateTime":"2022-11-06T16:18:06"}
{"name":"DB1","dateTime":"2022-11-06T16:18:07"}
{"name":"DB1","dateTime":"2022-11-06T16:18:08"}
{"name":"DB1","dateTime":"2022-11-06T16:18:09"}
{"name":"DB1","dateTime":"2022-11-06T16:18:10"}
{"name":"DB2","dateTime":"2022-11-06T16:18:11"}
{"name":"DB2","dateTime":"2022-11-06T16:18:12"}
{"name":"DB2","dateTime":"2022-11-06T16:18:13"}

また、任意のタイミングでDNSサーバー(Consul)へ登録されているPostgreSQLのアドレスを2台目から1台目へ戻して、curlで取得している値が"DB2"から"DB1"へ変化することも確認します。

docker exec dns consul services deregister -id=db
docker exec dns consul services register -address=172.16.0.20 -name=db
{"name":"DB2","dateTime":"2022-11-06T16:18:11"}
{"name":"DB2","dateTime":"2022-11-06T16:18:12"}
{"name":"DB2","dateTime":"2022-11-06T16:18:13"}
{"name":"DB2","dateTime":"2022-11-06T16:18:14"}
{"name":"DB2","dateTime":"2022-11-06T16:18:15"}
{"name":"DB2","dateTime":"2022-11-06T16:18:16"}
{"name":"DB2","dateTime":"2022-11-06T16:18:17"}
{"name":"DB2","dateTime":"2022-11-06T16:18:18"}
{"name":"DB2","dateTime":"2022-11-06T16:18:19"}
{"name":"DB2","dateTime":"2022-11-06T16:18:20"}
{"name":"DB1","dateTime":"2022-11-06T16:18:21"}
{"name":"DB1","dateTime":"2022-11-06T16:18:22"}
{"name":"DB1","dateTime":"2022-11-06T16:18:23"}

以上で動作確認ができました。

コンテナの後始末
docker stop app db1 db2 dns
docker rm app db1 db2 dns
docker network rm demo-nw
docker rmi networkaddress-cache-ttl-example:0.0.1-SNAPSHOT

Discussion