신입 엔지니어 재현 씨가 온보딩 첫 주에 Tomcat을 root 계정으로 실행했습니다. 서비스는 잘 돌아갔고 아무도 몰랐습니다. 두 달 뒤, 보안 감사팀이 서버 접근 로그를 분석하다가 발견합니다. "이 WAS, root로 실행 중이네요. 즉시 수정 요청합니다." 긴급 조치 티켓이 열리고 서비스 재배포와 권한 재설정 작업이 주말을 날렸습니다.
root로 실행된 서비스가 공격당하면 서버 전체가 뚫립니다. 계정과 권한 설계는 선택이 아닙니다.
- 1useradd/usermod/userdel로 사용자를 생성·수정·삭제할 수 있다
- 2chmod 숫자 표기로 파일 권한을 읽고 설정할 수 있다
- 3visudo로 특정 명령어만 sudo를 허용하는 제한 설정을 할 수 있다
- 4서비스 전용 계정을 생성하고 디렉터리 소유권을 올바르게 설정할 수 있다
- 5/etc/passwd와 /etc/shadow의 구조를 읽고 해석할 수 있다
id && groupswhich useradd groupadd usermod userdelwhich sudo && sudo -V | head -1사용자와 그룹 관리
사용자 계정의 구조 — /etc/passwd와 /etc/shadow
보안 감사에서 "서비스 계정으로 SSH 로그인이 가능한 상태입니다"라는 지적을 받았습니다. /etc/passwd를 보니 Tomcat 계정의 쉘이 /bin/bash로 설정되어 있었고, 비밀번호도 설정된 상태였습니다. 공격자가 이 계정을 탈취하면 서버에 접속해 쉘을 열 수 있습니다. 계정이 어떻게 저장되는지 이해해야 서비스 계정을 올바르게 설정하고 감사 지적을 받지 않을 수 있습니다.
Linux는 모든 사용자를 /etc/passwd에 기록하고, 실제 패스워드 해시는 루트만 읽을 수 있는 /etc/shadow에 분리 저장합니다. 이 분리 덕분에 일반 사용자가 암호화된 패스워드를 볼 수 없어 무차별 대입 공격을 막습니다.
cat /etc/passwd | tail -3
# 형식: 사용자명: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
# '!' = 패스워드 로그인 비활성화 (서비스 계정의 정상 상태)
appuser:!:19800:0:99999:7:::
UID 범위 의미:
| 범위 | 의미 |
|---|---|
0 | root — 모든 권한 |
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를 걸어두면 서버에 접근한 누구나 그 내용을 읽을 수 있습니다. 숫자 세 자리의 의미를 체계적으로 이해해야 의도한 대로 접근을 제어하고, 보안 감사에서 지적받지 않을 수 있습니다.

배포한 설정 파일에 DB 비밀번호가 들어있었는데 권한이 644로 열려있어서, 서버에 접근 가능한 모든 계정이 그 파일을 읽을 수 있었습니다. 보안 감사 항목 중 가장 흔하게 적발되는 것이 이 권한 설정 오류입니다. 반대로 실행 스크립트에 실행 권한을 빠뜨려 배포 자동화가 멈추는 경우도 마찬가지입니다. 숫자 세 자리의 의미를 정확히 이해해야 의도대로 접근을 제어할 수 있습니다.
모든 파일·디렉터리에는 소유자(owner) / 그룹(group) / 기타(others) 세 계층으로 읽기(r=4) · 쓰기(w=2) · 실행(x=1) 권한이 독립적으로 설정됩니다.
ls -la /opt/app/
# 형식: 권한(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
자주 쓰는 권한 조합:
| 숫자 | 권한 | 용도 |
|---|---|---|
755 | rwxr-xr-x | 실행 스크립트, 디렉터리 |
644 | rw-r--r-- | 설정 파일, HTML |
600 | rw------- | 개인 키, 비밀 파일 |
750 | rwxr-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 등 정상 파일 외 항목은 즉시 조사
- deploy.sh(700): -rwx------ 소유자만 실행 가능, 다른 사람은 읽지도 못함
- secret.key(600): -rw------- 소유자만 읽기/쓰기 — DB 패스워드, SSL 개인키 표준 권한
- config.yml(640): -rw-r----- 소유자 읽기/쓰기, 그룹 읽기만 — 설정 파일 표준
- app.jar(644): -rw-r--r-- 소유자 읽기/쓰기, 나머지 읽기만 — 일반 배포 파일 표준
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
Jan 15 14:23:01 server01 sudo: appuser : TTY=pts/0 ; PWD=/home/appuser ; USER=root ; COMMAND=/usr/bin/systemctl restart tomcat

서비스 계정 관리 — 실무 핵심
왜 서비스를 root로 실행하면 안 되는가
root로 실행된 서비스는 프로세스가 탈취됐을 때 시스템 전체를 잃습니다. Tomcat에 원격 코드 실행 취약점이 있다면, 공격자는 Tomcat 프로세스 권한으로 명령어를 실행합니다. Tomcat이 root라면 백도어 계정 생성, 모든 파일 열람이 즉시 가능합니다. 서비스 전용 계정은 서비스가 동작하는 데 필요한 최소한의 권한만 가집니다.
# 위험한 상태 vs 안전한 상태
ps aux | grep -E 'tomcat|java' | grep -v grep
# 위험: 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의 ! |
| 최소 파일 소유 | 서비스 디렉터리만 소유 |
- id appuser 결과에 appgroup이 포함되는지 확인
- ls -la /opt/app 소유자가 appuser:appgroup으로 표시되는지 확인
- /opt/app 권한이 drwxr-x--- (750)인지 확인
- sudo -u appuser 로 /opt/app 내 파일 생성이 성공하는지 확인
- cat /etc/passwd | grep appuser 마지막 필드가 /sbin/nologin인지 확인
실제 업무에서 이 지식이 쓰이는 상황:
현업 인프라 팀에서는 서비스마다 전용 계정을 두는 것이 표준입니다. 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 내로 제한됐습니다. 계정 분리 하나가 사고의 반경을 결정합니다.
다음 모듈에서는 SSH 키 기반 인증과 서버 접근 제어 설정을 다룹니다.