Merge branch 'jetty-9.3.x' into jetty-9.4.x

This commit is contained in:
Joakim Erdfelt 2016-10-19 16:17:25 -07:00
commit 167f6c8d23
4 changed files with 218 additions and 86 deletions

7
Jenkinsfile vendored
View File

@ -20,7 +20,7 @@ node {
{
stage 'Compile'
withEnv(mvnEnv) {
timeout(15) {
timeout(time: 15, unit: 'MINUTES') {
sh "mvn -B clean install -Dtest=None"
}
}
@ -33,7 +33,7 @@ node {
{
stage 'Javadoc'
withEnv(mvnEnv) {
timeout(15) {
timeout(time: 15, unit: 'MINUTES') {
sh "mvn -B javadoc:javadoc"
}
}
@ -46,7 +46,7 @@ node {
{
stage 'Test'
withEnv(mvnEnv) {
timeout(60) {
timeout(time: 60, unit: 'MINUTES') {
// Run test phase / ignore test failures
sh "mvn -B install -Dmaven.test.failure.ignore=true"
// Report failures in the jenkins UI
@ -54,6 +54,7 @@ node {
testResults: '**/target/surefire-reports/TEST-*.xml'])
// Collect up the jacoco execution results
step([$class: 'JacocoPublisher',
inclusionPattern: "**/org/eclipse/jetty/**/*.class",
execPattern: '**/target/jacoco.exec',
classPattern: '**/target/classes',
sourcePattern: '**/src/main/java'])

View File

@ -396,18 +396,30 @@ jetty.sslContext.keyStorePassword::
To enable two-way authentication, you first need to activate the ssl module as shown in the previous section.
First you need load the `ssl` module and `https` module.
[source%nowrap,ini,linenums]
.start.d/ssl.ini
.$JETTY_BASE/start.d/ssl.ini
----
# Module: ssl
--module=ssl
jetty.secure.port=8443
jetty.keystore=etc/keystore
jetty.keystore.password=OBF:
jetty.keymanager.password=OBF:
jetty.truststore=etc/truststore
jetty.truststore.password=OBF:
jetty.ssl.host=0.0.0.0
jetty.ssl.port=8583
jetty.sslContext.keyStorePath=etc/keystore
jetty.sslContext.trustStorePath=etc/keystore
jetty.sslContext.keyStorePassword=OBF:
jetty.sslContext.keyManagerPassword=OBF:
jetty.sslContext.trustStorePassword=OBF:
jetty.sslContext.trustStoreType=JKS
# enable two way authentication
jetty.ssl.needClientAuth=true
jetty.sslContext.needClientAuth=true
----
[source%nowrap,ini,linenums]
.$JETTY_BASE/start.d/https.ini
----
# Module: https
--module=https
----
[[layout-of-keystore-and-truststore]]
@ -415,19 +427,47 @@ jetty.ssl.needClientAuth=true
`keystore` only contains the server's private key and certificate.
[[img-certificate-chain]]
image::images/certificate-chain.png[title="Certificate chain", alt="Certificate chain"]
[literal]
.The structure of KeyStore file
....
├── PrivateKeyEntry
│   ├── PrivateKey
│   ├── Certificate chain
│   │   ├── Server certificate (end entity)
│   │   ├── Intermediary CA certificate
│   │   └── Root CA certificate
├── TrustedCertEntry
│   └── Intermediary CA certificate
└── TrustedCertEntry
   └── Root CA certificate
....
[TIP]
====
└── PrivateKeyEntry +
   └── Certificate chain +
      ├── Intermediary CA certificate +
      └── Root CA certificate +
are optional
====
[source%nowrap,plain,linenums]
----
$ keytool -list -keystore keystore -storetype jks -storepass '' -v
$ cd $JETTY_BASE
$ keytool -list -keystore etc/keystore -storetype jks -storepass '' -v
Keystore type: JKS
Keystore provider: SUN
Your keystore contains 1 entry
Your keystore contains 3 entries
Alias name: *.example.com
Creation date: Sep 12, 2016
Creation date: Sep 20, 2016
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate chain length: 3
Certificate[1]:
Owner: CN=*.example.com, OU=Web Servers, O="Example.com Co.,Ltd.", C=CN
Issuer: CN="Example.com Co.,Ltd. ETP CA", OU=CA Center, O="Example.com Co.,Ltd.", C=CN
@ -477,26 +517,98 @@ KeyIdentifier [
]
]
Certificate[2]:
Owner: CN="Example.com Co.,Ltd. ETP CA", OU=CA Center, O="Example.com Co.,Ltd.", C=CN
Issuer: CN="Example.com Co.,Ltd. Root CA", OU=CA Center, O="Example.com Co.,Ltd.", C=CN
Serial number: f6e7b86f6fdb467f9498fb599310198f
Valid from: Wed Nov 18 00:00:00 CST 2015 until: Sun Nov 18 00:00:00 CST 2035
Certificate fingerprints:
MD5: ED:A3:91:57:D8:B8:6E:B1:01:58:55:5C:33:14:F5:99
SHA1: D9:A4:93:9D:A6:F8:A3:F9:FD:85:51:E2:C5:2E:0B:EE:80:E7:D0:22
SHA256: BF:54:7A:F6:CA:0C:FA:EF:93:B6:6B:6E:2E:D7:44:A8:40:00:EC:69:3A:2C:CC:9A:F7:FE:8E:6F:C0:FA:22:38
Signature algorithm name: SHA256withRSA
Version: 3
Extensions:
#1: ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
KeyIdentifier [
0000: A6 BD 5F B3 E8 7D 74 3D 20 44 66 1A 16 3B 1B DF .._...t= Df..;..
0010: E6 E6 04 46 ...F
]
]
#2: ObjectId: 2.5.29.19 Criticality=true
BasicConstraints:[
CA:true
PathLen:2147483647
]
#3: ObjectId: 2.5.29.15 Criticality=true
KeyUsage [
Key_CertSign
Crl_Sign
]
#4: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 44 9B AD 31 E7 FE CA D5 5A 8E 17 55 F9 F0 1D 6B D..1....Z..U...k
0010: F5 A5 8F C1 ....
]
]
Certificate[3]:
Owner: CN="Example.com Co.,Ltd. Root CA", OU=CA Center, O="Example.com Co.,Ltd.", C=CN
Issuer: CN="Example.com Co.,Ltd. Root CA", OU=CA Center, O="Example.com Co.,Ltd.", C=CN
Serial number: f0a45bc9972c458cbeae3f723055f1ac
Valid from: Wed Nov 18 00:00:00 CST 2015 until: Sun Nov 18 00:00:00 CST 2114
Certificate fingerprints:
MD5: 50:61:62:22:71:60:F7:69:2E:27:42:6B:62:31:82:79
SHA1: 7A:6D:A6:48:B1:43:03:3B:EA:A0:29:2F:19:65:9C:9B:0E:B1:03:1A
SHA256: 05:3B:9C:5B:8E:18:61:61:D1:9C:AA:0E:8C:B1:EA:44:C2:6E:67:5D:96:30:EC:8C:F6:6F:E1:EC:AD:00:60:F1
Signature algorithm name: SHA256withRSA
Version: 3
Extensions:
#1: ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
KeyIdentifier [
0000: A6 BD 5F B3 E8 7D 74 3D 20 44 66 1A 16 3B 1B DF .._...t= Df..;..
0010: E6 E6 04 46 ...F
]
]
#2: ObjectId: 2.5.29.19 Criticality=true
BasicConstraints:[
CA:true
PathLen:2147483647
]
#3: ObjectId: 2.5.29.15 Criticality=true
KeyUsage [
Key_CertSign
Crl_Sign
]
#4: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: A6 BD 5F B3 E8 7D 74 3D 20 44 66 1A 16 3B 1B DF .._...t= Df..;..
0010: E6 E6 04 46 ...F
]
]
*******************************************
*******************************************
----
`truststore` contains intermediary CA and root CA.
[source%nowrap,plain,linenums]
----
$ keytool -list -keystore truststore -storetype jks -storepass '' -v
Keystore type: JKS
Keystore provider: SUN
Your keystore contains 2 entries
Alias name: example.com co.,ltd. etp ca
Creation date: Sep 12, 2016
Creation date: Sep 20, 2016
Entry type: trustedCertEntry
Owner: CN="Example.com Co.,Ltd. ETP CA", OU=CA Center, O="Example.com Co.,Ltd.", C=CN
@ -547,7 +659,7 @@ KeyIdentifier [
Alias name: example.com co.,ltd. root ca
Creation date: Sep 12, 2016
Creation date: Sep 20, 2016
Entry type: trustedCertEntry
Owner: CN="Example.com Co.,Ltd. Root CA", OU=CA Center, O="Example.com Co.,Ltd.", C=CN
@ -597,10 +709,27 @@ KeyIdentifier [
*******************************************
----
____
[NOTE]
If you use a keystore which contains only one `PrivateKeyEntry` item as the `keystore` and the `truststore`, you may get a `javax.net.ssl.SSLHandshakeException` with `null cert chain` message.
____
In addition, you can split `$JETTY/etc/keystore` as two files.
One is `$JETTY/etc/keystore` which only contains the servers private key and certificate,
the other is `$JETTY/etc/truststore` which contains intermediary CA and root CA.
[literal]
.The structure of `$JETTY/etc/keystore`
....
└── PrivateKeyEntry
   ├── PrivateKey
   └── Certificate chain
      └── Server certificate (end entity)
....
[literal]
.The structure of `$JETTY/etc/truststore`
....
├── TrustedCertEntry
│   └── Intermediary CA certificate
└── TrustedCertEntry
   └── Root CA certificate
....
[[configuring-sslcontextfactory]]
==== Configuring the Jetty SslContextFactory

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

@ -181,20 +181,22 @@ public class LdapLoginModule extends AbstractLoginModule
public class LDAPUserInfo extends UserInfo
{
Attributes attributes;
/**
* @param userName
* @param credential
*/
public LDAPUserInfo(String userName, Credential credential)
public LDAPUserInfo(String userName, Credential credential, Attributes attributes)
{
super(userName, credential);
this.attributes = attributes;
}
@Override
public List<String> doFetchRoles() throws Exception
{
return getUserRoles(_rootContext, getUserName());
return getUserRoles(_rootContext, getUserName(), attributes);
}
}
@ -214,7 +216,8 @@ public class LdapLoginModule extends AbstractLoginModule
*/
public UserInfo getUserInfo(String username) throws Exception
{
String pwdCredential = getUserCredentials(username);
Attributes attributes = getUserAttributes(username);
String pwdCredential = getUserCredentials(attributes);
if (pwdCredential == null)
{
@ -223,7 +226,7 @@ public class LdapLoginModule extends AbstractLoginModule
pwdCredential = convertCredentialLdapToJetty(pwdCredential);
Credential credential = Credential.getCredential(pwdCredential);
return new LDAPUserInfo(username, credential);
return new LDAPUserInfo(username, credential, attributes);
}
protected String doRFC2254Encoding(String inputString)
@ -258,7 +261,7 @@ public class LdapLoginModule extends AbstractLoginModule
}
/**
* attempts to get the users credentials from the users context
* attempts to get the users LDAP attributes from the users context
* <p>
* NOTE: this is not an user authenticated operation
*
@ -266,53 +269,39 @@ public class LdapLoginModule extends AbstractLoginModule
* @return
* @throws LoginException
*/
private String getUserCredentials(String username) throws LoginException
private Attributes getUserAttributes(String username) throws LoginException
{
String ldapCredential = null;
Attributes attributes = null;
SearchControls ctls = new SearchControls();
ctls.setCountLimit(1);
ctls.setDerefLinkFlag(true);
ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
String filter = "(&(objectClass={0})({1}={2}))";
LOG.debug("Searching for users with filter: \'" + filter + "\'" + " from base dn: " + _userBaseDn);
try
{
Object[] filterArguments = {_userObjectClass, _userIdAttribute, username};
NamingEnumeration<SearchResult> results = _rootContext.search(_userBaseDn, filter, filterArguments, ctls);
LOG.debug("Found user?: " + results.hasMoreElements());
if (!results.hasMoreElements())
{
throw new LoginException("User not found.");
}
SearchResult result = findUser(username);
Attributes attributes = result.getAttributes();
Attribute attribute = attributes.get(_userPasswordAttribute);
if (attribute != null)
{
try
{
byte[] value = (byte[]) attribute.get();
ldapCredential = new String(value);
}
catch (NamingException e)
{
LOG.debug("no password available under attribute: " + _userPasswordAttribute);
}
}
}
catch (NamingException e)
{
SearchResult result;
try {
result = findUser(username);
attributes = result.getAttributes();
}
catch (NamingException e) {
throw new LoginException("Root context binding failure.");
}
return attributes;
}
private String getUserCredentials(Attributes attributes) throws LoginException
{
String ldapCredential = null;
Attribute attribute = attributes.get(_userPasswordAttribute);
if (attribute != null)
{
try
{
byte[] value = (byte[]) attribute.get();
ldapCredential = new String(value);
}
catch (NamingException e)
{
LOG.debug("no password available under attribute: " + _userPasswordAttribute);
}
}
LOG.debug("user cred is: " + ldapCredential);
@ -330,9 +319,22 @@ public class LdapLoginModule extends AbstractLoginModule
* @return
* @throws LoginException
*/
private List<String> getUserRoles(DirContext dirContext, String username) throws LoginException, NamingException
private List<String> getUserRoles(DirContext dirContext, String username, Attributes attributes) throws LoginException, NamingException
{
String userDn = _userRdnAttribute + "=" + username + "," + _userBaseDn;
String rdnValue = username;
Attribute attribute = attributes.get(_userRdnAttribute);
if (attribute != null)
{
try
{
rdnValue = (String) attribute.get(); // switch to the value stored in the _userRdnAttribute if we can
}
catch (NamingException e)
{
}
}
String userDn = _userRdnAttribute + "=" + rdnValue + "," + _userBaseDn;
return getUserRolesByDn(dirContext, userDn);
}
@ -537,7 +539,7 @@ public class LdapLoginModule extends AbstractLoginModule
String filter = "(&(objectClass={0})({1}={2}))";
if (LOG.isDebugEnabled())
LOG.debug("Searching for users with filter: \'" + filter + "\'" + " from base dn: " + _userBaseDn);
LOG.debug("Searching for user " + username + " with filter: \'" + filter + "\'" + " from base dn: " + _userBaseDn);
Object[] filterArguments = new Object[]{
_userObjectClass,