Files
ilnmors-homelab/docs/archives/2025-06/on-premise.txt
2026-03-15 04:41:02 +09:00

12151 lines
225 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
개인 활용 목적의 On-premise 홈 네트워크 환경 구축 프로젝트
이 프로젝트의 목표는 개인이 활용하기 위한 On-premise 홈 네트워크 환경을 집에서 사용 가능한 장비와 네트워크 구조로 구현하면서 다양한 기술 요소(네트워크, OS, 가상화와 컨테이너, 자원 관리, 데이터 베이스, 상용 서비스 운영, 보안)이 네트워크 상에서 어떻게 하나의 시스템을 이루고 작동하는지에 대하여 깊이 이해하고, 이를 체득하는 것에 있습니다.
1. 프로젝트 개요
1.1. 동기 및 목표
동기
본 프로젝트의 시작은 개인적인 사진 및 데이터를 저장하기 위한 방법을 찾는 것이었습니다. 처음 고려한 방법은 클라우드 서비스를 사용하는 것이었습니다. 하지만 클라우드를 사용하는 방식은 클라우드 업체에서 소유한 데이터에 언제나 접근할 수 있다는 위험을 가지고 있습니다. 이는 소유한 데이터에 대하여 완전한 통제가 불가능함과 동시에 온전한 권리의 부재를 의미합니다. 따라서 이러한 문제를 해결하기 위해 처음 선택한 방식은 NAS(Network Attached Storage)를 구축하는 것이었습니다. 하지만 NAS를 구축한 이후 이 방식 역시 설정과 사용에 있어 추상화된 부분이 많아 해당 문제에서 자유로울 수 없었습니다. 데이터를 사용하며 자유롭게 설정하고 다루지 못한다면 이는 클라우드 환경과 다를 것 없거나 혹은 더 위험할 수 있다 생각했습니다.
이에 단순한 NAS 구성을 벗어나기로 결정하였습니다. 개인 데이터의 모든 권한과 책임을 가지고 사용 가능한 On-premise 환경을 구축한다는 목표를 세웠습니다. 개인이 구현한 On-premise 환경이 상용 서비스 수준의 가용성과 안정성을 담보하기는 힘들 것입니다. 하지만 본 프로젝트를 통하여 직접 On-premise 환경을 구축하면서 현대 IT(Information Technology) 인프라의 필수 구성요소들과 상호작용, 또 이러한 환경 자체에 대한 이해와 경험을 하고자 합니다. 이를 통해 IT 서비스와 네트워크 환경을 이용함에 있어 스스로 가장 약한 고리가 되지 않는 것 역시 목표입니다.
과거 군 복무를 하며 분산된 지역망과 삼중화된 물리적 HA(High Availability)를 갖춘 대규모 On-premise 환경의 전장망을 관리하였습니다. 당시의 경험 속 이해하기 힘들었던 복잡한 매뉴얼과 SoC(Separation of Concerns)에 맞춰 설계된 구조들, LTO(Linear Tape Open) 기반의 자동 백업 시스템, 체계적인 복구 시나리오를 직접 운영했던 경험을 본 프로젝트에 적용하려 합니다. 나아가 단순히 명령어를 입력하고 매뉴얼 중심의 운용을 했던 과거의 경험에서 벗어나 아래와 같이 목표를 설정하고 이를 성취하려 합니다.
목표
구현 목표
대상
2~5명의 소규모 인원
요구 기능
IT 서비스 제공을 위하여 다음과 같은 기능을 가진 서버 및 네트워크 운영
방화벽
Local CA(Certificate Authority)
Local DNS(Domain Name System)
Proxy
DB(Database)
파일, 미디어, 어플리케이션 서버 등의 개인 클라우드 환경 구축
블로그, wiki 등의 웹 서비스 호스팅
IPS/IDS(Intrusion Prevention/Detection System), Kali 등을 통한 정보보안 실습 환경 제공
단, 다음 기능들은 상용 서비스급 가용성 및 기능을 확보할 필요가 없으며, 학습 및 경험을 우선
시스템 아키텍쳐 설계 및 기술 통합
제한된 물리적 환경 및 하드웨어/네트워크 제약 사항 속에서 주어진 요구 사항을 최선의 방식으로 구현합니다. 필수 요구 사항을 구현하며 과도한 투자를 지양하고 효율적인 자원 배분을 하는 능력을 기르고자 합니다. 또한, 구현 전 발생 가능한 문제점을 사전에 발견하고, 이러한 문제점을 해결하기 위하여 어떤 기술을 선택하고 조합하여 설계하고 구현할 것인지에 대한 능력을 기르고자 합니다.
추상적 개념의 구체화 및 실무 적용
OS(Operating System), 네트워크, 정보보안 등 여러 분야 속 추상적이고 개별적인 개념들을 조합하여 실제로 구현합니다. 이 과정 속에서 각 개념들의 연결과 동작 원리를 자세하게 파악합니다. 이를 통해 이론적인 지식을 어떻게 현실에서 구현하고 적용할 수 있는지에 대한 감각을 익히려 합니다. 모든 기술 스택을 전부 직접 구현하는 것이 아닌 인프라의 핵심 요소와 통제하에 위임 가능한 요소를 분리합니다. 필수적인 부분에 집중하여 주도권 있는 위임을 통해 프로젝트 전반의 기술 품질과 유지 관리성을 높이는 분업 환경을 구현하고자 합니다.
문제 해결 및 제약 조건의 극복
프로젝트를 진행하면서 사전에 고려하지 못한 다양한 기술적, 환경적 문제들과 마주할 것입니다. 이러한 문제들이 발생하였을 때 당황하지 않고 정확하게 분석하고 기술적인 근거에 기반하여 해결책을 찾아 적용하는 능력을 기르고자 합니다.
체계적인 문서화 및 지식 관리
문서화는 모든 프로젝트 진행에 있어 가장 큰 의미를 갖습니다. 프로젝트 진행 중 모든 과정(계획, 설계 및 근거, 구현 절차 및 로그, 설정, 문제 해결 사항에 대한 기록 등)을 BookStack을 활용하여 체계적으로 문서화를 함으로 단순한 구축 경험을 넘어 지식과 경험을 내재화 하고 관리 및 공유 가능한 자산으로 만드는 능력을 기르고자 합니다.
결론
본 프로젝트는 처음부터 완벽한 인프라 설계와 오류 및 시행 착오 없는 구축을 목표로 하지 않습니다. 프로젝트를 진행하며 얻고자 하는 궁극적인 가치는 위와 같은 목표를 가지고 단순하게 개인이 사용 가능한 IT 환경을 구축하는 것과 기술을 배우는 것에 그치지 않습니다. 프로젝트 자체와 이를 통해 경험하는 실패와 시행 착오 역시 훌륭한 학습이 될 것입니다. 특히 문제 해결을 위한 사고력과 실행력을 기르고 작은 범위에서라도 안정적인 서비스를 설계 통제 하며 이를 기반으로 안정적인 서비스 운영을 할 수 있는 수준에 도달하는 것이 궁극적인 가치이자 목표입니다.
2025-05-13 - 초안 작성
2025-05-15 - 태그 수정 및 동기와 결론, 오타 수정
2025-05-16 - 동기와 결론 수정
2025-05-29 - 결론 수정/요구 기능 제약 조건 구체화
2025-06-14 - 부록 1.1. 문서 작성 가이드라인 에 따라 본문 재작성
2025-06-20 - 날짜 표기 변경
1.2. 하드웨어 구성
목표
프로젝트를 진행하기 위하여 구매한 하드웨어를 목록으로 정리합니다.
하드웨어 구성
라우터/L2 스위치
메인 라우터(ipTime T5004; T5004)
가격 42,900 KRW
외부 Gateway 역할 수행
메인 DHCP(Dynamic Host Configuration Protocol) 서버 역할 수행
EasyMesh controller 역할 수행
1차 방화벽 기능 수행
VLAN 기능 미지원
OpenWRT 지원 기종으로 추후 프로젝트시 VLAN(Virtual Local Area Network; 802.1Q) 기능 지원 기기로 사용 가능
서브 라우터(ipTime AX6000M; AX6000M)
가격 99,000 KRW
무선 AP(Access Point)
EasyMesh node 역할 수행
VLAN 기능 미지원
스위치(ipTime H8005G-IGMP; H8005G)
가격 17,900 KRW
5포트 L2 스위치
VLAN 기능 미지원
추후 OpenWRT 업데이트 프로젝트 진행 시 T5004로 변경 예정
서버
서버 하드웨어(Aoostar WTR Pro N150; WTR Pro)
가격 254,900 KRW
Intel N150 4Core/4Thread/TDP 6W
DDR4 SO-DIMM slot x1
M.2 NVMe slot x1
SATA 3.5" slot x4
2.5Gbps NIC(Network Interface Card) x2
Hypervisor OS 설치를 통해 여러 역할을 수행하는 VM 및 LXC가 논리적으로 격리되어 각 역할 수행
RAM(Samsung DDR4 SO-DIMM 32GB)
가격 106,900 KRW
SSD(Samsung M.2 NVMe SSD 1TB 980PRO)
기존 사용하던 SSD로 프로젝트를 위하여 따로 구매 하지 않음
가격 276,000 KRW(2022/04/22 기준, 현재 최저가 184,000 KWR)
HDD(Seagate/HGST 3.5" HDD 2TB x4)
가격 200,000 KRW
SATA 6Gbps
외부 백업 서버
서버 하드웨어(Synology DS124; DS124)
가격 233,000KRW
Realtek RTD1619B 4Core/4Thread
DDR4 1GB RAM x1
SATA 3.5" x1
1Gbps NIC x1
홈 서버가 구축되기 전 까지 홈 서버 역할 수행
홈 서버 구축 이후로는 홈 서버의 외부 백업 서버 역할 수행
HDD(Toshiba 3.5" HDD 4TB)
가격 55,000KRW
4TB SATA 3.3Gbps
2025-05-13 - 초안 작성, 각 서버의 역할 추가
2025-05-15 - 태그 수정
2025-05-25 - 네트워크 장비 VLAN 미지원 명시
2025-05-29 - SSD 변경(256GB Hynix > 1TB Samsung)
2025-05-30 - T5004가 OpenWRT를 지원하는 사실 명시
2025-06-14 - 부록 1.1. 문서 작성 가이드라인 에 따라 본문 재작성
2025-06-20 - 날짜 표기 변경
1.3. 목표 아키텍처
목표
현재 집안의 물리적인 구조를 파악합니다.
프로젝트를 통해 구현하고자 하는 서비스를 포함한 목표 아키텍처를 정의합니다.
목표 아키텍처 구조
물리 구조도
물리적 네트워크 구조 설명
WAN(Wide Area Network) - ISP(Internet Service Provider) Modem - T5004로 WAN과 LAN(Local Area Network)이 구별
통신 단자함에는 전원부가 존재하지 않으며 PoE(Power over Ethernet; 802.3af, 802.3at, 802.3bt)로 전원을 공급
T5004는 LAN-WAN 통신의 메인 Gateway로 DHCP 서버 역할 수행, DHCP Relay 및 VLAN 지원하지 않음
AX6000M은 AP로 허브 모드로 작동하며, 외부 백업 서버(NAS)와 연결, VLAN 지원하지 않음
H5008G는 L2 허브로 WTR Pro의 NIC0, NIC1과 연결, VLAN 지원하지 않음
집안의 물리적인 제약 사항들로 인하여 통신 단자함에 새로운 기기 추가의 어려움이 있습니다. 또한 WTR Pro 외부 클라이언트에 대하여 자유로운 VLAN 설정이 불가능합니다.
아키텍처 구조도
아키텍처 구조 설명
사용자
2~5명의 소규모 인원
네트워크
WAN
Public DNS를 통하여 들어오는 Well-Known DNS 패킷만을 허용(DDoS; Distributed Denial of Service 방지)
Public IP(Internet Protocol) adress를 통한 직접 접속 차단(Port Scan 방지)
https(443) 및 VPN(Virtual Private Network) 포트만 외부 개방
WAN에서 T5004로 접속 차단, UPnP(Universal Plug and Play) 등의 취약 기능 전체 차단, 강력한 비밀번호 사용, 최신 펌웨어 유지
Cloudflare API를 통한 DDNS(Dynamic Domain Name System) 자동화
Cloudflare를 통한 공인 SSL/TLS(Secure Sockets Layer/Transport Layer Security) 인증서 발급 및 자동 갱신
T5004는 Untagged VLAN을 위한 DHCP 서버 및 외부 Gateway 역할 수행
LAN
OPNsense가 LAN(VLAN 포함)의 내부 라우터로 작동
WTR Pro의 NIC0과 NIC1의 역할을 분리
NIC0은 vmbr0, NIC1은 vmbr1 할당
vmbr0: OPNsense의 vtnet0과(T5004와 연결되어 WAN 통신)
vmbr1: OPNsense의 vtnet1과 연결(Trunk mode, Untagged VLAN, VLAN2,3,4,10 연결되어 LAN 통신, PVE Web UI 연결)
모든 LAN(VLAN 포함)의 기본 Gateway는 OPNsense(T5004의 DHCP 서버 기본 Gateway 포함)
OPNsense의 Gateway는 T5004로 고정 할당
T5004는 NAT가 허용된 모든 패킷을 OPNsense로 포트 포워딩
FreeIPA를 통한 LDAP(Lightweight Directory Access Protocol)/SSO(Single Sign ON) 및 Kerberos, Local CA, Local DNS(Split Horizon DNS) 구현
AdGuard Home LXC를 통하여 광고 차단 필터를 적용한 DNS 서비스 구현
AdGuard Home > FreeIPA DNS > Cloudflare DNS(DoH; DNS over Https 사용)
Zero Trust 원칙에 따라 클라이언트 간 iptables 등 Local 방화벽 설정을 통한 통신 제어 강화
PVE(Proxmox Virtual Environment) 내부의 vmbr(Linux Bridge) 기능을 활용하여 VM(Virtual Machine)/LXC(LinuX Container)/Docker를 OPNsense와 연결
보안 실습을 위한 Kali와 실습 Client는 별도의 VLAN을 통하여 격리
각 서비스 별 VLAN을 나누어 IP 할당
Untagged VLAN(192.168.0.0/24): Untagged VLAN으로 외부 클라이언트 망(PC, DS124, Mobile Devices)
주요 Clients(PC, DS124 등) Static IP 할당
Mobile Devices 및 IoT 기기는 Dynamic IP 할당
VLAN2(192.168.2.0/24): PVE 내부 서버 및 서비스망
VLAN3(192.168.3.0/24): Kali 망
VLAN4(192.168.4.0/24): 정보보안 실습 Client 망
VLAN10(10.0.0.0/24): VPN Clients 망
VLAN 간 통신 제어
VLAN 간 통신시 접근 가능 IP 제한
외부 통신시 VPN 이용한 접근만 가능
특정 서비스(Reverse Proxy 등)은 서비스 목적에 따라 필요한 범위 내에서 접근 허용
WTR Pro
기본 사항
PVE를 통하여 여러 기능의 서버를 중요도와 특성에 맞게 각각 VM 및 LXC로 논리적 분리 구축
모든 LXC는 기본적으로 root 권한이 포함되지 않은 Unprivileged LXC로 구축
LXC 상에서 Host의 Root 권한이 필요한 Docker 서비스를 운영하지 않음(Docker 사용 시 VM 활용)
NIC0, NIC1은 OPNsense에 vtnet으로 할당
NIC0: WAN 통신 담당
NIC1: LAN(VLAN) 통신 담당
각 서비스 별 대표 UID/GID Matrix를 작성(FreeIPA LDAP/SSO 전용 UID 대역과 분리)
OPNsense 및 FreeIPA가 SPOF(Single Point of Failure)가 되므로 자동 복구 절차 및 수동 복구 절차 수립(자동화 모니터링/복구 스크립트 작성 후 활용)
각 VM과 LXC에 지정된 리소스는 모니터링 하며 세부 조정
로깅 및 모니터링과 서버 설정
로깅 및 모니터링
Prometheus와 Grafana를 통한 모니터링 및 시각화
Loki와 Promtail을 활용한 log 중앙 집중
Alertmanager를 통한 특이 log 알림
Ansible + Semaphore(Web UI)를 활용한 서버 설정 자동화
Gitea를 활용한 설정 파일 아카이빙
추후 n8n과 연동하여 모든 과정 자동화
파일 및 백업
Btrfs(B-Tree File System)의 CoW(Copy on Write) 기능으로 인한 외부 단편화 방지를 위하여 파일 서버 상 3개월 마다 한 번씩 btrfs filesystem defragment 명령어 수행
 시스템
Btrfs 스냅샷을 활용하여 하루 1회, 15일간 스냅샷 보관
OS 파티션 및 VM/LXC 저장소 한 달 1회 백업(설정 변경 시 이상 없는 경우 즉시 백업)하여 파일 서버에 저장, 최근 3개 버전 유지
 어플리케이션
OPNsense/FreeIPA 설정, DB, docker-compose.yml 등의 주요 설정 및 데이터 백업하여 Gitea에 저장
각 서버의 로그 보관 기간 7일, 이후 파일 서버로 이관 후 3 개월 보관 후 파기(일반 삭제)
데이터
파일 서버의 데이터(NFS 공유 데이터 및 백업 데이터)를 DS124 및 클라우드에 백업 (Kopia를 통한 백업 기능 활용, E2EE 기능 적용)
추후 데이터 용량이 커지면 Amazon Glacier 서비스 이용 고려
실제 환경 구축 후 더욱 구체적이고 실질적인 백업 전략 수립
필요 서비스(VM/LXC/Docker)
OPNsense VM
Rocky Linux - FreeIPA VM
AdGuard Home LXC
Monitoring Server LXC
Proxy Server LXC
File Server VM - Docker
Portainer Agent - 도커 통합 관리
Seafile - 파일 관리 및 동기화
Kopia - 백업 및 아카이빙
DB Server VM - Docker
Portainer Agent - 도커 통합 관리
Maria DB/Postgre SQL - DBMS
Web Server/WAS VM - Docker
Portainer Agent - 도커 통합 관리
Homepage - 홈페이지
Ghost - 블로그
Bookstack - wiki, 노트
Gitea - Git 버전 관리
Application Server VM - Docker
Portainer Server - 도커 통합 관리
Uptime Kuma - 도커 서비스  모니터링
Diun - 도커 업데이트 관리
n8n - 워크 플로우 자동화
Redis - 캐시 DB
Code-Server - 웹 코드 에디터
Immich - 사진 및 동영상 관리
PeerTube - 동영상 컨텐츠 관리
Navidrome - 음악 관리
Vaultwarden - 사용자 암호 관리
Infisical - DB, API key 등 관리
Matrix-Synapse/element - 채팅/통화/영상 통화
Radicale - 캘린더 및 주소록
Vikunja - To-Do 리스트
Firefly III - 가계부
Paperless-ngx - 문서 관리
Snipe-IT - ITAM; IT 자산 관리(하드웨어, 소프트웨어 라이센스 등)
Calibre-web - 전자책 관리
Kmoga - 만화책 관리
Audiobookshelf - 오디오북 관리
Kali and practice client VM
가용성 확보를 위한 리소스 및 복구 우선 순위
네트워크 및 인증 핵심 기능을 우선 복구하여 서비스 의존성 문제 최소화
PVE > OPNsense > FreeIPA > Proxy Server > DB Server > File Server > etc
DS124
구축 초기에는 Docker 등의 필요 서비스 임시 호스팅
구축 중반에는 초기 역할을 마이그레이션 하서 백업 서버 역할 수행
구축 이후에는 완전한 백업 서버로 역할 전환
결론
해당 목표 아키텍처는 프로젝트가 진행되며 변경될 수 있습니다. 목표 아키텍처를 계속해서 발전시키며 구현해 나갈 것입니다.
2025-05-13 - 초안 작성
2025-05-15 - 태그 수정 및 최종 목표 아키텍처 수정, 이상적인 아키텍쳐 항목 작성
2025-05-24 - 최종 목표 아키텍쳐 및 이상적인 구조의 아키텍쳐 구조도 수정
2025-05-25 - 최종 목표 아키텍쳐 및 이상적인 구조의 아키텍쳐 구조도 수정/VLAN, vtnet, vmbr 관련 내용 추가
2025-05-28 - DB server LXC를 VM으로 변경/로깅 및 모니터링 전략 추가/ 백업 전략 구체화
2025-05-29 - T5004 보안 대책 추가/Ansible을 통한 설정 자동화 및 조각모음 대책 추가
2025-05-30 - 네트워크 설계 VLAN 반영 수정/OpenWRT 적용 시 VLAN, HA 가능 구조 적용
2025-05-31 - VLAN/HA 추가 구조를 1.4. 추가 프로젝트 계획 으로 옮김
2025-06-02 - vtnet0과 vtnet1의 역할 명확화
2025-06-15 - 부록 1.1. 문서 작성 가이드라인 에 따라 본문 재작성
2025-06-16 - AdGuard Home LXC 내용 추가
2025-06-19 - VPN Clients 망 추가
2025-06-20 - 날짜 표기 변경
2025-06-23 - VLAN 표기 방식 수정
1.4. 추가 프로젝트 계획
목표
프로젝트의 목표 아키텍처 구현한 이후 이어서 진행할 추가 프로젝트의 청사진을 정의합니다.
OpenWRT 적용 계획
목표
현 프로젝트에서 메인 라우터인 T5004는 VLAN을 지원하지 않아 WTR Pro 외부 클라이언트에 대한 VLAN 구성이 어렵습니다. 이때 T5004는 오픈 소스 라우터 OS인 OpenWRT를 설치할 수 있습니다(비고: OpenWRT 지원 목록 - ipTime T5004 ). 따라서 T5004에 OpenWRT를 설치 시 WTR Pro 외부의 클라이언트 또한 VLAN으로 L3 망 격리가 가능해져 보다 정밀한 네트워크 관리가 가능합니다.
OpenWRT 적용시 구조 및 아키텍처
현 프로젝트의 목표 아키텍처를 구현한 후 안정적인 운영을 할 수 있을 때 T5004에 OpenWRT를 설치하여 구현하고자 하는 아키텍처입니다.
물리 구조도
물리적 네트워크 구조 설명 (기존 구조에서 수정된 부분)
WAN - ISP Modem - T5004(OpenWRT) - T5004(OpenWRT) - OPNsense 구조
각 방의 랜 포트가 하나 뿐이고, 회선도 하나 뿐인 물리적 한계로 인한 타협사항
두 장비 모두 L2 관리형 스위치로 연결된 포트에 VLAN10(WAN Paththrough 망)을 태깅
이 계획에서는 WAN Paththrough(VLAN을 통한 WAN 트래픽 격리 전달)를 통해, OPNsense는 공인 IP 그대로 전달 받음
WTR Pro가 주 라우터 및 IPS/IDS로 작동
AX6000M은 무선 AP로 허브 모드로 작동하며, 외부 백업 서버(NAS)와 연결, VLAN 지원하지 않음
H5008G가 T5004(OpenWRT)로 대체
아키텍처 구조도 (기존 구조에서 수정된 부분)
아키텍처 구조 설명 (기존 아키텍처에서 수정된 부분)
사용자
2~5명
네트워크
WAN
외부 DNS를 통한 Well-Known DNS의 패킷만을 허용(DDoS 방지/ OPNsense에서 처리)
T5004는 WAN Paththrough가 가능한 L2 관리형 스위치로 작동
모든 라우팅 및 IPS/IDS, Gateway는 OPNsense에서 처리
LAN
VLAN을 통한 엄격한 내부 망분리(DMZ는 관제/CERT 운영 없이는 비효율적)
각 장비 별(네트워크 장비, 서버, 주요 클라이언트) 등에 대한 고정 IP 설정
관리할 고정 IP가 적으므로, MAC기반 DHCP 예약이 아닌 IP 매트릭스를 운영하며 수동으로 정의 
VLAN100(192.168.10.0/24 ): WAN Paththrough 망
VLAN2(192.168.2.0/24): 클라이언트(PC 등) 망
VLAN3(192.168.3.0/24): Hypervisor 내부 서버 및 서비스 망
VLAN4(192.168.4.0/24): Mobile Devices 및 Guest 망
VLAN5(192.168.5.0/24): Kali 망
VLAN6(192.168.6.0/24): Kali를 통한 정보보안 실습망
VLAN10(10.0.0.0/24): VPN Clients 망
HA를 위한 계획
목표
필요 선행 프로젝트: OpenWRT 적용
현 프로젝트의 목표는 상용 서비스 만큼의 가용성을 확보하는 것은 아닙니다. 이는 많은 시간과 자원을 투자하여야 하여야 하며, 현실적인 제약 조건을 고려하여야 합니다. 하지만 HA는 현대 IT 인프라의 중요한 요소로 이를 경험 및 학습을 하는 것은 큰 의미가 있습니다. 따라서 현재 가장 큰 SPOF인 OPNsense와 FreeIPA를 HA 구성하여 실제 작동을 경험하고 테스트 해보고자 합니다. 
HA 적용시 구조 및 아키텍처
물리 구조도
물리적 네트워크 구조 설명 (OpenWRT 계획 구조에서 수정된 부분)
WAN - ISP Modem - T5004(OpenWRT) - T5004(OpenWRT) - OPNsense 구조
각 방의 랜 포트가 하나 뿐이고, 회선도 하나 뿐인 물리적 한계로 인한 타협사항
두 장비 모두 L2 관리형 스위치로 연결된 포트에 VLAN100(WAN Paththrough 망)을 태깅
이 계획에서는 WAN Paththrough(VLAN을 통한 WAN 트래픽 격리 전달)를 통해, OPNsense는 공인 IP 그대로 전달 받음
Mini PC가 주 라우터 및 IPS/IDS로 작동
AX6000M은 무선 AP로 허브 모드로 작동하며, 외부 백업 서버(NAS)와 연결, VLAN 지원하지 않음
H5008G가 T5004(OpenWRT)로 대체
아키텍처 구조도
아키텍처 구조 설명 (OpenWRT 계획 구조에서 수정된 부분)
사용자
2~5명
네트워크
WAN
외부 DNS를 통한 Well-Known DNS의 패킷만을 허용(DDoS 방지/ OPNsense에서 처리)
T5004는 WAN Paththrough가 가능한 L2 관리형 스위치로 작동
모든 라우팅 및 IPS/IDS, Gateway는 OPNsense main에서 처리(OPNsense HA도 stanby로 설정)
LAN
VLAN을 통한 엄격한 내부 망분리(DMZ는 관제/CERT 운영 없이는 비효율적)
각 OPNsense의 vtnet0은 Trunk로 WAN(VLAN100) 및 HA LAN(VLAN200)과 연결
각 OPNsense의 vtnet1은 Trunk로 VLAN(VLAN2, 3, 4, 5, 6, 10) 과 연결
각 장비 별(네트워크 장비, 서버, 주요 클라이언트) 등에 대한 고정 IP 설정
관리할 고정 IP가 적으므로, MAC기반 DHCP 예약이 아닌 IP 매트릭스를 운영하며 수동으로 정의
VLAN100(192.168.10.0/24 ): WAN Paththrough 망
VLAN200(192.168.20.0/24): HA 구성 및 관리 망
VLAN2(192.168.2.0/24): 클라이언트(PC 등) 망
VLAN3(192.168.3.0/24): Hypervisor 내부 서버 및 서비스 망
VLAN4(192.168.4.0/24): Mobile Devices 및 Guest 망
VLAN5(192.168.5.0/24): Kali 망
VLAN6(192.168.6.0/24): Kali를 통한 정보보안 실습망
VLAN10(10.0.0.0/24): VPN Clients 망
WTR Pro, mini PC
기본 사항
OPNsense 및 FreeIPA가 SPOF(단일 실패 지점)가 될 수 있으므로 물리적인 HA 구성
각 서버의 리소스 부하를 고려하여 메인 서버를 mini PC, 스탠바이 서버를 WTR Pro에 구성
각 VM 및 LXC에 지정된 리소스는 모니터링 하면서 유동적으로 변경
필요 서비스(VM/LXC/Docker)
OPNsense VM(HA 구성)
FreeIPA VM(HA 구성)
AdGuard Home LXC
Monitoring Server LXC
Proxy Server LXC(HA 구성)
File Server VM
DB Server VM
Web Server/WAS VM
Application Server VM
Kali and practice client VM
가용성 확보를 위한 복구 우선 순위
네트워크 및 인증 핵심 기능을 우선 복구하여 서비스 의존성 문제 최소화
PVE > OPNsense > FreeIPA >Proxy Server > DB Server > File Server > etc
On-premise 환경 규범집 작성 및 정보보안 법률 적용 계획
목표
단순한 서버 구현 및 서비스 제공을 넘어서, 이 서비스를 안전하게 운영하기 위한 첫 걸음을 다음 추가 프로젝트를 통해 진행하려 합니다. 개인 On-premise 환경을 위한 규범집을 작성하고, 법률을 서비스 환경에 적용하여 ISMS-P 기준을 간략하게 적용하여 간이 보고서를 작성하려 합니다. 이는 보안이 실 서비스 운영에 있어서 얼마나 중요한지 확인하고, 법과 규정을 실제로 서비스에서 왜 필요한지에 대한 심도 싶은 이해를 하고자 합니다.
IPv4, IPv6 동시 서비스 계획
목표
현대 인터넷 환경은 IPv4 체계의 주소 고갈과 한계로 인하여 IPv6 체계의 도입하고 있는 과도기적 상황입니다. 이러한 상황 속에서 IPv4의 NAT(Network Address Translation), Subnetting, Broadcast와 Unicast, IPv4 환경에서 네트워크 보안 등에 대하여 심도 깊은 이해를 하고, IPv6 환경에서 어떻게 같은 서비스를 지속할 수 있을지에 대한 학습 및 경험을 하고자 합니다.
IaaS(Infrastructure as a Service) 등 외부 서비스와 연동 계획
목표
현대 IT 인프라는 절대 On-premise 환경에서만 작동하지 않습니다. 수많은 서비스와 On-premise 환경이 동시에 공존하며 상호 보완적인 관계를 띄고 있습니다. 이에 On-premise 환경 구축 후 이를 Azure와 같은 외부 IaaS 서비스와 직접 연동을 하는 프로젝트를 통해 현대 IT 인프라가 어떤 형태로 작동하는 지에 대한 더욱 깊은 이해를 추구합니다.
2025-05-24 - 초안 작성
2025-05-28 - IPv4, IPv6 동시 서비스 계획 추가 작성
2025-05-29 - IaaS 등 외부 서비스와 연동 계획 추가 작성
2025-05-30 - HA 계획에 OpenWRT 내용 추가
2025-06-16 - OpenWRT와 HA 계획 분리 및 부록 1.1. 문서 작성 가이드라인 에 따라 본문 재작성
2025-06-19 - VPN Clients 망 추가
2025-06-20 - 날짜 표기 변경
2. 프로젝트 설계
2.1. 하드웨어 선정
목표
본 프로젝트의 요구 사항을 만족시킬 수 있는 하드웨어를 선정합니다.
선택된 하드웨어 (WTR Pro N150 및 상세 사양)이 성능 한계와 기대치를 확인합니다.
각 과정의 고려사항 및 근거를 명확하게 기록합니다.
요구 사항
실사용 및 학습 목적에 성능 요구사항을 충족해야 합니다.
다중 VM/LXC 환경
IPS/IDS 학습 목적 운용(통제된 Inbound 트래픽 및 학습 목적의 최소 운영)
소규모 사용자를 위한 Web, Media, Application Service 제공(트랜스코딩 및 동시 부하 가능성 매우 낮음)
24/7 운영을 전제로 하고 있으므로 다음과 같은 조건을 만족해야 합니다.
저전력
저발열
저소음
안정성
다음과 같은 필수 인터페이스를 포함해야 합니다.
File Server를 위한 다중 SATA Slot
OS가 설치되어 작동할 SSD Slot
트래픽 분산을 위한 두 개 이상의 NIC
예산이 제한되어 있으므로 가능한 저렴한 제품을 선택합니다.
검토 사항
서버 하드웨어
직접 구성
24/7 운영을 위한 저전력 프로세서는 주로 일체형 메인보드 형태로 판매합니다. 따라서, 직접 서버를 구성한다고 하더라도 구성이 PC를 조립하는 것에 비하여 자유롭지 않습니다. 또한, 메인보드 및 프로세스를 제외하더라도 케이스 및 여러 가지 인터페이스를 구매하기 위한 추가 비용이 있습니다.
기성 제품
기성 제품은 특화된 기능을 저럼한 가격에 좋은 품질로 제공하는 경우가 많습니다. 특히 저전력 프로세서를 사용한 제품은 구성의 자유도 면에서 기성 제품이 오히려 더 좋은 가격에 더 좋은 품질로 구매할 수 있습니다.
결론
기성 제품 중에서 프로젝트에 필요한 모든 인터페이스가 포함되어 있던, Aoostar 의  WTR Pro 모델로 서버 하드웨어를 결정하였습니다.
프로세서
WTR Pro 모델의 프로세서 선택
WTR Pro 모델은 Ryzen R7 5825U 모델과 Intel N150 모델 두 가지가 있습니다. WTR Pro의 프로세서를 선택하기 위하여 다음과 같은 점들을 고려하였습니다.
성능 조건
프로세서
R7 5825U
N150
Core/Thread
8/16
4/4
Architecture
x86-64
x86-64
Clock Speed
2.0GHz(4.5GHz as Turbo Speed)
0.8GHz(3.6GHz as Turbo Speed)
L3 Cache
16MB
6MB
TDP
15W(25W MAX)
6W
Price(WTR Pro)
379,400 KRW
254,900 KRW
절대적인 성능은 R7 5825U 모델이 N150 모델보다 훨씬 좋습니다. Core는 2배, Thread는 4배이며 Clock Speed 역시 압도적인 격차가 있습니다. 하지만 프로젝트의 요구사항을 고려할 때, N150 모델 역시 충분히 이를 구현 가능한 성능을 가지고 있습니다. 하지만 N150 모델은 R7 5825U에 비하여 고부하 상황에서 언제나 쾌적한 환경을 제공하는 것이 힘들 수 있다는 것을 예상하고 실제 서비스 운영 시 자원 배분에 각별한 주의가 필요합니다.
24/7 운영을 위한 조건
24/7 운영을 위하여 프로세서는 저전력, 저발열, 저소음, 안정성 조건을 만족하여야 합니다. R7 5825U 모델은 N150 모델에 비하여 매우 좋은 성능을 가지고 있습니다. 하지만 N150 모델에 비하여 전력 소모량 및 발열량이 높았습니다. TDP가 약 2.5~4배 가까이 차이가 났으며, 이는 가정에서 24/7로 운영하며 사용할 때 발열 및 전력 소모가 더 높은 것을 의미합니다. 이는 시스템에 안정성에도 큰 영향을 끼칠 것입니다.
가격 조건
R7 5825U 모델은 N150 모델에 비하여 약 30% 가량 비싸 약 120,000 KRW 정도의 추가 지출이 필요하였습니다.
기타 고려 사항
R7 5825U 모델은 다음과 같은 특정한 하드웨어 이슈를 가지고 있었습니다.
특정한 작업(트랜스코딩 등)이 N150을 사용할 때에 비하여 불안정함이 보고됨.
SATA Controller가 Hypervisor 상에서 Path through 할 때 비정상적인 작동이 보고됨.
쿨링이 원활하지 못한 미니 PC 구조와 더불어 4Bay HDD를 지원하여, 발열이 쉽게 해소되지 않는 문제 보고 됨.
R7 5825U 모델은 N150 모델에 비하여 RAM Slot과 M.2 Slot이 한 개씩 더 많습니다.
예상 프로세서 할당량 및 우선 순위(N150)
서비스 이름
타입
할당 vCPU (core)
우선 순위 (cpuunits)
할당 이유
OPNsense
VM
2
매우 높음
트래픽 처리, IDS/IPS 학습 및 내부 방화벽, VPN 핵심 기능: 무중단 필수
Rocky Linux -FreeIPA 
VM
2
매우 높음
ID 관리(LDAP/SSO), 내부 CA, DNS 등 핵심 인증 서비스: 무중단 필수
AdGuard Home
LXC
1
낮음
광고 차단을 목적으로 하는 DNS 필터링 서비스: 무중단 필수, FreeIPA 연동
Monitoring Server
LXC
1
낮음
백그라운드 상 지속 운영하나, 부하 자체는 낮을 것으로 보임
Proxy Server
LXC
1
보통
SSL/TLS 처리시 순간 부하 발생 가능, 지속 운영
File Server
VM
1
보통
I/O 작업 및 파일전송 프로토콜, 접속자에 따라 변동
Web Server/WAS
VM
1
낮음
저트래픽 기술 블로그, Bookstack 운영 (DB 서버 분리) 부하는 낮을 것으로 보임
DB Server
VM
2
높음
중앙 집중식 데이터베이스 서버: 무중단 필수
Application Server
VM
2
높음
Immich, PeerTube, Vaultwarden 등 미디어 및 주요 서비스(DB 도커는 별도 운영)
Kali Linux
VM
2(1)
높음(낮음)
실행 시 Docker VM 일시 중지 조건부 운영 (+practice client)
vCPU: VM/LXC에 할당되는 가상 CPU 코어 개별 VM/LXC에 pCPU의 Thread 초과 할당 비권장. 하지만 전체 VM/LXC는 초과 가능 cpuunits: CPU 자원 경합 시 값이 높은 쪽이 우선 순위가 높고 더 많은 시간을 할당 (매우 높음: 기본값 2배, 높음: 기본값 1.5배, 보통: 기본값, 낮음: 기본값 0.5배 등으로 설정) cpulimit: 보조적으로 과도한 CPU점유를 막기 위해 절대적인 CPU 사용 상한 제한 socket/core: vCPU의 CPU와 Core 개념, vCPU는 pCPU의 Thread 위에서 실행되며, 각각 soket(vCPU)과 core(vCore)로 구분
결론
R7 5825U 모델은 N150 모델에 비하여 막강한 성능을 가지고 있습니다. 하지만 저부하 IPS/IDS 운용과 소규모 인원을 위한 WEB, Media, Application Service 운용, 매우 낮은 트랜스코딩 사용 가능성과 동시 부하 가능성이 매우 낮다는 프로젝트의 요구 사항을 반영하였을 때,  목적 달성에는 N150 모델로 충분할 것입니다. 이는 가장 중요한 부분으로 요구 사항을 넘어서는 고부하 상황 조건을 고려한다면 매우 고성능 서버 프로세서와 고용량 RAM으로도 성능의 부족을 경험할 수 있을 것입니다. 모든 하드웨어 선택은 항상 성능과 요구 사항 사이의 균형을 찾아야 합니다. 따라서 성능과 요구 사항 사이의 균형, 안정성 및 가격, 기타 고려 사항을 종합하여 최종적으로 N150 모델을 선택하였습니다.
RAM
RAM의 용량은 서비스의 가용성을 유지하기 위하여 매우 중요합니다. RAM 용량이 부족하면 일차적으로 Swap이 일어나 서비스가 느려집니다. 이후 지속적으로 RAM 용량이 부족하면 시스템은 자동으로 OOM Killer 기능을 이용하여 RAM을 차지하는 서비스를 강제로 중지합니다. 이는 서비스의 가용성의 큰 문제를 만듭니다. 따라서 RAM 용량을 항상 적절하게 할당하고 배분해야 합니다.
서비스 별 RAM 사용 예상량
서비스 이름
타입
할당 RAM
할당 이유
OPNsense
VM
4GB
IDS/IPS 학습 및 내부 방화벽/VPN 핵심 기능
Rocky Linux -FreeIPA 
VM
4GB
ID 관리(LDAP/SSO), 내부 CA, DNS 등 핵심 인증 서비스
AdGuard Home
LXC
0.5GB
광고 차단을 목적으로 하는 DNS 필터링 서비스, FreeIPA와 연동
Monitoring Server
LXC
2GB
Prometheus, Grafana, Loki, Promtail 기반 시스템 모니터링 및 로깅과 Ansible을 통한 서버 자동 설정
Proxy Server
LXC
1.5GB
리버스 프록시 및 SSO 프록시, SSL 인증서 관리, DDNS 스크립트 작동
File Server
VM
4GB
NFS, WebDAV 등 데이터 저장 및 공유(SATA 패스스루 RAID 관리)
Web Server/WAS 
VM
2GB
저트래픽 기술 블로그, Bookstack 운영(DB 서버 분리)
DB Server
VM
4GB
중앙 집중식 데이터베이스 서버(여러 서비스에서 사용, 2~3개 DBMS 도커로 운용, 각 DBMS 별로 1~1.5GB 램 제한)
Application Server
VM
6GB
Immich, PeerTube, Vaultwarden 등 미디어 및 주요 서비스(DB 서버 분리)
Kali Linux 
VM
(4GB + 1GB)
실행 시 Application Server VM 일시 중지 조건부 운영 (+practice client)
Guest RAM
-
28GB(27GB)
일반 환경 시 RAM(Kali 실습 시 RAM)
PVE
OS
4GB(5GB)
Btrfs 스냅샷 기능 및 OS 안정성 확보(kali 실습 시 5GB)
결론
계획 초기 현 계획보다 적은 기능을 고려하여 16GB RAM으로 충분히 모든 서비스를 제공할 수 있을 것이라 생각했습니다. 하지만 위와 같이 계획이 구체화 되면서 각 서비스를 제공하기 위한 필요 RAM 용량이 늘어났습니다. 따라서 최종적으로 32GB RAM 을 선택하였습니다. 각 서비스가 동시에 고부하 작업을 할 확률이 매우 낮은 점, 각 서비스 별로 RAM 용량의 여유를 둔 점, PVE 자체의 KSM(Kernel Same-page Merging) 기능을 통한 메모리 절약이 가능한 점을 고려하면 32GB의 RAM 용량은 요구 사항을 충분히 충족할 수 있을 것입니다. 하지만 실제로 서비스를 운영하며 RAM 할당량을 모니터링 및 조율할 필요가 있습니다.
저장 장치
SSD
OS가 설치되는 부분 및 DB가 실행되는 부분은 IO 속도가 매우 중요하므로 M.2 NVMe SSD를 선택하여 사용하기로 선택하였습니다. OS 및 DB 데이터를 비롯해 각종 로그 데이터를 저장하기 위해 충분한 용량인 M.2 NVMe SSD 1TB  모델을 선택하였습니다.
서비스 별 SSD 사용 예상량
서비스 이름
타입
할당 SSD
할당 이유
OPNsense
VM
32GB
단기 로그, IPS/IDS 규칙, 캐시 등을 저장
Rocky Linux -FreeIPA 
VM
64GB
LDAP 데이터베이스, 인증서, 사용자 정보 등을 저장
AdGuard Home
LXC
16GB
단기 쿼리 로그 등 저장
Monitoring Server
LXC
64GB
단기 메트릭, 로그 저장. 추후 File Server로 옮기더라도 충분한 용량 부여.
Proxy Server
LXC
16GB
웹 캐시 등을 저장
File Server
VM
32GB
OS 및 Docker 설치
Web Server/WAS 
VM
32GB
OS 및 Docker 설치(Git 혹은 Application Data는 File Server NFS 이용)
DB Server
VM
128GB
DB는 I/O 성능이 중요, DB 데이터 저장(백업 파일; Dump는 File Server NFS 이용 저장)
Application Server
VM
128GB
DB 데이터는 DB Server, 대용량 파일은 File Server 이용하나 Cache 및 Metadata, Tumbnail 등 저장
Kali Linux + Practice Client
VM
64GB + 16GB
Kali와 Clients OS 데이터 저장
PVE
OS
100GB
PVE 자체 용량(자동 지정)
총합
692GB/1TB
SSD 성능 상 필요 예비 공간 100GB, 추후 필요시 약 150GB~200GB 추가 할당 가능
HDD
이에 반하여 일반 데이터 파일 및 미디어 파일이 저장되는 부분은 IO 속도보다는 용량과 안정성이 중요합니다. 따라서 저장 용량 대비 가격이 저렴한  2TB 3.5" HDD 4개 를 사용하기로 선택하였습니다.
이와 동시에 저장장치의 가용성을 확보하기 위하여 여러 RAID(Redundant Array of Independent Disk) 방식을 고려하였습니다. 이 때, RAID 10, 5, 6이 고려하였고, HDD 갯수가 4개인 점, IO 성능의 향상폭이 가장 높은 점, 데이터 복구시 HDD 별 부하량이 가장 낮은 점과 같은 고려사항을 통해 RAID 10 (총 8TB 중 4TB 사용 가능)을 선택하였습니다.
결론
다음과 같은 조건 및 고려사항 하에서 WTR Pro N150 모델은 주요 기능을 운영하고 학습 목표를 실행하는데 있어서 큰 무리가 없다고 판단됩니다. 하지만 N150 프로세서의 절대적인 성능의 한계에 대하여 명확하게 파악하고, 실제로 고부하 작업(특히 미디어 스캔 혹은 트랜스코딩 등)의 상황에서 쾌적하지 못한 상황이 발생할 수 있다는 것을 인지하여야 합니다. 하지만 실제 요구 조건 하에서 그런 상황은 거의 일어나지 않을 것이므로 실제 운영 상 성능 여유(Headroom)가 없다는 점만 인지하고 있으면 충분할 것으로 보입니다. 따라서 현재 가지고 있는 N150 서버에 대한 꾸준한 모니터링 및 성능 할당량 조정을 통하여 제한된 하드웨어 조건 하에서 어떻게 가용성을 지킬 수 있는 가에 대한 부분을 학습 할 수 있을 것입니다.
최종적인 하드웨어 선택 및 각 구성은  1.2. 하드웨어 구성 에 정리되어 있습니다.
2025-05-24 - 초안 작성
2025-05-25 - HDD RAID 부분 수정
2025-05-28 - RAM 용량 조정: SSO proxy와 reverse proxy 분리로 인한 RAM 0.5GB 추가/모니터링 서버 RAM 0.5GB 추가/ Proxmox RAM 1GB 감소/ kali 실습시 Practice client 추가로 +1GB/DB 서버 분리, 도커 환경을 위한 VM 변경 및 램 2GB 추가/OPNsense 2GB 감소
2025-05-29 - 예상 프로세서 할당량 및 우선 순위 추가 / SSD 용량 1TB로 상향(로그 및 DB 데이터)
2025-06-16 - AdGuard Home LXC 내용 추가 및 RAM 용량 조정/ Proxy Server 0.5GB 추가/ AdGuardHome LXC 0.5GB 추가/PVE 1GB 감소
2025-06-17 - 부록 1.1. 문서 작성 가이드라인 에 따라 본문 재작성
2025-06-19 - Rocky Linux 내용 추가
2025-06-20 - 날짜 표기 변경
2025-06-24 - 각 VM/LXC 별 SSD 용량표 추가
2.2. 네트워크 설계
목표
네트워크 설계 중 물리적인 한계 및 하드웨어적인 한계를 파악하고 이를 극복할 수 있는 기술적인 방안을 선택합니다.
최종적으로 설계된 네트워크 구조의 잠재적인 위험성을 파악하고 장애 발생시 어떻게 대처 할지 구상합니다.
각 과정의 고려사항 및 근거를 명확하게 기록합니다.
요구사항
집안 네트워크 회선의 물리적인 구조로 인한 제약 사항을 파악 및 회피합니다.
주요 네트워크 장비의 VLAN 미지원에 대응하여 L2 통신간 E2E 보안 및 클라이언트 격리 방안을 모색합니다.
방화벽 및 IPS/IDS로 Inbound, Outbound 통신을 중앙에서 감시 및 제어할 수 있도록 합니다.
L2 통신과 L3 통신의 차이를 명확히 파악하여 각 통신별 보안 대책을 수립합니다.
검토사항
물리적 네트워크 구조 구성도
물리적 제약 사항
Terminal Box 내의 전원의 부재로 인하여 기반 네트워크 장비 PoE 사용(새로운 Node를 추가 불가)
각 방과 Terminal Box 사이의 물리적인 회선이 하나
주요 네트워크 장비(T5004, AX6000M, H5008G)의 VLAN 미지원으로 인하여 PVE 밖 Clients에 VLAN 적용 불가
T5004의 DHCP Relay 미지원으로 OPNsense의 DHCP 운영 불가 및 서브네팅 활용 불가
이중 게이트웨이 구조
제약 사항
OPNsense가 Gateway가 되어 DHCP를 운영하기 위해서는 Terminal Box에 위치하거나, DHCP Relay 기능을 활용하여야 합니다. 하지만 Terminal Box에 전원이 부재한 제약 사항으로 인하여 불가능합니다. 또한 T5004는 VLAN을 미지원하고, DHCP Relay 기능 역시 미지원하여 불가능합니다. 이는 OPNsense가 직접 DHCP를 운영하고 Gateway로 작동하는 것이 불가능함을 뜻합니다.
회피 방안 및 결론
T5004 내에서 DHCP 운영시 DHCP의 기본 Gateway를 변경할 수 있는 점에서 착안하여, OPNsense를 T5004의Untagged VLAN의 DHCP 기본 Gateway로 설정합니다. 이때 OPNsense는 Static IP를 할당하여 자신의 Gateway를 T5004로 설정하는 이중 게이트웨이 구조를 운영할 수 있습니다. 이 경우 다음과 같은 예상되는 위험이 존재합니다.
OPNsense의 IP가 Static IP가 아닌 경우, 자기 자신을 게이트웨이로 지정하여 Routing loop 현상이 발생 가능
하지만 OPNsense의 IP가 Static IP로 설정되어 T5004로부터 IP를 할당받는 경우가 아니라면 Routing loop의 위험성은 없습니다. 동시에 모든 L3 통신은 OPNsense를 거치게 됩니다. 따라서, 물리적으로 T5004 바로 뒤에 방화벽 OPNsense를 두지 못하는 상황에서 이러한 이중 게이트웨이 구조는 가장 현실적으로 L3 통신을 통제할 수 있는 방안입니다.
또한 PVE 내부의 있는 서버 및 서비스들은 vmbr을 통한 VLAN 적용이 가능하므로 OPNsense의 라우팅 기능을 활용하여 라우팅 테이블을 작성하여야 합니다.
Untagged VLAN - Untagged VLAN 통신: ARP를 통한 unicast 통신
Untagged VLAN - Other VLAN 통신: OPNsense가 내부망의 게이트웨이이므로, OPNsense 자체 라우팅
Untagged VLAN - WAN 통신: OPNsense가 내부망의 게이트웨이이므로, OPNsense에서 T5004로 라우팅
Other VLAN - WAN 통신: OPNsense가 내부망의 게이트웨이이므로, OPNsense에서 T5004로 라우팅
OPNsense의 NIC 구성 방안
제약 사항
OPNsense에 할당 가능한 물리 NIC는 2개입니다. 그리고 T5004는 LACP(Link Aggregation Control Protocol)를 지원하지 않습니다. 이러한 상황 속에서 설계 목표 중 하나였던 WAN 통신은 NIC0에서, LAN(VLAN 포함) 통신은 NIC1에서 제어하여 병목을 최소화하고, 모든 L3 통신을 OPNsense 중앙에서 통제하여야 합니다.
회피 방안 및 결론
위 제약 사항을 해결하기 위하여 다음 다이어그램과 같이 두 가지 방안을 고려하였습니다. 
NIC0과 NIC1를 vmbr0에 통합하여 OPNsense 내 vtnet0과 연결하여 단일 인터페이스로 관리
NIC0과 NIC1 각각 vtnet0, vtnet1로 할당하여 따로 관리
방안 1은 OPNsense에서 Untagged VLAN에 대하여 단 하나의 인터페이스와 IP만을 관리하므로 설정이 단순하고, LACP를 지원하지 않더라도 Active-backup 설정시 자동으로 NIC 장애 대처가 가능한 점이 있습니다. 하지만 프로젝트의 목표였던 WAN과 LAN 통신의 NIC 분리가 불가능합니다. 이는 OPNsense의 단일 vtnet으로 트래픽이 전달되며 vmbr0은 가상 L2 장비로 NIC를 구별하지 않기 때문입니다. 또 Active-backup 설정시 평상시에는 하나의 주 물리 NIC만 사용되므로 NIC 별 효과적인 트래픽 분산을 하는 것이 불가능합니다.
2번 방안은 두 물리 NIC에 각각 vmbr에 연결하여, OPNsense에 할당하고 각각 별도의 인터페이스 및 IP로 관리합니다. 따라서 OPNsense의 L3 인터페이스를 통해 물리적인 NIC 경로를 WAN 트래픽과 LAN 트래픽을 분리하여 사용할 수 있습니다. 이를 통해, vtnet0과 vtnet1에 각각에 방화벽 규칙 등을 적용하여 정교한 통제가 가능해지며, 두 물리 NIC를 전부 적극적으로 활용할 수 있습니다. 하지만 설정이 1번 방안에 비하여 더 복잡해집니다. 각 물리 NIC가 장애 시, 각 NIC가 가진 역할이 중단될 수 있는 SPOF가 됩니다. 또한 명시적으로 라우팅 정책을 설정하지 않을 시, 비대칭 라우팅으로 인한 네트워크 장애가 발생할 수 있습니다.
최종적으로는 다음과 같은 이유로 방안 2를 선택하였습니다.
L2/L3 통신의 차이를 명확하게 하고, OPNsense의 라우팅, 방화벽 정책, 인터페이스 관리에 대한 더 깊은 이해 가능
WAN 통신은 NIC0에서, LAN(VLAN 포함) 통신은 NIC1에서 처리한다는 초기 설계 목표를 명시적으로 L3상에서 구현 가능
두 개의 별도 물리 NIC를 적극적으로 활용하여, 성능의 이점을 가짐
이중 게이트웨이 구조의 명확화
Zero Trust 원칙 하 클라이언트 방화벽 정책 제어
제약 사항
T5004를 비롯한 홈 네트워크 장비는 VLAN 및 Subnet을 지원하지 않습니다. 이는 PVE 밖의 클라이언트에 대한 유연한 논리적 L2 망 분리가 불가능함을 뜻합니다. LAN 통신은 L2 통신으로, L3 장비인 방화벽(OPNsense)을 거치지 않고 Gateway 또한 통과하지 않습니다. L2 통신에서는 클라이언트가 직접 ARP broadcast를 통해 전송 대상의 MAC 주소를 획득하여 unicast로 통신합니다. 따라서 VLAN과 Subnetting이 불가능하므로 방화벽을 통한 ACL(Access Control List) 관리는 불가능합니다. 이는 내부망을 위한 보안 대책의 필요성을 높입니다. 다만, PVE 내부의 클라이언트에 대하여서는 VLAN을 통한 L2 망 분리가 가능합니다.
회피 방안 및 결론
외부자가 쉽게 네트워크를 접근할 수 없도록 WPA3 프로토콜을 이용한 통신 암호화가 필요합니다. 또한 VLAN 기능은 없지만, T5004에 존재하는 Guest network 기능을 활용하여 외부인이 사용 가능한 네트워크를 격리할 수 있습니다. Guest network는 LAN 통신을 막고, WAN 통신만 가능하도록 하는 기능입니다.  각 VLAN(Untagged VLAN 포함) 내부 통신의 ACL 정책은 클라이언트 단에서 처리되어야 합니다. 이 때, Zero Trust 원칙 하 각 서버 및 클라이언트의 iptables, Windows 방화벽 등 자체 방화벽 정책을 철저하게 설정하여 최소 권한 원칙을 통해 꼭 필요한 포트와 IP로 부터의 접근만을 허용해야 합니다. 이 때 홈 네트워크 특성상 관리해야 할 서버 및 클라이언트의 숫자가 크지 않으므로 이러한 조치들로도 과도하게 복잡해지지 않고 충분히 관리가 될 것으로 보입니다. 또한 Ansible 등의 서버 설정 자동화 툴 도입 역시 관리 피로를 낮출 수 있습니다.
각 서버 및 클라이언트의 대역대를 VLAN으로 나누어 각 통신을 L3 장비인 OPNsense에서 담당할 수 있도록 한다면 더욱 확실하게 이러한 문제를 해결할 수 있을 것으로 보입니다. 특히 WTR Pro 내부 VLAN2, 3, 4에 접근 가능한 IP를 OPNsense에서 직접 제어할 수 있기 때문에 다음과 같은 정책을 시도할 것입니다.
내부 접근 가능 IP 제한(특정 Client IP)
외부 접근 시 VPN 이용 접근만 가능
특정 서비스(Reverse Proxy 등)만, 서비스 목적에 따라 필요한 범위 내에서 전체 접근 허용
확인 결과 T5004에 OpenWRT 적용이 가능한 것을 확인하였습니다. 현재 계획 대로 On-premise 환경을 구축한 이후, T5004에 대한 OpenWRT 적용 및 전체 네트워크 VLAN 적용을 테스트 해볼 수 있을 것으로 보입니다. 다만 VLAN을 통한 망 분리를 넘어, DMZ (Demilitarized Zone) 를 구성하는 경우 홈 네트워크 특성 상 24/7 관제가 불가능하며 CERT(Computer Emergency Response Team) 운용 역시 불가능합니다. 따라서 해당 DMZ를 운영시 오히려 관리 지점 및 보안 취약점을 늘리는 결과를 만들 수 있습니다. 따라서, OpenWRT를 적용한 프로젝트를 진행하더라도 추가로 DMZ를 운영하지는 않을 것입니다.
통신 보안 및 암호화
제약 사항
WAN 통신 시에는 외부 CA를 통한 TLS 인증서를 발급 받아 통신을 비교적 쉽게 암호화 할 수 있습니다. 하지만 내부망 통신시에는 외부 TLS 인증서를 통한 암화가 불가능합니다.  또한 내부망을 위한 일부 특정 취약 프로토콜들(NFS, SMB 등)은 TLS를 통한 암호화가 불가능하며 평문 통신을 하므로 이를 암호화 할 방안이 필요합니다. LAN을 위한 특정 프로토콜들은 WAN에서 접근 시 사용이 불가능하기에 이를 위하여 VPN 역시 도입이 필요합니다.
회피 방안 및 결론
기본적으로 WAN에서 접근 시 웹 서비스를 위한 https(443)와 VPN을 위한 포트만을 T5004에서 NAT로 개방합니다. 이를 통해 WAN에서 LAN을 위협하는 포트 스캔 등은 막을 수 있습니다. 이는 OPNsense의 부하 역시 줄여줍니다.  또한 WAN에서 IP를 통한 직접 접근을 막고, Domain을 통한 접근만 허용하며, Cloudflare를 통한 DDoS 방어 서비스를 사용한다고 모든 웹 서비스는 Reverse Proxy를 거치게 한다면 더욱 안전한 WAN 통신이 가능할 것으로 보입니다. T5004 내에서도 역시 외부 관리페이지 접속 차단, UPnP등 보안 취약점 기능 전체 차단, 강력한 PW 사용, 최신 펌웨어 유지 등의 조치가 필요할 것으로 보입니다.
WAN 통신 뿐 아니라 LAN 통신 시 암호화를 적용하기 위하여 FreeIPA를 통한 내부 CA(TLS 암호화) 구축 및 Kerberos(NFS, SMB 등 암호화 및 추후 SSO 구현 토대) 인증 구조를 구현하고자 합니다. 또한 장기적으로 LDAP 및 SSO를 구현하는 데 있어 Kerberos가 사용될 수 있으므로 통합하여 FreeIPA를 구축합니다. FreeIPA 구축 시, 내부 root CA를 통한 내부 도메인용 인증서 직접 서명/발급/갱신/폐기 등을 자동화 할 수 있는 스크립트를 작성하는 것도 중요합니다.
FreeIPA는 자체 DNS 기능 역시 포함하고 있으므로 Local DNS를 구축하여 외부와 내부에서 같은 Domain을 통한 접근이 가능하도록 Split Horizon DNS를 구현하고자 합니다. 이때 Adguard Home을 통한 광고 및 악성 Domain을 차단하고 FreeIPA와 연동할 수 있습니다. 또한 Local DNS에 없는 Domain을 요청 받은 경우 신뢰할 수 있는 외부 DNS로 DNS 포워딩을 하여 구현 가능합니다. DoH를 통하여 Cloudflare Public DNS를 이용합니다.
WAN을 통한 내부 서버 및 클라이언트 관리는 지양하고, 필요시 VPN을 통해 관리합니다. 또한 LAN 서비스(AdGuard Home 등)을 이용시 역시 VPN을 통한 LAN 접속 후 이용합니다.
SFOP(단일 실패 지점) 문제
제약 사항
OPNsense와 FreeIPA는 홈 네트워크에서 가장 중요한 부분을 차지하고 있는 서비스입니다. 이 서비스들에 이상이 생긴다면, 다른 하드웨어 장비가 이상이 없더라도 전체 네트워크가 중단될 수 있습니다. 
OPNsense: 게이트웨이 + 방화벽 + VPN 서버
FreeIPA: 내부 CA + 내부 DNS + KDC
물론 L2 통신은 ARP를 통한 MAC 기반 unicast 통신으로, 이들에 문제가 있더라도 가능하지만 Outbound/Inbound 통신이 불가능해 지는 시점에서 홈 네트워크의 가용성에 치명적인 타격을 줄 수 있습니다.
회피 방안 및 결론
가장 좋은 해결 방안은 Scale out을 통한 물리적 HA 구성이겠으나 이는 예산의 문제로 현재 불가합니다. 또한 프로젝트 목표 역시 상용 서비스 수준을 따라가는 것이 아닌 학습과 체험 목적 역시 존재하므로 SPOF 문제는 어느 정도 타협이 필요합니다. 따라서 이러한 문제를 최대한 회피하기 위하여 다음과 같은 방안을 모색했습니다.
OPNsense 및 FreeIPA의 설정 백업 (각 서비스 별 기능 및 Gitea, Ansible 활용)
Proxmox 내 기능 활용하여 VM 스냅샷 백업
모니터링 서버를 이용하여 이상 발생시 알림 발송(단 Outbound/Inbound 통신 불가시 메일 발송 등은 불가함)
Proxmox 내에서 주기적으로 각 VM의 상태를 체크하여 문제 발생 시 자동으로 재시동, 문제 지속 시 스냅샷으로 VM 복원을 진행
다음과 같은 방안들이 자동화 된 스크립트에서 작동한다면 RTO(목표 복구 시간)을 최소화 하고 RPO(목표 복구 지점)의 관리가 가능할 것으로 예상됩니다.
VM/LXC/Docker/Client 간 통신
PVE는 기본적으로 Linux Bridge(vmbr) 및 vNIC(vtnet)을 통해 각 VM/LXC에 할당이 가능하며, Docker 역시 dockerd를 통해 내부 Bridge를 이용하여 통신하는 것이 가능합니다.
이를 통하여 서버 망과, 특수 목적 망(Kali 및 실습 목적 LAN)은 물리적 NIC에 연결되지 않은 vtnet과 vmbr을 할당하여 OPNsense 내에서 VLAN을 통해 격리하고자 합니다. 모든 VLAN은 OPNsense를 게이트웨이로 갖습니다.
Docker의 통신은 특수한 경우에만 macvlan 기능을 사용하여 직접 IP 주소를 부여합니다. 이외에는 host 내부에서 Bridge 네트워크를 통해 통신하며, 외부 통신 필요시 host의 IP 주소와 port를 기반으로 통신합니다.
결론
다음과 같은 검토 사항을 통하여 볼 때 현실적인 물리적/하드웨어적 제약 하에서도 원하는 기능 및 구조를 구현하는 것에는 무리가 없다고 여겨집니다. 하지만 제약 조건을 회피할 수 있는 것이지, 한계점이 사라지는 것은 아니기에 철저한 정책 수립과 꾸준한 모니터링 및 로깅, 그리고 조정이 필요한 것은 인지하여야 합니다. 이 과정을 통하여 언제 어떠한 프로토콜이 어떠한 방식으로 어떻게 작동하는지에 대하여와 전반적인 네트워크 통신의 구조에 대하여 깊은 이해를 하고자 합니다.
최종적인 네트워크 설계 구조는 1.3. 최종 목표 아키텍처 에 정리되어 있습니다.
2025-05-25 - 초안 작성 시작
2025-05-28 - 초안 작성 완료
2025-05-30 - VLAN 관련하여, Hypervisor 내부 서버에 대한 VLAN 분리 적용
2025-06-02 - OPNsense의 NIC 구성 방안에 대한 내용 추가
2025-06-17 - 부록 1.1. 문서 작성 가이드라인 에 따라 본문 재작성
2025-06-20 - 날짜 표기 변경
2025-06-23 - VLAN 표기 방식 수정
2.3. 시스템 선정
목표
하나의 하드웨어(WTR Pro) 안에서 가상화 기능을 통해 각 사항을 중요도와 특성에 따라 논리적으로 분리 구축합니다
요구 사항을 SoC 원칙 하 나누어 이를 효과적으로 달성할 수 있는 시스템을 식별 및 선정합니다.
선정된 OS와 서비스를 논리적으로 분리할 가상화 기술의 특징 및 장단점을 명확하게 하여 적재적소의 기술을 선택합니다.
각 과정의 고려사항 및 근거를 명확하게 기록합니다.
요구사항
하나의 하드웨어 안에서 여러 시스템이 격리되어 실행될 수 있는 Hypervisor를 사용합니다.
각 시스템 다음과 같은 프로젝트 요구 기능을 구현할 수 있어야 합니다.
각종 서비스 제공을 위한 DB, local DNS, CA, Proxy, Firewall 등 서버 및 네트워크 운영
파일, 미디어, 사진, 어플리케이션 서버 등의 개인 클라우드 환경 구축
블로그, wiki 등의 웹 서비스 호스팅
IPS/IDS, Kali 등을 통한 정보보안 실습 환경 제공
위와 같은 요구 기능들이 SoC 원칙 하에서 어떻게 분리되어 구축할지 정의해야 합니다.
VM/LXC/Docker의 장단점 및 논리적 분리 정도 차이를 명확히 파악하여, 각 기술별 보안 대책을 수립합니다.
검토 사항
시스템 선정에 가장 중요한 핵심 고려 사항
Open Source로 무료 사용이 가능할 것
무료 버전의 기능상 제약이 없거나 적을 것
실행 사양이 높지 않을 것
사용자가 많아 정보를 찾기 쉬울 것
Hypervisor
프로젝트가 성공적으로 구현되기 위해서 가장 중요한 것은 하나의 하드웨어에서 여러 가지의 시스템이 동시에 논리적으로 격리되어 작동할 수 있어야 한다는 것입니다. 이를 위한 개념이 바로 Hypervisor입니다. Hypervisor는 하나의 하드웨어 자원을 가상으로 논리적 분할하여, 각 시스템들이 마치 별도의 독립된 하드웨어에서 시스템이 동작하는 것 처럼 작동할 수 있도록 지원합니다. 이 중 직접 HW 위에서 동작하는 Hypervisor를 Type 1이라고 부릅니다.
Type 1 Hypervisor OS는 MS의 Hyper-V, VMWare의 ESXi, Xen, Proxmox VE 등 여러 종류가 있습니다. 이 중 프로젝트에 가장 부합하는 Hypervisor를 찾기 위해 각 시스템을 비교하고, 다음과 같은 사항을 고려했습니다.
시스템 비교
시스템
Hyper-V
ESXi
Xen
Proxmox
아키텍처
Windows Server
베어 메탈
베어 메탈(Dom0 필요)
KVM(Debian Linux base)
라이센스
Windows server에 포함
(무료 버전 지원 중단)
무료/유료
(기능 차이 존재)
Open Source (상용 버전 존재)
Open Source
(모든 기능 무료)
UI
GUI, CLI, Web UI
모두 지원
CLI, Web UI 지원
CLI 기본 지원
GUI는 서드파티 지원
CLI, Web UI 지원
핵심 기능
Windows/Linux guest 실시간 마이그레이션, HA 지원 등
Windows/Linux/Mac 등 다양한 Guest 실시간 마이그레이션, HA 지원 등
Windows/Linux/BSD 등 다양한 Guest, 실시간 마이그레이션, HA 지원 등
Windows/Linux/BSD/LXC 등 다양한 Guest 실시간 마이그레이션, HA, 통합 백업 및 복원 등
최소 사양
높음
중간
중간
낮음
추가 고려 사항
Linux 기반일 것 (다른 서비스와 호환성 고려)
결론
각 시스템에 대한 비교와 고려 사항을 검토한 결과 가장 적합한 Hypervisor OS로 Proxmox VE 를 최종적으로 선택했습니다.
내부 라우팅 및 Firewall
철저한 SoC 원칙에 따른다면 라우팅과 Firewall 역시 분리되어야 하지만, 제한된 조건 하에서 WAN-LAN 통신 간 모든 패킷이 통과하는 내부 게이트웨이에 Firewall이 같이 있는 것이 큰 문제는 아닐 것이라 판단했습니다. 실제로 분리하여 구축한다고 하더라도, Friewall은 주로 게이트웨이 바로 앞에 물리적으로 위치하게 되며, 이들의 분리는 SoC에 따른 것이라기 보다는 SPOF를 막기 위한 것입니다.
내부 라우팅과 Firewall, IPS/IDS 기능을 모두 지원하는 OS로는 크게 pfSense와 OPNsense 두 가지입니다. pfSense가 2006년 출시되었고, OPNsense는 2014는 pfSense로 부터 포크되어 출시되었습니다. 같은 뿌리를 가지고 있는 만큼 두 OS는 비슷한 점도 많지만, 다른 점도 생겨났습니다. 이 중 프로젝트에 가장 부합하는 OS를 찾기 위하여 다음과 같은 사항을 고려했습니다.
시스템 비교
시스템
pfSense
OPNsense
출시
2006
2014
아키텍처
FreeBSD
FreeBSD
라이센스
CE/Plus 이원화 (기능 차이 존재)
완전한 Open Soruce (기술지원 버전 존재하나 기능은 동일)
UI
전통적이고 안정적, 간결하며 페이지 리로드 적음
현대적이고 사용자 친화적, AJAX를 활용한 실시간 업데이트
핵심 기능
Firewall, IPS/IDS, NAT, VPN(OpenVPN/IPsec), 강력한 플러그인 지원 (pfBlockerNG, Suricata 등)
Firewall, IPS/IDS, NAT, VPN(OpenVPN/WireGuard), 2FA
업데이트 주기
CE는 주기가 길며 안정성 중심, Plus는 더욱 잦은 업데이트
연 2회 주요 릴리즈를 포함하여 잦은 업데이트
커뮤니티
방대하고 활발한 커뮤니티
빠른 성장 중인 커뮤니티와 개발팀과 원할한 소통
사양
낮음
낮음
추가 고려 사항
사용자 친화적인 UI를 가지고 있을 것
결론
본 프로젝트는 Firewall 및 IPS/IDS의 모든 고급 기능(추가적인 고급 플러그인 사용 등)을 사용하는 것을 목표로 하는 것이 아닙니다. 이러한 Firewall 및 IPS/IDS의 기능을 체험하고, 경험하며 익숙해지는 것이 목표입니다. 이런 상황 속에서 두 OS의 차이가 크게 없다면 Open Source이고 기본 기능의 제한이 없는 쪽을 선택하는 것이 맞다고 판단했습니다. 또한 아직 익숙하지 않은 상황에서 사용자 친화적인 UI는 기술 학습 곡선을 조금 더 완만하게 만들 수 있을 것으로 기대합니다. 따라서 기본 기능의 제한이 없고 조금 더 사용자 친화적인 UI를 갖춘 OPNsense 를 선택했습니다.
LDAP, 인증, 감사 서비스 및 DNS
프로젝트를 구체화 하는 과정 속에서 통합 ACL 관리 및 SSO 인증, 내부 CA를 통한 통신 보안을 구현하고 추가적으로 내부 DNS를 운영하기 위한 통합 관리 솔루션이 필요합니다. 물론 각각의 기능을 별도로 분리하여 구축하는 것도 좋지만, 이는 시스템의 관리 포인트를 무척 늘리고 유지 보수를 어렵게 합니다. 앞서 언급했듯 본 프로젝트의 주된 목표는 각각의 기능을 체험하고, 경험하고 익숙해 지는 것입니다. 각각의 기능을 별도로 구축하는 것 보다는 이러한 통합 인증 서버를 구현할 수 있는 시스템을 선택하는 것이 유리하다고 판단했습니다. 이러한 시스템은 크게 FreeIPA, Samba AD DC, Microsoft Active Directory 등이 있었습니다.
시스템 비교
시스템
FreeIPA
Samba AD DC
Microsoft Active Directory
일반 서버 OS 하
개별 솔루션 조합
아키텍처
Linux/Unix 중심
Active Directory 호환
Windows 중심
필요 기능만 조합
라이센스
Open Source
Open Source
유료
서비스 별로 다름
UI
CLI(ipa 명령어), Web UI 지원
CLI(samba-tool), Windows RSAT 지원
CLI, GUI 지원
통합 UI 부재
핵심 기능
LDAP, Kerberos, DNS, CA, 정책 관리, OTP 등 지원
(Linux/Unix 환경 중심)
LDAP, Kerberos, DNS, 정책 관리, AD 스키마 호환 등 지원 (Winodws/linux/unix
호환 중심)
LDAP, Kerberos, DNS, CA, 정책 관리, ADFS 등 지원 (Windows 중심)
 
필요한 프로토콜 및
기능 별도로 구현
설정 및
유지보수 난이도
높음
높음
중간
매우 높음
최소 사양
중간
중간
높음
가변적
추가 고려 사항
현재 목표 구조는 주로 Linux와 Unix를 사용하므로 이에 대한 호환성이 좋을 것
결론
현재 목표로 하는 홈 네트워크 환경은 주로 Linux와 Unix를 기반으로 설계되어 있습니다. Samba AD DC나 Microsoft Active Directory 같은 경우 Linux와 Unix 자체에 초점이 맞춰진 것이 아닌, Linux/Unix - Windows 간 호환성 혹은 Windows 자체에 초점이 맞춰져 있었습니다.
거기에 개별 솔루션 조합은 아무리 본 프로젝트가 학습과 경험에 조금 더 초점을 맞춘다고 할지라도, 중앙 관리 및 모니터링이 불가능한 점은 너무나 많은 관리 포인트를 만들어 프로젝트 진행을 어렵게 만들 것이라 판단했습니다.
따라서 본 프로젝트에 가장 잘 어울리는 시스템으로 Rocky Linux 위의 FreeIPA 를 선택했습니다. 최대한 비영리 Open Source를 사용하고자 OS로 Rocky Linux를 선택하였습니다. 단, FreeIPA를 통한 LDAP를 구현하더라도 관리의 편의를 위하여 별도의 UID/GID 대역 매트릭스를 운영합니다.
또한, DNS를 통한 광고 필터링이 가능한데 이를 FreeIPA에 직접 구현하는 것은 관리상 큰 어려움이 있습니다. 따라서 이를 쉽게 구현하기 위하여  AdGuard Home 서비스를 LXC로 별도 구축하여 FreeIPA의 DNS와 연결합니다.
중앙 모니터링 및 로깅, 서버 설정 자동화
프로젝트의 핵심 하드웨어인 WTR Pro의 경우 N150 프로세서와 32GB RAM을 사용하고 있어 성능의 여유(headroom)이 매우 한정적인 것이 사실입니다. 이러한 제약조건 속에서 프로젝트를 성공적으로 구현하기 위해서는 성능의 미세한 조정이 선택이 아닌 필수입니다.
넉넉한 성능의 하드웨어를 가지고 있다면 이러한 중앙 모니터링의 중요성이 낮아질 수 있으나, 현재 상황으로서는 매우 중요한 위치를 가지고 있습니다. 또한 여러 시스템이 동시에 올라가 작동하는 네트워크 구조 상 디버깅을 위한 로그를 중앙에서 관리하는 것이 중요합니다.
거기에 일관된 서버 설정을 위하여 Matrix를 관리하는 것 뿐 아니라 자동화 된 도구가 필요했습니다. 이를 위하여 모니터링 및 로깅을 동시에 할 수 있는 시스템을 다음과 같이 선정하였습니다.
모니터링 및 로깅, 알람, 자동화 기능은 특별한 권한이 필요하거나 엄격한 논리적 분리가 필요한 기능이 아니므로 성능의 부하를 줄이기 위하여 LXC로 구축합니다.
추가 고려 사항
각 서버의 상태를 한 곳에서 모니터링 할 수 있을 것
각 서버의 로그를 한 곳에서 일괄 확인할 수 있을 것
문제 발생 시 자동으로 알림을 보낼 수 있을 것
각 서버의 설정을 자동화된 관리가 가능하게 할 것
결론
다음 고려 사항을 확인하였을 때, 이 부분은 실무 환경에서 가장 많이 흔히 사용하는 조합을 가져오기로 하였습니다. 홈 네트워크 구조 상 무시 받기 쉬운 로깅, 및 모니터링 시스템이지만 본 프로젝트의 목적인 경험과 학습, 그리고 미세한 성능 조정을 위하여 다음과 같은 시스템을 하나의 서버에서 운영하기로 결정했습니다.
모니터링: Prometheus (성능 모니터링 결과 수집)와 Grafana (대시보드 및 시각화)
로깅: Loki (수집 로그 관리)와 promtail (로그 수집)
알림: Alertmanager
서버 설정 자동화: Ansible + Semaphore (경량 Web UI)
Proxy 및 DDNS
안정적인 웹 서비스 접근 및 SSO를 위하여, Reverse Proxy 및 SSO Proxy 관련 시스템을 하나로 묶어 구축하려고 합니다. 웹 요청이 있을 경우 Reverse Proxy는 SSO Proxy에게 인증 정보를 요청하고, SSO Proxy는 idP(identity Provider)를 통해(혹은 직접) Kerberos(혹은 다른 프로토콜)과 연동하여 인증 여부를 포함한 정보를 Reverse Proxy로 전달, 마지막으로 Reverse Proxy에서 요청을 웹 서비스로 전달하게 됩니다. DDNS는 WAN과 연결된 공인 IP를 탐지하여, 공인 IP 정보가 바뀌면 외부 DNS에 IP정보를 자동으로 전달하여 갱신 하는 역할을 합니다.
DDNS에 경우 API(주로 DNS에서 제공)를 통한 간단한 스크립트로 구현이 가능하나, Reverse Proxy와 SSO Proxy(+idP)를 구현하기 위하여 주로 다음과 같은 시스템을 사용합니다. 
Proxy server 역시 Monitoring server와 마찬가지로 특별한 권한이 필요하거나 엄격한 논리적 분리가 필요한 기능이 아니므로 성능의 부하를 줄이기 위하여 LXC로 구축합니다.
시스템 비교
시스템
Apache HTTP server + mod_auth_gssapi
Nginx + oauth2-proxy + keycloak(idP)
Authentick
아키텍처
단일형(+모듈)
분리형
통합형
라이센스
무료
Open Source
무료(개인 사용자 한정)
UI
설정 파일 외 관리 UI 부재
Nginx/oauth2-proxy: 설정 파일
Keycloak: Web UI 지원
Web UI 지원
핵심 기능
Web, Reverse Proxy, Kerberos 직접 인증 및 위임 등 지원
Nginx: Reverse Proxy
oauth2-proxy: OIDC/OAuth2, idP 등 지원
keycloak: Kerberos 인증, OIDC/SAML 토큰 발급, MFA 등 지원
idP(OIDC, SAML, LDAP), Kerberos 인증, 정책 엔진,
사용자/그룹 관리 지원, MFA,
Application Proxy 등 지원
설정 및 유지 보수 난이도
중간
높음
높음
최소 사양
낮음
중간
중간
추가 고려 사항
SSO라는 이해하기 힘든 개념에 대하여 대해 체계적으로 학습 가능하게 할 것
결론
단순한 구현이 목표라면 Apache를 사용하는 것이 가장 간단할 수 있을 것입니다. 하지만 프로젝트의 목표는 구현을 넘어 학습과 개념에 체득에 있습니다.
점점 SSO의 중요성이 부각되는 현대 사회에서 단순 Apache 사용은 앞으로 더더욱 입지가 줄어들 것입니다. 또한 Reverse Proxy와 SSO Proxy, 추가로 idP라는 복잡한 개념을 이해하려면 각각의 기능들을 이해할 수 있다면, 어떤 시스템을 사용한다고 하더라도 쉽게 익숙해지고 적응 할 것입니다.
또한 FreeIPA의 경우와 다르게, 이러한 기능은 십수가지가 아닌 3개로 나눠집니다. 따라서 SoC에 따른 분리를 하더라도 관리 포인트가 엄청나게 느는 것은 아닐 것으로 생각됩니다.
이런 고려사항을 따라 SoC에 따라 분리되어 있는  Nginx + oauth2-proxy + keycloack 을 선택해 적용하여 각 개념을 더욱 정밀하게 배울 수 있을 것으로 기대됩니다. 추후, 이러한 개념이 내재화 된다면, Authentick 같은 통합형 시스템도 쉽게 익숙해 질 수 있을 것을 기대합니다.
또한 DNS가 작동하는 FreeIPA가 아닌, Proxy Server에서 DDNS 스크립트가 작동하는 이유는 SoC 때문입니다. FreeIPA는 내부의 보안과 인증을 담당하는 서비스로 이러한 FreeIPA가 외부에 노출되는 것은 보안상 바람직하지 못합니다. 따라서, 외부와 통신하는 것이 주요한 역할인 Proxy에서 DDNS를 두어 SoC 원칙 하에 서비스를 분리하여 구축합니다.
VPN
WAN에서 안전하게 LAN으로 접근하여 서비스를 이용하고, 서버 및 클라이언트를 관리하기 위해서는 VPN이 필요합니다. VPN은 암호화된 터널을 구성해, WAN에서도 마치 특정 LAN에 속한 것 처럼 네트워크를 사용할 수 있도록 하는 기술입니다.
프로젝트에서 계획 중인 대부분의 서비스는 DDNS를 통한 외부 공개 접속이 가능하나, 특정 서비스(AdGuard Home, SSH, NFS, FTP 등)는 외부에 노출시 보안의 문제가 발생하기 쉽습니다. 따라서 이러한 특정 서비스를 안전하게 외부에서 이용하기 위해 VPN을 구축하여 사용하고자 합니다.
특히 OPNsense를 통한 VPN 서버를 구축 예정이므로, 공식적으로 지원하는 OpenVPN, WireGuard, IKEv2/IPSec을 비교하려 합니다.
시스템 비교
시스템
OpenVPN
WireGuard
IKEv2/IPSec
 호환성
(아키텍처)
다양함
(Windows, Linux, 모바일 등)
다양함
(Windows, Linux, 모바일 등)
대부분의 모바일 플랫폼
(RFC 프로토콜)
라이센스
Open Source
Open Source
Microsoft/Cisco
속도
보통
매우 빠름
빠름
 
핵심 기능
높은 배터리 소모, 높은 보안성과 안정성, 네트워크 전환 시 느린 재연결, 공식 앱 지원, 서드 파티 분할 터널링 지원
낮은 배터리 소모, 높은 보안성과 안정성, 네트워크 전환 시 즉각적인 재연결, 공식 앱 지원, 공식 앱 분할 터널링 지원
낮은 배터리 소모, 높은 보안성과 안정성, 네트워크 전환 시 즉각적인 재연결, OS 내장형, 분할 터널링 기능 지원하나 설정의 까다로움
 
설정 및 
유지 보수 난이도
보통
(인증서 관리 필수, 수동으로 클라이언트 설정)
 
매우 쉬움
(직관적인 UI, QR 코드를 통한 클라이언트 설정 가능)
어려움
(두 단계의 설정 및 일치,
벤더간 호환성 문제 발생 쉬움)
결론
 VPN을 사용할 때 있어서 중요한 것은 높은 보안성과 안정성 외에도 편의성도 큰 비중을 차지합니다. 특히 모바일 기기를 통한 VPN을 주로 사용하는 환경에서는 낮은 배터리 소모와 분할 터널링, 쉽고 직관적인 클라이언트 설정이 사용 경험을 크게 올려줄 것입니다. 따라서 세 프로토콜 전부 높은 보안성과 안정성을 보장할 수 있으므로, 배터리 소모량이 가장 적고 직관적이며 설정이 쉬운 WireGuard 를 선택했습니다.
파일 및 백업 관리와 클라우드 서비스
On-premise 환경을 구축하는 데 있어 파일 및 백업 관리와 클라우드 서비스는 가장 중요한 기능 중 하나입니다. 특히 빠른 I/O 속도가 상대적으로 중요하지 않은 대용량 파일(미디어 파일, 개인 파일, 백업 파일 등)은 SSD가 아닌 비용 효율적인 HDD에 저장하는 것이 가장 이상적입니다.
하지만 모든 서버에 HDD를 직접 마운트하는 방식은 복잡하고 비효율적입니다. 따라서, 중앙에 파일 서버를 구축하여 NFS 및 백업 서비스, 클라우드 서비스를 제공하는 것이 더욱 효율적입니다.
이때 Hypervisor로 부터 독립적인 파일 시스템을 관리하기 위하여 LXC가 아닌 VM으로 구축합니다. 이러한 중앙 통합적인 파일 서버를 구축하기 위하여 다음과 같은 사항들을 고려했습니다.
시스템 비교
OS
시스템
NAS OS (TrueNAS, OMV 등)
Ubuntu Server
Debian (Stable)
아키텍처
Unix/Linux 등
Linux
Linux
라이센스
OS 별로 다름
Open Source
Open Source
UI
전문 GUI 지원
CLI 지원
CLI 지원
핵심 기능
파일 서버에 필요한 모든 기능들을 통합적으로 지원
(SMB, NFS, 인증, 백업, 미디어 서버, DDNS, 자체 클라우드 등)
필요한 프로토콜 및 기능 별도로 구현
필요한 프로토콜 및 기능 별도로 구현
설정 및 유지 보수 난이도
낮음
(타 서버/서비스 연동시 높음)
중간
낮음~중간
최소 사양
중간
낮음
낮음
Cloud service
시스템
NAS OS의 패키지
NextCloud
Seafile
아키텍처
SW
SW
SW
라이센스
OS 별로 다름
Open Source
무료/유료 버전
UI
전문 GUI 지원
Web GUI 지원
Web GUI 지원
핵심 기능
OS 별 차이가 있을 수 있으나 기본적으로 모든 기능을 포함한 통합 파일 관리 솔루션 제공
파일 동기화, PIM 기능(캘린더, 할일, 주소록 등), media/office 기능을 포함한 통합 파일 관리 솔루션, LDAP/SSO 지원
파일 동기화 및 관리/공유 기능 특화, LDAP/SSO 지원
 
설정 및 유지 보수 난이도
낮음
(타 서버/서비스 연동시 높음)
중간
중간
최소 사양
높음
중간
낮음
Backup service
시스템
NAS OS의 패키지
duplicati
BorgBackup (+Vorta)
Kopia
아키텍처
SW
SW
SW
SW
라이센스
OS 별로 다름
Open Source
Open Source
Open Source
UI
전문 GUI 지원
CLI, Web GUI 지원
CLI(기본),
GUI(vorta) 지원
CLI, GUI 지원
핵심 기능
OS 별 차이가 있을 수 있으나 기본적으로 스냅샷,클라우드 동기화/백업, 중복 제거 증분 백업, E2EE 백업, 스케줄 백업 등 제공
다양한 클라우드 백업 등 백엔드 직접 지원, E2EE 암호화, 자동 스케줄링, 버전 관리 등 제공
최고 수준 중복제거/압축, 강력한 E2EE 암호화, 스냅샷(아카이브) 기반, 스케줄 백업, 강력한 CLI 등 제공
최신 기술(CDC 중복제거), 다양한 클라우드 백업 직접 지원, 정책 기반 관리, E2EE 암호화, 스케줄 백업 등 제공
설정 및
유지 보수 난이도
낮음
(타 서버/서비스 연동시 높음)
낮음
중간(CLI 사용 시 높음)
낮음
최소 사양
높음
 낮음
낮음~중간
 중간
추가 고려 사항
타 서비스(OPNsense, FreeIPA, Proxy 등)과 연동 용이할 것
다양한 기능보다는 File Server 자체 기능의 집중할 것
결론
프로젝트 진행에 있어 가장 중요한 점은 학습 가능성입니다. 존재하는 NAS OS를 그대로 파일 서버로 사용한다면 직접 설정하는 것보다는 편리할 수 있습니다. 하지만 다른 서비스들과의 통합과 부족한 하드웨어 성능, 그리고 학습 가능성을 고려했을 때 NAS OS 보다는 파일 서버를 직접 구축하는 것이 낫다는 결론을 내렸습니다.
따라서 서버를 구축하기 위한 OS로 NAS OS를 제외한 Ubuntu와 Debian(stable)을 비교하였습니다. Ubuntu와 Debian모두 안정적이고 가벼운 OS이나 Hypervisor인 PVE의 기반이 Debian이므로 다른 LXC와 VM 간 일관성을 위하여 Debian 을 선택하였습니다.
이때 기본 파일 시스템을 Btrfs로 적용하여 사용하려 하는데, Btrfs는 자체 중복 처리 기능과 CoW 기능, 그리고 스냅샷 기능을 제공합니다. 다만 CoW로 인한 외부 단편화가 발생할 수 있으므로 주기적인 디스크 압축 기능을 사용하여야 합니다. 
개인 파일 관리를 위한 Cloud Service를 위한 시스템을 선정하는데 있어서 가장 중요한 점은 필요 성능이 낮아야 한다는 것과 특화된 기능입니다. 파일 관리를 제외한 나머지 기능들, 즉 PIM 및 Multimedia 기능 등은 필요시 별도로 Docker를 통해 Application Server에 구축될 예정이기 때문입니다. 따라서 NextCloud와 Seafile을 비교하였을 때, 다양한 기능을 가진 NextCloud 보다 본연의 파일 관리 기능에 특화되어 있는 Seafile 을 선택하였습니다.
마지막으로 File Server의 파일들을 보존하기 위한 Backup Service를 위하여 고려한 점 역시 학습 가능성과 사용 편의성이 었습니다.
Duplicati는 오랜 기간동안 쓰여온 파일 백업 시스템이지만, 최신 중복 제거 알고리즘 지원등이 다른 서비스보다 부족하였고 타 시스템보다 CLI를 통한 강력한 기능 지원이 부족하였습니다.
BorgBackup 역시 오랜 기간 동안 쓰여온 강력한 시스템이며, 가장 강력하고 최신인 중복 제거 알고리즘을 사용합니다. 또, CLI를 통한 세밀한 관리 등이 가능하였으나, Vorta라는 별도의 GUI 시스템을 사용하지 않으면 GUI를 지원하지 않고 백엔트 연동 등의 편의성 부분에서 부족하였습니다.
Kopia는 출시된 지 오래된 시스템은 아니지만 빠르게 발전하는 시스템입니다. 최신 중복 제거 알고리즘을 지원하며, CLI와 GUI 모두 지원하였습니다. 또한, 다양한 백엔드 시스템(타 사 클라우드 서비스 포함) 연동 기능을 자체적으로 보유하고 있습니다.
다음과 같은 점들을 모두 고려 하였을 때  Kopia 가 학습 가능성과 사용 편의성을 모두 충족시키는 것이라 생각되어 선택하였습니다.
이 경우, Seafile과 Kopia 모두 일관된 환경에서 실행하기 위하여 Docker로 배포합니다.
따라서 File Server는 Debian(Stable - netist) 위에서  Seafile + Kopia(on Docker) 을 구축하기로 운영하기로 했습니다. 
DB, Web, Application 서비스
DB, Web, Application 서비스는 VM 위에서 Docker를 통하여 어느 환경에서나 동일한 작동을 보장하도록 구축할 것입니다. Docker의 경우 편리하지만, rootless docker는 사용에 있어 큰 제약(내부 인터페이스 생성 등)이 있기에 root 권한으로 실행되어야 합니다.
따라서 Docker를 사용하기 위하여서는 LXC가 아닌 VM을 사용하여야 합니다. 이는 LXC 상에서 Docker가 실행되었을 때 root 권한을 통해 LXC를 넘어 Hypervisor의 커널 영역까지 침범할 수 있는 위험성을 가지고 있기 때문입니다. LXC의 경우 appArmor/seccomp의 존재로 인하여 충분한 권한을 얻지 못한 Docker 서비스에 오류가 발생할 가능성이 큽니다. unprivileged LXC와 rootless docker의 경우 앞서 언급한 제약으로 인해 문제가 발생할 가능성이 있으므로 역시 권장되는 사용법이 아닙니다. 따라서 Docker 활용시에는 반드시 VM을 통한 완벽한 논리적 분리가 필요합니다.
따라서 앞선 File server의 경우처럼 VM을 통해 Proxmox VE와 일관성을 유지하기 위한 Debian(Stable - netist) 환경에서 Docker 서비스를 배포하는 형식으로 구현하고자 합니다. 구현하고자 하는 서비스의 목록은 다음과 같습니다.
DB
Maria DB, Postgre SQL 등 - DBMS
Web
Hompage - 홈페이지 및 대쉬보드
Ghost - 블로그 서비스
Bookstack - 개인 wiki, 노트
Gitea - Git 버전 관리
Application
Portainer - 도커 통합 관리
Uptime Kuma - 도커 서비스 모니터링
Diun - 도커 업데이트 관리(수동 업데이트)
n8n - 워크 플로우 자동화
Redis - 캐시 DB
Code-Server - 웹 코드 에디터(Git 연동)
Immich - 사진 및 개인 동영상 관리
PeerTube - 동영상 컨텐츠 관리 및 스트리밍
Navidrome - 음악 관리
Vaultwarden - 개인 암호 관리
Infisical - DB, API key 등 관리
Matrix-Synapse/element - 채팅/통화/영상 통화
Radicale - 캘린더 및 주소록
Vikunja - To-DO 리스트
Firefly III - 가계부
Paperless-ngx - 문서 관리
Snipe-IT - ITAM; IT 자산 관리
Calibre-web - 전자책 관리
Kmoga - 만화책 관리
Audiobookshelf - 오디오 북 관리
보안 실습 환경
보안 실습 환경은 Kali linux와 Practice server로 나누어 운영를 할 것입니다. 두 서버 모두 보안 실습에 활용될 예정이므로 Hypervisor에 영향을 줄 수 없도록 VM으로 격리하여야 합니다. Kali는 보안 실습에 필요한 많은 도구들을 자체적으로 내장하고 있는 OS이며, Practice server는 목적에 맞게 여러 OS를 활용할 계획입니다. Windows 부터 Unix, Linux 등을 필요할 때 설치하고, 실습 완료 후 제거하는 방식으로 운영할 것입니다.
결론
SoC 원칙 및 VM/LXC/Docker 환경 내에서 주의하여야 할 점들을 명확하게 하여 필요한 서비스들을 정리 및 시스템을 선정하였습니다. 이러한 필요 서비스들을 한 번에 구축하는 것이 아닌, 중요 순서 별로 차례 차례 구축하며 할당 성능을 조절한다면 주어진 물리적 제약 조건 속에서도 목표하는 모든 사항을 구현할 수 있을 것으로 보입니다.
최종적인 시스템 선정 결과는  1.3. 최종 목표 아키텍처 에 정리되어 있습니다.
2025-05-29 - 초안 작성 중
2025-06-04 - 초안 작성 완료
2025-06-16 - AdGuard Home LXC 내용 추가
2025-06-17 - VPN 내용 추가 및 부록 1.1. 문서 작성 가이드라인 에 따라 본문 재작성
2025-06-19 - Rocky Linux 내용 추가
2025-06-20 - 날짜 표기 변경
2.4. 보안 설계
목표
네트워크, 시스템, 인증, 및 정책 모든 부분에 대한 잠재적인 보안 위협을 예상하고, 이를 효율적으로 차단할 수 있는 방안을 구상합니다.
각 과정의 고려사항 및 근거를 명확하게 기록합니다.
요구 사항
최소 권한 원칙 및 Zero Trust 원칙 하, 보안 위험을 식별하고 차단합니다.
T5004/AX6000M/OPNsense/FreeIPA 등의 네트워크 관련 정책을 설계합니다.
방화벽, IPS/IDS, Port Fowarding 및 NAT, VLAN, VPN 등이 포함합니다.
각 서버 별 공통으로 적용되어야 할 보안 규칙(System Hardening)과 예외 사항을 작성합니다.
ACL을 위한 UID/GID 관리와 FreeIPA를 통한 SSO 규칙을 작성합니다.
데이터 보안 정책을 설계합니다.
검토 사항
네트워크 보안 정책 설계
T5004/AX6000M 보안 정책
관리자 접근
WAN에서의 관리자 페이지 접속을 원천적으로 차단
LAN 혹은 VPN을 통해서만 접근 허용
Wi-Fi 보안
무선 네트워크에 대하여 WPA3 암호화 방식 사용
강력한 비밀번호 설정
IoT 기기와 Client의 IP 대역 분리
OPNsense 상에서 IoT 기기의 IP 대역에 대하여 다음 규칙 적용
Allow: 목적지가 AdGuard Home의 DNS 포트인 트래픽과 WAN인 트래픽
Dney: 모든 내부망 트래픽
Clients는 전부 각각 Static IP 주소 할당하여, 대역이 아닌 IP 주소로 접근 제어
IP 위변조시 IP 주소 충돌로 확인 가능 , WTR Pro 외부 VLAN 적용 불가인 현 환경에서 최선
펌웨어 관리
주기적으로 최신 펌웨어 확인 및 유지
취약 기능 비활성화
UPnP와 같은 보안에 취약한 기능은 비활성화 원칙
방화벽 정책
기본 정책(Default Deny)
모든 인터페이스 간 통신은 기본적으로 차단
VLAN 간 통신 정책
특정 Client만 SSH등 접속 허용
그 외 모든 통신은 원칙적으로 Proxy Server의 https 포트와, DNS 통신만 허용
CrowdSec
각 서버에 별개로 Fail2Ban을 설정하지 않고 중앙 OPNsense에서 CrowdSec 플러그인을 통해 중앙 관제 및 대응
커뮤니티 기반의 악성 IP 차단 목록 활성화하여 알려진 공격자 차단
모든 VM/LXC에 Ansible을 기반으로 CrowdSec 에이전트를 설치하여 OPNsense에서 통합 분석
IPS/IDS 정책
초기 운영 정책
운영 초기 서버 부하를 알기 위하여 IDS 모드로 운영하여 충분히 검토
이후 점진적인 IPS 규칙 적용
규칙 셋
커뮤니티 기반 ET Open 규칙셋을 적용
최대한 가벼운 체험 목적의 운영
오탐 규칙은 개별적인 비활성화
Port Fowarding 및 NAT 정책
INBOUND 접근은 https(tcp/443)과 WireGuard VPN의 포트만 허용
T5004는 모든 트래픽을 OPNsense로 전달(Port Fowarding)
DNS 정책
기본 DNS를 AdGuard Home으로 설정
AdGuard Home은 광고 Domain을 차단 후 FreeIPA DNS로 upstream
FreeIPA는 Spilt Horizon DNS를 구현, 매치되지 않는 Domain에 대하여 Public DNS로 Fowarding
VPN 접속 정책
VPN 프로토콜
WireGuard 프로토콜 사용
VPN Server
OPNsense에서 구동
접근 원칙
외부에서 내부 관리 기능(SSH, PVE 웹 UI, Reverse Proxy 등) 이용시 반드시 VPN을 통해 접속
https 접근이 아닌 모든 프로토콜(RDP, NFS, SMB 등)은 반드시 VPN을 통해 접속
시스템 보안 정책 설계
root 계정 보안
명시적인 ssh root 접속 차단
서버 관리용 계정 생성
root 권한 필요시 sudo group을 활용한 sudo 사용
sudo 사용 기록 로깅
시스템 하드닝(System hardening) 정책
자동화 관리
Ansible 플레이북을 통해 신규 VM/LXC 배포시 다음 정책 강제 적용
최소 패키지 원칙
정기적 업데이트
로컬 방화벽(iptables) 정책
최소 권한 원칙 적용
ssh 등 필수 포트 개방
추가 포트 개방 필요시 수동으로 정의
CrowdSec 에이전트 설치하여 침입 탐지 및 공격 차단
Secrets 관리 정책
Infisical Secret
Infisical이 자신의 DB에 접속하기 위한 초기 비밀번호는 Ansible Vault를 통해 암호화
Vault 마스터 비밀번호는 개인 암호 관리자(Vaultwarden)에 보관
Infisical 정책
Infisical 실행 이후 DB 접속 정보, API 키 등 모든 Secret은 Infisical를 통해 중앙 집중식 관리
주요 API 키 및 DB 비밀번호는 주기적인 변경
개인 Secret 관리
Vaultwarden 서버를 활용하여 저장
인증 및 접근 제어 정책 설계
FreeIAP를 통한 SSO 정책
Application 인증
BookStack, Gitea 등 SSO를 지원하는 서비스의 사용자 로그인은 FreeIPA를 통한 LDAP/SSO를 통해 적용
직접 SSO가 지원되지 않는 서비스는 idP를 이용하여 SSO 적용
Server 인증
모든 서버의 SSH 접속은 비밀번호가 아닌, FreeIPA를 통한 Kerberos 혹은 SSH 공개키 인증 방식 적용
계정 및 권한 정책
Local UID/GID 정책
로컬 UID/GID 대역: 2000~9999
서버 자체의 root를 대신할 관리자 UID:GID
ACL 관리는 FreeIPA가 LDAP로 담당
서버별 대표 UID:GID 설정(예시)
서버 관리 그룹 GID: 2000 
DB 서버의 대표 UID: 2001
파일 서버의 대표 UID: 2002
FreeIPA에 할당될 LDAP/SSO 대역: 10000~
최소 권한 원칙
원격 root 접속은 제한
모든 사용자는 일반 권한 보유
관리자 권한 필요 작업시 sudo 사용 후 사용 내역 로깅
DB 정책
접속 호스트 제한
각 user는 자신의 DB에 모든 권한 보유
DB 로그인은 'user'@'ip_address' 형태로 해당 계정을 사용하는 서비스의 IP 주소에서만 접근 가능하도록 명시적 제한
데이터 보안 정책
파일 시스템
Btrfs 사용하여 CoW, 스냅샷 등 고급 기능 활성화
DB의 논리적 백업
모든 DB는 pg_dump (PostgreSQL) 혹은 mysqldump (MariaDB) 사용하여 논리적 백업 수행
백업 수행 결과는 File Server로 이동하여 저장
데이터 백업
파일 서버의 데이터는 Kopia를 사용하여 DS124에 1차 백업
주요 데이터는 E2EE를 적용하여 클라우드 백업
결론
보안 분야는 무궁무진하므로 새로운 위험과 취약점을 발견 시 언제든 수정될 수 있습니다. 고정된 설계가 아닌 프로젝트가 진행 되며 같이 변경되는 설계로 보안 분야에 대한 학습과 이해, 그리고 정책 수립 등에 익숙해지고자 합니다.
최종적인 보안 설계 구조는 1.3. 최종 목표 아키텍처 에 정리되어 있습니다.
2025-06-08 - 초안 작성
2025-06-19 - 세부 사항 작성
2025-06-20 - 날짜 표기 변경
2.5. 구현 계획
목표
실제로 프로젝트를 구현하기 앞서 구체적인 구현 순서를 계획합니다.
계획에 따라 순차적으로 서비스를 구현하고 검증합니다.
구현 계획
네트워크 장비 설정
기보유 네트워크 장비들을 계획에 맞도록 설정합니다.
IP Matrix 작성
네트워크 장비 설정에 앞서, 각 네트워크 장비와 서버 및 클라이언트의 IP 대역을 미리 계획합니다. 이를 Matrix 형태로 정리하여 추후 관리 및 유지보수가 편리하도록 합니다.
T5004/AX6000M 설정
현 네트워크 구조의 핵심인 T5004와, AX6000M의 설정을 합니다. 필요 설정은 크게 다음과 같습니다.
DHCP 서버 설정(기본 게이트웨이, DNS, Static/Dynamic IP 범위 등)
보안 설정(NAT 설정, UPnP 설정, 라우터 WAN 접근 차단, 관리자 ID/PW 설정, 접속 국가 차단, 무선 LAN 설정 등)
펌웨어 업데이트 확인
물리적인 보안 대책 확인
PVE 설정
VM/LXC를 통한 각 서버 구현 전 Hypervisor인 Proxmox VE를 설치하고 설정합니다.
UID/GID Matrix 작성
앞으로 구현될 각 서버의 UID/GID 대역을 설정합니다. FreeIPA 설정 후 통합될 LDAP/SSO 전용 대역과 로컬 UID/GID를 분리합니다.
PVE 설치
임시 서비스 Debain(Stable - netist) 설정
Bookstack, Gitea 등 프로젝트 진행을 위하여 필수적인 서비스를 임시로 설치합니다. 추후 WTR Pro에 서비스 구현시 마이그레이션합니다.
필수 설치 패키지 목록
curl
sudo
dockerd
임시 서비스 목록
nginx-proxy-manager[jc21/nginx-proxy-manager]
MariaDB[mariadb]
BookStack[linuxserver/bookstack]
Gitea[gitea/gitea]
Code-Server
PostgreSQL[postgres]
redis[redis]
Infisical[infisical/infisical]
immich
OPNsense 설정
Monitoring Server 설정
AdGuard Home 설정
FreeIPA 설정
Proxy Server 설정
DB Server 설정
기존 서비스의 Local DB 마이그레이션
File Server 설정
Web Server/WAS 설정
Application Server 설정
정보보안 실습환경 설정
결론
이 계획 문서는 프로젝트의 진행에 따라 계속해서 갱신됩니다. 현재 프로젝트 구현의 큰 흐름이 잡힌 상태입니다. 이러한 계획에 따라 프로젝트를 구현합니다.
2025-06-07 - 초안 작성
2025-06-16 - AdGuard Home 내용 추가
2025-06-17 - DS124 상의 임시 서비스 내용 추가
2025-06-19 - DS124 상의 임시 서비스 내용 삭제 및 Debian (stable - netits) 상의 임시 서비스 내용 추가
2025-06-20 - 날짜 표기 변경
3. 네트워크 장비 설정
3.1. IP Matrix 작성
목표
추후 구현될 Server들이 속할 VLAN과 IP 주소 대역을 깔끔하고 보기 쉽게 관리합니다.
On-premise 환경을 구현하기 앞서 VLAN과 IP를 명확하게 Matrix로 작성합니다.
요구 사항
VLAN Matirx 작성
각 VLAN 별 IP Matrix 작성
Matrix
VLAN
VLAN ID
VLAN 이름
IP 대역(CIDR)
Gateway
DHCP 범위
역할/설명
Untagged VLAN
default LAN
192.168.0.0/24
192.168.0.3
192.168.0.50~254
WTR Pro 외부 Clients
2
Server
192.168.2.0/24
192.168.2.1
N/A
서버 및
관리 인프라
3
Kali
192.168.3.0/24
192.168.3.1
N/A
정보보호 실습
4
Practice
192.168.4.0/24
192.168.4.1
N/A
정보보호 실습
10
VPN
10.0.0.0/24
10.0.0.1
10.0.0.2~254
VPN 할당 대역
Static IP Address
Untagged VLAN
장치/서비스
이름
IP 주소
MAC 주소
주요 서비스 및 포트
접근 정책
비고
T5004
router
ISP 할당 WAN 주소
192.168.0.1
물리 NIC
[WAN: 58-86-94-96-BF-E9]
[LAN: 58:86:94:96:BF:E8]
WAN gateway
Inbound/Outbound 트래픽 제어, NAT 수행
OPNsense
opnsense-vm
192.168.0.2(vtnet0)
192.168.0.3(vtnet1)
vmbr0,1
[NIC0: C8:FF:BF:05:AA:B0]
[NIC1: C8:FF:BF:05:AA:B1]
Firewall, LAN gateway, DHCP, VPN Server, IPS/IDS
Inbound/Outbound 트래픽 제어, 내부 라우팅, 중앙 방화벽
AX6000M
ap
192.168.0.4
 물리 NIC
[58:86:94:43:BC:8F]
AP
AP
Temporary PVE
pve
192.168.0.48
(OPNsense 구축 전)
vmbr1
[NIC1: C8:FF:BF:05:AA:B1]
PVE Web(tmp)
임시, Console에서만 접근 가능
NAS
ds124
192.168.0.5
물리 NIC
[90:09:D0:65:A9:DB]
Backup/File server
Untagged VLAN, VLAN2 접근 가능
PC1
pc-1
192.168.0.6
물리 NIC
[CC:28:AA:A7:D9:33] 
Client/Console
모든 VLAN 접근 가능
PC2
pc-2
192.168.0.7
물리 NIC
[3C:7C:3F:D3:1B:D7]
Client
DNS, Proxy 접근 가능
Peinter
printer
192.168.0.8
물리 NIC
[38:CA:84:94:5E:07]
Client
DNS, Proxy 접근 가능
TV
TV
192.168.0.9
물리 NIC [7C:0A:3F:3D:12:37]
Client
DNS, Proxy 접근 가능
Temporary Server
temp-app
192.168.0.49
(임시 서비스 서버)
vmbr1
[가상 eth: BC:24:11:ED:10:79]
docker host
임시, Console 및 Web 접근 가능,
SSH는 Console 만 가능
↳Docker Services
containers
172.16.0.x
docker bridge
N/A
Reverse Proxy 접근 통해 가능
VLAN2
VLAN3
VLAN4
VLAN10
결론
현재 임시 환경 Matrix 작성 중입니다. 추후 서버 및 정보보호 환경, VPN 환경 구축 시 Matrix 업데이트 예정입니다.
2025-06-19 - 초안 작성
2025-06-20 - IP Matrix MAC 주소 추가
2025-06-23 - VLAN 표기 방식 수정
3.2. T5004/AX6000M 설정
목표
현 집안 환경의 물리적 네트워크 구조 핵심인 T5004와 AX6000M을 설정합니다.
요구 사항
T5004의 초기 설정을 진행합니다.
T5004의 DHCP 서버를 설정합니다.
T5004와 AX6000M의 보안 설정을 합니다.
포트 포워딩 설정
UPnP 설정
라우터 관리 페이지 WAN 접근 차단 설정
관리자 ID/PW 설정
접속 국가 차단 설정
무선 LAN 설정
펌웨어 업데이트를 진행합니다.
설정을 백업합니다.
물리적인 보안 대책을 확인합니다.
수행 작업
초기 설정
시스템 관리
관리자 설정
관리자 계정 설정
로그인 인증 방법
세션 방식
10분간 미사용 시 자동 로그아웃
Captcha 활성화
펌웨어 업그레이드
자동 업그레이드 수행
월 1회 주기적인 재확인
기타 설정
공유기 관리 포트
기본 http 지정
UPnP 설정 끄기
특수 기능
IPTV 설정
IPTV 사용 안함
DHCP 서버 설정
네트워크 관리
인터넷 설정 정보
WAN 정보(ISP 기본 설정) 확인
DNS 주소 확인
DNS 주소는 AdGuard Home 구축 후 변경
WAN MAC 주소 확인
내부 네트워크 설정
내부 IP 주소(T5004) 확인
LAN Subnetmask 확인
DHCP 서버 설정
DHCP 실행
DHCP 설정
대여 범위(Dynamic IP range): 192.168.0.50 ~ 192.168.0.254
Subnetmask: 255.255.255.0
Gateway: 192.168.0.1
OPNsense 구축 후 192.168.0.3으로 변경
OPNsense의 Gateway는 192.168.0.1로, vtnet0(192.168.0.2) 통해서 나감 
 DNS 주소
AdGuard Home 구축 후 변경
작성한 IP Matric를 바탕으로 Static  IP 할당
등록된 주소 관리 + 버튼 클릭
IP 주소, MAC 주소, 설명 추가
IP 재할당을 위하여 재시작
Easy Mesh 관리툴
Easy Mesh 기본 설정
Controller mode 설정
Controller mode 네트워크 이름 및 암호 지정
인증방법 WPA3SAE/WPA2PSK + AES
이름 알림 비활성화
WiFi 설정
2.4GHz, 5GHz SSID 개별 설정
인증방법 WPA3SAE/WPA2PSK + AES
이름 알림 활성화
 6GHz 비활성화
비밀번호 설정
게스트 네트워크 사용 비활성화
AdGuard Home 사용시 필요
Easy Mesh 고급 설정
자동 재시작
주 1회 05:00 순차적 재시작
Easy Mesh 관리
Easy Mesh 연결할 T5004와 AX6000M LAN 포트간 연결 후 Easy Mesh 관리툴 접속
연결 가능한 ipTIME 건색
AX6000M 확인 후 IP 자동으로 받아오기 및 Agent 이름 지정
설정 페이지 비활성화
고급 설정 - 무선 백홀 사용 안함
보안 설정
보안기능
공유기 접속/보안 관리
원격 관리 포트 비활성화
외부 접속 보안 비활성화
내부 접속 보안 비활성화
추후 Reverse Proxy 구축 시 Proxy 및 클라이언트 IP만 허용
국가별 접속 제한
국가 허용
한국, 캐나다만 허용
NAT/라우터 관리
포트포워드 설정
추후 VPN 서버 구축시 VPN 포트 허용
https(443) 포트만 DS124로 포트포워딩
OPNsense 구축 시 OPNsense로 포트포워딩
고급 NAT 설정
인터넷 공유 기능 활성화
포트포워드 UPnP 릴레이 비활성화
설정 백업
시스템 관리
기타 설정
설정 백업 복구
공유기 백업
물리적 보안 대책
통신 단자함 내 T5004 위치
거실 내 AX6000M 위치
미사용 LAN 포트 봉인
결론
네트워크 연결 확인
Static IP 할당 확인
WiFi 설정 확인
물리적 보안을 위한 미사용 LAN 포트 봉인
추후 프로젝트 진행하며 업데이트 예정
2025-06-20 - 초안 작성
4. PVE 설정
4.1. 서비스 UID/GID Matrix 작성
목표
프로젝트 내 모든 서버의 파일 권한을 일관되게 관리하기 위한 체계를 수립합니다.
서버 간 상호 작용을 위한 Local 계정의 대역과, 사람이 사용할 LDAP 계정의 대역을 명확하게 구별하고 대역을 분리합니다.
분리한 대역을 명확하게 Matrix로 작성합니다.
요구 사항
Local UID/GID 대역 정의
각 서버 별 대표 UID/GID 정의
Docker의 UID/GID
FreeIPA LDAP/SSO 전용 UID/GID 대역 정의
Matrix
UID/GID 대역
종류
Local Server
FreeIPA LDAP/SSO
Docker
UID
2000~9999
10000~
Container가 가진 기본 값
GID
2000~9999
*2000(svadmins), *9000(security)
대역 설명
각 서버 관리 계정은 기본적으로 동일한(같은 숫자의) UID:GID를 갖는다.
ex) 2006(dbsvadmin):2006(dbsv)
모든 서버 관리 계정은 2000 svadmins라는 추가 그룹에 속한다.
Docker의 경우 Docker Container가 가진 기본 UID/GID를 사용한다.
도커 유저가 사용하는 파일 및 디렉토리는 기본적으로 [도커 컨테이너 내부 유저]:[호스트 서버 그룹] 형태이다.
ex) 999(mariadb):2006(dbsv)
둘 이상의 서버가 공유하는 파일 및 디렉토리 만이 2000(svadmins)를 guid로 갖는다.
(백업 디렉토리 등)
특정 서버와만 공유 하는 파일 및 디렉튜리는 해당 서버의 gid를 부여한다.
(로그 파일 등)
모든 서버에 그룹 add를 통해 각 서버의 gid를 모두 정의한다.
추후 LDAP를 통한 파일 ACL 관리를 추가한다.
Local UID/GID 정의
서비스
UID
GID
대표 GID
PVE
2000(pveadmin)
2000(svadmins)
2000(svadmins, 대표)
OPNsense
2001
2001
Rocky Linux - FreeIPA
2002
2002
AdGuard Home
2003
2003
Monitoring Server
2004
2004
Proxy Server
2005
2005
DB Server
2006
2006
File Server
2007
2007
Web Server/WAS
2008
2008
Application Server
2009
2009
임시 Debian Server
2999(temp-app)
2000(svadmins)
Kali
9000
9000(security)
9000(security)
Pactice Client
9001~9999
9001~9999
FreeIPA LDAP/SSO UID/GID 정의
서비스
UID
GID
FreeIPA LDAP/SSO
10000~
10000~
결론
프로젝트를 진행하며 LDAP/SSO에 부여할 UID와 GID를 세분화 하여 업데이트 예정입니다.
2025-06-21 - 초안 작성
2026-06-27 - uid/gid 관리 방법 세분화
4.2. PVE 설치
목표
PVE를 WTR Pro에 설치합니다.
설치 절차 및 방법을 순서에 따라 명확하게 문서로 기록합니다.
요구 사항
PVE를 WTR Pro에 설치
PVE 원격 접속
PVE의 초기 설정
수행 작업
PVE 설치 사전 준비
설치 USB 준비
프로젝트를 위하여 많은 OS 설치 파일이 필요합니다. 이러한 OS 설치 파일을 USB에 하나씩 넣기에는 비효율적입니다. 따라서 Ventoy라는 소프트웨어를 준비하여 하나의 USB로 여러 가지 OS 설치 파일을 한 번에 관리할 수 있도록 합니다.
사용 USB는 Samsung FIT Plus 64GB 입니다.
Ventoy 설치
Ventoy는 오픈 소스로 여러 OS 설치 파일을 USB에 복사하여 부팅 가능하게 만들 수 있는 USB 드라이브 생성도구입니다.
공식 사이트: https://www.ventoy.net/en/download.html
파일을 다운 받은 후, Powershell에서  Get-FileHash -Path [file_path] -Algorithm SHA256 명령어를 통해 해시 값을 확인
공식 사이트에 명시된 Hash값과 차이 없을 시 준비된 Ventoy USB에 ISO 파일 복사(무결성 확인)
USB를 연결 후 Ventoy 파일을 실행하고,  Install 버튼을 눌러 USB에 Ventoy 설치
이후 OS 설치 파일을 마운트 된 Ventoy Directory에 복사
PVE 설치 파일 준비
공식 사이트: https://www.proxmox.com/en/downloads
파일을 다운 받은 후, Powershell에서  Get-FileHash -Path [file_path] -Algorithm SHA256 명령어를 통해 해시 값을 확인
공식 사이트에 명시된 Hash값과 차이 없을 시 준비된 Ventoy USB에 ISO 파일 복사(무결성 확인)
PVE 설치
WTR Pro 부팅
WTR Pro에 USB 연결 후 전원 켜기
Ventoy 화면에서 PVE 설치 파일을 선택
Boot in normal mode 선택
PVE 설치
Install Proxmox VE (Graphic) 선택
약관 동의
저장소 선택 (SSD, 기본 Ext4)
국가, 타임 존, 키보드 레이아웃 선택
South Korea
Asia/Seoul
U.S. English
(root) 비밀번호, E-mail 지정
NIC 지정
Hostname(FQDN): pve.example.com
IP(CIDR)
Gateway/DNS: DHCP
Summary 확인 후 Install 선택
CLI 상 https://[ip_address]:8006 확인
브라우저 상에서 위 주소로 Proxmox Web UI 접속
보안 경고시, 고급 -  안전하지 않음으로 이동 선택
PVE 초기 설정
PVE 로그인
User name: root
Password: 설치 시 설정한 Password
언어: 한국어 - 국어
사용자 이름 저장: 미체크
PVE Enterprise 라이센스 해지
Web UI 나 Shell 중에 하나 선택하여 라이센스 해지
Web UI
데이터센터 - pve - 업데이트 -  리포지토리 에서 다음 항목  비활성화
https://enterprise.proxmox.com/debian/ceph-quincy
https://enterprise.proxmox.com/debian/pve
Shell
#!/bin/bash
vi /etc/apt/sources.list.d/pve-enterprise.list
#편집(주석 처리)
#https://enterprise.proxmox.com/debian/ceph-quincy
vi /etc/apt/sources.list.d/ceph.list
#편집(주석 처리)
#https://enterprise.proxmox.com/debian/pve
유효한 구독 없음 팝업 비활성화
Shell
#!/bin/bash
cd /usr/share/javascript/proxmox-widget-toolkit
#파일 백업
cp proxmoxlib.js proxmoxlib.js.bak
vi proxmoxlib.js
#ESC+'/' 이후 No valid 검색
#.data.status.toLowerCase() !== 'active') 행을
#.data.status.toLowerCase() == 'active') 로 변경
systemctl restart pveproxy.service
#서비스 재시작
브라우저 Cache가 남아 있는 경우 계속 팝업이 활성화 될 수 있으므로 강화된 새로고침 필수
추후 PVE 업데이트 시 다시 활성화 될 수 있으므로, 업데이트 후 재실행
관리자 계정 생성
계정 생성
Shell
#!/bin/bash
apt update
apt install sudo -y
apt update
#sudo 명령어 설치
groupadd -g 2000 svadmins
useradd -u 2000 -g 2000 -m -s /bin/bash -c "Proxmox Admin" -G sudo pveadmin
#-m: 홈 디렉터리 자동 설정, -s: 셸, -c: 설명, -G sudo: sudo group 포함
passwd pveadmin
#Password 설정
id pveadmin
#결과 예시: uid=2000(pveadmin) gid=2000(pveadmins) groups=2000(pveadmins),27(sudo)
Web UI
데이터센터 - 사용자 - 추가
계정: pveadmin
영역: Linux PAM standard authentication
email
비밀번호
추가
데이터센터 -  권한 - 추가 - 사용자 권한
경로: /  
사용자: pveadmin
역할: administration
root 계정 ssh 로그인 금지
Shell
#!/bin/bash
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
sudo vi /etc/ssh/sshd_config
#PermitRootLogin no로 변경
sudo service sshd restart
Web UI
데이터센터 - 사용자 - root 선택 - 수정
활성화 됨 체크 해제
iptables 설정
Shell
#!/bin/bash
sudo apt install iptables-persistent #debian iptables 저장 패키지
#Save current IPv4 Rulse <yes> 선택
# 명령어 설명
# 1. 모든 규칙 초기화
iptables -F
iptables -X
iptables -t nat -F
iptables -t nat -X
iptables -t mangle -F
iptables -t mangle -X
# 2. 기본 정책 설정 (Default-Drop)
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
# 3. 필수 규칙 추가
# 로컬호스트 허용
iptables -A INPUT -i lo -j ACCEPT
# 이미 연결된 세션 허용
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# 4. 서비스별 규칙 추가
# SSH (22/tcp) 허용
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# 웹서버 (80/tcp, 443/tcp) 허용
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
# Ping (ICMP) 허용
iptables -A INPUT -p icmp -j ACCEPT
# 5. 설정된 규칙 확인
iptables -L -v -n
# 실제 적용 규칙
iptables -F
iptables -X
iptables -t nat -F
iptables -t nat -X
iptables -t mangle -F
iptables -t mangle -X
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT # 연결된 세션 및 연관된 패킷 허용 # 연결된 세션 및 연관된 패킷 허용 *가장 먼저 적용*
iptables -A INPUT -i lo -j ACCEPT #local host 접근 허용
iptables -A INPUT -p icmp -j ACCEPT
iptables -A INPUT -s 192.168.0.6 -p tcp --dport 22 -j ACCEPT#콘솔 PC에서만 ssh 접근 허용
iptables -A INPUT -s 192.168.0.6 -p tcp --dport 8006 -j ACCEPT #콘솔 PC에서만 WEB UI 접근 허용
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
#*허용 규칙 이후 차단 규칙 적용*
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
iptables -L -v -n
sudo netfilter-persistent save 현재 설정 저장
PVE NIC 설정
vmbr
PVE내에서 가상 L2 스위치 처럼 동작합니다.
일반적으로 vmbr에 할당된 IP 주소는 PVE의 host IP 입니다.
vmbr은 L2 스위치이므로 IP주소를 할당하지 않아도 됩니다. (PVE Web UI에 접근하는 IP 주소 제외)
VLAN을 지원합니다.
STP(Spanning Tree Protocol; IEEE 802.1D)가 기본적으로 비활성화 되어 있습니다.
vmbr 설정
추후 OPNsense가 WAN/LAN 통신시 다른 물리 NIC로 통신하기 위하여 vmbr을 두 개 생성합니다. 이때, vmbr0은 이미 PVE의 웹 host용 vmbr로 생성되어 있습니다. 따라서 계획에 따라 vmbr1을 생성하고, vmbr1에 PVE의 Web용 host와 임시 Server를 연결합니다.
데이터센터 - pve - 시스템 - 네트워크 - vmbr0 - 수정
IPv4/CIDR 삭제
Gateway 삭제
생성
이름: vmbr1
IPv4/CIDR: vmbr0의 내용
Gateway: vmbr0의 내용
자동시작 체크
브릿지 포트: enp3s0 (NIC1)
생성
설정 적용
STP
STP는 Switch - Switch 간 브로드캐스트 발생 시 서로에게 무한하게 Broadcast 패킷을 보내는 루프로 인한 브로드캐스팅 스톰을 방지하기 위한 프로토콜입니다. 따라서, Switch가 STP를 지원하지 않으면 Switch - Switch간 이중화된 연결이 불가능합니다. 따라서, 한 vmbr 안에 여러 NIC를 할당하려면, STP를 활성화하여야, 네트워크가 정상 작동합니다. (권장되는 방식은 LACP를 사용하여, Bonding을 구성하는 것입니다.)
#!/bin/bash
#STP 활성화 방법
#shell 접속
cat /etc/network/interfaces
sudo vi /etc/network/interfaces
iface vmbr0 inet static
address 192.168.0.48/24
gateway 192.168.0.1
bridge-ports enp2s0 # NIC 할당
bridge-stp on #STP 활성화(이 부분을 off에서 on으로 바꿔야 STP 활성화)
# bridge-fd 0 #이 부분은 STP 활성화 시, 주석 처리하는 것이 권장 됩니다.
#:wq!
sudo systemctl restart networking #network.d 재실행
다만, OPNsense에 두 NIC에 같은 LAN 대역의 다른 IP를 부여하여도, OPNsense 내에서 두 NIC를 각각 다른 End point 취급하므로(Switch 처럼 작동하지 않으므로) 정상 작동합니다.
결론
WTR Pro에 PVE 설치 완료
PVE 원격 접속 가능
admin 계정 생성 및 root 계정 설정
root 계정 ssh 접근 비활성화
root 계정 Web UI 접근 비활성화
pveadmin 계정 생성(2000:2000, sudo)
Web UI 상 pveadmin 권한 부여
 vmbr 생성 및 설정
