Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,7 @@ When using the interactive Swagger UI, simply log in, the token will be automati

## Setting CISO Assistant for production

The docker-compose-prod.yml highlights a relevant configuration with a Caddy proxy in front of the frontend. It exposes API calls only for SSO. Note that docker-compose.yml exposes the full API, which is not yet recommended for production.
The docker-compose.yml highlights a relevant configuration with a Caddy proxy in front of the frontend. It exposes API calls only for SSO. Note that docker-compose.yml exposes the full API, which is not yet recommended for production.

Set DJANGO_DEBUG=False for security reason.

Expand All @@ -597,6 +597,17 @@ Set DJANGO_DEBUG=False for security reason.
> [!NOTE]
> Caddy needs to receive a SNI header. Therefore, for your public URL (the one declared in CISO_ASSISTANT_URL), you need to use a FQDN, not an IP address, as the SNI is not transmitted by a browser if the host is an IP address. Another tricky issue!

> [!NOTE]
> The docker-compose template files are now launching the backend, huey and frontend in non-root mode. If you use an old docker-compose.yml file, it is recommended to udpate it. The containers are compatible with both root and non-root modes.

### Non-root docker containers

docker-compose.yml now relies on a non-root user 1001:1001, which is available in the image. Older deployments are using root user, which is still supported. To transition to non-root, use the following steps in the host:
- docker compose down
- update the docker-compose.yml file
- sudo chown -R 1001:1001 db
- docker compose up -d

## Supported languages 🌐

1. FR: French
Expand Down
3 changes: 3 additions & 0 deletions backend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,8 @@ RUN poetry install --no-root \
#watch out for local files during dev and maintenance of .dockerignore
COPY . .

RUN groupadd -g 1001 app && useradd -u 1001 -g 1001 -m -s /usr/sbin/nologin app
# USER app # not activated by default to keep compatibility

EXPOSE 8000
ENTRYPOINT ["poetry", "run", "bash", "startup.sh"]
2 changes: 1 addition & 1 deletion backend/startup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ if [ ! -n "$DJANGO_SETTINGS_MODULE" ]; then
fi
if [ ! -n "$DJANGO_SECRET_KEY" ]; then
if [ ! -f db/django_secret_key ]; then
cat /proc/sys/kernel/random/uuid >db/django_secret_key
install -m 600 <(cat /proc/sys/kernel/random/uuid) db/django_secret_key
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's check the actual file to see indentation and context
cat -n backend/startup.sh | head -20

Repository: intuitem/ciso-assistant-community

Length of output: 986


🏁 Script executed:

# Check git history for this specific change
git log --oneline -10 -- backend/startup.sh

Repository: intuitem/ciso-assistant-community

Length of output: 128


🏁 Script executed:

# Look for any related fallback mechanisms or error handling
rg -A 5 -B 5 'django_secret_key' backend/

Repository: intuitem/ciso-assistant-community

Length of output: 907


🏁 Script executed:

# Check if /proc/sys/kernel/random/uuid is used elsewhere
rg '/proc/sys/kernel/random/uuid'

Repository: intuitem/ciso-assistant-community

Length of output: 169


🏁 Script executed:

# Examine the docker-compose files to understand the security context
fd 'docker-compose.*\.ya?ml' | head -5 | xargs cat | head -100

Repository: intuitem/ciso-assistant-community

Length of output: 2676


Fix indentation: line 8 uses tabs while the rest of the script uses spaces.

Line 8 mixes indentation styles (tabs) with the rest of the file (spaces), creating a formatting inconsistency.

The concern about /proc/sys/kernel/random/uuid availability in hardened containers is unlikely to be an issue. Despite cap_drop: ALL and read_only: true, /proc/sys/kernel/random/uuid remains accessible because procfs is a separate kernel-provided mount point not affected by filesystem read-only settings or capability restrictions. Additionally, this is a development-only fallback—production deployments should set DJANGO_SECRET_KEY as an environment variable (which is already checked first in the script).

🤖 Prompt for AI Agents
In `@backend/startup.sh` at line 8, Fix the mixed indentation on the install
command: replace the tab characters at the start of the line containing "install
-m 600 <(cat /proc/sys/kernel/random/uuid) db/django_secret_key" with the same
number of spaces used elsewhere in the script so indentation is consistent;
locate that exact command string in the startup.sh and normalize it to
spaces-only indentation.

