mirror of
https://github.com/getcompanion-ai/computer-guest.git
synced 2026-04-15 07:04:43 +00:00
feat: remove wakeup path, return on create, host managed ssh-keygen, ack nonce dep
This commit is contained in:
parent
5ad9722a84
commit
2b2ece6778
4 changed files with 94 additions and 83 deletions
|
|
@ -132,6 +132,7 @@ COPY sshd_config /etc/ssh/sshd_config
|
||||||
COPY microagent-init.sh /usr/local/bin/microagent-init
|
COPY microagent-init.sh /usr/local/bin/microagent-init
|
||||||
COPY microagent-desktop-session.sh /usr/local/bin/microagent-desktop-session
|
COPY microagent-desktop-session.sh /usr/local/bin/microagent-desktop-session
|
||||||
COPY microagent-network-up.sh /usr/local/bin/microagent-network-up
|
COPY microagent-network-up.sh /usr/local/bin/microagent-network-up
|
||||||
|
COPY microagent-ready-agent.py /usr/local/bin/microagent-ready-agent
|
||||||
COPY defaults/.zshrc /home/node/.zshrc
|
COPY defaults/.zshrc /home/node/.zshrc
|
||||||
COPY defaults/.bashrc /home/node/.bashrc
|
COPY defaults/.bashrc /home/node/.bashrc
|
||||||
COPY defaults/.profile /home/node/.profile
|
COPY defaults/.profile /home/node/.profile
|
||||||
|
|
@ -140,7 +141,7 @@ COPY defaults/AGENTS.md /home/node/AGENTS.md
|
||||||
COPY terminfo/xterm-ghostty.terminfo /tmp/xterm-ghostty.terminfo
|
COPY terminfo/xterm-ghostty.terminfo /tmp/xterm-ghostty.terminfo
|
||||||
COPY terminfo/xterm-kitty.terminfo /tmp/xterm-kitty.terminfo
|
COPY terminfo/xterm-kitty.terminfo /tmp/xterm-kitty.terminfo
|
||||||
|
|
||||||
RUN chmod 755 /usr/local/bin/microagent-init /usr/local/bin/microagent-desktop-session /usr/local/bin/microagent-network-up \
|
RUN chmod 755 /usr/local/bin/microagent-init /usr/local/bin/microagent-desktop-session /usr/local/bin/microagent-network-up /usr/local/bin/microagent-ready-agent \
|
||||||
&& chmod 755 /opt/desktop/scripts/apply-desktop-profile.sh \
|
&& chmod 755 /opt/desktop/scripts/apply-desktop-profile.sh \
|
||||||
&& chown node:node /home/node/.zshrc /home/node/.bashrc /home/node/.profile /home/node/AGENTS.md \
|
&& chown node:node /home/node/.zshrc /home/node/.bashrc /home/node/.profile /home/node/AGENTS.md \
|
||||||
&& ln -sf /home/node/AGENTS.md /home/node/CLAUDE.md \
|
&& ln -sf /home/node/AGENTS.md /home/node/CLAUDE.md \
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,6 @@ log() {
|
||||||
printf '[microagent-init] %s\n' "$*" >&2
|
printf '[microagent-init] %s\n' "$*" >&2
|
||||||
}
|
}
|
||||||
|
|
||||||
MMDS_IPV4_ADDRESS="169.254.170.2"
|
|
||||||
|
|
||||||
read_machine_name() {
|
read_machine_name() {
|
||||||
if [ -r /etc/microagent/machine-name ]; then
|
if [ -r /etc/microagent/machine-name ]; then
|
||||||
tr -d '\r\n' </etc/microagent/machine-name
|
tr -d '\r\n' </etc/microagent/machine-name
|
||||||
|
|
@ -21,74 +19,6 @@ read_machine_name() {
|
||||||
printf 'microagentcomputer'
|
printf 'microagentcomputer'
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch_mmds_metadata() {
|
|
||||||
local iface="${1:-}"
|
|
||||||
local token payload attempt=0
|
|
||||||
|
|
||||||
[ -n "$iface" ] || return 1
|
|
||||||
|
|
||||||
ip route replace "${MMDS_IPV4_ADDRESS}/32" dev "$iface" >/dev/null 2>&1 || return 1
|
|
||||||
|
|
||||||
while [ "$attempt" -lt 50 ]; do
|
|
||||||
token="$(curl -fsS -X PUT "http://${MMDS_IPV4_ADDRESS}/latest/api/token" \
|
|
||||||
-H 'X-metadata-token-ttl-seconds: 30' 2>/dev/null || true)"
|
|
||||||
if [ -n "$token" ]; then
|
|
||||||
payload="$(curl -fsS "http://${MMDS_IPV4_ADDRESS}/latest/meta-data" \
|
|
||||||
-H 'Accept: application/json' \
|
|
||||||
-H "X-metadata-token: ${token}" 2>/dev/null || true)"
|
|
||||||
if [ -n "$payload" ]; then
|
|
||||||
printf '%s' "$payload"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
attempt=$((attempt + 1))
|
|
||||||
sleep 0.1
|
|
||||||
done
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
apply_guest_metadata() {
|
|
||||||
local payload="${1:-}"
|
|
||||||
local machine_name
|
|
||||||
|
|
||||||
[ -n "$payload" ] || return 1
|
|
||||||
install -d -m 0755 /etc/microagent
|
|
||||||
|
|
||||||
machine_name="$(printf '%s' "$payload" | jq -r '.hostname // .machine_id // empty')"
|
|
||||||
if [ -n "$machine_name" ]; then
|
|
||||||
printf '%s\n' "$machine_name" >/etc/microagent/machine-name
|
|
||||||
printf '%s\n' "$machine_name" >/etc/hostname
|
|
||||||
cat >/etc/hosts <<EOF
|
|
||||||
127.0.0.1 localhost
|
|
||||||
127.0.1.1 $machine_name
|
|
||||||
::1 localhost ip6-localhost ip6-loopback
|
|
||||||
ff02::1 ip6-allnodes
|
|
||||||
ff02::2 ip6-allrouters
|
|
||||||
EOF
|
|
||||||
hostname "$machine_name" >/dev/null 2>&1 || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
if printf '%s' "$payload" | jq -e '.authorized_keys | length > 0' >/dev/null 2>&1; then
|
|
||||||
log "installing MMDS authorized_keys for node"
|
|
||||||
install -d -m 0700 -o node -g node /home/node/.ssh
|
|
||||||
printf '%s' "$payload" | jq -r '.authorized_keys[]' >/home/node/.ssh/authorized_keys
|
|
||||||
chmod 0600 /home/node/.ssh/authorized_keys
|
|
||||||
chown node:node /home/node/.ssh/authorized_keys
|
|
||||||
printf '%s' "$payload" | jq -r '.authorized_keys[]' >/etc/microagent/authorized_keys
|
|
||||||
chmod 0600 /etc/microagent/authorized_keys
|
|
||||||
fi
|
|
||||||
|
|
||||||
if printf '%s' "$payload" | jq -e '.trusted_user_ca_keys | length > 0' >/dev/null 2>&1; then
|
|
||||||
log "installing MMDS trusted user CA keys"
|
|
||||||
printf '%s' "$payload" | jq -r '.trusted_user_ca_keys[]' >/etc/microagent/trusted_user_ca_keys
|
|
||||||
chmod 0644 /etc/microagent/trusted_user_ca_keys
|
|
||||||
fi
|
|
||||||
|
|
||||||
printf '%s' "$payload" | jq '{authorized_keys, trusted_user_ca_keys, login_webhook}' >/etc/microagent/guest-config.json
|
|
||||||
chmod 0600 /etc/microagent/guest-config.json
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
mountpoint -q /proc || mount -t proc proc /proc
|
mountpoint -q /proc || mount -t proc proc /proc
|
||||||
mountpoint -q /sys || mount -t sysfs sysfs /sys
|
mountpoint -q /sys || mount -t sysfs sysfs /sys
|
||||||
mountpoint -q /dev || mount -t devtmpfs devtmpfs /dev
|
mountpoint -q /dev || mount -t devtmpfs devtmpfs /dev
|
||||||
|
|
@ -105,6 +35,7 @@ resize2fs /dev/vda >/dev/null 2>&1 || true
|
||||||
cleanup() {
|
cleanup() {
|
||||||
trap - INT TERM
|
trap - INT TERM
|
||||||
[ -n "${rng_pid:-}" ] && kill "$rng_pid" >/dev/null 2>&1 || true
|
[ -n "${rng_pid:-}" ] && kill "$rng_pid" >/dev/null 2>&1 || true
|
||||||
|
[ -n "${ready_agent_pid:-}" ] && kill "$ready_agent_pid" >/dev/null 2>&1 || true
|
||||||
[ -n "${sshd_pid:-}" ] && kill "$sshd_pid" >/dev/null 2>&1 || true
|
[ -n "${sshd_pid:-}" ] && kill "$sshd_pid" >/dev/null 2>&1 || true
|
||||||
[ -n "${desktop_pid:-}" ] && kill "$desktop_pid" >/dev/null 2>&1 || true
|
[ -n "${desktop_pid:-}" ] && kill "$desktop_pid" >/dev/null 2>&1 || true
|
||||||
wait >/dev/null 2>&1 || true
|
wait >/dev/null 2>&1 || true
|
||||||
|
|
@ -130,6 +61,13 @@ start_sshd() {
|
||||||
sshd_pid=$!
|
sshd_pid=$!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
start_ready_agent() {
|
||||||
|
reap_if_needed "${ready_agent_pid:-}"
|
||||||
|
log "starting ready agent on vsock 1024"
|
||||||
|
/usr/local/bin/microagent-ready-agent >>/var/log/ready-agent.log 2>&1 &
|
||||||
|
ready_agent_pid=$!
|
||||||
|
}
|
||||||
|
|
||||||
start_desktop() {
|
start_desktop() {
|
||||||
reap_if_needed "${desktop_pid:-}"
|
reap_if_needed "${desktop_pid:-}"
|
||||||
log "starting noVNC desktop on 6080"
|
log "starting noVNC desktop on 6080"
|
||||||
|
|
@ -145,12 +83,6 @@ if ! /usr/local/bin/microagent-network-up >/var/log/network.log 2>&1; then
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
primary_iface="$(find /sys/class/net -mindepth 1 -maxdepth 1 -printf '%f\n' | grep -v '^lo$' | head -n1 || true)"
|
|
||||||
if metadata_payload="$(fetch_mmds_metadata "$primary_iface")"; then
|
|
||||||
log "applying guest metadata from MMDS"
|
|
||||||
apply_guest_metadata "$metadata_payload" || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
machine_name="$(read_machine_name)"
|
machine_name="$(read_machine_name)"
|
||||||
export COMPUTER_NAME="$machine_name"
|
export COMPUTER_NAME="$machine_name"
|
||||||
printf '%s\n' "$machine_name" >/etc/hostname
|
printf '%s\n' "$machine_name" >/etc/hostname
|
||||||
|
|
@ -163,11 +95,6 @@ ff02::2 ip6-allrouters
|
||||||
EOF
|
EOF
|
||||||
hostname "$machine_name" >/dev/null 2>&1 || true
|
hostname "$machine_name" >/dev/null 2>&1 || true
|
||||||
|
|
||||||
if [ ! -f /etc/ssh/ssh_host_ed25519_key ]; then
|
|
||||||
log "generating ssh host keys"
|
|
||||||
ssh-keygen -A
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -f /etc/microagent/authorized_keys ]; then
|
if [ -f /etc/microagent/authorized_keys ]; then
|
||||||
log "installing injected authorized_keys for node"
|
log "installing injected authorized_keys for node"
|
||||||
install -d -m 0700 -o node -g node /home/node/.ssh
|
install -d -m 0700 -o node -g node /home/node/.ssh
|
||||||
|
|
@ -195,10 +122,15 @@ if command -v jitterentropy-rngd >/dev/null 2>&1; then
|
||||||
rng_pid=$!
|
rng_pid=$!
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
start_ready_agent
|
||||||
start_sshd
|
start_sshd
|
||||||
start_desktop
|
start_desktop
|
||||||
|
|
||||||
while true; do
|
while true; do
|
||||||
|
if ! pid_running "${ready_agent_pid:-}"; then
|
||||||
|
log "ready agent exited; restarting"
|
||||||
|
start_ready_agent
|
||||||
|
fi
|
||||||
if ! pid_running "${sshd_pid:-}"; then
|
if ! pid_running "${sshd_pid:-}"; then
|
||||||
log "sshd exited; restarting"
|
log "sshd exited; restarting"
|
||||||
start_sshd
|
start_sshd
|
||||||
|
|
|
||||||
79
microagent-ready-agent.py
Normal file
79
microagent-ready-agent.py
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import json
|
||||||
|
import socket
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
LISTEN_PORT = 1024
|
||||||
|
SSH_PORT = 2222
|
||||||
|
WAIT_TIMEOUT_SECONDS = 15.0
|
||||||
|
POLL_INTERVAL_SECONDS = 0.1
|
||||||
|
HOST_KEY_PATH = Path("/etc/ssh/ssh_host_ed25519_key.pub")
|
||||||
|
|
||||||
|
|
||||||
|
def log(message: str) -> None:
|
||||||
|
print(f"[microagent-ready-agent] {message}", file=sys.stderr, flush=True)
|
||||||
|
|
||||||
|
|
||||||
|
def local_port_ready(port: int) -> bool:
|
||||||
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as probe:
|
||||||
|
probe.settimeout(POLL_INTERVAL_SECONDS)
|
||||||
|
try:
|
||||||
|
probe.connect(("127.0.0.1", port))
|
||||||
|
except OSError:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def wait_for_ready() -> str:
|
||||||
|
deadline = time.monotonic() + WAIT_TIMEOUT_SECONDS
|
||||||
|
while time.monotonic() < deadline:
|
||||||
|
if HOST_KEY_PATH.is_file() and local_port_ready(SSH_PORT):
|
||||||
|
return HOST_KEY_PATH.read_text(encoding="utf-8").strip()
|
||||||
|
time.sleep(POLL_INTERVAL_SECONDS)
|
||||||
|
raise TimeoutError("guest services did not become ready before timeout")
|
||||||
|
|
||||||
|
|
||||||
|
def handle_connection(connection: socket.socket) -> None:
|
||||||
|
with connection:
|
||||||
|
payload_line = b""
|
||||||
|
while not payload_line.endswith(b"\n"):
|
||||||
|
chunk = connection.recv(4096)
|
||||||
|
if not chunk:
|
||||||
|
break
|
||||||
|
payload_line += chunk
|
||||||
|
if not payload_line:
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
payload = json.loads(payload_line.decode("utf-8"))
|
||||||
|
guest_ssh_public_key = wait_for_ready()
|
||||||
|
response = {
|
||||||
|
"status": "ok",
|
||||||
|
"ready_nonce": str(payload.get("ready_nonce") or "").strip(),
|
||||||
|
"guest_ssh_public_key": guest_ssh_public_key,
|
||||||
|
}
|
||||||
|
except Exception as exc:
|
||||||
|
response = {"status": "error", "error": str(exc)}
|
||||||
|
connection.sendall((json.dumps(response) + "\n").encode("utf-8"))
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
cid_any = getattr(socket, "VMADDR_CID_ANY", 0xFFFFFFFF)
|
||||||
|
with socket.socket(socket.AF_VSOCK, socket.SOCK_STREAM) as listener:
|
||||||
|
listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||||
|
listener.bind((cid_any, LISTEN_PORT))
|
||||||
|
listener.listen()
|
||||||
|
log(f"listening on vsock port {LISTEN_PORT}")
|
||||||
|
while True:
|
||||||
|
connection, _ = listener.accept()
|
||||||
|
handle_connection(connection)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
raise SystemExit(main())
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
raise SystemExit(0)
|
||||||
|
|
@ -2,7 +2,6 @@ Port 2222
|
||||||
ListenAddress 0.0.0.0
|
ListenAddress 0.0.0.0
|
||||||
|
|
||||||
HostKey /etc/ssh/ssh_host_ed25519_key
|
HostKey /etc/ssh/ssh_host_ed25519_key
|
||||||
HostKey /etc/ssh/ssh_host_rsa_key
|
|
||||||
|
|
||||||
PermitRootLogin no
|
PermitRootLogin no
|
||||||
PubkeyAuthentication yes
|
PubkeyAuthentication yes
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue