Skip to content

Commit

Permalink
cells,dcap,ftp: Support for accepting connections from an allowed lis…
Browse files Browse the repository at this point in the history
…t of subnets and IP addresses

Motivation:

To disallow incoming connections from clients or satellite domains which
are not in the list of configured allowed-subnets. This can be
additionally used to accept unencrypted connections from only trusted subnets.

Modification:

Both IPv4 and IPv6 subnets and addresses are be supported.

To use configure,

1. dcache.broker.core.allowed-subnets: Allowed subnets to connect to CoreDomain
2. ftp.net.allowed-subnets:    Allowed subnets to connect to ftp door
3. dcap.net.allowed-subnets:   Allowed subnets to connect to dcap door

Ticket:
Acked-by: Paul Millar <paul.millar@desy.de>
Target: master
Request: 3.1
Require-book: no
Require-notes: no
Patch: https://rb.dcache.org/r/10392/
  • Loading branch information
anupash committed Sep 2, 2017
1 parent a2a51d1 commit 19e5cd3
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 3 deletions.
Expand Up @@ -81,7 +81,6 @@
import org.dcache.util.ColumnWriter;

import static com.google.common.base.Preconditions.checkArgument;
import static dmg.cells.nucleus.CellDomainRole.CORE;
import static java.nio.charset.StandardCharsets.UTF_8;