STP로 비활성화로 인한 주의점 명시
2025-06-21 - 초안 작성
2025-06-23 - vmbr 생성 및 설정, 주의점 추가
2025-06-25 - iptables-persistent 설치 및 규칙 추가
4.3. PVE 상 VM/LXC 설치법
목표
PVE 상 VM/LXC 설치법을 확인합니다.
PVE 상 임시 Server (Debian)을 설치합니다.
설치 절차 및 방법을 순서에 따라 명확하게 문서로 기록합니다.
요구 사항
임시 Server(Debain)을 PVE에 설치
수행 작업
VM/LXC 설치 전 사전 준비
VM과 LXC간 차이점
VM은 PVE와 분리된 별도의 OS입니다. PVE를 통해서 가상화된 하드웨어 위에 커널부터 격리된 OS를 설치합니다. 이와 반대로 LXC는 PVE와 커널을 공유하므로, 별도의 OS 설치 파일 없이 PVE 위에서 바로 생성이 가능합니다.
설치 파일 준비 (VM)
Debian 설치 파일 준비
공식 사이트: https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/
Debian-netins ISO 파일과 checksum 파일 다운로드
파일을 다운 받은 후, Powershell에서  Get-FileHash -Path [file_path] -Algorithm SHA256 명령어를 통해 Hash 값을 확인
checksum 파일에 명시된 Hash값과 차이 없을 시 ISO 파일 관리용 USB에 Debian-netins 파일 복사(무결성 확인)
기타 설치 파일 준비
PVE는 Linux 뿐 아니라, Unix, Windows 등 다양한 OS를 지원하므로 VM 추가 필요시 해당 설치 파일을 구하여 설치할 수 있습니다.
설치
VM 설치
ISO 파일 업로드
데이터센터 - PVE -  local(pve) - ISO 이미지 - 업로드
파일 선택
OS 설치 파일 선택
해시 알고리즘 선택
해시 값 붙여 넣기
업로드
TASK OK 확인
VM 설치
데이터센터 - PVE 우클릭
VM 생성 클릭
일반 설정
노트: pve
VM ID: 서버별 대표 UID와 동일하게 관리 - 임시 Server: 2999
이름: temp-app
부팅시 시작 체크
시작/종료 순서, 시작 지연, 종료 시간 제한: 추후 VM이 늘어날 때 설정
OS 설정
CD/DVD 디스크 이미지 파일(ISO) 사용
ISO 이미지 선택
게스트 OS
유형: Linux (설치할 OS 종류에 따라)
버전: 6.x - 2.6 Kernel (설치할 OS 종류에 따라)
시스템 설정
기본 설정
디스크 설정
스토리지: local-lvm 선택
삭제(Discard): 체크 (파일이 삭제될 때 마다 SSD에 블록이 비었다고 알리는 형식, 실시간 TRIM)
디스크 크기: 59.60; 64GB (VM에 할당할 용량을 GiB 단위로 입력)
PVE는 기본 용량 단위를 iB 체계를 사용하므로, B체계 사용 시 변환 작업 필요
추후 VM - 하드웨어 - 디스크 Action 에서 디스크 작업 크기조정으로 용량 변경 가능, VM OS 내 파티션 변경 작업 별도
버스/디바이스: VirtlO Block (기본 값, 가장 좋은 성능)/VM 내부 디스크의 순서
디스크 형식: Raw Disk Image (약간의 성능상 우위, 고급기능 X)
 CPU 설정
