Siyao Meng created HADOOP-20040:
-----------------------------------

             Summary: SecurityUtil host resolver cache shares one mutable 
InetAddress per IP literal, leaking reverse lookups to other callers
                 Key: HADOOP-20040
                 URL: https://issues.apache.org/jira/browse/HADOOP-20040
             Project: Hadoop Common
          Issue Type: Bug
          Components: security, net
    Affects Versions: 3.5.0
            Reporter: Siyao Meng
            Assignee: Siyao Meng


h3. Problem

HADOOP-19447 added a cache to {{SecurityUtil.CacheableHostResolver}}, enabled 
when {{hadoop.security.hostname.cache.expire-interval.seconds}} > 0. The cache 
returns the same {{InetAddress}} instance to every caller resolving the same 
host string, and {{NetUtils.createSocketAddrForHost}} wraps that instance 
directly in a new {{InetSocketAddress}}.

{{InetAddress}} is not immutable. For an IP literal resolved by 
{{StandardHostResolver}} (the default, since 
{{hadoop.security.token.service.use_ip}} defaults to true), the hostname is 
unset, and the first {{getHostName()}} call does a reverse lookup and stores 
the PTR name in the object. With the cache on, that one call changes the 
address seen by every later caller in the JVM:

{code:java}
InetSocketAddress a = NetUtils.createSocketAddr("10.0.0.5:8020");
InetSocketAddress b = NetUtils.createSocketAddr("10.0.0.5:9870");
b.getHostString();   // "10.0.0.5"
a.getHostName();     // reverse lookup, e.g. "nn1.example.com"
b.getHostString();   // now "nn1.example.com"
NetUtils.createSocketAddr("10.0.0.5:50070").getHostString(); // 
"nn1.example.com"
{code}

Without the cache, each resolution returns a fresh object and 
{{getHostString()}} keeps returning the literal. So enabling the cache makes 
the host string of a configured IP address depend on unrelated earlier calls. 
The cache key is the host only, so addresses on different ports share the 
object too.

Callers that trigger the reverse lookup include {{NetUtils.canonicalizeHost}} 
(via {{FileSystem}} URI canonicalization of {{hdfs://<ip>:<port>}}), 
{{NetUtils.getHostPortString}} and {{NameNode#loginAsNameNodeUser}}. Callers 
that read the host string include 
{{DatanodeHttpServer#getHostnameForSpnegoPrincipal}} (SPNEGO {{_HOST}} 
substitution), {{RBFMetrics}} router web URI, and 
{{InetSocketAddress#toString}} in logs. HDFS-18004 makes the NameNode RPC 
server bind to {{getHostString()}} so an IP literal is bound as given; with the 
cache on it silently falls back to binding the reverse lookup name.

{{QualifiedHostResolver}} ({{use_ip=false}}) is not affected for literals, 
since it builds the address with {{InetAddress.getByAddress(host, bytes)}} and 
the hostname is already set.

h3. Proposed fix

Bypass the cache for IP literals in {{CacheableHostResolver#getByName}}. 
Resolving a literal needs no DNS lookup, so the cache saves nothing for it, and 
bypassing restores the exact pre-HADOOP-19447 behavior for literals. Hostnames 
stay cached; {{InetAddress.getByName(hostname)}} already sets the hostname, so 
{{getHostName()}} does not mutate those objects.

Returning a copy via {{InetAddress.getByAddress(host, bytes)}} was considered 
and rejected: it would make {{getHostName()}} return the literal without the 
reverse lookup, which is a behavior change of its own.

The existing {{TestSecurityUtil#testCacheableResolve}} used {{127.0.0.1}} to 
check cache hits, so it switches to {{localhost}} and checks expiry on the 
cache entry (the JDK returns the same object for a hostname from its own lookup 
cache). A new test checks that literals are not shared and keep their host 
string after {{getHostName()}}.



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to