Hostveil / 문서 / 점검 항목

점검 항목

Hostveil은 셀프호스터가 실제로 침해당하는 영향이 가장 큰 경로들을 점검한 뒤, 모든 것을 하나의 0–100 점수로 합칩니다. 각 발견 항목은 domain.rule 형태의 고정된 ID를 가지며, explain, fix, rollback과 함께 사용합니다.

영역 한눈에 보기

영역접두사필요 조건
Docker / Composecompose.Docker
SSHssh.
방화벽firewall.
자동 업데이트updates.
노출된 서비스ports.ss
계정accounts.root (/etc/shadow용)
파일 권한fileperms.
AI 에이전트 런타임agent.OpenClaw 또는 Hermes 설치
커널 하드닝sysctl.
Docker 데몬dockerd.Docker
서비스 하드닝systemd.systemd
리버스 프록시proxy.nginx 또는 Docker
이미지 CVE (선택)cve.Trivy

Docker나 Trivy가 없나요? 해당 영역은 깔끔하게 건너뛰어지고 점수가 재정규화되므로, 실행되지도 않은 스캔에 대해 오해를 살 만큼 완벽한 결과를 받는 일은 없습니다.

Hostveil은 이 영역들에서 발견 항목 175개를 보고할 수 있고, 그중 128개에 수정이 붙어 있습니다. 75개는 무인으로 적용하고, 53개는 차이를 읽은 뒤에만 적용합니다. 나머지는 일부러 Manual로 둔 것이고, 하나하나 internal/fix/register.go의 목록에 Hostveil이 손대지 않는 이유와 함께 적혀 있습니다. 계정 삭제는 체크포인트로 되돌릴 수 없고, Docker 소켓 마운트를 떼면 그것을 필요로 하는 도구가 망가집니다. 아무도 들여다보지 않아서 Manual인 것이 아니라 판단해서 Manual인 것이며, 그 차이에서 테스트가 빌드를 깨뜨립니다. 이 문단의 개수도 여기 타이핑한 것이 아니라 레지스트리에서 계산한 값입니다.

175개 발견 항목의 처리 방식

Auto
75
Review
53
Manual
47

심각도와 점수

Hostveil은 발견 항목을 얼마나 급한지로 등급을 매깁니다. 얼마나 나쁜지가 아닙니다. 설정 파일을 읽는 스캐너는 "root로 도는 컨테이너"가 얼마나 나쁜지 말해 줄 수 없습니다. 그건 그 컨테이너가 무엇을 하느냐에 전적으로 달려 있으니까요. 스캐너가 말할 수 있는 것은 지금 이 순간 공격자와 그 문제 사이에 무엇이 얼마나 서 있느냐이고, 이 세 단계가 재는 것이 바로 그것입니다.

단계가져가는 몫
HIGH아무것도 가지지 않은 사람이 지금 호스트 밖에서 닿거나 쓸 수 있습니다. 인증 없는 열린 서비스, 비밀번호 없는 계정, 이미 호스트 root를 넘겨받은 컨테이너. 먼저 뚫어야 할 것이 없습니다.남은 점수의 1/2
MEDIUM발판이나 추측한 자격 증명, 로컬 계정이 있으면 무너지는 경계입니다. 공격자에게 아직 없는 무언가가 필요하고, 그것을 손에 넣을 값어치를 만들어 주는 것이 이 항목입니다.1/8
LOW심층 방어입니다. 오늘 알려진 경로는 없고, 앞으로의 침해가 닿을 범위를 좁혀 줍니다.1/16

네 단계가 아니라 세 단계이고, 위의 정의가 분류 체계의 전부입니다. 이름이 평범한 것은 여기서 이름이 할 일이 순서를 담는 것 하나뿐이기 때문입니다. 그 하나를 칩 하나, --json 한 줄, SARIF 레벨 하나에서 해내야 하니, 다른 것이 들어갈 자리는 없습니다. 각 단계가 무엇을 뜻하는지는 옆 칸에 있고, 그것은 발견 항목에 대한 의견이 아니라 호스트에 대한 진술입니다.

3.11에서 변경

Hostveil에는 원래 심각/높음/보통/낮음, 네 단계가 있었습니다. CVE의 공개 등급을 그대로 통과시키려고 Trivy에서 물려받은 것이었습니다. 지금은 취약점을 이미지 단위로 묶기 때문에 발견 항목에 닿는 것은 CVE별 등급이 아니라 이미지 하나당 한 단계이고, 네 번째 단계는 설정 파일이 답할 수 없는 질문을 계속 묻고 있었습니다.

HIGH는 기존 심각과 높음을 합한 집합과 정확히 같으므로, scan의 종료 코드와 SARIF 내보내기는 예전과 똑같은 뜻입니다. Trivy 출력과 나란히 비교한다면 한 가지만 유의하세요. Trivy가 CRITICAL이라 부르는 취약점은 여기서 HIGH로 표시됩니다. Hostveil은 세 단계이고 Trivy는 네 단계라, 한쪽의 최상위와 다른 쪽의 최상위는 이름으로 맞출 수 없습니다.

고정된 점수가 아니라 남은 점수의 일부를 가져가는 방식이라, 항목 두 개만으로 축이 바닥나지 않습니다. 열 번째 항목도 여전히 대가를 치르고, 문제가 30개인 호스트는 3개인 호스트보다 낮은 점수를 받습니다. 전체 모형은 점수 산정에 있습니다 — 계산식, 실제 스캔으로 만든 예시, 그리고 이 숫자가 알려주지 않는 것까지.

Hostveil이 Unavailable로 표시한 항목 — 업스트림 패치가 없는 취약점 — 도 조치 가능한 항목과 똑같이 심각도가 정한 만큼 그대로 반영됩니다. 아직 고칠 방법이 없다고 위험이 줄어드는 건 아니라서 따로 깎지 않으며, 미패치 이미지를 계속 쓰는 호스트라면 그 사실이 취약점 축에 그대로 드러납니다.

도메인별 비중

축은 셀프호스트 서버에서 얼마나 중요한지에 따라 가중치를 갖고, 합은 100입니다. 실행되지 않은 도메인은 가중치가 총합에서 빠지고 나머지가 재정규화됩니다.

가중치
컨테이너 노출13
SSH 하드닝13
취약점9
호스트 방화벽8
노출된 서비스7
AI 에이전트 런타임7
리버스 프록시6
계정 위생7
자동 업데이트7
Docker 데몬7
서비스 하드닝6
파일 권한5
커널 하드닝5

영역을 온전히 점검하지 못했을 때

"들여다보지 못했다"와 "거기에 아무것도 없었다"는 똑같이 빈 목록을 만들어 내지만, 뜻은 정반대입니다. Hostveil은 둘을 절대 뒤섞지 않으며, 모든 영역이 실제로 얼마나 확인했는지를 함께 보고합니다.

상태의미점수에 미치는 영향
완료영역을 끝까지 점검했습니다.정상적으로 점수에 반영됩니다.
건너뜀의존 도구가 없습니다 — Docker 없음, Trivy 없음, ufw 없음.N/A로 표시되고 제외되며, 나머지 축이 재정규화됩니다.
부분일부만 확인했습니다 — 예를 들어 Trivy가 이미지 다섯 개 중 셋만 스캔한 경우.확인한 범위로 채점하되 표시가 남습니다. TUI는 42~, 대시보드는 호박색 42~로 씁니다.
실패점검기가 해당 영역을 전혀 확인하지 못했습니다.건너뜀과 마찬가지로 제외되며, 이유가 표시됩니다.

두 UI 모두 발견 목록 위에 그 이유를 출력합니다 — "Container partial: cannot inspect containers started outside Compose", "CVEs skipped: Trivy not installed". 제외된 축은 결코 100점으로 처리되지 않습니다. 이미지를 볼 수 없었던 스캔은 이미지가 깨끗하다고 확인한 스캔이 아니기 때문입니다. root 없이 실행하면 점수가 높아지는 게 아니라 낮아지는 이유가 이것입니다.

hostveil scan은 수정되지 않은 발견 항목 중 HIGH가 하나라도 있으면 0이 아닌 값으로 종료합니다 — CI나 cron 게이트로 유용합니다.

SSH ssh.

sshd_config에서 직접 파싱하며, Include 지시어를 따라 sshd_config.d/까지 읽습니다. Debian과 Ubuntu는 Include를 파일 맨 위에 두고, sshd는 각 키워드에 대해 처음 얻은 값을 사용하므로 drop-in 파일이 아래 줄들을 이깁니다. 읽으려면 root 권한이 필요하며, hostveilsudo로 자동 상승하여 이를 얻습니다. 수정은 실제로 적용되고 있는 지시어가 있는 파일에 가해집니다.

읽기는 첫 Match에서 멈춥니다. sshd가 그 뒤의 지시어를 조건에 맞는 접속에만 적용하므로, 그 안의 지시어는 전역 지시어처럼 호스트를 설명하지 않기 때문입니다 — 다만 읽지 않았다는 것은 “문제 없음”이 아니라 못 본 영역이므로, 도메인은 해당 파일을 밝히며 Degraded로 보고됩니다. Match Address 0.0.0.0/0 다음에 PasswordAuthentication yes가 오면 존재하는 모든 접속에 비밀번호가 다시 열리는데, 그런 호스트를 “전부 점검했다”고 하는 스캔은 들여다본 적도 없는 것을 주장하는 셈입니다.

ID무엇을 표시하는지심각도수정
ssh.emptypasswords빈 비밀번호가 허용됨HIGHAuto-fix
ssh.rootlogin비밀번호를 사용한 root 로그인이 허용됨HIGHReview
ssh.passwordauth비밀번호 인증이 허용됨MEDIUMReview
ssh.gatewayports원격 포워딩 포트가 네트워크에 노출됨MEDIUMReview
ssh.hostbasedauth호스트 기반 인증이 활성화됨MEDIUMReview
ssh.kbdinteractivePasswordAuthentication no 이후에도 대화형 비밀번호 입력이 살아 있음MEDIUMReview
ssh.permituserenvironmentSSH 세션에 사용자 환경 변수를 주입할 수 있음MEDIUMReview
ssh.permittunnelSSH 네트워크 터널을 만들 수 있음MEDIUMReview
ssh.maxauthtries연결당 인증 시도 횟수가 너무 많음LOWAuto-fix
ssh.logingracetime미인증 연결을 너무 오래 열어 둠LOWAuto-fix
ssh.x11forwardingX11 포워딩이 활성화됨LOWAuto-fix
ssh.allowtcpforwardingTCP 포워딩이 활성화됨LOWReview
ssh.clientalivecountmax응답 없는 SSH 세션을 너무 오래 유지함LOWAuto-fix
ssh.clientaliveinterval사용하지 않는 SSH 세션에 서버 측 제한 시간이 없음LOWAuto-fix
ssh.fingerprinthash호스트 키 지문에 오래된 해시를 사용함LOWAuto-fix
ssh.ignorerhosts오래된 rhosts 인증을 명시적으로 무시하지 않음LOWAuto-fix
ssh.loglevelSSH 인증 로그가 충분히 상세하지 않음LOWAuto-fix
ssh.maxsessionsSSH 연결 하나가 너무 많은 세션을 열 수 있음LOWReview
ssh.printlastlog사용자에게 이전 로그인 기록을 보여 주지 않음LOWAuto-fix
ssh.strictmodesSSH가 소유권과 파일 권한을 검사하지 않음LOWAuto-fix
ssh.tcpkeepaliveSSH가 위조 가능한 TCP keepalive를 받아들임LOWAuto-fix
ssh.usednsSSH가 불필요한 역방향 DNS 조회를 수행함LOWAuto-fix
ssh.allowagentforwardingSSH 에이전트 포워딩이 활성화됨LOWReview

