Pulsa ESC para cerrar · Ctrl+K para abrir

IIS Reverse Proxy para embeber SQL Server Reporting Services en WinCC Unified

Orden recomendado: primero aplicar script, despues validar reglas en IIS y finalmente comprobar que Reports/ReportServer responden correctamente.

IIS Reverse Proxy para embeber SQL Server Reporting Services en WinCC Unified

Objetivo y arquitectura

El objetivo es publicar SQL Server Reporting Services bajo la URL frontal de IIS para que WinCC Unified lo cargue dentro de un Web Control sin infringir la politica de mismo origen del navegador.

Con reverse proxy, el cliente accede siempre por el endpoint frontal de IIS, por ejemplo https://wserver-eng/Reports. IIS intercepta la peticion, la reescribe internamente y la reenvia al backend de SSRS en https://wserver-eng:4430. Asi centralizas TLS en un unico punto, simplificas la topologia de red y eliminas los bloqueos de embebido por origen cruzado.

Requisitos minimos

  • IIS con URL Rewrite y Application Request Routing (ARR).
  • Permisos para administrar IIS y ejecutar PowerShell como administrador.
  • SSRS operativo en backend con rutas /Reports y /ReportServer.
  • Certificado valido para el hostname usado por Unified.

1) Aplicar el reverse proxy con PowerShell

El script crea un backup de la configuracion IIS, habilita ARR Proxy a nivel global, crea (o verifica) las reglas de rewrite para /Reports y /ReportServer, y configura Content-Security-Policy: frame-ancestors 'self' para permitir el embebido en Unified sin bloqueos de iframe.

Codigo
Import-Module WebAdministration

# =====================================================================
# CONFIGURACION
# =====================================================================
$siteName    = "WinCC Unified SCADA"
$ssrsBackend = "https://WSERVER-ENG:4430"

$psPath         = "IIS:\Sites\$siteName"
$headersSection = "system.webServer/httpProtocol/customHeaders"

$ruleReportServer = "ReverseProxySSRS_ReportServer"
$ruleReports      = "ReverseProxySSRS_Reports"

# =====================================================================
# 0) BACKUP IIS (OPCIONAL PERO RECOMENDADO)
# =====================================================================
$backupName = "Before_ReverseProxySSRS_" + (Get-Date -Format "yyyyMMdd_HHmmss")
Backup-WebConfiguration -Name $backupName | Out-Null
Write-Host "BACKUP IIS creado: $backupName"

# =====================================================================
# 1) HABILITAR ARR PROXY A NIVEL GLOBAL
# =====================================================================
Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' `
    -filter "system.webServer/proxy" -name "enabled" -value "True"
Write-Host "1.- Proxy ARR habilitado a nivel global"

# =====================================================================
# 2) CREAR/VERIFICAR REGLAS URL REWRITE EN EL SITIO
#     - /ReportServer/*
#     - /Reports/*
# =====================================================================
$rules = Get-WebConfigurationProperty -pspath $psPath -Filter "system.webServer/rewrite/rules" -Name "."

function Ensure-RewriteRule {
    param(
        [string]$Name,
        [string]$MatchWildcard,
        [string]$TargetUrl
    )

    $existing = $rules.Collection | Where-Object { $_.Name -eq $Name }

    if (-not $existing) {
        Add-WebConfiguration -pspath $psPath -filter "system.webServer/rewrite/rules" -value @{
            name          = $Name
            enabled       = "true"
            patternSyntax = "Wildcard"
            stopProcessing= "true"
            match         = @{ url = $MatchWildcard }
            action        = @{
                type            = "Rewrite"
                url             = $TargetUrl
                logRewrittenUrl = "true"
            }
        }
        Write-Host "2.- Regla URL Rewrite '$Name' creada (match: $MatchWildcard)"
        $script:rules = Get-WebConfigurationProperty -pspath $psPath -Filter "system.webServer/rewrite/rules" -Name "."
    }
    else {
        Write-Host "2.- Regla '$Name' ya existe"
    }
}

Ensure-RewriteRule -Name $ruleReportServer -MatchWildcard "ReportServer/*" -TargetUrl "$ssrsBackend/ReportServer/{R:1}"
Ensure-RewriteRule -Name $ruleReports      -MatchWildcard "Reports/*"      -TargetUrl "$ssrsBackend/Reports/{R:1}"

# =====================================================================
# 3) HEADERS: ELIMINAR X-FRAME-OPTIONS (SI EXISTE COMO CUSTOM HEADER)
# =====================================================================
$customHeaders = Get-WebConfiguration -pspath $psPath -Filter $headersSection
$headerXFO = $customHeaders.Collection | Where-Object { $_.Name -eq "X-Frame-Options" }

