authorclover caruso <git@paperclover.net> 2026-10-06 23:11:12-07:00
committerclover caruso <git@paperclover.net> 2026-10-09 11:21:14-07:00
loga938184751633f3e50941c60bbc8ef3566475802
treef38708c3047a0c1eefb2bfcdbd8ff668d1f51f4c
parente4ffc9282a8149a9b8a2cacee98111ddddd8d95c
signature Signed by SSH key SHA256:52mNGHRsVFBDED9IAX5pe+LRWUefqTbxEReunq21QvU (~clover)

Add the isolated OpenClaw trial VM and authenticated gateway


14 files changed, 455 insertions(+), 1 deletions(-)

service/forward-auth/service.pkl+1-1
......@@ -14,7 +14,7 @@ requirements {
1414 name = module.meta.name
1515 redirectUris {
1616 when (isPreview) { "https://\(module.id).\(site.domain)/snow.oauth2/callback" }
17 when (!isPreview) { for (host in new Listing<String> { "music"; "seedbox"; "ddns"; "redis"; "pg"; "snr"; "rdr"; "logs"; "metrics"; "traces"; "jkt" }) {
17 when (!isPreview) { for (host in new Listing<String> { "music"; "seedbox"; "ddns"; "redis"; "pg"; "snr"; "rdr"; "logs"; "metrics"; "traces"; "jkt"; "openclaw" }) {
1818 "https://\(host).\(site.domain)/snow.oauth2/callback"
1919 } }
2020 }
service/openclaw/Dockerfile.guest created+4
......@@ -0,0 +1,4 @@
1FROM ghcr.io/openclaw/openclaw@sha256:032e25ae4a15aa2e6d2653699833e0965e99f580e679024e31e3ddda375e9bcd
2USER root
3RUN apt-get update && apt-get install -y --no-install-recommends python3 python3-venv python3-pip git ripgrep curl build-essential && rm -rf /var/lib/apt/lists/*
4USER node
service/openclaw/install.sh created+22
......@@ -0,0 +1,22 @@
1#!/bin/bash
2set -e
3export http_proxy=http://10.0.2.100:3128 https_proxy=http://10.0.2.100:3128
4export no_proxy=localhost,127.0.0.1,10.0.2.101
5systemctl disable --now systemd-resolved
6rm -f /etc/resolv.conf
7install -m 644 /opt/openclaw/resolv.conf /etc/resolv.conf
8if ! command -v docker >/dev/null; then
9 apt-get -o Acquire::Retries=10 update
10 DEBIAN_FRONTEND=noninteractive apt-get install -y docker.io
11fi
12systemctl daemon-reload
13systemctl restart docker
14mkdir -p /opt/openclaw/state/workspace
15if ! test -f /opt/openclaw/state/openclaw.json; then
16 cp /opt/openclaw/openclaw.json /opt/openclaw/state/openclaw.json
17fi
18chown -R 1000:1000 /opt/openclaw/state
19chmod 700 /opt/openclaw/state
20docker build --build-arg http_proxy --build-arg https_proxy -t openclaw-trial - < /opt/openclaw/Dockerfile
21systemctl enable openclaw
22systemctl restart openclaw
service/openclaw/machine-openclaw.slice created+8
......@@ -0,0 +1,8 @@
1[Unit]
2Description=OpenClaw virtual machine
3Before=libvirtd.service
4
5[Slice]
6TasksMax=512
7MemoryMax=8G
8CPUQuota=400%
service/openclaw/nginx.conf created+47
......@@ -0,0 +1,47 @@
1worker_processes 1;
2pid /tmp/nginx.pid;
3error_log /dev/stderr;
4events { worker_connections 256; }
5http {
6 access_log off;
7 client_body_temp_path /tmp/client;
8 proxy_temp_path /tmp/proxy;
9 fastcgi_temp_path /tmp/fastcgi;
10 uwsgi_temp_path /tmp/uwsgi;
11 scgi_temp_path /tmp/scgi;
12 map $http_upgrade $connection_upgrade { default upgrade; '' close; }
13 server {
14 listen 127.0.0.1:18889;
15 client_max_body_size 32m;
16 location = /healthz { proxy_pass http://127.0.0.1:18890/healthz; }
17 location / {
18 if ($http_x_openclaw_user = '') { return 403; }
19 proxy_pass http://127.0.0.1:18890;
20 proxy_http_version 1.1;
21 proxy_set_header Upgrade $http_upgrade;
22 proxy_set_header Connection $connection_upgrade;
23 proxy_set_header Host openclaw.paperclover.net;
24 proxy_set_header X-Forwarded-Host openclaw.paperclover.net;
25 proxy_set_header X-Forwarded-Proto https;
26 proxy_set_header X-Forwarded-For $http_x_forwarded_for;
27 proxy_set_header X-OpenClaw-User $http_x_openclaw_user;
28 proxy_buffering off;
29 proxy_read_timeout 28800s;
30 }
31 }
32 server {
33 listen 127.0.0.1:18881;
34 location /v1/ {
35 proxy_pass https://ai.paperclover.net;
36 proxy_ssl_server_name on;
37 proxy_ssl_verify on;
38 proxy_ssl_verify_depth 5;
39 proxy_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;
40 proxy_set_header Host ai.paperclover.net;
41 proxy_set_header Authorization "Bearer MODEL_API_KEY";
42 proxy_buffering off;
43 proxy_read_timeout 28800s;
44 }
45 location / { return 404; }
46 }
47}
service/openclaw/openclaw-install.service created+14
......@@ -0,0 +1,14 @@
1[Unit]
2Description=Prepare OpenClaw trial
3After=network-online.target
4StartLimitIntervalSec=0
5
6[Service]
7Type=oneshot
8RemainAfterExit=yes
9Restart=on-failure
10RestartSec=15
11ExecStart=/usr/local/sbin/openclaw-install
12
13[Install]
14WantedBy=multi-user.target
service/openclaw/openclaw.json created+58
......@@ -0,0 +1,58 @@
1{
2 "gateway": {
3 "mode": "local",
4 "bind": "lan",
5 "port": 18789,
6 "publicOrigin": "https://openclaw.paperclover.net",
7 "trustedProxies": ["10.0.2.2"],
8 "auth": {
9 "mode": "trusted-proxy",
10 "trustedProxy": {
11 "userHeader": "x-openclaw-user",
12 "requiredHeaders": ["x-forwarded-proto", "x-forwarded-host"],
13 "allowUsers": ["snow", "ai"],
14 "deviceAutoApprove": {"enabled": true}
15 },
16 "identityScopes": {"snow": ["operator.admin"], "ai": ["operator.admin"]}
17 },
18 "controlUi": {"allowedOrigins": ["https://openclaw.paperclover.net"]}
19 },
20 "agents": {
21 "defaults": {
22 "workspace": "/home/node/.openclaw/workspace",
23 "model": {"primary": ""},
24 "thinkingDefault": "medium",
25 "heartbeat": {"every": "0m"},
26 "maxConcurrent": 1,
27 "sandbox": {"mode": "off"}
28 }
29 },
30 "models": {
31 "mode": "replace",
32 "providers": {
33 "snowglobe": {
34 "baseUrl": "http://10.0.2.101:8080/v1",
35 "apiKey": "vm-inference-proxy",
36 "api": "openai-completions",
37 "models": [{
38 "id": "", "name": "", "contextWindow": 0,
39 "reasoning": true, "input": ["text", "image"], "maxTokens": 16384,
40 "compat": {"toolSchemaProfile": "llamacpp", "supportsReasoningEffort": true}
41 }]
42 }
43 }
44 },
45 "tools": {
46 "profile": "full",
47 "elevated": {"enabled": false},
48 "exec": {"host": "gateway", "security": "full", "ask": "off"}
49 },
50 "browser": {
51 "enabled": true, "headless": true, "noSandbox": true,
52 "ssrfPolicy": {"dangerouslyAllowPrivateNetwork": true},
53 "defaultProfile": "openclaw",
54 "extraArgs": ["--proxy-server=http://10.0.2.100:3128", "--disable-gpu"]
55 },
56 "discovery": {"mdns": {"mode": "off"}},
57 "update": {"checkOnStart": false}
58}
service/openclaw/openclaw.service created+14
......@@ -0,0 +1,14 @@
1[Unit]
2Description=OpenClaw trial
3Requires=docker.service
4After=docker.service network-online.target
5
6[Service]
7Restart=always
8RestartSec=5
9ExecStartPre=-/usr/bin/docker rm -f openclaw
10ExecStart=/usr/bin/docker run --name openclaw --network host --memory 4g --cpus 3 --pids-limit 512 --cap-drop ALL --security-opt no-new-privileges --shm-size 512m -e HTTP_PROXY=http://10.0.2.100:3128 -e HTTPS_PROXY=http://10.0.2.100:3128 -e http_proxy=http://10.0.2.100:3128 -e https_proxy=http://10.0.2.100:3128 -e NO_PROXY=localhost,127.0.0.1,10.0.2.101 -e no_proxy=localhost,127.0.0.1,10.0.2.101 -e NODE_USE_ENV_PROXY=1 -e TZ=America/Los_Angeles -v /opt/openclaw/state:/home/node/.openclaw openclaw-trial
11ExecStop=/usr/bin/docker stop -t 15 openclaw
12
13[Install]
14WantedBy=multi-user.target
service/openclaw/prepare.py created+130
......@@ -0,0 +1,130 @@
1#!/usr/bin/env python3
2import glob
3import hashlib
4import json
5import os
6from pathlib import Path
7import secrets
8import shutil
9import subprocess
10import sys
11import urllib.request
12import xml.etree.ElementTree as ET
13
14
15data = json.load(sys.stdin)
16root = Path(data["hostRoot"])
17if root.name != "openclaw":
18 raise ValueError("OpenClaw's dedicated VM is production-only")
19source = Path(__file__).parent
20vm = root / "vm"
21vm.mkdir(mode=0o700, exist_ok=True)
22cfg = root / "cfg"
23cfg.mkdir(mode=0o700, exist_ok=True)
24key = json.loads(subprocess.check_output(
25 ["nomad", "var", "get", "-out=json", "nomad/jobs/local-ai"],
26 text=True, stderr=subprocess.DEVNULL,
27))["Items"]["api_key"]
28if not key.isalnum():
29 raise ValueError("Invalid model API credential")
30nginx = source.joinpath("nginx.conf").read_text().replace("MODEL_API_KEY", key)
31pending = cfg / ".nginx.conf.pending"
32pending.write_text(nginx)
33pending.chmod(0o600)
34os.chown(pending, data["uid"], data["uid"])
35pending.replace(cfg / "nginx.conf")
36os.chown(cfg, data["uid"], data["uid"])
37
38if not (vm / "ssh_key").exists():
39 subprocess.run(["ssh-keygen", "-q", "-t", "ed25519", "-N", "", "-f", str(vm / "ssh_key")], check=True)
40
41catalog = json.loads(source.parent.joinpath("local-ai/catalog.json").read_text())["models"][0]
42config = json.loads(source.joinpath("openclaw.json").read_text())
43# Browser control needs its own password under trusted-proxy auth.
44previous = json.loads((vm / "openclaw.json").read_text()) if (vm / "openclaw.json").exists() else {}
45config["gateway"]["auth"]["password"] = previous.get("gateway", {}).get("auth", {}).get("password") or secrets.token_hex(24)
46model = config["models"]["providers"]["snowglobe"]["models"][0]
47model.update(id=catalog["slug"], name=catalog["display_name"], contextWindow=catalog["context_window"])
48config["agents"]["defaults"]["model"]["primary"] = "snowglobe/" + catalog["slug"]
49(vm / "openclaw.json").write_text(json.dumps(config, indent=2) + "\n")
50
51if not (vm / "disk.qcow2").exists():
52 image = vm / "ubuntu.img"
53 expected = "6a81c37564db9b1ee84e141922625e1d7c5b389b99bb3c572e0243607d5bb4d2"
54 if not image.exists():
55 urllib.request.urlretrieve(
56 "https://cloud-images.ubuntu.com/noble/20260926/noble-server-cloudimg-amd64.img", image,
57 )
58 with image.open("rb") as stream:
59 if hashlib.file_digest(stream, "sha256").hexdigest() != expected:
60 raise ValueError("Ubuntu image checksum mismatch")
61 subprocess.run(["qemu-img", "convert", "-O", "qcow2", str(image), str(vm / ".disk.pending")], check=True)
62 subprocess.run(["qemu-img", "resize", str(vm / ".disk.pending"), "32G"], check=True)
63
64 writes = [
65 {"path": "/etc/apt/apt.conf.d/90proxy", "content": 'Acquire::http::Proxy "http://10.0.2.100:3128";\nAcquire::https::Proxy "http://10.0.2.100:3128";\n'},
66 {"path": "/etc/systemd/system/docker.service.d/proxy.conf", "content": "[Service]\nEnvironment=HTTP_PROXY=http://10.0.2.100:3128 HTTPS_PROXY=http://10.0.2.100:3128 NO_PROXY=localhost,127.0.0.1,10.0.2.101\n"},
67 {"path": "/opt/openclaw/openclaw.json", "content": json.dumps(config, indent=2), "permissions": "0600"},
68 {"path": "/opt/openclaw/Dockerfile", "content": source.joinpath("Dockerfile.guest").read_text()},
69 {"path": "/opt/openclaw/resolv.conf", "content": source.joinpath("resolv.conf").read_text()},
70 {"path": "/etc/systemd/system/openclaw.service", "content": source.joinpath("openclaw.service").read_text()},
71 {"path": "/usr/local/sbin/openclaw-install", "content": source.joinpath("install.sh").read_text(), "permissions": "0755"},
72 {"path": "/etc/systemd/system/openclaw-install.service", "content": source.joinpath("openclaw-install.service").read_text()},
73 ]
74 cloud = {
75 "hostname": "openclaw", "manage_etc_hosts": True, "ssh_pwauth": False,
76 "disable_root": True, "users": [{"name": "ocadmin", "lock_passwd": True,
77 "shell": "/bin/bash", "sudo": "ALL=(ALL) NOPASSWD:ALL",
78 "ssh_authorized_keys": [(vm / "ssh_key.pub").read_text().strip()]}],
79 "write_files": writes,
80 "bootcmd": [["systemctl", "mask", "--now", "snapd.service", "snapd.socket", "snapd.seeded.service", "snapd.autoimport.service"]],
81 "runcmd": [["systemctl", "daemon-reload"], ["systemctl", "enable", "--now", "openclaw-install"]],
82 }
83 (vm / "user-data").write_text("#cloud-config\n" + json.dumps(cloud))
84 (vm / "meta-data").write_text("instance-id: openclaw-trial\nlocal-hostname: openclaw\n")
85 iso = shutil.which("genisoimage") or sorted(glob.glob("/nix/store/*-cdrkit-*/bin/genisoimage"))[0]
86 subprocess.run([iso, "-quiet", "-output", str(vm / "seed.iso"), "-volid", "cidata", "-joliet", "-rock", str(vm / "user-data"), str(vm / "meta-data")], check=True)
87 (vm / ".disk.pending").replace(vm / "disk.qcow2")
88 image.unlink()
89
90qemu = "http://libvirt.org/schemas/domain/qemu/1.0"
91units = Path("/etc/systemd/system.control")
92units.mkdir(exist_ok=True)
93shutil.copyfile(source / "machine-openclaw.slice", units / "machine-openclaw.slice")
94dependency = units / "libvirtd.service.d"
95dependency.mkdir(exist_ok=True)
96(dependency / "openclaw.conf").write_text("[Unit]\nRequires=machine-openclaw.slice\nAfter=machine-openclaw.slice\n")
97subprocess.run(["systemctl", "daemon-reload"], check=True)
98subprocess.run(["systemctl", "start", "machine-openclaw.slice"], check=True)
99ET.register_namespace("qemu", qemu)
100domain = ET.fromstring(f"""<domain type="kvm">
101 <name>openclaw</name><memory unit="MiB">6144</memory><vcpu>4</vcpu>
102 <resource><partition>/machine/openclaw</partition></resource>
103 <os><type arch="x86_64" machine="q35">hvm</type><boot dev="hd"/></os>
104 <features><acpi/><apic/></features><cpu mode="host-passthrough"/>
105 <on_poweroff>destroy</on_poweroff><on_reboot>restart</on_reboot><on_crash>restart</on_crash>
106 <devices>
107 <disk type="file" device="disk"><driver name="qemu" type="qcow2"/><source file="{vm}/disk.qcow2"/><target dev="vda" bus="virtio"/></disk>
108 <disk type="file" device="cdrom"><driver name="qemu" type="raw"/><source file="{vm}/seed.iso"/><target dev="sda" bus="sata"/><readonly/></disk>
109 <serial type="pty"><target port="0"/></serial>
110 <memballoon model="virtio"/><rng model="virtio"><backend model="random">/dev/urandom</backend></rng>
111 </devices>
112</domain>""")
113existing = subprocess.run(["virsh", "domuuid", "openclaw"], capture_output=True, text=True)
114if existing.returncode == 0:
115 ET.SubElement(domain, "uuid").text = existing.stdout.strip()
116args = ET.SubElement(domain, "{" + qemu + "}commandline")
117capabilities = ET.SubElement(domain, "{" + qemu + "}capabilities")
118# Libvirt's spawn/setsid filters block the fixed per-connection proxy commands.
119ET.SubElement(capabilities, "{" + qemu + "}del", capability="seccomp-sandbox")
120for value in [
121 "-sandbox", "on,obsolete=deny,elevateprivileges=allow,spawn=allow,resourcecontrol=deny",
122 "-netdev", "user,id=ocnet,restrict=on,ipv6=off,hostfwd=tcp:127.0.0.1:18890-:18789,hostfwd=tcp:127.0.0.1:18892-:22,guestfwd=tcp:10.0.2.100:3128-cmd:/run/current-system/sw/bin/nc 127.0.0.1 18882,guestfwd=tcp:10.0.2.101:8080-cmd:/run/current-system/sw/bin/nc 127.0.0.1 18881,guestfwd=tcp:10.0.2.102:53-cmd:/run/current-system/sw/bin/nc 1.1.1.1 53",
123 "-device", "virtio-net-pci,netdev=ocnet,bus=pcie.0,addr=0x10",
124]:
125 ET.SubElement(args, "{" + qemu + "}arg", value=value)
126(vm / "domain.xml").write_text(ET.tostring(domain, encoding="unicode"))
127subprocess.run(["virsh", "define", str(vm / "domain.xml")], check=True)
128subprocess.run(["virsh", "autostart", "openclaw"], check=True)
129if subprocess.check_output(["virsh", "domstate", "openclaw"], text=True).strip() != "running":
130 subprocess.run(["virsh", "start", "openclaw"], check=True)
service/openclaw/resolv.conf created+2
......@@ -0,0 +1,2 @@
1nameserver 10.0.2.102
2options use-vc timeout:3 attempts:2
service/openclaw/service.pkl created+42
......@@ -0,0 +1,42 @@
1amends "../../config/Service.pkl"
2
3meta { name = "OpenClaw"; access = "infra-admin" }
4enabled = (read?("env:STUDIO_LOCAL_AI") ?? "false") == "true"
5stageIsolation = "fresh"
6healthyDeadline = "30m"
7healthRestartGrace = "30m"
8prepare = "prepare.py"
9setup = "setup.py"
10
11containers {
12 ["gateway"] {
13 image = "docker.io/library/nginx@sha256:17ae566734b63632e543c907ba74757e0c1a25d812ab9f10a07a6bed98dd199c"
14 hostNetwork = true
15 entrypoint = "/usr/sbin/nginx"
16 args { "-c"; "/cfg/nginx.conf"; "-g"; "daemon off;" }
17 cpu = 200
18 memory = 256
19 http {
20 containerPort = 18889
21 hostPort = 18889
22 loopback = true
23 subdomain = "openclaw"
24 authRole = "infra-admin"
25 userHeader = "X-OpenClaw-User"
26 checkPath = "/healthz"
27 }
28 volumes {
29 ["/cfg"] {}
30 ["/etc/ssl/certs/ca-certificates.crt"] { src = "/var/lib/studio/ca-bundle.crt"; readOnly = true }
31 }
32 }
33 ["internet"] {
34 image = "docker.io/ubuntu/squid@sha256:8fafd41d6ddceb295d26eea9938321d825ac5351c7e46cf6a8aa5d093b8ed1ce"
35 hostNetwork = true
36 entrypoint = "/usr/sbin/squid"
37 args { "-N"; "-f"; "/config/squid.conf" }
38 cpu = 200
39 memory = 256
40 volumes { ["/config/squid.conf"] { config = "squid.conf" } }
41 }
42}
service/openclaw/setup.py created+33
......@@ -0,0 +1,33 @@
1#!/usr/bin/env python3
2import json
3from pathlib import Path
4import subprocess
5import sys
6
7
8data = json.load(sys.stdin)
9vm = Path(data["hostRoot"]) / "vm"
10source = Path(__file__).parent
11options = ["-i", str(vm / "ssh_key"), "-o", "BatchMode=yes", "-o", "IdentitiesOnly=yes",
12 "-o", "ForwardAgent=no", "-o", "StrictHostKeyChecking=accept-new",
13 "-o", "UserKnownHostsFile=" + str(vm / "known_hosts")]
14for file, destination in [(vm / "openclaw.json", "/tmp/openclaw.json"),
15 (source / "Dockerfile.guest", "/tmp/Dockerfile.guest"),
16 (source / "openclaw.service", "/tmp/openclaw.service"),
17 (source / "install.sh", "/tmp/openclaw-install"),
18 (source / "openclaw-install.service", "/tmp/openclaw-install.service"),
19 (source / "resolv.conf", "/tmp/resolv.conf")]:
20 subprocess.run(["scp", "-P", "18892", *options, str(file), "ocadmin@127.0.0.1:" + destination], check=True)
21subprocess.run(["ssh", "-p", "18892", *options, "ocadmin@127.0.0.1", "sudo bash -se"], check=True, input="""
22set -e
23install -m 755 /tmp/openclaw-install /usr/local/sbin/openclaw-install
24install /tmp/openclaw-install.service /etc/systemd/system/openclaw-install.service
25install /tmp/resolv.conf /opt/openclaw/resolv.conf
26install /tmp/Dockerfile.guest /opt/openclaw/Dockerfile
27install -o 1000 -g 1000 -m 600 /tmp/openclaw.json /opt/openclaw/state/openclaw.json
28install /tmp/openclaw.service /etc/systemd/system/openclaw.service
29systemctl daemon-reload
30systemctl enable openclaw-install
31systemctl restart openclaw-install
32rm /tmp/openclaw.json /tmp/Dockerfile.guest /tmp/openclaw.service /tmp/openclaw-install /tmp/openclaw-install.service /tmp/resolv.conf
33""", text=True)
service/openclaw/squid.conf created+17
......@@ -0,0 +1,17 @@
1http_port 127.0.0.1:18882
2acl local src 127.0.0.1/32
3acl private dst 0.0.0.0/8 10.0.0.0/8 100.64.0.0/10 127.0.0.0/8 169.254.0.0/16 172.16.0.0/12 192.168.0.0/16 224.0.0.0/4 240.0.0.0/4
4acl private dst ::/128 ::1/128 ::ffff:0:0/96 64:ff9b::/96 64:ff9b:1::/48 100::/64 fc00::/7 fe80::/10 ff00::/8
5http_access deny private
6http_access allow local
7http_access deny all
8cache deny all
9cache_mem 8 MB
10maximum_object_size 0 KB
11access_log none
12cache_log /dev/stderr
13cache_store_log none
14pid_filename /tmp/squid.pid
15coredump_dir /tmp
16netdb_filename none
17shutdown_lifetime 1 seconds
tools/openclaw-verify.py created+63
......@@ -0,0 +1,63 @@
1#!/usr/bin/env python3
2import argparse
3import shlex
4import subprocess
5
6
7parser = argparse.ArgumentParser()
8parser.add_argument("--host", default="root@zenith")
9task = parser.add_mutually_exclusive_group()
10task.add_argument("--prepare-task", action="store_true")
11task.add_argument("--check-task", action="store_true")
12args = parser.parse_args()
13guest = ["ssh", "-i", "/srv/prod/openclaw/vm/ssh_key", "-p", "18892",
14 "-o", "BatchMode=yes", "-o", "IdentitiesOnly=yes", "-o", "ForwardAgent=no",
15 "-o", "StrictHostKeyChecking=yes", "-o", "UserKnownHostsFile=/srv/prod/openclaw/vm/known_hosts",
16 "ocadmin@127.0.0.1", "sudo docker exec -i openclaw python3 -"]
17script = '''
18import json, os, pathlib, socket, subprocess, urllib.error, urllib.request
19for name in ["NO_PROXY", "no_proxy"]:
20 os.environ.pop(name, None)
21proxy = urllib.request.build_opener(urllib.request.ProxyHandler({"http": "http://10.0.2.100:3128", "https": "http://10.0.2.100:3128"}))
22with proxy.open("https://example.com", timeout=20) as response:
23 assert response.status == 200
24for target in ["127.0.0.1", "10.0.0.1", "169.254.169.254", "192.168.1.1", "[::1]"]:
25 try:
26 proxy.open("http://" + target, timeout=5)
27 raise AssertionError("Private proxy target accepted: " + target)
28 except urllib.error.HTTPError as error:
29 assert error.code == 403, (target, error.code)
30for target, port in [("10.0.2.2", 4646), ("10.0.0.1", 22), ("1.1.1.1", 443)]:
31 try:
32 connection = socket.create_connection((target, port), timeout=2)
33 except OSError:
34 continue
35 connection.close()
36 raise AssertionError("Direct egress accepted: " + target)
37direct = urllib.request.build_opener(urllib.request.ProxyHandler({}))
38with direct.open("http://10.0.2.101:8080/v1/models", timeout=20) as response:
39 assert json.load(response)["data"]
40assert socket.getaddrinfo("example.com", 443)
41assert not pathlib.Path("/var/run/docker.sock").exists()
42assert not pathlib.Path("/srv/clover").exists()
43print(json.dumps({"public_internet": True, "private_proxy_denied": 5, "direct_egress_denied": 3, "inference": True, "dns": True, "host_mounts": False}), flush=True)
44'''
45if args.prepare_task or args.check_task:
46 script += r'''
47directory = pathlib.Path("/home/node/.openclaw/workspace/openclaw-trial-check")
48tests = "from median import median\nassert median([3,1,2]) == 2\nassert median([7,1,4,2]) == 3\na=[3,1,2]; median(a); assert a == [3,1,2]\nprint('MEDIAN_OK')\n"
49'''
50 if args.prepare_task:
51 script += r'''
52directory.mkdir(exist_ok=True)
53(directory / "median.py").write_text("def median(items):\n items.sort()\n return items[len(items) // 2]\n")
54(directory / "check.py").write_text(tests)
55print("Prepared coding fixture: " + str(directory))
56'''
57 else:
58 script += """
59assert (directory / "check.py").read_text() == tests
60subprocess.run(["python3", "check.py"], cwd=directory, check=True)
61"""
62subprocess.run(["ssh", "-o", "BatchMode=yes", args.host, shlex.join(guest)],
63 input=script, text=True, check=True)