Docker / Compose compose.

Compose 파일에 대한 네이티브 감사입니다 — 외부 스캐너가 필요 없습니다. docker run으로 직접 띄운 컨테이너도 파일이 아니라 데몬에서 읽어 함께 점검합니다. 이 발견 항목들은 항상 Manual입니다 — 수정할 파일이 없으므로, 동작하지 않을 버튼을 보여주는 대신 컨테이너를 다시 만들 때 무엇을 바꿔야 하는지 알려줍니다.

무엇을 노출로 보는가. 공개된 포트의 호스트 주소가 0.0.0.0이거나 ::이거나 아예 적혀 있지 않으면 모든 인터페이스에서 닿을 수 있는 것으로 봅니다. 마지막 경우가 많이들 걸리는 자리입니다. "6379:6379"는 주소를 적지 않았고 Docker는 그것을 모든 인터페이스에 바인딩하므로, 이 규칙에서도 데몬에서도 노출입니다. "127.0.0.1:6379:6379"처럼 루프백을 명시한 경우만 아니며, 호스트 쪽이 아예 없는 포트는 공개된 것이 아니라 애초에 해당하지 않습니다.

이 규칙은 어느 한 파일이 아니라 docker compose config가 출력하는 병합된 프로젝트에 적용합니다. Compose는 포트 매핑을 파일 간에 교체하지 않고 덧붙이므로, 베이스 파일이 루프백에 묶어 두고 오버라이드가 다시 공개하면 그 서비스는 노출된 상태입니다. 베이스만 읽으면 그 호스트를 깨끗하다고 부르게 됩니다.

ID무엇을 표시하는지심각도수정
compose.ds016Docker 소켓이 컨테이너에 마운트됨HIGHManual
compose.ds018데이터 저장소가 모든 인터페이스에 노출됨HIGHAuto-fix
compose.ds001컨테이너가 특권 모드로 실행됨HIGHManual
compose.ds019관리자 패널이 모든 인터페이스에 노출됨HIGHAuto-fix
compose.dr001컨테이너가 호스트 네트워킹 모드를 사용함HIGHManual
compose.ds017민감한 호스트 경로가 읽기-쓰기로 마운트됨HIGHManual
compose.ds005위험한 리눅스 capability를 추가함HIGHManual
compose.ds020호스트 PID 네임스페이스를 공유함HIGHManual
compose.dr005환경 변수에 하드코딩된 비밀값HIGHManual
compose.ds006no-new-privileges 강화 설정 누락MEDIUMAuto-fix
compose.ds021호스트 IPC 네임스페이스를 공유함MEDIUMManual
compose.ds009컨테이너가 root로 실행됨MEDIUMManual
compose.dr002포트가 모든 인터페이스에 게시됨MEDIUMAuto-fix
compose.ds008재시작 정책이 설정되지 않음LOWAuto-fix
compose.ds022컨테이너 파일시스템이 쓰기 가능함LOWManual
compose.ds023seccomp 필터가 꺼져 있음HIGHManual
compose.ds024AppArmor 격리가 꺼져 있음MEDIUMManual
compose.ds026호스트 사용자 네임스페이스를 사용함MEDIUMManual
compose.ds010메모리 제한이 설정되지 않음LOWReview
compose.ds012헬스체크가 정의되지 않음LOWManual
compose.dr004env_file에서 비밀값을 불러옴LOWManual

방화벽 firewall.

ID무엇을 표시하는지심각도수정
firewall.inactive활성화된 호스트 방화벽이 없음 (ufw, firewalld, nftables, 또는 iptables)HIGHReview
firewall.default-allow방화벽이 켜져 있지만 기본 인바운드 정책이 전부 허용HIGHReview
firewall.docker-bypassufw가 켜져 있는데도 publish된 컨테이너 포트가 외부에서 접근 가능HIGHManual

자동 업데이트 updates.

ID무엇을 표시하는지심각도수정
updates.disabled자동 보안 업데이트가 활성화되지 않음 (apt unattended-upgrades 또는 dnf-automatic)MEDIUMReview
updates.pending-security보안 업데이트가 나와 있지만 설치되지 않음HIGH / MEDIUMReview
updates.reboot-required설치된 업데이트가 적용되려면 재부팅이 필요함HIGHManual
updates.process-accounting프로세스 사용 기록 도구가 설치되지 않음LOWReview
updates.sysstat시스템 활동 기록 도구가 설치되지 않음LOWReview
updates.auditdLinux 감사 데몬이 설치되지 않음MEDIUMReview
updates.debsums설치된 패키지 파일을 검증할 수 없음LOWReview
updates.rkhunterrootkit 점검기가 설치되지 않음LOWReview
updates.pam-pwqualityPAM에 비밀번호 품질 구성 요소가 없음MEDIUMReview
updates.apt-show-versions패치 목록 도구가 설치되지 않음LOWReview
updates.sysstat-disabled시스템 활동 수집이 비활성화됨LOWAuto-fix
updates.apt-listchangesAPT 변경 기록 검토 도구가 설치되지 않음LOWReview
updates.pam-tmpdirPAM 세션이 전역 임시 디렉터리를 공유함LOWReview
updates.fail2ban반복되는 인증 실패를 자동 차단하지 않음MEDIUMReview

노출된 서비스 ports.

ss로 호스트의 수신 대기 중인 TCP 소켓을 읽어, 루프백이 아닌 주소에 바인딩된 서비스를 표시합니다 — 컨테이너가 아니기 때문에 Compose 파일 점검으로는 볼 수 없는, 호스트에 직접 설치된 데이터베이스·관리 패널·앱입니다. 루프백 전용 바인딩은 무시하며, SSH는 정상으로 간주해 여기서 표시하지 않습니다.

ID표시 대상심각도수정
ports.exposed-datastore데이터스토어(Postgres, MySQL, Redis, MongoDB 등)가 네트워크에서 접근 가능HIGHManual
ports.exposed-admin관리 UI(예: Portainer)가 네트워크에서 접근 가능HIGHManual
ports.exposed다른 서비스가 루프백이 아닌 주소에서 수신 대기 중이고 활성 호스트 방화벽이 없음LOWManual
ports.redis-bindRedis가 루프백 밖에서 수신하도록 설정됨MEDIUMReview
ports.redis-protected-modeRedis 보호 모드가 비활성화됨MEDIUMAuto-fix
ports.redis-disable-configRedis CONFIG 명령이 노출됨MEDIUMReview

계정 accounts.

하나의 질문을 세 가지 방식으로 던집니다. 누가 root가 될 수 있고, 그 사이에 무엇이 서 있는가. 앞의 두 답은 /etc/passwd/etc/shadow에서 직접 파싱합니다. /etc/shadow 읽기에는 root 권한이 필요하며, hostveilsudo로 자동 승격해 이를 얻습니다. 권한이 없으면 빈 비밀번호 점검은 건너뛰고 UID 0 점검은 계속 실행됩니다.

세 번째 답은 파일에서 나오지 않습니다. sudo 규칙은 별칭과, /etc/group이 아니라 LDAP로 해석될 수도 있는 그룹 지정과, include 순서를 가진 계층형 설정입니다. 그래서 Hostveil은 /etc/sudoers를 다시 해석하는 대신 sudo 자신에게 각 계정이 실제로 무엇을 실행할 수 있는지 물어봅니다. 여기에도 root 권한이 필요하며, 권한이 없으면 아무도 발견되지 않았다고 보고하는 대신 확인하지 못했다고 보고합니다.

ID표시 대상심각도수정
accounts.uid0root가 아닌 계정이 root의 UID(0)를 가짐HIGHManual
accounts.emptypassword로그인 계정에 빈 비밀번호가 설정됨HIGHReview
accounts.sudo-nopasswdsudo 규칙이 비밀번호를 묻지 않고 무엇이든 root로 실행하도록 허용함MEDIUMManual
accounts.duplicate-uid여러 계정이 같은 UID를 사용함MEDIUMManual
accounts.weak-password-hash로그인 계정이 약한 비밀번호 해시를 사용함MEDIUMManual
accounts.password-rounds비밀번호 해시 반복 횟수가 강화되지 않음LOWAuto-fix
accounts.default-umask새 파일의 기본 권한이 너무 넓음LOWAuto-fix
accounts.local-banner로컬 로그인 경고가 무단 접근을 경고하지 않음LOWReview
accounts.remote-banner원격 로그인 경고가 무단 접근을 경고하지 않음LOWReview
accounts.password-aging비밀번호 사용 기간 기본값이 너무 느슨함LOWAuto-fix
accounts.core-dumpscore dump가 명시적으로 비활성화되지 않음LOWAuto-fix

리버스 프록시 proxy.

이 도구의 사용자가 가장 갖고 있을 법하고 가장 설정해 두지 않았을 법한 구성 요소입니다. 여기 나머지 전부가 443번에서 응답하는 그것 뒤에 있습니다. 표면이 둘인 이유는 사람들이 프록시를 굴리는 방식이 둘이기 때문입니다. 패키지로 설치한 nginx는 /etc/nginx에서 읽고 include를 따라 conf.dsites-enabled까지 들어갑니다. 컨테이너로 띄운 Traefik은 컨테이너 영역이 이미 찾아 둔 Compose 파일에서 읽습니다.

세 가지는 일부러 점검하지 않습니다. 평문 HTTP로 서빙하는 프록시는, HTTPS로 리다이렉트만 하는 vhost와 이미 TLS를 끝낸 무언가 뒤에 있는 프록시가 둘 다 올바른데 설정만 봐서는 구분되지 않기 때문입니다. 빠진 보안 헤더는, 있으면 좋지만 그것 때문에 침해당하지는 않기 때문입니다. 그리고 인증서에 관한 것은, 만료와 발급자가 파일이 아니라 네트워크에 대한 사실이기 때문입니다.

