|
| 1 | +# ServiceControl Hosting and Security Guide |
| 2 | + |
| 3 | +This guide covers all hosting and security options available for ServiceControl, ServiceControl.Audit, and ServiceControl.Monitoring instances. |
| 4 | + |
| 5 | +## Configuration Settings by Instance |
| 6 | + |
| 7 | +Each ServiceControl instance uses a different configuration prefix: |
| 8 | + |
| 9 | +| Instance | Configuration Prefix | Default Port | |
| 10 | +|----------|---------------------|--------------| |
| 11 | +| ServiceControl (Primary) | `ServiceControl/` | 33333 | |
| 12 | +| ServiceControl.Audit | `ServiceControl.Audit/` | 44444 | |
| 13 | +| ServiceControl.Monitoring | `Monitoring/` | 33633 | |
| 14 | + |
| 15 | +Settings can be configured via: |
| 16 | + |
| 17 | +- App.config / Web.config files |
| 18 | +- Windows Registry (legacy) |
| 19 | + |
| 20 | +--- |
| 21 | + |
| 22 | +## Hosting Model |
| 23 | + |
| 24 | +ServiceControl runs as a standalone Windows service with Kestrel as the built-in web server. It does not support being hosted inside IIS (in-process hosting). |
| 25 | + |
| 26 | +If you place IIS, nginx, or another web server in front of ServiceControl, it acts as a **reverse proxy** forwarding requests to Kestrel. |
| 27 | + |
| 28 | +--- |
| 29 | + |
| 30 | +## Deployment Scenarios |
| 31 | + |
| 32 | +### Scenario 1: Default Configuration |
| 33 | + |
| 34 | +The default configuration with no additional setup required. Backwards compatible with existing deployments. |
| 35 | + |
| 36 | +**Security Features:** |
| 37 | + |
| 38 | +| Feature | Status | |
| 39 | +|---------|--------| |
| 40 | +| JWT Authentication | ❌ Disabled | |
| 41 | +| Kestrel HTTPS | ❌ Disabled | |
| 42 | +| HTTPS Redirection | ❌ Disabled | |
| 43 | +| HSTS | ❌ Disabled | |
| 44 | +| Restricted CORS Origins | ❌ Disabled (any origin) | |
| 45 | +| Forwarded Headers | ✅ Enabled (trusts all) | |
| 46 | +| Restricted Proxy Trust | ❌ Disabled | |
| 47 | + |
| 48 | +```xml |
| 49 | +<!-- No configuration needed - defaults work out of the box --> |
| 50 | +<!-- Authentication: disabled by default --> |
| 51 | +<!-- CORS: allows any origin by default --> |
| 52 | +<!-- HTTPS: disabled by default (HTTP only) --> |
| 53 | +<!-- Forwarded Headers: enabled and trusts all proxies by default --> |
| 54 | +``` |
| 55 | + |
| 56 | +Or explicitly: |
| 57 | + |
| 58 | +```xml |
| 59 | +<appSettings> |
| 60 | + <add key="ServiceControl/Authentication.Enabled" value="false" /> |
| 61 | + <add key="ServiceControl/Https.Enabled" value="false" /> |
| 62 | + <add key="ServiceControl/Cors.AllowAnyOrigin" value="true" /> |
| 63 | + <add key="ServiceControl/ForwardedHeaders.Enabled" value="true" /> |
| 64 | + <add key="ServiceControl/ForwardedHeaders.TrustAllProxies" value="true" /> |
| 65 | +</appSettings> |
| 66 | +``` |
| 67 | + |
| 68 | +--- |
| 69 | + |
| 70 | +### Scenario 2: Reverse Proxy with SSL Termination |
| 71 | + |
| 72 | +ServiceControl behind a reverse proxy (nginx, IIS, cloud load balancer) that handles SSL/TLS termination. |
| 73 | + |
| 74 | +**Security Features:** |
| 75 | + |
| 76 | +| Feature | Status | |
| 77 | +|---------|--------| |
| 78 | +| JWT Authentication | ❌ Disabled | |
| 79 | +| Kestrel HTTPS | ❌ Disabled (handled by proxy) | |
| 80 | +| HTTPS Redirection | ❌ Disabled (handled by proxy) | |
| 81 | +| HSTS | ❌ Disabled (handled by proxy) | |
| 82 | +| Restricted CORS Origins | ✅ Enabled | |
| 83 | +| Forwarded Headers | ✅ Enabled | |
| 84 | +| Restricted Proxy Trust | ✅ Enabled | |
| 85 | + |
| 86 | +```xml |
| 87 | +<appSettings> |
| 88 | + <!-- Trust forwarded headers from your reverse proxy --> |
| 89 | + <add key="ServiceControl/ForwardedHeaders.Enabled" value="true" /> |
| 90 | + <add key="ServiceControl/ForwardedHeaders.TrustAllProxies" value="false" /> |
| 91 | + <add key="ServiceControl/ForwardedHeaders.KnownProxies" value="10.0.0.5,10.0.0.6" /> |
| 92 | + |
| 93 | + <!-- Or use CIDR notation for networks --> |
| 94 | + <!-- <add key="ServiceControl/ForwardedHeaders.KnownNetworks" value="10.0.0.0/24" /> --> |
| 95 | + |
| 96 | + <!-- Restrict CORS to your ServicePulse domain --> |
| 97 | + <add key="ServiceControl/Cors.AllowedOrigins" value="https://servicepulse.yourcompany.com" /> |
| 98 | +</appSettings> |
| 99 | +``` |
| 100 | + |
| 101 | +--- |
| 102 | + |
| 103 | +### Scenario 3: Direct HTTPS (No Reverse Proxy) |
| 104 | + |
| 105 | +Kestrel handles TLS directly without a reverse proxy. |
| 106 | + |
| 107 | +**Security Features:** |
| 108 | + |
| 109 | +| Feature | Status | |
| 110 | +|---------|--------| |
| 111 | +| JWT Authentication | ❌ Disabled | |
| 112 | +| Kestrel HTTPS | ✅ Enabled | |
| 113 | +| HTTPS Redirection | ✅ Enabled | |
| 114 | +| HSTS | ✅ Enabled | |
| 115 | +| Restricted CORS Origins | ✅ Enabled | |
| 116 | +| Forwarded Headers | ❌ Disabled (no proxy) | |
| 117 | +| Restricted Proxy Trust | N/A | |
| 118 | + |
| 119 | +```xml |
| 120 | +<appSettings> |
| 121 | + <!-- Enable Kestrel HTTPS --> |
| 122 | + <add key="ServiceControl/Https.Enabled" value="true" /> |
| 123 | + <add key="ServiceControl/Https.CertificatePath" value="C:\certs\servicecontrol.pfx" /> |
| 124 | + <add key="ServiceControl/Https.CertificatePassword" value="your-certificate-password" /> |
| 125 | + |
| 126 | + <!-- Enable HSTS for browsers --> |
| 127 | + <add key="ServiceControl/Https.EnableHsts" value="true" /> |
| 128 | + <add key="ServiceControl/Https.HstsMaxAgeSeconds" value="31536000" /> |
| 129 | + |
| 130 | + <!-- Redirect HTTP to HTTPS --> |
| 131 | + <add key="ServiceControl/Https.RedirectHttpToHttps" value="true" /> |
| 132 | + |
| 133 | + <!-- Restrict CORS --> |
| 134 | + <add key="ServiceControl/Cors.AllowedOrigins" value="https://servicepulse.yourcompany.com" /> |
| 135 | + |
| 136 | + <!-- Disable forwarded headers (no proxy) --> |
| 137 | + <add key="ServiceControl/ForwardedHeaders.Enabled" value="false" /> |
| 138 | +</appSettings> |
| 139 | +``` |
| 140 | + |
| 141 | +--- |
| 142 | + |
| 143 | +### Scenario 4: Reverse Proxy with Authentication |
| 144 | + |
| 145 | +Reverse proxy with SSL termination and JWT authentication via an identity provider (Azure AD, Okta, Auth0, Keycloak, etc.). |
| 146 | + |
| 147 | +**Security Features:** |
| 148 | + |
| 149 | +| Feature | Status | |
| 150 | +|---------|--------| |
| 151 | +| JWT Authentication | ✅ Enabled | |
| 152 | +| Kestrel HTTPS | ❌ Disabled (handled by proxy) | |
| 153 | +| HTTPS Redirection | ❌ Disabled (handled by proxy) | |
| 154 | +| HSTS | ❌ Disabled (handled by proxy) | |
| 155 | +| Restricted CORS Origins | ✅ Enabled | |
| 156 | +| Forwarded Headers | ✅ Enabled | |
| 157 | +| Restricted Proxy Trust | ✅ Enabled | |
| 158 | + |
| 159 | +```xml |
| 160 | +<appSettings> |
| 161 | + <!-- Authentication --> |
| 162 | + <add key="ServiceControl/Authentication.Enabled" value="true" /> |
| 163 | + <add key="ServiceControl/Authentication.Authority" value="https://login.microsoftonline.com/{tenant-id}/v2.0" /> |
| 164 | + <add key="ServiceControl/Authentication.Audience" value="api://servicecontrol" /> |
| 165 | + |
| 166 | + <!-- Token validation settings --> |
| 167 | + <add key="ServiceControl/Authentication.ValidateIssuer" value="true" /> |
| 168 | + <add key="ServiceControl/Authentication.ValidateAudience" value="true" /> |
| 169 | + <add key="ServiceControl/Authentication.ValidateLifetime" value="true" /> |
| 170 | + <add key="ServiceControl/Authentication.ValidateIssuerSigningKey" value="true" /> |
| 171 | + <add key="ServiceControl/Authentication.RequireHttpsMetadata" value="true" /> |
| 172 | + |
| 173 | + <!-- ServicePulse client configuration --> |
| 174 | + <add key="ServiceControl/Authentication.ServicePulse.ClientId" value="your-servicepulse-client-id" /> |
| 175 | + <add key="ServiceControl/Authentication.ServicePulse.Authority" value="https://login.microsoftonline.com/{tenant-id}/v2.0" /> |
| 176 | + <add key="ServiceControl/Authentication.ServicePulse.ApiScopes" value="api://servicecontrol/.default" /> |
| 177 | + |
| 178 | + <!-- Forwarded headers for reverse proxy --> |
| 179 | + <add key="ServiceControl/ForwardedHeaders.Enabled" value="true" /> |
| 180 | + <add key="ServiceControl/ForwardedHeaders.TrustAllProxies" value="false" /> |
| 181 | + <add key="ServiceControl/ForwardedHeaders.KnownNetworks" value="10.0.0.0/8" /> |
| 182 | + |
| 183 | + <!-- Restrict CORS --> |
| 184 | + <add key="ServiceControl/Cors.AllowedOrigins" value="https://servicepulse.yourcompany.com" /> |
| 185 | +</appSettings> |
| 186 | +``` |
| 187 | + |
| 188 | +> **Note:** The token validation settings (`ValidateIssuer`, `ValidateAudience`, `ValidateLifetime`, `ValidateIssuerSigningKey`, `RequireHttpsMetadata`) all default to `true`. |
| 189 | +
|
| 190 | +--- |
| 191 | + |
| 192 | +### Scenario 5: Direct HTTPS with Authentication |
| 193 | + |
| 194 | +Kestrel handles TLS directly with JWT authentication. No reverse proxy. |
| 195 | + |
| 196 | +**Security Features:** |
| 197 | + |
| 198 | +| Feature | Status | |
| 199 | +|---------|--------| |
| 200 | +| JWT Authentication | ✅ Enabled | |
| 201 | +| Kestrel HTTPS | ✅ Enabled | |
| 202 | +| HTTPS Redirection | ✅ Enabled | |
| 203 | +| HSTS | ✅ Enabled | |
| 204 | +| Restricted CORS Origins | ✅ Enabled | |
| 205 | +| Forwarded Headers | ❌ Disabled (no proxy) | |
| 206 | +| Restricted Proxy Trust | N/A | |
| 207 | + |
| 208 | +```xml |
| 209 | +<appSettings> |
| 210 | + <!-- Kestrel HTTPS --> |
| 211 | + <add key="ServiceControl/Https.Enabled" value="true" /> |
| 212 | + <add key="ServiceControl/Https.CertificatePath" value="C:\certs\servicecontrol.pfx" /> |
| 213 | + <add key="ServiceControl/Https.CertificatePassword" value="your-certificate-password" /> |
| 214 | + <add key="ServiceControl/Https.EnableHsts" value="true" /> |
| 215 | + <add key="ServiceControl/Https.HstsMaxAgeSeconds" value="31536000" /> |
| 216 | + <add key="ServiceControl/Https.RedirectHttpToHttps" value="true" /> |
| 217 | + |
| 218 | + <!-- Authentication --> |
| 219 | + <add key="ServiceControl/Authentication.Enabled" value="true" /> |
| 220 | + <add key="ServiceControl/Authentication.Authority" value="https://login.microsoftonline.com/{tenant-id}/v2.0" /> |
| 221 | + <add key="ServiceControl/Authentication.Audience" value="api://servicecontrol" /> |
| 222 | + <add key="ServiceControl/Authentication.ValidateIssuer" value="true" /> |
| 223 | + <add key="ServiceControl/Authentication.ValidateAudience" value="true" /> |
| 224 | + <add key="ServiceControl/Authentication.ValidateLifetime" value="true" /> |
| 225 | + <add key="ServiceControl/Authentication.ValidateIssuerSigningKey" value="true" /> |
| 226 | + <add key="ServiceControl/Authentication.RequireHttpsMetadata" value="true" /> |
| 227 | + |
| 228 | + <!-- ServicePulse client --> |
| 229 | + <add key="ServiceControl/Authentication.ServicePulse.ClientId" value="your-servicepulse-client-id" /> |
| 230 | + <add key="ServiceControl/Authentication.ServicePulse.Authority" value="https://login.microsoftonline.com/{tenant-id}/v2.0" /> |
| 231 | + <add key="ServiceControl/Authentication.ServicePulse.ApiScopes" value="api://servicecontrol/.default" /> |
| 232 | + |
| 233 | + <!-- Restrict CORS --> |
| 234 | + <add key="ServiceControl/Cors.AllowedOrigins" value="https://servicepulse.yourcompany.com" /> |
| 235 | + |
| 236 | + <!-- No forwarded headers (no proxy) --> |
| 237 | + <add key="ServiceControl/ForwardedHeaders.Enabled" value="false" /> |
| 238 | +</appSettings> |
| 239 | +``` |
| 240 | + |
| 241 | +--- |
| 242 | + |
| 243 | +### Scenario 6: End-to-End Encryption with Reverse Proxy and Authentication |
| 244 | + |
| 245 | +End-to-end TLS encryption where the reverse proxy terminates external TLS and re-encrypts traffic to ServiceControl over HTTPS. Includes JWT authentication. |
| 246 | + |
| 247 | +**Security Features:** |
| 248 | + |
| 249 | +| Feature | Status | |
| 250 | +|---------|--------| |
| 251 | +| JWT Authentication | ✅ Enabled | |
| 252 | +| Kestrel HTTPS | ✅ Enabled | |
| 253 | +| HTTPS Redirection | ❌ Disabled (handled by proxy) | |
| 254 | +| HSTS | ❌ Disabled (handled by proxy) | |
| 255 | +| Restricted CORS Origins | ✅ Enabled | |
| 256 | +| Forwarded Headers | ✅ Enabled | |
| 257 | +| Restricted Proxy Trust | ✅ Enabled | |
| 258 | + |
| 259 | +```xml |
| 260 | +<appSettings> |
| 261 | + <!-- Kestrel HTTPS for internal encryption --> |
| 262 | + <add key="ServiceControl/Https.Enabled" value="true" /> |
| 263 | + <add key="ServiceControl/Https.CertificatePath" value="C:\certs\servicecontrol-internal.pfx" /> |
| 264 | + <add key="ServiceControl/Https.CertificatePassword" value="your-certificate-password" /> |
| 265 | + |
| 266 | + <!-- HSTS/Redirection handled by reverse proxy --> |
| 267 | + <add key="ServiceControl/Https.EnableHsts" value="false" /> |
| 268 | + <add key="ServiceControl/Https.RedirectHttpToHttps" value="false" /> |
| 269 | + |
| 270 | + <!-- Authentication --> |
| 271 | + <add key="ServiceControl/Authentication.Enabled" value="true" /> |
| 272 | + <add key="ServiceControl/Authentication.Authority" value="https://login.microsoftonline.com/{tenant-id}/v2.0" /> |
| 273 | + <add key="ServiceControl/Authentication.Audience" value="api://servicecontrol" /> |
| 274 | + <add key="ServiceControl/Authentication.ValidateIssuer" value="true" /> |
| 275 | + <add key="ServiceControl/Authentication.ValidateAudience" value="true" /> |
| 276 | + <add key="ServiceControl/Authentication.ValidateLifetime" value="true" /> |
| 277 | + <add key="ServiceControl/Authentication.ValidateIssuerSigningKey" value="true" /> |
| 278 | + <add key="ServiceControl/Authentication.RequireHttpsMetadata" value="true" /> |
| 279 | + |
| 280 | + <!-- ServicePulse client --> |
| 281 | + <add key="ServiceControl/Authentication.ServicePulse.ClientId" value="your-servicepulse-client-id" /> |
| 282 | + <add key="ServiceControl/Authentication.ServicePulse.Authority" value="https://login.microsoftonline.com/{tenant-id}/v2.0" /> |
| 283 | + <add key="ServiceControl/Authentication.ServicePulse.ApiScopes" value="api://servicecontrol/.default" /> |
| 284 | + |
| 285 | + <!-- Forwarded headers - trust only your reverse proxy --> |
| 286 | + <add key="ServiceControl/ForwardedHeaders.Enabled" value="true" /> |
| 287 | + <add key="ServiceControl/ForwardedHeaders.TrustAllProxies" value="false" /> |
| 288 | + <add key="ServiceControl/ForwardedHeaders.KnownProxies" value="10.0.0.5" /> |
| 289 | + |
| 290 | + <!-- Restrict CORS --> |
| 291 | + <add key="ServiceControl/Cors.AllowedOrigins" value="https://servicepulse.yourcompany.com" /> |
| 292 | +</appSettings> |
| 293 | +``` |
| 294 | + |
| 295 | +--- |
| 296 | + |
| 297 | +## Configuration Reference |
| 298 | + |
| 299 | +### Authentication Settings |
| 300 | + |
| 301 | +| Setting | Type | Default | Description | |
| 302 | +|---------|------|---------|-------------| |
| 303 | +| `Authentication.Enabled` | bool | `false` | Enable JWT Bearer authentication | |
| 304 | +| `Authentication.Authority` | string | - | OpenID Connect authority URL (required when enabled) | |
| 305 | +| `Authentication.Audience` | string | - | Expected audience for tokens (required when enabled) | |
| 306 | +| `Authentication.ValidateIssuer` | bool | `true` | Validate token issuer | |
| 307 | +| `Authentication.ValidateAudience` | bool | `true` | Validate token audience | |
| 308 | +| `Authentication.ValidateLifetime` | bool | `true` | Validate token expiration | |
| 309 | +| `Authentication.ValidateIssuerSigningKey` | bool | `true` | Validate token signing key | |
| 310 | +| `Authentication.RequireHttpsMetadata` | bool | `true` | Require HTTPS for metadata endpoint | |
| 311 | +| `Authentication.ServicePulse.ClientId` | string | - | OAuth client ID for ServicePulse | |
| 312 | +| `Authentication.ServicePulse.Authority` | string | - | Authority URL for ServicePulse (defaults to main Authority) | |
| 313 | +| `Authentication.ServicePulse.ApiScopes` | string | - | API scopes for ServicePulse to request | |
| 314 | + |
| 315 | +### HTTPS Settings |
| 316 | + |
| 317 | +| Setting | Type | Default | Description | |
| 318 | +|---------|------|---------|-------------| |
| 319 | +| `Https.Enabled` | bool | `false` | Enable Kestrel HTTPS with certificate | |
| 320 | +| `Https.CertificatePath` | string | - | Path to PFX/PEM certificate file | |
| 321 | +| `Https.CertificatePassword` | string | - | Certificate password (if required) | |
| 322 | +| `Https.RedirectHttpToHttps` | bool | `false` | Redirect HTTP requests to HTTPS | |
| 323 | +| `Https.EnableHsts` | bool | `false` | Enable HTTP Strict Transport Security | |
| 324 | +| `Https.HstsMaxAgeSeconds` | int | `31536000` | HSTS max-age in seconds (1 year) | |
| 325 | +| `Https.HstsIncludeSubDomains` | bool | `false` | Include subdomains in HSTS | |
| 326 | + |
| 327 | +### Forwarded Headers Settings |
| 328 | + |
| 329 | +| Setting | Type | Default | Description | |
| 330 | +|---------|------|---------|-------------| |
| 331 | +| `ForwardedHeaders.Enabled` | bool | `true` | Enable forwarded headers processing | |
| 332 | +| `ForwardedHeaders.TrustAllProxies` | bool | `true` | Trust X-Forwarded-* from any source | |
| 333 | +| `ForwardedHeaders.KnownProxies` | string | - | Comma-separated list of trusted proxy IPs | |
| 334 | +| `ForwardedHeaders.KnownNetworks` | string | - | Comma-separated list of trusted CIDR networks | |
| 335 | + |
| 336 | +> **Note:** If `KnownProxies` or `KnownNetworks` are configured, `TrustAllProxies` is automatically set to `false`. |
| 337 | +
|
| 338 | +### CORS Settings |
| 339 | + |
| 340 | +| Setting | Type | Default | Description | |
| 341 | +|---------|------|---------|-------------| |
| 342 | +| `Cors.AllowAnyOrigin` | bool | `true` | Allow requests from any origin | |
| 343 | +| `Cors.AllowedOrigins` | string | - | Comma-separated list of allowed origins | |
| 344 | + |
| 345 | +> **Note:** If `AllowedOrigins` is configured, `AllowAnyOrigin` is automatically set to `false`. |
| 346 | +
|
| 347 | +--- |
| 348 | + |
| 349 | +## Scenario Comparison Matrix |
| 350 | + |
| 351 | +| Feature | Default | Reverse Proxy (SSL Termination) | Direct HTTPS | Reverse Proxy + Auth | Direct HTTPS + Auth | End-to-End Encryption + Auth | |
| 352 | +|---------|:-------:|:-------------------------------:|:------------:|:--------------------:|:-------------------:|:----------------------------:| |
| 353 | +| **JWT Authentication** | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | |
| 354 | +| **Direct (Kestrel) HTTPS** | ❌ | ❌ | ✅ | ❌ | ✅ | ✅ | |
| 355 | +| **HTTPS Redirection** | ❌ | ❌ | ✅ | ❌ | ✅ | ❌ (Handled by Reverse Proxy) | |
| 356 | +| **HSTS** | ❌ | ❌ | ✅ | ❌ | ✅ | ❌ (Handled by Reverse Proxy) | |
| 357 | +| **Restricted CORS** | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | |
| 358 | +| **Forwarded Headers** | ✅ | ✅ | ❌ | ✅ | ❌ (Not needed. No Reverse Proxy) | ✅ | |
| 359 | +| **Restricted Proxy Trust** | ❌ | ✅ | N/A | ✅ | N/A | ✅ | |
| 360 | +| | | | | | | | |
| 361 | +| **Reverse Proxy** | Optional | Yes | No | Yes | No | Yes | |
| 362 | +| **Internal Traffic Encrypted** | ❌ | ❌ | ✅ | ❌ | ✅ | ✅ | |
| 363 | + |
| 364 | +**Legend:** |
| 365 | + |
| 366 | +- ✅ = Enabled |
| 367 | +- ❌ = Disabled |
| 368 | +- N/A = Not Applicable |
0 commit comments