Windows GUI → Ubuntu CLI SSH Remote Backend Setup.
Windows GUI 앱은 로컬에서 사용하고, 실제 코드 실행·파일 읽기·명령 실행은 Ubuntu CLI 머신에서 수행한다.
구조 :
```text
Windows
├─ Codex App / Claude Desktop GUI
├─ OpenSSH Client
└─ ~/.ssh/config
↓ SSH
Ubuntu Server 24.04 LTS
├─ OpenSSH Server
├─ Node.js 22, Codex CLI, Claude Code
├─ ~/code
└─ Samba share: \\<UBUNTU_IP>\code
```
예시값 :
| 항목 | 예시값 | 설명 |
| ------------------ | ----------------: | ------------------ |
| `<UBUNTU_USER>` | `user_name` | Ubuntu 로그인 사용자명 |
| `<UBUNTU_IP>` | `192.168.50.30` | Ubuntu 고정 IP |
| `<WINDOWS_IP>` | `192.168.50.10` | Windows 머신 IP |
| `<WINDOWS_SUBNET>` | `192.168.50.0/24` | 개발용 내부망 대역 |
| `<GATEWAY_IP>` | `192.168.50.1` | 공유기 또는 게이트웨이 |
| `<DNS_1>` | `192.168.50.1` | DNS 서버 |
| `<NIC_NAME>` | `enp6s18` | Ubuntu 네트워크 인터페이스명 |
| | | |
---
# 1. Ubuntu Server 설치
Ubuntu는 GUI 없이 CLI 환경으로 설치한다.
권장:
```text
Ubuntu Server 24.04 LTS
```
설치 중 선택값:
```text
Language: English
Layout / Variant: Korean
Choose the base for the installation: Ubuntu Server
SSH configuration: Install OpenSSH server
```
설치가 끝나면 Ubuntu에 로그인한다.
---
# 2. Ubuntu 기본 패키지 설치 및 재부팅
```bash
sudo apt update && sudo apt -y full-upgrade
sudo apt -y install build-essential curl ca-certificates git ripgrep fd-find unzip tmux jq bubblewrap
# Ubuntu 패키지명은 fd-find이고 실행 파일명은 fdfind이므로 fd alias를 만든다.
sudo ln -sf /usr/bin/fdfind /usr/local/bin/fd
# 시간대 설정
sudo timedatectl set-timezone Asia/Seoul
timedatectl
sudo reboot
```
---
# 3. Ubuntu Network 설정
## 3-1. 네트워크 인터페이스명 확인
```bash
ip -br link
ip addr
```
예를 들어 인터페이스명이 `enp6s18`이면 이후 `<NIC_NAME>` 자리에 `enp6s18`을 넣는다. 설치 환경마다 이름이 다를 수 있으므로 하드코딩하지 않는다.
## 3-2. Netplan 파일 확인 및 백업
```bash
ls /etc/netplan
```
예시로 `50-cloud-init.yaml`이 나온 경우:
```bash
sudo nano /etc/netplan/50-cloud-init.yaml
```
## 3-3. Static IP 설정 예시
아래 예시는 Ubuntu IP를 `192.168.50.30`으로 고정하는 설정이다.
```yaml
network:
version: 2
renderer: networkd
ethernets:
enp6s18:
dhcp4: false
addresses:
- 192.168.50.30/24
routes:
- to: default
via: 192.168.50.1
nameservers:
addresses:
- 192.168.50.1
- 1.1.1.1
# ctrl x, y, enter
```
실제 적용 시 아래 값을 바꾼다.
```text
enp6s18 → <NIC_NAME>
192.168.50.30 → <UBUNTU_IP>
192.168.50.1 → <GATEWAY_IP>, <DNS_1>
```
## 3-4. Netplan 적용
로컬 콘솔에서 작업 중이면 아래 순서로 적용한다.
```bash
sudo netplan try
sudo netplan apply
sudo netplan status -a
ip addr
ip route
```
`netplan try`에서 연결 문제가 생기면 자동 롤백할 수 있다. 원격 SSH 세션에서 네트워크 설정을 바꾸는 경우에는 특히 `netplan try`를 먼저 사용한다.
---
# 4. Windows OpenSSH Client / Git / Terminal 설치
관리자 권한 PowerShell에서 실행.
1) Winget 설치
```powershell
Install-PackageProvider -Name NuGet -Force | Out-Null
Install-Module -Name Microsoft.WinGet.Client -Force -Repository PSGallery | Out-Null
Repair-WinGetPackageManager -Force -Latest
```
2) 사전 도구 설치
```powershell
winget upgrade --id Microsoft.AppInstaller
winget install --id Git.Git -e
Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0
winget install --id Microsoft.WindowsTerminal -e
```
3) OpenSSH 확인
```powershell
ssh -V
```
---
# 5. Windows SSH key 생성
일반 PowerShell에서 실행한다.
```powershell
New-Item -ItemType Directory -Force $HOME\.ssh | Out-Null
ssh-keygen -t ed25519 -C "win10"
```
권장:
```text
저장 경로: 기본값 사용, 보통 C:\Users\<WindowsUser>\.ssh\id_ed25519
Passphrase: 선택 사항
```
SSH key에 passphrase를 설정했다면 GUI 앱에서 SSH 연결 시 `ssh-agent` 등록이 필요할 수 있다. 관련 명령은 Troubleshooting 섹션에 있다.
---
# 6. Windows `~/.ssh/config` 작성
일반 PowerShell에서 실행한다.
```powershell
notepad $HOME\.ssh\config
```
아래 내용을 저장한다. 파일 이름은 `config.txt`가 아니라 확장자 없는 `config`여야 한다.
```sshconfig
Host ubuntu-code
HostName 192.168.50.30
User user_name
IdentityFile ~/.ssh/id_ed25519
IdentitiesOnly yes
ServerAliveInterval 60
ServerAliveCountMax 3
```
실제 적용 시 아래 값을 바꾼다.
```text
HostName 192.168.50.30 → HostName <UBUNTU_IP>
User user_name → User <UBUNTU_USER>
```
`IdentitiesOnly yes`는 OpenSSH가 엉뚱한 key를 여러 개 시도하다가 실패하는 일을 줄인다.
---
# 7. Windows 공개키를 Ubuntu에 전송
일반 PowerShell에서 실행한다.
```powershell
$UbuntuUser = "user_name"
$UbuntuIP = "192.168.50.30"
Get-Content $HOME\.ssh\id_ed25519.pub | ssh "$($UbuntuUser)@$($UbuntuIP)" "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
```
처음 접속할 때 host key 확인 메시지가 나오면 fingerprint를 확인한 뒤 `yes`를 입력한다.
> 이 명령을 여러 번 실행하면 `authorized_keys`에 같은 공개키가 중복으로 들어갈 수 있다. 일반적으로 큰 문제는 아니지만, 깔끔하게 관리하려면 Ubuntu에서 `nano ~/.ssh/authorized_keys`로 중복 줄을 삭제한다.
수동 등록이 필요한 경우 Ubuntu에서 아래처럼 직접 붙여넣을 수 있다.
```bash
mkdir -p ~/.ssh && chmod 700 ~/.ssh
nano ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
```
공개키 등록은 Windows 전송 방식과 Ubuntu 수동 붙여넣기 방식 중 하나만 수행한다.
---
# 8. Windows에서 SSH 접속 확인
일반 PowerShell에서 실행한다.
```powershell
ssh ubuntu-code
```
성공 기준:
```text
Windows PowerShell에서 ssh ubuntu-code 입력
→ Ubuntu shell prompt가 열림
```
아직 SSH hardening을 적용하지 않는다. 먼저 key 기반 로그인이 정상인지 확인한 뒤에 비밀번호 로그인을 끈다.
---
# 9. Ubuntu SSH hardening
이 단계는 반드시 8번에서 `ssh ubuntu-code` 접속이 성공한 뒤에만 수행한다.
Ubuntu에서 실행한다.
```bash
sudo nano /etc/ssh/sshd_config.d/10-hardening.conf
```
아래 내용을 입력한다.
```text
PubkeyAuthentication yes
PasswordAuthentication no
PermitRootLogin no
KbdInteractiveAuthentication no
```
문법 검사 후 SSH를 재시작한다.
```bash
sudo sshd -t
sudo systemctl restart ssh.service
```
Windows에서 새 PowerShell 창을 열고 다시 확인한다.
```powershell
ssh ubuntu-code
```
> 기존 SSH 세션 하나는 열어둔 상태에서 새 창으로 재접속을 확인한다. 새 접속이 실패하면 기존 세션에서 hardening 파일을 수정하거나 삭제해 복구할 수 있다.
---
# 10. NodeSource 시스템 Node.js 22 설치
nvm은 사용하지 않는다. 원격 GUI 백엔드에서는 비대화식 SSH shell에서 `PATH` 문제가 생길 수 있으므로 시스템 경로에 잡히는 NodeSource APT 설치를 사용한다.
Ubuntu에서 실행한다.
```bash
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt-get install -y nodejs
which node
node -v
npm -v
```
정상 예시:
```text
/usr/bin/node
v22.x.x
```
이 문서는 Node 22 계열 고정 설치를 기준으로 한다. `최신 LTS`라는 표현 대신 `Node 22 LTS 고정`으로 이해한다.
---
# 11. Codex CLI 설치 및 device-auth 로그인
Ubuntu에서 실행한다.
```bash
sudo npm install -g @openai/codex
which codex
codex --version
```
로그인은 사용자 홈에 인증 정보를 저장해야 하므로 `sudo` 없이 실행한다.
```bash
codex login --device-auth
codex login status
```
`--device-auth` 실행 중 device code authentication 활성화가 필요하다는 메시지가 나오면:
```text
1. Windows 브라우저에서 ChatGPT 보안 설정으로 이동
2. Enable device code authentication for Codex 활성화
3. Ubuntu에서 codex login --device-auth 재실행
4. 출력된 URL과 one-time code를 Windows 브라우저에서 입력
5. codex login status로 성공 확인
```
Windows에서 원격 shell의 PATH 확인:
```powershell
ssh ubuntu-code 'command -v codex && codex --version'
```
업데이트:
```bash
sudo npm install -g @openai/codex@latest
```
> `sudo npm install -g`로 설치하면 전역 npm 패키지가 root-owned가 된다. 업데이트도 동일하게 `sudo npm install -g @openai/codex@latest`로 수행한다. 단, `codex login`과 `codex login status`는 절대 `sudo`로 실행하지 않는다.
---
# 12. Claude Code 설치 또는 Desktop 자동 설치 사용
Claude Desktop SSH 세션만 사용할 목적이면, Desktop이 최초 SSH 연결 시 원격 머신에 Claude Code를 자동 설치할 수 있다. 그래도 Ubuntu CLI에서도 직접 `claude`를 쓰거나 설치 상태를 명확히 관리하려면 아래 apt 설치 방식을 사용한다.
## 12-A. 권장: apt repository 방식
Ubuntu에서 실행한다.
```bash
sudo install -d -m 0755 /etc/apt/keyrings
sudo curl -fsSL https://downloads.claude.ai/keys/claude-code.asc \
-o /etc/apt/keyrings/claude-code.asc
# GPG key fingerprint 확인
# 출력에 아래 fingerprint가 포함되어야 한다.
# 31DD DE24 DDFA B679 F42D 7BD2 BAA9 29FF 1A7E CACE
gpg --show-keys /etc/apt/keyrings/claude-code.asc
echo "deb [signed-by=/etc/apt/keyrings/claude-code.asc] https://downloads.claude.ai/claude-code/apt/stable stable main" \
| sudo tee /etc/apt/sources.list.d/claude-code.list
sudo apt update
sudo apt install claude-code
claude --version
claude doctor
```
로그인은 사용자 계정으로 실행한다.
```bash
claude
# 최초 실행 시 로그인 프롬프트를 따른다.
```
업데이트:
```bash
sudo apt update && sudo apt upgrade claude-code
```
## 12-B. 비권장: npm global 설치
Claude Code는 npm global 설치도 지원하지만, 이 문서에서는 사용하지 않는다.
```bash
npm install -g @anthropic-ai/claude-code
```
특히 아래 방식은 권한 문제와 보안 리스크 때문에 피한다.
```bash
sudo npm install -g @anthropic-ai/claude-code
```
---
# 13. Ubuntu 작업 디렉터리 생성
```bash
mkdir -p ~/code
sudo chown -R "$USER:$USER" ~/code
```
권장 작업 위치:
```text
/home/<UBUNTU_USER>/code
```
Codex App과 Claude Desktop에서 원격 프로젝트 폴더로 이 디렉터리를 선택한다.
---
# 14. Samba 공유 설정
Windows에서 Ubuntu의 `~/code`를 네트워크 드라이브처럼 접근하기 위한 설정이다.
## 14-1. Samba 설치
Ubuntu에서 실행한다.
```bash
sudo apt -y install samba
```
## 14-2. Samba 사용자 등록
Linux 계정과 같은 이름으로 Samba 사용자를 등록한다.
```bash
sudo smbpasswd -a user_name
sudo smbpasswd -e user_name
```
실제 적용 시 `user_name`을 `<UBUNTU_USER>`로 바꾼다. Samba 비밀번호는 Linux 로그인 비밀번호와 별개다.
## 14-3. Samba share 추가
Ubuntu에서 실행한다.
```bash
sudo nano /etc/samba/smb.conf
```
파일 맨 아래에 추가한다.
```ini
[code]
path = /home/user_name/code
browseable = yes
read only = no
create mask = 0644
directory mask = 0755
valid users = user_name
force user = user_name
```
실제 적용 시 `user_name`을 `<UBUNTU_USER>`로 바꾼다.
문법 검사 및 재시작:
```bash
sudo testparm
sudo systemctl restart smbd
sudo systemctl enable smbd
sudo systemctl status smbd --no-pager
```
`nmbd`는 NetBIOS browsing이 필요한 경우에만 사용한다. Windows에서 `\\<UBUNTU_IP>\code`로 직접 접근한다면 보통 `445/tcp`와 `smbd`만으로 충분하다.
## 14-4. Windows에서 Samba 접근
PowerShell 또는 파일 탐색기 주소창에서:
```powershell
\\192.168.50.30\code
```
네트워크 드라이브로 매핑하려면:
```powershell
$UbuntuIP = "192.168.50.30"
$UbuntuUser = "user_name"
net use Z: "\\$UbuntuIP\code" /user:$UbuntuUser
```
---
# 15. UFW 방화벽 설정
공유기 포트포워딩을 하지 않더라도 UFW는 켜는 편이 안전하다. SSH와 SMB는 Windows 호스트 또는 개발용 내부망에서만 허용한다.
## 15-1. Windows 1대만 허용하는 권장 설정
Ubuntu에서 실행한다.
```bash
sudo ufw allow from 192.168.50.10 to any port 22 proto tcp
sudo ufw allow from 192.168.50.10 to any port 445 proto tcp
sudo ufw enable
sudo ufw status verbose
```
실제 적용 시 `192.168.50.10`을 `<WINDOWS_IP>`로 바꾼다.
## 15-2. 개발 서브넷 전체를 허용하는 대안
Windows IP가 자주 바뀌면 내부 개발 서브넷을 허용한다.
```bash
sudo ufw allow from 192.168.50.0/24 to any port 22 proto tcp
sudo ufw allow from 192.168.50.0/24 to any port 445 proto tcp
sudo ufw enable
sudo ufw status verbose
```
NetBIOS가 필요할 때만 139/tcp를 추가한다.
```bash
sudo ufw allow from 192.168.50.0/24 to any port 139 proto tcp
```
> 절대 `0.0.0.0/0`에 SMB를 열지 않는다. 외부 접속이 필요하면 공유기 포트포워딩 대신 VPN 또는 Tailscale 같은 mesh network를 사용한다.
---
# 16. Codex App에서 SSH host 추가
Windows에서 먼저 확인한다.
```powershell
ssh ubuntu-code
ssh ubuntu-code 'command -v codex && codex --version'
```
그다음 Codex App에서:
```text
Settings → Connections → Add SSH host → ubuntu-code 선택
```
정상 조건:
```text
1. Windows ~/.ssh/config에 Host ubuntu-code가 있음
2. Windows에서 ssh ubuntu-code가 성공함
3. Ubuntu에서 codex가 login shell PATH에 있음
4. Ubuntu에서 codex login status가 성공 상태임
```
이 문서에서는 `~/.codex/config.toml`에 `remote_connections = true`를 기본으로 넣지 않는다. 현재 기본 흐름은 앱의 Settings → Connections에서 SSH host를 추가하는 방식이다.
---
# 17. Claude Desktop에서 SSH connection 추가
Windows에서 먼저 확인한다.
```powershell
ssh ubuntu-code
ssh ubuntu-code 'command -v claude && claude --version'
```
Claude Code를 Ubuntu에 미리 설치하지 않았다면 두 번째 명령은 실패할 수 있다. Claude Desktop은 최초 SSH 연결 시 원격 머신에 Claude Code를 자동 설치할 수 있다.
Claude Desktop에서:
```text
Code tab → Environment dropdown → + Add SSH connection
```
권장 입력값:
```text
Name: ubuntu-code
SSH Host: ubuntu-code
SSH Port: 22
Identity File: ~/.ssh/id_ed25519
Start Directory: ~/code
```
또는 SSH Host에 직접 입력할 수 있다.
```text
[email protected]
```
`~/.ssh/config`를 이미 정확히 작성했다면 `SSH Host`에는 `ubuntu-code`만 쓰는 편이 관리하기 쉽다.
# ETC. Python 패키지 설치
```
sudo apt install python3 python3-venv python3-pip pipx
```
---
# 18. Troubleshooting
## 18-1. `Permission denied (publickey,password)`
가능한 원인:
```text
- SSH key passphrase가 있는데 ssh-agent에 등록되지 않음
- Windows ~/.ssh/config의 HostName/User/IdentityFile 불일치
- Ubuntu ~/.ssh 또는 authorized_keys 권한 문제
- Ubuntu IP 불일치
- 서버 측 PasswordAuthentication/PubkeyAuthentication 설정 문제
- 최초 host key 확인을 GUI 앱이 처리하지 못함
```
Windows에서 디버깅:
```powershell
ssh -vvv ubuntu-code
ssh-add -l
```
Ubuntu에서 디버깅:
```bash
ls -ld ~/.ssh
ls -l ~/.ssh/authorized_keys
sudo journalctl -fu ssh.service
```
권한 복구:
```bash
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
```
## 18-2. SSH key passphrase를 쓴 경우 ssh-agent 등록
관리자 권한 PowerShell:
```powershell
Set-Service ssh-agent -StartupType Automatic
Start-Service ssh-agent
```
일반 PowerShell:
```powershell
ssh-add $env:USERPROFILE\.ssh\id_ed25519
ssh-add -l
```
## 18-3. Host key changed 또는 IP 재사용 문제
Ubuntu IP를 바꿨거나 같은 IP에 다른 머신을 붙였다면 Windows에서 기존 host key를 삭제한다.
```powershell
ssh-keygen -R 192.168.50.30
ssh ubuntu-code
```
## 18-4. Codex App에서 remote 연결 실패
Windows에서 확인:
```powershell
ssh ubuntu-code
ssh ubuntu-code 'echo $PATH; command -v codex; codex --version; codex login status'
```
Ubuntu에서 확인:
```bash
which codex
codex --version
codex login status
```
문제 원인별 조치:
```text
codex: command not found
→ NodeSource 시스템 Node로 설치했는지 확인
→ sudo npm install -g @openai/codex 재실행
codex login status 실패
→ codex login --device-auth 재실행
Windows ssh ubuntu-code 실패
→ Codex App 문제가 아니라 SSH 설정 문제부터 해결
```
## 18-5. Claude Desktop SSH 연결 실패
Windows에서 확인:
```powershell
ssh ubuntu-code
```
Ubuntu에서 Claude Code 설치 상태 확인:
```bash
command -v claude
claude --version
claude doctor
```
Claude Desktop의 SSH Host에는 `ubuntu-code` 또는 `
[email protected]`을 입력한다. Identity File을 비워두면 기본 key 또는 SSH config를 사용한다.
## 18-6. Samba 접근 실패
Ubuntu에서 확인:
```bash
sudo testparm
sudo systemctl status smbd --no-pager
sudo ufw status verbose
pdbedit -L
```
Windows에서 확인:
```powershell
net use
net use Z: /delete
net use Z: \\192.168.50.30\code /user:user_name
```
Samba 사용자 비밀번호를 다시 설정하려면:
```bash
sudo smbpasswd -a user_name
sudo smbpasswd -e user_name
```
## 18-7. Netplan 적용 후 접속 불가
로컬 콘솔에서 백업 파일을 확인한다.
```bash
ls -l /etc/netplan
sudo nano /etc/netplan/50-cloud-init.yaml
sudo netplan try
sudo netplan apply
```
IP, gateway, DNS, interface name이 실제 환경과 일치하는지 확인한다.
```bash
ip -br link
ip addr
ip route
resolvectl status
```
---
# 19. 최종 점검 체크리스트
Windows PowerShell:
```powershell
ssh ubuntu-code
ssh ubuntu-code 'hostname; pwd; whoami'
ssh ubuntu-code 'command -v node && node -v && npm -v'
ssh ubuntu-code 'command -v codex && codex --version && codex login status'
ssh ubuntu-code 'command -v claude && claude --version || true'
```
Ubuntu:
```bash
sudo sshd -t
sudo systemctl status ssh.service --no-pager
sudo systemctl status smbd --no-pager
sudo ufw status verbose
ls -ld ~/code
```
Windows 파일 탐색기:
```text
\\192.168.50.30\code
```
Codex App:
```text
Settings → Connections → ubuntu-code → ~/code 선택
```
Claude Desktop:
```text
Code tab → Environment dropdown → ubuntu-code → ~/code 시작
```
---
# 20. 유지보수 명령
Ubuntu 시스템 업데이트:
```bash
sudo apt update && sudo apt -y full-upgrade
sudo reboot
```
Codex CLI 업데이트:
```bash
sudo npm install -g @openai/codex@latest
codex --version
```
Claude Code apt 업데이트:
```bash
sudo apt update && sudo apt upgrade claude-code
claude --version
```
Samba 재시작:
```bash
sudo testparm
sudo systemctl restart smbd
```
SSH 설정 변경 후 검사:
```bash
sudo sshd -t
sudo systemctl restart ssh.service
```
---
# 21. 참고 공식 문서
- Codex remote connections: https://developers.openai.com/codex/remote-connections
- Codex authentication / device code: https://developers.openai.com/codex/auth
- Codex CLI reference: https://developers.openai.com/codex/cli/reference
- Claude Code Desktop SSH sessions: https://code.claude.com/docs/en/desktop
- Claude Code setup / apt repository: https://code.claude.com/docs/en/setup
- Ubuntu Netplan network configuration: https://ubuntu.com/server/docs/explanation/networking/configuring-networks/
- NodeSource Debian/Ubuntu repository: https://deb.nodesource.com/