Skip to content

Commit

Permalink
Merge pull request #1135 from eclipse-tractusx/feature/786-additional…
Browse files Browse the repository at this point in the history
…-port-for-internal-requests

feature(chore):786 added alternative port for internal access only.
  • Loading branch information
ds-mwesener authored Jul 2, 2024
2 parents b46661c + 0a9b9c7 commit ce6b15c
Show file tree
Hide file tree
Showing 16 changed files with 253 additions and 16 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ _**For better traceability add the corresponding GitHub issue number in each cha
- #985 Added function to filter notifications for contractAgreementIds
- #786 Added authorization as admin for submodel api & registry api
- #884 Upgraded tractionBatteryCode from 1.0.0 to 2.0.0
- #786 Added alternative port (only accessible within same cluster) for application which is used for unsecured API endpoints.

### Added
- #832 added policymanagement list view, creator and editor
Expand All @@ -29,6 +30,11 @@ _**For better traceability add the corresponding GitHub issue number in each cha
- #985 Added reference to part/notification under contract
- #786 Added icons on part table to let admin reload registry / sync assets via IRS

### Known knowns

- #786 Implemented short term solution for securing EDC Callback APIs


### Removed

- XXX Removed EdcNotifiactionMockServiceImpl class and replaced with mocks
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ spec:
value: {{ .Values.config.allowedCorsOriginSecond | quote }}
- name: EDC_DATA_ENDPOINT_URL
value: {{ .Values.edc.dataEndpointUrl | quote }}
- name: TRUSTED_PORT
value: {{ .Values.config.trustedPort | quote }}
- name: DISCOVERY_FINDER_URL_WITH_PATH
value: {{ .Values.discoveryfinder.baseUrl | quote }}
- name: JWT_RESOURCE_CLIENT
Expand Down Expand Up @@ -157,6 +159,9 @@ spec:
- name: metrics
containerPort: 8081
protocol: TCP
- name: http-trusted
containerPort: 8181
protocol: TCP
# @url: https://cloud.google.com/blog/products/containers-kubernetes/kubernetes-best-practices-setting-up-health-checks-with-readiness-and-liveness-probes
{{- if .Values.healthCheck.enabled }}
livenessProbe:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,9 @@ spec:
targetPort: {{ .Values.service.port }}
protocol: TCP
name: http
- port: {{ .Values.service.trustedPort }}
targetPort: {{ .Values.service.trustedPort }}
protocol: TCP
name: http-trusted
selector:
{{- include "traceability-foss-backend.selectorLabels" . | nindent 4 }}
2 changes: 2 additions & 0 deletions charts/traceability-foss/charts/backend/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ securityContext:
service:
type: ClusterIP
port: 8080
trustedPort: 8181

autoscaling:
enabled: false
Expand Down Expand Up @@ -176,6 +177,7 @@ portal:
config:
allowedCorsOriginFirst: "https://replace.me"
allowedCorsOriginSecond: "https://replace.me"
trustedPort: 8181

dependencies:
enabled: false
Expand Down
1 change: 1 addition & 0 deletions charts/traceability-foss/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ backend:
service:
type: ClusterIP
port: 8080
trustedPort: 8181

autoscaling:
enabled: false
Expand Down
7 changes: 7 additions & 0 deletions docs/src/docs/arc42/cross-cutting/safety-security.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ This component requires authentication via a Verifiable Credential (VC), which i

The VC identifies and authenticates the EDC and is used to acquire access permissions for the data transferred via EDC.

=== Trusted Port for Internal APIs

A second port, called the trusted port, has been introduced which can only be accessed by internal services within the Kubernetes cluster. This measure is implemented to handle APIs that are difficult to secure and involve several systems and processes. The trusted port ensures that only internal, trusted components can access these sensitive APIs, enhancing overall security.

- Quality notification callback APIs (receive, update) - called by EDC Dataplane
- Endpoint Data Reference callback API - called by EDC Controlplane

== Credentials

