diff --git a/pom.xml b/pom.xml index 0474f97a0..3f6cc9a4d 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.uid2 uid2-operator - 5.63.22 + 5.63.31-alpha-212-SNAPSHOT UTF-8 diff --git a/scripts/aws/config-server/app.py b/scripts/aws/config-server/app.py index c0c94fc63..d94857ada 100644 --- a/scripts/aws/config-server/app.py +++ b/scripts/aws/config-server/app.py @@ -1,4 +1,5 @@ from flask import Flask +from datetime import datetime, timezone import json import os @@ -14,5 +15,12 @@ def get_config(): except Exception as e: return str(e), 500 +@app.route('/getCurrentTime', methods=['GET']) +def get_time(): + try: + return datetime.now(timezone.utc).isoformat(timespec="seconds") + except Exception as e: + return str(e), 500 + if __name__ == '__main__': app.run(processes=8) diff --git a/scripts/aws/entrypoint.sh b/scripts/aws/entrypoint.sh index 6d4fbe15e..f606df6ef 100755 --- a/scripts/aws/entrypoint.sh +++ b/scripts/aws/entrypoint.sh @@ -22,6 +22,83 @@ ifconfig lo 127.0.0.1 echo "Starting vsock proxy..." /app/vsockpx --config /app/proxies.nitro.yaml --daemon --workers $(( ( $(nproc) + 3 ) / 4 )) --log-level 3 +TIME_SYNC_URL="http://127.0.0.1:27015/getCurrentTime" +TIME_SYNC_PROXY="socks5h://127.0.0.1:3305" +TIME_SYNC_TRIGGER_PORT="${TIME_SYNC_TRIGGER_PORT:-27100}" +TIME_SYNC_OFFSET_SECONDS="${TIME_SYNC_OFFSET_SECONDS:-30}" + +sync_enclave_time_with_offset_once() { + local current_time + local parent_epoch + if current_time=$(curl -s -f -x socks5h://127.0.0.1:3305 "${TIME_SYNC_URL}"); then + parent_epoch=$(date -u -d "${current_time}" +%s 2>/dev/null || true) + if [[ -n "${parent_epoch}" ]]; then + parent_epoch=$((parent_epoch + TIME_SYNC_OFFSET_SECONDS)) + if ! date -u -s "@${parent_epoch}"; then + echo "Time sync: failed to set enclave time from '${current_time}' with offset ${TIME_SYNC_OFFSET_SECONDS}s" + return 1 + fi + echo "Time sync: updated enclave time to ${current_time} + ${TIME_SYNC_OFFSET_SECONDS}s" + fi + else + echo "Time sync: failed to fetch time from parent instance" + return 1 + fi +} + +sync_enclave_time_with_offset_once || true + + + +start_time_sync_server() { + python3 -u - <<'PY' & +import sys +import os +import subprocess +from http.server import BaseHTTPRequestHandler, HTTPServer + +sys.stdout.reconfigure(line_buffering=True) + +TIME_SYNC_URL = os.environ.get("TIME_SYNC_URL", "http://127.0.0.1:27015/getCurrentTime") +TIME_SYNC_PROXY = os.environ.get("TIME_SYNC_PROXY", "socks5h://127.0.0.1:3305") +TIME_SYNC_TRIGGER_PORT = int(os.environ.get("TIME_SYNC_TRIGGER_PORT", "27100")) + +def sync_time() -> str: + current_time = subprocess.check_output( + ["curl", "-sSf", "-x", TIME_SYNC_PROXY, TIME_SYNC_URL], + text=True, + ).strip() + subprocess.check_call(["date", "-u", "-s", current_time]) + return current_time + +class Handler(BaseHTTPRequestHandler): + def do_GET(self) -> None: + if self.path not in ("/", "/sync"): + self.send_response(404) + self.end_headers() + return + try: + result = sync_time() + print(f"Time sync: updated enclave time to {result}", flush=True) + self.send_response(200) + self.end_headers() + self.wfile.write(f"OK {result}\n".encode()) + except Exception as exc: # pragma: no cover - best effort logging + print(f"Time sync error: {exc}", flush=True) + self.send_response(500) + self.end_headers() + self.wfile.write(f"ERROR {exc}\n".encode()) + + def log_message(self, format, *args): # noqa: N802 - match base class + return + +server = HTTPServer(("127.0.0.1", TIME_SYNC_TRIGGER_PORT), Handler) +server.serve_forever() +PY +} + +start_time_sync_server + build_parameterized_config() { curl -s -f -o "${PARAMETERIZED_CONFIG}" -x socks5h://127.0.0.1:3305 http://127.0.0.1:27015/getConfig REQUIRED_KEYS=("optout_base_url" "core_base_url" "core_api_token" "optout_api_token" "environment" "uid_instance_id_prefix") diff --git a/scripts/aws/proxies.host.yaml b/scripts/aws/proxies.host.yaml index 5a2ae0623..5ed149f3c 100644 --- a/scripts/aws/proxies.host.yaml +++ b/scripts/aws/proxies.host.yaml @@ -19,3 +19,8 @@ syslogng: service: direct listen: vsock://-1:2011 connect: tcp://127.0.0.1:2011 +time-sync: + service: direct + listen: tcp://127.0.0.1:27100 + connect: vsock://42:27100 + diff --git a/scripts/aws/proxies.nitro.yaml b/scripts/aws/proxies.nitro.yaml index 0f459b150..f981a9a29 100644 --- a/scripts/aws/proxies.nitro.yaml +++ b/scripts/aws/proxies.nitro.yaml @@ -19,3 +19,8 @@ syslogng: service: direct listen: tcp://127.0.0.1:2011 connect: vsock://3:2011 +time-sync: + service: direct + listen: vsock://-1:27100 + connect: tcp://127.0.0.1:27100 + diff --git a/scripts/aws/uid2-operator-ami/ansible/playbook.yml b/scripts/aws/uid2-operator-ami/ansible/playbook.yml index a5ec77809..308dadc55 100644 --- a/scripts/aws/uid2-operator-ami/ansible/playbook.yml +++ b/scripts/aws/uid2-operator-ami/ansible/playbook.yml @@ -19,6 +19,11 @@ name: nmap-ncat state: latest + - name: Install cron + ansible.builtin.dnf: + name: cronie + state: latest + - name: Install python ansible.builtin.dnf: name: @@ -167,6 +172,15 @@ dest: /etc/systemd/system/uid2operator.service remote_src: yes + - name: Install time sync trigger script + ansible.builtin.copy: + dest: /usr/local/bin/uid2-time-sync + mode: "0755" + content: | + #!/usr/bin/env bash + set -euo pipefail + curl -sSf http://127.0.0.1:27100/sync > /dev/null + - name: Install AWS Nitro Enclaves CLI ansible.builtin.dnf: name: aws-nitro-enclaves-cli @@ -240,6 +254,19 @@ ansible.builtin.systemd: name: uid2operator.service enabled: yes + + - name: Ensure cron is enabled at boot + ansible.builtin.systemd: + name: crond + state: started + enabled: yes + + - name: Install time sync cron job + ansible.builtin.copy: + dest: /etc/cron.d/uid2-time-sync + mode: "0644" + content: | + */5 * * * * root /usr/local/bin/uid2-time-sync - name: Clean up tmp files file: