infra
Platform

모듈 맵

[Infra Ops] Linux 사용자/그룹/권한 체계와 서비스 계정 관리

0 / 52 완료

펼치기
0 / 52 완료0%

인프라 운영 & SRE · 06 / 52

[Infra Ops] Linux 사용자/그룹/권한 체계와 서비스 계정 관리

useradd/groupadd, chmod/chown, sudo 설정, 서비스 전용 계정 생성까지 — 인프라 엔지니어가 매일 쓰는 권한 관리 실무

🚨INCIDENT ALERT
HIGH

신입 엔지니어 재현 씨가 온보딩 첫 주에 Tomcat을 root 계정으로 실행했습니다. 서비스는 잘 돌아갔고 아무도 몰랐습니다. 두 달 뒤, 보안 감사팀이 서버 접근 로그를 분석하다가 발견합니다. "이 WAS, root로 실행 중이네요. 즉시 수정 요청합니다." 긴급 조치 티켓이 열리고 서비스 재배포와 권한 재설정 작업이 주말을 날렸습니다.

root로 실행된 서비스가 공격당하면 서버 전체가 뚫립니다. 계정과 권한 설계는 선택이 아닙니다.

이번 챕터에서 배울 것
  • 1useradd/usermod/userdel로 사용자를 생성·수정·삭제할 수 있다
  • 2chmod 숫자 표기로 파일 권한을 읽고 설정할 수 있다
  • 3visudo로 특정 명령어만 sudo를 허용하는 제한 설정을 할 수 있다
  • 4서비스 전용 계정을 생성하고 디렉터리 소유권을 올바르게 설정할 수 있다
  • 5/etc/passwd와 /etc/shadow의 구조를 읽고 해석할 수 있다
실습 환경 준비
현재 사용자 및 그룹 확인
id && groups
useradd, groupadd 경로 확인
which useradd groupadd usermod userdel
sudo 설치 확인
which sudo && sudo -V | head -1

사용자와 그룹 관리

💡개념

사용자 계정의 구조 — /etc/passwd와 /etc/shadow

보안 감사에서 "서비스 계정으로 SSH 로그인이 가능한 상태입니다"라는 지적을 받았습니다. /etc/passwd를 보니 Tomcat 계정의 쉘이 /bin/bash로 설정되어 있었고, 비밀번호도 설정된 상태였습니다. 공격자가 이 계정을 탈취하면 서버에 접속해 쉘을 열 수 있습니다. 계정이 어떻게 저장되는지 이해해야 서비스 계정을 올바르게 설정하고 감사 지적을 받지 않을 수 있습니다.

Linux는 모든 사용자를 /etc/passwd에 기록하고, 실제 패스워드 해시는 루트만 읽을 수 있는 /etc/shadow에 분리 저장합니다. 이 분리 덕분에 일반 사용자가 암호화된 패스워드를 볼 수 없어 무차별 대입 공격을 막습니다.

/etc/passwd와 /etc/shadow 구조 해부 — passwd 한 줄은 사용자명:x:UID:GID:주석(GECOS):홈:로그인쉘 7필드로, x는 패스워드가 shadow에 있다는 표시이고 마지막 로그인 쉘이 /sbin/nologin이면 로그인이 차단된 서비스 계정이다. shadow는 root만 읽으며 사용자명:패스워드해시:마지막변경:min:max:warn 형식으로, 두 번째 필드가 !이면 패스워드 로그인 비활성(서비스 계정의 정상 상태)이고 $6$…이면 SHA-512 해시(로그인 계정)다. UID는 0=root, 1~999=시스템·서비스 계정(useradd -r), 1000+=일반 로그인 사용자(useradd -m)로 성격을 구분한다. 서비스 계정을 잠그는 두 스위치는 passwd의 쉘을 /sbin/nologin으로 두는 것과 shadow의 패스워드를 !로 두는 것이며, 쉘이 /bin/bash에 패스워드가 설정된 계정은 감사 지적 대상이라 usermod -s /sbin/nologin과 passwd -l로 잠근다확대

