termal.in

Connector · Docker

Connector를 컨테이너에서 실행하세요 — 인바운드 포트 없이

공식 이미지 ghcr.io/termalinapp/termalin-connector는 Termalin Connector를 네트워크의 어떤 머신에서든 컨테이너 하나로 실행합니다 — NAS, 홈랩, 라우터, Kubernetes 클러스터, ECS 서비스 등에서요. Termalin을 향해 바깥쪽으로 연결하고, 그 네트워크의 서버, 내부 사이트, 데스크톱에 인바운드 포트를 열지 않고 닿습니다. 크기는 약 4 MB, amd64/arm64, uid 10001로 실행됩니다.

amd64 / arm64 · 인바운드 포트 불필요 · Pro(또는 14일 체험)

01  /  컨테이너를 쓰는 이유

네트워크에 컨테이너 하나, 열 것은 없이

Connector는 인바운드 포트도, 상주하는 키도 없이 Termalin이 비공개 머신에 닿게 해 줍니다. 어떤 머신은 바이너리와 서비스를 설치하기보다 컨테이너로 추가하는 편이 쉽고 — 어떤 머신은 컨테이너만 실행할 수 있습니다. 이미지는 그런 머신에 알맞습니다.

  • ▸NAS나 홈랩. LAN의 Synology, Unraid, 또는 Docker 호스트 — 그 머신 자체의 SSH와 네트워크의 다른 모든 서버, 사이트, 데스크톱에 닿습니다.
  • ▸라우터. MikroTik RouterOS 7은 컨테이너를 실행합니다. 컨테이너 하나가 라우터와 그 뒤의 모든 것을 위한 Connector를 담습니다.
  • ▸Kubernetes. 레플리카 1개짜리 Deployment가 클러스터 서비스에 내부 주소로 닿습니다.
  • ▸AWS ECS / VPC. 아웃바운드 HTTPS만 쓰는 Fargate 또는 EC2 작업 — 로드 밸런서도, 인바운드 규칙도 없이 — 이 비공개 인스턴스, 내부 사이트, 데스크톱에 닿습니다.

02  /  이미지

4메가바이트, 셸도 SSH 서버도 없이

이미지는 ghcr.io/termalinapp/termalin-connector:<version>(그리고 :latest)이며, linux/amd64와 linux/arm64를 지원합니다.

  • ▸약 4 MB. 정적 Connector 바이너리와 CA 인증서뿐, 그 외에는 아무것도 없습니다 — 셸도, 패키지 관리자도, SSH 서버도 없습니다. uid 10001로 실행되고 설정은 /data 볼륨에 보관합니다.
  • ▸공개적으로 빌드. 공개 저장소 termalinapp/termalin에서, termal.in의 서명된 릴리스 바이너리만으로 만듭니다 — 각 바이너리는 들어가기 전에 릴리스 매니페스트와 서명으로 검증됩니다.
  • ▸armv7은 아직 빌드하지 않습니다(32비트 ARM). 현재는 amd64와 arm64만 지원합니다.

03  /  빠른 시작

docker run 한 번으로 컨테이너를 등록합니다

캐비닛에서 등록 토큰을 받으세요(Connector 추가). 그런 다음:

docker run -d --name termalin --restart unless-stopped \
  -v termalin:/data \
  -e TERMALIN_TOKEN=<token> \
  -e TERMALIN_TARGET=192.168.1.10:22 \
  -e TERMALIN_USER=deploy \
  ghcr.io/termalinapp/termalin-connector

변수 설명:

변수역할
TERMALIN_TOKEN등록 토큰. 볼륨에 아직 설정이 없는 첫 시작 때만 사용됩니다.
TERMALIN_TARGET터미널, 명령, 파일이 향하는 SSH 서버의 host:port(Docker 호스트 또는 다른 머신). 컨테이너 안에서 127.0.0.1은 컨테이너 자신이며, 거기에는 SSH 서버가 없습니다.
TERMALIN_USER그 SSH 서버의 로그인 사용자(기본값: 계정의 기본 사용자).
TERMALIN_NAME캐비닛에 표시되는 이름(기본값: 컨테이너의 호스트 이름 — --hostname이나 이 값을 설정하세요).
TERMALIN_SERVER기본값은 https://termal.in입니다.

