Skip to content

Commit b9e7a47

Browse files
committed
Merge branch 'master' into feature/related-files
2 parents 52a3497 + 5e81388 commit b9e7a47

File tree

182 files changed

+7247
-29889
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

182 files changed

+7247
-29889
lines changed

deploy/docker/Dockerfile

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,32 @@
1+
FROM python:3.8-alpine AS build
2+
3+
WORKDIR /app
4+
RUN python3 -m venv /app/venv
5+
RUN /app/venv/bin/pip --no-cache-dir install wheel
6+
7+
RUN apk add --no-cache libffi libffi-dev py3-cffi build-base python3-dev automake m4 autoconf libtool gcc g++ musl-dev openssl-dev cargo postgresql-dev
8+
9+
COPY requirements.txt /app
10+
RUN /app/venv/bin/pip --no-cache-dir install -r /app/requirements.txt
11+
12+
COPY docker/plugins /app/plugins
13+
ARG plugins
14+
RUN for plugin in $plugins $(find /app/plugins -name 'setup.py' -exec dirname {} \; | sort -u); \
15+
do /app/venv/bin/pip --no-cache-dir install $plugin; done
16+
117
FROM python:3.8-alpine
218

319
LABEL maintainer="info@cert.pl"
420

521
RUN apk add --no-cache postgresql-client postgresql-dev libmagic
622

7-
COPY requirements.txt docker/plugins/requirements-*.txt /tmp/
8-
RUN apk add --no-cache -t build libffi libffi-dev py3-cffi build-base python3-dev automake m4 autoconf libtool gcc g++ musl-dev openssl-dev cargo \
9-
&& pip --no-cache-dir install -r /tmp/requirements.txt \
10-
&& ls /tmp/requirements-*.txt | xargs -i,, pip --no-cache-dir install -r ,, \
11-
&& apk del build
12-
1323
# Copy backend files
24+
COPY --from=build /app/venv /app/venv
1425
COPY docker/ setup.py MANIFEST.in requirements.txt /app/
1526
COPY mwdb /app/mwdb/
1627

1728
# Install mwdb-core package
18-
RUN pip install /app
29+
RUN /app/venv/bin/pip install /app
1930

2031
# Create a /app/uploads directory
2132
# Give +r to everything in /app and +x for directories

deploy/docker/Dockerfile-web

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,25 @@
1-
FROM node:14 AS build
1+
FROM node:16-alpine AS build
2+
3+
LABEL maintainer="info@cert.pl"
24

35
COPY ./mwdb/web /app
4-
COPY ./docker/plugins /plugins
6+
COPY ./docker/plugins /app/plugins
57

8+
ARG web_plugins
69
RUN cd /app \
7-
&& npm install --unsafe-perm . $(find /plugins -name 'package.json' -printf "%h\n" | sort -u) \
10+
&& npm install --unsafe-perm . $web_plugins $(find /app/plugins -name 'package.json' -exec dirname {} \; | sort -u) \
811
&& CI=true npm run build \
912
&& npm cache clean --force
1013

1114
FROM nginx:stable
1215

16+
LABEL maintainer="info@cert.pl"
17+
1318
ENV PROXY_BACKEND_URL http://mwdb.:8080
1419

1520
COPY docker/nginx.conf.template /etc/nginx/conf.d/default.conf.template
1621
COPY docker/start-web.sh /start-web.sh
17-
COPY --from=build /app/build /usr/share/nginx/html
22+
COPY --from=build /app/dist /usr/share/nginx/html
1823

1924
# Give +r to everything in /usr/share/nginx/html and +x for directories
2025
RUN chmod u=rX,go= -R /usr/share/nginx/html

deploy/docker/Dockerfile-web-dev

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
1-
FROM node:14 AS build
1+
FROM node:16-alpine AS build
2+
3+
LABEL maintainer="info@cert.pl"
24

35
COPY ./mwdb/web /app
4-
COPY ./docker/plugins /plugins
6+
COPY ./docker/plugins /app/plugins
57

8+
ARG web_plugins
69
RUN cd /app \
7-
&& npm install --unsafe-perm . $(find /plugins -name 'package.json' -printf "%h\n" | sort -u) \
10+
&& npm install --unsafe-perm . $web_plugins $(find /app/plugins -name 'package.json' -exec dirname {} \; | sort -u) \
811
&& CI=true npm run build \
912
&& npm cache clean --force
1013

1114
ENV PROXY_BACKEND_URL http://mwdb.:8080
1215

