ROAMSWITCH SERVER 1.10.6 · 최종 업데이트: 2026-09-26

RoamSwitch Server Edition 운영 매뉴얼

Cloud VPS(AWS, GCP, Azure, Linode, DigitalOcean), 베어메탈 데이터 센터 및 컨테이너 호스트에 대한 공식 설치, 배포 및 운영 참조입니다.

1. 개요 및 요구사항

RoamSwitch Server Edition은 공용 인터넷에 노출되는 Linux 서버용으로 설계된 헤드리스(GUI 종속성 없음) 자율 방어 및 무결성 모니터링 제품군입니다.

Wi-Fi 상태에 따라 프로필을 동적으로 전환하는 클라이언트 버전과 달리 서버 버전은 인바운드 기본 삭제, 중요 경로 파일 무결성 모니터링(FIM), 예방적 커널 LPE 완화(Frag Gap), eBPF 런타임 위협 감지, SSH 잠금 방지 및 자동화된 다중 채널 긴급 경고(Telegram/LINE/Webhook)을 시행합니다.

Zero Telemetry 원칙(사용자 정보는 보내지 않음. 기본값의 외부 통신은 서명된 데이터의 수신뿐)
RoamSwitch는 고객 서버의 IP 주소, 포트 정보, 파일 해시, 시스템 로그, 진단 결과를 벤더나 클라우드로 전송하지 않습니다. 데몬 바이너리 자체에는 TLS / HTTP 클라이언트가 내장되어 있지 않습니다. 기본 설정에서 외부로 나가는 통신은 패키지 매니저의 업데이트를 제외하면 서명된 데이터의 익명 가져오기(cve_kernel_map_updates_enabled, 기본값 켜짐)뿐입니다. 가져오는 것은 커널 CVE 데이터, Web 규칙, 그리고 Ubuntu 서버에서는 OS 패키지 CVE 대조에 쓰는 Ubuntu 패키지 CVE 데이터입니다. roamswitch-updater가 lafine.net에서 Ed25519 서명이 붙은 데이터를 HTTPS GET으로 받는 것으로(서명된 매니페스트는 매일 확인, CVE 데이터 본체는 약 30일에 1회, Web 규칙은 매니페스트의 버전이 새로울 때만), 쿼리 문자열과 쿠키가 없고, 소프트웨어를 나타내는 헤더는 고정된 User-Agent: RoamSwitch-Updater뿐이며, 이 호스트의 정보는 일절 보내지 않습니다. roamswitch server config set cve_kernel_map_updates_enabled false(또는 설정 마법사, 서버 TUI)로 끌 수 있고, 꺼도 감사는 내장 베이스라인으로 동작하지만, Ubuntu 서버의 OS 패키지 CVE 대조는 'CVE 데이터 없음'으로 표시됩니다. 그 밖에 외부로 나가는 통신은 운영자가 명시적으로 활성화한 다음 항목뿐입니다. 알림(Telegram / LINE / 웹훅), npm 서명 검증(npm_audit_signatures_enabled. 명령 실행 시에만), 그리고 이벤트 전달(forward_enabled, 기본값 꺼짐. §16). 이벤트 전달은 운영자가 지정한 운영자 조직의 대상(SIEM·syslog·웹훅)에만 전송되며, TLS syslog는 시스템의 openssl, 구조화 웹훅은 curl 서브프로세스로 전송합니다. 이 밖에 Sensor 페어링(sensor_pairing_enabled, 기본값 꺼짐)과 동봉된 별도 도구 accessscope의 LLM 연동(기본값 꺼짐. 명령줄에서는 --llm을 지정했을 때만)도 운영자가 활성화했을 때만 외부에 접속합니다. 예외 목록은 Server Edition 보안 설계 문서의 Zero Telemetry 항목에 있습니다.

시스템 요구사항

2. 설치 절차

공식 서명된 저장소를 사용하면 표준 시스템 패키지 관리자를 통해 자동 종속성 해결 및 원활한 업데이트가 보장됩니다.

2.1 APT(우분투/데비안/라즈베리파이 OS)

# 1. 저장소 서명 키 등록
curl -fsSL https://lafine.net/apt/roamswitch-archive-keyring.asc \
  | sudo gpg --dearmor -o /usr/share/keyrings/roamswitch-archive-keyring.gpg

# 2. 저장소 추가
echo "deb [arch=amd64,arm64 signed-by=/usr/share/keyrings/roamswitch-archive-keyring.gpg] https://lafine.net/apt stable main" \
  | sudo tee /etc/apt/sources.list.d/roamswitch.list

# 3. 설치
sudo apt update && sudo apt install roamswitch-server

2.2 DNF / RPM (페도라 / RHEL / AlmaLinux / 록키 리눅스)

# 1. GPG 키 가져오기
sudo rpm --import https://lafine.net/rpm/RPM-GPG-KEY-roamswitch

# 2. 저장소 설정 파일 추가
sudo curl -fsSL -o /etc/yum.repos.d/roamswitch.repo https://lafine.net/rpm/fedora/roamswitch.repo

# 3. 설치
sudo dnf install roamswitch-server

2.3 오픈수세(zypper)

sudo rpm --import https://lafine.net/rpm/RPM-GPG-KEY-roamswitch
sudo zypper addrepo https://lafine.net/rpm/opensuse/roamswitch.repo
sudo zypper refresh && sudo zypper install roamswitch-server
패키지 상호 배제(충돌)
Client Edition(roamswitch) 및 Server Edition(roamswitch-server)은 상호 패키지 충돌을 선언합니다. 헤드리스 서버 환경에서는 항상 roamswitch-server를 선택하십시오.

3. 초기 설정 및 서비스 확인

3.1 서비스 현황

설치 후 roamswitch-server.service은(는) 즉시 시작되고 시스템 부팅 시 활성화됩니다.

sudo systemctl status roamswitch-server.service

3.2 대화형 설정 마법사

아래 명령으로 대화형 설정 마법사를 실행하여 포트, 유지보수 소스 IP, 알림 채널, 격리 조치를 대화식으로 구성합니다(각 기본값을 적용하려면 Enter를 누르세요).

sudo roamswitch server setup
  1. 포트: SSH 유지보수 포트(기본값 22)와 공개 서비스 포트(기본값 80,443)를 각각 쉼표로 구분하여 입력합니다.
  2. 유지보수 소스 IP 지정: SSH 배스천이나 모니터링 IP/CIDR(예: 203.0.113.50/32)을 지정합니다. 마법사에서는 필수이며 비워 둔 채 진행할 수 없습니다. 모든 IP에서 SSH를 허용하려면 ANYWHERE를 입력합니다(이 경우 격리 중에도 관리용 SSH가 전 세계에 열려 있습니다).
  3. 알림 채널: Telegram 봇, LINE Messaging API 및/또는 범용 Webhook(Slack / Discord / Teams)을 각각 활성화합니다.
  4. 중대한 eBPF 이벤트에 대한 대응: isolate(호스트 격리), freeze(프로세스 정지), alert_only(알림만) 중에서 선택합니다. 저장 후 테스트 알림 전송 및 서비스 재시작 여부를 확인합니다.
  5. CVE 데이터 자동 업데이트(기본값 y): 컨테이너 격리 감사가 사용하는 커널 CVE 데이터베이스와 Web 규칙, 그리고 Ubuntu 서버에서는 OS 패키지 CVE 대조에 쓰는 Ubuntu 패키지 CVE 데이터를 lafine.net에서 익명으로 가져올지(서명된 매니페스트는 매일 확인, CVE 데이터 본체는 약 30일에 1회, Web 규칙은 매니페스트의 버전이 새로울 때만. HTTPS GET만, 쿼리 문자열·쿠키 없음, 소프트웨어를 나타내는 헤더는 고정된 User-Agent: RoamSwitch-Updater뿐, Ed25519 서명 검증) 선택합니다. 이 호스트의 정보는 보내지 않습니다. 서버 버전에서 기본값으로 켜져 있는 유일한 외부 통신이며, n으로 하면 멈춥니다(감사 자체는 내장 베이스라인으로 동작하지만, Ubuntu 서버의 OS 패키지 CVE 대조는 'CVE 데이터 없음'으로 표시됩니다). 기존 server.conf에서 이미 꺼 두었다면 기본 답도 n 그대로입니다.
  6. File Scan Guard 설정: 메일 중계·공유 폴더·업로드 대상의 바이러스 스캔(§13)을 활성화할지와 대상 디렉터리를 설정합니다. 선택 기능이며 기본값은 비활성화입니다.
  7. Resource Guard 설정: 네트워크에 공개된 서비스의 지속적인 RSS 증가(메모리 누수/DoS)나 크래시 루프를 탐지하는 리소스 고갈 가드의 활성화를 설정합니다.
  8. 상시 가동 핵심 보호 기능 안내 표시: 인바운드 기본 거부, FIM, eBPF 연동 등 마법사를 거치지 않고 항상 활성화되는 핵심 방어 기능 목록이 표시됩니다(입력 항목 없음).
  9. 조사 에이전트 연계(investigation) 설정: eBPF 중대 이벤트나 크리티컬 패스 FIM·의존성 락파일 변조 탐지(모두 통지 전용 판정 시)에 대해, 외부 AI 에이전트 CLI(Claude Code / agy / Codex CLI / OpenCode, 또는 사용자 지정 명령)에 1차 조사를 자동으로 위임할지 설정합니다(§10.1). 기본값은 비활성화입니다.
Telegram Chat ID 관련 참고 사항
슈퍼그룹의 Chat ID는 -100으로 시작하는 음수입니다(예: -1001234567890). curl -s "https://api.telegram.org/bot<token>/getUpdates" 응답의 "chat":{"id":...} 필드에서 올바른 값을 확인할 수 있습니다.

3.3 34개 항목 서버 보안 감사

sudo roamswitch status --server

방화벽 규칙, Frag Gap 완화, Yama LSM 제한, 권한 및 Docker 포트 노출에 대한 즉각적인 34회 검사 감사를 수행하여 0~100의 보안 점수 및 등급(S~C)을 생성합니다.

각 점검 항목에는 컴플라이언스 보고에 활용할 수 있도록 NIST CSF 2.0 기능/범주(해당하는 경우 반드시 부여)와, 확신도 높은 매핑이 존재하는 경우에 한해 CIS Controls v8 관리 항목 번호가 부여됩니다.

4. 방화벽 및 포트 규칙

