상세 컨텐츠

본문 제목

시놀로지 DSM 6.2 PHP 7.4 → 8.4 업데이트, 도커로 올리고 AH00141 오류 해결까지

IT

by Admimistrator 2026. 10. 23. 10:03

본문

728x90
반응형

어제 PHP 취약점(PHP2Shell) 글을 쓰고 나서 회사 장비를 쭉 훑어봤는데요, 딱 하나가 걸렸어요. 사내 웹 앱 하나 돌리려고 시놀로지 NAS Web Station에 올려 둔 PHP 7.4. 공식 패치가 아예 없는 버전이죠.

"그럼 올리면 되지" 하고 패키지 센터 들어갔더니 업데이트 버튼이 없어요ㅋㅋ DSM 6.2에서 받을 수 있는 PHP가 7.4가 끝이거든요. DSM을 7.2로 올리면 되는데, 이 NAS가 파일 서버에 백업에 부서 공유 폴더까지 다 떠안고 있어서 영향도 검토가 끝나기 전엔 못 건드려요. 보안 패치하다 파일 서버 멈추면 그날로 제 자리가 위험하니까요.

그래서 DSM은 그대로 두고 PHP만 도커 컨테이너로 최신 버전을 쓰는 쪽으로 갔어요. 근데 공식 이미지 띄우자마자 바로 죽어 버리는 함정까지 만나서… 그 삽질 기록을 따라 하기 쉽게 정리해 둘게요.

이런 분께 맞는 방법이에요

상황 이 글 방법
DSM 6.2 쓰는 구형 시놀로지 (x86 모델) 끝까지 보세요
Web Station + PHP 7.4로 작은 웹 앱·관리 페이지를 돌리는 중 꼭 보세요
NAS가 인터넷이 막힌 내부망 이미지를 PC에서 받아 옮기는 방법까지 있어요
DSM 7.2로 바로 올릴 수 있는 환경 그냥 DSM 올리고 패키지 센터에서 PHP 8.x 쓰는 게 편해요
ARM 계열 모델 (j, se, 일부 play 등) 시놀로지 Docker 패키지 자체가 없어서 안 돼요

완성된 구성부터 보고 갈게요

컨테이너를 두 개 써요. 접속은 nginx가 받고, PHP 실행은 PHP-FPM 8.4가 해요. 앱 코드 폴더는 컨테이너에 읽기 전용으로 연결하고, 앱이 기록을 남기는 폴더만 쓰기를 열어 둬요. 혹시 뚫리더라도 코드를 바꿔치기할 수는 없게요.

처음부터 이렇게 짠 건 아니고, 원래는 공식 php 아파치 이미지 하나로 끝낼 생각이었어요. 그게 안 됐던 이유는 아래 함정 파트에서 볼게요.

1단계: Docker 패키지 설치

패키지 센터에서 Docker를 검색해서 설치하면 돼요. DSM 6용 마지막 버전은 Docker 20.10.3-0554예요. 내부망이라 패키지 센터가 안 열리면 시놀로지 공식 아카이브에서 Docker-x64-20.10.3-0554.spk를 받아서 패키지 센터 → 수동 설치로 올리면 돼요. 시놀로지 서명이 들어간 패키지라 설치할 때 DSM이 한 번 더 검증해 줘요.

설치하면 docker라는 공유 폴더가 새로 생기는데, 기본 권한이 관리자만이라 직원들 눈엔 안 보여요. 설정 파일이랑 이미지 tar는 여기 두면 깔끔해요.

2단계: 이미지 준비 (내부망이면 PC에서 받아 옮기기)

NAS가 인터넷이 되면 Docker → 레지스트리에서 검색해서 받으면 끝이에요. 저처럼 내부망이면 도커가 깔린 PC에서 받아서 tar로 저장해 옮겨야 해요. 버전은 꼭 태그로 박아 두세요. latest로 받으면 나중에 뭐가 들어갔는지 모르거든요.

📋 PC에서 이미지 받아 tar로 저장
docker pull php:8.4.27-fpm-alpine
docker pull nginx:1.30.6-alpine
docker save -o php-fpm.tar php:8.4.27-fpm-alpine
docker save -o nginx.tar nginx:1.30.6-alpine

