- Deleted the deploy.cmd script as it was no longer needed. - Updated Dockerfile to include curl for health checks and added a non-root user for improved security. - Modified health check command to use curl for better reliability. - Adjusted docker-compose.test.yml to reflect changes in health check configuration. - Cleaned up appsettings.json and removed unused configuration properties. - Removed Resource entity and related requests from the codebase as part of the architectural shift. - Updated documentation to reflect the removal of hardware binding and related endpoints. Co-authored-by: Cursor <cursoragent@cursor.com>
7.1 KiB
Infrastructure & Configuration Review
Date: 2026-05-13
Scope: Dockerfile, docker.test/Dockerfile, e2e/Dockerfile, docker-compose.test.yml, appsettings*.json, env/db/*.sql, .gitignore, .dockerignore. No CI/CD pipeline files (.github/workflows/, .gitlab-ci.yml, azure-pipelines.yml) are present in this workspace.
Container Security
Production image (Dockerfile)
| Check | Result | Notes |
|---|---|---|
| Non-root user | FAIL | No USER directive — runs as root. See F-6. |
| Minimal base image | PASS | mcr.microsoft.com/dotnet/aspnet:10.0 (runtime-only) for the final stage; SDK image used only for build. |
| Multi-stage build | PASS | Build / publish / runtime stages cleanly separated. |
| No secrets in build args | PASS | Only CI_COMMIT_SHA (passed as AZAION_REVISION env at runtime) — non-sensitive. |
| Health check defined | PASS (compose layer) | docker-compose.test.yml:42-51 defines a TCP health check; the production deployment must define an equivalent. Not verified in this audit because no production compose file exists in this workspace. |
| Image pinned by digest | WARN | Base images use :10.0 tag, not @sha256:... digest. Tag floats — a poisoned upstream tag would be picked up on the next rebuild. Acceptable if the build runs from a controlled cache; otherwise pin. |
EXPOSE matches Kestrel binding |
PASS | EXPOSE 8080, ASPNETCORE_URLS=http://+:8080. |
Test sidecar image (docker.test/Dockerfile)
FROM alpine:latest
CMD echo hello
This image is essentially a no-op stub. It's referenced from somewhere in the test/CI tooling but contributes nothing functional. Recommendation: either remove it (if nothing references it) or document its purpose. From a security standpoint it's inert — alpine:latest is fine for an echo and the floating tag is irrelevant for a stub. Flag as operational hygiene, not a security finding.
E2E runner image (e2e/Dockerfile)
| Check | Result | Notes |
|---|---|---|
| Non-root | FAIL (test-only) | Same root-by-default behavior. Test runner only — no exposed network, runs in an ephemeral container per CI run. Acceptable. |
| Uses SDK image as final | WARN (test-only) | Final stage is mcr.microsoft.com/dotnet/sdk:10.0 — needed for dotnet test. SDK images carry more attack surface than runtime images, but this container is only reachable on the internal e2e-net bridge. Acceptable for test-only. |
docker-compose.test.yml
| Check | Result | Notes |
|---|---|---|
| Secrets via env vars (not committed prod secrets) | PASS (test-only) | The DB password, JWT secret, and master key are committed because this is the e2e harness only. F-10 captures the rule: these literals must never appear in a production compose file. |
| No port leaks | PASS | Only 8080:8080 is published from system-under-test; test-db is internal to the bridge. |
| Healthcheck for the API | PASS | TCP probe on 127.0.0.1:8080. |
| Network isolation | PASS | All three services share e2e-net only; no host network mode. |
| Image lock | WARN (test-only) | postgres:16-alpine floats. For test reproducibility consider pinning a digest, but not security-critical for an ephemeral test stack. |
There is no production docker-compose.yml in this workspace. Any production deployment must:
- Inject
JwtConfig__Secretand bothConnectionStrings__*values from a secret manager (Vault, AWS Secrets Manager, Azure Key Vault). (ResourcesConfig__EncryptionMasterKeywas the third item here pre-revert; that field has since been deleted along with the OTA feature.) - NOT carry over the
ASPNETCORE_ENVIRONMENT=Developmentvalue used in the test compose — that environment value enables Swagger at the root path.
Environment Configuration
Azaion.AdminApi/appsettings.json
| Field | Value committed | Risk |
|---|---|---|
Logging.LogLevel.* |
Information / Warning |
OK |
AllowedHosts |
"*" |
OK at the framework level — host filtering is normally enforced upstream by the reverse proxy. |
ResourcesConfig.ResourcesFolder |
"Content" |
Relative path; resolves under the working directory inside the container. OK. |
ResourcesConfig.EncryptionMasterKey |
— | Removed in the post-cycle-1 revert along with the OTA feature; field no longer exists in ResourcesConfig, appsettings.json, or docker-compose.test.yml. Closes F-5 automatically. |
JwtConfig.{Issuer, Audience, TokenLifetimeHours} |
committed | Public-by-design (not secrets). |
JwtConfig.Secret |
NOT committed | Correct. Supplied via env var. |
ConnectionStrings.* |
NOT committed | Correct. Supplied via env var. |
Azaion.AdminApi/appsettings.Development.json
Empty except for log levels — fine.
e2e/Azaion.E2E/appsettings.test.json
Test-only. F-10 captures this; not a production concern.
Secrets Hygiene
| Pattern | Where | Disposition |
|---|---|---|
| Plaintext DB passwords | env/db/01_permissions.sql |
F-11 — operator template, not runtime. Add a header comment. |
Plaintext DB / JWT / master-key in docker-compose.test.yml |
docker-compose.test.yml:31-37 |
F-10 — test-only. CI guard recommended. |
Plaintext admin/uploader passwords in e2e/Azaion.E2E/appsettings.test.json |
as above | F-10 — test-only. |
.env ignored |
.gitignore:10 |
PASS |
bin/, obj/, logs/, Content/ ignored |
.gitignore:2-3, 7, 9 |
PASS — keeps build artifacts and runtime data out of git. |
CI/CD
No CI configuration files were found in the workspace (.github/workflows/, .gitlab-ci.yml, azure-pipelines.yml, Jenkinsfile — none present). Either:
- CI is configured outside this repo (e.g., upstream meta-repo), in which case the security guardrails (
dotnet list package --vulnerable, secret-scanning, dependency-review) need to be verified there. - Or there is no automated CI today, in which case this is a meta-finding: the dependency-bump (D-1) and the test suite have no automated gate. Recommend: introduce at least a
dotnet build && dotnet test && dotnet list package --vulnerablejob before deploy.
Network Security
| Check | Status | Notes |
|---|---|---|
| HTTPS enforcement in code | FAIL | F-13 — assumed at reverse proxy. |
| HSTS | not configured | Acceptable if reverse proxy injects it. |
| CORS | tight | AdminCorsPolicy whitelist-only (https://admin.azaion.com, http://admin.azaion.com). The plaintext http:// origin can be removed once the SaaS UI is HTTPS-only. |
| Security headers | not configured in app | X-Frame-Options, X-Content-Type-Options, Content-Security-Policy not set in code. Reverse proxy responsibility today; document the assumption. |
Self-verification
- All Dockerfiles reviewed (
Dockerfile,docker.test/Dockerfile,e2e/Dockerfile) - All compose files reviewed (
docker-compose.test.yml— only one in repo) - All environment / config files reviewed (3
appsettings*.json) - CI/CD reviewed (none present in repo — surfaced as meta-finding)