infra
Platform

모듈 맵

[Linux] 리눅스 다중 사용자 권한 분리와 그룹 설정 실무

0 / 37 완료

펼치기
0 / 37 완료0%

리눅스 서버 운영 · 05 / 37

[Linux] 리눅스 다중 사용자 권한 분리와 그룹 설정 실무

useradd, usermod, sudo, su로 Linux 사용자와 권한을 관리합니다

🚨INCIDENT ALERT
HIGH

팀에 새 개발자가 합류했습니다. 온보딩 첫날, 그에게 서버 접근 권한을 줘야 합니다. root 계정을 공유하면 편하지만 누가 무엇을 했는지 추적이 안 됩니다. 개인 계정을 만들어주려니 어떤 그룹에 넣어야 할지, sudo는 어디까지 줄지 판단이 서지 않습니다. 결국 sudo 그룹을 통째로 줬는데 — 3일 후 그가 실수로 rm -rf /etc/nginx/ 를 실행했습니다.

사용자 계정과 그룹을 제대로 설계하면 "이 사람은 이 명령만 실행할 수 있다"를 정밀하게 제어할 수 있습니다.

사용자와 그룹 관리

이번 챕터에서 배울 것
  • 1/etc/passwd, /etc/shadow, /etc/group 파일 구조를 읽고 UID/GID 범위를 설명할 수 있다
  • 2useradd, usermod, userdel로 사용자 계정을 생성하고 관리할 수 있다
  • 3기본 그룹과 보조 그룹의 차이를 이해하고 usermod -aG로 그룹을 추가할 수 있다
  • 4sudo와 su의 차이를 이해하고 /etc/sudoers를 visudo로 안전하게 편집할 수 있다
  • 5최소 권한 원칙을 적용한 서비스 전용 계정을 설계할 수 있다
실습 환경 준비
현재 사용자 및 그룹 정보 확인
id && whoami && groups
사용자 계정 파일 구조 확인
getent passwd | tail -5
sudoers 편집은 반드시 visudo 사용

직접 /etc/sudoers를 편집하면 문법 오류 시 sudo가 잠길 수 있습니다

💡개념

/etc/passwd · /etc/shadow · /etc/group 파일 구조

서버에서 특정 사용자가 갑자기 로그인이 안 된다는 제보가 들어왔을 때, 또는 새로 만든 계정이 docker 명령을 못 쓴다고 할 때 — 첫 번째로 확인해야 할 것이 이 세 파일입니다. Linux는 사용자 정보를 데이터베이스가 아닌 텍스트 파일로 관리하고, 각 파일이 다른 역할을 담당합니다. 이 파일들을 직접 읽을 줄 알면, id 명령 출력이 왜 그렇게 나오는지, 그룹 추가가 왜 바로 반영되지 않는지, 패스워드가 어디에 어떤 형태로 저장되는지를 모두 직접 확인할 수 있습니다.

/etc/passwd · /etc/shadow · /etc/group 파일 필드 구조와 역할확대

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를 봤을 때 어떤 계정이 사람용이고 어떤 게 데몬용인지 바로 판별할 수 있습니다.

범위유형설명판단 기준
0root시스템의 절대 관리자 — 모든 파일과 프로세스에 접근 가능프로세스가 UID 0으로 실행 중이면 즉시 확인 필요
1 – 999시스템 계정데몬·서비스용 계정. 로그인 불가(nologin shell)가 일반적shell이 /bin/bash면 비정상 — /sbin/nologin이어야 정상
1000+일반 사용자실제 사람이 로그인하는 계정. Ubuntu 기준 1000부터 시작퇴직자 계정이 목록에 남아있으면 즉시 userdel 또는 usermod -L

조합 해석 — 이 두 가지가 동시에 보이면:

  • 시스템 계정(UID 1~999)인데 shell이 /bin/bash + 최근 로그인 기록 있음 → 계정 탈취 또는 설정 오류 의심, last <계정명>으로 로그인 이력 즉시 확인
  • 일반 계정인데 sudo 그룹 + 퇴사 3개월 경과 → 접근 통제 누락, usermod -L <계정명>으로 잠금

두 번째 필드가 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)**에 속할 수 있습니다. 파일에 접근할 때 커널은 이 목록 전체를 확인합니다.

UID/GID 관계 — 사용자·그룹·파일 권한의 연결 구조확대


💡개념

로그인하면 실제로 무슨 일이 — 사용자 인증·식별 6단계

