openclaw onboard --install-daemon으로 launchd 고정, openclaw doctor와 문서의 헬스 경로 curl로 게이트웨이를 점검하는 흐름이 흔합니다. 벤더 문서의 참고 절차로 두고 특정 외부 URL을 필수로 두지 않아도 됩니다. 연관: GHCR·사설 레지스트리 매트릭스, 게이트웨이 보안, Docker CI 배포 가이드.
- 콜드 풀: 매니페스트는 Harbor에 올라왔는데 러너는 여전히 두꺼운 레이어를 기다립니다.
- 위조·재전송: JSON을 다시 직렬화한 뒤 비밀을 비교하거나 원시 본문을 건너뛰면 검증이 무너집니다.
- 무제한 이그레스: 요약 콜백 URL을 임의로 열면 화이트리스트·메타데이터 유출 정책을 위반합니다.
목표와 전제 조건
목표. Harbor 이벤트 시 Mac에서 (1) 웹훅 인증 (2) 이미지 참조 도출 (3) 유한 병렬·디스크 게이트로 프리페치 (4) 허용 호스트로만 JSON 요약을 보내 UI 없이 성공·실패 파악.
전제. Harbor 프로젝트 웹훅 권한, TLS→OpenClaw(보통 127.0.0.1+리버스 프록시), 레지스트리에 로그인된 Docker, Mac에 chmod 600 자격 증명. Node 22 LTS+를 셸·plist 동일화 → Node는 패키지 또는 문서의 curl 설치기 → openclaw onboard 및 지원 시 openclaw onboard --install-daemon → openclaw doctor 경고 Runbook화 → Harbor가 치는 URL로 헬스 curl 주기 점검. 이그레스 화이트리스트: Harbor 인그레스, pull 엔드포인트, 요약 수신 CI·Slack 호환 URL만.
최소 성공 기준. 스테이징 Mac에서 캡처한 Harbor 페이로드를 재생했을 때 로그의 다이제스트 줄이 실제와 일치하고, 타임아웃 예산 안에 프리페치가 끝나며, 요약 JSON이 5KB 미만으로 유지됩니다.
Harbor Webhook 이벤트 필드와 서명 검증 체크리스트
봉투는 type(PUSH_ARTIFACT 등), occur_at, operator, event_data를 담습니다. 프리페치에는 저장소·태그/다이제스트·아티팩트 다이제스트가 핵심입니다. 운영 Harbor에서 샘플 JSON 한 건을 fixture로 고정하세요.
검증 체크리스트. (1) JSON.parse 전 원시 본문. (2) 공유 비밀 상수 시간 비교. (3) application/json만(예외 명시 시 제외). (4) 필요 시 occur_at 신선도. (5) 로그에는 저장소+다이제스트만. (6) HMAC은 재직렬화가 아닌 원시 바이트에 적용—프록시 pretty-print가 흔한 실패 원인입니다.
| 필드·관심사 | OpenClaw 핸들러에서의 용도 |
|---|---|
type | push·리태그일 때만 프리페치, 삭제는 로컬 prune 정책이 있을 때만 처리. |
event_data.resources | 각 리소스를 registry/project/repo:tag로 매핑, 태그가 비면 digest 고정 pull. |
operator | 감사 추적·자동화 주체별 속도 제한. |
| 원시 본문 무결성 | 비밀·HMAC을 먼저 검증, 실패 시 401과 함께 입력을 에코하지 않음. |
curl 재생으로 실제 이벤트와 로그 일치 확인.원격 Mac 프리페치 스크립트 매개변수(동시성·타임아웃·디스크 할당)
라우트는 짧게 두고 셸 스크립트로 docker pull을 감쌉니다. 공유 Mini 기본값 예: 동시 2~4, 풀당 6~15분, 잔여 15~25GB 미만 중단. plist 환경: PREFETCH_CONCURRENCY, PULL_TIMEOUT_SEC, MIN_FREE_GB, MAX_IMAGES_PER_EVENT(선택). BSD xargs -P 한계를 확인하고 배치마다 JSON 한 줄 로그를 남깁니다. 자세한 디스크·동시성은 빌드 풀 FAQ.
#!/usr/bin/env bash
set -euo pipefail
: "${PREFETCH_CONCURRENCY:=3}"
: "${PULL_TIMEOUT_SEC:=600}"
: "${MIN_FREE_GB:=18}"
avail_gib="$(df -g / | awk 'NR==2 {print $4}')"
if [ "${avail_gib:-0}" -lt "${MIN_FREE_GB}" ]; then
printf '{"prefetch":"skipped","reason":"disk_low","avail_gib":%s}\n' "${avail_gib}"
exit 0
fi
printf '%s\n' "$@" | xargs -n1 -P"${PREFETCH_CONCURRENCY}" -IREF docker pull REF
# 타임아웃: GNU coreutils gtimeout, Python 래퍼, 또는 런타임별 권장 래핑 중 선택
OpenClaw 게이트웨이 라우팅과 실패 요약 템플릿
경로는 통합별로 분리(예: /hooks/harbor/v1). 검증된 type으로 분기·큐 적재, 하류가 정규식 없이 파싱하도록 실패 요약 JSON을 고정합니다.
{
"source": "harbor-webhook",
"event_type": "PUSH_ARTIFACT",
"repository": "acme/base-images/nodejs",
"digest": "sha256:…",
"prefetch": { "ok": false, "failed_image": "reg.example/acme/base-images/nodejs:20", "exit_code": 1, "log_tail": "…마지막 400자…" },
"host": "mac-ci-07",
"duration_ms": 8420
}
화이트리스트 호스트로만 POST, 가능하면 DNS 전 호스트 거절. 실패는 2·4·8초 백오프·curl --max-time 상한. Harbor 타임아웃이 짧으면 202 Accepted 후 비동기 프리페치.
흔한 오류 FAQ(인증·타임아웃·디스크 부족)
로봇 비밀번호는 갱신됐는데 plist는 옛값을 export합니다. docker pull 호스트명이 토큰 스코프와 다릅니다. 비GUI 세션에서 Keychain 대화에 막혔습니다. 비대화형으로 재로그인하고 EnvironmentVariables에 절대 경로를 고정한 뒤 sudo -u 자동화계정으로 단일 pull을 재현하세요.
Harbor는 HTTP 응답을 기다리는 동안 레이어 다운로드를 기다리지 않습니다. 빠르게 확인 응답을 내고 pull은 워커 큐로 넘기거나 이벤트당 작업량을 줄이세요. 보안팀이 허용하는 범위에서만 타임아웃을 늘리고, TLS를 끄는 식의 우회는 금지입니다.
공유 러너에 dangling 이미지가 쌓입니다. MIN_FREE_GB를 강제하고, 성공 빌드 뒤 일정에 docker image prune -f를 둡니다. 대용량 테스트 픽스처는 Docker 루트 외부에 두세요.