Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Seccomp #356

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
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
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ webui_test: venv
server: venv
PYTHONPATH=.:$(VENV) $(VENV)/python -u redbot/daemon.py config.txt

.PHONY: trace
trace: venv
PYTHONPATH=.:$(VENV) strace -qqq -f -e openat $(VENV)/python -u redbot/daemon.py config.txt

.PHONY: cli
cli: venv
PYTHONPATH=.:$(VENV) $(VENV)/python redbot/cli.py $(filter-out $@,$(MAKECMDGOALS))
Expand Down
2 changes: 1 addition & 1 deletion Makefile.pyproject
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ lint_py: venv

.PHONY: typecheck_py
typecheck_py: venv
PYTHONPATH=$(VENV) $(VENV)/python -m mypy $(PROJECT)
PYTHONPATH=$(VENV) $(VENV)/python -m mypy --platform=linux $(PROJECT)

## Release

Expand Down
7 changes: 3 additions & 4 deletions config.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ max_sample_size = 8192
## Web server configuration

# Hostname to listen on. Comment out to listen on all interfaces.
host = localhost
# host = localhost

# Port to listen on.
port = 8000
Expand Down Expand Up @@ -67,10 +67,9 @@ static_root = static

## Saved Tests

# Where to keep files when users `save` them. Note that files will be automatically
# deleted from this directory, so it needs to be only used for REDbot.
# Directory to keep test results when users `save` them.
# Comment out to disable saving.
save_dir = /tmp/redbot/
save_dir = /tmp/

# How long to store things when users save them, in days.
save_days = 30
Expand Down
5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,15 @@ classifiers = [
"License :: OSI Approved :: MIT License"
]
dependencies = [
"httplint >= 2023.12.2",
"httplint >= 2024.01.1",
"importlib_resources",
"Jinja2 >= 3.1.2",
"markdown >= 3.4.4",
"MarkupSafe >= 2.1.3",
"netaddr >= 0.9.0",
"thor >= 0.9.6",
"thor >= 0.11.0",
"typing-extensions >= 4.8.0",
"pyseccomp; sys_platform == 'linux'"
]

[project.urls]
Expand Down
1 change: 1 addition & 0 deletions redbot/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from redbot import __version__
from redbot.formatter import find_formatter, available_formatters
from redbot.resource import HttpResource
from redbot.formatter import * # pylint: disable=wildcard-import,unused-wildcard-import


def main() -> None:
Expand Down
50 changes: 34 additions & 16 deletions redbot/daemon.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,24 @@
import locale
import os
from pstats import Stats
import signal
import sys
import traceback
from typing import Dict, Optional
from types import FrameType
from typing import Dict, Optional, Any, Union
from urllib.parse import urlsplit

from importlib_resources import files as resource_files

import httplint
import thor
from thor.loop import _loop

import redbot
from redbot.type import RawHeaderListType
from redbot.util.linux import enable_seccomp
from redbot.util.python import module_files
from redbot.webui import RedWebUi
from redbot.webui.saved_tests import clean_saved_tests
from redbot.webui.saved_tests import SavedTests
from redbot.formatter import * # pylint: disable=wildcard-import,unused-wildcard-import

if os.environ.get("SYSTEMD_WATCHDOG"):
try:
Expand Down Expand Up @@ -59,11 +62,24 @@ def __init__(self, config: SectionProxy) -> None:

# Read static files
self.static_root = os.path.join("/", config["static_root"]).encode("ascii")
self.static_files = resource_files("redbot.assets")
self.static_files = module_files("redbot.assets")
self.extra_files = {}
if self.config.get("extra_base_dir"):
self.extra_files = self.walk_files(self.config["extra_base_dir"])

# Set up signal handlers
signal.signal(signal.SIGINT, self.shutdown_handler)
signal.signal(signal.SIGABRT, self.shutdown_handler)
signal.signal(signal.SIGTERM, self.shutdown_handler)

# open the save db
self.saved = SavedTests(config, self.console)

# turn on seccomp
seccomp_enabled = enable_seccomp(True)
if seccomp_enabled:
self.console("Seccomp enabled.")

# Start garbage collection
if config.get("save_dir", ""):
thor.schedule(10, self.gc_state)
Expand All @@ -74,20 +90,22 @@ def __init__(self, config: SectionProxy) -> None:
self.config.getint("port", fallback=8000),
)
server.on("exchange", self.handler)
try:
thor.run()
except KeyboardInterrupt:
self.console("Stopping...")
thor.stop()
thor.run()

def watchdog_ping(self) -> None:
notify(Notification.WATCHDOG)
thor.schedule(self.watchdog_freq, self.watchdog_ping)

def gc_state(self) -> None:
clean_saved_tests(self.config)
self.saved.clean()
thor.schedule(self.config.getint("gc_mins", fallback=2) * 60, self.gc_state)

def shutdown_handler(self, sig: int, frame: Union[FrameType, None]) -> Any:
self.console("Stopping...")
thor.stop()
self.saved.shutdown()
sys.exit(0)