소켓: 1 (pCPU 갯수, 1개) 
Cores: 2 (원하는 vCPU 갯수 만큼)
vCPUs: 2 (Socket x Cores)
CPU 제한: (추후 VM 여러 개 생성시 지정)
Memory 설정
Memory: 5722(약 6GB) (계획에 따라. MiB 단위로 입력)
네트워크 설정
브릿지: vmbr1 (계획에 따라)
모델: VirtlO
VLAN 태그: 없음 (계획에 따라)
방화벽 체크 해제 (추후 iptable등 UFW 사용)
추후 VM - 하드웨어 탭에서 네트워크 인터페이스 추가 가능
확인
생성 후 시작 체크 해제
VM - 하드웨어 - 네트워크 디바이스 에서 생성된 MAC 주소 확인하여 DHCP 예약 추가
VM - 옵션 - 부팅 순서 변경
virtio(HDD/SSD)
ide(CD/DVD)
net(network) 
해당 설정 미실행시, 부팅 불가 현상 발생 가능
LXC 설치
LXC 템플릿 다운로드
데이터센터 - PVE -  local(pve) - LXC 템플릿 - 템플릿
패키지 선택
debian 선택
다운로드
TASK OK 확인
LXC 설치(VM과 다른 부분만)
데이터센터 - PVE 우클릭
CT 생성 클릭
일반 설정
CT ID: 서버별 대표 UID와 동일하게 관리
권한 없는 컨테이너 체크 (unprivileged LXC)
부팅시 시작 체크
시작/종료 순서, 시작 지연, 종료 시간 제한: 추후 VM이 늘어날 때 설정
비밀번호 입력
SSH 공개키 입력 (선택 사항)
템플릿
템플릿 선택
디스크 설정
마운트 옵션: Discard
CPU
Cores만 선택
네트워크
이름: eth0
브릿지: vmbr1
IPv4: Static/DHCP 선택
DNS
확인
생성 후 시작 체크 해제
LXC - 하드웨어 - 네트워크 디바이스 에서 생성된 MAC 주소 확인하여 DHCP 예약 추가
결론
임시 Server VM 설치 준비 완료
2025-06-24 - 초안 작성 완료
5. 임시 Server 설정
5.x. NPM 설정
목표
임시 Server에 NPM을 설치합니다.
설치 절차 및 방법을 순서에 따라 명확하게 문서로 기록합니다.
요구 사항
NPM 설치
NPM에 내부망 접속
NAT 설정
SSL/TLS 이용을 위한 인증서를 적용
수행 작업
docker-compose-npm.yml 작성
도커 설정
이미지
jc21/nginx-proxy-manager:latest
 포트
