팀에 새 개발자가 합류했습니다. 온보딩 첫날, 그에게 서버 접근 권한을 줘야 합니다. root 계정을 공유하면 편하지만 누가 무엇을 했는지 추적이 안 됩니다. 개인 계정을 만들어주려니 어떤 그룹에 넣어야 할지, sudo는 어디까지 줄지 판단이 서지 않습니다. 결국 sudo 그룹을 통째로 줬는데 — 3일 후 그가 실수로 rm -rf /etc/nginx/ 를 실행했습니다.
사용자 계정과 그룹을 제대로 설계하면 "이 사람은 이 명령만 실행할 수 있다"를 정밀하게 제어할 수 있습니다.
사용자와 그룹 관리
id && whoami && groups
getent passwd | tail -5
서버에서 특정 사용자가 갑자기 로그인이 안 된다는 제보가 들어왔을 때, 또는 새로 만든 계정이 docker 명령을 못 쓴다고 할 때 — 첫 번째로 확인해야 할 것이 이 세 파일입니다. Linux는 사용자 정보를 데이터베이스가 아닌 텍스트 파일로 관리하고, 각 파일이 다른 역할을 담당합니다. 이 파일들을 직접 읽을 줄 알면, id 명령 출력이 왜 그렇게 나오는지, 그룹 추가가 왜 바로 반영되지 않는지, 패스워드가 어디에 어떤 형태로 저장되는지를 모두 직접 확인할 수 있습니다.
Linux의 사용자 정보는 세 개의 텍스트 파일에 나뉘어 저장됩니다.
/etc/passwd — 사용자 계정 데이터베이스
모든 사용자(사람 + 시스템 계정)가 한 줄씩 기록됩니다.
username:x:UID:GID:comment:home_dir:shell
실제 예시를 보면 다음과 같습니다.
root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin deploy:x:1001:1001:서비스 배포 계정:/home/deploy:/bin/bash
UID 범위별 역할
UID 숫자 범위에 따라 계정의 유형이 구분됩니다. 이 범위를 알면 낯선 시스템에서 /etc/passwd를 봤을 때 어떤 계정이 사람용이고 어떤 게 데몬용인지 바로 판별할 수 있습니다.
| 범위 | 유형 | 설명 |
|---|---|---|
| 0 | root | 시스템의 절대 관리자 — 모든 파일과 프로세스에 접근 가능 |
| 1 – 999 | 시스템 계정 | 데몬·서비스용 계정. 로그인 불가(nologin shell)가 일반적 |
| 1000+ | 일반 사용자 | 실제 사람이 로그인하는 계정. Ubuntu 기준 1000부터 시작 |
두 번째 필드가 x인 것에 주목하세요. 실제 패스워드 해시는 여기에 없습니다.
/etc/shadow — 패스워드 해시 분리 저장
과거에는 패스워드 해시가 /etc/passwd에 직접 들어 있었습니다. 이 파일은 누구나 읽을 수 있기 때문에 오프라인 크래킹 공격에 노출됐습니다. 현대 Linux는 해시를 /etc/shadow로 분리하고, root만 읽을 수 있도록 권한을 640 또는 000으로 제한합니다.
username:$hash:last_change:min:max:warn:inactive:expire:reserved
deploy:$6$rounds=656000$salt$longhashstring...:19800:0:99999:7:::
각 필드의 의미입니다.
$6$...— SHA-512 해시 (알고리즘 번호 6)19800— 마지막 패스워드 변경일 (1970-01-01 기준 일수)99999— 패스워드 최대 유효 기간 (일)7— 만료 7일 전부터 경고
보안 원칙:
/etc/shadow는 절대 직접 편집하지 마세요.passwd,chage,usermod명령을 사용하면 형식 오류 없이 안전하게 수정할 수 있습니다.
/etc/group — 그룹 멤버십
그룹 정보는 /etc/group 파일에 저장됩니다. 각 줄이 하나의 그룹이며, 쉼표로 구분된 사용자 목록이 마지막 필드에 옵니다.
groupname:x:GID:member1,member2,...
docker:x:998:alice,deploy sudo:x:27:alice
사용자는 하나의 **주 그룹(primary group)**과 여러 개의 **보조 그룹(supplementary group)**에 속할 수 있습니다. 파일에 접근할 때 커널은 이 목록 전체를 확인합니다.
— — —
배포 서버에 접속해서 Nginx를 재시작해야 하는데, 현재 계정으로는 권한이 없습니다. 이 상황에서 두 가지 선택지가 있습니다. root로 완전히 전환하거나, 이 명령 하나만 권한 상승해서 실행하거나. 전자가 su, 후자가 sudo입니다. 실무에서는 대부분 sudo가 더 안전한 선택입니다. 실행 내역이 로그에 남고, 권한 범위를 명령 단위로 제한할 수 있기 때문입니다. su는 여러 명령을 연속으로 실행해야 할 때나 root 환경이 필요한 경우에 씁니다.
su — 사용자 전환 (Switch User)
su는 다른 사용자로 완전히 전환합니다. 대상 계정의 패스워드가 필요합니다.
bash# root로 전환 (root 패스워드 필요) su - # 특정 사용자로 전환 (해당 사용자 패스워드 필요) su - deploy # 전환 없이 단일 명령만 실행 su - deploy -c "systemctl status myapp"
- (하이픈)는 중요합니다. 이것이 있으면 대상 사용자의 환경변수(PATH, HOME 등)까지 완전히 불러옵니다. 없으면 현재 환경을 유지한 채 사용자만 바뀌어 예상치 못한 동작이 생길 수 있습니다.
sudo — 특정 명령에 권한 위임 (Superuser Do)
sudo는 현재 사용자가 자신의 패스워드로 인증한 뒤 지정된 명령을 다른 사용자(보통 root) 권한으로 실행합니다. 모든 실행 내역이 /var/log/auth.log 또는 /var/log/secure에 기록됩니다.
bash# root 권한으로 단일 명령 실행 sudo apt update # root 쉘로 진입 sudo -s # 다른 사용자 권한으로 실행 sudo -u deploy ls /home/deploy # 현재 계정에 부여된 sudo 권한 확인 sudo -l
/etc/sudoers 구조
visudo 명령으로만 편집하세요. 문법 검사를 자동으로 수행해 파일이 망가지는 것을 막아줍니다.
# 기본 문법 WHO WHERE=(AS_WHOM) WHAT # alice는 어디서든 root로 모든 명령 실행 가능 alice ALL=(ALL) ALL # deploy는 패스워드 없이 systemctl restart myapp만 실행 가능 deploy ALL=(ALL) NOPASSWD: /bin/systemctl restart myapp
두 방법의 차이를 한 표로 정리하면 어떤 상황에서 무엇을 써야 할지 명확해집니다.
| 방법 | 패스워드 | 감사 로그 | 권한 범위 |
|---|---|---|---|
su |
대상 계정 패스워드 | 없음 | 계정 전체 권한 |
sudo |
본인 패스워드 | /var/log/auth.log |
sudoers에 정의된 명령만 |
— — —
실습
실습 전 디렉토리와 예제 파일을 먼저 준비합니다.
bash# 실습 디렉토리 준비 mkdir -p /tmp/linux/part1/exam_1 && cd /tmp/linux/part1/exam_1
이제 실습을 진행합니다.
시스템에서 자신이 누구인지, 어떤 그룹에 속하는지 확인하는 것부터 시작합니다.
bash# 현재 사용자의 UID, GID, 그룹 목록 출력 id # 출력 예시 uid=1000(alice) gid=1000(alice) groups=1000(alice),4(adm),27(sudo),998(docker) # 사용자 이름만 출력 whoami # 속한 그룹 이름 목록만 출력 groups
id 출력에서 27(sudo)가 보이면 sudo 권한이 있는 계정입니다. 998(docker)가 있으면 sudo 없이 docker 명령을 실행할 수 있습니다.
확인 포인트: /etc/passwd에서 자신의 계정 항목을 직접 찾아보세요.
bashgrep "^$(whoami):" /etc/passwd
bashid && whoami && groups
애플리케이션 배포에 사용할 전용 계정을 만듭니다. 개인 계정이 아니라 역할 중심 계정을 사용하면 퇴직자 처리와 감사 로그 추적이 훨씬 쉬워집니다.
bash# 실습 디렉토리 준비 mkdir -p /tmp/linux/part1/exam_1 && cd /tmp/linux/part1/exam_1 # 배포 전용 계정 생성 (-m: 홈 디렉토리 생성, -s: 로그인 쉘 지정) sudo useradd -m -s /bin/bash deploy # 생성 확인 id deploy # uid=1002(deploy) gid=1002(deploy) groups=1002(deploy) # 홈 디렉토리 생성 확인 ls -la /home/deploy/ # 계정 정보를 /etc/passwd에서 확인 getent passwd deploy
최소 권한 원칙: 그룹은 실제로 필요한 것만 부여하세요.
sudo그룹 대신 아래 실습 4에서 다루는 특정 명령만 허용하는 sudoers 설정이 더 안전합니다.
bashsudo useradd -m -s /bin/bash -G docker,sudo deploy
이미 존재하는 계정에 그룹을 추가할 때는 usermod -aG를 사용합니다. -a (append) 플래그를 반드시 붙여야 합니다. 빠뜨리면 기존 보조 그룹이 모두 제거됩니다.
bash# 현재 사용자를 docker 그룹에 추가 sudo usermod -aG docker $USER # 그룹 추가 확인 (재로그인 없이 현재 세션 확인) groups # 아직 docker가 없을 수 있음 — 새 세션에서 적용됨 # 새 그룹으로 즉시 전환 (재로그인 없이) newgrp docker # 확인 groups # ... docker ... # deploy 계정에도 그룹 추가 sudo usermod -aG docker deploy id deploy
bashsudo usermod -aG docker $USER
deploy 계정이 패스워드 없이 systemctl restart myapp만 실행할 수 있도록 설정합니다. sudo 그룹 전체 권한보다 훨씬 안전한 방식입니다.
bash# visudo로만 편집 — 직접 편집하면 실수 시 sudo 자체가 잠김 sudo visudo
파일 끝에 다음 줄을 추가합니다.
# deploy 계정은 myapp 재시작만 가능 (패스워드 불필요) deploy ALL=(ALL) NOPASSWD: /bin/systemctl restart myapp, /bin/systemctl status myapp
저장 후 테스트합니다.
bash# deploy 계정으로 전환하여 테스트 sudo -u deploy sudo systemctl status myapp # 다른 명령은 거부되어야 함 sudo -u deploy sudo apt update # Sorry, user deploy is not allowed to execute '/usr/bin/apt update' as root
bashsudo visudo
서비스 계정에 초기 패스워드를 설정하고, 만료 정책을 검토합니다.
bash# 패스워드 설정 sudo passwd deploy # 패스워드 만료 정책 확인 sudo chage -l deploy # Last password change : Jan 15, 2024 # Password expires : never # Password inactive : never # Account expires : never # Minimum number of days between password change : 0 # Maximum number of days between password change : 99999 # Number of days of warning before password expires : 7 # 보안 정책 설정 예시: 90일마다 변경, 만료 7일 전 경고 sudo chage -M 90 -W 7 deploy # 계정 만료일 설정 (특정 날짜 이후 사용 불가) sudo chage -E 2025-12-31 deploy
서비스 계정 패스워드 정책: 사람이 로그인하지 않는 서비스 계정은 SSH 키 인증만 허용하고 패스워드 로그인을 비활성화하는 것이 더 안전합니다.
bashsudo passwd deploy && sudo chage -l deploy
배포 스크립트나 CI/CD 파이프라인에서 특정 서비스 계정의 환경으로 명령을 실행하는 방법입니다.
bash# deploy 계정의 환경에서 단일 명령 실행 sudo su - deploy -c "whoami && pwd && env | grep HOME" # 인터랙티브 쉘로 전환 sudo su - deploy # 전환 후 확인 id pwd # /home/deploy
대부분의 배포 자동화에서는 sudo su - deploy -c가 더 예측 가능한 환경을 제공합니다. PATH와 환경변수가 완전히 초기화되기 때문입니다.
bashsudo su - deploy -c 'systemctl status myapp'
— — —
비밀번호가 1234인 계정이 프로덕션 서버에 있다면, 브루트포스 공격에 몇 초도 안 걸립니다. 반대로 로그인 실패가 일정 횟수를 넘으면 자동으로 잠기는 정책이 있다면 공격 가능성이 크게 줄어듭니다. PAM은 이런 보안 정책을 시스템 전반에 일관되게 적용하는 Linux의 인증 프레임워크입니다. 패스워드 복잡도 요구사항, 로그인 실패 시 계정 잠금 — 이 두 가지가 기본 방어선이고, 여기서 다루는 설정이 그 구현입니다.
패스워드 관련 보안 정책은 PAM(Pluggable Authentication Modules)을 통해 시스템 전반에 일관되게 적용합니다. 설정 파일 위치는 배포판마다 다릅니다.
패스워드 품질 정책 (pwquality):
pwquality는 패스워드를 설정할 때 길이, 대소문자, 숫자, 특수문자 조건을 강제합니다. /etc/security/pwquality.conf에 정책을 정의하면 passwd 명령 실행 시 자동으로 적용됩니다.
bash# /etc/security/pwquality.conf 설정 예 sudo tee /etc/security/pwquality.conf << 'EOF' minlen = 12 # 최소 12자 dcredit = -1 # 숫자 최소 1개 이상 ucredit = -1 # 대문자 최소 1개 이상 lcredit = -1 # 소문자 최소 1개 이상 ocredit = -1 # 특수문자 최소 1개 이상 maxrepeat = 3 # 같은 문자 3번 연속 금지 usercheck = 1 # 사용자명 포함 금지 EOF # 설정 테스트 (패스워드 품질 점수 확인) pwscore # 입력: 12345 → 점수: 0 (너무 약함) # 입력: P@ssw0rd123! → 점수: 100 (강함)
로그인 실패 잠금 정책 (pam_faillock):
pam_faillock은 지정한 횟수 이상 로그인에 실패하면 계정을 일시 잠금합니다. 브루트포스 공격의 기본 방어선입니다.
bash# RHEL 8+ / Rocky / AlmaLinux — /etc/security/faillock.conf sudo tee /etc/security/faillock.conf << 'EOF' deny = 5 # 5회 실패 시 잠금 fail_interval = 120 # 2분 안에 실패 횟수 집계 unlock_time = 300 # 5분 후 자동 잠금 해제 (0이면 영구 잠금) silent # 잠금 여부 숨기기 (보안 강화) audit # 잠금 이벤트를 audit 로그에 기록 EOF # Ubuntu 22.04+ — /etc/pam.d/common-auth 확인 grep pam_faillock /etc/pam.d/common-auth # 현재 잠긴 계정 확인 sudo faillock --user alice # 계정 수동 잠금 해제 (관리자) sudo faillock --user alice --reset
배포판별 PAM 설정 파일 위치:
Ubuntu와 RHEL 계열은 설정 파일 경로가 달라 혼동하기 쉽습니다. 서버 배포판을 확인한 뒤 해당 경로를 수정하세요.
| 배포판 | 패스워드 품질 | 로그인 실패 잠금 |
|---|---|---|
| RHEL 8+ / Rocky | /etc/security/pwquality.conf |
/etc/security/faillock.conf |
| Ubuntu 22.04+ | pam_pwquality in /etc/pam.d/common-password |
pam_faillock in /etc/pam.d/common-auth |
| Ubuntu 20.04 이하 | pam_cracklib (레거시) |
pam_tally2 (deprecated) |
운영 주의: PAM 설정을 잘못 적용하면 모든 사용자가 로그인 불가 상태가 됩니다. 변경 전 반드시 root 세션을 별도 유지하고,
pam_config --service=sshd --query또는pamtester sshd로 검증하세요.authenticate
— — —
자주 발생하는 오류
상황: useradd deploy 로 계정을 만들고 SSH 키도 등록했는데 ssh deploy@server 에서 위 오류가 납니다. 로그인 후 ~ 가 /root 를 가리키는 경우도 있습니다.
원인: useradd 시 -m 옵션을 빠뜨리면 홈 디렉토리가 생성되지 않습니다. .ssh/authorized_keys 를 저장할 위치가 없어 공개키 인증이 실패합니다.
진단:
bash# 홈 디렉토리 존재 확인 ls -la /home/deploy
outputls: cannot access '/home/deploy': No such file or directory
해결:
bash# 방법 1: 계정 삭제 후 -m 옵션과 함께 재생성 sudo userdel deploy sudo useradd -m -s /bin/bash deploy # 방법 2: 기존 계정에 홈 디렉토리 수동 생성 sudo mkdir -p /home/deploy sudo chown deploy:deploy /home/deploy sudo chmod 755 /home/deploy
상황: 새로 만든 계정으로 sudo apt update 를 실행했더니 위 메시지가 뜹니다. 분명히 계정은 정상인데 sudo 가 안 됩니다.
원인: 계정이 sudo 그룹(Ubuntu/Debian) 또는 wheel 그룹(RHEL/CentOS)에 속하지 않거나, /etc/sudoers 에 해당 계정 항목이 없습니다.
진단:
bash# 현재 그룹 목록 확인 groups alice
outputalice : alice
sudo 또는 wheel 이 보이지 않으면 권한이 없는 것입니다.
해결:
bash# sudo 그룹에 추가 (Ubuntu/Debian) sudo usermod -aG sudo alice # wheel 그룹에 추가 (RHEL/CentOS) sudo usermod -aG wheel alice # 변경 후 alice 가 새 터미널 세션에서 재로그인해야 적용됩니다 # 즉시 확인하려면: su - alice sudo -l
상황: sudo usermod -G docker alice 로 docker 그룹을 추가했는데, 이후 alice 계정으로 docker ps 를 실행하니 위 오류가 납니다. 게다가 기존에 있던 sudo 권한도 사라졌습니다.
원인: -G 옵션은 보조 그룹 목록을 완전히 대체합니다. -a (append) 플래그 없이 사용하면 기존 보조 그룹이 모두 제거되고 지정한 그룹 하나만 남습니다.
진단:
bash# 현재 그룹 확인 id alice
outputuid=1001(alice) gid=1001(alice) groups=1001(alice),998(docker)
sudo, adm 등 기존 그룹이 사라진 것을 확인합니다.
해결:
bash# 올바른 방법: -a 플래그로 기존 그룹 유지하며 추가 sudo usermod -aG docker alice # 복구: 제거된 그룹 재추가 sudo usermod -aG sudo,adm alice # 확인 id alice
outputuid=1001(alice) gid=1001(alice) groups=1001(alice),4(adm),27(sudo),998(docker)
— — —
실무 시나리오
' | sudo -u cicd tee /home/cicd/.ssh/authorized_keys", "배포 명령만 sudo 허용: echo 'cicd ALL=(ALL) NOPASSWD: /bin/systemctl restart myapp, /usr/bin/rsync' | sudo tee /etc/sudoers.d/cicd", "sudoers 파일 권한 설정: sudo chmod 440 /etc/sudoers.d/cicd", ]} />
CI/CD 파이프라인에서 배포 스크립트가 sudo systemctl restart myapp을 실행해야 한다면, 해당 계정에 sudo 전체 권한을 주는 건 과합니다. root 권한으로 모든 명령을 실행할 수 있게 되기 때문입니다. sudoers의 Cmnd_Alias를 쓰면 "이 계정은 이 명령만, 패스워드 없이"를 정밀하게 설정할 수 있습니다. 실무에서 서비스 계정의 sudo 권한을 설계할 때 가장 많이 쓰는 패턴입니다.
sudoers 파일은 최소 권한 원칙(Least Privilege)을 적용해 꼭 필요한 명령만 허용해야 합니다.
Cmnd_Alias로 명령 그룹화:
Cmnd_Alias는 관련 명령을 하나의 이름으로 묶어 sudoers 파일을 읽기 쉽게 유지하고, 추후 명령 추가·제거를 한 곳에서 관리할 수 있게 합니다.
bash# /etc/sudoers.d/deploy (visudo -f /etc/sudoers.d/deploy로 편집) # 명령 그룹 정의 Cmnd_Alias DEPLOY_CMDS = /bin/systemctl restart myapp, \ /bin/systemctl start myapp, \ /bin/systemctl stop myapp, \ /usr/bin/rsync Cmnd_Alias LOG_CMDS = /usr/bin/journalctl, \ /bin/tail # 배포 계정에 명령 그룹 허용 (패스워드 없이) deploy ALL=(root) NOPASSWD: DEPLOY_CMDS # 개발자는 로그만 볼 수 있음 %developers ALL=(root) NOPASSWD: LOG_CMDS # 특정 IP에서만 NOPASSWD 허용 (내부 CI/CD 서버 IP) cicd 10.0.1.0/24=(root) NOPASSWD: DEPLOY_CMDS
NOPASSWD 사용 시 감사 보강:
NOPASSWD를 허용하면 패스워드 확인이 없어 편리하지만 그만큼 감사 로그가 중요해집니다. auditd를 함께 구성하면 sudoers 파일 자체의 변경 이력도 추적할 수 있습니다.
bash# sudo 실행 내역은 /var/log/auth.log 또는 /var/log/secure에 자동 기록 grep "sudo" /var/log/auth.log | grep "COMMAND" # sudo: deploy : ... COMMAND=/bin/systemctl restart myapp # auditd로 더 세밀한 감사 (설치 필요) sudo apt install auditd # /etc/audit/rules.d/sudo.rules # -w /etc/sudoers -p wa -k sudoers_change # -w /etc/sudoers.d/ -p wa -k sudoers_change sudo augenrules --load # sudo 실행 내역 감사 로그 확인 sudo ausearch -k sudoers_change
sudoers 파일 안전 편집 규칙:
/etc/sudoers를 직접 편집하다가 문법 오류가 생기면 sudo 자체가 잠겨 root 접근이 불가능해질 수 있습니다. visudo는 저장 전에 문법을 검사해 이를 방지합니다.
bash# 반드시 visudo 사용 (문법 검증 포함) sudo visudo # 특정 파일 편집 sudo visudo -f /etc/sudoers.d/deploy # 문법 오류 있으면 저장 거부 → 파일 손상 방지 # 절대 직접 편집하지 말 것: sudo vi /etc/sudoers ← 위험! # 현재 자신의 sudo 권한 확인 sudo -l
다음 모듈에서는 파일 권한(File Permissions) — chmod, chown, umask로 파일과 디렉토리에 대한 접근을 정밀하게 제어하는 방법을 다룹니다.
'Linux' 카테고리의 다른 글
| [Linux] 패키지 관리 (apt/yum/dnf) (0) | 2026.05.22 |
|---|---|
| [Linux]파일 권한 (File Permissions) (0) | 2026.05.22 |
| [Linux]텍스트 편집 기초 — vim과 nano (0) | 2026.05.22 |
| [Linux] 파일시스템 탐색 (0) | 2026.05.22 |
| 터미널 접속 & 기본 조작 (0) | 2026.05.22 |