Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support deployment behind NGINX reverse proxy #41

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# https://spring.io/guides/topicals/spring-boot-docker#_multi_stage_build
FROM eclipse-temurin:17-jdk-alpine as build
WORKDIR /workspace/app
RUN apk add maven
COPY pom.xml .
COPY maven maven
RUN mvn dependency:resolve -s maven/cnaf-mirror-settings.xml
RUN mvn dependency:resolve-plugins -s maven/cnaf-mirror-settings.xml
COPY .git .git
COPY etc etc
COPY src src
RUN mvn package -s maven/cnaf-mirror-settings.xml -Dmaven.test.skip
RUN mkdir -p target/dependency && (cd target/dependency; jar -xf ../*.jar)

FROM eclipse-temurin:17-jdk-alpine
VOLUME /tmp
ARG DEPENDENCY=/workspace/app/target/dependency
COPY --from=build ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY --from=build ${DEPENDENCY}/META-INF /app/META-INF
COPY --from=build ${DEPENDENCY}/BOOT-INF/classes /app
COPY src src
EXPOSE 8086
ENTRYPOINT ["java","-Dspring.profiles.active=dev","-cp","app:app/lib/*","org.italiangrid.storm.webdav.WebdavService"]
1 change: 1 addition & 0 deletions build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
podman build -t storm-webdav .
33 changes: 33 additions & 0 deletions compose-nginx/assets/nginx/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
user nobody;
worker_processes 1;

error_log /var/log/nginx/error.log debug;

load_module modules/ngx_http_voms_module.so;

events {
worker_connections 1024;
}

http {
include mime.types;
default_type application/octet-stream;

resolver 127.0.0.11 ipv6=off;

log_format storm '$time_iso8601 [$request_id] $remote_addr - $remote_user "$request" <$upstream_response_time> '
'$ssl_protocol/$ssl_cipher '
'"$ssl_client_s_dn" '
'[$voms_fqans] '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

access_log /var/log/nginx/access.log storm;

sendfile on;
#tcp_nopush on;

keepalive_timeout 65;

include /etc/nginx/conf.d/*.conf;
}
45 changes: 45 additions & 0 deletions compose-nginx/assets/nginx/storm.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
server {
location /internal {
internal;
alias /src/test/resources/storage/nginx;
}
location / {
proxy_pass http://storm-webdav:8086;
proxy_set_header Host $http_host;
proxy_set_header X-VOMS-voms_user $voms_user;
proxy_set_header X-VOMS-ssl_client_ee_s_dn $ssl_client_ee_s_dn;
proxy_set_header X-VOMS-voms_user_ca $voms_user_ca;
proxy_set_header X-VOMS-ssl_client_ee_i_dn $ssl_client_ee_i_dn;
proxy_set_header X-VOMS-voms_fqans $voms_fqans;
proxy_set_header X-VOMS-voms_server $voms_server;
proxy_set_header X-VOMS-voms_server_ca $voms_server_ca;
proxy_set_header X-VOMS-voms_vo $voms_vo;
proxy_set_header X-VOMS-voms_server_uri $voms_server_uri;
proxy_set_header X-VOMS-voms_not_before $voms_not_before;
proxy_set_header X-VOMS-voms_not_after $voms_not_after;
proxy_set_header X-VOMS-voms_generic_attributes $voms_generic_attributes;
proxy_set_header X-VOMS-voms_serial $voms_serial;
proxy_redirect off;
}
listen [::]:8443 ssl http2;
listen 8443 ssl http2;
ssl_certificate /etc/grid-security/hostcert.pem;
ssl_certificate_key /etc/grid-security/hostkey.pem;
ssl_client_certificate /etc/pki/ca-trust/extracted/pem/tls-ca-bundle-all.pem;
ssl_verify_client optional;
ssl_verify_depth 10;
error_page 497 https://$host:8443$request_uri; # https://ma.ttias.be/force-redirect-http-https-custom-port-nginx/#forcing-https-redirects-on-non-standard-ports
}
server {
location /internal {
internal;
alias /src/test/resources/storage/nginx;
}
location / {
proxy_pass http://127.0.0.1:8086;
proxy_set_header Host $http_host;
proxy_redirect off;
}
listen [::]:8085;
listen 8085;
}
2 changes: 2 additions & 0 deletions compose-nginx/assets/wlcg/wlcg-voms.cloud.cnaf.infn.it.lsc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/DC=org/DC=terena/DC=tcs/C=IT/ST=Roma/O=Istituto Nazionale di Fisica Nucleare/CN=wlcg-voms.cloud.cnaf.infn.it
/C=NL/O=GEANT Vereniging/CN=GEANT eScience SSL CA 4
31 changes: 31 additions & 0 deletions compose-nginx/compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
services:
trust:
image: indigoiam/egi-trustanchors:igi-test-ca
volumes:
- trustanchors:/etc/grid-security/certificates
- cabundle:/etc/pki
environment:
- FORCE_TRUST_ANCHORS_UPDATE=1
nginx-httpg-voms:
image: cnafsd/nginx-httpg-voms
volumes:
- /etc/grid-security/hostcert.pem:/etc/grid-security/hostcert.pem:ro
- /etc/grid-security/hostkey.pem:/etc/grid-security/hostkey.pem:ro
- trustanchors:/etc/grid-security/certificates
- cabundle:/etc/pki
- ../src:/src:z
- ./assets/nginx/nginx.conf:/etc/nginx/nginx.conf:z
- ./assets/nginx/storm.conf:/etc/nginx/conf.d/storm.conf:z
- ./assets/wlcg:/etc/grid-security/vomsdir/wlcg:z
ports:
- "8443:8443"
# to wait the trust service to finish
restart: on-failure
storm-webdav:
build:
context: ../
dockerfile: Dockerfile

volumes:
trustanchors:
cabundle:
3 changes: 3 additions & 0 deletions compose-nginx/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env bash
podman-compose up trust
podman-compose up -d
15 changes: 15 additions & 0 deletions nginx.repo
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true

[nginx-mainline]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
gpgcheck=1
enabled=0
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true
1 change: 1 addition & 0 deletions run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
podman stop storm-webdav; podman rm storm-webdav; podman run -d -p 8085:8085 -p 8443:8443 -p 8086:8086 --name storm-webdav localhost/storm-webdav:latest
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* Copyright (c) Istituto Nazionale di Fisica Nucleare, 2014-2023.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://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.
*/
package org.italiangrid.storm.webdav.authz;

import javax.security.auth.x500.X500Principal;
import javax.servlet.http.HttpServletRequest;

import org.springframework.security.authentication.AuthenticationManager;

public class VOMSNginxFilter extends VOMSAuthenticationFilter {

public VOMSNginxFilter(AuthenticationManager mgr) {
super(mgr);
}

@Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
if (request.getHeader("X-VOMS-voms_user") != null) {
return new X500Principal(request.getHeader("X-VOMS-ssl_client_ee_s_dn")).getName();
}
return null;
}

@Override
protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
return new Object();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,28 @@
*/
package org.italiangrid.storm.webdav.authz;

import java.math.BigInteger;
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.text.ParseException;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.regex.Matcher;

import javax.security.auth.x500.X500Principal;
import javax.servlet.http.HttpServletRequest;

import org.italiangrid.storm.webdav.authz.vomap.VOMapDetailsService;
import org.italiangrid.voms.VOMSAttribute;
import org.italiangrid.voms.VOMSGenericAttribute;
import org.italiangrid.voms.ac.VOMSACValidator;
import org.italiangrid.voms.ac.impl.VOMSAttributesImpl;
import org.italiangrid.voms.ac.impl.VOMSGenericAttributeImpl;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.core.GrantedAuthority;

Expand All @@ -41,12 +50,14 @@ public class VOMSPreAuthDetailsSource
private final AuthorizationPolicyService policyService;
private final VOMSACValidator validator;
private final VOMapDetailsService voMapDetailsService;
private final boolean nginxReverseProxy;

public VOMSPreAuthDetailsSource(VOMSACValidator vomsValidator,
AuthorizationPolicyService policyService, VOMapDetailsService voMapDetailsService) {
AuthorizationPolicyService policyService, VOMapDetailsService voMapDetailsService, boolean nginxReverseProxy) {
this.policyService = policyService;
this.validator = vomsValidator;
this.voMapDetailsService = voMapDetailsService;
this.nginxReverseProxy = nginxReverseProxy;
}

@Override
Expand Down Expand Up @@ -116,6 +127,45 @@ protected Optional<X509SubjectAuthority> getSubjectAuthority(HttpServletRequest
}

protected List<VOMSAttribute> getAttributes(HttpServletRequest request) {
if (nginxReverseProxy) {
VOMSAttributesImpl attrs = new VOMSAttributesImpl();
attrs.setVO(request.getHeader("X-VOMS-voms_vo"));
if (request.getHeader("X-VOMS-voms_user_ca") != null) {
attrs.setIssuer(new X500Principal(request.getHeader("X-VOMS-ssl_client_ee_i_dn"))); // ???
}
attrs.setFQANs(Arrays.asList(request.getHeader("X-VOMS-voms_fqans").split(",")));
attrs.setHost(request.getHeader("X-VOMS-voms_server_uri").split(":")[0]);
attrs.setPort(Integer.parseInt(request.getHeader("X-VOMS-voms_server_uri").split(":")[1]));
if (request.getHeader("X-VOMS-voms_user") != null) {
attrs.setHolder(new X500Principal(request.getHeader("X-VOMS-ssl_client_ee_s_dn"))); // ???
}
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmss'Z'");
try {
attrs.setNotAfter(simpleDateFormat.parse(request.getHeader("X-VOMS-voms_not_after")));
attrs.setNotBefore(simpleDateFormat.parse(request.getHeader("X-VOMS-voms_not_before")));
} catch (ParseException e) {}
//attrs.setSignature();
if (request.getHeader("X-VOMS-voms_generic_attributes") != null) {
List<VOMSGenericAttribute> generic_attrs = Collections.emptyList();
Pattern pattern = Pattern.compile("n=(\\S*) v=(\\S*) q=(\\S*)");
for (String genericAttribute : request.getHeader("X-VOMS-voms_generic_attributes").split(",")) {
Matcher matcher = pattern.matcher(genericAttribute);
if (matcher.find()) {
VOMSGenericAttributeImpl generic_attr = new VOMSGenericAttributeImpl();
generic_attr.setName(matcher.group(1));
generic_attr.setValue(matcher.group(2));
generic_attr.setContext(matcher.group(3));
generic_attrs.add(generic_attr);
}
}
attrs.setGenericAttributes(generic_attrs);
}
//attrs.setTargets();
//attrs.setAACertificates();
//attrs.setVOMSAC();
attrs.setHolderSerialNumber(new BigInteger(request.getHeader("X-VOMS-voms_serial"), 16));
return List.of(attrs);
}

Optional<X509Certificate[]> chain = Utils.getCertificateChainFromRequest(request);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,8 @@ public void setTrustStore(VOMSTrustStoreProperties trustStore) {

private ChecksumStrategy checksumStrategy = ChecksumStrategy.EARLY;

private boolean nginxReverseProxy = false;

private BufferProperties buffer;

private RedirectorProperties redirector;
Expand Down Expand Up @@ -843,6 +845,14 @@ public void setChecksumStrategy(ChecksumStrategy checksumStrategy) {
this.checksumStrategy = checksumStrategy;
}

public boolean getNginxReverseProxy() {
return nginxReverseProxy;
}

public void setNginxReverseProxy(boolean nginxReverseProxy) {
this.nginxReverseProxy = nginxReverseProxy;
}


@Override
public boolean useConscrypt() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,12 @@ public Resource getResource(String pathInContext) {

}

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
resourceService.doGet(request, response, pathResolver, serviceConfig);
}

@Override
protected void doHead(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@
*/
package org.italiangrid.storm.webdav.server.servlet.resource;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
Expand All @@ -26,6 +28,9 @@
import org.eclipse.jetty.server.ResourceService;
import org.eclipse.jetty.util.URIUtil;

import org.italiangrid.storm.webdav.config.ServiceConfigurationProperties;
import org.italiangrid.storm.webdav.server.PathResolver;

public class StormResourceService extends ResourceService {

private String pathInContext(HttpServletRequest request) {
Expand Down Expand Up @@ -73,4 +78,19 @@ public boolean doHead(HttpServletRequest request, HttpServletResponse response)
return true;
}

public boolean doGet(HttpServletRequest request, HttpServletResponse response, PathResolver resolver, ServiceConfigurationProperties serviceConfig)
throws ServletException, IOException {

if (serviceConfig.getNginxReverseProxy()) {
final String pathInContext = pathInContext(request);
String resolvedPath = resolver.resolvePath(pathInContext);
File f = new File(resolvedPath);
if (f.isFile()) {
response.setHeader("X-Accel-Redirect", "/internal" + pathInContext);
return response.isCommitted();
}
}
return super.doGet(request, response);
}

}
Loading
Loading