인바운드 포트 없이 모든 머신의 터미널, GUI 창 및 관리 패널에 접근할 수 있는 릴레이를 직접 구축했습니다
요약
본 글은 인바운드 포트 개방 없이도 모든 머신의 터미널, GUI 창 및 관리 패널에 접근할 수 있는 릴레이 시스템 구축 방법을 설명합니다. 이 아키텍처는 에이전트가 외부로만 연결(dial out)하며 리스닝하지 않아 보안성이 매우 높습니다. Cloudflare Workers를 활용하여 직접 구현 가능하며, 강력한 종단 간 암호화와 기기별 신뢰 기능을 제공합니다.
핵심 포인트
- 인바운드 포트 개방 없이 모든 장치에 접근 가능한 릴레이 구축
- 에이전트는 외부 연결(dial out)만 하며 리스닝하지 않아 보안성이 높음
- AES-256-GCM 등 종단 간 암호화 및 X25519 핸드셰이크 사용
- Cloudflare Workers를 이용해 직접 구현 가능하며, 소스 코드 공개로 투명성 확보
제 장비들은 밤새 긴 작업(에이전트, 빌드, 백업)을 실행하는데, 저는 이 모든 것을 한 브라우저 탭에서 보고 싶었습니다. 즉, 모든 터미널 세션, 각 박스에서 실행되는 것들, Mac의 실제 GUI 창, 그리고 항상 localhost에서만 리스닝하는 관리 패널까지 말입니다. 그 어떤 것도 인바운드 포트를 열지 않고서요. 제가 이것을 빌리거나 임대하지 않고 직접 구축하게 된 계기는 이 분야의 모든 제품들이 작업을 자신들의 컴퓨터에서 수행하기를 원한다는 점 때문이었습니다. 이는 개인 저장소, 라이선스된 툴체인 또는 로컬 GPU와 관련된 어떤 작업에도 적합하지 않습니다. 목표는 제가 가진 하드웨어를 작업이 이루어지는 장소로 만드는 것이었습니다. 아키텍처가 중요하므로 이 부분을 말씀드리겠습니다: 에이전트들은 오직 외부로 연결(dial out)만 합니다. 아무것도 리스닝하지 않습니다. 스캔하거나, 무차별 대입 공격을 하거나, 제로데이 취약점을 찾을 포트 자체가 없습니다. NAT, CGNAT 및 방화벽은 무관합니다. 릴레이는 눈이 멀어 있습니다. 세션은 종단 간 암호화(end-to-end encrypted)되어 있습니다 (AES-256-GCM, 실행마다 새로운 키 사용, PIN 인증 X25519 핸드셰이크). 릴레이는 자신이 복호화할 수 없는 암호문만 전달하며, 이 암호문은 오프라인으로 PIN을 무차별 대입 공격하는 데 사용될 수 없습니다. 추측 한 번당 장비와 전체 온라인 핸드셰이크가 필요하며, 소스에서 속도 제한이 걸립니다. Cloudflare Workers에 릴레이를 몇 분 만에 직접 구축할 수 있으며, 같은 AGPL 레포지토리에서 가능합니다. 릴레이의 기본 설정은 포크 및 로컬 빌드를 위해 구성 가능하므로, 소스를 패치하지 않고도 자신의 엔드포인트를 대상으로 빌드를 지정할 수 있습니다. 이 기능은 누군가 이를 직접 구축하여 사용하면서 PR로 제출되었습니다. 임시 자격 증명(Ephemeral credentials). 세션 ID와 PIN은 프로세스가 실행되는 동안에만 존재합니다. 계정이나 보는 장치에 저장된 키는 없습니다. 브라우저가 클라이언트입니다. 가지고 있는 장치에 설치할 것이 아무것도 없습니다; QR 코드를 스캔하면 연결됩니다. 소유자 장치는 한 번 등록하고 PIN 단계를 건너뛸 수 있으며, 개별적으로 취소할 수 있는 기기별 신뢰(per-device trust) 기능이 있습니다. 특권적인 작업은 서명됩니다. 브라우저를 통해 호스트를 업그레이드하는 과정에는 해당 머신의 소유자로 등록된 장치의 서명이 포함되며, 무언가 실행되기 전에 확인됩니다. PIN으로 세션을 공유한 사람은 방문 중인 호스트를 업그레이드할 수 없습니다. 텔레메트리(Telemetry)는 전송되지 않습니다.
분석 기능도, 사용량 보고서도, 충돌 보고서도, 외부 SDK도 없습니다. 개인 정보 보호 페이지에서는 grep 명령을 제공하여 저를 신뢰하는 대신 직접 소스 코드를 통해 확인할 수 있습니다. localhost에서만 리스닝하는 것들에 접근할 수 있습니다. reminal expose 8006은 포트에 reminal.app에 자체 주소를 부여하며, PIN으로 보호되고 여전히 아웃바운드 전용이므로 대부분의 홈랩 환경을 커버합니다. 로컬 서비스와 HTTPS로 통신하므로 Proxmox, UniFi, webmin 및 NAS 패널들이 연결 실패 대신 접속할 수 있게 해줍니다. 웹소켓과 스트리밍 응답을 처리하므로 Node-RED, Jupyter, 라이브 리로드 및 실시간 로그 페이지가 있는 개발 서버들이 사용자의 화면에서처럼 원활하게 작동합니다. 각 포트는 공유된 경로 접두사 대신 실제 호스트 이름을 얻게 되며, 이것이 / 에서 에셋을 로드하거나, 서비스 워커를 등록하거나, 동일 출처 정책(same-origin)을 강제하는 앱들이 실제로 작동하게 만드는 요소입니다. 세션은 재부팅 후에도 유지됩니다. 기계가 재부팅되거나 충돌하거나 전원이 끊겨도, 실행 중이던 모든 세션은 로그인할 때 돌아옵니다: 동일한 링크와 PIN, 화면에 표시되었던 내용, 그리고 작업하던 폴더의 셸까지 말입니다. 코딩 에이전트(claude, codex, gemini, qwen, cursor-agent, opencode, pi)는 동일한 대화에서 다시 시작됩니다. 의도적으로 종료한 세션은 계속 종료된 상태로 유지됩니다. 플릿(Fleet), 일상적인 사용: 소유한 모든 기계가 하나의 목록에 실시간 CPU 및 메모리 배터리와 함께 표시되어 어디에 무언가를 시작할 공간이 있는지 확인할 수 있습니다. 소유한 기계는 이 목록에 남아 있으며 유휴 상태에서도 시작 가능한 상태를 유지하고; 작은 백그라운드 도우미가 접근성을 유지하며 재부팅 후에도 돌아옵니다. 실제 색상, 스크롤백 검색 기능을 갖춘 브라우저 내 전체 터미널과 다른 도시에서 동일한 셸로 다시 연결할 수 있습니다. macOS, Linux 및 Windows 호스트의 실시간 GUI 창들이 모두 입력이 있는 실제 비디오 스트리밍으로 제공되어, 휴대폰이나 약한 연결에서도 창이 부드럽게 유지됩니다. reminal upgrade --all-owned-machines는 전체 플릿을 한 번의 명령으로 최신 버전으로 업데이트합니다. 또한 인터넷 연결 없이도 작동합니다.
같은 머신에서 세션에 연결하는 경우, 동일한 PIN, 동일한 종단 간 암호화(end-to-end encryption), 그리고 동일한 화면을 사용하여 직접적인 로컬 경로를 자동으로 사용합니다. 보안이 강화된 네트워크, 비행기 안, 또는 접근 불가능한 릴레이 때문에 자신의 박스에 대한 접근 권한을 잃지 않습니다. 여러 머신에 걸친 에이전트들. reminal은 MCP 서버를 전송하므로, 에이전트는 소유한 모든 머신의 모든 세션을 나열하고, 그들의 실시간 스크롤백을 정규표현식(regex) 검색하며, 세션의 터미널을 읽고, 거기에 타이핑할 수 있습니다. 서로 다른 박스에 있는, 그리고 서로 다른 벤더의 에이전트들은 채택해야 할 프로토콜 없이 서로를 읽고 구동할 수 있습니다: 버스는 PTY입니다. 만약 그것이 터미널에서 실행된다면, 읽히고 타이핑될 수 있습니다. 또한 모든 세션은 자신의 에이전트가 무엇을 하고 있는지(작업 중, 도움이 필요함, 또는 완료됨)를 reminal 목록과 Machines 화면에 보고하므로, 전체 플릿(fleet)을 통해 각 개별 항목을 열지 않고도 어떤 것이 도움을 필요로 하는지 알 수 있습니다. 레포: https://github.com/harshalgajjar/Reminal Single Go binary, 런타임 없음, 설치 명령어 하나로 macOS, Linux 및 Windows 지원 (Windows 빌드는 Authenticode 서명되어 있어 SmartScreen 경고가 없습니다). 프로토콜이나 위협 모델에 대한 AMA(Ask Me Anything); 위협 모델 문서는 레포에 있습니다. /u/harshalgajjar 제출 [링크] [댓글]
AI 자동 생성 콘텐츠
본 콘텐츠는 r/SelfHosted (AI filter)의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기