Skip to content

Commit 3b64c77

Browse files
Merge pull request #751 from DocArmoryTech/poetic-website
migrate website to Poetry and simplify arch.
2 parents 36a504b + c23b60e commit 3b64c77

32 files changed

Lines changed: 2078 additions & 490 deletions

etc/logrotate.d/misp-modules

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/var/log/misp-modules*.log {
2+
daily
3+
missingok
4+
rotate 14
5+
compress
6+
delaycompress
7+
notifempty
8+
copytruncate
9+
create 0640 root root
10+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
[Unit]
2+
Description=MISP modules website
3+
After=network.target misp-modules.service
4+
Requires=misp-modules.service
5+
6+
[Service]
7+
Type=simple
8+
User=misp
9+
Group=misp
10+
WorkingDirectory=/home/misp/misp-modules/website
11+
Environment="PATH=/home/misp/.local/bin:/usr/bin"
12+
EnvironmentFile=/home/misp/misp-modules/website/.env
13+
ExecStart=/home/misp/.local/bin/poetry run gunicorn --workers 4 --bind ${FLASK_URL}:${FLASK_PORT} --access-logfile - --error-logfile - "main:app"
14+
Restart=always
15+
RestartSec=10
16+
StandardOutput=append:/var/log/misp-modules-website_message.log
17+
StandardError=append:/var/log/misp-modules-website_error.log
18+
19+
[Install]
20+
WantedBy=multi-user.target
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[Unit]
2+
Description=MISP modules
3+
After=network.target
4+
5+
[Service]
6+
Type=simple
7+
User=misp
8+
Group=misp
9+
WorkingDirectory=/home/misp/misp-modules
10+
Environment="PATH=/home/misp/.local/bin:/usr/bin"
11+
ExecStart=/usr/bin/env poetry run misp-modules -l 127.0.0.1
12+
Restart=always
13+
RestartSec=10
14+
StandardOutput=append:/var/log/misp-modules_message.log
15+
StandardError=append:/var/log/misp-modules_error.log
16+
17+
[Install]
18+
WantedBy=multi-user.target

website/.flake8

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[flake8]
2+
exclude =
3+
migrations/,
4+
app/static/
5+
6+
per-file-ignores =
7+
__init__.py: F401

website/.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Ignore Python byte‑code cache
2+
__pycache__/
3+
4+
# Ignore the .env file
5+
.env
6+
7+
# Ignore everything in instance/
8+
instance/

website/README.md

Lines changed: 139 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,165 @@
1-
# MISP-module website
1+
# MISP Modules Website
22

3-
Use all modules with a dedicate website without any MISP
3+
Use all MISP modules through a dedicated website without requiring a MISP instance.
44

5-
![home](https://github.com/MISP/misp-modules/blob/main/website/doc/home_misp_module.png?raw=true)
5+
![Home](https://github.com/MISP/misp-modules/blob/main/website/doc/home_misp_module.png?raw=true)
66

7-
![query](https://github.com/MISP/misp-modules/blob/main/website/doc/query_misp_module.png?raw=true)
7+
![Query](https://github.com/MISP/misp-modules/blob/main/website/doc/query_misp_module.png?raw=true)
88

99
## Installation
1010

11-
**It is strongly recommended to use a virtual environment**
11+
The MISP Modules website uses [Poetry](https://python-poetry.org/) for dependency management. It is recommended to install dependencies in a virtual environment managed by Poetry.
1212

13-
If you want to know more about virtual environments, [python has you covered](https://docs.python.org/3/tutorial/venv.html)
13+
### Prerequisites
14+
- Python 3.8 or higher
15+
- Poetry
16+
- `misp-modules` installed in the parent directory (`../`)
1417

15-
```bash
16-
sudo apt-get install screen -y
17-
pip install -r requirements.txt
18-
git submodule init && git submodule update ## Initialize misp-objects submodule
19-
python3 app.py -i ## Initialize db
20-
```
18+
### Steps
19+
1. **Clone the Repository**:
20+
```bash
21+
git clone https://github.com/MISP/misp-modules.git
22+
cd misp-modules/website
23+
```
2124

22-
Don't forget to install **misp-modules**...
25+
2. **Initialize Submodules**:
26+
```bash
27+
git submodule init
28+
git submodule update # Initialize misp-objects submodule
29+
```
2330

24-
## Config
31+
3. **Install Dependencies**:
32+
```bash
33+
poetry install
34+
```
2535

26-
Edit `config.py`
36+
4. **Initialize the Database**:
37+
```bash
38+
poetry run db-init
39+
```
40+
This creates the database (`misp-module.sqlite`), initializes modules, and sets up the admin password (generated in development if not set).
2741

28-
- `SECRET_KEY`: Secret key for the app
42+
5. **Install `misp-modules`**:
43+
Ensure `misp-modules` is installed in the parent directory (`../misp-modules`). Follow the main repository’s instructions for setup.
2944

30-
- `FLASK_URL` : url for the instance
45+
## Configuration
3146

32-
- `FLASK_PORT`: port for the instance
47+
Configuration is managed via a `.env` file in `website/`. Copy the example and edit as needed:
3348

34-
- `MISP_MODULE`: url and port where misp-module is running
49+
```bash
50+
cp .env.example .env
51+
nano .env
52+
```
3553

36-
- `ADMIN_USER`: If True, config page will not be accessible
54+
### `.env` Settings
55+
- `DATABASE_URI`: Database URL (default: `sqlite:///misp-module.sqlite`).
56+
- `SECRET_KEY`: Secure key for the Flask app (generate with `python -c "import secrets; print(secrets.token_hex(16))"` or `openssl rand -hex 16`).
57+
- `FLASK_URL`: Host for the website (default: `127.0.0.1`).
58+
- `FLASK_PORT`: Port for the website (default: `7008`).
59+
- `MISP_MODULE`: URL and port of `misp-modules` (default: `127.0.0.1:6666`).
60+
- `ADMIN_PASSWORD`: Admin user password (optional in development, required in production).
61+
- `QUERIES_LIMIT`: Maximum queries allowed (default: `100`).
62+
- `SESSION_TYPE`: Session storage type (default: `sqlalchemy`).
63+
- `SESSION_SQLALCHEMY_TABLE`: Session table name (default: `flask_sessions`).
64+
- `FLASK_APP`: Flask entry point (default: `main`).
65+
66+
Example `.env`:
67+
```
68+
DATABASE_URI=sqlite:///misp-module.sqlite
69+
SECRET_KEY=your-secure-secret-key
70+
FLASK_URL=127.0.0.1
71+
FLASK_PORT=7008
72+
MISP_MODULE=127.0.0.1:6666
73+
QUERIES_LIMIT=100
74+
SESSION_TYPE=sqlalchemy
75+
SESSION_SQLALCHEMY_TABLE=flask_sessions
76+
FLASK_APP=main
77+
# ADMIN_PASSWORD=your-admin-password # Uncomment and set for production
78+
```
3779

38-
- `ADMIN_PASSWORD`: Password for Admin user if `ADMIN_USER` is True
80+
## Launch
3981

40-
Rename `config.cfg.sample` to `config.cfg` then edit it:
82+
### Development
83+
Run both `misp-modules` and the website in development mode with debug enabled:
4184

42-
- `ADMIN_USER`: If True, config page will not be accessible
85+
```bash
86+
poetry run dev-site
87+
```
4388

44-
- `ADMIN_PASSWORD`: Password for Admin user if `ADMIN_USER` is True
89+
- If `ADMIN_PASSWORD` is unset in `.env`, a random 20-character password is generated and printed.
90+
- Access the website at `http://127.0.0.1:7008` (or as configured).
4591

46-
## Launch
92+
### Production
93+
Use systemd services for production deployment (see **Systemd Services** below). Ensure `ADMIN_PASSWORD` is set in `.env` to avoid startup errors.
94+
95+
## Admin User
96+
97+
If `ADMIN_PASSWORD` is set in `.env`, the admin user is active. Access the login page at `/login` and use the password from `.env` (or the generated password in development).
98+
99+
- **Development**: If `ADMIN_PASSWORD` is unset, a password is generated and printed to the console.
100+
- **Production**: `ADMIN_PASSWORD` must be set in `.env`, or the application will fail to start with an error.
101+
102+
## Database Management
103+
104+
Manage the database with the following commands:
47105

48106
```bash
49-
./launch.sh -l
107+
poetry run db-init # Initialize database and modules
108+
poetry run db-migrate # Generate a new migration
109+
poetry run db-upgrade # Apply migrations
110+
poetry run db-downgrade # Revert the latest migration
50111
```
51112

52-
## Admin user
113+
## Systemd Services
114+
115+
Template systemd service files are provided in `etc/` for `misp-modules` and the website.
116+
117+
### Installation
118+
1. **Copy Service Files**:
119+
```bash
120+
sudo cp website/etc/misp-modules.service /etc/systemd/system/
121+
sudo cp website/etc/misp-modules-website.service /etc/systemd/system/
122+
```
123+
124+
2. **Reload Systemd**:
125+
```bash
126+
sudo systemctl daemon-reload
127+
```
128+
129+
3. **Enable and Start Services**:
130+
```bash
131+
sudo systemctl enable misp-modules.service
132+
sudo systemctl enable misp-modules-website.service
133+
sudo systemctl start misp-modules.service
134+
sudo systemctl start misp-modules-website.service
135+
```
136+
137+
4. **Check Status**:
138+
```bash
139+
sudo systemctl status misp-modules.service
140+
sudo systemctl status misp-modules-website.service
141+
```
142+
143+
Logs are written to `/var/log/misp-modules_*.log` and `/var/log/misp-modules-website_*.log`.
144+
145+
## Log Rotation
146+
147+
Log rotation configurations are provided in `etc/logrotate.d/` to manage service logs.
148+
149+
### Installation
150+
1. **Copy Logrotate Files**:
151+
```bash
152+
sudo cp etc/logrotate.d/misp-modules /etc/logrotate.d/
153+
```
154+
155+
2. **Test Logrotate**:
156+
```bash
157+
sudo logrotate -d /etc/logrotate.d/misp-modules
158+
```
159+
160+
Logs are rotated daily, compressed, and retained for 7 days.
53161

54-
If admin user is active, type `/login` in url to access a login page and type the password wrote in `config.py` in `ADMIN_PASSOWRD`.
162+
## Notes
163+
- Ensure `misp-modules` is installed and running in `../misp-modules`.
164+
- Set a secure `ADMIN_PASSWORD` in `.env` for production.
165+
- Adjust `.service` and `logrotate.d` paths or user settings for your environment.

website/app/__init__.py

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,45 @@
1-
from flask import Flask
2-
from flask_sqlalchemy import SQLAlchemy
3-
from flask_wtf import CSRFProtect
1+
#!/usr/bin/env python3
2+
import os
3+
4+
from flask import Flask, render_template
5+
from flask_login import LoginManager
46
from flask_migrate import Migrate
57
from flask_session import Session
6-
from flask_login import LoginManager
7-
8-
from conf.config import config as Config
9-
import os
8+
from flask_sqlalchemy import SQLAlchemy
9+
from flask_wtf import CSRFProtect
1010

11+
from dotenv import load_dotenv
12+
load_dotenv()
1113

1214
db = SQLAlchemy()
1315
csrf = CSRFProtect()
1416
migrate = Migrate()
1517
session = Session()
1618
login_manager = LoginManager()
1719

20+
1821
def create_app():
1922
app = Flask(__name__)
20-
config_name = os.environ.get("FLASKENV")
2123

22-
app.config.from_object(Config[config_name])
24+
# Configure app from environment variables
25+
app.config["SQLALCHEMY_DATABASE_URI"] = os.getenv(
26+
"DATABASE_URI", "sqlite:///misp-module.sqlite"
27+
)
28+
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
29+
app.config["SESSION_TYPE"] = os.getenv("SESSION_TYPE", "sqlalchemy")
30+
app.config["SESSION_SQLALCHEMY_TABLE"] = os.getenv(
31+
"SESSION_SQLALCHEMY_TABLE", "flask_sessions"
32+
)
33+
app.config["SECRET_KEY"] = os.getenv("SECRET_KEY")
34+
app.config["FLASK_URL"] = os.getenv("FLASK_URL", "127.0.0.1")
35+
app.config["FLASK_PORT"] = int(os.getenv("FLASK_PORT", "7008"))
36+
app.config["MISP_MODULE"] = os.getenv("MISP_MODULE", "127.0.0.1:6666")
2337

24-
Config[config_name].init_app(app)
38+
# Validate critical settings
39+
if not app.config["SECRET_KEY"]:
40+
raise ValueError("SECRET_KEY must be set in .env")
2541

42+
# Initialize extensions
2643
db.init_app(app)
2744
csrf.init_app(app)
2845
migrate.init_app(app, db, render_as_batch=True)
@@ -31,15 +48,21 @@ def create_app():
3148
login_manager.login_view = "account.login"
3249
login_manager.init_app(app)
3350

34-
from .home import home_blueprint
35-
from .history.history import history_blueprint
51+
# Register blueprints
3652
from .account.account import account_blueprint
3753
from .external_tools.external_tools import external_tools_blueprint
54+
from .history.history import history_blueprint
55+
from .home import home_blueprint
56+
3857
app.register_blueprint(home_blueprint, url_prefix="/")
3958
app.register_blueprint(history_blueprint, url_prefix="/")
4059
app.register_blueprint(account_blueprint, url_prefix="/")
4160
app.register_blueprint(external_tools_blueprint, url_prefix="/")
4261
csrf.exempt(home_blueprint)
4362

63+
# Register 404 error handler
64+
@app.errorhandler(404)
65+
def error_page_not_found(e):
66+
return render_template("404.html"), 404
67+
4468
return app
45-

0 commit comments

Comments
 (0)