새 계정을 만들었는데 SSH 로그인이 "안 되고", 비번은 맞는데 계정이 잠겼다 하고, 서비스 계정은 인증이 되는데도 접속하자마자 끊깁니다. 이 증상들은 전부 로그인 한 번에 순서대로 일어나는 조회 → 인증 → 자격증명 부여 → 셸 실행의 어느 한 단계가 막힌 것입니다. 로그인은 "비번이 맞나"만 보는 게 아니라, 앞서 본 /etc/passwd·/etc/shadow·/etc/group 세 파일을 차례로 거쳐 UID/GID라는 정체를 세션에 새기는 과정입니다. 이 흐름을 알면 "로그인이 안 된다"를 단계로 좁혀 진단할 수 있습니다.

TEXT
[입력]  로그인: 사용자명 + 비밀번호   (login / sshd / su / sudo)
   │
   ① 조회: 사용자명으로 /etc/passwd 검색 → UID·주 GID·홈·셸 확인
   │
   ② 인증: 입력 비번을 해싱해 /etc/shadow 의 저장 해시와 대조 (PAM)
   │        └─ 동시에 faillock(실패 누적 잠금)·pwquality·계정 만료도 검사
   │
   ③ 자격증명: 인증 성공 → 세션 프로세스에 신원 부여
   │        UID · 주 GID(/etc/passwd) + 보조 그룹 목록(/etc/group)
   │
   ④ 환경: HOME=/home/<사용자> 로 이동, 로그인 셸(/etc/passwd 7번째 필드) 실행
   │
   ⑤ 초기화: 셸이 /etc/profile·~/.bash_profile 읽고 프롬프트 표시
   ▼
[세션]  이후 이 세션의 모든 프로세스가 ③의 UID/GID로 파일 접근·권한을 판정

각 단계에서 무슨 일이 일어나고, 막히면 어떤 증상인가:

단계하는 일여기서 막히면
① 조회/etc/passwd에서 사용자명 → UID·GID·홈·셸. 이름은 라벨일 뿐, 진짜 정체는 UID 숫자계정 없음 또는 UID 충돌 → 로그인 실패, 혹은 남이 남긴 파일이 내 소유로 보임
② 인증PAM이 입력 비번을 같은 알고리즘($6$=SHA-512)으로 해싱해 /etc/shadow와 비교비번 오류 → Authentication failure / 실패 누적 → Account locked(faillock)
③ 자격증명세션에 UID·주 GID·보조 그룹을 세트 — 파일 권한 판정의 기준이 여기서 고정됨usermod -aG 후 재로그인 안 하면 이 단계가 옛 그룹으로 굳어 새 그룹 미반영
④ 셸 실행/etc/passwd 7번째 필드의 로그인 셸 실행셸이 /sbin/nologin인 서비스 계정에 SSH → 인증은 되고도 즉시 끊김
⑤ 초기화로그인 셸이 /etc/profile·~/.bash_profile을 읽고 프롬프트홈 미생성(-m 누락) → ~/를 가리키거나 기본 프롬프트로 뜸

"로그인이 안 된다"는 이 다섯 관문 중 하나가 막힌 것입니다. 비번 오류는 ②, 계정 잠금은 ②의 faillock, nologin 즉시 끊김은 ④, 그룹 변경이 반영 안 되는 것은 ③(재로그인 필요), 홈이 없어 생기는 문제는 ⑤입니다. 그래서 id <사용자>로 ③의 결과(UID·그룹)를, getent passwd <사용자>로 ①·④의 값(홈·셸)을 확인하면 어느 관문에서 막혔는지 바로 좁혀집니다.


💡개념

사용자·그룹 관리 명령 — useradd · usermod · id

파일을 직접 편집하는 대신, 다중 사용자 환경을 구축하고 권한을 분리할 때 쓰는 표준 명령이 있습니다. 이 명령들이 결국 위의 세 파일을 안전하게 갱신합니다 — 손으로 /etc/passwd를 고치다 한 글자 틀리면 로그인이 막히지만, 명령은 일관성을 보장합니다.

  • useradd --create-home (또는 -m): 사용자를 새로 생성할 때 홈 디렉토리를 함께 자동 생성합니다. 이 옵션이 없으면 사용자는 로그인 시 홈 디렉토리가 없어 루트 디렉토리 /나 갈 곳 없는 환경에 놓입니다.
    로컬 터미널
    $ sudo useradd --create-home alice
    
  • usermod -aG: 기본 그룹을 유지하면서 보조 그룹(예: sudo, developers)에 추가합니다. -a(append)가 누락되면 기존 보조 그룹 정보가 전부 덮어써지는 사고가 납니다 — 반드시 -aG로 씁니다.
    로컬 터미널
    $ sudo usermod -aG sudo alice
    
  • id: 사용자의 현재 보안 식별자(uid, gid, 소속 groups)를 출력하는 검증 도구입니다. 변경 후 반영 여부를 확인할 때 씁니다.
    로컬 터미널
    $ id alice
    uid=1001(alice) gid=1001(alice) groups=1001(alice),27(sudo)
    

