Как заставить Java соблюдать тайм-аут кеширования DNS?

102

Мы используем GSLB для географического распределения и балансировки нагрузки. Каждой услуге присваивается фиксированное доменное имя. С помощью некоторой магии DNS имя домена преобразуется в IP-адрес, ближайший к серверу с наименьшей нагрузкой. Чтобы балансировка нагрузки работала, сервер приложений должен соблюдать TTL из ответа DNS и снова разрешать доменное имя при истечении времени ожидания кеша. Однако я не мог придумать, как это сделать на Java.

Приложение написано на Java 5, работает в Linux (Centos 5).

ZZ Coder
источник

Ответы:

77

Согласно ответу Байрона, вы не можете установить networkaddress.cache.ttlили в networkaddress.cache.negative.ttlкачестве свойств системы с помощью -Dфлага или вызова, System.setPropertyпотому что это не свойства системы - это свойства безопасности .

Если вы хотите использовать свойство System для запуска этого поведения (чтобы вы могли использовать -Dфлаг или вызов System.setProperty), вам нужно будет установить следующее свойство System :

-Dsun.net.inetaddr.ttl=0

Это системное свойство позволит добиться желаемого эффекта.

Но имейте в виду: если вы не используете -Dфлаг при запуске процесса JVM и выбираете вызов этого из кода вместо этого:

java.security.Security.setProperty("networkaddress.cache.ttl" , "0")

Этот код должен выполняться до того, как любой другой код в JVM попытается выполнить сетевые операции.

Это важно, потому что, например, если вы вызвали Security.setPropertyфайл .war и развернули этот .war в Tomcat, это не сработает: Tomcat использует сетевой стек Java для инициализации намного раньше, чем выполняется ваш код .war. Из-за этого «состояния гонки» обычно удобнее использовать -Dфлаг при запуске процесса JVM.

Если вы не используете -Dsun.net.inetaddr.ttl=0или не вызываете Security.setProperty, вам нужно будет отредактировать $JRE_HOME/lib/security/java.securityи установить эти свойства безопасности в этом файле, например

networkaddress.cache.ttl = 0
networkaddress.cache.negative.ttl = 0

Но обратите внимание на предупреждения о безопасности в комментариях к этим свойствам. Делайте это только в том случае, если вы достаточно уверены, что не подвержены атакам с подменой DNS .

Les Hazlewood
источник
2
FQN java.security.Security(по крайней мере, в jdk7)
Пабло Фернандес
1
Просто комментарий, эти предупреждения безопасности в основном относятся к диспетчерам безопасности и удаленной загрузке. Для любого обычного серверного приложения, которое в некоторой степени доверяет DNS, уменьшение TTL вполне нормально. (Однако я не думаю, что 0 - это хороший минимум, а значение по умолчанию 30 секунд для менеджеров, не связанных с безопасностью, в большинстве случаев вполне подходит).
eckes
3
Свойство системы также работает с OpenJDK или оно специфично для Oracle?
mhlz
Никогда больше не проверять DNS после первого поиска не защищает вас от атак с подделкой, это делает атаку спуфинга постоянной, а не временной.
kbolino
67

У Java есть очень странное поведение кеширования DNS. Лучше всего отключить кеширование DNS или установить какое-то небольшое число, например, 5 секунд.

networkaddress.cache.ttl (по умолчанию: -1)
Указывает политику кэширования для успешного поиска имени из службы имен. Значение указывается как целое число, чтобы указать количество секунд для кэширования успешного поиска. Значение -1 указывает «кешировать навсегда».

networkaddress.cache.negative.ttl (по умолчанию: 10)
Указывает политику кэширования для неуспешных поисков имен из службы имен. Значение указывается как целое число, чтобы указать количество секунд для кэширования ошибки при неудачных поисках. Значение 0 означает «никогда не кэшировать». Значение -1 указывает «кешировать навсегда».

