| | | 1 | | <# |
| | | 2 | | .SYNOPSIS |
| | | 3 | | Adds Forwarded Headers middleware to a Kestrun server. |
| | | 4 | | .DESCRIPTION |
| | | 5 | | This cmdlet adds and configures the ASP.NET Core Forwarded Headers middleware |
| | | 6 | | for a Kestrun server. This middleware processes proxy-related headers such as |
| | | 7 | | X-Forwarded-For, X-Forwarded-Proto, X-Forwarded-Host, and X-Forwarded-Prefix to |
| | | 8 | | update the request's Scheme, Host, and Remote IP address accordingly. |
| | | 9 | | This is essential when hosting behind reverse proxies or load balancers that |
| | | 10 | | modify these headers. |
| | | 11 | | .PARAMETER Server |
| | | 12 | | The Kestrun server instance to which the Forwarded Headers middleware will be added. |
| | | 13 | | If not specified, the cmdlet will attempt to resolve the current server context. |
| | | 14 | | .PARAMETER Options |
| | | 15 | | An instance of Microsoft.AspNetCore.Builder.ForwardedHeadersOptions to configure |
| | | 16 | | the middleware. This allows for full customization of the middleware behavior. |
| | | 17 | | .PARAMETER XForwardedFor |
| | | 18 | | Switch to enable processing of the X-Forwarded-For header. |
| | | 19 | | .PARAMETER XForwardedProto |
| | | 20 | | Switch to enable processing of the X-Forwarded-Proto header. |
| | | 21 | | .PARAMETER XForwardedHost |
| | | 22 | | Switch to enable processing of the X-Forwarded-Host header. |
| | | 23 | | .PARAMETER XForwardedPrefix |
| | | 24 | | Switch to enable processing of the X-Forwarded-Prefix header. |
| | | 25 | | .PARAMETER All |
| | | 26 | | Switch to enable processing of all supported forwarded headers. |
| | | 27 | | .PARAMETER ForwardLimit |
| | | 28 | | Specifies the maximum number of entries to read from the forwarded headers. |
| | | 29 | | Default is 1. |
| | | 30 | | .PARAMETER KnownNetworks |
| | | 31 | | An array of IPNetwork objects representing known networks from which forwarded |
| | | 32 | | headers will be accepted. |
| | | 33 | | .PARAMETER KnownProxies |
| | | 34 | | An array of IPAddress objects representing known proxy servers from which |
| | | 35 | | forwarded headers will be accepted. |
| | | 36 | | .PARAMETER ForwardedForHeaderName |
| | | 37 | | Custom header name for X-Forwarded-For. |
| | | 38 | | .PARAMETER ForwardedProtoHeaderName |
| | | 39 | | Custom header name for X-Forwarded-Proto. |
| | | 40 | | .PARAMETER ForwardedHostHeaderName |
| | | 41 | | Custom header name for X-Forwarded-Host. |
| | | 42 | | .PARAMETER ForwardedPrefixHeaderName |
| | | 43 | | Custom header name for X-Forwarded-Prefix. |
| | | 44 | | .PARAMETER OriginalForHeaderName |
| | | 45 | | Custom header name for Original-For. |
| | | 46 | | .PARAMETER OriginalProtoHeaderName |
| | | 47 | | Custom header name for Original-Proto. |
| | | 48 | | .PARAMETER OriginalHostHeaderName |
| | | 49 | | Custom header name for Original-Host. |
| | | 50 | | .PARAMETER OriginalPrefixHeaderName |
| | | 51 | | Custom header name for Original-Prefix. |
| | | 52 | | .PARAMETER RequireHeaderSymmetry |
| | | 53 | | Switch to require that all enabled forwarded headers are present in the request. |
| | | 54 | | .PARAMETER PassThru |
| | | 55 | | If specified, the cmdlet returns the Kestrun server instance after adding |
| | | 56 | | the middleware. |
| | | 57 | | .EXAMPLE |
| | | 58 | | Add-KrForwardedHeader -XForwardedFor -XForwardedProto -KnownProxies $proxyIps |
| | | 59 | | Adds Forwarded Headers middleware to the current Kestrun server, enabling |
| | | 60 | | processing of X-Forwarded-For and X-Forwarded-Proto headers, and |
| | | 61 | | trusting the specified proxy IP addresses. |
| | | 62 | | .NOTES |
| | | 63 | | This cmdlet is part of the Kestrun PowerShell module. |
| | | 64 | | #> |
| | | 65 | | function Add-KrForwardedHeader { |
| | | 66 | | [KestrunRuntimeApi('Definition')] |
| | | 67 | | [CmdletBinding(DefaultParameterSetName = 'Items')] |
| | | 68 | | [OutputType([Kestrun.Hosting.KestrunHost])] |
| | | 69 | | param( |
| | | 70 | | [Parameter(ValueFromPipeline = $true)] |
| | | 71 | | [Kestrun.Hosting.KestrunHost]$Server, |
| | | 72 | | |
| | | 73 | | # --- ParameterSet: Options (verbatim pass-through) --- |
| | | 74 | | [Parameter(Mandatory = $true, ParameterSetName = 'Options')] |
| | | 75 | | [Microsoft.AspNetCore.Builder.ForwardedHeadersOptions]$Options, |
| | | 76 | | |
| | | 77 | | # --- ParameterSet: Items (switches compose the enum) --- |
| | | 78 | | [Parameter(ParameterSetName = 'Items')] |
| | | 79 | | [switch]$XForwardedFor, |
| | | 80 | | [Parameter(ParameterSetName = 'Items')] |
| | | 81 | | [switch]$XForwardedProto, |
| | | 82 | | [Parameter(ParameterSetName = 'Items')] |
| | | 83 | | [switch]$XForwardedHost, |
| | | 84 | | [Parameter(ParameterSetName = 'Items')] |
| | | 85 | | [switch]$XForwardedPrefix, |
| | | 86 | | [Parameter(ParameterSetName = 'Items')] |
| | | 87 | | [switch]$All, # convenience |
| | | 88 | | |
| | | 89 | | [Parameter(ParameterSetName = 'Items')] |
| | | 90 | | [int]$ForwardLimit = 1, |
| | | 91 | | |
| | | 92 | | [Parameter(ParameterSetName = 'Items')] |
| | | 93 | | [string[]]$KnownNetworks, |
| | | 94 | | [Parameter(ParameterSetName = 'Items')] |
| | | 95 | | [string[]]$KnownProxies, |
| | | 96 | | |
| | | 97 | | # Optional header name overrides (default to ForwardedHeadersDefaults) |
| | | 98 | | [Parameter(ParameterSetName = 'Items')] |
| | | 99 | | [string]$ForwardedForHeaderName, |
| | | 100 | | [Parameter(ParameterSetName = 'Items')] |
| | | 101 | | [string]$ForwardedProtoHeaderName, |
| | | 102 | | [Parameter(ParameterSetName = 'Items')] |
| | | 103 | | [string]$ForwardedHostHeaderName, |
| | | 104 | | [Parameter(ParameterSetName = 'Items')] |
| | | 105 | | [string]$ForwardedPrefixHeaderName, |
| | | 106 | | [Parameter(ParameterSetName = 'Items')] |
| | | 107 | | [string]$OriginalForHeaderName, |
| | | 108 | | [Parameter(ParameterSetName = 'Items')] |
| | | 109 | | [string]$OriginalProtoHeaderName, |
| | | 110 | | [Parameter(ParameterSetName = 'Items')] |
| | | 111 | | [string]$OriginalHostHeaderName, |
| | | 112 | | [Parameter(ParameterSetName = 'Items')] |
| | | 113 | | [string]$OriginalPrefixHeaderName, |
| | | 114 | | |
| | | 115 | | [Parameter(ParameterSetName = 'Items')] |
| | | 116 | | [switch]$RequireHeaderSymmetry, |
| | | 117 | | |
| | | 118 | | [switch]$PassThru |
| | | 119 | | ) |
| | | 120 | | begin { |
| | 0 | 121 | | $Server = Resolve-KestrunServer -Server $Server |
| | | 122 | | } |
| | | 123 | | process { |
| | 0 | 124 | | if ($PSCmdlet.ParameterSetName -eq 'Items') { |
| | | 125 | | # Compose ForwardedHeadersOptions from switches |
| | 0 | 126 | | $fhEnum = [Microsoft.AspNetCore.HttpOverrides.ForwardedHeaders] |
| | | 127 | | |
| | | 128 | | # Create options |
| | 0 | 129 | | $Options = [Microsoft.AspNetCore.Builder.ForwardedHeadersOptions]::new() |
| | | 130 | | # Compose flags from switches |
| | 0 | 131 | | if ($All) { |
| | 0 | 132 | | $Options.ForwardedHeaders = [Microsoft.AspNetCore.HttpOverrides.ForwardedHeaders]::All |
| | | 133 | | } else { |
| | 0 | 134 | | $flags = $fhEnum::None |
| | 0 | 135 | | if ($XForwardedFor) { $flags = $flags -bor $fhEnum::XForwardedFor } |
| | 0 | 136 | | if ($XForwardedProto) { $flags = $flags -bor $fhEnum::XForwardedProto } |
| | 0 | 137 | | if ($XForwardedHost) { $flags = $flags -bor $fhEnum::XForwardedHost } |
| | 0 | 138 | | if ($XForwardedPrefix) { $flags = $flags -bor $fhEnum::XForwardedPrefix } |
| | 0 | 139 | | if ($flags -eq $fhEnum::None) { |
| | | 140 | | # Sensible default if user didn’t specify: For + Proto |
| | 0 | 141 | | $flags = $fhEnum::XForwardedFor -bor $fhEnum::XForwardedProto |
| | | 142 | | } |
| | 0 | 143 | | $Options.ForwardedHeaders = $flags |
| | | 144 | | } |
| | | 145 | | |
| | | 146 | | # Forward limit |
| | 0 | 147 | | $Options.ForwardLimit = $ForwardLimit |
| | | 148 | | |
| | | 149 | | # Known proxies |
| | 0 | 150 | | if ($PSBoundParameters.ContainsKey('KnownProxies')) { |
| | 0 | 151 | | $Options.KnownProxies.Clear() |
| | 0 | 152 | | foreach ($p in $KnownProxies) { |
| | 0 | 153 | | $ip = if ($p -is [System.Net.IPAddress]) { $p } else { [System.Net.IPAddress]::Parse($p) } |
| | 0 | 154 | | [void]$Options.KnownProxies.Add($ip) |
| | | 155 | | } |
| | | 156 | | } |
| | | 157 | | # Known networks |
| | 0 | 158 | | if ($PSBoundParameters.ContainsKey('KnownNetworks')) { |
| | 0 | 159 | | $Options.KnownNetworks.Clear() |
| | 0 | 160 | | foreach ($n in $KnownNetworks) { |
| | | 161 | | # Allow "10.0.0.0/24" or IPNetwork objects directly |
| | 0 | 162 | | $net = if ($n -is [System.Net.IPNetwork]) { $n } else { [System.Net.IPNetwork]::Parse($n) } |
| | 0 | 163 | | [void]$Options.KnownNetworks.Add($net) |
| | | 164 | | } |
| | | 165 | | } |
| | | 166 | | # Custom header names |
| | 0 | 167 | | if ($PSBoundParameters.ContainsKey('ForwardedForHeaderName')) { |
| | 0 | 168 | | $Options.ForwardedForHeaderName = $ForwardedForHeaderName |
| | | 169 | | } |
| | 0 | 170 | | if ($PSBoundParameters.ContainsKey('ForwardedProtoHeaderName')) { |
| | 0 | 171 | | $Options.ForwardedProtoHeaderName = $ForwardedProtoHeaderName |
| | | 172 | | } |
| | 0 | 173 | | if ($PSBoundParameters.ContainsKey('ForwardedHostHeaderName')) { |
| | 0 | 174 | | $Options.ForwardedHostHeaderName = $ForwardedHostHeaderName |
| | | 175 | | } |
| | 0 | 176 | | if ($PSBoundParameters.ContainsKey('ForwardedPrefixHeaderName')) { |
| | 0 | 177 | | $Options.ForwardedPrefixHeaderName = $ForwardedPrefixHeaderName |
| | | 178 | | } |
| | 0 | 179 | | if ($PSBoundParameters.ContainsKey('OriginalForHeaderName')) { |
| | 0 | 180 | | $Options.OriginalForHeaderName = $OriginalForHeaderName |
| | | 181 | | } |
| | 0 | 182 | | if ($PSBoundParameters.ContainsKey('OriginalProtoHeaderName')) { |
| | 0 | 183 | | $Options.OriginalProtoHeaderName = $OriginalProtoHeaderName |
| | | 184 | | } |
| | 0 | 185 | | if ($PSBoundParameters.ContainsKey('OriginalHostHeaderName')) { |
| | 0 | 186 | | $Options.OriginalHostHeaderName = $OriginalHostHeaderName |
| | | 187 | | } |
| | 0 | 188 | | if ($PSBoundParameters.ContainsKey('OriginalPrefixHeaderName')) { |
| | 0 | 189 | | $Options.OriginalPrefixHeaderName = $OriginalPrefixHeaderName |
| | | 190 | | } |
| | | 191 | | |
| | | 192 | | # Symmetry |
| | 0 | 193 | | $Options.RequireHeaderSymmetry = $RequireHeaderSymmetry.IsPresent |
| | | 194 | | } |
| | | 195 | | |
| | | 196 | | # Attach to server so host.Apply() can call app.UseForwardedHeaders($Options) |
| | 0 | 197 | | $Server.ForwardedHeaderOptions = $Options |
| | | 198 | | |
| | 0 | 199 | | if ($PassThru) { return $Server } |
| | | 200 | | } |
| | | 201 | | } |
| | | 202 | | |
| | | 203 | | |