echo "generating initial Django secret key"
fi
export DJANGO_SECRET_KEY=$(<db/django_secret_key)
Expand Down
36 changes: 36 additions & 0 deletions config/docker-compose-barebone.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,20 @@ services:
- CISO_ASSISTANT_URL=http://localhost:3000
- DJANGO_DEBUG=True
- AUTH_TOKEN_TTL=7200
- HOME=/tmp
- XDG_CACHE_HOME=/tmp/.cache
- XDG_CONFIG_HOME=/tmp/.config
- XDG_DATA_HOME=/tmp/.local/share
read_only: true
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
tmpfs:
- /tmp:rw,noexec,nosuid,nodev
volumes:
- ./db:/code/db
user: "1001:1001"
healthcheck:
test: ["CMD-SHELL", "curl -f http://backend:8000/api/health/ || exit 1"]
interval: 10s
Expand All @@ -33,9 +45,21 @@ services:
- CISO_ASSISTANT_URL=http://localhost:3000
- DJANGO_DEBUG=False
- AUTH_TOKEN_TTL=7200
- HOME=/tmp
- XDG_CACHE_HOME=/tmp/.cache
- XDG_CONFIG_HOME=/tmp/.config
- XDG_DATA_HOME=/tmp/.local/share
read_only: true
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
tmpfs:
- /tmp:rw,noexec,nosuid,nodev

volumes:
- ./db:/code/db
user: "1001:1001"
entrypoint:
- /bin/sh
- -c
Expand All @@ -50,10 +74,22 @@ services:
- ORIGIN=http://localhost:3000
- PROTOCOL_HEADER=x-forwarded-proto
- HOST_HEADER=x-forwarded-host
- HOME=/tmp
- XDG_CACHE_HOME=/tmp/.cache
- XDG_CONFIG_HOME=/tmp/.config
- XDG_DATA_HOME=/tmp/.local/share
image: ghcr.io/intuitem/ciso-assistant-community/frontend:latest
pull_policy: always
read_only: true
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
tmpfs:
- /tmp:rw,noexec,nosuid,nodev
depends_on:
backend:
condition: service_healthy
ports:
- 3000:3000
user: "1001:1001"
21 changes: 21 additions & 0 deletions config/templates/docker-compose-postgresql-bunkerweb.yml.j2
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ services:
- CISO_ASSISTANT_URL=https://{{ fqdn }}:{{ port }}
- DJANGO_DEBUG={{ enable_debug }}
- AUTH_TOKEN_TTL=7200
- HOME=/tmp
- XDG_CACHE_HOME=/tmp/.cache
- XDG_CONFIG_HOME=/tmp/.config
- XDG_DATA_HOME=/tmp/.local/share
- POSTGRES_NAME={{ postgres.name }}
- POSTGRES_USER={{ postgres.user }}
{% if postgres.password is defined %}
Expand Down Expand Up @@ -72,6 +76,10 @@ services:
- CISO_ASSISTANT_URL=https://{{ fqdn }}:{{ port }}
- DJANGO_DEBUG=False
- AUTH_TOKEN_TTL=7200
- HOME=/tmp
- XDG_CACHE_HOME=/tmp/.cache
- XDG_CONFIG_HOME=/tmp/.config
- XDG_DATA_HOME=/tmp/.local/share
- POSTGRES_NAME={{ postgres.name }}
- POSTGRES_USER={{ postgres.user }}
{% if postgres.password is defined %}
Expand All @@ -86,8 +94,16 @@ services:
- EMAIL_HOST_PASSWORD={{ email.password }}
- DEFAULT_FROM_EMAIL={{ email.from_email }}
{% endif %}
read_only: true
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
tmpfs:
- /tmp:rw,noexec,nosuid,nodev
volumes:
- ./db:/code/db
user: "1001:1001"
entrypoint:
- /bin/sh
- -c
Expand All @@ -103,11 +119,16 @@ services:
- PUBLIC_BACKEND_API_EXPOSED_URL=https://{{ fqdn }}:{{ port }}/api
- PROTOCOL_HEADER=x-forwarded-proto
- HOST_HEADER=x-forwarded-host
- HOME=/tmp
- XDG_CACHE_HOME=/tmp/.cache
- XDG_CONFIG_HOME=/tmp/.config
- XDG_DATA_HOME=/tmp/.local/share
image: ghcr.io/intuitem/ciso-assistant-community/frontend:latest
pull_policy: always
depends_on:
backend:
condition: service_healthy
user: "1001:1001"
networks:
- bw-services