ID표시 대상심각도수정
proxy.traefik-api-insecureTraefik의 API와 대시보드가 인증 없이 떠 있어, 프록시 뒤의 모든 라우터와 백엔드 주소를 나열함HIGHManual
proxy.tls-deprecated-protocolsssl_protocols가 아직 TLS 1.0이나 1.1을 제공함. RFC 8996이 폐기했고 요즘 브라우저는 전부 거절함MEDIUMManual
proxy.directory-listingautoindex on이 인덱스 파일 없는 모든 디렉터리를 훑어볼 수 있게 만듦MEDIUMManual
proxy.no-scan-jailfail2ban은 설치돼 있지만, 존재하지 않는 경로나 취약점 스캔 시도를 잡아내는 nginx-botsearch 규칙이 꺼져 있음MEDIUMReview

파일 권한 fileperms.

보안상 중요한 파일 목록의 권한을 확인해, 필요 이상으로 느슨하게 설정된 권한을 표시합니다.

ID표시 대상심각도수정
fileperms.shadow/etc/shadow0640보다 느슨함HIGHAuto-fix
fileperms.passwd/etc/passwd가 root가 아닌 사용자에게 쓰기 가능HIGHAuto-fix
fileperms.group/etc/group이 root가 아닌 사용자에게 쓰기 가능HIGHAuto-fix
fileperms.hostkeySSH 호스트 개인 키를 root 외 사용자가 읽을 수 있음HIGHAuto-fix
fileperms.gshadow/etc/gshadow 권한이 너무 넓음HIGHAuto-fix
fileperms.sudoers/etc/sudoers 권한이 너무 넓음HIGHAuto-fix
fileperms.sudoers-dropinssudoers 추가 파일 권한이 너무 넓음HIGHAuto-fix
fileperms.cron시스템 cron 정의를 root 외 사용자가 쓸 수 있음HIGHAuto-fix
fileperms.systemd-units시스템 서비스 유닛을 root 외 사용자가 쓸 수 있음HIGHAuto-fix
fileperms.docker-configroot의 Docker 자격 증명 파일 권한이 너무 넓음HIGHAuto-fix
fileperms.grub-configGRUB 설정 권한이 너무 넓음HIGHAuto-fix
fileperms.grub2-configGRUB2 설정 권한이 너무 넓음HIGHAuto-fix
fileperms.at-allowat 허용 목록 권한이 너무 넓음MEDIUMAuto-fix
fileperms.at-denyat 차단 목록 권한이 너무 넓음MEDIUMAuto-fix
fileperms.cron-allowcron 허용 목록 권한이 너무 넓음MEDIUMAuto-fix
fileperms.cron-denycron 차단 목록 권한이 너무 넓음MEDIUMAuto-fix
fileperms.crontab시스템 crontab 권한이 너무 넓음HIGHAuto-fix
fileperms.passwd-backuppasswd 백업을 root 외 사용자가 쓸 수 있음HIGHAuto-fix
fileperms.owner위 파일 중 하나가 root 소유가 아님 — 누가 읽을 수 있는지는 권한 비트만으로 정해지지 않음HIGHManual
fileperms.sshd-configsshd_config가 root가 아닌 사용자에게 쓰기 가능MEDIUMAuto-fix
fileperms.compiler모든 로컬 사용자가 시스템 컴파일러를 실행할 수 있음LOWReview

커널 하드닝 sysctl.

/proc/sys에서 직접 읽습니다 — sysctl 바이너리가 필요 없습니다. 이 항목들은 사실상 모든 서버에서 안전한 값이 같은 조용한 손잡이들입니다. 공격자가 들어오는 구멍은 아니지만, 각각은 발판이 root가 되는 것과 위조된 패킷이 경로가 되는 것을 막아 줍니다. 이 커널에 없는 파라미터(예: Yama가 빌드되지 않은 경우)는 그냥 건너뜁니다.

값이 모호하지 않은 항목은 Auto-fix입니다. Hostveil은 /etc/sysctl.d 아래에 되돌릴 수 있는 드롭인 파일 하나를 쓰고 실행 중인 커널은 건드리지 않습니다. 다음 부팅 때 적용되며, 직접 sysctl --system을 실행하면 즉시 적용됩니다. 롤백은 파일을 삭제하거나 복원하고, 그 뒤 파일이 직접 수정됐다면 안전하게 거절합니다.

라우터·릴레이·다중 회선 구성에 따라 답이 달라지는 항목은 Review로 남습니다. 엄격한 역방향 경로 필터링은 비대칭 라우팅을 깨뜨릴 수 있고 Proxy ARP 비활성화는 의도적으로 릴레이 역할을 하는 호스트를 깨뜨릴 수 있습니다. 이런 항목은 영구 적용과 즉시 적용 대안을 유지해 운영자가 서버 구성에 맞게 선택합니다.

net.ipv4.ip_forward는 의도적으로 점검하지 않습니다. Docker, WireGuard, Tailscale 종료 노드, 가상화 호스트가 모두 정당하게 켜는 값이고, Hostveil은 그것을 실수와 구별할 수 없습니다.

ID무엇을 표시하는지심각도수정
sysctl.ptrace-scope아무 프로세스나 형제 프로세스를 디버깅해 메모리를 읽을 수 있음MEDIUMAuto-fix
sysctl.syncookiesTCP SYN 플러드 보호가 꺼져 있음MEDIUMAuto-fix
sysctl.accept-redirectsICMP 리다이렉트가 이 호스트의 라우팅을 바꿀 수 있음MEDIUMAuto-fix
sysctl.protected-links심볼릭/하드 링크 경쟁 보호가 비활성화됨MEDIUMAuto-fix
sysctl.kptr-restrict커널 포인터 주소가 모든 사용자에게 보임LOWAuto-fix
sysctl.dmesg-restrict아무 사용자나 커널 로그를 읽을 수 있음LOWAuto-fix
sysctl.sysrqMagic SysRq가 전부 활성화됨LOWReview
sysctl.rp-filter출발지 주소 위조 필터가 꺼져 있음 (strict와 loose 모두 통과)LOWReview
sysctl.accept-source-route소스 라우팅 패킷을 허용함MEDIUMAuto-fix
sysctl.send-redirectsICMP 리다이렉트를 전송함MEDIUMReview
sysctl.suid-dumpable권한 프로세스가 core dump를 쓸 수 있음MEDIUMAuto-fix
sysctl.protected-fifosFIFO 보호가 약함MEDIUMAuto-fix
sysctl.protected-regular일반 파일 보호가 약함MEDIUMAuto-fix
sysctl.unprivileged-bpf비권한 사용자가 BPF를 로드할 수 있음MEDIUMAuto-fix
sysctl.perf-events성능 이벤트가 사용자에게 노출됨MEDIUMAuto-fix
sysctl.icmp-broadcasts브로드캐스트 ping을 허용함LOWAuto-fix
sysctl.bogus-icmp-errors잘못된 ICMP 오류를 무시하지 않음LOWAuto-fix
sysctl.log-martians의심스러운 출발지 주소를 로그하지 않음LOWAuto-fix
sysctl.aslrASLR이 완전히 활성화되지 않음MEDIUMAuto-fix
sysctl.core-uses-pidcore dump 이름에 PID가 없음LOWAuto-fix
sysctl.ctrl-alt-delCtrl-Alt-Delete가 즉시 재부팅함LOWAuto-fix
sysctl.bpf-jit-hardenBPF JIT 보호가 충분하지 않음MEDIUMAuto-fix
sysctl.tty-ldisc-autoloadTTY가 line discipline을 자동 로드함MEDIUMAuto-fix
sysctl.ipv6-accept-redirects-allIPv6 redirect를 전역으로 허용함MEDIUMAuto-fix
sysctl.ipv6-accept-redirects-default새 IPv6 interface가 redirect를 허용함MEDIUMAuto-fix
sysctl.ipv6-accept-source-route-allIPv6 source routing을 허용함MEDIUMAuto-fix
sysctl.ipv6-accept-source-route-default새 IPv6 interface가 source routing을 허용함MEDIUMAuto-fix
sysctl.ipv6-send-redirectsIPv6 redirect를 전송함LOWAuto-fix
sysctl.ipv4-default-accept-redirects새 IPv4 interface가 redirect를 허용함MEDIUMAuto-fix
sysctl.ipv4-default-rp-filter새 IPv4 interface에 출발지 검증이 없음LOWReview
sysctl.proxy-arp-allProxy ARP가 전역으로 활성화됨MEDIUMReview
sysctl.proxy-arp-default새 interface가 Proxy ARP를 활성화함MEDIUMReview
sysctl.multicast-forwardingIPv4 multicast forwarding이 활성화됨LOWReview
sysctl.bootp-relayBOOTP relay가 활성화됨LOWReview
sysctl.tcp-rfc1337TCP TIME-WAIT 보호가 꺼져 있음LOWAuto-fix
sysctl.module-dccp선택적 DCCP 프로토콜을 사용할 수 있음LOWReview
sysctl.module-sctp선택적 SCTP 프로토콜을 사용할 수 있음LOWReview
sysctl.module-rds선택적 RDS 프로토콜을 사용할 수 있음LOWReview
sysctl.module-tipc선택적 TIPC 프로토콜을 사용할 수 있음LOWReview
sysctl.module-usbstorageUSB 저장 장치 드라이버를 사용할 수 있음LOWReview

Docker 데몬 dockerd.

Compose 점검은 서비스가 무엇을 선언했는지를 보고, CVE 점검은 그 서비스가 돌리는 이미지를 봅니다. 이 영역은 그 둘 밑에 있는 데몬 자체 — 실제로 root를 쥐고 있는 부분 — 을 봅니다.

여기서 가장 심각한 항목은 Hostveil 전체에서 가장 심각한 항목이므로 분명히 말해 둘 필요가 있습니다. TLS 클라이언트 검증 없이 TCP 포트로 듣고 있는 데몬은 그 포트에 닿을 수 있는 누구에게나 호스트의 root입니다. 비밀번호도, 취약점도 필요 없고, 파일시스템을 통째로 마운트한 컨테이너를 시작하는 HTTP 요청 하나면 됩니다. 소켓 그룹의 멤버십은 로컬 계정에서 얻는 같은 권한이고, sudo와 달리 아무것도 묻지 않고 아무것도 기록하지 않습니다.

