Skip to content
Open
Show file tree
Hide file tree
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 @@ -86,6 +86,44 @@ public interface Connector extends Service {

long getMaxConnectionExceededCount();

/** @return the configured remote address allow list, comma separated CIDRs or a file: URI, or null */
String getAllowList();

/** @return the configured remote address deny list, comma separated CIDRs or a file: URI, or null */
String getDenyList();

/** @return true if remote addresses are checked against the allow and deny lists */
boolean isAllowDenyValidationEnabled();

/** Turn the remote address check on or off at runtime; the lists stay configured */
void setAllowDenyValidationEnabled(boolean enabled);

/** @return connections accepted by the remote address check since the last statistics reset */
long getAllowedCount();

/** @return connections refused by the remote address check since the last statistics reset */
long getDeniedCount();

/** @return number of valid CIDR entries loaded into the allow list */
long getAllowListCount();

/** @return number of valid CIDR entries loaded into the deny list */
long getDenyListCount();

/** @return number of allow list entries skipped because they were not valid CIDR blocks */
long getAllowListInvalidCount();

/** @return number of deny list entries skipped because they were not valid CIDR blocks */
long getDenyListInvalidCount();

/**
* Runs an IP literal or a CIDR block through the connector's allow/deny
* decision, ignoring whether enforcement is enabled. An address gets the exact
* decision a connection would; a block is allowed when an allow entry covers it
* and no deny entry covers the whole block.
*/
boolean allowed(String addressOrCidr);

boolean isAutoStart();

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@
import org.apache.activemq.transport.TransportServer;
import org.apache.activemq.transport.discovery.DiscoveryAgent;
import org.apache.activemq.transport.discovery.DiscoveryAgentFactory;
import org.apache.activemq.transport.tcp.TcpTransportServer;
import org.apache.activemq.util.CidrListLoader;
import org.apache.activemq.util.ExceptionUtils;
import org.apache.activemq.util.RemoteAddressValidator;
import org.apache.activemq.util.ServiceStopper;
import org.apache.activemq.util.ServiceSupport;
import org.slf4j.Logger;
Expand Down Expand Up @@ -80,6 +83,15 @@
private int maximumConsumersAllowedPerConnection = Integer.MAX_VALUE;
private PublishedAddressPolicy publishedAddressPolicy = new PublishedAddressPolicy();
private boolean allowLinkStealing = false;
// remote address allow/deny lists; see setAllowList / setDenyList
private String allowList;
private String denyList;
private boolean allowDenyValidationEnabled = false;
private RemoteAddressValidator remoteAddressValidator;
private long allowListCount;
private long denyListCount;
private long allowListInvalidCount;
private long denyListInvalidCount;
private boolean warnOnRemoteClose = false;
private boolean displayStackTrace = false;
private boolean autoStart = true;
Expand Down Expand Up @@ -134,6 +146,9 @@
rc.setMaximumProducersAllowedPerConnection(getMaximumProducersAllowedPerConnection());
rc.setPublishedAddressPolicy(getPublishedAddressPolicy());
rc.setAllowLinkStealing(allowLinkStealing);
rc.setAllowList(allowList);
rc.setDenyList(denyList);
rc.setAllowDenyValidationEnabled(allowDenyValidationEnabled);
rc.setWarnOnRemoteClose(isWarnOnRemoteClose());
rc.setAutoStart(isAutoStart());
return rc;
Expand Down Expand Up @@ -268,6 +283,7 @@
}
}
});
installRemoteAddressValidator();
getServer().setBrokerInfo(brokerInfo);
getServer().start();

Expand Down Expand Up @@ -607,7 +623,7 @@

@Deprecated(forRemoval = true)
@Override
public int connectionCount() {

Check warning on line 626 in activemq-broker/src/main/java/org/apache/activemq/broker/TransportConnector.java

View workflow job for this annotation

GitHub Actions / test

connectionCount() in org.apache.activemq.broker.Connector has been deprecated and marked for removal
return connections.size();
}

Expand All @@ -620,6 +636,104 @@
this.allowLinkStealing = allowLinkStealing;
}

/**
* Loads the allow and deny lists, builds the validator and hands it to the
* transport server. Only socket based servers (tcp, nio, ssl, auto and the
* protocol variants built on them) can check remote addresses.
*/
private void installRemoteAddressValidator() throws Exception {
var allow = CidrListLoader.load(allowList, getName() + " allowList");
var deny = CidrListLoader.load(denyList, getName() + " denyList");
allowListCount = allow.cidrs().size();
denyListCount = deny.cidrs().size();
allowListInvalidCount = allow.invalidCount();
denyListInvalidCount = deny.invalidCount();
remoteAddressValidator = new RemoteAddressValidator(allow.cidrs(), deny.cidrs());
remoteAddressValidator.setEnabled(allowDenyValidationEnabled);
var transportServer = getServer();
if (transportServer instanceof TcpTransportServer) {
((TcpTransportServer) transportServer).setRemoteAddressValidator(remoteAddressValidator);
} else if (allowList != null || denyList != null) {
LOG.warn("allowList/denyList configured on connector {} but its transport does not support remote address validation", getName());
}
}

