From 221fc94fab1a258ed208536e3aba73d067aa97de Mon Sep 17 00:00:00 2001 From: Andy Doan Date: Fri, 7 Jul 2023 09:57:03 -0500 Subject: [PATCH 1/5] Remove old print statement mistakely merged Signed-off-by: Andy Doan --- jobserv/api/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/jobserv/api/__init__.py b/jobserv/api/__init__.py index 73711f71..cfd7a7f8 100644 --- a/jobserv/api/__init__.py +++ b/jobserv/api/__init__.py @@ -58,7 +58,6 @@ def unexpected_error(e): "error_msg": str(e), "stack_trace": traceback.format_exc(), } - print(dir(bp)) current_app.logger.exception("Unexpected error caught in BP error handler") return jsendify(data, 500) From 1e7e7c9962c54a95b0ccd099a342bcca976e36b0 Mon Sep 17 00:00:00 2001 From: Andy Doan Date: Fri, 7 Jul 2023 09:53:51 -0500 Subject: [PATCH 2/5] Convert to json_logging This fixes *all* loggers to emit json logging. Signed-off-by: Andy Doan --- jobserv/flask.py | 9 +++++++++ requirements.txt | 1 + 2 files changed, 10 insertions(+) diff --git a/jobserv/flask.py b/jobserv/flask.py index ffde0031..028c0dd8 100644 --- a/jobserv/flask.py +++ b/jobserv/flask.py @@ -9,6 +9,7 @@ from flask.json import JSONEncoder from flask_migrate import Migrate +import json_logging from werkzeug.middleware.proxy_fix import ProxyFix from werkzeug.routing import UnicodeConverter @@ -59,6 +60,14 @@ def create_app(settings_object="jobserv.settings"): app.wsgi_app = ProxyFix(app.wsgi_app) app.config.from_object(settings_object) + # json_logging can only be initialized *once*. When running with gunicorn, + # this gets called a couple times. + if not getattr(create_app, "__logging_hack_initialized", None): + json_logging.init_flask(enable_json=True) + json_logging.init_request_instrument(app) + json_logging.config_root_logger() + create_app.__logging_hack_initialized = True + ProjectConverter.settings = settings_object app.url_map.converters["project"] = ProjectConverter diff --git a/requirements.txt b/requirements.txt index ac4b4aad..ddd8f5ff 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,6 +18,7 @@ google-crc32c==1.1.2 # indirect, 1.1.3 won't build in alpine # gunicorn 20.1.0 switched from os.sendfile to socket.sendfile which causes # a bug in our console tailing: https://github.com/benoitc/gunicorn/commit/2d40e6daceb9735d27bb91d9c32743695de8e01c gunicorn==20.0.4 +json-logging==1.3.0 pykwalify==1.8.0 python-dateutil==2.8.2 pytz==2021.1 From df3325e57741a2ca7a3897b2f132e08325fd1e49 Mon Sep 17 00:00:00 2001 From: Andy Doan Date: Fri, 7 Jul 2023 12:52:14 -0500 Subject: [PATCH 3/5] requirements: Bump the easy requirements We still need to do flask but these are the easy ones. Signed-off-by: Andy Doan --- requirements.txt | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/requirements.txt b/requirements.txt index ddd8f5ff..a367443d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,23 +5,22 @@ Flask==1.1.4 Jinja2==2.11.3 Mako==1.2.2 MarkupSafe==2.0.1 -PyJWT==2.4.0 -PyMySQL==1.0.2 +PyJWT==2.7.0 +PyMySQL==1.1.0 PyYAML==5.4.1 SQLAlchemy==1.4.23 Werkzeug==0.16.1 -bcrypt==3.2.0 -cryptography==41.0.0 +bcrypt==4.0.1 +cryptography==41.0.1 dataclasses==0.6 -google-cloud-storage==1.42.0 -google-crc32c==1.1.2 # indirect, 1.1.3 won't build in alpine +google-cloud-storage==1.44.0 # gunicorn 20.1.0 switched from os.sendfile to socket.sendfile which causes # a bug in our console tailing: https://github.com/benoitc/gunicorn/commit/2d40e6daceb9735d27bb91d9c32743695de8e01c gunicorn==20.0.4 json-logging==1.3.0 pykwalify==1.8.0 python-dateutil==2.8.2 -pytz==2021.1 -requests==2.26.0 -setproctitle==1.2.2 -wheel==0.38.1 +pytz==2023.3 +requests==2.31.0 +setproctitle==1.3.2 +wheel==0.40.0 From 495e508fba9c1b7e915a48cc19f59c902a280bc3 Mon Sep 17 00:00:00 2001 From: Andy Doan Date: Wed, 28 Jun 2023 22:36:46 -0500 Subject: [PATCH 4/5] Add capp-add/cap-drop This will allow us to instruct more fine-grained container security for certain CI runs. Signed-off-by: Andy Doan --- examples/projects/privileged-container.yml | 10 ++++++++++ jobserv/project-schema.yml | 10 ++++++++++ jobserv/project.py | 2 ++ runner/jobserv_runner/handlers/simple.py | 10 ++++++++++ 4 files changed, 32 insertions(+) diff --git a/examples/projects/privileged-container.yml b/examples/projects/privileged-container.yml index 78598424..bf9e4444 100644 --- a/examples/projects/privileged-container.yml +++ b/examples/projects/privileged-container.yml @@ -10,6 +10,16 @@ triggers: privileged: true script: compile + - name: cap-add-drop-example + container: alpine + host-tag: amd64 + cap-add: + - NET_ADMIN + - SYS_ADMIN + cap-drop: + - MKNOD + script: compile + scripts: compile: | #!/bin/sh -ex diff --git a/jobserv/project-schema.yml b/jobserv/project-schema.yml index 6817faf9..8fe5dee1 100644 --- a/jobserv/project-schema.yml +++ b/jobserv/project-schema.yml @@ -107,6 +107,16 @@ mapping: container-auth: type: str required: False + cap-add: + type: seq + required: False + sequence: + - type: str + cap-drop: + type: seq + required: False + sequence: + - type: str privileged: type: bool required: false diff --git a/jobserv/project.py b/jobserv/project.py index 10024cd4..1e7ddf02 100644 --- a/jobserv/project.py +++ b/jobserv/project.py @@ -118,6 +118,8 @@ def get_run_definition(self, dbrun, run, trigger, params, secrets): "container": run["container"], "container-auth": run.get("container-auth"), "privileged": run.get("privileged", False), + "cap-add": run.get("cap-add", None), + "cap-drop": run.get("cap-drop", None), "container-user": run.get("container-user"), "container-entrypoint": run.get("container-entrypoint"), "env": {}, diff --git a/runner/jobserv_runner/handlers/simple.py b/runner/jobserv_runner/handlers/simple.py index 497e3ac8..4b509fb0 100644 --- a/runner/jobserv_runner/handlers/simple.py +++ b/runner/jobserv_runner/handlers/simple.py @@ -237,6 +237,16 @@ def hung_cb(): if self.rundef.get("privileged"): log.info('Running with "--privileged"') cmd.append("--privileged") + if self.rundef.get("cap-add"): + adds = self.rundef.get("cap-add") + log.info("Running with cap-adds: %s", adds) + for add in adds: + cmd.extend(["--cap-add", add]) + if self.rundef.get("cap-drop"): + drops = self.rundef.get("cap-drop") + log.info("Running with cap-drops: %s", drops) + for drop in drops: + cmd.extend(["--cap-drop", drop]) if self.rundef.get("max-mem-bytes"): maxbytes = self.rundef.get("max-mem-bytes") log.info("Running with --memory=%d", maxbytes) From 67645b2cfc8537765926baee69a3b4a75a6ee0ea Mon Sep 17 00:00:00 2001 From: Andy Doan Date: Fri, 7 Jul 2023 16:34:44 -0500 Subject: [PATCH 5/5] api: Add a unique URL for healthchecks Signed-off-by: Andy Doan --- jobserv/api/__init__.py | 4 ++++ jobserv/flask.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/jobserv/api/__init__.py b/jobserv/api/__init__.py index cfd7a7f8..1ae33ef4 100644 --- a/jobserv/api/__init__.py +++ b/jobserv/api/__init__.py @@ -62,3 +62,7 @@ def unexpected_error(e): return jsendify(data, 500) app.register_blueprint(bp) + + @app.route("/healthz") + def _healthz(): + return "" diff --git a/jobserv/flask.py b/jobserv/flask.py index 028c0dd8..f23db402 100644 --- a/jobserv/flask.py +++ b/jobserv/flask.py @@ -64,7 +64,7 @@ def create_app(settings_object="jobserv.settings"): # this gets called a couple times. if not getattr(create_app, "__logging_hack_initialized", None): json_logging.init_flask(enable_json=True) - json_logging.init_request_instrument(app) + json_logging.init_request_instrument(app, exclude_url_patterns=["/healthz"]) json_logging.config_root_logger() create_app.__logging_hack_initialized = True