production
This commit is contained in:
parent
431063ab32
commit
956df03cc1
9 changed files with 120 additions and 41 deletions
1
docker/prod/standalone/selfsigned/.gitignore
vendored
Normal file
1
docker/prod/standalone/selfsigned/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/secrets
|
|
@ -1,41 +1,36 @@
|
|||
spring.profiles.include=prod-ws,prod-gui
|
||||
|
||||
file.encoding=UTF-8
|
||||
logging.level.org.apache.tomcat.util.net.NioEndpoint=DEBUG
|
||||
logging.level.ch=DEBUG
|
||||
|
||||
sebserver.certs.password=[SET_PWD]
|
||||
sebserver.mariadb.password=[SET_PWD]
|
||||
sebserver.password=[SET_PWD]
|
||||
|
||||
server.address=0.0.0.0
|
||||
server.port=443
|
||||
server.servlet.context-path=/
|
||||
|
||||
##########################################################
|
||||
### Security
|
||||
|
||||
security.require-ssl=true
|
||||
server.ssl.key-store-type=PKCS12
|
||||
server.ssl.key-store=file:/certs/seb-server-keystore.pkcs12
|
||||
server.ssl.key-store=C:/dev/workspaces/sebserver/seb-server/docker/prod/standalone/selfsigned/certs/seb-server-keystore.pkcs12
|
||||
server.ssl.key-store-password=${sebserver.certs.password}
|
||||
server.ssl.key-alias=sebserver
|
||||
server.ssl.key-password=${sebserver.certs.password}
|
||||
server.ssl.trust-store=file:/certs/seb-server-truststore.pkcs12
|
||||
server.ssl.trust-store=C:/dev/workspaces/sebserver/seb-server/docker/prod/standalone/selfsigned/certs/seb-server-truststore.pkcs12
|
||||
server.ssl.trust-store-password=${sebserver.certs.password}
|
||||
server.ssl.enabled-protocols=SSLv3,TLSv1,TLSv1.1,TLSv1.2
|
||||
server.ssl.enabled-protocols=TLSv1,TLSv1.1,TLSv1.2
|
||||
|
||||
javax.net.ssl.keyStore=/certs/seb-server-keystore.pkcs12
|
||||
javax.net.ssl.keyStorePassword=${sebserver.certs.password}
|
||||
javax.net.ssl.trustStore=/certs/seb-server-truststore.pkcs12
|
||||
javax.net.ssl.trustStorePassword=${sebserver.certs.password}
|
||||
##########################################################
|
||||
### SEB Server Overall
|
||||
|
||||
sebserver.webservice.api.admin.clientSecret=${sebserver.password}
|
||||
sebserver.webservice.internalSecret=${sebserver.password}
|
||||
# Default logging level in the form "logging.level" + namespace=LEVEL
|
||||
logging.level.ch=DEBUG
|
||||
logging.file=log/sebserver.log
|
||||
|
||||
# If webservice or gui runs on ssl and this flag is true, an integrated redirect from http to https is activated
|
||||
# Disable this if a redirect is done by a pre-processing proxy
|
||||
sebserver.ssl.redirect.enabled=true
|
||||
|
||||
##########################################################
|
||||
### SEB Server Webservice configuration
|
||||
|
||||
# logging
|
||||
logging.file=log/sebserver.log
|
||||
|
||||
# database server
|
||||
datastore.mariadb.server.address=seb-server-mariadb
|
||||
datastore.mariadb.server.port=3306
|
||||
|
@ -53,9 +48,11 @@ spring.datasource.hikari.maxLifetime=1800000
|
|||
spring.datasource.password=${sebserver.mariadb.password}
|
||||
|
||||
# webservice configuration
|
||||
sebserver.webservice.api.admin.clientSecret=${sebserver.password}
|
||||
sebserver.webservice.internalSecret=${sebserver.password}
|
||||
sebserver.webservice.distributed=false
|
||||
sebserver.webservice.http.scheme=https
|
||||
sebserver.webservice.http.server.name=0.0.0.0
|
||||
sebserver.webservice.http.server.name=${server.address}
|
||||
sebserver.webservice.http.redirect.gui=/gui
|
||||
sebserver.webservice.api.admin.clientId=guiClient
|
||||
sebserver.webservice.api.admin.endpoint=/admin-api/v1
|
||||
|
@ -75,8 +72,6 @@ sebserver.webservice.lms.openedx.api.token.request.paths=/oauth2/access_token
|
|||
management.endpoints.web.base-path=/actuator
|
||||
management.endpoints.web.exposure.include=logfile,loggers
|
||||
|
||||
|
||||
|
||||
##########################################################
|
||||
### SEB Server GUI configuration
|
||||
server.servlet.session.cookie.http-only=true
|
||||
|
@ -84,11 +79,12 @@ server.servlet.session.tracking-modes=cookie
|
|||
|
||||
sebserver.gui.entrypoint=/gui
|
||||
sebserver.gui.webservice.protocol=https
|
||||
sebserver.gui.webservice.address=0.0.0.0
|
||||
sebserver.gui.webservice.address=localhost
|
||||
sebserver.gui.webservice.port=443
|
||||
sebserver.gui.webservice.apipath=/admin-api/v1
|
||||
# defines the polling interval that is used to poll the webservice for client connection data on a monitored exam page
|
||||
sebserver.gui.webservice.poll-interval=500
|
||||
sebserver.gui.webservice.mock-lms-enabled=true
|
||||
|
||||
|
||||
sebserver.gui.theme=css/sebserver.css
|
||||
|
|
|
@ -2,26 +2,28 @@ FROM openjdk:11-jre-stretch
|
|||
|
||||
RUN apt-get update && apt-get install -y openssl
|
||||
|
||||
ENV KEYSTORE_PWD=
|
||||
ENV OPENSSL_SUBJ="/C=CH/ST=Zuerich/L=Zuerich"
|
||||
ENV OPENSSL_CA="${OPENSSL_SUBJ}/CN=SEB_SEVER_CN"
|
||||
|
||||
VOLUME /certs
|
||||
WORKDIR /certs
|
||||
|
||||
RUN export $(grep -v '^#' secrets | xargs)
|
||||
|
||||
CMD openssl genrsa -out ca-key.pem 2048 \
|
||||
&& openssl req -new -x509 -key ca-key.pem -nodes -days 3600 -subj "${OPENSSL_CA}" -out ca.pem \
|
||||
&& openssl req -newkey rsa:2048 -days 3600 -nodes -config /certs/config/certs.cnf -keyout server-key.pem -out server-req.pem \
|
||||
&& openssl req -newkey rsa:2048 -days 3600 -nodes -config certs.cnf -keyout server-key.pem -out server-req.pem \
|
||||
&& openssl rsa -in server-key.pem -out server-key.pem \
|
||||
&& openssl x509 -req -in server-req.pem -days 3600 -CA ca.pem -CAkey ca-key.pem -set_serial 01 -out server-cert.pem \
|
||||
&& openssl req -newkey rsa:2048 -days 3600 -nodes -config /certs/config/certs.cnf -keyout client-key.pem -out client-req.pem \
|
||||
&& openssl req -newkey rsa:2048 -days 3600 -nodes -config certs.cnf -keyout client-key.pem -out client-req.pem \
|
||||
&& openssl rsa -in client-key.pem -out client-key.pem \
|
||||
&& openssl x509 -req -in client-req.pem -days 3600 -CA ca.pem -CAkey ca-key.pem -set_serial 01 -out client-cert.pem \
|
||||
&& openssl verify -CAfile ca.pem server-cert.pem client-cert.pem \
|
||||
&& openssl x509 -in ca.pem -inform pem -out ca.der -outform der \
|
||||
&& openssl pkcs12 -export -out client-cert.pkcs12 -in client-cert.pem -inkey client-key.pem -passout pass:"${KEYSTORE_PWD}" \
|
||||
&& keytool -importkeystore -destkeystore seb-server-keystore.pkcs12 -deststorepass "${KEYSTORE_PWD}" -srckeystore client-cert.pkcs12 -srcstoretype PKCS12 -srcstorepass "${KEYSTORE_PWD}" \
|
||||
&& keytool -import -file ca.pem -keystore seb-server-truststore.pkcs12 -storepass "${KEYSTORE_PWD}" -srcstoretype PKCS12 -noprompt \
|
||||
&& keytool -genkeypair -alias sebserver -dname "CN=localhost, OU=ETHZ, O=ETHZ, L=Zuerich, S=Zuerich, C=CH" -keyalg RSA -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore seb-server-keystore.pkcs12 -storepass "${KEYSTORE_PWD}" -validity 3650 \
|
||||
&& keytool -export -alias sebserver -keystore seb-server-keystore.pkcs12 -rfc -file sebserver.cert -storetype PKCS12 -storepass "${KEYSTORE_PWD}" -noprompt \
|
||||
&& keytool -importcert -trustcacerts -alias sebserver -file sebserver.cert -keystore seb-server-truststore.pkcs12 -storetype PKCS12 -storepass "${KEYSTORE_PWD}" -noprompt
|
||||
&& keytool -importcert -trustcacerts -alias sebserver -file sebserver.cert -keystore seb-server-truststore.pkcs12 -storetype PKCS12 -storepass "${KEYSTORE_PWD}" -noprompt \
|
||||
&& keytool -import -alias mariadb-ca -file ca.pem -keystore seb-server-truststore.pkcs12 -storepass "${KEYSTORE_PWD}" -srcstoretype PKCS12 -noprompt \
|
||||
&& keytool -import -alias mariadb-client -file client-cert.pem -keystore seb-server-truststore.pkcs12 -storepass "${KEYSTORE_PWD}" -srcstoretype PKCS12 -noprompt \
|
||||
&& keytool -import -alias mariadb-server -file server-cert.pem -keystore seb-server-keystore.pkcs12 -storepass "${KEYSTORE_PWD}" -srcstoretype PKCS12 -noprompt \
|
|
@ -7,11 +7,11 @@ services:
|
|||
container_name: gencerts
|
||||
volumes:
|
||||
- ./certs:/certs
|
||||
- .:/certs/config
|
||||
- ./certs.cnf:/certs/certs.cnf
|
||||
- ./secrets:/certs/secrets
|
||||
environment:
|
||||
- SERVER_CN=seb-server-mariadb
|
||||
- CLIENT_CN=seb-server-mariadb
|
||||
- KEYSTORE_PWD=[SET_PWD]
|
||||
|
||||
mariadb:
|
||||
image: "mariadb/server:10.3"
|
||||
|
@ -20,8 +20,8 @@ services:
|
|||
- .:/etc/mysql/conf.d
|
||||
- ./certs:/etc/mysql/certs
|
||||
- seb-server-mariadb-data:/var/lib/mysql
|
||||
environment:
|
||||
- MYSQL_ROOT_PASSWORD=[SET_PWD]
|
||||
env_file:
|
||||
- secrets
|
||||
ports:
|
||||
- 3306:3306
|
||||
networks:
|
||||
|
|
|
@ -25,6 +25,15 @@ ENV SEBSERVER_VERSION=${SEBSERVER_VERSION}
|
|||
WORKDIR /sebserver
|
||||
COPY --from=1 /sebserver/target/seb-server-"$SEBSERVER_VERSION".jar /sebserver
|
||||
|
||||
ENTRYPOINT exec java -Djavax.net.debug=SSL -jar seb-server-"${SEBSERVER_VERSION}".jar --spring.profiles.active=prod --spring.config.location=file:/config/,classpath:/config/
|
||||
RUN export $(grep -v '^#' secrets | xargs)
|
||||
|
||||
ENTRYPOINT exec java \
|
||||
-Djavax.net.debug=SSL \
|
||||
-jar seb-server-"${SEBSERVER_VERSION}".jar \
|
||||
--spring.profiles.active=prod \
|
||||
--spring.config.location=file:/config/,classpath:/config/ \
|
||||
--sebserver.certs.password="${KEYSTORE_PWD}" \
|
||||
--sebserver.mariadb.password="${MYSQL_ROOT_PASSWORD}" \
|
||||
--sebserver.password="${SEBSERVER_PWD}" \
|
||||
|
||||
EXPOSE 443
|
|
@ -8,10 +8,24 @@
|
|||
|
||||
package ch.ethz.seb.sebserver;
|
||||
|
||||
import org.apache.catalina.Context;
|
||||
import org.apache.catalina.connector.Connector;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.tomcat.util.descriptor.web.SecurityCollection;
|
||||
import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration;
|
||||
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
|
||||
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.ProdGuiProfile;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.ProdWebServiceProfile;
|
||||
|
||||
/** SEB-Server (Safe Exam Browser Server) is a server component to maintain and support
|
||||
* Exams running with SEB (Safe Exam Browser). TODO add link(s)
|
||||
|
@ -25,9 +39,7 @@ import org.springframework.cache.annotation.EnableCaching;
|
|||
* SEB-Server uses Spring's profiles to consequently separate sub-components of the webservice
|
||||
* and GUI and can be used to start the components on separate servers or within the same
|
||||
* server instance. Additional to the usual profiles like dev, prod, test there are combining
|
||||
* profiles like dev-ws, dev-gui and prod-ws, prod-gui
|
||||
*
|
||||
* TODO documentation for presets to start all-in-one server or separated gui- and webservice- server */
|
||||
* profiles like dev-ws, dev-gui and prod-ws, prod-gui */
|
||||
@SpringBootApplication(exclude = {
|
||||
UserDetailsServiceAutoConfiguration.class,
|
||||
})
|
||||
|
@ -39,4 +51,53 @@ public class SEBServer {
|
|||
SpringApplication.run(SEBServer.class, args);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add an additional redirect Connector on http port to redirect all http calls
|
||||
* to https.
|
||||
*
|
||||
* NOTE: This works with TomcatServletWebServerFactory and embedded tomcat.
|
||||
* If the webservice and/or gui is going to running on another server or
|
||||
* redirect is handled by a proxy, this redirect can be deactivated within
|
||||
* the "sebserver.ssl.redirect.enabled" property set to false
|
||||
*/
|
||||
@Bean
|
||||
@ProdWebServiceProfile
|
||||
@ProdGuiProfile
|
||||
public ServletWebServerFactory servletContainer(
|
||||
final Environment env,
|
||||
final ApplicationContext applicationContext) {
|
||||
|
||||
final String enabled = env.getProperty(
|
||||
"sebserver.ssl.redirect.enabled",
|
||||
Constants.FALSE_STRING);
|
||||
|
||||
if (!BooleanUtils.toBoolean(enabled)) {
|
||||
return new TomcatServletWebServerFactory();
|
||||
}
|
||||
|
||||
final TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory() {
|
||||
@Override
|
||||
protected void postProcessContext(final Context context) {
|
||||
final SecurityConstraint securityConstraint = new SecurityConstraint();
|
||||
securityConstraint.setUserConstraint("CONFIDENTIAL");
|
||||
final SecurityCollection collection = new SecurityCollection();
|
||||
collection.addPattern("/*");
|
||||
securityConstraint.addCollection(collection);
|
||||
context.addConstraint(securityConstraint);
|
||||
}
|
||||
};
|
||||
tomcat.addAdditionalTomcatConnectors(redirectConnector(env));
|
||||
return tomcat;
|
||||
}
|
||||
|
||||
private Connector redirectConnector(final Environment env) {
|
||||
final String sslPort = env.getRequiredProperty("server.port");
|
||||
final Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
|
||||
connector.setScheme("http");
|
||||
connector.setPort(80);
|
||||
connector.setSecure(false);
|
||||
connector.setRedirectPort(Integer.valueOf(sslPort));
|
||||
return connector;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -154,7 +154,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements E
|
|||
log.info("Initialize with secure ClientHttpRequestFactory for production");
|
||||
|
||||
final String truststoreFilePath = env
|
||||
.getProperty("javax.net.ssl.trustStore", "");
|
||||
.getProperty("server.ssl.trust-store", "");
|
||||
|
||||
if (StringUtils.isBlank(truststoreFilePath)) {
|
||||
throw new IllegalArgumentException("Missing trust-store file path");
|
||||
|
@ -163,7 +163,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements E
|
|||
final File trustStoreFile = ResourceUtils.getFile("file:" + truststoreFilePath);
|
||||
|
||||
final char[] password = env
|
||||
.getProperty("javax.net.ssl.trustStorePassword", "")
|
||||
.getProperty("server.ssl.trust-store-password", "")
|
||||
.toCharArray();
|
||||
|
||||
if (password.length < 3) {
|
||||
|
@ -171,6 +171,10 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements E
|
|||
throw new IllegalArgumentException("Missing or incorrect trust-store password");
|
||||
}
|
||||
|
||||
// Set the specified trust-store also on javax.net.ssl level
|
||||
System.setProperty("javax.net.ssl.trustStore", truststoreFilePath);
|
||||
System.setProperty("javax.net.ssl.trustStorePassword", String.valueOf(password));
|
||||
|
||||
final SSLContext sslContext = SSLContextBuilder
|
||||
.create()
|
||||
.loadTrustMaterial(trustStoreFile, password)
|
||||
|
|
|
@ -22,6 +22,7 @@ import java.util.stream.Collectors;
|
|||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.joda.time.DateTimeZone;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
|
@ -98,15 +99,18 @@ public class ResourceService {
|
|||
private final I18nSupport i18nSupport;
|
||||
private final RestService restService;
|
||||
private final CurrentUser currentUser;
|
||||
private final boolean mock_lms_enabled;
|
||||
|
||||
protected ResourceService(
|
||||
final I18nSupport i18nSupport,
|
||||
final RestService restService,
|
||||
final CurrentUser currentUser) {
|
||||
final CurrentUser currentUser,
|
||||
@Value("${sebserver.gui.webservice.mock-lms-enabled:true}") final boolean mock_lms_enabled) {
|
||||
|
||||
this.i18nSupport = i18nSupport;
|
||||
this.restService = restService;
|
||||
this.currentUser = currentUser;
|
||||
this.mock_lms_enabled = mock_lms_enabled;
|
||||
}
|
||||
|
||||
public I18nSupport getI18nSupport() {
|
||||
|
@ -137,6 +141,7 @@ public class ResourceService {
|
|||
public List<Tuple<String>> lmsTypeResources() {
|
||||
return Arrays.asList(LmsType.values())
|
||||
.stream()
|
||||
.filter(lmsType -> lmsType != LmsType.MOCKUP || this.mock_lms_enabled)
|
||||
.map(lmsType -> new Tuple<>(
|
||||
lmsType.name(),
|
||||
this.i18nSupport.getText(LMSSETUP_TYPE_PREFIX + lmsType.name(), lmsType.name())))
|
||||
|
|
|
@ -12,6 +12,7 @@ sebserver.gui.webservice.port=8080
|
|||
sebserver.gui.webservice.apipath=/admin-api/v1
|
||||
# defines the polling interval that is used to poll the webservice for client connection data on a monitored exam page
|
||||
sebserver.gui.webservice.poll-interval=500
|
||||
sebserver.gui.webservice.mock-lms-enabled=true
|
||||
|
||||
|
||||
sebserver.gui.theme=css/sebserver.css
|
||||
|
|
Loading…
Reference in a new issue