소스 검색

SONAR-6140 Ability to restrict list of HTTPS ciphers with the new propert sonar.web.https.ciphers

tags/5.1-RC1
Simon Brandhof 9 년 전
부모
커밋
b50745d4f8

+ 4
- 8
server/sonar-server/src/main/java/org/sonar/server/app/Connectors.java 파일 보기

@@ -21,7 +21,6 @@ package org.sonar.server.app;

import org.apache.catalina.connector.Connector;
import org.apache.catalina.startup.Tomcat;
import org.slf4j.LoggerFactory;
import org.sonar.process.Props;

import javax.annotation.Nullable;
@@ -33,6 +32,9 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
* Configuration of Tomcat connectors
*/
class Connectors {

private static final int DISABLED_PORT = -1;
@@ -77,7 +79,6 @@ class Connectors {
if (port > DISABLED_PORT) {
connector = newConnector(props, HTTP_PROTOCOL, "http");
connector.setPort(port);
info("HTTP connector is enabled on port " + port);
}
return connector;
}
@@ -89,7 +90,6 @@ class Connectors {
if (port > DISABLED_PORT) {
connector = newConnector(props, AJP_PROTOCOL, "http");
connector.setPort(port);
info("AJP connector is enabled on port " + port);
}
return connector;
}
@@ -115,12 +115,12 @@ class Connectors {
setConnectorAttribute(connector, "truststoreType", props.value("sonar.web.https.truststoreType", "JKS"));
setConnectorAttribute(connector, "truststoreProvider", props.value("sonar.web.https.truststoreProvider"));
setConnectorAttribute(connector, "clientAuth", props.value("sonar.web.https.clientAuth", "false"));
setConnectorAttribute(connector, "ciphers", props.value("sonar.web.https.ciphers"));
// SSLv3 must not be enable because of Poodle vulnerability
// See https://jira.codehaus.org/browse/SONAR-5860
setConnectorAttribute(connector, "sslEnabledProtocols", "TLSv1,TLSv1.1,TLSv1.2");
setConnectorAttribute(connector, "sslProtocol", "TLS");
setConnectorAttribute(connector, "SSLEnabled", true);
info("HTTPS connector is enabled on port " + port);
}
return connector;
}
@@ -153,8 +153,4 @@ class Connectors {
c.setAttribute(key, value);
}
}

private static void info(String message) {
LoggerFactory.getLogger(Connectors.class).info(message);
}
}

+ 4
- 0
server/sonar-server/src/main/java/org/sonar/server/app/EmbeddedTomcat.java 파일 보기

@@ -60,12 +60,16 @@ class EmbeddedTomcat {
webappContext = Webapp.configure(tomcat, props);
try {
tomcat.start();
new StartupLogs(LoggerFactory.getLogger(getClass())).log(tomcat);
} catch (LifecycleException e) {
Throwables.propagate(e);
}
}

