GUACAMOLE-577: Add support for Proxy Configuration to Connections stored in LDAP.

This commit is contained in:
Virtually Nick
2021-05-14 10:15:15 -04:00
parent 2799df6797
commit 6fab0f5036
4 changed files with 184 additions and 20 deletions

View File

@@ -20,9 +20,24 @@
dn: cn=guacConfigGroup,cn=schema,cn=config dn: cn=guacConfigGroup,cn=schema,cn=config
objectClass: olcSchemaConfig objectClass: olcSchemaConfig
cn: guacConfigGroup cn: guacConfigGroup
olcAttributeTypes: {0}( 1.3.6.1.4.1.38971.1.1.1 NAME 'guacConfigProtocol' SYNTAX 1.3.6.1.4.1.1466
.115.121.1.15 ) olcAttributeTypes: ( 1.3.6.1.4.1.38971.1.1.1 NAME 'guacConfigProtocol'
olcAttributeTypes: {1}( 1.3.6.1.4.1.38971.1.1.2 NAME 'guacConfigParameter' SYNTAX 1.3.6.1.4.1.146 SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
6.115.121.1.15 ) olcAttributeTypes: ( 1.3.6.1.4.1.38971.1.1.2 NAME 'guacConfigParameter'
olcObjectClasses: {0}( 1.3.6.1.4.1.38971.1.2.1 NAME 'guacConfigGroup' DESC 'Guacamole config SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
uration group' SUP groupOfNames MUST guacConfigProtocol MAY guacConfigParameter ) olcAttributeTypes: ( 1.3.6.1.4.1.38971.1.1.3 NAME 'guacConfigProxyHostname'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
olcAttributeTypes: ( 1.3.6.1.4.1.38971.1.1.4 NAME 'guacConfigProxyPort'
SINGLE-VALUE
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )
olcAttributeTypes: ( 1.3.6.1.4.1.38971.1.1.5 NAME 'guacConfigProxyEncryption'
SINGLE-VALUE
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
olcObjectClasses: ( 1.3.6.1.4.1.38971.1.2.1 NAME 'guacConfigGroup'
DESC 'Guacamole configuration group'
SUP groupOfNames
MUST guacConfigProtocol
MAY ( guacConfigParameter $
guacConfigProxyHostname $
guacConfigProxyPort $
guacConfigProxyEncryption ) )

View File

@@ -23,9 +23,23 @@ attributetype ( 1.3.6.1.4.1.38971.1.1.1 NAME 'guacConfigProtocol'
attributetype ( 1.3.6.1.4.1.38971.1.1.2 NAME 'guacConfigParameter' attributetype ( 1.3.6.1.4.1.38971.1.1.2 NAME 'guacConfigParameter'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
attributetype ( 1.3.6.1.4.1.38971.1.1.3 NAME 'guacConfigProxyHostname'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
attributetype ( 1.3.6.1.4.1.38971.1.1.4 NAME 'guacConfigProxyPort'
SINGLE-VALUE
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )
attributetype ( 1.3.6.1.4.1.38971.1.1.5 NAME 'guacConfigProxyEncryption'
SINGLE-VALUE
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
objectClass ( 1.3.6.1.4.1.38971.1.2.1 NAME 'guacConfigGroup' objectClass ( 1.3.6.1.4.1.38971.1.2.1 NAME 'guacConfigGroup'
DESC 'Guacamole configuration group' DESC 'Guacamole configuration group'
SUP groupOfNames SUP groupOfNames
MUST guacConfigProtocol MUST guacConfigProtocol
MAY guacConfigParameter ) MAY ( guacConfigParameter $
guacConfigProxyHostname $
guacConfigProxyPort $
guacConfigProxyEncryption ) )

View File

@@ -42,7 +42,11 @@ import org.apache.guacamole.auth.ldap.ConnectedLDAPConfiguration;
import org.apache.guacamole.auth.ldap.ObjectQueryService; import org.apache.guacamole.auth.ldap.ObjectQueryService;
import org.apache.guacamole.auth.ldap.group.UserGroupService; import org.apache.guacamole.auth.ldap.group.UserGroupService;
import org.apache.guacamole.auth.ldap.user.LDAPAuthenticatedUser; import org.apache.guacamole.auth.ldap.user.LDAPAuthenticatedUser;
import org.apache.guacamole.environment.LocalEnvironment;
import org.apache.guacamole.net.auth.AuthenticatedUser;
import org.apache.guacamole.net.auth.Connection; import org.apache.guacamole.net.auth.Connection;
import org.apache.guacamole.net.auth.GuacamoleProxyConfiguration;
import org.apache.guacamole.net.auth.GuacamoleProxyConfiguration.EncryptionMethod;
import org.apache.guacamole.net.auth.TokenInjectingConnection; import org.apache.guacamole.net.auth.TokenInjectingConnection;
import org.apache.guacamole.net.auth.simple.SimpleConnection; import org.apache.guacamole.net.auth.simple.SimpleConnection;
import org.apache.guacamole.protocol.GuacamoleConfiguration; import org.apache.guacamole.protocol.GuacamoleConfiguration;
@@ -60,6 +64,33 @@ public class ConnectionService {
*/ */
private static final Logger logger = LoggerFactory.getLogger(ConnectionService.class); private static final Logger logger = LoggerFactory.getLogger(ConnectionService.class);
/**
* The name of the LDAP attribute that stores connection configuration
* parameters for Guacamole.
*/
public static final String LDAP_ATTRIBUTE_PARAMETER = "guacConfigParameter";
/**
* The name of the LDAP attribute that stores the protocol for a Guacamole
* connection.
*/
public static final String LDAP_ATTRIBUTE_PROTOCOL = "guacConfigProtocol";
/**
* The name of the LDAP attribute that stores guacd proxy hostname.
*/
public static final String LDAP_ATTRIBUTE_PROXY_HOSTNAME = "guacConfigProxyHostname";
/**
* The name of the LDAP attribute that stores guacd proxy port.
*/
public static final String LDAP_ATTRIBUTE_PROXY_PORT = "guacConfigProxyPort";
/**
* The name of the LDAP attribute that stores guacd proxy hostname.
*/
public static final String LDAP_ATTRIBUTE_PROXY_ENCRYPTION = "guacConfigProxyEncryption";
/** /**
* Service for executing LDAP queries. * Service for executing LDAP queries.
*/ */
@@ -192,12 +223,22 @@ public class ConnectionService {
config.setProtocol(protocol.getString()); config.setProtocol(protocol.getString());
} }
catch (LdapInvalidAttributeValueException e) { catch (LdapInvalidAttributeValueException e) {
logger.error("Invalid value of the protocol entry: {}", logger.error("Invalid value of the protocol entry: {}", e.getMessage());
e.getMessage());
logger.debug("LDAP exception when getting protocol value.", e); logger.debug("LDAP exception when getting protocol value.", e);
return null; return null;
} }
// Get proxy configuration, if any
GuacamoleProxyConfiguration proxyConfig;
try {
proxyConfig = getProxyConfiguration(entry);
}
catch (GuacamoleException e) {
logger.error("Failed to retrieve proxy configuration.", e.getMessage());
logger.debug("Guacamole Exception when retrieving proxy configuration.", e);
return null;
}
// Get parameters, if any // Get parameters, if any
Attribute parameterAttribute = entry.get(LDAP_ATTRIBUTE_NAME_PARAMETER); Attribute parameterAttribute = entry.get(LDAP_ATTRIBUTE_NAME_PARAMETER);
if (parameterAttribute != null) { if (parameterAttribute != null) {
@@ -209,10 +250,8 @@ public class ConnectionService {
parameter = parameterAttribute.getString(); parameter = parameterAttribute.getString();
} }
catch (LdapInvalidAttributeValueException e) { catch (LdapInvalidAttributeValueException e) {
logger.warn("Parameter value not valid for {}: {}", logger.warn("Parameter value not valid for {}: {}", cnName, e.getMessage());
cnName, e.getMessage()); logger.debug("LDAP exception when getting parameter value.", e);
logger.debug("LDAP exception when getting parameter value.",
e);
return null; return null;
} }
parameterAttribute.remove(parameter); parameterAttribute.remove(parameter);
@@ -234,7 +273,7 @@ public class ConnectionService {
} }
// Store connection using cn for both identifier and name // Store connection using cn for both identifier and name
Connection connection = new SimpleConnection(cnName, cnName, config, true); Connection connection = new SimpleConnection(cnName, cnName, proxyConfig, config, true);
connection.setParentIdentifier(LDAPAuthenticationProvider.ROOT_CONNECTION_GROUP); connection.setParentIdentifier(LDAPAuthenticationProvider.ROOT_CONNECTION_GROUP);
// Inject LDAP-specific tokens only if LDAP handled user // Inject LDAP-specific tokens only if LDAP handled user
@@ -302,4 +341,63 @@ public class ConnectionService {
return searchFilter; return searchFilter;
} }
/**
* Given an LDAP entry that stores a GuacamoleConfiguration, generate a
* GuacamoleProxyConfiguration that tells the client how to connect to guacd.
* If the proxy configuration values are not found in the LDAP entry the
* defaults from the environment are used. If errors occur while trying to
* ready or parse values from the LDAP entry a GuacamoleException is thrown.
*
* @param connectionEntry
* The LDAP entry that should be checked for proxy configuration values.
*
* @return
* The GuacamoleProxyConfiguration that contains information on how
* to contact guacd for the given Guacamole connection configuration.
*
* @throws GuacamoleException
* If errors occur trying to parse LDAP values from the entry.
*/
private GuacamoleProxyConfiguration getProxyConfiguration(Entry connectionEntry)
throws GuacamoleException {
try {
// Get default proxy configuration values
GuacamoleProxyConfiguration proxyConfig = LocalEnvironment.getInstance().getDefaultGuacamoleProxyConfiguration();
String proxyHostname = proxyConfig.getHostname();
int proxyPort = proxyConfig.getPort();
EncryptionMethod proxyEncryption = proxyConfig.getEncryptionMethod();
// Get the proxy hostname
Attribute proxyHostAttr = connectionEntry.get(LDAP_ATTRIBUTE_PROXY_HOSTNAME);
if (proxyHostAttr != null && proxyHostAttr.size() > 0)
proxyHostname = proxyHostAttr.getString();
// Get the proxy port
Attribute proxyPortAttr = connectionEntry.get(LDAP_ATTRIBUTE_PROXY_PORT);
if (proxyPortAttr != null && proxyPortAttr.size() > 0)
proxyPort = Integer.parseInt(proxyPortAttr.getString());
// Get the proxy encryption method
Attribute proxyEncryptionAttr = connectionEntry.get(LDAP_ATTRIBUTE_PROXY_ENCRYPTION);
if (proxyEncryptionAttr != null && proxyEncryptionAttr.size() > 0) {
try {
proxyEncryption = EncryptionMethod.valueOf(proxyEncryptionAttr.getString());
}
catch (IllegalArgumentException e) {
throw new GuacamoleServerException("Unknown encryption method specified, value must be either \"NONE\" or \"SSL\".", e);
}
}
// Return a new proxy configuration
return new GuacamoleProxyConfiguration(proxyHostname, proxyPort, proxyEncryption);
}
catch (LdapInvalidAttributeValueException e) {
logger.error("Invalid value in proxy configuration: {}", e.getMessage());
logger.debug("LDAP exception fetching proxy attribute value.", e);
throw new GuacamoleServerException("Invalid LDAP value in proxy configuration.", e);
}
}
} }

View File

@@ -24,7 +24,6 @@ import java.util.Date;
import java.util.Map; import java.util.Map;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleServerException; import org.apache.guacamole.GuacamoleServerException;
import org.apache.guacamole.environment.Environment;
import org.apache.guacamole.environment.LocalEnvironment; import org.apache.guacamole.environment.LocalEnvironment;
import org.apache.guacamole.net.GuacamoleSocket; import org.apache.guacamole.net.GuacamoleSocket;
import org.apache.guacamole.net.GuacamoleTunnel; import org.apache.guacamole.net.GuacamoleTunnel;
@@ -54,6 +53,11 @@ public class SimpleConnection extends AbstractConnection {
*/ */
private GuacamoleConfiguration fullConfig; private GuacamoleConfiguration fullConfig;
/**
* The proxy configuration describing how to connect to guacd.
*/
private GuacamoleProxyConfiguration proxyConfig;
/** /**
* Whether parameter tokens in the underlying GuacamoleConfiguration should * Whether parameter tokens in the underlying GuacamoleConfiguration should
* be automatically applied upon connecting. If false, parameter tokens * be automatically applied upon connecting. If false, parameter tokens
@@ -159,6 +163,39 @@ public class SimpleConnection extends AbstractConnection {
} }
/**
* Creates a new SimpleConnection having the given identifier,
* GuacamoleConfiguration, and GuacamoleProxyConfiguration. Parameter tokens
* will be interpreted if explicitly requested.
*
* @param name
* The name to associate with this connection.
*
* @param identifier
* The identifier to associate with this connection.
*
* @param proxyConfig
* The Guacamole proxy configuration describing how the connection to
* guacd should be established, or null if the default settings will be
* used.
*
* @param config
* The configuration describing how to connect to this connection.
*
* @param interpretTokens
* Whether parameter tokens in the underlying GuacamoleConfiguration
* should be automatically applied upon connecting. If false, parameter
* tokens will not be interpreted at all.
*/
public SimpleConnection(String name, String identifier,
GuacamoleProxyConfiguration proxyConfig,
GuacamoleConfiguration config, boolean interpretTokens) {
this(name, identifier, config, interpretTokens);
this.proxyConfig = proxyConfig;
}
/** /**
* Returns the GuacamoleConfiguration describing how to connect to this * Returns the GuacamoleConfiguration describing how to connect to this
* connection. Unlike {@link #getConfiguration()}, which is allowed to omit * connection. Unlike {@link #getConfiguration()}, which is allowed to omit
@@ -201,9 +238,9 @@ public class SimpleConnection extends AbstractConnection {
public GuacamoleTunnel connect(GuacamoleClientInformation info) public GuacamoleTunnel connect(GuacamoleClientInformation info)
throws GuacamoleException { throws GuacamoleException {
// Retrieve proxy configuration from environment // Retrieve proxy configuration from environment if we don't have one
Environment environment = LocalEnvironment.getInstance(); if (proxyConfig == null)
GuacamoleProxyConfiguration proxyConfig = environment.getDefaultGuacamoleProxyConfiguration(); proxyConfig = LocalEnvironment.getInstance().getDefaultGuacamoleProxyConfiguration();
// Get guacd connection parameters // Get guacd connection parameters
String hostname = proxyConfig.getHostname(); String hostname = proxyConfig.getHostname();