출처가 셋이고, 서로 어긋납니다. Docker 설정은 /etc/docker/daemon.json, 서비스 유닛의 플래그, 그리고 실행 중인 데몬 안에 각각 있습니다 — 재시작 없이 파일만 고쳤다면 이 셋은 서로 다른 답입니다. 그래서 데몬 기본값은 docker info에서 읽습니다. 누가 적어 둔 내용이 아니라 데몬이 실제로 적재한 내용이기 때문입니다. 소켓과 TLS 설정은 파일과 유닛을 함께 읽습니다. docker info가 아예 보고하지 않기 때문입니다. ss는 직접 확인해 볼 수 있는 엔드포인트를 증거로 덧붙이되, 발견 항목의 존재 여부를 결정하지는 않습니다.

여기의 모든 항목은 의도적으로 Manual입니다. 점검기는 실행 중인 데몬을 읽지만, 수정은 데몬이 재시작하기 전까지 다시 읽지 않는 파일을 고치는 셈입니다. 문제는 Docker를 재시작하면 호스트의 모든 컨테이너가 멈춘다는 것입니다. 공격자에게 보이는 것은 아무것도 바꾸지 않은 채 항목을 해결됨으로 표시하고 점수만 올리는 수정은 수정이 없는 편만 못합니다. 그래서 조치 안내에 정확한 변경 내용만 담아 두고, 적용 시점은 운영자가 직접 고르게 합니다.

ID의미심각도수정
dockerd.api-unauthenticatedDocker API가 TLS 클라이언트 검증 없이 TCP로 제공됩니다 — 포트에 닿는 누구에게나 인증 없는 rootHIGHManual
dockerd.socket-world-writable모든 로컬 계정이 Docker 소켓에 접속할 수 있고, 따라서 root가 될 수 있습니다HIGHManual
dockerd.api-tls-unverifiedAPI가 암호화되어 있지만 아무 클라이언트나 받습니다 — tlsverify 없는 TLS는 서버를 인증할 뿐 호출자를 인증하지 않습니다HIGHManual
dockerd.group-membersroot 외의 계정이 소켓 그룹을 갖고 있습니다. 비밀번호 확인도 감사 기록도 없는 root 동등 권한입니다MEDIUM / HIGHManual
dockerd.no-new-privileges컨테이너가 여전히 setuid 바이너리로 권한을 얻을 수 있습니다MEDIUMManual
dockerd.userns-remap사용자 네임스페이스가 재매핑되지 않아 컨테이너 root가 곧 호스트 root입니다LOWManual
dockerd.live-restore데몬을 재시작하면 모든 컨테이너가 멈춥니다. 그래서 데몬 업데이트가 미뤄집니다LOWManual

dockerd.group-members는 그룹을 가진 계정이 사람일 때 MEDIUM, 그중 하나라도 서비스 계정 — 시스템 UID이거나 대화형 로그인이 없는 계정 — 이면 HIGH입니다. CI 러너나 모니터링 에이전트에게 root가 될 능력을 일부러 주는 사람은 없고, 한 번도 로그인하지 않는 자격 증명은 아무도 지켜보지 않는 자격 증명입니다.

루프백 전용 TCP 소켓은 표시하지 않습니다. 유닉스 소켓이 닿지 않던 곳에 닿지 않고, 데몬 앞에 프록시를 두는 문서화된 방법이기 때문입니다. rootless로 실행 중인 데몬은 userns-remapgroup-members가 억제됩니다. API 항목은 그대로 HIGH입니다. rootless는 피해 범위를 호스트가 아니라 그 사용자의 컨테이너로 한정하며 그 차이는 설명문에 적히지만, 포트에 닿기 어려워지는 것은 아니기 때문입니다. live-restore는 Docker가 지원하지 않는 swarm 노드에서 억제됩니다.

서비스 하드닝 systemd.

자체 호스팅 서버의 절반가량은 컨테이너 밖에 있습니다. Compose 도메인은 컨테이너가 선언한 내용 — privileged, root 실행, no-new-privileges 해제 — 을 읽는데, 유닛 파일로 뜬 서비스에도 똑같은 판단이 필요합니다. 이 도메인이 생기기 전까지는 아무도 그쪽을 보지 않았습니다.

답은 systemd가 이미 알고 있습니다. systemctl show는 유닛 파일과 모든 드롭인, 그리고 기본값까지 병합한 뒤의 실효 값을 알려줍니다. 그래서 Hostveil이 읽는 값은 어떤 파일에 적힌 값이 아니라 서비스가 실제로 그 값으로 돌아간다는 뜻입니다.

직접 만든 유닛만 검사합니다. 유닛 파일이 /etc/systemd/system 또는 /usr/local/lib/systemd/system 아래에 있으면 대상이 됩니다 — 사용자가 직접 쓰거나 손으로 설치한 것들입니다. 배포판 유닛은 배포판이 자기 일정에 맞춰 하드닝하며, 그걸 뒤집는 것은 남의 서비스 유지보수를 떠안는 일입니다. 게다가 그것들까지 보고하면 사용자가 고르지도 않은 소프트웨어에 대한 수십 건의 항목에 정작 본인 서비스가 묻힙니다. 그렇게 되면 아무도 그 도메인을 읽지 않습니다.

ID의미심각도수정
systemd.no-new-privilegessetuid 바이너리를 통해 서비스가 권한을 얻을 수 있습니다. 서비스 안의 발판이 서비스 밖의 발판으로 바뀌는 통상적인 경로입니다LOW / MEDIUMReview
systemd.protect-system서비스가 /usr, /boot, /etc에 쓸 수 있습니다. /etc를 고칠 수 있는 서비스는 이 호스트의 모든 로그인을 손에 넣습니다MEDIUM / LOWManual
systemd.protect-home서비스가 /home, /root, /run/user를 읽을 수 있습니다. 모든 사용자의 SSH 키, 클라우드 자격 증명, 비밀번호 데이터베이스가 그 안에 있습니다MEDIUM / LOWManual
systemd.private-tmp서비스가 다른 모든 프로세스와 /tmp를 공유합니다. 심볼릭 링크 경쟁이 벌어지는 바로 그 자리입니다LOWManual
systemd.private-devices서비스가 호스트 디바이스에 접근할 수 있음MEDIUMManual
systemd.protect-kernel-tunables서비스가 커널 설정을 바꿀 수 있음MEDIUMManual
systemd.protect-kernel-modules서비스가 kernel module을 바꿀 수 있음MEDIUMReview
systemd.protect-control-groups서비스가 제어 그룹을 바꿀 수 있음MEDIUMManual
systemd.protect-kernel-logs서비스가 커널 로그에 접근할 수 있음LOWReview
systemd.protect-clock서비스가 시스템 시계를 바꿀 수 있음LOWReview
systemd.restrict-suid-sgid서비스가 setuid 파일을 만들 수 있음MEDIUMReview
systemd.restrict-namespaces서비스가 네임스페이스를 만들 수 있음MEDIUMManual
systemd.lock-personality서비스가 실행 퍼스널리티를 바꿀 수 있음LOWReview
systemd.memory-deny-write-execute서비스가 쓰기와 실행이 모두 가능한 메모리를 만들 수 있음MEDIUMManual

심각도가 두 가지이며, 어느 쪽인지는 실행 계정이 정합니다. no-new-privileges는 root가 아닌 계정으로 도는 서비스에서 MEDIUM, root로 도는 서비스에서 LOW입니다. root 서비스는 이미 무엇이든 할 수 있으므로 이 설정이 막는 setuid 경로의 값어치가 거기서는 작습니다. 파일시스템 보호 두 가지는 같은 이유로 반대 방향입니다 — no-new-privileges의 값어치가 가장 작은 자리에서 가장 큽니다.

여기의 모든 항목은 의도적으로 Manual입니다. 드롭인은 두 줄이고 Hostveil이 쓸 수도 있습니다. 하지만 쓰는 것과 안전하다고 아는 것은 다릅니다. ProtectSystem=full/usr 아래에 쓰는 서비스를 망가뜨리고, PrivateTmp=yes/tmp로 파일을 주고받는 두 서비스를 망가뜨립니다. 어느 쪽도 다음 재시작 전까지는 드러나지 않는데, 자체 호스팅 환경에서 그 시점은 다음 재부팅입니다 — 그리고 돌아오지 못한 서비스가 하필 데이터를 쥐고 있던 쪽입니다. 그래서 수정 안내에 정확한 경로와 정확한 두 줄을 담고, 재시작해서 지켜볼 시점은 사용자가 고릅니다.

이 systemd가 구현하지 않은 속성은 아무 값도 보고되지 않으며, 값이 없다는 것은 보호가 꺼져 있다는 근거가 아닙니다. 그런 경우는 항목으로 만들지 않고 그대로 둡니다.

AI 에이전트 런타임 agent.

셀프호스트 AI 에이전트인 OpenClawHermes Agent는 네트워크 게이트웨이를 띄우고 API 키를 홈 디렉터리에 저장합니다. 설정이 잘못되면 최악의 경우 외부에서 그 게이트웨이에 접근해, 내 파일을 읽고 내 권한으로 명령을 실행하는 에이전트를 그대로 조종할 수 있습니다.

이 도메인은 해당 런타임이 실제로 설치되어 있지 않으면 아예 건너뜁니다. 에이전트를 쓴 적 없는 호스트는 이 항목으로 점수를 매기지 않습니다. Hostveil은 /etc/passwd에서 홈 디렉터리를 찾은 뒤 아래 표에 적힌 경로만 stat합니다. 홈 디렉터리 전체를 훑지 않습니다.

두 프로젝트 모두 자체 설정 점검 도구(openclaw security audit)를 제공하며, 설정 항목 자체는 그쪽이 훨씬 촘촘합니다. Hostveil이 더하는 것은 그 도구들이 볼 수 없는 부분입니다. 게이트웨이가 실제로 외부에서 닿는 주소에서 대기 중인지, 그 앞에 방화벽이 있는지, 자격 증명 파일을 같은 호스트의 다른 계정이 읽을 수 있는지입니다.

ID무엇을 잡아내는지심각도수정
agent.auth-disabled외부에 노출된 게이트웨이가 인증 없이 요청을 받음HIGHManual
agent.gateway-exposed게이트웨이가 네트워크에서 닿는 주소에 바인딩됨HIGHManual
agent.secret-exposed자격 증명 파일 또는 디렉터리를 소유자 외에도 읽을 수 있음HIGHAuto-fix
agent.exec-unrestricted에이전트가 승인 절차 없이 셸 명령을 실행할 수 있음HIGHReview
agent.elevated-enabled호스트 수준의 권한 상승 실행이 활성화됨HIGHAuto-fix
agent.sandbox-off에이전트 도구가 샌드박스 없이 호스트에서 직접 실행됨HIGHManual
agent.control-ui-insecure제어 UI의 인증 또는 기기 확인이 꺼져 있음HIGHAuto-fix
agent.config-perms설정·상태 경로가 업스트림 기본값보다 느슨함MEDIUMAuto-fix
agent.ssrf-private-network에이전트 브라우저가 사설망 주소에 접근할 수 있음MEDIUMAuto-fix