💡개념

sudo vs su — 권한 위임의 두 가지 방법

sudo vs su — 패스워드 방식·감사 로그·권한 범위 비교확대

배포 서버에 접속해서 Nginx를 재시작해야 하는데, 현재 계정으로는 권한이 없습니다. 이 상황에서 두 가지 선택지가 있습니다. root로 완전히 전환하거나, 이 명령 하나만 권한 상승해서 실행하거나. 전자가 su, 후자가 sudo입니다. 실무에서는 대부분 sudo가 더 안전한 선택입니다. 실행 내역이 로그에 남고, 권한 범위를 명령 단위로 제한할 수 있기 때문입니다. su는 여러 명령을 연속으로 실행해야 할 때나 root 환경이 필요한 경우에 씁니다.

su — 사용자 전환 (Switch User)

su는 다른 사용자로 완전히 전환합니다. 대상 계정의 패스워드가 필요합니다.

로컬 터미널
# 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에 기록됩니다.

로컬 터미널
# 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.logsudoers에 정의된 명령만
계정·권한 위임 판단 — 누구에게 무엇을, 어떻게 주나
새 팀원에게 서버 접근root 공유 절대 금지(누가 뭘 했는지 추적 불가) → 개인 계정 + 필요한 그룹만. 감사 추적·퇴사자 처리가 쉬워진다. 'root 공유 = 감사 불가'
권한을 어디까지 줄까sudo 그룹 통째 부여(=사실상 root) 금지 → sudoers에 필요한 명령만(예: systemctl restart nginx). 최소 권한. 'sudo 전체는 rm -rf / 사고의 문'
su vs sudo단일 명령 권한 상승=sudo(감사 로그 남음, 명령 단위 제한), root 환경이 길게 필요=su. 실무 기본은 sudo. '한 번이면 sudo, 길면 su'
서비스 실행 계정개인 계정 아니라 역할 계정(deploy·www-data) + 로그인 셸 제한(/usr/sbin/nologin). 사람 떠나도 서비스 유지, 침해 시 피해 격리. '서비스는 전용 계정'
그룹 추가했는데 적용 안 됨usermod -aG는 이미 열린 세션엔 미반영 → 재로그인/프로세스 재시작 필요. id(세션)와 getent group(실제)을 대조. '그룹 추가 후 재로그인'
주 그룹 vs 보조 그룹주 그룹=새로 만든 파일의 기본 소유 그룹(1개), 보조 그룹=추가 권한(여러 개, docker·sudo 등). 파일 접근 시 커널은 보조 그룹까지 전부 확인. '권한 추가는 보조 그룹으로'

실습

실습 전 디렉토리와 예제 파일을 먼저 준비합니다.

로컬 터미널
# 실습 디렉토리 준비
mkdir -p /tmp/linux/part1/exam_1 && cd /tmp/linux/part1/exam_1

이제 실습을 진행합니다.

1현재 사용자 정보 확인

시스템에서 자신이 누구인지, 어떤 그룹에 속하는지 확인하는 것부터 시작합니다.

로컬 터미널
# 현재 사용자의 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에서 자신의 계정 항목을 직접 찾아보세요.

로컬 터미널
grep "^$(whoami):" /etc/passwd
id && whoami && groups
🔍실행 후 확인할 것
  • 읽기 순서: id 출력은 uid → groups 순으로 확인한다. uid=0 이면 root, 1000+ 이면 일반 사용자. groups에 sudo/wheel/docker 가 있는지 바로 확인한다
  • id 결과에 sudo 또는 wheel 그룹이 포함되면 sudo 권한이 있는 계정이다
  • 조합 해석 — sudo 그룹 + docker 그룹 동시에 있으면: 이 계정은 사실상 root 수준 권한이다. docker run --rm -v /:/host 같은 명령으로 호스트 전체를 읽을 수 있기 때문이다
  • grep "^$(whoami):" /etc/passwd 결과 마지막 필드(7번째)가 쉘 경로다 — /sbin/nologin 이면 로그인 불가 계정, /bin/bash 이면 로그인 가능
  • groups 명령으로 현재 계정이 속한 그룹 목록이 공백 구분으로 출력된다