81:81
80:80
443:443
볼륨 마운트
/home/temp-app/docker/nginx-proxy-manager/data:/data
/home/temp-app/docker/nginx-proxy-manager/
환경 변수
없음
DB 연동시 다음과 같은 환경변수 설정 필요(MYSQL 부분은, POSTGRES 사용 시 POSTGRES로 대체 가능)
DB_MYSQL_HOST
DB_MYSQL_PORT
DB_MYSQL_USER
DB_MYSQL_PASSWORD
DB_MYSQL_NAME
하지만 현재 임시 서버 환경이므로 간단한 SQLite를 사용(환경 변수 필요 없음)
docker-compose.yml 작성
도커 컨테이너를 위한 디렉터리 생성
mkdir -p /home/temp-app/docker/nginx-proxy-manager/{data,letsencrypt,websites}
디렉토리 권한 변경
chmod -R 755 /home/temp-app/docker/ngnix-proxy-manager
chmod -R 700 /home/temp-app/docker/ngnix-proxy-manager/letsencrypt
/home/temp-app/docker/ngnix-proxy-manager 위에서 docker-compose-npm.yml 작성
docker-compose.yml은 'tab'을 인식하지 않으므로 'space'를 사용 
services:
app:
image: 'jc21/nginx-proxy-manager:latest'
restart: unless-stopped
ports:
# These ports are in format <host-port>:<container-port>
- '80:80' # Public HTTP Port
- '443:443' # Public HTTPS Port
- '81:81' # Admin Web Port
# environment:
# Uncomment this if you want to change the location of
# the SQLite DB file within the container
# Mysql/Maria connection parameters:
#DB_MYSQL_HOST: "db"
#DB_MYSQL_PORT: 3306
#DB_MYSQL_USER: "npm"
#DB_MYSQL_PASSWORD: "npm"
#DB_MYSQL_NAME: "npm"
# Postgres parameters:
#DB_POSTGRES_HOST: 'db'
#DB_POSTGRES_PORT: '5432'
#DB_POSTGRES_USER: 'npm'
#DB_POSTGRES_PASSWORD: 'npmpass'
#DB_POSTGRES_NAME: 'npm'
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
#the direcrtory for static web files which you want to host
- ./websites:/mnt/user/appdata/NginxProxyManager/websites
도커 실행
docker-compose.yml 파일이 있는 곳에서 docker compose -f docker-compose-npm.yml up -d 명령어 실행
iptables 설정 변경
sudo iptables -A INPUT -s 192.168.0.6 -p tcp --dport 81 -j ACCEPT # 콘솔 PC에서만 웹 UI 접근 허용
sudo iptables -L -v -n # 설정 확인
sudo netfilter-persistent save # 설정 적용
기존에 iptables 설정에서 이미 -p tcp --dport 443, -p tcp --dport 80은 이미 허용
웹 UI 접속
초기 설정
[temp-app_ip]:81로 접속
초기 계정: admin@example.com/changeme
Fullname, nickname, password 입력하여 변경
비밀번호 변경
SSL/TLS 인증서 발급
인증서 발급 전 T5004의 포트 포워딩 규칙 추가
443포트를 temp-app 서버로 포트 포워딩
메뉴 - SSL certificates - add SSL certificate
Domain names: example.com
email adress for Let's Encrypt
Use DNS Challenge
DNS providor: cloudflare
Credentials File Content: API 키 입력(DDNS 사용 시 사용했던 것)
메뉴 - proxy hosts - add new proxy host 
결과
5.4. DB 설치
목표
임시 Server에 DBMS를 설치합니다.
설치 절차 및 방법을 순서에 따라 명확하게 문서로 기록합니다.
요구 사항
MariaDB 설치
PostgreSQL 설치
DBMS 계정 설정
Redis 설치
수행 작업
결과
5.1. Debian 설치
목표
PVE 상 Debian을 설치합니다.
설치 절차 및 방법을 순서에 따라 명확하게 문서로 기록합니다.
요구 사항
Debian VM을 PVE 위에 설치
Debian의 초기 설정
수행 작업
Debian 설치
VM 실행
데이터센터 - pve - 2999(temp-app) - 콘솔
Start now
Debian 설치
Debian 설치 화면  Graphic Install 클릭
언어 및 위치 설정
언어: korean
위치: 대한민국
키보드 설정: 한국어
네트워크 설정
호스트 이름: temp-app
도메인 네임: temp-app.example.com
사용자 계정 설정
root 암호 설정
사용자 이름: temp-app
사용자 암호 설정
디스크 설정
디스크 파티션 설정: 자동 - 디스크 전체 사용
가상 디스크 선택
파티션: 모두 한 파티션에 사용 (추후 다른 서버 구축할 때 NFS 적용시, 분리 필요)
파티션 나누기를 마치고 바뀐 사항을 디스크에 쓰기
바뀐 점을 디스크에 쓰시겠습니까?: 예
패키지 관리자
추가 설치 미디어를 검사하겠습니까?: 아니요
지역: 대한민국
아카이브 미러 사이트: deb.debian.org
http 프록시 설정: 없음
패키지 인기 투표: 안함
소프트웨어 선택
SSH Server 체크
표준 시스템 유틸리티 체크
나머지 전부 체크 해제
GRUB 부트로더
사용
장치: 메인 저장소(/dev/vda 등)
ISO 파일 제거
데이터센터 - pve -  2999(temp-app)  -  하드웨어 - CD/DVD 드라이브 - 수정
미디어 사용 안함
VM 재시작
데이터센터 - pve - 2999(temp-app) - 콘솔
계속
Debian 초기 설정
Debian 로그인
root/password
필수 패키지 설치
#!/bin/bash
apt update
apt install sudo curl iptables-persistent #sudo와 curl, iptables-persistent 기본 설치 안되어 있음
apt update
계정 정보 확인 및 수정
#!/bin/bash
#계정 정보 확인
cat /etc/passwd
#temp-app 1000:1000 처음 설치시 설정한 계정 temp-app이 1000:1000이므로 수정
groupadd -g 2000 svadmins #svadmins 그룹 생성
usermod -u 2999 -g 2000 -s /bin/bash -c "Temp-app Admin" -G sudo temp-app
#-u [UID] -g [GID] -s [shell] -c [discription] -G [additional group] user_name
#User name을 변경하고 싶을 시 -l [user_name]을 입력한다.
id temp-app
#결과 예시: uid=2999(temp-app) gid=2000(pveadmins) groups=2000(pveadmins),27(sudo)
root 계정 ssh 로그인 금지
sshd_config 확인
#!/bin/bash
cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
cat /etc/ssh/sshd_config
#PermitRootLogin 옵션 확인 후 명시적인 차단
vi /etc/ssh/sshd_config
#PermitRootLogin no
sudo service sshd restart
ssh 접속하여 root 로그인 가능 여부 확인
ssh [id]@[ip]
접속 불가 확인(permission denied)
ssh 접속하여 일반 계정 로그인 가능 여부 확인
ssh [id]@[ip]
접속 가능 확인
iptables 설정
#!/bin/bash
sudo apt install iptables-persistent #debian iptables 저장 패키지
#Save current IPv4 Rulse <yes> 선택
sudo apt update
# 명령어 설명
# 1. 모든 규칙 초기화
iptables -F
iptables -X
iptables -t nat -F
iptables -t nat -X
iptables -t mangle -F
iptables -t mangle -X
# 2. 기본 정책 설정 (Default-Drop)
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
# 3. 필수 규칙 추가
# 로컬호스트 허용
iptables -A INPUT -i lo -j ACCEPT
# 이미 연결된 세션 허용
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# 4. 서비스별 규칙 추가
# SSH (22/tcp) 허용
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# 웹서버 (80/tcp, 443/tcp) 허용
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
# Ping (ICMP) 허용
iptables -A INPUT -p icmp -j ACCEPT
# 5. 설정된 규칙 확인
iptables -L -v -n
# 실제 적용 규칙
iptables -F
iptables -X
iptables -t nat -F
iptables -t nat -X
iptables -t mangle -F
iptables -t mangle -X
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT # 연결된 세션 및 연관된 패킷 허용 # 연결된 세션 및 연관된 패킷 허용 *가장 먼저 적용*
iptables -A INPUT -i lo -j ACCEPT #local host 접근 허용
iptables -A INPUT -p icmp -j ACCEPT
iptables -A INPUT -s 192.168.0.6 -p tcp --dport 22 -j ACCEPT#콘솔 PC에서만 ssh 접근 허용
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
#*허용 규칙 이후 차단 규칙 적용*
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
iptables -L -v -n
sudo netfilter-persistent save 현재 설정 저장
결론
PVE 상 Debian 설치 완료
root 계정 및 local 계정 설정
ssh root 계정 접근 차단
필수 패키지(iptables, sudo, curl) 설치 완료
iptables 규칙 적용
2025-06-25 - 초안 작성
5.2. DDNS 설정
목표
임시 Server에서 Cloudflare API를 통한 DDNS를 설정합니다.
설정 절차 및 방법을 순서에 따라 명확하게 문서로 기록합니다.
요구 사항
Cloudflare API를 통한 DDNS 설정 shell script 프로그램 작성
도메인 등록, 갱신, 삭제 기능 구현
cron을 통한 shell script 자동 실행
수행 작업
Cloudflare Domain 구입
https://www.cloudflare.com 접속
로그인 후 대시보드 로 이동
도메인 등록 - 도메인 등록 으로 이동하여 도메인 구입
도메인 등록 - 도메인 관리 로 이동하여 구매 도메인 활성 확인
DDNS 스크립트 작성
API 키 확인
https://www.cloudflare.com 접속
내 프로필 - API 토큰 으로 이동
토큰 생성 - 영역 DNS 편집 - 템플릿 사용
권한
영역 - DNS - 편집
영역 리소스
포함 - 특정 영역 - Domain
포함 -
요약 계속 - 토큰 생성
이 토큰 테스트에 있는 명령어 실행하여 정상 작동하는 지 확인 후 복사
#!/bin/bash
curl "https://api.cloudflare.com/client/v4/user/tokens/verify" \
-H "Authorization: Bearer $API_KEY"
#결과 예시: {"result":{"id":"---","status":"active"},"success":true,"errors":[],"messages":[{"code":10000,"message":"This API Token is valid and active","type":null}]}
계정 홈 - 사용할 도메인 클릭
API - 영역 ID(Zone_ID) 확인 후 복사
Cloudflare API 스크립트 확인
https://developers.cloudflare.com/api 접속
Ctrl + k 눌러 DNS Record 검색 후 이동
API 스크립트 확인
#!/bin/bash
#List DNS Records
curl "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?name=$DOMAIN" \
-H "Authorization: Bearer $API_KEY"
#결과가 없을 시 "result": [], success: "true", ...
#결과 형식 "result": [{..., ...}, {..., ...},...], success: "true", ...
#Create DNS Record
curl "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records" \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $API_KEY" \
-d "{
\"name\": \"$DOMAIN\",
\"ttl\": 3600,
\"type\": \"A\",
\"comment\": \"Domain verification record\",
\"content\": \"$IP\",
\"proxied\": true
}"
#Overwrite DNS Record
curl "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$DNS_RECORD_ID" \
-X PUT \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $API_KEY" \
-d "{
\"name\": \"$DOMAIN\",
\"ttl\": 3600,
\"type\": \"A\",
\"comment\": \"Domain verification record\",
\"content\": \"$IP\",
\"proxied\": true
}"
#Delete DNS Record
curl "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$DNS_RECORD_ID" \
-X DELETE \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $API_KEY" \
}"
Shell script 작성
Script 파일을 생성합니다.
touch /home/temp-app/ddns/ddns.sh
touch /home/temp-app/ddns/ddns.conf
Script 파일 실행 권한을 부여합니다.
chmod 744 /home/temp-app/ddns/ddns.sh
설정 파일에 소유자 읽기 권한만 부여합니다.
chmod 600 /home/temp-app/ddns/ddns.conf
Script 파일을 작성합니다.
getopts 를 활용하여 Domain, proxied, ttl, 삭제 여부 등을 매개변수로 받습니다.
함수를 활용하여, Cloudflare DNS API기능을 정의합니다. (조회, 생성, 업데이트, 삭제)
최대한 범용성 있는 스크립트를 만듭니다.
vi /home/temp-app/ddns/ddns.sh
#!/bin/bash
DOMAIN=""
TTL=180 # basic value
C_TTL=86400
PROXIED="false" # basic value
DELETE_FLAG=0
CURRENT_IP=""
FLAG=""
# usage func
usage() {
echo "Usage: $0 -d \"domain\" [-t \"ttl\"] [-p] [-r] [-c]"
echo " -d <domain> : Specify the domain to update."
echo " -t <ttl> : Specify the TTL (Time to Live)."
echo " -p : Specify the cloudflare proxy to use."
echo " -r : Delete the DNS record."
exit 1
}
# using getopts to get arguemnts
while getopts "d:t:pr" opt; do
case $opt in
d)
DOMAIN="$OPTARG"
;;
t)
TTL="$OPTARG"
;;
p)
PROXIED="true"
;;
r)
DELETE_FLAG=1
;;
\?) # unknown options
echo "Invalid option: -$OPTARG" >&2
usage
;;
:) # paramenter required option
echo "Option -$OPTARG requires an argument." >&2
usage
;;
esac
done
# get option and move to parameters
shift $((OPTIND - 1))
# check necessary option
if [ -z "$DOMAIN" ]; then
echo "Error: -d option (domain) is required." >&2
usage
fi
if ! [[ "$TTL" =~ ^[0-9]+$ ]] || [ "$TTL" -le 0 ]; then
echo "Error: -t option (ttl) requires a number above 0." >&2
usage
fi
# change directory for cron
cd "$(dirname "$0")"
# make config directory and log directory
if [ ! -d "./config" ]; then
mkdir ./config
fi
if [ ! -d "./log" ]; then
mkdir ./log
fi
LOG_FILE="./log/ddns_$(date "+%Y-%m-%d").log"
CONF_FILE="./config/ddns.conf"
# log func
log()
{
local text="$1"
echo "---------" >> "$LOG_FILE"
echo -e "$(date "+%Y-%m-%d %H:%M:%S"): $text" >> $LOG_FILE
}
# check and create log file
if [ ! -f "$LOG_FILE" ]; then
log "Notice: log file is created"
fi
# check and create conf file
if [ ! -f "$CONF_FILE" ]; then
log "Error: Set ./config/ddns.conf first"
echo -e "#!/bin/bash" >> $CONF_FILE
echo -e "# --- ddns.conf ---" >> $CONF_FILE
echo -e "ZONE_ID=\"\"" >> $CONF_FILE
echo -e "API_KEY=\"\"" >> $CONF_FILE
chmod 600 "$CONF_FILE"
exit
fi
# check environmental value
source "$CONF_FILE"
if [ -z "$ZONE_ID" -o -z "$API_KEY" -o -z "$DOMAIN" ]; then
log "Error: There is no correct option in \"ddns.conf\"\nZONE_ID, API_KEY are needed"
exit
fi
# check package
if ! command -v curl &> /dev/null; then
log "Error: curl package is needed"
exit
fi
if ! command -v jq &> /dev/null; then
log "Error: jq package is needed"
exit
fi
# API options
URL="https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records"
CONTENT_TYPE="Content-Type: application/json"
AUTHORIZATION="Authorization: Bearer $API_KEY"
CURRENT_IP=$(curl -sf "https://ifconfig.me") ||\
CURRENT_IP=$(curl -sf "https://ifconfig.kr") ||\
CURRENT_IP=$(curl -sf "https://api.ipify.org")
if [ "$CURRENT_IP" == "" ]; then
log "Error: Can't get an IP"
exit
fi
get_dns_record()
{
local type="$1"
local name="$2"
local response="$(
curl -s "$URL?type=$type&name=$name"\
-H "$CONTENT_TYPE"\
-H "$AUTHORIZATION")"
if [ "$(echo "$response" | jq -r '.success')" == "false" ]; then
log "Error: Can't get dns record\"Reason: $response"
exit
else
echo "$response"
fi
}
create_dns_record()
{
local type="$1"
local name="$2"
local ttl="$3"
local comment="$4"
local content="$5"
local response="$(
curl -s "$URL"\
-X POST\
-H "$CONTENT_TYPE"\
-H "$AUTHORIZATION"\
-d "{
\"name\": \"$name\",
\"ttl\": $ttl,
\"type\": \"$type\",
\"comment\": \"$comment\",
\"content\": \"$content\",
\"proxied\": $PROXIED
}")"
if [ "$(echo "$response" | jq -r '.success')" == "false" ]; then
log "Error: Can't create dns record\"Reason: $response"
exit
else
echo "$response"
fi
}
update_dns_record()
{
local type="$1"
local name="$2"
local ttl="$3"
local comment="$4"
local content="$5"
local id="$6"
local response=$(
curl -s "$URL/$id"\
-X PUT\
-H "$CONTENT_TYPE"\
-H "$AUTHORIZATION"\
-d "{
\"name\": \"$name\",
\"ttl\": $ttl,
\"type\": \"$type\",
\"comment\": \"$comment\",
\"content\": \"$content\",
\"proxied\": $PROXIED
}")
if [ "$(echo "$response" | jq -r '.success')" == "false" ]; then
log "Error: Can't update dns record\"Reason: $response"
exit
else
echo "$response"
fi
}
delete_dns_record()
{
local type="$1"
local id="$2"
local response=$(
curl -s "$URL/$id"\
-X DELETE\
-H "$CONTENT_TYPE"\
-H "$AUTHORIZATION"
)
if [ "$(echo "$response" | jq -r '.success')" == "false" ]; then
log "Error: Can't delete dns record\"Reason: $response"
exit
else
echo "$response"
fi
}
A_DNS_RECORD=$(get_dns_record "A" "$DOMAIN")
S_DNS_RECORD=$(get_dns_record "cname" "*.$DOMAIN")
W_DNS_RECORD=$(get_dns_record "cname" "www.$DOMAIN")
if [ "$DELETE_FLAG" -eq 1 ]; then # Delete flag
FLAG="false"
if [ "$(echo $A_DNS_RECORD | jq -r '.result | length')" -eq 1 ]; then
A_DNS_ID="$(echo $A_DNS_RECORD | jq -r '.result[0].id')"
delete_dns_record "A" "$A_DNS_ID"
log "Delete: root DNS record is deleted"
FLAG="true"
fi
if [ "$(echo $S_DNS_RECORD | jq -r '.result | length')" -eq 1 ]; then
S_DNS_ID="$(echo $S_DNS_RECORD | jq -r '.result[0].id')"
delete_dns_record "cname" "$S_DNS_ID"
log "Delete: sub DNS record is deleted"
FLAG="true"
fi
if [ "$(echo $W_DNS_RECORD | jq -r '.result | length')" -eq 1 ]; then
W_DNS_ID="$(echo $W_DNS_RECORD | jq -r '.result[0].id')"
delete_dns_record "cname" "$W_DNS_ID"
log "Delete: www DNS record is deleted"
FLAG="true"
fi
if [ "$FLAG" == "false" ]; then
log "Notice: Nothing is Deleted. There are no DNS records"
fi
exit
fi
if [ "$(echo $A_DNS_RECORD | jq -r '.result | length')" -eq 1 ]; then # root DNS record exist
A_DNS_ID="$(echo $A_DNS_RECORD | jq -r '.result[0].id')"
A_DNS_CONTENT="$(echo $A_DNS_RECORD | jq -r '.result[0].content')"
A_DNS_TTL="$(echo $A_DNS_RECORD | jq -r '.result[0].ttl')"
A_DNS_PROXIED="$(echo $A_DNS_RECORD | jq -r '.result[0].proxied')"
if [ "$A_DNS_CONTENT" != $CURRENT_IP -o "$A_DNS_TTL" != "$TTL" -o "$A_DNS_PROXIED" != "$PROXIED" ]; then
update_dns_record "A" "$DOMAIN" "$TTL" "$(date "+%Y-%m-%d %H:%M:%S"): root domain from ddns.sh" "$CURRENT_IP" "$A_DNS_ID"
log "Update: Root DNS record is successfully changed\nDomain: $DOMAIN\nIP: $A_DNS_CONTENT to $CURRENT_IP\nTTL: $A_DNS_TTL to $TTL\nproxied: $A_DNS_PROXIED to $PROXIED"
else
log "Notice: Root DNS record is not changed\nDomain: $DOMAIN\nIP: $CURRENT_IP\nTTL: $TTL\nproxied: $PROXIED"
fi
else # root DNS record does not exist
create_dns_record "A" "$DOMAIN" "$TTL" "$(date "+%Y-%m-%d %H:%M:%S"): root domain from ddns.sh" "$CURRENT_IP"
log "Create: Root DNS record is successfully created\nDomain: $DOMAIN\nIP: $CURRENT_IP\nTTL: $TTL\nproxied: $PROXIED"
fi
if [ "$(echo $S_DNS_RECORD | jq -r '.result | length')" -eq 1 ]; then # sub DNS record exist
S_DNS_ID="$(echo $S_DNS_RECORD | jq -r '.result[0].id')"
S_DNS_CONTENT="$(echo $S_DNS_RECORD | jq -r '.result[0].content')"
S_DNS_TTL="$(echo $S_DNS_RECORD | jq -r '.result[0].ttl')"
S_DNS_PROXIED="$(echo $S_DNS_RECORD | jq -r '.result[0].proxied')"
if [ "$S_DNS_CONTENT" != "$DOMAIN" -o "$S_DNS_TTL" != "$C_TTL" -o "$S_DNS_PROXIED" != "$PROXIED" ]; then
update_dns_record "cname" "*.$DOMAIN" "$C_TTL" "$(date "+%Y-%m-%d %H:%M:%S"): sub domain from ddns.sh" "$DOMAIN" "$S_DNS_ID"
log "Update: Sub DNS record is successfully changed\nDomain: $S_DNS_CONTENT to *.$DOMAIN\ncname: $DOMAIN \nTTL: $S_DNS_TTL to $C_TTL\nproxied: $S_DNS_PROXIED to $PROXIED"
else
log "Notice: Sub DNS record is not changed\nDomain: *.$DOMAIN\ncname: $DOMAIN\nTTL: $C_TTL\nproxied: $PROXIED"
fi
else # sub DNS record does not exist
create_dns_record "cname" "*.$DOMAIN" "$C_TTL" "$(date "+%Y-%m-%d %H:%M:%S"): sub domain from ddns.sh" "$DOMAIN"
log "Create: Sub DNS record is successfully created\nDomain: *.$DOMAIN\ncname: $DOMAIN\nTTL: $C_TTL\nproxied: $PROXIED"
fi
if [ "$(echo $W_DNS_RECORD | jq -r '.result | length')" -eq 1 ]; then # www DNS record exist
W_DNS_ID="$(echo $W_DNS_RECORD | jq -r '.result[0].id')"
W_DNS_CONTENT="$(echo $W_DNS_RECORD | jq -r '.result[0].content')"
W_DNS_TTL="$(echo $W_DNS_RECORD | jq -r '.result[0].ttl')"
W_DNS_PROXIED="$(echo $W_DNS_RECORD | jq -r '.result[0].proxied')"
if [ "$W_DNS_CONTENT" != "$DOMAIN" -o "$W_DNS_TTL" != "$C_TTL" -o "$W_DNS_PROXIED" != "$PROXIED" ]; then
update_dns_record "cname" "www.$DOMAIN" "$C_TTL" "$(date "+%Y-%m-%d %H:%M:%S"): www domain from ddns.sh" "$DOMAIN" "$W_DNS_ID"
log "Update: www DNS record is successfully changed\nDomain: $W_DNS_CONTENT to www.$DOMAIN\ncname: $DOMAIN\nTTL: $W_DNS_TTL to $C_TTL\nproxied: $W_DNS_PROXIED to $PROXIED"
else
log "Notice: www DNS record is not changed\nDomain: www.$DOMAIN\ncname: $DOMAIN\nTTL: $C_TTL\nproxied: $PROXIED"
fi
else # www DNS record does not exist
create_dns_record "cname" "www.$DOMAIN" "$C_TTL" "$(date "+%Y-%m-%d %H:%M:%S"): www domain from ddns.sh" "$DOMAIN"
log "Create: www DNS record is successfully created\nDomain: www.$DOMAIN\ncname: $DOMAIN\nTTL: $C_TTL\nproxied: $PROXIED"
fi
vi /home/temp-app/ddns/config/ddns.conf  
#!/bin/bash
#ddns.conf
# --- Cloudflare DDNS update script ---
# --- ddns.conf ---
#"Your Cloudflare Zone ID"
ZONE_ID=""
#"Your Cloudflare API key"
API_KEY=""
Shell script 실행 환경 만들기
해당 쉘 스크립트는 jq라는 jason 파일 처리 명령어가 필요합니다. 따라서 스크립트를 실행하기 앞서 jq 명령어를 설치합니다.
#!/bin/bash
sudo apt update
sudo apt install jq -y
sudo apt update
Shell script 실행 및 cron 등록하기
스크립트를 실행합니다.
/home/temp-app/ddns/ddns.sh -d example.com
ddns.log 파일을 확인합니다.
cat /home/temp-app/ddns/log/[date]_ddns.log
---------
2025-06-27 02:49:02: Notice: log file is created
---------
2025-06-27 02:49:04: Create: Root DNS record is successfully created
Domain: example.com
IP: 0.0.0.0
TTL: 300
proxied: true
---------
2025-06-27 02:49:04: Create: Sub DNS record is successfully created
Domain: *.example.com
cname: example.com
TTL: 400
proxied: true
---------
2025-06-27 02:49:05: Create: www DNS record is successfully created
Domain: www.example.com
cname: example.com
TTL: 400
proxied: true
---------
 등록된 DNS가 작동하는 지 확인합니다.
