Come fare in modo che Java rispetti il ​​timeout della cache DNS?


102

Usiamo GSLB per la distribuzione geografica e il bilanciamento del carico. Ad ogni servizio viene assegnato un nome di dominio fisso. Attraverso un po 'di magia DNS, il nome di dominio viene risolto in un IP più vicino al server con il carico minimo. Affinché il bilanciamento del carico funzioni, il server delle applicazioni deve rispettare il TTL dalla risposta DNS e risolvere nuovamente il nome di dominio quando la cache scade. Tuttavia, non sono riuscito a trovare un modo per farlo in Java.

L'applicazione è in Java 5, in esecuzione su Linux (Centos 5).

Risposte:


77

Secondo la risposta di Byron, non è possibile impostare networkaddress.cache.ttlo networkaddress.cache.negative.ttlcome proprietà di sistema utilizzando il -Dflag o chiamando System.setPropertyperché queste non sono proprietà di sistema: sono proprietà di sicurezza .

Se desideri utilizzare una proprietà System per attivare questo comportamento (in modo da poter utilizzare il -Dflag o la chiamata System.setProperty), ti consigliamo di impostare la seguente proprietà System :

-Dsun.net.inetaddr.ttl=0

Questa proprietà di sistema abiliterà l'effetto desiderato.

Ma attenzione: se non usi il -Dflag quando avvii il processo JVM e scegli di chiamarlo invece dal codice:

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

Questo codice deve essere eseguito prima che qualsiasi altro codice nella JVM tenti di eseguire operazioni di rete.

Questo è importante perché, ad esempio, se si richiama Security.setPropertyun file .war e si distribuisce tale .war su Tomcat, ciò non funzionerebbe: Tomcat utilizza lo stack di rete Java per inizializzarsi molto prima dell'esecuzione del codice .war. A causa di questa "condizione di competizione", di solito è più conveniente utilizzare il -Dflag quando si avvia il processo JVM.

Se non usi -Dsun.net.inetaddr.ttl=0o chiami Security.setProperty, dovrai modificare $JRE_HOME/lib/security/java.securitye impostare quelle proprietà di sicurezza in quel file, ad es

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

Ma fai attenzione agli avvisi di sicurezza nei commenti che circondano tali proprietà. Fallo solo se sei ragionevolmente sicuro di non essere soggetto ad attacchi di spoofing DNS .


2
Il FQN è java.security.Security(almeno in jdk7)
Pablo Fernandez

1
Solo un commento, questi avvisi di sicurezza riguardano principalmente i gestori della sicurezza e il caricamento remoto. Per qualsiasi normale applicazione server che si fida del DNS in una certa misura, la riduzione del TTL va bene. (Tuttavia non penso che 0 sia un buon minimo e il valore predefinito di 30 s per i gestori non di sicurezza va bene nella maggior parte dei casi).
eckes

3
La proprietà di sistema funziona anche con OpenJDK o è specifica per Oracle?
mhlz

Non controllare mai più il DNS dopo la prima ricerca non ti protegge dagli attacchi di spoofing, ma rende un attacco di spoofing permanente invece che temporaneo.
kbolino

67

Java ha un comportamento di caching DNS davvero strano. La soluzione migliore è disattivare la cache DNS o impostarla su un numero basso come 5 secondi.

networkaddress.cache.ttl (default: -1)
Indica la politica di memorizzazione nella cache per ricerche di nomi riuscite dal servizio di nomi. Il valore viene specificato come numero intero per indicare il numero di secondi per memorizzare nella cache la ricerca riuscita. Un valore di -1 indica "cache per sempre".

networkaddress.cache.negative.ttl (predefinito: 10)
Indica la politica di memorizzazione nella cache per ricerche di nomi non riuscite dal servizio di nomi. Il valore viene specificato come numero intero per indicare il numero di secondi per memorizzare nella cache l'errore per le ricerche non riuscite. Un valore di 0 indica "non memorizzare mai nella cache". Un valore di -1 indica "cache per sempre".


7
Nota: questo non disabilita tutta la cache DNS nel tuo sistema operativo. Disabilita semplicemente la cache in memoria danneggiata di Java nella libreria. È possibile impostare semplicemente queste proprietà sulla riga di comando quando si richiama la JVM.
Nelson

2
Non so che "rotto" sia valido. Java (per motivi di sicurezza) memorizza nella cache le voci DNS per sempre o fino al riavvio della JVM, a seconda dell'evento che si verifica per primo. Questo (da quello che posso dire) era di progettazione. Le impostazioni possono essere effettuate nel file dei criteri java.security o dalla riga di comando. Le impostazioni sono diverse per ciascuno. Riferimento: rgagnon.com/javadetails/java-0445.html
Milner

4
Notare che non è possibile impostarli come proprietà di sistema (cioè utilizzando i flag -D o System.setProperty) perché non sono proprietà di sistema, sono proprietà di sicurezza.
Les Hazlewood