Байрон Уитлок
источник
7
Примечание: это не отключает все кеширование DNS в вашей ОС. Просто отключает собственное нарушенное кеширование в памяти Java в библиотеке. Вы можете просто установить эти свойства в командной строке при вызове JVM.
Нельсон
2
Я не знаю, что "сломанный" действительно. Java (из соображений безопасности) кэширует записи DNS навсегда или до перезапуска JVM, в зависимости от того, что произойдет раньше. Это (насколько я могу судить) было задумано. Настройки можно выполнить в файле политики java.security или в командной строке. Настройки у каждого разные. Ссылка: rgagnon.com/javadetails/java-0445.html
Милнер,
4
Обратите внимание, что вы не можете установить их как системные свойства (например, используя флаги -D или System.setProperty), потому что они не системные свойства - это свойства безопасности.
Les Hazlewood
6
Эта документация немного отличается в версии 1.7. В частности, кэширование навсегда теперь происходит только тогда, когда присутствует диспетчер безопасности: «По умолчанию выполняется постоянное кэширование, когда диспетчер безопасности установлен, и кэширование на определенный период времени, когда диспетчер безопасности не установлен». docs.oracle.com/javase/7/docs/technotes/guides/net/…
Бретт Оккен
1
@ Майкл System.getSecurityManager(). Документы для Java 8: docs.oracle.com/javase/8/docs/api/java/lang/…
gesellix
22

Очевидно, это было исправлено в новых версиях (SE 6 и 7). Максимальное время кэширования составляет 30 секунд при запуске следующего фрагмента кода при просмотре активности порта 53 с помощью tcpdump.

/**
 * http://stackoverflow.com/questions/1256556/any-way-to-make-java-honor-the-dns-caching-timeout-ttl
 *
 * Result: Java 6 distributed with Ubuntu 12.04 and Java 7 u15 downloaded from Oracle have
 * an expiry time for dns lookups of approx. 30 seconds.
 */

import java.util.*;
import java.text.*;
import java.security.*;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;

public class Test {
    final static String hostname = "www.google.com";
    public static void main(String[] args) {
        // only required for Java SE 5 and lower:
        //Security.setProperty("networkaddress.cache.ttl", "30");

        System.out.println(Security.getProperty("networkaddress.cache.ttl"));
        System.out.println(System.getProperty("networkaddress.cache.ttl"));
        System.out.println(Security.getProperty("networkaddress.cache.negative.ttl"));
        System.out.println(System.getProperty("networkaddress.cache.negative.ttl"));

        while(true) {
            int i = 0;
            try {
                makeRequest();
                InetAddress inetAddress = InetAddress.getLocalHost();
                System.out.println(new Date());
                inetAddress = InetAddress.getByName(hostname);
                displayStuff(hostname, inetAddress);
            } catch (UnknownHostException e) {
                e.printStackTrace();
            }
            try {
                Thread.sleep(5L*1000L);
            } catch(Exception ex) {}
            i++;
        }
    }

    public static void displayStuff(String whichHost, InetAddress inetAddress) {
        System.out.println("Which Host:" + whichHost);
        System.out.println("Canonical Host Name:" + inetAddress.getCanonicalHostName());
        System.out.println("Host Name:" + inetAddress.getHostName());
        System.out.println("Host Address:" + inetAddress.getHostAddress());
    }

