본문으로 바로가기 본문 바로가기
WIKI 기술 지식 베이스

SSH 서버 실행하기 (Run an SSH server)

원문 보기 위키 갱신

여러 도구가 SSH를 통해 서버와 상호작용할 수 있게 해 주는데, 이 가이드에서는 WireGuard VPN 대신 Machine에 SSH 서버를 직접 구성하고 배포하는 방법을 알려드려요. 파일 복사, 편집, 배포 등 다양한 작업에 SSH 도구를 활용할 수 있어요.

출처: 문서

본문

Illustration by Annie Ruygt of a terminal window, a computer and some numbers

개요

여러 도구가 SSH를 통해 서버와 상호작용할 수 있게 해 줘요. 이런 도구들은 파일 복사(rsync, scp, sshfs), 편집(emacs, vim, vscode), 그리고 배포(ansible, github actions, kamal) 같은 작업에 유용해요.

이런 도구를 사용하는 한 가지 방법은 wireguard VPN을 설정하고 새 SSH 자격 증명을 발급하는 거예요. 하지만 일부 사용 사례(예: github actions)에서는 이것이 비현실적일 수 있어요.

대안으로, Machine에 SSH 서버를 구성하고 배포할 수 있어요. 이 가이드가 그 과정을 안내해 드릴게요.

진행하기 전에 주의사항 하나: 모든 클라이언트가 이 서비스를 IPv6로만 접근한다고 확신하지 않는다면 전용 IPv4 주소가 필요해요.

opensshd 설치 및 구성

대부분의 Docker 이미지는 궁극적으로 Debian 기반이므로 다음이 잘 작동할 거예요. 다른 운영 체제에 맞게 조정하세요(예: Alpine).

RUN apt-get update \
 && apt-get install -y openssh-server \
 && cp /etc/ssh/sshd_config /etc/ssh/sshd_config-original \
 && sed -i 's/^#\s*Port.*/Port 2222/' /etc/ssh/sshd_config \
 && sed -i 's/^#\s*PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config \
 && mkdir -p /root/.ssh \
 && chmod 700 /root/.ssh \
 && mkdir /var/run/sshd \
 && chmod 755 /var/run/sshd \
 && rm -rf /var/lib/apt/lists /var/cache/apt/archives

참고:

  • 이렇게 하면 sshd가 내부적으로 포트 2222에서 실행돼요. 기본적으로 sshd는 모든 네트워크 인터페이스에서 수신 대기하지만, fly.io machines는 이미 프라이빗 네트워크(ipv6) 인터페이스의 포트 22에서 수신 대기하고 있어서 sshd가 오류를 내거나 시작에 실패할 수 있기 때문이에요. 게다가 fly deploy는 앱이 export하는 인터페이스에서 수신 대기 중인지 확인하려고 시도하는데, fly가 포트 22에서 수신 대기하는 것을 앱이 실행 중이라는 증거로 잘못 해석할 수 있어요.
  • Password Authentication은 비활성화돼요. 불필요한 프롬프트를 피하기 위해서예요. 대신 SSH 키를 사용할 거예요.
  • 위 내용은 root 사용자를 가정해요. 앱이 다른 사용자로 실행된다면 /root를 그 사용자의 홈 디렉터리로 바꾸세요(예: /home/rails).

내부 포트 2222를 외부 포트 22로 매핑

fly.toml에 다음을 추가하세요:

[[services]]
  internal_port = 2222
  protocol = "tcp"
  auto_stop_machines = true
  auto_start_machines = true
  [[services.ports]]
    port = 22

이 섹션은 내부 포트와 외부 포트가 같더라도 필요해요.

참고:

  • internal_port는 이전 단계에서 선택한 포트와 일치해야 해요.
  • port는 사용 가능한 아무 포트나 될 수 있어요. 22는 SSH의 기본 포트이고 대부분의 애플리케이션이 사용할 것으로 기대하는 포트예요.
  • 웹 서버 포트처럼, 서버가 유휴 상태일 때 꺼졌다가 접근 시 다시 시작되도록 구성할 수 있어요. 필요에 따라 auto_stop_machines와 auto_start_machines를 자유롭게 조정하세요.

openssh 서버 시작

여러 프로세스를 실행하는 방법은 다양해요. 가장 간단한 방법은 앱보다 먼저 sshd를 시작하는 거예요. Dockerfile에서 ENTRYPOINT를 찾으세요. 없다면 앱 디렉터리에 스크립트를 만들고, 그 스크립트 이름을 ENTRYPOINT로 지정하고 실행 가능하게 만드세요.

그런 스크립트의 예시:

#!/bin/bash -e

/usr/sbin/sshd

exec "$@"

주의: 위 내용은 root로 실행해야 해요. Dockerfile이 다른 USER를 지정한다면 sudo를 설치하고 구성한 다음 메인 프로세스를 실행하기 전에 sudo 접근을 제거하는 방식으로 해결할 수 있어요.

