Skip to content

Commit fb7b7b3

Browse files
Merge pull request #190 from cert-ee/162-change-django-wsgi-for-asgi
162 change django wsgi for asgi
2 parents 64d9aab + 19d06e4 commit fb7b7b3

File tree

4 files changed

+207
-56
lines changed

4 files changed

+207
-56
lines changed

docs/src/configuring/web-ui.md

+114-22
Original file line numberDiff line numberDiff line change
@@ -2,47 +2,125 @@
22

33
Cuckoo3 frontend is built with [Django](https://www.djangoproject.com/){:target=_blank}.
44
Default settings can be overwritten in `~/.cuckoocwd/web/web_local_settings.py`.
5-
You can use the frontend in two ways - commandline or [Nginx](https://nginx.org/en/){:target=_blank} and [uWSGI](https://uwsgi-docs.readthedocs.io/en/latest/){:target=_blank}.
5+
You can use the frontend in two ways - commandline or [Nginx](https://nginx.org/en/){:target=_blank} and [WSGI](https://uwsgi-docs.readthedocs.io/en/latest/){:target=_blank} / [ASGI](https://asgi.readthedocs.io/en/latest/){:target=_blank}.
66

7-
## Commandline
7+
!!! note "Recommendation"
8+
We recommend ASGI from Cuckoo version 0.10.0 and on
89

9-
!!! warning "Unverified"
10+
## Generating static content
11+
Before serving Cuckoo web server, you have to generate the static content.
1012

11-
This is from the old documentation and needs verification.
12-
It may contain errors, bugs or outdated information.
13+
**Steps**
1314

14-
This is a development server.
15-
You can start Cuckoo frontend from the commandline with the following command:
15+
1. Set `STATIC_ROOT` variable in `~/.cuckoocwd/web/web_local_settings.py`. Cuckoo user must have access to that path.
1616

17-
```bash
18-
cuckoo web --host <listen ip> --port <listen port>
19-
```
17+
2. Build documentation for Cuckoo. You need to be in `cuckoo3` directory and have Python virtual environment active.
2018

21-
## Serving the web UI with Nginx And uWSGI
19+
cd docs
20+
python3.10 -m pip install -r requirements.txt
21+
mkdocs build
22+
cp -R site ../web/cuckoo/web/static/docs
23+
24+
3. Run Django `collectstatic` command.
25+
26+
cuckoo web djangocommand collectstatic
27+
28+
## Serving the web UI with Daphne(ASGI) and Nginx
2229

2330
!!! note "Requirements"
2431

2532
Please make sure that you have:
2633

27-
- installed all dependencies for [serving API and web](../installing/dependencies.md#serving-api-and-web){:target=_blank}
34+
- installed all dependencies for [serving API and web - ASGI](../installing/dependencies.md#asgi){:target=_blank}
2835

29-
If you want to serve Cuckoo in an environment such as development, testing, staging or production, you need to use uWSGI and Nginx.
30-
uWSGI is used as an application server and Nginx as a webserver.
36+
Daphne is used as an ASGI application server and Nginx as a webserver.
3137

3238
**Steps**
3339

34-
1. Set `STATIC_ROOT` variable in `~/.cuckoocwd/web/web_local_settings.py`. Cuckoo user must have access to that path.
40+
1. Create a systemd service for Daphne
41+
42+
sudo cat <<EOF > /etc/systemd/system/cuckoo-asgi.service
43+
[Unit]
44+
Description=Daphne ASGI Server
45+
After=network.target
46+
47+
[Service]
48+
User=cuckoo
49+
Group=cuckoo
50+
WorkingDirectory=/home/cuckoo/cuckoo3/web/cuckoo/web
51+
ExecStart=/home/cuckoo/cuckoo3/venv/bin/daphne -p 9090 cuckoo.web.web.asgi:application
52+
Environment=CUCKOO_APP=web
53+
Environment=CUCKOO_CWD=/home/cuckoo/.cuckoocwd
54+
Environment=CUCKOO_LOGLEVEL=DEBUG
55+
Restart=always
56+
57+
[Install]
58+
WantedBy=multi-user.target
59+
EOF
60+
61+
2. Enable and start ASGI service
62+
63+
sudo systemctl enable cuckoo-asgi.service && \
64+
sudo systemctl start cuckoo-asgi.service
65+
66+
3. Create a new configuration for Nginx
67+
68+
sudo cat <<EOF > /etc/nginx/sites-available/cuckoo-web.conf
69+
upstream _asgi_server {
70+
server 127.0.0.1:9090;
71+
}
72+
73+
server {
74+
listen 8080;
75+
76+
# Directly serve the static files for Cuckoo web. Copy
77+
# (and update these after Cuckoo updates) these by running:
78+
# 'cuckoo web djangocommand collectstatic'. The path after alias should
79+
# be the same path as STATIC_ROOT. These files can be cached. Be sure
80+
# to clear the cache after any updates.
81+
location /static {
82+
alias /opt/cuckoo3/static;
83+
}
84+
85+
# Pass any non-static requests to the Cuckoo web wsgi application run
86+
# by uwsgi. It is not recommended to cache paths here, this can cause
87+
# the UI to no longer reflect the correct state of analyses and tasks.
88+
location / {
89+
proxy_set_header Host $host; # Ensures Host header is preserved
90+
proxy_set_header X-Real-IP $remote_addr;
91+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
92+
proxy_set_header X-Forwarded-Proto $scheme;
93+
client_max_body_size 1G;
94+
proxy_redirect off;
95+
proxy_pass http://_asgi_server;
96+
}
97+
}
98+
EOF
99+
100+
4. Create symlink to enable Nginx configuration.
35101

36-
2. Build documentation for Cuckoo. You need to be in `cuckoo3` directory and have Python virtual environment active.
102+
sudo ln -s /etc/nginx/sites-available/cuckoo-web.conf /etc/nginx/sites-enabled/cuckoo-web.conf
37103

38-
cd docs
39-
python3.10 -m pip install -r requirements.txt
40-
mkdocs build
41-
cp -R site ../web/cuckoo/web/static/docs
104+
5. Delete Nginx default enabled configuration.
42105

43-
3. Run Django `collectstatic` command.
106+
sudo rm /etc/nginx/sites-enabled/default
44107

45-
cuckoo web djangocommand collectstatic
108+
6. Reload the new Nginx configuration.
109+
110+
sudo systemctl reload nginx
111+
112+
113+
## Serving the web UI with uWSGI(WSGI) and Nginx
114+
115+
!!! note "Requirements"
116+
117+
Please make sure that you have:
118+
119+
- installed all dependencies for [serving API and web - WSGI](../installing/dependencies.md#wsgi){:target=_blank}
120+
121+
uWSGI is used as WSGI application server and Nginx as a webserver.
122+
123+
**Steps**
46124

47125
4. Generate uWSGI configuration.
48126

@@ -76,3 +154,17 @@ uWSGI is used as an application server and Nginx as a webserver.
76154

77155
sudo systemctl reload nginx
78156

157+
158+
## Commandline
159+
160+
!!! warning "Unverified"
161+
162+
This is from the old documentation and needs verification.
163+
It may contain errors, bugs or outdated information.
164+
165+
This is a development server.
166+
You can start Cuckoo frontend from the commandline with the following command:
167+
168+
```bash
169+
cuckoo web --host <listen ip> --port <listen port>
170+
```

docs/src/installing/dependencies.md

+14
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ VMCloak uses QEMU to create and manage virtual machines.
133133
|`qemu-system-x86`|full system emulation binaries to emulate the following x86 hardware: x86_64|
134134

135135
### Serving API and web
136+
#### WSGI
136137
```bash
137138
sudo apt-get install -y uwsgi uwsgi-plugin-python3 nginx
138139
```
@@ -143,3 +144,16 @@ sudo apt-get install -y uwsgi uwsgi-plugin-python3 nginx
143144
|`uwsgi-plugin-python3`|Python plugin for uWSGI|
144145
|`nginx`|Webserver|
145146

147+
#### ASGI
148+
Install Python dependencies inside Cuckoo virtual environment
149+
```bash
150+
python3.10 -m pip install daphne
151+
sudo apt-get install -y nginx
152+
```
153+
154+
| Package | Description |
155+
|---|---|
156+
|`daphne`|Django ASGI server|
157+
|`nginx`|Webserver|
158+
159+

web/cuckoo/web/web/asgi.py

+8-3
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,13 @@
99

1010
import os
1111

12-
from django.core.asgi import get_asgi_application
12+
if os.environ.get("CUCKOO_APP", "").lower() == "web":
13+
from cuckoo.web.web.startup import init_and_get_asgi
1314

14-
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'web.settings')
15+
application = init_and_get_asgi()
1516

16-
application = get_asgi_application()
17+
else:
18+
from django.core.asgi import get_asgi_application
19+
20+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "web.settings")
21+
application = get_asgi_application()

web/cuckoo/web/web/startup.py

+71-31
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@
99
from cuckoo.common import shutdown
1010
from cuckoo.common.log import exit_error, name_to_level
1111
from cuckoo.common.startup import (
12-
init_global_logging, load_configuration, init_database,
13-
init_elasticsearch, StartupError
12+
init_global_logging,
13+
load_configuration,
14+
init_database,
15+
init_elasticsearch,
16+
StartupError,
1417
)
1518
from cuckoo.common.storage import cuckoocwd, Paths, CWD_ENVVAR, CWDError
1619
from cuckoo.common.submit import settings_maker
@@ -31,38 +34,32 @@ def set_path_settings():
3134
def _init_remote_storage():
3235
api = APIClient(
3336
cfg("web.yaml", "remote_storage", "api_url", subpkg="web"),
34-
cfg("web.yaml", "remote_storage", "api_key", subpkg="web")
37+
cfg("web.yaml", "remote_storage", "api_key", subpkg="web"),
3538
)
3639
retriever.set_api_client(api)
3740

3841

3942
def _init_elasticsearch_web():
4043
hosts = cfg("web.yaml", "elasticsearch", "hosts", subpkg="web")
41-
indices = cfg(
42-
"web.yaml", "elasticsearch", "indices", "names", subpkg="web"
44+
indices = cfg("web.yaml", "elasticsearch", "indices", "names", subpkg="web")
45+
max_window = cfg("web.yaml", "elasticsearch", "max_result_window", subpkg="web")
46+
user = cfg("web.yaml", "elasticsearch", "user", subpkg="web")
47+
password = cfg("web.yaml", "elasticsearch", "password", subpkg="web")
48+
ca_certs = cfg("web.yaml", "elasticsearch", "ca_certs", subpkg="web")
49+
50+
init_elasticsearch(
51+
hosts,
52+
indices,
53+
max_result_window=max_window,
54+
create_missing_indices=False,
55+
user=user,
56+
password=password,
57+
ca_certs=ca_certs,
4358
)
44-
max_window = cfg(
45-
"web.yaml", "elasticsearch", "max_result_window", subpkg="web"
46-
)
47-
user = cfg(
48-
"web.yaml", "elasticsearch", "user", subpkg="web"
49-
)
50-
password = cfg(
51-
"web.yaml", "elasticsearch", "password", subpkg="web"
52-
)
53-
ca_certs = cfg(
54-
"web.yaml", "elasticsearch", "ca_certs", subpkg="web"
55-
)
56-
57-
init_elasticsearch(hosts, indices, max_result_window=max_window,
58-
create_missing_indices=False,
59-
user=user, password=password, ca_certs=ca_certs)
6059

6160

6261
def _init_statistics_web():
63-
charts = cfg(
64-
"web.yaml", "elasticsearch", "statistics", "charts", subpkg="web"
65-
)
62+
charts = cfg("web.yaml", "elasticsearch", "statistics", "charts", subpkg="web")
6663

6764
try:
6865
for chart in charts:
@@ -103,12 +100,8 @@ def init_web(cuckoo_cwd, loglevel, logfile=""):
103100
if cfg("web.yaml", "remote_storage", "enabled", subpkg="web"):
104101
_init_remote_storage()
105102

106-
search = cfg(
107-
"web.yaml", "elasticsearch", "web_search", "enabled", subpkg="web"
108-
)
109-
stats = cfg(
110-
"web.yaml", "elasticsearch", "statistics", "enabled", subpkg="web"
111-
)
103+
search = cfg("web.yaml", "elasticsearch", "web_search", "enabled", subpkg="web")
104+
stats = cfg("web.yaml", "elasticsearch", "statistics", "enabled", subpkg="web")
112105
if search or stats:
113106
_init_elasticsearch_web()
114107

@@ -136,9 +129,55 @@ def djangocommands(*args):
136129
execute_from_command_line(("cuckoo",) + args)
137130

138131

132+
def init_and_get_asgi():
133+
"""Initializes and returns ASGI server application
134+
135+
Args:
136+
None
137+
138+
Returns:
139+
ASGI 3 callable
140+
141+
Raises:
142+
ValueError: When log level is invalid
143+
custom error: When Cuckoo CWD is not set
144+
"""
145+
146+
import logging
147+
from cuckoo.common.log import disable_console_colors
148+
149+
levelname = os.environ.get("CUCKOO_LOGLEVEL")
150+
if not levelname:
151+
loglevel = logging.DEBUG
152+
else:
153+
try:
154+
loglevel = name_to_level(levelname)
155+
except ValueError as e:
156+
exit_error(f"Invalid log level name. {e}")
157+
158+
cwd_path = os.environ.get(CWD_ENVVAR)
159+
if not cwd_path:
160+
exit_error(
161+
f"Cannot start. Environment variable '{CWD_ENVVAR}' must "
162+
f"contain path to Cuckoo CWD."
163+
)
164+
165+
# Disable console colors, because log files for services such as uWSGI
166+
# capture console logs. Color formatting characters can make the log file
167+
# unreadable.
168+
disable_console_colors()
169+
init_web(cwd_path, loglevel)
170+
171+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cuckoo.web.web.settings")
172+
from django.core.asgi import get_asgi_application
173+
174+
return get_asgi_application()
175+
176+
139177
def init_and_get_wsgi():
140178
import logging
141179
from cuckoo.common.log import disable_console_colors
180+
142181
levelname = os.environ.get("CUCKOO_LOGLEVEL")
143182
if not levelname:
144183
loglevel = logging.DEBUG
@@ -161,6 +200,7 @@ def init_and_get_wsgi():
161200
disable_console_colors()
162201
init_web(cwd_path, loglevel)
163202

164-
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'cuckoo.web.web.settings')
203+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cuckoo.web.web.settings")
165204
from django.core.wsgi import get_wsgi_application
205+
166206
return get_wsgi_application()

0 commit comments

Comments
 (0)