설정 파일 /etc/roamswitch/server.conf는 민감한 정보를 보호하기 위해 권한 0600(root만 읽기/쓰기 가능)이 강제됩니다. 형식은 플랫한 key=value이며, [network]와 같은 섹션 헤더 줄은 무시됩니다.

# --- 방화벽 ---
ssh_ports=22                     # SSH 관리용 포트(긴급 격리 시에도 유지)
allowed_ports=80,443             # 공개 서비스 포트
whitelist_ips=203.0.113.10/32    # 유지보수 소스 IP / CIDR(비우면 전체 허용, 이전 이름 admin_source_ips 가능)
protect_docker_ports=true        # DOCKER-USER 보호(컨테이너의 방화벽 우회 차단)
preserve_ssh_on_isolation=true   # 긴급 격리 시에도 SSH 관리 경로 유지(false로 설정 시 완전 차단)

# --- eBPF 런타임 가드 ---
action_on_critical=isolate       # isolate | freeze(SIGSTOP+네트워크 차단) | alert_only
ebpf_socket_path=/run/roamswitch/events.sock

# --- Egress / C2 차단 & DNS 싱크홀 ---
egress_ip_blocklist_enabled=true # 기본값 true. 패키지에 포함된 시드에는 JPCERT/CC가 공표한 공격 서버 3개만 들어 있으며, 피드를 설치하기 전에는 그 외에는 아무것도 차단하지 않음
metadata_watch_enabled=true      # 기본값 true. root가 아닌 프로그램/컨테이너가 클라우드 메타데이터에 접속하면 알림(기록만, 차단 안 함. SSRF 감지)
metadata_watch_allow_users=      # 메타데이터를 정상적으로 사용하는 계정(이름 또는 숫자 ID, 쉼표 구분. root는 항상 제외)
dns_sinkhole_enabled=false       # 기본값 false(사내 DNS/split-horizon 환경을 깨지 않기 위해)
dns_sinkhole_provider=quad9      # quad9 | cloudflare | adguard | cleanbrowsing

# --- 컨테이너 격리 CVE 데이터(기본값 켜짐) ---
cve_kernel_map_updates_enabled=true # 기본값 true. 기본값으로 켜져 있는 유일한 외부 통신(서명된 커널 CVE 데이터, Web 규칙, Ubuntu 서버에서는 Ubuntu 패키지 CVE 데이터. 매니페스트는 매일, CVE 데이터 본체는 약 30일에 1회. 익명·수신 전용·서명 검증). false로 하면 멈춤

# --- 알림 ---
telegram_enabled=false
telegram_bot_token=
telegram_chat_id=
line_enabled=false
webhook_enabled=false
webhook_url=
webhook_block_private_addresses=false   # opt-in SSRF guard
webhook_allow_addresses=

# --- 시스템 ---
language=ja
fim_check_interval_secs=300      # 이전 이름 scan_interval 가능
isolation_hard_cap_secs=3600     # 고신뢰도 격리가 축소 모드로 전환되기까지의 초 수(0 = 해제될 때까지 유지)

설정을 직접 편집한 경우 서비스를 재시작해야 반영됩니다(roamswitch-server는 reload를 지원하지 않음):

sudo roamswitch server restart

4.1 심각도별 정책(guard.yaml, 선택 사항)

action_on_critical만으로는 세밀한 제어가 부족한 경우 /etc/roamswitch/guard.yaml을 생성합니다(배포 템플릿은 /etc/roamswitch/guard.yaml.example). 파일이 없으면 server.conf의 action_on_critical로부터 동등한 정책이 자동으로 합성되므로 기존 운영에는 영향이 없습니다.

policies:
  on_warning:
    action: log_and_notify
  on_critical:
    server_action: isolate_process
    kill_process: true
    fallback_to_host_isolation_after_retries: 3   # 동일 프로세스가 재발하면 호스트 격리로 자동 승격
  on_emergency:
    action: isolate_host_all
    kill_process: true

server_safeguards:
  maintenance_ssh_ports: [22]
  safety_timer_secs: 300   # 저신뢰도 격리를 자동 복구하기까지의 초 수(0이면 비활성화). 고신뢰도 격리에는 적용되지 않음(아래 참조)

격리의 자동 해제(신뢰도별): 고정 타이머만으로 해제하면 기다리기만 하는 공격자에게 돌파당합니다. 그래서 격리의 트리거를 신뢰도로 분류합니다. (1) 저신뢰도(eBPF의 Error / Warning 이하, 범용 포트 이상 등): 짧은 타이머(safety_timer_secs, 기본값 300초) 후에 전체 복구합니다. (2) 고신뢰도(카나리아 변조, 랜섬웨어성 대량 덮어쓰기, eBPF의 Critical / Emergency / Alert, 동일 규칙의 반복 에스컬레이션): 조용히 복구하지 않습니다. 짧은 타이머 이후에는 원인의 재확인을 계속하며, 원인이 사라진 것을 확인했을 때만 알림과 함께 해제합니다. 확인하지 못한 채 ack도 없이 상한(server.conf의 isolation_hard_cap_secs, 기본값 3600초, 0 = 상한 없음)에 도달하면 축소(DEGRADED) 모드로 전환됩니다. (3) 축소 모드: 유지보수 SSH 포트와 화이트리스트 IP의 관리 경로 이외의 신규 통신은 계속 차단하며, 시간 경과만으로는 절대 자동으로 열리지 않습니다. 해제는 원인 소멸의 재확인, 명시적인 ack(축소 중의 ack는 해제를 의미합니다), 또는 sudo roamswitch emergency-restore뿐입니다. 어떤 격리에서도 관리 경로(maintenance_ssh_ports와 whitelist_ips) 이외의 확립된 연결(공격자가 이미 맺어 둔 C2나 리버스 셸 등)은 차단됩니다. 각 전환은 인시던트 타임라인에 기록되며 10개 언어로 알림이 전달됩니다.

sudo roamswitch server ack

roamswitch server ack는 FULL 격리 중에는 격리를 엄격하게 유지한 채 확인 응답하며(운영자가 인계받은 것으로 간주하여 축소 모드로의 전환이나 자동 해제를 멈춥니다), 축소 모드 중에는 해제합니다.

4.2 Egress/C2 차단 및 DNS 싱크홀

egress_ip_blocklist_enabled=true(기본값)이면 알려진 악성 IP로의 아웃바운드 연결을 nftables로 차단합니다. 피드는 /var/lib/roamswitch/threatfeed/malicious_ips.txt(업데이트 메커니즘이 관리)가 있으면 그것을, 없으면 패키지에 포함된 시드(/usr/share/roamswitch/threatfeed/)를 사용합니다. 둘을 합치는 것이 아니라 한쪽만 사용합니다. 시드에는 JPCERT/CC가 2026년 10월 15일 보고서(웹 서비스·API를 노린 공격)에서 공표한 공격 서버 주소 3개(210.149.87.120, 118.26.36.16, 69.10.51.162)만 들어 있습니다. 임대 서버의 주소는 다른 사람에게 재할당될 수 있으므로, 정상적인 접속 대상이 막히면 이 파일의 해당 줄을 확인하세요.

dns_sinkhole_enabled=true로 설정하면 필터링 DNS 리졸버(Quad9 / Cloudflare / AdGuard / CleanBrowsing)로 강제 전환되어 알려진 악성 도메인 해석을 차단합니다. 사내 DNS나 split-horizon 구성을 깨뜨릴 수 있어 기본값은 false입니다.

metadata_watch_enabled 开启(默认)时,只有当非 root 程序或容器新建到云元数据地址(169.254.169.254、169.254.170.2、100.100.100.200、fd00:ec2::254)的连接时,nftables 表 inet roamswitch_metadata_watch 才会写入一条内核日志,服务器守护进程按账户(容器按来源地址)每天通知一次。只记录和通知,不阻断。攻击者传入自己服务器上的 URL(以 302 转发到内部地址)时,访问日志里只会留下外部 URL,因此只有这条出站流量能看到第二跳。正常使用云角色的应用同样会被报告,可在 metadata_watch_allow_users 中加入其账户予以豁免(容器无法豁免)。

4.3 착신 포트 스캔 탐지 가드

짧은 시간 안에 다수의 서로 다른 포트에 접속한 송신 IP를, nftables 로그 규칙과 journald의 대조로 탐지합니다(기본 활성화). 확도가 높은 스캔 발신지는 일정 시간 자동으로 차단할 수 있습니다. RoamSwitch Sensor(§10.1과는 별개 기능. 페어링된 Sensor에 의한 전체 포트 감사 프로브는 오탐 대책으로 제외됩니다)와 병용할 경우, Sensor 측 IP는 자동으로 제외됩니다.

# --- 착신 포트 스캔 탐지(기본 활성화) ---
port_scan_detect_enabled=true              # false로 비활성화
port_scan_detect_min_distinct_ports=15     # 이 수 이상의 서로 다른 포트로의 접속을 스캔으로 판정
port_scan_detect_window_secs=300           # 판정 집계 윈도우(초)
port_scan_block_enabled=true               # 탐지된 스캔 발신 IP의 자동 차단
port_scan_block_duration_secs=600          # 자동 차단의 지속 시간(초)

탐지 이력은 roamswitch port-scan 명령으로 확인할 수 있습니다.

4.4 DNS 터널링 탐지 가드

이 호스트 자신이 송신하는 DNS 트래픽(UDP/TCP 53번 포트)만을 대상으로 전용 NFQUEUE를 통해 검사합니다. 레이블 길이가 40바이트를 초과하면서 섀넌 엔트로피도 3.8을 초과하는 질의, 또는 TXT/NULL 레코드의 qtype인 질의를 의심스러운 형태로 간주하며, 최근 60초 동안 이러한 질의가 10건에 도달하면 통지합니다(동일 조건의 재통지에는 300초의 쿨다운을 둡니다). iodine, dnscat2 등 DNS 터널링/정보 유출 도구에 특징적인 질의 패턴 탐지가 목적입니다. 배후의 nftables 규칙에는 다른 DNS 계열 가드와 동일한 bypass 플래그가 붙어 있어, 이 가드가 정지되거나 응답하지 않는 경우에도 DNS 이름 해석이 방해받는 일은 전혀 없습니다. 통지 전용 기능으로, 자동으로 차단하지 않습니다. 다른 설정 키와 마찬가지로 dns_tunnel_detect_enabled의 변경은 서비스 재시작 후에 반영됩니다.

