DeepSeek Harness VPS 설치 및 SSH 터널링 보안 설정
DeepSeek Harness를 Linux VPS에 설치하고 3080 포트를 안전하게 사용하는 방법을 안내합니다. npm 버전 고정 방법과 플러그인 구조를 이해하고, SSH 터널을 통해 외부 노출 없이 웹 UI에 접속하여 에이전트를 안전하게 운영하는 구체적인 절차를 확인하십시오.
DeepSeek Harness란 무엇인가
DeepSeek Harness(dsh)는 VPS(가상 사설 서버)에서 실행할 수 있는 Node.js 에이전트 런타임입니다. 이를 안전하게 실행하는 방법은 127.0.0.1에 바인딩하고 SSH(Secure Shell) 터널을 통해 브라우저로 접속하는 것입니다. 이 도구는 터미널이 아닌 3080 포트에서 웹 UI(사용자 인터페이스)를 제공합니다. 웹 서버 자체는 비밀번호를 요구하지 않으므로, 3080 포트를 외부에 공개하면 해당 포트를 발견한 누구든 사용자의 파일에 접근하고 Linux 사용자 권한으로 명령을 실행할 수 있는 에이전트를 얻게 됩니다.
DeepSeek는 2026년 8월 13일에 이를 MIT 라이선스로 공개했으며, npm 패키지 @deepseek-ai/dsh로 제공됩니다. 이 프로젝트는 개발자 프리뷰 단계이며 호환성을 깨뜨리는 변경 사항이 발생할 수 있다고 명시되어 있습니다. 아래의 모든 버전 번호는 2026년 8월 기준의 스냅샷이므로, 중요한 서버에 설치하기 전에 저장소를 확인하십시오.
전체 설계에는 하나의 핵심 아이디어가 관통합니다. 바로 모든 것이 플러그인이라는 점입니다. 모델 어댑터, 도구 레지스트리, 세션 로그, 샌드박스, 스케줄러, 그리고 에이전트 루프 자체가 하나의 공유 컨텍스트에 로드되는 플러그인이며, 무엇이든 교체할 수 있습니다. 플러그인이 단순히 장식하는 특권 코어 같은 것은 존재하지 않습니다. 이것이 바로 Harness를 시도해 볼 가치가 있는 이유이자, 동시에 유일한 실질적인 위험이 존재하는 지점이기도 합니다.
하네스는 모델이 아닙니다
하네스는 에이전트 루프를 실행합니다. 추론은 다른 곳에 있는 모델에서 수행되므로, API(Application Programming Interface) 키나 직접 호스팅하는 모델 엔드포인트 주소를 입력하기 전까지는 아무것도 작동하지 않습니다.
이 설정은 UI의 Settings 메뉴 내 Models 항목에서 수행합니다. 카탈로그에는 주요 API 제공업체(DeepSeek, OpenAI, Anthropic)를 위한 준비된 카드가 있으며, 여기에 키를 붙여넣으면 됩니다. "Add a custom provider"는 유용한 옵션입니다. 이 옵션은 제공업체 ID, 표시 이름, 베이스 URL, API 프로토콜, 자격 증명을 입력받습니다. 이 옵션은 OpenAI 호환 프로토콜을 사용하므로, 해당 프로토콜을 구현하는 모든 게이트웨이나 로컬 서버와 연동할 수 있습니다. 커스텀 제공업체는 OpenAI 호환 GET /models 엔드포인트를 쿼리하여 모델 목록을 자동으로 채울 수도 있습니다.
이 방법을 통해 하네스를 동일한 VPS에 있는 모델로 연결할 수 있습니다. Ollama는 http://127.0.0.1:11434/v1/에서 OpenAI 호환 API를 노출합니다. 이때 API 키 필드는 필수 항목이지만 실제로는 무시되므로, 관례적으로 ollama와 같은 임의의 문자열을 입력하면 됩니다. VPS에 탑재할 수 있을 만큼 작은 모델이 에이전트를 구동하기에 충분한 성능을 내는지는 더 어려운 문제이며, Ollama와 vLLM의 로컬 모델 서버로서의 차이에 따라 필요한 RAM 용량이 결정됩니다.
UI에 입력한 키는 쓰기 전용입니다. 하네스는 이를 $DSH_HOME/.credentials.yaml에 저장하고 settings.yaml에는 자격 증명 참조값만 유지합니다. $DSH_HOME의 기본값은 ~/.dsh입니다. 이 파일은 비밀번호 파일과 다름없으므로 각별히 주의해서 관리하십시오. 이 파일을 읽을 수 있는 사람은 누구나 사용자의 API 예산을 사용할 수 있습니다.
설치 전 준비 사항
- SSH 접근이 가능한 Ubuntu 24.04 또는 최신 Linux가 설치된 VPS
- Node.js 22.19 이상(22.x 버전) 또는 Node.js 24 이상 (해당 프로젝트가 빌드 및 테스트를 수행하는 버전입니다)
root가 아닌 일반 사용자 계정 (에이전트는 프로세스를 시작한 사용자의 권한으로 셸 명령을 실행하기 때문입니다)- 플러그인 설치 계획이 있는 경우 PATH에 포함된
pnpm(플러그인 명령이 이를 호출하여 셸을 실행하기 때문입니다) - 방화벽 및 제공업체의 별도 네트워크 방화벽에서 3080 포트 차단
Ubuntu의 기본 nodejs 패키지는 이 하네스(harness)가 요구하는 버전보다 낮으므로, apt install nodejs를 사용하는 대신 NodeSource나 nvm을 통해 Node를 설치하십시오. VPS를 새로 설정하는 경우, 가장 먼저 SSH 보안을 강화하는 데 10분을 투자할 가치가 있습니다. 앞으로 의존하게 될 터널의 안정성은 그 기반이 되는 SSH 서버의 보안 수준에 달려 있기 때문입니다.
VPS에 DeepSeek Harness 설치 및 특정 버전 고정
node --version
npx @deepseek-ai/dsh@0.1.0-rc.6 webnpx은 패키지를 다운로드하고 dsh 바이너리를 실행합니다. web는 --profile web의 별칭이며, 브라우저 애플리케이션을 구동하고 프로세스가 수신 대기 중인 주소를 출력합니다. 기본값은 http://127.0.0.1:3080입니다.
버전을 고정하십시오. npx @deepseek-ai/dsh web은 실행하는 시점에 latest 태그가 가리키는 모든 것을 확인합니다. 해당 프로젝트는 이미 여러 릴리스 후보를 배포했으며, 향후 호환성을 깨뜨리는 변경 사항이 있을 것이라고 예고했습니다. 0.1.0-rc.6은 2026년 8월 13일 기준으로 latest이 가리키던 버전입니다. 버전을 고정하면 오늘 설정한 서버가 다음 달에도 동일하게 동작하므로, 업그레이드는 예기치 못한 사고가 아니라 사용자가 직접 결정하는 작업이 됩니다.
매일 사용하는 경우, 매번 새로 확인하지 말고 한 번만 설치하십시오.
npm install -g @deepseek-ai/dsh@0.1.0-rc.6
dsh --profile web --help두 번째 줄은 실행할 가치가 있습니다. 런처와 웹 애플리케이션이 서로 다른 플래그 세트를 사용하기 때문입니다. dsh --help는 런처 자체의 옵션을 보여줍니다. dsh --profile web --help은 웹 애플리케이션이 허용하는 플래그를 보여주며, 이곳에 --port, --host 및 반복 가능한 --trusted-host이 포함되어 있습니다.
이제 수신 대기 중인 주소를 확인하십시오.
ss -tlnp | grep 3080로컬 주소 열에는 127.0.0.1:3080이 표시되어야 합니다. 만약 0.0.0.0:3080로 표시된다면 UI가 인터넷에서 접근 가능한 상태이므로, 다른 작업을 수행하기 전에 즉시 프로세스를 중단해야 합니다.
포트 3080을 절대 공개해서는 안 되는 이유
이 웹 서버에는 인증 계층이 없습니다. 설정 파일에서 수신 호스트와 수신 포트를 지정하는 것이 보안의 전부입니다. 루프백이 아닌 환경에서의 접근 제어는 신뢰할 수 있는 호스트 설정에 의존하며, 이는 로그인 화면과는 무관합니다.
해당 포트 뒤에서 실행되는 기능을 고려하십시오. 에이전트는 작업 공간의 파일을 수정하고 셸 명령을 실행하며, 공급자 자격 증명이 디스크에 함께 저장됩니다. 따라서 포트 3080이 열려 있다는 것은 API 키가 포함된 상태로, 프로세스를 실행한 사용자의 권한으로 동작하는 채팅 인터페이스가 달린 원격 셸을 노출하는 것과 같습니다. 이를 위해 별도의 익스플로잇은 필요하지 않습니다. 공격자는 포트 번호만 알면 되며, 스캐너는 호스트가 온라인 상태가 된 지 몇 시간 내에 포트 번호를 찾아냅니다.
CLI(명령줄 인터페이스)도 이러한 보안 정책을 따릅니다. 0.1.0-rc.6 버전부터는 의도적으로 --host 0.0.0.0을 지원하지 않으며, 실행 시 사용법 오류를 출력하고 종료됩니다. 이러한 거부 동작은 기능의 일부이므로, 이를 제거하는 패치를 찾지 마십시오.
터널링이 적합하지 않은 경우, 합리적인 배포 방식은 두 가지가 있습니다. 첫째, 호스트를 사설 오버레이 네트워크에 배치하여 본인의 장치에서만 라우팅 가능한 주소를 할당하는 것입니다. 이는 자체 호스팅 Headscale 제어 서버를 통해 구현할 수 있습니다. 둘째, 포트 3080에 도달하기 전에 요청을 인증하는 리버스 프록시를 앞단에 두는 것입니다. 예를 들어 Authentik 싱글 사인온 서버를 사용하여 포워드 인증을 수행할 수 있습니다. 인증 기능이 없는 리버스 프록시는 보안 제어 수단이 아니며, 단지 URL만 길어질 뿐입니다.
SSH 터널을 통해 웹 UI에 접속하기
이 작업은 서버가 아닌 로컬 노트북에서 수행합니다.
ssh -N -L 3080:127.0.0.1:3080 you@your-server-L은 노트북의 3080 포트를 열고, 해당 포트로 들어오는 모든 연결을 암호화된 SSH 세션을 통해 전달합니다. 127.0.0.1:3080 부분은 서버 측에서 해석되므로, 연결은 마치 사용자가 해당 장비 앞에 직접 앉아 있는 것처럼 루프백(loopback)을 통해 harness로 전달됩니다. -N은 원격 셸을 시작하지 않도록 지시하는데, 이는 포트 포워딩 외에 다른 기능은 필요하지 않기 때문입니다.
그런 다음 로컬 브라우저에서 http://127.0.0.1:3080를 엽니다. 만약 노트북에서 3080 포트가 이미 사용 중이라면 왼쪽 숫자를 변경하십시오. 예를 들어 ssh -N -L 3180:127.0.0.1:3080 you@your-server로 설정한 뒤 http://127.0.0.1:3180으로 접속합니다. 왼쪽 숫자는 로컬 포트이고 오른쪽 숫자는 서버 측 포트이므로, 왼쪽 숫자만 변경하면 됩니다.
이 명령어를 ~/.ssh/config에 저장하여 매번 입력하는 번거로움을 줄이십시오.
Host dsh
HostName 203.0.113.10
User deploy
IdentityFile ~/.ssh/id_ed25519
LocalForward 3080 127.0.0.1:3080그 후 ssh -N dsh을 실행하여 터널을 시작합니다. 브라우저에서 연결이 거부되었다는 메시지가 나타난다면, 대개 터널은 정상적으로 연결되었으나 원격 측에서 아무것도 수신 대기 중이지 않다는 의미입니다. SSH는 harness 실행 여부와 관계없이 포트를 포워딩하기 때문입니다. 위에서 언급한 ss 명령어로 서버 상태를 확인하십시오.
로그아웃 후에도 하네스(harness)를 계속 실행하기
npx 명령은 셸이 종료되면 함께 종료됩니다. systemd 사용자 서비스는 로그아웃 후에도 유지되며, 충돌이나 재부팅이 발생해도 하네스를 자동으로 다시 시작합니다.
loginctl enable-linger $USER
mkdir -p ~/.config/systemd/user
command -v dshenable-linger 설정이 중요한 이유는, 사용자 서비스가 기본적으로 마지막 세션이 종료될 때 함께 중단되기 때문입니다. 이 설정이 없으면 터널을 닫는 즉시 하네스가 종료됩니다. command -v dsh 명령으로 출력된 절대 경로를 유닛 파일에 입력하십시오. systemd는 로그인 셸이 구성한 PATH 환경 변수를 참조하지 않기 때문입니다.
[Unit]
Description=DeepSeek Harness web UI
After=network-online.target
[Service]
Type=simple
WorkingDirectory=%h/projects/site
ExecStart=/usr/local/bin/dsh web
Restart=on-failure
RestartSec=5
[Install]
WantedBy=default.targetWorkingDirectory 설정은 단순히 보기 좋으라고 하는 것이 아닙니다. dsh 프로세스는 실행된 디렉터리를 기본 파일 시스템 위치로 사용하므로, 잘못된 위치에서 서비스를 시작하면 에이전트가 엉뚱한 작업 공간을 기본값으로 인식하게 됩니다. 물론 UI에서 작업 공간을 직접 선택할 수도 있습니다.
systemctl --user daemon-reload
systemctl --user enable --now dsh
systemctl --user status dsh서비스가 시작되지 않는다면 대부분 ExecStart 경로가 잘못되었거나 바이너리가 거부하는 Node 버전 문제일 가능성이 높으며, journalctl --user -u dsh -n 50 명령을 통해 원인을 확인할 수 있습니다. 이와 동일한 패턴을 적용하여 VPS에서 코딩 에이전트를 상시 가동할 수 있으며, 발생하는 오류 유형도 동일합니다.
플러그인의 허용 범위
플러그인은 공유 컨텍스트에 서비스, 타입이 지정된 이벤트, 가역적 효과를 제공하는 모듈입니다. 확장 지점은 주의 깊게 읽어볼 필요가 있습니다.
ctx.llm에 모델 공급자 등록ctx.tools에 모델용 도구 추가ctx.shell를 통해 셸 백엔드 제공ctx.fs을 통해 파일 시스템 접근 또는 정책 제공ctx.commands에 사용자 명령 등록ctx.jobs를 통해 백그라운드 작업 실행ctx.sandbox백엔드로 생성된 프로세스 래핑agent/*및tools/*이벤트를 통해 요청 및 도구 호출 가로채기- 지속 가능한 세션 상태 확장
ctx.agents을 통해 UI 구동
이 목록을 공격자의 관점에서 읽어보십시오. 플러그인은 파일 시스템 계층과 셸 계층을 제공할 수 있으며, 모델이 수행하는 모든 도구 호출의 중간에 위치할 수 있습니다. 플러그인은 다른 모든 것과 동일한 프로세스에 로드되는 일반적인 Node 코드이므로, 플러그인과 이러한 연결 지점 사이에 권한 확인 대화 상자는 존재하지 않습니다. 플러그인을 설치하는 것은 에이전트의 권한으로 낯선 사람의 코드를 실행하는 것과 같으며, 에이전트의 권한은 곧 사용자의 Unix 계정 권한과 같습니다.
이는 VPS에서 에이전트에 MCP 서버를 연결할 때 내리는 신뢰 결정과 동일합니다. 여기서 MCP는 모델 컨텍스트 프로토콜(Model Context Protocol)을 의미합니다. 또한 VPS에서 코딩 에이전트를 안전하게 실행하는 것이 모델이 아닌 실행 계정부터 시작해야 하는 이유이며, npm 공급망 공격이 서버에 치명적인 이유이기도 합니다. 설치 단계 자체가 곧 침해이며, 그 과정에서 어떠한 경고도 표시되지 않기 때문입니다.
플러그인의 출처
플러그인은 프로필 내에 존재합니다. 프로필은 $DSH_HOME 아래에 저장되는 명명된 구성이며 기본값은 ~/.dsh입니다. 각 프로필 디렉터리는 설치된 out-of-tree 플러그인을 보관합니다. CLI는 프로필 디렉터리를 작업 디렉터리로 사용하여 인수를 pnpm로 직접 전달함으로써 이를 관리합니다.
dsh plugin --profile web add github:deepseek-harness/turtle-ui
dsh plugin --profile web remove turtle-ui인수가 변경되지 않은 상태로 pnpm에 도달하므로 add, remove, update 및 why는 모든 pnpm 프로젝트에서와 동일하게 동작하며, 플러그인은 npm 패키지나 GitHub 참조가 될 수 있습니다. pnpm는 먼저 PATH에 존재해야 합니다. Node 22 이상 버전에서는 corepack enable pnpm이 이를 PATH에 추가합니다.
플러그인 탐색은 GitHub 토픽을 통해 이루어집니다. 플러그인 작성자는 자신의 저장소에 dsh-plugin 토픽을 추가하며, 해당 토픽을 탐색하여 존재하는 플러그인을 찾을 수 있습니다. 토픽은 작성자가 자신의 저장소에 적용하는 레이블입니다. 이를 검토하거나 서명하는 주체는 없으며, 토픽 페이지는 안전성이 아닌 인기도를 측정하는 별점순으로 정렬됩니다.
다음 네 가지 습관을 통해 관리 효율을 유지하십시오. 대부분의 플러그인은 10분 내에 읽을 수 있을 만큼 작으므로 설치하기 전에 소스 코드를 읽으십시오. 브랜드를 추적하는 대신 정확한 버전이나 커밋을 고정하십시오. 하네스는 다른 자원을 소유하지 않는 사용자 계정으로 실행하고, 언제든 재구축할 의향이 있는 VPS에서 운영하십시오. 에이전트에는 프로덕션 서비스에서 사용하는 키와 분리된, 자체 지출 한도가 설정된 별도의 API 키를 부여하십시오.
설계 방식을 결정하기 전에 비교해 보고 싶다면, Omnigent 다중 에이전트 하네스가 다른 구조로 동일한 문제를 해결하며, 플러그인을 사용하기 시작하면 그 장단점이 명확해집니다.
가장 먼저 확인해야 할 문제
Node 버전이 너무 낮음. 이 프로젝트는 Node 22.19 이상의 22.x 버전 또는 Node 24 이상을 대상으로 하며, CI 환경도 이를 기준으로 테스트합니다. 이전 런타임에서는 코드에 포함된 문법이나 API를 지원하지 않아 시작 시 실패합니다. 다른 작업을 수행하기 전에 node --version을 실행하십시오.
포트 3080이 이미 사용 중임. 두 번째 하네스(harness), 좀비 프로세스, 또는 동일하게 3080 포트를 사용하는 다른 애플리케이션이 원인일 수 있습니다. ss -tlnp | grep 3080로 해당 프로세스를 찾아 중단하거나, dsh web --port 3180을 사용하여 다른 포트에서 하네스를 시작하십시오. --port은 웹 애플리케이션에 속하므로 web 뒤에 위치해야 합니다.
브라우저가 터널을 통해 연결하지 못함. 서버의 공인 주소가 아닌 127.0.0.1으로 접속했는지 확인하십시오. 포트 포워딩은 로컬 컴퓨터에서만 유효하기 때문입니다. 또한, SSH는 원격지에서 응답 여부와 관계없이 포워딩을 설정하므로, 하네스가 서버에서 정상적으로 리스닝 중인지 확인하십시오.
dsh plugin가 즉시 실패함. 이 명령어는 pnpm를 래핑한 것이므로, pnpm 바이너리가 없으면 플러그인 작업이 시작되기도 전에 중단됩니다.
에이전트가 프로젝트를 인식하지 못함. 워크스페이스는 프로세스가 시작된 디렉터리를 기본값으로 사용합니다. 따라서 WorkingDirectory이 홈 디렉터리로 설정된 유닛은 에이전트에게 홈 디렉터리를 제공하게 됩니다. UI에서 워크스페이스를 선택하거나, 유닛 설정을 수정한 뒤 다시 로드하십시오.
FAQ
DeepSeek Harness 웹 UI를 3080 포트로 노출해도 안전합니까?
아닙니다. 해당 웹 서버에는 자체 로그인 기능이 없으며, 프로세스를 실행한 사용자의 권한으로 파일 수정 및 셸 명령 실행을 수행합니다. 또한 공급자 API 키가 동일한 디스크에 저장됩니다. 리스너를 127.0.0.1에 유지하고 SSH 터널을 통해 접속하십시오. 사설 오버레이 네트워크를 사용하거나, 포트에 도달하기 전 모든 요청을 인증하는 리버스 프록시를 사용하는 방법도 있습니다. 버전 0.1.0-rc.6부터 CLI는 --host 0.0.0.0를 거부하고 사용법 오류를 출력하며 종료되는데, 이는 개발자들이 이 방식에 대해 어떻게 생각하는지를 보여줍니다.
DeepSeek API 키가 필요한가요, 아니면 로컬 모델을 사용할 수 있나요?
둘 다 가능합니다. 이 하네스는 모델이 아니라 런타임이기 때문입니다. 설정(Settings)의 모델(Models) 메뉴에서 카탈로그 공급자 카드에 키를 붙여넣거나, "사용자 지정 공급자 추가(Add a custom provider)"를 선택하여 OpenAI 호환 프로토콜을 지원하는 베이스 URL을 입력할 수 있습니다. 로컬 Ollama 서버는 http://127.0.0.1:11434/v1/에서 응답하며, API 키 필드에는 아무 문자열이나 입력해도 됩니다. 키는 $DSH_HOME/.credentials.yaml에 저장되며, 기본 경로는 ~/.dsh/.credentials.yaml입니다.
DeepSeek Harness 플러그인을 설치하면 플러그인에 어떤 권한이 부여됩니까?
하네스를 실행하는 계정의 권한이 부여됩니다. 플러그인은 동일한 프로세스 내에서 로드되는 Node 코드이며, 확장 지점에는 셸 백엔드, 파일 시스템 계층, 도구 레지스트리 및 모든 도구 호출을 감싸는 이벤트가 포함됩니다. 플러그인 자체가 샌드박스를 제공하지 않는 한, 이러한 영역으로부터 플러그인을 격리할 수 있는 방법은 없습니다. 설치하기 전에 소스 코드를 검토하고, 하네스는 보호할 데이터가 없는 사용자 계정으로 실행하십시오.
어떤 버전을 설치해야 하며, 계속 작동할까요?
npx @deepseek-ai/dsh@0.1.0-rc.6 web과 같이 정확한 버전을 설치하십시오. 이는 2026년 8월 13일 기준으로 latest 태그가 가리키던 버전입니다. 이 프로젝트는 스스로를 개발자 프리뷰라고 지칭하며 호환성을 깨뜨리는 변경 사항이 발생할 수 있다고 명시하고 있습니다. 따라서 버전을 고정하지 않은 명령은 매일 다르게 동작할 수 있습니다. 업그레이드 전 저장소를 확인하고, 버전이 0으로 시작하는 동안에는 설정 키와 플러그인 인터페이스가 변경될 수 있음을 인지하십시오.