2서비스 배포용 전용 계정 생성

애플리케이션 배포에 사용할 전용 계정을 만듭니다. 개인 계정이 아니라 역할 중심 계정을 사용하면 퇴직자 처리와 감사 로그 추적이 훨씬 쉬워집니다.

로컬 터미널
# 실습 디렉토리 준비
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 설정이 더 안전합니다.

sudo useradd -m -s /bin/bash -G docker,sudo deploy
🔍실행 후 확인할 것
  • id deploy 출력에서 uid=1002(deploy) gid=1002(deploy) groups=1002(deploy) — uid가 1000+ 이면 일반 사용자 계정으로 올바르게 생성된 것이다
  • ls -la /home/deploy/ 결과에 total 24 이상이고 .bashrc .profile 파일이 보이면 홈 디렉토리와 기본 파일이 정상 생성된 것이다
  • getent passwd deploy 결과 마지막 필드가 /bin/bash 이면 로그인 가능한 쉘이 올바르게 설정된 것이다 — /sbin/nologin 이면 SSH 로그인 불가
3현재 사용자를 docker 그룹에 추가

이미 존재하는 계정에 그룹을 추가할 때는 usermod -aG를 사용합니다. -a (append) 플래그를 반드시 붙여야 합니다. 빠뜨리면 기존 보조 그룹이 모두 제거됩니다.

로컬 터미널
# 현재 사용자를 docker 그룹에 추가
sudo usermod -aG docker $USER

# 그룹 추가 확인 (재로그인 없이 현재 세션 확인)
groups
# 아직 docker가 없을 수 있음 — 새 세션에서 적용됨

# 새 그룹으로 즉시 전환 (재로그인 없이)
newgrp docker

# 확인
groups
# ... docker ...

# deploy 계정에도 그룹 추가
sudo usermod -aG docker deploy
id deploy
sudo usermod -aG docker $USER
🔍실행 후 확인할 것
  • newgrp docker 실행 후 groups 출력에 docker 가 포함되면 현재 세션에 그룹이 즉시 적용된 것이다
  • id deploy 출력에서 groups=1002(deploy),998(docker) 처럼 docker가 보이면 그룹 추가 성공 — 보이지 않으면 usermod -aG 대신 usermod -G (덮어쓰기)를 실행한 것
  • 재로그인 없이 docker 명령이 필요하면 newgrp docker, 다음 로그인부터 자동 적용이 목적이면 재로그인으로 충분하다
4sudoers에서 특정 명령만 허용 설정

deploy 계정이 패스워드 없이 systemctl restart myapp만 실행할 수 있도록 설정합니다. sudo 그룹 전체 권한보다 훨씬 안전한 방식입니다.

로컬 터미널
# visudo로만 편집 — 직접 편집하면 실수 시 sudo 자체가 잠김
sudo visudo

파일 끝에 다음 줄을 추가합니다.

# deploy 계정은 myapp 재시작만 가능 (패스워드 불필요)
deploy ALL=(ALL) NOPASSWD: /bin/systemctl restart myapp, /bin/systemctl status myapp

저장 후 테스트합니다.

로컬 터미널
# 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
sudo visudo
🔍실행 후 확인할 것
  • sudo -u deploy sudo systemctl status myapp 이 패스워드 입력 없이 실행되면 NOPASSWD 설정이 올바르게 적용된 것이다
  • 허용되지 않은 명령 실행 시 'Sorry, user deploy is not allowed to execute' 메시지가 나와야 정상 — 이 메시지가 없고 실행된다면 sudoers 설정이 더 넓게 열려있다는 뜻
  • visudo 저장 시 문법 오류가 있으면 'parse error' 경고와 함께 재편집을 유도한다 — 직접 vi /etc/sudoers 로 편집하면 이 검증이 없어 sudo 전체가 잠길 수 있다
5패스워드 설정과 만료 정책 확인

서비스 계정에 초기 패스워드를 설정하고, 만료 정책을 검토합니다.

로컬 터미널
# 패스워드 설정
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 키 인증만 허용하고 패스워드 로그인을 비활성화하는 것이 더 안전합니다.

sudo passwd deploy && sudo chage -l deploy
🔍실행 후 확인할 것
  • sudo chage -l deploy 결과에서 'Maximum number of days between password change: 99999' 이면 만료 정책이 없는 상태 — 90일 정책 적용 후 90으로 바뀌어야 정상
  • Account expires 필드가 'never' 이면 계정 만료 없음, 날짜가 표시되면 해당 일 이후 로그인 불가 — 퇴직 예정자는 반드시 만료일 설정
  • 판단 기준 — Password expires: never + 일반 사용자 계정 조합이면 보안 정책 미적용 상태다. 서비스 계정은 never가 허용되지만 사람 계정은 최대 90일(chage -M 90) 적용 권장