# --- DNS 터널링 탐지(기본 활성화) ---
dns_tunnel_detect_enabled=true              # false로 비활성화

4.5 SSH 무차별 대입 가드

sshd의 로그인 실패를 로그에 기록되는 즉시 읽어 접속 출처별로 집계합니다(기본 활성). 설정한 횟수(기본 600초에 6회)에 도달한 접속 출처는 nftables의 inet roamswitch_sshguard 테이블로 SSH 포트에 대한 새 연결만 차단합니다. 첫 차단은 1시간이며, 같은 출처가 반복될 때마다 4배로 늘어나 최대 7일입니다. 금지와 동시에, 그 접속 출처가 이미 SSH에 맺어 둔 연결도 끊습니다(conntrack 항목을 지우고 응답 방향 패킷을 버리는 규칙을 더해 끊습니다). 금지 대상은 인증된 세션도, 공개 키 로그인 성공 기록도, 면제도 없는 접속 출처뿐이며 끊기 직전에 한 번 더 확인하므로, 인증된 세션과 면제된 접속 출처는 끊지 않습니다. 외부로는 통신하지 않으며, journalctl·nft·loginctl 등을 로컬에서 실행할 뿐입니다.

로그는 journalctl -f로 흘려 읽습니다. journald는 기록한 프로세스를 함께 남기므로 sshd 이외가 쓴 줄은 읽지 않습니다. journald가 없는 환경에서는 /var/log/auth.log 또는 /var/log/secure를 추적하는데, 이 파일들은 syslog를 쓸 수 있는 로컬 사용자라면 줄을 위조해 제3자를 차단시킬 수 있습니다. journald가 있는 환경에서는 ssh_guard_log_source를 기본값 auto로 두십시오. 집계하는 실패는 비밀번호 실패, 존재하지 않는 사용자, 사용자 이름을 밝힌 뒤의 인증 전 연결 종료, 시도 횟수 초과, PAM 인증 실패입니다. 공개 키 실패(Failed publickey)는 집계하지 않습니다. 키를 여러 개 가진 정상 클라이언트가 매번 남기기 때문입니다. 접속 출처 주소는 줄 끝의 from <IP> port <N>에서 읽습니다. 사용자 이름에는 공격자가 임의의 문자열을 넣을 수 있지만, 거기에 위조한 출처를 적어도 제3자는 차단되지 않습니다. IPv6는 /64 단위로 집계하고 차단도 /64 단위입니다(그 /64에 게이트웨이처럼 지켜야 할 주소가 있으면 접속 출처 주소 하나만 차단).

차단하지 않는 대상은 게이트웨이, DNS 리졸버, 이 서버 자신, 루프백, whitelist_ips, ssh_guard_allow, 인증에 성공한 SSH 세션이 지금 열려 있는 접속 출처, 최근 30일(ssh_guard_trust_key_days) 안에 공개 키 인증으로 성공한 접속 출처(비밀번호 성공은 포함하지 않음), 페어링된 Sensor입니다. 이런 출처가 실패를 거듭해도 기록만 남깁니다. 사설 주소는 면제하지 않습니다. 내부의 침해된 단말도 무차별 대입의 발신원이 되기 때문입니다. 인증 전 연결만 열어 둔 출처도 면제하지 않습니다.

fail2ban(sshd jail이 활성인 경우)이나 CrowdSec(firewall bouncer가 동작 중인 경우)이 이미 sshd를 지키고 있으면, 같은 주소를 이중으로 차단하지 않도록 차단은 양보하고 탐지와 알림만 수행합니다(ssh_guard_yield_to_fail2ban, 기본값 true). ssh_guard_block_enabled=false일 때도 탐지와 알림만 합니다. 알림은 차단했을 때(탐지만 하는 경우에는 임계치에 도달했을 때)와, 실패를 거듭한 접속 출처에서 로그인에 성공했을 때(중대. 무차별 대입이 성공했을 가능성이 있습니다. 세션은 끊지 않음)에 한합니다. 실패 한 건마다 알리지 않으며, 1시간에 10건을 넘는 접속 출처는 1통으로 묶습니다.

차단 목록은 roamswitch server bans(--json 지원. 목록은 root만 읽을 수 있으므로 sudo를 붙입니다)로, 해제는 sudo roamswitch server unban <IP | IPv6 /64 | --all>로 합니다. 해제하면 그 접속 출처의 실패 횟수도 지워집니다. 차단은 서버를 재시작한 뒤에도 남은 시간과 함께 복원됩니다. roamswitch emergency-restore는 SSH 차단을 해제하지 않습니다(네트워크를 되돌린다고 해서 비밀번호를 추측하던 상대를 되돌릴 이유는 없기 때문입니다). 복구 메뉴(roamswitch recover)와 제거는 테이블째 삭제합니다. MCP의 get_ssh_guard_incidents는 읽기 전용이며 차단을 해제하는 도구는 없습니다. Server TUI에서는 인시던트 화면의 "SSH 무차별 대입"에서 차단 목록을 확인하고 해제할 수 있습니다(root로 실행했을 때. §17).

roamswitch server bans
sudo roamswitch server unban 203.0.113.9
sudo roamswitch server unban --all

설정 키는 10개이며 모두 ssh_guard_로 시작합니다(§18.1). 예를 들어 sudo roamswitch server config set ssh_guard_max_failures 10처럼 변경하고, 반영하려면 sudo roamswitch server restart가 필요합니다. 가드 전체를 멈추려면 ssh_guard_enabled=false, 탐지와 알림만 하려면 ssh_guard_block_enabled=false로 합니다. 진단(34개 항목)의 "SSH 무차별 대입 보호"는 이 가드가 동작하며 차단하는 설정일 때도 통과합니다.

한계: 공개 키 실패만 거듭하는 시도는 집계하지 않습니다. 많은 접속 출처에 분산된 무차별 대입은 출처별 횟수로 집계하므로, 하나의 출처가 임계치에 도달하기 전까지는 막지 못합니다. 근본적인 대책은 비밀번호 인증을 끄고 공개 키 인증만 허용하는 것입니다.

5. SSH 잠금 방지 안전 장치

잘못된 방화벽 구성으로 인한 우발적인 잠금은 용납할 수 없는 운영상의 재앙입니다. RoamSwitch Server Edition에는 다계층 보호 기능이 통합되어 있습니다.

6. 컨테이너 보호(Docker/Podman)

Docker의 기본 동작은 표준 호스트 방화벽을 우회하여 컨테이너 포트(예: -p 8080:8080)를 인터넷에 직접 노출하는 사용자 지정 iptables 규칙을 만듭니다.

protect_docker_ports=true(기본값)일 때 RoamSwitch는 Docker의 방화벽 방식에 맞추어 컨테이너로 향하는 트래픽도 ssh_ports와 allowed_ports에 포함된 포트 이외는 외부에서 도달하지 못하도록 합니다. iptables / iptables-nft 방식에서는 DOCKER-USER 체인 맨 앞에 검사 규칙을 삽입합니다(IPv4에 더해 Docker의 IPv6가 활성화되어 있으면 ip6tables에서도). Docker 29 이후의 네이티브 nftables 방식(DOCKER-USER가 존재하지 않음)에서는 전용 inet roamswitch_docker_guard 테이블을 Docker의 체인보다 앞선 forward 훅에 생성합니다(하나의 inet 테이블로 IPv4 / IPv6를 모두 처리합니다). 판정은 DNAT 이전의 원래 목적지 포트(ctorigdstport)로 수행하며 외부 인터페이스로부터의 통신만을 대상으로 하므로, 컨테이너의 외부로 나가는 통신이나 컨테이너 간 통신에는 영향이 없습니다. 의도하지 않은 개발용 데이터베이스 컨테이너 등의 외부 노출을 방지합니다.

이 가드는 멱등성을 가지며 1분마다 자가 복구합니다. dockerd가 RoamSwitch보다 나중에 시작된 경우, Docker의 재시작이나 리로드로 규칙이 덮어써진 경우, 수동 nft flush나 nftables 테이블 소실이 있었던 경우에도, 현재 유효한 가드에는 손대지 않은 채 재설정합니다.

아울러 docker events를 감시하는 실시간 가드가 --privileged로 시작한 컨테이너나 /var/run/docker.sock이 마운트된 컨테이너(컨테이너 탈출 위험)를 시작 직후에 감지하여 알립니다. 또한 컨테이너 exec 가드(container_exec_guard_enabled, 기본값 켜짐)가 컨테이너 내 바이너리의 실행을 YARA로 검사합니다(실행 후 파일을 삭제하는 회피도, 열린 파일 디스크립터에서 내용을 읽기 때문에 검사할 수 있습니다). 이 가드는 이미 가동 중인 컨테이너에도 시작 시에 표시를 하며, 새 컨테이너에도 자동으로 추적합니다. 둘 다 알림만 하며 자동 차단은 하지 않습니다. Docker 29에서도 동작합니다(1.11.1에서 대응). 이벤트 구독이 계속 실패할 때는 재연결 간격을 5초에서 최대 5분까지 늘립니다.

7. 중요 경로 파일 무결성 모니터링(FIM)

150개 이상의 중요 파일(/bin/login, /usr/bin/sudo, /usr/sbin/sshd 등의 중요 바이너리, /etc/shadow, /etc/sudoers 등의 인증 설정, systemd 유닛)을 SHA-256으로 감시합니다.

감시 범위는 개별 파일만이 아닙니다. 중요 디렉터리(/etc/pam.d, /etc/sudoers.d, /etc/ssh/sshd_config.d, /etc/cron.*, /etc/systemd/system, /etc/ld.so.conf.d, /etc/profile.d, /root/.ssh(known_hosts 제외))에서는 새 파일이 나타난 것도 감지하며, 로그인 가능한 모든 계정의 authorized_keys, /etc/ld.so.preload(존재하지 않는 것이 정상이며 출현은 로컬 루트킷의 징후), RoamSwitch 자신의 바이너리도 대상입니다. snapd가 자동 생성하는 임시 유닛(snap-*.mount 등)은 대상에서 제외됩니다. 새 파일 감지는 이 범위를 포함한 베이스라인에서만 유효하므로, 업그레이드 후에는 한 번 sudo roamswitch fim update를 실행하십시오. root가 아닌 사용자로 fim verify를 실행했을 때 읽을 수 없는 파일은 변조가 아니라 「읽기 불가로 건너뜀」으로 취급합니다.

