-
Notifications
You must be signed in to change notification settings - Fork 102
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[NEEDS REVIEW] Fixes JENKINS-14520 - LDAP StartTLS support #97
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -54,13 +54,20 @@ | |
import org.springframework.security.ldap.search.FilterBasedLdapUserSearch; | ||
import org.springframework.security.ldap.search.LdapUserSearch; | ||
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator; | ||
import org.springframework.ldap.core.support.DefaultTlsDirContextAuthenticationStrategy; | ||
|
||
import javax.naming.Context; | ||
import javax.naming.NamingException; | ||
import javax.naming.directory.Attribute; | ||
import javax.naming.directory.Attributes; | ||
import javax.naming.directory.DirContext; | ||
import javax.naming.directory.InitialDirContext; | ||
import javax.naming.ldap.InitialLdapContext; | ||
import javax.naming.ldap.LdapContext; | ||
import javax.naming.ldap.StartTlsRequest; | ||
import javax.naming.ldap.StartTlsResponse; | ||
import javax.net.ssl.SSLPeerUnverifiedException; | ||
import javax.net.ssl.SSLSession; | ||
|
||
import java.io.IOException; | ||
import java.net.InetAddress; | ||
|
@@ -405,18 +412,37 @@ public FormValidation doCheckServer(@QueryParameter String value, @QueryParamete | |
|
||
try { | ||
Hashtable<String,String> props = new Hashtable<String,String>(); | ||
if(managerDN!=null && managerDN.trim().length() > 0 && !"undefined".equals(managerDN)) { | ||
props.put(Context.SECURITY_PRINCIPAL,managerDN); | ||
props.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); | ||
props.put(Context.PROVIDER_URL, LDAPSecurityRealm.toProviderUrl(server,rootDN)); | ||
|
||
LdapContext ctx = new InitialLdapContext(props, null); | ||
StartTlsResponse tls = (StartTlsResponse) ctx.extendedOperation(new StartTlsRequest()); | ||
SSLSession session = tls.negotiate(); | ||
|
||
if (!session.isValid()) { | ||
throw new IOException("Couldn't negotiate StartTls session, session is invalid"); | ||
} | ||
ctx.addToEnvironment(Context.SECURITY_AUTHENTICATION, "simple"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this needed, it would appear to be an unrelated change and would preclude the use of any stronger sasl algorithms? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
is copy pasted from https://docs.oracle.com/javase/jndi/tutorial/ldap/ext/starttls.html#TLS%20with%20Simple%20Authentication I did see the |
||
if (managerDN != null && managerDN.trim().length() > 0 && !"undefined".equals(managerDN)) { | ||
ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, managerDN); | ||
props.put(Context.SECURITY_PRINCIPAL, managerDN); | ||
} | ||
if(managerPassword!=null && managerPassword.trim().length() > 0 && !"undefined".equals(managerPassword)) { | ||
props.put(Context.SECURITY_CREDENTIALS,managerPassword); | ||
ctx.addToEnvironment(Context.SECURITY_CREDENTIALS, managerPassword); | ||
props.put(Context.SECURITY_CREDENTIALS, managerPassword); | ||
} | ||
props.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); | ||
props.put(Context.PROVIDER_URL, LDAPSecurityRealm.toProviderUrl(server,rootDN)); | ||
|
||
DirContext ctx = new InitialDirContext(props); | ||
|
||
ctx.getAttributes(""); | ||
|
||
// Stop TLS | ||
tls.close(); | ||
// Close the context when we're done | ||
ctx.close(); | ||
|
||
return FormValidation.ok(); // connected | ||
} catch (IOException ioe) { | ||
return FormValidation.error(Messages.LDAPSecurityRealm_UnableToConnect(server, ioe.getMessage())); | ||
} catch (NamingException e) { | ||
// trouble-shoot | ||
Matcher m = Pattern.compile("(ldaps?://)?([^:]+)(?:\\:(\\d+))?(\\s+(ldaps?://)?([^:]+)(?:\\:(\\d+))?)*").matcher(server.trim()); | ||
|
@@ -458,26 +484,38 @@ public DescriptorExtensionList<LDAPGroupMembershipStrategy, Descriptor<LDAPGroup | |
private String inferRootDN(String server) { | ||
try { | ||
Hashtable<String, String> props = new Hashtable<String, String>(); | ||
props.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); | ||
props.put(Context.PROVIDER_URL, LDAPSecurityRealm.toProviderUrl(getServerUrl(), "")); | ||
|
||
LdapContext ctx = new InitialLdapContext(props, null); | ||
StartTlsResponse tls = (StartTlsResponse) ctx.extendedOperation(new StartTlsRequest()); | ||
SSLSession session = tls.negotiate(); | ||
if (session.isValid()) { | ||
LOGGER.log(Level.INFO, "session is valid!s"); | ||
} | ||
ctx.addToEnvironment(Context.SECURITY_AUTHENTICATION, "simple"); | ||
|
||
if (managerDN != null) { | ||
props.put(Context.SECURITY_PRINCIPAL, managerDN); | ||
ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, managerDN); | ||
props.put(Context.SECURITY_CREDENTIALS, getManagerPassword()); | ||
ctx.addToEnvironment(Context.SECURITY_CREDENTIALS, getManagerPassword()); | ||
} | ||
props.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); | ||
props.put(Context.PROVIDER_URL, LDAPSecurityRealm.toProviderUrl(getServerUrl(), "")); | ||
|
||
DirContext ctx = new InitialDirContext(props); | ||
Attributes atts = ctx.getAttributes(""); | ||
Attribute a = atts.get("defaultNamingContext"); | ||
if (a != null && a.get() != null) // this entry is available on Active Directory. See http://msdn2.microsoft.com/en-us/library/ms684291(VS.85).aspx | ||
return a.get().toString(); | ||
|
||
a = atts.get("namingcontexts"); | ||
tls.close(); | ||
ctx.close(); | ||
if (a == null) { | ||
LOGGER.warning("namingcontexts attribute not found in root DSE of " + server); | ||
return null; | ||
} | ||
return a.get().toString(); | ||
} catch (NamingException e) { | ||
} catch (IOException|NamingException e) { | ||
LOGGER.log(Level.WARNING, "Failed to connect to LDAP to infer Root DN for " + server, e); | ||
return null; | ||
} | ||
|
@@ -577,6 +615,10 @@ public ApplicationContext createApplicationContext(LDAPSecurityRealm realm) { | |
vars.put("com.sun.jndi.ldap.read.timeout", "60000"); // timeout if no response after 60 seconds | ||
vars.putAll(getExtraEnvVars()); | ||
contextSource.setBaseEnvironmentProperties(vars); | ||
|
||
// pooled connections do not work with StartTLS | ||
contextSource.setPooled(false); | ||
Comment on lines
+619
to
+620
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. interesting. so you mean There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah, this one took me a while to figure out, but every example i was able to find on the internet related to StartTLS and Spring had
Also there seems to be some known issues as documented in the |
||
contextSource.setAuthenticationStrategy(new DefaultTlsDirContextAuthenticationStrategy()); | ||
contextSource.afterPropertiesSet(); | ||
|
||
FilterBasedLdapUserSearch ldapUserSearch = new FilterBasedLdapUserSearch(getUserSearchBase(), getUserSearch(), contextSource); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does this not break for anyone using non SSL LDAP? (in other words this should be available for users to opt into, or opt out of).
For the AD plugin we also need to close the
StartTlsResponse
https://github.com/jenkinsci/active-directory-plugin/blob/19e9fbcf0518537c6c4bd24fb1fd09901e7fd60e/src/main/java/hudson/plugins/active_directory/ActiveDirectorySecurityRealm.java#L627-L644There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agree definitely using startTLS must be optional and not per default.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agreed, this definitely breaks non-STARTTLS implementations. I commented below with some additional context for this PR.