로컬 터미널
cat /etc/passwd | tail -3
OUTPUT
# 형식: 사용자명:x:UID:GID:주석(GECOS):홈디렉터리:로그인쉘
nginx:x:997:995:Nginx Web Server:/var/cache/nginx:/sbin/nologin
appuser:x:1001:1001::/opt/app:/sbin/nologin
로컬 터미널
sudo cat /etc/shadow | grep appuser
OUTPUT
# '!' = 패스워드 로그인 비활성화 (서비스 계정의 정상 상태)
appuser:!:19800:0:99999:7:::

UID 범위 의미:

범위의미
0root — 모든 권한
1 ~ 999시스템 계정 (서비스용)
1000+일반 사용자 (로그인 계정)
로컬 터미널
# useradd — 일반 vs 서비스 계정
sudo useradd -m -s /bin/bash devuser          # 일반 사용자
sudo useradd -r -s /sbin/nologin -d /opt/app -m appuser  # 서비스 계정
# -r: 시스템 계정(UID < 1000), -s: 쉘 지정, -d: 홈 경로, -m: 홈 생성

# usermod — 계정 수정
sudo usermod -aG appgroup appuser   # 그룹 추가 (-a 없으면 기존 그룹 제거됨)
sudo usermod -L devuser             # 계정 잠금
sudo usermod -U devuser             # 잠금 해제

# groupadd / gpasswd
sudo groupadd appgroup
sudo gpasswd -a appuser appgroup    # 그룹에 사용자 추가
sudo gpasswd -d appuser appgroup    # 그룹에서 제거

# 확인
id appuser
# uid=1001(appuser) gid=1001(appuser) groups=1001(appuser),1002(appgroup)

파일 권한 체계

💡개념

rwx 권한 구조와 chmod

파일 권한은 서비스의 보안과 안정성을 동시에 결정합니다. 설정 파일에 실행 권한이 없으면 배포 자동화가 멈추고, DB 비밀번호가 든 파일에 644를 걸어두면 서버에 접근한 누구나 그 내용을 읽을 수 있습니다. 숫자 세 자리의 의미를 체계적으로 이해해야 의도한 대로 접근을 제어하고, 보안 감사에서 지적받지 않을 수 있습니다.

Linux 파일 권한 체계 — rwx 구조와 숫자 표기확대

배포한 설정 파일에 DB 비밀번호가 들어있었는데 권한이 644로 열려있어서, 서버에 접근 가능한 모든 계정이 그 파일을 읽을 수 있었습니다. 보안 감사 항목 중 가장 흔하게 적발되는 것이 이 권한 설정 오류입니다. 반대로 실행 스크립트에 실행 권한을 빠뜨려 배포 자동화가 멈추는 경우도 마찬가지입니다. 숫자 세 자리의 의미를 정확히 이해해야 의도대로 접근을 제어할 수 있습니다.

모든 파일·디렉터리에는 소유자(owner) / 그룹(group) / 기타(others) 세 계층으로 읽기(r=4) · 쓰기(w=2) · 실행(x=1) 권한이 독립적으로 설정됩니다.

로컬 터미널
ls -la /opt/app/
OUTPUT
# 형식: 권한(10자) 링크수 소유자 그룹 크기 날짜 이름
drwxr-x---  3 appuser appgroup 4096 Jan 15 09:00 .
-rwxr-xr-x  1 appuser appgroup 8192 Jan 15 09:00 start.sh   # 755
-rw-r--r--  1 appuser appgroup  512 Jan 15 09:00 app.conf    # 644
-rw-------  1 appuser appgroup  128 Jan 15 09:00 secret.key  # 600

자주 쓰는 권한 조합:

숫자권한용도
755rwxr-xr-x실행 스크립트, 디렉터리
644rw-r--r--설정 파일, HTML
600rw-------개인 키, 비밀 파일
750rwxr-x---서비스 디렉터리 (others 차단)
로컬 터미널
# 숫자 표기 (권장)
chmod 755 start.sh
chmod 644 app.conf
chmod 600 secret.key
chmod 750 /opt/app/

# 문자 표기 (부분 변경 시 유용)
chmod +x deploy.sh          # 실행 권한 추가
chmod o-rwx secret.key      # others 권한 전체 제거