/**
Expand Down Expand Up @@ -381,7 +380,7 @@ protected CoreDomains(String domainName, CuratorFramework client, PathChildrenCa
public static CoreDomains createWithMode(String domainName, CuratorFramework client, String mode)
throws BadConfigException
{
LOGGER.error("Creating CoreDomains: {}, {}", domainName, mode);
LOGGER.info("Creating CoreDomains: {}, {}", domainName, mode);
ConnectionType type = ConnectionType.fromConfig(mode).orElseThrow(() -> new BadConfigException("Bad mode " + mode));

switch (type) {
Expand Down Expand Up @@ -808,7 +807,9 @@ public synchronized void reset(Mode mode, State state)
private void startListenerWithTcp()
throws ExecutionException, InterruptedException, UnknownHostException
{
String cellArgs = args.argv(0);
String cellArgs = String.format("%s -netmask='%s'",
args.argv(0),
args.getOption("netmask", ""));
lmPlain = startListener(cellArgs);
LOGGER.info("lmPlain: {}; port; {} ", lmPlain, lmPlain.getListenPort());
info.addCore("tcp", InetAddress.getLocalHost().getCanonicalHostName(), lmPlain.getListenPort());
Expand Down
@@ -1,5 +1,6 @@
package dmg.cells.services.login;

import com.google.common.base.CharMatcher;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.base.Throwables;
Expand All @@ -22,6 +23,7 @@
import java.net.Socket;
import java.nio.channels.ServerSocketChannel;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
Expand All @@ -31,6 +33,8 @@
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

import dmg.cells.nucleus.CellAdapter;
import dmg.cells.nucleus.CellEvent;
Expand All @@ -44,6 +48,7 @@

import org.dcache.util.Args;
import org.dcache.util.NDC;
import org.dcache.util.Subnet;
import org.dcache.util.Version;

import static com.google.common.base.Preconditions.checkArgument;
Expand Down Expand Up @@ -82,6 +87,8 @@ public class LoginManager
private volatile boolean _sending = true;
private volatile int _maxLogin = -1;

private final Set<Subnet> _allowed;

/**
* Tagging interface that a CellMessage payload implements to indicate
* the notification should be forwarded to all children.
Expand Down Expand Up @@ -115,6 +122,9 @@ public LoginManager(String name, String cellType, String argString)
super(name, cellType, argString);
_nucleus = getNucleus();
_args = getArgs();

//allowed subnets
_allowed = allowedSubnets(_args.getOption("netmask", ""));
}

@Override
Expand Down Expand Up @@ -543,6 +553,19 @@ public void run()
while (!_serverSocket.isClosed()) {
try {
Socket socket = _serverSocket.accept();
InetSocketAddress remoteAddress = (InetSocketAddress)socket.getRemoteSocketAddress();


if (!remoteAddress.getAddress().isAnyLocalAddress() &&
!remoteAddress.getAddress().isLoopbackAddress() &&
!_allowed.isEmpty() &&
_allowed.stream()
.noneMatch(s -> s.contains(remoteAddress.getAddress()))) {
throw new IOException("Remote Host ("
+ remoteAddress.getAddress()
+ ") not in the list of allowed subnets");
}

socket.setKeepAlive(true);
socket.setTcpNoDelay(true);
LOGGER.debug("Socket OPEN (ACCEPT) remote = {} local = {}",
Expand Down Expand Up @@ -814,4 +837,27 @@ public boolean validateUser(String userName, String password)
return false;
}
}

public static Set<Subnet> allowedSubnets(String netmask)
{
return StreamSupport.stream(Splitter.on(CharMatcher.whitespace())
.trimResults()
.omitEmptyStrings()
.split(netmask)
.spliterator(),
false)
.filter(s -> !s.isEmpty())
.map(LoginManager::validateCreateSubnet)
.collect(Collectors.toSet());
}

public static Subnet validateCreateSubnet(String subnet)
{
try {
return Subnet.create(subnet);
} catch (IllegalArgumentException iae) {
throw new IllegalArgumentException(
String.format("Invalid IP/subnet '%s': %s\n.", subnet, iae.getMessage()));
}
}
}
1 change: 1 addition & 0 deletions skel/share/cells/tunnel-core.fragment
Expand Up @@ -16,6 +16,7 @@ create -- dmg.cells.services.CoreRoutingManager RoutingMgr "-role=core"
create -- dmg.cells.services.LocationManager lm \
"-role=core \
-mode='${dcache.broker.core.client.channel.security}' \
-netmask='${dcache.broker.core.allowed-subnets}' \
-socketfactory='org.dcache.ssl.CanlSslServerSocketCreator \
-service_key="${dcache.broker.channel.credential.key}" \
-service_cert="${dcache.broker.channel.credential.cert}" \
Expand Down
21 changes: 21 additions & 0 deletions skel/share/defaults/dcache.properties
Expand Up @@ -1117,6 +1117,27 @@ dcache.macaroons.default-lifetime.unit = DAYS
(immutable)dcache.net.ports.tcp=${dcache.net.ports.tcp-when-scheme-is-${dcache.broker.scheme}}
(immutable)dcache.net.ports.udp=${dcache.net.ports.udp-when-scheme-is-${dcache.broker.scheme}}


# -----------------------------------------------------------------------
# List of Subnets allowed to connect
# -----------------------------------------------------------------------
# Space separated list of subnets (in CIDR notation) and IP addresses.
# Both IPv4 and IPv6 subnets and addresses are supported.
# Clients connecting from IP addresses outside these subnets are rejected.
#
# e.g.
#
# 172.16.0.0/255.240.0.0 64:ff9b::/96 198.18.0.0/15
dcache.net.allowed-subnets=


# -----------------------------------------------------------------------
# List of Subnets allowed to connect to the CoreDomain.
# -----------------------------------------------------------------------
# Setting this property restricts unencrypted cell communication originating
# from satellite domains within the allowed subnets.
dcache.broker.core.allowed-subnets=${dcache.net.allowed-subnets}

# OpenID Connect hostnames
#
# Space separated list of multiple hostnames
Expand Down
7 changes: 7 additions & 0 deletions skel/share/defaults/dcap.properties
Expand Up @@ -185,4 +185,11 @@ dcap.limits.client-version =
#
(immutable)dcap.net.ports.tcp=${dcap.net.port}

# -----------------------------------------------------------------------
# List of Subnets allowed to connect to the dCap Door
# -----------------------------------------------------------------------
# Space separated list of subnets (in CIDR notation) and IP addresses
# See dcache.net.allowed-subnets for details
dcap.net.allowed-subnets=${dcache.net.allowed-subnets}

(obsolete)dcap.cell.export = See dcap.cell.consume
7 changes: 7 additions & 0 deletions skel/share/defaults/ftp.properties
Expand Up @@ -298,5 +298,12 @@ ftp.loginbroker.root = ${ftp.root}
# Document which TCP ports are opened
(immutable)ftp.net.ports.tcp=${ftp.net.port} ${ftp.net.port-range}

# -----------------------------------------------------------------------
# List of Subnets allowed to connect to the FTP Door
# -----------------------------------------------------------------------
# Space separated list of subnets (in CIDR notation) and IP addresses
# See dcache.net.allowed-subnets for details
ftp.net.allowed-subnets=${dcache.net.allowed-subnets}

(forbidden)ftp.authz.upload-directory=See gplazma.authz.upload-directory
(obsolete)ftp.cell.export = See ftp.cell.consume
1 change: 1 addition & 0 deletions skel/share/services/dcap.batch
Expand Up @@ -117,5 +117,6 @@ create dmg.cells.services.login.LoginManager ${dcap.cell.name} \
-io-queue-overwrite=${dcap.authz.mover-queue-overwrite} \
-anonymous-access=${dcap.authz.anonymous-operations} \
-clientVersion=\"${dcap.limits.client-version}\" \
-netmask=\"${dcap.net.allowed-subnets}\" \
${arguments-${dcap.authn.protocol}} \
"
1 change: 1 addition & 0 deletions skel/share/services/ftp.batch
Expand Up @@ -121,4 +121,5 @@ create dmg.cells.services.login.LoginManager ${ftp.cell.name} \
-cipher-flags=\"${ftp.authn.ciphers}\" \
-key-cache-lifetime=\"${ftp.authn.gsi.delegation.cache.lifetime}\" \
-key-cache-lifetime-unit=\"${ftp.authn.gsi.delegation.cache.lifetime.unit}\" \
-netmask=\"${ftp.net.allowed-subnets}\" \
"

0 comments on commit 19e5cd3

Please sign in to comment.