환경 변수
Hostveil에는 설정 파일이 없어서, 환경 변수가 곧 설정의 전부입니다. 이 페이지는 Hostveil이 읽는 변수, 자신이 실행하는 명령에 설정해 주는 변수, 그리고 물려받아 화면을 바꾸는 변수를 하나도 빼놓지 않고 기본값·허용 값과 함께 정리합니다.
Hostveil 자체 설정
Hostveil이 직접 읽는 변수들입니다. 플래그로도 줄 수 있는 것이 있고, 변수로만 줄 수 있는 것도 있습니다. 셸 프로필이나 systemd 유닛에 한 번 넣어 두거나, 명령 앞에 그때그때 붙여 쓰면 됩니다.
| 변수 | 기본값 | 값 | 동작 |
|---|---|---|---|
HOSTVEIL_DEBUG | 미설정 — 추적하지 않음 | 비어 있지 않은 아무 문자열 | 실행한 명령마다 한 줄씩 stderr에 씁니다. 따옴표로 묶인 인자 목록, 밀리초 단위 소요 시간, 실패 여부 — 도메인이 아예 실행될지를 결정하는 바이너리 탐색까지 포함됩니다. 명령의 출력은 의도적으로 절대 기록하지 않습니다. docker inspect는 모든 컨테이너의 환경 변수를 그대로 보여 주므로, 버그 리포트에 붙여 넣는 추적 로그가 자격 증명 유출이 되어서는 안 되기 때문입니다. 그 외에는 아무것도 달라지지 않으며, 설정하지 않았을 때 Hostveil은 예전과 똑같은 경로를 탑니다. |
HOSTVEIL_ASSUME_HOST | 미설정 — 컨테이너를 감지하고 그렇다고 말합니다 | 비어 있지 않은 아무 문자열 | 명백히 컨테이너인 파일시스템도 호스트로 간주해 점검합니다. 설정하지 않으면 컨테이너 안에서 방화벽 영역은 건너뜀이 됩니다(컨테이너에는 호스트 방화벽이 없고 앞으로도 없습니다. 그 포트가 응답할지 정하는 패킷 필터는 호스트의 것입니다). 커널 하드닝 항목은 Manual이 됩니다(값이 호스트 커널의 것이고, 안에서 쓴 drop-in으로는 바꿀 수 없습니다). 그 컨테이너를 곧 점검 대상 호스트로 쓸 때 설정하세요. 배포 전 골든 이미지를 점검하거나, 이 프로젝트의 종단 간 작업처럼 버릴 수 있는 컨테이너를 루트 파일시스템으로 쓸 때입니다. |
HOSTVEIL_NO_SUDO | 미설정 — 자동 권한 상승이 켜짐 | 비어 있지 않은 아무 문자열 | sudo로 자신을 다시 실행하지 않습니다. root가 필요한 영역 — sshd_config, /etc/shadow, 방화벽 상태 — 은 오류가 아니라 이유와 함께 건너뜀으로 보고되고, 점수는 실제로 실행된 영역만으로 재정규화됩니다. 탐지나 수정 로직은 전혀 바뀌지 않습니다. 억제하는 것은 재실행뿐이므로, 이미 root로 돌고 있는 Hostveil에는 아무 영향이 없습니다. |
HOSTVEIL_THEME | 기억된 값, 없으면 onedark | onedark, gruvbox, nord, catppuccin, tokyonight | TUI의 색상 팔레트이자 대시보드가 처음 열릴 때의 팔레트입니다. 우선순위는 --theme > 이 변수 > 상태 디렉터리에 기억된 값 > 기본값입니다. 이름은 공백 처리 없이 정확히, 대소문자를 구분해서 비교하므로 HOSTVEIL_THEME=Nord는 아무 일도 하지 않고 아무 말도 하지 않습니다 — 여기서 알 수 없는 값은 조용히 무시되고, 오류가 되는 것은 잘못된 --theme뿐입니다. |
HOSTVEIL_GLYPHS | 기억된 값, 없으면 plain | plain, nerd | TUI와 hostveil scan이 상태 표시에 쓰는 기호 세트입니다. 우선순위는 --glyphs > 이 변수 > 기억된 값 > plain입니다. 앞뒤 공백은 잘라내지만 이름은 대소문자를 구분하며, 알 수 없는 값은 조용히 기본값으로 돌아갑니다. nerd는 패치된 Nerd Font가 설치돼 있어야 합니다. 터미널에 어떤 폰트를 쓰는지 물어볼 방법이 없고, 없는 글리프도 있는 글리프와 똑같이 한 칸을 차지하기 때문에 자동 감지가 아니라 명시적 선택입니다. |
HOSTVEIL_LAYOUT | 기억된 값, 없으면 console | console, split, triage, railverdict, lanes, inline | TUI의 화면 배치이자 대시보드가 처음 표시될 배치입니다. 우선순위는 --layout > 이 변수 > 상태 디렉터리에 기억된 값 > console입니다. 앞뒤 공백은 잘라내지만 이름은 대소문자를 구분하며 정확히 일치해야 합니다. 알 수 없는 값은 조용히 무시되고, 오류가 되는 것은 잘못된 --layout뿐입니다. 브라우저에서는 대시보드의 선택기로 고른 값이 그 브라우저에 저장되어 이 변수를 이깁니다. |
HOSTVEIL_AI_PROVIDER | ollama | ollama, anthropic, openai | explain --ai와 AI 버튼이 쓸 AI 백엔드를 고릅니다. ollama는 로컬이라 이 값을 일부러 바꾸지 않는 한 아무것도 호스트를 떠나지 않습니다. anthropic은 Claude API와, openai는 OpenAI의 chat-completions 형식을 쓰는 아무 벤더(OpenAI 자신, OpenRouter, Groq, Together, 다른 기계의 자체 호스팅 서버 등)와 통신합니다. Hostveil을 돌리는 기계가 로컬 모델을 감당하기엔 약할 때 유용합니다. 알 수 없는 값은 조용히 무시되지 않습니다 — explain --ai는 일반적인 “닿는 AI 프로바이더가 없음” 대신 그 값을 그대로 지목해 알려 줍니다. AI 설명을 참고하세요. |
HOSTVEIL_OLLAMA_HOST | http://127.0.0.1:11434 | 아무 베이스 URL | provider=ollama: 로컬 서버가 듣고 있는 주소입니다. 접두사로만 쓰여 /api/version과 /api/generate가 뒤에 붙습니다. 끝의 슬래시는 잘라내며, 그 밖에는 값을 파싱하거나 검증하지 않습니다. 빈 값은 미설정으로 취급합니다. 조언 전용이므로 explain --ai와 AI 버튼에만 영향을 주고, 서버가 닿지 않아도 점수·발견 항목·수정은 어느 것도 영향받지 않습니다. |
HOSTVEIL_OLLAMA_MODEL | llama3.2 | Ollama가 받아들이는 아무 모델 태그 | provider=ollama: 어느 모델에게 물을지 정합니다. 로컬에서 검증하지 않으므로, 받아 두지 않은 모델이면 Ollama가 그 이름을 담은 오류로 답합니다. 빈 값은 미설정으로 취급합니다. 위의 호스트와 마찬가지로 조언 전용입니다. |
ANTHROPIC_API_KEY | 미설정 | Claude API 키 | provider=anthropic: 인증 정보입니다. 다른 Anthropic 도구들과 똑같은 변수를 읽으므로, 다른 도구용으로 이미 내보낸 키가 여기서도 그대로 동작합니다. 설정하지 않으면 요청을 보내는 대신 Available이 프로바이더에 닿을 수 없다고 답합니다. 위 Ollama 변수들과 같은 뜻에서 조언 전용입니다. |
HOSTVEIL_ANTHROPIC_MODEL | claude-opus-5 | 아무 Claude 모델 ID | provider=anthropic: 어느 모델에게 물을지 정합니다. 로컬에서 검증하지 않으므로, 쓸 수 없는 모델이면 API가 그 이름을 담은 오류로 답합니다. |
HOSTVEIL_OPENAI_BASE_URL | https://api.openai.com/v1 | 아무 베이스 URL | provider=openai: API 주소입니다. 접두사로만 쓰여 /chat/completions와 /models가 뒤에 붙습니다. 끝의 슬래시는 잘라냅니다. OpenRouter, Groq, Together, 또는 자체 호스팅한 OpenAI 호환 서버를 가리키면 그 벤더를 대신 쓸 수 있습니다. |
HOSTVEIL_OPENAI_API_KEY | 미설정 | 설정한 벤더용 API 키 | provider=openai: 인증 정보입니다. 설정하지 않으면 요청 자체에서 아예 빠집니다. 키를 요구하지 않는 로컬 서버를 위한 동작입니다. |
HOSTVEIL_OPENAI_MODEL | 미설정 | 벤더가 받아들이는 아무 모델 이름 | provider=openai: 어느 모델에게 물을지 정합니다. Ollama의 모델과 달리 이 값이 다루는 모든 벤더에 공통으로 존재하는 이름은 없으므로, 비워 두면 조용한 기본값이 아니라 explain --ai가 그 사실을 그대로 지목하는 설정 오류가 됩니다. |
HOSTVEIL_ELEVATED | 미설정 — Hostveil은 이 값을 설정하지 않음 | 비어 있지 않은 아무 문자열 | 권한 상승이 이미 끝났다고 알려서 Hostveil이 sudo를 부르지 않게 합니다. Hostveil 대신 권한을 올려 주는 래퍼 스크립트를 위한 변수입니다. 예전에는 Hostveil이 자식 프로세스에 이 값을 직접 넣고 다시 읽었는데, 그 방식은 애초에 동작할 수 없었습니다 — 아래를 보세요. |
HOSTVEIL_NO_UPDATE_CHECK | unset | any value | 아무 값이나 설정하면 Hostveil이 스스로 GitHub에 연결하지 않습니다. 설정하지 않으면 스캔이 “새 릴리스가 있는가”의 캐시된 답을 하루에 최대 한 번 백그라운드로 갱신하며, 스캔이 그것을 기다리는 일은 없습니다. 결과는 사람이 읽는 보고서 아래 한 줄로만 나오고 --json, SARIF, 파일로 쓴 보고서에는 실리지 않습니다. hostveil update는 직접 실행하면 그대로 동작합니다. |
SSH_CONNECTION | set by sshd | — | 직접 설정하는 값이 아니라 sshd가 넣어 줍니다. hostveil serve는 이 값으로 원격 접속 상태임을 알아채고, 사용자 본인 기기에서 실행할 ssh -L 명령을 함께 출력합니다. 대시보드는 루프백에서만 듣기 때문에 출력되는 URL은 서버 쪽 주소입니다. 명령에 들어가는 호스트는 클라이언트가 실제로 닿은 주소이며 이 변수의 세 번째 필드에서 가져옵니다. |
USER, LOGNAME | — | — | 위 ssh -L 명령의 로그인 이름을 정할 때 마지막 대안으로만 읽습니다. serve는 권한을 승격하므로 그 아래에서 이 값들은 root가 되고, 그래서 SUDO_USER를 먼저 봅니다. |
플래그처럼 생긴 세 개 — HOSTVEIL_DEBUG, HOSTVEIL_NO_SUDO, HOSTVEIL_ELEVATED — 는 값이 아니라 비어 있지 않은지만 검사합니다. 그래서 HOSTVEIL_DEBUG=false는 추적을 켜고 HOSTVEIL_NO_SUDO=0은 sudo를 끕니다. 어디서나 =1로 적는 이유는 그것만이 동작과 읽히는 대로 일치하는 값이기 때문입니다. 끄려면 변수를 해제하거나 빈 문자열로 두세요.
sudo 재실행을 넘어 살아남는 것
root 권한이 필요한 명령을 일반 사용자로 실행하면 Hostveil은 sudo 아래에서 자신을 다시 실행합니다. 그런데 주요 배포판이 기본으로 켜 두는 sudo의 env_reset은 env_keep에 적힌 것 — TERM, LANG, LC_*와 짧은 목록 정도 — 만 남깁니다. 애플리케이션이 자기 이름으로 만든 변수는 절대 남지 않습니다. 그래서 Hostveil은 여러분이 설정한 값을 sudo의 명령줄에 대입문 형태로 직접 넘겨줍니다. 그래야 자식 프로세스에 도착하기 때문입니다.
넘어가는 것은 HOSTVEIL_DEBUG, HOSTVEIL_THEME, HOSTVEIL_GLYPHS, HOSTVEIL_LAYOUT, HOSTVEIL_AI_PROVIDER, HOSTVEIL_OLLAMA_HOST, HOSTVEIL_OLLAMA_MODEL, ANTHROPIC_API_KEY, HOSTVEIL_ANTHROPIC_MODEL, HOSTVEIL_OPENAI_BASE_URL, HOSTVEIL_OPENAI_API_KEY, HOSTVEIL_OPENAI_MODEL, NO_COLOR입니다 — 각각 실제로 설정되어 있을 때만 넘어가므로, 아무것도 설정하지 않은 호스트는 예전과 똑같은 명령을 만듭니다. explain도 root 권한이 필요한 다른 명령들과 똑같이 권한을 올리므로, 재실행을 넘기지 못한 인증 정보는 프로바이더에 닿지 않을 때와 똑같이 조용히 실패합니다. 여기서 넘겨주는 것이 막는 것이 바로 그 실패입니다. HOSTVEIL_NO_SUDO는 재실행 지점에 도달했다는 사실 자체가 설정되지 않았다는 뜻이라서 넘기지 않고, HOSTVEIL_ELEVATED는 아래에 적은 이유로 넘기지 않습니다.
HOSTVEIL_DEBUG=1 hostveil scan
실행: sudo HOSTVEIL_DEBUG=1 /usr/local/bin/hostveil scan이 장치가 필요했던 이유는, 오랫동안 이 변수들이 하나도 넘어가지 않았기 때문입니다. HOSTVEIL_DEBUG=1 hostveil scan은 hostveil help와 README, 그리고 문제 해결 페이지에 버그 리포트에 첨부할 바로 그 한 줄로 적혀 있는데, 평범한 비-root 호스트에서는 평범한 스캔 결과만 내놓았습니다. 추적 로그도, 오류도, 변수가 버려졌다는 낌새조차 없었습니다. 테마나 기호 세트를 설정해도 똑같이 조용히 무시됐습니다. CI가 이것을 못 잡은 이유는, 종단 간 작업이 root로 HOSTVEIL_NO_SUDO=1과 함께 돌기 때문입니다 — 재실행을 건너뛰는 두 갈래 모두에 해당합니다. 위 목록에 없는 변수는 여전히 env_reset에 버려지므로, sudo -E를 쓰거나 env_keep에 항목을 추가하세요.
예전에는 HOSTVEIL_ELEVATED=1을 sudo 자식 프로세스에 넣고 거기서 다시 읽어 무한 반복을 막는 장치로 썼는데, env_reset이 매번 그 값을 지웠기 때문에 장치가 작동할 수 없었습니다. sudoers가 runas_default를 root가 아닌 계정으로 잡아 둔 호스트에서는 sudo가 권한 없는 프로세스를 돌려주고 그 프로세스가 다시 sudo를 부르는 일이 끝없이 반복되면서, 매번 비밀번호를 물었습니다. 실제로 작동하는 장치는 SUDO_USER입니다. 이 값은 전달되는 것이 아니라 sudo가 대상 환경에 직접 넣어 주기 때문입니다.
Hostveil이 환경에서 존중하는 것
일부는 Hostveil이 직접 읽고, 나머지는 TUI가 올라타 있는 터미널 라이브러리를 통해 들어옵니다. 이 중 어느 것도 발견 항목이나 점수, 수정을 바꾸지 않습니다 — 다만 몇 개는 보이는 화면을 바꾸고, 둘은 파일이 어디에 놓이는지를 바꿉니다.
색상과 터미널
| 변수 | 기본값 | 값 | 동작 |
|---|---|---|---|
NO_COLOR | 미설정 — 터미널에서는 색상 출력 | 아래 안내를 보세요 — 두 구현이 서로 다릅니다 | ANSI 색상을 억제합니다. hostveil scan에서는 Hostveil 자신의 판정이고 색상을 결정하는 유일한 곳이며, TUI에서는 터미널 라이브러리를 통해 들어옵니다. 대시보드는 HTML이라 이 변수를 아예 보지 않습니다. |
TERM | 물려받은 값. 미설정은 dumb로 취급 | 아무 terminfo 항목 이름 | TUI의 색상 깊이와 키 해석을 결정합니다. dumb이거나 비어 있으면 색 없이 그리고, *-256color는 256색, *-color는 16색입니다. hostveil scan은 이 값을 전혀 보지 않으므로, TERM=dumb에서도 scan은 색을 내보내고 TUI는 내보내지 않습니다. sudo가 남겨 주는 몇 안 되는 변수라 자동 권한 상승을 넘어서도 살아남습니다. |
COLORTERM | 미설정 | truecolor, 24bit, yes, true (대소문자 무시) | TERM에 대한 terminfo가 뭐라고 하든 TUI를 24비트 색상으로 올립니다. SSH는 이 변수를 전달하지 않으므로 원격 세션은 대개 256색이며, 팔레트는 24비트 16진수로 적혀 있고 프로파일에 맞춰 낮춰집니다. 모든 테마는 256색 터미널이 실제로 그리는 결과를 기준으로 검증됩니다 — 심각도는 회색으로 무너지지 않고 색으로 남으며, 서로 다른 역할이 같은 팔레트 항목으로 겹치지 않습니다. 따라서 256색은 더 거친 테마일 뿐 오해를 부르는 테마는 아닙니다. 그래도 설정하면 더 나은 그림을 봅니다. |
CLICOLOR | 미설정 | 1, true, t (그리고 그 반대 값들) | clicolors 관례입니다. TERM이 dumb이 아닌 터미널에서 TUI의 색상 하한을 16색으로 올립니다. NO_COLOR가 이 값을 이깁니다. TUI에만 적용됩니다. |
CLICOLOR_FORCE | 미설정 | 1, true, t (그리고 그 반대 값들) | 출력이 터미널이 아니어도 TUI가 색을 내보내게 하고 TERM=dumb도 무시합니다. 그래도 NO_COLOR에는 집니다. hostveil scan은 이 값을 존중하지 않습니다 — 판정 조건에 실제 터미널이 들어 있어서, scan의 색상 출력을 파이프로 넘기는 방법은 지원되지 않습니다. |
TTY_FORCE | 미설정 | 1, true, t (그리고 그 반대 값들) | 파일 디스크립터를 확인하지 않고 출력을 터미널로 간주하게 합니다. |
TMUX | tmux 세션 안에서 tmux가 설정 | 존재 여부만 봄. 값은 파싱하지 않음 | TUI에게 tmux 안에 있다고 알려 주고, 그러면 TUI는 tmux info를 실행해 256색과 24비트 중 어느 쪽인지 판단합니다. 이 호출은 Hostveil 프로세스가 실행하는 명령 중 유일하게 자체 명령 계층을 거치지 않습니다. 그래서 HOSTVEIL_DEBUG로 추적되지 않고, Hostveil의 명령 타임아웃이 걸리지 않으며, 로케일 고정도 적용되지 않습니다. |
TERM_PROGRAM | 터미널 에뮬레이터가 설정 | 에뮬레이터 식별자 (iTerm.app, vscode …) | TUI가 시작할 때 어떤 터미널 특성을 적용할지 판단하는 데 씁니다. |
SSH_TTY | 원격 로그인 시 sshd가 설정 | 존재 여부만 봄. 경로 값은 쓰지 않음 | TUI에게 SSH 너머에서 돌고 있다고 알려 주며, 터미널이 무엇을 지원한다고 가정할지가 달라집니다. 셀프호스팅 서버는 대개 이렇게 접속하므로 여기서 의미가 있습니다. |
WT_SESSION | Windows Terminal이 설정 | 존재 여부만 봄 | TUI 시작 시 읽히지만 Hostveil이 배포하는 플랫폼에서는 아무 효과가 없습니다. 목록을 빠짐없이 두기 위해 적어 둡니다. |
GOOGLE_CLOUD_SHELL | Google Cloud Shell이 설정 | 1, true, t | COLORTERM이 없어도 TUI를 24비트 색으로 올립니다. 같은 함수에서 WT_SESSION 네 줄 아래에 있고, 적어 두는 이유도 같습니다. |
TERMINFO | 미설정 — 시스템 데이터베이스 | terminfo 데이터베이스가 있는 디렉터리 | TUI가 TERM의 능력을 찾아보는 곳입니다. 트루컬러 질의의 답과 TUI가 키를 해석하는 표를 여기서 정하므로, 실제 터미널을 설명하지 않는 데이터베이스를 가리키면 색이 틀리거나 키가 먹지 않습니다. hostveil scan은 terminfo 항목을 전혀 읽지 않습니다. |
TERMINFO_DIRS | 미설정 | 콜론으로 구분된 디렉터리들 | 같은 조회이고, TERMINFO 다음으로 찾습니다. |
GOBIN | unset | a path | hostveil update와 hostveil uninstall에서만 읽습니다. go install이 놓은 바이너리인지 알아내기 위해서이고, 그런 바이너리는 배포본으로 교체하지 않고 소스에서 다시 빌드합니다. Hostveil이 설정하지는 않습니다. |
GOPATH | ~/go | a path | GOBIN이 없을 때 같은 질문에 답합니다. $GOPATH/bin이고, 그것도 없으면 ~/go/bin입니다. |
hostveil scan을 좌우하는 Hostveil 자신의 판정은 변수가 존재하는지만 봅니다. 그래서 NO_COLOR=0이든 빈 값 NO_COLOR=이든, 어떤 값이라도 색상을 끕니다. 반면 TUI의 판정은 터미널 라이브러리에서 오고 값을 불리언으로 해석합니다. 그래서 NO_COLOR=0, NO_COLOR=, NO_COLOR=yes는 모두 색상을 켠 채로 둡니다. 결과적으로 NO_COLOR=0 hostveil scan은 흑백이고 NO_COLOR=0 hostveil tui는 컬러입니다. 1로 설정하면 둘이 일치합니다. 참고로 빈 값을 "끄기"로 취급하는 것은 빈 값이면 끄지 말아야 한다는 no-color.org 관례와도 어긋납니다.
참고용 설명기 요청에 걸리는 프록시 변수
Hostveil이 스스로 내는 네트워크 요청은 딱 한 종류, 참고용 설명기가 HOSTVEIL_AI_PROVIDER가 가리키는 프로바이더와 이야기하는 것뿐입니다. Go의 기본 HTTP 전송을 쓰므로 표준 프록시 변수를 따릅니다.
| 변수 | 기본값 | 값 | 하는 일 |
|---|---|---|---|
HTTP_PROXY, HTTPS_PROXY | 미설정 | 프록시 URL. 소문자 표기도 동작합니다 | AI 요청을 프록시로 보냅니다. 기본 프로바이더(Ollama)는 루프백과 통신하므로 예외로 빠지고, Ollama의 주소를 다른 기계로 바꾸거나 anthropic·openai로 전환한 경우에만 실제로 걸립니다 — 그 경우 발견 항목이 담긴 프롬프트가 그 프록시를 지나갑니다. |
NO_PROXY | 미설정 | 쉼표로 구분된 호스트·도메인·CIDR | 위 설정에서 특정 호스트를 제외합니다. 루프백은 이것 없이도 이미 제외됩니다. |
Hostveil의 나머지는 소켓을 열지 않습니다. 스캔은 파일을 읽고 로컬 명령을 실행하며, 대시보드는 루프백에 바인딩해 서비스하고, CVE 스캔은 Trivy 자신의 프로세스가 자신의 설정으로 합니다.
Hostveil이 무엇을 어디에 두고, 무엇을 찾을 수 있는지
| 변수 | 기본값 | 값 | 동작 |
|---|---|---|---|
HOME | 로그인 시 설정됨. Hostveil 자체의 기본값은 없음 | 절대 경로 | Hostveil이 root가 아닐 때의 상태 디렉터리를 결정합니다: $HOME/.local/share/hostveil. 여기에 저장된 스캔, 롤백 체크포인트, 기억된 테마·기호·레이아웃 선택이 들어 있습니다. root일 때는 /var/lib/hostveil이고 HOME은 보지 않습니다 — sudo로 적용한 수정이 권한 없는 hostveil history에 보이지 않는 이유가 이것입니다. |
TMPDIR | /tmp | 디렉터리 경로 | 두 가지 역할을 합니다. 파일 편집 수정이 검증용으로 적용 전후 사본을 놓아 두는 곳이라, SSH 수정마다 sshd_config 사본이 이곳을 거칩니다 — 새로 만든 디렉터리에 0600으로 쓰고 끝나면 지웁니다. 그리고 HOME을 알아낼 수 없을 때의 마지막 상태 디렉터리이기도 합니다. 검증에 쓰는 바이너리가 놓아 둔 사본을 읽지 못하면 검증을 건너뛰고, 수정을 막는 대신 검증 없이 진행합니다. |
PATH | 물려받은 값. Hostveil은 설정하거나 손대지 않음 | 콜론으로 구분된 경로 목록 | Hostveil이 무엇이 설치돼 있다고 믿을지를 결정합니다. 모든 도메인의 사용 가능 여부 판정이 바이너리 탐색이라, PATH는 도메인이 실행되느냐 건너뜀으로 보고되느냐를 가릅니다 — 그리고 로그인 셸이 아닌 PATH에 /usr/sbin이 없으면, 거기 있는 바이너리는 설치돼 있지 않은 바이너리와 완전히 똑같아 보입니다. 권한 상승 여부도 여기서 갈립니다. PATH에 sudo가 없으면 Hostveil은 조용히 권한 없이 실행됩니다. HOSTVEIL_DEBUG가 탐색 결과를 하나하나 기록하는 이유가 이것입니다. |
Debian과 Ubuntu에서는 sudo의 secure_path가 권한이 올라간 자식의 PATH를 통째로 갈아치웁니다. 그래서 다시 실행된 Hostveil은 자신을 시작시킨 프로세스와 다른 바이너리 집합을 찾게 될 수 있습니다.
HOME을 알아낼 수 없으면 — 맨몸의 systemd 유닛, 일부 컨테이너 엔트리포인트 — 상태 디렉터리는 $TMPDIR/hostveil, 즉 보통 호스트에서는 /tmp/hostveil로 떨어집니다. 거기 쓰인 체크포인트는 재부팅을 넘기지 못하므로 "수정은 언제든 되돌릴 수 있다"가 조용히 사실이 아니게 되고, 출력에는 그 사실이 어디에도 나오지 않습니다. 유닛에 HOME을 지정하거나, root로 실행해 /var/lib/hostveil을 쓰세요.
DOCKER_HOST
| 변수 | 기본값 | 값 | 동작 |
|---|---|---|---|
DOCKER_HOST | 미설정 — docker CLI가 /var/run/docker.sock을 씀 | unix://, tcp://, ssh://user@host — docker CLI가 받아들이는 것이면 무엇이든 | Hostveil은 이 값을 읽지 않습니다. 다만 물려받은 환경을 그대로 실행 명령에 넘겨주므로, docker·docker compose·trivy가 이 기계에 없는 데몬에 닿을 수 있습니다. 의도적인 설계입니다. |
그 결과는 분명히 짚어 둘 만합니다. DOCKER_HOST가 다른 곳을 가리키면, Compose·이미지 CVE·Docker 데몬·Docker 방화벽 도메인은 원격 데몬을 설명하고 SSH·sysctl·파일 권한·계정 도메인은 로컬 호스트를 설명합니다. 그리고 이 둘은 하나의 점수로 합쳐지며, 어떤 인터페이스도 둘을 구분해 주지 않습니다. 가끔은 이것이 원하는 바이겠지만, 실수로 그렇게 되는 것이 원하는 바인 경우는 없습니다. 지금 앉아 있는 기계를 스캔할 때는 먼저 이 변수를 해제하세요.
TUI가 물려받는 디버그 스위치
TUI는 자체 디버그 스위치를 가진 터미널 라이브러리 위에 올라가 있습니다. Hostveil은 그것들을 설정하지도, 지우지도 않으며, hostveil tui를 실행할 때마다 살아 있습니다.
| 변수 | 기본값 | 값 | 동작 |
|---|---|---|---|
TEA_TRACE | 미설정 — 로그 없음 | 쓰기 가능한 파일 경로. 빈 값은 무시 | 그 경로를 0600 모드로 이어쓰기 열어서, 실행할 때마다 TUI 프레임워크 내부 로그를 씁니다. |
TEA_DEBUG | 미설정 | 1, true, t | TUI가 패닉할 때 현재 작업 디렉터리에 bubbletea-panic-<타임스탬프>.log를 씁니다. 설정하지 않아도 패닉 스택은 stderr에 그대로 출력됩니다. |
UV_DEBUG | 미설정 — 로그 없음 | 파일 경로. 빈 값은 무시 | 한 겹 아래에서 같은 일을 합니다. 그 밑에 있는 터미널 렌더러의 디버그 파일입니다. |
hostveil tui는 자동으로 권한을 올리므로, 이 파일을 만드는 프로세스는 root이고 경로는 환경 변수에서 옵니다. Hostveil 실행의 환경을 건드릴 수 있는 로컬 일반 사용자 — 래퍼 스크립트, 공용 셸 프로필, cron 항목 — 는 자기가 고른 경로에 root 소유 이어쓰기를 얻는 셈입니다. 셋 중 어느 것도 시스템 전역으로 내보내지 말고, 프로필에 설명 없이 들어 있는 것을 발견하면 다른 특권 파일 쓰기와 똑같이 다루세요.
Hostveil이 실행하는 명령에 설정해 주는 것
Hostveil은 자신의 환경을 실행하는 모든 명령에 그대로 넘기며, 딱 하나를 더합니다.
| 변수 | 기본값 | 값 | 동작 |
|---|---|---|---|
LC_ALL | 무엇을 설정해 두었든 자식에서는 항상 C | 설정 불가 — 이를 덮는 플래그나 변수는 없음 | Hostveil이 실행하는 모든 명령의 환경에 덧붙여서, 도구가 번역되지 않은 영어로 답하게 합니다. 숫자가 아니라 단어를 찾아 읽는 모든 파서가 여기에 기대고 있습니다. |
이것은 실제로 있었던 실패 때문에 존재합니다. Hostveil은 운영자의 환경을 물려받고 sudo는 LANG과 LC_*를 기본으로 남기므로, 독일어 호스트에서 Hostveil은 apt에게 독일어로 묻고는 영어를 찾았습니다. [upgradable from: %s]라는 문자열은 21개 로케일로 번역되어 있고, 개수를 세는 코드는 그 영어 문장이 정확히 들어 있지 않은 줄을 전부 건너뛰었습니다. 그 결과가 패치되지 않은 기계에서 대기 중인 보안 업데이트 0건이었습니다. 이 도구 전체가 거부하는 바로 그 종류의 실패입니다. 믿을 수 없는 깨끗한 보고서는 보고서가 없는 것보다 나쁩니다.
LANG이 아니라 LC_ALL인 점은 기억해 둘 만합니다. gettext는 LANG보다 LANGUAGE를 우선하므로 LANG만 고정해서는 데스크톱의 LANGUAGE=de:en이 그대로 주도권을 쥡니다. 로케일이 C이면 LANGUAGE는 무시되고, 그래서 이 변수 하나로 충분합니다.
로케일은 레이아웃을 바꾸지 않습니다. 일부러 그렇습니다
Hostveil이 링크하는 문자 폭 라이브러리는 시작할 때 이 값들을 읽어 전역 설정을 만들지만, Hostveil은 그 전역을 쓰지 않습니다 — 동아시아 폭 처리를 끈 자체 측정 방식을 따로 만들어 씁니다. 흔히 반대로 짐작하는 부분이라 분명히 적어 둡니다. 아래 네 변수 중 어느 것도 화면 배치를 바꾸지 않습니다.
| 변수 | 기본값 | 값 | 동작 |
|---|---|---|---|
LANG | 로그인 시 설정됨 | 아무 로케일 이름 | Hostveil 자신의 프로세스에는 아무 영향도 없습니다. 시작 시 문자 폭 라이브러리에 닿지만 거기서 무력화됩니다. 다만 위의 고정이 존재하는 이유이기도 합니다 — 그냥 두면 apt, systemctl, docker가 번역된 로케일로 답하게 됩니다. |
LC_CTYPE | 로그인 시 설정됨 | 아무 로케일 이름 | 같은 이유로 아무 영향이 없습니다. 그 라이브러리의 로케일 우선순위에서 두 번째이고, 어차피 모든 자식에서 LC_ALL=C 고정에 덮입니다. |
LANGUAGE | 일부 데스크톱이 설정 | 콜론으로 구분된 로케일 목록 | 아무 영향이 없습니다 — Hostveil은 이 값을 읽지도, 지우지도 않습니다. 위 설명에서 결정적인 역할을 하기 때문에 적어 둡니다. gettext에서 LANG보다 우선하고, 그래서 고정 대상이 LC_ALL인 것입니다. |
RUNEWIDTH_EASTASIAN | 미설정 | strconv.ParseBool이 참으로 읽는 값 — 1, t, true. 그 밖의 값은, 해석되지 않는 값을 포함해 꺼진 상태로 둡니다. | 동아시아 애매폭 문자 — Hostveil이 그리는 말줄임표·화살표·불릿 — 를 한 칸이 아니라 두 칸으로 잽니다. 터미널 UI를 그리는 두 라이브러리 모두에서요. 터미널이 이 문자들을 넓게 그린다면 설정하세요. Hostveil이 로케일에서 가져오지 않는 것은 LANG에서 유추되는 같은 설정입니다. 같은 바이너리가 서울의 운영자와 베를린의 운영자에게 화면을 다르게 그리면 안 되고, 이 변수는 유추가 아니라 명시적인 요청이기 때문입니다. |
주변 도구에서 읽어 오는 것
| 변수 | 기본값 | 값 | 동작 |
|---|---|---|---|
SUDO_USER | sudo가 설정. Hostveil은 설정하지 않음 | 비어 있지 않으면 "이미 권한이 올라갔다"는 뜻. 사용자 이름 자체는 보지 않음 | 자동 권한 상승의 무한 반복 방지 장치입니다. 값이 비어 있지 않으면 sudo가 이미 실행되었다는 뜻이므로, Hostveil은 다시 부르지 않습니다. |
SUDO_UID, SUDO_GID | sudo가 설정. Hostveil은 설정하지 않음 | 0 이상의 숫자 사용자/그룹 ID | scan --output이 sudo 자식으로 실행될 때, 새 보고서가 root 소유로 남지 않고 실행한 계정의 소유가 되게 합니다. 실효 사용자가 root이고 SUDO_USER도 설정된 경우에만 사용하며, 기존 보고서는 소유자와 모드를 유지합니다. |
sudo는 이 값을 전달하는 것이 아니라 대상 환경에 직접 넣습니다. env_reset이 이 값을 지울 수 없는 이유이자, Hostveil 자신의 표시가 실패한 자리에서 이 값이 작동하는 이유입니다. 게다가 sudo의 대상이 root가 아닐 때도 설정되는데, 바로 그 경우가 잡아야 할 경우입니다. sudoers가 runas_default를 root가 아닌 계정으로 잡아 둔 호스트에서는 자식이 권한 없이 돌아오고, 반복되는 대신 여기서 멈춥니다.
거짓 양성 — Hostveil을 실행한 sudo가 아닌 다른 무언가가 SUDO_USER를 환경에 남겨 둔 경우 — 의 대가는 권한 없는 스캔이고, root가 필요한 도메인은 이유와 함께 건너뜀으로 보고됩니다. 틀린다면 그쪽으로 틀리는 편이 안전합니다. 끝없이 이어지는 비밀번호 요구는 그렇지 않습니다.
이 페이지에 없는 것
테스트용·빌드용 변수. 테스트 스위트는 배포되는 바이너리가 결코 읽지 않는 변수를 몇 개 읽고, 릴리스 빌드는 런타임에는 존재하지도 않는 변수를 몇 개 고정합니다. 실행 중인 Hostveil의 조절 손잡이가 아니므로, 여기에 적으면 누군가 설정해 보라고 부추기는 셈밖에 되지 않습니다. 이들은 쓰이는 자리, 즉 저장소의 개발 문서에 설명돼 있습니다 — 기여하기를 참고하세요.
Hostveil이 읽는 것이 아니라 들여다보는 변수. 일부 점검은 여러분의 서비스에 속한 환경 변수를 검사합니다. Compose 파일의 environment: 블록이나 컨테이너의 .env에 들어 있는, 자격 증명처럼 생긴 키들입니다. 그것들은 Hostveil이 디스크에서 살펴보는 데이터입니다. Hostveil은 비밀 값을 찾겠다고 여러분의 셸 환경을 읽지 않으며, 이 페이지의 어떤 값도 어디로도 전송되지 않습니다 — 여기 있는 변수는 모두 로컬에서 읽혀 로컬에서 쓰입니다.
발견 항목을 느슨하게 만드는 것. 심각도를 바꾸거나, 점수를 조정하거나, 수정이 말한 것보다 적게 하도록 만드는 변수는 없습니다. 탐지는 환경이 아니라 호스트가 결정합니다. 스캔 범위를 바꾸는 변수들도 Hostveil이 무엇에 닿을 수 있는지를 바꿔서 그렇게 하는 것뿐입니다 — PATH, HOSTVEIL_NO_SUDO, DOCKER_HOST가 그렇습니다.
여기에 가장 가까운 것이 HOSTVEIL_ASSUME_HOST인데, 정확히 짚어 둘 값어치가 있습니다. 이것은 점검을 끄지 않습니다. 오히려 둘을 다시 켭니다. 이 기계가 어떤 종류인지에 답하는 방식으로요. 컨테이너 안에서 Hostveil은 호스트 방화벽을 점검하지 않고 커널 하드닝 수정도 제안하지 않습니다. 안에서는 둘 다 손쓸 수 없기 때문입니다. 이 변수를 설정한다는 것은 이 파일시스템을 곧 점검 대상 호스트로 다루겠다는 뜻입니다. 발견 항목을 작게 만들 수는 없고, Hostveil에게 무언가를 눈감아 달라고 할 방법은 여전히 없습니다.