def walk_files(self, dir_name: str, uri_base: bytes = b"") -> Dict[bytes, bytes]:
out: Dict[bytes, bytes] = {}
for root, _, files in os.walk(dir_name):
Expand Down Expand Up @@ -158,6 +176,7 @@ def request_done(self, trailers: RawHeaderListType) -> None:
try:
RedWebUi(
self.server.config,
self.server.saved,
self.method.decode(self.server.config["charset"]),
p_uri.query,
self.req_hdrs,
Expand All @@ -179,6 +198,7 @@ def request_done(self, trailers: RawHeaderListType) -> None:
dump = traceback.format_exc()
thor.stop()
self.server.console(dump)
self.server.saved.shutdown()
sys.exit(1)
else:
return self.serve_static(p_uri.path)
Expand All @@ -188,11 +208,8 @@ def serve_static(self, path: bytes) -> None:
if path.startswith(self.server.static_root + b"/"):
path = b"/".join(path.split(b"/")[2:])
try:
with self.server.static_files.joinpath(path.decode("ascii")).open(
mode="rb"
) as fh:
content = fh.read()
except OSError:
content = self.server.static_files[path.decode("ascii")]
except KeyError:
return self.not_found(path)
else:
try:
Expand Down Expand Up @@ -252,6 +269,7 @@ def main() -> None:
args = parser.parse_args()
conf = ConfigParser()
conf.read_file(args.config_file)
args.config_file.close()

try:
locale.setlocale(locale.LC_ALL, locale.normalize(conf["redbot"]["lang"]))
Expand Down
11 changes: 7 additions & 4 deletions redbot/formatter/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
Formatters for REDbot output.
"""


from collections import defaultdict
from configparser import SectionProxy
import inspect
Expand All @@ -20,7 +19,12 @@
if TYPE_CHECKING:
from redbot.resource import HttpResource # pylint: disable=cyclic-import

_formatters = ["html", "text", "har"]
_formatters = [
"html",
"text",
"har",
]
__all__ = ["html", "text", "har", "slack"]


def find_formatter(
Expand All @@ -37,9 +41,8 @@ def find_formatter(
name = default
try:
module_name = f"redbot.formatter.{name}"
__import__(module_name)
module = sys.modules[module_name]
except (ImportError, KeyError, TypeError):
except (KeyError, TypeError):
return find_formatter(default)
formatter_candidates = [
v
Expand Down
1 change: 0 additions & 1 deletion redbot/formatter/har.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
HAR Formatter for REDbot.
"""


import datetime
import json
from typing import Optional, Any, Dict, List
Expand Down
11 changes: 9 additions & 2 deletions redbot/formatter/html_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@
from urllib.parse import urljoin, urlencode, quote as urlquote

import httplint
from jinja2 import Environment, PackageLoader, select_autoescape
from jinja2 import Environment, DictLoader, select_autoescape
from markupsafe import Markup, escape
from typing_extensions import Unpack

import redbot
from redbot.formatter import Formatter, FormatterArgs, relative_time, f_num
from redbot.util.python import module_files
from redbot.webui.captcha import CAPTCHA_PROVIDERS

NL = "\n"
Expand Down Expand Up @@ -58,7 +59,13 @@ class BaseHtmlFormatter(Formatter):

media_type = "text/html"
templates = Environment(
loader=PackageLoader("redbot.formatter"),
loader=DictLoader(
{
k: v.decode("utf-8")
for (k, v) in module_files("redbot.formatter.templates").items()
}
),
auto_reload=False,
trim_blocks=True,
autoescape=select_autoescape(
enabled_extensions=("html", "xml"),
Expand Down
1 change: 1 addition & 0 deletions redbot/resource/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ def __init__(self, config: SectionProxy, descend: bool = False) -> None:
self.ims_support: bool = False
self.gzip_support: bool = False
self.gzip_savings: int = 0
self.save_expires: float = 0.0
self._task_map: Set[RedFetcher] = set([])
self.subreqs = {ac.check_name: ac(config, self) for ac in active_checks}
self.once("fetch_done", self.run_active_checks)
Expand Down
5 changes: 4 additions & 1 deletion redbot/resource/fetch.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,10 @@ def __getstate__(self) -> Dict[str, Any]:
del state["exchange"]
except KeyError:
pass
del state["response_content_processors"]
try:
del state["response_content_processors"]
except KeyError:
pass
return state

def __repr__(self) -> str:
Expand Down
9 changes: 3 additions & 6 deletions redbot/type.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,8 @@
class HttpResponseExchange(Protocol):
def response_start(
self, status_code: bytes, status_phrase: bytes, res_hdrs: RawHeaderListType
) -> None:
...
) -> None: ...

def response_body(self, chunk: bytes) -> None:
...
def response_body(self, chunk: bytes) -> None: ...

def response_done(self, trailers: RawHeaderListType) -> None:
...
def response_done(self, trailers: RawHeaderListType) -> None: ...
1 change: 1 addition & 0 deletions redbot/util/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# intentionally left blank
Loading
Loading