6서비스 계정으로 명령 실행

배포 스크립트나 CI/CD 파이프라인에서 특정 서비스 계정의 환경으로 명령을 실행하는 방법입니다.

로컬 터미널
# deploy 계정의 환경에서 단일 명령 실행
sudo su - deploy -c "whoami && pwd && env | grep HOME"

# 인터랙티브 쉘로 전환
sudo su - deploy

# 전환 후 확인
id
pwd  # /home/deploy

배포 자동화에서는 sudo su - deploy -c가 더 예측 가능한 환경을 제공합니다. PATH와 환경변수가 완전히 초기화되기 때문입니다.

sudo su - deploy -c 'systemctl status myapp'
🔍실행 후 확인할 것
  • whoami 출력이 deploy 이고 pwd 출력이 /home/deploy 이면 deploy 계정의 환경으로 완전히 전환된 것이다
  • env | grep HOME 결과가 HOME=/home/deploy 이면 홈 환경변수까지 정상 초기화된 것이다 — HOME=/root 이면 su - 의 -l 플래그가 빠진 것
  • su - deploy (대시 있음)와 su deploy (대시 없음)의 차이: 대시 있으면 환경변수 완전 초기화, 없으면 이전 사용자 환경 일부 유지 — CI/CD 스크립트에서는 반드시 대시 있음 사용

💡개념

PAM 기반 계정 잠금 & 패스워드 품질 정책 (pam_faillock / pwquality)

PAM 인증 흐름 — pam_faillock 계정 잠금과 pam_pwquality 복잡도 검사확대

비밀번호가 1234인 계정이 프로덕션 서버에 있다면, 브루트포스 공격에 몇 초도 안 걸립니다. 반대로 로그인 실패가 일정 횟수를 넘으면 자동으로 잠기는 정책이 있다면 공격 가능성이 크게 줄어듭니다. PAM은 이런 보안 정책을 시스템 전반에 일관되게 적용하는 Linux의 인증 프레임워크입니다. 패스워드 복잡도 요구사항, 로그인 실패 시 계정 잠금 — 이 두 가지가 기본 방어선이고, 여기서 다루는 설정이 그 구현입니다.

패스워드 관련 보안 정책은 PAM(Pluggable Authentication Modules)을 통해 시스템 전반에 일관되게 적용합니다. 설정 파일 위치는 배포판마다 다릅니다.

패스워드 품질 정책 (pwquality):

pwquality는 패스워드를 설정할 때 길이, 대소문자, 숫자, 특수문자 조건을 강제합니다. /etc/security/pwquality.conf에 정책을 정의하면 passwd 명령 실행 시 자동으로 적용됩니다.

로컬 터미널
# /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은 지정한 횟수 이상 로그인에 실패하면 계정을 일시 잠금합니다. 브루트포스 공격의 기본 방어선입니다.

로컬 터미널
# 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-passwordpam_faillock in /etc/pam.d/common-auth
Ubuntu 20.04 이하pam_cracklib (레거시)pam_tally2 (deprecated)

운영 주의: PAM 설정을 잘못 적용하면 모든 사용자가 로그인 불가 상태가 됩니다. 변경 전 반드시 root 세션을 별도 유지하고, pam_config --service=sshd --query 또는 pamtester sshd <user> authenticate로 검증하세요.


자주 발생하는 오류

상황: useradd deploy 로 계정을 만들고 SSH 키도 등록했는데 ssh deploy@server 에서 위 오류가 납니다. 로그인 후 ~/root 를 가리키는 경우도 있습니다.

원인: useradd-m 옵션을 빠뜨리면 홈 디렉토리가 생성되지 않습니다. .ssh/authorized_keys 를 저장할 위치가 없어 공개키 인증이 실패합니다.

진단:

로컬 터미널
# 홈 디렉토리 존재 확인
ls -la /home/deploy
OUTPUT
ls: cannot access '/home/deploy': No such file or directory

해결:

계정을 삭제하고 재생성합니다. userdel 은 계정 데이터베이스만 삭제하지만, -r 옵션 추가 시 홈 디렉토리와 메일 스풀까지 함께 삭제되므로 주의하세요.

