Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@
package org.apache.hugegraph.pd.raft.auth;

import java.net.InetSocketAddress;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import io.netty.channel.ChannelDuplexHandler;
Expand All @@ -30,11 +33,14 @@
@ChannelHandler.Sharable
public class IpAuthHandler extends ChannelDuplexHandler {

// Retained for potential refresh of resolvedIps on membership changes
private final Set<String> allowedIps;
private volatile Set<String> resolvedIps;
private static volatile IpAuthHandler instance;

private IpAuthHandler(Set<String> allowedIps) {
this.allowedIps = Collections.unmodifiableSet(allowedIps);
this.resolvedIps = resolveAll(allowedIps);
Copy link
Member

@imbajin imbajin Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

‼️ resolvedIps is only built once in the constructor, but IpAuthHandler is still a JVM-wide singleton. RaftEngine#changePeerList() can replace the peer set after startup, and hostname-based peers can also resolve to a different IP later. In both cases this handler keeps the original resolved addresses forever, so a valid PD peer can be blocked until the whole process restarts. Please add a refresh path for membership/DNS changes, or defer hostname resolution to validation time with a bounded cache/TTL.

peer hostname
    |
    v
startup resolve once
    |
    v
resolvedIps (singleton, frozen)
    |
    +--> peer list changed
    |
    +--> DNS changed
            |
            v
      new peer IP not in set
            |
            v
        connection blocked

}

public static IpAuthHandler getInstance(Set<String> allowedIps) {
Expand Down Expand Up @@ -65,7 +71,24 @@ private static String getClientIp(ChannelHandlerContext ctx) {
}

private boolean isIpAllowed(String ip) {
return allowedIps.isEmpty() || allowedIps.contains(ip);
Set<String> resolved = this.resolvedIps;
return resolved.isEmpty() || resolved.contains(ip);
}

private static Set<String> resolveAll(Set<String> entries) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ This change introduces new hostname-resolution behavior, but the PR still has no regression coverage around it. Please add at least one test for a hostname allowlist entry and one for a resolution failure/refresh scenario, otherwise it will be very easy to regress this path again.

Set<String> result = new HashSet<>(entries);

for (String entry : entries) {
try {
for (InetAddress addr : InetAddress.getAllByName(entry)) {
result.add(addr.getHostAddress());
}
} catch (UnknownHostException e) {
log.warn("Could not resolve allowlist entry '{}': {}", entry, e.getMessage());
}
}

return Collections.unmodifiableSet(result);
}

@Override
Expand Down
Loading