Skip to content

Commit

Permalink
fix: Repair CORS support in Snap NGINX (#4644)
Browse files Browse the repository at this point in the history
Closes #4627

Signed-off-by: Bryon Nevis <bryon.nevis@intel.com>
  • Loading branch information
bnevis-i authored Aug 25, 2023
1 parent d712b70 commit 541cc85
Show file tree
Hide file tree
Showing 6 changed files with 260 additions and 146 deletions.
2 changes: 1 addition & 1 deletion cmd/security-proxy-setup/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ fi
: ${EDGEX_SERVICE_CORSCONFIGURATION_CORSMAXAGE:=`yq -r .all-services.Service.CORSConfiguration.CORSMaxAge /edgex/res/common_configuration.yaml`}

echo "$(date) CORS settings dump ..."
( env | grep EDGEX_SERVICE_CORSCONFIGURATION ) || true
( set | grep EDGEX_SERVICE_CORSCONFIGURATION ) || true

corssnippet=/etc/nginx/templates/cors.block.$$
touch "${corssnippet}"
Expand Down
1 change: 1 addition & 0 deletions snap/local/helper-go/configure.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ func configure() {
supportScheduler,
securitySecretStoreSetup,
securityBootstrapper, // local executable
securityBootstrapperNginx,
securityProxyAuth,
)
if err != nil {
Expand Down
254 changes: 254 additions & 0 deletions snap/local/runtime-helpers/bin/security-bootstrapper-nginx
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
#!/bin/bash

keyfile=nginx.key
certfile=nginx.crt

# Check for default TLS certificate for reverse proxy, create if missing
# Normally we would run the below command in the nginx container itself,
# but nginx:alpine-slim does not container openssl, thus run it here instead.
mkdir -p "${SNAP_DATA}/nginx"
cd "${SNAP_DATA}/nginx"
if test ! -f "${keyfile}" ; then
# (NGINX will restart in a failure loop until a TLS key exists)
# Create default TLS certificate with 1 day expiry -- user must replace in production (do this as nginx user)
openssl req -x509 -nodes -days 1 -newkey ec -pkeyopt ec_paramgen_curve:secp384r1 -subj '/CN=localhost/O=EdgeX Foundry' -keyout "${keyfile}" -out "${certfile}" -addext "keyUsage = digitalSignature, keyCertSign" -addext "extendedKeyUsage = serverAuth"
echo "Default TLS certificate created. Recommend replace with your own."
fi


#
# Import CORS configuration from common config
#

: ${EDGEX_SERVICE_CORSCONFIGURATION_ENABLECORS:=`yq -r .all-services.Service.CORSConfiguration.EnableCORS $SNAP_DATA/config/core-common-config-bootstrapper/res/configuration.yaml`}
: ${EDGEX_SERVICE_CORSCONFIGURATION_CORSALLOWCREDENTIALS:=`yq -r .all-services.Service.CORSConfiguration.CORSAllowCredentials $SNAP_DATA/config/core-common-config-bootstrapper/res/configuration.yaml`}
: ${EDGEX_SERVICE_CORSCONFIGURATION_CORSALLOWEDORIGIN:=`yq -r .all-services.Service.CORSConfiguration.CORSAllowedOrigin $SNAP_DATA/config/core-common-config-bootstrapper/res/configuration.yaml`}
: ${EDGEX_SERVICE_CORSCONFIGURATION_CORSALLOWEDMETHODS:=`yq -r .all-services.Service.CORSConfiguration.CORSAllowedMethods $SNAP_DATA/config/core-common-config-bootstrapper/res/configuration.yaml`}
: ${EDGEX_SERVICE_CORSCONFIGURATION_CORSALLOWEDHEADERS:=`yq -r .all-services.Service.CORSConfiguration.CORSAllowedHeaders $SNAP_DATA/config/core-common-config-bootstrapper/res/configuration.yaml`}
: ${EDGEX_SERVICE_CORSCONFIGURATION_CORSEXPOSEHEADERS:=`yq -r .all-services.Service.CORSConfiguration.CORSExposeHeaders $SNAP_DATA/config/core-common-config-bootstrapper/res/configuration.yaml`}
: ${EDGEX_SERVICE_CORSCONFIGURATION_CORSMAXAGE:=`yq -r .all-services.Service.CORSConfiguration.CORSMaxAge $SNAP_DATA/config/core-common-config-bootstrapper/res/configuration.yaml`}

echo "$(date) CORS settings dump ..."
( set | grep EDGEX_SERVICE_CORSCONFIGURATION ) || true

corssnippet=/tmp/cors.block.$$
touch "${corssnippet}"
if test "${EDGEX_SERVICE_CORSCONFIGURATION_ENABLECORS}" = "true"; then
echo " if (\$request_method = 'OPTIONS') {" >> "${corssnippet}"
echo " add_header 'Access-Control-Allow-Origin' '${EDGEX_SERVICE_CORSCONFIGURATION_CORSALLOWEDORIGIN}';" >> "${corssnippet}"
echo " add_header 'Access-Control-Allow-Methods' '${EDGEX_SERVICE_CORSCONFIGURATION_CORSALLOWEDMETHODS}';" >> "${corssnippet}"
echo " add_header 'Access-Control-Allow-Headers' '${EDGEX_SERVICE_CORSCONFIGURATION_CORSALLOWEDHEADERS}';" >> "${corssnippet}"
# Access-Control-Expose-Headers should not be set on OPTIONS request
if test "${EDGEX_SERVICE_CORSCONFIGURATION_CORSALLOWCREDENTIALS}" = "true"; then
# CORS specificaiton says that if not true, omit the header entirely
echo " add_header 'Access-Control-Allow-Credentials' '${EDGEX_SERVICE_CORSCONFIGURATION_CORSALLOWCREDENTIALS}';" >> "${corssnippet}"
fi
echo " add_header 'Access-Control-Max-Age' ${EDGEX_SERVICE_CORSCONFIGURATION_CORSMAXAGE};" >> "${corssnippet}"
echo " add_header 'Vary' 'origin';" >> "${corssnippet}"
echo " add_header 'Content-Type' 'text/plain; charset=utf-8';" >> "${corssnippet}"
echo " add_header 'Content-Length' 0;" >> "${corssnippet}"
echo " return 204;" >> "${corssnippet}"
echo " }" >> "${corssnippet}"
echo " if (\$request_method != 'OPTIONS') {" >> "${corssnippet}"
# Always add headers regardless of response code. Omit preflight-related headers (allow-methods, allow-headers, allow-credentials, max-age)
echo " add_header 'Access-Control-Allow-Origin' '${EDGEX_SERVICE_CORSCONFIGURATION_CORSALLOWEDORIGIN}' always;" >> "${corssnippet}"
echo " add_header 'Access-Control-Expose-Headers' '${EDGEX_SERVICE_CORSCONFIGURATION_CORSEXPOSEHEADERS}' always;" >> "${corssnippet}"
echo " add_header 'Vary' 'origin' always;" >> "${corssnippet}"
echo " }" >> "${corssnippet}"
echo "" >> "${corssnippet}"
fi

#
# Generate NGINX configuration based on EDGEX_ADD_PROXY_ROUTE and standard settings
#

echo "$(date) Generating default NGINX config ..."

IFS=', '
for service in ${EDGEX_ADD_PROXY_ROUTE}; do
prefix=$(echo -n "${service}" | sed -n -e 's/\([-0-9a-zA-Z]*\)\..*/\1/p')
host=$(echo -n "${service}" | sed -n -e 's/.*\/\/\([-0-9a-zA-Z]*\):.*/\1/p')
port=$(echo -n "${service}" | sed -n -e 's/.*:\(\d*\)/\1/p')
varname=$(echo -n "${prefix}" | tr '-' '_')
echo $service $prefix $host $port
cat <<EOH >> "${SNAP_DATA}/nginx/conf.d/generated-routes.inc"
set \$upstream_$varname $host;
location /$prefix {
`cat "${corssnippet}"`
rewrite /$prefix/(.*) /\$1 break;
resolver 127.0.0.11 valid=30s;
proxy_pass http://\$upstream_$varname:$port;
proxy_redirect off;
proxy_set_header Host \$host;
auth_request /auth;
auth_request_set \$auth_status \$upstream_status;
}
EOH

done
unset IFS


# This file can be modified by the user; deleted when docker volumes are pruned;
# but preserved across start/up and stop/down actions
if test -f "${SNAP_DATA}/nginx/conf.d/edgex-custom-rewrites.inc"; then
echo "Using existing custom-rewrites."
else
cat <<'EOH' > "${SNAP_DATA}/nginx/conf.d/edgex-custom-rewrites.inc"
# Add custom location directives to this file, for example:
# set $upstream_device_virtual edgex-device-virtual;
# location /device-virtual {
# rewrite /device-virtual/(.*) /$1 break;
# resolver 127.0.0.11 valid=30s;
# proxy_pass http://$upstream_device_virtual:59900;
# proxy_redirect off;
# proxy_set_header Host $host;
# auth_request /auth;
# auth_request_set $auth_status $upstream_status;
# }
EOH
fi

cat <<EOH > "${SNAP_DATA}/nginx/conf.d/edgex-default.conf"
#
# Copyright (C) Intel Corporation 2023
# SPDX-License-Identifier: Apache-2.0
#
# generated 2023-01-19, Mozilla Guideline v5.6, nginx 1.17.7, OpenSSL 1.1.1k, modern configuration, no HSTS, no OCSP
# https://ssl-config.mozilla.org/#server=nginx&version=1.17.7&config=modern&openssl=1.1.1k&hsts=false&ocsp=false&guideline=5.6
server {
listen 127.0.0.1:8000; # Snap listen insecure on localhost only
listen 8443 ssl;
ssl_certificate "/var/snap/edgexfoundry/current/nginx/nginx.crt";
ssl_certificate_key "/var/snap/edgexfoundry/current/nginx/nginx.key";
ssl_session_tickets off;
access_log syslog:server=unix:/dev/log,tag=edgexfoundry;
# Subrequest authentication
location /auth {
internal;
proxy_pass http://127.0.0.1:59842;
proxy_redirect off;
proxy_set_header Host \$host;
proxy_set_header Content-Length "";
proxy_set_header X-Forwarded-URI \$request_uri;
proxy_pass_request_body off;
}
# Rewriting rules (customized for snaps)
location /core-data {
`cat "${corssnippet}"`
rewrite /core-data/(.*) /\$1 break;
proxy_pass http://127.0.0.1:59880;
proxy_redirect off;
proxy_set_header Host \$host;
auth_request /auth;
auth_request_set \$auth_status \$upstream_status;
}
location /core-metadata {
`cat "${corssnippet}"`
rewrite /core-metadata/(.*) /\$1 break;
proxy_pass http://127.0.0.1:59881;
proxy_redirect off;
proxy_set_header Host \$host;
auth_request /auth;
auth_request_set \$auth_status \$upstream_status;
}
location /core-command {
`cat "${corssnippet}"`
rewrite /core-command/(.*) /\$1 break;
proxy_pass http://127.0.0.1:59882;
proxy_redirect off;
proxy_set_header Host \$host;
auth_request /auth;
auth_request_set \$auth_status \$upstream_status;
}
location /support-notifications {
`cat "${corssnippet}"`
rewrite /support-notifications/(.*) /\$1 break;
proxy_pass http://127.0.0.1:59860;
proxy_redirect off;
proxy_set_header Host \$host;
auth_request /auth;
auth_request_set \$auth_status \$upstream_status;
}
location /support-scheduler {
`cat "${corssnippet}"`
rewrite /support-scheduler/(.*) /\$1 break;
proxy_pass http://127.0.0.1:59861;
proxy_redirect off;
proxy_set_header Host \$host;
auth_request /auth;
auth_request_set \$auth_status \$upstream_status;
}
location /app-rules-engine {
`cat "${corssnippet}"`
rewrite /app-rules-engine/(.*) /\$1 break;
proxy_pass http://127.0.0.1:59701;
proxy_redirect off;
proxy_set_header Host \$host;
auth_request /auth;
auth_request_set \$auth_status \$upstream_status;
}
location /rules-engine {
`cat "${corssnippet}"`
rewrite /rules-engine/(.*) /\$1 break;
proxy_pass http://127.0.0.1:59720;
proxy_redirect off;
proxy_set_header Host \$host;
auth_request /auth;
auth_request_set \$auth_status \$upstream_status;
}
# Note: Consul implements its own authentication mechanism (only allow API, /v1, through)
location /consul/v1 {
`cat "${corssnippet}"`
rewrite /consul/(.*) /\$1 break;
proxy_pass http://127.0.0.1:8500;
proxy_redirect off;
proxy_set_header Host \$host;
}
# Note: Vault login API does not require authentication at the gateway for obvious reasons
# Expose URLs to log in to vault and to get a JWT
location /vault/v1/auth/userpass/login {
`cat "${corssnippet}"`
rewrite /vault/(.*) /\$1 break;
proxy_pass http://127.0.0.1:8200;
proxy_redirect off;
proxy_set_header Host \$host;
}
location /vault/v1/identity/oidc/token {
`cat "${corssnippet}"`
rewrite /vault/(.*) /\$1 break;
proxy_pass http://127.0.0.1:8200;
proxy_redirect off;
proxy_set_header Host \$host;
}
include /var/snap/edgexfoundry/current/nginx/conf.d/edgex-custom-rewrites.inc;
}
# Don't output NGINX version in Server: header
server_tokens off;
EOH

rm -f "${corssnippet}"
16 changes: 0 additions & 16 deletions snap/local/runtime-helpers/bin/setup-nginx.sh

This file was deleted.

Loading

0 comments on commit 541cc85

Please sign in to comment.