설정 관련 항목은 이제 대부분 고칠 수 있습니다. OpenClaw 설정은 JSON5라 사용자가 직접 쓴 주석이 들어 있는데, Hostveil은 값 하나에 해당하는 바이트만 정확히 바꾸고 나머지 파일은 건드리지 않습니다. 그리고 자기가 쓴 결과를 다시 읽어, 지정한 키 외에 달라진 것이 있으면 저장을 거부합니다. 키 하나를 바꾼 변경은 한 줄 diff로 남고, 주석은 쓴 자리에 그대로 있습니다.

세 항목은 여전히 Manual이며, 이유는 각각 다릅니다. agent.sandbox-offoff가 잘못됐다는 것은 알아도 샌드박스를 켜는 값이 무엇인지 모르며, 찍어서 넣은 값은 조치가 아닙니다. agent.auth-disabled는 키가 아예 없는 상태가 안전한 설정인데(OpenClaw는 이 값이 없으면 닫힌 쪽으로 동작합니다) Hostveil은 값을 바꿀 뿐 지우지는 않습니다. 그리고 노출된 게이트웨이를 다시 바인딩하면 원격으로 관리하던 에이전트와의 연결이 끊길 수 있습니다. Hermes 설정은 전부 손대지 않습니다. 설정 파일, .env, systemd 유닛, docker -e 플래그 중 어디서 온 값인지 판별할 수 없기 때문입니다.

Auto-fix되는 것은 권한 관련 두 항목뿐이며, 둘 다 접근 권한을 줄이기만 하는 chmod입니다. 체크포인트가 남으므로 hostveil rollback으로 이전 권한을 그대로 되돌릴 수 있습니다.

이미지 CVE cve. (선택)

Trivy가 설치되어 있으면, Hostveil은 Compose 서비스가 실행하는 이미지를 스캔하여 알려진 심각·높음·보통 등급의 취약점을 찾습니다. 발견 항목은 취약점 단위가 아니라 이미지 단위로 보고됩니다 — 이미지 하나가 CVE 수백 개를 싣고 있는 건 흔한 일이고, 그 전부가 같은 조치 하나를 공유하기 때문입니다.

스캔된 이미지마다 최대 2개의 발견 항목이 생기며, 실제로 할 수 있는 조치를 기준으로 나뉩니다:

ID탐지 내용심각도수정
cve.outdated-image업스트림에 이미 패치가 나온 취약점들해당 묶음 중 최악Review
cve.unpatched-image아직 아무도 패치를 내놓지 않은 취약점들해당 묶음 중 최악Unavailable

각 항목은 심각도별 개수와 가장 심각한 CVE 몇 개를 함께 보여줍니다. 전체 취약점 ID 목록은 항목의 evidence에 들어 있으며 hostveil scan --json으로 확인할 수 있습니다.

왜 CVE 하나당 항목 하나가 아닌가

Trivy는 패키지가 3.0.11-1~deb12u2 버전에 도달해야 한다고 알려줍니다. 이건 이미지 에 있는 OS 패키지 버전이지 이미지 태그가 아니고, 둘 사이를 잇는 매핑은 존재하지 않습니다 — 개별 CVE 수정을 만들려면 그 매핑을 지어내야 합니다. 애초에 CVE 하나에 대해 할 수 있는 일이 전체에 대해 할 일과 다르지 않으므로, 정직한 단위는 이미지입니다. 이미 사용자가 고른 태그를 다시 받는 데는 버전 매핑이 필요 없고, 약속하는 것도 "태그를 다시 해석한다"까지일 뿐 특정 CVE가 사라진다고 말하지 않습니다. 다이제스트로 고정된 이미지에는 수정이 붙지 않습니다 — 다이제스트를 pull해봐야 아무것도 바뀌지 않기 때문입니다.

"아직 패치가 없음"에 대한 솔직함

패치가 없는 취약점은 수정 가능한 쪽에 섞거나 버리지 않고 별도 항목으로 남깁니다. 이걸 뭉개버리면 취약점이 전부 미패치인 이미지는 리포트에서 통째로 사라지고, Hostveil은 그저 손쓸 방법이 없을 뿐인 호스트를 깨끗하다고 말하게 됩니다. 그래서 고칠 수 있는 척하는 대신 Unavailable이라는 정식 상태로 모델링합니다.

각 fix가 실제로 하는 일

위 표에서 Fix 열에 Auto-fix 또는 Review가 적힌 항목은 모두 internal/fix에 등록된 fix를 갖고 있고, 아래는 그 fix가 실제로 하는 일입니다. 현재 빌드에서 바로 생성하므로 코드가 하지 않는 일을 적어 둘 수 없습니다. Review 항목 중 대안이 둘 이상인 것은 제공하는 모든 대안을 나열하며, 첫 번째가 fix --all --review를 포함해 어디서나 먼저 선택되는 대안입니다. 항목별 설명 문구는 아직 한국어로 새로 쓰지 않아 원문 그대로 영어로 보여 줍니다.

컨테이너

compose.dr002 Auto-fix

Bind published port to 127.0.0.1

✓ Once recreated, the service stops being reachable from the network at all — only processes on this host can reach it, closing off whatever guessing or scanning the open port currently invites.

⚠ Once app is recreated, it is reachable only from this host. If you access it from another machine, use an SSH tunnel, VPN, or reverse proxy. This edits the file, not the running container: docker compose up -d app is what puts the change into force.

compose.ds006 Auto-fix

Add security_opt no-new-privileges:true

✓ A process that breaks into this container can no longer gain more privilege than it started with via a setuid binary — one of the more common container-escape stepping stones, closed.

⚠ This edits the file, not the running container: docker compose up -d app is what puts the change into force.

compose.ds008 Auto-fix

Set restart: unless-stopped

✓ The service comes back on its own after a crash, an OOM kill, or a host reboot instead of silently staying down until someone notices.

⚠ This edits the file, not the running container: docker compose up -d app is what puts the change into force.

compose.ds010 Review
  • Limit app to 1g — typical application container (recommended)

    ✓ Gives the container a memory ceiling, so a leak or a runaway process inside it gets OOM-killed and restarted instead of exhausting the host's memory and taking every other service down with it.

    ⚠ Too low a limit gets the container OOM-killed under load. Start generous, watch docker stats, and tighten later. This is a file edit, so it is fully reversible. This edits the file, not the running container: docker compose up -d app is what puts the change into force.

  • Limit app to 512m — small service (proxy, exporter, static site)

    ✓ Gives the container a memory ceiling, so a leak or a runaway process inside it gets OOM-killed and restarted instead of exhausting the host's memory and taking every other service down with it.

    ⚠ Too low a limit gets the container OOM-killed under load. Start generous, watch docker stats, and tighten later. This is a file edit, so it is fully reversible. This edits the file, not the running container: docker compose up -d app is what puts the change into force.

  • Limit app to 2g — database or JVM service

    ✓ Gives the container a memory ceiling, so a leak or a runaway process inside it gets OOM-killed and restarted instead of exhausting the host's memory and taking every other service down with it.

    ⚠ Too low a limit gets the container OOM-killed under load. Start generous, watch docker stats, and tighten later. This is a file edit, so it is fully reversible. This edits the file, not the running container: docker compose up -d app is what puts the change into force.

compose.ds018 Auto-fix

Bind published port to 127.0.0.1

✓ Once recreated, the service stops being reachable from the network at all — only processes on this host can reach it, closing off whatever guessing or scanning the open port currently invites.

⚠ Once app is recreated, it is reachable only from this host. If you access it from another machine, use an SSH tunnel, VPN, or reverse proxy. This edits the file, not the running container: docker compose up -d app is what puts the change into force.

compose.ds019 Auto-fix

Bind published port to 127.0.0.1

✓ Once recreated, the service stops being reachable from the network at all — only processes on this host can reach it, closing off whatever guessing or scanning the open port currently invites.

⚠ Once app is recreated, it is reachable only from this host. If you access it from another machine, use an SSH tunnel, VPN, or reverse proxy. This edits the file, not the running container: docker compose up -d app is what puts the change into force.

SSH

ssh.allowagentforwarding Auto-fix

Disable SSH agent forwarding

✓ Stops a compromised host from riding a forwarded SSH agent to authenticate elsewhere as the user, using credentials that were never actually stored here.

⚠ Existing workflows that hop through this host using a forwarded agent will stop working.

ssh.allowtcpforwarding Auto-fix

Disable SSH TCP forwarding

✓ Closes SSH's local, remote, and dynamic port forwarding — a common pivot and tunnel-out vector for anyone who gets a session on this host.

⚠ Existing local, remote, and dynamic SSH tunnels will stop working.

ssh.clientalivecountmax Auto-fix

Limit unanswered SSH keepalives

✓ Bounds how long a dead or hung session is held open, freeing the session slot instead of leaving it reserved indefinitely.

ssh.clientaliveinterval Auto-fix

Probe idle SSH clients every five minutes

✓ Detects and disconnects idle or dead clients on a schedule, instead of leaving a session open for as long as the network happens to keep the TCP connection alive.

⚠ Long-running idle sessions may be disconnected when their clients stop responding.

ssh.emptypasswords Auto-fix

Disable empty passwords

✓ Closes SSH login with a blank password — the one credential check an attacker never even has to guess.

ssh.fingerprinthash Auto-fix

Use SHA-256 host-key fingerprints

✓ Uses a stronger hash for the host-key fingerprint shown to users and tooling verifying this server's identity.

ssh.gatewayports Auto-fix

Bind remote-forwarded ports to loopback only

✓ Stops an ssh -R remote-forwarded port becoming reachable from other machines, closing a route around whatever this host's own firewall is doing.

⚠ If you rely on ssh -R tunnels being reachable from other machines, this closes them to loopback.

ssh.hostbasedauth Auto-fix

Disable host-based authentication

✓ Removes a trust-based login path that bypasses per-user keys entirely.

⚠ If any user logs in via host-based trust rather than their own key, this removes that path.

ssh.ignorerhosts Auto-fix

Ignore legacy rhosts trust files

✓ Ignores legacy .rhosts trust files, closing a decades-old authentication bypass that has no place on a hardened host.

ssh.kbdinteractive Auto-fix

Disable keyboard-interactive authentication