위험 명령어-r 옵션은 홈 디렉토리(/home/deploy)와 메일 스풀을 함께 삭제합니다. SSH 키, 설정 파일, 스크립트 등 해당 계정의 모든 파일이 영구 삭제됩니다.

계정 삭제 + 홈 디렉토리 삭제 — 복구 불가

안전한 실행 조건: ls -la /home/deploy 로 삭제해도 되는 계정인지 확인하고, 중요 파일은 미리 백업한 후 실행

실행 전 반드시 확인

  • 삭제 대상 계정이 실행 중인 프로세스가 없는지 ps aux | grep deploy 로 확인
  • /home/deploy 안에 보존해야 할 파일이 없는지 ls -la /home/deploy 로 확인
  • 아래 예시에서는 -r 없이 userdel만 사용 (홈 디렉토리는 유지)
sudo userdel -r deploy

위 항목을 모두 확인한 후 복사할 수 있습니다

로컬 터미널
# 방법 1: 계정 삭제 후 -m 옵션과 함께 재생성 (-r 없이 계정 레코드만 삭제)
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 에 해당 계정 항목이 없습니다.

진단:

로컬 터미널
# 현재 그룹 목록 확인
groups alice
OUTPUT
alice : alice

sudo 또는 wheel 이 보이지 않으면 권한이 없는 것입니다.

해결:

로컬 터미널
# 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) 플래그 없이 사용하면 기존 보조 그룹이 모두 제거되고 지정한 그룹 하나만 남습니다.

진단:

로컬 터미널
# 현재 그룹 확인
id alice
OUTPUT
uid=1001(alice) gid=1001(alice) groups=1001(alice),998(docker)

sudo, adm 등 기존 그룹이 사라진 것을 확인합니다.

해결:

로컬 터미널
# 올바른 방법: -a 플래그로 기존 그룹 유지하며 추가
sudo usermod -aG docker alice

# 복구: 제거된 그룹 재추가
sudo usermod -aG sudo,adm alice

# 확인
id alice
OUTPUT
uid=1001(alice) gid=1001(alice) groups=1001(alice),4(adm),27(sudo),998(docker)

심화 — 커널은 이름을 모른다: 계정의 진짜 정체는 숫자다

💡개념

심화: 리눅스는 사용자를 '이름'이 아니라 'UID(숫자)'로 다룬다

지금까지 alice, deploy처럼 이름으로 계정을 다뤘습니다. 하지만 커널과 파일시스템은 그 이름을 전혀 모릅니다. 내부에서는 오직 숫자(UID/GID)만 저장하고 비교합니다. 이 사실을 알면 '이름은 다른데 권한이 새는' 당혹스러운 사고들의 정체가 보입니다.

  • 이름은 라벨, 숫자가 정체: 파일 소유권은 inode에 UID/GID 숫자로 박히고, 커널의 권한 검사도 이 숫자만 봅니다. ls -l이 사용자 이름을 보여 주는 건 UID를 /etc/passwd에서 되짚어 표시하는 편의일 뿐입니다.
  • 이름을 바꿔도 권한은 그대로: usermod -l로 이름만 바꿔도 UID가 같으면 그 계정이 소유하던 파일·권한은 하나도 바뀌지 않습니다. 반대로 이름이 같아도 UID가 다르면 완전히 다른 주체입니다.
  • UID 재사용의 함정: userdel/etc/passwd에서 이름 항목만 지웁니다. 그 사용자가 남긴 파일은 여전히 옛 UID 소유로 디스크에 남고(고아 UID), 나중에 만든 새 계정이 같은 UID를 재할당받으면 그 옛 파일들을 소급 상속합니다 — 이름은 다른데 커널 눈엔 같은 사람.
  • 컨테이너·NFS로 넘어가면 더 선명: 컨테이너 안 UID나 다른 호스트의 UID가 볼륨/NFS를 통해 같은 숫자면 같은 권한으로 취급됩니다. 컨테이너 안 root(UID 0)가 마운트한 호스트 파일을 root로 만지는 이유가 이것입니다.

정리하면 계정을 '이름'으로만 생각하면 반은 놓칩니다. 안전한 계정 회수란 이름을 지우는 것이 아니라, 그 UID가 남긴 흔적까지 정리하는 것입니다.

상황: 퇴사한 alice(UID 1001) 계정을 userdel alice로 삭제했습니다(홈은 -r 없이 남겨 둠). 며칠 뒤 인턴 bob을 useradd bob으로 만들었습니다. bob이 처음 로그인해 둘러보니, /home/alice 아래 옛 파일들과 여기저기 alice가 만들어 둔 파일이 전부 bob 소유로 표시됩니다. alice만 접근하던 자료에 bob이 접근됩니다.