7.1 수동 해시 검증

sudo roamswitch fim verify

7.2 이벤트 기반 탐지(즉시)

감시 대상 파일은 fanotify(FAN_CLASS_NOTIF, 쓰기 완료 이벤트)로 개별 감시되므로, 다음 주기 스캔을 기다리지 않고 변조가 발생한 순간 해시를 재확인합니다. 주기 스캔(기본값 fim_check_interval_secs=300초 간격)은 이벤트를 놓쳤을 경우의 백스톱으로 함께 유지됩니다. 새 파일을 같은 이름 아래에 두는 편집(install, sed -i 등)도 탐지하며, 알림에는 변경한 프로세스와 SSH 로그인을 덧붙입니다(§7.5).

7.3 OS 업데이트 시 자동 기준선 동기화

Debian/Ubuntu에서는 패키지 설치 시 /etc/apt/apt.conf.d/99roamswitch-fim의 DPkg::Post-Invoke 훅이 자동으로 구성됩니다. Fedora/RHEL/Rocky/AlmaLinux/openSUSE에서는 /var/lib/rpm을 감시하는 systemd.path 유닛(roamswitch-fim-update.path)이 동일한 역할을 합니다.

Debian/Ubuntu에서 /etc/apt/apt.conf.d/99roamswitch-fim은 DPkg::Post-Invoke 후크를 연결합니다. sudo apt upgrade를 통한 합법적인 패키지 업데이트는 FIM 기준을 자동으로 새로 고쳐 잘못된 긍정 경고를 제거합니다.

sudo roamswitch fim update

데몬이 처음 시작되어 기준선을 만들 때 패키지 관리자(apt/dpkg, dnf/rpm, zypper, pacman)가 작업 중이면, 끝날 때까지 최대 120초 기다린 뒤에 만듭니다. Apache 등과 같은 apt install로 설치해도 dpkg가 작업 중인 임시 파일(*.dpkg-new 등)이 기준선에 들어가 직후 검증에서 변조로 보이는 일이 없습니다(1.11.18 이후). 120초가 지나도 끝나지 않으면 경고를 기록하고 그대로 만듭니다.

7.4 허니토큰(자격 증명 미끼)

데몬 시작 시, root 권한으로 동작하는 특성을 이용해 /root 하위에 .aws/credentials, .ssh/id_rsa, .docker/config.json 세 가지 미끼 자격 증명 파일(허니토큰)을 멱등하게 배치합니다. 해당 경로에 실제 파일이 이미 존재하는 경우 절대 덮어쓰지 않습니다. 백그라운드 감시 프로세스가 inotify로 이 파일들에 대한 읽기 접근을 감지하면 침입 후 자격 증명 정찰(MITRE ATT&CK T1552: Unsecured Credentials)로 간주하여 Critical 등급의 통지와 인시던트 타임라인 기록을 즉시 수행합니다. 기본적으로 켜져 있으며, 설정 키 honeytokens_enabled(false로 하면 배치와 감시를 멈춥니다. 이미 놓인 미끼는 남습니다)와 honeytokens_home(배치 위치. 기본값 /root)로 바꿀 수 있습니다. 1.10.4까지의 배포 패키지에는 이 기능에 필요한 roamswitch-honeytokens이 포함되어 있지 않아, 패키지로 설치한 환경에서는 동작하지 않았습니다. 1.10.5부터 포함되었고, 서버 에디션에서 systemd 샌드박스가 /root에 대한 쓰기를 막던 문제는 1.10.10에서 고쳐졌습니다. 1.11.1부터 RoamSwitch 자체가 실행하는 docker 명령은 빈 전용 디렉터리(root에서는 /run/roamswitch/docker-config)를 DOCKER_CONFIG로 지정해 시작하므로 미끼 .docker/config.json에는 반응하지 않습니다(그전에는 데몬의 감시나 roamswitch status --server가 미끼를 읽어 잘못 감지하는 일이 있었습니다). 다른 프로그램이 읽으면 지금까지처럼 감지합니다.

허니토큰 중 하나에 실제 접근이 있었던 경우, 공격자가 더 깊이 탐색할 것을 예상하여 문맥상 관련된 새로운 미끼(예: .aws/credentials 도달 후에는 .aws/config, .ssh/id_rsa 도달 후에는 .ssh/config, .docker/config.json 도달 후에는 .kube/config)를 트리거당 최대 2건, 전체 최대 6건까지 추가로 배치합니다. 침해 조사 중 /root 하위에서 이러한 낯선 파일이 늘어나 있어도 이는 예상된 동작입니다.

7.5 침입 경로(누가, 어느 SSH 로그인으로 변경했는가)

파일 변경을 알리는 알림(Critical Path FIM과 락파일 FIM)에, 변경한 프로세스(계정·실행 파일·명령)와 그 프로세스가 속한 SSH 로그인(접속 출처의 IP와 포트·로그인 시각·인증 방식·공개 키 지문)을 덧붙입니다(기본 활성. fim_intrusion_context_enabled). 이벤트를 읽은 그 자리에서 쓴 프로세스의 /proc/<pid>(실행 파일, 사용자, loginuid, sessionid, cgroup, 부모 체인)를 촬영합니다. tee나 sed처럼 순식간에 끝나는 프로세스 때문입니다. 이미 종료되어 얻지 못한 프로세스는 실행 레코더(활성일 때)의 기록으로 보충합니다. 로그인과의 연결은 SSH 가드의 세션 표를 먼저 쓰고, 없으면 logind와 journald의 Accepted 행에서 찾습니다. SSH 가드를 꺼 두어도 동작합니다. 외부로는 아무것도 보내지 않습니다.

신뢰하는 것은 커널이 보고하는 값뿐입니다. 실행 파일은 /proc/<pid>/exe(argv[0]가 아닙니다), 로그인은 loginuid(로그인 시 한 번만 설정되며 sudo와 su를 넘어 이어집니다)와 cgroup의 session-N.scope, 그리고 부모 체인입니다. comm, 명령줄, 환경 변수처럼 프로세스 스스로 바꿀 수 있는 값은 표시만 하고 판정에는 쓰지 않습니다. sshd를 자칭할 뿐인 프로세스는 SSH 로그인으로 보지 않습니다. 알림은 세 가지를 구분해서 씁니다. 찾음(접속 출처·시각·인증 방식·키), "SSH 로그인은 찾지 못했습니다"(조사한 결과 systemd 서비스, 컨테이너, 로컬 로그인 세션, 로그인 세션에 속하지 않는 프로세스 중 하나였을 때), "로그인 경로를 확인하지 못했습니다"(프로세스가 이미 없고 실행 레코더에도 없음, logind가 응답하지 않음, 정기 검사로 찾은 변경이라 프로세스를 알 수 없음 등). 확인하지 못한 것을 로그인이 아니라고 쓰지는 않습니다. 조회에 실패해도 알림은 반드시 나갑니다.

/etc/ssh/sshd_config: modified
  프로세스: root (로그인한 계정: admin) / /usr/bin/tee / 명령: tee /etc/ssh/sshd_config (pid 4121)
  SSH 로그인: admin 계정이 2026-10-17 03:12:08에 203.0.113.25:51544에서 연 세션입니다(인증: publickey, 키 SHA256:AbCdEf0123456789)

/etc/sudoers: modified
  로그인 경로를 확인하지 못했습니다(정기 검사에서 발견한 변경이라 변경한 프로세스는 알 수 없습니다)

install, sed -i, 같은 디렉터리에서의 cp와 mv처럼 새 파일을 같은 이름 아래에 두는 편집도 탐지합니다. 파일 단위 표시는 inode에 붙기 때문에, 예전에는 이런 편집에서 이벤트가 나오지 않았습니다. 이제는 감시 대상 파일이 있는 디렉터리에도 표시를 붙이고, 같은 디렉터리에서 다른 이름의 파일이 닫히면 몇 밀리초 뒤에 이름을 다시 조회합니다. 한계: 다른 디렉터리에서 쓴 파일을 mv로 가져오는 경우는 정기 스캔(fim_check_interval_secs)이 찾을 때까지 알 수 없습니다. 그때는 쓴 프로세스를 알 수 없으며 알림에도 그렇게 적힙니다.

기록은 알림 외에도 인시던트 타임라인(종류 fim)과 /var/lib/roamswitch/fim_intrusions.json(root 전용, 최신 100건)에 남습니다. MCP의 verify_fim에는 추가 필드 recentIntrusionContext로 나옵니다(root로 실행했을 때만). 변조 여부의 판정은 달라지지 않습니다. 한계: 실행 레코더를 끈 상태에서 쓴 프로세스가 극단적으로 짧게 살면 실행 파일을 알 수 없어, 부모 프로세스의 실행 파일과 명령줄을 대신 표시합니다. 지속성 감시(persistence_watch)의 알림에는 프로세스 정보가 없으므로 침입 경로가 붙지 않습니다. 침입 경로가 보여 주는 것은 변경한 프로세스와 로그인까지입니다. 침입의 입구 자체(공격당한 취약점이나 키가 도난된 경위)는 알 수 없습니다. 끄려면 fim_intrusion_context_enabled=false로 합니다.

8. eBPF 런타임 가드 및 Falco 통합

커널 공간 eBPF 이벤트(Falco/Tetragon)에 연결하여 Frag Gap 커널 권한 에스컬레이션, 컨테이너 이스케이프 및 무단 역쉘을 탐지합니다.

8.1 안티 블로트 아키텍처

기존 보안 도구에서 문제가 되는 「syslog의 폭발적 비대화」「디스크 용량 압박」을 막기 위해 RoamSwitch는 Falco와 UNIX 도메인 소켓 /run/roamswitch/events.sock으로 직접 연결합니다. 이 소켓은 root 전용(0600)으로 생성되며 쓸 수 있는 것은 root로 동작하는 Falco뿐입니다(추가 권한 설정은 필요하지 않습니다).

8.2 자율 격리

심각한 공격이 감지되면 RoamSwitch는 즉시 SIGSTOP을 발행하여 문제가 되는 프로세스 PID를 동결하고 nftables Air-Gap 격리를 적용합니다.

8.3 침해 증거 자동 보전(Forensic Evidence Capture)