예를 들어, 사용자 id가 rails라면 Dockerfile에 다음을 추가해요:

RUN apt-get install -y sudo && \
    echo "%rails ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers

그리고 entrypoint 스크립트를 업데이트해요:

sudo /usr/sbin/sshd
sudo sed -i "/^%rails/d" /etc/sudoers

위 명령들은 sshd를 root로 시작한 다음 권한이 없는 사용자 id에서 sudo 접근을 제거해요.

SSH 키 업로드

먼저 SSH 키를 찾으세요. ssh-keygen으로 새 키를 만들거나, 기존 키를 사용할 수 있어요: 홈 디렉터리의 .ssh 폴더에서 id_rsa.pub 같은 이름의 파일을 찾아보세요.

찾았다면 진행하는 방법은 여러 가지가 있어요. 아래에 두 가지 방법을 소개할게요. 앱이 사용하는 프레임워크에 따라 자격 증명이나 환경 변수를 저장하는 다른 방법이 있을 수도 있어요. 이 단계는 공개 키만 다루므로 누출을 막기 위한 특별한 조치가 필요하진 않아요.

대안 1: 시크릿을 설정하고 사용하기

fly secrets set "AUTHORIZED_KEYS=$(cat ~/.ssh/id_rsa.pub)"

Powershell 사용자는 대신 다음을 사용하세요:

fly secrets set "AUTHORIZED_KEYS=$(Get-Content $HOME\.ssh\id_rsa.pub)"

다음으로 entrypoint 스크립트에 다음 내용을 포함하도록 업데이트하세요:

echo $AUTHORIZED_KEYS > /root/.ssh/authorized_keys

sudo로 실행 중이라면 대신 다음을 하면 돼요:

echo $AUTHORIZED_KEYS | sudo tee /root/.ssh/authorized_keys > /dev/null

대안 2: 볼륨에서 복사

볼륨이 있고 거기에 파일을 업로드할 수 있다면, entrypoint 스크립트의 일부로 직접 복사할 수 있어요.

cp /volume/.ssh/authorized_keys /root/.ssh

[권장] SSH 호스트 키를 안정적으로 유지하기

서버에 처음 SSH로 접속하면 접근하는 서버의 지문(fingerprint)이 표시돼요. 그 지문을 수락하면 .ssh 디렉터리의 known_hosts 파일에 추가돼요. 이 키는 openssh-server를 설치할 때 생성돼요.

문제는 앱을 재배포할 때 발생해요. 뭔가가 바뀌거나(또는 docker 캐시가 만료되면) openssh-server 설치가 다시 실행되면서 새 키가 생성될 수 있어요. 이런 키가 사용되는 것을 막으려면 키를 캡처해서 복원하면 돼요.

다음은 /volume에 마운트된 볼륨이 있고 entrypoint 스크립트가 있다고 가정해요.

mkdir -p /volume/.ssh

if [[ "$(ls /volume/.ssh/*_key)" = "" ]]; then
  cp /etc/ssh/*_key /volume/.ssh
else
  cp /volume/.ssh/*_key /etc/ssh
fi

참고:

  • 이 키들은 비밀로 취급되므로, 다른 방법을 쓴다면 이 값들이 공개 저장소에 암호화 없이 커밋되지 않도록 해야 해요.
  • sudo로 실행 중이라면 mkdir, ls, 두 cp 문 앞에 sudo를 추가해야 해요.
  • Machine이 여러 대라면 모두 동일한 키를 공유하고 싶을 수도 있어요.

[선택 사항] 클라이언트 사용자와 별칭 구성

전체 dnsname이 길 수도 있고, 앱이 실행되는 사용자가 랩톱에서 쓰는 사용자와 다를 수도 있어요. 노출하는 포트가 비표준일 수도 있고, Machine이 여러 대라 특정 Machine에 SSH로 접속하고 싶을 수도 있어요.

이 중 하나라도 해당된다면 .ssh 디렉터리에 config라는 파일을 만들거나 업데이트할 수 있어요. 다음은 위 사례 여러 가지를 다루는 예시예요:

Host appname
  Hostname appname.fly.dev
  User rails
  Port 2222
Host *.appname
  HostName %h.internal
  ProxyJump rails@appname.fly.dev:2222
  User rails
  Port 2222

추가로 지문 검사를 피하고 싶다면 각 Host 항목에 다음을 추가할 수 있어요:

StrictHostKeyChecking no

관련 자료

  • flyctl SSH 명령 fly ssh/fly ssh sftp를 사용해 실행 중인 머신에 접근하는 방법의 공식 레퍼런스예요.
  • Fly.io 앱 안의 여러 프로세스 단일 machine/container에서 여러 프로세스 그룹을 실행하는 방법을 설명해요. 메인 앱과 함께 sshd를 추가할 때 관련이 있어요.

더 알아보기 (Learn more)