첫 시작: /data에 설정이 없고 토큰이 지정돼 있으면, 컨테이너는 등록하고 아래의 로그인 줄을 출력한 뒤 연결합니다. 이후 시작에서는 연결만 합니다 — 토큰은 환경에 남겨 두어도 되며, 다시 전송되지 않습니다. 설정도 토큰도 없으면 그렇다고 알리고 종료 코드 1로 끝납니다. docker stop은 즉시 멈춥니다. 다시 등록하려면 볼륨을 삭제하고 새 토큰으로 시작하세요.

04  /  로그인 줄

대상에서 인증서를 신뢰시키기

컨테이너의 Connector는 대상을 건드리지 않습니다. 등록 시 이 계정의 Termalin 단기 인증서가 로그인할 수 있게 해 주는 줄을 출력하며, docker exec termalin termalin-agent check로 다시 출력할 수 있습니다:

cert-authority,principals="termalin-<account id>" ssh-ed25519 AAAA… termalin-connector

이 줄을 대상의 로그인 사용자 ~/.ssh/authorized_keys에 넣으세요(한 줄, 파일 권한 600, ~/.ssh는 700). root로 관리하는 서버라면 대신 sshd_config에 TrustedUserCAKeys와 AuthorizedPrincipalsFile을 추가할 수도 있습니다 — check가 둘 다 출력합니다. 이 설정이 없으면 이 Connector를 통한 터미널과 명령은 대상이 거부합니다. 내부 사이트와 데스크톱에는 필요 없습니다. check는 대상의 SSH 서버가 응답하는지도 알려 주며(sshd=up), 로그인 상태는 ca=unknown으로 표시됩니다. 컨테이너 안에서는 대상의 파일을 읽을 수 없기 때문입니다.

05  /  SSH 서버가 필요 없는 경우

어디에도 sshd 없이, 내부 사이트와 데스크톱

내부 사이트와 원격 데스크톱(RDP, VNC)은 SSH를 거치지 않습니다. 백엔드가 Connector에 사이트나 데스크톱 자체의 host:port로 연결을 열라고 요청하면, Connector가 그곳에 직접 연결합니다. 그래서 VPC, ECS 서비스, 가정용 네트워크의 컨테이너는 SSH 서버가 전혀 없어도 내부 사이트와 데스크톱을 공개할 수 있습니다. 데이터베이스 비밀번호를 쓰는 데이터베이스 페이지도 같은 방식으로 데이터베이스 포트에 연결합니다.

  • ▸사설 주소만. 연결 대상은 사설 네트워크에 있어야 합니다 — RFC 1918, 루프백, 링크 로컬, CGNAT 100.64/10(예: Tailscale), IPv6 ULA. Connector는 인터넷으로 나가는 중계기가 아니며, 클라우드 메타데이터 주소(169.254.169.254 등)는 항상 거부됩니다.
  • ▸대상에 SSH 서버가 필요한 것: 터미널, ssh_exec를 비롯한 명령 도구, SFTP 파일, 비밀번호 없는 데이터베이스 도구, 메트릭, 백업. TERMALIN_TARGET을 설정하고 그곳에 로그인 줄을 추가하세요.

06  /  docker compose

같은 내용을 compose 파일로

services:
  termalin:
    image: ghcr.io/termalinapp/termalin-connector:latest
    restart: unless-stopped
    hostname: office-nas
    environment:
      TERMALIN_TOKEN: ${TERMALIN_TOKEN}
      TERMALIN_TARGET: 192.168.1.10:22
      TERMALIN_USER: deploy
    volumes:
      - termalin:/data
volumes:
  termalin:

Docker 호스트 자체에 닿으려면 그 LAN 주소를 쓰거나, Docker Desktop에서는 host.docker.internal:22를 쓰세요(Linux에서는 extra_hosts: ["host.docker.internal:host-gateway"]를 추가하세요).

07  /  NAS