eBPF 런타임 가드가 프로세스 단위 봉쇄(SIGSTOP에 의한 동결 + 네트워크 차단)를 실행했을 때 자동으로 증거 보전 번들을 생성합니다(§4.1의 기본 정책에서는 Critical 수준 이벤트가 이 경로에 해당합니다. 호스트 전체를 격리하는 Emergency 수준 이벤트에서는 현재 이 번들이 생성되지 않습니다). 번들의 내용은 기본 라우팅 인터페이스에서의 베스트에포트 약 30초간 경계 지정 패킷 캡처(tshark가 설치되지 않은 환경에서는 이 항목만 조용히 건너뛰고 그 사실을 기록합니다)와, 대상 PID를 알고 있는 경우의 /proc/<pid> 스냅샷(status, cmdline, maps, environ(이름에 KEY/TOKEN/SECRET/PASSWORD가 포함된 값은 [REDACTED]로 치환), 열려 있는 파일 디스크립터의 링크 대상 목록, PID 1까지의 조상 프로세스 체인)입니다. 취득한 모든 파일의 SHA-256 해시를 기록한 manifest.json이 함께 포함되어 이후의 변조 검증에 사용할 수 있습니다.

저장 위치는 /var/lib/roamswitch/incident-evidence/<타임스탬프>-<ID>/입니다. 번들은 자동으로 삭제되지 않습니다. 디스크 압박을 방지하려면 보존 기간(기본 30일)을 초과한 오래된 번들을 다음 명령으로 명시적으로 삭제하십시오:

sudo roamswitch-incident-capture prune [--keep-days N]

또한 봉쇄 발동을 기다리지 않고 원하는 시점에 수동으로 스냅샷을 캡처할 수도 있습니다:

sudo roamswitch-incident-capture snapshot [--pid PID] [--reason TEXT] [--json]

8.4 격리 속도의 측정과 경보 경로의 하트비트

eBPF 격리의 정확도와 속도는 센서(Falco나 Tetragon)의 규칙에 좌우됩니다. 실제로 Falco 0.45.0의 표준 규칙(29건)에는 호스트의 리버스 셸을 탐지하는 것이 없습니다. 리버스 셸용 규칙은 컨테이너 전용이고, 일부는 RoamSwitch의 priority: warning 조건에 걸려 버려집니다. RoamSwitch는 규칙의 완성도를 전제로 삼지 않고 세 가지로 보완합니다. 격리에 걸린 시간을 재는 것, 경보 경로가 살아 있는지 주기적으로 확인하는 것, 리버스 셸을 스스로도 탐지하는 것입니다.

격리 지연의 측정: eBPF 인시던트마다 Falco가 탐지한 뒤 데몬이 경보를 읽기까지(detect_to_receive_ms), 읽은 뒤 대응이 끝나기까지(receive_to_action_ms. 격리 호출이 돌아온 뒤에 잽니다. 호스트 Air-Gap은 룰셋을 다 읽어 들일 때까지), 그 합계(detect_to_action_ms)를 기록합니다. 값은 ebpf_incidents.json과 MCP의 get_ebpf_incidents에 추가 필드로 나옵니다. 프로세스를 격리했을 때의 알림에는 "탐지부터 대응까지 N밀리초" 한 줄이 붙습니다. 실측(Ubuntu 24.04 VM, Falco 0.45.0, modern eBPF)은 탐지에서 수신까지 1~2밀리초, 수신에서 프로세스 격리와 SIGKILL까지 41~62밀리초(합계 42~64밀리초), 호스트 Air-Gap은 합계 109밀리초였습니다(목표 150밀리초 이내). 값은 이 VM의 것입니다. Tetragon은 수신 시에 시각을 붙이므로 detect_to_receive_ms가 없습니다.

하트비트(ebpf_heartbeat_interval_secs. 기본 600초, 0이면 비활성, 활성화할 때는 60~86400): 패키지에는 RoamSwitch 자체의 Falco 규칙 96-roamswitch-heartbeat.yaml이 들어 있습니다. 데몬은 주기적으로 root만 쓸 수 있는 일회용 경로(/run/roamswitch/heartbeat/ 아래의 난수)를 열고, 그 규칙의 경보가 Falco의 출력으로 돌아올 때까지의 시간을 잽니다. 한 번의 왕복으로 규칙이 로드되어 있고, Falco가 동작하며, 출력 경로가 살아 있음을 한꺼번에 확인할 수 있습니다. 돌아오지 않는 확인이 2회 연속될 때(첫 번째 1분 뒤에 재확인)만 "규칙이 로드되지 않았거나 출력이 도달하지 않습니다"라는 경고를 24시간에 한 번까지 알립니다. 하트비트 행은 격리·알림·허용 목록·인시던트 기록 어디에도 넘기지 않고 버립니다. 외부로는 아무것도 보내지 않습니다. 실측 왕복은 2~6밀리초였고, 규칙을 빼자 약 164초 뒤에 경고가 나왔으며, 되돌리자 약 75초 만에 정상으로 돌아왔습니다.

상태는 /var/lib/roamswitch/ebpf_heartbeat.json에 남고, MCP get_ebpf_incidents의 heartbeat와 진단 K-04(eBPF 런타임 에이전트. 진단 항목 수는 34 그대로입니다)가 읽습니다. Falco가 동작하고 있어도 하트비트가 돌아오지 않으면 K-04는 불합격입니다. 한계: 확인하는 것은 규칙 하나(open 계열 시스템 호출)의 왕복이라, 다른 규칙이 개별적으로 망가진 것까지는 알 수 없습니다. 확인 간격(기본 10분) 사이에 망가진 것은 다음 확인까지 알 수 없습니다. Tetragon에서는 수행하지 않습니다. Falco가 동작하지 않거나 규칙 파일이 없는 호스트에서는 아무것도 확인하지 않고 아무것도 경고하지 않습니다(정지는 기존 알림이 담당합니다). Falco는 확인할 때마다 경고를 한 줄 syslog에 씁니다(기본이면 하루 144줄).

8.5 리버스 셸 탐지(RoamSwitch 자체)

Falco의 규칙에 의존하지 않고, RoamSwitch가 직접 2초마다 /proc을 읽어 표준 입력과 표준 출력이 이 호스트에서 외부 주소로 맺은 하나의 TCP 연결에 이어진 셸·인터프리터(sh·bash·python·perl·ruby·php·node 등)를 찾습니다. bash -i >& /dev/tcp/…, python, perl, nc -e, socat exec:가 대상입니다. 2회 연속으로 보인 것만 프로세스당 한 번 보고합니다. 기본은 외부 공개 주소로 향하는 것만이며, 사설 주소(RFC 1918·CGNAT·링크 로컬)는 reverse_shell_watch_include_private=true일 때만 셉니다(ssh의 ProxyCommand, 컨테이너, 로컬 개발에 정상적인 것이 많기 때문입니다). 루프백은 보지 않습니다. 밖에서 받아들인 연결(inetd나 socat TCP-LISTEN이 accept한 것)도 대상이 아닙니다. 실측(Ubuntu 24.04 VM)에서는 bash의 /dev/tcp, python, perl, nc -e, socat exec:를 연결 후 약 2.4초에 탐지했습니다. ssh, ProxyCommand가 붙은 ssh, git over ssh, nc 클라이언트는 탐지하지 않습니다.

탐지는 RoamSwitch Reverse Shell이라는 Critical eBPF 이벤트로서 Falco 경보와 같은 경로(허용 목록, 알림 쿨다운, guard.yaml의 심각도별 동작, 인시던트 기록)에 들어갑니다. 기본에서는 프로세스를 격리하고 종료(kill)합니다. root가 소유한 리버스 셸은 프로세스 단위 격리에 실패하므로, 기존 Critical 방침대로 호스트 전체의 Air-Gap까지 진행됩니다. 정상 프로그램이 잘못 탐지될까 걱정될 때의 탈출구는 둘입니다. reverse_shell_watch_notify_only=true(Warning으로 알림만 하고 격리도 kill도 하지 않음. 기본 false)와 허용 목록 등록 sudo roamswitch server allowlist add "RoamSwitch Reverse Shell" <프로세스 이름>입니다. 설정 키는 reverse_shell_watch_enabled(기본 true), reverse_shell_watch_include_private(기본 false), reverse_shell_watch_notify_only(기본 false)의 세 가지입니다.

한계: 이름(프로세스 이름과 실행 파일 이름)으로 좁히기 때문에, 셸이나 인터프리터가 아닌 이름으로 표준 입출력을 소켓에 연결한 자작 바이너리는 찾지 못합니다. 루프백을 경유하는 연결(ssh -R의 되돌림 등)과 2초보다 짧은 연결은 보지 않습니다. 호스트의 프로세스 공간에서 보이지 않는 프로세스도 볼 수 없습니다. 이 탐지는 Server Edition에만 있는 기능으로, Mac과 Linux 클라이언트 버전에는 없습니다.

9. 긴급알림(텔레그램/LINE/웹훅)

포트 이상, FIM 변조 또는 eBPF 억제 이벤트가 발생할 경우 외부 알림 채널에 즉각적인 경고를 전달합니다.

[notifications]
language = ja
# Telegram: 봇 토큰과 Chat ID
telegram_bot_token = "123456789:ABCdefGHIjklMNOpqrSTUvwxYZ"
telegram_chat_id = "-1001234567890"

# LINE: Messaging API 채널 액세스 토큰과 사용자 ID
line_channel_access_token = "YOUR_LINE_ACCESS_TOKEN"
line_user_id = "U1234567890abcdef1234567890abcdef"

# 범용 Webhook(Slack, Discord, Teams, 자체 모니터링 시스템)
webhook_url = "https://hooks.slack.com/services/T00/B00/XXXXX"

이러한 외부 통지 채널을 설정하지 않아도, 발송된 알림은 최근 1주일간 로컬에 보관됩니다. roamswitch notifications로 확인할 수 있습니다.

내부 주소로의 Webhook 전송 거부(옵트인): webhook_block_private_addresses=true(기본 false = 기존 동작)로 설정하면, 알림 Webhook의 대상이 루프백, 사설(10/8, 172.16/12, 192.168/16), 링크 로컬(169.254/16, 클라우드 메타데이터 169.254.169.254 포함), ULA(fc00::/7), CGNAT(100.64/10) 중 하나이거나(호스트 이름이면 해석 결과 중 하나라도 해당) 전송을 거부하고, 이유를 로그와 roamswitch notify-test 오류에 표시합니다. 2130706433, 0x7f.1 같은 다른 표기와 IPv4-mapped IPv6도 판정하며, 검증한 주소를 curl에 고정해 DNS rebinding도 막습니다. 사내 SIEM 같은 정당한 내부 대상은 webhook_allow_addresses=10.20.0.0/16,fd00::/8(CIDR 쉼표 구분)로 허용합니다. 이벤트 전달 Webhook에는 같은 쌍인 forward_webhook_block_private_addresses / forward_webhook_allow_addresses가 있습니다. 옵션이 false여도 대상이 클라우드 메타데이터 주소이면 경고 로그를 남깁니다(전송은 막지 않음). HTTP 프록시 환경 변수가 설정되어 있으면 해석과 연결이 프록시에서 이뤄지므로 이 검사 대상이 아닙니다.

