홈

© 2026 Ki Chang. All rights reserved.

본 블로그의 콘텐츠는 CC BY-NC-SA 4.0 라이선스를 따릅니다.

소개JSON Formatter개인정보처리방침이용약관

© 2026 Ki Chang. All rights reserved.

콘텐츠: CC BY-NC-SA 4.0

소개|JSON Formatter|개인정보처리방침|이용약관

macOS에서 autossh로 SSH 터널 자동화하기: 운영 DB 안전하게 접근하는 방법

정기창·2026년 2월 11일

왜 SSH 터널 자동화가 필요한가

개발을 하다 보면 운영 데이터베이스에 접근해야 할 때가 있습니다. 데이터 확인, 스키마 비교, 마이그레이션 검증 등 다양한 이유가 있죠. 그때마다 터미널을 열고 SSH 터널링 명령어를 입력하는 건 은근히 번거로운 일이었습니다.

특히 저처럼 로컬 Docker MySQL(개발용)과 운영 MySQL(OCI HeatWave)을 동시에 사용하는 환경에서는, 터널을 열 때마다 포트가 겹치지 않는지 신경 써야 했습니다. DBeaver에서 운영 DB를 확인하려면 먼저 터널부터 열어야 하고, 앱을 운영 DB에 연결해서 테스트하려면 또 터널을 확인해야 하고... 이런 반복이 쌓이다 보니, 한번 제대로 자동화해두자는 생각이 들었습니다.

사전 지식

이 글은 다음을 알고 있다고 가정합니다:

  • SSH 기본 사용법 (ssh -i key user@host)
  • SSH 포트 포워딩 개념 (-L 옵션)
  • macOS 터미널 기본 조작

전체 구조

먼저 완성된 구조를 보면 이해가 쉽습니다:

맥북 부팅
  ↓
LaunchAgent가 autossh 실행
  ↓
autossh가 SSH 터널 생성 (Tailscale VPN 경유)
  ↓
127.0.0.1:23306 → (SSH 터널) → 운영 MySQL HeatWave:3306
  ↓
앱, DBeaver 등에서 127.0.0.1:23306으로 접속하면 끝

로컬 Docker MySQL은 별도 포트(13306)에서 돌고 있으므로, 두 DB를 동시에 사용할 수 있습니다:

127.0.0.1:13306 → 로컬 Docker MySQL (개발)
127.0.0.1:23306 → 운영 MySQL HeatWave (SSH 터널)

autossh란

autossh는 SSH 연결을 모니터링하고, 끊어지면 자동으로 재연결하는 도구입니다. 일반 SSH 터널은 네트워크가 불안정하거나 맥이 잠자기에서 깨어날 때 끊어지는데, autossh는 이를 감지하고 다시 연결합니다.

설치는 Homebrew로 간단합니다:

brew install autossh

1단계: SSH config 설정

~/.ssh/config 파일을 생성하거나 수정합니다. 이 파일에 터널 설정을 저장해두면, 매번 긴 명령어를 입력할 필요가 없습니다.

Host heatwave-tunnel
    HostName [Bastion 서버 IP]
    User [SSH 유저명]
    IdentityFile ~/.ssh/[키 파일명]
    LocalForward 23306 [MySQL 내부 IP]:3306
    ServerAliveInterval 30
    ServerAliveCountMax 3
    ExitOnForwardFailure yes

각 설정의 의미를 짚어보겠습니다:

  • LocalForward 23306 [MySQL IP]:3306: 로컬 23306 포트를 원격 MySQL 3306 포트로 연결합니다
  • ServerAliveInterval 30: 30초마다 keepalive 패킷을 전송합니다. 연결이 살아있는지 확인하는 용도입니다
  • ServerAliveCountMax 3: keepalive 응답이 3번 연속 실패하면 연결을 끊습니다. 즉, 최대 90초 후에 끊어진 연결을 감지합니다
  • ExitOnForwardFailure yes: 포트 포워딩이 실패하면 SSH 자체를 종료합니다. 이게 없으면 포트 포워딩 없이 SSH만 연결된 채로 남아있을 수 있습니다

파일 권한도 설정해야 합니다:

chmod 600 ~/.ssh/config

포트 번호 선택 시 고려사항

로컬 포트를 변경하는 것이 통상적입니다. 원격 측 3306은 실제 MySQL이 리스닝하는 포트이므로 바꿀 이유가 없고, 로컬 포트는 내 머신에서 터널 진입점을 어디로 할지 정하는 것일 뿐입니다. 저는 기존에 로컬 Docker MySQL이 13306을 쓰고 있어서, 운영 터널은 23306으로 정했습니다.

