bash -x ./ohs-to-nginx.sh ./ohs_config ./nginx_from_ohs 2>&1 | tee run.log
#!/usr/bin/env bash
# ohs-to-nginx.sh
# Convert *all* Oracle HTTP Server (OHS/Apache httpd) .conf files in a directory
# into ONE CIS-minded nginx.conf. File names are unrestricted as long as they end in .conf.
#
# Multi-instance support:
# - Files may be grouped by a prefix before the first underscore, e.g., OHS1_*.conf, OHS2_*.conf.
# - Files without a prefix belong to the "default" instance.
#
# Output
# - Writes ./nginx_from_ohs/nginx.conf (or to a custom output dir if provided).
#
# Security profile
# - TLS 1.2 ONLY (per requirement) with ECDHE AES-GCM ciphers.
# - CIS-minded http defaults: server_tokens off, hardened timeouts, HSTS, etc.
#
# Usage
# ./ohs-to-nginx.sh [<ohs_config_dir> [<output_dir>]]
# example: ./ohs-to-nginx.sh ./ohs_config ./nginx_from_ohs
#
# Notes / Limitations
# - Complex Include graphs, multiple <VirtualHost> blocks, PathTrim/PathPrepend rules are best-effort.
# - This emits at most one HTTP and one HTTPS server per instance. Review & tailor as needed.
#
set -euo pipefail
OHSDIR=${1:-"./ohs_config"}
OUTDIR=${2:-"./nginx_from_ohs"}
OUTFILE="$OUTDIR/nginx.conf"
mkdir -p "$OUTDIR"
# ----------------------------- helpers ---------------------------------------
trim() {
# usage: trim " text " -> prints "text"
local s=$1
# strip leading
s=${s#${s%%[!$'\t\r\n ']*}}
# strip trailing
s=${s%${s##*[!$'\t\r\n ']}}
printf '%s' "$s"
}
lower() { printf '%s' "${1,,}"; }
append_tmp() { TMPFILES+=("$1"); }
declare -a TMPFILES=()
cleanup() { [[ ${#TMPFILES[@]} -gt 0 ]] && rm -f -- "${TMPFILES[@]}" 2>/dev/null || true; }
trap cleanup EXIT ERR INT TERM
# Save original shopt state and enable desired globs locally
save_globs() { SAVED_GLOBS=$(shopt -p nullglob nocaseglob); shopt -s nullglob nocaseglob; }
restore_globs() { eval "$SAVED_GLOBS"; }
# ------------------------ per-instance state (associative) -------------------
declare -A FILES_BY_INST
# core
declare -A LISTEN SERVERNAME DOCROOT ERRLOG ACCLOG
# ssl
declare -A SSL_ON SSL_CERT SSL_KEY SSL_CHAIN SSL_PROTOCOLS SSL_CIPHERS
# weblogic
declare -A WL_MODE WL_CLUSTER WL_SINGLE_HOST WL_SINGLE_PORT WL_TIMEOUT WL_KEEPALIVE WL_LOC_TMP
init_instance() {
local inst=$1
LISTEN[$inst]=""
SERVERNAME[$inst]=""
DOCROOT[$inst]=""
ERRLOG[$inst]=""
ACCLOG[$inst]=""
SSL_ON[$inst]=0
SSL_CERT[$inst]=""
SSL_KEY[$inst]=""
SSL_CHAIN[$inst]=""
SSL_PROTOCOLS[$inst]=""
SSL_CIPHERS[$inst]=""
WL_MODE[$inst]="none"
WL_CLUSTER[$inst]=""
WL_SINGLE_HOST[$inst]=""
WL_SINGLE_PORT[$inst]=""
WL_TIMEOUT[$inst]=""
WL_KEEPALIVE[$inst]=""
local t
t=$(mktemp)
append_tmp "$t"
WL_LOC_TMP[$inst]="$t"
}
# ----------------------------- discover files --------------------------------
# Recursively gather all *.conf files under OHSDIR
save_globs
CONF_LIST=$(find "$OHSDIR" -type f -name '*.conf' 2>/dev/null | sort || true)
restore_globs
if [[ -z ${CONF_LIST} ]]; then
echo "No .conf files found under: $OHSDIR" >&2
# still create an empty scaffold nginx.conf to make behavior explicit
mkdir -p "$OUTDIR"
cat >"$OUTDIR/nginx.conf" <<'EMPTY'
# Empty nginx.conf generated: no .conf files discovered in the source directory.
# Verify your input path and ensure files end with .conf (recursively searched).
EMPTY
echo "Wrote: $OUTDIR/nginx.conf (empty scaffold)" >&2
exit 0
fi
# Bucket files by instance
while IFS= read -r f; do
base=$(basename -- "$f")
lbase=$(lower "$base")
inst="default"
if [[ "$lbase" == *_*.conf ]]; then
inst=${lbase%%_*}
fi
if [[ -n ${FILES_BY_INST[$inst]:-} ]]; then
FILES_BY_INST[$inst]+=$'
'"$f"
else
FILES_BY_INST[$inst]="$f"
fi
done <<< "$CONF_LIST"
if [[ ${#FILES_BY_INST[@]} -eq 0 ]]; then
echo "No .conf files found under: $OHSDIR" >&2
exit 1
fi
# ----------------------------- parsing ---------------------------------------
parse_conf_file() {
local inst=$1 file=$2 inloc=0 lpath="" matchExpr="" sawWL=0
while IFS= read -r raw || [[ -n $raw ]]; do
local line=${raw%%#*}
line=$(trim "$line")
[[ -z $line ]] && continue
# <Location ...> tracking for weblogic-handler
if [[ $line =~ ^<Location[[:space:]]+ ]]; then
inloc=1
lpath=${line#<Location }
lpath=${lpath%>}
matchExpr=""; sawWL=0
continue
fi
if (( inloc==1 )) && [[ $line =~ ^</Location> ]]; then
if (( sawWL==1 )); then
if [[ -n $matchExpr ]]; then
printf '%s\t%s\t%s\n' "$lpath" "regex" "$matchExpr" >>"${WL_LOC_TMP[$inst]}"
else
printf '%s\t%s\t%s\n' "$lpath" "path" "$lpath" >>"${WL_LOC_TMP[$inst]}"
fi
fi
inloc=0; lpath=""; matchExpr=""; sawWL=0
continue
fi
if (( inloc==1 )); then
case "${line,,}" in
sethandler\ weblogic-handler) sawWL=1 ;;
matchexpression*) matchExpr=${line#* } ;;
esac
continue
fi
# flat directives
local key=${line%% *}
local rest=""; [[ "$line" == *" "* ]] && rest=${line#* }
case "${key,,}" in
listen)
# handle "IP:PORT" or "PORT"
local prt=${rest##*:}
prt=$(trim "$prt")
LISTEN[$inst]="$prt" ;;
servername)
SERVERNAME[$inst]="$(trim "$rest")" ;;
documentroot)
DOCROOT[$inst]="${rest%\"}"; DOCROOT[$inst]="${DOCROOT[$inst]#\"}" ;;
errorlog)
ERRLOG[$inst]="$(trim "$rest")" ;;
customlog)
ACCLOG[$inst]="$(printf '%s' "$rest" | awk '{print $1}')" ;;
# SSL
sslcertificatefile)
SSL_ON[$inst]=1; SSL_CERT[$inst]="$(trim "$rest")" ;;
sslcertificatekeyfile)
SSL_ON[$inst]=1; SSL_KEY[$inst]="$(trim "$rest")" ;;
sslcertificatechainfile)
SSL_ON[$inst]=1; SSL_CHAIN[$inst]="$(trim "$rest")" ;;
sslprotocol)
SSL_ON[$inst]=1; SSL_PROTOCOLS[$inst]="$(trim "$rest")" ;;
sslciphersuite)
SSL_ON[$inst]=1; SSL_CIPHERS[$inst]="$(trim "$rest")" ;;
# WebLogic
weblogiccluster)
WL_MODE[$inst]="cluster"; WL_CLUSTER[$inst]="$(trim "$rest" | tr -d '"')" ;;
weblogichost)
WL_SINGLE_HOST[$inst]="$(trim "$rest" | tr -d '"')" ;;
weblogicport)
WL_SINGLE_PORT[$inst]="$(trim "$rest" | tr -d '"')" ;;
connecttimeoutsecs)
WL_TIMEOUT[$inst]="$(trim "$rest")" ;;
keepaliveenabled)
WL_KEEPALIVE[$inst]="$(lower "$(trim "$rest")")" ;;
esac
done < "$file"
}
# init + parse per instance
INSTANCES=()
for inst in "${!FILES_BY_INST[@]}"; do INSTANCES+=("$inst"); init_instance "$inst"; done
for inst in "${INSTANCES[@]}"; do
while IFS= read -r f; do parse_conf_file "$inst" "$f"; done < <(printf '%s\n' "${FILES_BY_INST[$inst]}")
# derive single-host mode if host+port present and no cluster
if [[ ${WL_MODE[$inst]} == "none" && -n ${WL_SINGLE_HOST[$inst]} && -n ${WL_SINGLE_PORT[$inst]} ]]; then
WL_MODE[$inst]="single"
fi
[[ -z ${LISTEN[$inst]} ]] && LISTEN[$inst]=80
done
# ----------------------------- security profile ------------------------------
CIS_TLS12_CIPHERS='ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256'
# ----------------------------- emit nginx.conf -------------------------------
{
printf '# Generated by ohs-to-nginx.sh on %s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
printf '# Source: %s\n\n' "$OHSDIR"
cat <<'HDR'
user nginx;
worker_processes auto;
events {
worker_connections 10240;
}
http {
# --- CIS-minded global defaults ---
server_tokens off;
include mime.types;
default_type application/octet-stream;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 15;
client_body_timeout 15;
client_header_timeout 15;
send_timeout 15;
client_max_body_size 10m;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log warn;
HDR
for inst in "${INSTANCES[@]}"; do
iname=$inst; [[ $iname == "default" ]] && iname="ohs"
# upstream
if [[ ${WL_MODE[$inst]} != "none" ]]; then
printf ' upstream %s_weblogic {\n' "$iname"
if [[ ${WL_MODE[$inst]} == "cluster" ]]; then
IFS=',' read -r -a parts <<< "${WL_CLUSTER[$inst]}"
for hp in "${parts[@]}"; do
hp=$(trim "$hp"); [[ -z $hp ]] && continue
printf ' server %s;\n' "$hp"
done
else
printf ' server %s:%s;\n' "${WL_SINGLE_HOST[$inst]}" "${WL_SINGLE_PORT[$inst]}"
fi
printf ' keepalive 64;\n'
printf ' }\n\n'
fi
# HTTP :80
printf ' server {\n'
printf ' listen 80;\n'
if [[ -n ${SERVERNAME[$inst]} ]]; then
printf ' server_name %s;\n' "${SERVERNAME[$inst]}"
else
printf ' server_name _;\n'
fi
if [[ -n ${DOCROOT[$inst]} ]]; then
printf ' root %s;\n' "${DOCROOT[$inst]}"
else
printf ' # root /usr/share/nginx/html;\n'
fi
cat <<'SHEAD'
add_header X-Content-Type-Options nosniff always;
add_header X-Frame-Options SAMEORIGIN always;
add_header Referrer-Policy no-referrer-when-downgrade always;
SHEAD
# optional logs reference
[[ -n ${ERRLOG[$inst]} ]] && printf ' # error_log %s;\n' "${ERRLOG[$inst]}"
[[ -n ${ACCLOG[$inst]} ]] && printf ' # access_log %s;\n' "${ACCLOG[$inst]}"
# locations → upstream
if [[ ${WL_MODE[$inst]} != "none" && -s ${WL_LOC_TMP[$inst]} ]]; then
proxy_tout=${WL_TIMEOUT[$inst]:-60}
keepal=${WL_KEEPALIVE[$inst]:-on}
if [[ $keepal == on ]]; then ka_comment="#"; else ka_comment=""; fi
while IFS=$'\t' read -r lpath ltype lpat; do
[[ -z $lpath ]] && continue
if [[ $ltype == regex ]]; then
printf ' location ~ %s {\n' "$lpat"
else
printf ' location %s {\n' "$lpath"
fi
cat <<LOC
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
proxy_http_version 1.1;
${ka_comment}proxy_set_header Connection "\$connection_upgrade"; # keepalive hint
proxy_connect_timeout ${proxy_tout}s;
proxy_read_timeout ${proxy_tout}s;
proxy_send_timeout ${proxy_tout}s;
proxy_pass http://${iname}_weblogic;
# TODO: If OHS PathTrim/PathPrepend was used, add equivalent rewrite rules.
}
LOC
done < "${WL_LOC_TMP[$inst]}"
else
printf ' # No mod_wl_ohs upstream detected for instance "%s"\n' "$inst"
fi
printf ' }\n\n'
# HTTPS :443
if [[ ${SSL_ON[$inst]} -eq 1 ]]; then
printf ' server {\n'
printf ' listen 443 ssl;\n'
if [[ -n ${SERVERNAME[$inst]} ]]; then
printf ' server_name %s;\n' "${SERVERNAME[$inst]}"
else
printf ' server_name _;\n'
fi
if [[ -n ${DOCROOT[$inst]} ]]; then
printf ' root %s;\n' "${DOCROOT[$inst]}"
else
printf ' # root /usr/share/nginx/html;\n'
fi
printf ' ssl_protocols TLSv1.2;\n'
printf ' ssl_ciphers %s;\n' "$CIS_TLS12_CIPHERS"
cat <<'TLSA'
ssl_prefer_server_ciphers on;
ssl_session_timeout 10m;
ssl_session_cache shared:SSL:20m;
ssl_session_tickets off;
TLSA
if [[ -n ${SSL_CERT[$inst]} && -n ${SSL_KEY[$inst]} ]]; then
printf ' ssl_certificate %s;\n' "${SSL_CERT[$inst]}"
printf ' ssl_certificate_key %s;\n' "${SSL_KEY[$inst]}"
else
printf ' # ssl_certificate /path/to/fullchain.pem;\n'
printf ' # ssl_certificate_key /path/to/privkey.pem;\n'
fi
if [[ -n ${SSL_CHAIN[$inst]} ]]; then
printf ' # (Chain typically bundled into ssl_certificate) # %s\n' "${SSL_CHAIN[$inst]}"
fi
cat <<'HSTS'
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options nosniff always;
add_header X-Frame-Options SAMEORIGIN always;
add_header Referrer-Policy no-referrer-when-downgrade always;
HSTS
if [[ ${WL_MODE[$inst]} != "none" && -s ${WL_LOC_TMP[$inst]} ]]; then
proxy_tout=${WL_TIMEOUT[$inst]:-60}
keepal=${WL_KEEPALIVE[$inst]:-on}
if [[ $keepal == on ]]; then ka_comment="#"; else ka_comment=""; fi
while IFS=$'\t' read -r lpath ltype lpat; do
[[ -z $lpath ]] && continue
if [[ $ltype == regex ]]; then
printf ' location ~ %s {\n' "$lpat"
else
printf ' location %s {\n' "$lpath"
fi
cat <<LOC2
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
proxy_http_version 1.1;
${ka_comment}proxy_set_header Connection "\$connection_upgrade"; # keepalive hint
proxy_connect_timeout ${proxy_tout}s;
proxy_read_timeout ${proxy_tout}s;
proxy_send_timeout ${proxy_tout}s;
proxy_pass http://${iname}_weblogic;
}
LOC2
done < "${WL_LOC_TMP[$inst]}"
fi
printf ' }\n\n'
fi
done
printf '}\n'
} >"$OUTFILE"
# ----------------------------- README ----------------------------------------
cat > "$OUTDIR/README.txt" <<'EOT'
This nginx.conf was auto-generated from Oracle HTTP Server configs.
Key properties
- Reads ALL *.conf files from the provided directory; names are unrestricted.
- Groups files by instance prefix before the first underscore; unprefixed => "default".
- CIS-minded HTTP defaults; TLS 1.2 only with ECDHE AES-GCM cipher suites.
Review checklist
1) Verify upstream servers/ports (under upstream <instance>_weblogic).
2) Confirm server_name/root and log paths.
3) If OHS used PathTrim/PathPrepend/MatchExpression, add equivalent rewrites.
4) Check certificate/key paths and bundle chain as needed.
5) Validate and reload: nginx -t && systemctl reload nginx
EOT
printf 'Wrote: %s\n' "$OUTFILE"
Subscribe
Login
0 Comments
Oldest