if ($headerXFO) {
    Remove-WebConfigurationProperty -pspath $psPath -Filter $headersSection -Name "." -AtElement @{name="X-Frame-Options"}
    Write-Host "3.- Encabezado X-Frame-Options eliminado (customHeader)"
} else {
    Write-Host "3.- No se encontró encabezado X-Frame-Options (customHeader)"
}

# =====================================================================
# 4) CONTENT-SECURITY-POLICY: AGREGAR O ACTUALIZAR (EVITAR DUPLICADOS)
# =====================================================================
$customHeaders = Get-WebConfiguration -pspath $psPath -Filter $headersSection
$headerCSP = $customHeaders.Collection | Where-Object { $_.Name -eq "Content-Security-Policy" }

if ($headerCSP) {
    Set-WebConfigurationProperty -pspath $psPath `
        -Filter "$headersSection/add[@name='Content-Security-Policy']" `
        -Name "value" -Value "frame-ancestors 'self'"
    Write-Host "4.- Content-Security-Policy actualizado a: frame-ancestors 'self'"
} else {
    Add-WebConfigurationProperty -pspath $psPath -Filter $headersSection -Name "." `
        -Value @{name="Content-Security-Policy"; value="frame-ancestors 'self'"}
    Write-Host "4.- Content-Security-Policy agregado con: frame-ancestors 'self'"
}

# =====================================================================
# 5) REINICIAR IIS
# =====================================================================
Write-Host "5.- Reiniciando IIS (iisreset)..."
iisreset

Write-Host "FINALIZADO."
Write-Host "Rollback completo (si hiciera falta): Restore-WebConfiguration -Name $backupName ; iisreset"

Antes de ejecutar, revisa $siteName y $ssrsBackend para tu entorno real.

Salida del script PowerShell: backup IIS creado, ARR habilitado, reglas verificadas y Content-Security-Policy actualizado

2) Verificar IIS y reglas URL Rewrite

Con el script aplicado, abre el Administrador de IIS, navega al sitio y abre el modulo URL Rewrite. Debes ver las dos reglas inbound: ReverseProxySSRS con patron ReportServer/* y ReverseProxySSRS_Reports con patron Reports/*.

Listado de reglas Reverse Proxy SSRS en IIS
Detalle de regla ReportServer en IIS URL Rewrite
Detalle de regla Reports en IIS URL Rewrite

Abriendo el detalle de cada regla, verifica que la accion es Rewrite, la URL destino apunta al backend SSRS (https://WSERVER-ENG:4430/...) y la opcion Stop processing subsequent rules esta activa para que otras reglas del sitio no interfieran.

3) Comprobar funcionamiento

Estas capturas verifican dos cosas: que el backend de SSRS responde correctamente y que la ruta a traves del reverse proxy resuelve el mismo contenido sin puerto explicito.

Acceso directo al backend de SSRS en wserver-eng:4430/Reports/browse/. El portal responde y muestra los informes y origenes de datos disponibles.

SSRS accedido directamente en el backend wserver-eng:4430/Reports

El mismo portal accedido a traves del reverse proxy en wserver-eng/Reports/browse. Contenido identico, URL sin puerto explicito. Este es el endpoint que debe configurarse en el Web Control de Unified.

SSRS accedido via reverse proxy en wserver-eng/Reports/browse, sin puerto explicito

Por que funciona en el embebido de Unified

WinCC Unified usa un motor de navegador embebido que aplica la politica de mismo origen (Same-Origin Policy). En esa politica, el origen se define como la combinacion de esquema + host + puerto: https://wserver-eng:4430 y https://wserver-eng son origenes distintos aunque compartan el nombre de host, porque el numero de puerto difiere.

Si el Web Control de Unified apunta directamente al backend SSRS con la URL que incluye el puerto, el navegador detecta origen cruzado respecto al sitio padre y bloquea el iframe, aunque SSRS devuelva HTTP 200.

Web Control de Unified bloqueado: URL con puerto wserver-eng:4430 produce origen cruzado y el iframe no carga

Cuando Unified carga SSRS a traves del reverse proxy, la URL no incluye puerto explicito (https://wserver-eng/Reports/browse), por lo que el origen del iframe coincide con el del sitio padre. El script configuro Content-Security-Policy: frame-ancestors 'self' en IIS, lo que permite el embebido desde el mismo origen. El header X-Powered-By: ARR/3.0 visible en DevTools confirma que Application Request Routing esta activo y enrutando al backend.

Web Control de Unified cargando SSRS correctamente: mismo origen, frame-ancestors self activo, ARR/3.0 confirmado en DevTools