Skip to content

Commit 44ef6da

Browse files
warwickschroederjasontaylordev
authored andcommitted
Add additional options for flexible and secure hosting; SSL/TLS, Reverse Proxy, Direct HTTPS, CORS
1 parent 87e6f95 commit 44ef6da

File tree

44 files changed

+1837
-125
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1837
-125
lines changed

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,3 +101,10 @@ src/scaffolding.config
101101

102102
# Visual Studio Code
103103
.vscode
104+
105+
# AI config
106+
.claude/
107+
CLAUDE.md
108+
109+
# User-specific files
110+
.local

docs/hosting-guide.md

Lines changed: 368 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,368 @@
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

Comments
 (0)