10. AI 에이전트/MCP 통합(모델 컨텍스트 프로토콜)

RoamSwitch Server Edition에는 roamswitch-mcp이 포함되어 있어 AI 에이전트(Claude, Gemini, Cursor)가 인프라 상태 및 상태를 모니터링할 수 있습니다.

읽기 전용 안전 보장
즉각적인 주입 공격으로 인해 인프라가 손상되는 것을 방지하기 위해 MCP 인터페이스는 엄격하게 읽기 전용입니다. AI 에이전트는 MCP를 통해 포트를 열거나 격리를 제거하거나 보안 구성을 변경할 수 없습니다. 교정을 위해서는 인증된 로컬 TTY 셸 액세스가 필요합니다.

구성 예(`claude_desktop_config.json`)

{
  "mcpServers": {
    "roamswitch": {
      "command": "/usr/bin/roamswitch-mcp",
      "args": []
    }
  }
}

10.1 조사 에이전트 연계(investigation, 1차 자동 조사의 AI 에이전트 위임)

§10의 roamswitch-mcp가 「사람의 AI 클라이언트로부터의 조회」를 위한 것이라면, investigation 연계는 데몬 자신이 탐지 이벤트를 계기로 외부 AI 에이전트 CLI를 자율적으로 기동하는 별도의 메커니즘입니다. 대상은 통지 전용으로 판정된(자동 격리·정지에 이르지 않은) eBPF 중대 이벤트, 그리고 크리티컬 패스 FIM·의존성 락파일 변조 탐지입니다. 활성화하면 데몬은 먼저 로컬 휴리스틱에 의한 1차 판정 리포트(오탐 가능성·근거·다음 조사 단계)를 생성한 뒤, 이를 첨부하여 설정된 AI 에이전트 CLI를 서브프로세스로 기동해 추가 조사와 요약을 위임합니다. 기본값은 비활성화입니다.

# /etc/roamswitch/guard.yaml에 추가(server.conf와는 별도 파일)
investigation:
  enabled: true
  command: "claude"                 # "claude" | "agy" | "codex" | "opencode" | 임의의 사용자 지정 명령
  args: ["-p"]                      # 프롬프트는 자동으로 마지막 인수로 추가됩니다
  timeout_secs: 180

같은 내용을 sudo roamswitch server setup의 대화형 마법사(§3.2 마지막 단계)에서도 설정할 수 있습니다. 도구는 Claude Code(claude -p) / agy(agy -p --dangerously-skip-permissions) / Codex CLI(codex exec) / OpenCode(opencode run) 프리셋 또는 임의의 사용자 지정 명령 중에서 선택합니다. 재실행 시 저장된 설정에서 해당 프리셋이 자동 선택됩니다.

프롬프트 인젝션에 대한 주의
investigation 연동은 탐지 이벤트의 상세 내용(공격자가 일부 제어할 수 있는 프로세스 이름·파일 경로 등의 문자열 포함)을 길이를 자른 JSON 문자열로 AI 에이전트의 프롬프트에 전달합니다. JSON 문자열로 만들어 두면 줄바꿈이나 따옴표로 구분선이나 가짜 지시 영역을 만들 수 없습니다. 다만 문자열의 내용이 지시로 읽힐 가능성까지 없어지지는 않으므로, 에이전트는 root로 실행할 수 없습니다. 일반 사용자를 run_as에 지정하지 않은 설정은 마법사(CLI와 TUI)가 저장하지 않으며 데몬도 시작하지 않습니다. 특히 --dangerously-skip-permissions 등 권한 확인을 건너뛰는 모드로 도구를 실행하는 설정은 악의적인 입력으로 에이전트가 의도하지 않은 동작을 할 위험을 높입니다. 마법사는 agy를 선택하면 이 주의를 표시합니다. 신뢰할 수 없는 로그 입력 경로가 있는 환경에서는 권한 확인을 건너뛰지 않는 도구 설정을 권장합니다.

네트워크가 차단된 동안(Air-Gap 발동 시 등, isolate_host_all에 해당하는 상태)에는 investigation 연계가 호출되지 않습니다.

11. CLI 명령 치트 시트

명령 권한 설명
sudo roamswitch status --server root 34개 항목의 서버 보안 진단 점수와 상태를 표시(--json으로 동일한 내용을 JSON으로 출력)
sudo roamswitch ports root 열린 청취 포트 및 바인딩된 프로세스를 감사합니다.
sudo roamswitch fim verify root 중요한 경로 시스템 파일의 SHA-256 무결성 확인
roamswitch notifications 사용자 최근 1주일의 알림 이력(전송된 모든 경고)을 표시(--json / --export 파일도 지원)
sudo roamswitch server setup root 대화형 초기 구성 마법사 실행
sudo roamswitch fim update root FIM SHA-256 기준 데이터베이스 새로 고침
sudo roamswitch server config set <key> <value> root 설정 키를 하나 변경하고 저장합니다(목록은 §18 참조)
sudo roamswitch emergency-restore 루트 eBPF·방화벽 긴급 격리를 모두 해제하고 초기 기준선으로 복원
roamswitch port-scan 일반 착신 포트 스캔 탐지 가드(§4.3)의 탐지 이력(송신 IP, 자동 차단 여부·지속 시간)을 표시
roamswitch sensor [key|list|pair|unpair|request-audit|results] 일반/root RoamSwitch Sensor와의 페어링 코드 방식 상호 신뢰. pair --addr <IP> --code <코드> --fingerprint <SHA-256>로 페어링(핑거프린트는 Sensor 화면에 표시되는 값), repin으로 Sensor의 인증서 재생성 후 핀을 갱신, request-audit로 능동적 취약점 감사를 요청(결과는 results로 취득). sensor_pairing_enabled: true(기본값 OFF)가 필요합니다. 자세한 내용은 Sensor 운영 매뉴얼을 참조하십시오
roamswitch server tui 일반/root 모든 기능을 조작할 수 있는 TUI를 실행(일반 사용자는 읽기 전용, root로 모든 작업. §17)
sudo roamswitch server ack root 현재의 긴급 격리를 확인 응답(FULL 격리 중에는 엄격한 격리를 유지하고, 축소 모드 중에는 해제. §4.1)
roamswitch server ebpf 사용자 eBPF 런타임 가드의 현재 격리 상태와 탐지된 인시던트 이력을 표시
roamswitch server bans [--json] 일반/root SSH 무차별 대입 가드(§4.5)의 상태와 현재 차단 중인 접속 출처(남은 시간 포함)를 표시. --json 지원. 차단 목록은 root만 읽을 수 있습니다
sudo roamswitch server unban <IP | IPv6 /64 | --all> root SSH 무차별 대입 가드의 차단을 해제하고 그 접속 출처의 실패 횟수도 지웁니다(IPv6는 /64로 지정, --all은 전체)
sudo roamswitch server allowlist [list|add <rule> <process> [--exe <path>]|remove <rule> <process>] root 오탐의 영구 허용 목록을 관리. --exe를 붙이면 실행 파일의 경로에 묶이며 프로세스 이름 위장으로는 회피할 수 없습니다(이름만 등록하는 것은 위장이 가능하며 경고가 표시됩니다)
roamswitch frozen [list|resume <PID>|resume all] 일반/root 가드가 SIGSTOP으로 동결 중인 프로세스의 목록과, 오탐이었을 경우의 해제(SIGCONT). resume all로 전체 해제
sudo roamswitch events search|tree|export|verify|status root 실행 레코더 검색(--since 2h --pid --exe --user --parent --text --json), 프로세스 트리, 내보내기, 해시 체인 검증, 상태 표시(§15)
sudo roamswitch forward status|test|queue root 이벤트 전달의 상태 확인·테스트 이벤트 전송·재전송 큐 표시(§16)
roamswitch timeline / notifications [--json] [--export FILE] 사용자 통합 인시던트 타임라인(실험적) / 알림 이력을 표시. --json으로 JSON 배열, --export 파일로 JSON Lines를 출력
roamswitch audit-agent-setup [--json] 사용자 AI 에이전트에게 읽히는 지시 파일·스킬·OpenClaw 설정의 위험한 내용을 읽기 전용으로 점검(홈 폴더만, 통신하지 않음, --json은 JSON 출력). Server TUI의 '도구'에서도 실행 가능

12. 문제 해결 및 FAQ

Q1. RoamSwitch가 예기치 않게 SSH 세션 연결을 끊습니까?

없습니다. RoamSwitch는 평상시 알려진 ESTABLISHED/RELATED 연결 상태 및 ssh_ports(기본값 22)를 무조건 허용합니다. 또한 중대한 공격으로 인한 Air-Gap 긴급 격리가 발동된 경우에도 preserve_ssh_on_isolation=true(기본값)에 의해 유지보수 SSH 포트와 관리 IP의 관리 세션은 유지됩니다(그 외의 확립된 연결은 차단됩니다). 1.10.4 이하의 서버 에디션에는 결함이 있었습니다. 격리 규칙이 SSH 응답을 차단하여 격리 중에는 운영자의 SSH가 통하지 않았습니다. 1.10.5에서 수정되었습니다.

Q2. 실수로 잠긴 경우 어떻게 다시 액세스할 수 있나요?

클라우드 제공업체의 웹 관리 콘솔(VNC/시리얼 콘솔)로 로그인한 뒤 sudo roamswitch emergency-restore로 긴급 격리를 해제하거나, sudo systemctl stop roamswitch-server로 서비스를 중지하세요.

Q3. 새로 추가된 웹 서비스(예: Nginx)에 대한 포트를 어떻게 노출합니까?

sudo roamswitch server config set allowed_ports 80,443,3000을 실행하거나(또는 /etc/roamswitch/server.conf를 직접 편집) sudo roamswitch server restart를 실행하세요.

