security hardening
This commit is contained in:
@@ -16,8 +16,24 @@ ALLOWED_SHOP=linumiq-dev.myshopify.com
|
||||
# Must match `scopes` in shopify.app.dev.toml.
|
||||
SCOPES=read_orders,write_orders,read_all_orders,read_customers,read_companies,read_files,write_files
|
||||
|
||||
# --- Secrets at rest ---
|
||||
# Field-level encryption key for secrets stored in the DB (SMTP password,
|
||||
# Shopify session access/refresh tokens). Must be base64 of exactly 32 bytes.
|
||||
# Generate with: node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
|
||||
DATA_ENCRYPTION_KEY=REPLACE_ME_BASE64_32_BYTES
|
||||
|
||||
# Dedicated HMAC key for signing public GiroCode URLs. base64 of 32 bytes.
|
||||
# If unset, the app falls back to SHOPIFY_API_SECRET (kept for backward compat).
|
||||
# Generate with: node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
|
||||
GIROCODE_SIGNING_KEY=REPLACE_ME_BASE64_32_BYTES
|
||||
|
||||
# --- Runtime ---
|
||||
NODE_ENV=production
|
||||
PORT=3000
|
||||
|
||||
# DATABASE_URL is set in docker-compose.dev.yml (file:/data/prod.sqlite on the bind mount).
|
||||
|
||||
# --- Email (optional) ---
|
||||
# Archival BCC for every invoice email. Off by default for privacy/GDPR.
|
||||
# Set to a single address or a comma-separated list to opt in.
|
||||
# INVOICE_BCC=archive@example.com
|
||||
|
||||
@@ -8,6 +8,15 @@
|
||||
# DEV — installed on linumiq-dev.myshopify.com
|
||||
invoice-app-dev.linumiq.com {
|
||||
encode zstd gzip
|
||||
# Security response headers. NOTE: deliberately no X-Frame-Options here —
|
||||
# this is an embedded Shopify app, and framing is governed by the
|
||||
# Content-Security-Policy `frame-ancestors` directive that the Shopify
|
||||
# library injects via addDocumentResponseHeaders (see app/entry.server.tsx).
|
||||
header {
|
||||
Strict-Transport-Security "max-age=31536000; includeSubDomains"
|
||||
X-Content-Type-Options nosniff
|
||||
Referrer-Policy strict-origin-when-cross-origin
|
||||
}
|
||||
reverse_proxy linumiq-invoice-dev:3000
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,25 @@ docker compose up -d --build
|
||||
|
||||
Append `Caddyfile.snippet` to your Caddy config and `docker exec caddy caddy reload --config /etc/caddy/Caddyfile`.
|
||||
|
||||
## Container runs as a non-root user (uid 1000)
|
||||
|
||||
The image runs as the unprivileged `node` user (uid/gid **1000**), not root. The
|
||||
SQLite database is written to the `/data` bind mount, so the **host** directory
|
||||
mounted at `/data` (e.g. `/docker/linumiq-invoice/dev/data` and
|
||||
`…/prod/data`) must be writable by uid 1000, otherwise `prisma migrate deploy`
|
||||
and DB writes fail on startup:
|
||||
|
||||
```bash
|
||||
sudo chown -R 1000:1000 /docker/linumiq-invoice/dev/data
|
||||
sudo chown -R 1000:1000 /docker/linumiq-invoice/prod/data
|
||||
```
|
||||
|
||||
The dev container additionally runs with a **read-only root filesystem**
|
||||
(`read_only: true` + `tmpfs: /tmp`), `no-new-privileges`, all Linux capabilities
|
||||
dropped, and memory/pids/cpu limits. The app only writes to the `/data` bind
|
||||
mount and the tmpfs `/tmp`, so this is safe. (The prod compose is intentionally
|
||||
left unchanged.)
|
||||
|
||||
## Day-to-day redeploy
|
||||
|
||||
```bash
|
||||
|
||||
@@ -6,6 +6,24 @@ services:
|
||||
image: linumiq-invoice:dev
|
||||
container_name: linumiq-invoice-dev
|
||||
restart: unless-stopped
|
||||
# --- Container hardening (DEV) ---------------------------------------
|
||||
# Prevent privilege escalation and drop all Linux capabilities (the app
|
||||
# is a plain Node HTTP server — it needs none).
|
||||
security_opt:
|
||||
- "no-new-privileges:true"
|
||||
cap_drop:
|
||||
- ALL
|
||||
# Read-only root filesystem: the app never writes to the image at runtime
|
||||
# (Prisma client is baked at build; the SQLite DB lives on the /data bind
|
||||
# mount; logo/image caches live in the DB or in-memory). npm/Prisma
|
||||
# incidental writes are redirected to the tmpfs /tmp (see Dockerfile env).
|
||||
read_only: true
|
||||
tmpfs:
|
||||
- /tmp
|
||||
# Resource limits (Compose v2 / docker compose, non-swarm).
|
||||
mem_limit: 512m
|
||||
pids_limit: 256
|
||||
cpus: 1.5
|
||||
env_file:
|
||||
- .env.dev
|
||||
environment:
|
||||
|
||||
Reference in New Issue
Block a user