# 소유자·그룹 변경
sudo chown appuser:appgroup /opt/app/log.txt
sudo chown -R appuser:appgroup /opt/app/   # 재귀

특수 권한 (보안 감사 필수 확인 항목):

SUID(s)가 root 소유 실행 파일에 설정되면 일반 사용자도 root 권한으로 실행됩니다. /tmp의 Sticky Bit(t)는 자신이 만든 파일만 삭제 가능하게 합니다.

로컬 터미널
# 서버 내 SUID 파일 전체 목록 (의심 파일 확인)
sudo find / -perm -4000 -type f 2>/dev/null
# passwd, sudo, ping 등 정상 파일 외 항목은 즉시 조사
파일 권한 설정 및 검증 실습
1

1. 실습 파일 생성

mkdir -p /tmp/perm-test && touch /tmp/perm-test/{app.jar,config.yml,deploy.sh,secret.key} && ls /tmp/perm-test/
2

2. 용도별 권한 적용

chmod 644 /tmp/perm-test/app.jar && chmod 640 /tmp/perm-test/config.yml && chmod 700 /tmp/perm-test/deploy.sh && chmod 600 /tmp/perm-test/secret.key && ls -la /tmp/perm-test/
3

3. 권한 숫자 읽기 연습

stat -c '%n: %a (%A)' /tmp/perm-test/*
🔍실행 후 확인할 것
  • ls -la 출력에서 먼저 각 파일의 권한 문자열 첫 자리(-rwx------)를 보고, 그 다음 소유자:그룹 필드 확인 — 파일 유형에 맞는 권한인지 순서대로 체크
  • 권한 기준: 600 미만이면 소유자도 접근 불가(잘못된 설정), 644 이상 others 쓰기 허용이면 보안 위험 — secret.key/privkey.pem은 반드시 600, config.yml은 640이 최대
  • deploy.sh가 700인데 서비스 계정이 실행 실패하면 → 소유자가 서비스 계정과 다른 것 — stat deploy.sh로 소유자 확인 후 chown <서비스계정> deploy.sh 실행

sudo 설정

💡개념

/etc/sudoers — 최소 권한으로 sudo 제한

배포 계정에 NOPASSWD: ALL을 설정해뒀다가 보안 감사에서 "root 권한과 동일한 계정"으로 지적을 받았습니다. 해커가 이 계정 하나를 탈취하면 서버 전체를 장악할 수 있습니다. 반대로 권한을 너무 좁게 설정하면 배포 스크립트가 중간에 실패합니다. 어떤 명령어에만 sudo를 허용할지 정밀하게 지정하는 것이 보안과 운영 자동화를 동시에 잡는 방법입니다.

sudo는 일반 계정이 특정 명령어를 root 권한으로 실행하게 합니다. ALL=(ALL) ALL은 사실상 root와 동일합니다. 실무에서는 필요한 명령어만 허용하는 최소 권한(least privilege) 원칙을 적용합니다.

/etc/sudoers반드시 visudo로만 편집합니다. visudo는 저장 전 문법을 검사해 오류로 인한 sudo 잠금을 방지합니다.

로컬 터미널
# 전용 파일로 관리 (권장 — 메인 파일 직접 수정하지 않음)
sudo visudo -f /etc/sudoers.d/appuser
로컬 터미널
# /etc/sudoers.d/appuser 내용 예시
# 형식: 사용자 호스트=(실행계정) [NOPASSWD:] 명령어

# 전체 허용 (위험 — 피할 것)
appuser ALL=(ALL) ALL

# 특정 명령어만, 패스워드 없이 (서비스 재시작용 — 실무 표준)
appuser ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart tomcat

# 여러 명령어
appuser ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart tomcat, /usr/bin/systemctl status tomcat

# 그룹 단위 설정
%appgroup ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart tomcat
서버 터미널
# sudo 로그 확인 (누가 언제 어떤 명령어를 실행했는지)
sudo journalctl _COMM=sudo --since "today" | grep appuser
OUTPUT
Jan 15 14:23:01 server01 sudo: appuser : TTY=pts/0 ; PWD=/home/appuser ; USER=root ; COMMAND=/usr/bin/systemctl restart tomcat

최소 권한 원칙 — sudoers 패턴과 서비스 계정 보안확대

서비스 계정 관리 — 실무 핵심

💡개념

왜 서비스를 root로 실행하면 안 되는가

root로 실행된 서비스는 프로세스가 탈취됐을 때 시스템 전체를 잃습니다. Tomcat에 원격 코드 실행 취약점이 있다면, 공격자는 Tomcat 프로세스 권한으로 명령어를 실행합니다. Tomcat이 root라면 백도어 계정 생성, 모든 파일 열람이 즉시 가능합니다. 서비스 전용 계정은 서비스가 동작하는 데 필요한 최소한의 권한만 가집니다.

로컬 터미널
# 위험한 상태 vs 안전한 상태
ps aux | grep -E 'tomcat|java' | grep -v grep
OUTPUT
# 위험: root가 WAS를 실행 중
root      1234  2.5  8.3 3145728 340000 ? Sl 09:00 java -jar /opt/app/app.jar

# 안전: 서비스 계정으로 격리됨
appuser   1234  2.5  8.3 3145728 340000 ? Sl 09:00 java -jar /opt/app/app.jar

서비스 계정 설계 원칙:

원칙내용
로그인 쉘 차단/sbin/nologin — 쉘 접속 불가
전용 홈 디렉터리서비스 루트 디렉터리를 홈으로 설정
패스워드 비활성화/etc/shadow!
최소 파일 소유서비스 디렉터리만 소유
서비스 계정 생성 및 디렉터리 권한 설정
1

1. WAS용 서비스 계정 및 그룹 생성

sudo useradd -r -s /sbin/nologin -d /opt/app -m appuser && sudo groupadd appgroup && sudo usermod -aG appgroup appuser && id appuser
2

2. 디렉터리 소유권 및 권한 설정

sudo chown -R appuser:appgroup /opt/app && sudo chmod 750 /opt/app && ls -la /opt/ | grep app
3

3. 서비스 계정 권한 검증

sudo -u appuser touch /opt/app/test.txt && ls -la /opt/app/test.txt
4

4. sudo 제한 설정 (특정 명령어만 허용)

sudo visudo -f /etc/sudoers.d/appuser
5

5. sudo 로그 확인

sudo journalctl _COMM=sudo --since '5 min ago' --no-pager | tail -10
🔍실행 후 확인할 것
  • id appuser 출력에서 먼저 그룹 목록에 appgroup이 있는지 보고, 그 다음 ls -la /opt/app 으로 소유자:그룹이 appuser:appgroup인지 확인 — 두 조건이 모두 맞아야 디렉터리 접근 가능
  • /opt/app 권한 기준: 750이면 appgroup 소속 계정이 읽기/실행 가능(정상), 700이면 appuser 외 접근 불가(너무 좁음), 755이면 others도 접근 가능(너무 넓음)
  • id appuser에 appgroup이 있는데 /opt/app 접근 실패하면 → 디렉터리 소유 그룹이 appgroup이 아닌 것 — ls -la /opt/ 로 app 디렉터리 소유 그룹 확인 후 chown -R appuser:appgroup /opt/app 실행

심화 — 공유 디렉터리에서 새 파일의 그룹은 누가 정하나

💡개념

심화: setgid와 그룹 상속 — '공용 그룹'만으로는 부족하다

공유 작업 디렉터리를 만들 때 흔히 "공용 그룹을 만들고 디렉터리를 그 그룹 소유로 주면 끝"이라고 생각합니다. 실제로는 한 가지가 더 필요합니다 — 새로 생기는 파일의 그룹을 누가 정하는가 하는 문제입니다.

  • 기본 동작: 생성자의 primary 그룹: 리눅스에서 새 파일의 그룹은 기본적으로 그 파일을 만든 사용자의 primary 그룹입니다. 그래서 alicedeploy 소유 디렉터리에 파일을 만들어도 그 파일의 그룹은 alice(개인 그룹)가 되고, bob은 그룹 권한으로 그 파일을 못 고칩니다. 공용 그룹을 만든 의미가 사라집니다.
  • setgid 비트로 그룹 상속: 디렉터리에 setgid 비트를 걸면(chmod 2775 dir 또는 chmod g+s dir) 그 안에서 생성되는 파일과 하위 디렉터리는 디렉터리의 그룹을 상속합니다. ls -ld에서 그룹 실행 자리가 s로 보이면 켜진 것입니다. 하위 디렉터리도 setgid를 물려받아 트리 전체에서 그룹이 유지됩니다.
  • 쓰기까지 되게 하려면 umask도: 그룹이 상속돼도 파일이 644(그룹 쓰기 없음)로 태어나면 여전히 못 고칩니다. 공유 작업 그룹에는 umask 002(그룹 쓰기 허용)를 쓰거나, 더 정밀하게는 기본 ACL(setfacl -d -m g:deploy:rwx dir)로 강제합니다.
  • 그룹 변경은 즉시 반영이 아니다: usermod -aG deploy alice로 그룹에 넣어도 alice의 현재 로그인 세션에는 반영되지 않습니다. 그룹 목록은 로그인 시점에 프로세스에 박히기 때문입니다. 재로그인하거나 newgrp deploy로 새 그룹을 적용해야 합니다.

정리하면 공유 디렉터리는 '공용 그룹 소유 + setgid + 적절한 umask/ACL' 세 가지가 맞아야 완성이고, 멤버 추가 후에는 재로그인을 잊지 말아야 합니다.

상황: 여러 명이 함께 쓰는 /opt/deploy 디렉터리를 deploy 그룹 소유로 만들고 chmod 775까지 줬습니다. 그런데 A가 올린 파일을 B가 수정하려 하면 Permission denied, 반대도 마찬가지입니다. 그룹은 분명히 맞게 넣었는데 협업이 안 됩니다.

원인: 디렉터리에 setgid가 없어, 그 안에서 A가 만든 파일의 그룹이 deploy가 아니라 A의 개인 primary 그룹으로 생성됐습니다. 게다가 A의 umask가 022라 파일이 644(그룹 쓰기 없음)로 태어나, 설령 그룹이 맞았어도 B가 못 씁니다. 두 요인이 겹친 전형적 사례입니다.

진단: ls -l /opt/deploy로 문제 파일의 그룹 소유자를 봅니다 — deploy가 아니라 개인 그룹이면 상속이 안 되는 것입니다. ls -ld /opt/deploy로 그룹 자리에 s(setgid)가 있는지, 그리고 파일 권한에 그룹 쓰기(rw)가 있는지 확인합니다.

해결: 디렉터리에 setgid를 걸고(chmod 2775 /opt/deploy) 기존 파일의 그룹을 정리합니다(chgrp -R deploy /opt/deploy, 필요하면 chmod -R g+w). 공유 작업자는 umask를 002로 맞추거나 기본 ACL(setfacl -d -m g:deploy:rwx /opt/deploy)을 걸어 새 파일이 처음부터 그룹 쓰기로 태어나게 합니다. 마지막으로 방금 그룹에 추가된 사람은 재로그인해야 새 그룹이 적용됩니다.

💼
실무 맥락
현업 패턴

실제 업무에서 이 지식이 쓰이는 상황:

현업 인프라 팀에서는 서비스마다 전용 계정을 두는 것이 표준입니다. Nginx는 nginx 계정, Tomcat은 tomcat 계정, PostgreSQL은 postgres 계정으로 실행합니다. 이 분리가 안 된 서버는 보안 감사를 통과하지 못합니다.

서버 초기화 스크립트 패턴 (실제 현장에서 쓰는 방식):

로컬 터미널
APP_USER="appuser"
APP_GROUP="appgroup"
APP_HOME="/opt/app"

if ! id "$APP_USER" &>/dev/null; then
    sudo groupadd "$APP_GROUP"
    sudo useradd -r -s /sbin/nologin -d "$APP_HOME" -g "$APP_GROUP" -m "$APP_USER"
fi

sudo chown -R "$APP_USER:$APP_GROUP" "$APP_HOME"
sudo chmod 750 "$APP_HOME"
sudo chmod 640 "$APP_HOME"/config/*.conf
sudo chmod 770 "$APP_HOME"/logs/

보안 사고 사례: 2021년 Log4Shell(CVE-2021-44228) 취약점이 발견됐을 때, root로 실행 중이던 WAS는 즉각 서버 전체 장악으로 이어졌습니다. 서비스 계정으로 격리된 서버는 피해가 /opt/app 내로 제한됐습니다. 계정 분리 하나가 사고의 반경을 결정합니다.

명령어·단축키 빠른 참조

사용자·그룹·권한 관리와 서비스 계정 격리에서 다룬 명령을 모았습니다.

명령어/단축키용도자주 쓰는 예
useradd일반·서비스 계정 생성useradd -r -s /sbin/nologin -d /opt/app -m appuser
usermod계정 수정(그룹·잠금·셸)usermod -aG appgroup appuser, usermod -L devuser
groupadd / gpasswd그룹 생성·멤버 관리groupadd appgroup, gpasswd -a appuser appgroup
id / getent계정 UID·GID·그룹 확인id appuser, getent passwd appuser
/etc/passwd·/etc/shadow계정·패스워드 상태 확인grep appuser /etc/passwd, sudo grep appuser /etc/shadow(!=잠금)
chmod(숫자)파일 권한 설정chmod 640 config.yml, chmod 600 secret.key
chmod 2775 / g+s디렉터리 setgid(그룹 상속)chmod 2775 /opt/deploy
chown / chgrp소유자·그룹 변경chown -R appuser:appgroup /opt/app, chgrp -R deploy dir
stat -c권한 숫자·rwx 동시 확인stat -c '%n: %a (%A)' file
find / -perm -4000SUID 파일 감사find / -perm -4000 -type f 2>/dev/null
visudo -fsudoers 안전 편집(전용 파일)visudo -f /etc/sudoers.d/appuser
sudo -l계정 sudo 권한 점검sudo -l, sudo -l -U appuser
newgrp새 그룹 즉시 적용(재로그인 대체)newgrp deploy
journalctl _COMM=sudosudo 실행 감사 로그journalctl _COMM=sudo --since today | grep appuser

관련 모듈로 더 깊이:

다음 모듈에서는 SSH 키 기반 인증과 서버 접근 제어 설정을 다룹니다.

지식 확인

퀴즈 — 8문제

Q1

배포 자동화 스크립트 deploy.sh를 작성하고 실행하려 했더니 'Permission denied' 오류가 발생했습니다. ls -la로 확인하니 권한이 -rw-r--r-- (644)입니다. 어떻게 수정해야 하는가?

Q2

서비스 계정 생성 시 로그인 쉘을 /sbin/nologin으로 설정하는 이유는?

Q3

visudo로 appuser가 systemctl restart tomcat 명령어만 sudo 실행할 수 있게 설정하는 올바른 방법은?

Q4

/etc/passwd의 각 필드가 올바르게 설명된 것은? (예: appuser:x:1001:1001::/opt/app:/sbin/nologin)

Q5

여러 운영자가 같은 배포 디렉터리에 접근해야 한다. 권한을 관리하기 쉽게 하려면?

Q6

한 계정이 어떤 sudo 명령을 쓸 수 있는지 점검하려면?

Q7

[심화] 그룹 소유가 deploy인 공유 디렉터리에서 팀원이 새로 만든 파일의 그룹이 자꾸 각자의 개인 그룹이 된다. 새 파일이 디렉터리 그룹(deploy)을 상속하게 하려면?

Q8

[심화] usermod -aG deploy alice로 그룹에 넣었는데, alice가 그 세션에서는 여전히 그룹 권한으로 파일에 접근하지 못한다. 이유는?

0 / 8 답변

🧪 실습으로 확인하기

Nginx 설치 및 기동

초급

Linux 서버에 Nginx를 설치하고 systemd 서비스로 등록하여 80포트에서 응답하는 상태까지 만든다.

40📋 3단계💻 직접 환경
실습 시작하기 →

이것도 배워보세요