tar 두 개를 File Station으로 docker 폴더에 올리고, Docker → 이미지 → 추가 → 파일에서 추가로 하나씩 불러오면 돼요. PHP는 PHP2Shell이 고쳐진 8.4.27 이상인지 꼭 확인하시고요.

함정: 공식 php:apache 이미지가 바로 죽어요

제일 먼저 해 본 게 php:8.4-apache 이미지 하나 띄우는 거였어요. 근데 컨테이너가 켜지자마자 꺼지고, 3초 뒤에 또 켜졌다 꺼지고를 무한 반복하더라고요. 로그엔 AH00141: Could not initialize random number generator만 계속 찍히고요.

원인은 DSM 6.2의 리눅스 커널이에요. 커널이 3.10이라 최신 아파치가 켜질 때 쓰는 난수 기능(getrandom)이 없어요. 재밌는 건 PHP는 같은 커널에서 멀쩡히 돈다는 거예요. PHP는 그 기능이 없으면 /dev/urandom으로 알아서 넘어가는데, 아파치는 그냥 포기해 버리는 거죠.

그래서 결론은 아파치만 빼면 된다였어요. nginx 컨테이너 + PHP-FPM 컨테이너로 나누니까 한 번에 떴어요. 둘 다 alpine 이미지라 합쳐서 60MB 정도밖에 안 되는 것도 덤이고요. 정품이든 아니든 DSM 6.2 쓰는 시놀로지면 똑같이 겪을 거예요.

3단계: 설정 파일 3개 만들기

docker 공유 폴더 안에 앱용 폴더를 하나 만들고(예: /docker/app/) 아래 파일 세 개를 넣어 둬요. 예시는 Web Station의 web 폴더 안에 app이라는 앱이 있다고 가정했어요. 경로랑 막을 파일 이름은 본인 앱에 맞게 바꾸세요.

① nginx 설정: .htaccess 대신 여기서 막아요

이거 진짜 중요해요. nginx는 .htaccess를 안 읽어요. 아파치 시절에 .htaccess로 막아 두던 설정 파일이나 기록 폴더가 있으면, nginx 설정에 따로 막는 규칙을 넣어야 해요. 안 그러면 주소만 치면 기록 파일이 그대로 열려요.

📋 /docker/app/nginx-default.conf
server {
    listen 80;
    server_tokens off;
    root /var/www/html;
    index index.php index.html;
    client_max_body_size 2m;
    autoindex off;

    # 숨김 파일, 기록 폴더, 설정 파일, 문서는 웹에서 막기
    location ~ /\. { deny all; }
    location ^~ /app/data/ { deny all; }
    location ~ ^/app/(config|lib)\.php$ { deny all; }
    location ~ \.md$ { deny all; }

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_pass php:9000;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }

    location / { try_files $uri $uri/ =404; }
}

② PHP-FPM 실행 계정: Web Station이 쓰던 http 계정으로

기존 앱이 남긴 기록 파일은 Web Station의 http 계정 소유예요. 컨테이너 PHP도 같은 계정 번호로 돌리면 파일 권한을 하나도 안 건드려도 돼요. 시놀로지 http 계정은 보통 1023번인데, SSH가 열려 있으면 id http로 확인할 수 있어요.

📋 /docker/app/zzz-pool.conf
[www]
user = 1023
group = 1023

③ PHP 설정: 버전 숨기고 에러는 로그로만

📋 /docker/app/zz-site.ini
expose_php = Off
display_errors = Off
log_errors = On
date.timezone = Asia/Seoul
session.cookie_httponly = 1
session.use_strict_mode = 1

4단계: 컨테이너 두 개 만들기

Docker → 이미지에서 이미지를 고르고 실행을 누르면 설정 마법사가 떠요. 고급 설정에서 아래처럼 넣으면 돼요. PHP 컨테이너를 먼저 만들어야 nginx에서 링크로 고를 수 있어요.

PHP 컨테이너 (이름 예: app-php, 이미지 php:8.4.27-fpm-alpine)

