/* dCache - http://www.dcache.org/
*
* Copyright (C) 2014-2015 Deutsches Elektronen-Synchrotron
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.dcache.util.jetty;
import eu.emi.security.authn.x509.CrlCheckingMode;
import eu.emi.security.authn.x509.NamespaceCheckingMode;
import eu.emi.security.authn.x509.OCSPCheckingMode;
import org.eclipse.jetty.server.ConnectionFactory;
import org.eclipse.jetty.server.ForwardedRequestCustomizer;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.ProxyConnectionFactory;
import org.eclipse.jetty.server.SecureRequestCustomizer;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Required;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.dcache.gsi.KeyPairCache;
import static com.google.common.base.Preconditions.checkState;
import static org.dcache.util.Crypto.getBannedCipherSuitesFromConfigurationValue;
import static org.dcache.util.jetty.ConnectorFactoryBean.Protocol.PLAIN;
public class ConnectorFactoryBean implements FactoryBean<ServerConnector>
{
private int acceptors = -1;
private int port;
private String host;
private int backlog;
private long idleTimeout;
private TimeUnit idleTimeoutUnit;
private Server server;
private long hostCertificateTimeout;
private TimeUnit hostCertificateTimeoutUnit;
private long caCertificateTimeout;
private TimeUnit caCertificateTimeoutUnit;
private File serverCertificatePath;
private File serverKeyPath;
private File certificateAuthorityPath;
private boolean needClientAuth;
private boolean wantClientAuth;
private String[] excludedCipherSuites = {};
private boolean isUsingLegacyClose;
private CrlCheckingMode crlCheckingMode = CrlCheckingMode.IF_VALID;
private OCSPCheckingMode ocspCheckingMode = OCSPCheckingMode.IF_AVAILABLE;
private NamespaceCheckingMode namespaceMode = NamespaceCheckingMode.EUGRIDPMA_GLOBUS;
private KeyPairCache keyPairCache;
private boolean isProxyConnectionEnabled;
private boolean isForwardedHeaderProcessingEnabled;
private Protocol protocol;
public int getAcceptors()
{
return acceptors;
}
public void setAcceptors(int acceptors)
{
this.acceptors = acceptors;
}
public int getPort()
{
return port;
}
public void setPort(int port)
{
this.port = port;
}
public String getHost()
{
return host;
}
public void setHost(String host)
{
this.host = host;
}
public int getBacklog()
{
return backlog;
}
public void setBacklog(int backlog)
{
this.backlog = backlog;
}
public long getIdleTimeout()
{
return idleTimeout;
}
@Required
public void setIdleTimeout(long idleTimeout)
{
this.idleTimeout = idleTimeout;
}
public TimeUnit getIdleTimeoutUnit()
{
return idleTimeoutUnit;
}
@Required
public void setIdleTimeoutUnit(TimeUnit idleTimeoutUnit)
{
this.idleTimeoutUnit = idleTimeoutUnit;
}
public Server getServer()
{
return server;
}
@Required
public void setServer(Server server)
{
this.server = server;
}
public long getServerCertificateTimeout()
{
return hostCertificateTimeout;
}
public void setServerCertificateTimeout(long serverCertificateTimeout)
{
this.hostCertificateTimeout = serverCertificateTimeout;
}
public TimeUnit getServerCertificateTimeoutUnit()
{
return hostCertificateTimeoutUnit;
}
public void setServerCertificateTimeoutUnit(TimeUnit serverCertificateTimeoutUnit)
{
this.hostCertificateTimeoutUnit = serverCertificateTimeoutUnit;
}
public long getCaPathTimeout()
{
return caCertificateTimeout;
}
public void setCaPathTimeout(long caPathTimeout)
{
this.caCertificateTimeout = caPathTimeout;
}
public TimeUnit getCaPathTimeoutUnit()
{
return caCertificateTimeoutUnit;
}
public void setCaPathTimeoutUnit(TimeUnit caPathTimeoutUnit)
{
this.caCertificateTimeoutUnit = caPathTimeoutUnit;
}
public File getServerCertificatePath()
{
return serverCertificatePath;
}
public void setServerCertificatePath(File serverCertificatePath)
{
this.serverCertificatePath = serverCertificatePath;
}
public File getServerKeyPath()
{
return serverKeyPath;
}
public void setServerKeyPath(File serverKeyPath)
{
this.serverKeyPath = serverKeyPath;
}
public File getCaPath()
{
return certificateAuthorityPath;
}
public void setCaPath(File caPath)
{
this.certificateAuthorityPath = caPath;
}
public boolean isNeedClientAuth()
{
return needClientAuth;
}
public void setNeedClientAuth(boolean needClientAuth)
{
this.needClientAuth = needClientAuth;
}
public boolean isWantClientAuth()
{
return wantClientAuth;
}
public void setWantClientAuth(boolean wantClientAuth)
{
this.wantClientAuth = wantClientAuth;
}
public String[] getExcludedCipherSuites()
{
return excludedCipherSuites;
}
public void setExcludedCipherSuites(String[] excludedCipherSuites)
{
this.excludedCipherSuites = excludedCipherSuites;
}
public void setCipherFlags(String cipherFlags)
{
this.excludedCipherSuites = getBannedCipherSuitesFromConfigurationValue(cipherFlags);
}
@Required
public void setProtocol(Protocol protocol)
{
this.protocol = protocol;
}
public boolean isUsingLegacyClose()
{
return isUsingLegacyClose;
}
public void setUsingLegacyClose(boolean isUsingLegacyClose)
{
this.isUsingLegacyClose = isUsingLegacyClose;
}
public CrlCheckingMode getCrlCheckingMode()
{
return crlCheckingMode;
}
public void setCrlCheckingMode(CrlCheckingMode crlCheckingMode)
{
this.crlCheckingMode = crlCheckingMode;
}
public OCSPCheckingMode getOcspCheckingMode()
{
return ocspCheckingMode;
}
public void setOcspCheckingMode(OCSPCheckingMode ocspCheckingMode)
{
this.ocspCheckingMode = ocspCheckingMode;
}
public NamespaceCheckingMode getNamespaceMode()
{
return namespaceMode;
}
public void setNamespaceMode(NamespaceCheckingMode namespaceMode)
{
this.namespaceMode = namespaceMode;
}
public KeyPairCache getKeyPairCache()
{
return keyPairCache;
}
public void setKeyPairCache(KeyPairCache keyPairCache)
{
this.keyPairCache = keyPairCache;
}
public boolean isProxyConnectionEnabled()
{
return isProxyConnectionEnabled;
}
public void setProxyConnectionEnabled(boolean proxyConnectionEnabled)
{
isProxyConnectionEnabled = proxyConnectionEnabled;
}
public boolean isForwardedHeaderProcessingEnabled()
{
return isForwardedHeaderProcessingEnabled;
}
public void setForwardedHeaderProcessingEnabled(boolean forwardedHeaderProcessingEnabled)
{
isForwardedHeaderProcessingEnabled = forwardedHeaderProcessingEnabled;
}
private SslContextFactory createContextFactory() throws Exception
{
CanlContextFactory factory = new CanlContextFactory();
factory.setCertificatePath(serverCertificatePath);
factory.setKeyPath(serverKeyPath);
factory.setCertificateAuthorityPath(certificateAuthorityPath);
factory.setNeedClientAuth(needClientAuth);
factory.setWantClientAuth(wantClientAuth);
factory.setExcludeCipherSuites(excludedCipherSuites);
factory.setGsiEnabled(protocol == Protocol.GSI);
factory.setUsingLegacyClose(isUsingLegacyClose);
factory.setKeyPairCache(keyPairCache);
factory.setCertificateAuthorityUpdateInterval(caCertificateTimeoutUnit.toMillis(caCertificateTimeout));
factory.setCredentialUpdateInterval(hostCertificateTimeoutUnit.toMillis(hostCertificateTimeout));
factory.setNamespaceMode(namespaceMode);
factory.setCrlCheckingMode(crlCheckingMode);
factory.setOcspCheckingMode(ocspCheckingMode);
factory.start();
return factory;
}
@Override
public ServerConnector getObject() throws Exception
{
checkState(protocol == PLAIN || hostCertificateTimeout > 0);
checkState(protocol == PLAIN || hostCertificateTimeoutUnit != null);
checkState(protocol == PLAIN || caCertificateTimeout > 0);
checkState(protocol == PLAIN || caCertificateTimeoutUnit != null);
checkState(protocol == PLAIN || serverCertificatePath != null);
checkState(protocol == PLAIN || serverKeyPath != null);
checkState(protocol == PLAIN || certificateAuthorityPath != null);
HttpConnectionFactory httpConnectionFactory = new HttpConnectionFactory();
switch (protocol) {
case PLAIN:
break;
case TLS:
httpConnectionFactory.getHttpConfiguration().addCustomizer(new SecureRequestCustomizer());
break;
case GSI:
httpConnectionFactory.getHttpConfiguration().addCustomizer(new SecureRequestCustomizer());
httpConnectionFactory.getHttpConfiguration().addCustomizer(new GsiRequestCustomizer());
break;
}
if (isForwardedHeaderProcessingEnabled) {
httpConnectionFactory.getHttpConfiguration().addCustomizer(new ForwardedRequestCustomizer());
}
List<ConnectionFactory> factories = new ArrayList<>();
if (isProxyConnectionEnabled) {
factories.add(new ProxyConnectionFactory());
}
if (protocol != PLAIN) {
factories.add(new SslConnectionFactory(createContextFactory(), httpConnectionFactory.getProtocol()));
}
factories.add(httpConnectionFactory);
ServerConnector serverConnector =
new ServerConnector(server, null, null, null, acceptors, -1, factories.stream().toArray(ConnectionFactory[]::new));
serverConnector.setPort(port);
serverConnector.setHost(host);
serverConnector.setAcceptQueueSize(backlog);
serverConnector.setIdleTimeout(idleTimeoutUnit.toMillis(idleTimeout));
return serverConnector;
}
@Override
public Class<?> getObjectType()
{
return ServerConnector.class;
}
@Override
public boolean isSingleton()
{
return true;
}
public enum Protocol
{
PLAIN, TLS, GSI
}
}