WebWolf DataSource Discovery

This commit is contained in:
Àngel Ollé Blázquez 2021-09-22 14:58:23 +02:00 committed by Nanne Baars
parent 8e567b0f86
commit 9af514f3eb
10 changed files with 260 additions and 28 deletions

View File

@ -16,8 +16,6 @@ java \
--add-opens java.base/java.io=ALL-UNNAMED \ --add-opens java.base/java.io=ALL-UNNAMED \
-jar webgoat.jar --webgoat.build.version="$1" --server.address=0.0.0.0 > webgoat.log & -jar webgoat.jar --webgoat.build.version="$1" --server.address=0.0.0.0 > webgoat.log &
sleep 30
echo "Starting WebWolf..." echo "Starting WebWolf..."
java -Duser.home=/home/webgoat -Dfile.encoding=UTF-8 -jar webwolf.jar --webgoat.build.version=$1 --server.address=0.0.0.0 > webwolf.log & java -Duser.home=/home/webgoat -Dfile.encoding=UTF-8 -jar webwolf.jar --webgoat.build.version=$1 --server.address=0.0.0.0 > webwolf.log &

View File

@ -58,7 +58,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception { protected void configure(HttpSecurity http) throws Exception {
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry security = http ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry security = http
.authorizeRequests() .authorizeRequests()
.antMatchers("/css/**", "/images/**", "/js/**", "fonts/**", "/plugins/**", "/registration", "/register.mvc").permitAll() .antMatchers("/css/**", "/images/**", "/js/**", "fonts/**", "/plugins/**", "/registration", "/register.mvc", "/actuator/**").permitAll()
.anyRequest().authenticated(); .anyRequest().authenticated();
security.and() security.and()
.formLogin() .formLogin()

View File

@ -55,4 +55,8 @@ exclude.categories=${EXCLUDE_CATEGORIES:none,none}
#exclude based on the enum of the Category #exclude based on the enum of the Category
exclude.lessons=${EXCLUDE_LESSONS:none,none} exclude.lessons=${EXCLUDE_LESSONS:none,none}
#exclude based on the class name of a lesson e.g.: LessonTemplate #exclude based on the class name of a lesson e.g.: LessonTemplate
management.health.db.enabled=true
management.endpoint.health.show-details=always
management.endpoints.web.exposure.include=health,configprops

View File

@ -67,6 +67,10 @@
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId> <artifactId>spring-boot-starter-actuator</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.thymeleaf.extras</groupId> <groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId> <artifactId>thymeleaf-extras-springsecurity5</artifactId>

View File

@ -22,18 +22,14 @@
package org.owasp.webwolf; package org.owasp.webwolf;
import java.io.IOException;
import java.net.Socket;
import org.owasp.webwolf.requests.WebWolfTraceRepository; import org.owasp.webwolf.requests.WebWolfTraceRepository;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.actuate.trace.http.HttpTraceRepository; import org.springframework.boot.actuate.trace.http.HttpTraceRepository;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import java.io.IOException;
import java.net.Socket;
import javax.sql.DataSource;
@SpringBootApplication @SpringBootApplication
public class WebWolf { public class WebWolf {
@ -45,26 +41,20 @@ public class WebWolf {
public static void main(String[] args) { public static void main(String[] args) {
System.setProperty("spring.config.name", "application-webwolf"); System.setProperty("spring.config.name", "application-webwolf");
String webwolfPort = System.getenv("WEBWOLF_PORT"); String webwolfPort = System.getenv("WEBWOLF_PORT");
String databasePort = System.getenv("WEBGOAT_HSQLPORT");
String webGoatHost = null==System.getenv("WEBGOAT_HOST")?"127.0.0.1":System.getenv("WEBGOAT_HOST"); String webGoatHost = null==System.getenv("WEBGOAT_HOST")?"127.0.0.1":System.getenv("WEBGOAT_HOST");
String webWolfHost = null==System.getenv("WEBWOLF_HOST")?"127.0.0.1":System.getenv("WEBWOLF_HOST"); String webWolfHost = null==System.getenv("WEBWOLF_HOST")?"127.0.0.1":System.getenv("WEBWOLF_HOST");
String fileEncoding = System.getProperty("file.encoding"); String fileEncoding = System.getProperty("file.encoding");
int wolfPort = webwolfPort == null?9090:Integer.parseInt(webwolfPort); int wolfPort = webwolfPort == null?9090:Integer.parseInt(webwolfPort);
int dbPort = databasePort == null?9001:Integer.parseInt(databasePort);
if (null==fileEncoding || !fileEncoding.equals("UTF-8")) { if (null==fileEncoding || !fileEncoding.equals("UTF-8")) {
System.out.println("It seems the application is startd on a OS with non default UTF-8 encoding:"+fileEncoding); System.out.println("It seems the application is startd on a OS with non default UTF-8 encoding:"+fileEncoding);
System.out.println("Please add: -Dfile.encoding=UTF-8"); System.out.println("Please add: -Dfile.encoding=UTF-8");
System.exit(-1); System.exit(-1);
} }
if (!isAlreadyRunning(webGoatHost, dbPort)) {
System.out.println("It seems that the required database is not running. Please start WebGoat with the integrated or standalone database first.");
System.exit(-1);
}
if (isAlreadyRunning(webGoatHost, wolfPort)) { if (isAlreadyRunning(webGoatHost, wolfPort)) {
System.out.println("Port "+webWolfHost+":"+wolfPort+" is in use. Use environment value WEBWOLF_PORT to set a different value."); System.out.println("Port "+webWolfHost+":"+wolfPort+" is in use. Use environment value WEBWOLF_PORT to set a different value.");
System.exit(-1); System.exit(-1);
@ -72,13 +62,6 @@ public class WebWolf {
SpringApplication.run(WebWolf.class, args); SpringApplication.run(WebWolf.class, args);
} }
@Bean
public DataSource dataSource(@Value("${spring.datasource.url}") String url, @Value("${spring.datasource.driver-class-name}") String driverClassName) {
DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource(url);
driverManagerDataSource.setDriverClassName(driverClassName);
return driverManagerDataSource;
}
private static boolean isAlreadyRunning(String host, int port) { private static boolean isAlreadyRunning(String host, int port) {
try (var ignored = new Socket(host, port)) { try (var ignored = new Socket(host, port)) {
return true; return true;

View File

@ -0,0 +1,58 @@
/*
* This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/
*
* Copyright (c) 2002 - 2021 Bruce Mayhew
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* Getting Source ==============
*
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.
*/
package org.owasp.webwolf.db;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
/**
*
* @author Angel Olle Blazquez
*
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class ActuatorDsJsonParser {
protected static final String getDsPropertyFromConfigProps(JsonNode node, String propertyName) {
return node
.get("application")
.get("beans")
.get("spring.datasource-org.springframework.boot.autoconfigure.jdbc.DataSourceProperties")
.get("properties")
.get(propertyName)
.asText();
}
protected static final String getDsHealthStatus(JsonNode node) {
return node
.get("components")
.get("db")
.get("components")
.get("dataSource")
.get("status")
.asText();
}
}

View File

@ -0,0 +1,52 @@
/*
* This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/
*
* Copyright (c) 2002 - 2021 Bruce Mayhew
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* Getting Source ==============
*
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.
*/
package org.owasp.webwolf.db;
import static org.owasp.webwolf.db.ActuatorDsJsonParser.getDsPropertyFromConfigProps;
import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.Data;
/**
*
* @author Angel Olle Blazquez
*
*/
@Data
public class DataSourceProperties implements Serializable {
private static final long serialVersionUID = -5897408528235134090L;
private String url;
private String driverClassName;
@JsonProperty("contexts")
protected void props(JsonNode node) {
url = getDsPropertyFromConfigProps(node, "url");
driverClassName = getDsPropertyFromConfigProps(node, "driverClassName");
}
}

View File

@ -0,0 +1,111 @@
/*
* This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/
*
* Copyright (c) 2002 - 2021 Bruce Mayhew
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* Getting Source ==============
*
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.
*/
package org.owasp.webwolf.db;
import static org.owasp.webwolf.db.ActuatorDsJsonParser.getDsHealthStatus;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.DependsOn;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.EnableRetry;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.extern.slf4j.Slf4j;
/**
*
* @author Angel Olle Blazquez
*
*/
@Slf4j
@Component
@EnableRetry
public class DataSourceResolver {
@Value("${webgoat.actuator.base.url}")
private String baseUrl;
@Value("${webgoat.actuator.health.db.path:/health}")
private String dbHealthPath;
@Value("${webgoat.actuator.configprops.path:/configprops}")
private String configPropsPath;
@Autowired
ApplicationContext ctx;
@Bean
@DependsOn("dsConfigDiscovery")
public DataSource dataSource(DataSourceProperties dataSourceProperties) {
DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource(dataSourceProperties.getUrl());
driverManagerDataSource.setDriverClassName(dataSourceProperties.getDriverClassName());
return driverManagerDataSource;
}
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
@Bean
@Retryable(maxAttemptsExpression = "${webwolf.datasource-discovery.retry.max-attempts:5}",
value = Exception.class,
backoff = @Backoff(
multiplierExpression = "${webwolf.datasource-discovery.retry.backoff.multiplier:1.5}",
maxDelayExpression = "${webwolf.datasource-discovery.retry.backoff.max-delay:30000}",
delayExpression = "${webwolf.datasource-discovery.retry.backoff.delay:5000}"))
public DataSourceProperties dsConfigDiscovery(RestTemplate restTemplate) {
healthCheck(restTemplate);
return restTemplate.getForObject(baseUrl + configPropsPath, DataSourceProperties.class);
}
public void healthCheck(RestTemplate restTemplate) {
log.info("Checking database availability.");
JsonNode json = restTemplate.getForObject(baseUrl + dbHealthPath, JsonNode.class);
String status = getDsHealthStatus(json);
if (!status.equals("UP")) {
throw new ResourceUnavailableException();
}
}
@Recover
public DataSourceProperties exitOnResourceUnavailable(Exception e, RestTemplate restTemplate) {
log.error("It seems that the required database is not running. Please start WebGoat with the integrated or standalone database first.");
System.exit(SpringApplication.exit(ctx, () -> 1));
return null;
}
}

View File

@ -0,0 +1,22 @@
package org.owasp.webwolf.db;
public class ResourceUnavailableException extends RuntimeException {
private static final long serialVersionUID = 1L;
public ResourceUnavailableException() {
super();
}
public ResourceUnavailableException(Throwable cause) {
super(cause);
}
public ResourceUnavailableException(String message) {
super(message);
}
public ResourceUnavailableException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -7,8 +7,6 @@ server.address=${WEBWOLF_HOST:127.0.0.1}
server.servlet.session.cookie.name=WEBWOLFSESSION server.servlet.session.cookie.name=WEBWOLFSESSION
server.servlet.session.timeout=6000 server.servlet.session.timeout=6000
spring.datasource.url=jdbc:hsqldb:hsql://${WEBGOAT_HOST:127.0.0.1}:${WEBGOAT_HSQLPORT:9001}/webgoat
spring.datasource.driver-class-name=org.hsqldb.jdbc.JDBCDriver
spring.jpa.properties.hibernate.default_schema=CONTAINER spring.jpa.properties.hibernate.default_schema=CONTAINER
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.HSQLDialect spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.HSQLDialect
spring.jpa.hibernate.ddl-auto=update spring.jpa.hibernate.ddl-auto=update
@ -32,9 +30,11 @@ multipart.max-file-size=1Mb
multipart.max-request-size=1Mb multipart.max-request-size=1Mb
webgoat.build.version=@project.version@ webgoat.build.version=@project.version@
webgoat.actuator.base.url=http://${WEBGOAT_HOST:127.0.0.1}:${WEBGOAT_PORT:8080}/WebGoat/actuator
webgoat.server.directory=${user.home}/.webgoat-${webgoat.build.version}/ webgoat.server.directory=${user.home}/.webgoat-${webgoat.build.version}/
webwolf.fileserver.location=${java.io.tmpdir}/webwolf-fileserver webwolf.fileserver.location=${java.io.tmpdir}/webwolf-fileserver
spring.jackson.serialization.indent_output=true spring.jackson.serialization.indent_output=true
spring.jackson.serialization.write-dates-as-timestamps=false spring.jackson.serialization.write-dates-as-timestamps=false