    public static void makeRequest() {
        try {
            URL url = new URL("http://"+hostname+"/");
            URLConnection conn = url.openConnection();
            conn.connect();
            InputStream is = conn.getInputStream();
            InputStreamReader ird = new InputStreamReader(is);
            BufferedReader rd = new BufferedReader(ird);
            String res;
            while((res = rd.readLine()) != null) {
                System.out.println(res);
                break;
            }
            rd.close();
        } catch(Exception ex) {
            ex.printStackTrace();
        }
    }
}
оборота пользователь 1050755
источник
16
Да, в Java 1.5 по умолчанию установлено бесконечное кэширование. Java 1.6 и 1.7 имеют значение по умолчанию 30 секунд.
Майкл
7
В документации для версии 1.7 указано, что это может быть правдой только в том случае, если диспетчер безопасности отсутствует: «Поведение по умолчанию - кэшировать навсегда, когда установлен диспетчер безопасности, и кэшировать на определенный период времени, когда безопасность менеджер не установлен. " docs.oracle.com/javase/7/docs/technotes/guides/net/…
Бретт Оккен
1
@Michael не желает делиться источником этой информации?
rustyx
4
@rustyx Oracle 1.6 и 1.7 JDK имеет это в jre / lib / security / java.security для networkaddress.cache.ttl: "# значение по умолчанию вечно (НАВСЕГДА). По соображениям безопасности это # ​​кеширование выполняется навсегда, когда менеджер безопасности установлен. Если диспетчер безопасности # не установлен, по умолчанию выполняется кэширование в течение 30 секунд ". Таким образом, апплеты и приложения, развернутые через Java Web Start, по-прежнему кэшируются навсегда, иначе это 30 секунд.
Майкл
1
Вот указатель кода на java.security OpenJDK 8, в котором говорится, что без менеджера безопасности TTL составляет 30 секунд : hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/f940e7a48b72/src/share/… . Я тестировал это на Mac OS X и Ubuntu 14.04.
Tro
18

Чтобы расширить ответ Байрона, я считаю, что вам нужно отредактировать файл java.securityв %JRE_HOME%\lib\securityкаталоге, чтобы выполнить это изменение.

Вот соответствующий раздел:

#
# The Java-level namelookup cache policy for successful lookups:
#
# any negative value: caching forever
# any positive value: the number of seconds to cache an address for
# zero: do not cache
#
# default value is forever (FOREVER). For security reasons, this
# caching is made forever when a security manager is set. When a security
# manager is not set, the default behavior is to cache for 30 seconds.
#
# NOTE: setting this to anything other than the default value can have
#       serious security implications. Do not set it unless 
#       you are sure you are not exposed to DNS spoofing attack.
#
#networkaddress.cache.ttl=-1 

Документация по java.securityфайлу здесь .

Мэтт Би
источник
5
Чтобы добавить к этому, при использовании tomcat6 мне пришлось изменить свой файл lib / security, поскольку установка networkaddress.cache.ttl или sun.net.inetaddr.ttl программно или через переменную JAVA_OPTS не работала.
bramp
1
@bramp Спасибо, братан, я тоже столкнулся с той же проблемой и решил ее, используя ваш комментарий и ответы +1 за комментарий и ответ.
Бхавик Амбани,
7

Подводя итог другим ответам, <jre-path>/lib/security/java.securityвы можете установить значение свойства, networkaddress.cache.ttlчтобы настроить кэширование запросов DNS. Обратите внимание, что это не системное свойство, а свойство безопасности. Я смог установить это, используя:

java.security.Security.setProperty("networkaddress.cache.ttl", "<value>");

Это также может быть установлено системным свойством, -Dsun.net.inetaddr.ttlхотя это не переопределит свойство безопасности, если оно установлено в другом месте.

Я также хотел бы добавить, что если вы видите эту проблему с веб-сервисами в WebSphere, как и я, настройки networkaddress.cache.ttlбудет недостаточно. Вам необходимо установить системное свойство disableWSAddressCachingна true. В отличие от свойства time-to-live, это может быть установлено как аргумент JVM или через System.setProperty).

IBM имеет довольно подробный пост о том , как WebSphere ручки кэширования DNS здесь . Соответствующая часть вышесказанного:

Чтобы отключить кэширование адресов для веб-служб, необходимо установить дополнительное настраиваемое свойство JVM disableWSAddressCaching в значение true. Используйте это свойство, чтобы отключить кеширование адресов для веб-служб. Если ваша система обычно работает с большим количеством клиентских потоков и вы сталкиваетесь с конфликтом блокировки в кэше wsAddrCache, вы можете установить для этого настраиваемого свойства значение true, чтобы предотвратить кэширование данных веб-служб.

квадрат
источник
2

Согласно официальным свойствам Java oracle , sun.net.inetaddr.ttlэто свойство Sun , зависящее от реализации, которое «может не поддерживаться в будущих выпусках». «предпочтительный способ - использовать свойство безопасности» networkaddress.cache.ttl.

CloudStax
источник