Credentials must never be stored in Git!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@
[matTooltipShowDelay]="500"
[matTooltipDisabled]="isAdmin() && atLeastOneSelected() && !isIllegalSelectionToPublish()">
<app-button
iconName="published_with_changes"
iconName="upload"
class="action-button-tile"
matTooltip="{{'actions.publishAssets'| i18n}}"
[matTooltipShowDelay]="500"
Expand All @@ -104,7 +104,7 @@
[class.mdc-tooltip--multiline]="true"
[matTooltipShowDelay]="500">
<app-button
iconName="upload"
iconName="published_with_changes"
class="action-button-tile"
[isDisabled]="!isAdmin()"
(click)="partReloadIconClicked()"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
@EnableJpaRepositories(basePackages = "org.eclipse.tractusx.traceability.*")
public class ApplicationConfig {

public static final String CONTEXT_PATH = "/api";
public static final String INTERNAL_ENDPOINT = "/internal";

@Bean
public InternalResourceViewResolver defaultViewResolver() {
return new InternalResourceViewResolver();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,8 @@ public class SecurityConfig {
"/v3/api-docs/**",
"/swagger-ui/**",
"/webjars/swagger-ui/**",
"/qualitynotifications/receive",
"/qualityalerts/receive",
"/qualitynotifications/update",
"/qualityalerts/update",
"/callback/endpoint-data-reference",
"/internal/endpoint-data-reference",
"/internal/**",
"/api/internal/**",
"/actuator/**",
"/irs/job/callback"

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/********************************************************************************
* Copyright (c) 2024 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
********************************************************************************/
package org.eclipse.tractusx.traceability.common.config;

import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequestWrapper;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponseWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.tractusx.irs.common.ApiConstants;
import org.springframework.context.annotation.Profile;

import java.io.IOException;

import static org.eclipse.tractusx.traceability.common.config.ApplicationProfiles.NOT_INTEGRATION_TESTS;

@Profile(NOT_INTEGRATION_TESTS)
@Slf4j
public class TrustedEndpointsFilter implements Filter {
private int trustedPortNum;

/* package */ TrustedEndpointsFilter(final String trustedPort) {
try {
if (StringUtils.isNotEmpty(trustedPort)) {
trustedPortNum = Integer.parseInt(trustedPort);
} else {
trustedPortNum = 0;
}
} catch (NumberFormatException e) {
trustedPortNum = 0;
}
}

@Override
public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse,
final FilterChain filterChain) throws IOException, ServletException {
if (trustedPortNum != 0) {

if (isRequestForTrustedEndpoint(servletRequest) && servletRequest.getLocalPort() != trustedPortNum) {
log.warn("denying request for trusted endpoint on untrusted port");
if (servletResponse instanceof HttpServletResponseWrapper httpServletResponse) {
httpServletResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
}
servletResponse.getOutputStream().close();
return;
}

if (!isRequestForTrustedEndpoint(servletRequest) && servletRequest.getLocalPort() == trustedPortNum) {
log.warn("denying request for untrusted endpoint on trusted port");
if (servletResponse instanceof HttpServletResponseWrapper httpServletResponse) {
httpServletResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
}
servletResponse.getOutputStream().close();
return;
}
}

filterChain.doFilter(servletRequest, servletResponse);
}

private boolean isRequestForTrustedEndpoint(final ServletRequest servletRequest) {
log.warn(((HttpServletRequestWrapper) servletRequest).getRequestURI());
return ((HttpServletRequestWrapper) servletRequest).getRequestURI()
.startsWith(ApplicationConfig.CONTEXT_PATH + ApplicationConfig.INTERNAL_ENDPOINT);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/********************************************************************************
* Copyright (c) 2024 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
********************************************************************************/
package org.eclipse.tractusx.traceability.common.config;

import org.apache.catalina.connector.Connector;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.autoconfigure.web.servlet.TomcatServletWebServerFactoryCustomizer;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import static org.eclipse.tractusx.traceability.common.config.ApplicationProfiles.NOT_INTEGRATION_TESTS;

/**
* Configures the trusted port
*/
@Profile(NOT_INTEGRATION_TESTS)
@Configuration
public class TrustedPortConfiguration {
private final String serverPort;

private final String managementPort;

private final String trustedPort;

public TrustedPortConfiguration(@Value("${server.port:8080}") final String serverPort,
@Value("${management.server.port:${server.port:8080}}") final String managementPort,
@Value("${server.trustedPort}") final String trustedPort) {

this.serverPort = serverPort;
this.managementPort = managementPort;
this.trustedPort = trustedPort;
}

@Bean
public WebServerFactoryCustomizer<TomcatServletWebServerFactory> servletContainer() {

final Connector[] additionalConnectors = this.additionalConnector();

final ServerProperties serverProperties = new ServerProperties();
serverProperties.getServlet().setContextPath(ApplicationConfig.CONTEXT_PATH);
return new TomcatMultiConnectorServletWebServerFactoryCustomizer(serverProperties, additionalConnectors);
}

private Connector[] additionalConnector() {

if (StringUtils.isEmpty(this.trustedPort)) {
return new Connector[0];
}

final Set<String> defaultPorts = new HashSet<>();
defaultPorts.add(serverPort);
defaultPorts.add(managementPort);

if (defaultPorts.contains(trustedPort)) {
return new Connector[0];
} else {
final Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
connector.setScheme("http");
try {
connector.setPort(Integer.parseInt(trustedPort));
} catch (final NumberFormatException e) {
connector.setPort(0);
}
return new Connector[] { connector };
}
}

/**
* Customizer for additional connectors
*/
private static class TomcatMultiConnectorServletWebServerFactoryCustomizer
extends TomcatServletWebServerFactoryCustomizer {
private final Connector[] additionalConnectors;

/* package */ TomcatMultiConnectorServletWebServerFactoryCustomizer(final ServerProperties serverProperties,
final Connector... additionalConnectors) {
super(serverProperties);
serverProperties.getServlet().setContextPath(ApplicationConfig.CONTEXT_PATH);
this.additionalConnectors = Arrays.copyOf(additionalConnectors, additionalConnectors.length);
}

@Override
public void customize(final TomcatServletWebServerFactory factory) {
super.customize(factory);

if (additionalConnectors != null && additionalConnectors.length > 0) {
factory.addAdditionalTomcatConnectors(additionalConnectors);
}
}
}

@Bean
public FilterRegistrationBean<TrustedEndpointsFilter> trustedEndpointsFilter() {
return new FilterRegistrationBean<>(new TrustedEndpointsFilter(trustedPort));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
********************************************************************************/
package org.eclipse.tractusx.traceability.notification.domain.contract;

import org.eclipse.tractusx.traceability.common.config.ApplicationConfig;
import policies.response.PolicyResponse;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand Down Expand Up @@ -58,8 +59,8 @@ public class EdcNotificationContractService {
private final PolicyService policyService;


private static final String TRACE_FOSS_QUALITY_NOTIFICATION_INVESTIGATION_URL_TEMPLATE = "/api/qualitynotifications/%s";
private static final String TRACE_FOSS_QUALITY_NOTIFICATION_ALERT_URL_TEMPLATE = "/api/qualityalerts/%s";
private static final String TRACE_FOSS_QUALITY_NOTIFICATION_INVESTIGATION_URL_TEMPLATE = ApplicationConfig.CONTEXT_PATH + ApplicationConfig.INTERNAL_ENDPOINT + "/qualitynotifications/%s";
private static final String TRACE_FOSS_QUALITY_NOTIFICATION_ALERT_URL_TEMPLATE = ApplicationConfig.CONTEXT_PATH + ApplicationConfig.INTERNAL_ENDPOINT + "/qualityalerts/%s";

public CreateNotificationContractResponse handle(CreateNotificationContractRequest request) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import static org.eclipse.tractusx.traceability.common.model.SecurityUtils.sanitize;
Expand All @@ -47,6 +48,7 @@
@RestController
@Validated
@RequiredArgsConstructor
@RequestMapping(path = "/internal")
public class EdcController {

private final NotificationReceiverService notificationReceiverService;
Expand Down
1 change: 1 addition & 0 deletions tx-backend/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ irs-edc-client:


server:
trustedPort: ${TRUSTED_PORT}
servlet:
context-path: /api

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ void testHandle() throws CreateEdcAssetException, CreateEdcPolicyDefinitionExcep
assertThat(accessPolicyId).isEqualTo(response.accessPolicyId());
assertThat(contractDefinitionId).isEqualTo(response.contractDefinitionId());
verify(edcNotificationAssetService).createNotificationAsset(
"https://test/api/qualitynotifications/resolve",
"https://test/api/internal/qualitynotifications/resolve",
"QUALITY_INVESTIGATION RESOLVE",
org.eclipse.tractusx.irs.edc.client.asset.model.NotificationMethod.RESOLVE,
org.eclipse.tractusx.irs.edc.client.asset.model.NotificationType.QUALITY_INVESTIGATION);
Expand Down
Loading

0 comments on commit ce6b15c

Please sign in to comment.