fluksio-worker now refuses ws:// and http:// engine and artifact URLs unless they are loopback or --insecure is given; the engine still accepts both. An artifact reference must be sha256: plus 64 hex digits before it is joined into a path, and node code gets the host environment through the engine's credential denylist instead of all of it.
78 lines
2.7 KiB
Python
78 lines
2.7 KiB
Python
"""The remote worker's own guards: where its token goes, and what node code gets."""
|
|
|
|
import argparse
|
|
|
|
import pytest
|
|
from fluksio_worker import agent
|
|
from fluksio_worker.worker_main import _Reporter
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("url", "trusted"),
|
|
[
|
|
("wss://engine.example.com/api/v1/workers/attach", True),
|
|
("https://engine.example.com/api/v1/artifacts", True),
|
|
("ws://127.0.0.1:8000/api/v1/workers/attach", True),
|
|
("ws://127.1.2.3:8000/api/v1/workers/attach", True),
|
|
("ws://[::1]:8000/api/v1/workers/attach", True),
|
|
("ws://localhost:8000/api/v1/workers/attach", True),
|
|
("ws://192.0.2.10:8000/api/v1/workers/attach", False),
|
|
("ws://engine.local:8000/api/v1/workers/attach", False),
|
|
("http://192.0.2.10:8000/api/v1/artifacts", False),
|
|
],
|
|
)
|
|
def test_only_tls_or_this_machine_is_handed_the_token(url: str, trusted: bool) -> None:
|
|
assert agent._encrypted_or_local(url) is trusted
|
|
|
|
|
|
def test_a_cleartext_engine_elsewhere_needs_insecure(
|
|
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
|
|
) -> None:
|
|
url = "ws://192.0.2.10:8000/api/v1/workers/attach"
|
|
with pytest.raises(SystemExit):
|
|
agent.main(["--url", url, "--token", "t"])
|
|
assert "--insecure" in capsys.readouterr().err
|
|
|
|
async def attached(self: agent.Agent) -> None:
|
|
return None
|
|
|
|
monkeypatch.setattr(agent.Agent, "serve_forever", attached)
|
|
assert agent.main(["--url", url, "--token", "t", "--insecure"]) == 0
|
|
|
|
|
|
def test_node_code_does_not_get_the_hosts_credentials(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
monkeypatch.setenv("FLUKSIO_WORKER_TOKEN", "worker-token")
|
|
monkeypatch.setenv("AWS_SECRET_ACCESS_KEY", "not-for-nodes")
|
|
monkeypatch.setenv("CUDA_VISIBLE_DEVICES", "2")
|
|
args = argparse.Namespace(
|
|
url="wss://engine.example.com/api/v1/workers/attach",
|
|
artifact_url="",
|
|
token="worker-token",
|
|
)
|
|
|
|
env = agent.Agent(args).env
|
|
|
|
assert "FLUKSIO_WORKER_TOKEN" not in env
|
|
assert "AWS_SECRET_ACCESS_KEY" not in env
|
|
assert env["CUDA_VISIBLE_DEVICES"] == "2"
|
|
assert "PATH" in env
|
|
# Put back on purpose: the artifact store is reached with it.
|
|
assert env["FLUKSIO_ARTIFACT_URL"] == "https://engine.example.com/api/v1/artifacts"
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"digest",
|
|
[
|
|
"sha256:../../../etc/passwd",
|
|
"sha256:" + "a" * 63,
|
|
"sha256:" + "A" * 64,
|
|
"md5:" + "a" * 64,
|
|
],
|
|
)
|
|
def test_only_a_whole_digest_is_opened(digest: str) -> None:
|
|
"""The digest is joined into a path, so anything else could read any file."""
|
|
with pytest.raises(ValueError, match="not an artifact reference"):
|
|
_Reporter("fluksio").load_artifact({"digest": digest})
|