1316
WORKDIR /app
14-
CMD ["npm", "run", "start"]
17+
CMD ["npm", "run", "dev"]

docker-compose-dev-karton.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,7 @@ services:
2626
# NOTE: use gen_vars.sh in order to generate this file
2727
- mwdb-vars.env
2828
environment:
29-
UWSGI_PY_AUTORELOAD: 1
30-
UWSGI_ENABLE_THREADS: 1
29+
HOT_RELOAD: 1
3130
MWDB_MAIL_SMTP: "mailhog:1025"
3231
MWDB_MAIL_FROM: "noreply@mwdb.dev"
3332
MWDB_RECAPTCHA_SITE_KEY: "6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI"

docker-compose-dev-remote.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,7 @@ services:
2525
# NOTE: use gen_vars.sh in order to generate this file
2626
- mwdb-vars.env
2727
environment:
28-
UWSGI_PY_AUTORELOAD: 1
29-
UWSGI_ENABLE_THREADS: 1
28+
HOT_RELOAD: 1
3029
MWDB_MAIL_SMTP: "mailhog:1025"
3130
MWDB_MAIL_FROM: "noreply@mwdb.dev"
3231
MWDB_RECAPTCHA_SITE_KEY: "6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI"

docker-compose-dev.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,7 @@ services:
2525
# NOTE: use gen_vars.sh in order to generate this file
2626
- mwdb-vars.env
2727
environment:
28-
UWSGI_PY_AUTORELOAD: 1
29-
UWSGI_ENABLE_THREADS: 1
28+
HOT_RELOAD: 1
3029
MWDB_MAIL_SMTP: "mailhog:1025"
3130
MWDB_MAIL_FROM: "noreply@mwdb.dev"
3231
MWDB_RECAPTCHA_SITE_KEY: "6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI"
@@ -58,6 +57,7 @@ services:
5857
volumes:
5958
- "./mwdb/web/public:/app/public"
6059
- "./mwdb/web/src:/app/src"
60+
- "./docker/plugins:/app/plugins"
6161
restart: on-failure
6262
postgres:
6363
image: postgres

docker-compose-e2e.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ services:
2323
MWDB_BASE_URL: http://127.0.0.1
2424
MWDB_ENABLE_RATE_LIMIT: 0
2525
MWDB_ENABLE_REGISTRATION: 1
26-
UWSGI_PROCESSES: 4
2726
MWDB_MAIL_SMTP: "mailhog:1025"
2827
MWDB_MAIL_FROM: "noreply@mwdb.dev"
2928
volumes:

docker-compose-oidc-dev.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,7 @@ services:
2525
# NOTE: use gen_vars.sh in order to generate this file
2626
- mwdb-vars.env
2727
environment:
28-
UWSGI_PY_AUTORELOAD: 1
29-
UWSGI_ENABLE_THREADS: 1
28+
HOT_RELOAD: 1
3029
MWDB_MAIL_SMTP: "mailhog:1025"
3130
MWDB_MAIL_FROM: "noreply@mwdb.dev"
3231
MWDB_RECAPTCHA_SITE_KEY: "6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI"

docker/gunicorn.conf.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import os
2+
3+
wsgi_app = "mwdb.app:app"
4+
bind = "0.0.0.0:8080"
5+
user = "nobody"
6+
reload = bool(int(os.getenv("HOT_RELOAD", "0")))
7+
workers = int(os.getenv("GUNICORN_WORKERS", "4"))

docker/start.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ until psql "$MWDB_POSTGRES_URI" -c "\q" ; do
77
done
88

99
echo "Configuring mwdb-core instance"
10-
mwdb-core configure --quiet basic && exec uwsgi --ini /app/uwsgi.ini
10+
/app/venv/bin/mwdb-core configure --quiet basic && exec /app/venv/bin/gunicorn

docker/uwsgi.ini

Lines changed: 0 additions & 11 deletions
This file was deleted.

docs/setup-and-configuration.rst

Lines changed: 54 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,48 @@
11
Setup and configuration
22
=======================
33

