#!/usr/bin/env bash
# ohs-to-nginx.sh
# Convert Oracle HTTP Server (OHS/Apache httpd) configs into a single Nginx config.
#
# Input layout (in ./ohs_config by default):
# HTTPD.conf, ssl.conf, mod_wl_ohs.conf
# Optional multi-instance files are supported via prefixes, e.g. OHS1_httpd.conf, OHS1_ssl.conf, OHS1_mod_wl_ohs.conf
# You can mix default (unprefixed) and prefixed sets. The prefix is everything before the first underscore.
#
# Output: ./nginx_from_ohs/nginx.conf
#
# What is translated:
# - Listen, ServerName, DocumentRoot, ErrorLog, CustomLog (best-effort)
# - SSL: SSLCertificateFile, SSLCertificateKeyFile, SSLProtocol, SSLCipherSuite
# - WebLogic upstreams from mod_wl_ohs: WebLogicCluster or WebLogicHost/WebLogicPort
# - <Location ...> SetHandler weblogic-handler → proxy_pass to upstream
# - Common timeout/keepalive hints (ConnectTimeoutSecs, KeepAliveEnabled) mapped to proxy_* directives
#
# Limitations (by design to keep this portable and dependency-free):
# - Complex conditional blocks, Includes, IfDefine, and per-virtualhost overrides are not fully modeled
# - Regex MatchExpression is mapped as a location ~ regex when found, otherwise path prefix
# - PathTrim/PathPrepend is applied in comments for manual adjustment (advanced rewrites not auto-generated)
# - Multiple VirtualHost blocks aren’t individually split; we emit one server per instance for :80 and :443
# - Use this as a starting point and review the generated nginx.conf before production use
#
set -euo pipefail
OHSDIR=${1:-"./ohs_config"}
OUTDIR=${2:-"./nginx_from_ohs"}
OUTFILE="$OUTDIR/nginx.conf"
mkdir -p "$OUTDIR"
# --- Helpers -----------------------------------------------------------------
trim() { sed -e 's/^\s*//' -e 's/\s*$//'; }
val() { awk '{sub(/^\s+/,""); print}'; }
# safe print (escape # for nginx comments only when needed)
pecho() { printf '%s\n' "$*"; }
# Map of instance → files
# For each file in $OHSDIR, determine instance prefix (part before first underscore) if present.
# Recognized basenames: httpd.conf, HTTPD.conf, ssl.conf, mod_wl_ohs.conf
declare -A HTTPD_FILE SSL_FILE WL_FILE
shopt -s nullglob nocaseglob
for f in "$OHSDIR"/*; do
base=$(basename "$f")
lower=${base,,}
inst="default"
namepart="$lower"
if [[ "$lower" == *_httpd.conf || "$lower" == *_ssl.conf || "$lower" == *_mod_wl_ohs.conf ]]; then
inst=${lower%%_*}
namepart=${lower#*_}
fi
case "$namepart" in
httpd.conf) HTTPD_FILE[$inst]="$f" ;;
ssl.conf) SSL_FILE[$inst]="$f" ;;
mod_wl_ohs.conf) WL_FILE[$inst]="$f" ;;
*) ;; # ignore other files
esac
done
shopt -u nocaseglob
# Collect unique instances
instances=()
for k in "${!HTTPD_FILE[@]}"; do instances+=("$k"); done
for k in "${!SSL_FILE[@]}"; do [[ " ${instances[*]} " == *" $k "* ]] || instances+=("$k"); done
for k in "${!WL_FILE[@]}"; do [[ " ${instances[*]} " == *" $k "* ]] || instances+=("$k"); done
if [[ ${#instances[@]} -eq 0 ]]; then
echo "No recognizable OHS config files found in $OHSDIR" >&2
exit 1
fi
# Data structures: per-instance associative arrays in bash via namerefs
parse_httpd() {
local inst=$1 file=$2
# Defaults
eval "LISTEN_$inst='' SERVERNAME_$inst='' DOCROOT_$inst='' ERRLOG_$inst='' ACCLOG_$inst=''"
[[ -f "$file" ]] || return 0
while IFS= read -r line; do
line=${line%%#*}
line=$(echo "$line" | trim)
[[ -z "$line" ]] && continue
key=${line%% *}
rest=${line#* }
case "${key,,}" in
listen)
# Could be ip:port or port
port=$(echo "$rest" | awk -F: '{print $NF}' | tr -d ' ')
eval "LISTEN_$inst='${port}'" ;;
servername)
eval "SERVERNAME_$inst='$(echo "$rest" | val)'" ;;
documentroot)
eval "DOCROOT_$inst='$(echo "$rest" | val | sed "s/\"//g")'" ;;
errorlog)
eval "ERRLOG_$inst='$(echo "$rest" | val)'" ;;
customlog)
# CustomLog path format
path=$(echo "$rest" | awk '{print $1}')
eval "ACCLOG_$inst='${path}'" ;;
esac
done < "$file"
}
parse_ssl() {
local inst=$1 file=$2
eval "SSL_ON_$inst=0 SSL_CERT_$inst='' SSL_KEY_$inst='' SSL_CHAIN_$inst='' SSL_PROTOCOLS_$inst='' SSL_CIPHERS_$inst=''"
[[ -f "$file" ]] || return 0
eval "SSL_ON_$inst=1"
while IFS= read -r line; do
line=${line%%#*}; line=$(echo "$line" | trim); [[ -z "$line" ]] && continue
key=${line%% *}; rest=${line#* }
case "${key,,}" in
sslcertificatefile) eval "SSL_CERT_$inst='$(echo "$rest" | val)'" ;;
sslcertificatekeyfile) eval "SSL_KEY_$inst='$(echo "$rest" | val)'" ;;
sslcertificatechainfile) eval "SSL_CHAIN_$inst='$(echo "$rest" | val)'" ;;
sslprotocol) eval "SSL_PROTOCOLS_$inst='$(echo "$rest" | val)'" ;;
sslciphersuite) eval "SSL_CIPHERS_$inst='$(echo "$rest" | val)'" ;;
esac
done < "$file"
}
# Structures to hold mod_wl info per instance
# WL_MODE_$inst = cluster|single|none
# WL_CLUSTER_$inst = "host1:port1,host2:port2"
# WL_SINGLE_HOST_$inst, WL_SINGLE_PORT_$inst
# WL_LOCATIONS_$inst = file path to a temp file listing location mappings: path<TAB>type<TAB>pattern
# WL_TIMEOUT_$inst, WL_KEEPALIVE_$inst
parse_modwl() {
local inst=$1 file=$2
eval "WL_MODE_$inst='none' WL_CLUSTER_$inst='' WL_SINGLE_HOST_$inst='' WL_SINGLE_PORT_$inst='' WL_TIMEOUT_$inst='' WL_KEEPALIVE_$inst=''"
local loc_tmp
loc_tmp=$(mktemp)
eval "WL_LOCATIONS_$inst='$loc_tmp'"
[[ -f "$file" ]] || return 0
# First, fetch global cluster/single definitions (outside <Location>)
while IFS= read -r line; do
line=${line%%#*}; line=$(echo "$line" | trim); [[ -z "$line" ]] && continue
key=${line%% *}; rest=${line#* }
case "${key,,}" in
weblogiccluster)
eval "WL_MODE_$inst='cluster' WL_CLUSTER_$inst='$(echo "$rest" | val | tr -d '\"')'" ;;
weblogichost)
eval "WL_SINGLE_HOST_$inst='$(echo "$rest" | val | tr -d '\"')'" ;;
weblogicport)
eval "WL_SINGLE_PORT_$inst='$(echo "$rest" | val | tr -d '\"')'" ;;
connecttimeoutsecs)
eval "WL_TIMEOUT_$inst='$(echo "$rest" | val)'" ;;
keepaliveenabled)
eval "WL_KEEPALIVE_$inst='$(echo "$rest" | val | tr '[:upper:]' '[:lower:]')'" ;;
esac
done < "$file"
# Determine mode if only host/port given
eval "mode=\${WL_MODE_$inst} shost=\${WL_SINGLE_HOST_$inst} sport=\${WL_SINGLE_PORT_$inst}"
if [[ "$mode" == "none" && -n "$shost" && -n "$sport" ]]; then
eval "WL_MODE_$inst='single'"
fi
# Parse <Location ..> blocks that set weblogic-handler
awk -v out="$loc_tmp" '
BEGIN{inloc=0; path=""; matchExpr=""}
{
line=$0; sub(/#.*/, "", line);
gsub(/^\s+|\s+$/, "", line);
if (line ~ /^<Location[[:space:]]+/) {
inloc=1;
path=line;
sub(/^<Location[[:space:]]+/, "", path);
sub(/>$/, "", path);
next;
}
if (inloc==1 && line ~ /^<\/Location>/) {
# emit if weblogic-handler was seen for this block
if (seen){
if (matchExpr!="") {printf("%s\tregex\t%s\n", path, matchExpr) >> out}
else {printf("%s\tpath\t%s\n", path, path) >> out}
}
inloc=0; seen=0; matchExpr=""; path="";
next;
}
if (inloc==1) {
if (tolower(line) ~ /^sethandler[[:space:]]+weblogic-handler/) { seen=1 }
if (tolower(line) ~ /^matchexpression[[:space:]]+/) {
m=line; sub(/^[^ ]+[ ]+/, "", m); matchExpr=m;
}
# ignore other directives inside for now
}
}
' "$file"
}
# Parse all instances
for inst in "${instances[@]}"; do
parse_httpd "$inst" "${HTTPD_FILE[$inst]:-}"
parse_ssl "$inst" "${SSL_FILE[$inst]:-}"
parse_modwl "$inst" "${WL_FILE[$inst]:-}"
done
# Generate nginx.conf
{
pecho "# Generated by ohs-to-nginx.sh on $(date -u +%Y-%m-%dT%H:%M:%SZ)"
pecho "# Source directory: $OHSDIR"
echo
pecho "user nginx;"
pecho "worker_processes auto;"
echo
pecho "events { worker_connections 10240; }"
echo
pecho "http {"
pecho " include mime.types;"
pecho " default_type application/octet-stream;"
pecho " sendfile on;"
pecho " tcp_nopush on;"
pecho " tcp_nodelay on;"
pecho " keepalive_timeout 65;"
echo
for inst in "${instances[@]}"; do
iname=${inst}
[[ "$iname" == "default" ]] && iname="ohs"
# Upstream definition from WL
eval "mode=\${WL_MODE_$inst} cluster=\${WL_CLUSTER_$inst} shost=\${WL_SINGLE_HOST_$inst} sport=\${WL_SINGLE_PORT_$inst}"
if [[ "$mode" != "none" ]]; then
upname="${iname}_weblogic"
pecho " upstream $upname {"
if [[ "$mode" == "cluster" ]]; then
IFS=',' read -r -a parts <<< "$cluster"
for p in "${parts[@]}"; do
hp=$(echo "$p" | trim)
[[ -z "$hp" ]] && continue
host=${hp%%:*}; port=${hp##*:}
pecho " server $host:$port;"
done
else
pecho " server $shost:$sport;"
fi
pecho " keepalive 64;"
pecho " }"
echo
fi
# Gather general settings
eval "port=\${LISTEN_$inst} sname=\${SERVERNAME_$inst} docroot=\${DOCROOT_$inst}"
[[ -z "$port" ]] && port=80
# Access/Error log comments
eval "elog=\${ERRLOG_$inst} alog=\${ACCLOG_$inst}"
# HTTP server block (port 80)
pecho " server {"
pecho " listen 80;"
[[ -n "$sname" ]] && pecho " server_name $sname;" || pecho " # server_name _;"
[[ -n "$docroot" ]] && pecho " root $docroot;" || pecho " # root /usr/share/nginx/html;"
[[ -n "$elog" ]] && pecho " # error_log $elog;"
[[ -n "$alog" ]] && pecho " # access_log $alog;"
# Location mappings from mod_wl_ohs
if [[ "$mode" != "none" ]]; then
eval "locfile=\${WL_LOCATIONS_$inst} timeout=\${WL_TIMEOUT_$inst} keepal=\${WL_KEEPALIVE_$inst}"
proxy_tout=${timeout:-60}
[[ "${keepal:-on}" == "on" ]] && ka_comment="#" || ka_comment=""
while IFS=$'\t' read -r lpath ltype lpat; do
[[ -z "$lpath" ]] && continue
if [[ "$ltype" == "regex" ]]; then
pecho " location ~ $lpat {"
else
pecho " location ${lpath} {"
fi
pecho " proxy_set_header Host \$host;"
pecho " proxy_set_header X-Real-IP \$remote_addr;"
pecho " proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;"
pecho " proxy_set_header X-Forwarded-Proto \$scheme;"
pecho " proxy_http_version 1.1;"
pecho " ${ka_comment}proxy_set_header Connection \"\$connection_upgrade\"; # keepalive hint"
pecho " proxy_connect_timeout ${proxy_tout}s;"
pecho " proxy_read_timeout ${proxy_tout}s;"
pecho " proxy_send_timeout ${proxy_tout}s;"
pecho " proxy_pass http://${iname}_weblogic;"
pecho " # NOTE: If OHS used PathTrim/Prepend here, consider adding rewrite directives accordingly."
pecho " }"
done < "${locfile:-/dev/null}"
else
pecho " # No mod_wl_ohs upstream detected for instance '$inst'"
fi
pecho " }" # end server 80
echo
# HTTPS server block if SSL present
eval "ssl_on=\${SSL_ON_$inst} cert=\${SSL_CERT_$inst} key=\${SSL_KEY_$inst} chain=\${SSL_CHAIN_$inst} protos=\${SSL_PROTOCOLS_$inst} ciphers=\${SSL_CIPHERS_$inst}"
if [[ "$ssl_on" == "1" ]]; then
pecho " server {"
pecho " listen 443 ssl;"
[[ -n "$sname" ]] && pecho " server_name $sname;"
[[ -n "$docroot" ]] && pecho " root $docroot;"
if [[ -n "$cert" && -n "$key" ]]; then
pecho " ssl_certificate $cert;"
pecho " ssl_certificate_key $key;"
else
pecho " # ssl_certificate /path/to/fullchain.pem;"
pecho " # ssl_certificate_key /path/to/privkey.pem;"
fi
[[ -n "$chain" ]] && pecho " # (chain file is typically bundled in ssl_certificate) # $chain"
# Map Apache SSLProtocol → nginx ssl_protocols (best-effort)
if [[ -n "$protos" ]]; then
# Example: +TLSv1.2 +TLSv1.3 -TLSv1 -SSLv3
# We will include only listed positives
wanted=$(echo "$protos" | grep -Eo 'TLSv1(\.[0-9])?' | tr '\n' ' ' | xargs)
[[ -n "$wanted" ]] && pecho " ssl_protocols $wanted;"
fi
[[ -n "$ciphers" ]] && pecho " ssl_ciphers $ciphers;"
pecho " ssl_prefer_server_ciphers on;"
if [[ "$mode" != "none" ]]; then
eval "locfile=\${WL_LOCATIONS_$inst} timeout=\${WL_TIMEOUT_$inst} keepal=\${WL_KEEPALIVE_$inst}"
proxy_tout=${timeout:-60}
[[ "${keepal:-on}" == "on" ]] && ka_comment="#" || ka_comment=""
while IFS=$'\t' read -r lpath ltype lpat; do
[[ -z "$lpath" ]] && continue
if [[ "$ltype" == "regex" ]]; then
pecho " location ~ $lpat {"
else
pecho " location ${lpath} {"
fi
pecho " proxy_set_header Host \$host;"
pecho " proxy_set_header X-Real-IP \$remote_addr;"
pecho " proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;"
pecho " proxy_set_header X-Forwarded-Proto \$scheme;"
pecho " proxy_http_version 1.1;"
pecho " ${ka_comment}proxy_set_header Connection \"\$connection_upgrade\"; # keepalive hint"
pecho " proxy_connect_timeout ${proxy_tout}s;"
pecho " proxy_read_timeout ${proxy_tout}s;"
pecho " proxy_send_timeout ${proxy_tout}s;"
pecho " proxy_pass http://${iname}_weblogic;"
pecho " }"
done < "${locfile:-/dev/null}"
fi
pecho " }" # end server 443
echo
fi
# Cleanup tmp loc file
eval "locfile=\${WL_LOCATIONS_$inst}"
[[ -n "${locfile:-}" && -f "$locfile" ]] && rm -f "$locfile"
done
pecho "}" # end http
} > "$OUTFILE"
echo "Wrote: $OUTFILE"
# Also emit a brief README next to the file with guidance
cat > "$OUTDIR/README.txt" <<'EOT'
This nginx.conf was auto-generated from Oracle HTTP Server configs.
Checklist before going live:
1) Review upstream servers and ports under `upstream <instance>_weblogic`.
2) Confirm `server_name`, `root`, and logging directives for your environment.
3) If OHS used PathTrim/PathPrepend/MatchExpression, verify location blocks and add `rewrite` rules as needed.
4) Validate TLS cert/key paths and combine chain into the certificate file if required.
5) Test with: nginx -t && nginx -s reload (or your system’s service manager).
Regenerate anytime by re-running ohs-to-nginx.sh with your ohs_config directory.
EOT
Subscribe
Login
0 Comments
Oldest