ping example.com
정상 실행이 확인되면 cron을 등록합니다.
#!/bin/bash
crontab -e # crontab 파일 편집
*/5 * * * * /home/temp-app/ddns/ddns.sh # 분 시 일 월 요일 [실행 파일], 매 5분마다 실행
#(*,-/) 사용하여 기간 조정 가능
#*/[숫자]: 매 [숫자] 분/시/일 ... 마다 반복
#[숫자], [숫자]: [숫자], [숫자] 분/시/일 마다 반복
#[숫자]-[숫자]: [숫자] 분/시/일 부터 [숫자] 분/시/일까지 매 분/시/일 반복 등등
crontab -l # crontab 파일 확인
#crontab -r # crontab 삭제
#crontab 시작
service cron start
#crontab 중지
#service cron stop
#crontab 작동 확인
#service cron status
#crontab 재시작
#service cron restart
몇 분 후 ddns.log 파일 확인하여 정상 작동하는 지 확인합니다.
cat /home/temp-app/ddns/log/ddns.log
sudo service cron status
결과
jq 패키지 설치
DDNS 스크립트 작성
API를 통한 등록, 갱신, 삭제 기능 구현
JSON 형식에 대한 이해
jq를 통한 JSON 파일 조작에 애한 이해
DNS 및 DDNS 이해
A, AAAA, CNAME, TTL, PROXIED 등의 레코드의 의미 이해
cron 등록
2025-06-25 - 초안 작성
5.3. Docker 설치 환경 설정
목표
임시 Server에 Docker 설치 환경을 설정합니다.
설치 절차 및 방법을 순서에 따라 명확하게 문서로 기록합니다.
요구 사항
임시 서버에 Docker 설치 환경 설정
관리자 계정 Docker 그룹 추가
수행 작업
Docker 설치 환경 설정
 Docker docs 확인