Expand Down
22 changes: 22 additions & 0 deletions config/templates/docker-compose-postgresql-caddy.yml.j2
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ services:
- CISO_ASSISTANT_URL=https://{{ fqdn }}:{{ port }}
- DJANGO_DEBUG={{ enable_debug }}
- AUTH_TOKEN_TTL=7200
- HOME=/tmp
- XDG_CACHE_HOME=/tmp/.cache
- XDG_CONFIG_HOME=/tmp/.config
- XDG_DATA_HOME=/tmp/.local/share
- POSTGRES_NAME={{ postgres.name }}
- POSTGRES_USER={{ postgres.user }}
{% if postgres.password is defined %}
Expand All @@ -48,6 +52,7 @@ services:
{% endif %}
volumes:
- ./db:/code/db
user: "1001:1001"
healthcheck:
test: ["CMD-SHELL", "curl -f http://backend:8000/api/health/ || exit 1"]
interval: 10s
Expand All @@ -68,6 +73,10 @@ services:
- CISO_ASSISTANT_URL=https://{{ fqdn }}:{{ port }}
- DJANGO_DEBUG=False
- AUTH_TOKEN_TTL=7200
- HOME=/tmp
- XDG_CACHE_HOME=/tmp/.cache
- XDG_CONFIG_HOME=/tmp/.config
- XDG_DATA_HOME=/tmp/.local/share
- POSTGRES_NAME={{ postgres.name }}
- POSTGRES_USER={{ postgres.user }}
{% if postgres.password is defined %}
Expand All @@ -82,8 +91,16 @@ services:
- EMAIL_HOST_PASSWORD={{ email.password }}
- DEFAULT_FROM_EMAIL={{ email.from_email }}
{% endif %}
read_only: true
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
tmpfs:
- /tmp:rw,noexec,nosuid,nodev
volumes:
- ./db:/code/db
user: "1001:1001"
entrypoint:
- /bin/sh
- -c
Expand All @@ -97,11 +114,16 @@ services:
- PUBLIC_BACKEND_API_EXPOSED_URL=https://{{ fqdn }}:{{ port }}/api
- PROTOCOL_HEADER=x-forwarded-proto
- HOST_HEADER=x-forwarded-host
- HOME=/tmp
- XDG_CACHE_HOME=/tmp/.cache
- XDG_CONFIG_HOME=/tmp/.config
- XDG_DATA_HOME=/tmp/.local/share
image: ghcr.io/intuitem/ciso-assistant-community/frontend:latest
pull_policy: always
depends_on:
backend:
condition: service_healthy
user: "1001:1001"

caddy:
container_name: caddy
Expand Down
21 changes: 21 additions & 0 deletions config/templates/docker-compose-postgresql-traefik.yml.j2
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ services:
- CISO_ASSISTANT_URL=https://{{ fqdn }}:{{ port }}
- DJANGO_DEBUG={{ enable_debug }}
- AUTH_TOKEN_TTL=7200
- HOME=/tmp
- XDG_CACHE_HOME=/tmp/.cache
- XDG_CONFIG_HOME=/tmp/.config
- XDG_DATA_HOME=/tmp/.local/share
Comment on lines +35 to +38
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Backend is missing read_only, cap_drop, security_opt, tmpfs, and user directives.

The environment variables were added, but the runtime hardening block that every other compose file applies to backend is absent here. Compare with docker-compose.yml (lines 16–26) and docker-compose-barebone.yml (lines 16–25) where backend gets the full set. This template only adds the HOME/XDG env vars but omits the security constraints and non-root user, leaving the backend container running as root without filesystem or capability restrictions.

Proposed fix — add the missing hardening block to backend
       - XDG_DATA_HOME=/tmp/.local/share
       - POSTGRES_NAME={{ postgres.name }}
       ...
+    read_only: true
+    cap_drop:
+      - ALL
+    security_opt:
+      - no-new-privileges:true
+    tmpfs:
+      - /tmp:rw,noexec,nosuid,nodev
     volumes:
       - ./db:/code/db
+    user: "1001:1001"
     healthcheck:
🤖 Prompt for AI Agents
In `@config/templates/docker-compose-postgresql-traefik.yml.j2` around lines 35 -
38, The backend service in the docker-compose-postgresql-traefik.yml.j2 template
is missing the runtime hardening directives; update the backend service (same
service block that sets HOME/XDG_* env vars) to include read_only: true,
cap_drop: ['ALL'], security_opt: ['no-new-privileges:true',
'seccomp:unconfined'] (or match your other compose files), a tmpfs mount entry
(tmpfs: /tmp or matching pattern used elsewhere), and set user to a non-root
UID/GID consistent with other templates; ensure these directives mirror the
backend hardening block used in docker-compose.yml and
docker-compose-barebone.yml so the container does not run as root and has
filesystem/capability restrictions.