@Override
public String getAllowList() {
return allowList;
}

/**
* CIDR blocks that remote addresses must fall within to connect, as a comma
* separated list (e.g. {@code 10.0.0.0/8,192.168.1.0/24}) or a {@code file:} URI
* to a file with one CIDR block per line. {@code ${activemq.conf}} and
* {@code ${activemq.data}} may be used in the URI. Empty means no restriction
* beyond the deny list.
*/
public void setAllowList(String allowList) {
this.allowList = allowList;
}

@Override
public String getDenyList() {
return denyList;
}

/**
* CIDR blocks that are refused regardless of the allow list, in the same
* comma separated or {@code file:} URI form as the allow list. Deny entries
* are checked first.
*/
public void setDenyList(String denyList) {
this.denyList = denyList;
}

@Override
public boolean isAllowDenyValidationEnabled() {
return allowDenyValidationEnabled;
}

/**
* Turns the allow/deny check on or off without removing the lists. Default
* false, so configured lists take effect only when this is set; the default
* may change in a future major release. May be changed at runtime through JMX.
*/
@Override
public void setAllowDenyValidationEnabled(boolean allowDenyValidationEnabled) {
this.allowDenyValidationEnabled = allowDenyValidationEnabled;
var validator = remoteAddressValidator;
if (validator != null) {
validator.setEnabled(allowDenyValidationEnabled);
}
}

@Override
public long getAllowListCount() {
return allowListCount;
}

@Override
public long getDenyListCount() {
return denyListCount;
}

@Override
public long getAllowListInvalidCount() {
return allowListInvalidCount;
}

@Override
public long getDenyListInvalidCount() {
return denyListInvalidCount;
}

@Override
public boolean allowed(String addressOrCidr) {
var validator = remoteAddressValidator;
// before start no lists are in effect, so nothing is refused
return validator == null || validator.isAllowed(addressOrCidr);
}

@Override
public boolean isAuditNetworkProducers() {
return auditNetworkProducers;
Expand Down Expand Up @@ -693,6 +807,16 @@
return (server != null ? server.getMaxConnectionExceededCount() : 0l);
}

@Override
public long getAllowedCount() {
return server instanceof TcpTransportServer ? ((TcpTransportServer) server).getAllowedCount() : 0L;
}

@Override
public long getDeniedCount() {
return server instanceof TcpTransportServer ? ((TcpTransportServer) server).getDeniedCount() : 0L;
}

