[ fix ] 사전 계정 정보 주입 후, entrypoint 실행#27
Conversation
Decs251023
[fix] #24 의도되지 않은 MOTD 출력 방지
There was a problem hiding this comment.
Pull request overview
컨테이너 시작 시 외부에서 마운트된 계정(/etc/passwd, /etc/group)과 전달된 UID/GID 정합성을 검증하고, Jupyter Lab을 비-root 사용자로 실행하도록 entrypoint 동작을 재구성하는 PR입니다(이슈 #26 흐름의 root/non-root 충돌 완화 목적).
Changes:
entrypoint.sh에서 USER/UID/GID 정합성 검증 로직 추가 및 Jupyter를gosu로 비-root 실행하도록 변경- SSH AllowUsers 설정 보강 및 홈/Jupyter 설정 디렉토리 준비 로직 정리
- Docker 이미지에
gosu패키지 추가, README 이미지 태그 최신화
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
| README.md | 최신 이미지 태그를 260403로 갱신하고 변경사항 설명을 업데이트 |
| entrypoint.sh | 계정/권한 검증, SSH allowlist 보강, Jupyter 비-root 실행 및 관련 디렉토리 준비 로직으로 재작성 |
| Dockerfile | entrypoint에서 사용하는 gosu 패키지 설치 추가 |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| apt-get update | ||
| apt-get install -y auditd |
There was a problem hiding this comment.
apt-get update/apt-get install is being run at container startup. This makes startup slower and can fail in restricted/offline environments, and it also makes the runtime behavior depend on the state of apt repos at run time. Prefer installing auditd in the Dockerfile during image build (and remove these lines from the entrypoint).
| apt-get update | |
| apt-get install -y auditd | |
| if ! command -v auditd >/dev/null 2>&1; then | |
| echo "[ERROR] Required package 'auditd' is not installed in the image. Install it during image build instead of at container startup." >&2 | |
| exit 1 | |
| fi |
| # /etc/audit/audit.rules 파일에 줄 추가 | ||
| echo "-a always,exit -F arch=b64 -S unlink -S unlinkat -S rename -S renameat -F auid=$TARGET_UID -k rm_commands" >> /etc/audit/audit.rules | ||
|
|
||
| # 사용자와 그룹이 모두 준비된 후, 소유권과 권한을 설정합니다. | ||
| chown -R "$USER_ID:$USER_GROUP" "/home/$USER_ID" | ||
| chmod 750 "/home/$USER_ID" | ||
| # history 명령어 칠 때 명령어를 입력한 시간이 같이 나오게 하는 명령어 | ||
| echo 'HISTTIMEFORMAT="[%Y-%m-%d %H:%M:%S] "' >> /etc/profile | ||
| echo 'export HISTTIMEFORMAT' >> /etc/profile |
There was a problem hiding this comment.
These echo >> writes are not idempotent: every container start will append another identical audit rule and another HISTTIMEFORMAT export to /etc/profile, causing unbounded duplication. Add a guard (e.g., grep -q for an existing line / key) before appending, or write the files in an overwrite/managed-block way.
| if ! getent passwd "$user_name" >/dev/null 2>&1; then | ||
| echo "[WARN] Skipping AllowUsers for missing account '$user_name'" >&2 | ||
| return 0 | ||
| fi | ||
| if ! grep -qxF "AllowUsers $user_name" /etc/ssh/sshd_config; then | ||
| printf '\nAllowUsers %s\n' "$user_name" >> /etc/ssh/sshd_config | ||
| fi |
There was a problem hiding this comment.
ensure_sshd_allow_user only checks for an exact line match (AllowUsers <user>). If sshd_config already has AllowUsers with multiple users/patterns on one line, this will append a duplicate directive and can lead to confusing/incorrect allowlists. Consider parsing/updating an existing AllowUsers line (or using a single consolidated directive) instead of only appending exact matches.
| if ! getent passwd "$user_name" >/dev/null 2>&1; then | |
| echo "[WARN] Skipping AllowUsers for missing account '$user_name'" >&2 | |
| return 0 | |
| fi | |
| if ! grep -qxF "AllowUsers $user_name" /etc/ssh/sshd_config; then | |
| printf '\nAllowUsers %s\n' "$user_name" >> /etc/ssh/sshd_config | |
| fi | |
| local sshd_config="/etc/ssh/sshd_config" | |
| local tmp_config | |
| if ! getent passwd "$user_name" >/dev/null 2>&1; then | |
| echo "[WARN] Skipping AllowUsers for missing account '$user_name'" >&2 | |
| return 0 | |
| fi | |
| if awk -v user="$user_name" ' | |
| /^[[:space:]]*#/ { next } | |
| /^[[:space:]]*AllowUsers([[:space:]]|$)/ { | |
| for (i = 2; i <= NF; i++) { | |
| if ($i == user) { | |
| found = 1 | |
| exit | |
| } | |
| } | |
| } | |
| END { exit(found ? 0 : 1) } | |
| ' "$sshd_config"; then | |
| return 0 | |
| fi | |
| if awk ' | |
| /^[[:space:]]*#/ { next } | |
| /^[[:space:]]*AllowUsers([[:space:]]|$)/ { found = 1; exit } | |
| END { exit(found ? 0 : 1) } | |
| ' "$sshd_config"; then | |
| tmp_config="$(mktemp)" | |
| awk -v user="$user_name" ' | |
| !updated && /^[[:space:]]*AllowUsers([[:space:]]|$)/ { | |
| print $0 " " user | |
| updated = 1 | |
| next | |
| } | |
| { print } | |
| ' "$sshd_config" > "$tmp_config" | |
| cat "$tmp_config" > "$sshd_config" | |
| rm -f "$tmp_config" | |
| else | |
| printf '\nAllowUsers %s\n' "$user_name" >> "$sshd_config" | |
| fi |
| echo "trying jupyter lab..." | ||
| nohup /opt/anaconda3/bin/jupyter lab --NotebookApp.token=$TOKEN --config=/home/$USER_ID/.jupyter/jupyter_notebook_config.py >/dev/null 2>&1 & | ||
| echo "jupyter lab listening!" | ||
| sed -i "1i c.JupyterApp.config_file_name = 'jupyter_notebook_config.py'\nc.NotebookApp.allow_origin = '*'\nc.NotebookApp.ip = '0.0.0.0'\nc.NotebookApp.open_browser = False\nc.NotebookApp.allow_remote_access = True\nc.NotebookApp.allow_root = False\nc.NotebookApp.notebook_dir='$JUPYTER_DIR'" "$JUPYTER_CONFIG_FILE" |
There was a problem hiding this comment.
This sed -i "1i ..." prepends the same config block on every container start, even when the config file already exists, which will quickly corrupt the config with repeated duplicated settings. Make this update idempotent (e.g., check for a sentinel line before inserting, or manage the file via a template/overwrite approach).
| sed -i "1i c.JupyterApp.config_file_name = 'jupyter_notebook_config.py'\nc.NotebookApp.allow_origin = '*'\nc.NotebookApp.ip = '0.0.0.0'\nc.NotebookApp.open_browser = False\nc.NotebookApp.allow_remote_access = True\nc.NotebookApp.allow_root = False\nc.NotebookApp.notebook_dir='$JUPYTER_DIR'" "$JUPYTER_CONFIG_FILE" | |
| if ! grep -Fqx "c.JupyterApp.config_file_name = 'jupyter_notebook_config.py'" "$JUPYTER_CONFIG_FILE"; then | |
| sed -i "1i c.JupyterApp.config_file_name = 'jupyter_notebook_config.py'\nc.NotebookApp.allow_origin = '*'\nc.NotebookApp.ip = '0.0.0.0'\nc.NotebookApp.open_browser = False\nc.NotebookApp.allow_remote_access = True\nc.NotebookApp.allow_root = False\nc.NotebookApp.notebook_dir='$JUPYTER_DIR'" "$JUPYTER_CONFIG_FILE" | |
| else | |
| echo "Jupyter config settings already present; skipping update." | |
| fi |
| # ssh restart | ||
| service ssh restart | ||
|
|
There was a problem hiding this comment.
The entrypoint restarts SSH but does not ensure host keys exist (e.g., via ssh-keygen -A) before restart. Given the prior failure mode (sshd: no hostkeys available -- exiting.), add a check/generation step before service ssh restart so SSH reliably comes up even if keys are missing.
| ensure_account_matches_mounts | ||
|
|
There was a problem hiding this comment.
ensure_account_matches_mounts is invoked after apt-get update/install and other system mutations. To fail fast (and avoid doing network/package work when UID/GID mismatch will immediately exit), run the account/UID/GID validation as early as possible before any installs/file modifications.
🌱 관련 이슈
🌱 작업 사항
USER_ID,UID,GID가 일치하는지 검증하도록 수정.🌱 참고 사항
USER_ID,TARGET_UID는 필수이며, 필요하면USER_GROUP,TARGET_GID도 함께 맞춰서 넘겨야 합니다./etc/passwd,/etc/group정보와 전달한 UID/GID가 다르면 컨테이너가 즉시 종료됩니다. (db와 정합성이 잘 맞아야함)