13. File Scan Guard(메일 릴레이·공유 폴더·업로드 대상 보호)

메일 릴레이, Samba/NFS 공유, 파일 업로드 대상 등 다른 사람에게 전달할 파일을 저장하는 디렉터리가 있는 경우에만 관련 있는 선택적 기능입니다. 내장 YARA 엔진은 외부 의존성 없이 clamav_scan_dirs를 상시 스캔하며, clamav_enabled=true로 설정하면 ClamAV의 2차 검사도 추가됩니다. 확인된 위협은 자동으로 /var/lib/roamswitch/quarantine에 격리되고 운영자에게 알림이 전송됩니다.

# --- File Scan Guard(ClamAV, 옵트인) ---
# 내장 YARA 엔진은 항상 clamav_scan_dirs를 무조건 스캔합니다.
clamav_enabled=false             # true로 설정하면 clamdscan을 통한 추가 검사 활성화
clamav_scan_dirs=                # 스캔 대상 절대 경로(쉼표로 구분)
clamav_scan_interval_secs=86400  # 스캔 간격(초, 최소 3600)
clamav_freshclam_interval_secs=7200  # 시그니처 DB 자동 업데이트 간격(초, 최소 1800)

대화형 마법사 sudo roamswitch server setup에서도 설정할 수 있으며, get_file_scan_guard_status MCP 도구로 설정 및 격리 상태를 확인할 수 있습니다.

14. Log Audit(로그 패턴 이상 탐지 및 알림)

journalctl을 주기적으로 스캔하여 로그 메시지를 템플릿화하고, 신규 패턴(이전에 없던 로그 형태)과 빈도 이상(통계적 급증, Z-score > 3.0)을 탐지합니다. 추가 데몬이나 외부 의존성이 필요 없는 경량 방식이므로 기본적으로 활성화되어 있습니다. 탐지 시 File Scan Guard 및 FIM과 동일한 알림 채널(Telegram / LINE / Webhook)로 통지됩니다. 로그에 포함될 수 있는 API 키나 토큰은 알림 본문에 포함되기 전에 자동으로 마스킹됩니다.

# --- Log Audit (기본적으로 활성화) ---
log_audit_enabled=true           # false로 비활성화
log_audit_interval_secs=1800     # 스캔 간격(초, 최소 300)

대화형 마법사 sudo roamswitch server setup에서도 설정할 수 있으며, roamswitch audit-logs CLI 명령 또는 audit_security_logs MCP 도구로 최신 탐지 결과를 확인할 수 있습니다.

액세스 로그 진단 도구 AccessScope(별도 도구): Server Edition의 deb / rpm에는 웹 서버와 SSH의 액세스 로그를 진단하는 별도 도구의 명령 accessscope가 동봉됩니다(화면 앱은 들어 있지 않습니다). 읽기 전용이며, 분석에는 LLM도 네트워크도 사용하지 않습니다. LLM에 의한 분석은 선택 사항이며 기본값은 꺼짐입니다(켜면 root로도 실행할 수 있지만 LLM이 root 권한으로 동작하므로 권한 없는 사용자로 실행하기를 권합니다). accessscope scan --auto로 이 호스트의 로그를 진단하고, accessscope discover로 로그의 위치를 목록으로 보여 줍니다. 자세한 내용은 AccessScope 페이지를 참조하세요. 같은 Server Edition의 deb / rpm에는 또 다른 별도 도구인 PersonalSOC(이 호스트의 로그와 방어 상태 확인)와 PathScope(권한 상승 경로 분석)의 명령 personalsoc, pathscope도 포함됩니다(화면 앱은 들어 있지 않습니다). 두 명령 자체는 네트워크 통신을 하지 않습니다. PersonalSOC의 LLM 분석은 --llm을 지정했을 때만 동작합니다.

15. 실행 레코더 & 상관관계 규칙(로컬, 알림 전용)

프로세스의 실행(exec)을 netlink의 proc connector로 기록합니다(실행 파일·인수·부모 프로세스 계보·UID·cwd·컨테이너 ID·SHA-256). 기본값에서 켜져 있으며, 기록은 이 호스트의 root 전용 디렉터리 /var/lib/roamswitch/exec_log에 해시 체인이 포함된 세그먼트형 JSON Lines(0600, 기본값 200 MB / 14일)로 저장되고 외부로는 전송되지 않습니다. 변조를 감지할 수 있는(events verify) 것이며 변조를 방지하는 것은 아닙니다. proc connector를 열 수 없는 경우에는 저빈도 /proc 스캔으로 축소됩니다. 기록 처리는 fanotify의 허가 이벤트 루프와는 별도 스레드에서 동작하며, 자신의 CPU 사용률을 예산(기본값 3%) 이내로 억제합니다.

로컬에서 검색·내보내기를 합니다(root 전용 로그이므로 보통 sudo가 필요합니다).

sudo roamswitch events status
sudo roamswitch events search --since 2h --exe curl
sudo roamswitch events tree 4242
sudo roamswitch events export --since 1d > exec-events.jsonl
sudo roamswitch events verify

상관관계 규칙 EXR-001~007(알림 전용·MITRE ATT&CK 기법 ID 포함): EXR-001 웹 서비스에서 시작된 셸 / EXR-002 /tmp·삭제됨·memfd에서의 실행 / EXR-003 다운로드 직후의 실행 / EXR-004 base64·eval을 사용하는 인터프리터 원라이너 / EXR-005 setuid에서 유래하지 않은 uid 0 획득 / EXR-006 cron·systemd가 직전에 배치한 바이너리의 실행 / EXR-007 ld.so.preload 변경 후의 실행. 각 규칙은 조용한 기본값이며 단독으로는 격리하지 않습니다. exec_rules_disabled(예 EXR-003,EXR-004)로 개별적으로 비활성화할 수 있고, exec_rule_service_users로 EXR-001의 대상 사용자를 조정할 수 있습니다.

exec_rules_isolation_optin(기본값 false)을 명시적으로 true로 한 경우에 한해, 심각도가 높은 규칙이 고신뢰도 격리 트리거로 취급됩니다(§4.1). 기본값에서는 규칙은 알리기만 합니다. 읽기 전용 MCP 도구 search_exec_events / get_process_tree에서도 참조할 수 있습니다.

16. 이벤트 전달(SIEM / syslog / 웹훅, 옵트인)

탐지 이벤트를 운영자가 지정한 자사 조직의 대상으로 전달할 수 있습니다. 기본값은 꺼짐이며, forward_enabled=true로 설정하고 대상을 하나 이상 설정할 때까지 아무것도 전송되지 않고, 벤더의 대상은 내장되어 있지 않습니다. 전달 대상은 인시던트 타임라인·알림 이력·eBPF 가드·격리의 상태 전환·FIM·카나리아·exec 규칙의 경고이며, exec의 원시 로그 자체는 전달하지 않습니다. 대상은 다음 3종류입니다.

전송하지 못한 이벤트(TCP / TLS / 웹훅)는 디스크의 재전송 큐(0600, forward_queue_max_mb 기본값 20과 forward_queue_max_age_hours 기본값 72로 상한)에 쌓이며, 재시작 후에도 지수 백오프로 재전송됩니다. forward_min_severity(기본값 low)로 전송할 심각도의 하한을 설정할 수 있습니다. 핫 패스를 멈추지 않도록 전송은 전용 스레드에서 수행하며, 큐가 넘치는 경우에는 가장 오래된 것부터 폐기합니다.

상태 확인·연결 테스트·재전송 큐 확인은 다음 명령으로 수행합니다(설정을 변경했다면 서비스를 재시작하십시오).

# /etc/roamswitch/server.conf
forward_enabled=true
forward_syslog_url=tls://siem.example.org:6514
forward_syslog_format=rfc5424
forward_tls_ca_file=/etc/ssl/certs/corp-ca.pem
forward_min_severity=medium

sudo roamswitch server restart
sudo roamswitch forward status
sudo roamswitch forward test
sudo roamswitch forward queue

17. Server TUI(모든 기능을 조작할 수 있는 터미널 UI)

roamswitch-server-tui(roamswitch server tui로도 실행)는 CLI 동사와 2개의 설정 파일(server.conf와 guard.yaml)에 흩어져 있는 Server Edition의 모든 기능을 SSH 너머의 키보드 조작만으로 다룰 수 있는 TUI입니다(80x24에서 동작, 10개 언어). CLI 동사나 설정 키가 늘었는데 TUI에 대응하는 것이 없는 상태는 개발 시 테스트에서 실패하도록 되어 있습니다.

실행:

sudo roamswitch-server-tui    # full control
roamswitch-server-tui         # read-only
roamswitch server tui         # same as roamswitch-server-tui

18. 설정 키 목록(server.conf / guard.yaml)

roamswitch-server는 설정의 다시 읽기(reload)를 지원하지 않습니다. 모든 키의 반영에는 서비스 재시작이 필요합니다(sudo roamswitch server restart). server.conf의 각 키는 roamswitch server config set <키> <값> 또는 §17의 편집기로 변경할 수 있습니다. guard.yaml은 편집기나 수동 편집으로 변경합니다.

18.1 server.conf(109개 키)

