sudo를 사용할 수 있는 기존 계정과 끊지 않고 유지할 현재 SSH 세션
SERVER FOUNDATION
Ubuntu 24.04 LTS 서버 초기 설정
새 서버의 환경과 접속 경로를 먼저 확인한 뒤 업데이트, 관리자 계정, 시간 동기화, SSH 공개키, UFW를 잠금 위험 없이 순서대로 설정합니다.BEFORE YOU START
시작 전에 준비하세요
클라우드 콘솔·시리얼 콘솔 등 SSH 실패 때 복구할 대체 접속 수단
등록할 관리자 공개키와 실제 SSH 포트 번호
업데이트와 재부팅을 수행해도 되는 점검 시간 및 중요 데이터 백업
권장 대상 새 Ubuntu 서버를 인수했거나 운영 서비스 설치 전 공통 보안 기준을 잡으려는 개발자·운영자
FOLLOW THE RECIPE
8단계 구성·점검 레시피
운영체제와 접속 환경 확인
이 레시피는 Ubuntu 24.04 LTS를 기준으로 합니다. 배포판, 아키텍처, 가상화 방식, 현재 권한을 기록해 이후 명령의 전제를 고정합니다.
cat /etc/os-releaseuname -a
dpkg --print-architecturesystemd-detect-virtnone이면 베어메탈일 수 있습니다. 비정상 결과로 보지 않습니다.whoami
id
sudo -v- VERSION_ID가 24.04인지 확인했습니다.
- amd64 또는 arm64인지 확인했습니다.
- 현재 세션 외에 복구 가능한 콘솔 경로를 확보했습니다.
다른 Ubuntu 버전이나 파생 배포판이면 패키지·기본 설정이 다를 수 있으므로 그대로 진행하지 않습니다.
현재 서비스와 네트워크 상태를 변경 전 기준값으로 남깁니다.
디스크·네트워크·서비스 사전 점검
업데이트 공간, 기본 경로, 리슨 포트, 방화벽과 실패한 서비스를 먼저 확인합니다. 출력은 롤백 판단을 위한 기준 자료입니다.
df -hT /ip -brief address
ip routesudo ss -lntupsudo ufw status verbose
systemctl --failed --no-pagersudo sshd -T | grep -E '^(port|permitrootlogin|pubkeyauthentication|passwordauthentication|kbdinteractiveauthentication) '- 업데이트를 받을 디스크 여유가 충분합니다.
- 현재 SSH 포트와 클라우드 인바운드 규칙을 기록했습니다.
- 기존 실패 서비스가 이번 작업 전부터 있었는지 구분했습니다.
디스크 부족, 기본 경로 없음, 패키지 관리자 잠금 또는 기존 장애가 있으면 초기 설정보다 해당 원인을 먼저 해결합니다.
APT 변경 내용을 검토하면서 보안 업데이트와 필수 패키지를 설치합니다.
업데이트와 운영 필수 패키지 설치
패키지 목록을 갱신하고 예정된 변경을 확인한 후 업그레이드합니다. Ubuntu의 단계적 업데이트로 보류된 일반 패키지는 억지로 설치하지 않습니다.
sudo apt updateapt list --upgradable삭제·대규모 교체가 예상되면 진행 전에 원인을 확인합니다.sudo apt upgrade표시되는 패키지 변경을 읽고 승인합니다. 원격 작업 중에는 무인 -y 옵션을 사용하지 않습니다.sudo apt install openssh-server unattended-upgrades update-notifier-commonls -l /var/run/reboot-required*파일이 없다는 메시지는 재부팅이 필수가 아니라는 뜻입니다.- apt update와 apt upgrade가 오류 없이 끝났습니다.
- 보류 패키지는 단계적 업데이트인지 확인하고 기본 정책을 유지했습니다.
- 커널·라이브러리 업데이트 후 재부팅 필요 여부를 기록했습니다.
dpkg 중단이나 의존성 오류가 나오면 반복 설치하지 말고 APT·dpkg 복구 가이드로 전환합니다.
관리자 계정, 시간대, SSH 보안 스니펫을 준비합니다.
관리자 계정·시간대·SSH 공개키 구성
새 관리자 계정을 만들고 공개키 접속을 별도 세션에서 검증한 뒤에만 암호 로그인과 root 로그인을 제한합니다. 검증 전에는 기존 SSH 세션을 닫지 않습니다.
timedatectl status
timedatectl list-timezones | grep <REGION>sudo timedatectl set-timezone <TIMEZONE>예: Asia/Seoul. 서비스 표준이 UTC이면 Etc/UTC를 사용합니다.sudo adduser <ADMIN_USER>
sudo usermod -aG sudo <ADMIN_USER>기존 계정과 겹치지 않는 이름을 사용하고 대화형 비밀번호 입력을 기록하거나 공유하지 않습니다.ssh-copy-id -p <SSH_PORT> <ADMIN_USER>@<SERVER_IP>이 명령은 서버가 아니라 공개키를 가진 로컬 PC에서 실행합니다.sudo cp --archive --no-clobber /etc/ssh/sshd_config.d/10-haruspace-hardening.conf /etc/ssh/sshd_config.d/10-haruspace-hardening.conf.<BACKUP_SUFFIX>기존 파일이 있을 때만 실행하고 <BACKUP_SUFFIX>는 아직 존재하지 않는 값으로 바꿉니다.sudoedit /etc/ssh/sshd_config.d/10-haruspace-hardening.conf아래 설정은 새 관리자 공개키 로그인이 성공한 뒤에만 저장합니다.PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no
DebianBanner noSSH 포트는 이 예제에서 바꾸지 않습니다. 암호·root 로그인을 끄기 전에 새 계정으로 두 번째 세션 접속과 sudo -v를 반드시 성공시킵니다.- 새 계정이 sudo 그룹에 포함됐습니다.
- 새 계정 공개키 로그인이 별도 세션에서 성공했습니다.
- 현재 세션과 복구 콘솔을 유지하고 있습니다.
- 기존 SSH 스니펫이 있었다면 별도 이름으로 백업했습니다.
ssh-copy-id가 실패하거나 새 세션에서 sudo가 안 되면 보안 스니펫을 적용하지 말고 키 권한과 계정 그룹부터 바로잡습니다.
설정 문법을 검사한 뒤 SSH와 자동 보안 업데이트를 활성화합니다.
SSH 설정 반영과 자동 업데이트 활성화
문법 검사가 성공해야 SSH를 재시작합니다. 재시작 직후에도 기존 세션을 유지한 채 새 세션으로 다시 접속합니다.
sudo sshd -t아무 출력 없이 종료 코드 0이면 문법 검사를 통과한 것입니다.sudo systemctl enable ssh.service
sudo systemctl restart ssh.service현재 원격 세션을 닫지 않은 상태에서 실행합니다.sudo dpkg-reconfigure unattended-upgrades대화형 화면에서 조직의 재부팅·점검 정책에 맞게 선택합니다.systemctl is-enabled ssh.service
systemctl is-active ssh.service
systemctl status unattended-upgrades.service --no-pager- sshd -t가 성공했습니다.
- ssh.service가 enabled·active입니다.
- 자동 재부팅 여부를 조직 정책과 맞췄습니다.
SSH 재시작이 실패해도 현재 세션은 유지될 수 있습니다. 세션을 닫지 말고 journalctl과 백업 스니펫으로 복구합니다.
새 접속, sudo, 시간 동기화, 부팅 후 상태까지 실제로 검증합니다.
새 SSH 세션과 재부팅 후 검증
구성 파일 존재가 아니라 실제 접속과 권한으로 성공을 판정합니다. 커널 업데이트가 있으면 승인된 점검 시간에 재부팅하고 다시 확인합니다.
ssh -p <SSH_PORT> <ADMIN_USER>@<SERVER_IP>sudo -v
sudo sshd -T | grep -E '^(permitrootlogin|pubkeyauthentication|passwordauthentication|kbdinteractiveauthentication) 'timedatectl status
systemctl --failed --no-pagersudo systemctl reboot콘솔 복구 경로, 새 SSH 접속, 서비스 영향 확인이 끝난 경우에만 실행합니다.ssh -p <SSH_PORT> <ADMIN_USER>@<SERVER_IP>
systemctl is-system-running
who -b첫 줄은 로컬 PC에서, 나머지는 다시 접속한 서버에서 실행합니다.- 암호 없이 공개키로 새 관리자 접속에 성공했습니다.
- 새 계정에서 sudo가 동작합니다.
- 재부팅 뒤 SSH와 필수 서비스가 정상입니다.
is-system-running이 degraded이면 systemctl --failed로 원인을 확인합니다. 새 SSH 접속 실패 시 기존 세션 또는 콘솔에서 즉시 롤백합니다.
SSH 경로를 보존한 상태에서 UFW와 계정 보안 최종 점검을 수행합니다.
UFW와 계정 보안 최종 점검
방화벽 활성화는 원격 잠금 가능성이 있습니다. 실제 SSH 포트, 클라우드 보안그룹, 콘솔 접속을 확인하고 dry-run을 검토한 뒤 적용합니다.
sudo ufw status verbosesudo ufw status numbered같은 포트의 더 넓은 allow 규칙이 limit 규칙보다 앞에 있으면 먼저 일치해 rate limit 효과가 없을 수 있습니다. 기존 규칙은 소유자·영향을 확인해 별도 승인 없이 삭제하거나 재정렬하지 않습니다.sudo ufw --dry-run limit <SSH_PORT>/tcp<SSH_PORT>를 sshd -T에서 확인한 실제 포트로 바꾸고 출력만 먼저 검토합니다.sudo ufw limit <SSH_PORT>/tcp실제 SSH 포트와 정확히 일치하는지 다시 확인하고 기존 세션과 콘솔 복구 경로를 유지합니다.sudo ufw enableSSH 허용 규칙, 클라우드 방화벽, 콘솔 복구 경로를 모두 확인한 경우에만 승인합니다.sudo ufw status numbered
who
last -n 10- root 직접 로그인과 SSH 암호 로그인이 비활성화됐습니다.
- UFW에는 실제 SSH 포트와 필요한 서비스 포트만 허용됐습니다.
- 기존의 더 넓은 SSH allow 규칙과 번호 순서를 확인했으며 limit 적용 효과를 과장하지 않습니다.
- 클라우드 보안그룹과 호스트 UFW 규칙이 서로 모순되지 않습니다.
- unattended-upgrades 상태와 업데이트 로그를 정기 점검합니다.
- 불필요한 sudo 계정과 장기간 미사용 공개키를 제거하는 절차가 있습니다.
UFW 활성화 직후 두 번째 SSH 연결을 열어 성공을 확인합니다. 실패하면 현재 세션을 닫지 말고 규칙을 수정합니다.
문제가 생기면 변경한 SSH 스니펫과 UFW만 되돌리고 업데이트 자체는 유지합니다.
SSH·방화벽 변경 롤백
전체 서버를 초기화하지 않고 이번 레시피에서 추가한 SSH 스니펫과 방화벽 상태만 되돌립니다. 기존 세션이나 콘솔에서 실행합니다.
sudo mv /etc/ssh/sshd_config.d/10-haruspace-hardening.conf /etc/ssh/sshd_config.d/10-haruspace-hardening.conf.disableddisabled 대상 파일이 아직 없고, 이번 레시피에서 만든 스니펫이 맞을 때만 실행합니다.sudo cp --archive /etc/ssh/sshd_config.d/10-haruspace-hardening.conf.<BACKUP_SUFFIX> /etc/ssh/sshd_config.d/10-haruspace-hardening.conf실제로 만든 백업 파일을 확인한 뒤 <BACKUP_SUFFIX>를 바꿉니다.sudo sshd -t
sudo systemctl restart ssh.servicesudo ufw delete limit <SSH_PORT>/tcp사전 기록에 같은 규칙이 없었고 이번 레시피가 추가한 규칙임을 확인한 경우에만 실제 SSH 포트로 바꿔 실행합니다. 기존 규칙은 삭제하지 않습니다.sudo ufw disable작업 전 ufw status가 inactive였고 이번 레시피에서 enable한 경우에만 콘솔에서 실행합니다. 작업 전부터 active였다면 UFW를 끄지 않습니다.sudo sshd -T | grep -E '^(permitrootlogin|pubkeyauthentication|passwordauthentication) '
sudo ufw status verbose- 현재 SSH 세션 또는 콘솔을 유지한 채 롤백했습니다.
- 복원 후 sshd -t와 새 SSH 접속을 다시 검증했습니다.
- 이번 레시피가 추가한 SSH 규칙만 제거하고 작업 전 UFW 활성·비활성 상태를 보존했습니다.
업데이트된 패키지는 임의로 다운그레이드하지 않습니다. 패키지 업데이트로 인한 회귀는 영향 패키지와 Ubuntu 보안 공지를 확인해 별도 변경으로 처리합니다.
복구 뒤 원인과 변경 파일, 접속 검증 결과를 운영 기록에 남깁니다.
SECURITY CHECK
운영 전 마지막 보안 점검
- 공개키 로그인과 새 관리자 sudo를 확인하기 전에는 암호·root 로그인을 끄지 않습니다.
- SSH 설정 변경 전 sshd -t를 실행하고 현재 세션과 콘솔 복구 경로를 유지합니다.
- UFW 활성화 전 실제 SSH 포트 허용과 클라우드 보안그룹을 함께 확인합니다.
- 자동 업데이트의 재부팅 정책을 서비스 점검 시간과 맞춥니다.
- 관리자 키와 sudo 권한은 최소 인원에게만 부여하고 정기 회수합니다.
COMMON ERRORS
자주 막히는 지점
새 설정 뒤 SSH 접속이 거부됨
- 증상
- 새 세션이 timeout 또는 Permission denied로 실패합니다.
- 가능한 원인
- 공개키·홈 디렉터리 권한, sshd 설정, 실제 포트와 방화벽 규칙 중 하나가 맞지 않을 수 있습니다.
- 확인 순서
- 기존 세션이나 콘솔을 유지하고 sshd -t, journalctl -u ssh, ufw status numbered 순서로 확인한 뒤 스니펫을 롤백합니다.
apt 또는 dpkg가 중단됨
- 증상
- 패키지 설치가 lock, interrupted, unmet dependencies로 끝납니다.
- 가능한 원인
- 다른 업데이트 프로세스가 실행 중이거나 이전 패키지 구성이 완료되지 않았을 수 있습니다.
- 확인 순서
- 잠금 파일을 지우지 말고 실행 중 프로세스와 journal을 확인한 후 APT·dpkg 복구 가이드를 따릅니다.
재부팅 뒤 서비스가 실패함
- 증상
- systemctl is-system-running이 degraded로 표시됩니다.
- 가능한 원인
- 기존 서비스의 부팅 의존성, 마운트, 네트워크 또는 업데이트 후 설정 불일치일 수 있습니다.
- 확인 순서
- systemctl --failed와 해당 unit journal을 확인하고 실패 서비스별 가이드로 분기합니다.
시간이 맞지 않거나 NTP가 동기화되지 않음
- 증상
- timedatectl에 System clock synchronized: no가 계속 표시됩니다.
- 가능한 원인
- UDP 123 차단, 잘못된 시간원 또는 다른 시간 동기화 서비스와의 충돌일 수 있습니다.
- 확인 순서
- timesyncd·chrony 중 실제 사용 서비스를 확인하고 시간 동기화 장애 가이드로 점검합니다.
PRIMARY REFERENCES
공식 문서
- Ubuntu Server · OpenSSH server
- Ubuntu Server · Firewall
- Ubuntu Security · Security updates
- Ubuntu Server · timedatectl and timesyncd
설치 저장소와 지원 버전은 바뀔 수 있습니다. 검토일 이후에는 링크된 공식 문서와 현재 서버의 패키지 후보 버전을 함께 확인하세요.