boolean isReady() {
if (webappContext == null) {
return false;
}
switch (webappContext.getState()) {
case NEW:
case INITIALIZING:

+ 74
- 0
server/sonar-server/src/main/java/org/sonar/server/app/StartupLogs.java 파일 보기

@@ -0,0 +1,74 @@
/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2014 SonarSource
* mailto:contact AT sonarsource DOT com
*
* SonarQube is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* SonarQube 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.server.app;

import org.apache.catalina.connector.Connector;
import org.apache.catalina.startup.Tomcat;
import org.apache.commons.lang.StringUtils;
import org.apache.coyote.ProtocolHandler;
import org.apache.coyote.http11.AbstractHttp11JsseProtocol;
import org.slf4j.Logger;

class StartupLogs {

private final Logger log;

StartupLogs(Logger log) {
this.log = log;
}

void log(Tomcat tomcat) {
Connector[] connectors = tomcat.getService().findConnectors();
for (Connector connector : connectors) {
if (StringUtils.containsIgnoreCase(connector.getProtocol(), "AJP")) {
logAjp(connector);
} else if (StringUtils.equalsIgnoreCase(connector.getScheme(), "https")) {
logHttps(connector);
} else if (StringUtils.equalsIgnoreCase(connector.getScheme(), "http")) {
logHttp(connector);
} else {
throw new IllegalArgumentException("Unsupported connector: " + connector);
}
}
}

private void logAjp(Connector connector) {
log.info(String.format("%s connector enabled on port %d", connector.getProtocol(), connector.getPort()));
}

private void logHttp(Connector connector) {
log.info(String.format("HTTP connector enabled on port %d", connector.getPort()));
}

private void logHttps(Connector connector) {
StringBuilder additional = new StringBuilder();
ProtocolHandler protocol = connector.getProtocolHandler();
if (protocol instanceof AbstractHttp11JsseProtocol) {
additional.append("| ciphers=");
String ciphers = ((AbstractHttp11JsseProtocol) protocol).getCiphers();
if (StringUtils.isBlank(ciphers)) {
additional.append("JVM defaults");
} else {
additional.append(ciphers);
}
}
log.info(String.format("HTTPS connector enabled on port %d %s", connector.getPort(), additional));
}
}

+ 3
- 0
server/sonar-server/src/main/java/org/sonar/server/app/Webapp.java 파일 보기

@@ -30,6 +30,9 @@ import org.sonar.process.Props;
import java.io.File;
import java.util.Map;

/**
* Configures webapp into Tomcat
*/
class Webapp {

private static final String JRUBY_MAX_RUNTIMES = "jruby.max.runtimes";

+ 78
- 0
server/sonar-server/src/test/java/org/sonar/server/app/EmbeddedTomcatTest.java 파일 보기

@@ -0,0 +1,78 @@
/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2014 SonarSource
* mailto:contact AT sonarsource DOT com
*
* SonarQube is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* SonarQube 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.server.app;

import org.apache.commons.io.FileUtils;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.sonar.process.NetworkUtils;
import org.sonar.process.Props;

import java.io.File;
import java.net.ConnectException;
import java.net.Inet4Address;
import java.net.URL;
import java.util.Properties;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;

public class EmbeddedTomcatTest {

@Rule
public TemporaryFolder temp = new TemporaryFolder();

@Test
public void start() throws Exception {
Props props = new Props(new Properties());

// prepare file system
File home = temp.newFolder();
File webDir = new File(home, "web");
FileUtils.write(new File(home, "web/WEB-INF/web.xml"), "<web-app/>");
props.set("sonar.path.home", home.getAbsolutePath());
props.set("sonar.path.web", webDir.getAbsolutePath());

// start server on a random port
int httpPort = NetworkUtils.freePort();
int ajpPort = NetworkUtils.freePort();
props.set("sonar.web.port", String.valueOf(httpPort));
props.set("sonar.ajp.port", String.valueOf(ajpPort));
EmbeddedTomcat tomcat = new EmbeddedTomcat(props);
assertThat(tomcat.isReady()).isFalse();
tomcat.start();
assertThat(tomcat.isReady()).isTrue();

// check that http connector accepts requests
URL url = new URL("http://" + Inet4Address.getLocalHost().getHostAddress() + ":" + httpPort);
url.openConnection().connect();

// stop server
tomcat.terminate();
// tomcat.isReady() must not be called. It is used to wait for server startup, not shutdown.
try {
url.openConnection().connect();
fail();
} catch (ConnectException e) {
// expected
}
}
}

+ 104
- 0
server/sonar-server/src/test/java/org/sonar/server/app/StartupLogsTest.java 파일 보기

@@ -0,0 +1,104 @@
/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2014 SonarSource
* mailto:contact AT sonarsource DOT com
*
* SonarQube is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* SonarQube 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.server.app;

import org.apache.catalina.connector.Connector;
import org.apache.catalina.startup.Tomcat;
import org.apache.coyote.http11.Http11Protocol;
import org.junit.Test;
import org.mockito.Mockito;
import org.slf4j.Logger;

import static org.junit.Assert.fail;
import static org.mockito.Mockito.*;

public class StartupLogsTest {

Tomcat tomcat = mock(Tomcat.class, Mockito.RETURNS_DEEP_STUBS);
Logger logger = mock(Logger.class);
StartupLogs sut = new StartupLogs(logger);

@Test
public void logAjp() throws Exception {
Connector connector = newConnector("AJP/1.3", "http");
when(tomcat.getService().findConnectors()).thenReturn(new Connector[] {connector});

sut.log(tomcat);

verify(logger).info("AJP/1.3 connector enabled on port 1234");
verifyNoMoreInteractions(logger);
}

@Test
public void logHttp() throws Exception {
Connector connector = newConnector("HTTP/1.1", "http");
when(tomcat.getService().findConnectors()).thenReturn(new Connector[] {connector});

sut.log(tomcat);

verify(logger).info("HTTP connector enabled on port 1234");
verifyNoMoreInteractions(logger);
}

@Test
public void logHttps_default_ciphers() throws Exception {
Connector connector = newConnector("HTTP/1.1", "https");
when(tomcat.getService().findConnectors()).thenReturn(new Connector[] {connector});

sut.log(tomcat);

verify(logger).info("HTTPS connector enabled on port 1234 | ciphers=JVM defaults");
verifyNoMoreInteractions(logger);
}

@Test
public void logHttps_overridden_ciphers() throws Exception {
Connector connector = newConnector("HTTP/1.1", "https");
connector.setProtocolHandlerClassName("org.apache.coyote.http11.Http11Protocol");
((Http11Protocol) connector.getProtocolHandler()).setCiphers("SSL_RSA,TLS_RSA_WITH_RC4");
when(tomcat.getService().findConnectors()).thenReturn(new Connector[] {connector});

sut.log(tomcat);

verify(logger).info("HTTPS connector enabled on port 1234 | ciphers=SSL_RSA,TLS_RSA_WITH_RC4");
verifyNoMoreInteractions(logger);
}

@Test
public void unsupported_connector() throws Exception {
Connector connector = mock(Connector.class, Mockito.RETURNS_DEEP_STUBS);
when(connector.getProtocol()).thenReturn("SPDY/1.1");
when(connector.getScheme()).thenReturn("spdy");
when(tomcat.getService().findConnectors()).thenReturn(new Connector[] {connector});
try {
sut.log(tomcat);
fail();
} catch (IllegalArgumentException e) {
// expected
}
}

private Connector newConnector(String protocol, String schema) {
Connector httpConnector = new Connector(protocol);
httpConnector.setScheme(schema);
httpConnector.setPort(1234);
return httpConnector;
}
}

+ 9
- 0
sonar-application/src/main/assembly/conf/sonar.properties 파일 보기

@@ -154,6 +154,15 @@
# and 'true' (certificates are required).
#sonar.web.https.clientAuth=false

# HTTPS - comma separated list of encryption ciphers to support for HTTPS connections.
# If specified, only the ciphers that are listed and supported by the SSL implementation will be used.
# By default, the default ciphers for the JVM will be used. Note that this usually means that the weak
# export grade ciphers will be included in the list of available ciphers.
# The ciphers are specified using the JSSE cipher naming convention (see
# https://www.openssl.org/docs/apps/ciphers.html)
# Example: sonar.web.https.ciphers=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
#sonar.web.https.ciphers=

# The maximum number of connections that the server will accept and process at any given time.
# When this number has been reached, the server will not accept any more connections until
# the number of connections falls below this value. The operating system may still accept connections

Loading…
취소
저장