원인: 커널은 소유권을 이름이 아니라 UID 숫자로 판단합니다. userdel alice는 이름 항목만 지웠을 뿐, alice가 남긴 파일들은 여전히 UID 1001 소유로 남아 있었습니다. useradd bob이 다음 빈 UID로 하필 1001을 재할당하자, 커널 눈에는 'UID 1001 = bob'이므로 alice의 옛 파일 전부가 bob 것이 된 것입니다.

진단:

로컬 터미널
# bob에게 재할당된 UID 확인
id -u bob                        # 예: 1001

# 그 UID로 소유된 '옛 파일'이 bob의 홈 밖에도 남아 있는지
sudo find / -uid 1001 -not -path '/home/bob/*' 2>/dev/null | head

bob의 홈 밖에서 UID 1001 소유 파일이 쏟아지고, 그 UID가 최근 삭제한 alice의 것이었다면 UID 재사용 사고로 확정입니다.

해결:

로컬 터미널
# 1) 재사용된 UID가 상속한 옛 파일을 찾아 정리하거나 올바른 소유자로 이관
sudo find / -uid 1001 -not -path '/home/bob/*' 2>/dev/null
#   보존 대상은 아카이브, 불필요하면 삭제, 넘겨줄 파일은 chown 으로 재소유

# 2) 재발 방지: 계정 회수를 '이름'이 아니라 'UID' 기준으로
#   삭제 전에  sudo find / -uid 1001   로 잔여 파일을 먼저 목록화
#   userdel -r 로 홈까지 지우거나, 남길 파일은 명시적으로 다른 소유자에게 chown
#   퇴사자 UID를 당분간 재발급 풀에서 제외

핵심 교훈: 계정 '삭제'는 이름을 지우는 일이고, 보안상 '회수'는 그 UID가 디스크에 남긴 흔적까지 지우는 일입니다. 이름과 숫자를 분리해서 봐야 이런 조용한 권한 누수를 막을 수 있습니다.


실무 시나리오

💼
실무 맥락GitHub Actions에서 프로덕션 서버에 배포할 때 최소 권한 원칙을 적용한 전용 계정 설정
현업 패턴
💼
실무 맥락Nginx + Python WAS 서버에서 웹 서비스가 최소 권한으로 동작하도록 전용 계정과 그룹 구성
현업 패턴
💡개념

sudoers 최소 권한 운영 템플릿 — Cmnd_Alias 분리·NOPASSWD 안전 사용

sudoers Alias 구조 — User_Alias·Cmnd_Alias·Host_Alias 조합으로 최소 권한 규칙 만들기확대

CI/CD 파이프라인에서 배포 스크립트가 sudo systemctl restart myapp을 실행해야 한다면, 해당 계정에 sudo 전체 권한을 주는 건 과합니다. root 권한으로 모든 명령을 실행할 수 있게 되기 때문입니다. sudoers의 Cmnd_Alias를 쓰면 "이 계정은 이 명령만, 패스워드 없이"를 정밀하게 설정할 수 있습니다. 실무에서 서비스 계정의 sudo 권한을 설계할 때 가장 많이 쓰는 패턴입니다.

sudoers 파일은 최소 권한 원칙(Least Privilege)을 적용해 꼭 필요한 명령만 허용해야 합니다.

Cmnd_Alias로 명령 그룹화:

Cmnd_Alias는 관련 명령을 하나의 이름으로 묶어 sudoers 파일을 읽기 쉽게 유지하고, 추후 명령 추가·제거를 한 곳에서 관리할 수 있게 합니다.

로컬 터미널
# /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/local/sbin/deploy-sync

Cmnd_Alias LOG_CMDS = /usr/bin/journalctl, \
                      /bin/tail

# 배포 계정에 명령 그룹 허용 (패스워드 없이)
deploy ALL=(root) NOPASSWD: DEPLOY_CMDS

# deploy-sync는 root:root 소유·쓰기 불가(0755)로 두고
# 소스·대상·옵션을 코드에 고정합니다. /usr/bin/rsync를 직접 허용하면
# --rsync-path 등 옵션으로 임의의 root 명령 실행이 가능해집니다.

# 개발자는 로그만 볼 수 있음
%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 파일 자체의 변경 이력도 추적할 수 있습니다.

로컬 터미널
# 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는 저장 전에 문법을 검사해 이를 방지합니다.

로컬 터미널
# 반드시 visudo 사용 (문법 검증 포함)
sudo visudo

