Files
obsidian/services/searxng.md

8.5 KiB

title, updated, tags
title updated tags
SearXNG 검색 엔진 2026-06-10
search
proxy
google

개요

SearXNG 메타 검색 엔진. K3s 클러스터(searxng 네임스페이스)에 Helm으로 배포.

  • URL: https://searxng.inouter.com/
  • Helm chart: 자체 차트 gitea.inouter.com/kaffa/helm-chartscharts/searxng (ArgoCD app searxng, repo는 k8s 아님 — 2026-06-10 정정)
  • Values: charts/searxng/values.yaml (proxy.address로 google_proxy 주소 지정)
  • 배포: ArgoCD GitOps (syncPolicy automated, prune+selfHeal). 라이브 패치는 self-heal로 되돌아가므로 git 소스를 고칠 것
  • settings.yml: Secret searxng-config (ArgoCD가 values에서 렌더)
  • 라우팅: gateway-api HTTPRoute (Ingress 사용 안 함)

라우팅 (Gateway API)

HTTPRoute로 외부 노출. gateway-api 참조.

  • Gateway: traefik-gateway (kube-system)
  • Host: searxng.inouter.com
  • Backend: searxng:8080
  • TLS: Gateway 레벨에서 종료 (wildcard-inouter-com-tls)
  • Helm values에서 ingress.main.enabled: false (Ingress 비활성)

Google 차단 우회 (tlsproxy)

Google이 SearXNG의 요청을 차단하는 두 가지 메커니즘:

  1. TLS fingerprint — Python httpx의 TLS ClientHello가 봇으로 인식됨
  2. GSA User-Agent — SearXNG가 사용하는 Google Search App UA가 특정 IP에서 차단됨

tlsproxy

Go + utls 기반 MITM forward proxy. HTTP CONNECT 프록시로 동작하며, Google 도메인에 대해 Chrome TLS fingerprint로 연결을 중계함.

  • 소스: 유실 (sandbox-tokyo /tmp/tlsproxy/는 NixOS 전환 시 소실, Gitea 미이관). 현재는 빌드된 tlsproxy:latest 이미지로만 존재 → TODO: Dockerfile/소스를 Gitea로 복구
  • Docker 이미지: tlsproxy:latest (x86_64, 15.4MB). apisix-osaka에 보존본 있어 거기서 docker save로 배포
  • 포트: 8443 (host network), 컨테이너 실행: docker run -d --name tlsproxy --restart always --network host -v /opt/tlsproxy:/etc/tlsproxy tlsproxy:latest
  • CA 인증서: /opt/tlsproxy/ca.crt + ca.key (CN=TLS Proxy CA, fingerprint C4:3E:98:7F..., 2026-03-21 ~ 2036-03-18). 서버 간 동일 페어 공유, K8s searxng-ca-bundle Secret에 이 CA 포함됨 (신뢰됨)

동작 방식:

  1. SearXNG → HTTP CONNECT → tlsproxy
  2. tlsproxy가 Google에 Chrome TLS fingerprint(utls HelloChrome_Auto, ALPN http/1.1)로 연결
  3. 클라이언트에게 자체 CA로 서명한 인증서 제시
  4. 양방향 relay

배포 현황

서버 상태 Google IP 비고
zlambda (100.78.51.18:8443) 가동 (2026-06-10 재배포) 200 (미차단) apisix-osaka에서 이미지+CA 이식. host network, restart=always. SearXNG google_proxy가 이 주소를 가리킴
apisix-osaka (100.108.39.107:8443) 가동(이미지/CA 보존본) 403 차단 IP 차단되어 Google엔 무효. tlsproxy 배포 소스로 활용
relay4wd 미배포 403 IP 차단됨
incus-jp1 미배포 403 IP 차단됨

2026-06-10 재배포 기록: 기존 SearXNG proxy.address가 죽은 IP 100.79.87.48:8443을 가리켜 google 엔진이 HTTP connection error로 0건. apisix-osaka의 tlsproxy:latest 이미지와 CA 페어를 zlambda로 이식(docker save|load + scp), host network로 기동. charts/searxng/values.yamlproxy.address100.78.51.18:8443(zlambda 실제 tailscale IP)로 교정·푸시 → ArgoCD synced. 프록시 경유 Google 접근은 200/정상 HTML 복구됨(connection error 해소).

2026-06-10 google 0건 후속 조치 (위 재배포 직후):

