Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Config/database.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class DATABASE_CONFIG {
'persistent' => false,
'host' => '{{ MYSQL_HOST }}',
'login' => '{{ MYSQL_LOGIN }}',
'port' => 3306, // MySQL & MariaDB
'port' => {{ MYSQL_PORT }},
'password' => '{{ MYSQL_PASSWORD }}',
'database' => '{{ MYSQL_DATABASE }}',
'prefix' => '',
Expand Down
22 changes: 11 additions & 11 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,20 @@ RUN dnf install -y --setopt=tsflags=nodocs --setopt=install_weak_deps=False pyth
su-exec build pip3 wheel pydeep -w /tmp/wheels && \
dnf history undo -y 0

# Build PHP extensions
# Build PHP extensions that are not included in packages
FROM builder as php-build
COPY bin/misp_compile_php_extensions.sh /tmp/
RUN dnf module enable -y php:7.4 && \
dnf install -y --setopt=tsflags=nodocs --setopt=install_weak_deps=False php-devel php-mbstring php-json php-xml brotli-devel && \
chmod u+x /tmp/misp_compile_php_extensions.sh && \
/tmp/misp_compile_php_extensions.sh && \
COPY bin/misp_compile_php_extensions.sh /build/
RUN --mount=type=tmpfs,target=/tmp dnf module enable -y php:7.4 && \
dnf install -y --setopt=tsflags=nodocs --setopt=install_weak_deps=False php-devel php-mbstring php-json php-xml brotli-devel diffutils && \
chmod u+x /build/misp_compile_php_extensions.sh && \
/build/misp_compile_php_extensions.sh && \
dnf history undo -y 0

# Build jobber, that is not released for arm64 arch
FROM builder as jobber-build
COPY bin/misp_compile_jobber.sh /tmp/
RUN chmod u+x /tmp/misp_compile_jobber.sh && \
/tmp/misp_compile_jobber.sh
COPY bin/misp_compile_jobber.sh /build/
RUN --mount=type=tmpfs,target=/tmp chmod u+x /build/misp_compile_jobber.sh && \
/build/misp_compile_jobber.sh

# MISP image
FROM base as misp
Expand All @@ -49,8 +49,8 @@ RUN dnf module -y enable mod_auth_openidc php:7.4 python39 && \

COPY --from=builder /usr/local/bin/su-exec /usr/local/bin/
COPY --from=python-build /tmp/wheels /wheels
COPY --from=php-build /tmp/php-modules/* /usr/lib64/php/modules/
COPY --from=jobber-build /tmp/jobber*.rpm /tmp
COPY --from=php-build /build/php-modules/* /usr/lib64/php/modules/
COPY --from=jobber-build /build/jobber*.rpm /tmp
COPY bin/ /usr/local/bin/
COPY misp.conf /etc/httpd/conf.d/misp.conf
COPY httpd-errors/* /var/www/html/
Expand Down
77 changes: 40 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@
This image contains the latest version of MISP and the required dependencies. Image is intended as immutable, which means that it is not possible
to update MISP from the user interface and instead, an admin should download a newer image.

## Why to use this image?
## Key features

* Image is based on CentOS 8 Stream, so perfectly fits your infrastructure if you use CentOS or RHEL as a host system
* 🎩 Image is based on CentOS Stream 8, so perfectly fits your infrastructure if you use CentOS or RHEL as a host system
* ✅ Modern MISP features are enabled by default (like advanced audit log or storing setting in the database)
* ✅ Integrated support for OpenID Connect (OIDC) authentication
* ✅ PHP is by default protected by Snuffleupagus extensions with [rules](snuffleupagus-misp.rules) tailored to MISP
* ✅ Optional extensions and configurations that will make MISP faster are enabled
* ✅ Integrated support for logging exceptions to Sentry and forwarding logs to syslog server
* ✅ Final image is automatically tested, so every release should work as expected

* 👩‍💻 Integrated support for OpenID Connect (OIDC) authentication
* 🔒️ PHP is by default protected by Snuffleupagus extensions with [rules](snuffleupagus-misp.rules) tailored to MISP
* 🚀 Optional extensions and configurations that will make MISP faster are enabled
* 📓 Integrated support for logging exceptions to Sentry and forwarding logs to syslog server
* 🧪 Final image is automatically tested, so every release should work as expected
* 🏛 Build for amd64 (x86_64) and arm64 (aarch64)

## Usage

First, you have to install Docker. Follow [these manuals](https://docs.docker.com/engine/install/) how to install Docker on your machine. Windows, macOS, or Linux are supported.
Expand Down Expand Up @@ -46,7 +47,7 @@ For production usage, please:

### Usage in air-gapped environment

MISP by default does not require access to Internet. So it is possible to use MISP in air-gapped environment or with blocked outgoing connections. Easies way how to
MISP by default does not require access to Internet. So it is possible to use MISP in air-gapped environment or an environment with blocked outgoing connections. Easies way how to
do that is export container images to compressed tar and transfer them to air-gapped system.

### Image building
Expand Down Expand Up @@ -78,7 +79,8 @@ By changing or defining these container environment variables, you can change co

MISP requires MySQL or MariaDB database.

* `MYSQL_HOST` (required, string)
* `MYSQL_HOST` (required, string) - hostname or IP address
* `MYSQL_PORT` (optional, int, default `3306`)
* `MYSQL_LOGIN` (required, string) - database user
* `MYSQL_PASSWORD` (optional, string)
* `MYSQL_DATABASE` (required, string) - database name
Expand All @@ -87,8 +89,8 @@ MISP requires MySQL or MariaDB database.

By default, MISP requires Redis. MISP will connect to Redis defined in `REDIS_HOST` variable on port `6379`.

* `REDIS_HOST` (required, string)
* `REDIS_PASSWORD` (optional, string)
* `REDIS_HOST` (required, string) - hostname or IP address
* `REDIS_PASSWORD` (optional, string) - password used to connect password protected Redis instance

#### Default Redis databases

Expand All @@ -97,36 +99,37 @@ By default, MISP requires Redis. MISP will connect to Redis defined in `REDIS_HO
* `12` - session data if `PHP_SESSIONS_IN_REDIS` is enabled
* `13` - MISP app

### E-mail setting
### Application

* `MISP_BASEURL` (required, string) - full URL with https:// or http://
* `MISP_UUID` (required, string) - MISP instance UUID (can be generated by `uuidgen` command)
* `MISP_ORG` (required, string) - MISP default organisation name
* `MISP_HOST_ORG_ID` (optional, int, default `1`) - MISP default organisation ID
* `MISP_MODULE_URL` (optional, string) - full URL to MISP modules
* `MISP_DEBUG` (optional, boolean, default `false`) - enable debug mode (do not enable on production environment)

### Email setting

* `SMTP_HOST` (optional, string) - SMTP server that will be used for sending e-mails
* `SMTP_HOST` (optional, string) - SMTP server that will be used for sending emails. SMTP server must listen on port 25 and support STARTTLS.
* `SMTP_USERNAME` (optional, string)
* `SMTP_PASSWORD` (optional, string)
* `MISP_EMAIL` (required, string) - the e-mail address that MISP should use for all notifications
* `MISP_EMAIL_REPLY_TO` (optional, string) - the e-mail address that will be used in `Reply-To` header
* `MISP_EMAIL` (required, string) - the email address that MISP should use for all notifications
* `MISP_EMAIL_REPLY_TO` (optional, string) - the email address that will be used in `Reply-To` header
* `SUPPORT_EMAIL` (optional, string) - the email address that will be included in Apache error pages

### PGP for e-mail encryption and signing
### PGP for email encryption and signing

* `GNUPG_SIGN` (optional, boolean, default `false`) - sign outgoing e-mails by PGP
* `GNUPG_PRIVATE_KEY_PASSWORD` (optional, string) - password for PGP key that is used to sign e-mails send by MISP
* `GNUPG_SIGN` (optional, boolean, default `false`) - sign outgoing emails by PGP
* `GNUPG_PRIVATE_KEY_PASSWORD` (optional, string) - password for PGP key that is used to sign emails send by MISP
* `GNUPG_BODY_ONLY_ENCRYPTED` (optional, boolean, default `false`)

If you want to generate new PGP keys for e-mail signing, you can do it by running this command inside the container:
If you want to generate new PGP keys for email signing, you can do it by running this command inside the container:

gpg --homedir /var/www/MISP/.gnupg --full-generate-key --pinentry-mode=loopback --passphrase "password"

### Application

* `MISP_BASEURL` (required, string) - whole URL with https:// or http://
* `MISP_UUID` (required, string) - MISP instance UUID (can be generated by `uuidgen` command)
* `MISP_ORG` (required, string) - MISP default organisation name
* `MISP_HOST_ORG_ID` (optional, int, default `1`) - MISP default organisation ID
* `MISP_MODULE_URL` (optional, string) - URL to MISP modules
* `MISP_DEBUG` (optional, boolean, default `false`) - enable debug mode (do not enable on production environment)

### Security

* `SECURITY_SALT` (required, string) - random string (recommended at least 32 chars) used for salting hashed values (you can use `openssl rand -base64 32` output)
* `SECURITY_SALT` (required, string) - random string (recommended at least 32 chars) used for salting hashed values (you can use `openssl rand -base64 32` output as value)
* `SECURITY_ADVANCED_AUTHKEYS` (optional, boolean, default `false`) - enable advanced auth keys support
* `SECURITY_HIDE_ORGS` (optional, boolean, default `false`) - hide org names for normal users
* `SECURITY_ENCRYPTION_KEY` (optional, string) - encryption key with at least 32 chars that will be used to encrypt sensitive information stored in database
Expand All @@ -153,7 +156,7 @@ If a request to MISP is made with `Authorization` header, that contains an auth
* `OIDC_CLIENT_ID` (optional, string)
* `OIDC_CLIENT_SECRET` (optional, string)
* `OIDC_PASSWORD_RESET` (optional, string) - URL to password reset page
* `OIDC_CLIENT_CRYPTO_PASS` (optional, string) - password for cookie encryption used by Apache
* `OIDC_CLIENT_CRYPTO_PASS` (optional, string) - password used for cookie encryption by Apache
* `OIDC_DEFAULT_ORG` (optional, bool, default `false`) - for new user without use `MISP_ORG`

#### Inner
Expand All @@ -174,8 +177,8 @@ You can use a different provider for authentication in MISP. If you don't provid
### ZeroMQ

* `ZEROMQ_ENABLED` (optional, boolean, default `false`) - enable ZeroMQ integration, server will listen at `*:50000`
* `ZEROMQ_USERNAME` (optional, string) - ZeroMQ username
* `ZEROMQ_PASSWORD` (optional, string) - ZeroMQ password
* `ZEROMQ_USERNAME` (optional, string) - ZeroMQ server username
* `ZEROMQ_PASSWORD` (optional, string) - ZeroMQ server password

### PHP config

Expand All @@ -186,24 +189,24 @@ You can use a different provider for authentication in MISP. If you don't provid
* `PHP_MAX_EXECUTION_TIME` (optional, int, default `300`) - sets [max_execution_time](https://www.php.net/manual/en/info.configuration.php#ini.max-execution-time) (in seconds)
* `PHP_UPLOAD_MAX_FILESIZE` (optional, string, default `50M`) - sets [upload_max_filesize](https://www.php.net/manual/en/ini.core.php#ini.upload-max-filesize) and [post_max_size](https://www.php.net/manual/en/ini.core.php#ini.post-max-size)
* `PHP_XDEBUG_ENABLED` (optional, boolean, default `false`) - enable [Xdebug](https://xdebug.org) PHP extension for debugging purposes (do not enable on production environment)
* `PHP_XDEBUG_PROFILER_TRIGGER` (optional, string) - secret value for `XDEBUG_PROFILE` GET/POST variable
* `PHP_XDEBUG_PROFILER_TRIGGER` (optional, string) - secret value for `XDEBUG_PROFILE` GET/POST variable that will enable profiling

### Syslog

If enabled, all logs from the container are forwarded to a defined syslog server.

* `SYSLOG_TARGET` (optional, string)
* `SYSLOG_TARGET` (optional, string) - hostname or IP address of the system that shall receive messages
* `SYSLOG_PORT` (optional, integer, default `601`)
* `SYSLOG_PROTOCOL` (optional, string, default `tcp`)

## Log locations

* `/var/log/messages` - all logs captured by rsyslog (see `rsyslog.conf` for definition)
* `/var/log/messages` - all logs captured by rsyslog (see [rsyslog.conf](rsyslog.conf) for definition)
* `/var/log/httpd/` - Apache logs
* `/var/log/php-fpm/` - PHP-FPM logs
* `/var/www/MISP/app/tmp/logs/` - application logs (PHP)

`X-Request-ID` HTTP header is logged in Apache, PHP-FPM and Sentry logs, so you can use this value to correlate requests between logs.
`X-Request-ID` HTTP header is logged in Apache, PHP-FPM, audit, and Sentry logs, so you can use this value to correlate requests between logs.

## Container volumes

Expand Down
1 change: 0 additions & 1 deletion bin/docker-entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ if [ "$1" = 'supervisord' ]; then
echo "MISP $MISP_VERSION container image provided by National Cyber and Information Security Agency of the Czech Republic"
echo "In case of any problem with this image, please fill issue at https://github.com/NUKIB/misp/issues"
echo "======================================"
echo

misp_create_configs.py

Expand Down
9 changes: 6 additions & 3 deletions bin/misp_compile_jobber.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@ cd /tmp/jobber

download_and_check https://github.com/dshearer/jobber/archive/refs/tags/v1.4.4.tar.gz fd88a217a413c5218316664fab5510ace941f4fdb68dcb5428385ff09c68dcc2

dnf install -y --setopt=tsflags=nodocs --setopt=install_weak_deps=False rpmdevtools yum-utils
dnf install -y --setopt=tsflags=nodocs --setopt=install_weak_deps=False rpmdevtools
dnf builddep -y --setopt=tsflags=nodocs --setopt=install_weak_deps=False packaging/rpm/*.spec
# Required for jobber makefile
echo '#!/usr/bin/env bash
dnf builddep $*' > /usr/local/bin/yum-builddep
chmod u+x /usr/local/bin/yum-builddep

make -C packaging/rpm pkg-local "DESTDIR=/tmp/"
make -C packaging/rpm pkg-local "DESTDIR=/build/"

# Cleanup
dnf history rollback -y last-2
rm -rf /tmp/jobber
16 changes: 6 additions & 10 deletions bin/misp_compile_php_extensions.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ download_and_check () {
rm -f package.tar.gz
}

mkdir /tmp/php-modules/
mkdir /build/php-modules/

# Compile igbinary
mkdir /tmp/igbinary
Expand All @@ -21,7 +21,7 @@ phpize
./configure --silent CFLAGS="-O2 -g" --enable-igbinary
make -j2
make install # `make install` is necessary, so redis extension can be compiled with `--enable-redis-igbinary`
mv modules/*.so /tmp/php-modules/
mv modules/*.so /build/php-modules/

# Compile redis
mkdir /tmp/redis
Expand All @@ -30,7 +30,7 @@ download_and_check https://github.com/phpredis/phpredis/archive/refs/tags/5.3.5.
phpize
./configure --silent --enable-redis-igbinary
make -j2
mv modules/*.so /tmp/php-modules/
mv modules/*.so /build/php-modules/

# Compile ssdeep
mkdir /tmp/ssdeep
Expand All @@ -39,7 +39,7 @@ download_and_check https://github.com/php/pecl-text-ssdeep/archive/refs/tags/1.1
phpize
./configure --silent --with-ssdeep=/usr --with-libdir=lib64
make -j2
mv modules/*.so /tmp/php-modules/
mv modules/*.so /build/php-modules/

# Compile brotli
mkdir /tmp/brotli
Expand All @@ -48,7 +48,7 @@ download_and_check https://github.com/kjdev/php-ext-brotli/archive/refs/tags/0.1
phpize
./configure --silent --with-libbrotli
make -j2
mv modules/*.so /tmp/php-modules/
mv modules/*.so /build/php-modules/

# Compile snuffleupagus
mkdir /tmp/snuffleupagus
Expand All @@ -58,8 +58,4 @@ cd src
phpize
./configure --silent --enable-snuffleupagus
make -j2
mv modules/*.so /tmp/php-modules/

# Cleanup
cd /tmp
rm -rf /tmp/igbinary /tmp/redis /tmp/ssdeep /tmp/brotli /tmp/snuffleupagus
mv modules/*.so /build/php-modules/
25 changes: 11 additions & 14 deletions bin/misp_create_configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"MISP_MODULE_URL", "MISP_ATTACHMENT_SCAN_MODULE", "SECURITY_ADVANCED_AUTHKEYS", "SECURITY_HIDE_ORGS",
"OIDC_DEFAULT_ORG", "SENTRY_ENVIRONMENT", "MISP_DEBUG", "SUPPORT_EMAIL", "PHP_SNUFFLEUPAGUS",
"SECURITY_ENCRYPTION_KEY", "PHP_TIMEZONE", "PHP_MEMORY_LIMIT", "PHP_MAX_EXECUTION_TIME", "PHP_UPLOAD_MAX_FILESIZE",
"MYSQL_PORT",
)
bool_variables = (
"PHP_XDEBUG_ENABLED", "PHP_SESSIONS_IN_REDIS", "ZEROMQ_ENABLED", "OIDC_LOGIN",
Expand All @@ -37,6 +38,9 @@
"PHP_MEMORY_LIMIT": "2048M",
"PHP_MAX_EXECUTION_TIME": "300",
"PHP_UPLOAD_MAX_FILESIZE": "50M",
"MYSQL_PORT": "3306",
"SYSLOG_PORT": "601",
"SYSLOG_PROTOCOL": "tcp",
}


Expand All @@ -63,6 +67,9 @@ def collect() -> dict:
for bool_variable in bool_variables:
variables[bool_variable] = convert_bool(bool_variable, variables[bool_variable])

for int_variable in ("MISP_HOST_ORG_ID", "PHP_MAX_EXECUTION_TIME", "MYSQL_PORT", "SYSLOG_PORT"):
variables[int_variable] = convert_int(int_variable, variables[int_variable])

return variables


Expand All @@ -75,9 +82,9 @@ def convert_int(variable_name: str, input_string: str) -> int:

def convert_bool(variable_name: str, input_string: str) -> bool:
value = input_string.lower()
if value in ("true", "1", "yes"):
if value in ("true", "1", "yes", "on"):
return True
if value in ("false", "0", "no", ""):
if value in ("false", "0", "no", "off", ""):
return False

error("Environment variable '{}' must be boolean (`true`, `1`, `yes`, `false`, `0` or `no`), `{}` given".format(variable_name, input_string))
Expand Down Expand Up @@ -140,17 +147,10 @@ def generate_sessions_in_redis_config(enabled: bool, redis_host: str, redis_pass
open(config_path, "w").write(config)


def generate_rsyslog_config(syslog_target: Optional[str], syslog_port: Optional[str] = None,
syslog_protocol: Optional[str] = None):
def generate_rsyslog_config(syslog_target: Optional[str], syslog_port: int, syslog_protocol: str):
if not syslog_target:
return

if not syslog_port:
syslog_port = 601

if not syslog_protocol:
syslog_protocol = "tcp"

# Recommended setting from https://github.com/grafana/loki/blob/master/docs/clients/promtail/scraping.md#rsyslog-output-configuration
config_template = """
action(type="omfwd" target="{syslog_target}" port="{syslog_port}" protocol="{syslog_protocol}"
Expand Down Expand Up @@ -187,9 +187,6 @@ def generate_php_config(variables: dict):
def main():
variables = collect()

variables["MISP_HOST_ORG_ID"] = convert_int("MISP_HOST_ORG_ID", variables["MISP_HOST_ORG_ID"])
variables["PHP_MAX_EXECUTION_TIME"] = convert_int("PHP_MAX_EXECUTION_TIME", variables["PHP_MAX_EXECUTION_TIME"])

baseurl = urlparse(variables["MISP_BASEURL"])
if baseurl.scheme not in ("http", "https"):
error("Environment variable 'MISP_BASEURL' must start with 'http://' or 'https://'")
Expand Down Expand Up @@ -235,7 +232,7 @@ def main():
generate_sessions_in_redis_config(variables["PHP_SESSIONS_IN_REDIS"], variables["REDIS_HOST"], variables["REDIS_PASSWORD"])
generate_apache_config(variables)
generate_rsyslog_config(variables["SYSLOG_TARGET"], variables["SYSLOG_PORT"], variables["SYSLOG_PROTOCOL"])
generate_error_messages(variables["SUPPORT_EMAIL"] if "SUPPORT_EMAIL" in variables["SUPPORT_EMAIL"] else "no@example.com")
generate_error_messages(variables["SUPPORT_EMAIL"])
generate_php_config(variables)


Expand Down
Loading