✓ Closes the same password-equivalent prompt path PasswordAuthentication closes, for the keyboard-interactive method some clients use instead.

⚠ PAM-based one-time codes (2FA prompts) also use this mechanism — keep it enabled if your logins go through one.

ssh.logingracetime Auto-fix

Lower LoginGraceTime to 60 seconds

✓ Shortens the window an unauthenticated connection can hold a login slot open, reducing exposure to connection-slot exhaustion.

ssh.loglevel Auto-fix

Log SSH key fingerprints

✓ Logs the key fingerprint used on every login, giving the operator forensic evidence of exactly which key authenticated — not just that a login succeeded.

ssh.maxauthtries Auto-fix

Lower MaxAuthTries to 3

✓ Throttles how many credential guesses a single SSH connection gets before it is dropped, slowing a brute-force attempt against this host specifically.

ssh.maxsessions Auto-fix

Limit multiplexed SSH sessions

✓ Caps multiplexed sessions per connection, limiting how much one compromised or stolen connection can do at once.

⚠ Clients opening more than two sessions over one SSH connection will be refused.

ssh.passwordauth Auto-fix

Disable password authentication

✓ Removes password-guessing as an SSH attack surface entirely — only a held key gets in from here on.

⚠ Make sure key-based login works BEFORE applying this, or you may lock yourself out of SSH.

ssh.permittunnel Auto-fix

Disable SSH tun/tap tunnels

✓ Disables SSH's own VPN-like tun/tap tunneling, removing a route around whatever network controls this host is behind.

⚠ Existing SSH VPN or tun/tap workflows will stop working.

ssh.permituserenvironment Auto-fix

Disable user-supplied SSH environments

✓ Stops a user's ~/.ssh/environment overriding the server process's own environment — a known way to hijack LD_PRELOAD or similar into a login session.

⚠ Login automation that relies on ~/.ssh/environment will stop receiving those variables.

ssh.printlastlog Auto-fix

Show the previous login

✓ Shows the previous login on every connect, so a user has a chance to notice a login they did not make.

ssh.rootlogin Review
  • Allow root only with an SSH key (prohibit-password) (recommended)

    ✓ Keeps key-based root access working while removing the one thing root logins are actually dangerous for: a guessable or stolen password.

    ⚠ Keep a working key for root, or use a sudo user instead.

  • Disable root login entirely (no)

    ✓ Removes direct root login over SSH entirely, so compromising this host's authentication means compromising a named, sudo-capable user instead of the one account every attacker already knows the name of.

    ⚠ Make sure another user can log in and use sudo before applying this.

ssh.strictmodes Auto-fix

Enforce SSH login-file ownership

✓ Refuses to honor login files (keys, config) with loose ownership or permissions, closing a local tampering vector.

ssh.tcpkeepalive Auto-fix

Disable TCP keepalives for SSH

✓ Stops relying on spoofable TCP keepalives for liveness, leaving ClientAliveInterval's encrypted check as the one source of truth for whether a session is still alive.

⚠ Use ClientAliveInterval for encrypted liveness checks; dead connections may otherwise take longer to disappear from intermediate network devices.

ssh.usedns Auto-fix

Disable SSH reverse-DNS lookups

✓ Removes a DNS lookup from the login path — both a minor timing/DoS surface and a spoofing vector, for a lookup the login never actually needed.

ssh.x11forwarding Auto-fix

Disable X11 forwarding

✓ Removes an X11 forwarding channel that malware or an attacker with a foothold could otherwise ride out of the session.

방화벽

firewall.default-allow Review

Allow SSH on 22/tcp, then set ufw's default inbound policy to deny

✓ Flips a firewall that is already running from allow-by-default to deny-by-default, closing every port nothing has explicitly opened on a host where a firewall was already assumed to be doing that job.

⚠ Every inbound port except 22/tcp stops being reachable the moment this runs, including anything a container publishes. There is no rollback checkpoint — exec fixes are not file-backed — so undoing it means ufw default allow incoming by hand.

firewall.inactive Review

Allow SSH on 22/tcp, then enable ufw with a default-deny inbound policy

✓ Turns on default-deny, so only the ports hostveil confirmed sshd is actually listening on stay reachable — everything else stops accepting connections from off this host.

⚠ Every inbound port except 22/tcp stops being reachable the moment this runs, including anything a container publishes. There is no rollback checkpoint — exec fixes are not file-backed — so undoing it means ufw disable by hand.

업데이트

updates.apt-listchanges Review

Install and enable auditd

✓ Installs a kernel-level audit trail of security-relevant actions — file access, privilege changes — that survives a process exiting or an application log being cleared.

⚠ Installs auditd and enables its service. There is no rollback checkpoint; undoing this means removing the package by hand.

updates.apt-show-versions Review

Install and enable auditd

✓ Installs a kernel-level audit trail of security-relevant actions — file access, privilege changes — that survives a process exiting or an application log being cleared.

⚠ Installs auditd and enables its service. There is no rollback checkpoint; undoing this means removing the package by hand.

updates.auditd Review

Install and enable auditd

✓ Installs a kernel-level audit trail of security-relevant actions — file access, privilege changes — that survives a process exiting or an application log being cleared.

⚠ Installs auditd and enables its service. There is no rollback checkpoint; undoing this means removing the package by hand.

updates.debsums Review

Install and enable auditd

✓ Installs a kernel-level audit trail of security-relevant actions — file access, privilege changes — that survives a process exiting or an application log being cleared.

⚠ Installs auditd and enables its service. There is no rollback checkpoint; undoing this means removing the package by hand.

updates.disabled Review

Install and enable dnf-automatic

✓ Once enabled, security patches for every installed package arrive on their own instead of depending on someone remembering to run apt/dnf.

⚠ Installs dnf-automatic and enables its timer. There is no rollback checkpoint; undoing this means removing the package by hand.

updates.fail2ban Review

Install and enable auditd

✓ Installs a kernel-level audit trail of security-relevant actions — file access, privilege changes — that survives a process exiting or an application log being cleared.

⚠ Installs auditd and enables its service. There is no rollback checkpoint; undoing this means removing the package by hand.

updates.pam-pwquality Review

Install and enable auditd

✓ Installs a kernel-level audit trail of security-relevant actions — file access, privilege changes — that survives a process exiting or an application log being cleared.

⚠ Installs auditd and enables its service. There is no rollback checkpoint; undoing this means removing the package by hand.

updates.pam-tmpdir Review

Install and enable auditd

✓ Installs a kernel-level audit trail of security-relevant actions — file access, privilege changes — that survives a process exiting or an application log being cleared.

⚠ Installs auditd and enables its service. There is no rollback checkpoint; undoing this means removing the package by hand.

updates.pending-security Review

Refresh apt metadata and fully upgrade installed packages

✓ Applies whatever security patches are already sitting unapplied right now — closes today's already-known gaps rather than waiting for the next automatic run.

⚠ Upgrading packages can restart the services they belong to, including sshd and the Docker daemon — and a container whose compose file has no restart policy in force stays down until docker compose up -d brings it back. A kernel or libc update may also need a reboot to actually take effect. There is no rollback checkpoint; undoing this means downgrading the affected packages by hand.

updates.process-accounting Review

Install and enable auditd

✓ Installs a kernel-level audit trail of security-relevant actions — file access, privilege changes — that survives a process exiting or an application log being cleared.

⚠ Installs auditd and enables its service. There is no rollback checkpoint; undoing this means removing the package by hand.

updates.rkhunter Review

Install and enable auditd

✓ Installs a kernel-level audit trail of security-relevant actions — file access, privilege changes — that survives a process exiting or an application log being cleared.

⚠ Installs auditd and enables its service. There is no rollback checkpoint; undoing this means removing the package by hand.

updates.sysstat Review

Install and enable auditd

✓ Installs a kernel-level audit trail of security-relevant actions — file access, privilege changes — that survives a process exiting or an application log being cleared.

⚠ Installs auditd and enables its service. There is no rollback checkpoint; undoing this means removing the package by hand.

updates.sysstat-disabled Auto-fix

Set ENABLED to true

✓ Turns on the data collection the sysstat package already has installed, so historical resource data actually starts accumulating instead of the package sitting present but silent.

CVE

cve.outdated-image Review
  • Pull the new image and recreate app now (recommended)

    ✓ Re-resolves the tag to whatever it currently points at, which is how the vendor ships fixes for a floating-tag image — it may include a patch for the CVE that triggered this finding, though hostveil cannot guarantee it does, since the same tag can also move for unrelated reasons. On a host where staying current matters more than staying unchanged, this is the fastest way to find out.

    ⚠ This recreates the container: the service goes down briefly and comes back on a different image. There is no rollback checkpoint: exec fixes are not file-backed, so Hostveil cannot undo this. Note the current image ID (docker compose -f /opt/example/docker-compose.yml images) before applying, so you can pin it back if the new one misbehaves. The new image may also carry unrelated upstream changes since you last pulled — on a host you are not trying to disturb, that is a real cost to weigh against a fix that is not guaranteed to land anyway.

  • Download the new image only; recreate app on your own schedule

    ✓ Downloads the new image without touching the running container, so there is a chance to inspect it — its digest, its changed packages, its release notes — before deciding whether pulling it in is worth the risk to a service that is stable today.

    ⚠ This changes nothing that is running: the image is downloaded but the container keeps using the old one until you recreate it, and the finding will still be reported until then. There is no rollback checkpoint: exec fixes are not file-backed, so Hostveil cannot undo this.

포트

ports.redis-bind Auto-fix

Bind Redis to loopback

✓ Redis ships with no authentication by default; binding it to loopback means the only way to reach it is already being on this host — closes it to the network entirely.

⚠ Restarting Redis can interrupt clients; validate this setting first.

ports.redis-disable-config Auto-fix

Disable the Redis CONFIG command

✓ Removes remote CONFIG SET/GET, closing the well-known Redis-to-remote-code-execution chain that writes a webshell or SSH key to disk via CONFIG SET dir/dbfilename.

⚠ Restarting Redis can interrupt clients; validate this setting first.

ports.redis-protected-mode Auto-fix

Enable Redis protected mode

✓ Turns on Redis's own built-in refusal to serve remote clients when no password is set — a second, independent barrier behind the bind address, so a firewall or compose slip elsewhere doesn't leave Redis exposed anyway.

⚠ Restarting Redis can interrupt clients; validate this setting first.

계정

accounts.core-dumps Auto-fix

Set a hard core-size limit of zero

✓ Stops a crashed privileged process writing a core file to disk — the easiest place a live secret in memory (a password, a key) ends up sitting in plaintext after a crash.

accounts.default-umask Auto-fix

Set the default umask to 027

