최근 home-server 를 구축하던 중 symbolic link 의 잘못된 이해로 인한 트러블 슈팅을 경험하였고, 이를 기록하고 공유하고자 포스팅을 작성합니다.
구축하는 home-server 는 테스트용 API 서버
로 추후 프로젝트 진행을 위해 구축하는 서버입니다.
home-server 를 통해 다수의 프로젝트를 진행하고자 저는 현재 생성된 유저를 간단히 보여주는
shell script 를 만들었고, 이는 다음과 같습니다.
#!/bin/bash
# Header for the output with extended widths
printf "%-25s %-25s %-10s %-10s\n" "USERNAME" "PRIORITY GROUP" "UID" "GID"
# Iterate through each user in the /etc/passwd file
while IFS=: read -r username _ uid gid _ _ _; do
# Ignore system accounts with UID less than 1000
if [ "$uid" -ge 1000 ]; then
# Get the primary group name for the user
group=$(getent group "$gid" | cut -d: -f1)
# If the group name exists, print user, group, uid, and gid
if [ -n "$group" ]; then
printf "%-25s %-25s %-10s %-10s\n" "$username" "$group" "$uid" "$gid"
fi
fi
done < /etc/passwd
$ ./list-users-with-group.sh
USERNAME PRIORITY GROUP UID GID
nobody nogroup 65534 65534
cheongju-admin cheongju-admin 1000 1003
test-user-1 test-user-1 1001 1004
test-user-2 test-user-2 1002 1006
그리고 이와 연관된 심볼릭 링크를 /usr/bin
에 생성해, 다른 유저들도 실행 가능하도록 만들고자 하였습니다.
# /usr/bin 에 list-users-with-group.sh 와 연관된 심볼릭 파일을 생성
$ sudo ln -s /[생략]/list-users-with-group.sh /usr/bin/list-users
# 권한 설정
$ chmod o+rx list-users-with-group.sh && cd /usr/bin
$ sudo chmod o+rx list-users
# cheongju-admin 계정에서 확인
cheongju-admin@cheongju-raspi:~$ printf "command ---> $(which list-users)\n\n" && list-users
# 잘 실행되는 모습
command ---> /usr/bin/list-users
USERNAME PRIORITY GROUP UID GID
nobody nogroup 65534 65534
cheongju-admin cheongju-admin 1000 1003
test-user-1 test-user-1 1001 1004
test-user-2 test-user-2 1002 1006
위 list-users
가 작동하는 것으로 올바른 구성을 맞쳤다 생각했으나, 실제 다른 user 에서 확인하니 그렇지 않았습니다.
# test-user-1 에서 확인
test-user-1@cheongju-raspi:~$ list-users
list-users: command not found
분명히 /usr/bin/list-users
가 존재하고 list-users
의 권한도 풀어놨는데, 무엇이 문제였을까요?
그 이유는 심볼릭 링크의 원본이 접근할 수 없는 곳에 위치해 있기 때문입니다.
사용자가 list-users
처럼 심볼릭 링크로 등록된 명령어를 실행할 때 다음처럼 작동합니다.
$PATH
에서 list-users
와 일치하는 목록을 탐색합니다.list-users
는 심볼릭 링크이므로, 그 원본 주소를 확인합니다.이 때 심볼릭 링크의 원본 주소에 접근할 수 없어 Command not found
가 발생합니다.
즉, 쉘 스크립트를 심볼릭 링크 명령어로 등록하기 위해선, 해당 파일까지 모든 경로가 접근 가능해야 합니다.
제가 등록한 list-users
명령어는 아래처럼 권한이 설정되어 있습니다.
$ namei -l list-users
f: list-users
# 심볼릭 링크 자체
lrwxrwxrwx root root list-users -> /home/cheongju-admin/shell-scripts/show-info/list-users-with-group.sh
# 원본 파일의 재귀적 권한
drwxr-xr-x root root /
drwxr-xr-x root root home
drwxr-x--- cheongju-admin cheongju-admin cheongju-admin
drwxr-x--- cheongju-admin cheongju-admin shell-scripts
drwxr-x--- cheongju-admin cheongju-admin show-info
-rwxr-xr-x cheongju-admin cheongju-admin list-users-with-group.sh
list-users-with-group.sh
파일 자체는 실행 가능토록 만들었지만, 이를 찾아갈 수 없도록 만든 것입니다.
때문에 이를 해결하려면 list-users-with-group.sh
까지의 경로, cheongju-admin
, shell-scripts
, show-info
디렉토리를 외부 유저가 접근 가능토록 구성해야 합니다.
실제로 /usr/bin
에 존재하는 다른 심볼릭 링크 명령어를 보면 모두 완전히 접근 가능한 것을 볼 수 있습니다.
# 원본이 다른 곳에 존재하는 심볼릭 명령어 조회
$ ls -l /usr/bin | grep '\-> /' -m 5
lrwxrwxrwx 1 root root 25 Jan 13 12:47 animate -> /etc/alternatives/animate
lrwxrwxrwx 1 root root 29 Jan 13 12:47 animate-im6 -> /etc/alternatives/animate-im6
lrwxrwxrwx 1 root root 21 Sep 11 23:23 awk -> /etc/alternatives/awk
lrwxrwxrwx 1 root root 25 Jan 13 12:47 compare -> /etc/alternatives/compare
lrwxrwxrwx 1 root root 29 Jan 13 12:47 compare-im6 -> /etc/alternatives/compare-im6
# 재귀적 권환 조회
$ namei -l compare
# 모두 others 의 권한이 r-x 이상인 것을 확인 가능
f: compare
lrwxrwxrwx root root compare -> /etc/alternatives/compare
drwxr-xr-x root root /
drwxr-xr-x root root etc
drwxr-xr-x root root alternatives
lrwxrwxrwx root root compare -> /usr/bin/compare-im6.q16
drwxr-xr-x root root /
drwxr-xr-x root root usr
drwxr-xr-x root root bin
-rwxr-xr-x root root compare-im6.q16
이를 해결하는 방법은 크게 3 가지가 있습니다.
첫째로 이전에 언급했듯, list-users-with-group.sh
까지의 모든 경로를 외부 유저가 접근 가능토록 만드는 것입니다.
# 재귀적으로 chmod 실행
$ chmod o+rx -R /home/cheongju-admin
# 권환 확인
$ namei -l /usr/bin/list-users
f: /usr/bin/list-users
drwxr-xr-x root root /
drwxr-xr-x root root usr
drwxr-xr-x root root bin
lrwxrwxrwx root root list-users -> /home/cheongju-admin/shell-scripts/show-info/list-users-with-group.sh
drwxr-xr-x root root /
drwxr-xr-x root root home
drwxr-xr-x cheongju-admin cheongju-admin cheongju-admin
drwxr-xr-x cheongju-admin cheongju-admin shell-scripts
drwxr-xr-x cheongju-admin cheongju-admin show-info
-rwxr-xr-x cheongju-admin cheongju-admin list-users-with-group.sh
하지만 이는 새로운 스크립트를 만들때 마다 권한설정을 진행해야 하며, 무엇보다 유저의 home 디렉토리 권한을 개방 하는 단점이 존재합니다.
cheongju-admin
이 일반 계정이면 상관없을지 몰라도 저의 서버의 관리자 계정이기 때문에 이는 매우 적절치 못한 방법이라 판단했습니다.
둘째로 작성한 쉘 스크립트 자체를 /usr/bin
에 옮겨두는 것입니다.
# 파일 복사
$ sudo cp list-users-with-group.sh /usr/bin/list-users
# 권한 설정
$ sudo chmod o+rx /usr/bin/list-users
하지만 이는 스크립트를 수정할 때마다 cp
해야하므로 귀찮고 번거롭다 판다했습니다.
그래서 저는 세번째 방법으로 public 한 home 디렉토리를 구성 하는 것을 추천드립니다.
# 커스텀 쉘 스크립트 전용 디렉토리 구성
$ sudo mkdir /home/custom-shell-scripts
# 권한 open
$ sudo chmod o+rwx /home/custom-shell-scripts
# 파일 이동
$ mv list-users-with-group.sh /home/custom-shell-scripts/
# 심볼릭 명령어 생성
$ sudo ln -s /home/custom-shell-scripts/list-users-with-group.sh /usr/bin/list-users
# 재귀적 권한 조회
$ namei -l /usr/bin/list-users
f: /usr/bin/list-users
drwxr-xr-x root root /
drwxr-xr-x root root usr
drwxr-xr-x root root bin
lrwxrwxrwx root root list-users -> /home/custom-shell-scripts/list-users-with-group.sh
drwxr-xr-x root root /
drwxr-xr-x root root home
drwxr-xrwx root root custom-shell-scripts
-rwxr-xr-x cheongju-admin cheongju-admin list-users-with-group.sh
/home/custom-shell-scripts
를 오직 쉘 스크립 전용 디렉토리로 구성함으로서 앞서 말한 단점을 모두 극복할 수 있습니다.
민감한 admin
디렉토리 권한을 열지 않으며, 스크립을 수정할 때마다 cp
하는 번거로움도 사라졌습니다.
또한 custom-shell-scripts
디렉토리의 소유 그룹
을 따로 지정해 쉘 스크립 담당자
를 부여할 수도 있게 되었습니다.
이제 처음으로 돌아가 test-user-1
에서 list-users
를 실행하면 아래처럼 정상 작동함을 확인할 수 있습니다.
$ test-user-1@cheongju-raspi:~$ printf "command ---> $(which list-users)\n\n" && list-users
command ---> /usr/bin/list-users
USERNAME PRIORITY GROUP UID GID
nobody nogroup 65534 65534
cheongju-admin cheongju-admin 1000 1003
test-user-1 test-user-1 1001 1004
test-user-2 test-user-2 1002 1006
처음에는 심볼릭 명령어의 오해로부터 시작되었지만, 문제를 해결하고 개선하며 어떻게 구성하는 것이 linux 스러운지
느낄 수 있는 경험이었습니다.
앞으로도 home-server 를 구축하며 더욱 다양한 트러블 슈팅으로 뵙겠습니다.