InfraGuard

인스턴스 SSH 터미널

브라우저에서 등록 인스턴스에 진짜 PTY 대화형 SSH 셸을 열어 top·vi·sudo 를 그대로 실행합니다(xterm.js ⇄ WS 브리지 ⇄ ssh2).

개요 — 무엇을 하나

인스턴스 SSH 터미널은 Assets ▸ Instances 카드의 [ssh] 액션으로 여는 브라우저 내장 대화형 셸입니다. 별도 SSH 클라이언트(PuTTY·터미널 앱)를 띄우지 않고, 등록된 OS 인스턴스(infr_instance)에 콘솔에서 바로 접속해 명령을 실행합니다.

핵심은 진짜 PTY(의사 터미널) 라는 점입니다. 단발 요청/응답 실행이 아니라 원격 sshd 에 PTY 채널을 할당하므로 top·vi·less·sudo·htop 같은 대화형·전체화면 프로그램이 그대로 동작합니다(원격에서 tty 를 찍으면 /dev/pts/* 가 나옵니다).

브라우저 xterm.js  ⇄  (WebSocket)  ⇄  브리지 scripts/infr_ssh_bridge.cjs (ssh2)  ⇄  원격 sshd (PTY)

같은 컨트롤러(InstanceSshController)에는 요청/응답 콘솔용 test·exec 엔드포인트도 남아 있습니다. 이는 프로브 같은 단발 명령 실행용이며, UI 의 대화형 터미널은 항상 PTY(WS 브리지) 경로를 씁니다. 원격 에뮬레이트의 프로브 실행 등이 요청/응답 경로를 활용합니다.


언제 쓰나

반복 진단은 셸에서 손으로 치기보다 원격 에뮬레이트·버전 프로브로 자동화하는 편이 재현 가능하고 감사 추적에 남습니다. SSH 터미널은 탐색적·일회성 작업에 알맞습니다.


화면 구성

영역 내용
터미널 본체 xterm.js 캔버스. 원격 PTY 출력을 그대로 렌더(색·커서·전체화면 TUI).
상태 표시 connecting → ready → closed/error. 연결 대상 user@host:port·오류 메시지.
비밀번호 게이트 password 인증 인스턴스에서 접속 전 비밀번호 입력. "이 브라우저에 기억"(선택) 토글.
전체화면 토글 터미널을 확장/복원. 크기 변경 시 자동 fit() → 원격에 resize 전송.
재접속 / 닫기 세션 재시작(새 티켓 발급) · 종료(WS·터미널 정리).

상태 값

상태 의미
idle 모달만 열림, 아직 미접속.
connecting 티켓 발급·WS·SSH 핸드셰이크 진행 중.
ready 원격 셸 준비 완료 — 입력 가능.
error 인증 실패·셸 열기 실패·WS 오류 등(메시지 표시).
closed 세션 종료(원격 exit·유휴 타임아웃·최대수명 초과·수동 닫기).

사용법

1. 접속

  1. Assets ▸ Instances 에서 대상 카드의 [ssh] 아이콘을 클릭합니다.
  2. 인증 방식에 따라:
    • key 인스턴스: 즉시 접속 시도(저장 개인키 또는 브리지 호스트 키/에이전트).
    • password 인스턴스: 비밀번호 게이트에 비밀번호를 입력하고 접속. (선택) "이 브라우저에 기억" 체크 시 브라우저 로컬 볼트(암호화)에만 저장됩니다 — 서버에는 저장되지 않습니다.
  3. 상태가 ready 가 되면 터미널에 입력할 수 있습니다.

2. 크기 조정 · 전체화면

3. 종료

인증 흐름(2단계)

1) Laravel: POST /asset/instances/ssh/session
   └ level≥2 확인 → 서명 티켓 발급(20s TTL, 감사 로그)
     payload = {iid, host, port, user, auth, uid, cols, rows, exp}   ← 비밀번호 없음

2) 브라우저: WebSocket 접속  WebSocket/?ticket=<티켓>
   └ 브리지가 verifyTicket() 로 서명·만료·필수필드 검증
   └ 최초 프레임으로 자격 전달:
       password 인스턴스 → {t:'auth', password:'…'}          (WS 로만, 미저장)
       key 인스턴스      → {t:'auth', privateKey:'…', passphrase:'…'}

브리지 프로토콜(WebSocket 프레임)

방향 형식 프레임
client → bridge JSON text {t:'auth', password|privateKey|passphrase} · {t:'data', d} · {t:'resize', cols, rows}
bridge → client binary 터미널 출력 바이트(term.write)
bridge → client JSON text {t:'status', s, m} (connecting/ready/error/closed)

데이터 소스

이 화면은 시계열 테이블을 읽지 않습니다. 접속 대상·자격의 원천은:

원천 용도
infr_instance 접속 대상 — ssh_host·ssh_port·ssh_user·auth_method. 인스턴스 CMDB 소유.
infr_instance.ssh_key_enc / ssh_key_pass_enc 인스턴스별 개인키(Laravel Crypt 암호화). key 인증 시 /session 이 복호화·릴레이. has_ssh_key 불리언만 UI 에 노출.
브리지 설정 접속 주소·수명 등. 설치 시 도입 담당자가 구성합니다.
브라우저 로컬 볼트(clientVault) (선택) 기억한 비밀번호 — 암호화되어 브라우저에만 저장, 서버 전송·저장 없음.

브리지 실행(개발 런처가 ssh pane 으로 띄움 · 프로덕션은 supervised):

브리지는 설치 시 도입 담당자가 구성·기동합니다. 서명 키가 설정되지 않으면 대화형 셸은 동작하지 않습니다. 터미널이 열리지 않으면 도입 담당자에게 문의해 주세요.


권한

작업 최소 레벨 근거
/session 티켓 발급(대화형 접속) ≥ 2 InstanceSshController::allowed() — 세션 infr_user_level >= 2.
/exec 원격 명령 실행(요청/응답) ≥ 2 동일 게이트. 인증된 임의 원격 명령 실행이므로 전량 감사.
/test 연결 프리플라이트 ≥ 2 동일 게이트.

문제해결 · FAQ

대화형 셸이 안 열리고 503(pty_off) 이 뜹니다

Connection error / WS 가 바로 닫힙니다

authentication failed (인증 실패)

too many active sessions (code 1013)

세션이 저절로 끊깁니다

비밀번호는 어디에 저장되나?

서버에는 저장하지 않습니다. 브라우저가 WS 최초 프레임으로만 브리지에 전달합니다. "이 브라우저에 기억"을 켜면 브라우저 로컬 볼트에 암호화되어 저장되며(clientVault), 서버로는 오지 않습니다. "잊기"로 지울 수 있습니다.

호스트키 검증은?

개발 브리지는 호스트키를 accept(hostVerifier 가 true)합니다. 프로덕션은 wss + 호스트키 핀닝을 권장합니다(브리지를 127.0.0.1 밖에 노출한다면 특히).

no_pw_tool 오류(요청/응답 test/exec)

요청/응답 경로에서 비밀번호 인증을 쓰려면 서버에 plink(Windows) 또는 sshpass(Linux)가 필요합니다. 대화형 PTY 경로는 이 도구가 필요 없습니다(브리지의 ssh2 가 직접 처리).


관련 항목

카드의 [ssh] 버튼
카드의 [ssh] 버튼