# 특정 파일 편집
sudo visudo -f /etc/sudoers.d/deploy

# 문법 오류 있으면 저장 거부 → 파일 손상 방지
# 절대 직접 편집하지 말 것: sudo vi /etc/sudoers  ← 위험!

# 현재 자신의 sudo 권한 확인
sudo -l

명령어·단축키 빠른 참조

이 모듈에서 다룬 계정·그룹 관리와 권한 위임(su·sudo) 명령을 실전 옵션과 함께 모았습니다. "예" 열의 조합을 그대로 써도 됩니다.

명령어/단축키용도자주 쓰는 예
id / whoami / groups현재 사용자·그룹 확인id && whoami && groups
su사용자 전환su -(root 전환), su - deploy
sudo단일 명령 권한 위임sudo systemctl restart myapp, sudo -i(root 쉘)
sudo -l내 sudo 권한 확인어떤 명령이 허용됐는지 목록
useradd -m -s -G계정 생성sudo useradd -m -s /bin/bash -G docker,sudo deploy
usermod -aG보조 그룹 추가sudo usermod -aG docker $USER (반드시 -a)
newgrp재로그인 없이 새 그룹 적용newgrp docker (현재 세션에 즉시 반영)
passwd패스워드 설정sudo passwd deploy
chage -l패스워드 만료 정책 확인sudo chage -l deploy
visudosudoers 안전 편집(문법 검사)sudo visudo -f /etc/sudoers.d/deploy
/etc/passwd·shadow·group계정·해시·그룹 저장 파일grep deploy /etc/passwd

관련 모듈로 더 깊이:

다음 모듈에서는 파일 권한(File Permissions) — chmod, chown, umask로 파일과 디렉토리에 대한 접근을 정밀하게 제어하는 방법을 다룹니다.

지식 확인

퀴즈 — 10문제

Q1

새 배포 계정(deploy)을 생성했는데 `/home/deploy` 디렉토리가 없어서 SSH 접속 시 오류가 납니다. 처음부터 홈 디렉토리가 함께 생성되려면 어떤 옵션이 필요합니까?

Q2

보안 감사 중 `/etc/passwd`를 열어보니 패스워드 필드에 모두 `x`가 표시됩니다. 감사 도구가 이를 취약점으로 오탐지했습니다. 이 `x`의 실제 의미는?

Q3

주니어 개발자에게 서버 접근을 주되, 특정 명령(`systemctl restart nginx`)만 허용하고 전체 root 셸은 주고 싶지 않습니다. 이 요건에 맞는 접근 방식은?

Q4

개발자 alice가 docker 명령을 실행하려는데 'permission denied'가 납니다. `id alice` 결과는 `uid=1001(alice) gid=1001(alice) groups=1001(alice),27(sudo)`입니다. docker 그룹에는 없습니다. 어떤 조치가 필요합니까?

Q5

운영 서버에서 root 권한이 필요한 단일 명령을 실행할 때 su -로 root 셸에 진입하기보다 sudo <명령>을 권장하는 이유는?

Q6

사용자를 docker 그룹에 추가하려고 usermod -G docker alice를 실행했더니 alice가 sudo 권한을 잃었다. 원인과 올바른 명령은?

Q7

[심화] 리눅스에서 파일 소유권과 권한 판단의 진짜 기준은 무엇인가?

Q8

[심화] 퇴사자 alice(UID 1001)를 userdel로 지우고 며칠 뒤 인턴 bob을 만들었더니, bob이 alice가 남긴 파일들을 자기 소유로 보게 됐다. 원인과 재발 방지로 옳은 것은?

Q9

[1급] /etc/shadow의 한 줄이 deploy 다음에 해시·19000·0·90·7 순서로 이어질 때, 다섯 번째 필드인 90의 의미로 옳은 것은?

Q10

[1급] useradd가 새 계정을 만들 때 초기 dotfile(.bashrc 등)의 원본을 두는 디렉터리와, useradd의 시스템 기본값을 조회·변경하는 옵션이 옳게 짝지어진 것은?

0 / 10 답변

🧪 실습으로 확인하기

파일 권한 — chmod/chown/umask 실전 적용

초급

배포한 앱이 로그 파일에 쓰지 못하는 Permission denied 오류를 해결하면서 리눅스 파일 권한의 핵심인 rwxrwxrwx 표기 읽기, chmod 숫자/심볼 방식, chown 소유자 변경, umask 기본 권한 설정을 실전처럼 체득합니다.

25📋 4단계💻 직접 환경
실습 시작하기 →

이것도 배워보세요