Docker docs 접속
Debian 상 engine 설치를 위하여 다음 페이지 접속: https://docs.docker.com/engine/install/debian/
Docker docs의 절차를 따라 Docker engine 설치
임시 Server 접속
임시 Server가 구성되었으므로 더 이상 PVE web UI를 통한 접속이 아닌 SSH로 접속합니다.
ssh temp-app@[ip_address]
pw 입력
이전 버전 제거
운영 체제에서 자체적으로 지원하는  비공식적 인 도커 이미지를 확인합니다.
docker.io
docker-compose
docker-doc
podman-docker
(docker engine은 containerd와 runc에 의존합니다. 만약 설치되어 있다면 추가로 제거하면 됩니다.)
다음 명령어를 통해 패키지 제거를 시도합니다.
for pkg in docker.io docker-doc docker-compose podman-docker containerd runc; do sudo apt-get remove $pkg; done
설치된 패키지가 없는 경우  패키지가 설치되지 않았습니다. 라는 반환이 나올 수 있습니다.
클린 설치를 원할 시 uninstall docker engine 섹션을 다시 확인합니다.
설치 과정
도커는 다음과 같은 경로로 설치될 수 있습니다.
Linux Desktop 환경을 위한 bundle
apt 명령어를 통한 설치
manual 설치(업그레이드 포함)
script를 통한 설치(오직 테스트 및 개발 환경에서만 추천합니다.)
현재 임시 Server는 CLI 환경이므로 apt 명령어를 통해 설치합니다.
Docker Engine 설치
Docker Engine은 Docker가 구동될 수 있도록 만들어 주는 핵심 부분입니다. Docker의 CLI 명령어를 통해, 컨테이너를 만들고, 실행하고, 관리하는 모든 작업을 수행합니다.
설치의 앞서 Docker의 apt 저장소를 설정합니다.
#!/bin/bash
# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# Add the repository to Apt sources:
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
만약 kali같은 특수한 Linux 파생 버전을 사용한다면,  $(. /etc/os-release && echo "VERSOION_CODENAME") 부분을  brookworm 등으로 대체해야 할 수 있습니다.
 다음 명령어를 통해 Docker의 최신 버전을 설치합니다.
#!/bin/bash
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
만약 특정 버전을 설치하고 싶다면 다음 명령어를 통해 설치합니다.
#!/bin/bash
#List the available versions:
apt-cache madison docker-ce | awk '{ print $3 }'
5:28.3.0-1~debian.12~bookworm
5:28.2.2-1~debian.12~bookworm
....
#Select the version which you want
VERSION_STRING=5:28.3.0-1~debian.12~bookworm
#Install
sudo apt-get install docker-ce=$VERSION_STRING docker-ce-cli=$VERSION_STRING containerd.io docker-buildx-plugin docker-compose-plugin
hello-world 이미지를 실행하여 성공적으로 설치가 되었는지 확인합니다.
sudo docker run hello-world
Hello from Docker! 라는 멘트를 확인합니다.
hello-world 이미지를 제거합니다.
docker container ls -a
hello-world 컨테이너 명 확인
docker rm [컨테이너 명]
docker images ls
hello-world 이미지 id 확인
docker rmi [이미지 id]
업데이트 시 해당 절차의 2. 로 돌아가 명령어를 다시 실행합니다.
Docker Compose 설치
Docker Compose는 여러 개의 컨테이너로 구성된 애플리케이션을 한번의 정의하고 관리하거나, 복잡한 설정을 파일에서 관리할 수 있도록 하는 도구입니다. docker run의 명령어들을 모아 yaml 파일인 docker-compose.yml을 통해 정의하고 제어할 수 있도록 도와줍니다.
Docker engine이 설치를 완료 한 뒤 이어서 Docker compose를 설치합니다.
 
#!/bin/bash
#Install docker compose plugin
sudo apt-get update
sudo apt-get install docker-compose-plugin
#Check docker compose version
sudo docker compose version
서버 관리 계정에 docker 그룹을 추가하여, sudo 없이 docker 명령어를 사용할 수 있도록 합니다.
#!/bin/bash
#Add group to Server admin
sudo usermod -aG docker "$user"
#Check the group
id "$user"
업데이트 시 해당 절차의 1.로 돌아가 명령어를 실행 합니다.
Dockerfile과 docker-compose.yml
Dockerfile 이란
도커 이미지를 만들기 위한 스크립트로 다음과 같은 형식을 통해 작성됩니다.
# 1. FROM: 베이스 이미지 지정 (필수)
# - 어떤 OS 또는 미리 만들어진 이미지를 기반으로 시작할지 결정합니다.
# - 예: ubuntu:22.04, node:20, python:3.10-slim, mariadb:latest 등
FROM <base_image>
# 2. WORKDIR: 작업 디렉토리 설정
# - 컨테이너 내부에서 작업할 경로를 지정합니다. 이후의 COPY, RUN, CMD 명령어는 이 디렉토리에서 실행됩니다.
WORKDIR /app
# 3. COPY: 파일 복사
# - 호스트(내 컴퓨터)의 파일을 컨테이너 내부로 복사합니다.
# - 예: COPY . . (현재 디렉토리의 모든 파일 복사)
COPY <host_path> <container_path>
# 4. RUN: 명령어 실행
# - 이미지 빌드 중에 실행할 명령어를 지정합니다.
# - 예: RUN apt-get update && apt-get install -y vim (패키지 설치)
# - 예: RUN npm install (의존성 설치)
# - 하나의 RUN 명령어로 묶어서 빌드 레이어를 줄이면 이미지 크기 최적화에 도움
# - 만약 패키지를 설치 했다면, 캐시는 삭제하는 게 좋다. && rm -rf /var/lib/apt/lists/*
RUN <command>
# 5. EXPOSE: 포트 노출 (문서화 목적)
# - 컨테이너가 어떤 포트를 사용할지 명시합니다. 실제 포트 연결은 'docker run -p' 또는 'docker-compose.yml'에서 합니다.
EXPOSE <port_number>
# 6. CMD: 컨테이너 실행 시 명령어
# - 컨테이너가 시작될 때 실행될 명령어입니다.
# - Dockerfile에 하나만 존재할 수 있습니다.
# - 예: CMD ["npm", "start"]
# - CMD나 ENTRYPOINT는 기본 이미지의 것을 그대로 사용합니다.
# - CMD:docker run에서 다른 명령 주면 덮어 쓰여짐. (기본 명령)
# - ENTRYPOINT: 어떤 일이 있어도 항상 먼저 실행되는 실행 파일
# - 필요하다면 재정의할 수 있지만, 대부분은 건드리지 않습니다.
CMD ["executable", "param1", "param2"]
# 7. ENV: 환경 변수 설정
# - 컨테이너 내부에서 사용할 환경 변수를 지정합니다.
ENV <key>=<value>
Docker 파일로 실행된 이미지는 상위 이미지의 업데이트 정보를 알 수 없기 때문에, yml 파일에서 label을 추가하여 diun을 통해 관리합니다.
labels:
- "diun.enable=true"
- "diun.watch_repo=Original_Image"
- "diun.watch_tag=latest"
diun 프로세스는 실행되면서 해당 도커를 감시합니다. 
diun은 외부 원격 서버에 있는 도커도 감시할 수 있습니다. 다만 IPTABLES를 통한 명시적인 허가를 해줘야 합니다. [Docker API의 기본 포트: 2375(평문), 2376(ssl/tls)]
평문 전송은 공격에 취약할 수 있으므로, SSL/TLS를 적용합니다.
docker-compose.yml
도커 이미지를 컨테이너로 만들 때 필요한 설정들의 모음입니다. 다음과 같이 정의합니다.
# 1. version: Compose 파일의 버전
version: '3.8'
# 2. services: 컨테이너 목록
services:
# 서비스 이름 (컨테이너 이름과 다를 수 있지만, 네트워크 내부에서 식별자로 사용됨)
<service_name>:
# 3. image: 사용할 이미지
# - Docker Hub에서 이미지를 가져옴
image: <image_name>:<tag>
# 4. build: Dockerfile로 이미지를 빌드
# - 현재 디렉토리의 Dockerfile을 사용
build: .
# - 또는 특정 디렉토리의 Dockerfile을 사용
# build: ./my-app
# 5. container_name: 컨테이너에 이름 지정
container_name: <custom_container_name>
# 6. ports: 포트 포워딩
# - '호스트포트:컨테이너포트'
ports:
- "80:80"
# 7. volumes: 데이터 영속성을 위한 볼륨 마운트
# - '호스트경로:컨테이너경로'
volumes:
- ./data:/var/lib/mysql
- ./nginx.conf:/etc/nginx/nginx.conf
# 8. environment: 환경 변수 설정
environment:
- DB_USER=myuser
- DB_PASSWORD=mypassword
# 9. depends_on: 서비스 간 종속성 설정
# - DB 컨테이너가 먼저 시작되어야 Node.js 앱이 실행되도록 설정
depends_on:
- db
# 10. networks: 네트워크 설정 (선택 사항)
networks:
<network_name>:
driver: bridge
# 11. volumes: 볼륨 정의 (선택 사항)
volumes:
<volume_name>:
Docker 명령어
Docker image 명령어
docker images [ls]
로컬 도커 이미지 목록 확인
docker rmi 이미지 id[:리포지토리:태그명]
도커 이미지 삭제, id를 통한 삭제 권장
docker rmi prune
사용되지 않는 모든 도커 이미지 삭제
docker image bulid -t 이미지명[:태그명] path
Dockerfile 이미지 생성, 도커 파일은 path의 root에 위치
docker image pull 레파지토리명[:태그명]
docker 이미지를 다운로드
Docker container 명령어
docker ps
실행중인 컨테이너 목록 확인
docker ps -a
전체 컨테이너 목록 확인
docker container ls -a
전체 컨테이너 목록 확인
docker start 컨테이너명[:컨테이너 id]
컨테이너 시작
docker attach 컨테이너명[:컨테이너 id]
컨테이너 접속
docker stop 컨테이너명[:컨테이너 id]
컨테이너 멈춤
docker run [옵션] 이미지명 
컨테이너 생성 및 시작
-d: 백그라운드 실행
--name: 컨테이너 이름 지정
-p [host_port:container_port]: 호스트와 컨테이너가 연결될 포트 지정
-v [host_directory]:[container_directory]: 마운트 될 볼륨 지정
--rm: 일회성으로 실행
-it 이미지명 [쉘]: 상호작용 모드 활성화 및 가상 터미널 할당
docker exec -it 컨테이너명[:컨테이너 id] [쉘]
이미 실행 중인 컨테이너 접속
docker rm 컨테이너명[:컨테이너 id]
컨테이너 삭제
exit : 컨테이너 빠져나오기
결과
Docker Engine 설치
Docker Compose 설치
서버 관리자 계정에 Docker 그룹 추가
2025-06-27 - 초안 작성 완료
2025-06-30 - dockerfile과 docker-compose.yml 내용 추가
부록 1. 문서 작성 가이드라인 및 용어집
부록 1.1. 문서 작성 가이드라인
목표
이 가이드라인은 해당 프로젝트의 문서 품질을 향상시키고, 모든 내용을 명확하고 일관되게 작성하기 위한 기준 문서입니다. 개인 프로젝트로 작성자 1인을 위한 가이드라인이며, 추후 작성자 및 예상 독자(기술면접관 등)를 위하여 다음과 같은 목표를 추구합니다.
정확성: 모든 정보는 사실에 기반하여 이해하기 쉽도록 작성합니다.
간결성: 명확하고 간결한 문장을 사용하여 핵심 내용을 전달합니다.
최신성: 모든 진행 사항은 문서에 업데이트 되어 항상 최신 상태를 유지합니다.
문서 구조 및 형식
제목 계층
모든 페이지는 H1, H2, H3 등의 HTML 제목 태그를 일관되게 사용하여 내용의 계층 구조를 명확하게 합니다. 
목차
BookStack의 기본 기능을 사용하여 관리합니다.
날짜 표기
날짜의 표기는 국제 표준 ISO 8061에 따라 YYYY-MM-DD hh:mm:ss로 표기합니다.
변경 이력 표시
BookStack의 기본 기능과 별개로 페이지 하단 구분선으로 구분된 영역에 문서 변경 이력(날짜 및 주요 변경 내용)을 기록하여 버전 관리를 합니다. 변경 이력은 다음과 같이 기록합니다.
YYYY-MM-DD - 변경 내용 요약
단락 및 목록
하나의 단락에는 하나의 주제를 담습니다. 정보를 나열할 때는 순서 있는 목록(<OL>) 혹은 순서 없는 목록 (<UL>) 태그를 적절하게 활용합니다.
강조 및 인용
BookStack 내부의 callout 기능을 활용하여 인용, 정보, 성공, 경고, 위험 등의 범례를 적절하게 활용합니다.
글로 설명하기 어려운 복잡한 데이터를 비교할 시 표(<TABLE>)를 활용하여 시각적으로 보기 좋게 정리합니다. 표 활용 시 행 머리글 옵션을 활용합니다. 셀 속성에서 내부 텍스트를 가로, 세로 전부 가운데 정렬하여 보기 좋게합니다.
이미지 및 다이어그램
시각 자료는 draw.io를 사용하여 만든 뒤, [file_name].drawio.png 형태로 업로드하여 사용합니다. 이미지는 선명하고 가독성이 높아야 하며, 필요한 경우 캡션(설명)을 추가합니다. png 파일을 저장할 때, drawio 정보를 함께 저장하여 관리합니다. 
코드 및 명령어
BookStack 내부의 소스 코드 기능을 활용하여 코드 및 명령어를 입력합니다.
링크
BookStack 내부의 내외부 링크 기능을 활용하여, 다른 문서를 빠르게 참조할 수 있도록 합니다. 이 때, 내부 참조 링크는 BookStack의 기능을 활용하여 추가합니다. 이는 추후 문서 수정 시 링크의 유효성을 유지할 수 있도록 합니다.
내용 작성 스타일
용어 사용
문서에 사용되는 용어는 문서 전체에 일관되게 사용합니다. 처음 등장하는 용어는 완전한 명칭과 함께 약어를 병기하고, 부록 1 아래의 용어집에 정의합니다. 한 번 사용된 용어는 약어로 표기하되 강조가 필요한 특별한 경우에는 완전한 명칭을 사용합니다.
문체
최대한 객관적이고 명료하게 능동 표현으로 작성합니다. 피동 표현은 꼭 필요한 경우에만 사용합니다. 만연체가 아닌 간결체로 작성하며, 계획 분석 결과 위주로 작성합니다. 또한, 시제를 상황에 맞게 사용하여 문서의 일관성을 유지합니다. (기본적으로 현재 시제. 과거/미래 시제는 해당 경우에만)
문법 오류 및 오탈자
문서 작성시 용어집을 통한 일관된 용어를 사용합니다. 문서 작성 후 교정 작업을 통해 문법적으로 어색한 부분과 잘못된 용어 사용을 교정합니다.
문장 부호
온점(.), 반점(,) 등을 남용하지 않고, 적재적소에 사용하여 문장을 깔끔하게 유지합니다.
문서 관리
검토 및 업데이트
프로젝트 진행 도중 계획에 수정 사항이 생길 시, 즉시 관련된 모든 문서를 검토하고 수정합니다. 수정 사항이 없더라도 주기적으로 문서 전체 내용을 확인하며 정합성과 일관성이 어긋나는 부분이 있는 지 확인하여 수정합니다.
To-Do list 활용
프로젝트 진행 시 정식 진행 사항에 넣기 전 임시로 활용할 수 있는 To-Do list 페이지를 적극적으로 활용합니다. 양식, 용어 등에 구애받지 않고 자유롭게 작성합니다. 추후 정식으로 문서에 포함 시 문서 작성 가이드 라인에 맞추어 재작성 후 해당 내용을 삭제합니다.
주기적인 문서 재작성
문서 작성 가이드라인과 용어집 용례 준수를 위하여 매주 1회 문서를 검토하고 수정합니다.
결론
프로젝트를 진행하며 해당 가이드라인에 맞추어 명확하고 일관된 가독성있는 문서를 작성합니다. 추후 프로젝트 진행 시 현 문서 역시 수정될 수 있습니다.
2025-06-05 - 초안 작성
2025-06-20 - ISO 8601 적용 및 주기적인 문서 재작성 항목 추가
부록 1.2. 용어집(임시)
목표
해당 프로젝트에 사용되는 용어를 분류하기 전 임시로 작성합니다. 추후 용어의 성격에 맞춰 분류된 별도의 용어집을 작성합니다.
용어
1.1. 동기 및 목표의 용어
NAS(Network Attached Storage) - 네트워크 기반의 파일 저장 서버
On-premise - 기관, 기업, 개인이 자체적으로 운영하는 서버/소프트웨어 환경
IT(Information Technology) - 정보 기술; 컴퓨터 시스템, 소프트웨어, 네트워크, 데이터 처리 기술 등을 총칭
HA(High Availability) - 고 가용성; 물리적/논리적 오류에 대응하여 가용성을 높이는 것
SoC(Separation of Concerns) - 관심사 분리; 어떠한 하나의 기능을 실행하는 데 있어 물리적/논리적으로 구별하여 독립적인 구현을 하는 것
LTO(Linear Tape Open) - 백업 및 데이터 보관에 사용되는 자기 테이프 기술
CA(Certificate Authority) - 인증 기관; SSL/TLS 프로토콜을 사용하기 위한 인증서를 발급하는 주체
DNS(Domain Name System) - IP(Internet Protocol) 주소를 사람이 이해하기 쉬운 Domain Name으로 사용할 수 있게 하는 시스템; Domain Name에 대응하는 IP 주소를 반환
DB(Database) - 여러 사람 혹은 서비스가 공유하여 사용할 목적으로 생성되어 관리되는 데이터의 집합
IPS/IDS(Intrusion Prevention/Detection System) - 침입 방지/탐지 시스템; 실시간으로 패킷을 탐지 하거나 차단하여 악의적인 접근에 대처하는 시스템
OS(Operating System) - 하드웨어와 응용프로그램 사이에 위치하며, 하드웨어 자원을 분배하는 등의 역할을 수행
1.2. 하드웨어 구성의 용어
DHCP(Dynamic Host Configuration Protocol) - Host의 IP 주소, Subnet Mask, Gateway, DNS 주소 등을 자동으로 설정하는 프로토콜
VLAN(Virtual Local Area Network) - 패킷에 VLAN 태그를 붙여 가상의 LAN을 설정, 여러 스위치가 포함된 네트워크 환경 속에서 태그 별로 각각 하나의 LAN 처럼 동작(Broadcast 단위의 분리; L2 통신 분리); IEEE 802.1Q에 정의
AP(Access Point) - 유선 LAN과 무선 LAN을 연결해주는 장치; WAP(Wireless Access Point)
NIC(Network Interface Card) - 컴퓨터, 혹은 네트워크 장비가 네트워크에 접근할 수 있도록 하는 하드웨어 장치
1.3. 목표 아키텍처의 용어
WAN(Wide Area Network) - LAN과 LAN을 연결하는 네트워크; 라우터를 거쳐 IP를 이용하여 L3 통신
LAN(Local Area Network) - 각 호스트와 서버를 연결하는 네트워크; 스위치를 거쳐 MAC을 이용하여 L2 통신
ISP(Internet Service Provider) - 개인 혹은 기업에게 인터넷 접속을 제공하는 주체 혹은 기업
PoE(Power over Ethernet) - LAN 통신 캐이블을 이용하여 장비에 전원을 공급하는 기술; IEEE 802.3af, 802.3at, 802.3bt에서 정의
DDoS(Distributed Denial of Service) - 기존의 서비스 거부 공격(DoS)에서 발전형 공격 기법; 다수의 감염된 시스템이 조직되어 대상 서비스에 트래픽을 집중하여 서비스 가용성을 손상
IP(Internet Protocol) - 인터넷을 통하여 네트워크에서 어떤 정보를 수신하고 송신하는지에 대한 통신 규약
Port Scan - 네트워크에 접속된 컴퓨터 혹은 장치의 어떤 포트가 열려 있는지 확인하는 작업
VPN(Virtual Private Network) - WAN에서 특정 LAN으로 암호화된 터널을 구성해 접속하여, LAN에 속한 것 처럼 네트워크를 사용할 수 있도록 하는 기술
UPnP(Universal Plug and Play) - 네트워크에 연결된 기기들이 별도의 설정 없이 서로를 인식하고 필요 포트를 자동으로 포트를 개방하는 기능
DDNS(Dynamic Domain Name System) - 유동적으로 변경되는 Public IP 주소에 대한 DNS를 제공하기 위한 기술; 유동적인 Public IP에 대하여 고정된 Domain Name을 사용 가능
SSL/TLS( Secure Sockets Layer / Transport Layer Security) - 클라이언트와 서버 간의 통신을 암호화하여 제 3자가 데이터를 가로채거나 변조하는 것을 방지하는 프로토콜; TLS가 SSL의 최신 버전
Gateway - 네트워크 간 통신을 가능하게 하는 컴퓨터 혹은 소프트웨어; 각 LAN과 LAN, LAN과 WAN의 사이에서 서로의 통신을 가능하게 중계하는 역할
OPNsense - 오픈 소스 방화벽 OS
FreeIPA - 오픈 소스 통합 인증 솔루션(LDAP/Kerberos 등 제공)
LDAP(Lightweight Directory Access Protocol) - 네트워크 상의 조직, 개인, 파일, 장치 등에 대한 정보를 중앙 집중식으로 관리하고 검색하는 프로토콜
SSO(Single Sign On) - 사용자가 한 번의 로그인으로 여러 어플리케이션에 접근할 수 있도록 하는 보안 기술
Split Horizon DNS - 요청한 위치가 LAN 혹은 WAN으로 다르더라도, 동일 Domain Name으로 부터 다른 IP를 얻어 일관적인 서비스를 제공할 수 있게 하는 기술
DoH(DNS over Https) - DNS 요청을 https 프로토콜을 사용하여 암호화; DoT(DNS over TLS) DNS 요청 자체를 https가 아닌 TLS로 자체 암호화
Kerberos - 네트워크 상 사용자 인증을 위한 프로토콜; 티켓 기반으로 KDC(Key Distribution Center)을 운영하여 관리
Zero Trust - 절대 신뢰하지 말고 항상 검증하라는 원칙을 기반으로 하는 보안 모델; 핵심 요소로 항상 검증, 최소 권한 접근, 침해 가정, 마이크로 세그멘테이션, 모니터링 및 분석, 행위 분석 등이 존재
PVE(Proxmox Virtual Environment) - Debian Linux를 기반으로 만들어진 오픈 소스 Hypervisor OS
VM(Virtual Machine) - 컴퓨티 환경을 소프트웨어로 구현한 것으로, 하드웨어를 추상화 하여 OS를 실행할 수 있는 가상 컴퓨팅 환경
LXC(LinuX Container) - 단일 Linux 호스트 위에서 호스트의 커널을 공유하는 여러 개의 독립적인 가상 Linux System을 실행
Docker - 각 응용 프로그램의 실행 환경에 대한 종속성을 벗어나기 위하여 가상화한 Container; OS 전체를 가상화 하는 VM, 커널을 공유하나 독립적인 Linux System으로 가상화된 LXC와 달리 실행에 필요한 라이브러리만을 패키징하여 가상화
SPOF(Single Point of Failure) - 단일 실패 지점; 시스템 구성 요소 중 동작 불능 시 전체 시스템이 중단되는 부분
Prometheus - 오픈 소스 시스템 모니터링 및 경고 툴킷
Grafana - 수집된 데이터를 시각적으로 표현해 주는 오픈 소스 분석 및 시각화 웹 어플리케이션
Loki/Promtail - 로그 데이터를 수집하고 저장하는 로그 집계 시스템
Alertmanager - Prometheus가 보낸 경고를 관리, 중복된 경고 그룹화 및 알림 라우팅
Ansible - 오픈 소스 IT 자동화 도구, YAML 형식의 플레이북을 사용하여 서버 구성, 애플리케이션 배포, 작업 자동화 수행
Kopia - 오픈 소스 백업/아카이빙 스프트웨어
NFS(Network File System) - Linux/Unix 상 네트워크를 통해 파일을 로컬 저장소 처럼 사용할 수 있게 하는 프로토콜
E2EE(End to End Encryption) - 종단간 암호화
Btrfs(B-tree File System) -  CoW, 스냅샷, 데이터 무결성 검증, 통합 볼륨 관리 등의 고급 기능에 중점을 둔 리눅스 용 최신 파일 시스템
CoW(Copy on Write) - 쓰기 시 복사; 파일이 수정되면 파일 전체를 덮어 쓰는 것이 아닌 새로운 공간에 수정된 부분의 블록을 기록, 강력한 무결성 지원, 스냅샷 기능, 높은 용량 효율성 등의 성능상 이점이 있으나 단점으로 외부 단편화 발생
AdGuard Home - DNS 광고 필터링 기능을 포함한 솔루션; FreeIPA로 upstream
Monitoring server - 성능 모니터링 및 로깅, 서버 설정 자동화 담당 서버
Proxy Server - Reverse Proxy, DDNS, idP 등 담당 서버
File Server - 파일 관리 및 백업 담당 서버
DB Server - DB 서버
Web Server/WAS - Bookstack, Gitea, Ghost 등의 웹 서비스 담당 서버
Application Server - 여러 응용 서비스 담당 서버
Kali and practice client - 정보 보안 실습 담당
1.4. 추가 프로젝트
OpenWRT - 주로 공유기(가정용 라우터)에 사용되는 임베디드 기기를 위한 Linux 배포판
ISMS-P - 정보보호 및 개인정보보호 관리체계 인증; KISA에서 공인하는 정보 보호/개인 정보 보호를 위한 관리체계 인증 제도
NAT (Network Address Translation) - 네트워크 주소 변환 기술; 여러 개의 LAN IP 대역과 하나의 WAN IP 주소로 연결하는 기술
Subnetting - 기존 Class 단위의 IPv4 체계를 벗어나, CIDR를 통하여 Subnet Mask 를 이용해 네트워크 단위를 분할; 하나의 IP 대역을 여러개로 나누어 사용 가능
Broadcast - LAN 전체를 대상으로 한 번에 모두 전송하는 방식(ARP, DHCP Broadcast 등에 사용)
Unicast - 특정 Client 하나를 대상으로 전송하는 방식(http, https, FTP 등에 사용)
(Multicast - 특정 그룹을 대상으로 전송하는 방식(IPTV, 실시간 스트리밍 등에 사용))
IaaS( Infrastructure as a Service ) - 클라우드 컴퓨팅 서비스 모델 중 하나; IT 인프라를 가상화 형태로 제공하는 것(AWS EC2, Microsoft Azure 등)
(PaaS(Platform as a Service) - 클라우드 컴퓨팅 서비스 모델 중 하나; OS, DB 등의 플랫폼을 가상화 형태로 제공하는 것(Google App Engine 등)
SaaS(Software as a Service) - 클라우드 컴퓨팅 서비스 모델 중 하나; 완성된 소프트웨어를 가상화 형태로 제공하는 것(MS Office365, Google Workspace 등))
2.1. 하드웨어 선정
프로세서(CPU; Central Processing Unit) - 중앙 처리 장치; 연산, 제어, 기억, 해석을 담당하는 컴퓨터 부품
OOM Killer - Linux 시스템 상에서 RAM 용량이 부족할 시, 서비스를 강제 종료하여 RAM을 회수하는 프로세스
KSM(Kernel Same-page Merging) - PVE 내부에서 중복되는 Memory Page를 병합하고, 실제 물리 RAM 사용량을 줄여주는 기술
RAID(Redundant Array of Independent Disk) - 여러 개의 저장장치(하드 디스크, SSD 등)에 일부 중복된 데이터를 나누어 저장하는 기술; 단순 병합 및 미러링부터 오류 검출을 위한 패리티를 사용하는 등 여러 방식이 존재
2.2. 네트워크 설계
E2E(End to End) - 종단간
Terminal Box - 통신 단자함
LACP(Link Aggregation Control Protocol) - 두 개 이상의 NIC를 묶어 하나의 NIC 처럼 사용하는 기술; 802.3ad에서 정의
vmbr - Linux Bridge; PVE 내부에서 사용되는 가상의 스위치
vtnet - PVE 내부에서 사용되는 가상의 NIC
ARP( Address Resolution Protocol ) - IP 주소를 통해 MAC 주소를 알아내는 시스템; Broadcast를 사용
ACL(Access Control List) - 접근 제어 목록
DMZ(Demilitarized Zone) - 
CERT(Computer Emergency Response Team) - 
2.3. 시스템 선정
Hypervisor - 하나의 물리적 서버에서 여러 개의 VM을 실행할 수 있도록 하는 소프트웨어 또는 펌웨어 계층
idP(identity Provider) - 사용자의 디지털 신원을 관리하고 인증 서비스를 제공하는 시스템
2025-06-14 - 초안 작성
2025-06-20 - 날짜 표기 변경
부록 2. 구현 일지
부록 2.1. To-Do List
목표
구현 로그로 남기기 전 바로 바로 해야 할 작업들을 임시로 저장하는 곳입니다. 작업 완료 시 구현 로그로 이동 후 해당 내용을 삭제합니다.
내용
문서 재정제 계획
임시 Server 호스팅
임서 Server + Gitea, Code-server, hedgedoc, mkdoc, Reverse Proxy(ngnix) 설정
문서 재정제
용어집, 구현로그, 용례 처리, 문체, 일관된 callout 및 형식 등 처리
문서 모듈화
'부록 4. 패키지 사용법 및 공통 설정' 챕터 신설
iptables 등 별도로 사용법을 기록해야 하는 명령어(패키지) 별로 문서 생성
서버 설정시 필수로 필요한 패키지 정리
중복되는 설정들(iptables, root 계정 설정, useradd/usermod/groupadd 등등) 통합하여 페이지 생성
서버별 특별히 필요한 설정만 서버 설정 페이지에서 정의
Debian 계열 뿐 아니라 RedHat 등 같은 기능 다른 명령어도 병기 가능하면 병기
각 서비스(Docker 등) 별로 필요한 Port 정리
bookstack에서 벗어나, wiki.js + Gitea 환경으로 이전
bookstack 내부의 엄격한 위계 구조(책장 - 책 - 챕터 -페이지)와 관리 및 유지 보수의 어려움(계속 바뀌는 프로젝트와, 사소한 변경도 전부 버전으로 기록)을 Git 기반의 Gitea로 이전하여 해소.
예상 레퍼리토리
/my-onpremise-docs (Gitea 저장소)   ├── mkdocs.yml   └── docs/       ├── index.md       ├── flows/       │   ├── 02_design/      │    ├── index.md                  # 설계 개요      │    ├── 01_considerations.md      # 왜? (고려사항, 비교, 결정)      │    └── 02_final_architecture.md  # 무엇을? (최종 구조, 다이어그램)      │   └── 03_implementation_plan.md  # 구현 순서에 대한 목차 역할(ex - 문서화 > 네트워크 환경 설정 > PVE 설치 > ...., 로그와 링크)      ├── components/                     # (기존 hardwares and softwares - 간단한 명세 위주로 - 추후 설정 및 스크립트와 링크)       │   ├── 01_hardware_list.md         # 1.1 하드웨어 구성       │   ├── 02_software_stack.md        # 1.2 소프트웨어 스택 (PVE, OPNsense 등)       │   └── 03_service_list.md          # 1.3 서비스 목록 (Docker 컨테이너 등)       ├── logs/ # 실제 구현 과정 중 있던 일들에 대한 간단한 명세  (ex - 네트워크 설정 - T5004를 설정 중 어떤 어떤 작업을 했고, 어떤 문제 발생 했는지만 명세 - 구체적인 스크립트, 설정, 시도 조치 등은 링크)      │   ├── 01_documentalize/       │    ├── index.md                        │    ├── 01_bookstack.md       │    └── 02_code-server_and_gitea.md      │   ├── 02_network_setup/      │   ├── 03_pve_setup/      │   └── 04_ddns_script/       ├── troubleshooting/       │   └── 01_504_gateway_timeout.md       ├── scripts_and_configs/        │   ├── ddns/       │   ├── debian/       │   └── pve/       ├── references/       │   ├── glossary.md       │   └── style_guide.md       └── assets/          └── images/              └── architecture.png
계획(작업 계획서이자 마스터 체크 리스트)
실행 로그(실제로 수행한 기록이 담기는 작업 일지 간단한 명세 > 계획에서 링크)
결과물(코드, 스크립트, 설정 파일, 매트릭스 등 등 > 실행 로그에서 링크)
Hedgedoc(마크다운 편집기) + code-server(여러 문서 편집기 + git) > gitea > mkdoc 
PVE 상에서 PCI Passthrough 시
추후 File Server 구현 시 PVE 상에서 PCI Passthrough를 사용해야 하므로 미리 정리
설정 방법: 이 방식은 VM에 가상 디스크를 추가하는 것이 아니라, SATA 컨트롤러라는 하드웨어 자체를 VM에 직접 넘겨주는 PCI Passthrough 방식을 사용
사전 준비: PVE 호스트의 BIOS/UEFI와 부트로더에서 IOMMU (Intel VT-d 또는 AMD-Vi)를 활성화해야 합니다. 이는 고급 설정에 속함
VM 생성: 파일 서버용 VM(Debian)을 생성합니다. 이때 OS 디스크는 1번 방식 을 따라 NVMe SSD( local-lvm )에 작게 생성합니다. 데이터용 HDD 디스크는 이 단계에서 추가하지 않습니다.
PCI 디바이스 추가:
생성된 VM의 하드웨어 탭으로 이동합니다.
추가 -> PCI 디바이스 를 선택합니다.
디바이스 목록에서 WTR Pro의 SATA 컨트롤러를 찾아 선택하고 추가합니다.
VM 부팅 및 설정:
VM을 부팅하면, Debian OS는 4개의 물리 HDD를 마치 자신의 컴퓨터에 직접 연결된 것처럼 인식합니다.
이후 Debian 내에서 mdadm 이나 Btrfs의 내장 기능을 사용하여 4개의 디스크로 RAID 10 어레이를 구성할 수 있습니다.
기타 사항
Immich 사진 Import 방법 정리
추후 immich 구현시, 사진 import 방법 정리 필요
공식 import 방법
모바일 app을 통해서 import
web을 통해서 import
immich-cli를 통해서 import
immich-cli를 통한 방법(File Server에서 바로 하는 법)
immich-cli를 공식 도커 이미지 받기
docker run 명령어 사용
#!/bin/bash
docker run --rm -it \
-v "/path/on/your/vm/to/photos:/import" \
ghcr.io/immich-app/immich-cli \
upload --key [API_Key] --server [Server Address] /import
각 명령어 옵션 설명
--rm: 명령 실행이 끝나면 컨테이너를 자동으로 삭제하여 시스템을 깨끗하게 유지합니다.
-it: 터미널을 통해 진행 상황을 실시간으로 보기 위한 옵션입니다.
-v "/path/on/your/vm/to/photos:/import" : [호스트 사진 경로]:[컨테이너 내부 경로]
ghcr.io/immich-app/immich-cli: 실행할 공식 immich-cli 도커 이미지의 이름입니다.
upload --key ... /import: 컨테이너 내부에서 실행될 실제 명령어입니다. 마지막의 경로가 컨테이너 내부 경로인 /import로 지정된 것을 볼 수 있습니다.
대량의 사진을 호스트 환경의 오염 없이 격리된 도커 내부 컨테이너에서 immich docker로 전송 가능하며, 해당 명령어를 shell script 화 하면 더 간단하게 재사용 가능
Git 정리
Git과 Gitea
Git: 로컬에서 관리되는 버전 관리 프로그램
Gitea: 이 Git으로 관리되 파일을 원격 저장 및 공유, 웹 인터페이스로 보여주는 서버
Git의 저장 공간
Working Directory(작업 공간): 로컬 폴터
Staging Area(스테이징 공간): 작업한 파일 중 이번 버전에 포함시킬 변경 사항만 올리는 공간
Local Repository(로컬 저장소): 스테이징 공간에서 준비된 병경 사항을 하나의 의미있는 버전(Commit)으로 확정하여 저장하는 일종의 데이터베이스(.git 이라는 숨김 폴더에 모든 이력 기록)
사용 방식
Git으로 작업한 결과를 Gitea 서버로 업로드(push)
작업 공간 파일 수정
스테이징 공간 변경 사항 추가
로컬 저장소에 버전으로 확정(커밋)
Code-Server에 Git 설치하기
임시 설치
code-server 컨테이너 실행
컨테이너 터미널 들어가기
apt-get update && apt-get install -y git 명령어를 통해 설치
추후 컨테이너 재생성(업데이트 등의 이유)시 초기화
Dockerfile 생성
#Dockerfile.tamplate
FROM codercom/code-server:latest
USER root
RUN apt-get update && apt-get install -y git
USER coder
Git과 Gitea 연동
Gitea에 프로젝트 생성
Gitea 서버 인증(git push 최초 실행 전 설정)
Code-Server 터미널 접속
#!/bin/bash
ssh-keyget -t rsa -b 4096 #ssh 키 생성
cat ~/.ssh/id_rsa.pub #생성된 ssh 키 출력(공개키) 후 내용 복사
Gitea 웹 UI 설정 > SSH/GPG 키 이동
키 추가 버튼 누른 뒤, 공개키 붙여 넣고 저장
Gitea의 저장소(Repository) 복제
Gitea 웹 사이트에서 프로젝트 페이지 접속
오른쪽에 있는 HTTPS 또는 SSH 주소를 복사
Code-Server 웹 사이트 메뉴의 Terminal > New Terminal 클릭 하여 내장 터미널 접속
#!/bin/bash
git clone git@<GITEA_SERVER_IP>:<user>/[Gitea Address]
Code-Server의 왼쪽 파일 탐색기에 [프로젝트] 폴더 생성 확인
파일 수정 및 버전 관리
Code-Server 편집기 활용 [프로젝트] 폴더 내 파일 생성 혹은 수정
파일이 끝나면 터미널에서 git 명령어 차례로 실행
#!/bin/bash
cd [project directory] #해당 폴더로 이동
git add file_name #특정 파일 하나만 올릴 때 git add . #전체 파일 올릴 때
git commit -m "messages" #의미있는 메세지와 함께 버전으로 확정
git push origin main #Gitea 서버에 업로드(push)
협업 흐름(Branch)
#!/bin/bash
#'update-monitoring'이라는 새로운 브랜치를 만들고, 그 브랜치로 이동
git checkout -b update-monitoring
#새로운 브랜치에서 모니터링 관련 설정을 수정하고 커밋
git add . git commit -m "Update Prometheus configuration"
#작업이 성공적으로 끝나면, 다시 원래의 main 브랜치로 복귀
git checkout main
#main 브랜치에서 새로운 브랜치의 변경 사항을 합치기 (Merge)
git merge update-monitoring
추후 자동화 연동
Gitea +n8n을 이용한 GitOps 자동화 워크플로우
트리거 (Trigger): Gitea Webhook
Gitea의 homelab-config 저장소 설정에서 웹훅(Webhook)을 생성
이 웹훅은 push 이벤트가 발생할 때마다, n8n으로 알림을 보내도록 설정
수신 (Listen): n8n Webhook Node
n8n 워크플로우에서는 'Webhook' 노드를 사용하여 Gitea가 보내는 알림 대기
이 방식은 5분마다 확인하는 Cron Job과 달리, 변경 사항이 발생하는 즉시 실시간으로 반응
실행 (Execute): n8n Execute Command Node
cd /path/to/homelab-config (프로젝트 디렉토리로 이동)
git pull origin main (Gitea로부터 최신 설정 가져오기)
docker-compose up -d --remove-orphans (변경된 docker-compose.yml을 기반으로 도커 컨테이너 재시작/업데이트)
n8n이 호스트의 명령어를 실행하고 도커를 제어하기 위하여 특정 폴더와 도커 소켓을 볼륨으로 마운트 필요
#n8n 서비스의 docker-compose.yml 예시
services:
n8n:
  image: n8nio/n8n
  restart: always
  ports:
    - "5678:5678"
  volumes:
    - n8n_data:/home/node/.n8n