2단계: 수동 테스트

LaunchAgent를 설정하기 전에, 먼저 수동으로 터널이 잘 작동하는지 확인합니다:

# 터널 열기
autossh -M 0 -N heatwave-tunnel

# 다른 터미널에서 확인
lsof -i :23306 -sTCP:LISTEN

-M 0은 별도 모니터링 포트 없이 SSH의 ServerAlive 메커니즘에 의존하겠다는 뜻입니다. 별도 포트를 할당하는 것보다 단순하고 안정적입니다.

MySQL 접속도 테스트해봅니다:

mysql -h 127.0.0.1 -P 23306 -u [유저명] -p

여기서 연결이 되면 터널 설정은 정상입니다.

3단계: macOS LaunchAgent로 자동 실행

매번 터미널에서 autossh를 실행하는 건 자동화가 아닙니다. macOS의 LaunchAgent를 활용하면 부팅 시 자동으로 터널이 열립니다.

~/Library/LaunchAgents/com.heatwave.mysql-tunnel.plist 파일을 생성합니다:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.heatwave.mysql-tunnel</string>
    <key>ProgramArguments</key>
    <array>
        <string>/opt/homebrew/bin/autossh</string>
        <string>-M</string>
        <string>0</string>
        <string>-N</string>
        <string>heatwave-tunnel</string>
    </array>
    <key>KeepAlive</key>
    <true/>
    <key>RunAtLoad</key>
    <true/>
    <key>StandardOutPath</key>
    <string>/tmp/heatwave-tunnel.log</string>
    <key>StandardErrorPath</key>
    <string>/tmp/heatwave-tunnel.err</string>
    <key>EnvironmentVariables</key>
    <dict>
        <key>AUTOSSH_GATETIME</key>
        <string>0</string>
    </dict>
</dict>
</plist>

주요 설정을 살펴보면:

  • KeepAlive true: 프로세스가 종료되면 자동으로 다시 실행합니다
  • RunAtLoad true: LaunchAgent가 로드될 때(부팅 시) 자동 실행합니다
  • AUTOSSH_GATETIME 0: 첫 연결이 실패해도 즉시 재시도합니다. 기본값은 30초인데, 네트워크가 아직 준비되지 않은 부팅 직후에는 0이 적합합니다

LaunchAgent 등록:

launchctl load ~/Library/LaunchAgents/com.heatwave.mysql-tunnel.plist

4단계: 확인 및 관리

# 터널 상태 확인
lsof -i :23306 -sTCP:LISTEN

# 에러 로그 확인
cat /tmp/heatwave-tunnel.err

# 터널 중지
launchctl unload ~/Library/LaunchAgents/com.heatwave.mysql-tunnel.plist

# 터널 재시작
launchctl unload ~/Library/LaunchAgents/com.heatwave.mysql-tunnel.plist
launchctl load ~/Library/LaunchAgents/com.heatwave.mysql-tunnel.plist

끊어졌을 때 어떻게 되는가

autossh의 재연결 흐름은 이렇습니다:

SSH 연결 정상
  ↓
30초마다 keepalive 전송 (ServerAliveInterval)
  ↓
3회 연속 응답 없음 (ServerAliveCountMax)
  ↓
SSH 프로세스 종료 감지
  ↓
autossh가 새 SSH 프로세스 시작
  ↓
터널 복구, 23306 포트 다시 사용 가능

여기에 LaunchAgent의 KeepAlive가 이중 안전장치 역할을 합니다. autossh 프로세스 자체가 죽더라도 macOS가 다시 살려줍니다.

실제 사용 모습

이렇게 설정해두니, 이제 터널을 의식할 필요가 없어졌습니다:

  • 앱 개발: MYSQL_PORT=23306으로 환경변수만 바꾸면 운영 DB에 연결됩니다
  • DBeaver: 127.0.0.1:23306으로 일반 MySQL 연결을 만들어두면 끝입니다. SSH 탭 설정이 필요 없습니다
  • Drizzle Studio: 동일하게 로컬 접속처럼 사용합니다

로컬 Docker MySQL(13306)과 운영 HeatWave(23306)를 동시에 열어두고 자유롭게 전환할 수 있습니다.

보안 고려사항

편리한 만큼 보안에 신경 써야 할 부분이 있습니다.