Synology와 Unraid

  • ▸Synology(Container Manager): Registry → ghcr.io/termalinapp/termalin-connector를 추가하고, 위 변수로 컨테이너를 만들고, /data용 볼륨을 지정하고, ‘auto restart’를 켜세요. 터미널을 쓰려면 Control Panel → Terminal & SNMP에서 SSH를 활성화하고, TERMALIN_TARGET=<NAS address>:22를 설정하세요.
  • ▸Unraid: Docker → Add Container, 리포지터리 ghcr.io/termalinapp/termalin-connector, 경로 /data → /mnt/user/appdata/termalin, 변수는 컨테이너 변수로 넣으세요.
  • ▸바인드 마운트한 폴더는 uid 10001이 쓸 수 있어야 합니다(chown 10001:10001 /mnt/…/termalin). 이름이 지정된 볼륨은 Docker가 알아서 준비해 줍니다.

08  /  MikroTik RouterOS 7

라우터 자체에서

지금은 arm64와 x86 라우터만 지원합니다(아직 armv7 빌드가 없습니다). container 패키지와 device-mode를 활성화한 뒤:

/interface/veth add name=veth-termalin address=172.17.0.2/24 gateway=172.17.0.1
/interface/bridge/port add bridge=containers interface=veth-termalin
/container/envs add name=termalin key=TERMALIN_TOKEN value=<token>
/container/envs add name=termalin key=TERMALIN_TARGET value=192.168.88.1:22
/container/mounts add name=termalin-data src=/usb1/termalin dst=/data
/container add remote-image=ghcr.io/termalinapp/termalin-connector:latest \
    interface=veth-termalin envlist=termalin mounts=termalin-data \
    root-dir=/usb1/termalin-root start-on-boot=yes logging=yes

이는 MikroTik 자체 컨테이너 가이드처럼 172.17.0.1/24와 아웃바운드 NAT를 갖춘 containers 브리지를 전제로 합니다. 라우터 자체 SSH(/ip/service enable ssh)나 그 뒤의 어떤 호스트든 대상으로 삼으세요.

09  /  Kubernetes

레플리카 하나, Secret 하나, 작은 PVC 하나

apiVersion: v1
kind: Secret
metadata: { name: termalin }
stringData: { token: "<token>" }
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata: { name: termalin-data }
spec:
  accessModes: [ReadWriteOnce]
  resources: { requests: { storage: 16Mi } }
---
apiVersion: apps/v1
kind: Deployment
metadata: { name: termalin-connector }
spec:
  replicas: 1
  strategy: { type: Recreate }
  selector: { matchLabels: { app: termalin-connector } }
  template:
    metadata: { labels: { app: termalin-connector } }
    spec:
      securityContext: { runAsUser: 10001, runAsGroup: 10001, fsGroup: 10001, runAsNonRoot: true }
      containers:
        - name: connector
          image: ghcr.io/termalinapp/termalin-connector:0.2.3
          env:
            - { name: TERMALIN_TOKEN, valueFrom: { secretKeyRef: { name: termalin, key: token } } }
            - { name: TERMALIN_TARGET, value: "10.0.12.7:22" }
            - { name: TERMALIN_NAME, value: "k8s-prod" }
          securityContext: { readOnlyRootFilesystem: true, allowPrivilegeEscalation: false, capabilities: { drop: [ALL] } }
          resources: { requests: { cpu: 10m, memory: 16Mi }, limits: { memory: 64Mi } }
          volumeMounts: [{ name: data, mountPath: /data }]
      volumes:
        - { name: data, persistentVolumeClaim: { claimName: termalin-data } }

클러스터 내부 서비스는 내부 사이트에서 클러스터 주소로 지정해 닿습니다 — http://grafana.monitoring.svc:3000은 클러스터 DNS를 거쳐 사설 주소로 해석됩니다. 등록 하나당 레플리카는 하나로 유지하세요. 같은 볼륨을 쓰는 pod 둘은 같은 Connector가 두 번 도는 셈입니다.

10  /  AWS ECS

아웃바운드 전용 Fargate 또는 EC2 작업

