Skip to content

Commit e80fd13

Browse files
Merge pull request #33 from Particular/genxp-3600-security
Add example for a secure compose deployment of the platform
2 parents b2cfe96 + f5b2894 commit e80fd13

2 files changed

Lines changed: 267 additions & 13 deletions

File tree

docker-compose/README.md

Lines changed: 116 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,28 +7,131 @@ Running ServiceControl and ServicePulse locally in containers provides a way to
77
## Usage
88

99
**Pull the latest images:** Before running the containers, ensure you're using the latest version of each image by executing the following command:
10-
```shell
10+
11+
```pwsh
1112
docker compose pull
1213
```
13-
This command checks for any updates to the images specified in the docker-compose.yml file and pulls them if available.
14+
15+
This command checks for any updates to the images specified in the docker-compose.yml file and pulls them if available.
1416

1517
**Start the containers:** After pulling the latest images, modify the [environment file](.env), if necessary, and then start up the containers using:
16-
```shell
18+
19+
```pwsh
1720
docker compose up -d
1821
```
1922

2023
Once composed:
2124

22-
* [ServicePulse](https://docs.particular.net/servicepulse/) can be accessed at http://localhost:9090
23-
* [ServiceInsight](https://docs.particular.net/serviceinsight/) can be used with a connection URL of http://localhost:33333/api
25+
- [ServicePulse](https://docs.particular.net/servicepulse/) can be accessed at http://localhost:9090
26+
- [ServiceInsight](https://docs.particular.net/serviceinsight/) can be used with a connection URL of http://localhost:33333/api
2427

2528
## Implementation details
2629

27-
* The ports for all services are exposed to localhost:
28-
* `33333`: ServiceControl API
29-
* `44444`: Audit API
30-
* `33633`: Monitoring API
31-
* `8080`: Database backend
32-
* `9090` ServicePulse UI
33-
* One instance of the [`servicecontrol-ravendb` container](https://docs.particular.net/servicecontrol/ravendb/containers) is used for both the [`servicecontrol`](https://docs.particular.net/servicecontrol/servicecontrol-instances/deployment/containers) and [`servicecontrol-audit`](https://docs.particular.net/servicecontrol/audit-instances/deployment/containers) containers.
34-
* _A single database container should not be shared between multiple ServiceControl instances in production scenarios._
30+
- The ports for all services are exposed to localhost:
31+
- `33333`: ServiceControl API
32+
- `44444`: Audit API
33+
- `33633`: Monitoring API
34+
- `8080`: Database backend
35+
- `9090` ServicePulse UI
36+
- One instance of the [`servicecontrol-ravendb` container](https://docs.particular.net/servicecontrol/ravendb/containers) is used for both the [`servicecontrol`](https://docs.particular.net/servicecontrol/servicecontrol-instances/deployment/containers) and [`servicecontrol-audit`](https://docs.particular.net/servicecontrol/audit-instances/deployment/containers) containers.
37+
- _A single database container should not be shared between multiple ServiceControl instances in production scenarios._
38+
39+
## Running with HTTPS and authentication
40+
41+
The `compose-secure.yml` file provides a configuration with HTTPS enabled and OAuth2/OIDC authentication using Microsoft Entra ID (Azure AD).
42+
43+
### Prerequisites
44+
45+
1. **SSL Certificate**: A PFX certificate file for HTTPS
46+
2. **CA Bundle**: A CA certificate bundle for validating the identity provider's certificates
47+
3. **Microsoft Entra ID App Registration**: Configure an app registration for authentication
48+
49+
> [!NOTE]
50+
> The [PFX file](#generate-a-pfx-certificate-for-local-testing-only) contains the private key and certificate for the service to **serve** HTTPS. The [CA bundle](#generate-a-ca-bundle-for-local-testing-only) contains only public CA certificates for the service to **verify** other services' certificates. Both are required when containers communicate over HTTPS.
51+
52+
### Configuration
53+
54+
Add the following variables to your `.env` file and replace the `{placeholder}` values with your actual configuration:
55+
56+
```text
57+
CERTIFICATE_PASSWORD="{password}"
58+
CERTIFICATE_PATH="./certs/servicecontrol.pfx"
59+
CA_BUNDLE_PATH="./certs/ca-bundle.crt"
60+
IDP_AUTHORITY="https://login.microsoftonline.com/{tenant-id}"
61+
SERVICECONTROL_AUDIENCE="api://{servicecontrol-client-id}"
62+
SERVICEPULSE_CLIENTID="{servicepulse-client-id}"
63+
SERVICEPULSE_APISCOPES=["api://{servicecontrol-client-id}/{scope-name}"]
64+
```
65+
66+
| Variable | Description |
67+
|---------------------------|------------------------------------------------------------------------------------------|
68+
| `CERTIFICATE_PASSWORD` | Password for the PFX certificate (e.g., the password used when generating with mkcert) |
69+
| `CERTIFICATE_PATH` | Path to the PFX certificate file (e.g., `./certs/servicecontrol.pfx`) |
70+
| `CA_BUNDLE_PATH` | Path to the CA bundle file (e.g., `./certs/ca-bundle.crt`) |
71+
| `IDP_AUTHORITY` | Microsoft Entra ID authority URL (e.g., `https://login.microsoftonline.com/{tenant-id}`) |
72+
| `SERVICECONTROL_AUDIENCE` | Application ID URI from ServiceControl app registration (e.g., `api://{servicecontrol-client-id}`) |
73+
| `SERVICEPULSE_CLIENTID` | Application (client) ID from ServicePulse app registration AD |
74+
| `SERVICEPULSE_APISCOPES` | Array of API scopes ServicePulse should request when calling ServiceControl (e.g., ["api://{servicecontrol-client-id}/{scope-name}"]) |
75+
76+
#### Generate a PFX Certificate for Local Testing Only
77+
78+
> [!WARNING]
79+
> The certificate generated below is for local testing only. Use a certificate from a trusted Certificate Authority for production deployments.
80+
81+
The below assume the `mkcert` tool has been installed.
82+
83+
> [!IMPORTANT]
84+
> The certificate must include every hostname that will be used to access a service over HTTPS. In a Docker Compose network, containers reach each other using service names as hostnames (e.g., `https://servicecontrol:33333`). During the TLS handshake the client checks that the server's certificate contains a [Subject Alternative Name](https://en.wikipedia.org/wiki/Subject_Alternative_Name) (SAN) matching the hostname it connected to. If the name is missing, the connection is rejected.
85+
86+
```pwsh
87+
# Install mkcert's root CA (one-time setup)
88+
mkcert -install
89+
90+
# Navigate/create a folder to store the certificates. e.g.
91+
mkdir certs
92+
cd certs
93+
94+
# Generate PFX certificate for localhost/servicecontrol instances
95+
mkcert -p12-file servicecontrol.pfx -pkcs12 localhost 127.0.0.1 ::1 servicecontrol servicecontrol-audit servicecontrol-monitoring
96+
```
97+
98+
#### Generate a CA Bundle for Local Testing Only
99+
100+
> [!WARNING]
101+
> The CA bundle generated below is for local testing only. Use certificates from a trusted Certificate Authority for production deployments.
102+
103+
When running ServiceControl in Docker containers, each container needs a CA bundle file to trust certificates presented by other services. The `SSL_CERT_FILE` environment variable tells .NET where to find this bundle.
104+
105+
#### What is a CA Bundle?
106+
107+
A CA bundle is a file containing one or more Certificate Authority (CA) certificates. When a container makes an HTTPS request to another service, it uses this bundle to verify the server's certificate chain. Without it, containers would reject connections to services using your mkcert certificates. Unlike your host machine (where `mkcert -install` adds the CA to the system trust store), Docker containers don't share the host's trust store. You must explicitly provide the CA certificates that containers should trust.
108+
109+
#### Generate the CA bundle
110+
111+
```pwsh
112+
# Get the mkcert CA root location
113+
$CA_ROOT = mkcert -CAROOT
114+
115+
# Navigate to the folder containing the PFX certificate
116+
cd certs
117+
118+
# For local development only (just mkcert CA)
119+
copy "$CA_ROOT/rootCA.pem" ca-bundle.crt
120+
```
121+
122+
### Pull the latest images
123+
Before running the containers, ensure you're using the latest version of each image by executing the following command:
124+
125+
```pwsh
126+
docker compose -f compose-secure.yml pull
127+
```
128+
129+
### Starting the secure containers
130+
131+
```pwsh
132+
docker compose -f compose-secure.yml up -d
133+
```
134+
135+
Once composed:
136+
137+
- [ServicePulse](https://docs.particular.net/servicepulse/) can be accessed at https://localhost:9090

docker-compose/compose-secure.yml

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
name: service-platform
2+
3+
services:
4+
servicecontrol:
5+
image: particular/servicecontrol:${SERVICECONTROL_TAG}
6+
env_file: .env
7+
ports:
8+
- "33333:33333"
9+
environment:
10+
RAVENDB_CONNECTIONSTRING: http://servicecontrol-db:8080
11+
REMOTEINSTANCES: '[{"api_uri":"https://servicecontrol-audit:44444/api"}]'
12+
SERVICECONTROL_HTTPS_ENABLED: "true"
13+
SERVICECONTROL_HTTPS_CERTIFICATEPATH: "/usr/share/ParticularSoftware/certificate.pfx"
14+
SERVICECONTROL_HTTPS_CERTIFICATEPASSWORD: "${CERTIFICATE_PASSWORD}"
15+
SSL_CERT_FILE: "/etc/ssl/certs/ca-bundle.crt"
16+
SERVICECONTROL_AUTHENTICATION_ENABLED: "true"
17+
SERVICECONTROL_AUTHENTICATION_AUTHORITY: "${IDP_AUTHORITY}"
18+
SERVICECONTROL_AUTHENTICATION_AUDIENCE: "${SERVICECONTROL_AUDIENCE}"
19+
SERVICECONTROL_AUTHENTICATION_SERVICEPULSE_CLIENTID: "${SERVICEPULSE_CLIENTID}"
20+
SERVICECONTROL_AUTHENTICATION_SERVICEPULSE_AUTHORITY: "${IDP_AUTHORITY}/v2.0"
21+
SERVICECONTROL_AUTHENTICATION_SERVICEPULSE_APISCOPES: '${SERVICEPULSE_APISCOPES}'
22+
command: --setup-and-run
23+
restart: unless-stopped
24+
volumes:
25+
- ${CERTIFICATE_PATH}:/usr/share/ParticularSoftware/certificate.pfx
26+
- ${CA_BUNDLE_PATH}:/etc/ssl/certs/ca-bundle.crt:ro
27+
healthcheck:
28+
test: ["CMD", "/healthcheck/healthcheck", "https://localhost:33333/api"]
29+
interval: 30s
30+
timeout: 10s
31+
start_period: 60s
32+
retries: 3
33+
depends_on:
34+
servicecontrol-db:
35+
condition: service_healthy
36+
rabbitmq:
37+
condition: service_healthy
38+
39+
# WARNING: A single database container should not be shared between multiple ServiceControl instances in production scenarios.
40+
servicecontrol-db:
41+
image: particular/servicecontrol-ravendb:${SERVICECONTROL_TAG}
42+
ports:
43+
- "8080:8080"
44+
volumes:
45+
- raven-config:/var/lib/ravendb/config
46+
- raven-data:/var/lib/ravendb/data
47+
48+
servicecontrol-audit:
49+
image: particular/servicecontrol-audit:${SERVICECONTROL_TAG}
50+
env_file: .env
51+
ports:
52+
- "44444:44444"
53+
environment:
54+
RAVENDB_CONNECTIONSTRING: http://servicecontrol-db:8080
55+
SERVICECONTROLQUEUEADDRESS: Particular.ServiceControl
56+
SERVICECONTROL_AUDIT_HTTPS_ENABLED: "true"
57+
SERVICECONTROL_AUDIT_HTTPS_CERTIFICATEPATH: "/usr/share/ParticularSoftware/certificate.pfx"
58+
SERVICECONTROL_AUDIT_HTTPS_CERTIFICATEPASSWORD: "${CERTIFICATE_PASSWORD}"
59+
SSL_CERT_FILE: "/etc/ssl/certs/ca-bundle.crt"
60+
SERVICECONTROL_AUDIT_AUTHENTICATION_ENABLED: "true"
61+
SERVICECONTROL_AUDIT_AUTHENTICATION_AUTHORITY: "${IDP_AUTHORITY}"
62+
SERVICECONTROL_AUDIT_AUTHENTICATION_AUDIENCE: "${SERVICECONTROL_AUDIENCE}"
63+
command: --setup-and-run
64+
restart: unless-stopped
65+
volumes:
66+
- ${CERTIFICATE_PATH}:/usr/share/ParticularSoftware/certificate.pfx
67+
- ${CA_BUNDLE_PATH}:/etc/ssl/certs/ca-bundle.crt:ro
68+
healthcheck:
69+
test: ["CMD", "/healthcheck/healthcheck", "https://localhost:44444/api"]
70+
interval: 30s
71+
timeout: 10s
72+
start_period: 60s
73+
retries: 3
74+
depends_on:
75+
servicecontrol-db:
76+
condition: service_healthy
77+
rabbitmq:
78+
condition: service_healthy
79+
80+
servicecontrol-monitoring:
81+
image: particular/servicecontrol-monitoring:${SERVICECONTROL_TAG}
82+
env_file: .env
83+
environment:
84+
MONITORING_HTTPS_ENABLED: "true"
85+
MONITORING_HTTPS_CERTIFICATEPATH: "/usr/share/ParticularSoftware/certificate.pfx"
86+
MONITORING_HTTPS_CERTIFICATEPASSWORD: "${CERTIFICATE_PASSWORD}"
87+
SSL_CERT_FILE: "/etc/ssl/certs/ca-bundle.crt"
88+
MONITORING_AUTHENTICATION_ENABLED: "true"
89+
MONITORING_AUTHENTICATION_AUTHORITY: "${IDP_AUTHORITY}"
90+
MONITORING_AUTHENTICATION_AUDIENCE: "${SERVICECONTROL_AUDIENCE}"
91+
restart: unless-stopped
92+
command: --setup-and-run
93+
ports:
94+
- "33633:33633"
95+
volumes:
96+
- ${CERTIFICATE_PATH}:/usr/share/ParticularSoftware/certificate.pfx
97+
- ${CA_BUNDLE_PATH}:/etc/ssl/certs/ca-bundle.crt:ro
98+
healthcheck:
99+
test: ["CMD", "/healthcheck/healthcheck", "https://localhost:33633/"]
100+
interval: 30s
101+
timeout: 10s
102+
start_period: 60s
103+
retries: 3
104+
depends_on:
105+
rabbitmq:
106+
condition: service_healthy
107+
108+
servicepulse:
109+
image: particular/servicepulse:${SERVICEPULSE_TAG}
110+
ports:
111+
- "9090:9090"
112+
environment:
113+
SERVICECONTROL_URL: https://servicecontrol:33333
114+
MONITORING_URL: https://servicecontrol-monitoring:33633
115+
SERVICEPULSE_HTTPS_ENABLED: "true"
116+
SERVICEPULSE_HTTPS_CERTIFICATEPATH: "/usr/share/ParticularSoftware/certificate.pfx"
117+
SERVICEPULSE_HTTPS_CERTIFICATEPASSWORD: "${CERTIFICATE_PASSWORD}"
118+
SSL_CERT_FILE: "/etc/ssl/certs/ca-bundle.crt"
119+
ASPNETCORE_URLS: "https://+:9090"
120+
restart: unless-stopped
121+
volumes:
122+
- ${CERTIFICATE_PATH}:/usr/share/ParticularSoftware/certificate.pfx
123+
- ${CA_BUNDLE_PATH}:/etc/ssl/certs/ca-bundle.crt:ro
124+
depends_on:
125+
servicecontrol:
126+
condition: service_healthy
127+
servicecontrol-monitoring:
128+
condition: service_healthy
129+
rabbitmq:
130+
condition: service_healthy
131+
132+
rabbitmq:
133+
image: rabbitmq:3-management
134+
ports:
135+
- "5672:5672"
136+
- "15672:15672"
137+
restart: unless-stopped
138+
healthcheck:
139+
test: rabbitmq-diagnostics check_port_connectivity
140+
interval: 30s
141+
timeout: 10s
142+
start_period: 30s
143+
start_interval: 10s
144+
retries: 3
145+
volumes:
146+
- rabbitmq-data:/var/lib/rabbitmq
147+
148+
volumes:
149+
rabbitmq-data:
150+
raven-config:
151+
raven-data:

0 commit comments

Comments
 (0)