✓ New files a user creates default to unreadable by other accounts on the box, closing the easiest way a stray world-readable file leaks something it shouldn't.

accounts.emptypassword Review

Lock alice with passwd -l

✓ Closes the one account whose password prompt currently succeeds for anyone who tries it, including a stranger who has never touched this host before.

⚠ If alice is your only way to reach this machine locally (console, su) and it has no other credential, locking it removes that access. Confirm you have another route in — an SSH key, a different sudo-capable account — before applying. The remote SSH path is already closed by ssh.emptypasswords; this only closes the local one.

accounts.local-banner Auto-fix

Append the standard access warning

✓ Puts a legal notice in front of anyone who logs into this host, local or remote, which several jurisdictions require before monitoring or logging a session can be used as evidence against an intruder.

⚠ Have counsel or the system owner approve login-banner wording for this organization.

accounts.password-aging Auto-fix

Harden password aging defaults

✓ Forces credentials to actually expire and blocks changing a password back to itself immediately, so a leaked password has a shelf life instead of being valid forever.

accounts.password-rounds Auto-fix

Harden password hashing rounds

✓ Raises the SHA-512 rounds used to hash new or changed passwords, slowing an offline dictionary or brute-force attempt against a stolen /etc/shadow by roughly the same factor.

accounts.remote-banner Auto-fix

Append the standard access warning

✓ Puts a legal notice in front of anyone who logs into this host, local or remote, which several jurisdictions require before monitoring or logging a session can be used as evidence against an intruder.

⚠ Have counsel or the system owner approve login-banner wording for this organization.

파일 권한

fileperms.at-allow Auto-fix

Tighten /etc/shadow to 0640

✓ Keeps control of who is allowed to schedule future one-off commands to root alone.

fileperms.at-deny Auto-fix

Tighten /etc/shadow to 0640

✓ Keeps control of who is blocked from scheduling future one-off commands to root alone.

fileperms.compiler Auto-fix

Tighten /etc/shadow to 0640

✓ Removes the system compiler from every local account's reach by default, taking away a convenient way to build exploit code from a local foothold.

fileperms.cron Auto-fix

Tighten /etc/shadow to 0640

✓ Stops a non-root account scheduling a command to run as root through a system cron definition.

fileperms.cron-allow Auto-fix

Tighten /etc/shadow to 0640

✓ Keeps control of who is allowed to schedule recurring cron jobs to root alone.

fileperms.cron-deny Auto-fix

Tighten /etc/shadow to 0640

✓ Keeps control of who is blocked from scheduling recurring cron jobs to root alone.

fileperms.crontab Auto-fix

Tighten /etc/shadow to 0640

✓ Closes an avoidable persistence path: the system crontab can run commands as root, and this keeps it restricted to what the scheduler itself needs.

fileperms.docker-config Auto-fix

Tighten /etc/shadow to 0640

✓ Stops other accounts on the host reading root's Docker registry credentials, which could otherwise pull private images or publish under this host's identity.

fileperms.group Auto-fix

Tighten /etc/shadow to 0640

✓ Stops a non-root account adding itself to a privileged group — sudo, docker — by editing the group database directly.

fileperms.grub-config Auto-fix

Tighten /etc/shadow to 0640

✓ Stops a non-root account reading or rewriting the bootloader configuration, which controls what runs before any of the host's normal access controls are active.

fileperms.grub2-config Auto-fix

Tighten /etc/shadow to 0640

✓ Same protection as the GRUB config, for the GRUB2 variant.

fileperms.gshadow Auto-fix

Tighten /etc/shadow to 0640

✓ Stops a non-root account reading group passwords and membership data that /etc/gshadow is supposed to keep to root and the shadow group alone.

fileperms.hostkey Auto-fix

Tighten /etc/shadow to 0640

✓ Stops a non-root account reading this server's SSH private host key — the one thing that would let them impersonate this host to anyone connecting.

fileperms.passwd Auto-fix

Tighten /etc/shadow to 0640

✓ Stops a non-root account editing the account database directly, closing a path to adding an account or handing itself UID 0.

fileperms.passwd-backup Auto-fix

Tighten /etc/shadow to 0640

✓ Stops a non-root account planting a change in the passwd backup that becomes a live credential the next time it is restored.

fileperms.shadow Auto-fix

Tighten /etc/shadow to 0640

✓ Stops any account other than root (and the shadow group) reading password hashes off disk, closing the offline-cracking path entirely.

fileperms.sshd-config Auto-fix

Tighten /etc/shadow to 0640

✓ Stops a non-root account weakening the SSH server's own config (re-enabling root login or password auth) to open a way back in.

fileperms.sudoers Auto-fix

Tighten /etc/shadow to 0640

✓ Stops a non-root account granting itself root by editing sudo's own policy file.

fileperms.sudoers-dropins Auto-fix

Tighten /etc/shadow to 0640

✓ Same protection as the main sudoers file, for a drop-in carrying the exact same authority.

fileperms.systemd-units Auto-fix

Tighten /etc/shadow to 0640

✓ Stops a non-root account replacing the command a systemd unit runs the next time it starts as root.

AI 에이전트

agent.config-perms Auto-fix

Tighten /etc/shadow to 0640

✓ Stops other local accounts on this host reading the agent runtime's own configuration, which can carry API keys and tool permissions.

agent.control-ui-insecure Auto-fix

Set kernel.kptr_restrict to 1

✓ Closes the agent's control UI to unauthenticated access, so reaching it requires being someone the operator actually let in.

agent.elevated-enabled Auto-fix

Set kernel.kptr_restrict to 1

✓ Stops the agent running with elevated or administrative privileges by default, so a prompt-injected or misused tool call can't act with more authority than the task ever needed.

agent.exec-unrestricted Auto-fix

Set kernel.kptr_restrict to 1

✓ Stops the agent executing shell commands at all without an explicit human decision each time — closes the most direct path from "the agent was tricked by its input" to "a shell command ran on this host."

agent.secret-exposed Auto-fix

Tighten /etc/shadow to 0640

✓ Stops other local accounts reading a secret file the agent runtime keeps in its state directory.

agent.ssrf-private-network Auto-fix

Set kernel.kptr_restrict to 1

✓ Stops the agent's own tool calls reaching internal or private network addresses on the operator's behalf — closes the classic SSRF pivot from "the agent fetched a URL" to "the agent probed the internal network."

커널

sysctl.accept-redirects Auto-fix

Persist it: write /etc/sysctl.d/60-hostveil-accept-redirects.conf

✓ Stops the kernel trusting ICMP redirects, closing an easy way for something on the local network to quietly re-route this host's outbound traffic through itself.

sysctl.accept-source-route Auto-fix

Persist it: write /etc/sysctl.d/60-hostveil-accept-source-route.conf

✓ Stops the kernel honoring source-routed packets, closing a way to make traffic take a path chosen by whoever sent it rather than by this network's own routing.

sysctl.aslr Auto-fix

Persist it: write /etc/sysctl.d/60-hostveil-aslr.conf

✓ Turns on full address space layout randomization, making a memory-corruption bug substantially harder to turn into a reliable exploit.

sysctl.bogus-icmp-errors Auto-fix

Persist it: write /etc/sysctl.d/60-hostveil-bogus-icmp-errors.conf

✓ Stops the kernel logging bogus ICMP error responses as if real, reducing log noise and a minor spoofing-detection blind spot.

sysctl.bootp-relay Review
  • Persist it: write /etc/sysctl.d/60-hostveil-bootp-relay.conf (recommended)

    ✓ Stops this host forwarding BOOTP/DHCP broadcasts between networks it has no business relaying for.

  • Apply it now: kernel.kptr_restrict

    ✓ Stops this host forwarding BOOTP/DHCP broadcasts between networks it has no business relaying for.

    ⚠ Changes the running kernel immediately and has no rollback checkpoint.

sysctl.bpf-jit-harden Auto-fix

Persist it: write /etc/sysctl.d/60-hostveil-bpf-jit-harden.conf

✓ Hardens the BPF JIT compiler's output against the spraying techniques used to turn a BPF bug into kernel code execution.

sysctl.core-uses-pid Auto-fix

Persist it: write /etc/sysctl.d/60-hostveil-core-uses-pid.conf

✓ Includes the process ID in a core dump's filename, so a crash does not silently overwrite an earlier one a security investigation might still need.

sysctl.ctrl-alt-del Auto-fix

Persist it: write /etc/sysctl.d/60-hostveil-ctrl-alt-del.conf

✓ Stops Ctrl-Alt-Del triggering an instant reboot, closing a trivial denial-of-service available to anyone with physical or console access.

sysctl.dmesg-restrict Auto-fix

Persist it: write /etc/sysctl.d/60-hostveil-dmesg-restrict.conf

✓ Stops unprivileged users reading the kernel log, which can leak addresses and details useful for a kernel-level exploit.

sysctl.icmp-broadcasts Auto-fix

Persist it: write /etc/sysctl.d/60-hostveil-icmp-broadcasts.conf

✓ Stops this host answering ICMP echo requests sent to a broadcast address, closing its use as an amplifier in a Smurf-style denial-of-service attack.

sysctl.ipv4-default-accept-redirects Auto-fix

Persist it: write /etc/sysctl.d/60-hostveil-ipv4-default-accept-redirects.conf

✓ Same protection as accept-redirects, as the IPv4 default template new interfaces inherit.

sysctl.ipv4-default-rp-filter Review
  • Persist it: write /etc/sysctl.d/60-hostveil-ipv4-default-rp-filter.conf (recommended)

    ✓ Same protection as rp-filter, as the default template.

  • Apply it now: kernel.kptr_restrict

    ✓ Same protection as rp-filter, as the default template.

    ⚠ Changes the running kernel immediately and has no rollback checkpoint.

sysctl.ipv6-accept-redirects-all Auto-fix

Persist it: write /etc/sysctl.d/60-hostveil-ipv6-accept-redirects-all.conf

✓ Same protection as accept-redirects, for IPv6, applied to every interface.

sysctl.ipv6-accept-redirects-default Auto-fix

Persist it: write /etc/sysctl.d/60-hostveil-ipv6-accept-redirects-default.conf

✓ Same protection as accept-redirects, for IPv6, as the default template new interfaces inherit.

sysctl.ipv6-accept-source-route-all Auto-fix

Persist it: write /etc/sysctl.d/60-hostveil-ipv6-accept-source-route-all.conf

✓ Same protection as accept-source-route, for IPv6, every interface.

sysctl.ipv6-accept-source-route-default Auto-fix

Persist it: write /etc/sysctl.d/60-hostveil-ipv6-accept-source-route-default.conf

✓ Same protection as accept-source-route, for IPv6, as the default template.