6
Questa documentazione è leggermente diversa nella 1.7. In particolare, la cache per sempre ora si verifica solo quando è presente un gestore della sicurezza: "Il comportamento predefinito è memorizzare nella cache per sempre quando è installato un gestore della sicurezza e memorizzare nella cache per un periodo di tempo specifico di implementazione, quando un gestore della sicurezza non è installato." docs.oracle.com/javase/7/docs/technotes/guides/net/…
Brett Okken

1
@Michael vedi System.getSecurityManager(). Documenti per Java 8: docs.oracle.com/javase/8/docs/api/java/lang/…
gesellix

22

Questo è stato ovviamente risolto nelle versioni più recenti (SE 6 e 7). Ho riscontrato un tempo massimo di memorizzazione nella cache di 30 secondi durante l'esecuzione del seguente frammento di codice mentre guardo l'attività della porta 53 utilizzando 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();
        }
    }
}

16
Sì, Java 1.5 aveva un valore predefinito di cache infinita. Java 1.6 e 1.7 hanno un valore predefinito di 30 secondi.
Michael

7
La documentazione per 1.7 indica che questo può essere vero solo nel caso in cui non sia presente un gestore della sicurezza: "Il comportamento predefinito è di memorizzare nella cache per sempre quando è installato un gestore di sicurezza e di memorizzare nella cache per un periodo di tempo specifico di implementazione, quando un manager non è installato. " docs.oracle.com/javase/7/docs/technotes/guides/net/…
Brett Okken

1
@Michael ti interessa condividere la fonte di queste informazioni?
rustyx

4
@rustyx Oracle 1.6 e 1.7 JDK ha questo in jre / lib / security / java.security per networkaddress.cache.ttl: "# il valore predefinito è per sempre (PER SEMPRE). Per motivi di sicurezza, questo # caching viene effettuato per sempre quando un gestore della sicurezza è impostato. Quando un gestore della sicurezza # non è impostato, il comportamento predefinito è memorizzare nella cache per 30 secondi. " Quindi le applet e le app distribuite tramite Java Web Start rimangono nella cache per sempre, altrimenti è di 30 secondi.
Michael

1
Ecco un puntatore di codice a java.security di OpenJDK 8, che dice che senza un security manager il TTL è di 30 secondi : hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/f940e7a48b72/src/share/… . Ho provato questo su Mac OS X e Ubuntu 14.04.
tro

18

Per espandere la risposta di Byron, credo che sia necessario modificare il file java.securitynella %JRE_HOME%\lib\securitydirectory per effettuare questa modifica.

Ecco la sezione pertinente:

#
# 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 

Documentazione sul java.securityfile qui .


5
Per aggiungere a questo, quando si utilizza tomcat6 ho dovuto modificare il mio file lib / security, poiché l'impostazione di networkaddress.cache.ttl o sun.net.inetaddr.ttl a livello di programmazione o tramite la variabile JAVA_OPTS non funzionava.
Bramp

1
@bramp Grazie fratello, anch'io sto affrontando lo stesso problema e l'ho risolto utilizzando il tuo commento e le risposte +1 per commento e risposta.
Bhavik Ambani

7

Per riassumere le altre risposte, <jre-path>/lib/security/java.securitypuoi impostare il valore della proprietà networkaddress.cache.ttlper regolare il modo in cui le ricerche DNS vengono memorizzate nella cache. Notare che questa non è una proprietà di sistema ma una proprietà di sicurezza. Sono stato in grado di impostarlo utilizzando:

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

Questo può anche essere impostato dalla proprietà di sistema -Dsun.net.inetaddr.ttl sebbene questo non sovrascriverà una proprietà di sicurezza se è impostato altrove.

Vorrei anche aggiungere che se stai riscontrando questo problema con i servizi web in WebSphere, come me, l'impostazione networkaddress.cache.ttlnon sarà sufficiente. È necessario impostare la proprietà di sistema disableWSAddressCachingsu true. A differenza della proprietà time-to-live, questa può essere impostata come argomento JVM o tramite System.setProperty).

IBM ha un post piuttosto dettagliato su come WebSphere gestisce la cache DNS qui . La parte rilevante di quanto sopra è:

Per disabilitare la memorizzazione nella cache degli indirizzi per i servizi Web, è necessario impostare una proprietà personalizzata JVM aggiuntiva disableWSAddressCaching su true. Utilizzare questa proprietà per disabilitare la memorizzazione nella cache degli indirizzi per i servizi Web. Se il sistema in genere viene eseguito con molti thread client e si verificano conflitti di blocco nella cache wsAddrCache, è possibile impostare questa proprietà personalizzata su true, per impedire la memorizzazione nella cache dei dati dei servizi Web.


2

Secondo le proprietà ufficiali di Oracle Java , sun.net.inetaddr.ttlè una proprietà specifica per l'implementazione di Sun, che "potrebbe non essere supportata nelle versioni future". "il modo preferito è usare la proprietà di sicurezza" networkaddress.cache.ttl.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.