├── core ├── files │ ├── var │ │ ├── log │ │ │ └── supervisor │ │ │ │ └── .keep │ │ └── www │ │ │ └── html │ │ │ └── index.php │ ├── etc │ │ ├── misp-docker │ │ │ ├── db_enable.envars.json │ │ │ ├── gpg.defaults.json │ │ │ ├── critical.envars.json │ │ │ ├── gpg.envars.json │ │ │ ├── s3.defaults.json │ │ │ ├── proxy.envars.json │ │ │ ├── optional.envars.json │ │ │ ├── critical.defaults.json │ │ │ ├── s3.envars.json │ │ │ ├── initialisation.envars.json │ │ │ ├── optional.defaults.json │ │ │ ├── minimum_config.envars.json │ │ │ ├── minimum_config.defaults.json │ │ │ └── initialisation.defaults.json │ │ ├── supervisor │ │ │ ├── conf.d │ │ │ │ ├── 10-supervisor.conf.kubernetes │ │ │ │ ├── 10-supervisor.conf │ │ │ │ ├── 50-workers.conf.kubernetes │ │ │ │ └── 50-workers.conf │ │ │ └── supervisord.conf │ │ └── nginx │ │ │ ├── sites-available │ │ │ ├── php-fpm-status │ │ │ ├── misp80 │ │ │ └── misp443 │ │ │ └── includes │ │ │ └── misp │ ├── kubernetes │ │ ├── entrypoint_nginx.sh │ │ └── entrypoint_fpm.sh │ ├── rest_client.sh │ ├── utilities.sh │ ├── entrypoint.sh │ ├── entrypoint_fpm.sh │ ├── entrypoint_nginx.sh │ └── configure_misp.sh └── Dockerfile ├── experimental └── podman-systemd │ ├── misp.network │ ├── misp-core_gpg.volume │ ├── misp-db_data.volume │ ├── misp-core_conf.volume │ ├── misp-core_logs.volume │ ├── misp-core_files.volume │ ├── misp-redis_cache.volume │ ├── misp-core_certs.volume │ ├── misp-mail.container │ ├── misp-db.container │ ├── misp-modules.container │ ├── misp-redis.container │ └── misp-core.container ├── kubernetes ├── mysql-credentials.env ├── instance-secrets.env ├── kustomization.yaml ├── manifests │ ├── redis.yaml │ ├── cronjobs │ │ ├── update-galaxies.yaml │ │ ├── cache-all-feeds.yaml │ │ ├── fetch-all-feeds.yaml │ │ ├── update-noticelists.yaml │ │ ├── update-taxonomies.yaml │ │ ├── update-warninglists.yaml │ │ ├── pull-all-servers.yaml │ │ └── push-all-servers.yaml │ ├── mysql.yaml │ ├── deployment-nginx.yaml │ ├── services.yaml │ ├── policy-example-cilium.yaml │ └── deployment-misp.yaml └── README.md ├── .gitignore ├── guard ├── files │ └── entrypoint.sh ├── Dockerfile └── config.json ├── .github └── workflows │ ├── test-build-latest.yml │ └── release-latest.yml ├── modules └── Dockerfile ├── docker-bake.hcl ├── docs └── keycloak-integration-guide.md ├── template.env ├── docker-compose.yml └── README.md /core/files/var/log/supervisor/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /experimental/podman-systemd/misp.network: -------------------------------------------------------------------------------- 1 | [Network] 2 | NetworkName=misp 3 | DisableDNS=false 4 | -------------------------------------------------------------------------------- /experimental/podman-systemd/misp-core_gpg.volume: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=misp-core gpg volume 3 | 4 | [Volume] 5 | VolumeName=misp-core_gpg 6 | -------------------------------------------------------------------------------- /experimental/podman-systemd/misp-db_data.volume: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=misp database volume 3 | 4 | [Volume] 5 | VolumeName=misp-db_data 6 | -------------------------------------------------------------------------------- /experimental/podman-systemd/misp-core_conf.volume: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=misp-core cofig volume 3 | 4 | [Volume] 5 | VolumeName=misp-core_conf 6 | -------------------------------------------------------------------------------- /experimental/podman-systemd/misp-core_logs.volume: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=misp-core logs volume 3 | 4 | [Volume] 5 | VolumeName=misp-core_logs 6 | -------------------------------------------------------------------------------- /core/files/etc/misp-docker/db_enable.envars.json: -------------------------------------------------------------------------------- 1 | { 2 | "MISP.system_setting_db": { 3 | "default_value": "$ENABLE_DB_SETTINGS" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /experimental/podman-systemd/misp-core_files.volume: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=misp-core files volume 3 | 4 | [Volume] 5 | VolumeName=misp-core_files 6 | -------------------------------------------------------------------------------- /experimental/podman-systemd/misp-redis_cache.volume: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=misp redis cache volume 3 | 4 | [Volume] 5 | VolumeName=misp-redis_cache 6 | -------------------------------------------------------------------------------- /experimental/podman-systemd/misp-core_certs.volume: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=misp-core certificate volume 3 | 4 | [Volume] 5 | VolumeName=misp-core_certs 6 | -------------------------------------------------------------------------------- /kubernetes/mysql-credentials.env: -------------------------------------------------------------------------------- 1 | MYSQL_PASSWORD=sometimespasswordsarelongsometimespasswordsareshort 2 | MYSQL_ROOT_PASSWORD=sometimespasswordsarelongsometimespasswordsareshort 3 | -------------------------------------------------------------------------------- /core/files/etc/misp-docker/gpg.defaults.json: -------------------------------------------------------------------------------- 1 | { 2 | "GnuPG.onlyencrypted": { 3 | "default_value": false 4 | }, 5 | "SMIME.enabled": { 6 | "default_value": false 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /configs/ 2 | /files/ 3 | /gnupg/ 4 | /logs/ 5 | /ssl/ 6 | /custom/ 7 | .env 8 | env.hcl 9 | rootca.crt 10 | cert.pem 11 | docker-compose.override.yml 12 | .DS_Store 13 | .AppleDouble 14 | .LSOverride 15 | -------------------------------------------------------------------------------- /core/files/etc/misp-docker/critical.envars.json: -------------------------------------------------------------------------------- 1 | { 2 | "MISP.external_baseurl": { 3 | "default_value": "${BASE_URL}" 4 | }, 5 | "Security.rest_client_baseurl": { 6 | "default_value": "${BASE_URL}" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /core/files/etc/supervisor/conf.d/10-supervisor.conf.kubernetes: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | nodaemon=true 3 | user=root 4 | 5 | [inet_http_server] 6 | port=127.0.0.1:9001 7 | username=%(ENV_SUPERVISOR_USERNAME)s 8 | password=%(ENV_SUPERVISOR_PASSWORD)s -------------------------------------------------------------------------------- /core/files/etc/misp-docker/gpg.envars.json: -------------------------------------------------------------------------------- 1 | { 2 | "GnuPG.email": { 3 | "default_value": "${SETTING_EMAIL}" 4 | }, 5 | "GnuPG.homedir": { 6 | "default_value": "${GPG_DIR}" 7 | }, 8 | "GnuPG.password": { 9 | "default_value": "${GPG_PASSPHRASE}" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /core/files/etc/nginx/sites-available/php-fpm-status: -------------------------------------------------------------------------------- 1 | server { 2 | listen 8999; 3 | location ~ ^/status$ { 4 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 5 | include fastcgi_params; 6 | fastcgi_pass unix:/run/php/php-fpm-status.sock; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /core/files/etc/misp-docker/s3.defaults.json: -------------------------------------------------------------------------------- 1 | { 2 | "Plugin.S3_enable": { 3 | "default_value": true 4 | }, 5 | "Plugin.S3_aws_compatible": { 6 | "default_value": true 7 | }, 8 | "MISP.attachments_dir": { 9 | "default_value": "s3://" 10 | } 11 | } -------------------------------------------------------------------------------- /core/files/etc/nginx/sites-available/misp80: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80 default_server; 3 | listen [::]:80 default_server; 4 | 5 | # disable access logs 6 | access_log off; 7 | log_not_found off; 8 | error_log /dev/stderr error; 9 | 10 | # include includes/misp; 11 | # return 301 https://$host$request_uri; 12 | } 13 | -------------------------------------------------------------------------------- /experimental/podman-systemd/misp-mail.container: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=misp mail system 3 | After=local-fs.target 4 | 5 | [Container] 6 | AutoUpdate=registry 7 | HostName=mail 8 | ContainerName=misp-mail 9 | Image=docker.io/ixdotai/smtp 10 | Network=misp.network 11 | EnvironmentFile=misp.env 12 | 13 | [Install] 14 | WantedBy=default.target 15 | -------------------------------------------------------------------------------- /core/files/var/www/html/index.php: -------------------------------------------------------------------------------- 1 | 8 | 9 | MISP is loading... 10 | -------------------------------------------------------------------------------- /kubernetes/instance-secrets.env: -------------------------------------------------------------------------------- 1 | REDIS_PASSWORD=replacemewithsomethingelse 2 | ADMIN_KEY=REPLACEMEWITHANACTUALKEYTHATWILLWORKFORU 3 | ADMIN_PASSWORD=sometimespasswordsarelongsometimespasswordsareshort 4 | BASE_URL=https://localhost:9002 5 | S3_BUCKET=files-storage-bucket 6 | S3_ENDPOINT=https://s3-service-endpoint 7 | S3_ACCESS_KEY=s3-access-key 8 | S3_SECRET_KEY=s3-secret-key 9 | -------------------------------------------------------------------------------- /core/files/etc/misp-docker/proxy.envars.json: -------------------------------------------------------------------------------- 1 | { 2 | "Proxy.host": { 3 | "default_value": "${PROXY_HOST}" 4 | }, 5 | "Proxy.port": { 6 | "default_value": "${PROXY_PORT}" 7 | }, 8 | "Proxy.method": { 9 | "default_value": "${PROXY_METHOD}" 10 | }, 11 | "Proxy.user": { 12 | "default_value": "${PROXY_USER}" 13 | }, 14 | "Proxy.password": { 15 | "default_value": "${PROXY_PASSWORD}" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /core/files/etc/misp-docker/optional.envars.json: -------------------------------------------------------------------------------- 1 | { 2 | "Plugin.Enrichment_services_url": { 3 | "default_value": "${MISP_MODULES_FQDN}" 4 | }, 5 | "Plugin.Import_services_url": { 6 | "default_value": "${MISP_MODULES_FQDN}" 7 | }, 8 | "Plugin.Export_services_url": { 9 | "default_value": "${MISP_MODULES_FQDN}" 10 | }, 11 | "Plugin.Action_services_url": { 12 | "default_value": "${MISP_MODULES_FQDN}" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /core/files/etc/misp-docker/critical.defaults.json: -------------------------------------------------------------------------------- 1 | { 2 | "MISP.host_org_id": { 3 | "default_value": 1 4 | }, 5 | "Plugin.Enrichment_hover_enable": { 6 | "default_value": false 7 | }, 8 | "Plugin.Enrichment_hover_popover_only": { 9 | "default_value": false 10 | }, 11 | "Security.csp_enforce": { 12 | "default_value": true 13 | }, 14 | "Security.do_not_log_authkeys": { 15 | "default_value": true 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /core/files/etc/misp-docker/s3.envars.json: -------------------------------------------------------------------------------- 1 | { 2 | "Plugin.S3_bucket_name": { 3 | "default_value": "${S3_BUCKET}" 4 | }, 5 | "Plugin.S3_aws_endpoint": { 6 | "default_value": "${S3_ENDPOINT}" 7 | }, 8 | "Plugin.S3_aws_access_key": { 9 | "default_value": "${S3_ACCESS_KEY}" 10 | }, 11 | "Plugin.S3_aws_secret_key": { 12 | "default_value": "${S3_SECRET_KEY}" 13 | }, 14 | "MISP.attachments_dir": { 15 | "default_value": "s3://", 16 | "command_args": "-f" 17 | } 18 | } -------------------------------------------------------------------------------- /core/files/kubernetes/entrypoint_nginx.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | echo "INIT | Loading environment and functions" 3 | source /entrypoint.sh 4 | source /entrypoint_nginx.sh 5 | 6 | # Initialize nginx 7 | echo "INIT | Initialize NGINX ..." && init_nginx 8 | 9 | echo "... setting 'fastcgi_pass' to misp-php:9002" 10 | sed -i "s@fastcgi_pass .*;@fastcgi_pass misp-php:9002;@" /etc/nginx/includes/misp 11 | 12 | echo "INIT | Flip NGINX live ..." && flip_nginx true false 13 | 14 | # launch nginx as current shell process in container 15 | exec /usr/bin/tini -- nginx -g 'daemon off;' -------------------------------------------------------------------------------- /core/files/etc/misp-docker/initialisation.envars.json: -------------------------------------------------------------------------------- 1 | { 2 | "debug": { 3 | "default_value": "${DEBUG}", 4 | "command_args": "-f" 5 | }, 6 | "MISP.baseurl": { 7 | "default_value": "${BASE_URL}" 8 | }, 9 | "MISP.email": { 10 | "default_value": "${SETTING_EMAIL}" 11 | }, 12 | "MISP.contact": { 13 | "default_value": "${SETTING_CONTACT}" 14 | }, 15 | "MISP.attachments_dir": { 16 | "default_value": "${ATTACHMENTS_DIR}" 17 | }, 18 | "Security.encryption_key": { 19 | "default_value": "${ENCRYPTION_KEY}", 20 | "command_args": "-f" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /experimental/podman-systemd/misp-db.container: -------------------------------------------------------------------------------- 1 | [Unit] 2 | After=local-fs.target 3 | Description=misp database 4 | 5 | [Container] 6 | AutoUpdate=registry 7 | HostName=db 8 | ContainerName=misp-db 9 | Image=docker.io/library/mariadb:10.11 10 | Network=misp.network 11 | Volume=misp-db_data.volume:/var/lib/mysql 12 | EnvironmentFile=misp.env 13 | AddCapability=SYS_NICE 14 | HealthCmd=mysqladmin --user=$MYSQL_USER --password=$MYSQL_PASSWORD status 15 | HealthInterval=2s 16 | HealthTimeout=1s 17 | HealthRetries=3 18 | HealthStartPeriod=30s 19 | HealthStartupInterval=5s 20 | HealthOnFailure=kill 21 | 22 | [Service] 23 | Restart=always 24 | 25 | [Install] 26 | WantedBy=default.target 27 | -------------------------------------------------------------------------------- /guard/files/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Entry point for misp-guard. 4 | # This ensures MISP-core's IP is set at runtime when misp-guard starts. 5 | # config.json must reflect this structure to ensure the source container is targeted 6 | # { 7 | # "instances": { 8 | # "misp_container": { 9 | # "ip": "placeholder" 10 | # } 11 | # } 12 | # } 13 | 14 | 15 | set -e 16 | 17 | # resolve misp-core from docker dns 18 | if [ -z "$MISP_IP" ]; then 19 | MISP_IP=$(getent hosts misp-core | awk '{print $1}') 20 | fi 21 | 22 | # replace runtime ip into config.json 23 | jq --arg ip "$MISP_IP" \ 24 | '.instances.misp_container.ip = $ip' \ 25 | /config.json > /srv/misp-guard/src/config.json 26 | 27 | exec mitmdump -s mispguard.py -p ${GUARD_PORT:-8888} ${GUARD_ARGS:+$GUARD_ARGS} --set config=config.json 28 | -------------------------------------------------------------------------------- /experimental/podman-systemd/misp-modules.container: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=misp modules 3 | After=local-fs.target 4 | 5 | [Container] 6 | AutoUpdate=registry 7 | HostName=misp-modules 8 | ContainerName=misp-modules 9 | Image=ghcr.io/misp/misp-docker/misp-modules:latest 10 | Network=misp.network 11 | EnvironmentFile=misp.env 12 | Volume=%h/.config/misp_conf/custom/action_mod/:/custom/action_mod/:Z 13 | Volume=%h/.config/misp_conf/custom/expansion/:/custom/expansion/:Z 14 | Volume=%h/.config/misp_conf/custom/export_mod/:/custom/export_mod/:Z 15 | Volume=%h/.config/misp_conf/custom/import_mod/:/custom/import_mod/:Z 16 | HealthCmd=/bin/bash -c ' /dev/null || exit 1 21 | HealthInterval=2s 22 | HealthTimeout=1s 23 | HealthRetries=3 24 | HealthStartPeriod=30s 25 | HealthStartupInterval=30s 26 | 27 | [Install] 28 | WantedBy=default.target 29 | -------------------------------------------------------------------------------- /kubernetes/manifests/cronjobs/update-galaxies.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1 2 | kind: CronJob 3 | metadata: 4 | name: misp-cron-update-galaxies 5 | labels: 6 | app.kubernetes.io/name: misp-cron 7 | spec: 8 | schedule: "0 3 * * *" 9 | jobTemplate: 10 | spec: 11 | template: 12 | metadata: 13 | labels: 14 | app.kubernetes.io/name: misp-cron 15 | spec: 16 | containers: 17 | - name: curl 18 | image: registry.gitlab.com/gitlab-ci-utils/curl-jq:3.2.1 19 | imagePullPolicy: IfNotPresent 20 | command: ["/bin/sh", "-c"] 21 | args: 22 | - 'curl -s -k -X POST -H "Authorization: ${ADMIN_KEY}" -H "Accept: application/json" -H "Content-Type: application/json" https://nginx/galaxies/update' 23 | envFrom: 24 | - secretRef: 25 | name: instance-secrets 26 | restartPolicy: Never 27 | backoffLimit: 2 28 | activeDeadlineSeconds: 1200 29 | concurrencyPolicy: Forbid -------------------------------------------------------------------------------- /kubernetes/manifests/cronjobs/cache-all-feeds.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1 2 | kind: CronJob 3 | metadata: 4 | name: misp-cron-feeds-cache-all 5 | labels: 6 | app.kubernetes.io/name: misp-cron 7 | spec: 8 | schedule: "20 2 * * *" 9 | jobTemplate: 10 | spec: 11 | template: 12 | metadata: 13 | labels: 14 | app.kubernetes.io/name: misp-cron 15 | spec: 16 | containers: 17 | - name: curl 18 | image: registry.gitlab.com/gitlab-ci-utils/curl-jq:3.2.1 19 | imagePullPolicy: IfNotPresent 20 | command: ["/bin/sh", "-c"] 21 | args: 22 | - 'curl -s -k -X POST -H "Authorization: ${ADMIN_KEY}" -H "Accept: application/json" -H "Content-Type: application/json" https://nginx/feeds/cacheFeeds/all' 23 | envFrom: 24 | - secretRef: 25 | name: instance-secrets 26 | restartPolicy: Never 27 | backoffLimit: 2 28 | activeDeadlineSeconds: 1200 29 | concurrencyPolicy: Forbid -------------------------------------------------------------------------------- /core/files/etc/supervisor/conf.d/10-supervisor.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | nodaemon=true 3 | user=root 4 | stdout_logfile=/dev/stdout 5 | stdout_logfile_maxbytes=0 6 | stderr_logfile=/dev/stderr 7 | stderr_logfile_maxbytes=0 8 | 9 | [inet_http_server] 10 | port=127.0.0.1:9001 11 | username=%(ENV_SUPERVISOR_USERNAME)s 12 | password=%(ENV_SUPERVISOR_PASSWORD)s 13 | 14 | [program:nginx] 15 | command=/entrypoint_nginx.sh 16 | autorestart=true 17 | redirect_stderr=true 18 | stdout_logfile=/dev/stdout 19 | stdout_logfile_maxbytes=0 20 | stderr_logfile=/dev/stderr 21 | stderr_logfile_maxbytes=0 22 | 23 | [program:php-fpm] 24 | command=/entrypoint_fpm.sh 25 | autorestart=true 26 | redirect_stderr=true 27 | stdout_logfile=/dev/stdout 28 | stdout_logfile_maxbytes=0 29 | stderr_logfile=/dev/stderr 30 | stderr_logfile_maxbytes=0 31 | 32 | [program:rsyslog] 33 | command=/usr/sbin/rsyslogd -n 34 | autorestart=true 35 | redirect_stderr=true 36 | stdout_logfile=/dev/stdout 37 | stdout_logfile_maxbytes=0 38 | stderr_logfile=/dev/stderr 39 | stderr_logfile_maxbytes=0 40 | -------------------------------------------------------------------------------- /kubernetes/manifests/cronjobs/fetch-all-feeds.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1 2 | kind: CronJob 3 | metadata: 4 | name: misp-cron-feeds-fetch-all 5 | labels: 6 | app.kubernetes.io/name: misp-cron 7 | spec: 8 | schedule: "30 2 * * *" 9 | jobTemplate: 10 | spec: 11 | template: 12 | metadata: 13 | labels: 14 | app.kubernetes.io/name: misp-cron 15 | spec: 16 | containers: 17 | - name: curl 18 | image: registry.gitlab.com/gitlab-ci-utils/curl-jq:3.2.1 19 | imagePullPolicy: IfNotPresent 20 | command: ["/bin/sh", "-c"] 21 | args: 22 | - 'curl -s -X POST -k -H "Authorization: ${ADMIN_KEY}" -H "Accept: application/json" -H "Content-Type: application/json" https://nginx/feeds/fetchFromAllFeeds' 23 | envFrom: 24 | - secretRef: 25 | name: instance-secrets 26 | restartPolicy: Never 27 | backoffLimit: 2 28 | activeDeadlineSeconds: 1200 29 | concurrencyPolicy: Forbid -------------------------------------------------------------------------------- /kubernetes/manifests/cronjobs/update-noticelists.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1 2 | kind: CronJob 3 | metadata: 4 | name: misp-cron-update-noticelists 5 | labels: 6 | app.kubernetes.io/name: misp-cron 7 | spec: 8 | schedule: "30 3 * * *" 9 | jobTemplate: 10 | spec: 11 | template: 12 | metadata: 13 | labels: 14 | app.kubernetes.io/name: misp-cron 15 | spec: 16 | containers: 17 | - name: curl 18 | image: registry.gitlab.com/gitlab-ci-utils/curl-jq:3.2.1 19 | imagePullPolicy: IfNotPresent 20 | command: ["/bin/sh", "-c"] 21 | args: 22 | - 'curl -s -X POST -k -H "Authorization: ${ADMIN_KEY}" -H "Accept: application/json" -H "Content-Type: application/json" https://nginx/noticelists/update' 23 | envFrom: 24 | - secretRef: 25 | name: instance-secrets 26 | restartPolicy: Never 27 | backoffLimit: 2 28 | activeDeadlineSeconds: 1200 29 | concurrencyPolicy: Forbid -------------------------------------------------------------------------------- /kubernetes/manifests/cronjobs/update-taxonomies.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1 2 | kind: CronJob 3 | metadata: 4 | name: misp-cron-update-taxonomies 5 | labels: 6 | app.kubernetes.io/name: misp-cron 7 | spec: 8 | schedule: "10 3 * * *" 9 | jobTemplate: 10 | spec: 11 | template: 12 | metadata: 13 | labels: 14 | app.kubernetes.io/name: misp-cron 15 | spec: 16 | containers: 17 | - name: curl 18 | image: registry.gitlab.com/gitlab-ci-utils/curl-jq:3.2.1 19 | imagePullPolicy: IfNotPresent 20 | command: ["/bin/sh", "-c"] 21 | args: 22 | - 'curl -s -X POST -k -H "Authorization: ${ADMIN_KEY}" -H "Accept: application/json" -H "Content-Type: application/json" https://nginx/taxonomies/update' 23 | envFrom: 24 | - secretRef: 25 | name: instance-secrets 26 | restartPolicy: Never 27 | backoffLimit: 2 28 | activeDeadlineSeconds: 1200 29 | concurrencyPolicy: Forbid -------------------------------------------------------------------------------- /kubernetes/manifests/cronjobs/update-warninglists.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1 2 | kind: CronJob 3 | metadata: 4 | name: misp-cron-update-warninglists 5 | labels: 6 | app.kubernetes.io/name: misp-cron 7 | spec: 8 | schedule: "20 3 * * *" 9 | jobTemplate: 10 | spec: 11 | template: 12 | metadata: 13 | labels: 14 | app.kubernetes.io/name: misp-cron 15 | spec: 16 | containers: 17 | - name: curl 18 | image: registry.gitlab.com/gitlab-ci-utils/curl-jq:3.2.1 19 | imagePullPolicy: IfNotPresent 20 | command: ["/bin/sh", "-c"] 21 | args: 22 | - 'curl -s -X POST -k -H "Authorization: ${ADMIN_KEY}" -H "Accept: application/json" -H "Content-Type: application/json" https://minginxr/warninglists/update' 23 | envFrom: 24 | - secretRef: 25 | name: instance-secrets 26 | restartPolicy: Never 27 | backoffLimit: 2 28 | activeDeadlineSeconds: 600 29 | concurrencyPolicy: Forbid -------------------------------------------------------------------------------- /core/files/etc/nginx/sites-available/misp443: -------------------------------------------------------------------------------- 1 | server { 2 | listen 443 ssl; 3 | listen [::]:443 ssl; 4 | http2 on; 5 | 6 | # disable access logs 7 | access_log off; 8 | log_not_found off; 9 | error_log /dev/stderr error; 10 | 11 | # ssl options 12 | ssl_certificate /etc/nginx/certs/cert.pem; 13 | ssl_certificate_key /etc/nginx/certs/key.pem; 14 | ssl_session_timeout 1d; 15 | ssl_session_cache shared:MozSSL:10m; # about 40000 sessions 16 | ssl_session_tickets off; 17 | 18 | # ssl intermediate configuration 19 | ssl_protocols TLSv1.2 TLSv1.3; 20 | ssl_ecdh_curve X25519:prime256v1:secp384r1; 21 | ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305; 22 | ssl_prefer_server_ciphers off; 23 | 24 | # ssl enable HSTS 25 | add_header Strict-Transport-Security "max-age=15768000; includeSubdomains"; 26 | add_header X-Frame-Options SAMEORIGIN; 27 | 28 | # include misp 29 | include includes/misp; 30 | } 31 | -------------------------------------------------------------------------------- /core/files/etc/misp-docker/optional.defaults.json: -------------------------------------------------------------------------------- 1 | { 2 | "MISP.welcome_text_top": { 3 | "default_value": "", 4 | "command_args": "-f" 5 | }, 6 | "MISP.welcome_text_bottom": { 7 | "default_value": "", 8 | "command_args": "-f" 9 | }, 10 | "MISP.log_client_ip": { 11 | "default_value": true 12 | }, 13 | "MISP.log_user_ips": { 14 | "default_value": true 15 | }, 16 | "MISP.log_user_ips_authkeys": { 17 | "default_value": true 18 | }, 19 | "Plugin.Enrichment_services_enable": { 20 | "default_value": true 21 | }, 22 | "Plugin.Enrichment_timeout": { 23 | "default_value": 30 24 | }, 25 | "Plugin.Enrichment_hover_timeout": { 26 | "default_value": 5 27 | }, 28 | "Plugin.Import_services_enable": { 29 | "default_value": true 30 | }, 31 | "Plugin.Action_services_enable": { 32 | "default_value": true 33 | }, 34 | "Plugin.Export_services_enable": { 35 | "default_value": true 36 | }, 37 | "Plugin.Cortex_services_enable": { 38 | "default_value": false 39 | }, 40 | "Plugin.Workflow_enable": { 41 | "default_value": true 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /core/files/etc/nginx/includes/misp: -------------------------------------------------------------------------------- 1 | # define the root dir 2 | root /var/www/MISP/app/webroot; 3 | index index.php; 4 | 5 | # incrase the maximum body size 6 | client_max_body_size 50M; 7 | 8 | # added headers for hardening browser security 9 | add_header X-Content-Type-Options "nosniff" always; 10 | add_header X-Download-Options "noopen" always; 11 | add_header X-Frame-Options "SAMEORIGIN" always; 12 | add_header X-Permitted-Cross-Domain-Policies "none" always; 13 | add_header X-Robots-Tag "none" always; 14 | 15 | # remove X-Powered-By and nginx version, which is an information leak 16 | fastcgi_hide_header X-Powered-By; 17 | server_tokens off; 18 | 19 | location / { 20 | try_files $uri $uri/ /index.php$is_args$query_string; 21 | } 22 | 23 | location ~ ^/[^/]+\.php(/|$) { 24 | include snippets/fastcgi-php.conf; 25 | fastcgi_pass unix:/var/run/php/php8.4-fpm.sock; 26 | fastcgi_read_timeout 300s; 27 | fastcgi_send_timeout 300s; 28 | fastcgi_connect_timeout 300s; 29 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 30 | set $path_info $fastcgi_path_info; 31 | fastcgi_param PATH_INFO $path_info; 32 | } 33 | -------------------------------------------------------------------------------- /kubernetes/manifests/mysql.yaml: -------------------------------------------------------------------------------- 1 | # This works as a minimum viable db, but is not recommended 2 | apiVersion: apps/v1 3 | kind: StatefulSet 4 | metadata: 5 | name: mysql 6 | labels: 7 | app.kubernetes.io/name: misp-mysql 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app.kubernetes.io/name: misp-mysql 13 | serviceName: "misp-mysql" 14 | template: 15 | metadata: 16 | labels: 17 | app.kubernetes.io/name: misp-mysql 18 | spec: 19 | containers: 20 | - name: misp-mysql 21 | image: "mariadb:10.11" 22 | imagePullPolicy: IfNotPresent 23 | ports: 24 | - containerPort: 3306 25 | name: misp-mysql 26 | protocol: TCP 27 | resources: {} 28 | envFrom: 29 | - secretRef: 30 | name: mysql-credentials 31 | volumeMounts: 32 | - mountPath: /var/lib/mysql 33 | name: mysql-data 34 | volumeClaimTemplates: 35 | - metadata: 36 | name: mysql-data 37 | spec: 38 | accessModes: ["ReadWriteOnce"] 39 | resources: 40 | requests: 41 | storage: 15Gi 42 | -------------------------------------------------------------------------------- /core/files/etc/misp-docker/minimum_config.envars.json: -------------------------------------------------------------------------------- 1 | { 2 | "MISP.python_bin": { 3 | "default_value": "${PYTHON_BIN}" 4 | }, 5 | "SimpleBackgroundJobs.supervisor_host": { 6 | "default_value": "${SUPERVISOR_HOST}" 7 | }, 8 | "SimpleBackgroundJobs.supervisor_user": { 9 | "default_value": "${SUPERVISOR_USERNAME}" 10 | }, 11 | "SimpleBackgroundJobs.supervisor_password": { 12 | "default_value": "${SUPERVISOR_PASSWORD}" 13 | }, 14 | "MISP.redis_host": { 15 | "default_value": "${REDIS_HOST}" 16 | }, 17 | "MISP.redis_port": { 18 | "default_value": "${REDIS_PORT}" 19 | }, 20 | "MISP.redis_password": { 21 | "default_value": "${REDIS_PASSWORD}" 22 | }, 23 | "Plugin.ZeroMQ_redis_host": { 24 | "default_value": "${REDIS_HOST}" 25 | }, 26 | "Plugin.ZeroMQ_redis_port": { 27 | "default_value": "${REDIS_PORT}" 28 | }, 29 | "Plugin.ZeroMQ_redis_password": { 30 | "default_value": "${REDIS_PASSWORD}" 31 | }, 32 | "GnuPG.binary": { 33 | "default_value": "${GPG_BINARY}" 34 | }, 35 | "SimpleBackgroundJobs.redis_host": { 36 | "default_value": "${REDIS_HOST}" 37 | }, 38 | "SimpleBackgroundJobs.redis_port": { 39 | "default_value": "${REDIS_PORT}" 40 | }, 41 | "SimpleBackgroundJobs.redis_password": { 42 | "default_value": "${REDIS_PASSWORD}" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /core/files/kubernetes/entrypoint_fpm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | echo "INIT | Loading environment and functions" 3 | source /entrypoint.sh 4 | export PHP_LISTEN_FPM=true 5 | source /entrypoint_nginx.sh 6 | source /entrypoint_fpm.sh 7 | 8 | echo "INIT | Configuring supervisord for kubernetes" 9 | mv /etc/supervisor/conf.d/10-supervisor.conf{.kubernetes,} 10 | mv /etc/supervisor/conf.d/50-workers.conf{.kubernetes,} 11 | 12 | echo "INIT | Starting supervisord" 13 | /usr/local/bin/supervisord -c /etc/supervisor/supervisord.conf & 14 | 15 | # Initialize MySQL 16 | echo "INIT | Initialize MySQL ..." && init_mysql 17 | 18 | # Initialize MISP 19 | echo "INIT | Initialize MISP files and configurations ..." && init_misp_data_files 20 | echo "INIT | Update MISP app/files directory ..." && update_misp_data_files 21 | echo "INIT | Mirror file logs to stdout ..." && redirect_logs 22 | echo "INIT | Enforce MISP permissions ..." && enforce_misp_data_permissions 23 | 24 | # Run configure MISP script 25 | echo "INIT | Configure MISP installation ..." 26 | /configure_misp.sh 27 | 28 | if [[ -x /custom/files/customize_misp.sh ]]; then 29 | echo "INIT | Customize MISP installation ..." 30 | /custom/files/customize_misp.sh 31 | fi 32 | 33 | echo "Configure PHP | Change PHP values ..." && change_php_vars 34 | 35 | echo "Configure PHP | Starting PHP FPM" 36 | 37 | exec /usr/bin/tini -- /usr/sbin/php-fpm8.4 -R -F 38 | -------------------------------------------------------------------------------- /core/files/rest_client.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if ! command -v jq &> /dev/null 4 | then 5 | echo "aborting. jq could not be found" 6 | exit 1 7 | fi 8 | 9 | if ! command -v curl &> /dev/null 10 | then 11 | echo "aborting. curl could not be found" 12 | exit 1 13 | fi 14 | 15 | add_organization() { 16 | # empty uuid fallbacks to auto-generate 17 | curl -s --show-error -k \ 18 | -H "Authorization: ${2}" \ 19 | -H "Accept: application/json" \ 20 | -H "Content-type: application/json" \ 21 | -d "{ \ 22 | \"uuid\": \"${5}\", \ 23 | \"name\": \"${3}\", \ 24 | \"local\": ${4} \ 25 | }" ${1}/admin/organisations/add 26 | } 27 | 28 | get_organization() { 29 | curl -s --show-error -k \ 30 | -H "Authorization: ${2}" \ 31 | -H "Accept: application/json" \ 32 | -H "Content-type: application/json" ${1}/organisations/view/${3} | jq -e -r ".Organisation.id // empty" 33 | } 34 | 35 | add_server() { 36 | curl -s --show-error -k \ 37 | -H "Authorization: ${2}" \ 38 | -H "Accept: application/json" \ 39 | -H "Content-type: application/json" \ 40 | -d "${3}" ${1}/servers/add 41 | } 42 | 43 | get_server() { 44 | curl -s --show-error -k \ 45 | -H "Authorization: ${2}" \ 46 | -H "Accept: application/json" \ 47 | -H "Content-type: application/json" ${1}/servers | jq -e -r ".[] | select(.Server[\"name\"] == \"${3}\") | .Server.id" 48 | } 49 | -------------------------------------------------------------------------------- /kubernetes/manifests/deployment-nginx.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: nginx 5 | labels: 6 | app.kubernetes.io/name: nginx 7 | spec: 8 | strategy: 9 | type: Recreate 10 | replicas: 1 11 | selector: 12 | matchLabels: 13 | app.kubernetes.io/name: nginx 14 | template: 15 | metadata: 16 | labels: 17 | app.kubernetes.io/name: nginx 18 | spec: 19 | enableServiceLinks: false 20 | securityContext: 21 | # We want imported files to be owned by www-data group 22 | fsGroup: 33 23 | containers: 24 | - name: nginx 25 | command: 26 | - /kubernetes/entrypoint_nginx.sh 27 | image: "ghcr.io/misp/misp-docker/misp-core:latest" 28 | imagePullPolicy: IfNotPresent 29 | readinessProbe: 30 | tcpSocket: 31 | port: 443 32 | initialDelaySeconds: 1 33 | periodSeconds: 5 34 | timeoutSeconds: 5 35 | failureThreshold: 2 36 | successThreshold: 1 37 | env: 38 | # We only expose https anyway 39 | - name: DISABLE_SSL_REDIRECT 40 | value: "true" 41 | ports: 42 | - name: misp-https 43 | containerPort: 443 44 | protocol: TCP 45 | resources: 46 | limits: 47 | memory: 2048Mi 48 | requests: 49 | memory: 512Mi 50 | cpu: 50m 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /core/files/etc/supervisor/supervisord.conf: -------------------------------------------------------------------------------- 1 | ; supervisor config file 2 | 3 | [unix_http_server] 4 | file=/var/run/supervisor.sock ; (the path to the socket file) 5 | chmod=0700 ; sockef file mode (default 0700) 6 | nodaemon=true 7 | username=%(ENV_SUPERVISOR_USERNAME)s 8 | password=%(ENV_SUPERVISOR_PASSWORD)s 9 | 10 | [supervisord] 11 | logfile=/var/log/supervisor/supervisord.log ; (main log file;default $CWD/supervisord.log) 12 | pidfile=/var/run/supervisord.pid ; (supervisord pidfile;default supervisord.pid) 13 | childlogdir=/var/log/supervisor ; ('AUTO' child log dir, default $TEMP) 14 | nodaemon=true 15 | user=root 16 | 17 | ; the below section must remain in the config file for RPC 18 | ; (supervisorctl/web interface) to work, additional interfaces may be 19 | ; added by defining them in separate rpcinterface: sections 20 | [rpcinterface:supervisor] 21 | supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface 22 | 23 | [supervisorctl] 24 | serverurl=unix:///var/run/supervisor.sock ; use a unix:// URL for a unix socket 25 | username=%(ENV_SUPERVISOR_USERNAME)s 26 | password=%(ENV_SUPERVISOR_PASSWORD)s 27 | 28 | ; The [include] section can just contain the "files" setting. This 29 | ; setting can list multiple files (separated by whitespace or 30 | ; newlines). It can also contain wildcards. The filenames are 31 | ; interpreted as relative to this file. Included files *cannot* 32 | ; include files themselves. 33 | 34 | [include] 35 | files = /etc/supervisor/conf.d/*.conf 36 | -------------------------------------------------------------------------------- /guard/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG DOCKER_HUB_PROXY="" 2 | 3 | FROM "${DOCKER_HUB_PROXY}python:3.12-slim-trixie" AS python-build 4 | ENV DEBIAN_FRONTEND=noninteractive 5 | ARG GUARD_TAG 6 | ARG GUARD_COMMIT 7 | 8 | RUN <<-EOF 9 | apt-get update 10 | apt-get install -y --no-install-recommends \ 11 | ca-certificates \ 12 | git 13 | apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/lists/* 14 | EOF 15 | 16 | RUN mkdir /wheels 17 | 18 | RUN <<-EOF 19 | if [ ! -z ${GUARD_COMMIT} ]; then 20 | git clone https://github.com/MISP/misp-guard.git /srv/misp-guard && cd /srv/misp-guard && git checkout ${GUARD_COMMIT} 21 | else 22 | git clone --branch ${GUARD_TAG} --depth 1 https://github.com/MISP/misp-guard.git /srv/misp-guard 23 | fi 24 | EOF 25 | 26 | WORKDIR /srv/misp-guard/src 27 | RUN pip wheel -r requirements.txt --no-cache-dir -w /wheels/ 28 | 29 | 30 | 31 | FROM "${DOCKER_HUB_PROXY}python:3.12-slim-trixie" 32 | ENV DEBIAN_FRONTEND=noninteractive 33 | 34 | RUN <<-EOF 35 | apt-get update 36 | apt-get install -y --no-install-recommends \ 37 | jq 38 | apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/lists/* 39 | EOF 40 | 41 | COPY --from=python-build /wheels /wheels 42 | RUN pip install --no-cache-dir /wheels/*.whl && rm -rf /wheels 43 | 44 | COPY --from=python-build /srv /srv 45 | COPY files/ / 46 | 47 | WORKDIR /srv/misp-guard/src 48 | ENTRYPOINT [ "/entrypoint.sh" ] 49 | -------------------------------------------------------------------------------- /.github/workflows/test-build-latest.yml: -------------------------------------------------------------------------------- 1 | name: Build the Docker images 2 | 3 | on: 4 | pull_request: 5 | branches: ["master"] 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | 11 | strategy: 12 | matrix: 13 | target: [misp-core, misp-modules, misp-core-slim, misp-modules-slim, misp-guard] 14 | 15 | steps: 16 | - name: Checkout repository 17 | uses: actions/checkout@v4 18 | 19 | - name: Set up QEMU 20 | uses: docker/setup-qemu-action@v3 21 | 22 | - name: Set up Docker Buildx 23 | uses: docker/setup-buildx-action@v3 24 | 25 | - name: Initialize environment 26 | env: 27 | GITHUB_CONTEXT: ${{ toJson(github) }} 28 | run: | 29 | sed -e '/^[[:space:]]*$/d' -e '/[#@]/d' -e 's/\"//g' -e 's/\(^[^=]*\)=\(.*\)/\1="\2"/' template.env > env.hcl 30 | echo "COMMIT_HASH=`echo '${{ github.sha }}' | cut -c 1-7`" >> "$GITHUB_ENV" 31 | echo "NAMESPACE=local" >> "$GITHUB_ENV" 32 | 33 | - name: Prepare .env for compose validation 34 | run: cp template.env .env 35 | 36 | - name: Validate Docker Compose configuration 37 | run: docker compose -f docker-compose.yml config -q 38 | 39 | - name: Build 40 | uses: docker/bake-action@v6 41 | with: 42 | source: . 43 | push: false 44 | provenance: false 45 | files: docker-bake.hcl, env.hcl 46 | targets: ${{ matrix.target }} 47 | # set: | 48 | # *.cache-from=type=gha 49 | # *.cache-to=type=gha,mode=max 50 | -------------------------------------------------------------------------------- /kubernetes/manifests/cronjobs/pull-all-servers.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1 2 | kind: CronJob 3 | metadata: 4 | name: misp-cron-pull-all-servers 5 | labels: 6 | app.kubernetes.io/name: misp-cron 7 | spec: 8 | schedule: "0/5 * * * *" 9 | jobTemplate: 10 | spec: 11 | template: 12 | metadata: 13 | labels: 14 | app.kubernetes.io/name: misp-cron 15 | spec: 16 | containers: 17 | - name: curl 18 | image: registry.gitlab.com/gitlab-ci-utils/curl-jq:3.2.1 19 | imagePullPolicy: IfNotPresent 20 | command: ["/bin/sh", "-c"] 21 | args: 22 | - | 23 | echo "Fetching server list" 24 | json_data=$(curl -s -k -H "Authorization: ${ADMIN_KEY}" -H "Accept: application/json" -H "Content-Type: application/json" https://nginx/servers) 25 | echo "Fetched server list" 26 | id_list=$(echo "$json_data" | jq -r '.[]? | select(.Server.pull == true) | .Server.id') 27 | echo "Pulling all servers with pull enabled" 28 | for id in $id_list 29 | do 30 | echo "Creating job to pull from server with id $id" 31 | result=$(curl -s -k -H "Authorization: ${ADMIN_KEY}" -H "Accept: application/json" -H "Content-Type: application/json" https://nginx/servers/pull/$id) 32 | echo " - $(echo $result | jq -r '.message')" 33 | done 34 | envFrom: 35 | - secretRef: 36 | name: instance-secrets 37 | restartPolicy: Never 38 | backoffLimit: 2 39 | activeDeadlineSeconds: 1200 40 | concurrencyPolicy: Forbid -------------------------------------------------------------------------------- /kubernetes/manifests/cronjobs/push-all-servers.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1 2 | kind: CronJob 3 | metadata: 4 | name: misp-cron-push-all-servers 5 | labels: 6 | app.kubernetes.io/name: misp-cron 7 | spec: 8 | schedule: "0/15 * * * *" 9 | jobTemplate: 10 | spec: 11 | template: 12 | metadata: 13 | labels: 14 | app.kubernetes.io/name: misp-cron 15 | spec: 16 | containers: 17 | - name: curl 18 | image: registry.gitlab.com/gitlab-ci-utils/curl-jq:3.2.1 19 | imagePullPolicy: IfNotPresent 20 | command: ["/bin/sh", "-c"] 21 | args: 22 | - | 23 | echo "Fetching server list" 24 | json_data=$(curl -s -k -H "Authorization: ${ADMIN_KEY}" -H "Accept: application/json" -H "Content-Type: application/json" https://nginx/servers) 25 | echo "Fetched server list" 26 | id_list=$(echo "$json_data" | jq -r '.[]? | select(.Server.push == true) | .Server.id') 27 | echo "Pulling all servers with push enabled" 28 | for id in $id_list 29 | do 30 | echo "Creating job to push from server with id $id" 31 | result=$(curl -s -k -H "Authorization: ${ADMIN_KEY}" -H "Accept: application/json" -H "Content-Type: application/json" https://nginx/servers/push/$id) 32 | echo " - $(echo $result | jq -r '.message')" 33 | done 34 | envFrom: 35 | - secretRef: 36 | name: instance-secrets 37 | restartPolicy: Never 38 | backoffLimit: 2 39 | activeDeadlineSeconds: 1200 40 | concurrencyPolicy: Forbid -------------------------------------------------------------------------------- /kubernetes/manifests/services.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: misp-modules 5 | labels: 6 | app.kubernetes.io/name: misp-modules 7 | spec: 8 | type: ClusterIP 9 | ports: 10 | - port: 6666 11 | targetPort: misp-modules 12 | protocol: TCP 13 | name: misp-modules 14 | selector: 15 | app.kubernetes.io/name: misp 16 | --- 17 | apiVersion: v1 18 | kind: Service 19 | metadata: 20 | name: misp-php 21 | labels: 22 | app.kubernetes.io/name: misp-php 23 | spec: 24 | type: ClusterIP 25 | ports: 26 | - port: 9002 27 | targetPort: misp-php 28 | protocol: TCP 29 | name: misp-php 30 | selector: 31 | app.kubernetes.io/name: misp 32 | --- 33 | apiVersion: v1 34 | kind: Service 35 | metadata: 36 | name: nginx 37 | labels: 38 | app.kubernetes.io/name: nginx 39 | spec: 40 | type: ClusterIP 41 | ports: 42 | - port: 443 43 | targetPort: misp-https 44 | protocol: TCP 45 | name: misp-https 46 | selector: 47 | app.kubernetes.io/name: nginx 48 | --- 49 | apiVersion: v1 50 | kind: Service 51 | metadata: 52 | name: misp-mysql 53 | labels: 54 | app.kubernetes.io/name: misp-mysql 55 | spec: 56 | type: ClusterIP 57 | ports: 58 | - port: 3306 59 | targetPort: misp-mysql 60 | protocol: TCP 61 | name: misp-mysql 62 | selector: 63 | app.kubernetes.io/name: misp-mysql 64 | --- 65 | apiVersion: v1 66 | kind: Service 67 | metadata: 68 | name: redis 69 | labels: 70 | app.kubernetes.io/name: redis 71 | spec: 72 | type: ClusterIP 73 | ports: 74 | - port: 6379 75 | targetPort: redis 76 | protocol: TCP 77 | name: redis 78 | selector: 79 | app.kubernetes.io/name: redis 80 | -------------------------------------------------------------------------------- /.github/workflows/release-latest.yml: -------------------------------------------------------------------------------- 1 | name: Build the Docker images and push them to the container registry 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | workflow_dispatch: # manual trigger 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | 12 | strategy: 13 | matrix: 14 | target: [misp-core, misp-modules, misp-core-slim, misp-modules-slim, misp-guard] 15 | 16 | permissions: 17 | contents: read 18 | packages: write 19 | 20 | steps: 21 | - name: Checkout repository 22 | uses: actions/checkout@v4 23 | 24 | - name: Set up QEMU 25 | uses: docker/setup-qemu-action@v3 26 | 27 | - name: Set up Docker Buildx 28 | uses: docker/setup-buildx-action@v3 29 | 30 | - name: Initialize environment 31 | env: 32 | GITHUB_CONTEXT: ${{ toJson(github) }} 33 | run: | 34 | sed -e '/^[[:space:]]*$/d' -e '/[#@]/d' -e 's/\"//g' -e 's/\(^[^=]*\)=\(.*\)/\1="\2"/' template.env > env.hcl 35 | echo "COMMIT_HASH=`echo '${{ github.sha }}' | cut -c 1-7`" >> "$GITHUB_ENV" 36 | OWNER=$(echo "$GITHUB_REPOSITORY_OWNER" | tr '[:upper:]' '[:lower:]') 37 | echo "NAMESPACE=ghcr.io/${OWNER}/misp-docker" >> "$GITHUB_ENV" 38 | 39 | - name: Log in to the container registry 40 | uses: docker/login-action@v3 41 | with: 42 | registry: ghcr.io 43 | username: ${{ github.actor }} 44 | password: ${{ secrets.GITHUB_TOKEN }} 45 | 46 | - name: Build and push 47 | uses: docker/bake-action@v6 48 | with: 49 | source: . 50 | push: true 51 | provenance: false 52 | files: docker-bake.hcl, env.hcl 53 | targets: ${{ matrix.target }} 54 | # set: | 55 | # *.cache-from=type=gha 56 | # *.cache-to=type=gha,mode=max 57 | -------------------------------------------------------------------------------- /guard/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "allowlist": { 3 | "urls": [], 4 | "domains": [] 5 | }, 6 | "compartments_rules": { 7 | "can_reach": { 8 | "compartment_1": [ 9 | "compartment_1" 10 | ] 11 | } 12 | }, 13 | "instances": { 14 | "misp_container": { 15 | "ip": "AUTO_REPLACED_AT_RUNTIME", 16 | "host": "local-misp", 17 | "port": 443, 18 | "compartment_id": "compartment_1", 19 | "affiliation": "internal", 20 | "taxonomies_rules": { 21 | "required_taxonomies": [], 22 | "allowed_tags": {}, 23 | "blocked_tags": [ 24 | "tlp:red" 25 | ] 26 | }, 27 | "blocked_distribution_levels": [], 28 | "blocked_sharing_groups_uuids": [], 29 | "blocked_attribute_types": [], 30 | "blocked_attribute_categories": [], 31 | "blocked_object_types": [] 32 | }, 33 | "partner_example": { 34 | "ip": "10.0.0.1", 35 | "host": "partner.misp.org", 36 | "port": 443, 37 | "compartment_id": "compartment_1", 38 | "affiliation": "partner", 39 | "taxonomies_rules": { 40 | "required_taxonomies": [], 41 | "allowed_tags": { 42 | "tlp": [ 43 | "tlp:clear", 44 | "tlp:white", 45 | "tlp:green" 46 | ] 47 | }, 48 | "blocked_tags": [ 49 | "tlp:red" 50 | ] 51 | }, 52 | "blocked_distribution_levels": [], 53 | "blocked_sharing_groups_uuids": [], 54 | "blocked_attribute_types": [], 55 | "blocked_attribute_categories": [], 56 | "blocked_object_types": [] 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /core/files/etc/misp-docker/minimum_config.defaults.json: -------------------------------------------------------------------------------- 1 | { 2 | "MISP.tmpdir": { 3 | "default_value": "/var/www/MISP/app/tmp" 4 | }, 5 | "MISP.attachments_dir": { 6 | "default_value": "/var/www/MISP/app/files" 7 | }, 8 | "MISP.download_gpg_from_homedir": { 9 | "default_value": false 10 | }, 11 | "MISP.background_jobs": { 12 | "default_value": true 13 | }, 14 | "MISP.osuser": { 15 | "default_value": "www-data" 16 | }, 17 | "MISP.online_version_check": { 18 | "default_value": true 19 | }, 20 | "MISP.self_update": { 21 | "default_value": false 22 | }, 23 | "MISP.ca_path": { 24 | "default_value": "/etc/ssl/certs/ca-certificates.crt", 25 | "command_args": "-f" 26 | }, 27 | "MISP.redis_database": { 28 | "default_value": 13 29 | }, 30 | "MISP.menu_custom_right_link": { 31 | "default_value": "" 32 | }, 33 | "MISP.menu_custom_right_link_html": { 34 | "default_value": "" 35 | }, 36 | "Plugin.ZeroMQ_enable": { 37 | "default_value": false 38 | }, 39 | "Security.rest_client_enable_arbitrary_urls": { 40 | "default_value": false 41 | }, 42 | "Security.disable_local_feed_access": { 43 | "default_value": false 44 | }, 45 | "Security.disable_instance_file_uploads": { 46 | "default_value": false 47 | }, 48 | "Security.salt": { 49 | "default_value": "", 50 | "command_args": "-f" 51 | }, 52 | "SimpleBackgroundJobs.enabled": { 53 | "default_value": true 54 | }, 55 | "SimpleBackgroundJobs.supervisor_host": { 56 | "default_value": "127.0.0.1" 57 | }, 58 | "SimpleBackgroundJobs.supervisor_port": { 59 | "default_value": 9001 60 | }, 61 | "SimpleBackgroundJobs.supervisor_password": { 62 | "default_value": "supervisor" 63 | }, 64 | "SimpleBackgroundJobs.supervisor_user": { 65 | "default_value": "supervisor" 66 | }, 67 | "SimpleBackgroundJobs.redis_database": { 68 | "default_value": 1 69 | }, 70 | "SimpleBackgroundJobs.redis_namespace": { 71 | "default_value": "background_jobs" 72 | }, 73 | "SimpleBackgroundJobs.max_job_history_ttl": { 74 | "default_value": 86400 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /kubernetes/README.md: -------------------------------------------------------------------------------- 1 | # MISP kubernetes manifests 2 | 3 | These are minimum viable kubernetes manifests for 4 | 5 | - MISP [deployment](./manifests/deployment-misp.yaml) 6 | - Nginx [deployment](./manifests/deployment-nginx.yaml) 7 | - MySQL [statefulset](./manifests/mysql.yaml) 8 | - Redis [deployment](./manifests/redis.yaml) 9 | - Associated [services](./manifests/services.yaml) 10 | - [CronJobs](./manifests/cronjobs/) for various housekeeping tasks 11 | 12 | There is also an [example network policy](./manifests/policy-cilium.yaml) (Cilium specific) that assumes the use of ingress-nginx in the ingress-nginx namespace to expose MISP. 13 | 14 | The manifests and required secrets can be generated through [kustomize](./kustomization.yaml). 15 | 16 | ## Usage 17 | 18 | 1. Edit [instance-secrets.env](./instance-secrets.env). Optionally introduce other settings documented i [template.env](../template.env) 19 | - If S3_ variables are defined, MISP will store event attachments in the specified bucket. 20 | - To instead store locally, remove any S3_ variables an ensure you mount a PVC at `/var/www/MISP/app/files` 21 | 1. Edit [mysql-credentials.env](./mysql-credentials.env) to set your desired database credentials. 22 | 1. Build and apply all the manifests to the cluster: `kustomize build . | kubectl apply -f -` 23 | 24 | It is highly recommended to use a database operator such as Percona instead of relying on the example mysql statefulset found here. 25 | 26 | This kustomize can also be used as a base for further customization, e.g. one that overrides instance-secrets: 27 | 28 | ```yaml 29 | # kustomization.yaml 30 | apiVersion: kustomize.config.k8s.io/v1beta1 31 | kind: Kustomization 32 | 33 | resources: 34 | - 'https://github.com/MISP/misp-docker.git/kubernetes?ref=master' 35 | 36 | generators: 37 | - ./custom-instance-generator.yaml 38 | --- 39 | # custom-instance-generator.yaml 40 | apiVersion: builtin 41 | kind: SecretGenerator 42 | metadata: 43 | name: instance-secrets 44 | literals: 45 | - SUPERVISOR_HOST=remote-host.fqdn 46 | behavior: merge 47 | ``` 48 | 49 | This will then override or add the SUPERVISOR_HOST, while keeping everything else due to the `merge` behavior. 50 | 51 | ### Storage 52 | 53 | By default these manifests assume the use of S3 based attachment storage (see `S3_*` keys in [instance-secrets.env](./instance-secrets.env)). 54 | 55 | If these variables are not all supplied, the default is for MISP to store attachment data in `/var/www/MISP/app/files/`. This should be backed by a persistentVolumeClaim of your preferred storageclass named `misp-files`. -------------------------------------------------------------------------------- /core/files/etc/supervisor/conf.d/50-workers.conf.kubernetes: -------------------------------------------------------------------------------- 1 | # Workers are set to NOT auto start so we have time to enforce permissions on the cache first 2 | 3 | [group:misp-workers] 4 | programs=default,email,cache,prio,update,scheduler 5 | 6 | [program:default] 7 | directory=/var/www/MISP 8 | command=/var/www/MISP/app/Console/cake start_worker default 9 | process_name=%(program_name)s_%(process_num)02d 10 | numprocs=%(ENV_NUM_WORKERS_DEFAULT)s 11 | autostart=false 12 | autorestart=true 13 | stdout_logfile=/dev/stdout 14 | stdout_logfile_maxbytes=0 15 | stderr_logfile=/dev/stderr 16 | stderr_logfile_maxbytes=0 17 | directory=/var/www/MISP 18 | user=www-data 19 | 20 | [program:prio] 21 | directory=/var/www/MISP 22 | command=/var/www/MISP/app/Console/cake start_worker prio 23 | process_name=%(program_name)s_%(process_num)02d 24 | numprocs=%(ENV_NUM_WORKERS_PRIO)s 25 | autostart=false 26 | autorestart=true 27 | stdout_logfile=/dev/stdout 28 | stdout_logfile_maxbytes=0 29 | stderr_logfile=/dev/stderr 30 | stderr_logfile_maxbytes=0 31 | directory=/var/www/MISP 32 | user=www-data 33 | 34 | [program:email] 35 | directory=/var/www/MISP 36 | command=/var/www/MISP/app/Console/cake start_worker email 37 | process_name=%(program_name)s_%(process_num)02d 38 | numprocs=%(ENV_NUM_WORKERS_EMAIL)s 39 | autostart=false 40 | autorestart=true 41 | stdout_logfile=/dev/stdout 42 | stdout_logfile_maxbytes=0 43 | stderr_logfile=/dev/stderr 44 | stderr_logfile_maxbytes=0 45 | directory=/var/www/MISP 46 | user=www-data 47 | 48 | [program:update] 49 | directory=/var/www/MISP 50 | command=/var/www/MISP/app/Console/cake start_worker update 51 | process_name=%(program_name)s_%(process_num)02d 52 | numprocs=%(ENV_NUM_WORKERS_UPDATE)s 53 | autostart=false 54 | autorestart=true 55 | stderr_logfile=/var/www/MISP/app/tmp/logs/misp-workers-errors.log 56 | stdout_logfile=/var/www/MISP/app/tmp/logs/misp-workers.log 57 | directory=/var/www/MISP 58 | user=www-data 59 | 60 | [program:cache] 61 | directory=/var/www/MISP 62 | command=/var/www/MISP/app/Console/cake start_worker cache 63 | process_name=%(program_name)s_%(process_num)02d 64 | numprocs=%(ENV_NUM_WORKERS_CACHE)s 65 | autostart=false 66 | autorestart=true 67 | stdout_logfile=/dev/stdout 68 | stdout_logfile_maxbytes=0 69 | stderr_logfile=/dev/stderr 70 | stderr_logfile_maxbytes=0 71 | user=www-data 72 | 73 | [program:scheduler] 74 | directory=/var/www/MISP 75 | command=/var/www/MISP/app/Console/cake scheduler_worker 76 | process_name=%(program_name)s_%(process_num)02d 77 | numprocs=1 78 | autostart=false 79 | autorestart=true 80 | stdout_logfile=/dev/stdout 81 | stdout_logfile_maxbytes=0 82 | stderr_logfile=/dev/stderr 83 | stderr_logfile_maxbytes=0 84 | user=www-data -------------------------------------------------------------------------------- /kubernetes/manifests/policy-example-cilium.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: cilium.io/v2 2 | kind: CiliumNetworkPolicy 3 | metadata: 4 | name: nw-mysql 5 | spec: 6 | endpointSelector: 7 | matchLabels: 8 | app.kubernetes.io/name: misp-mysql 9 | ingress: 10 | - fromEndpoints: 11 | - matchLabels: 12 | app.kubernetes.io/name: misp 13 | toPorts: 14 | - ports: 15 | - port: "3306" 16 | egress: 17 | - toEndpoints: 18 | - matchLabels: 19 | app.kubernetes.io/name: misp 20 | - toEndpoints: 21 | - matchLabels: 22 | io.kubernetes.pod.namespace: kube-system 23 | k8s-app: kube-dns 24 | toPorts: 25 | - ports: 26 | - port: "53" 27 | protocol: UDP 28 | rules: 29 | dns: 30 | - matchPattern: "*" 31 | --- 32 | apiVersion: cilium.io/v2 33 | kind: CiliumNetworkPolicy 34 | metadata: 35 | name: nw-redis 36 | spec: 37 | endpointSelector: 38 | matchLabels: 39 | app.kubernetes.io/name: redis 40 | ingress: 41 | - fromEndpoints: 42 | - matchLabels: 43 | app.kubernetes.io/name: misp 44 | toPorts: 45 | - ports: 46 | - port: "6379" 47 | egress: 48 | - toEndpoints: 49 | - matchLabels: 50 | app.kubernetes.io/name: misp 51 | - toEndpoints: 52 | - matchLabels: 53 | io.kubernetes.pod.namespace: kube-system 54 | k8s-app: kube-dns 55 | toPorts: 56 | - ports: 57 | - port: "53" 58 | protocol: UDP 59 | rules: 60 | dns: 61 | - matchPattern: "*" 62 | --- 63 | apiVersion: cilium.io/v2 64 | kind: CiliumNetworkPolicy 65 | metadata: 66 | name: nw-nginx 67 | spec: 68 | endpointSelector: 69 | matchLabels: 70 | app.kubernetes.io/name: nginx 71 | ingress: 72 | - fromEndpoints: 73 | - matchLabels: 74 | app.kubernetes.io/name: misp-cron 75 | toPorts: 76 | - ports: 77 | - port: "443" 78 | - fromEndpoints: 79 | - matchLabels: 80 | app.kubernetes.io/component: controller 81 | io.kubernetes.pod.namespace: ingress-nginx 82 | toPorts: 83 | - ports: 84 | - port: "443" 85 | --- 86 | apiVersion: cilium.io/v2 87 | kind: CiliumNetworkPolicy 88 | metadata: 89 | name: nw-misp 90 | spec: 91 | endpointSelector: 92 | matchLabels: 93 | app.kubernetes.io/name: misp 94 | ingress: 95 | - fromEndpoints: 96 | - matchLabels: 97 | app.kubernetes.io/name: nginx 98 | toPorts: 99 | - ports: 100 | - port: "9002" 101 | -------------------------------------------------------------------------------- /core/files/etc/supervisor/conf.d/50-workers.conf: -------------------------------------------------------------------------------- 1 | # Workers are set to NOT auto start so we have time to enforce permissions on the cache first 2 | 3 | [group:misp-workers] 4 | programs=default,email,cache,prio,update,scheduler 5 | 6 | [program:default] 7 | directory=/var/www/MISP 8 | command=/var/www/MISP/app/Console/cake start_worker default 9 | process_name=%(program_name)s_%(process_num)02d 10 | numprocs=%(ENV_NUM_WORKERS_DEFAULT)s 11 | autostart=false 12 | autorestart=true 13 | redirect_stderr=false 14 | stderr_logfile=/var/www/MISP/app/tmp/logs/misp-workers-errors.log 15 | stdout_logfile=/var/www/MISP/app/tmp/logs/misp-workers.log 16 | directory=/var/www/MISP 17 | user=www-data 18 | 19 | [program:prio] 20 | directory=/var/www/MISP 21 | command=/var/www/MISP/app/Console/cake start_worker prio 22 | process_name=%(program_name)s_%(process_num)02d 23 | numprocs=%(ENV_NUM_WORKERS_PRIO)s 24 | autostart=false 25 | autorestart=true 26 | redirect_stderr=false 27 | stderr_logfile=/var/www/MISP/app/tmp/logs/misp-workers-errors.log 28 | stdout_logfile=/var/www/MISP/app/tmp/logs/misp-workers.log 29 | directory=/var/www/MISP 30 | user=www-data 31 | 32 | [program:email] 33 | directory=/var/www/MISP 34 | command=/var/www/MISP/app/Console/cake start_worker email 35 | process_name=%(program_name)s_%(process_num)02d 36 | numprocs=%(ENV_NUM_WORKERS_EMAIL)s 37 | autostart=false 38 | autorestart=true 39 | redirect_stderr=false 40 | stderr_logfile=/var/www/MISP/app/tmp/logs/misp-workers-errors.log 41 | stdout_logfile=/var/www/MISP/app/tmp/logs/misp-workers.log 42 | directory=/var/www/MISP 43 | user=www-data 44 | 45 | [program:update] 46 | directory=/var/www/MISP 47 | command=/var/www/MISP/app/Console/cake start_worker update 48 | process_name=%(program_name)s_%(process_num)02d 49 | numprocs=%(ENV_NUM_WORKERS_UPDATE)s 50 | autostart=false 51 | autorestart=true 52 | redirect_stderr=false 53 | stderr_logfile=/var/www/MISP/app/tmp/logs/misp-workers-errors.log 54 | stdout_logfile=/var/www/MISP/app/tmp/logs/misp-workers.log 55 | directory=/var/www/MISP 56 | user=www-data 57 | 58 | [program:cache] 59 | directory=/var/www/MISP 60 | command=/var/www/MISP/app/Console/cake start_worker cache 61 | process_name=%(program_name)s_%(process_num)02d 62 | numprocs=%(ENV_NUM_WORKERS_CACHE)s 63 | autostart=false 64 | autorestart=true 65 | redirect_stderr=false 66 | stderr_logfile=/var/www/MISP/app/tmp/logs/misp-workers-errors.log 67 | stdout_logfile=/var/www/MISP/app/tmp/logs/misp-workers.log 68 | user=www-data 69 | 70 | [program:scheduler] 71 | directory=/var/www/MISP 72 | command=/var/www/MISP/app/Console/cake scheduler_worker 73 | process_name=%(program_name)s_%(process_num)02d 74 | numprocs=1 75 | autostart=false 76 | autorestart=true 77 | redirect_stderr=false 78 | stderr_logfile=/var/www/MISP/app/tmp/logs/misp-workers-errors.log 79 | stdout_logfile=/var/www/MISP/app/tmp/logs/misp-workers.log 80 | user=www-data -------------------------------------------------------------------------------- /core/files/etc/misp-docker/initialisation.defaults.json: -------------------------------------------------------------------------------- 1 | { 2 | "MISP.language": { 3 | "default_value": "eng" 4 | }, 5 | "MISP.showorg": { 6 | "default_value": true 7 | }, 8 | "MISP.default_event_distribution": { 9 | "default_value": 1 10 | }, 11 | "MISP.default_event_tag_collection": { 12 | "default_value": 0 13 | }, 14 | "MISP.default_attribute_distribution": { 15 | "default_value": "event" 16 | }, 17 | "MISP.proposals_block_attributes": { 18 | "default_value": false 19 | }, 20 | "MISP.tagging": { 21 | "default_value": true 22 | }, 23 | "MISP.enableEventBlocklisting": { 24 | "default_value": true 25 | }, 26 | "MISP.enableOrgBlocklisting": { 27 | "default_value": true 28 | }, 29 | "MISP.store_api_access_time": { 30 | "default_value": false 31 | }, 32 | "MISP.log_auth": { 33 | "default_value": true 34 | }, 35 | "MISP.log_new_audit": { 36 | "default_value": true 37 | }, 38 | "MISP.disable_user_login_change": { 39 | "default_value": false 40 | }, 41 | "MISP.incoming_tags_disabled_by_default": { 42 | "default_value": false 43 | }, 44 | "MISP.full_tags_on_event_index": { 45 | "default_value": 1 46 | }, 47 | "MISP.event_alert_republish_ban": { 48 | "default_value": true 49 | }, 50 | "MISP.showCorrelationsOnIndex": { 51 | "default_value": true 52 | }, 53 | "MISP.server_settings_skip_backup_rotate": { 54 | "default_value": false 55 | }, 56 | "MISP.user_email_notification_ban": { 57 | "default_value": true 58 | }, 59 | "MISP.delegation": { 60 | "default_value": true 61 | }, 62 | "MISP.take_ownership_xml_import": { 63 | "default_value": false 64 | }, 65 | "MISP.unpublishedprivate": { 66 | "default_value": false 67 | }, 68 | "MISP.terms_download": { 69 | "default_value": false 70 | }, 71 | "MISP.showorgalternate": { 72 | "default_value": false 73 | }, 74 | "MISP.event_alert_republish_ban_refresh_on_retry": { 75 | "default_value": true 76 | }, 77 | "MISP.event_alert_republish_ban_threshold": { 78 | "default_value": 120 79 | }, 80 | "Security.advanced_authkeys": { 81 | "default_value": true 82 | }, 83 | "Security.disable_browser_cache": { 84 | "default_value": true 85 | }, 86 | "Security.check_sec_fetch_site_header": { 87 | "default_value": true 88 | }, 89 | "Security.username_in_response_header": { 90 | "default_value": true 91 | }, 92 | "Security.do_not_log_authkeys": { 93 | "default_value": true 94 | }, 95 | "Security.log_each_individual_auth_fail": { 96 | "default_value": true 97 | }, 98 | "Security.alert_on_suspicious_logins": { 99 | "default_value": true 100 | }, 101 | "Security.require_password_confirmation": { 102 | "default_value": true 103 | }, 104 | "SecureAuth.amount": { 105 | "default_value": 5 106 | }, 107 | "SecureAuth.expire": { 108 | "default_value": 300 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /modules/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG DOCKER_HUB_PROXY="" 2 | ARG MISP_UID=1001 3 | ARG MISP_GID=1001 4 | 5 | 6 | FROM "${DOCKER_HUB_PROXY}python:3.12-slim-trixie" AS python-build 7 | ENV DEBIAN_FRONTEND=noninteractive 8 | ARG MODULES_TAG 9 | ARG MODULES_COMMIT 10 | ARG MODULES_FLAVOR 11 | 12 | # Uncomment when building in corporate environments 13 | # COPY ./cert.pem /usr/local/share/ca-certificates/rootca.pem 14 | # COPY ./cert.pem /usr/lib/ssl/cert.pem 15 | 16 | RUN <<-EOF 17 | apt-get update 18 | if [ "${MODULES_FLAVOR}" = "slim" ]; then 19 | apt-get install -y --no-install-recommends \ 20 | ca-certificates \ 21 | git 22 | else 23 | apt-get install -y --no-install-recommends \ 24 | ca-certificates \ 25 | git \ 26 | build-essential \ 27 | libpoppler-cpp-dev 28 | fi 29 | apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/lists/* 30 | EOF 31 | 32 | RUN mkdir /wheels 33 | 34 | RUN <<-EOF 35 | if [ ! -z ${MODULES_COMMIT} ]; then 36 | git clone https://github.com/MISP/misp-modules.git /srv/misp-modules && cd /srv/misp-modules && git checkout ${MODULES_COMMIT} 37 | else 38 | git clone --branch ${MODULES_TAG} --depth 1 https://github.com/MISP/misp-modules.git /srv/misp-modules 39 | fi 40 | EOF 41 | 42 | WORKDIR /srv/misp-modules 43 | RUN pip install poetry 44 | RUN sed -i "s/^requires-python = .*/requires-python = \"$(python -c 'import platform; print(platform.python_version())')\"/" pyproject.toml 45 | RUN poetry lock 46 | RUN poetry self add poetry-plugin-export 47 | RUN <<-EOF 48 | if [ "${MODULES_FLAVOR}" = "slim" ]; then 49 | poetry export --without-hashes -f requirements.txt -o requirements.txt 50 | else 51 | poetry export -E all --without-hashes -f requirements.txt -o requirements.txt 52 | fi 53 | EOF 54 | RUN pip wheel -r requirements.txt --no-cache-dir -w /wheels/ 55 | RUN poetry build --output /wheels/ 56 | 57 | WORKDIR /srv/ 58 | RUN rm -rf /srv/misp-modules 59 | 60 | 61 | FROM "${DOCKER_HUB_PROXY}python:3.12-slim-trixie" 62 | ENV DEBIAN_FRONTEND=noninteractive 63 | ARG MODULES_FLAVOR 64 | ARG MISP_UID 65 | ARG MISP_GID 66 | 67 | RUN <<-EOF 68 | apt-get update 69 | if [ "${MODULES_FLAVOR}" = "slim" ]; then 70 | : 71 | else 72 | apt-get install -y --no-install-recommends \ 73 | libgl1 \ 74 | libpoppler-cpp2 \ 75 | tesseract-ocr \ 76 | libzbar0 77 | fi 78 | apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/lists/* 79 | EOF 80 | 81 | RUN groupadd -g "${MISP_GID}" misp && \ 82 | useradd -u "${MISP_UID}" -g "${MISP_GID}" -m -d /home/misp -s /usr/sbin/nologin misp 83 | 84 | COPY --from=python-build /wheels /wheels 85 | RUN pip install --no-cache-dir /wheels/*.whl && rm -rf /wheels 86 | RUN bash -c 'mkdir -p /custom/{action_mod,expansion,export_mod,import_mod} && chown -R misp:misp /custom' 87 | 88 | USER misp 89 | 90 | ENTRYPOINT [ "/usr/local/bin/misp-modules", "-l", "0.0.0.0", "-c", "/custom/"] 91 | -------------------------------------------------------------------------------- /kubernetes/manifests/deployment-misp.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: misp 5 | labels: 6 | app.kubernetes.io/name: misp 7 | spec: 8 | strategy: 9 | type: Recreate 10 | replicas: 1 11 | selector: 12 | matchLabels: 13 | app.kubernetes.io/name: misp 14 | template: 15 | metadata: 16 | labels: 17 | app.kubernetes.io/name: misp 18 | spec: 19 | enableServiceLinks: false 20 | securityContext: 21 | # We want imported files to be owned by www-data group 22 | fsGroup: 33 23 | containers: 24 | - name: misp 25 | command: 26 | - /kubernetes/entrypoint_fpm.sh 27 | image: "ghcr.io/misp/misp-docker/misp-core:latest" 28 | imagePullPolicy: IfNotPresent 29 | readinessProbe: 30 | tcpSocket: 31 | port: 9002 32 | initialDelaySeconds: 15 33 | periodSeconds: 5 34 | timeoutSeconds: 5 35 | failureThreshold: 2 36 | successThreshold: 1 37 | resources: 38 | limits: 39 | memory: 2048Mi 40 | requests: 41 | memory: 512Mi 42 | cpu: 50m 43 | ports: 44 | - name: misp-php 45 | containerPort: 9002 46 | protocol: TCP 47 | env: 48 | # Don't print sensitive strings to log 49 | - name: DISABLE_PRINTING_PLAINTEXT_CREDENTIALS 50 | value: "true" 51 | # Skip waiting for updates before proceeding with startup 52 | - name: ENABLE_BACKGROUND_UPDATES 53 | value: "true" 54 | # Give us output NOW 55 | - name: PYTHONUNBUFFERED 56 | value: "1" 57 | - name: MISP_MODULES_FQDN 58 | value: http://localhost 59 | - name: MYSQL_HOST 60 | value: misp-mysql 61 | envFrom: 62 | - secretRef: 63 | name: mysql-credentials 64 | - secretRef: 65 | name: instance-secrets 66 | volumeMounts: 67 | # For centralised collection (e.g. tailing to stdout) 68 | - mountPath: /var/www/MISP/app/tmp/logs 69 | name: shared-logs 70 | # Non-S3 persistence for attachment data 71 | #- mountPath: /var/www/MISP/app/files 72 | # name: misp-files 73 | - name: misp-modules 74 | resources: 75 | limits: 76 | memory: 2048Mi 77 | requests: 78 | memory: 128Mi 79 | cpu: 20m 80 | image: "ghcr.io/misp/misp-docker/misp-modules:latest" 81 | imagePullPolicy: IfNotPresent 82 | readinessProbe: 83 | tcpSocket: 84 | port: 6666 85 | initialDelaySeconds: 1 86 | periodSeconds: 5 87 | timeoutSeconds: 5 88 | failureThreshold: 2 89 | successThreshold: 1 90 | ports: 91 | - name: misp-modules 92 | containerPort: 6666 93 | protocol: TCP 94 | volumes: 95 | - emptyDir: 96 | sizeLimit: 300Mi 97 | name: shared-logs 98 | #- name: misp-files 99 | # persistentVolumeClaim: 100 | # claimName: misp-files 101 | 102 | -------------------------------------------------------------------------------- /core/files/utilities.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | sensitive_settings=( 4 | "MISP.redis_password" 5 | "Plugin.ZeroMQ_redis_password" 6 | "SimpleBackgroundJobs.redis_password" 7 | "Plugin.S3_aws_secret_key" 8 | "Security.encryption_key" 9 | "GnuPG.password" 10 | ) 11 | 12 | # Check whether passed env variables are defined 13 | check_env_vars() { 14 | local required_vars=("$@") 15 | 16 | missing_vars=() 17 | for i in "${required_vars[@]}" 18 | do 19 | test -n "${!i:+y}" || missing_vars+=("$i") 20 | done 21 | if [ ${#missing_vars[@]} -ne 0 ] 22 | then 23 | echo "The following env variables are not set:" 24 | printf ' %q\n' "${missing_vars[@]}" 25 | exit 1 26 | fi 27 | } 28 | 29 | # Kludgy alternative to using cake Admin getSetting. 30 | setting_is_set_alt() { 31 | local setting="$1" 32 | local config_json=$(echo ''|/usr/bin/php) 33 | local db_settings_enabled=$(jq -e 'getpath(("MISP.system_setting_db" | split("."))) // false' <<< $config_json) 34 | local setting_in_config_file=$(jq -e 'getpath(("'"$setting"'" | split("."))) != null' <<< $config_json) 35 | if $setting_in_config_file; then 36 | return 0 37 | elif $db_settings_enabled; then 38 | local setting_in_db=$(echo "SELECT EXISTS(SELECT 1 FROM $MYSQL_DATABASE.system_settings WHERE setting = \"${setting}\");" | ${MYSQL_CMD}) 39 | if [[ $setting_in_db -eq 1 ]]; then 40 | return 0 41 | fi 42 | fi 43 | return 1 44 | } 45 | 46 | set_default_settings() { 47 | local settings_json="$1" 48 | local description="$2" 49 | 50 | for setting in $(jq -r 'keys[]' <<< $settings_json); do 51 | local default_value="$(jq -r '."'"$setting"'"["default_value"]' <<< $settings_json)" 52 | local command_args="$(jq -r '."'"$setting"'"["command_args"] // ""' <<< $settings_json)" 53 | 54 | set_safe_default "$setting" "$default_value" "$description" "$command_args" 55 | done 56 | } 57 | 58 | enforce_env_settings() { 59 | local settings_json="$1" 60 | local description="$2" 61 | for setting in $(jq -r 'keys[]' <<< $settings_json); do 62 | local default_value="$(jq -r '."'"$setting"'"["default_value"]' <<< $settings_json)" 63 | local command_args="$(jq -r '."'"$setting"'"["command_args"] // ""' <<< $settings_json)" 64 | local print_value="$default_value" 65 | if [[ "$DISABLE_PRINTING_PLAINTEXT_CREDENTIALS" == "true" ]] && [[ " ${sensitive_settings[@]} " =~ " ${setting} " ]]; then 66 | print_value='' 67 | fi 68 | echo "Enforcing $description setting '$setting' to env var or default value '$print_value'..." 69 | sudo -u www-data /var/www/MISP/app/Console/cake Admin setSetting -q $command_args "$setting" "$default_value" 70 | done 71 | } 72 | 73 | set_safe_default() { 74 | local setting="$1" 75 | local default_value="$2" 76 | local description="$3" 77 | local command_args="$4" 78 | 79 | if ! setting_is_set_alt "$setting"; then 80 | local print_value="$default_value" 81 | if [[ "$DISABLE_PRINTING_PLAINTEXT_CREDENTIALS" == "true" ]] && [[ " ${sensitive_settings[@]} " =~ " ${setting} " ]]; then 82 | print_value='' 83 | fi 84 | echo "Updating unset $description setting '$setting' to '$print_value'..." 85 | sudo -u www-data /var/www/MISP/app/Console/cake Admin setSetting -q $command_args "$setting" "$default_value" 86 | fi 87 | } 88 | 89 | init_settings() { 90 | local description="$1" 91 | local enforced="/etc/misp-docker/${description}.envars.json" 92 | local defaults="/etc/misp-docker/${description}.defaults.json" 93 | 94 | if [[ -e "$enforced" ]]; then 95 | echo "... enforcing env var settings" 96 | local settings_json="$(envsubst < $enforced)" 97 | enforce_env_settings "$settings_json" "$description" 98 | fi 99 | 100 | if [[ -e "$defaults" ]]; then 101 | echo "... checking for unset default settings" 102 | local settings_json="$(cat $defaults)" 103 | set_default_settings "$settings_json" "$description" 104 | fi 105 | } -------------------------------------------------------------------------------- /core/files/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # export env variables again so they are not mandatory in docker-compose.yml in a backward compatible manner 4 | export NUM_WORKERS_DEFAULT=${NUM_WORKERS_DEFAULT:-${WORKERS:-5}} 5 | export NUM_WORKERS_PRIO=${NUM_WORKERS_PRIO:-${WORKERS:-5}} 6 | export NUM_WORKERS_EMAIL=${NUM_WORKERS_EMAIL:-${WORKERS:-5}} 7 | export NUM_WORKERS_UPDATE=${NUM_WORKERS_UPDATE:-${WORKERS:-1}} 8 | export NUM_WORKERS_CACHE=${NUM_WORKERS_CACHE:-${WORKERS:-5}} 9 | 10 | export MYSQL_HOST=${MYSQL_HOST:-db} 11 | export MYSQL_PORT=${MYSQL_PORT:-3306} 12 | export MYSQL_USER=${MYSQL_USER:-misp} 13 | export MYSQL_PASSWORD=${MYSQL_PASSWORD:-example} 14 | export MYSQL_DATABASE=${MYSQL_DATABASE:-misp} 15 | export MYSQL_CMD="mysql -u $MYSQL_USER -p$MYSQL_PASSWORD -P $MYSQL_PORT -h $MYSQL_HOST -r -N $MYSQL_DATABASE" 16 | export MYSQL_TLS=${MYSQL_TLS:-false} 17 | export MYSQL_TLS_CA=${MYSQL_TLS_CA} 18 | export MYSQL_TLS_CERT=${MYSQL_TLS_CERT} 19 | export MYSQL_TLS_KEY=${MYSQL_TLS_KEY} 20 | if [[ "${MYSQL_TLS}" != true ]]; then 21 | MYSQL_CMD+=" --skip-ssl" 22 | export MYSQL_CMD 23 | fi 24 | export REDIS_HOST=${REDIS_HOST:-redis} 25 | export REDIS_PORT=${REDIS_PORT:-6379} 26 | export ENABLE_REDIS_EMPTY_PASSWORD=${ENABLE_REDIS_EMPTY_PASSWORD:-false} 27 | 28 | # Set Redis password based on ENABLE_REDIS_EMPTY_PASSWORD setting 29 | if [ "$ENABLE_REDIS_EMPTY_PASSWORD" = "true" ]; then 30 | # This still need to be set to empty string to ensure all places where it's used got the correct value 31 | export REDIS_PASSWORD="" 32 | else 33 | export REDIS_PASSWORD=${REDIS_PASSWORD:-redispassword} 34 | fi 35 | export BASE_URL=${BASE_URL:-https://localhost} 36 | export DISABLE_IPV6=${DISABLE_IPV6:-false} 37 | export DISABLE_SSL_REDIRECT=${DISABLE_SSL_REDIRECT:-false} 38 | export DISABLE_CA_REFRESH=${DISABLE_CA_REFRESH:-false} 39 | export SMTP_FQDN=${SMTP_FQDN:-mail} 40 | export SMTP_PORT=${SMTP_PORT:-25} 41 | 42 | export CRON_USER_ID=${CRON_USER_ID:-1} 43 | export CRON_PULLALL=${CRON_PULLALL:-86400} 44 | export CRON_PUSHALL=${CRON_PUSHALL:-86400} 45 | 46 | export ADMIN_EMAIL=${ADMIN_EMAIL:-admin@admin.test} 47 | export MISP_CONTACT=${MISP_CONTACT:-$ADMIN_EMAIL} 48 | export MISP_EMAIL=${MISP_EMAIL:-$ADMIN_EMAIL} 49 | export GPG_PASSPHRASE=${GPG_PASSPHRASE:-passphrase} 50 | export MISP_MODULES_FQDN=${MISP_MODULES_FQDN:-http://misp-modules} 51 | export ATTACHMENTS_DIR=${ATTACHMENTS_DIR:-/var/www/MISP/app/files} 52 | 53 | export AUTOCONF_GPG=${AUTOCONF_GPG:-true} 54 | export AUTOCONF_ADMIN_KEY=${AUTOCONF_ADMIN_KEY:-true} 55 | export AUTOGEN_ADMIN_KEY=${AUTOGEN_ADMIN_KEY:-$AUTOCONF_ADMIN_KEY} 56 | export OIDC_ENABLE=${OIDC_ENABLE:-false} 57 | export OIDC_MIXEDAUTH=${OIDC_MIXEDAUTH:-false} 58 | export LDAP_ENABLE=${LDAP_ENABLE:-false} 59 | export ENABLE_DB_SETTINGS=${ENABLE_DB_SETTINGS:-false} 60 | export ENABLE_BACKGROUND_UPDATES=${ENABLE_BACKGROUND_UPDATES:-false} 61 | export PROXY_ENABLE=${PROXY_ENABLE:-false} 62 | export DEBUG=${DEBUG:-0} 63 | 64 | export FASTCGI_READ_TIMEOUT=${FASTCGI_READ_TIMEOUT:-300s} 65 | export FASTCGI_SEND_TIMEOUT=${FASTCGI_SEND_TIMEOUT:-300s} 66 | export FASTCGI_CONNECT_TIMEOUT=${FASTCGI_CONNECT_TIMEOUT:-300s} 67 | 68 | export PHP_FCGI_CHILDREN=${PHP_FCGI_CHILDREN:-5} 69 | export PHP_FCGI_START_SERVERS=${PHP_FCGI_START_SERVERS:-2} 70 | export PHP_FCGI_SPARE_SERVERS=${PHP_FCGI_SPARE_SERVERS:-1} 71 | export PHP_FCGI_MAX_REQUESTS=${PHP_FCGI_MAX_REQUESTS:-0} 72 | 73 | export PHP_MEMORY_LIMIT=${PHP_MEMORY_LIMIT:-2048M} 74 | export PHP_MAX_EXECUTION_TIME=${PHP_MAX_EXECUTION_TIME:-300} 75 | export PHP_UPLOAD_MAX_FILESIZE=${PHP_UPLOAD_MAX_FILESIZE:-50M} 76 | export PHP_POST_MAX_SIZE=${PHP_POST_MAX_SIZE:-50M} 77 | export PHP_MAX_INPUT_TIME=${PHP_MAX_INPUT_TIME:-300} 78 | export PHP_MAX_FILE_UPLOADS=${PHP_MAX_FILE_UPLOADS:-50} 79 | 80 | export PHP_SESSION_TIMEOUT=${PHP_SESSION_TIMEOUT:-60} 81 | export PHP_SESSION_COOKIE_TIMEOUT=${PHP_SESSION_COOKIE_TIMEOUT:-10080} 82 | export PHP_SESSION_DEFAULTS=${PHP_SESSION_DEFAULTS:-php} 83 | export PHP_SESSION_AUTO_REGENERATE=${PHP_SESSION_AUTO_REGENERATE:-false} 84 | export PHP_SESSION_CHECK_AGENT=${PHP_SESSION_CHECK_AGENT:-false} 85 | export PHP_SESSION_COOKIE_SECURE=${PHP_SESSION_COOKIE_SECURE:-true} 86 | export PHP_SESSION_COOKIE_DOMAIN=${PHP_SESSION_COOKIE_DOMAIN} 87 | export PHP_SESSION_COOKIE_SAMESITE=${PHP_SESSION_COOKIE_SAMESITE:-Lax} 88 | export PHP_TIMEZONE=${PHP_TIMEZONE:-UTC} 89 | 90 | export NGINX_X_FORWARDED_FOR=${NGINX_X_FORWARDED_FOR:-false} 91 | export NGINX_SET_REAL_IP_FROM=${NGINX_SET_REAL_IP_FROM} 92 | export NGINX_CLIENT_MAX_BODY_SIZE=${NGINX_CLIENT_MAX_BODY_SIZE:-50M} 93 | 94 | export SUPERVISOR_HOST=${SUPERVISOR_HOST:-127.0.0.1} 95 | export SUPERVISOR_USERNAME=${SUPERVISOR_USERNAME:-supervisor} 96 | export SUPERVISOR_PASSWORD=${SUPERVISOR_PASSWORD:-supervisor} 97 | 98 | # Hinders further execution when sourced from other scripts 99 | if [[ "${BASH_SOURCE[0]}" != "$0" ]]; then 100 | return 101 | fi 102 | 103 | # start supervisord using the main configuration file so we have a socket interface 104 | exec /usr/bin/tini -- /usr/local/bin/supervisord -c /etc/supervisor/supervisord.conf 105 | -------------------------------------------------------------------------------- /core/files/entrypoint_fpm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | term_proc() { 4 | echo "Entrypoint FPM caught SIGTERM signal!" 5 | echo "Killing process $master_pid" 6 | kill -TERM "$master_pid" 2>/dev/null 7 | } 8 | 9 | trap term_proc SIGTERM 10 | 11 | redirect_logs() { 12 | tail -F /var/www/MISP/app/tmp/logs/error.log > /dev/stdout 2>/dev/null & 13 | } 14 | 15 | change_php_vars() { 16 | ESCAPED=$(printf '%s\n' "$REDIS_PASSWORD" | sed -e 's/[\/&]/\\&/g') 17 | for FILE in /etc/php/*/fpm/php.ini 18 | do 19 | [[ -e $FILE ]] || break 20 | echo "Configure PHP | Setting 'memory_limit = ${PHP_MEMORY_LIMIT}'" 21 | sed -i "s/memory_limit = .*/memory_limit = ${PHP_MEMORY_LIMIT}/" "$FILE" 22 | echo "Configure PHP | Setting 'max_execution_time = ${PHP_MAX_EXECUTION_TIME}'" 23 | sed -i "s/max_execution_time = .*/max_execution_time = ${PHP_MAX_EXECUTION_TIME}/" "$FILE" 24 | echo "Configure PHP | Setting 'upload_max_filesize = ${PHP_UPLOAD_MAX_FILESIZE}'" 25 | sed -i "s/upload_max_filesize = .*/upload_max_filesize = ${PHP_UPLOAD_MAX_FILESIZE}/" "$FILE" 26 | echo "Configure PHP | Setting 'max_file_uploads = ${PHP_MAX_FILE_UPLOADS}'" 27 | sed -i "s/max_file_uploads = .*/max_file_uploads = ${PHP_MAX_FILE_UPLOADS}/" "$FILE" 28 | echo "Configure PHP | Setting 'post_max_size = ${PHP_POST_MAX_SIZE}'" 29 | sed -i "s/post_max_size = .*/post_max_size = ${PHP_POST_MAX_SIZE}/" "$FILE" 30 | echo "Configure PHP | Setting 'max_input_time = ${PHP_MAX_INPUT_TIME}'" 31 | sed -i "s/max_input_time = .*/max_input_time = ${PHP_MAX_INPUT_TIME}/" "$FILE" 32 | sed -i "s/session.save_handler = .*/session.save_handler = redis/" "$FILE" 33 | if [[ "$ENABLE_REDIS_EMPTY_PASSWORD" = "true" ]]; then 34 | echo "Configure PHP | Setting 'session.save_path = '$(echo $REDIS_HOST | grep -E '^\w+://' || echo tcp://$REDIS_HOST):$REDIS_PORT' (passwordless)" 35 | sed -i "s|.*session.save_path = .*|session.save_path = '$(echo $REDIS_HOST | grep -E '^\w+://' || echo tcp://$REDIS_HOST):$REDIS_PORT'|" "$FILE" 36 | elif [[ -n "$REDIS_PASSWORD" ]]; then 37 | if [ "$DISABLE_PRINTING_PLAINTEXT_CREDENTIALS" == "true" ]; then 38 | echo "Configure PHP | Setting 'session.save_path = '$(echo $REDIS_HOST | grep -E '^\w+://' || echo tcp://$REDIS_HOST):$REDIS_PORT?auth='" 39 | else 40 | echo "Configure PHP | Setting 'session.save_path = '$(echo $REDIS_HOST | grep -E '^\w+://' || echo tcp://$REDIS_HOST):$REDIS_PORT?auth=${ESCAPED}'" 41 | fi 42 | sed -i "s|.*session.save_path = .*|session.save_path = '$(echo $REDIS_HOST | grep -E '^\w+://' || echo tcp://$REDIS_HOST):$REDIS_PORT?auth=${ESCAPED}'|" "$FILE" 43 | else 44 | echo "ERROR: REDIS_PASSWORD is not set but ENABLE_REDIS_EMPTY_PASSWORD is false. Please set REDIS_PASSWORD or enable ENABLE_REDIS_EMPTY_PASSWORD=true for passwordless Redis." 45 | exit 1 46 | fi 47 | sed -i "s/session.sid_length = .*/session.sid_length = 64/" "$FILE" 48 | sed -i "s/session.use_strict_mode = .*/session.use_strict_mode = 1/" "$FILE" 49 | echo "Configure PHP | Setting 'date.timezone = ${PHP_TIMEZONE}'" 50 | sed -i "s/;?date.timezone = .*/date.timezone = ${PHP_TIMEZONE}/" "$FILE" 51 | done 52 | 53 | for FILE in /etc/php/*/fpm/pool.d/www.conf 54 | do 55 | [[ -e $FILE ]] || break 56 | echo "Configure PHP | Setting 'pm.max_children = ${PHP_FCGI_CHILDREN}'" 57 | sed -i -E "s/;?pm.max_children = .*/pm.max_children = ${PHP_FCGI_CHILDREN}/" "$FILE" 58 | echo "Configure PHP | Setting 'pm.start_servers = ${PHP_FCGI_START_SERVERS}'" 59 | sed -i -E "s/;?pm.start_servers = .*/pm.start_servers = ${PHP_FCGI_START_SERVERS}/" "$FILE" 60 | echo "Configure PHP | Setting 'pm.(min|max)_spare_servers = ${PHP_FCGI_START_SERVERS}'" 61 | sed -i -E "s/;?pm.min_spare_servers = .*/pm.min_spare_servers = ${PHP_FCGI_SPARE_SERVERS}/" "$FILE" 62 | if [[ "$PHP_FCGI_START_SERVERS" -gt "$PHP_FCGI_SPARE_SERVERS" ]]; then 63 | sed -i -E "s/;?pm.max_spare_servers = .*/pm.max_spare_servers = ${PHP_FCGI_START_SERVERS}/" "$FILE" 64 | else 65 | sed -i -E "s/;?pm.max_spare_servers = .*/pm.max_spare_servers = ${PHP_FCGI_SPARE_SERVERS}/" "$FILE" 66 | fi 67 | echo "Configure PHP | Setting 'pm.max_requests = ${PHP_FCGI_MAX_REQUESTS}'" 68 | sed -i -E "s/;?pm.max_requests = .*/pm.max_requests = ${PHP_FCGI_MAX_REQUESTS}/" "$FILE" 69 | if [[ "$FASTCGI_STATUS_LISTEN" != "" ]]; then 70 | echo "Configure PHP | Setting 'pm.status_path = /status'" 71 | sed -i -E "s/;?pm.status_path = .*/pm.status_path = \/status/" "$FILE" 72 | echo "Configure PHP | Setting 'pm.status_path = /run/php/php-fpm-status.sock'" 73 | sed -i -E "s/;?pm.status_listen = .*/pm.status_listen = \/run\/php\/php-fpm-status.sock/" "$FILE" 74 | else 75 | echo "Configure PHP | Disabling 'pm.status_path'" 76 | sed -i -E "s/^pm.status_path = /;pm.status_path = /" "$FILE" 77 | echo "Configure PHP | Disabling 'pm.status_listen'" 78 | sed -i -E "s/^pm.status_listen =/;pm.status_listen =/" "$FILE" 79 | fi 80 | if [[ -n "$PHP_LISTEN_FPM" ]]; then 81 | echo "Configure PHP | Setting 'listen' to [::]:9002" 82 | sed -i "/^listen =/s@=.*@= [::]:9002@" "$FILE" 83 | fi 84 | 85 | done 86 | } 87 | 88 | # Hinders further execution when sourced from other scripts 89 | if [[ "${BASH_SOURCE[0]}" != "$0" ]]; then 90 | return 91 | fi 92 | 93 | echo "Configure PHP | Change PHP values ..." && change_php_vars 94 | 95 | echo "Configure PHP | Starting PHP FPM" 96 | /usr/sbin/php-fpm8.4 -R -F & master_pid=$! 97 | 98 | # Wait for it 99 | wait "$master_pid" 100 | -------------------------------------------------------------------------------- /docker-bake.hcl: -------------------------------------------------------------------------------- 1 | variable "PLATFORMS" { 2 | default = ["linux/amd64", "linux/arm64"] 3 | } 4 | 5 | variable "DOCKER_HUB_PROXY" { 6 | default = "" 7 | } 8 | 9 | variable "PYPI_REDIS_VERSION" { 10 | default = "" 11 | } 12 | 13 | variable "PYPI_LIEF_VERSION" { 14 | default = "" 15 | } 16 | 17 | variable "PYPI_PYDEEP2_VERSION" { 18 | default = "" 19 | } 20 | 21 | variable "PYPI_PYTHON_MAGIC_VERSION" { 22 | default = "" 23 | } 24 | 25 | variable "PYPI_MISP_LIB_STIX2_VERSION" { 26 | default = "" 27 | } 28 | 29 | variable "PYPI_MAEC_VERSION" { 30 | default = "" 31 | } 32 | 33 | variable "PYPI_MIXBOX_VERSION" { 34 | default = "" 35 | } 36 | 37 | variable "PYPI_CYBOX_VERSION" { 38 | default = "" 39 | } 40 | 41 | variable "PYPI_PYMISP_VERSION" { 42 | default = "" 43 | } 44 | 45 | variable "PYPI_MISP_STIX_VERSION" { 46 | default = "" 47 | } 48 | 49 | variable "PYPI_TAXII2_CLIENT" { 50 | default = "" 51 | } 52 | 53 | variable "PYPI_SETUPTOOLS_VERSION" { 54 | default = "" 55 | } 56 | 57 | variable "PYPI_SUPERVISOR_VERSION" { 58 | default = "" 59 | } 60 | 61 | variable "NAMESPACE" { 62 | default = null 63 | } 64 | 65 | variable "COMMIT_HASH" { 66 | default = null 67 | } 68 | 69 | variable "MODULES_TAG" { 70 | default = "" 71 | } 72 | 73 | variable "MODULES_COMMIT" { 74 | default = "" 75 | } 76 | 77 | variable "CORE_TAG" { 78 | default = "" 79 | } 80 | 81 | variable "CORE_COMMIT" { 82 | default = "" 83 | } 84 | 85 | variable "GUARD_TAG" { 86 | default = "" 87 | } 88 | 89 | variable "GUARD_COMMIT" { 90 | default = "" 91 | } 92 | 93 | variable "PHP_VER" { 94 | default = null 95 | } 96 | 97 | group "default" { 98 | targets = [ 99 | "misp-modules", 100 | "misp-modules-slim", 101 | "misp-core", 102 | "misp-core-slim", 103 | "misp-guard", 104 | ] 105 | } 106 | 107 | group "slim" { 108 | targets = [ 109 | "misp-modules-slim", 110 | "misp-core-slim", 111 | "misp-guard", 112 | ] 113 | } 114 | group "standard" { 115 | targets = [ 116 | "misp-modules", 117 | "misp-core", 118 | "misp-guard", 119 | ] 120 | } 121 | 122 | target "misp-modules" { 123 | context = "modules/." 124 | dockerfile = "Dockerfile" 125 | tags = flatten(["${NAMESPACE}/misp-modules:latest", "${NAMESPACE}/misp-modules:${COMMIT_HASH}", MODULES_TAG != "" ? ["${NAMESPACE}/misp-modules:${MODULES_TAG}"] : []]) 126 | args = { 127 | "MODULES_TAG": "${MODULES_TAG}", 128 | "MODULES_COMMIT": "${MODULES_COMMIT}", 129 | "MODULES_FLAVOR": "standard", 130 | "DOCKER_HUB_PROXY" : "${DOCKER_HUB_PROXY}", 131 | } 132 | platforms = "${PLATFORMS}" 133 | } 134 | 135 | target "misp-modules-slim" { 136 | inherits = [ "misp-modules" ] 137 | tags = flatten(["${NAMESPACE}/misp-modules:latest-slim", "${NAMESPACE}/misp-modules:${COMMIT_HASH}-slim", MODULES_TAG != "" ? ["${NAMESPACE}/misp-modules:${MODULES_TAG}-slim"] : []]) 138 | args = { 139 | "MODULES_TAG": "${MODULES_TAG}", 140 | "MODULES_COMMIT": "${MODULES_COMMIT}", 141 | "MODULES_FLAVOR": "slim", 142 | "DOCKER_HUB_PROXY" : "${DOCKER_HUB_PROXY}", 143 | } 144 | platforms = "${PLATFORMS}" 145 | } 146 | 147 | target "misp-core" { 148 | context = "core/." 149 | dockerfile = "Dockerfile" 150 | tags = flatten(["${NAMESPACE}/misp-core:latest", "${NAMESPACE}/misp-core:${COMMIT_HASH}", CORE_TAG != "" ? ["${NAMESPACE}/misp-core:${CORE_TAG}"] : []]) 151 | args = { 152 | "CORE_TAG": "${CORE_TAG}", 153 | "CORE_COMMIT": "${CORE_COMMIT}", 154 | "CORE_FLAVOR": "standard", 155 | "PHP_VER": "${PHP_VER}", 156 | "PYPI_REDIS_VERSION": "${PYPI_REDIS_VERSION}", 157 | "PYPI_LIEF_VERSION": "${PYPI_LIEF_VERSION}", 158 | "PYPI_PYDEEP2_VERSION": "${PYPI_PYDEEP2_VERSION}", 159 | "PYPI_PYTHON_MAGIC_VERSION": "${PYPI_PYTHON_MAGIC_VERSION}", 160 | "PYPI_MISP_LIB_STIX2_VERSION": "${PYPI_MISP_LIB_STIX2_VERSION}", 161 | "PYPI_MAEC_VERSION": "${PYPI_MAEC_VERSION}", 162 | "PYPI_MIXBOX_VERSION": "${PYPI_MIXBOX_VERSION}", 163 | "PYPI_CYBOX_VERSION": "${PYPI_CYBOX_VERSION}", 164 | "PYPI_PYMISP_VERSION": "${PYPI_PYMISP_VERSION}", 165 | "PYPI_MISP_STIX_VERSION": "${PYPI_MISP_STIX_VERSION}", 166 | "PYPI_TAXII2_CLIENT": "${PYPI_TAXII2_CLIENT}", 167 | "PYPI_SETUPTOOLS_VERSION": "${PYPI_SETUPTOOLS_VERSION}", 168 | "PYPI_SUPERVISOR_VERSION": "${PYPI_SUPERVISOR_VERSION}", 169 | "DOCKER_HUB_PROXY" : "${DOCKER_HUB_PROXY}", 170 | } 171 | platforms = "${PLATFORMS}" 172 | } 173 | 174 | target "misp-core-slim" { 175 | inherits = [ "misp-core" ] 176 | tags = flatten(["${NAMESPACE}/misp-core:latest-slim", "${NAMESPACE}/misp-core:${COMMIT_HASH}-slim", CORE_TAG != "" ? ["${NAMESPACE}/misp-core:${CORE_TAG}-slim"] : []]) 177 | args = { 178 | "CORE_TAG": "${CORE_TAG}", 179 | "CORE_COMMIT": "${CORE_COMMIT}", 180 | "CORE_FLAVOR": "slim", 181 | "PHP_VER": "${PHP_VER}", 182 | "PYPI_REDIS_VERSION": "${PYPI_REDIS_VERSION}", 183 | "PYPI_LIEF_VERSION": "${PYPI_LIEF_VERSION}", 184 | "PYPI_PYDEEP2_VERSION": "${PYPI_PYDEEP2_VERSION}", 185 | "PYPI_PYTHON_MAGIC_VERSION": "${PYPI_PYTHON_MAGIC_VERSION}", 186 | "PYPI_MISP_LIB_STIX2_VERSION": "${PYPI_MISP_LIB_STIX2_VERSION}", 187 | "PYPI_MAEC_VERSION": "${PYPI_MAEC_VERSION}", 188 | "PYPI_MIXBOX_VERSION": "${PYPI_MIXBOX_VERSION}", 189 | "PYPI_CYBOX_VERSION": "${PYPI_CYBOX_VERSION}", 190 | "PYPI_PYMISP_VERSION": "${PYPI_PYMISP_VERSION}", 191 | "PYPI_MISP_STIX_VERSION": "${PYPI_MISP_STIX_VERSION}", 192 | "PYPI_TAXII2_CLIENT": "${PYPI_TAXII2_CLIENT}", 193 | "PYPI_SETUPTOOLS_VERSION": "${PYPI_SETUPTOOLS_VERSION}", 194 | "PYPI_SUPERVISOR_VERSION": "${PYPI_SUPERVISOR_VERSION}", 195 | "DOCKER_HUB_PROXY" : "${DOCKER_HUB_PROXY}", 196 | } 197 | platforms = "${PLATFORMS}" 198 | } 199 | 200 | target "misp-guard" { 201 | context = "guard/." 202 | dockerfile = "Dockerfile" 203 | tags = flatten(["${NAMESPACE}/misp-guard:latest", "${NAMESPACE}/misp-guard:${COMMIT_HASH}", GUARD_TAG != "" ? ["${NAMESPACE}/misp-guard:${GUARD_TAG}"] : []]) 204 | args = { 205 | "GUARD_TAG": "${GUARD_TAG}", 206 | "GUARD_COMMIT": "${GUARD_COMMIT}" 207 | "DOCKER_HUB_PROXY" : "${DOCKER_HUB_PROXY}", 208 | } 209 | platforms = "${PLATFORMS}" 210 | } 211 | -------------------------------------------------------------------------------- /docs/keycloak-integration-guide.md: -------------------------------------------------------------------------------- 1 | # MISP Keycloak 26.1.x Basic Integration Guide 2 | 3 | This guide provides detailed instructions for integrating MISP with Keycloak using OpenID Connect (OIDC). It assumes that Keycloak is already installed and configured with a realm. Default settings are used unless otherwise specified. 4 | 5 | On unauthenticated access to the MISP console, users will be redirected by nginx to KeyCloak for authentication. If the user does not already exist in the MISP console, it will be created automatically under the organisation defined in the environment variables. 6 | 7 | ## Client 8 | 9 | ### Create Client 10 | 11 | This section outlines how to create and configure a Keycloak client for MISP. The client represents MISP in the Keycloak realm and facilitates authentication and authorization. 12 | 13 | **Steps** 14 | > Navigate to: `Manage -> Clients -> Create client` 15 | 16 | 1. **General Settings** 17 | - **Client Type**: OpenID Connect 18 | - **Client ID**: `misp` 19 | 20 | 2. **Capability Config** 21 | - **Client authentication**: `On` 22 | - **Authorization**: `On` 23 | - **Authentication Flow**: Enable `Standard flow`, `Direct access grants`, and `Service accounts roles` 24 | 25 | 3. **Login Settings** 26 | - **Root URL**: `` (e.g., `https://misp.domain.tld/`) 27 | - **Home URL**: Same as Root URL 28 | - **Valid Redirect URL**: `` (e.g., `https://misp.domain.tld/*`) 29 | - **Web Origins**: `` (e.g., `misp.domain.tld`) 30 | 31 | ### Define Roles 32 | 33 | Define roles in Keycloak that correspond to MISP roles. These roles will be used to control access levels within MISP. 34 | 35 | **Steps** 36 | 37 | > Navigate to: `Manage -> Clients -> misp -> Roles -> Create Role` 38 | 1. **Role name**: e.g., `admin` 39 | 40 | > Repeat this for every MISP role you want to assign through Keycloak (e.g., `admin`, `OrgAdmin`, `User`, `Read Only`, etc.) 41 | 42 | ### Assign Roles to Users 43 | 44 | Assign the defined roles to specific users to control their access in MISP. 45 | 46 | **Steps** 47 | 48 | > Navigate to: `Manage -> Users -> -> Role mapping -> Assign Role` 49 | 50 | 1. Ensure “Filter by clients” is visible in the top left corner 51 | 2. Select the appropriate MISP role (e.g., `misp admin`) 52 | 3. Click `Assign` 53 | 54 | > Alternatively, access can be assigned to groups using the same process. 55 | 56 | ## Client Scope 57 | 58 | Client scopes define what information is included in the tokens issued to the client. This section sets up a custom client scope for MISP. 59 | 60 | **Steps** 61 | 62 | 1. **Create Client Scope** 63 | > Navigate to: `Manage -> Realm roles -> Create Client Scope` 64 | - **Name**: `misp-oidc` 65 | - **Type**: Optional 66 | - **Protocol**: OpenID Connect 67 | - **Include in token scope**: Off 68 | 69 | 2. **Configure Predefined Client Scope Mappers** 70 | > Navigate to: `Manage -> Client Scopes -> misp-oidc -> Mappers -> Add Mapper -> From Predefined Mapper` 71 | - Select: `email`, `username` 72 | 73 | 3. **Configure Roles Client Mapper** 74 | > Navigate to: `Manage -> Client Scopes -> misp-oidc -> Mappers -> Add Mapper -> By Configuration` 75 | - **Name**: `roles` 76 | - **Mapper Type**: `User Client Role` 77 | - **Client ID**: Select `misp` from dropdown 78 | - **Client Role Prefix**: Leave blank 79 | - **Multivalued**: On 80 | - **Token Claim Name**: `roles` 81 | - **Claim JSON Type**: String 82 | - **Add to ID token**: On 83 | - **Add to access token**: On 84 | - **Add to lightweight access token**: Off 85 | - **Add to userinfo**: On 86 | - **Add to token introspection**: On 87 | 88 | 4. **Add Scope to Client** 89 | > Navigate to: `Manage -> Clients -> misp -> Client Scopes -> Add Client Scope` 90 | - Select `misp-oidc` -> Click `Add` -> Set as `Default` 91 | 92 | --- 93 | 94 | ## MISP Environment Variable Configuration 95 | 96 | Configure MISP to use Keycloak as an OIDC provider by setting the appropriate environment variables. These variables must all be set to trigger configure_misp.sh to enable OIDC during setup. 97 | 98 | ### Required Variables 99 | 100 | - `OIDC_ENABLE`: `"true"` — Enables OIDC authentication in MISP. 101 | - `OIDC_CLIENT_ID`: `"misp"` — The client ID configured in Keycloak. 102 | - `OIDC_DEFAULT_ORG`: `"[Org Name for default assignment]"` — The default organization name in MISP to associate with authenticated users. 103 | - `OIDC_PROVIDER_URL`: `"https://[key.cloak.host]/realms/[realm-name]/.well-known/openid-configuration"` — The discovery URL for the Keycloak realm. 104 | - `OIDC_ROLES_MAPPING`: `'{"admin":"1","user":"3"}'` — JSON mapping from Keycloak roles to MISP role IDs. 105 | - `OIDC_ROLES_PROPERTY`: `"realm_access.roles"` — The claim path in the token where roles are listed. 106 | - `OIDC_CLIENT_SECRET`: `` — The client secret from Keycloak (`Manage -> Clients -> misp -> Credentials -> Client Secret`) 107 | Example: 108 | ``` 109 | OIDC_ENABLE: "true" 110 | OIDC_CLIENT_ID: "misp" 111 | OIDC_DEFAULT_ORG: "ADMIN" 112 | OIDC_PROVIDER_URL: "https://auth.domain.com/realms/REALMNAME/.well-known/openid-configuration" 113 | OIDC_ROLES_MAPPING: '{"admin":"1","user":"3"}' 114 | OIDC_ROLES_PROPERTY: "roles" 115 | ``` 116 | 117 | --- 118 | 119 | ## Troubleshooting 120 | 121 | >Authentication errors are logged to the MISP error log `app/tmp/logs/error.log` 122 | 123 | **Warning: OIDC user 'user@example.com' - Role Property 'roles' is missing in claims, access prohibitied** 124 | 125 | This warning indicates an issue with the Client Scope Roles Mapper not correctly including the role in the token. Review the Client Scope configurations at `Manage -> Client Scopes -> misp-oidc -> Mappers` and `Manage -> Clients -> misp -> Client Scopes` 126 | 127 | **Warning: OIDC user 'user@example.com' - No role was assigned, access prohibited** 128 | 129 | The user authenticated in keycloak successfully, but they were not a member of any roles mapped to MISP. Ensure the user is a member of one of the client roles `Manage -> Clients -> misp -> Roles` and that the `OIDC_ROLES_MAPPING` variable correctly matches that role name (case sensitive) to a MISP role 130 | 131 | **Error: OIDC user 'user@example.com" - User sub doesn't match, could not login** 132 | 133 | The sub is a property mapping the username to the user's keycloak ID. This ID gets saved to the user in MISP. This most common causes of this error are the user was deleted and recreated in keycloak or if settings in keycloak were manually recreated. To resolve, delete the user from MISP and allow it to be recreated on next successful login. -------------------------------------------------------------------------------- /core/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG DOCKER_HUB_PROXY="" 2 | 3 | 4 | FROM "${DOCKER_HUB_PROXY}python:3.12-slim-trixie" AS php-base 5 | ENV DEBIAN_FRONTEND=noninteractive 6 | ARG CORE_TAG 7 | ARG CORE_COMMIT 8 | ARG PHP_VER 9 | ARG CORE_FLAVOR 10 | 11 | # Uncomment when building in corporate environments 12 | # COPY ./cert.pem /usr/local/share/ca-certificates/rootca.pem 13 | # COPY ./cert.pem /usr/lib/ssl/cert.pem 14 | 15 | RUN <<-EOF 16 | apt-get update 17 | if [ "${CORE_FLAVOR}" = "slim" ]; then 18 | apt-get install -y --no-install-recommends \ 19 | lsb-release \ 20 | ca-certificates \ 21 | tini \ 22 | gettext \ 23 | procps \ 24 | sudo \ 25 | nginx \ 26 | openssl \ 27 | gpg \ 28 | gpg-agent \ 29 | mariadb-client \ 30 | rsync \ 31 | rsyslog \ 32 | php8.4 \ 33 | php8.4-curl \ 34 | php8.4-xml \ 35 | php8.4-mbstring \ 36 | php8.4-mysql \ 37 | php8.4-redis \ 38 | php8.4-gd \ 39 | php8.4-fpm \ 40 | php8.4-zip \ 41 | unzip \ 42 | zip \ 43 | curl \ 44 | uuid-runtime \ 45 | jq 46 | else 47 | apt-get install -y --no-install-recommends \ 48 | lsb-release \ 49 | ca-certificates \ 50 | tini \ 51 | gettext \ 52 | procps \ 53 | sudo \ 54 | nginx \ 55 | openssl \ 56 | gpg \ 57 | gpg-agent \ 58 | mariadb-client \ 59 | rsync \ 60 | rsyslog \ 61 | php8.4 \ 62 | php8.4-apcu \ 63 | php8.4-curl \ 64 | php8.4-xml \ 65 | php8.4-intl \ 66 | php8.4-bcmath \ 67 | php8.4-mbstring \ 68 | php8.4-mysql \ 69 | php8.4-redis \ 70 | php8.4-gd \ 71 | php8.4-fpm \ 72 | php8.4-zip \ 73 | php8.4-ldap \ 74 | libmagic1 \ 75 | libldap-common \ 76 | librdkafka1 \ 77 | libbrotli1 \ 78 | libsimdjson25 \ 79 | libzstd1 \ 80 | ssdeep \ 81 | libfuzzy2 \ 82 | unzip \ 83 | zip \ 84 | curl \ 85 | uuid-runtime \ 86 | jq 87 | fi 88 | EOF 89 | 90 | FROM php-base AS composer-build 91 | ENV COMPOSER_ALLOW_SUPERUSER=1 92 | 93 | WORKDIR /tmp 94 | RUN curl -o /tmp/composer.json https://raw.githubusercontent.com/MISP/MISP/${CORE_COMMIT:-${CORE_TAG}}/app/composer.json 95 | RUN sed -i '/cake-resque/d' /tmp/composer.json 96 | RUN sed -i 's/authentication",/authentication"/' /tmp/composer.json 97 | 98 | COPY --from=composer:latest /usr/bin/composer /usr/bin/composer 99 | RUN composer config --no-interaction allow-plugins.composer/installers true 100 | RUN composer install 101 | RUN <<-EOF 102 | if [ "${CORE_FLAVOR}" = "slim" ]; then 103 | : 104 | else 105 | composer require --with-all-dependencies --no-interaction \ 106 | elasticsearch/elasticsearch:^8.7.0 \ 107 | jakub-onderka/openid-connect-php:^1.0.0 \ 108 | aws/aws-sdk-php 109 | fi 110 | EOF 111 | 112 | 113 | FROM php-base AS php-build 114 | ENV TZ=Etc/UTC 115 | 116 | RUN <<-EOF 117 | if [ "${CORE_FLAVOR}" = "slim" ]; then 118 | apt-get install -y --no-install-recommends \ 119 | gcc \ 120 | g++ \ 121 | git \ 122 | make \ 123 | php8.4-dev \ 124 | php-pear 125 | else 126 | apt-get install -y --no-install-recommends \ 127 | gcc \ 128 | g++ \ 129 | git \ 130 | make \ 131 | php8.4-dev \ 132 | php-pear \ 133 | libbrotli-dev \ 134 | libfuzzy-dev \ 135 | librdkafka-dev \ 136 | libsimdjson-dev \ 137 | libzstd-dev 138 | fi 139 | apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/lists/* 140 | EOF 141 | 142 | RUN update-alternatives --set php /usr/bin/php8.4 143 | RUN update-alternatives --set php-config /usr/bin/php-config8.4 144 | RUN update-alternatives --set phpize /usr/bin/phpize8.4 145 | 146 | RUN <<-EOF 147 | pecl channel-update pecl.php.net 148 | if [ "${CORE_FLAVOR}" = "slim" ]; then 149 | tar -czf /pecl_libs.tar.gz -T /dev/null 150 | else 151 | cp "/usr/lib/$(gcc -dumpmachine)"/libfuzzy.* /usr/lib 152 | pecl install rdkafka 153 | pecl install simdjson 154 | pecl install zstd 155 | pecl install brotli 156 | git clone --recursive --depth=1 https://github.com/JakubOnderka/pecl-text-ssdeep.git /tmp/pecl-text-ssdeep 157 | cd /tmp/pecl-text-ssdeep && phpize && ./configure && make && make install 158 | tar -czf /pecl_libs.tar.gz \ 159 | /usr/lib/php/${PHP_VER}/ssdeep.so \ 160 | /usr/lib/php/${PHP_VER}/rdkafka.so \ 161 | /usr/lib/php/${PHP_VER}/brotli.so \ 162 | /usr/lib/php/${PHP_VER}/simdjson.so \ 163 | /usr/lib/php/${PHP_VER}/zstd.so 164 | fi 165 | EOF 166 | 167 | 168 | FROM php-base AS python-build 169 | ARG PYPI_REDIS_VERSION 170 | ARG PYPI_LIEF_VERSION 171 | ARG PYPI_PYDEEP2_VERSION 172 | ARG PYPI_PYTHON_MAGIC_VERSION 173 | ARG PYPI_MISP_LIB_STIX2_VERSION 174 | ARG PYPI_MAEC_VERSION 175 | ARG PYPI_MIXBOX_VERSION 176 | ARG PYPI_CYBOX_VERSION 177 | ARG PYPI_PYMISP_VERSION 178 | ARG PYPI_MISP_STIX_VERSION 179 | ARG PYPI_TAXII2_CLIENT 180 | ARG PYPI_SETUPTOOLS_VERSION 181 | ARG PYPI_SUPERVISOR_VERSION 182 | 183 | RUN apt-get install -y --no-install-recommends \ 184 | git \ 185 | && apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/lists/* 186 | 187 | # Download MISP using git in the /var/www/ directory. Remove unnecessary items. 188 | RUN <<-EOF 189 | if [ ! -z "${CORE_COMMIT}" ]; then 190 | git clone https://github.com/MISP/MISP.git /var/www/MISP && cd /var/www/MISP && git checkout "${CORE_COMMIT}" 191 | else 192 | git clone --branch "${CORE_TAG}" --depth 1 https://github.com/MISP/MISP.git /var/www/MISP 193 | fi 194 | cd /var/www/MISP || exit; git submodule update --init --recursive . 195 | EOF 196 | 197 | RUN <<-EOF 198 | mkdir /wheels 199 | 200 | # Add additional dependencies (container specific) 201 | # The "set" line contains the list of modules we want to ensure are present. 202 | # PYPI_MODULE_NAME_VERSION env vars can be set to specify the version desired, 203 | # e.g. PYPI_SURICATA_VERSION="==2.0" to specify exactly version 2.0 for the suricata package 204 | # 205 | # 1. Check for presence of each module in requirements.txt 206 | # 2. If missing, add it (with optional version from env (defaults to empty string)) 207 | # 3. If present, replace with our specified version if it exists, otherwise leave 208 | # the upstream version alone. 209 | set -- "redis" "lief" "pydeep2" "python-magic" "misp-lib-stix2" "maec" "mixbox" "cybox" "pymisp" "misp-stix" "taxii2-client" "setuptools" "supervisor" 210 | for mod in "$@"; do 211 | mod_version_var=$(echo "PYPI_${mod}_VERSION" | tr '[:lower:]' '[:upper:]' | tr '-' '_') 212 | mod_version=$(eval "echo \"\$$mod_version_var\"") 213 | grep -q ${mod} /var/www/MISP/requirements.txt 214 | exists=$? 215 | if [ "${exists}" -eq "1" ]; then 216 | echo "Adding missing module ${mod} with version '${mod_version}'" 217 | echo ${mod}${mod_version} >> /var/www/MISP/requirements.txt 218 | else 219 | if [ "$(echo ${mod_version} | wc -m)" -gt 1 ]; then 220 | echo "Overwriting existing module ${mod}, version '${mod_version}'" 221 | sed -i "/${mod}/s/.*/${mod}${mod_version}/" /var/www/MISP/requirements.txt 222 | else 223 | echo "Skipping overwriting ${mod} due to missing version variable" 224 | fi 225 | fi 226 | done; 227 | 228 | pip wheel --no-cache-dir -w /wheels/ -r /var/www/MISP/requirements.txt 229 | 230 | # Remove files we do not care for 231 | find /var/www/MISP/INSTALL/* ! -name 'MYSQL.sql' -type f -exec rm {} + 232 | find /var/www/MISP/INSTALL/* ! -name 'MYSQL.sql' -type l -exec rm {} + 233 | # Remove most files in .git - we do not use git functionality in docker 234 | find /var/www/MISP/.git/* ! -name HEAD -exec rm -rf {} + 235 | # Remove libraries submodules 236 | rm -r /var/www/MISP/PyMISP 237 | rm -r /var/www/MISP/app/files/scripts/cti-python-stix2 238 | rm -r /var/www/MISP/app/files/scripts/misp-stix 239 | rm -r /var/www/MISP/app/files/scripts/mixbox 240 | rm -r /var/www/MISP/app/files/scripts/python-cybox 241 | rm -r /var/www/MISP/app/files/scripts/python-maec 242 | rm -r /var/www/MISP/app/files/scripts/python-stix 243 | EOF 244 | 245 | 246 | FROM php-base 247 | 248 | # Remove some defaults (kernel logging) and fix alternatives 249 | RUN sed -i '/imklog/s/^/#/' /etc/rsyslog.conf 250 | RUN update-alternatives --set php /usr/bin/php8.4 251 | RUN apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/lists/* 252 | 253 | # Install python modules 254 | COPY --from=python-build /wheels /wheels 255 | RUN pip install --no-cache-dir /wheels/*.whl && rm -rf /wheels 256 | # PHP: install prebuilt libraries, then install the app's PHP deps 257 | COPY --from=php-build /pecl_libs.tar.gz / 258 | RUN tar -xzf /pecl_libs.tar.gz && rm /pecl_libs.tar.gz 259 | 260 | # Do an early chown to limit image size 261 | COPY --from=python-build --chown=www-data:www-data --chmod=0550 /var/www/MISP /var/www/MISP 262 | COPY --from=composer-build --chown=www-data:www-data --chmod=0550 /tmp/composer.lock /var/www/MISP/app/composer.lock 263 | COPY --from=composer-build --chown=www-data:www-data --chmod=0550 /tmp/Vendor /var/www/MISP/app/Vendor 264 | COPY --from=composer-build --chown=www-data:www-data --chmod=0550 /tmp/Plugin /var/www/MISP/app/Plugin 265 | 266 | # Gather these in one layer, only act on actual directories under /etc/php/ 267 | RUN <<-EOF 268 | if [ "${CORE_FLAVOR}" = "slim" ]; then 269 | set -- 270 | else 271 | set -- "ssdeep" "rdkafka" "brotli" "simdjson" "zstd" 272 | fi 273 | for mod in "$@"; do 274 | for dir in /etc/php/*/; do 275 | echo "extension=${mod}.so" > "${dir}mods-available/${mod}.ini" 276 | done; 277 | phpenmod "${mod}" 278 | done; 279 | phpenmod redis 280 | EOF 281 | 282 | # nginx 283 | RUN rm /etc/nginx/sites-enabled/*; mkdir -p /run/php /etc/nginx/certs 284 | 285 | # Make a copy of the file and configuration stores, so we can sync from it 286 | 287 | # The spirit of the upstream dockerization is to make: 288 | # 1) User and group aligned in terms of permissions 289 | # 2) Files executable and read only, because of some rogue scripts like 'cake' 290 | # 3) Directories writable, because sometimes MISP add new files 291 | 292 | RUN <<-EOF 293 | cp -R /var/www/MISP/app/files /var/www/MISP/app/files.dist 294 | echo "${CORE_COMMIT:-${CORE_TAG}}" > /var/www/MISP/app/files/VERSION 295 | cp -R /var/www/MISP/app/Config /var/www/MISP/app/Config.dist 296 | find /var/www/MISP \( ! -user www-data -or ! -group www-data \) -exec chown www-data:www-data '{}' +; 297 | find /var/www/MISP -not -perm 550 -type f -exec chmod 0550 '{}' +; 298 | find /var/www/MISP -not -perm 770 -type d -exec chmod 0770 '{}' +; 299 | # Diagnostics wants this file to be present and writable even if we do not use git in docker land 300 | touch /var/www/MISP/.git/ORIG_HEAD && chmod 0600 /var/www/MISP/.git/ORIG_HEAD && chown www-data:www-data /var/www/MISP/.git/ORIG_HEAD 301 | EOF 302 | 303 | # Copy all our image specific files to appropriate locations 304 | COPY files/ / 305 | ENTRYPOINT [ "/entrypoint.sh" ] 306 | 307 | # Change Workdirectory 308 | WORKDIR /var/www/MISP 309 | -------------------------------------------------------------------------------- /template.env: -------------------------------------------------------------------------------- 1 | ## 2 | # Build-time variables 3 | ## 4 | 5 | CORE_TAG=v2.5.30 6 | # CORE_FLAVOR=standard 7 | MODULES_TAG=v3.0.4 8 | # MODULES_FLAVOR=standard 9 | GUARD_TAG=v1.2 10 | PHP_VER=20240924 11 | 12 | # PYPI_* vars take precedence over MISP's 13 | # PYPI_REDIS_VERSION="==5.0.*" 14 | # PYPI_LIEF_VERSION=">=0.13.1" 15 | # PYPI_PYDEEP2_VERSION="==0.5.*" 16 | # PYPI_PYTHON_MAGIC_VERSION="==0.4.*" 17 | # PYPI_MISP_LIB_STIX2_VERSION="==3.0.*" 18 | # PYPI_MAEC_VERSION="==4.1.*" 19 | # PYPI_MIXBOX_VERSION="==1.0.*" 20 | # PYPI_CYBOX_VERSION="==2.1.*" 21 | # PYPI_PYMISP_VERSION="==2.5.9" 22 | # PYPI_MISP_STIX_VERSION="==2.4.194" 23 | # PYPI_TAXII2_CLIENT="==2.3.0" 24 | PYPI_SETUPTOOLS_VERSION="==80.3.1" 25 | PYPI_SUPERVISOR_VERSION="==4.2.5" 26 | 27 | # CORE_COMMIT takes precedence over CORE_TAG 28 | # CORE_COMMIT=0bba3f5 29 | # MODULES_COMMIT takes precedence over MODULES_TAG 30 | # MODULES_COMMIT=de69ae3 31 | # GUARD_COMMIT takes precedence over GUARD_TAG 32 | # GUARD_COMMIT=370b043 33 | 34 | ## 35 | # Run-time variables 36 | ## 37 | 38 | # CORE_RUNNING_TAG=latest 39 | # MODULES_RUNNING_TAG=latest 40 | # GUARD_RUNNING_TAG=latest 41 | 42 | # Email/username for user #1, defaults to MISP's default (admin@admin.test) 43 | ADMIN_EMAIL= 44 | # name of org #1, default to MISP's default (ORGNAME) 45 | ADMIN_ORG= 46 | # uuid of org #1, defaults to an automatically generated one 47 | ADMIN_ORG_UUID= 48 | # defaults to an automatically generated one 49 | ADMIN_KEY= 50 | # defaults to MISP's default (admin) 51 | ADMIN_PASSWORD= 52 | # Prevent MISP Initialization from writing ADMIN_KEY and ADMIN_PASSWORD in plaintext 53 | # Recommend uncommenting / setting to true in production or kubernetes environments where output is logged. 54 | #DISABLE_PRINTING_PLAINTEXT_CREDENTIALS=true 55 | # defaults to 'passphrase' 56 | GPG_PASSPHRASE= 57 | # defaults to 1 (the admin user) 58 | CRON_USER_ID= 59 | # defaults to 'https://localhost' 60 | # note: if you are exposing MISP on a non-standard port (i.e., the port is part of the URL you would use to access it, e.g., https://192.168.0.1:4433) you need to include the port in the BASE_URL variable 61 | BASE_URL= 62 | # defaults to 80 and 443, don't forget to update the base url if not the defaults one 63 | # CORE_HTTP_PORT= 64 | # CORE_HTTPS_PORT= 65 | # store settings in db except those that must stay in config.php. true/false, defaults to false 66 | ENABLE_DB_SETTINGS= 67 | # encryption key. defaults to empty string 68 | ENCRYPTION_KEY= 69 | # enable background updates. defaults to false 70 | ENABLE_BACKGROUND_UPDATES= 71 | # use a different attachments_dir. defaults to /var/www/MISP/app/files 72 | ATTACHMENTS_DIR= 73 | 74 | # By default, a daily synchronization is performed, but you can modify this by changing the push and pull frequency (in seconds). 75 | # CRON_PULLALL="86400" 76 | # CRON_PUSHALL="86400" 77 | 78 | # defines the FQDN of the mail sub-system (defaults to 'mail') 79 | # SMTP_FQDN= 80 | # override the port of the mail sub-system (defaults to 25) 81 | # SMTP_PORT= 82 | 83 | # optional and used by the mail sub-system 84 | SMARTHOST_ADDRESS= 85 | SMARTHOST_PORT= 86 | SMARTHOST_USER= 87 | SMARTHOST_PASSWORD= 88 | SMARTHOST_ALIASES= 89 | 90 | # optional AWS SES SMTP relay settings (use these instead of the generic smarthost) 91 | # SES_USER= 92 | # SES_PASSWORD= 93 | # aws region (e.g., us-east-1) 94 | # SES_REGION= 95 | # default ses port is 587 96 | # SES_PORT= 97 | 98 | # optional comma separated list of IDs of syncservers (e.g. SYNCSERVERS=1) 99 | # For this to work ADMIN_KEY must be set, or AUTOGEN_ADMIN_KEY must be true (default) 100 | SYNCSERVERS= 101 | # note: if you have more than one syncserver, you need to update docker-compose.yml 102 | SYNCSERVERS_1_URL= 103 | SYNCSERVERS_1_NAME= 104 | SYNCSERVERS_1_UUID= 105 | SYNCSERVERS_1_KEY= 106 | # pull rules are JSON encoded (and escaped) dictionaries 107 | # Example: only pull events where the analysis is complete 108 | # SYNCSERVERS_1_PULL_RULES='{\"tags\":{\"OR\":[],\"NOT\":[]},\"orgs\":{\"OR\":[],\"NOT\":[]},\"url_params\":\"{\\\"searchanalysis\\\": \\\"2\\\"}\"}' 109 | SYNCSERVERS_1_PULL_RULES= 110 | 111 | # optional, specify host and credentials for externally running supervisord 112 | # SUPERVISOR_HOST= 113 | # SUPERVISOR_USERNAME= 114 | # SUPERVISOR_PASSWORD= 115 | 116 | # optional and used to set mysql db and credentials 117 | # MYSQL_HOST= 118 | # MYSQL_PORT= 119 | # MYSQL_USER= 120 | # MYSQL_PASSWORD= 121 | # MYSQL_ROOT_PASSWORD= 122 | # MYSQL_DATABASE= 123 | 124 | # optional and used to set mysql db TLS configuration 125 | # MYSQL_TLS=true 126 | # MYSQL_TLS_CA=/custom/files/tls/misp_ca.pem 127 | # MYSQL_TLS_CERT=/custom/files/tls/misp_cert.cert 128 | # MYSQL_TLS_KEY=/custom/files/tls/misp_key.key 129 | 130 | # optional and used to set redis 131 | # REDIS_HOST= 132 | # REDIS_PORT= 133 | # remember to escape special character '$', e.g., 'test1%<$1323>' becomes 'test1%<$$1323>' 134 | # REDIS_PASSWORD= 135 | # Enable passwordless Redis connection (defaults to false for security) 136 | # ENABLE_REDIS_EMPTY_PASSWORD=false 137 | 138 | # These variables allows overriding some MISP email values. 139 | # They all default to ADMIN_EMAIL. 140 | 141 | # MISP.email, used for notifications. Also used 142 | # for GnuPG.email and GPG autogeneration. 143 | # MISP_EMAIL= 144 | 145 | # MISP.contact, the e-mail address that 146 | # MISP should include as a contact address 147 | # for the instance's support team. 148 | # MISP_CONTACT= 149 | 150 | # Enable GPG autogeneration (default true) 151 | # AUTOCONF_GPG=true 152 | 153 | # Enable admin (user #1) API key autogeneration 154 | # if ADMIN_KEY is not set above (default true) 155 | # AUTOGEN_ADMIN_KEY=true 156 | 157 | # Disable IPv6 completely 158 | # DISABLE_IPV6=true 159 | 160 | # Disable SSL redirect 161 | # DISABLE_SSL_REDIRECT=true 162 | 163 | # Disable CA refresh 164 | # DISABLE_CA_REFRESH=true 165 | 166 | # Enable OIDC authentication, according to https://github.com/MISP/MISP/blob/2.4/app/Plugin/OidcAuth/README.md 167 | # OIDC_ENABLE=true 168 | # OIDC_PROVIDER_URL= 169 | # OIDC_ISSUER= 170 | # OIDC_CLIENT_ID= 171 | # OIDC_CLIENT_SECRET= 172 | # OIDC_CODE_CHALLENGE_METHOD=S256 173 | # OIDC_ROLES_PROPERTY="roles" 174 | # OIDC_ROLES_MAPPING="{\"admin\": 1}" 175 | # OIDC_DEFAULT_ORG= 176 | # OIDC_MIXEDAUTH=true 177 | # OIDC_AUTH_METHOD="client_secret_post" 178 | # OIDC_REDIRECT_URI= 179 | # OIDC_SCOPES="[\"profile\", \"email\"]" 180 | # OIDC_LOGOUT_URL= 181 | 182 | # Enable LDAP (using the ApacheSecureAuth component) authentication, according to https://github.com/MISP/MISP/issues/6189 183 | # NOTE: Once you enable LDAP authentication with the ApacheSecureAuth component, 184 | # users should not be able to control the HTTP header configured in LDAP_APACHE_ENV 185 | # (e.g. REMOTE_USER), this means you must not allow direct access to MISP. 186 | # NOTE 2: You need to escape special characters twice, e.g., "pass\word" becomes "pass\\\\word". 187 | # APACHESECUREAUTH_LDAP_ENABLE=true 188 | # APACHESECUREAUTH_LDAP_APACHE_ENV="REMOTE_USER" 189 | # APACHESECUREAUTH_LDAP_SERVER="ldap://your_domain_controller" 190 | # APACHESECUREAUTH_LDAP_STARTTLS=true 191 | # APACHESECUREAUTH_LDAP_READER_USER="CN=service_account_name,OU=Users,DC=domain,DC=net" 192 | # APACHESECUREAUTH_LDAP_READER_PASSWORD="password" 193 | # APACHESECUREAUTH_LDAP_DN="OU=Users,DC=domain,DC=net" 194 | # APACHESECUREAUTH_LDAP_SEARCH_FILTER="" 195 | # APACHESECUREAUTH_LDAP_SEARCH_ATTRIBUTE="uid" 196 | # APACHESECUREAUTH_LDAP_FILTER="[\"mail\", \"uid\", \"cn\" ]" 197 | # APACHESECUREAUTH_LDAP_DEFAULT_ROLE_ID="3" 198 | # APACHESECUREAUTH_LDAP_DEFAULT_ORG="1" 199 | # APACHESECUREAUTH_LDAP_EMAIL_FIELD="[\"mail\"]" 200 | # APACHESECUREAUTH_LDAP_OPT_PROTOCOL_VERSION="3" 201 | # APACHESECUREAUTH_LDAP_OPT_NETWORK_TIMEOUT="-1" 202 | # APACHESECUREAUTH_LDAP_OPT_REFERRALS=false 203 | 204 | # Enable LDAP (using the MISP plugin native) authentication, according to https://github.com/MISP/MISP/tree/2.5/app/Plugin/LdapAuth 205 | # NOTE 2: You need to escape special characters twice, e.g., "pass\word" becomes "pass\\\\word". 206 | # LDAPAUTH_ENABLE=true 207 | # LDAPAUTH_LDAPSERVER="ldap://your_domain_controller" 208 | # LDAPAUTH_LDAPDN="OU=Users,DC=domain,DC=net" 209 | # LDAPAUTH_LDAPREADERUSER="CN=service_account_name,OU=Users,DC=domain,DC=net" 210 | # LDAPAUTH_LDAPREADERPASSWORD="password" 211 | # LDAPAUTH_LDAPSEARCHFILTER="" 212 | # LDAPAUTH_LDAPSEARCHATTRIBUTE="mail" 213 | # LDAPAUTH_LDAPEMAILFIELD="mail" 214 | # LDAPAUTH_LDAPNETWORKTIMEOUT="-1" 215 | # LDAPAUTH_LDAPPROTOCOL="3" 216 | # LDAPAUTH_LDAPALLOWREFERRALS=true 217 | # LDAPAUTH_STARTTLS=false 218 | # LDAPAUTH_MIXEDAUTH=true 219 | # LDAPAUTH_LDAPDEFAULTORGID="1" 220 | # LDAPAUTH_LDAPDEFAULTROLEID="3" 221 | # LDAPAUTH_UPDATEUSER=true 222 | # LDAPAUTH_DEBUG=false 223 | # LDAPAUTH_LDAPTLSREQUIRECERT="LDAP_OPT_X_TLS_ALLOW" 224 | # LDAPAUTH_LDAPTLSCUSTOMCACERT=false 225 | # LDAPAUTH_LDAPTLSCRLCHECK="LDAP_OPT_X_TLS_CRL_PEER" 226 | # LDAPAUTH_LDAPTLSPROTOCOLMIN="LDAP_OPT_X_TLS_PROTOCOL_TLS1_2" 227 | 228 | # Enable Azure AD (Entra) authentication, according to https://github.com/MISP/MISP/blob/2.4/app/Plugin/AadAuth/README.md 229 | # AAD_ENABLE=true 230 | # AAD_CLIENT_ID= 231 | # AAD_TENANT_ID= 232 | # AAD_CLIENT_SECRET= 233 | # AAD_REDIRECT_URI="https://misp.mydomain.com/users/login" 234 | # AAD_PROVIDER="https://login.microsoftonline.com/" 235 | # AAD_PROVIDER_USER="https://graph.microsoft.com/" 236 | # AAD_MISP_USER="Misp Users" 237 | # AAD_MISP_ORGADMIN="Misp Org Admins" 238 | # AAD_MISP_SITEADMIN="Misp Site Admins" 239 | # AAD_CHECK_GROUPS=false 240 | 241 | # Enable the use of a Proxy server (MISP-Guard or external) 242 | # PROXY_ENABLE=true 243 | # PROXY_HOST= 244 | # PROXY_PORT= 245 | # PROXY_METHOD= 246 | # PROXY_USER= 247 | # PROXY_PASSWORD= 248 | 249 | # Set up S3 based attachment storage 250 | # S3_BUCKET= 251 | # S3_ENDPOINT= 252 | # S3_ACCESS_KEY= 253 | # S3_SECRET_KEY= 254 | 255 | ## MISP-Guard 256 | # Configure rules in ./guard/config.json. 257 | # Requires restart of misp-guard container after changes. 258 | 259 | # Toggle to enable MISP-Guard container (optional) 260 | # COMPOSE_PROFILES=misp-guard 261 | # If you enable MISP-Guard, you must also configure MISP to use it as a proxy: 262 | # PROXY_PORT must match GUARD_PORT 263 | 264 | # MISP-Guard runtime flags (optional) 265 | # GUARD_PORT=8888 266 | # mitmdump misp-guard runtime arguments (space separated, no quotes) 267 | # GUARD_ARGS=--ssl-insecure -v 268 | 269 | # Enable debugging 270 | # ALWAYS SET THIS TO 0 IN PRODUCTION 271 | # 0 - Debug off (default) 272 | # 1 - Debug on 273 | # 2 - Debug on + SQL dump 274 | # DEBUG= 275 | 276 | # FastCGI configuration on nginx 277 | # FASTCGI_READ_TIMEOUT=300s 278 | # FASTCGI_SEND_TIMEOUT=300s 279 | # FASTCGI_CONNECT_TIMEOUT=300s 280 | # Where to listen to PHP-FPM status. Can be a port or a ip:port. If not set the status page will not be shown. 281 | # Do not expose this page in public networks. 282 | # FASTCGI_STATUS_LISTEN="" 283 | 284 | # PHP FPM configuration 285 | 286 | ## Basic PHP settings 287 | # Maximum memory a PHP script can use. 288 | # PHP_MEMORY_LIMIT=2048M 289 | # Maximum execution time for a PHP script in seconds. 290 | # PHP_MAX_EXECUTION_TIME=300 291 | # Maximum file upload size for PHP scripts. 292 | # PHP_UPLOAD_MAX_FILESIZE=50M 293 | # Maximum size for POST data sent to PHP. 294 | # PHP_POST_MAX_SIZE=50M 295 | # Maximum time PHP spends parsing input data in seconds. 296 | # PHP_MAX_INPUT_TIME=300 297 | # Maximum number of file to upload per request. 298 | # PHP_MAX_FILE_UPLOADS=50 299 | 300 | ## PHP FPM pool setup 301 | # Maximum number of php-fpm processes, limits the number of simultaneous requests. 302 | # PHP_FCGI_CHILDREN=5 303 | # Number of processes created on startup. 304 | # PHP_FCGI_START_SERVERS=2 305 | # The desired number of idle server processes. 306 | # PHP_FCGI_SPARE_SERVERS=1 307 | # The number of requests each process should execute before respawning. "0" means endless request processing. 308 | # PHP_FCGI_MAX_REQUESTS=0 309 | 310 | ## Additional PHP settings 311 | # Timeout (in minutes) for user session inactivity before it expires. 312 | # PHP_SESSION_TIMEOUT=60 313 | # Session cookie validity period in minutes. 314 | # PHP_SESSION_COOKIE_TIMEOUT=10080 315 | # Default PHP configurations. 316 | # PHP_SESSION_DEFAULTS=php 317 | # Automatically regenerate session ID on each request. 318 | # PHP_SESSION_AUTO_REGENERATE=false 319 | # Check user agent on each request for security. 320 | # PHP_SESSION_CHECK_AGENT=false 321 | # Only send session cookies over HTTPS. 322 | # PHP_SESSION_COOKIE_SECURE=true 323 | # Domain for session cookie validity (leave empty for current domain). 324 | # PHP_SESSION_COOKIE_DOMAIN= 325 | # SameSite policy for cookies ("Lax" allows top-level navigation). 326 | # PHP_SESSION_COOKIE_SAMESITE=Lax 327 | 328 | # MariaSQL/MySQL (InnoDB) configuration 329 | # INNODB_BUFFER_POOL_SIZE=2048M 330 | # INNODB_CHANGE_BUFFERING=none 331 | # INNODB_IO_CAPACITY=1000 332 | # INNODB_IO_CAPACITY_MAX=2000 333 | # INNODB_LOG_FILE_SIZE=600M 334 | # INNODB_READ_IO_THREADS=16 335 | # INNODB_STATS_PERSISTENT=ON 336 | # INNODB_WRITE_IO_THREADS=4 337 | 338 | # Whether to enable processing of the X-Forwarded-For header (default to false) 339 | # NGINX_X_FORWARDED_FOR=true 340 | # Comma separated list of trusted IP addresses 341 | # NGINX_SET_REAL_IP_FROM=127.0.0.1 342 | 343 | # Security Settings 344 | # Maximum time (in seconds) for HSTS (HTTP Strict Transport Security), ensures HTTPS is used. 345 | # HSTS_MAX_AGE= 346 | 347 | # X-Frame-Options policy configuration: controls whether the site can be embedded in frames or iframes. 348 | # Options: DENY, SAMEORIGIN, ALLOW-FROM Default: SAMEORIGIN 349 | # X_FRAME_OPTIONS= 350 | 351 | # NGINX maximum allowed size of the client request body. 352 | # NGINX_CLIENT_MAX_BODY_SIZE=50M 353 | 354 | # Content-Security-Policy (CSP) configuration: defines allowed resources and prevents attacks like XSS. 355 | # Example: "frame-src 'self' https://*.example.com; frame-ancestors 'self' https://*.example.com; object-src 'none'; report-uri https://example.com/cspReport" 356 | # CONTENT_SECURITY_POLICY= 357 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | # This is capable to relay via gmail, Amazon SES, or generic relays 3 | # See: https://gitlab.com/egos-tech/smtp 4 | mail: 5 | image: ghcr.io/egos-tech/smtp:1.1.3 6 | restart: always 7 | environment: 8 | # For use with Amazon SES relay 9 | - "SES_USER=${SES_USER}" 10 | - "SES_PASSWORD=${SES_PASSWORD}" 11 | - "SES_REGION=${SES_REGION}" 12 | - "SES_PORT=${SES_PORT}" 13 | # Generic SMTP relay 14 | - "SMARTHOST_ADDRESS=${SMARTHOST_ADDRESS}" 15 | - "SMARTHOST_PORT=${SMARTHOST_PORT}" 16 | - "SMARTHOST_USER=${SMARTHOST_USER}" 17 | - "SMARTHOST_PASSWORD=${SMARTHOST_PASSWORD}" 18 | - "SMARTHOST_ALIASES=${SMARTHOST_ALIASES}" 19 | 20 | redis: 21 | image: valkey/valkey:7.2 22 | restart: always 23 | command: | 24 | sh -c ' 25 | if [ "$${ENABLE_REDIS_EMPTY_PASSWORD:-false}" = "true" ]; then 26 | exec valkey-server 27 | else 28 | exec valkey-server --requirepass "$${REDIS_PASSWORD:-redispassword}" 29 | fi 30 | ' 31 | environment: 32 | - "ENABLE_REDIS_EMPTY_PASSWORD=${ENABLE_REDIS_EMPTY_PASSWORD:-false}" 33 | - "REDIS_PASSWORD=${REDIS_PASSWORD:-redispassword}" 34 | healthcheck: 35 | test: | 36 | sh -c ' 37 | if [ "$${ENABLE_REDIS_EMPTY_PASSWORD:-false}" = "true" ]; then 38 | valkey-cli -p $${REDIS_PORT:-6379} ping | grep -q PONG || exit 1 39 | else 40 | valkey-cli -a "$${REDIS_PASSWORD:-redispassword}" -p $${REDIS_PORT:-6379} ping | grep -q PONG || exit 1 41 | fi 42 | ' 43 | interval: 2s 44 | timeout: 1s 45 | retries: 3 46 | start_period: 5s 47 | start_interval: 5s 48 | volumes: 49 | - cache_data:/data:Z 50 | 51 | db: 52 | # We use MariaDB because it supports ARM and has the expected collations 53 | image: mariadb:10.11 54 | restart: always 55 | environment: 56 | - "MYSQL_USER=${MYSQL_USER:-misp}" 57 | - "MYSQL_PASSWORD=${MYSQL_PASSWORD:-example}" 58 | - "MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-password}" 59 | - "MYSQL_DATABASE=${MYSQL_DATABASE:-misp}" 60 | command: "\ 61 | --innodb-buffer-pool-size=${INNODB_BUFFER_POOL_SIZE:-2048M} \ 62 | --innodb-change-buffering=${INNODB_CHANGE_BUFFERING:-none} \ 63 | --innodb-io-capacity=${INNODB_IO_CAPACITY:-1000} \ 64 | --innodb-io-capacity-max=${INNODB_IO_CAPACITY_MAX:-2000} \ 65 | --innodb-log-file-size=${INNODB_LOG_FILE_SIZE:-600M} \ 66 | --innodb-read-io-threads=${INNODB_READ_IO_THREADS:-16} \ 67 | --innodb-stats-persistent=${INNODB_STATS_PERSISTENT:-ON} \ 68 | --innodb-write-io-threads=${INNODB_WRITE_IO_THREADS:-4}" 69 | volumes: 70 | - mysql_data:/var/lib/mysql:Z 71 | cap_add: 72 | - SYS_NICE # CAP_SYS_NICE Prevent runaway mysql log 73 | healthcheck: 74 | test: mysqladmin --user=$$MYSQL_USER --password=$$MYSQL_PASSWORD status 75 | interval: 2s 76 | timeout: 1s 77 | retries: 3 78 | start_period: 30s 79 | start_interval: 5s 80 | 81 | misp-core: 82 | image: ghcr.io/misp/misp-docker/misp-core:${CORE_RUNNING_TAG:-latest} 83 | restart: always 84 | cap_add: 85 | - AUDIT_WRITE 86 | build: 87 | context: core/. 88 | args: 89 | - CORE_TAG=${CORE_TAG:?Missing or outdated .env file, see README.md for instructions} 90 | - CORE_COMMIT=${CORE_COMMIT} 91 | - CORE_FLAVOR=${CORE_FLAVOR:-standard} 92 | - PHP_VER=${PHP_VER:?Missing or outdated .env file, see README.md for instructions} 93 | - PYPI_REDIS_VERSION=${PYPI_REDIS_VERSION} 94 | - PYPI_LIEF_VERSION=${PYPI_LIEF_VERSION} 95 | - PYPI_PYDEEP2_VERSION=${PYPI_PYDEEP2_VERSION} 96 | - PYPI_PYTHON_MAGIC_VERSION=${PYPI_PYTHON_MAGIC_VERSION} 97 | - PYPI_MISP_LIB_STIX2_VERSION=${PYPI_MISP_LIB_STIX2_VERSION} 98 | - PYPI_MAEC_VERSION=${PYPI_MAEC_VERSION} 99 | - PYPI_MIXBOX_VERSION=${PYPI_MIXBOX_VERSION} 100 | - PYPI_CYBOX_VERSION=${PYPI_CYBOX_VERSION} 101 | - PYPI_PYMISP_VERSION=${PYPI_PYMISP_VERSION} 102 | - PYPI_MISP_STIX_VERSION=${PYPI_MISP_STIX_VERSION} 103 | - PYPI_TAXII2_CLIENT=${PYPI_TAXII2_CLIENT} 104 | - PYPI_SETUPTOOLS_VERSION=${PYPI_SETUPTOOLS_VERSION} 105 | - PYPI_SUPERVISOR_VERSION=${PYPI_SUPERVISOR_VERSION} 106 | depends_on: 107 | redis: 108 | condition: service_healthy 109 | db: 110 | condition: service_healthy 111 | misp-modules: 112 | condition: service_healthy 113 | healthcheck: 114 | test: curl -ks ${BASE_URL:-https://localhost}/users/heartbeat > /dev/null || exit 1 115 | interval: 2s 116 | timeout: 1s 117 | retries: 3 118 | start_period: 30s 119 | start_interval: 30s 120 | ports: 121 | - "${CORE_HTTP_PORT:-80}:80" 122 | - "${CORE_HTTPS_PORT:-443}:443" 123 | volumes: 124 | - "./configs/:/var/www/MISP/app/Config/:Z" 125 | - "./logs/:/var/www/MISP/app/tmp/logs/:Z" 126 | - "./files/:/var/www/MISP/app/files/:Z" 127 | - "./ssl/:/etc/nginx/certs/:Z" 128 | - "./gnupg/:/var/www/MISP/.gnupg/:Z" 129 | # customize by replacing ${CUSTOM_PATH} with a path containing 'files/customize_misp.sh' 130 | # - "${CUSTOM_PATH}/:/custom/:Z" 131 | # mount custom ca root certificates 132 | # - "./rootca.pem:/usr/local/share/ca-certificates/rootca.crt:Z" 133 | environment: 134 | - "BASE_URL=${BASE_URL}" 135 | - "CRON_USER_ID=${CRON_USER_ID}" 136 | - "CRON_PULLALL=${CRON_PULLALL}" 137 | - "CRON_PUSHALL=${CRON_PUSHALL}" 138 | - "DISABLE_IPV6=${DISABLE_IPV6}" 139 | - "DISABLE_SSL_REDIRECT=${DISABLE_SSL_REDIRECT}" 140 | - "ENABLE_DB_SETTINGS=${ENABLE_DB_SETTINGS}" 141 | - "ENABLE_BACKGROUND_UPDATES=${ENABLE_BACKGROUND_UPDATES}" 142 | - "ENCRYPTION_KEY=${ENCRYPTION_KEY}" 143 | - "DISABLE_CA_REFRESH=${DISABLE_CA_REFRESH}" 144 | - "DISABLE_PRINTING_PLAINTEXT_CREDENTIALS=${DISABLE_PRINTING_PLAINTEXT_CREDENTIALS}" 145 | # standard settings 146 | - "ADMIN_EMAIL=${ADMIN_EMAIL}" 147 | - "MISP_CONTACT=${MISP_CONTACT}" 148 | - "MISP_EMAIL=${MISP_EMAIL}" 149 | - "ADMIN_PASSWORD=${ADMIN_PASSWORD}" 150 | - "ADMIN_KEY=${ADMIN_KEY}" 151 | - "ADMIN_ORG=${ADMIN_ORG}" 152 | - "ADMIN_ORG_UUID=${ADMIN_ORG_UUID}" 153 | - "GPG_PASSPHRASE=${GPG_PASSPHRASE}" 154 | - "ATTACHMENTS_DIR=${ATTACHMENTS_DIR}" 155 | # OIDC authentication settings 156 | - "OIDC_ENABLE=${OIDC_ENABLE}" 157 | - "OIDC_PROVIDER_URL=${OIDC_PROVIDER_URL}" 158 | - "OIDC_ISSUER=${OIDC_ISSUER}" 159 | - "OIDC_CLIENT_ID=${OIDC_CLIENT_ID}" 160 | - "OIDC_CLIENT_SECRET=${OIDC_CLIENT_SECRET}" 161 | - "OIDC_CODE_CHALLENGE_METHOD=${OIDC_CODE_CHALLENGE_METHOD}" 162 | - "OIDC_ROLES_PROPERTY=${OIDC_ROLES_PROPERTY}" 163 | - "OIDC_ROLES_MAPPING=${OIDC_ROLES_MAPPING}" 164 | - "OIDC_DEFAULT_ORG=${OIDC_DEFAULT_ORG}" 165 | - "OIDC_MIXEDAUTH=${OIDC_MIXEDAUTH}" 166 | - "OIDC_AUTH_METHOD=${OIDC_AUTH_METHOD}" 167 | - "OIDC_REDIRECT_URI=${OIDC_REDIRECT_URI}" 168 | - "OIDC_SCOPES=${OIDC_SCOPES}" 169 | - "OIDC_LOGOUT_URL=${OIDC_LOGOUT_URL}" 170 | # APACHESECUREAUTH authentication settings 171 | - "APACHESECUREAUTH_LDAP_OLD_VAR_DETECT=${LDAP_ENABLE}" 172 | - "APACHESECUREAUTH_LDAP_ENABLE=${APACHESECUREAUTH_LDAP_ENABLE:-${LDAP_ENABLE}}" 173 | - "APACHESECUREAUTH_LDAP_APACHE_ENV=${APACHESECUREAUTH_LDAP_APACHE_ENV:-${LDAP_APACHE_ENV}}" 174 | - "APACHESECUREAUTH_LDAP_SERVER=${APACHESECUREAUTH_LDAP_SERVER:-${LDAP_SERVER}}" 175 | - "APACHESECUREAUTH_LDAP_STARTTLS=${APACHESECUREAUTH_LDAP_STARTTLS:-${LDAP_STARTTLS}}" 176 | - "APACHESECUREAUTH_LDAP_READER_USER=${APACHESECUREAUTH_LDAP_READER_USER:-${LDAP_READER_USER}}" 177 | - "APACHESECUREAUTH_LDAP_READER_PASSWORD=${APACHESECUREAUTH_LDAP_READER_PASSWORD:-${LDAP_READER_PASSWORD}}" 178 | - "APACHESECUREAUTH_LDAP_DN=${APACHESECUREAUTH_LDAP_DN:-${LDAP_DN}}" 179 | - "APACHESECUREAUTH_LDAP_SEARCH_FILTER=${APACHESECUREAUTH_LDAP_SEARCH_FILTER:-${LDAP_SEARCH_FILTER}}" 180 | - "APACHESECUREAUTH_LDAP_SEARCH_ATTRIBUTE=${APACHESECUREAUTH_LDAP_SEARCH_ATTRIBUTE:-${LDAP_SEARCH_ATTRIBUTE}}" 181 | - "APACHESECUREAUTH_LDAP_FILTER=${APACHESECUREAUTH_LDAP_FILTER:-${LDAP_FILTER}}" 182 | - "APACHESECUREAUTH_LDAP_DEFAULT_ROLE_ID=${APACHESECUREAUTH_LDAP_DEFAULT_ROLE_ID:-${LDAP_DEFAULT_ROLE_ID}}" 183 | - "APACHESECUREAUTH_LDAP_DEFAULT_ORG=${APACHESECUREAUTH_LDAP_DEFAULT_ORG:-${LDAP_DEFAULT_ORG}}" 184 | - "APACHESECUREAUTH_LDAP_EMAIL_FIELD=${APACHESECUREAUTH_LDAP_EMAIL_FIELD:-${LDAP_EMAIL_FIELD}}" 185 | - "APACHESECUREAUTH_LDAP_OPT_PROTOCOL_VERSION=${APACHESECUREAUTH_LDAP_OPT_PROTOCOL_VERSION:-${LDAP_OPT_PROTOCOL_VERSION}}" 186 | - "APACHESECUREAUTH_LDAP_OPT_NETWORK_TIMEOUT=${APACHESECUREAUTH_LDAP_OPT_NETWORK_TIMEOUT:-${LDAP_OPT_NETWORK_TIMEOUT}}" 187 | - "APACHESECUREAUTH_LDAP_OPT_REFERRALS=${APACHESECUREAUTH_LDAP_OPT_REFERRALS:-${LDAP_OPT_REFERRALS}}" 188 | # LdapAuth MISP authentication settings 189 | - "LDAPAUTH_ENABLE=${LDAPAUTH_ENABLE}" 190 | - "LDAPAUTH_LDAPSERVER=${LDAPAUTH_LDAPSERVER}" 191 | - "LDAPAUTH_LDAPDN=${LDAPAUTH_LDAPDN}" 192 | - "LDAPAUTH_LDAPREADERUSER=${LDAPAUTH_LDAPREADERUSER}" 193 | - "LDAPAUTH_LDAPREADERPASSWORD=${LDAPAUTH_LDAPREADERPASSWORD}" 194 | - "LDAPAUTH_LDAPSEARCHFILTER=${LDAPAUTH_LDAPSEARCHFILTER}" 195 | - "LDAPAUTH_LDAPSEARCHATTRIBUTE=${LDAPAUTH_LDAPSEARCHATTRIBUTE}" 196 | - "LDAPAUTH_LDAPEMAILFIELD=${LDAPAUTH_LDAPEMAILFIELD}" 197 | - "LDAPAUTH_LDAPNETWORKTIMEOUT=${LDAPAUTH_LDAPNETWORKTIMEOUT}" 198 | - "LDAPAUTH_LDAPPROTOCOL=${LDAPAUTH_LDAPPROTOCOL}" 199 | - "LDAPAUTH_LDAPALLOWREFERRALS=${LDAPAUTH_LDAPALLOWREFERRALS}" 200 | - "LDAPAUTH_STARTTLS=${LDAPAUTH_STARTTLS}" 201 | - "LDAPAUTH_MIXEDAUTH=${LDAPAUTH_MIXEDAUTH}" 202 | - "LDAPAUTH_LDAPDEFAULTORGID=${LDAPAUTH_LDAPDEFAULTORGID}" 203 | - "LDAPAUTH_LDAPDEFAULTROLEID=${LDAPAUTH_LDAPDEFAULTROLEID}" 204 | - "LDAPAUTH_UPDATEUSER=${LDAPAUTH_UPDATEUSER}" 205 | - "LDAPAUTH_DEBUG=${LDAPAUTH_DEBUG}" 206 | - "LDAPAUTH_LDAPTLSREQUIRECERT=${LDAPAUTH_LDAPTLSREQUIRECERT}" 207 | - "LDAPAUTH_LDAPTLSCUSTOMCACERT=${LDAPAUTH_LDAPTLSCUSTOMCACERT}" 208 | - "LDAPAUTH_LDAPTLSCRLCHECK=${LDAPAUTH_LDAPTLSCRLCHECK}" 209 | - "LDAPAUTH_LDAPTLSPROTOCOLMIN=${LDAPAUTH_LDAPTLSPROTOCOLMIN}" 210 | # AAD authentication settings 211 | - "AAD_ENABLE=${AAD_ENABLE}" 212 | - "AAD_CLIENT_ID=${AAD_CLIENT_ID}" 213 | - "AAD_TENANT_ID=${AAD_TENANT_ID}" 214 | - "AAD_CLIENT_SECRET=${AAD_CLIENT_SECRET}" 215 | - "AAD_REDIRECT_URI=${AAD_REDIRECT_URI}" 216 | - "AAD_PROVIDER=${AAD_PROVIDER}" 217 | - "AAD_PROVIDER_USER=${AAD_PROVIDER_USER}" 218 | - "AAD_MISP_USER=${AAD_MISP_USER}" 219 | - "AAD_MISP_ORGADMIN=${AAD_MISP_ORGADMIN}" 220 | - "AAD_MISP_SITEADMIN=${AAD_MISP_SITEADMIN}" 221 | - "AAD_CHECK_GROUPS=${AAD_CHECK_GROUPS}" 222 | # nginx settings 223 | - "NGINX_X_FORWARDED_FOR=${NGINX_X_FORWARDED_FOR}" 224 | - "NGINX_SET_REAL_IP_FROM=${NGINX_SET_REAL_IP_FROM}" 225 | - "NGINX_CLIENT_MAX_BODY_SIZE=${NGINX_CLIENT_MAX_BODY_SIZE:-50M}" 226 | # proxy settings 227 | - "PROXY_ENABLE=${PROXY_ENABLE}" 228 | - "PROXY_HOST=${PROXY_HOST}" 229 | - "PROXY_PORT=${PROXY_PORT}" 230 | - "PROXY_METHOD=${PROXY_METHOD}" 231 | - "PROXY_USER=${PROXY_USER}" 232 | - "PROXY_PASSWORD=${PROXY_PASSWORD}" 233 | # s3 settings 234 | - "S3_BUCKET=${S3_BUCKET}" 235 | - "S3_ENDPOINT=${S3_ENDPOINT}" 236 | - "S3_ACCESS_KEY=${S3_ACCESS_KEY}" 237 | - "S3_SECRET_KEY=${S3_SECRET_KEY}" 238 | # supervisor settings 239 | - "SUPERVISOR_HOST=${SUPERVISOR_HOST}" 240 | - "SUPERVISOR_USERNAME=${SUPERVISOR_USERNAME}" 241 | - "SUPERVISOR_PASSWORD=${SUPERVISOR_PASSWORD}" 242 | # sync server settings (see https://www.misp-project.org/openapi/#tag/Servers for more options) 243 | - "SYNCSERVERS=${SYNCSERVERS}" 244 | - | 245 | SYNCSERVERS_1_DATA= 246 | { 247 | "remote_org_uuid": "${SYNCSERVERS_1_UUID}", 248 | "name": "${SYNCSERVERS_1_NAME}", 249 | "authkey": "${SYNCSERVERS_1_KEY}", 250 | "url": "${SYNCSERVERS_1_URL}", 251 | "pull_rules": "${SYNCSERVERS_1_PULL_RULES}", 252 | "pull": true 253 | } 254 | # mysql settings 255 | - "MYSQL_HOST=${MYSQL_HOST:-db}" 256 | - "MYSQL_PORT=${MYSQL_PORT:-3306}" 257 | - "MYSQL_USER=${MYSQL_USER:-misp}" 258 | - "MYSQL_PASSWORD=${MYSQL_PASSWORD:-example}" 259 | - "MYSQL_DATABASE=${MYSQL_DATABASE:-misp}" 260 | # mysql TLS settings 261 | - "MYSQL_TLS=${MYSQL_TLS:-false}" 262 | - "MYSQL_TLS_CA=${MYSQL_TLS_CA}" 263 | - "MYSQL_TLS_CERT=${MYSQL_TLS_CERT}" 264 | - "MYSQL_TLS_KEY=${MYSQL_TLS_KEY}" 265 | # redis settings 266 | - "REDIS_HOST=${REDIS_HOST:-redis}" 267 | - "REDIS_PORT=${REDIS_PORT:-6379}" 268 | - "REDIS_PASSWORD=${REDIS_PASSWORD:-redispassword}" 269 | - "ENABLE_REDIS_EMPTY_PASSWORD=${ENABLE_REDIS_EMPTY_PASSWORD:-false}" 270 | # debug setting 271 | - "DEBUG=${DEBUG}" 272 | # SMTP setting 273 | - "SMTP_FQDN=${SMTP_FQDN}" 274 | - "SMTP_PORT=${SMTP_PORT:-25}" 275 | # NGINX settings 276 | - "FASTCGI_READ_TIMEOUT=${FASTCGI_READ_TIMEOUT:-300s}" 277 | - "FASTCGI_SEND_TIMEOUT=${FASTCGI_SEND_TIMEOUT:-300s}" 278 | - "FASTCGI_CONNECT_TIMEOUT=${FASTCGI_CONNECT_TIMEOUT:-300s}" 279 | - "FASTCGI_STATUS_LISTEN=${FASTCGI_STATUS_LISTEN}" 280 | # PHP settings 281 | - "PHP_MEMORY_LIMIT=${PHP_MEMORY_LIMIT:-2048M}" 282 | - "PHP_MAX_EXECUTION_TIME=${PHP_MAX_EXECUTION_TIME:-300}" 283 | - "PHP_UPLOAD_MAX_FILESIZE=${PHP_UPLOAD_MAX_FILESIZE:-50M}" 284 | - "PHP_POST_MAX_SIZE=${PHP_POST_MAX_SIZE:-50M}" 285 | - "PHP_MAX_INPUT_TIME=${PHP_MAX_INPUT_TIME:-300}" 286 | - "PHP_MAX_FILE_UPLOADS=${PHP_MAX_FILE_UPLOADS:-50}" 287 | # PHP FPM pool setup 288 | - "PHP_FCGI_CHILDREN=${PHP_FCGI_CHILDREN:-5}" 289 | - "PHP_FCGI_START_SERVERS=${PHP_FCGI_START_SERVERS:-2}" 290 | - "PHP_FCGI_SPARE_SERVERS=${PHP_FCGI_SPARE_SERVERS:-1}" 291 | - "PHP_FCGI_MAX_REQUESTS=${PHP_FCGI_MAX_REQUESTS:-0}" 292 | # additional PHP settings 293 | - "PHP_SESSION_TIMEOUT=${PHP_SESSION_TIMEOUT:-60}" 294 | - "PHP_SESSION_COOKIE_TIMEOUT=${PHP_SESSION_COOKIE_TIMEOUT:-10080}" 295 | - "PHP_SESSION_DEFAULTS=${PHP_SESSION_DEFAULTS:-php}" 296 | - "PHP_SESSION_AUTO_REGENERATE=${PHP_SESSION_AUTO_REGENERATE:-false}" 297 | - "PHP_SESSION_CHECK_AGENT=${PHP_SESSION_CHECK_AGENT:-false}" 298 | - "PHP_SESSION_COOKIE_SECURE=${PHP_SESSION_COOKIE_SECURE:-true}" 299 | - "PHP_SESSION_COOKIE_DOMAIN=${PHP_SESSION_COOKIE_DOMAIN}" 300 | - "PHP_SESSION_COOKIE_SAMESITE=${PHP_SESSION_COOKIE_SAMESITE:-Lax}" 301 | - "PHP_TIMEZONE=${PHP_TIMEZONE:-UTC}" 302 | # security settings 303 | - "HSTS_MAX_AGE=${HSTS_MAX_AGE}" 304 | - "X_FRAME_OPTIONS=${X_FRAME_OPTIONS}" 305 | - "CONTENT_SECURITY_POLICY=${CONTENT_SECURITY_POLICY}" 306 | 307 | misp-modules: 308 | image: ghcr.io/misp/misp-docker/misp-modules:${MODULES_RUNNING_TAG:-latest} 309 | restart: always 310 | build: 311 | context: modules/. 312 | args: 313 | - MODULES_TAG=${MODULES_TAG:?Missing or outdated .env file, see README.md for instructions} 314 | - MODULES_COMMIT=${MODULES_COMMIT} 315 | - MODULES_FLAVOR=${MODULES_FLAVOR:-standard} 316 | healthcheck: 317 | test: "/bin/bash -c '/dev/null 7 | } 8 | 9 | trap term_proc SIGTERM 10 | 11 | update_database_tls_config() { 12 | local key="$1" 13 | local value="$2" 14 | local config_file="$3" 15 | local enable="$4" 16 | 17 | [[ -z "$key" || -z "$config_file" ]] && { echo "key/config_file required"; return 1; } 18 | [[ ! -f "$config_file" ]] && { echo "Config file not found: $config_file"; return 1; } 19 | 20 | if [[ "$enable" == true && -z "$value" ]]; then 21 | #echo "Not setting $key as value is empty..." 22 | return 0 23 | fi 24 | 25 | if [[ "$enable" == true && "$key" =~ ^(ssl_ca|ssl_cert|ssl_key)$ ]]; then 26 | if [[ ! -f "$value" ]]; then 27 | echo "Cannot configure TLS key $key: file $value does not exist..." 28 | return 1 29 | fi 30 | fi 31 | 32 | local tmp 33 | tmp="$(mktemp)" 34 | 35 | if [[ "$enable" == true ]]; then 36 | if grep -qE "^[[:space:]]*'${key}'[[:space:]]*=>" "$config_file"; then 37 | sed -E "s@^([[:space:]]*'${key}'[[:space:]]*=>)[^,]*,@\1 '${value}',@g" \ 38 | "$config_file" > "$tmp" 39 | else 40 | sed -E "/public[[:space:]]+\\\$default[[:space:]]*=[[:space:]]*\\[/a\\ 41 | '${key}' => '${value}'," \ 42 | "$config_file" > "$tmp" 43 | fi 44 | else 45 | sed -E "/^[[:space:]]*'${key}'[[:space:]]*=>/d" \ 46 | "$config_file" > "$tmp" 47 | fi 48 | 49 | if [[ -s "$tmp" ]]; then 50 | cat "$tmp" > "$config_file" 51 | fi 52 | rm -f "$tmp" 53 | } 54 | 55 | init_mysql(){ 56 | # Test when MySQL is ready.... 57 | # wait for Database come ready 58 | isDBup () { 59 | echo "SHOW STATUS" | $MYSQL_CMD 1>/dev/null 60 | echo $? 61 | } 62 | 63 | isDBinitDone () { 64 | # Table attributes has existed since at least v2.1 65 | echo "DESCRIBE attributes" | $MYSQL_CMD 1>/dev/null 66 | echo $? 67 | } 68 | 69 | RETRY=100 70 | until [ $(isDBup) -eq 0 ] || [ $RETRY -le 0 ] ; do 71 | echo "... waiting for database to come up" 72 | sleep 5 73 | RETRY=$(( RETRY - 1)) 74 | done 75 | if [ $RETRY -le 0 ]; then 76 | >&2 echo "... error: Could not connect to Database on $MYSQL_HOST:$MYSQL_PORT" 77 | exit 1 78 | fi 79 | 80 | if [ $(isDBinitDone) -eq 0 ]; then 81 | echo "... database has already been initialized" 82 | export DB_ALREADY_INITIALISED=true 83 | else 84 | echo "... database has not been initialized, importing MySQL scheme..." 85 | $MYSQL_CMD < /var/www/MISP/INSTALL/MYSQL.sql 86 | fi 87 | } 88 | 89 | init_misp_data_files(){ 90 | # Init config (shared with host) 91 | echo "... initialize configuration files" 92 | MISP_APP_CONFIG_PATH=/var/www/MISP/app/Config 93 | # workaround for https://forums.docker.com/t/sed-couldnt-open-temporary-file-xyz-permission-denied-when-using-virtiofs/125473 94 | # [ -f $MISP_APP_CONFIG_PATH/bootstrap.php ] || cp $MISP_APP_CONFIG_PATH.dist/bootstrap.default.php $MISP_APP_CONFIG_PATH/bootstrap.php 95 | # [ -f $MISP_APP_CONFIG_PATH/database.php ] || cp $MISP_APP_CONFIG_PATH.dist/database.default.php $MISP_APP_CONFIG_PATH/database.php 96 | # [ -f $MISP_APP_CONFIG_PATH/core.php ] || cp $MISP_APP_CONFIG_PATH.dist/core.default.php $MISP_APP_CONFIG_PATH/core.php 97 | # [ -f $MISP_APP_CONFIG_PATH/config.php ] || cp $MISP_APP_CONFIG_PATH.dist/config.default.php $MISP_APP_CONFIG_PATH/config.php 98 | # [ -f $MISP_APP_CONFIG_PATH/email.php ] || cp $MISP_APP_CONFIG_PATH.dist/email.php $MISP_APP_CONFIG_PATH/email.php 99 | # [ -f $MISP_APP_CONFIG_PATH/routes.php ] || cp $MISP_APP_CONFIG_PATH.dist/routes.php $MISP_APP_CONFIG_PATH/routes.php 100 | [ -f $MISP_APP_CONFIG_PATH/bootstrap.php ] || dd if=$MISP_APP_CONFIG_PATH.dist/bootstrap.default.php of=$MISP_APP_CONFIG_PATH/bootstrap.php 101 | [ -f $MISP_APP_CONFIG_PATH/database.php ] || dd if=$MISP_APP_CONFIG_PATH.dist/database.default.php of=$MISP_APP_CONFIG_PATH/database.php 102 | [ -f $MISP_APP_CONFIG_PATH/core.php ] || dd if=$MISP_APP_CONFIG_PATH.dist/core.default.php of=$MISP_APP_CONFIG_PATH/core.php 103 | [ -f $MISP_APP_CONFIG_PATH/config.php.template ] || dd if=$MISP_APP_CONFIG_PATH.dist/config.default.php of=$MISP_APP_CONFIG_PATH/config.php.template 104 | [ -f $MISP_APP_CONFIG_PATH/config.php ] || echo -e "" > $MISP_APP_CONFIG_PATH/config.php 105 | [ -f $MISP_APP_CONFIG_PATH/email.php ] || dd if=$MISP_APP_CONFIG_PATH.dist/email.php of=$MISP_APP_CONFIG_PATH/email.php 106 | [ -f $MISP_APP_CONFIG_PATH/routes.php ] || dd if=$MISP_APP_CONFIG_PATH.dist/routes.php of=$MISP_APP_CONFIG_PATH/routes.php 107 | 108 | if ! grep -q "Detect what auth modules" "$MISP_APP_CONFIG_PATH/bootstrap.php"; then 109 | echo "... patch bootstrap.php settings" 110 | chmod +w $MISP_APP_CONFIG_PATH/bootstrap.php 111 | # workaround for https://forums.docker.com/t/sed-couldnt-open-temporary-file-xyz-permission-denied-when-using-virtiofs/125473 112 | sed -z "s|CakePlugin::loadAll(array(.*CakeResque.*));||g" $MISP_APP_CONFIG_PATH/bootstrap.php > tmp; cat tmp > $MISP_APP_CONFIG_PATH/bootstrap.php; rm tmp 113 | sed "s|CakePlugin::load('AadAuth');||g" $MISP_APP_CONFIG_PATH/bootstrap.php > tmp; cat tmp > $MISP_APP_CONFIG_PATH/bootstrap.php; rm tmp 114 | sed "s|CakePlugin::load('CertAuth');||g" $MISP_APP_CONFIG_PATH/bootstrap.php > tmp; cat tmp > $MISP_APP_CONFIG_PATH/bootstrap.php; rm tmp 115 | sed "s|CakePlugin::load('LdapAuth');||g" $MISP_APP_CONFIG_PATH/bootstrap.php > tmp; cat tmp > $MISP_APP_CONFIG_PATH/bootstrap.php; rm tmp 116 | sed "s|CakePlugin::load('LinOTPAuth');||g" $MISP_APP_CONFIG_PATH/bootstrap.php > tmp; cat tmp > $MISP_APP_CONFIG_PATH/bootstrap.php; rm tmp 117 | sed "s|CakePlugin::load('OidcAuth');||g" $MISP_APP_CONFIG_PATH/bootstrap.php > tmp; cat tmp > $MISP_APP_CONFIG_PATH/bootstrap.php; rm tmp 118 | sed "s|CakePlugin::load('ShibbAuth');||g" $MISP_APP_CONFIG_PATH/bootstrap.php > tmp; cat tmp > $MISP_APP_CONFIG_PATH/bootstrap.php; rm tmp 119 | cat <> $MISP_APP_CONFIG_PATH/bootstrap.php 120 | 121 | /** 122 | * Detect what auth modules need to be loaded based on the loaded config 123 | */ 124 | 125 | if (Configure::read('AadAuth')) { 126 | CakePlugin::load('AadAuth'); 127 | } 128 | 129 | if (Configure::read('CertAuth')) { 130 | CakePlugin::load('CertAuth'); 131 | } 132 | 133 | if (Configure::read('LdapAuth')) { 134 | CakePlugin::load('LdapAuth'); 135 | } 136 | 137 | if (Configure::read('LinOTPAuth')) { 138 | CakePlugin::load('LinOTPAuth'); 139 | } 140 | 141 | if (Configure::read('OidcAuth')) { 142 | CakePlugin::load('OidcAuth'); 143 | } 144 | 145 | if (Configure::read('ShibbAuth')) { 146 | CakePlugin::load('ShibbAuth'); 147 | } 148 | 149 | EOT 150 | else 151 | echo "... patch bootstrap.php settings not required" 152 | fi 153 | 154 | echo "... initialize database.php settings" 155 | # workaround for https://forums.docker.com/t/sed-couldnt-open-temporary-file-xyz-permission-denied-when-using-virtiofs/125473 156 | # sed -i "s/localhost/$MYSQL_HOST/" $MISP_APP_CONFIG_PATH/database.php 157 | # sed -i "s/db\s*login/$MYSQL_USER/" $MISP_APP_CONFIG_PATH/database.php 158 | # sed -i "s/3306/$MYSQL_PORT/" $MISP_APP_CONFIG_PATH/database.php 159 | # sed -i "s/db\s*password/$MYSQL_PASSWORD/" $MISP_APP_CONFIG_PATH/database.php 160 | # sed -i "s/'database' => 'misp'/'database' => '$MYSQL_DATABASE'/" $MISP_APP_CONFIG_PATH/database.php 161 | chmod +w $MISP_APP_CONFIG_PATH/database.php 162 | sed "s/localhost/$MYSQL_HOST/" $MISP_APP_CONFIG_PATH/database.php > tmp; cat tmp > $MISP_APP_CONFIG_PATH/database.php; rm tmp 163 | sed "s/db\s*login/$MYSQL_USER/" $MISP_APP_CONFIG_PATH/database.php > tmp; cat tmp > $MISP_APP_CONFIG_PATH/database.php; rm tmp 164 | sed "s/3306/$MYSQL_PORT/" $MISP_APP_CONFIG_PATH/database.php > tmp; cat tmp > $MISP_APP_CONFIG_PATH/database.php; rm tmp 165 | sed "s/db\s*password/$MYSQL_PASSWORD/" $MISP_APP_CONFIG_PATH/database.php > tmp; cat tmp > $MISP_APP_CONFIG_PATH/database.php; rm tmp 166 | sed "s/'database' => 'misp'/'database' => '$MYSQL_DATABASE'/" $MISP_APP_CONFIG_PATH/database.php > tmp; cat tmp > $MISP_APP_CONFIG_PATH/database.php; rm tmp 167 | 168 | # Enable MySQL TLS immediately, as TLS requiring hosts like AWS RDS may banlist non-TLS connecting hosts 169 | # Conversely, this is also a good spot to disable it if required 170 | 171 | update_database_tls_config ssl_ca "$MYSQL_TLS_CA" "$MISP_APP_CONFIG_PATH/database.php" "$MYSQL_TLS" 172 | update_database_tls_config ssl_cert "$MYSQL_TLS_CERT" "$MISP_APP_CONFIG_PATH/database.php" "$MYSQL_TLS" 173 | update_database_tls_config ssl_key "$MYSQL_TLS_KEY" "$MISP_APP_CONFIG_PATH/database.php" "$MYSQL_TLS" 174 | 175 | echo "... initialize email.php settings" 176 | chmod +w $MISP_APP_CONFIG_PATH/email.php 177 | tee $MISP_APP_CONFIG_PATH/email.php > /dev/null < 'Smtp', 182 | 'from' => array('misp-dev@admin.test' => 'Misp DEV'), 183 | 'host' => '$SMTP_FQDN', 184 | 'port' => $SMTP_PORT, 185 | 'timeout' => 30, 186 | 'client' => null, 187 | 'log' => false, 188 | ); 189 | public \$smtp = array( 190 | 'transport' => 'Smtp', 191 | 'from' => array('misp-dev@admin.test' => 'Misp DEV'), 192 | 'host' => '$SMTP_FQDN', 193 | 'port' => $SMTP_PORT, 194 | 'timeout' => 30, 195 | 'client' => null, 196 | 'log' => false, 197 | ); 198 | public \$fast = array( 199 | 'from' => 'misp-dev@admin.test', 200 | 'sender' => null, 201 | 'to' => null, 202 | 'cc' => null, 203 | 'bcc' => null, 204 | 'replyTo' => null, 205 | 'readReceipt' => null, 206 | 'returnPath' => null, 207 | 'messageId' => true, 208 | 'subject' => null, 209 | 'message' => null, 210 | 'headers' => null, 211 | 'viewRender' => null, 212 | 'template' => false, 213 | 'layout' => false, 214 | 'viewVars' => null, 215 | 'attachments' => null, 216 | 'emailFormat' => null, 217 | 'transport' => 'Smtp', 218 | 'host' => '$SMTP_FQDN', 219 | 'port' => $SMTP_PORT, 220 | 'timeout' => 30, 221 | 'client' => null, 222 | 'log' => true, 223 | ); 224 | } 225 | EOT 226 | 227 | # Init files (shared with host) 228 | echo "... initialize app files" 229 | MISP_APP_FILES_PATH=/var/www/MISP/app/files 230 | if [ ! -f ${MISP_APP_FILES_PATH}/INIT ]; then 231 | cp -R ${MISP_APP_FILES_PATH}.dist/* ${MISP_APP_FILES_PATH} 232 | touch ${MISP_APP_FILES_PATH}/INIT 233 | fi 234 | } 235 | 236 | update_misp_data_files(){ 237 | # If $MISP_APP_FILES_PATH was not changed since the build, skip file updates there 238 | FILES_VERSION= 239 | MISP_APP_FILES_PATH=/var/www/MISP/app/files 240 | CORE_COMMIT=${CORE_COMMIT:-${CORE_TAG}} 241 | if [ -f ${MISP_APP_FILES_PATH}/VERSION ]; then 242 | FILES_VERSION=$(cat ${MISP_APP_FILES_PATH}/VERSION) 243 | echo "... found local files/VERSION:" $FILES_VERSION 244 | if [ "$FILES_VERSION" = "${CORE_COMMIT:-$(jq -r '"v\(.major).\(.minor).\(.hotfix)"' /var/www/MISP/VERSION.json)}" ]; then 245 | echo "... local files/ match distribution version, skipping file sync" 246 | return 0; 247 | fi 248 | fi 249 | for DIR in $(ls /var/www/MISP/app/files.dist); do 250 | if [ "$DIR" = "certs" ] || [ "$DIR" = "img" ] || [ "$DIR" == "taxonomies" ] || [ "$DIR" == "terms" ] || [ "$DIR" == "misp-objects" ] ; then 251 | echo "... rsync -azh \"/var/www/MISP/app/files.dist/$DIR\" \"/var/www/MISP/app/files/\"" 252 | rsync -azh "/var/www/MISP/app/files.dist/$DIR" "/var/www/MISP/app/files/" 253 | else 254 | echo "... rsync -azh --delete \"/var/www/MISP/app/files.dist/$DIR\" \"/var/www/MISP/app/files/\"" 255 | rsync -azh --delete "/var/www/MISP/app/files.dist/$DIR" "/var/www/MISP/app/files/" 256 | fi 257 | done 258 | } 259 | 260 | enforce_misp_data_permissions(){ 261 | # If $MISP_APP_FILES_PATH was not changed since the build, skip file updates there 262 | MISP_APP_FILES_PATH=/var/www/MISP/app/files 263 | CORE_COMMIT=${CORE_COMMIT:-${CORE_TAG}} 264 | if [ -f "${MISP_APP_FILES_PATH}/VERSION" ] && [ "$(cat ${MISP_APP_FILES_PATH}/VERSION)" = "${CORE_COMMIT:-$(jq -r '"v\(.major).\(.minor).\(.hotfix)"' /var/www/MISP/VERSION.json)}" ]; then 265 | echo "... local files/ match distribution version, skipping data permissions in files/" 266 | else 267 | echo "... chown -R www-data:www-data /var/www/MISP/app/tmp" && find /var/www/MISP/app/tmp \( ! -user www-data -or ! -group www-data \) -exec chown www-data:www-data {} + 268 | # Files are also executable and read only, because we have some rogue scripts like 'cake' and we can not do a full inventory 269 | echo "... chmod -R 0550 files /var/www/MISP/app/tmp" && find /var/www/MISP/app/tmp -not -perm 550 -type f -exec chmod 0550 {} + 270 | # Directories are also writable, because there seems to be a requirement to add new files every once in a while 271 | echo "... chmod -R 0770 directories /var/www/MISP/app/tmp" && find /var/www/MISP/app/tmp -not -perm 770 -type d -exec chmod 0770 {} + 272 | # We make 'files' and 'tmp' (logs) directories and files user and group writable (we removed the SGID bit) 273 | echo "... chmod -R u+w,g+w /var/www/MISP/app/tmp" && chmod -R u+w,g+w /var/www/MISP/app/tmp 274 | 275 | echo "... chown -R www-data:www-data /var/www/MISP/app/files" && find /var/www/MISP/app/files \( ! -user www-data -or ! -group www-data \) -exec chown www-data:www-data {} + 276 | # Files are also executable and read only, because we have some rogue scripts like 'cake' and we can not do a full inventory 277 | echo "... chmod -R 0550 files /var/www/MISP/app/files" && find /var/www/MISP/app/files -not -perm 550 -type f -exec chmod 0550 {} + 278 | # Directories are also writable, because there seems to be a requirement to add new files every once in a while 279 | echo "... chmod -R 0770 directories /var/www/MISP/app/files" && find /var/www/MISP/app/files -not -perm 770 -type d -exec chmod 0770 {} + 280 | # We make 'files' and 'tmp' (logs) directories and files user and group writable (we removed the SGID bit) 281 | echo "... chmod -R u+w,g+w /var/www/MISP/app/files" && chmod -R u+w,g+w /var/www/MISP/app/files 282 | fi 283 | 284 | echo "... chown -R www-data:www-data /var/www/MISP/app/Config" && find /var/www/MISP/app/Config \( ! -user www-data -or ! -group www-data \) -exec chown www-data:www-data {} + 285 | # Files are also executable and read only, because we have some rogue scripts like 'cake' and we can not do a full inventory 286 | echo "... chmod -R 0550 files /var/www/MISP/app/Config ..." && find /var/www/MISP/app/Config -not -perm 550 -type f -exec chmod 0550 {} + 287 | # Directories are also writable, because there seems to be a requirement to add new files every once in a while 288 | echo "... chmod -R 0770 directories /var/www/MISP/app/Config" && find /var/www/MISP/app/Config -not -perm 770 -type d -exec chmod 0770 {} + 289 | # We make configuration files read only 290 | echo "... chmod 600 /var/www/MISP/app/Config/{config,database,email}.php" && chmod 600 /var/www/MISP/app/Config/{bootstrap,config,database,email}.php 291 | } 292 | 293 | flip_nginx() { 294 | local live="$1"; 295 | local reload="$2"; 296 | 297 | if [[ "$live" = "true" ]]; then 298 | NGINX_DOC_ROOT=/var/www/MISP/app/webroot 299 | elif [[ -x /custom/files/var/www/html/index.php ]]; then 300 | NGINX_DOC_ROOT=/custom/files/var/www/html/ 301 | else 302 | NGINX_DOC_ROOT=/var/www/html/ 303 | fi 304 | 305 | # must be valid for all roots 306 | echo "... nginx docroot set to ${NGINX_DOC_ROOT}" 307 | sed -i "s|root.*var/www.*|root ${NGINX_DOC_ROOT};|" /etc/nginx/includes/misp 308 | 309 | if [[ "$reload" = "true" ]]; then 310 | echo "... nginx reloaded" 311 | nginx -s reload 312 | fi 313 | } 314 | 315 | init_nginx() { 316 | # Adjust timeouts 317 | echo "... adjusting 'fastcgi_read_timeout' to ${FASTCGI_READ_TIMEOUT}" 318 | sed -i "s/fastcgi_read_timeout .*;/fastcgi_read_timeout ${FASTCGI_READ_TIMEOUT};/" /etc/nginx/includes/misp 319 | echo "... adjusting 'fastcgi_send_timeout' to ${FASTCGI_SEND_TIMEOUT}" 320 | sed -i "s/fastcgi_send_timeout .*;/fastcgi_send_timeout ${FASTCGI_SEND_TIMEOUT};/" /etc/nginx/includes/misp 321 | echo "... adjusting 'fastcgi_connect_timeout' to ${FASTCGI_CONNECT_TIMEOUT}" 322 | sed -i "s/fastcgi_connect_timeout .*;/fastcgi_connect_timeout ${FASTCGI_CONNECT_TIMEOUT};/" /etc/nginx/includes/misp 323 | 324 | # Adjust maximum allowed size of the client request body 325 | echo "... adjusting 'client_max_body_size' to ${NGINX_CLIENT_MAX_BODY_SIZE}" 326 | sed -i "s/client_max_body_size .*;/client_max_body_size ${NGINX_CLIENT_MAX_BODY_SIZE};/" /etc/nginx/includes/misp 327 | 328 | # Adjust forwarding header settings (clean up first) 329 | sed -i '/real_ip_header/d' /etc/nginx/includes/misp 330 | sed -i '/real_ip_recursive/d' /etc/nginx/includes/misp 331 | sed -i '/set_real_ip_from/d' /etc/nginx/includes/misp 332 | if [[ "$NGINX_X_FORWARDED_FOR" = "true" ]]; then 333 | echo "... enabling X-Forwarded-For header" 334 | echo "... setting 'real_ip_header X-Forwarded-For'" 335 | echo "... setting 'real_ip_recursive on'" 336 | sed -i "/index index.php/a real_ip_header X-Forwarded-For;\nreal_ip_recursive on;" /etc/nginx/includes/misp 337 | if [[ ! -z "$NGINX_SET_REAL_IP_FROM" ]]; then 338 | SET_REAL_IP_FROM_PRINT=$(echo $NGINX_SET_REAL_IP_FROM | tr ',' '\n') 339 | for real_ip in ${SET_REAL_IP_FROM_PRINT[@]}; do 340 | echo "... setting 'set_real_ip_from ${real_ip}'" 341 | done 342 | SET_REAL_IP_FROM=$(echo $NGINX_SET_REAL_IP_FROM | tr ',' '\n' | while read line; do echo -n "set_real_ip_from ${line};\n"; done) 343 | SET_REAL_IP_FROM_ESCAPED=$(echo $SET_REAL_IP_FROM | sed '$!s/$/\\/' | sed 's/\\n$//') 344 | sed -i "/real_ip_recursive on/a $SET_REAL_IP_FROM_ESCAPED" /etc/nginx/includes/misp 345 | fi 346 | fi 347 | 348 | # Adjust Content-Security-Policy 349 | echo "... adjusting Content-Security-Policy" 350 | # Remove any existing CSP header 351 | sed -i '/add_header Content-Security-Policy/d' /etc/nginx/includes/misp 352 | 353 | if [[ -n "$CONTENT_SECURITY_POLICY" ]]; then 354 | # If $CONTENT_SECURITY_POLICY is set, add CSP header 355 | echo "... setting Content-Security-Policy to '$CONTENT_SECURITY_POLICY'" 356 | sed -i "/add_header X-Download-Options/a add_header Content-Security-Policy \"$CONTENT_SECURITY_POLICY\";" /etc/nginx/includes/misp 357 | else 358 | # Otherwise, do not add any CSP headers 359 | echo "... no Content-Security-Policy header will be set as CONTENT_SECURITY_POLICY is not defined" 360 | fi 361 | 362 | # Adjust X-Frame-Options 363 | echo "... adjusting X-Frame-Options" 364 | # Remove any existing X-Frame-Options header 365 | sed -i '/add_header X-Frame-Options/d' /etc/nginx/includes/misp 366 | 367 | if [[ -z "$X_FRAME_OPTIONS" ]]; then 368 | echo "... setting 'X-Frame-Options SAMEORIGIN'" 369 | sed -i "/add_header X-Download-Options/a add_header X-Frame-Options \"SAMEORIGIN\" always;" /etc/nginx/includes/misp 370 | else 371 | echo "... setting 'X-Frame-Options $X_FRAME_OPTIONS'" 372 | sed -i "/add_header X-Download-Options/a add_header X-Frame-Options \"$X_FRAME_OPTIONS\";" /etc/nginx/includes/misp 373 | fi 374 | 375 | # Adjust HTTP Strict Transport Security (HSTS) 376 | echo "... adjusting HTTP Strict Transport Security (HSTS)" 377 | # Remove any existing HSTS header 378 | sed -i '/add_header Strict-Transport-Security/d' /etc/nginx/includes/misp 379 | 380 | if [[ -n "$HSTS_MAX_AGE" ]]; then 381 | # If $HSTS_MAX_AGE is defined, add the HSTS header 382 | echo "... setting HSTS to 'max-age=$HSTS_MAX_AGE; includeSubdomains'" 383 | sed -i "/add_header X-Download-Options/a add_header Strict-Transport-Security \"max-age=$HSTS_MAX_AGE; includeSubdomains\";" /etc/nginx/includes/misp 384 | else 385 | # Otherwise, do nothing, keeping without the HSTS header 386 | echo "... no HSTS header will be set as HSTS_MAX_AGE is not defined" 387 | fi 388 | 389 | # Testing for files also test for links, and generalize better to mounted files 390 | if [[ ! -f "/etc/nginx/sites-enabled/misp80" ]]; then 391 | echo "... enabling port 80 redirect" 392 | ln -s /etc/nginx/sites-available/misp80 /etc/nginx/sites-enabled/misp80 393 | else 394 | echo "... port 80 already enabled" 395 | fi 396 | if [[ "$DISABLE_IPV6" = "true" ]]; then 397 | echo "... disabling IPv6 on port 80" 398 | sed -i "s/[^#] listen \[/ # listen \[/" /etc/nginx/sites-enabled/misp80 399 | else 400 | echo "... enabling IPv6 on port 80" 401 | sed -i "s/# listen \[/listen \[/" /etc/nginx/sites-enabled/misp80 402 | fi 403 | if [[ "$DISABLE_SSL_REDIRECT" = "true" ]]; then 404 | echo "... disabling SSL redirect" 405 | sed -i "s/[^#] return / # return /" /etc/nginx/sites-enabled/misp80 406 | sed -i "s/# include /include /" /etc/nginx/sites-enabled/misp80 407 | else 408 | echo "... enabling SSL redirect" 409 | sed -i "s/[^#] include / # include /" /etc/nginx/sites-enabled/misp80 410 | sed -i "s/# return /return /" /etc/nginx/sites-enabled/misp80 411 | fi 412 | 413 | # Testing for files also test for links, and generalize better to mounted files 414 | if [[ ! -f "/etc/nginx/sites-enabled/misp443" ]]; then 415 | echo "... enabling port 443" 416 | ln -s /etc/nginx/sites-available/misp443 /etc/nginx/sites-enabled/misp443 417 | else 418 | echo "... port 443 already enabled" 419 | fi 420 | if [[ "$DISABLE_IPV6" = "true" ]]; then 421 | echo "... disabling IPv6 on port 443" 422 | sed -i "s/[^#] listen \[/ # listen \[/" /etc/nginx/sites-enabled/misp443 423 | else 424 | echo "... enabling IPv6 on port 443" 425 | sed -i "s/# listen \[/listen \[/" /etc/nginx/sites-enabled/misp443 426 | fi 427 | 428 | if [[ ! -f /etc/nginx/certs/cert.pem || ! -f /etc/nginx/certs/key.pem ]]; then 429 | echo "... generating new self-signed TLS certificate" 430 | openssl req -x509 -subj '/CN=localhost' -nodes -newkey rsa:4096 -keyout /etc/nginx/certs/key.pem -out /etc/nginx/certs/cert.pem -days 365 \ 431 | -addext "subjectAltName = DNS:localhost, IP:127.0.0.1, IP:::1" 432 | else 433 | echo "... TLS certificates found" 434 | fi 435 | 436 | if [[ "$FASTCGI_STATUS_LISTEN" != "" ]]; then 437 | echo "... enabling php-fpm status page" 438 | ln -s /etc/nginx/sites-available/php-fpm-status /etc/nginx/sites-enabled/php-fpm-status 439 | sed -i -E "s/ listen [^;]+/ listen $FASTCGI_STATUS_LISTEN/" /etc/nginx/sites-enabled/php-fpm-status 440 | elif [[ -f /etc/nginx/sites-enabled/php-fpm-status ]]; then 441 | echo "... disabling php-fpm status page" 442 | rm /etc/nginx/sites-enabled/php-fpm-status 443 | fi 444 | 445 | flip_nginx false false 446 | } 447 | 448 | # Hinders further execution when sourced from other scripts 449 | if [[ "${BASH_SOURCE[0]}" != "$0" ]]; then 450 | return 451 | fi 452 | 453 | # Initialize MySQL 454 | echo "INIT | Initialize MySQL ..." && init_mysql 455 | 456 | # Initialize NGINX 457 | echo "INIT | Initialize NGINX ..." && init_nginx 458 | nginx -g 'daemon off;' & master_pid=$! 459 | 460 | # Initialize MISP 461 | echo "INIT | Initialize MISP files and configurations ..." && init_misp_data_files 462 | echo "INIT | Update MISP app/files directory ..." && update_misp_data_files 463 | echo "INIT | Enforce MISP permissions ..." && enforce_misp_data_permissions 464 | echo "INIT | Flip NGINX live ..." && flip_nginx true true 465 | 466 | # Run configure MISP script 467 | echo "INIT | Configure MISP installation ..." 468 | /configure_misp.sh 469 | 470 | if [[ -x /custom/files/customize_misp.sh ]]; then 471 | echo "INIT | Customize MISP installation ..." 472 | /custom/files/customize_misp.sh 473 | fi 474 | 475 | # Restart PHP workers 476 | echo "INIT | Configure PHP ..." 477 | supervisorctl restart php-fpm 478 | echo "INIT | Done ..." 479 | 480 | # Wait for it 481 | wait "$master_pid" 482 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MISP Docker images 2 | 3 | [![Build Status](https://img.shields.io/github/actions/workflow/status/MISP/misp-docker/release-latest.yml)](https://github.com/orgs/MISP/packages) 4 | [![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/MISP/Docker) 5 | 6 | A production ready Docker MISP image (formerly hosted at , now deprecated) loosely based on CoolAcid and DSCO builds, with nearly all logic rewritten and verified for correctness and portability. 7 | 8 | Notable features: 9 | 10 | - MISP and MISP modules are split into two different Docker images, `misp-core` and `misp-modules` 11 | - Optional [MISP-Guard](https://github.com/MISP/misp-guard) container to filter traffic and enforce sharing policies via mitmproxy. 12 | - Docker images are pushed regularly, no build required 13 | - Lightweight Docker images by using multiple build stages and a slim parent image 14 | - Rely on off the shelf Docker images for Exim4, Redis, and MariaDB 15 | - Scheduled tasks run updates, pushes, and pulls 16 | - Fix supervisord process control (processes are correctly terminated upon reload) 17 | - Fix schema update by making it completely offline (no user interaction required) 18 | - Fix enforcement of permissions 19 | - Fix MISP modules loading of faup library 20 | - Fix MISP modules loading of gl library 21 | - Authentication using LDAP or OIDC 22 | - Add support for new background job [system](https://github.com/MISP/MISP/blob/2.4/docs/background-jobs-migration-guide.md) 23 | - Add support for building specific MISP and MISP-modules commits 24 | - Add automatic configuration of syncservers (see `configure_misp.sh`) 25 | - Add automatic configuration of authentication keys (see `configure_misp.sh`) 26 | - Add direct push of docker images to GitHub Packages 27 | - Consolidated `docker-compose.yml` file 28 | - Workaround VirtioFS bug when running Docker Desktop for Mac 29 | - ... and many others 30 | 31 | The underlying spirit of this project is to allow "repeatable deployments", and all pull requests in this direction will be merged post-haste. 32 | 33 | ## Getting Started 34 | 35 | ### Prerequisites 36 | 37 | Make sure the following tools are installed and up to date before you begin. Older 38 | releases are a common source of build and runtime issues. 39 | 40 | - Docker Engine **25+** or Podman **4.9+** 41 | - Docker Compose plugin **2.17+** (or Podman Compose when using Podman) 42 | - Access to pull container images from `ghcr.io` 43 | - Access to [Docker Hub](https://hub.docker.com) for pulling dependencies and base images 44 | 45 | You can confirm the installed versions with: 46 | 47 | ```bash 48 | docker -v 49 | docker compose version 50 | ``` 51 | 52 | ### Prepare the environment 53 | 54 | 1. Copy the `template.env` file to `.env` in the project root. 55 | 2. Customize `.env` according to your requirements (optional but recommended). 56 | 57 | ### Run 58 | 59 | - `docker compose pull` if you want to use pre-built images or `docker compose build` if you want to build your own (see the [Troubleshooting](#troubleshooting) section in case of errors) 60 | - `docker compose up` 61 | - Add `-d` to run the services in the background 62 | - Login to `https://localhost` 63 | - User: `admin@admin.test` 64 | - Password: `admin` 65 | 66 | Keeping the image up-to-date with upstream should be as simple as running `docker compose pull`. 67 | 68 | ### Configuration 69 | 70 | The `docker-compose.yml` file allows further configuration settings: 71 | 72 | ``` 73 | "MYSQL_HOST=db" 74 | "MYSQL_USER=misp" 75 | "MYSQL_PASSWORD=example" # NOTE: This should be AlphaNum with no Special Chars. Otherwise, edit config files after first run. 76 | "MYSQL_DATABASE=misp" 77 | "MISP_MODULES_FQDN=http://misp-modules" # Set the MISP Modules FQDN, used for Enrichment_services_url/Import_services_url/Export_services_url 78 | "SMTP_PORT=25" # Override the outbound SMTP port if your mail relay does not listen on 25 79 | "WORKERS=1" # Legacy variable controlling the number of parallel workers (use variables below instead) 80 | "NUM_WORKERS_DEFAULT=5" # To set the number of default workers 81 | "NUM_WORKERS_PRIO=5" # To set the number of prio workers 82 | "NUM_WORKERS_EMAIL=5" # To set the number of email workers 83 | "NUM_WORKERS_UPDATE=1" # To set the number of update workers 84 | "NUM_WORKERS_CACHE=5" # To set the number of cache workers 85 | ``` 86 | 87 | New options are added on a regular basis. 88 | 89 | #### Environment variable behaviour 90 | 91 | Set environment variables in .env to configure settings instead of in docker-compose.yml where possible. Setting the variables in .env will allow you to pull updates from Github without issues caused by a modified docker-compose.yml file, should there be an update for it. 92 | 93 | Environment variable driven settings are enforced every time the misp-core container starts. This means that if you change the config.php file or database for a setting that has a set environment variable, it will be changed to the environment variable value upon next container start. Empty environment variables may have a safe default which is enforced instead. 94 | 95 | If you push a change to add or remove an environment variable, please look in "core/files/etc/misp-docker/" for json files with "envars" in the name and adjust there. 96 | 97 | #### Unset safe default settings behaviour 98 | 99 | The misp-core container has definitions for minimum safe default settings which are set if needed each time the container starts. They will only be set if there is no existing entry in the config.php file or database for these settings. If you specify a custom value for any of these settings it will be respected. See the definitions of these in "core/files/etc/misp-docker" where the filenames contain the word "defaults". 100 | 101 | #### Storing system settings in the DB 102 | 103 | This container includes the "ENABLE_DB_SETTINGS" environment variable, which can be used to set "MISP.system_setting_db" to true or false. This changes the behaviour of where MISP chooses to store operator made settings changes; in config.php or in the system_settings database table. By default this is set to false. 104 | 105 | If a setting is not defined in the DB, but is defined in config.php, it will be read out of config.php and used. This can sometimes lead to operator confusion, so please check both locations for values when troubleshooting. 106 | 107 | If you change this setting from false to true, settings are not migrated from config.php to the database, but rather the above behaviour is relied upon. 108 | 109 | While storing system settings in the DB works as expected most of the time, you may come across some instances where a particular setting MUST be set in the config.php file. We have tried to side-step this issue by prepopulating the config.php file with all of these settings, but there could be more. If you encounter any issues like this, please raise an issue, and try configuring the setting in the config.php file instead. 110 | 111 | #### Overriding environment variable and unset safe default settings behaviours 112 | 113 | If you are trying to accomplish something and the above behaviours get in the way, please let us know as this is not intended. 114 | 115 | To override these behaviours edit the docker-compose.yml file's misp-core volume definitions to enable the "customize_misp.sh" behaviour (see the bottom of the Production section for details). The "customize_misp.sh" script triggers after the above behaviours complete and is an appropriate place to override a setting. It is suggested that you use the "/var/www/MISP/app/cake Admin setSetting" command to override a setting, as this tool is config.php file and database setting aware. 116 | 117 | #### Adding a new setting and unsure what files to edit? 118 | 119 | If it is just a default setting that is meant to be set if not already set by the user, add it in one of the `*.default.json` files. 120 | If it is a setting controlled by an environment variable which is meant to override whatever is set, add it in one of the `*.envars.json` files (note that you can still specify a default value). 121 | 122 | ### MISP-Guard (optional) 123 | 124 | [MISP-Guard](https://github.com/MISP/misp-guard) is a mitmproxy add-on designed to apply configurable filters that prevent the unintentional leakage of sensitive threat intelligence data while facilitating controlled information sharing. 125 | 126 | It is disabled by default, but can be enabled using compose profiles. 127 | 128 | #### Enabling 129 | 130 | 1. Enable the profile in your `.env` file: 131 | 132 | ```bash 133 | COMPOSE_PROFILES=misp-guard 134 | ``` 135 | 136 | 2. Ensure `misp-core` is configured to use a proxy: 137 | 138 | ```bash 139 | PROXY_ENABLE=true 140 | PROXY_HOST=misp-guard 141 | # this must match GUARD_PORT (DEFAULT=8888) 142 | PROXY_PORT=8888 143 | ``` 144 | 145 | #### Configuration 146 | 147 | - Rules are defined in `guard/config.json`. 148 | - The container automatically replaces the `misp-core` IP at runtime using `entrypoint.sh`. 149 | 150 | The following format is required to target the misp-core, the IP is replaced with the misp-core container's IP at runtime. 151 | 152 | ```json 153 | { 154 | "instances": { 155 | "misp_container": { 156 | "ip": "placeholder" 157 | } 158 | } 159 | } 160 | ``` 161 | 162 | - After making changes to `guard/config.json` restart the container to apply the changes: 163 | 164 | ```bash 165 | docker compose restart misp-guard 166 | ``` 167 | 168 | #### Environment Variables 169 | 170 | ```bash 171 | # Port for misp-guard to listen on (must match PROXY_PORT) 172 | # Default: 8888 173 | GUARD_PORT=8888 174 | 175 | # optional: mitmdump misp-guard runtime arguments (space separated) 176 | GUARD_ARGS=--ssl-insecure -v 177 | ``` 178 | 179 | ### Authentication 180 | 181 | #### LDAP Authentication 182 | 183 | You can configure LDAP authentication in MISP using 2 methods: 184 | 185 | - native plugin: LdapAuth () 186 | - previous approach with ApacheSecureAuth (). 187 | 188 | LdapAuth is recommended over ApacheSecureAuth because it doesn't require rproxy apache with the ldap module. 189 | 190 | #### OIDC Authentication 191 | 192 | OIDC Auth is implemented through the MISP OidcAuth plugin. 193 | 194 | For example configuration using KeyCloak, see [MISP Keycloak 26.1.x Basic Integration Guide](docs/keycloak-integration-guide.md) 195 | 196 | For Okta, create a new application integration: 197 | - Applications -> Applications -> Create App Integration 198 | - Select Sign-in method "OIDC - OpenID Connect" and Application type "Web Application" 199 | - In Client Authentication, select "Client secret" 200 | - Set the Sign-in redirect URI to: "https:///users/login" 201 | - Under the Sign-in tab, add a group claim called "roles" and an appropriate filter 202 | - In MISP docker `.env` file, set the following variables: 203 | ``` 204 | OIDC_ENABLE=true 205 | OIDC_PROVIDER_URL=https:///.well-known/openid-configuration 206 | OIDC_ISSUER=https:// 207 | OIDC_CLIENT_ID=[client_id] 208 | OIDC_CLIENT_SECRET=[client_secret] 209 | OIDC_ROLES_PROPERTY="roles" 210 | OIDC_ROLES_MAPPING="{\"Okta group - MISP Admin\": 1}" # 211 | OIDC_DEFAULT_ORG="[Your default org in MISP]" 212 | #OIDC_LOGOUT_URL= 213 | OIDC_SCOPES="[\"profile\", \"email\", \"groups\"]" 214 | OIDC_MIXEDAUTH=true # (Set this to false if you want to disable password login, make sure OIDC is working first) 215 | OIDC_CODE_CHALLENGE_METHOD=S256 216 | OIDC_AUTH_METHOD="client_secret_post" 217 | OIDC_REDIRECT_URI="https:///users/login" # (same value set in Okta) 218 | ``` 219 | Valid options for OIDC_AUTH_METHOD are: 220 | - client_secret_post: tested 221 | - client_secret_basic: the default if variable is not set, but seems broken with Okta. It will return the following error: _"Error 'invalid_request' received from IdP: Cannot supply multiple client credentials"_. 222 | - client_secret_jwt: *not tested* 223 | - private_key_jwt: *not tested* 224 | 225 | 226 | ### Production 227 | 228 | - It is recommended to specify the build you want run by editing `docker-compose.yml` (see here for the list of available tags ) 229 | - Directory volume mount SSL Certs `./ssl`: `/etc/ssl/certs` 230 | - Certificate File: `cert.pem` 231 | - Certificate Key File: `key.pem` 232 | - CA File for Cert Authentication (optional) `ca.pem` 233 | - Additional directory volume mounts: 234 | - `./configs`: `/var/www/MISP/app/Config/` 235 | - `./logs`: `/var/www/MISP/app/tmp/logs/` 236 | - `./files`: `/var/www/MISP/app/files/` 237 | - `./gnupg`: `/var/www/MISP/.gnupg/` 238 | - If you need to automatically run additional steps each time the container starts, create a new file `files/customize_misp.sh`, and replace the variable `${CUSTOM_PATH}` inside `docker-compose.yml` with its parent path. 239 | - If you are interested in running streamlined versions of the images (fewer dependencies, easier approval from compliance), you might want to use the `latest-slim` tag. Just adjust the `docker-compose.yml` file, and run again `docker compose pull` and `docker compose up`. 240 | 241 | ### Build Options 242 | 243 | This project supports multiple build methods to suit different needs. 244 | 245 | #### Using Docker Compose (Standard Method) 246 | 247 | For most users, the standard Docker Compose build is recommended: 248 | ```bash 249 | docker compose build 250 | ``` 251 | 252 | #### Using Docker Buildx Bake (Advanced) 253 | 254 | Docker Buildx bake provides advanced build capabilities including multi-platform builds and parallel building of multiple targets. This method uses the `docker-bake.hcl` configuration file. 255 | 256 | **Prerequisites:** 257 | - Docker Buildx plugin installed and enabled 258 | - `template.env` file in the project root 259 | 260 | **Build full-featured images:** 261 | ```bash 262 | export NAMESPACE=local 263 | export COMMIT_HASH=`git rev-parse --short HEAD` 264 | sed -e '/^[[:space:]]*$/d' -e '/[#@]/d' -e 's/\"//g' -e 's/\(^[^=]*\)=\(.*\)/\1="\2"/' template.env > env.hcl 265 | docker buildx bake -f docker-bake.hcl -f env.hcl --provenance false debian 266 | ``` 267 | 268 | This builds `misp-core`, `misp-modules`, and `misp-guard` with all features included. 269 | 270 | **Build slim images:** 271 | ```bash 272 | export NAMESPACE=local 273 | export COMMIT_HASH=`git rev-parse --short HEAD` 274 | sed -e '/^[[:space:]]*$/d' -e '/[#@]/d' -e 's/\"//g' -e 's/\(^[^=]*\)=\(.*\)/\1="\2"/' template.env > env.hcl 275 | docker buildx bake -f docker-bake.hcl -f env.hcl --provenance false debian-slim 276 | ``` 277 | 278 | This builds lightweight versions of `misp-core-slim`, `misp-modules-slim`, and `misp-guard` with reduced dependencies. 279 | 280 | **Available bake targets:** 281 | - `standard` - Full-featured images (misp-core, misp-modules, misp-guard) 282 | - `slim` - Lightweight images (misp-core-slim, misp-modules-slim, misp-guard) 283 | - `default` - Builds all variants (both standard and slim) 284 | 285 | **Note:** The (GNU) `sed` command converts `template.env` to `env.hcl` format by removing empty lines, comments, and properly formatting variables for the bake file (on OSX you should install `gsed`). 286 | 287 | **After building with buildx bake:** 288 | 289 | You can still use Docker Compose to run the services: 290 | ```bash 291 | docker compose up 292 | ``` 293 | #### Using slow disks as volume mounts 294 | 295 | Using a slow disk as the mounted volume or a volume with high latency like NFS, EFS or S3 might significantly increase the startup time and downgrade the performance of the service. To address this we will mount the bare minimum that needs to be persisted. 296 | 297 | - Remove the `/var/www/MISP/app/files/` volume mount. 298 | - Add the following volume mounts instead: 299 | - `./img/`: `/var/www/MISP/app/files/img` 300 | - `./terms`: `/var/www/MISP/app/files/terms` 301 | - `./attachments`: `/var/www/MISP/app/attachments` 302 | - Set the environment variable `ATTACHMENTS_DIR` to the above folder location (it is important that it doesn't replace the `/var/www/MISP/app/files/` folder). 303 | 304 | ### SELinux 305 | 306 | On systems using SELinux, volume binds are not given write permissions by default. Using the tag `:Z` or `:z` at the end of a volume bind files grants write permission through SELinux. 307 | 308 | - The `Z` option tells Docker to label the content with a private unshared label. 309 | - The `z` option tells Docker that two containers share the volume content. 310 | 311 | ## Installing custom root CA certificates 312 | 313 | Custom root CA certificates can be mounted under `/usr/local/share/ca-certificates` and will be installed during the `misp-core` container start. 314 | 315 | **Note:** It is important to have the .crt extension on the file, otherwise it will not be processed. 316 | 317 | ```yaml 318 | misp-core: 319 | # ... 320 | volumes: 321 | - "./configs/:/var/www/MISP/app/Config/" 322 | - "./logs/:/var/www/MISP/app/tmp/logs/" 323 | - "./files/:/var/www/MISP/app/files/" 324 | - "./ssl/:/etc/nginx/certs/" 325 | - "./gnupg/:/var/www/MISP/.gnupg/" 326 | # customize by replacing ${CUSTOM_PATH} with a path containing 'files/customize_misp.sh' 327 | # - "${CUSTOM_PATH}/:/custom/" 328 | # mount custom ca root certificates 329 | - "./rootca.pem:/usr/local/share/ca-certificates/rootca.crt" 330 | ``` 331 | 332 | ## Database Management 333 | 334 | It is possible to backup and restore the underlying database using volume archiving. 335 | The process is *NOT* battle-tested, so it is *NOT* to be followed uncritically. 336 | 337 | ### Backup 338 | 339 | 1. Stop the MISP containers: 340 | 341 | ```bash 342 | docker compose down 343 | ``` 344 | 345 | 2. Create an archive of the `misp-docker_mysql_data` volume using `tar`: 346 | 347 | ```bash 348 | tar -cvzf /root/misp_mysql_backup.tar.gz /var/lib/docker/volumes/misp-docker_mysql_data/ 349 | ``` 350 | 351 | 3. Start the MISP containers: 352 | 353 | ```bash 354 | docker compose up 355 | ``` 356 | 357 | ### Restore 358 | 359 | 1. Stop the MISP containers: 360 | 361 | ```bash 362 | docker compose down 363 | ``` 364 | 365 | 2. Unpack the backup and overwrite existing data by using the `--overwrite` option to replace existing files: 366 | 367 | ```bash 368 | tar -xvzf /path_to_backup/misp_mysql_backup.tar.gz -C /var/lib/docker/volumes/misp-docker_mysql_data/ --overwrite 369 | ``` 370 | 371 | 3. Start the MISP containers: 372 | 373 | ```bash 374 | docker compose up 375 | ``` 376 | 377 | ## Troubleshooting 378 | 379 | - Make sure you run a fairly recent version of Docker and Docker Compose (if in doubt, update following the steps outlined in ) 380 | - Make sure you are not running an old image or container; when in doubt run `docker system prune --volumes` and clone this repository into an empty directory 381 | - If you receive an error that the 'start_interval' does not match any of the regexes, update Docker following the steps outlined in ) 382 | - See below under **The image build fails or the image builds, but the container fails to start. Now what?** 383 | 384 | ## Versioning 385 | 386 | A GitHub Action builds `misp-core`, `misp-modules`, and `misp-guard` images automatically and pushes them to the [GitHub Package registry](https://github.com/orgs/MISP/packages). We do not use tags inside the repository; instead we tag images as they are pushed to the registry. For each build, `misp-core`, `misp-modules`, `misp-guard` images are tagged as follows: 387 | 388 | - `misp-core:${commit-sha1}[0:7]`, `misp-modules:${commit-sha1}[0:7]`, and `misp-guard:${commit-sha1}[0:7]` where `${commit-sha1}` is the commit hash triggering the build 389 | - `misp-core:latest`, `misp-modules:latest`, and `misp-guard:latest` in order to track the latest builds available 390 | - `misp-core:${CORE_TAG}`, `misp-modules:${MODULES_TAG}`, and `misp-guard:${GUARD_TAG}` reflecting the underlying versions as specified inside the `template.env` file at build time. 391 | 392 | ## Podman (experimental) 393 | 394 | It is possible to run the image using `podman-systemd` rather than `docker` to: 395 | 396 | - Run containers in **rootless** mode 397 | - Manage containers with **systemd** 398 | - Write container descriptions in an **ignition** file and deploy them (not covered in this documentation) 399 | 400 | Note that this is **experimental** and it is **NOT SUPPORTED** (issues will be automatically closed). 401 | 402 | ### Configuration 403 | 404 | Copy the following directories and files: 405 | 406 | - Content of `experimental/podman-systemd` to `$USER/.config/containers/systemd/` 407 | - `template.vars` to `$USER/.config/containers/systemd/misp.env` 408 | - Create `misp-modules` folders 409 | 410 | ```bash 411 | mkdir -p $HOME/.config/misp_conf/custom/{action_mod,expansion,export_mod,import_mod} 412 | ``` 413 | 414 | Edit `misp.env`, and initialize the following MySQL settings: 415 | 416 | ```bash 417 | MYSQL_HOST= 418 | MYSQL_USER= 419 | MYSQL_PASSWORD= 420 | MYSQL_ROOT_PASSWORD= 421 | MYSQL_DATABASE= 422 | ``` 423 | 424 | Set the Redis password: 425 | 426 | ```bash 427 | REDIS_PASSWORD= 428 | ``` 429 | 430 | Enable passwordless Redis connection (defaults to false for security): 431 | 432 | ```bash 433 | ENABLE_REDIS_EMPTY_PASSWORD=false 434 | ``` 435 | 436 | Set the base URL: 437 | 438 | ```bash 439 | BASE_URL=https://:10443 440 | ``` 441 | 442 | ### Run 443 | 444 | Reload systemd user daemon: 445 | 446 | ```bash 447 | systemctl --user daemon-reload 448 | ``` 449 | 450 | Start services: 451 | 452 | ```bash 453 | systemctl --user start misp-mail.service 454 | systemctl --user start misp-db.service 455 | systemctl --user start misp-redis.service 456 | systemctl --user start misp-core.service 457 | systemctl --user start misp-modules.service 458 | ``` 459 | 460 | Wait a bit and check your service at `https://:10443`. 461 | If everything checks out, you can make services persistent across reboots and logouts: 462 | 463 | ```bash 464 | sudo loginctl enable-linger $USER 465 | ``` 466 | 467 | You can even set podman to check for new container versions by activating the specific timer `podman-auto-update.timer`: 468 | 469 | ```bash 470 | systemctl --user enable podman-auto-update.timer --now 471 | ``` 472 | 473 | # The image build fails or the image builds, but the container fails to start. Now what? 474 | 475 | If your image build fails or the build completes successfully but the container fails to start, you can get help by creating a new [issue](https://github.com/MISP/misp-docker/issues). To receive the most effective support, please include the conditions under which you attempted to build the images, along with relevant debug output. 476 | 477 | ## Provide your build environment details 478 | 479 | Be sure to include the versions of your build environment, such as Docker (or Podman), Docker Compose (or Podman Compose), Python, your operating system, and whether you are building as root or a non-root user. 480 | 481 | For **Docker**, run: 482 | 483 | ``` 484 | python3 -V 485 | docker -v 486 | docker compose version 487 | ``` 488 | 489 | For **Podman**, run: 490 | 491 | ``` 492 | python3 -V 493 | podman -v 494 | podman compose version 495 | ``` 496 | 497 | ## Always start from a clean environment 498 | 499 | Build errors can occur if incomplete layers remain from previous builds. To ensure a clean environment, stop all running containers and remove old images and volumes. 500 | 501 | With **Docker**: 502 | 503 | ``` 504 | docker compose down 505 | docker system prune 506 | docker image rm ghcr.io/misp/misp-docker/misp-core 507 | docker image rm ghcr.io/misp/misp-docker/misp-modules 508 | docker image rm ghcr.io/misp/misp-docker/misp-guard 509 | ``` 510 | 511 | With **Podman**: 512 | 513 | ``` 514 | podman compose down 515 | podman system prune 516 | podman image rm ghcr.io/misp/misp-docker/misp-core 517 | podman image rm ghcr.io/misp/misp-docker/misp-modules 518 | podman image rm ghcr.io/misp/misp-docker/misp-guard 519 | ``` 520 | 521 | You can also use the `--no-cache` option during the build to ignore cached layers. 522 | 523 | ## Log the build output 524 | 525 | After cleaning your environment, use verbose logging to capture detailed output from the build process. Logging to a file is recommended for troubleshooting and **when requesting support**. 526 | 527 | For **Docker**: 528 | 529 | ``` 530 | docker compose --verbose build --no-cache | tee build.log 531 | ``` 532 | 533 | For **Podman**: 534 | 535 | ``` 536 | PODMAN_COMPOSE_VERBOSE=1 podman compose build --no-cache | tee build.log 537 | ``` 538 | 539 | ## Bringing it all together 540 | 541 | You can combine the above commands to fully reset and rebuild the images in one step. 542 | 543 | For **Docker**: 544 | 545 | ``` 546 | docker system prune ; docker image rm ghcr.io/misp/misp-docker/misp-core ; docker image rm ghcr.io/misp/misp-docker/misp-modules ; docker image rm ghcr.io/misp/misp-docker/misp-guard ; rm -f build.log ; docker compose --verbose build --no-cache | tee build.log 547 | ``` 548 | 549 | For **Podman**: 550 | 551 | ``` 552 | podman system prune ; podman image rm ghcr.io/misp/misp-docker/misp-core ; podman image rm ghcr.io/misp/misp-docker/misp-modules ; podman image rm ghcr.io/misp/misp-docker/misp-guard ; rm -f build.log ; PODMAN_COMPOSE_VERBOSE=1 podman compose build --no-cache | tee build.log 553 | ``` 554 | 555 | This ensures you are building from a clean state and not using remnants from previous builds. 556 | 557 | ## Debugging the Dockerfile 558 | 559 | With your build log, you can identify where the build fails. To pinpoint the exact step, add debug lines to the Dockerfile. Use unique markers to make them easy to find in the log: 560 | 561 | ``` 562 | RUN echo "____MYDEBUG___1" 563 | RUN echo "____MYDEBUG___2" 564 | RUN echo "____MYDEBUG___3" 565 | ``` 566 | 567 | ### Print variable values 568 | 569 | Many build errors are related to variables not being set or imported correctly. To debug, print their values: 570 | 571 | ``` 572 | RUN echo "____MYDEBUG___ CORE_TAG: ${CORE_TAG}" 573 | ``` 574 | 575 | Example output: 576 | 577 | ``` 578 | [4/5] STEP 19/20: RUN echo "____MYDEBUG___ CORE_TAG: ${CORE_TAG}" 579 | ____MYDEBUG___ CORE_TAG: v2.5.16 580 | --> 798999451f75 581 | ``` 582 | 583 | ### Print variables inside shell blocks 584 | 585 | For shell blocks in the Dockerfile, insert `echo` statements to print variable values: 586 | 587 | ``` 588 | RUN <<-EOF 589 | for mod in "$@"; do 590 | mod_version_var=$(echo "PYPI_${mod}_VERSION" | tr '[:lower:]' '[:upper:]' | tr '-' '_') 591 | mod_version=$(eval "echo \"\$$mod_version_var\"") 592 | echo "____MYDEBUG___ mod mod_version: ${mod}${mod_version}" 593 | # ... rest of the code ... 594 | done 595 | EOF 596 | ``` 597 | 598 | ### Variables not expanding 599 | 600 | Older versions (pre version 5) of Podman may not expand variables correctly inside shell blocks. If you encounter this, ensure you are using the correct shell syntax. For Podman, replace: 601 | 602 | ``` 603 | RUN <<-EOF 604 | ``` 605 | 606 | with (also notice the **'** quotes) 607 | 608 | ``` 609 | RUN bash <<-'EOF' 610 | ``` 611 | 612 | to ensure variables are expanded as expected. For reference, this problem first occurred after successfully building an image but getting a `/usr/local/bin/supervisord: No such file or directory` error after starting the container. See [265](https://github.com/MISP/misp-docker/issues/265) and [273](https://github.com/MISP/misp-docker/pull/273) for more details. 613 | 614 | By following these steps, you can efficiently troubleshoot and resolve build issues. If problems persist, include your build log and environment details when opening an issue for assistance. 615 | -------------------------------------------------------------------------------- /core/files/configure_misp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source /rest_client.sh 4 | source /utilities.sh 5 | 6 | # We now use envsubst for safe variable substitution with pseudo-json objects for env var enforcement 7 | # envsubst won't evaluate anything like $() or conditional variable expansion so lets do that here 8 | export PYTHON_BIN="$(which python3)" 9 | export GPG_BINARY="$(which gpg)" 10 | export SETTING_CONTACT="${MISP_CONTACT}" 11 | export SETTING_EMAIL="${MISP_EMAIL}" 12 | 13 | init_minimum_config() { 14 | # Temporarily disable DB to apply config file settings, reenable after if needed 15 | sudo -u www-data /var/www/MISP/app/Console/cake Admin setSetting -q "MISP.system_setting_db" false 16 | init_settings "minimum_config" 17 | } 18 | 19 | init_configuration() { 20 | init_settings "db_enable" 21 | init_settings "initialisation" 22 | } 23 | 24 | init_workers() { 25 | echo "... starting background workers" 26 | stdbuf -oL supervisorctl start misp-workers:* 27 | } 28 | 29 | configure_gnupg() { 30 | if [ "$AUTOCONF_GPG" != "true" ]; then 31 | echo "... GPG auto configuration disabled" 32 | return 33 | fi 34 | 35 | export GPG_DIR=/var/www/MISP/.gnupg 36 | GPG_ASC=/var/www/MISP/app/webroot/gpg.asc 37 | GPG_TMP=/tmp/gpg.tmp 38 | 39 | if [ ! -f "${GPG_DIR}/trustdb.gpg" ]; then 40 | echo "... generating new GPG key in ${GPG_DIR}" 41 | cat >${GPG_TMP} < ${GPG_ASC} 67 | else 68 | echo "... found exported key ${GPG_ASC}" 69 | fi 70 | 71 | init_settings "gpg" 72 | } 73 | 74 | set_up_oidc() { 75 | if [[ "$OIDC_ENABLE" == "true" ]]; then 76 | if [[ -z "$OIDC_ROLES_MAPPING" ]]; then 77 | OIDC_ROLES_MAPPING="\"\"" 78 | fi 79 | 80 | # Check required variables 81 | # OIDC_ISSUER may be empty 82 | check_env_vars OIDC_PROVIDER_URL OIDC_CLIENT_ID OIDC_ROLES_PROPERTY OIDC_ROLES_MAPPING OIDC_DEFAULT_ORG 83 | 84 | # Configure OIDC in MISP 85 | sudo -u www-data php /var/www/MISP/tests/modify_config.php modify "{ 86 | \"Security\": { 87 | \"auth\": [\"OidcAuth.Oidc\"] 88 | } 89 | }" > /dev/null 90 | 91 | # Set OIDC authentication details in MISP 92 | sudo -u www-data php /var/www/MISP/tests/modify_config.php modify "{ 93 | \"OidcAuth\": { 94 | \"provider_url\": \"${OIDC_PROVIDER_URL}\", 95 | ${OIDC_ISSUER:+\"issuer\": \"${OIDC_ISSUER}\",} 96 | \"client_id\": \"${OIDC_CLIENT_ID}\", 97 | \"client_secret\": \"${OIDC_CLIENT_SECRET}\", 98 | \"code_challenge_method\": \"${OIDC_CODE_CHALLENGE_METHOD}\", 99 | \"roles_property\": \"${OIDC_ROLES_PROPERTY}\", 100 | \"role_mapper\": ${OIDC_ROLES_MAPPING}, 101 | \"default_org\": \"${OIDC_DEFAULT_ORG}\", 102 | \"mixedAuth\": ${OIDC_MIXEDAUTH}, 103 | \"authentication_method\": \"${OIDC_AUTH_METHOD}\", 104 | \"redirect_uri\": \"${OIDC_REDIRECT_URI}\" 105 | } 106 | }" > /dev/null 107 | 108 | # Check if OIDC_SCOPES is set and is an array 109 | if [[ "$(echo "$OIDC_SCOPES" | jq type -r)" == "array" ]]; then 110 | # Run the modify_config.php script to update OidcAuth configuration with the provided OIDC_SCOPES 111 | # The 'scopes' field will only be added if OIDC_SCOPES has a value 112 | sudo -u www-data php /var/www/MISP/tests/modify_config.php modify "{ 113 | \"OidcAuth\": { 114 | \"scopes\": ${OIDC_SCOPES} 115 | } 116 | }" > /dev/null 117 | fi 118 | 119 | # Set the custom logout URL for OIDC if it is defined 120 | if [[ -n "${OIDC_LOGOUT_URL}" ]]; then 121 | if [[ "${OIDC_LOGOUT_URL}" == *"?"* ]]; then 122 | OIDC_LOGOUT_URL_COMPLETE="${OIDC_LOGOUT_URL}&post_logout_redirect_uri=${BASE_URL}/users/login" 123 | else 124 | OIDC_LOGOUT_URL_COMPLETE="${OIDC_LOGOUT_URL}?post_logout_redirect_uri=${BASE_URL}/users/login" 125 | fi 126 | sudo -u www-data /var/www/MISP/app/Console/cake Admin setSetting -q "Plugin.CustomAuth_custom_logout" "${OIDC_LOGOUT_URL_COMPLETE}" 127 | else 128 | echo "OIDC_LOGOUT_URL is not set" 129 | fi 130 | 131 | # Disable password confirmation as recommended in https://github.com/MISP/MISP/issues/8116 132 | sudo -u www-data /var/www/MISP/app/Console/cake Admin setSetting -q "Security.require_password_confirmation" false 133 | 134 | echo "... OIDC authentication enabled" 135 | 136 | else 137 | # Reset OIDC authentication settings to empty values 138 | sudo -u www-data php /var/www/MISP/tests/modify_config.php modify "{ 139 | \"OidcAuth\": { 140 | \"provider_url\": \"\", 141 | \"issuer\": \"\", 142 | \"client_id\": \"\", 143 | \"client_secret\": \"\", 144 | \"code_challenge_method\": \"\", 145 | \"roles_property\": \"\", 146 | \"role_mapper\": \"\", 147 | \"default_org\": \"\" 148 | } 149 | }" > /dev/null 150 | 151 | # Remove the line containing 'scopes' => from config.php 152 | # This prevents an empty scopes entry from being loaded in the configuration. 153 | sudo -u www-data sed -i "/'scopes' =>/d" /var/www/MISP/app/Config/config.php 154 | 155 | # Use sed to remove the OidcAuth.Oidc entry from the 'auth' array in the config.php 156 | sudo -u www-data sed -i "/'auth' =>/,/)/ { /0 => 'OidcAuth.Oidc',/d; }" /var/www/MISP/app/Config/config.php 157 | 158 | # Remove the custom logout URL 159 | sudo -u www-data sed -i "/'CustomAuth_custom_logout' =>/d" /var/www/MISP/app/Config/config.php 160 | 161 | # Re-enable password confirmation if necessary 162 | sudo -u www-data /var/www/MISP/app/Console/cake Admin setSetting -q "Security.require_password_confirmation" true 163 | 164 | echo "... OIDC authentication disabled" 165 | fi 166 | } 167 | 168 | set_up_apachesecureauth() { 169 | if [[ "$APACHESECUREAUTH_LDAP_ENABLE" != "true" ]]; then 170 | echo "... LDAP APACHESECUREAUTH authentication disabled" 171 | return 172 | fi 173 | 174 | 175 | if [ ! -z "$APACHESECUREAUTH_LDAP_OLD_VAR_DETECT" ]; then 176 | echo "WARNING: old variables used for APACHESECUREAUTH bloc in env file. Switch to the new naming convention." 177 | fi 178 | 179 | # Check required variables 180 | # APACHESECUREAUTH_LDAP_SEARCH_FILTER may be empty 181 | check_env_vars APACHESECUREAUTH_LDAP_APACHE_ENV APACHESECUREAUTH_LDAP_SERVER APACHESECUREAUTH_LDAP_STARTTLS APACHESECUREAUTH_LDAP_READER_USER APACHESECUREAUTH_LDAP_READER_PASSWORD APACHESECUREAUTH_LDAP_DN APACHESECUREAUTH_LDAP_SEARCH_ATTRIBUTE APACHESECUREAUTH_LDAP_FILTER APACHESECUREAUTH_LDAP_DEFAULT_ROLE_ID APACHESECUREAUTH_LDAP_DEFAULT_ORG APACHESECUREAUTH_LDAP_OPT_PROTOCOL_VERSION APACHESECUREAUTH_LDAP_OPT_NETWORK_TIMEOUT APACHESECUREAUTH_LDAP_OPT_REFERRALS 182 | 183 | sudo -u www-data php /var/www/MISP/tests/modify_config.php modify "{ 184 | \"ApacheSecureAuth\": { 185 | \"apacheEnv\": \"${APACHESECUREAUTH_LDAP_APACHE_ENV}\", 186 | \"ldapServer\": \"${APACHESECUREAUTH_LDAP_SERVER}\", 187 | \"starttls\": ${APACHESECUREAUTH_LDAP_STARTTLS}, 188 | \"ldapProtocol\": ${APACHESECUREAUTH_LDAP_OPT_PROTOCOL_VERSION}, 189 | \"ldapNetworkTimeout\": ${APACHESECUREAUTH_LDAP_OPT_NETWORK_TIMEOUT}, 190 | \"ldapReaderUser\": \"${APACHESECUREAUTH_LDAP_READER_USER}\", 191 | \"ldapReaderPassword\": \"${APACHESECUREAUTH_LDAP_READER_PASSWORD}\", 192 | \"ldapDN\": \"${APACHESECUREAUTH_LDAP_DN}\", 193 | \"ldapSearchFilter\": \"${APACHESECUREAUTH_LDAP_SEARCH_FILTER}\", 194 | \"ldapSearchAttribut\": \"${APACHESECUREAUTH_LDAP_SEARCH_ATTRIBUTE}\", 195 | \"ldapFilter\": ${APACHESECUREAUTH_LDAP_FILTER}, 196 | \"ldapDefaultRoleId\": ${APACHESECUREAUTH_LDAP_DEFAULT_ROLE_ID}, 197 | \"ldapDefaultOrg\": \"${APACHESECUREAUTH_LDAP_DEFAULT_ORG}\", 198 | \"ldapAllowReferrals\": ${APACHESECUREAUTH_LDAP_OPT_REFERRALS}, 199 | \"ldapEmailField\": ${APACHESECUREAUTH_LDAP_EMAIL_FIELD} 200 | } 201 | }" > /dev/null 202 | 203 | # Disable password confirmation as stated at https://github.com/MISP/MISP/issues/8116 204 | sudo -u www-data /var/www/MISP/app/Console/cake Admin setSetting -q "Security.require_password_confirmation" false 205 | } 206 | 207 | set_up_ldap() { 208 | if [[ "$LDAPAUTH_ENABLE" != "true" ]]; then 209 | echo "... LDAPAUTH authentication disabled" 210 | return 211 | fi 212 | 213 | # Check required variables 214 | # LDAPAUTH_LDAPSEARCHFILTER may be empty 215 | check_env_vars LDAPAUTH_LDAPSERVER LDAPAUTH_LDAPDN LDAPAUTH_LDAPREADERUSER LDAPAUTH_LDAPREADERPASSWORD LDAPAUTH_LDAPSEARCHATTRIBUTE LDAPAUTH_LDAPDEFAULTROLEID LDAPAUTH_LDAPDEFAULTORGID LDAPAUTH_LDAPEMAILFIELD LDAPAUTH_LDAPNETWORKTIMEOUT LDAPAUTH_LDAPPROTOCOL LDAPAUTH_LDAPALLOWREFERRALS LDAPAUTH_STARTTLS LDAPAUTH_MIXEDAUTH LDAPAUTH_UPDATEUSER LDAPAUTH_DEBUG LDAPAUTH_LDAPTLSREQUIRECERT LDAPAUTH_LDAPTLSCUSTOMCACERT LDAPAUTH_LDAPTLSCRLCHECK LDAPAUTH_LDAPTLSPROTOCOLMIN 216 | 217 | sudo -u www-data php /var/www/MISP/tests/modify_config.php modify "{ 218 | \"LdapAuth\": { 219 | \"ldapServer\": \"${LDAPAUTH_LDAPSERVER}\", 220 | \"ldapDn\": \"${LDAPAUTH_LDAPDN}\", 221 | \"ldapReaderUser\": \"${LDAPAUTH_LDAPREADERUSER}\", 222 | \"ldapReaderPassword\": \"${LDAPAUTH_LDAPREADERPASSWORD}\", 223 | \"ldapSearchFilter\": \"${LDAPAUTH_LDAPSEARCHFILTER}\", 224 | \"ldapSearchAttribute\": \"${LDAPAUTH_LDAPSEARCHATTRIBUTE}\", 225 | \"ldapEmailField\": [\"${LDAPAUTH_LDAPEMAILFIELD}\"], 226 | \"ldapNetworkTimeout\": ${LDAPAUTH_LDAPNETWORKTIMEOUT}, 227 | \"ldapProtocol\": ${LDAPAUTH_LDAPPROTOCOL}, 228 | \"ldapAllowReferrals\": ${LDAPAUTH_LDAPALLOWREFERRALS}, 229 | \"starttls\": ${LDAPAUTH_STARTTLS}, 230 | \"mixedAuth\": ${LDAPAUTH_MIXEDAUTH}, 231 | \"ldapDefaultOrgId\": ${LDAPAUTH_LDAPDEFAULTORGID}, 232 | \"ldapDefaultRoleId\": ${LDAPAUTH_LDAPDEFAULTROLEID}, 233 | \"updateUser\": ${LDAPAUTH_UPDATEUSER}, 234 | \"debug\": ${LDAPAUTH_DEBUG}, 235 | \"ldapTlsRequireCert\": \"${LDAPAUTH_LDAPTLSREQUIRECERT}\", 236 | \"ldapTlsCustomCaCert\": ${LDAPAUTH_LDAPTLSCUSTOMCACERT}, 237 | \"ldapTlsCrlCheck\": \"${LDAPAUTH_LDAPTLSCRLCHECK}\", 238 | \"ldapTlsProtocolMin\": \"${LDAPAUTH_LDAPTLSPROTOCOLMIN}\" 239 | } 240 | }" > /dev/null 241 | 242 | # Configure LdapAuth in MISP 243 | sudo -u www-data php /var/www/MISP/tests/modify_config.php modify "{ 244 | \"Security\": { 245 | \"auth\": [\"LdapAuth.Ldap\"] 246 | } 247 | }" > /dev/null 248 | 249 | 250 | # Disable password confirmation as stated at https://github.com/MISP/MISP/issues/8116 251 | sudo -u www-data /var/www/MISP/app/Console/cake Admin setSetting -q "Security.require_password_confirmation" false 252 | } 253 | 254 | set_up_aad() { 255 | if [[ "$AAD_ENABLE" != "true" ]]; then 256 | echo "... Entra (AzureAD) authentication disabled" 257 | return 258 | fi 259 | 260 | # Check required variables 261 | check_env_vars AAD_CLIENT_ID AAD_TENANT_ID AAD_CLIENT_SECRET AAD_REDIRECT_URI AAD_PROVIDER AAD_PROVIDER_USER AAD_MISP_ORGADMIN AAD_MISP_SITEADMIN AAD_CHECK_GROUPS 262 | 263 | # Note: Not necessary to edit bootstrap.php to load AadAuth Cake plugin because 264 | # existing loadAll() call in bootstrap.php already loads all available Cake plugins 265 | 266 | # Set auth mechanism to AAD in config.php file 267 | sudo -u www-data php /var/www/MISP/tests/modify_config.php modify "{ 268 | \"Security\": { 269 | \"auth\": [\"AadAuth.AadAuthenticate\"] 270 | } 271 | }" > /dev/null 272 | 273 | # Configure AAD auth settings from environment variables in config.php file 274 | sudo -u www-data php /var/www/MISP/tests/modify_config.php modify "{ 275 | \"AadAuth\": { 276 | \"client_id\": \"${AAD_CLIENT_ID}\", 277 | \"ad_tenant\": \"${AAD_TENANT_ID}\", 278 | \"client_secret\": \"${AAD_CLIENT_SECRET}\", 279 | \"redirect_uri\": \"${AAD_REDIRECT_URI}\", 280 | \"auth_provider\": \"${AAD_PROVIDER}\", 281 | \"auth_provider_user\": \"${AAD_PROVIDER_USER}\", 282 | \"misp_user\": \"${AAD_MISP_USER}\", 283 | \"misp_orgadmin\": \"${AAD_MISP_ORGADMIN}\", 284 | \"misp_siteadmin\": \"${AAD_MISP_SITEADMIN}\", 285 | \"check_ad_groups\": ${AAD_CHECK_GROUPS} 286 | } 287 | }" > /dev/null 288 | 289 | # Disable self-management, username change, and password change to prevent users from circumventing AAD login flow 290 | # Recommended per https://github.com/MISP/MISP/blob/2.4/app/Plugin/AadAuth/README.md 291 | sudo -u www-data /var/www/MISP/app/Console/cake Admin setSetting -q "MISP.disableUserSelfManagement" true 292 | sudo -u www-data /var/www/MISP/app/Console/cake Admin setSetting -q "MISP.disable_user_login_change" true 293 | sudo -u www-data /var/www/MISP/app/Console/cake Admin setSetting -q "MISP.disable_user_password_change" true 294 | 295 | # Disable password confirmation as stated at https://github.com/MISP/MISP/issues/8116 296 | sudo -u www-data /var/www/MISP/app/Console/cake Admin setSetting -q "Security.require_password_confirmation" false 297 | } 298 | 299 | set_up_session() { 300 | # Command to modify MISP session configuration 301 | sudo -u www-data php /var/www/MISP/tests/modify_config.php modify "{ 302 | \"Session\": { 303 | \"timeout\": ${PHP_SESSION_TIMEOUT}, 304 | \"cookie_timeout\": ${PHP_SESSION_COOKIE_TIMEOUT}, 305 | \"defaults\": \"${PHP_SESSION_DEFAULTS}\", 306 | \"autoRegenerate\": ${PHP_SESSION_AUTO_REGENERATE}, 307 | \"checkAgent\": ${PHP_SESSION_CHECK_AGENT}, 308 | \"ini\": { 309 | \"session.cookie_secure\": ${PHP_SESSION_COOKIE_SECURE}, 310 | \"session.cookie_domain\": \"${PHP_SESSION_COOKIE_DOMAIN}\", 311 | \"session.cookie_samesite\": \"${PHP_SESSION_COOKIE_SAMESITE}\" 312 | } 313 | } 314 | }" > /dev/null 315 | 316 | echo "... Session configured" 317 | } 318 | 319 | set_up_proxy() { 320 | if [[ "$PROXY_ENABLE" == "true" ]]; then 321 | echo "... configuring proxy settings" 322 | init_settings "proxy" 323 | else 324 | echo "... Proxy disabled" 325 | fi 326 | } 327 | 328 | apply_updates() { 329 | # Disable 'ZeroMQ_enable' to get better logs when applying updates 330 | # sudo -u www-data /var/www/MISP/app/Console/cake Admin setSetting -q "Plugin.ZeroMQ_enable" false 331 | # Run updates (strip colors since output might end up in a log) 332 | sudo -u www-data /var/www/MISP/app/Console/cake Admin runUpdates | stdbuf -oL sed -r "s/[[:cntrl:]]\[[0-9]{1,3}m//g" 333 | # Re-enable 'ZeroMQ_enable' 334 | # sudo -u www-data /var/www/MISP/app/Console/cake Admin setSetting -q "Plugin.ZeroMQ_enable" true 335 | } 336 | 337 | init_user() { 338 | # Create the main user if it is not there already 339 | sudo -u www-data /var/www/MISP/app/Console/cake user init -q > /dev/null 2>&1 340 | 341 | echo "UPDATE $MYSQL_DATABASE.users SET email = \"${ADMIN_EMAIL}\" WHERE id = 1;" | ${MYSQL_CMD} 342 | 343 | if [ ! -z "$ADMIN_ORG" ]; then 344 | echo "... setting admin org to '${ADMIN_ORG}'" 345 | echo "UPDATE $MYSQL_DATABASE.organisations SET name = \"${ADMIN_ORG}\" where id = 1;" | ${MYSQL_CMD} 346 | sudo -u www-data /var/www/MISP/app/Console/cake Admin setSetting -q "MISP.org" "${ADMIN_ORG}" 347 | fi 348 | 349 | if [ ! -z "$ADMIN_ORG_UUID" ]; then 350 | echo "... setting admin org uuid to '${ADMIN_ORG_UUID}'" 351 | echo "UPDATE $MYSQL_DATABASE.organisations SET uuid = \"${ADMIN_ORG_UUID}\" where id = 1;" | ${MYSQL_CMD} 352 | fi 353 | 354 | if [ -n "$ADMIN_KEY" ]; then 355 | if [ "$DISABLE_PRINTING_PLAINTEXT_CREDENTIALS" == "true" ]; then 356 | echo "... setting admin key from environment variable" 357 | else 358 | echo "... setting admin key to '${ADMIN_KEY}'" 359 | fi 360 | CHANGE_CMD=(sudo -u www-data /var/www/MISP/app/Console/cake User change_authkey 1 "${ADMIN_KEY}") 361 | elif [ -z "$ADMIN_KEY" ] && [ "$AUTOGEN_ADMIN_KEY" == "true" ]; then 362 | HAS_VALID_KEY=$($MYSQL_CMD -N -s -e 'SELECT EXISTS(SELECT 1 FROM auth_keys WHERE user_id = 1 AND (expiration = 0 OR expiration > UNIX_TIMESTAMP()));') 363 | if (( HAS_VALID_KEY == 0 )); then 364 | echo "... regenerating admin key (set \$ADMIN_KEY if you want it to change)" 365 | CHANGE_CMD=(sudo -u www-data /var/www/MISP/app/Console/cake User change_authkey 1) 366 | else 367 | echo "... valid admin key for admin user found, not changing" 368 | fi 369 | else 370 | echo "... admin user key auto generation disabled" 371 | fi 372 | 373 | if [[ -v CHANGE_CMD[@] ]]; then 374 | ADMIN_KEY=$("${CHANGE_CMD[@]}" | awk 'END {print $NF; exit}') 375 | if [ "$DISABLE_PRINTING_PLAINTEXT_CREDENTIALS" == "true" ]; then 376 | echo "... admin user key set" 377 | else 378 | echo "... admin user key set to '${ADMIN_KEY}'" 379 | fi 380 | fi 381 | 382 | if [ ! -z "$ADMIN_PASSWORD" ]; then 383 | if [ "$DISABLE_PRINTING_PLAINTEXT_CREDENTIALS" == "true" ]; then 384 | echo "... setting admin password from environment variable" 385 | else 386 | echo "... setting admin password to '${ADMIN_PASSWORD}'" 387 | fi 388 | PASSWORD_POLICY=$(sudo -u www-data /var/www/MISP/app/Console/cake Admin getSetting "Security.password_policy_complexity" | jq ".value" -r) 389 | PASSWORD_LENGTH=$(sudo -u www-data /var/www/MISP/app/Console/cake Admin getSetting "Security.password_policy_length" | jq ".value" -r) 390 | sudo -u www-data /var/www/MISP/app/Console/cake Admin setSetting -q "Security.password_policy_length" 1 391 | sudo -u www-data /var/www/MISP/app/Console/cake Admin setSetting -q "Security.password_policy_complexity" '/.*/' 392 | sudo -u www-data /var/www/MISP/app/Console/cake User change_pw "${ADMIN_EMAIL}" "${ADMIN_PASSWORD}" 393 | sudo -u www-data /var/www/MISP/app/Console/cake Admin setSetting -q "Security.password_policy_complexity" "${PASSWORD_POLICY}" 394 | sudo -u www-data /var/www/MISP/app/Console/cake Admin setSetting -q "Security.password_policy_length" "${PASSWORD_LENGTH}" 395 | else 396 | echo "... setting admin password skipped" 397 | fi 398 | echo "UPDATE $MYSQL_DATABASE.users SET change_pw = 0 WHERE id = 1;" | ${MYSQL_CMD} 399 | } 400 | 401 | apply_critical_fixes() { 402 | init_settings "critical" 403 | 404 | # Kludge for handling Security.auth array. Unrecognised by tools like cake admin setsetting. 405 | local config_json=$(echo ''|/usr/bin/php) 406 | if $(echo $config_json |jq -e 'getpath(("Security.auth" | split("."))) == null'); then 407 | echo "Updating unset critical setting 'Security.auth' to 'Array()'..." 408 | sudo -u www-data php /var/www/MISP/tests/modify_config.php modify "{ 409 | \"Security\": { 410 | \"auth\": {} 411 | } 412 | }" > /dev/null 413 | fi 414 | } 415 | 416 | apply_optional_fixes() { 417 | init_settings "optional" 418 | } 419 | 420 | apply_storage_settings() { 421 | if [[ -n "$S3_ACCESS_KEY" && -n "$S3_SECRET_KEY" && -n "$S3_BUCKET" && -n "$S3_ENDPOINT" ]]; then 422 | init_settings "s3" 423 | fi 424 | } 425 | 426 | # Some settings return a value from cake Admin getSetting even if not set in config.php and database. 427 | # This means we cannot rely on that tool which inspects both db and file. 428 | # Leaving this here though in case the serverSettings model for those odd settings is fixed one day. 429 | #setting_is_set() { 430 | # local setting="$1" 431 | # local current_value="$(sudo -u www-data /var/www/MISP/app/Console/cake Admin getSetting $setting)" 432 | # local error_value="$(jq -r '.errorMessage' <<< $current_value)" 433 | # 434 | # if [[ "$current_value" =~ ^\{.*\}$ && "$error_value" != "Value not set." && "$error_value" != Invalid* ]]; then 435 | # return 0 436 | # else 437 | # return 1 438 | # fi 439 | #} 440 | 441 | update_components() { 442 | UPDATE_SUDO_CMD="sudo -u www-data" 443 | if [ ! -z "${DB_ALREADY_INITIALISED}" ]; then 444 | if [[ "$ENABLE_BACKGROUND_UPDATES" = "true" ]]; then 445 | echo "... updates will run in the background" 446 | UPDATE_SUDO_CMD="sudo -b -u www-data" 447 | fi 448 | fi 449 | ${UPDATE_SUDO_CMD} /var/www/MISP/app/Console/cake Admin updateGalaxies 450 | ${UPDATE_SUDO_CMD} /var/www/MISP/app/Console/cake Admin updateTaxonomies 451 | ${UPDATE_SUDO_CMD} /var/www/MISP/app/Console/cake Admin updateWarningLists 452 | ${UPDATE_SUDO_CMD} /var/www/MISP/app/Console/cake Admin updateNoticeLists 453 | ${UPDATE_SUDO_CMD} /var/www/MISP/app/Console/cake Admin updateObjectTemplates "$CRON_USER_ID" 454 | } 455 | 456 | update_ca_certificates() { 457 | # Upgrade host os certificates 458 | update-ca-certificates 459 | if [[ "$DISABLE_CA_REFRESH" = "true" ]]; then 460 | echo "Updating /var/www/MISP/app/Lib/cakephp/lib/Cake/Config/cacert.pem using local data..." 461 | sudo cp /etc/ssl/certs/ca-certificates.crt /var/www/MISP/app/Lib/cakephp/lib/Cake/Config/cacert.pem 462 | else 463 | echo "Updating /var/www/MISP/app/Lib/cakephp/lib/Cake/Config/cacert.pem using curl data..." 464 | sudo -E -u www-data curl -s --etag-compare /var/www/MISP/app/Lib/cakephp/lib/Cake/Config/etag.txt --etag-save /var/www/MISP/app/Lib/cakephp/lib/Cake/Config/etag.txt https://curl.se/ca/cacert.pem -o /var/www/MISP/app/Lib/cakephp/lib/Cake/Config/cacert.pem 465 | fi 466 | } 467 | 468 | create_sync_servers() { 469 | if [ -z "$ADMIN_KEY" ]; then 470 | echo "... admin key auto configuration is required to configure sync servers" 471 | return 472 | fi 473 | 474 | SPLITTED_SYNCSERVERS=$(echo $SYNCSERVERS | tr ',' '\n') 475 | for ID in $SPLITTED_SYNCSERVERS; do 476 | DATA="SYNCSERVERS_${ID}_DATA" 477 | 478 | # Validate #1 479 | NAME=$(echo "${!DATA}" | jq -r '.name') 480 | if [[ -z $NAME ]]; then 481 | echo "... error missing sync server name" 482 | continue 483 | fi 484 | 485 | # Skip sync server if we can 486 | echo "... searching sync server ${NAME}" 487 | SERVER_ID=$(get_server ${BASE_URL} ${ADMIN_KEY} ${NAME}) 488 | if [[ -n "$SERVER_ID" ]]; then 489 | echo "... found existing sync server ${NAME} with id ${SERVER_ID}" 490 | continue 491 | fi 492 | 493 | # Validate #2 494 | UUID=$(echo "${!DATA}" | jq -r '.remote_org_uuid') 495 | if [[ -z "$UUID" ]]; then 496 | echo "... error missing sync server remote_org_uuid" 497 | continue 498 | fi 499 | 500 | # Get remote organization 501 | echo "... searching remote organization ${UUID}" 502 | ORG_ID=$(get_organization ${BASE_URL} ${ADMIN_KEY} ${UUID}) 503 | if [[ -z "$ORG_ID" ]]; then 504 | # Add remote organization if missing 505 | echo "... adding missing organization ${UUID}" 506 | add_organization ${BASE_URL} ${ADMIN_KEY} ${NAME} false ${UUID} > /dev/null 507 | ORG_ID=$(get_organization ${BASE_URL} ${ADMIN_KEY} ${UUID}) 508 | fi 509 | 510 | # Add sync server 511 | echo "... adding new sync server ${NAME} with organization id ${ORG_ID}" 512 | JSON_DATA=$(echo "${!DATA}" | jq --arg org_id ${ORG_ID} 'del(.remote_org_uuid) | . + {remote_org_id: $org_id} | del(..|select(. == ""))') 513 | add_server ${BASE_URL} ${ADMIN_KEY} "$JSON_DATA" > /dev/null 514 | done 515 | } 516 | 517 | convert_cron_to_seconds() { 518 | local expr="$1" 519 | 520 | # Match "*/N * * * *" -> every N minutes 521 | if [[ "$expr" =~ ^\*/([0-9]+)\ \*\ \*\ \*\ \*$ ]]; then 522 | echo "$(( ${BASH_REMATCH[1]} * 60 ))" 523 | return 524 | fi 525 | 526 | # Match "* */N * * *" -> every N hours 527 | if [[ "$expr" =~ ^\*\ \*/([0-9]+)\ \*\ \*\ \*$ ]]; then 528 | echo "$(( ${BASH_REMATCH[1]} * 3600 ))" 529 | return 530 | fi 531 | 532 | # Default fallback -> warn and use 86400 (daily) 533 | echo "WARNING: Unrecognized cron pattern '$expr', using default 86400 seconds (daily)" >&2 534 | echo "86400" 535 | } 536 | 537 | create_default_scheduled_tasks() { 538 | # Create default scheduled tasks 539 | 540 | if [[ "$CRON_PULLALL" =~ ^([0-9]+)$ ]]; then 541 | # Already seconds 542 | PULLALL_INTERVAL="$CRON_PULLALL" 543 | else 544 | # Convert old cron format 545 | PULLALL_INTERVAL="$(convert_cron_to_seconds "$CRON_PULLALL")" 546 | fi 547 | 548 | if [[ "$CRON_PUSHALL" =~ ^([0-9]+)$ ]]; then 549 | # Already seconds 550 | PUSHALL_INTERVAL="$CRON_PUSHALL" 551 | else 552 | # Convert old cron format 553 | PUSHALL_INTERVAL="$(convert_cron_to_seconds "$CRON_PUSHALL")" 554 | fi 555 | 556 | echo "INSERT INTO $MYSQL_DATABASE.scheduled_tasks (id, type, timer, description, user_id, action, params, enabled, next_execution_time, message) \ 557 | VALUES (1, 'Feed', 86400, 'Daily fetch of all Feeds', $CRON_USER_ID, 'fetch', 'all', 1, 0, '') \ 558 | ON DUPLICATE KEY UPDATE user_id=$CRON_USER_ID;" | ${MYSQL_CMD} 559 | echo "INSERT IGNORE INTO $MYSQL_DATABASE.scheduled_tasks (id, type, timer, description, user_id, action, params, enabled, next_execution_time, message) \ 560 | VALUES (2, 'Feed', 86400, 'Daily cache of all Feeds', $CRON_USER_ID, 'cache', 'all,all', 1, 0, '') \ 561 | ON DUPLICATE KEY UPDATE user_id=$CRON_USER_ID;" | ${MYSQL_CMD} 562 | echo "INSERT IGNORE INTO $MYSQL_DATABASE.scheduled_tasks (id, type, timer, description, user_id, action, params, enabled, next_execution_time, message) \ 563 | VALUES (3, 'Server', $PULLALL_INTERVAL, 'Daily pull of all Servers', $CRON_USER_ID, 'pull', 'all,full', 1, 0, '') \ 564 | ON DUPLICATE KEY UPDATE user_id=$CRON_USER_ID, timer=$PULLALL_INTERVAL;" | ${MYSQL_CMD} 565 | echo "INSERT IGNORE INTO $MYSQL_DATABASE.scheduled_tasks (id, type, timer, description, user_id, action, params, enabled, next_execution_time, message) \ 566 | VALUES (4, 'Server', $PUSHALL_INTERVAL, 'Daily push of all Servers', $CRON_USER_ID, 'push', 'all,full', 1, 0, '') \ 567 | ON DUPLICATE KEY UPDATE user_id=$CRON_USER_ID, timer=$PUSHALL_INTERVAL;" | ${MYSQL_CMD} 568 | echo "INSERT IGNORE INTO $MYSQL_DATABASE.scheduled_tasks (id, type, timer, description, user_id, action, enabled, next_execution_time, message) \ 569 | VALUES (5, 'Admin', 86400, 'Daily update of Galaxies', $CRON_USER_ID, 'updateGalaxies', 1, 0, '') \ 570 | ON DUPLICATE KEY UPDATE user_id=$CRON_USER_ID;" | ${MYSQL_CMD} 571 | echo "INSERT IGNORE INTO $MYSQL_DATABASE.scheduled_tasks (id, type, timer, description, user_id, action, enabled, next_execution_time, message) \ 572 | VALUES (6, 'Admin', 86400, 'Daily update of Taxonomies', $CRON_USER_ID, 'updateTaxonomies', 1, 0, '') \ 573 | ON DUPLICATE KEY UPDATE user_id=$CRON_USER_ID;" | ${MYSQL_CMD} 574 | echo "INSERT IGNORE INTO $MYSQL_DATABASE.scheduled_tasks (id, type, timer, description, user_id, action, enabled, next_execution_time, message) \ 575 | VALUES (7, 'Admin', 86400, 'Daily update of Warninglists', $CRON_USER_ID, 'updateWarningLists', 1, 0, '') \ 576 | ON DUPLICATE KEY UPDATE user_id=$CRON_USER_ID;" | ${MYSQL_CMD} 577 | echo "INSERT IGNORE INTO $MYSQL_DATABASE.scheduled_tasks (id, type, timer, description, user_id, action, enabled, next_execution_time, message) \ 578 | VALUES (8, 'Admin', 86400, 'Daily update of Noticelists', $CRON_USER_ID, 'updateNoticeLists', 1, 0, '') \ 579 | ON DUPLICATE KEY UPDATE user_id=$CRON_USER_ID;" | ${MYSQL_CMD} 580 | echo "INSERT IGNORE INTO $MYSQL_DATABASE.scheduled_tasks (id, type, timer, description, user_id, action, enabled, next_execution_time, message) \ 581 | VALUES (9, 'Admin', 86400, 'Daily update of Object Templates', $CRON_USER_ID, 'updateObjectTemplates', 1, 0, '') \ 582 | ON DUPLICATE KEY UPDATE user_id=$CRON_USER_ID;" | ${MYSQL_CMD} 583 | } 584 | 585 | print_version() { 586 | VERSION_FILE="/var/www/MISP/VERSION.json" 587 | if [[ -f "$VERSION_FILE" ]]; then 588 | VERSION=$(jq -r '"\(.major).\(.minor).\(.hotfix)"' ${VERSION_FILE}) 589 | else 590 | VERSION="unknown" 591 | fi 592 | echo "MISP | Version: ${VERSION}" 593 | } 594 | 595 | echo "MISP | Update CA certificates ..." && update_ca_certificates 596 | 597 | echo "MISP | Apply minimum configuration directives ..." && init_minimum_config 598 | 599 | echo "MISP | Initialize configuration ..." && init_configuration 600 | 601 | echo "MISP | Initialize workers ..." && init_workers 602 | 603 | echo "MISP | Apply DB updates ..." && apply_updates 604 | 605 | echo "MISP | Configure GPG key ..." && configure_gnupg 606 | 607 | echo "MISP | Init default user and organization ..." && init_user 608 | 609 | echo "MISP | Resolve critical issues ..." && apply_critical_fixes 610 | 611 | echo "MISP | Start component updates ..." && update_components 612 | 613 | echo "MISP | Resolve non-critical issues ..." && apply_optional_fixes 614 | 615 | echo "MISP | Configure storage ..." && apply_storage_settings 616 | 617 | echo "MISP | Create sync servers ..." && create_sync_servers 618 | 619 | echo "MISP | Set Up OIDC ..." && set_up_oidc 620 | 621 | echo "MISP | Set Up apachesecureauth ..." && set_up_apachesecureauth 622 | 623 | echo "MISP | Set Up LDAP ..." && set_up_ldap 624 | 625 | echo "MISP | Set Up AAD ..." && set_up_aad 626 | 627 | echo "MISP | Set Up Session ..." && set_up_session 628 | 629 | echo "MISP | Set Up Proxy ..." && set_up_proxy 630 | 631 | echo "MISP | Create default Scheduled Tasks ..." && create_default_scheduled_tasks 632 | 633 | echo "MISP | Mark instance live" && print_version 634 | sudo -u www-data /var/www/MISP/app/Console/cake Admin live 1 635 | --------------------------------------------------------------------------------