sysctl.ipv6-send-redirects Auto-fix

Persist it: write /etc/sysctl.d/60-hostveil-ipv6-send-redirects.conf

✓ Same protection as send-redirects, for IPv6.

sysctl.kptr-restrict Auto-fix

Persist it: write /etc/sysctl.d/60-hostveil-kptr-restrict.conf

✓ Hides kernel pointer addresses from unprivileged reads of /proc, taking away information an attacker would otherwise use to build a kernel exploit.

sysctl.log-martians Auto-fix

Persist it: write /etc/sysctl.d/60-hostveil-log-martians.conf

✓ Logs packets carrying an impossible source or destination address, giving visibility into spoofing or misconfiguration attempts that would otherwise pass silently.

sysctl.module-dccp Auto-fix

Persist a modprobe install block

✓ Removes the kernel's ability to load the DCCP module at all — a rarely-used protocol on an ordinary self-hosted server, and one that has shown up repeatedly as a local attack vector when left loadable.

⚠ Confirm this host does not use dccp before applying.

sysctl.module-rds Auto-fix

Persist a modprobe install block

✓ Removes the kernel's ability to load the RDS module at all — the same reasoning as DCCP: rarely used here, and a repeat source of local kernel bugs when loadable.

⚠ Confirm this host does not use dccp before applying.

sysctl.module-sctp Auto-fix

Persist a modprobe install block

✓ Removes the kernel's ability to load the SCTP module at all — the same reasoning as DCCP: rarely used here, and a repeat source of local kernel bugs when loadable.

⚠ Confirm this host does not use dccp before applying.

sysctl.module-tipc Auto-fix

Persist a modprobe install block

✓ Removes the kernel's ability to load the TIPC module at all — the same reasoning as DCCP: rarely used here, and a repeat source of local kernel bugs when loadable.

⚠ Confirm this host does not use dccp before applying.

sysctl.module-usbstorage Auto-fix

Persist a modprobe install block

✓ Removes the kernel's ability to load the USB mass-storage driver, closing a common way to pull data off or introduce malware onto a physically-accessible server via a USB drive.

⚠ Confirm this host does not use dccp before applying.

sysctl.multicast-forwarding Review
  • Persist it: write /etc/sysctl.d/60-hostveil-multicast-forwarding.conf (recommended)

    ✓ Stops this host relaying multicast traffic between interfaces it has no business routing for.

  • Apply it now: kernel.kptr_restrict

    ✓ Stops this host relaying multicast traffic between interfaces it has no business routing for.

    ⚠ Changes the running kernel immediately and has no rollback checkpoint.

sysctl.perf-events Auto-fix

Persist it: write /etc/sysctl.d/60-hostveil-perf-events.conf

✓ Restricts performance-counter access so an unprivileged user can't use it to leak kernel addresses or run a side-channel attack against other processes.

sysctl.protected-fifos Auto-fix

Persist it: write /etc/sysctl.d/60-hostveil-protected-fifos.conf

✓ Stops a process writing into a FIFO it does not own in a world-writable sticky directory — the same class protected-links closes for symlinks.

Persist it: write /etc/sysctl.d/60-hostveil-protected-links.conf

✓ Stops a process following a symlink it does not own in a world-writable sticky directory, closing a classic local privilege-escalation and file-clobbering trick.

sysctl.protected-regular Auto-fix

Persist it: write /etc/sysctl.d/60-hostveil-protected-regular.conf

✓ Stops a process writing into a regular file it does not own in a world-writable sticky directory, for the same reason.

sysctl.proxy-arp-all Review
  • Persist it: write /etc/sysctl.d/60-hostveil-proxy-arp-all.conf (recommended)

    ✓ Stops the kernel answering ARP on behalf of other hosts across interfaces, closing an easy way to bridge or spoof between network segments this host sits on.

  • Apply it now: kernel.kptr_restrict

    ✓ Stops the kernel answering ARP on behalf of other hosts across interfaces, closing an easy way to bridge or spoof between network segments this host sits on.

    ⚠ Changes the running kernel immediately and has no rollback checkpoint.

sysctl.proxy-arp-default Review
  • Persist it: write /etc/sysctl.d/60-hostveil-proxy-arp-default.conf (recommended)

    ✓ Same protection as proxy-arp-all, as the default template.

  • Apply it now: kernel.kptr_restrict

    ✓ Same protection as proxy-arp-all, as the default template.

    ⚠ Changes the running kernel immediately and has no rollback checkpoint.

sysctl.ptrace-scope Auto-fix

Persist it: write /etc/sysctl.d/60-hostveil-ptrace-scope.conf

✓ Restricts ptrace to a process's own children, closing a well-known technique for one compromised process to read another's memory or inject code into it.

sysctl.rp-filter Review
  • Persist it: write /etc/sysctl.d/60-hostveil-rp-filter.conf (recommended)

    ✓ Drops packets whose source address could not have arrived on the interface they came in on, closing a common IP-spoofing technique.

  • Apply it now: kernel.kptr_restrict

    ✓ Drops packets whose source address could not have arrived on the interface they came in on, closing a common IP-spoofing technique.

    ⚠ Changes the running kernel immediately and has no rollback checkpoint.

sysctl.send-redirects Review
  • Persist it: write /etc/sysctl.d/60-hostveil-send-redirects.conf (recommended)

    ✓ Stops this host sending ICMP redirects itself, which it should not be doing unless it is actually a router — removes a route-injection tool a compromised host could use against its neighbors.

  • Apply it now: kernel.kptr_restrict

    ✓ Stops this host sending ICMP redirects itself, which it should not be doing unless it is actually a router — removes a route-injection tool a compromised host could use against its neighbors.

    ⚠ Changes the running kernel immediately and has no rollback checkpoint.

sysctl.suid-dumpable Auto-fix

Persist it: write /etc/sysctl.d/60-hostveil-suid-dumpable.conf

✓ Stops a crashed setuid process writing a core dump that can contain whatever secrets were in its memory at the moment it crashed.

sysctl.syncookies Auto-fix

Persist it: write /etc/sysctl.d/60-hostveil-syncookies.conf

✓ Enables SYN cookies, so a SYN-flood cannot exhaust the connection queue and deny the host's legitimate services.

sysctl.sysrq Review
  • Persist it: write /etc/sysctl.d/60-hostveil-sysrq.conf (recommended)

    ✓ Restricts the SysRq magic-key functions to the safe subset, closing the ones that can dump memory or force a reboot from an unprivileged local session.

  • Apply it now: kernel.kptr_restrict

    ✓ Restricts the SysRq magic-key functions to the safe subset, closing the ones that can dump memory or force a reboot from an unprivileged local session.

    ⚠ Changes the running kernel immediately and has no rollback checkpoint.

sysctl.tcp-rfc1337 Auto-fix

Persist it: write /etc/sysctl.d/60-hostveil-tcp-rfc1337.conf

✓ Protects against TIME-WAIT assassination, so an off-path attacker cannot reset a connection sitting in TIME_WAIT by spoofing packets at it.

sysctl.tty-ldisc-autoload Auto-fix

Persist it: write /etc/sysctl.d/60-hostveil-tty-ldisc-autoload.conf

✓ Stops the kernel auto-loading obscure TTY line disciplines on request, closing a local privilege-escalation path specific line-discipline modules have shipped in the past.

sysctl.unprivileged-bpf Auto-fix

Persist it: write /etc/sysctl.d/60-hostveil-unprivileged-bpf.conf

✓ Removes unprivileged users' ability to load BPF programs into the kernel, closing a well-documented local privilege-escalation and kernel-exploit vector.

서비스

systemd.lock-personality Auto-fix

Set LockPersonality=yes for app

✓ Stops the unit switching to an alternate execution personality, closing an old technique for bypassing ASLR.

⚠ Needing an alternate execution personality is rare outside emulation and compatibility layers. An ordinary service is unaffected; one that needs one fails at the next restart.

systemd.no-new-privileges Auto-fix

Set NoNewPrivileges=yes for app

✓ Closes the setuid escalation path out of this unit — a compromised process cannot gain more privilege than the service already had, the same protection compose.ds006 gives a container.

⚠ Closes the setuid path out of this service. A service that deliberately escalates — anything calling a setuid helper — stops working, and it stops at the next restart rather than now.

systemd.protect-clock Auto-fix

Set ProtectClock=yes for app

✓ Stops the unit changing the system or hardware clock, closing off a way a compromised service could hide its tracks by tampering with timestamps or break time-based authentication.

⚠ Only time-sync daemons (chronyd, ntpd, systemd-timesyncd) legitimately need to change the system or hardware clock. A service that is not one of those stops being able to, at the next restart.

systemd.protect-kernel-logs Auto-fix

Set ProtectKernelLogs=yes for app

✓ Stops the unit reading /dev/kmsg directly, closing an information-leak path that can hand an attacker kernel addresses useful for a further exploit.

⚠ Only a service that reads kernel logs directly — a diagnostics tool, an agent reading /dev/kmsg — needs this off. Most services never touch it.

systemd.protect-kernel-modules Auto-fix

Set ProtectKernelModules=yes for app

✓ Stops the unit loading or removing kernel modules itself, closing a direct path to running arbitrary code in kernel space from what should be an ordinary service.

⚠ Only a service that loads or removes kernel modules itself at runtime — rather than modules already loaded at boot — needs this off.

systemd.restrict-suid-sgid Auto-fix

Set RestrictSUIDSGID=yes for app

✓ Stops the unit creating new setuid or setgid files, closing a persistence and privilege-escalation path a compromised service could otherwise leave behind for later.

⚠ Only a service that itself creates setuid or setgid files — a package manager, an installer — needs this off. An ordinary network daemon does not create such files and is unaffected.

프록시

proxy.no-scan-jail Review
  • Enable it (fail2ban's own default: 10 minute ban) (recommended)

    ✓ Once fail2ban reloads, an IP that repeatedly requests nonexistent or known-vulnerable paths gets banned automatically — no more finding it and banning it yourself.

    ⚠ Takes effect on the next fail2ban-client reload, not immediately. A shared or NAT'd IP address could ban more than the one visitor responsible for it. This only ever affects HTTP(S) traffic — never SSH, and never the operator's own access to the host.

  • Enable it with a longer ban (1 week)

    ✓ Once fail2ban reloads, an IP that repeatedly requests nonexistent or known-vulnerable paths gets banned automatically — no more finding it and banning it yourself.

    ⚠ Takes effect on the next fail2ban-client reload, not immediately. A shared or NAT'd IP address could ban more than the one visitor responsible for it. This only ever affects HTTP(S) traffic — never SSH, and never the operator's own access to the host.