#n8n 컨테이너가 호스트의 도커를 제어할 수 있게 함
    - /var/run/docker.sock:/var/run/docker.sock
#n8n 컨테이너가 Git 프로젝트 폴더에 접근할 수 있게 함
    - /path/to/homelab-config:/data/homelab-config
  environment:
    - GENERIC_TIMEZONE=Asia/Seoul
기존 DB 백업
mariadb-dump 백업
  mariadb (데이터 경로: /var/lib/mysql ) 사용
터미널 접속 및 명령어 실행
mkdir /backups/sqldumps
mariadb-dump -u [username] -p [password] [options] [database_name] > [export_file_name] 명령어 사용
#!/bin/bash
#하나의 데이터베이스만 백업
mariadb-dump -u user -p database > /backups/sqldumps/database_backup_$(date "+%Y-%m-%d %H:%M:%S").sql
#여러 데이터베이스 한번에 백업
mariadb-dump -u user[root|admin] -p database1 [database2|...] > /backups/sqldumps/databases_backup_$(date "+%Y-%m-%d %H:%M:%S").sql
#시스템 데이터베이스 제외한 모든 데이터베이스 한번에 백업
mariadb-dump -u user[root|admin] --all-databases --exclude-databases=mysql,information_schema,performance_schema,sys > /backups/sqldumps/all_databases_backup_$(date "+%Y-%m-%d %H:%M:%S").sql
데이터 백업
/var/lib/mysql 내 내용을 그대로 backups로 복사
#!/bin/bash
cp -aR /var/lib/mysql backups/mysql
DB 서비스 완전 중지 및 볼륨 데이터 삭제
container manager 를 통해 mariadb 종료
File station에서 /docker/mariadb/mysql/* 삭제
도커 업데이트
mariadb 설치
도커 설정
이미지
mariadb
포트
3306:3306
볼륨 마운트
/docker/mariadb/mysql /var/lib/mysql
/docker/mariadb/backups /backups
환경 변수
MARIADB_ROOT_PASSWORD MariaDB1Geonil!
TZ Asia/Seoul
restart: unless-stopped
컨테이너 설정
터미널 접속 및 명령어 실행
chown 999:999 /volume1/docker/mariadb/mysql
chown 999:999 /volume1/docker/mariadb/backups
File Station 설정
/docker/mariadb/mysql
/docker/mariadb/backups
권한 설정: 생성 > Owner > 모든 권한
도커 다시 시작
mariadb 설정
터미널 접속 및 명령어 실행
다음 명령어 입력하여 mariadb 접속
#!/bin/bash
mariadb -u root -p
다음 명령어 입력하여 root 계정 비밀번호 생성 및 로컬 접속만 허용
#ALTER USER 'root'@'localhost' IDENTIFIED BY 'MariaDB1Geonil!'; #비밀번호 생성 but 기본 생성
DELETE FROM mysql.user WHERE User='root' AND Host='%'; #로컬 접속만 허용
FLUSH PRIVILEGES; #변경된 권한 사항 즉시 적용
exit로 접속 종료 후, 재 접속해 정상적으로 적용되었는지 확인
다음 명령어를 이용하여 admin 계정 생성
CREATE USER 'mariadb_admin'@'%' IDENTIFIED BY 'MariaAdmin1Geonil!'; #계정 생성
GRANT ALL PRIVILEGES ON *.* TO 'mariadb_admin'@'%' WITH GRANT OPTION; #권한 부여
FLUSH PRIVILEGES; #변경된 권한 사항 즉시 적용
exit로 접속 종료 후, 해당 계정으로 접속해 정상적으로 적용되었는지 확인
어플리케이션용 데이터베이스 생성 및 계정 생성
bookstack
CREATE DATABASE IF NOT EXISTS bookstack_db; #데이터베이스 생성
CREATE USER 'bookstack_user'@'%' IDENTIFIED BY 'Bookstack1Geonil!'; #계정 생성
GRANT ALL PRIVILEGES ON bookstack_db.* TO 'bookstack_user'@'%'; #권한 부여
FLUSH PRIVILEGES; #변경된 권한 사항 즉시 적용
Gitea
CREATE DATABASE IF NOT EXISTS gitea_db; #데이터베이스 생성
CREATE USER 'gitea_user'@'%' IDENTIFIED BY 'Gitea1Geonil!'; #계정 생성
GRANT ALL PRIVILEGES ON gitea_db.* TO 'gitea_user'@'%'; #권한 부여
FLUSH PRIVILEGES; #변경된 권한 사항 즉시 적용
exit로 접속 종료
mariadb-dump를 통한 복구
명령어 입력
#!/bin/bash
mariadb -u bookstack_user -p bookstack_db < /backups/sqldumps/bookstack_db_time.sql
mariaDB 접속하여 확인
USE bookstack_db;
SHOW TABLES;
SELECT COUNT(*) FROM users; #실제 데이터 들어갔는지 확인
bookstack 설치 및 DB 연동
도커 설정
이미지
linuxserver/bookstack
 포트
:443
6875:80
내부 CA 구축시 HTTPS 사용 가능
볼륨 마운트
/docker/bookstack/config /config
환경 변수
APP_URL https://book.ilfamilynas.synology.me
APP_KEY base64:a3FqaHM0a3ZtMXJyZno2bm5zaTg0eHUzdXFkenpkNm4=
DB_HOST mariadb(추후 외부로 분리시 IP 혹은 도메인으로 설정)
DB_PORT 3306
DB_DATABASE bookstack_db
DB_USERNAME bookstack_user
DB_PASSWORD Bookstack1Geonil!
TZ Asia/Seoul
PUID 1000
PGID 1000
restart: unless-stopped
depends_on: mariadb
bookstack 에서는 PUID PGID 설정을 했으나, 안해도 무방
컨테이너 설정
터미널 접속 및 명령어 실행
chown 1000:1000 config
File Station 설정
/docker/bookstack/config 
권한 설정: 생성 > Owner > 모든 권한
도커 다시 시작
로그 확인
리버스 프록시 연동
접속 확인
기본 로그인 정보
ID: admin@admin.com
Password: password
로그인 후 확인
Gitea 설치 및 DB 연동
도커 설정
이미지
gitea/gitea
 포트
2222:22
3000:3000
볼륨 마운트
/docker/gitea/data /data
환경 변수
GITEA__server__ROOT_URL https://gitea.ilfamilynas.synology.me
GITEA_server__SSH_DOMAIN gitea.ilfamilynas.synology.me
GITEA_server__SSH_PORT 2222
GITEA_ssh_LISTEN_PORT 22
GITEA__database__DB_TYPE mysql
GITEA__database__HOST mariaDB:3306
GITEA__database__NAME gitea_db
GITEA__database__USER gitea_user
GITEA__database__PASSWD Gitea1Geonil!
TZ Asia/Seoul
USER_UID 1000
USER_GID 1000
restart: unless-stopped
depends_on: mariadb
Gitea에서는 UID GID 설정을 했으나, 안해도 무방
컨테이너 설정
터미널 접속 및 명령어 실행
chown -R 1000:1000 data
File Station 설정
/docker/gitea/data
권한 설정: 생성 > Owner > 모든 권한
도커 다시 시작
로그 확인
리버스 프록시 연동
접속 확인 및 초기 설정
홈페이지 이름 변경: Gitea: Il and Mors
사용자 등록 비활성화 체크
페이지를 보기 위해 로그인 하기 체크
관리자 계정 생성
Gitea 설치하기
사양을 많이 잡아 먹으므로, 충분한 Reverse Proxy의 연결 제한 시간 설정 필요
Code-server 설치 및 Gitea와 연동
도커 설정
이미지
codercom/code-server
포트
8080:8080
볼륨 마운트
/docker/code-server/config /home/coder/.config
/docker/code-server/data /home/coder/data
환경 변수
PASSWORD Codeserver1Geonil!
BIND_ADDR 0.0.0.0:8080
TZ Asia/Seoul
restart: unless-stopped
컨테이너 설정
권한 오류로 컨테이너 계속 비정상 종료되므로 도커 터미널이 아닌 NAS 직접 SSH 접속 후 다음 명령어 입력
#!/bin/bash
#ssh [username]@[ipaddress]
cd /volume1/docker/code-server
sudo chown -R 1000:1000 ./*
ls -l
권한 변경 확인
File Station 설정
/docker/code-server/config
/docker/code-server/data
권한 설정: 생성 > Owner > 모든 권한
리버스 프록시 연동
로그 확인
접속 확인 및 초기 Git 설정
사양 문제로 인하여 code-server 정상 작동 불가
따라서 VS code로 Git 동기화 하여 사용하다 추후 On-premise 네트워크 환경 구축 후 도커 설치
postgreSQL 설치
도커 설정
이미지
postgres
 포트
[5432:5432] - 기본값, DSM 상에서 5432 이미 사용중이므로 임의의 값 입력
5433:5432
볼륨 마운트
/docker/postgres/data /var/lib/postgres/data
/docker/postgres/backups /backups
/docker/postgres/conf/pg_hba.conf /var/lib/postgres/data/pg_hba.conf
환경 변수
POSTGRES_PASSWORD PostgreSQL1Geonil!
TZ Asia/Seoul
restart: unless-stopped
pg_hba.conf 파일
재적용 원할 시 bash에서  pg_ctl reload 명령어 입력, 혹은 psql 내부에서  SELECT pg_reload_conf(); 명령어 입력
# pg_hba.conf - 클라이언트 인증 설정
# ----------------------------------------------------------------------
# TYPE DATABASE USER ADDRESS METHOD
# ----------------------------------------------------------------------
# TYPE: 연결 유형 (local, host, hostssl, hostnossl)
# local: Unix 도메인 소켓 연결 (컨테이너 내부에서만 사용)
# host: TCP/IP 연결 (SSL 사용 여부 무관)
# hostssl: TCP/IP 연결 (SSL 필수)
# hostnossl: TCP/IP 연결 (SSL 필수 아님)
# DATABASE: 연결을 허용할 데이터베이스 (all, sameuser, samenet, replication, specific_db_name)
# all: 모든 데이터베이스
# sameuser: 사용자 이름과 동일한 데이터베이스
# samenet: 사용자 IP 주소와 동일한 서브넷의 데이터베이스
# replication: 복제 연결
# specific_db_name: 특정 데이터베이스 이름
# USER: 연결을 허용할 사용자 (all, specific_user_name, +group_name)
# all: 모든 사용자
# specific_user_name: 특정 사용자 이름
# +group_name: 특정 그룹에 속한 사용자
# ADDRESS: 연결을 허용할 IP 주소 또는 네트워크 대역 (IP/CIDR, hostname, all, samehost, samenet)
# IP/CIDR: IP 주소와 CIDR 마스크 (예: 192.168.1.0/24)
# all: 모든 IP 주소
# samehost: 서버 자체의 모든 IP 주소
# samenet: 서버의 모든 IP 주소가 속한 모든 서브넷
# METHOD: 인증 방식 (trust, reject, md5, scram-sha-256, peer, ident, gssapi, sspi, cert, pam, ldap, radius)
# trust: 비밀번호 없이 연결 허용 (매우 위험, 테스트 환경에서만)
# reject: 연결 거부
# md5: MD5 해시된 비밀번호 인증
# scram-sha-256: SCRAM-SHA-256 해시된 비밀번호 인증 (최신 권장)
# peer: Unix 도메인 소켓에서 운영체제 사용자명 일치 여부로 인증
# ident: 클라이언트 운영체제 사용자명으로 인증
# ldap: LDAP 서버를 통한 인증 (FreeIPA 연동 시 사용 가능)
# ----------------------------------------------------------------------
# 1. 로컬 연결 (컨테이너 내부에서 psql 클라이언트 등으로 접속 시)
# Unix 도메인 소켓을 통한 연결은 기본적으로 'trust' (비밀번호 없이 허용)로 설정되는 경우가 많습니다.
# 이는 컨테이너 내부에서만 가능하므로 비교적 안전합니다.
local all all trust
# 2. 호스트로부터의 연결 (Docker 'ports' 매핑 시)
# Docker 호스트에서 127.0.0.1 (localhost)로 접속을 시도할 때
host all all 127.0.0.1/32 scram-sha-256
# 3. Docker 내부 네트워크에서의 연결
# Docker Compose 네트워크 내의 다른 컨테이너(예: Infisical)에서 PostgreSQL 컨테이너로 접속 시
# 'docker network inspect <your_network_name>' 명령어로 정확한 서브넷 확인
# 예시: 172.18.0.0/16 또는 172.17.0.0/16
host all all 172.17.0.0/16 scram-sha-256
# 4. 내부망의 특정 VM/물리 서버에서 접속 허용 (예시)
# 만약 192.168.1.100 이라는 내부망 IP를 가진 VM에서 접속해야 한다면
# host all all 192.168.1.100/32 scram-sha-256
# 5. 특정 데이터베이스에 특정 사용자만 접근 허용 (더 세밀한 제어)
# 예시: infisical_db 데이터베이스에 infisical_user만 Docker 내부 네트워크에서 접근 허용
# host infisical_db infisical_user 172.18.0.0/16 scram-sha-256
# 예시: 관리자용 계정(postgres_admin)만 특정 IP 대역에서 모든 DB에 접근 허용
# host all postgres_admin 192.168.1.0/24 scram-sha-256
# 6. 모든 IP 주소에서 접속 허용 (보안상 매우 위험, 외부 노출 시 VPN 필수)
# 반드시 방화벽으로 접근을 제한하거나 VPN을 통해서만 접근하도록 강제해야 합니다.
# host all all 0.0.0.0/0 scram-sha-256
컨테이너 설정
터미널 접속 및 명령어 실행
chown 999:999 /volume1/docker/postgres/data
chown 999:999 /volume1/docker/postgres/backups
File Station 설정
/docker/mariadb/mysql
/docker/mariadb/backups
권한 설정: 생성 > Owner > 모든 권한
postgresql 설정
터미널 접속 및 명령어 실행
다음 명령어 입력하여 postgresql 접속
#!/bin/bash
psql -U postgres #pg_hba.conf의 local trust로 비밀번호 없이 로그인 됨
#비밀번호 강제 원할 시 psql -U postgres -W 입력
#접근 제어는 pg_hba.conf에서 설정
다음 명령어를 이용하여 admin 계정 생성
CREATE USER postgres_admin WITH PASSWORD 'PostgresAdmin1Geonil!'; -- admin 계정 생성
ALTER USER postgres_admin WITH SUPERUSER; -- Superuser 권한 부여
\du -- 유저 생성 확인
CREATE DATABASE postgres_admin; -- 계정 로그인 시 DB가 필수로 필요
ALTER DATABASE postgres_admin OWNER TO postgres_admin; -- 권한 부여
\l -- DB 확인 및 q로 종료
\q로 접속 종료 후, 해당 계정으로 접속해 정상적으로 적용되었는지 확인
어플리케이션용 데이터베이스 생성 및 계정 생성
infisical
CREATE USER infisical_user WITH PASSWORD 'Infisical1Geonil!'; -- 유저 생성
CREATE DATABASE infisical_db; -- 데이터베이스 생성
ALTER DATABASE infisical_db OWNER TO infisical_user; -- 권한 부여
\du
\l -- 확인
#!/bin/bash
psql -U infisical_user -W -d infisical_db # -d database 가 있어야 로그인이 된다
redis 설치
도커 설정
이미지
redis
 포트
6379:6379
볼륨 마운트
/docker/redis/data /data
#/docker/redis/data/redis.conf /usr/local/etc/redis/redis.conf
옵션, 지금은 마운트 안함
환경 변수
#REDIS_PASSWORD: "your_redis_password"
이 환경 변수는 redis.conf에서 참조하거나, Redis 스크립트에서 사용 가능
Redis 비밀번호 (필요 시), Redis에 비밀번호를 설정하려면 requirepass 지시어를 redis.conf 파일에 추가 필요
TZ Aisa/Seoul
restart: unless-stopped
redis.conf 파일
# redis.conf - Redis 설정 파일 예시
# 1. 일반 설정 (GENERAL)
# ========================
daemonize no # Redis를 데몬으로 실행할지 여부 (Docker 컨테이너에서는 'no'로 설정)
protected-mode yes # 보호 모드 활성화 (외부 접속 제한, 비활성화 시 보안 위험)
port 6379 # Redis 서버가 수신할 포트
# bind 127.0.0.1 # Redis가 수신할 IP 주소 (컨테이너에서는 일반적으로 설정 불필요, Docker 네트워크가 처리)
# '0.0.0.0'으로 설정하면 모든 인터페이스에서 수신 (컨테이너 내부에서 기본)
# loglevel notice # 로그 레벨 (debug, verbose, notice, warning)
# logfile "" # 로그 파일 경로 (비워두면 stdout/stderr로 출력, Docker에서는 stdout/stderr이 일반적)
# 2. 스냅샷 (SNAPSHOTS / RDB Persistence) - 데이터 영속성을 위한 핵심
# =======================================
# save <seconds> <changes>
# 지정된 시간(초) 동안 지정된 개수(<changes>)의 키가 변경되면 RDB 파일(.rdb)을 생성하여 디스크에 스냅샷 저장
save 900 1 # 900초(15분) 동안 1개 이상의 키가 변경되면 저장
save 300 10 # 300초(5분) 동안 10개 이상의 키가 변경되면 저장
save 60 10000 # 60초(1분) 동안 10000개 이상의 키가 변경되면 저장
stop-writes-on-bgsave-error yes # 백그라운드 저장(bgsave) 실패 시 쓰기 작업 중지 여부
rdbcompression yes # RDB 파일 압축 사용 여부
rdbchecksum yes # RDB 파일 체크섬 사용 여부
dbfilename dump.rdb # RDB 파일 이름
dir /data # RDB 파일이 저장될 디렉토리 (Docker 볼륨 마운트 경로와 일치)
# 3. AOF 영속성 (APPEND ONLY FILE / AOF Persistence) - 데이터 손실 최소화
# ==================================================
appendonly no # AOF 파일 사용 여부 (yes로 설정하면 AOF 활성화)
# AOF를 사용하면 RDB보다 데이터 손실 위험이 적으나, 파일 크기가 커짐
# appendfilename "appendonly.aof"
# appendfsync everysecond # AOF 파일을 디스크에 동기화하는 빈도 (always, everysecond, no)
# 'everysecond'가 좋은 균형
# no-appendfsync-on-rewrite no # AOF 재작성 중에도 fsync 실행 여부
# 4. 복제 (REPLICATION) - 고가용성/확장성 (클러스터 구성 시)
# ==========================================================
# replicaof <masterip> <masterport> # 이 서버를 다른 Redis 서버의 복제본으로 설정
# 5. 보안 (SECURITY)
# ==================
# requirepass your_redis_password # Redis에 접속하기 위한 비밀번호 설정 (강력 권장!)
# 이 비밀번호는 Infisical의 REDIS_URL에도 포함되어야 합니다.
# 'your_redis_password' 대신 실제 비밀번호로 교체
# rename-command CONFIG "" # CONFIG 명령 비활성화 (보안 강화)
# rename-command KEYS "" # KEYS 명령 비활성화 (큰 프로덕션 환경에서 성능 저하 방지)
# 6. 클라이언트 제한 (CLIENTS)
# ============================
maxclients 10000 # 최대 동시 클라이언트 연결 수
# 7. 메모리 관리 (MEMORY MANAGEMENT)
# ==================================
# maxmemory <bytes> # Redis가 사용할 최대 메모리 양 (초과 시 데이터 삭제 정책 적용)
# maxmemory-policy noeviction # 최대 메모리 도달 시 데이터 삭제 정책 (noeviction, allkeys-lru 등)
infisical 설치 및 DB 연동
도커 설정
이미지
infisical/infisical:latest-postgres
postgresql 사용시 태그는 latest-postgres
그냥 latest 사용시 mangodb를 불러오며, 에러 발생
 포트
8080:8080
볼륨 마운트
/docker/infisical/config /app/.infisical
환경 변수
ROOT_ENCRYPTION_KEY 7dc22797c2d74bc436f836696a888de5
openssl rand -hex 16 명령어로 생성
AUTH_SECRET bhR7cc44ItjJzz52g1C5tNmZPU2bfDQSIz4TlwdxxUU=
openssl rand -base64 32 명령어로 생성
DB_CONNECTION_URI postgresql://infisical_user:Infisical1Geonil!@postgres:5432/infisical_db
postgresql://[db_username]:[db_password]@[hostname:port]/[db_name]
REDIS_URL redis://redis:6379
SITE_URL https://infisical.ilfamilynas.synology.me
TZ Asia/Seoul
restart: unless-stopped
depends_on: postgres, redis
컨테이너 설정
권한 오류로 컨테이너 계속 비정상 종료되므로 도커 터미널이 아닌 NAS 직접 SSH 접속 후 다음 명령어 입력
#!/bin/bash
#ssh [username]@[ipaddress]
cd /volume1/docker/infisical
sudo chown -R 1000:1000 ./*
ls -l
권한 변경 확인
File Station 설정
/docker/infisical/config
권한 설정: 생성 > Owner > 모든 권한
도커 다시 시작
로그 확인
리버스 프록시 연동
접속 확인
일단은 편하게 let's encrpt를 쓰고 추후 cloudflare DNS 사용할 때 cloudflare 사용
2025-06-05 - 초안 작성
2025-06-20 - 날짜 표기 변경
부록 2.2. 프로젝트 구상 및 설계사항 북스택 문서화
목표
프로젝트 구상과 설계 사항을 북스택 문서화하여 체계적으로 정리합니다.
진행 사항
[2025-04-20 - 2025-05-12]
프로젝트 전반 기획
프로젝트 시작 후 많은 부분이 변경되고 체계적인 문서화가 이뤄지지 않아 간략하게 서술합니다.
[2025-04-20]
기존 NAS DS124의 성능의 한계를 느끼고 새로운 서버를 구축하기로 시도
[2025-04-21]
프로젝트 구상 시작. 여러가지 방안 구상
NAS OS 위 필요 Docker로 서비스
일반 서버 OS(ubuntu 혹은 다른 linux 계열 등) 위에 Docker 및 파일 서버 구축
Hypervisor 개념을 접하고, 가상화하여 예전 군에서 다루던 네트워크 환경 구축
[2025-04-21 - 2025-04-24]
단순 포트 개방 및 포트포워드 방식이 불편하던 중 reverse proxy 개념 접하여 sub domain 및 인증서 활용 시작
DS124내의 단순한 reverse proxy 기능 사용 후, SSL/TLS 개념에 대하여 처음으로 고찰하기 시작
프로젝트를 진행하기로 마음 먹은 김에 학습과, 경험을 동시에 쌓을 수 있는 hypervisor 안을 선정
안건 선정 후 가격, 운영비(전기세 포함), 필요 인터페이스, 24/7 운영 등을 고려하여 하드웨어 선정
Aoostar WTR Pro N150/RAM 16GB/SSD 256GB
https(443)/http(80) 외에 다른 프로토콜은 reverse proxy가 지원되지 않는 것을 확인
RDP(3389)를 리버스 프록시를 통해 이용하려 했으나 실패
reverse proxy 작동을 위해 Domain이 담긴 패킷이 필요하다는 것을 확인
VPN의 개념과 필요성 확인
[2025-04-24 - 2025-05-01]
hypervisor 위에 가상화를 고려하며 방화벽 및 IPS/IDS 서비스를 고려
실제 패킷의 흐름을 제어하고, 확인하고 싶어 선택
T5004의 기능적 한계(DHCP relay 부재, VLAN 부재)와 집안 네트워크 구조의 물리적 한계 확인
OPNsense를 통해 어느 정도 회피 계획을 수립 시작
필요 서비스들을 정리
LADP/SSO에 대한 개념의 부족, Kerberos는 아직 LADP/SSO를 고려하기 전이라 제외
내부 CA 도입으로 단순한 SSL/TLS를 통한 암호화 및 취약 알고리즘(NFS 등)을 VPN으로 격리 계획 
파일 서버/DNS 서버/Proxy 서버/내부 CA/도커를 활용한 VM 서버들/DB 서버/보안 실습 서버 등 선정
SPOF와 SoC에 대한 개념 정립 시작, 가지고 있는 한계 속에서 어떻게 최선의 결과를 만들어 낼 수 있을 지 고민
파일 시스템에 대한 고민을 시작, Btrfs/zfs 등의 시스템과 RAID를 통한 가용성에 대해 확인함
부족한 RAM과 4bay HDD를 위해서 btrfs 및 raid 10이 가장 적합하다고 결론
추후 접근이 편리하도록 Cloudflare를 이용하여 Domain을 구매함
[2025-05-01 - 2025-05-05]
필요한 서비스들을 점검하는 중 16GB RAM이 부족함을 확인 후 급하게 32GB RAM으로 교환
필요한 서비스들을 구축할 때 필요한 예상 장애 지점과 문제사항에 대하여 QnA 형식으로 정리를 시작
IPS/IDS 서비스가 많은 부하가 있다는 것을 확인 후 체험 및 공부 수준의 필터링/관제로 계획
N150 프로세서의 성능 한계 파악 후 RAM과 함께 리소스 분배 계획
데이터 백업과 모니터링에 대하여 계획(구체적 X)
추후 ACL의 관리의 용이함을 위하여 LADP/SSO에 대한 필요성을 인지, 하지만 잘 이해가 되지 않아 보류
VM/LXC/Docker의 논리적 구분 정도와 보안 위험성을 인지하고 서비스별로 올바른 수준의 격리를 실천 시도
LXC는 호스트의 커널을 공유
Docker는 호스트의 루트 권한을 요구
따라서 LXC에서 Docker의 실행은 비권장
Split Horizon DNS 개념을 확인 후 내외부망에서의 일관된 접속을 구현하려 계획
L2 통신이 게이트웨이를 거치지 않는고 ARP 및 Unicast 통신을 한다는 사실을 확인
OPNsense 상에서의 내부망 통신 통제가 아닌 각 클라이언트 별 iptable 및 방화벽 설정, Zero Trust 전략을 수립함
DMZ의 필요성과 어떤 경우의 효용성이 있는지 분석 후 도입하지 않기로 결정
관제팀 및 CERT 운용 없이는 공격 포인트만 추가
체계적인 문서화의 필요성을 깨닫고 문서화를 위한 도구들을 확인
Obsidian, Git 등이 있었으나 Bookstack이 가장 편리해 보여 Bookstack을 선택
[2025-05-05 - 2025-05-10]
서버에 필요한 하드웨어 배송되어 하드웨어 이상 확인
북스택으로 문서를 체계화 하기 전 프로젝트 내용을 최대한 다듬으며, 다이어그램 등으로 시각적 자료 생성 시작
각 서버에 필요한 OS 파일을 다운로드 하며 SHA 값을 체크(위장 OS 파일인지 확인)
DS124에 임시로 사용할 수 있는 Bookstack Docker를 설치
 Synology DSM 7.1 Container Manager 패키지 상에서 MariaDB 및 Bookstack 도커 실행 불가 상황
[2025-05-10 - 2025-05-12]
본격적인 Bookstack을 통한 문서 체계화 시작
LADP/SSO 기능 및 Kerberos, DNS, 내부 CA 까지 전담 가능한 FreeIPA의 존재를 확인 계획 전면 수정
VPN을 통한 NFS 암호화 > FreeIPA를 통한 Kerberos로 NFS 암호화
OPNsense 가 전담하던 내부 CA 기능을 SoC에 따라 FreeIPA에서 담당하도록 계획 수정
별도로 두려던 DNS를 FreeIPA와 통합
원래 DNS에서 담당하려 한 DDNS는 역시 SoC에 따라 외부와 통신하는 proxy에서 담당하도록 계획 수정
기존 Docker에서 사용하던 서비스를 그대로 이전할 수 있는 방법 확인(docker-compose.yml 및 .env, DB dump 등)
더 나아가 추후 프로젝트를 더욱 확장시킬 수 있는 HA 구성 등을 설립함(이상적인 아키텍쳐 환경)
[2025-05-13]
1. 프로젝트 개요 챕터 작성
1.1. 동기 및 목표
1.2. 하드웨어 구성  
[2025-05-15]
1. 프로젝트 개요 챕터 작성
1.3. 최종 목표 아키텍처
[2025-05-24]
1. 프로젝트 개요 챕터 작성
1.4. 추가 프로젝트 계획
2. 프로젝트 설계 챕터 작성
2.1. 하드웨어 선정
[2025-05-25 - 2025-05-28]
세부 내용 수정
각 서버 별 예상 RAM 수정
DBMS 단일 운영 불가 예상으로 DB server를 vm으로 변경 후 각 DBMS docker로 계획 수정
각 페이지의 내용 및 문체 수정
Ansible 도입을 통한 서버 설정 자동화 계획 추가
예상 프로세서 할당량 및 우선 순위 구체화
2. 프로젝트 설계 챕터 작성
2.2. 네트워크 설계
[2025-05-29]
세부 내용 수정
SSD를 SKHynix 256GB에서 Samsung 1TB로 변경
[2025-05-30 - 2025-05-31]
네트워크 구조 재설계
최종 목표 아키텍처에서 VLAN을 최대한 활용하여 untagged VLAN(VLAN0)과 hypervisor 내부의 VLAN1,2,3으로 구별
이상적인 아키텍처 부분 수정 - OpenWRT가 T5004를 지원하므로 물리적인 VLAN 구현이 생각보다 쉽기 때문에, 추후 OpenWRT 추가 프로젝트 진행
물리 구성도와 아키텍처 구성도 재작성
이상적인 아키텍처를 OpenWRT 적용과 HA(고가용성)을 위한 추후 프로젝트 계획으로 분리하여 현재 목표를 더욱 명확히 함
[2025-06-02]
네트워크 구조 고려사항 추가
NIC1과 NIC2를 vmbr 혹은 vtnet을 통해 어떻게 OPNsense와 연결할지에 대하여 명확하게 정의하고 다이어그램을 추가
[2025-06-04]
2. 프로젝트 설계 챕터 작성
2.3. 시스템 선정
[2025-06-05]
부록 1. 문서 작성 가이드라인 및 용어집 챕터 작성
부록 1.1 문서 작성 가이드라인
부록 2. 구현 일지 챕터 작성
챕터 작성 후 기존 구축 과정 및 일지 챕터의 [2025/04/20 - 현재] 프로젝트 구상 및 설계사항 북스택 문서화 (현재 페이지) 이동
To-Do List
부록 3. 문제 해결(Troubleshooting) 챕터 작성
챕터 작성 후 기존 구축 과정 및 일지 챕터의 [2025/05/06] Synology DSM 7.1 Container Manager 패키지 상에서 MariaDB 및 Bookstack 도커 실행 불가 상황  이동
구축 과정 및 일지 챕터 삭제
[2025-06-07]
2. 프로젝트 설계 챕터 작성
2.5. 구현 계획
[2025-06-08]
부록 3. 문제 해결(Troubleshooting)  챕터 작성
Bookstack 내 오류 해결 후 다음 내용 작성
Bookstack 도커 내부 게시글 이미지 갱신 불가 상황
2. 프로젝트 설계 챕터 작성
2.4. 보안 설계
[2025-06-14]
부록 1. 문서 작성 가이드라인 및 용어집 챕터 작성
부록 1.2. 용어집(임시)
1. 프로젝트 개요 챕터 부록 1. 문서 작성 가이드라인 에 따라 재작성 
1.1. 동기 및 목표
1.2. 하드웨어 구성  
[2025-06-15]
1. 프로젝트 개요 챕터 부록 1. 문서 작성 가이드라인 에 따라 재작성 
1.3. 목표 아키텍처
[2025-06-16]
세부 내용 수정
OpenWRT 적용과 HA(고가용성)을 위한 추후 프로젝트 계획을 각각 Open WRT 적용 계획과 HA를 위한 계획으로 분리
네트워크 구조 추가
AdGuard Home LXC를 추가하여 FreeIPA의 Local DNS와 연동, 광고 차단 기능 구현 및 다음 문서 수정
1. 프로젝트 개요 챕터 부록 1. 문서 작성 가이드라인 에 따라 재작성 
1.4. 추가 프로젝트 계획
[2025-06-17]
세부 내용 추가
VPN 내용 추가
PVE의 메모리 할당 부분에 KSM 내용 추가
DB/File/Web/WAS/Application Server 내 Docker 목록 작성
구현 계획에 DS124에 임시 서비스 설치 계획 추가
2. 프로젝트 설계  챕터 부록 1. 문서 작성 가이드라인 에 따라 재작성
2.1. 하드웨어 선정
2.2. 네트워크 설계
2.3. 시스템 선정
[2025-06-19]
2. 프로젝트 설계 챕터 작성
2.4. 보안 설계 내용 구체화
[2025-06-20]
세부 내용 변경
날짜 형식 표준에 맞게 변경
[2025-06-23]
세부 내용 변경
VLAN0을 Untagged VLAN로 수정
[2025-06-24]
세부 내용 추가
2.1 하드웨어 선정
각 VM/LXC 별 SSD 용량표 추가
2025-05-13 - 초안 작성
2025-06-20 - 날짜 표기 변경
부록 2.3. 네트워크 장비 설정
목표
네트워크 장비 설정의 진행 사항을 체계적으로 정리합니다.
진행 사항
[2025-06-19]
세부 내용 추가
VLAN10 - VPN Clients 망 추가
DS124 임시 서비스 구현 계획 변경
PVE 내 Debian (stable - netits) VM 위에 임시 서비스 구현
추후 서비스 생성 시 삭제
FreeIPA의 OS로 Rocky Linux 결정
IP Matrix 작성
3.1 IP Matrix 작성
임시 IP Matrix(VLAN0) 작성 완료
[2025-06-20]
세부 내용 추가 및 수정
임시 IP Matrix(VLAN0) Client 별 MAC 주소 추가
T5004/AX6000M 설정
3.2. T5004/AX600M 설정 작성
초기 설정
DHCP 서버 설정
Easy Mesh 설정
보안 설정
물리적 보안 확인
[2025-06-23]
세부 내용 추가 및 수정
VLAN0을 Untagged VLAN로 수정
2025-06-19 - 초안 작성
부록 2.4. PVE 설정
목표
PVE를 설치하고, 설정의 진행사항을 체계적으로 정리합니다.
진행 사항
[2025-06-21]
UID/GID Matrix 작성
4.1. UID/GID Matrix 작성
Local UID/GID 대역 정의
각 서버별 대표 UID/GID 정의
Docker의 UID/GID 정의
FreeIPA LDAP/SSO 전용 UID/GID 대역 정의
PVE 설치
4.2. PVE 설치
WTR Pro에 PVE 설치 완료
PVE 원격 접속 가능
sudo 기능 설치
admin 계정 생성 및 root 계정 설정
root 계정 ssh 접근 비활성화
root 계정 Web UI 접근 비활성화
pveadmin 계정 생성(2000:2000, sudo)
Web UI 상 pveadmin 권한 부여
[2025-06-23]
PVE 설치
4.2. PVE 설치
 vmbr 생성 및 설정
STP로 비활성화로 인한 주의점 명시
[2025-06-23]
PVE 상 VM/LXC 설치법
4.3. PVE 상 VM/LXC 설치법
VM 설치법 정리
LXC 설치법 정리
[2025-06-23]
PVE 내 iptable 적용
4.3. PVE 상 VM/LXC 설치법
iptables 규칙 적용
[2025-06-27] 세부내용 추가 4.1. Local Uid/gid 계획 세분화
2025-06-21 - 초안 작성
부록 2.5. 임시 Server 설정
목표
임시 Server VM을 PVE 위에 설치하고, 설정의 진행사항을 체계적으로 정리합니다.
진행 사항
[2025-06-25]
5.1. Debian 설치
PVE 상 Debian 설치 완료
root 계정 및 local 계정 설정
ssh root 계정 접근 차단
필수 패키지(iptables, sudo, curl) 설치 완료
iptables 규칙 적용
[2025-06-26]
5.2 DDNS 설정
DDNS 스크립트 작성(Cloudflare API 이용하여 직접 작성)
작동 확인
[2025-06-27]
5.3. Docker 설치 환경 설정
docker engine 및 compose plugin 설정 완료
2025-06-25 - 초안 작성
부록 3. 문제 해결(Troubleshooting)
[2025-05-06] MariaDB 및 Bookstack 도커 실행 불가 상황
목표
Synology DS124 모델 DSM 7.1 Container Manager 패키지 상에서 MariaDB 및 Bookstack 도커 실행 불가 상황 해결
장애 증상
MariaDB 도커 DB 생성 실패 및 Bookstack 도커 구동 실패
Container Manager 상에서는 정상 작동으로 표시
MariaDB 도커의 로그 중 Permission denied 메세지 확인
Bookstack 도커의 로그 중 DB connection failed 메세지 확인
Bookstack 도커 쉘에서 MariaDB 도커 접속 불가 확인
시도 조치
최초 MariaDB의 계정 및 DB 구성 문제로 판단 후 도커 쉘로 접속하여 Bookstack이 참조하는 DB 및 계정 수동 생성
DSM Container 패키지 상 도커 쉘 접속 방법: DSM Container manger > container > 도커 컨테이너 이름 > 작업 > 터미널 열기 > 생성으로 도커 내부 접속
이후 Bookstack에서 MariaDB로 접속 시도했으나 접속 불가
TLS 통신 문제인지 확인하기 위하여 TLS 접속 미사용 옵션 확인(have_SSL DISABLE)
show variables like '%ssl%';
---
have_SSL DISABLE
---
TLS 사용이 비활성화 되어 있으므로, 이로 인한 통신 문제가 아닌 것 확인 후 컨테이너 재배포 하여 구동 로그 확인
MariaDB 도커의 로그 중 Permission denied 메세지 확인
도커 내부 디렉토리(마운트 된 디렉토리) 권한 수동으로 설정
해결 방안
File station에서 마운트된 디렉토리의 소유자 확인
디렉토리의 소유자가 MariaDB 도커 컨테이너의 고유 UID인 1000이 맞는지 확인
단, 디렉토리를 미리 만들면 1000이 아닌 DSM 사용자의 ID가 지정되므로 도커가 자동으로 생성하게 둬야 한다.
마운트 된 디렉토리의 권한을 소유자+rwx 하고 난 뒤 컨테이너 재시작
MariaDB 도커의 로그 중 Permission denied 메세지 없이 정상 구동 되는 것 확인
Bookstack 도커에 마운트 된 디렉토리의 권한 역시 소유자+rwx로 설정
Bookstack 도커 실행 이후 MariaDB 도커 내에서 DB 정상 생성되는 것 확인
Bookstack 서비스 정상 작동 확인
원인 분석
Synology DSM은 도커 컨테이너가 마운트 하는 디렉토리에 ContainerManager 그룹이 rwx를 할 수 있도록 권한을 부여합니다. 이 때, MariaDB 및 Bookstack은 고유의 UID/GID를 컨테이너에서 할당하는데 이 부분에서 ACL 충돌이 발생한 것으로 보입니다. 따라서 소유자는 UID/GID:1000/1000인데 디렉토리의 소유자에게 권한이 없어서 디렉토리 접근이 불가능였습니다. 이는 정상적인 DB 생성 및 통신이 불가능한 결과를 만들었습니다. 명시적으로 디렉토리의 소유자에게 rwx 권한을 부여한 이후 문제는 더 이상 발생하지 않았습니다.
결론
Docker는 가상화된 환경을 제공하지만, Dockerd 실행 과정에서 host의 권한 구조를 따릅니다. 이 경우 특수한 목적의 OS(Synology DSM)등에서는 세부적이지 못한 권한 구조와 Docker의 권한 구조가 충돌할 수 있습니다. 그러므로 일반적인 Linux 환경이 아닌 이런 특수한 환경에서 Docker 구동시 반드시 마운트 된 디렉토리의 소유자와 권한, 그리고 컨테이너 내부의 UID/GID가 일치하는지 확인하여야 합니다.
또한 공식 문서나, 블로그 상의 안내 방법은 어디까지나 일반적인 상황에서의 방법이므로, 각 명령어에 대한 이해 없이 단순히 복사 붙여넣기를 할 경우 예기치 못한 오류 혹은 충돌이 발생할 수 있으므로, 장애 발생시 언제나 기본적인 ACL등 부터 체크하고 로그를 꼼꼼히 확인하는 습관을 들여야 할 것입니다.
2025-05-16 - 초안 작성 및 h2 태그 수정
2025-05-25 - 문체 수정
2025-05-31 - 도커 쉘 접속 방법 추가
2025-06-05 - TLS 통신 부분 명확화
2025-06-20 - 날짜 표기 변경
[2025-06-08] Bookstack 도커 내부 게시글 이미지 갱신 불가 상황
목표
Synology DS124 모델 DSM 7.1 Container Manager 패키지 상에서 실행되는 Bookstack 도커 내부 게시글의 이미지 갱신 불가 상황 해결
장애 증상
Bookstack 내부 페이지 작성 시, 이미지 파일을 갱신하여도 이전 이미지 출력
시도 조치
Bookstack 내부, 이미지 파일 업로드 디렉토리 확인
내부 디렉토리 (/www/uploads/images/gallary/YYYY-MM) 내부 파일 변경 확인
정상적인 갱신 확인 후, Cache 문제 확인 위하여 다른 기기의 브라우저로 접속
정상적으로 갱신 이미지 출력 확인
강력 새로 고침 시도(단축키 ctrl+F5 혹은 ctrl+shift+R) 후 기존 브라우저 상 갱신 이미지 정상 출력 확인
해결 방안
이미지 등 페이지 내부 콘텐츠가 갱신되지 않을 시 캐시 제거 및 강력 새로 고침 시도 후 갱신 확인
원인 분석
Cache는 서버의 지연을 줄이기 위하여 웹 페이지/이미지/멀티미디어 등을 임시로 저장하는 기술입니다. 브라우저는 페이지를 로딩할 때 HTML 파일을 새로 받아오지만, 브라우저 내부 Cache에 유효 기간이 남은 파일들을 다시 불러오지 않고 사용합니다. 이는 데이터 사용량을 줄일 뿐 더러, 속도면에서도 유리하기 때문입니다. 하지만 이러한 파일들이 수정되었을 경우, Cache 내부의 파일을 참조하고 새로 수정된 파일을 불러오지 않아 수정된 파일이 웹 페이지 상에서 갱신되지 않는 경우가 있습니다.
이러한 경우 직접 브라우저 설정에서 Cache를 제거하거나, 강력 새로 고침 기능을 이용하여 Cache 내부에 있는 모든 데이터를 무시하고 다시 불러온다면 해결됩니다.
결론
서비스에 발생하는 오류는 서비스 자체의 문제일 수도 있지만 반대로 서비스를 불러오는 클라이언트의 문제일 수도 있습니다. 따라서 어떠한 오류가 발생할 경우 서비스 부분만을 찾아보는 것이 아닌 클라이언트, 네트워크 등 다각변에서 접근할 필요가 있습니다.
2025-06-08 - 초안 작성
2025-06-20 - 날짜 표기 변경
[2025-06-19] - Gitea 도커 초기 설정 페이지에서 Gitea 설치 클릭시 504 Gateway Timeout 발생 상황
목표
Synology DS124 모델 DSM 7.1 Container Manager 패키지 상에서 Gitea 도커 초기 설정 도중, 초기 설정 페이지에서 Gitea 설치하기 버튼 클릭시 504 Gateway Timeout 발생 상황 해결
장애 증상
Gitea 도커 설치 후 초기 설정 페이지까지 정상 접속 확인
이후 Gitea 설치하기 버튼 클릭시 Synology NAS의 "죄송합니다. 찾고 있는 페이지를 발견하지 못했습니다." (404 Not Found) 오류 발생
Gitea container log 상에는 오류 메세지 발생 없음
브라우저 개발자 도구 확인하여 504 Gateway Timeout 오류 발생
시도 조치
최초 MariaDB의 계정 및 DB 구성 문제로 판단 후 도커 쉘로 접속하여 Gitea가 참조하는 DB 및 계정 설정 확인
DSM Container 패키지 상 도커 쉘 접속 방법: DSM Container manger > container > 도커 컨테이너 이름 > 작업 > 터미널 열기 > 생성으로 도커 내부 접속
DB 및 계정 정상 설정 확인 후 Gitea 도커 쉘로 접속하여 ping 명령어를 통해 연결 상태 확인
도커 container에 마운트 된 /docker/gitea/data 폴더의 권한 확인
모든 문제 확인 후 Synology의 Reverse Proxy 확인 후 프록시 연결 시간 제한 , 프록시 보내기 시간 제한 , 프록시 읽기 시간 제한 60초에서 600초로 변경
해결 방안
리버스 프록시 상 프록시 연결 시간 제한 , 프록시 보내기 시간 제한 , 프록시 읽기 시간 제한 설정을 충분히 길게 설정
초기 설정 페이지 리다이렉션 후 Gitea 설치 재시도
정상 설치 페이지 작동 확인
원인 분석
Synology의 Reverse proxy 기능은 대상 서버가 시스템 리소스의 과도한 사용을 막기 위해 다음과 같은 기본 타임 아웃 설정을 사용합니다.
프록시 연결 시간 제한 = 60초
프록시 보내기 시간 제한 = 60초
프록시 읽기 시간 제한 = 60초
이 때 DS124의 제한된 사양(RAM 1GB, 읽기/쓰기 cache 없음, HDD 사용)은 Gitea같은 서비스를 설정하는 시간을 지연시키는 원인이 됩니다. Gitea가 설치에 걸리는 시간이 60초를 넘어가면서 Reverse Proxy는 자동으로 클라이언트와의 연결을 종료하였고, 이에 504 Gateway Timeout 오류가 발생한 것입니다.
결론
특정 서비스, 특히 웹 서비스를 사용할 때 서비스나 네트워크 연결 등 자체의 문제가 전혀 없더라도 Reverse Proxy의 설정으로 인하여 504 Gateway Timeout과 같은 오류가 발생할 수 있습니다. 백엔드 작업 시간이 Reverse Proxy의 타임아웃 설정보다 길기 때문에 발생하는 문제이며, 특히 제한된 하드웨어 성능 하에서는 예상보다 초기화 시간이 길어질 수 있으므로 이를 잘 확인해야 합니다.
2025-06-19 - 초안 작성
2025-06-20 - 날짜 표기 변경