- POSTGRES_NAME={{ postgres.name }}
- POSTGRES_USER={{ postgres.user }}
{% if postgres.password is defined %}
Expand Down Expand Up @@ -75,6 +79,10 @@ services:
- CISO_ASSISTANT_URL=https://{{ fqdn }}:{{ port }}
- DJANGO_DEBUG=False
- AUTH_TOKEN_TTL=7200
- HOME=/tmp
- XDG_CACHE_HOME=/tmp/.cache
- XDG_CONFIG_HOME=/tmp/.config
- XDG_DATA_HOME=/tmp/.local/share
- POSTGRES_NAME={{ postgres.name }}
- POSTGRES_USER={{ postgres.user }}
{% if postgres.password is defined %}
Expand All @@ -89,8 +97,16 @@ services:
- EMAIL_HOST_PASSWORD={{ email.password }}
- DEFAULT_FROM_EMAIL={{ email.from_email }}
{% endif %}
read_only: true
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
tmpfs:
- /tmp:rw,noexec,nosuid,nodev
volumes:
- ./db:/code/db
user: "1001:1001"
entrypoint:
- /bin/sh
- -c
Expand All @@ -104,11 +120,16 @@ services:
- PUBLIC_BACKEND_API_EXPOSED_URL=https://{{ fqdn }}:{{ port }}/api
- PROTOCOL_HEADER=x-forwarded-proto
- HOST_HEADER=x-forwarded-host
- HOME=/tmp
- XDG_CACHE_HOME=/tmp/.cache
- XDG_CONFIG_HOME=/tmp/.config
- XDG_DATA_HOME=/tmp/.local/share
image: ghcr.io/intuitem/ciso-assistant-community/frontend:latest
pull_policy: always
depends_on:
backend:
condition: service_healthy
user: "1001:1001"
labels:
- "traefik.enable=true"
- "traefik.http.routers.frontend.rule=Host(`{{ fqdn }}`) && PathPrefix(`/`)"
Expand Down
21 changes: 21 additions & 0 deletions config/templates/docker-compose-sqlite-bunkerweb.yml.j2
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ services:
- CISO_ASSISTANT_URL=https://{{ fqdn }}:{{ port }}
- DJANGO_DEBUG={{ enable_debug }}
- AUTH_TOKEN_TTL=7200
- HOME=/tmp
- XDG_CACHE_HOME=/tmp/.cache
- XDG_CONFIG_HOME=/tmp/.config
- XDG_DATA_HOME=/tmp/.local/share
{% if need_mailer %}
- EMAIL_HOST={{ email.host }}
- EMAIL_PORT={{ email.port }}
Expand Down Expand Up @@ -42,6 +46,10 @@ services:
- CISO_ASSISTANT_URL=https://{{ fqdn }}:{{ port }}
- DJANGO_DEBUG=False
- AUTH_TOKEN_TTL=7200
- HOME=/tmp
- XDG_CACHE_HOME=/tmp/.cache
- XDG_CONFIG_HOME=/tmp/.config
- XDG_DATA_HOME=/tmp/.local/share
{% if need_mailer %}
- EMAIL_HOST={{ email.host }}
- EMAIL_PORT={{ email.port }}
Expand All @@ -50,8 +58,16 @@ services:
- EMAIL_HOST_PASSWORD={{ email.password }}
- DEFAULT_FROM_EMAIL={{ email.from_email }}
{% endif %}
read_only: true
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
tmpfs:
- /tmp:rw,noexec,nosuid,nodev
volumes:
- ./db:/code/db
user: "1001:1001"
entrypoint:
- /bin/sh
- -c
Expand All @@ -67,11 +83,16 @@ services:
- PUBLIC_BACKEND_API_EXPOSED_URL=https://{{ fqdn }}:{{ port }}/api
- PROTOCOL_HEADER=x-forwarded-proto
- HOST_HEADER=x-forwarded-host
- HOME=/tmp
- XDG_CACHE_HOME=/tmp/.cache
- XDG_CONFIG_HOME=/tmp/.config
- XDG_DATA_HOME=/tmp/.local/share
image: ghcr.io/intuitem/ciso-assistant-community/frontend:latest
pull_policy: always
depends_on:
backend:
condition: service_healthy
user: "1001:1001"
networks:
- bw-services

Expand Down
Loading
Loading