SSH 키 관리

  • SSH 키 파일 권한은 반드시 600으로 설정합니다 (chmod 600 ~/.ssh/your-key)
  • 키에 passphrase를 설정하고, macOS 키체인에 등록하면 매번 입력하지 않아도 됩니다
  • 키 파일은 절대 Git에 커밋하지 않습니다. .gitignore에 *.key, *.pem을 추가하세요

네트워크 보안

  • 저는 Tailscale VPN을 통해 Bastion 서버에 접근하고 있습니다. 공용 인터넷에 Bastion을 직접 노출하는 것보다 안전합니다
  • Tailscale ACL로 어떤 기기가 어떤 서버에 접근할 수 있는지 제어할 수 있습니다
  • VPN 없이 공인 IP로 접근하는 경우, Bastion 서버의 방화벽(Security Group)에서 접근 IP를 제한해야 합니다

DB 접근 권한

  • 운영 DB에 접속하는 계정은 읽기 전용(READ ONLY) 권한만 부여하는 것이 안전합니다
  • 꼭 쓰기가 필요하다면 별도 계정을 만들고, 필요한 테이블에만 권한을 부여하세요
  • MySQL의 GRANT 문으로 세밀하게 제어할 수 있습니다

터널 포트 보호

  • LocalForward는 기본적으로 127.0.0.1(localhost)에만 바인딩됩니다. 같은 네트워크의 다른 기기에서 접근할 수 없습니다
  • 0.0.0.0:23306으로 바인딩하면 외부에서도 접근 가능하므로, 특별한 이유가 없는 한 피하세요

로그 관리

  • /tmp/에 로그를 남기고 있는데, 재부팅 시 자동으로 삭제됩니다
  • 영구 로그가 필요하면 ~/Library/Logs/ 하위에 저장하는 것이 macOS 관례입니다
  • 로그에 민감 정보(비밀번호, 키 내용)가 포함되지 않는지 확인하세요

한계와 대안

이 방식이 모든 상황에 적합한 건 아닙니다:

  • OCI Bastion Service를 사용하는 경우: OCI Bastion은 세션이 최대 3시간으로 제한되어, autossh로 상시 연결을 유지할 수 없습니다. 매번 OCI CLI로 세션을 생성해야 합니다
  • 팀 환경: 개인 개발 환경에는 적합하지만, 팀원 전체가 운영 DB에 접근해야 한다면 VPN + DB Proxy(예: ProxySQL) 같은 중앙화된 방법이 더 적절합니다
  • 보안 정책이 엄격한 조직: 운영 DB에 개발자 로컬에서 직접 접근하는 것 자체가 금지된 곳도 있습니다. 이런 경우 읽기 전용 레플리카를 별도로 운영하는 방식을 고려해야 합니다

마무리

돌이켜 보면 별것 아닌 설정인데, 이걸 해두기 전에는 매번 터널 여는 게 은근히 스트레스였습니다. 특히 DBeaver에서 운영 DB를 확인하려고 할 때마다 "아, 터널 먼저 열어야지"하는 순간이 반복되니까요.

한번 설정해두면 맥북을 켤 때마다 자동으로 터널이 열리고, 끊어져도 알아서 재연결됩니다. 개발 흐름이 끊기지 않는다는 점에서, 투자 대비 효과가 꽤 좋은 자동화였다는 생각이 듭니다.

SSHautosshmacOSMySQLDevOps보안

관련 글

macOS에서 OCI HeatWave MySQL 접속하기: Bastion SSH 터널 설정 가이드

Oracle Cloud HeatWave MySQL에 로컬에서 접속하기 위한 OCI CLI 설정과 Bastion SSH 터널 구성 과정을 정리했습니다. API 키 등록부터 터널 스크립트 작성, 트러블슈팅까지 전체 흐름을 다룹니다.

관련도 94%

OCI MySQL HeatWave, Bastion 없이 Tailscale로 접속하기

Oracle Cloud MySQL HeatWave는 프라이빗 서브넷에만 배치되어 외부 접속이 번거롭습니다. Bastion Service 대신 Tailscale을 활용해 간편하게 접속하는 방법을 정리했습니다.

관련도 92%

OCI HeatWave MySQL + NestJS 연동: Bastion을 통한 보안 연결 가이드

Oracle Cloud HeatWave MySQL을 로컬 개발 환경에서 안전하게 연결하고, NestJS + TypeORM으로 연동하는 전체 과정. Bastion SSH 터널 설정부터 Security List 트러블슈팅까지 실제 경험을 바탕으로 정리했습니다.

관련도 90%