desired count 1인 서비스에 위 이미지를 쓰고, Secrets Manager의 토큰을 TERMALIN_TOKEN으로, /data에는 EFS 볼륨을 연결하세요(소유자는 10001 — uid/gid 10001인 EFS 액세스 포인트). 필요한 것은 termal.in으로 나가는 아웃바운드 HTTPS뿐입니다 — 로드 밸런서도, 인바운드 규칙도 없습니다. 대상의 보안 그룹은 사용하는 포트(22, 3389, 사이트의 포트)로 이 작업이 들어오도록 허용해야 합니다. ECS 작업 메타데이터 주소는 다른 모든 메타데이터 주소처럼 거부됩니다.

11  /  업데이트와 한계

새 태그 받기, 그리고 컨테이너가 할 수 없는 것

  • ▸업데이트. 컨테이너는 스스로 업데이트하지 않습니다 — 새 태그를 받아 다시 만드세요(docker compose pull && docker compose up -d, 또는 Deployment에 새 이미지 태그). 볼륨이 등록 정보를 유지합니다. 캐비닛에는 컨테이너용 ‘지금 업데이트’가 표시되지 않습니다.
  • ▸SSH 기능에는 대상이 필요합니다. 터미널, 명령, 파일, 비밀번호 없는 데이터베이스, 메트릭, 백업은 대상에 SSH 서버가 있고 그곳에 로그인 줄이 있어야 합니다. 이미지 자체에는 SSH 서버가 없습니다.
  • ▸Connector 하나당 대상 SSH 서버는 하나(TERMALIN_TARGET). 내부 사이트와 데스크톱은 어떤 사설 주소든 지정할 수 있습니다.
  • ▸armv7(32비트 ARM)은 아직 빌드하지 않습니다.

자주 묻는 질문

어떤 아키텍처를 지원하고 이미지 크기는 얼마인가요?

linux/amd64와 linux/arm64를 위한 멀티 아키텍처 이미지이며, 크기는 약 4 MB입니다. 정적 Connector 바이너리와 CA 인증서뿐, 그 외에는 아무것도 없습니다 — 셸도, 패키지 관리자도, SSH 서버도 없습니다. uid 10001로 실행되고 설정은 /data 볼륨에 보관합니다. armv7(32비트 ARM)은 아직 빌드하지 않습니다.

이미지는 어디서 만들어지나요?

공개 저장소 termalinapp/termalin에서 공개적으로 빌드되며, termal.in에 게시된 서명된 릴리스 바이너리만 사용합니다. 각 바이너리는 이미지에 들어가기 전에 릴리스 매니페스트와 서명으로 검증됩니다.

컨테이너 안이나 옆에 SSH 서버가 필요한가요?

내부 사이트, 원격 데스크톱(RDP/VNC), 데이터베이스 페이지에는 필요 없습니다. 이들은 Connector를 통해 대상 자체의 host:port로 바로 연결되므로, VPC나 가정용 네트워크의 컨테이너는 어디에도 sshd 없이 이들을 공개할 수 있습니다. 하지만 터미널, 명령, SFTP 파일, 비밀번호 없는 데이터베이스 도구, 메트릭, 백업은 대상(TERMALIN_TARGET)에 SSH 서버가 있고 그곳에 로그인 줄을 추가해야 합니다 — 이미지 자체에는 SSH 서버가 없습니다.

컨테이너를 어떻게 업데이트하나요?

컨테이너는 스스로 업데이트하지 않습니다. 새 태그를 받아 다시 만드세요 — docker compose pull && docker compose up -d를 실행하거나, Deployment에 새 이미지 태그를 지정하면 됩니다. /data 볼륨이 등록 정보를 유지하므로 다시 등록할 필요는 없습니다. 캐비닛에는 컨테이너용 ‘지금 업데이트’가 표시되지 않습니다.

비용은 얼마이고, 어떤 요금제가 필요한가요?

Connector는 호스팅 기능이므로 Pro 또는 14일 체험 계정이 필요합니다. 이미지는 무료로 받을 수 있고, 추가할 수 있는 Connector 수에는 제한이 없습니다.

컨테이너 하나로 네트워크 전체에 닿으세요

토큰 하나, docker run 한 번, 인바운드 포트 없이. NAS, 라우터, Kubernetes, ECS에서요.

Pro(또는 14일 체험) · 내부 사이트 · 원격 데스크톱