Skip to content
Open
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: 2 additions & 0 deletions Config/config.php
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@
'max_job_history_ttl' => 86400,
'supervisor_host' => 'unix:/run/supervisor/supervisor.sock',
'supervisor_port' => 9001,
'supervisor_username' => '{{ SUPERVISOR_USERNAME | default("supervisor") }}',
'supervisor_password' => '{{ SUPERVISOR_PASSWORD | default("changeme") }}',
],
'GnuPG' => [
'onlyencrypted' => false,
Expand Down
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,15 @@ MISP requires MySQL or MariaDB database.
* `MYSQL_DATABASE` (required, string) - database name
* `MYSQL_SETTINGS` (optional, string) - database settings, which should be set for each db connection (JSON dict, or semicolon separated key value pairs)
* `MYSQL_FLAGS` (required, string) - PDO flags which should be set for each db connection (JSON dict, or semicolon separated key value pairs)
* `MYSQL_TLS_CA_PATH` (optional, string) - Path to Certificate authority that is used to validate TLS connections with the database (See below)
* `MYSQL_TLS_CERT` (optional, string) - Path to Certificate that is used to identify mTLS connections with the database (See below)
* `MYSQL_TLS_KEY` (optional, string) - Path to Key that is used to identify mTLS connections with the database (See below)

#### TLS with MySQL

In order to use TLS when communicating with MySQL you need to have a path to a CA in `MYSQL_TLS_CA_PATH`. You also need to have corresponding PDO flags set: `MYSQL_FLAGS: "PDO::ATTR_STRINGIFY_FETCHES=true;PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT=true;PDO::MYSQL_ATTR_SSL_CA='/path/to/ca.crt'"` (Note: You may need to escape the single quotes depending on how you implement the container).

To use mTLS you also need to add the cert and keys and the PDO flags (`PDO::MYSQL_ATTR_SSL_CERT` and `PDO::MYSQL_ATTR_SSL_KEY`) as well as configure your MySQL settings to ensure TLS is used.

### Redis

Expand Down Expand Up @@ -208,7 +217,8 @@ If provided time string is empty, the job will be disabled.
### Supervisor

Supervisor is used to run all processes within the container, you can adjust the amount of workers that should be started by modifying these variables:

* `SUPERVISOR_USERNAME` (required, string, default `supervisor`) - Username for the supervisor user
* `SUPERVISOR_PASSWORD` (required, string, default `changeme`) - Password for the supervisor user
* `DEFAULT_WORKERS` (optional, int, default `1`) - number of default workers to start
* `EMAIL_WORKERS` (optional, int, default `3`) - number of email workers to start
* `CACHE_WORKERS` (optional, int, default `1`) - number of cache workers to start
Expand Down
2 changes: 2 additions & 0 deletions bin/misp_create_configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,8 @@ def parse_mysql_settings(variable_name: str, value: str) -> dict:
"PRIO_WORKERS": Option(typ=int, default=3, validation=check_uint),
"UPDATE_WORKERS": Option(typ=int, default=1, validation=check_uint),
"SCHEDULER_WORKERS": Option(typ=int, default=1, validation=check_uint),
"SUPERVISOR_USERNAME": Option(default="supervisor"),
"SUPERVISOR_PASSWORD": Option(default="changeme", sensitive=True),
}

CONFIG_CREATED_CANARY_FILE = "/.misp-configs-created"
Expand Down
63 changes: 55 additions & 8 deletions bin/misp_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import logging
import subprocess
import requests
import base64
import misp_redis_ready


Expand All @@ -22,20 +23,47 @@ def connect(self):


class UnixStreamTransport(xmlrpc.client.Transport, object):
def __init__(self, socket_path):
def __init__(self, socket_path, username=None, password=None):
self.socket_path = socket_path
self.username = username
self.password = password
self.verbose = False
super().__init__()

def make_connection(self, host):
return UnixStreamHTTPConnection(self.socket_path)