4-
Prerequisites
5-
-------------
4+
Installation and configuration with Docker Compose
5+
--------------------------------------------------
6+
7+
The quickest way setup MWDB is to just clone the repository and use Docker-Compose with all batteries included.
8+
9+
.. code-block:: console
10+
11+
$ git clone https://github.com/CERT-Polska/mwdb-core.git
12+
13+
After cloning repository, the first step is to go to the ``mwdb-core`` directory and generate configuration using ``./gen_vars.sh`` script.
14+
Generated variables can be found in mwdb-vars.env.
15+
16+
.. code-block:: console
17+
18+
$ ./gen_vars.sh
19+
Credentials for initial mwdb account:
20+
21+
-----------------------------------------
22+
Admin login: admin
23+
Admin password: la/Z7MsmKA3UxW8Psrk1Opap
24+
-----------------------------------------
25+
26+
Please be aware that initial account will be only set up on the first run. If you already have a database with at least one user, then this setting will be ignored for security reasons. You can always create an admin account manually by executing a command. See "flask create_admin --help" for reference.
27+
28+
Then build images via ``docker-compose build`` and run MWDB via ``docker-compose up -d``.
29+
30+
Your MWDB instance will be available on default HTTP port (80): http://127.0.0.1/
31+
32+
If you want to use Docker Compose for MWDB development, check out :ref:`Developer guide`.
33+
34+
Standalone installation
35+
-----------------------
36+
37+
Step 1.: Prerequisites
38+
~~~~~~~~~~~~~~~~~~~~~~
639

740
MWDB was tested on Debian-based systems, but should work as well on other Linux distributions.
841

942
For production environments, you need to install:
1043

1144

