Add ability to change CORS configuration on Che Server through env vars (#12046)

6.19.x
Mykhailo Kuznietsov 2018-12-04 14:56:38 +02:00 committed by GitHub
parent f6f0d69755
commit 3d366a1c19
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 227 additions and 84 deletions

View File

@ -11,6 +11,7 @@
*/
package org.eclipse.che.api.deploy;
import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.inject.matcher.Matchers.subclassesOf;
import static org.eclipse.che.inject.Matchers.names;
import static org.eclipse.che.multiuser.api.permission.server.SystemDomain.SYSTEM_DOMAIN_ACTIONS;
@ -61,6 +62,7 @@ import org.eclipse.che.api.workspace.server.spi.provision.env.JavaOptsEnvVariabl
import org.eclipse.che.api.workspace.server.spi.provision.env.MachineTokenEnvVarProvider;
import org.eclipse.che.api.workspace.server.spi.provision.env.MavenOptsEnvVariableProvider;
import org.eclipse.che.api.workspace.server.spi.provision.env.ProjectsRootEnvVariableProvider;
import org.eclipse.che.api.workspace.server.spi.provision.env.WorkspaceAgentCorsAllowedOriginsEnvVarProvider;
import org.eclipse.che.api.workspace.server.spi.provision.env.WorkspaceAgentJavaOptsEnvVariableProvider;
import org.eclipse.che.api.workspace.server.spi.provision.env.WorkspaceIdEnvVarProvider;
import org.eclipse.che.api.workspace.server.spi.provision.env.WorkspaceMavenServerJavaOptsEnvVariableProvider;
@ -187,6 +189,12 @@ public class WsMasterModule extends AbstractModule {
envVarProviders.addBinding().to(WorkspaceAgentJavaOptsEnvVariableProvider.class);
envVarProviders.addBinding().to(WorkspaceMavenServerJavaOptsEnvVariableProvider.class);
// propagate CORS allowed origin evn variable to WS agent only if corresponding env variable
// is defined on master
if (!isNullOrEmpty(System.getenv("CHE_WSAGENT_CORS_ALLOWED__ORIGINS"))) {
envVarProviders.addBinding().to(WorkspaceAgentCorsAllowedOriginsEnvVarProvider.class);
}
bind(org.eclipse.che.api.workspace.server.bootstrap.InstallerService.class);
bind(org.eclipse.che.api.workspace.server.event.WorkspaceJsonRpcMessenger.class)
.asEagerSingleton();

View File

@ -12,10 +12,7 @@
package org.eclipse.che.api.deploy;
import com.google.inject.servlet.ServletModule;
import java.util.HashMap;
import java.util.Map;
import javax.inject.Singleton;
import org.apache.catalina.filters.CorsFilter;
import org.eclipse.che.api.core.cors.CheCorsFilter;
import org.eclipse.che.commons.logback.filter.RequestIdLoggerFilter;
import org.eclipse.che.inject.DynaModule;
import org.eclipse.che.multiuser.keycloak.server.deploy.KeycloakServletModule;
@ -31,25 +28,10 @@ public class WsMasterServletModule extends ServletModule {
if (Boolean.valueOf(System.getenv("CHE_TRACING_ENABLED"))) {
install(new org.eclipse.che.core.tracing.web.TracingWebModule());
}
if (isCheCorsEnabled()) {
filter("/*").through(CheCorsFilter.class);
}
final Map<String, String> corsFilterParams = new HashMap<>();
corsFilterParams.put("cors.allowed.origins", "*");
corsFilterParams.put(
"cors.allowed.methods", "GET," + "POST," + "HEAD," + "OPTIONS," + "PUT," + "DELETE");
corsFilterParams.put(
"cors.allowed.headers",
"Content-Type,"
+ "X-Requested-With,"
+ "accept,"
+ "Origin,"
+ "Access-Control-Request-Method,"
+ "Access-Control-Request-Headers");
corsFilterParams.put("cors.support.credentials", "true");
// preflight cache is available for 10 minutes
corsFilterParams.put("cors.preflight.maxage", "10");
bind(CorsFilter.class).in(Singleton.class);
filter("/*").through(CorsFilter.class, corsFilterParams);
filter("/*").through(RequestIdLoggerFilter.class);
// Matching group SHOULD contain forward slash.
@ -67,6 +49,16 @@ public class WsMasterServletModule extends ServletModule {
}
}
private boolean isCheCorsEnabled() {
String cheCorsEnabledEnvVar = System.getenv("CHE_CORS_ENABLED");
if (cheCorsEnabledEnvVar == null) {
// by default CORS should be enabled
return true;
} else {
return Boolean.valueOf(cheCorsEnabledEnvVar);
}
}
private void configureSingleUserMode() {
filter("/*").through(org.eclipse.che.api.local.filters.EnvironmentInitializationFilter.class);
}

View File

@ -553,3 +553,16 @@ che.core.jsonrpc.processor_max_pool_size=100
## Port the the http server endpoint that would be exposed with Prometheus metrics
che.metrics.port=8087
# CORS settings
# CORS filter on WS Master is turned on by default.
# Use environment variable "CHE_CORS_ENABLED=false" to turn it off
# "cors.allowed.origins" indicates which request origins are allowed
che.cors.allowed_origins=*
# "cors.support.credentials" indicates if it allows processing of requests with credentials
# (in cookies, headers, TLS client certificates)
che.cors.allow_credentials=true
# This property is used to provide value for WS Agent CORS allowed origins env variable from WS Master,
# as it allows the automated initialization of preferred CORS configuration, if property value is
# set to WS Master domain.
che.wsagent.cors.allowed_origins=

View File

@ -11,46 +11,44 @@
*/
package org.eclipse.che.api.core.cors;
import static org.apache.catalina.filters.CorsFilter.DEFAULT_ALLOWED_ORIGINS;
import static org.apache.catalina.filters.CorsFilter.PARAM_CORS_ALLOWED_HEADERS;
import static org.apache.catalina.filters.CorsFilter.PARAM_CORS_ALLOWED_METHODS;
import static org.apache.catalina.filters.CorsFilter.PARAM_CORS_ALLOWED_ORIGINS;
import static org.apache.catalina.filters.CorsFilter.PARAM_CORS_EXPOSED_HEADERS;
import static org.apache.catalina.filters.CorsFilter.PARAM_CORS_PREFLIGHT_MAXAGE;
import static org.apache.catalina.filters.CorsFilter.PARAM_CORS_SUPPORT_CREDENTIALS;
import com.google.inject.Singleton;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import javax.inject.Inject;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.catalina.filters.CorsFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The special filter which provides filtering requests in according to settings which are set to
* {@link CorsFilter}. More information about filter and parameters you can find in documentation.
* The class contains business logic which allows to get allowed origin from any endpoint as it is
* used by export workspace.
* {@link CorsFilter}. Uses {@link CheCorsFilterConfig} for providing configuration.
*
* @author Dmitry Shnurenko
* @author Mykhailo Kuznietsov
*/
@Singleton
public class CheCorsFilter implements Filter {
private static final Logger LOG = LoggerFactory.getLogger(CheCorsFilter.class);
private CorsFilter corsFilter;
@Inject private CheCorsFilterConfig cheCorsFilterConfig;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
corsFilter = new CorsFilter();
corsFilter.init(new CheCorsFilterConfig());
corsFilter.init(cheCorsFilterConfig);
LOG.debug(
"CORS initialized with parameters: 'cors.support.credentials': '{}', 'cors.allowed.origins': '{}'",
cheCorsFilterConfig.getInitParameter("cors.support.credentials"),
cheCorsFilterConfig.getInitParameter("cors.allowed.origins"));
}
@Override
@ -64,50 +62,4 @@ public class CheCorsFilter implements Filter {
public void destroy() {
corsFilter.destroy();
}
private class CheCorsFilterConfig implements FilterConfig {
private final Map<String, String> filterParams;
public CheCorsFilterConfig() {
filterParams = new HashMap<>();
filterParams.put(PARAM_CORS_ALLOWED_ORIGINS, DEFAULT_ALLOWED_ORIGINS);
filterParams.put(
PARAM_CORS_ALLOWED_METHODS, "GET," + "POST," + "HEAD," + "OPTIONS," + "PUT," + "DELETE");
filterParams.put(
PARAM_CORS_ALLOWED_HEADERS,
"Content-Type,"
+ "X-Requested-With,"
+ "X-Oauth-Token,"
+ "accept,"
+ "Origin,"
+ "Authorization,"
+ "Access-Control-Request-Method,"
+ "Access-Control-Request-Headers");
filterParams.put(PARAM_CORS_EXPOSED_HEADERS, "JAXRS-Body-Provided");
filterParams.put(PARAM_CORS_SUPPORT_CREDENTIALS, "true");
// preflight cache is available for 10 minutes
filterParams.put(PARAM_CORS_PREFLIGHT_MAXAGE, "10");
}
@Override
public String getFilterName() {
return getClass().getName();
}
@Override
public ServletContext getServletContext() {
throw new UnsupportedOperationException("The method does not supported in " + getClass());
}
@Override
public String getInitParameter(String key) {
return filterParams.get(key);
}
@Override
public Enumeration<String> getInitParameterNames() {
throw new UnsupportedOperationException("The method does not supported in " + getClass());
}
}
}

View File

@ -0,0 +1,84 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.api.core.cors;
import static org.apache.catalina.filters.CorsFilter.PARAM_CORS_ALLOWED_HEADERS;
import static org.apache.catalina.filters.CorsFilter.PARAM_CORS_ALLOWED_METHODS;
import static org.apache.catalina.filters.CorsFilter.PARAM_CORS_ALLOWED_ORIGINS;
import static org.apache.catalina.filters.CorsFilter.PARAM_CORS_EXPOSED_HEADERS;
import static org.apache.catalina.filters.CorsFilter.PARAM_CORS_PREFLIGHT_MAXAGE;
import static org.apache.catalina.filters.CorsFilter.PARAM_CORS_SUPPORT_CREDENTIALS;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Named;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
/**
* Basic configuration for {@link CheCorsFilter}. Allowed origings and credentials support are
* configurable through properties.
*
* @author Mykhailo Kuznietsov
*/
public class CheCorsFilterConfig implements FilterConfig {
private final Map<String, String> filterParams;
@Inject
public CheCorsFilterConfig(
@Named("che.cors.allow_credentials") boolean allowCredentials,
@Named("che.cors.allowed_origins") String allowedOrigins) {
filterParams = new HashMap<>();
filterParams.put(PARAM_CORS_ALLOWED_ORIGINS, allowedOrigins);
filterParams.put(
PARAM_CORS_ALLOWED_METHODS, "GET," + "POST," + "HEAD," + "OPTIONS," + "PUT," + "DELETE");
filterParams.put(
PARAM_CORS_ALLOWED_HEADERS,
"Content-Type,"
+ "X-Requested-With,"
+ "X-Oauth-Token,"
+ "accept,"
+ "Origin,"
+ "Authorization,"
+ "Access-Control-Request-Method,"
+ "Access-Control-Request-Headers");
filterParams.put(PARAM_CORS_EXPOSED_HEADERS, "JAXRS-Body-Provided");
filterParams.put(PARAM_CORS_SUPPORT_CREDENTIALS, String.valueOf(allowCredentials));
// preflight cache is available for 10 minutes
filterParams.put(PARAM_CORS_PREFLIGHT_MAXAGE, "10");
}
@Override
public String getFilterName() {
return CheCorsFilter.class.getName();
}
@Override
public ServletContext getServletContext() {
throw new UnsupportedOperationException(
"The method is not supported in " + CheCorsFilter.class);
}
@Override
public String getInitParameter(String key) {
return filterParams.get(key);
}
@Override
public Enumeration<String> getInitParameterNames() {
throw new UnsupportedOperationException(
"The method is not supported in " + CheCorsFilter.class);
}
}

View File

@ -86,3 +86,7 @@ data:
{{- if .Values.workspaceSidecarDefaultRamLimit }}
CHE_WORKSPACE_SIDECAR_DEFAULT__MEMORY__LIMIT__MB: {{ .Values.workspaceSidecarDefaultRamLimit }}
{{- end }}
CHE_CORS_ENABLED: "true"
CHE_CORS_ALLOW__CREDENTIALS: "true"
CHE_CORS_ALLOWED__ORIGINS: "*"
CHE_WSAGENT_CORS_ALLOWED__ORIGINS: ""

View File

@ -270,6 +270,26 @@ spec:
configMapKeyRef:
key: CHE_WORKSPACE_NO__PROXY
name: che
- name: CHE_CORS_ALLOW__CREDENTIALS
valueFrom:
configMapKeyRef:
key: CHE_CORS_ALLOW__CREDENTIALS
name: che
- name: CHE_CORS_ALLOWED__ORIGINS
valueFrom:
configMapKeyRef:
key: CHE_CORS_ALLOWED__ORIGINS
name: che
- name: CHE_CORS_ENABLED
valueFrom:
configMapKeyRef:
key: CHE_CORS_ENABLED
name: che
- name: CHE_WSAGENT_CORS_ALLOWED__ORIGINS
valueFrom:
configMapKeyRef:
key: CHE_WSAGENT_CORS_ALLOWED__ORIGINS
name: che
{{- if .Values.workspaceDefaultRamRequest }}
- name: CHE_WORKSPACE_DEFAULT_MEMORY_REQUEST_MB
valueFrom:

View File

@ -161,6 +161,14 @@ objects:
value: "${CHE_TRACING_ENABLED}"
- name: CHE_METRICS_ENABLED
value: "false"
- name: CHE_CORS_ENABLED
value: "${CHE_CORS_ENABLED}"
- name: CHE_CORS_ALLOW__CREDENTIALS
value: "${CHE_CORS_ALLOW__CREDENTIALS}"
- name: CHE_CORS_ALLOWED__ORIGINS
value: "${CHE_CORS_ALLOWED__ORIGINS}"
- name: CHE_WSAGENT_CORS_ALLOWED__ORIGINS
value: "${CHE_WSAGENT_CORS_ALLOWED__ORIGINS}"
image: ${IMAGE_CHE}:${CHE_VERSION}
imagePullPolicy: "${PULL_POLICY}"
livenessProbe:
@ -309,6 +317,22 @@ parameters:
displayName: Eclipse Che tracing
description: Enable or disable tracing in Eclipse Che
value: 'false'
- name: CHE_CORS_ENABLED
displayName: CORS filter for WS Master
description: Enable or disable CORS filter for Eclipse Che WS Master
value: 'true'
- name: CHE_CORS_ALLOW__CREDENTIALS
displayName: CORS credentials support for WS Master
description: Allow requests with credentials for CORS filter
value: 'true'
- name: CHE_CORS_ALLOWED__ORIGINS
displayName: CORS allowed origins for WS Master
description: defines allowed origins in requests for CORS filter
value: '*'
- name: CHE_WSAGENT_CORS_ALLOWED__ORIGINS
displayName: CORS allowed origins for WS Agent
description: defines allowed origins in requests for CORS filter, that will be propagated as corresponding env variable on WS Agent. Only if its value is not null or empty
value: ''
labels:
app: che
template: che

View File

@ -62,3 +62,10 @@ workspace.activity.schedule_period_s=60
# Maximum size of the json processing pool
# in case if pool size would be exceeded message execution will be rejected
che.core.jsonrpc.processor_max_pool_size=100
# CORS settings
# "cors.allowed.origins" indicates which request origins are allowed
che.cors.allowed_origins=*
# "cors.support.credentials" indicates if it allows processing of requests with credentials
# (in cookies, headers, TLS client certificates)
che.cors.allow_credentials=true

View File

@ -0,0 +1,39 @@
/*
* Copyright (c) 2012-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.api.workspace.server.spi.provision.env;
import javax.inject.Inject;
import javax.inject.Named;
import org.eclipse.che.api.core.model.workspace.runtime.RuntimeIdentity;
import org.eclipse.che.api.workspace.server.spi.InfrastructureException;
import org.eclipse.che.commons.lang.Pair;
/**
* Add environment variable that defines allowed origins for {@link CheCorsFilterConfig} of WS Agent
*
* @author Mykhailo Kuznietsov
*/
public class WorkspaceAgentCorsAllowedOriginsEnvVarProvider implements EnvVarProvider {
private String wsAgentCorsAllowedOrigins;
@Inject
public WorkspaceAgentCorsAllowedOriginsEnvVarProvider(
@Named("che.wsagent.cors.allowed_origins") String cheWsMasterAllowedOrigins) {
this.wsAgentCorsAllowedOrigins = cheWsMasterAllowedOrigins;
}
@Override
public Pair<String, String> get(RuntimeIdentity runtimeIdentity) throws InfrastructureException {
return Pair.of("CHE_CORS_ALLOWED__ORIGINS", wsAgentCorsAllowedOrigins);
}
}