항목 NAS 쪽 컨테이너 쪽 권한
앱 코드 /web/app /var/www/html/app 읽기 전용
기록 폴더 /web/app/data /var/www/html/app/data 읽기/쓰기
PHP 설정 /docker/app/zz-site.ini /usr/local/etc/php/conf.d/zz-site.ini 읽기 전용
실행 계정 /docker/app/zzz-pool.conf /usr/local/etc/php-fpm.d/zzz-pool.conf 읽기 전용

포트는 열지 않아요. PHP-FPM은 nginx만 부르면 되니까요. 환경 변수에 TZ=Asia/Seoul 하나 넣고, 자동 재시작은 꼭 켜 두세요. NAS 재부팅되면 같이 올라와야 하니까요.

nginx 컨테이너 (이름 예: app-web, 이미지 nginx:1.30.6-alpine)

항목 설정
포트 로컬 8080 → 컨테이너 80
링크 app-php 컨테이너, 별칭 php (nginx 설정의 php:9000이 이 이름이에요)
앱 코드 /web/app → /var/www/html/app (읽기 전용)
nginx 설정 /docker/app/nginx-default.conf → /etc/nginx/conf.d/default.conf (읽기 전용)
자동 재시작 켜기

80번 포트를 안 쓰는 이유는, DSM 6.2에선 80번이 DSM 관리 화면 쪽으로 잡혀 있어서 컨테이너가 못 가져가요. 직원들한테는 :8080 붙은 주소로 안내하면 돼요.

5단계: 막힌 데가 진짜 막혔는지 확인

화면이 뜨는 것만 보고 끝내면 안 돼요. 막아 둔 파일이 정말 403이 나오는지까지 봐야 해요. 다른 PC에서 아래처럼 찍어 보면 숫자로 바로 나와요. 저는 기존 주소랑 새 주소 화면을 비교해서 글자 하나 안 다른 것까지 확인했어요.

📋 응답 코드 확인 (NAS주소는 본인 걸로)
curl -s -o /dev/null -w "%{http_code}\n" http://NAS주소:8080/app/
curl -s -o /dev/null -w "%{http_code}\n" http://NAS주소:8080/app/config.php
curl -s -o /dev/null -w "%{http_code}\n" http://NAS주소:8080/app/data/
curl -sI http://NAS주소:8080/app/ | grep -i -E "server|x-powered"

첫 줄은 200, 둘째·셋째 줄은 403이 나와야 정상이에요. 마지막 줄에 PHP 버전이 안 보이고 Server: nginx만 나오면 버전 숨기기도 된 거고요.

6단계: Web Station은 삭제 말고 중지

새 주소가 잘 돌면 패키지 센터에서 예전 PHP 7.4를 꺼요. 여기서 소소한 함정이 하나 더 있는데, Web Station부터 중지하려고 하면 "Apache HTTP Server 2.4가 쓰고 있다"며 거부당해요. Apache 2.4 → PHP 7.4 → Web Station 순서로 끄면 돼요.

삭제는 안 하고 중지만 해 두는 걸 추천해요. 혹시 문제 생기면 "실행" 한 번으로 되돌릴 수 있으니까요. 끄고 나면 예전 주소(80번)는 DSM 화면으로 넘어가서 PHP 7.4는 밖에서 더 이상 실행되지 않아요.

한줄 평

PHP는 최신이 됐지만 그 밑의 DSM 6.2랑 DSM 6용 Docker 패키지는 둘 다 업데이트가 끝난 버전이에요. 근본 해결은 결국 DSM 업그레이드나 장비 교체고요. 내부망이라 다음 PHP 패치 때도 이미지를 또 손으로 옮겨야 하는 것도 귀찮은 점이에요.

그래도 패치 방법이 아예 없는 PHP 7.4를 열어 두는 것보다는 비교도 안 되게 낫더라고요. 저는 옮긴 날짜, 버전, 확인 결과를 문서 한 장으로 남겨 뒀어요. 나중에 보안 점검 때 "그때 뭐 했냐" 물어보면 그거 내밀면 되니까요ㅎㅎ

참고한 곳

728x90
반응형

관련글 더보기