@Override
public boolean isStarted() {
return started.get();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@
* Returns the number of current connections
*/
@Override
public int connectionCount() {

Check warning on line 93 in activemq-broker/src/main/java/org/apache/activemq/broker/jmx/ConnectorView.java

View workflow job for this annotation

GitHub Actions / test

connectionCount() in org.apache.activemq.broker.jmx.ConnectorViewMBean has been deprecated and marked for removal
return connector.connectionCount();

Check warning on line 94 in activemq-broker/src/main/java/org/apache/activemq/broker/jmx/ConnectorView.java

View workflow job for this annotation

GitHub Actions / test

connectionCount() in org.apache.activemq.broker.Connector has been deprecated and marked for removal
}

/**
Expand Down Expand Up @@ -145,6 +145,61 @@
return this.connector.getMaxConnectionExceededCount();
}

@Override
public String getAllowList() {
return this.connector.getAllowList();
}

@Override
public String getDenyList() {
return this.connector.getDenyList();
}

@Override
public boolean isAllowDenyValidationEnabled() {
return this.connector.isAllowDenyValidationEnabled();
}

@Override
public void setAllowDenyValidationEnabled(boolean enabled) {
this.connector.setAllowDenyValidationEnabled(enabled);
}

@Override
public long getAllowedCount() {
return this.connector.getAllowedCount();
}

@Override
public long getDeniedCount() {
return this.connector.getDeniedCount();
}

@Override
public long getAllowListCount() {
return this.connector.getAllowListCount();
}

@Override
public long getDenyListCount() {
return this.connector.getDenyListCount();
}

@Override
public long getAllowListInvalidCount() {
return this.connector.getAllowListInvalidCount();
}

@Override
public long getDenyListInvalidCount() {
return this.connector.getDenyListInvalidCount();
}

@Override
public boolean allowed(String addressOrCidr) {
return this.connector.allowed(addressOrCidr);
}

@Override
public boolean isAutoStart() {
return this.connector.isAutoStart();
Expand Down Expand Up @@ -227,7 +282,7 @@
*/
@Override
public int getConnectionCount() {
return this.connector.connectionCount();

Check warning on line 285 in activemq-broker/src/main/java/org/apache/activemq/broker/jmx/ConnectorView.java

View workflow job for this annotation

GitHub Actions / test

connectionCount() in org.apache.activemq.broker.Connector has been deprecated and marked for removal
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,39 @@ public interface ConnectorViewMBean extends Service {
@MBeanInfo("Max connection exceeded count")
long getMaxConnectionExceededCount();

@MBeanInfo("Remote address allow list: comma separated CIDR blocks or a file: URI")
String getAllowList();

@MBeanInfo("Remote address deny list: comma separated CIDR blocks or a file: URI")
String getDenyList();

@MBeanInfo("Remote address allow/deny validation enabled")
boolean isAllowDenyValidationEnabled();

@MBeanInfo("Enable or disable remote address allow/deny validation; the lists stay configured")
void setAllowDenyValidationEnabled(boolean enabled);

@MBeanInfo("Connections accepted by the remote address check")
long getAllowedCount();

@MBeanInfo("Connections refused by the remote address check")
long getDeniedCount();

@MBeanInfo("Valid CIDR entries loaded into the allow list")
long getAllowListCount();

@MBeanInfo("Valid CIDR entries loaded into the deny list")
long getDenyListCount();

@MBeanInfo("Allow list entries skipped as invalid")
long getAllowListInvalidCount();

@MBeanInfo("Deny list entries skipped as invalid")
long getDenyListInvalidCount();

@MBeanInfo("Would the IP address, or the CIDR block as a whole, be allowed by the allow/deny lists (enabled flag ignored)")
boolean allowed(@MBeanInfo("addressOrCidr") String addressOrCidr);

/**
* @return true if transport connector auto start is enabled
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
import org.apache.activemq.util.IOExceptionSupport;
import org.apache.activemq.util.InetAddressUtil;
import org.apache.activemq.util.IntrospectionSupport;
import org.apache.activemq.util.RemoteAddressValidator;
import org.apache.activemq.util.ServiceListener;
import org.apache.activemq.util.ServiceStopper;
import org.apache.activemq.util.ServiceSupport;
Expand Down Expand Up @@ -124,7 +125,15 @@ public class TcpTransportServer extends TransportServerThreadSupport implements
* The maximum number of sockets allowed for this server
*/
protected int maximumConnections = Integer.MAX_VALUE;
protected final AtomicLong maximumConnectionsExceededCount = new AtomicLong(0l);
protected final AtomicLong maximumConnectionsExceededCount = new AtomicLong(0L);

/**
* Optional allow/deny check applied to the remote address of every accepted
* socket before any other processing. Null means no check.
*/
protected volatile RemoteAddressValidator remoteAddressValidator;
protected final AtomicLong allowedCount = new AtomicLong(0L);
protected final AtomicLong deniedCount = new AtomicLong(0L);
protected final AtomicInteger currentTransportCount = new AtomicInteger();

public TcpTransportServer(TcpTransportFactory transportFactory, URI location, ServerSocketFactory serverSocketFactory) throws IOException,
Expand Down Expand Up @@ -577,6 +586,24 @@ final protected void doHandleSocket(Socket socket) {
boolean closeSocket = true;
boolean countIncremented = false;
try {

// A refused remote address is policy at work, not an error: count it, close the
// socket without writing anything, and stay out of the accept error path. One
// line per attempt is kept at DEBUG so a scanner cannot flood the log.
var validator = remoteAddressValidator;
if (validator != null && validator.isEnabled()) {
if (!validator.isAllowed(socket)) {
deniedCount.incrementAndGet();
LOG.debug("Refused connection from {}: remote address not allowed", socket.getRemoteSocketAddress());
try {
socket.close();
} catch (IOException ignore) {
}
return;
}
allowedCount.incrementAndGet();
}

int currentCount;
do {
currentCount = currentTransportCount.get();
Expand Down Expand Up @@ -735,8 +762,28 @@ public long getMaxConnectionExceededCount() {
return this.maximumConnectionsExceededCount.get();
}

public RemoteAddressValidator getRemoteAddressValidator() {
return remoteAddressValidator;
}

public void setRemoteAddressValidator(RemoteAddressValidator remoteAddressValidator) {
this.remoteAddressValidator = remoteAddressValidator;
}

/** connections accepted by the remote address check since the last reset */
public long getAllowedCount() {
return allowedCount.get();
}

/** connections refused by the remote address check since the last reset */
public long getDeniedCount() {
return deniedCount.get();
}

@Override
public void resetStatistics() {
this.maximumConnectionsExceededCount.set(0l);
this.maximumConnectionsExceededCount.set(0L);
this.allowedCount.set(0L);
this.deniedCount.set(0L);
}
}
Loading
Loading