키 형식 / 가능한 값 기본값 재시작
languagesystem | ja | en | zh-hans | zh-hant | ko | de | fr | es | it | ptja필요
ssh_portsport list22필요
allowed_portsport list80,443필요
whitelist_ipslist—필요
preserve_ssh_on_isolationbooltrue필요
telegram_enabledboolfalse필요
telegram_bot_tokenstring—필요
telegram_chat_idstring—필요
line_enabledboolfalse필요
line_channel_access_tokenstring—필요
line_user_idstring—필요
webhook_enabledboolfalse필요
webhook_urlURL—필요
webhook_block_private_addressesboolfalse필요
webhook_allow_addressesCIDR list—필요
action_on_criticalisolate | freeze | alert_onlyisolate필요
ebpf_socket_pathabs path/run/roamswitch/events.sock필요
ebpf_heartbeat_interval_secs0 (off) | int 60–86400600필요
isolation_hard_cap_secsint ≥ 03600필요
fim_check_interval_secsint ≥ 10300필요
fim_intrusion_context_enabledbooltrue필요
protect_docker_portsbooltrue필요
dns_sinkhole_enabledboolfalse필요
dns_sinkhole_providerquad9 | cloudflare | adguard | cleanbrowsingquad9필요
egress_ip_blocklist_enabledbooltrue필요
metadata_watch_enabledbooltrue필요
metadata_watch_allow_userslist—필요
cve_kernel_map_updates_enabledbooltrue필요
clamav_enabledboolfalse필요
clamav_scan_dirsabs path list—필요
clamav_scan_interval_secsint ≥ 360086400필요
clamav_freshclam_interval_secsint ≥ 18007200필요
lockfile_watch_dirsabs path list—필요
npm_audit_signatures_enabledboolfalse필요
log_audit_enabledbooltrue필요
log_audit_interval_secsint ≥ 3001800필요
resource_guard_enabledbooltrue필요
resource_guard_rss_sample_interval_secsint ≥ 1060필요
resource_guard_rss_window_secsint ≥ 3001800필요
resource_guard_rss_min_samplesint ≥ 310필요
resource_guard_rss_min_growth_pctint ≥ 1050필요
resource_guard_rss_recovery_drop_pctint ≥ 110필요
resource_guard_rss_min_kbint ≥ 020000필요
resource_guard_crash_loop_window_secsint ≥ 60300필요
resource_guard_crash_loop_min_restartsint ≥ 23필요
resource_guard_correlation_window_secsint ≥ 60600필요
resource_guard_zombie_min_countint ≥ 020필요
resource_guard_load_min_normalizedfloat ≥ 0.00.5필요
resource_guard_load_enabledbooltrue필요
resource_guard_cpu_enabledbooltrue필요
resource_guard_cpu_sample_interval_secsint ≥ 060필요
resource_guard_cpu_window_secsint ≥ 0600필요
resource_guard_cpu_min_samplesint ≥ 08필요
resource_guard_cpu_pct_thresholdfloat 0.0–1000.080.0필요
container_exec_guard_enabledbooltrue필요
port_scan_detect_enabledbooltrue필요
port_scan_detect_min_distinct_portsint ≥ 015필요
port_scan_detect_window_secsint ≥ 0300필요
port_scan_block_enabledbooltrue필요
port_scan_block_duration_secsint ≥ 0600필요
ssh_guard_enabledbooltrue필요
ssh_guard_block_enabledbooltrue필요
ssh_guard_max_failuresint 2–10006필요
ssh_guard_window_secsint 10–86400600필요
ssh_guard_ban_secsint 60–25920003600필요
ssh_guard_ban_max_secsint 60–31536000604800필요
ssh_guard_allowIP/CIDR list—필요
ssh_guard_log_sourceauto | journal | abs pathauto필요
ssh_guard_yield_to_fail2banbooltrue필요
ssh_guard_trust_key_daysint 0–365030필요
dns_tunnel_detect_enabledbooltrue필요
reverse_shell_watch_enabledbooltrue필요
reverse_shell_watch_include_privateboolfalse필요
reverse_shell_watch_notify_onlyboolfalse필요
sensor_pairing_enabledboolfalse필요
sensor_colocatedboolfalse필요
forward_enabledboolfalse필요
forward_syslog_urlURL—필요
forward_syslog_formatrfc5424 | cefrfc5424필요
forward_syslog_framingoctet | lfoctet필요
forward_tls_ca_fileabs path—필요
forward_file_pathabs path—필요
forward_file_max_mbint 1–1000050필요
forward_file_keepint 1–1005필요
forward_webhook_urlURL—필요
forward_webhook_secretstring—필요
forward_webhook_secret_fileabs path—필요
forward_webhook_block_private_addressesboolfalse필요
forward_webhook_allow_addressesCIDR list—필요
forward_min_severityinfo | low | medium | high | criticallow필요
forward_queue_max_mbint 1–1000020필요
forward_queue_max_age_hoursint 1–876072필요
exec_recorder_enabledbooltrue필요
exec_recorder_retention_mbint 1–100000200필요
exec_recorder_retention_daysint 1–365014필요
exec_recorder_hash_chainbooltrue필요
exec_recorder_hash_exebooltrue필요
exec_recorder_cpu_budget_pctint 1–503필요
exec_rules_enabledbooltrue필요
exec_rules_disabledstring—필요
exec_rule_service_usersstring—필요
exec_rule_window_secsint 5–3600120필요
exec_rules_isolation_optinboolfalse필요
honeytokens_enabledbooltrue필요
honeytokens_homeabs path/root필요
harden_userns_enabledbooltrue필요
dns_tunnel_entropy_allowlistlist—필요
fleet_forwarding_allowed_hostslist—필요
fleet_forwarding_allow_plaintextboolfalse필요

18.2 guard.yaml(37개 키)

키 형식 / 가능한 값 기본값 재시작
sensor.typefalco | tetragonfalco필요
sensor.transportstringunix_socket필요
sensor.socket_pathabs path/run/roamswitch/events.sock필요
sensor.reconnect_interval_msint 100–36000001000필요
policies.on_warning.actionlog_and_notify | isolate_process | isolate_host_alllog_and_notify필요
policies.on_warning.server_actionlog_and_notify | isolate_process | isolate_host_all—필요
policies.on_warning.kill_processboolfalse필요
policies.on_warning.freeze_processboolfalse필요
policies.on_warning.fallback_to_host_isolation_after_retriesint 0–1000—필요
policies.on_error.actionlog_and_notify | isolate_process | isolate_host_allisolate_process필요
policies.on_error.server_actionlog_and_notify | isolate_process | isolate_host_all—필요
policies.on_error.kill_processboolfalse필요
policies.on_error.freeze_processboolfalse필요
policies.on_error.fallback_to_host_isolation_after_retriesint 0–1000—필요
policies.on_critical.actionlog_and_notify | isolate_process | isolate_host_allisolate_process필요
policies.on_critical.server_actionlog_and_notify | isolate_process | isolate_host_all—필요
policies.on_critical.kill_processbooltrue필요
policies.on_critical.freeze_processboolfalse필요
policies.on_critical.fallback_to_host_isolation_after_retriesint 0–10003필요
policies.on_emergency.actionlog_and_notify | isolate_process | isolate_host_allisolate_host_all필요
policies.on_emergency.server_actionlog_and_notify | isolate_process | isolate_host_all—필요
policies.on_emergency.kill_processbooltrue필요
policies.on_emergency.freeze_processboolfalse필요
policies.on_emergency.fallback_to_host_isolation_after_retriesint 0–1000—필요
server_safeguards.allow_established_connectionsbooltrue필요
server_safeguards.maintenance_ssh_portsport list22필요
server_safeguards.whitelist_cidrslist—필요
server_safeguards.whitelist_dns_serverslist—필요
server_safeguards.safety_timer_secsint ≥ 0300필요
adaptive_security.enabledbooltrue필요
adaptive_security.untrusted_network_actions.elevate_falco_profilestring—필요
adaptive_security.untrusted_network_actions.auto_enable_userns_restrictionboolfalse필요
investigation.enabledboolfalse필요
investigation.commandabs path—필요
investigation.argslist—필요
investigation.run_asstring—필요
investigation.timeout_secsint 1–86400180필요

19. 웹셸·삽입 코드 탐지 (Web Rules)

웹 서버에서 동작하는 PHP·JavaScript·JSP·ASP 웹셸과 삽입 코드, 이미지로 위장한 PHP, .htaccess 악용을 찾는 기능입니다(Linux 1.11.23부터). 설정은 필요 없습니다. Apache·nginx·Caddy 설정에서 문서 루트를 자동으로 찾아 그곳에 파일이 쓰이고 닫힌 직후 검사합니다(알림 전용 fanotify를 사용하므로 다른 프로세스를 멈추게 하지 않습니다). 놓친 이벤트와 시작 전부터 있던 파일을 위해 6시간마다 정기 스캔도 합니다. ClamAV는 필요 없습니다.

동작은 /etc/roamswitch/web_rules.conf(root 소유, 권한 0644. 다른 사용자가 쓸 수 있는 파일은 무시됩니다)로 정합니다. 바꾼 뒤에는 sudo roamswitch server restart로 반영합니다. 템플릿은 roamswitch web-rules example conf로 출력할 수 있습니다.

키의미(기본값)
quarantine자동 격리 수준. off는 격리 안 함, critical은 critical만, high는 critical과 high(기본값: critical). critical 수준에서 high를 발견하면 알림과 훅만 실행하고 파일에는 손대지 않습니다.
account_action탐지된 파일 소유자의 로그인 셸을 해제할지 여부. off / revoke_shell(기본값: off). 격리된 critical·high만 대상입니다. root, account_min_uid 미만, 로그인 중인 사용자, 데몬 자신에게는 아무것도 하지 않습니다. 원래 셸은 기록되며 sudo roamswitch web-rules unlock-account <사용자>로 되돌릴 수 있습니다.
account_min_uidaccount_min_uid: 계정 조치 대상이 되는 최소 UID(기본값 1000, 100 미만으로는 낮출 수 없음).
scan_dirs, auto_docroots, watch, scan_interval_secsscan_dirs(쉼표로 구분한 절대 경로), auto_docroots(기본값 true, false면 웹 서버 설정에서의 자동 탐지를 중단), watch(기본값 true, false면 쓰기 감시를 끄고 정기 스캔만 유지), scan_interval_secs(기본값 21600, 최소 3600).
# /etc/roamswitch/web_rules.conf
quarantine=critical
account_action=off
account_min_uid=1000
#scan_dirs=/srv/sites/a,/srv/sites/b
auto_docroots=true
watch=true
scan_interval_secs=21600

탐지하면 알림(Telegram·LINE·Webhook)이 옵니다. 알림에는 규칙 이름과, 격리했는지 그대로 두었는지가 적힙니다. 아무것도 옮기지 않고 확인만 하려면 sudo roamswitch web-scan <폴더>(확인만, --quarantine으로 격리) 또는 Server TUI의 "도구" 화면을 사용합니다. 사용자 규칙은 /etc/roamswitch/rules.d/*.json, 탐지 후 자신의 스크립트를 호출하려면 /etc/roamswitch/hooks/post-detection(탐지 내용이 JSON으로 표준 입력에 전달됩니다. root 소유이며 다른 사용자가 쓸 수 없고 상위 디렉터리도 안전한 파일만 실행되며, 셸을 거치지 않고 30초 후 중단됩니다)입니다. 규칙 업데이트는 일일 수신 전용 업데이트에 포함됩니다. cve_kernel_map_updates_enabled를 끄면 통신이 전혀 없고 내장 규칙으로 동작합니다.

← 서버 버전 화이트페이퍼 Linux 버전 설치 페이지로 →