정밀 진단 결과 — UA·파서 정합성:

  • 현재 이미지는 이미 최신 2026.6.8. 재pull 무의미 (버전 문제 아님).
  • gen_gsa_useragent()(Android Nexus5 GSA UA) + Accept: */* → Google이 data-ved 풍부한 정상 HTML(150KB+, dv 50~80) 반환. fetch→파서 전 구간 정상 (수동 재현 6~10건 추출 성공).
  • Chrome UA → data-ved 없는 lite-shell(90KB) → 파서 0건. 따라서 uaPatch(Chrome UA 교체)는 해롭다 → 비활성화. 기본 GSA UA가 정답. (uaPatch는 마운트만 되고 실행은 안 되던 inert 상태였음)
  • raw fetch 신뢰성: fresh/keepalive 모두 10/10. 연결 풀 문제 아님.

적용한 변경 (charts/searxng):

  • uaPatch.enabled: false (Chrome UA가 google 깨뜨림)
  • google 엔진 timeout: 8 → 15, outgoing request_timeout: 10 → 15 + max_request_timeout: 20
  • CPU limit 500m → 1 (동시 부하 대비, 단 포함률엔 영향 없었음)

결과 — google 포함률: 0% → 격리 라이브 ~83%, 전체검색 4050%. 결과 품질은 정상.

남은 한계: 전체(다엔진 동시) 검색에서 google이 절반가량 누락. raw fetch는 100%인데 라이브 엔진은 변동 — 프록시 경유 google이 타 엔진보다 느리고 + 엔진의 정확한 요청 패턴에 Google이 간헐적으로 lite-shell을 반환하는 SearXNG↔Google 고질 변동. 추가 개선 후보: google.py response()에서 결과 0 + lite-shell 감지 시 예외 발생시켜 SearXNG 재시도 유도하는 파서 패치(uaPatch와 동일한 sed 주입 메커니즘 활용 가능). brave/duckduckgo/qwant는 정상.

SearXNG 설정

outgoing.networksgoogle_proxy 네트워크를 정의하고, Google 엔진에서 network: google_proxy로 참조.

outgoing:
  networks:
    google_proxy:
      enable_http2: false
      verify: /etc/ssl/custom/ca-certificates.crt
      proxies: http://100.78.51.18:8443  # zlambda, 2026-06-10 가동 중 (charts/searxng/values.yaml proxy.address)
engines:
  - name: google
    engine: google
    network: google_proxy

CA 인증서 주입

Helm chart가 extraVolumes를 지원하지 않아 kubectl patch로 적용. helm upgrade 시 재적용 필요.

# CA 번들 Secret (시스템 CA + tlsproxy CA)
kubectl get secret searxng-ca-bundle -n searxng

# Deployment 패치 (helm upgrade 후 재실행)
kubectl patch deployment searxng -n searxng --type=json -p='[
  {"op":"add","path":"/spec/template/spec/volumes/-","value":{"name":"ca-bundle","secret":{"secretName":"searxng-ca-bundle"}}},
  {"op":"add","path":"/spec/template/spec/containers/0/volumeMounts/-","value":{"name":"ca-bundle","mountPath":"/etc/ssl/custom/ca-certificates.crt","subPath":"ca-certificates.crt","readOnly":true}},
  {"op":"add","path":"/spec/template/spec/containers/0/env/-","value":{"name":"SSL_CERT_FILE","value":"/etc/ssl/custom/ca-certificates.crt"}},
  {"op":"add","path":"/spec/template/spec/containers/0/env/-","value":{"name":"REQUESTS_CA_BUNDLE","value":"/etc/ssl/custom/ca-certificates.crt"}}
]'

IP 차단 상태 확인 방법

서버에서 GSA UA로 Google 검색을 요청하여 확인:

curl -4 -s -o /dev/null -w '%{http_code}' --max-time 10 \
  -H "User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 17_7_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) GSA/406.0.862495628 Mobile/15E148 Safari/604.1" \
  "https://www.google.com/search?q=test&hl=en-US"
# 200 = OK, 403 = 차단됨

주의사항

  • GSA UA 필수: SearXNG Google 엔진 파서가 GSA 응답 형식에 의존. Chrome UA로 바꾸면 파싱 실패 (결과 0)
  • 프록시 round-robin 불가: SearXNG의 proxies를 dict(list) 형태로 넣으면 프록시를 무시하는 버그 있음. 단일 문자열만 동작
  • CA 번들 패치 영속성: helm upgrade 시 volume mount/env 패치가 사라짐. upgrade 후 반드시 kubectl patch 재실행
  • 새 프록시 서버 추가 시: CA 키를 공유(/opt/tlsproxy/ca.crt, ca.key)하고, K8s Secret의 CA 번들도 갱신 필요

기타 프록시 (SOCKS5)

이전에 Google 우회 목적으로 배포했으나, TLS fingerprint 문제로 Google에는 무효. 다른 엔진이나 용도로는 사용 가능.

서버 포트 타입
sandbox-tokyo 1080 (제거됨, 2026-04-08 NixOS 전환)
relay4wd 1080 Docker microsocks
incus-jp1 socks5-proxy 1080 Incus 컨테이너 microsocks
apisix-osaka 1081, 1082 Docker microsocks (IPv6 1d00::1, 1d01::1)

Helm 업그레이드 절차

# 1. Helm upgrade
helm upgrade searxng searxng/searxng -n searxng -f ~/path/to/values.yaml

# 2. CA 번들 패치 재적용 (위 kubectl patch 명령)

# 3. 검증
kubectl exec -n searxng deploy/searxng -- wget -qO- \
  "http://localhost:8080/search?q=test&format=json&engines=google" | \
  python3 -c "import sys,json; d=json.load(sys.stdin); print(len(d['results']), 'results')"