어제 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 아파치 이미지 하나로 끝낼 생각이었어요. 그게 안 됐던 이유는 아래 함정 파트에서 볼게요.
패키지 센터에서 Docker를 검색해서 설치하면 돼요. DSM 6용 마지막 버전은 Docker 20.10.3-0554예요. 내부망이라 패키지 센터가 안 열리면 시놀로지 공식 아카이브에서 Docker-x64-20.10.3-0554.spk를 받아서 패키지 센터 → 수동 설치로 올리면 돼요. 시놀로지 서명이 들어간 패키지라 설치할 때 DSM이 한 번 더 검증해 줘요.
설치하면 docker라는 공유 폴더가 새로 생기는데, 기본 권한이 관리자만이라 직원들 눈엔 안 보여요. 설정 파일이랑 이미지 tar는 여기 두면 깔끔해요.
NAS가 인터넷이 되면 Docker → 레지스트리에서 검색해서 받으면 끝이에요. 저처럼 내부망이면 도커가 깔린 PC에서 받아서 tar로 저장해 옮겨야 해요. 버전은 꼭 태그로 박아 두세요. latest로 받으면 나중에 뭐가 들어갔는지 모르거든요.
tar 두 개를 File Station으로 docker 폴더에 올리고, Docker → 이미지 → 추가 → 파일에서 추가로 하나씩 불러오면 돼요. PHP는 PHP2Shell이 고쳐진 8.4.27 이상인지 꼭 확인하시고요.

제일 먼저 해 본 게 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 쓰는 시놀로지면 똑같이 겪을 거예요.
docker 공유 폴더 안에 앱용 폴더를 하나 만들고(예: /docker/app/) 아래 파일 세 개를 넣어 둬요. 예시는 Web Station의 web 폴더 안에 app이라는 앱이 있다고 가정했어요. 경로랑 막을 파일 이름은 본인 앱에 맞게 바꾸세요.
이거 진짜 중요해요. nginx는 .htaccess를 안 읽어요. 아파치 시절에 .htaccess로 막아 두던 설정 파일이나 기록 폴더가 있으면, nginx 설정에 따로 막는 규칙을 넣어야 해요. 안 그러면 주소만 치면 기록 파일이 그대로 열려요.
기존 앱이 남긴 기록 파일은 Web Station의 http 계정 소유예요. 컨테이너 PHP도 같은 계정 번호로 돌리면 파일 권한을 하나도 안 건드려도 돼요. 시놀로지 http 계정은 보통 1023번인데, SSH가 열려 있으면 id http로 확인할 수 있어요.
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 붙은 주소로 안내하면 돼요.
화면이 뜨는 것만 보고 끝내면 안 돼요. 막아 둔 파일이 정말 403이 나오는지까지 봐야 해요. 다른 PC에서 아래처럼 찍어 보면 숫자로 바로 나와요. 저는 기존 주소랑 새 주소 화면을 비교해서 글자 하나 안 다른 것까지 확인했어요.
첫 줄은 200, 둘째·셋째 줄은 403이 나와야 정상이에요. 마지막 줄에 PHP 버전이 안 보이고 Server: nginx만 나오면 버전 숨기기도 된 거고요.
새 주소가 잘 돌면 패키지 센터에서 예전 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를 열어 두는 것보다는 비교도 안 되게 낫더라고요. 저는 옮긴 날짜, 버전, 확인 결과를 문서 한 장으로 남겨 뒀어요. 나중에 보안 점검 때 "그때 뭐 했냐" 물어보면 그거 내밀면 되니까요ㅎㅎ
| [IT]VLAN이란? 개념·사용 이유부터 access trunk 차이, tagged untagged까지 쉽게 정리 (0) | 2026.10.26 |
|---|---|
| 힉스필드 Kling 4.0 가격 비교, 영상 모델 12개 초당 크레딧과 무료로 되는 것·할인 기간 (0) | 2026.10.24 |
| PHP 취약점(PHP2Shell) 긴급 보안 업데이트, 내 PHP 버전 확인법과 대안 | CVE-2026-109618 (0) | 2026.10.22 |
| KB5127648 문제 정리, 지울까 말까 (0) | 2026.10.21 |
| 클로드 코드 리뷰 사용법, AI한테 내 코드 검사시켜 봤다 (0) | 2026.10.21 |