1245
* **PostgreSQL database** (minimum supported version: 12, https://www.postgresql.org/download/linux/debian/)
13-
* **python-ssdeep library dependencies for Python 3** (https://python-ssdeep.readthedocs.io/en/latest/installation.html#id9)
1446

1547
Optionally you can install:
1648

@@ -41,8 +73,8 @@ It's highly recommended to create a fresh `virtualenv <https://docs.python.org/3
4173
4274
The connection string is: ``postgresql://mwdb:mwdb@127.0.0.1:54322/mwdb``
4375

44-
Installation & Configuration
45-
----------------------------
76+
Step 2.: Installation and configuration
77+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4678

4779
The recommended installation method is pip:
4880

@@ -87,14 +119,14 @@ Then, use ``mwdb-core configure`` to provide first configuration for your MWDB s
87119
3) Current directory
88120
: 3
89121
90-
For first installation we recommend to install everything in current folder via ``3`` option. If you want to install MWDB system-wide or locally for user: choose ``1`` or ``2``.
122+
For first installation we recommend to install everything in current folder via ``3`` option. If you want to install MWDB system-wide or locally for user: choose ``1`` or ``2``.
91123

92124
Then, input the connection string for PostgreSQL database. The database must be online and reachable at the time of configuration. After that, you will be asked for path for uploads and instance base URL. If the default value is ok, press Enter:
93125

94126
.. code-block::
95127
96128
PostgreSQL database connection string [postgresql://localhost/mwdb]:
97-
Uploads storage path [./uploads]:
129+
Uploads storage path [./uploads]:
98130
Base public URL of Malwarecage service [http://127.0.0.1]:
99131
100132
Depending on the installation type, your configuration will be stored in ``mwdb.ini`` file and can be changed any time you want:
@@ -136,42 +168,30 @@ And you are done! ``run`` command will start the Flask server:
136168
137169
Your MWDB instance will be available on port 5000 (use ``--port`` to change that): http://127.0.0.1:5000/
138170

139-
.. warning::
140-
141-
Remember to run ``mwdb-core configure`` after each version upgrade to apply database migrations
142-
143-
144-
Alternative setup with Docker Compose
145-
--------------------------------------
171+
Keep in mind that Flask server is meant to be used as development server and **is not suitable for production**.
172+
See also: https://flask.palletsprojects.com/en/2.2.x/server/
146173

147-
The quickest way setup MWDB is to just clone the repository and use Docker-Compose. We recommend this method **only for testing** because it can be a bit more difficult to install extensions and integrate with other services.
148-
149-
.. code-block:: console
150-
151-
$ git clone https://github.com/CERT-Polska/mwdb-core.git
152-
153-
After cloning repository, the first step is to go to the ``mwdb-core`` directory and generate configuration using ``./gen_vars.sh`` script.
174+
.. warning::
154175

155-
.. code-block:: console
176+
In standalone setup, remember to run ``mwdb-core configure`` after each version upgrade to apply database migrations.
156177

157-
$ ./gen_vars.sh
158-
Credentials for initial mwdb account:
178+
Step 3.: Setting up gunicorn and nginx
179+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
159180

160-
-----------------------------------------
161-
Admin login: admin
162-
Admin password: la/Z7MsmKA3UxW8Psrk1Opap
163-
-----------------------------------------
181+
It's recommended to deploy Flask applications using dedicated WSGI server. We highly recommend Gunicorn as it's used
182+
in our Docker images and combine it with Nginx serving as proxy server for best security and performance
164183

165-
Please be aware that initial account will be only set up on the first run. If you already have a database with at least one user, then this setting will be ignored for security reasons. You can always create an admin account manually by executing a command. See "flask create_admin --help" for reference.
184+
.. seealso::
166185

167-
Then build images via ``docker-compose build`` and run MWDB via ``docker-compose up -d``.
186+
https://flask.palletsprojects.com/en/2.2.x/deploying/gunicorn/
168187

169-
Your MWDB instance will be available on default HTTP port (80): http://127.0.0.1/
188+
https://docs.gunicorn.org/en/latest/deploy.html#deploying-gunicorn
170189

171-
If you want to use Docker Compose for MWDB development, check out :ref:`Developer guide`.
190+
Proper configuration files and templates used in our Docker images can be found in `docker directory on our Github repository
191+
<https://github.com/CERT-Polska/mwdb-core/tree/master/docker>`_
172192

173-
Upgrade mwdb-core to latest version
174-
-----------------------------------
193+
Upgrading mwdb-core to latest version
194+
-------------------------------------
175195

176196
For standalone installation (pip-based), upgrade mwdb-core package to the latest version.
177197

docs/user-guide/9-Sharing-objects.rst

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,12 @@ Each capability has its own name and scope:
173173
*
174174
**sharing_with_all - Can share objects with all groups in system**
175175

176-
Implies the access to the list of all group names, but without access to the membership information and management features. Allows to share object with arbitrary group in MWDB.
176+
Implies the access to the list of all group names, but without access to the membership information and management features. Allows to share object with arbitrary group in MWDB. It also allows the user to view full history of sharing an object (if the user has access to the object).
177+
178+
*
179+
**access_uploader_info - Can view who uploaded object and filter by uploader**
180+
181+
Can view who uploaded object and filter by uploader. Without this capability users can filter by / see only users in their workspaces.
177182

178183
*
179184
**adding_tags - Can add tags**

mwdb/core/capabilities.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ class Capabilities(object):
77
access_all_objects = "access_all_objects"
88
# Can share objects with all groups, have access to complete list of groups
99
sharing_with_all = "sharing_with_all"
10+
# Can view who uploaded object and filter by uploader
11+
access_uploader_info = "access_uploader_info"
1012
# Can add tags
1113
adding_tags = "adding_tags"
1214
# Can remove tags

mwdb/core/search/fields.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,17 @@ def _get_condition(
389389
.join(Member.group)
390390
.filter(Group.name == value)
391391
).all()
392+
elif g.auth_user.has_rights(Capabilities.access_uploader_info):
393+
uploaders = (
394+
db.session.query(User)
395+
.join(User.memberships)
396+
.join(Member.group)
397+
.filter(Group.name == value)
398+
).all()
399+
# Regular users can see only uploads to its own groups
400+
condition = and_(
401+
condition, g.auth_user.is_member(ObjectPermission.group_id)
402+
)
392403
else:
393404
uploaders = (
394405
db.session.query(User)

mwdb/model/file.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import tempfile
66

77
import pyzipper
8+
from Cryptodome.Util.strxor import strxor_c
89
from flask import g
910
from sqlalchemy import not_, or_
1011
from sqlalchemy.dialects.postgresql.array import ARRAY
@@ -231,6 +232,32 @@ def iterate(self, chunk_size=1024 * 256):
231232
finally:
232233
File.close(fh)
233234

235+
def iterate_obfuscated(self, chunk_size=1024 * 256):
236+
r"""
237+
Iterates over bytes in the file contents with xor applied
238+
The idea behind xoring before send is to prevent browsers
239+
from caching original samples (malware). Unxoring is provided
240+
in mwdb\web\src\components\ShowSample.js in SamplePreview
241+
"""
242+
243+
def negate_bits(chunk):
244+
return strxor_c(chunk, 255)
245+
246+
fh = self.open()
247+
try:
248+
if hasattr(fh, "stream"):
249+
yield from map(negate_bits, fh.stream(chunk_size))
250+
else:
251+
while True:
252+
chunk = fh.read(chunk_size)
253+
chunk = negate_bits(chunk)
254+
if chunk:
255+
yield chunk
256+
else:
257+
return
258+
finally:
259+
File.close(fh)
260+
234261
@staticmethod
235262
def close(fh):
236263
"""

0 commit comments

Comments
 (0)