def request(self, host, handler, request_body, verbose=False):
# Build connection and headers similar to xmlrpc.client.Transport.request
conn = self.make_connection(host)
if verbose:
conn.set_debuglevel(1)

class UnixStreamXMLRPCClient(xmlrpc.client.ServerProxy):
def __init__(self, addr, **kwargs):
transport = UnixStreamTransport(addr)
super().__init__(
"http://", transport=transport, **kwargs
)
headers = {
"Content-Type": "text/xml",
"User-Agent": self.user_agent,
"Content-Length": str(len(request_body)),
}

if self.username and self.password:
credentials = base64.b64encode(f"{self.username}:{self.password}".encode()).decode()
headers["Authorization"] = f"Basic {credentials}"
logging.debug(f"[Supervisor] Sending auth header: Authorization: Basic {credentials[:20]}...")
else:
logging.debug("[Supervisor] No credentials configured")

logging.debug(f"[Supervisor] Headers: {headers}")
conn.request("POST", handler, request_body, headers)
response = conn.getresponse()

logging.debug(f"[Supervisor] Response status: {response.status} {response.reason}")
logging.debug(f"[Supervisor] Response headers: {response.getheaders()}")

if response.status != 200:
# Match xmlrpc.client.Transport.request behavior by raising ProtocolError
raise xmlrpc.client.ProtocolError(host + handler, response.status, response.reason, response.getheaders())

return self.parse_response(response)


class SubprocessException(Exception):
Expand All @@ -46,7 +74,15 @@ def __init__(self, process: subprocess.CompletedProcess):


s = requests.Session()
supervisor_api = UnixStreamXMLRPCClient("/run/supervisor/supervisor.sock")

# Get supervisor credentials from environment
SUPERVISOR_USERNAME = os.environ.get('SUPERVISOR_USERNAME', 'supervisor')
SUPERVISOR_PASSWORD = os.environ.get('SUPERVISOR_PASSWORD', 'changeme')
SUPERVISOR_SOCKET = "/run/supervisor/supervisor.sock"

# Create transport with authentication
transport = UnixStreamTransport(SUPERVISOR_SOCKET, SUPERVISOR_USERNAME, SUPERVISOR_PASSWORD)
supervisor_api = xmlrpc.client.ServerProxy("http://", transport=transport)


def check_supervisor():
Expand Down Expand Up @@ -116,6 +152,17 @@ def check_redis():


def main() -> dict:
# Enable DEBUG logging only when MISP_DEBUG is set to a truthy value
misp_debug = os.environ.get("MISP_DEBUG", "").lower()
debug_enabled = misp_debug in ("1", "true", "yes", "on")
level = logging.DEBUG if debug_enabled else logging.INFO
logging.basicConfig(level=level, format='%(levelname)s:%(name)s:%(message)s')

# Reduce noisy library logs when not debugging
if not debug_enabled:
logging.getLogger('urllib3').setLevel(logging.WARNING)
logging.getLogger('xmlrpc').setLevel(logging.WARNING)

output = {
"supervisor": False,
"httpd": False,
Expand Down
2 changes: 2 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ services:
MISP_MODULE_URL: http://misp-modules
MISP_EMAIL: ahoj@example.com # Please change for production
SECURITY_SALT: PleaseChangeForProduction # Please change for production
SUPERVISOR_USERNAME: supervisor
SUPERVISOR_PASSWORD: PleaseChangeForProduction # Please change for production
ZEROMQ_ENABLED: yes
SYSLOG_ENABLED: no
ECS_LOG_ENABLED: yes
Expand Down
2 changes: 2 additions & 0 deletions supervisor.ini
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ user=root
file=/run/supervisor/supervisor.sock
chmod=0770
chown=root:apache
username={{ SUPERVISOR_USERNAME | default("supervisor") }}
password={{ SUPERVISOR_PASSWORD | default("changeme") }}

{% if SYSLOG_ENABLED %}
[program:rsyslog]
Expand Down