Merge pull request #56 from paasup/fix/apisix-keycloak-authz-tag
apisix keycloak-authz 태그를 published.json 경로 예외로 반영한다
This commit is contained in:
@@ -43,6 +43,15 @@ jobs:
|
|||||||
-o /tmp/published.json
|
-o /tmp/published.json
|
||||||
python3 -c "import json; json.load(open('/tmp/published.json'))" # 스키마 최소 확인
|
python3 -c "import json; json.load(open('/tmp/published.json'))" # 스키마 최소 확인
|
||||||
|
|
||||||
|
# apisix에 얹는 keycloak-authz 커스텀 플러그인 오버레이는 hardened-containers
|
||||||
|
# 표준 파이프라인 밖에서 별도로 빌드돼 같은 리포지토리에 -keycloak-authz 접미사로
|
||||||
|
# 추가 push된다 — published.json에는 이 접미사 태그가 절대 안 잡힌다. 이 스텝이
|
||||||
|
# /tmp/published.json의 apisix 항목을 제자리에서 고쳐, 아래 "반영 대상 계산"이
|
||||||
|
# 그걸 원래 발행된 태그인 것처럼 그대로 적용하게 한다. 상세:
|
||||||
|
# scripts/build/resolve-apisix-keycloak-authz-tag.py 모듈 docstring.
|
||||||
|
- name: apisix keycloak-authz 변형 확인
|
||||||
|
run: python3 scripts/build/resolve-apisix-keycloak-authz-tag.py --published /tmp/published.json
|
||||||
|
|
||||||
- name: 반영 대상 계산
|
- name: 반영 대상 계산
|
||||||
id: apply
|
id: apply
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -34,3 +34,20 @@ hardened-containers 가 알아야 하므로, 레포 분리 시 이 지식을 카
|
|||||||
여러 버전을 **의도적으로 병행 추적**해야 하는 예외적 케이스(같은 이미지를 쓰는 두
|
여러 버전을 **의도적으로 병행 추적**해야 하는 예외적 케이스(같은 이미지를 쓰는 두
|
||||||
메이저 라인을 동시에 지원해야 하는 경우 등)만 공백으로 여러 디렉토리를 나열한다 —
|
메이저 라인을 동시에 지원해야 하는 경우 등)만 공백으로 여러 디렉토리를 나열한다 —
|
||||||
이 경우 왜 병행이 필요한지 `CUSTOM-README.md`나 커밋 메시지에 근거를 남긴다.
|
이 경우 왜 병행이 필요한지 `CUSTOM-README.md`나 커밋 메시지에 근거를 남긴다.
|
||||||
|
|
||||||
|
## 발행 경로가 published.json 하나로 안 끝나는 이미지
|
||||||
|
|
||||||
|
원칙은 "`published.json`의 `ref`를 그대로 믿고 반영한다"지만, 일부 이미지는
|
||||||
|
hardened-containers의 표준 파이프라인 밖에서 만들어져 `published.json`에 안
|
||||||
|
잡힌다. 현재 유일한 사례: **apisix의 keycloak-authz 커스텀 플러그인 오버레이**
|
||||||
|
— 표준 빌드 뒤에 별도 프로세스가 같은 리포지토리에 `-keycloak-authz` 접미사를
|
||||||
|
붙여 추가로 push한다.
|
||||||
|
|
||||||
|
이런 이미지는 `published.json`을 그대로 신뢰하지 않고, `catalog-tag-update.yml`이
|
||||||
|
그 이미지 전용 전처리 스크립트로 published.json 사본을 먼저 고쳐 쓴 뒤에야 표준
|
||||||
|
반영 로직(`apply-published-tags.py`)이 돈다 — apisix의 경우
|
||||||
|
`scripts/build/resolve-apisix-keycloak-authz-tag.py`. **원하는 태그가 실제로
|
||||||
|
존재할 때만 대체하고, 없으면 그 이미지를 이번 실행에서 건너뛴다** — 접미사 없는
|
||||||
|
순정 태그로 되돌리는 일은 없다(순정으로 돌아가면 그 이미지가 실제로 요구하는
|
||||||
|
커스텀 기능이 빠지는 회귀가 된다). 다음에 비슷한 예외가 생기면 이 패턴을
|
||||||
|
참고한다.
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
|
# keycloak-authz 커스텀 플러그인 오버레이는 hardened-containers 표준 파이프라인
|
||||||
|
# 밖에서 별도로 빌드돼 같은 리포지토리에 -keycloak-authz 접미사로 추가 push된다 —
|
||||||
|
# published.json에는 이 접미사 태그가 안 잡힌다. catalog-tag-update.yml이
|
||||||
|
# scripts/build/resolve-apisix-keycloak-authz-tag.py로 published.json 사본을
|
||||||
|
# 먼저 고쳐 쓴 뒤에야 아래 CHART_DIRS/TAG_STYLE/TAG_BLOCK 기준 표준 반영 로직이
|
||||||
|
# 이 이미지에 대해 돈다. 상세: catalog/image-map/README.md.
|
||||||
CHART_DIRS="manifests/helm/apisix/2.17.0"
|
CHART_DIRS="manifests/helm/apisix/2.17.0"
|
||||||
TAG_STYLE=split
|
TAG_STYLE=split
|
||||||
TAG_BLOCK=image
|
TAG_BLOCK=image
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
# 자체는 그대로 둠).
|
# 자체는 그대로 둠).
|
||||||
image:
|
image:
|
||||||
repository: docker.io/paasup/apisix
|
repository: docker.io/paasup/apisix
|
||||||
tag: "3.18.0-security-hardened-20260826"
|
tag: "3.18.0-security-hardened-20260826-keycloak-authz"
|
||||||
pullPolicy: IfNotPresent
|
pullPolicy: IfNotPresent
|
||||||
|
|
||||||
replicaCount: 1
|
replicaCount: 1
|
||||||
|
|||||||
@@ -0,0 +1,129 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""resolve-apisix-keycloak-authz-tag.py — apisix의 keycloak-authz 커스텀 플러그인
|
||||||
|
오버레이 태그를 published.json에 반영한다(제자리 수정).
|
||||||
|
|
||||||
|
apisix에 얹는 keycloak-authz 오버레이는 hardened-containers의 표준 파이프라인
|
||||||
|
밖에서 별도로 빌드돼, 같은 `docker.io/paasup/apisix` 리포지토리에 표준 빌드보다
|
||||||
|
나중에 `-keycloak-authz` 접미사를 붙여 추가로 push된다. hardened-containers의
|
||||||
|
`published.json`에는 이 접미사 태그가 절대 안 잡힌다(그 레포가 만드는 게 아니므로).
|
||||||
|
|
||||||
|
이 스크립트는 `apply-published-tags.py`가 published.json을 읽기 *전에* 끼어들어
|
||||||
|
`images.apisix` 항목을 제자리에서 고쳐 쓴다:
|
||||||
|
|
||||||
|
1. published.json의 apisix base 태그(`<version>-security-hardened-<date>`)를 읽는다.
|
||||||
|
2. `<base 태그>-keycloak-authz`가 Docker Hub에 실제로 존재하는지 확인한다.
|
||||||
|
3. 있으면: `images.apisix`를 그 접미사 태그로 교체한다 — 이후
|
||||||
|
apply-published-tags.py는 이걸 원래 발행된 태그인 것처럼 그대로 적용한다.
|
||||||
|
4. 없으면: `images.apisix` 항목 자체를 지운다 — apply-published-tags.py의 기존
|
||||||
|
"published.json에 없다" 분기가 자연히 타서 apisix를 건너뛴다. **순정(접미사
|
||||||
|
없는) 태그로 되돌리는 일은 없다** — keycloak-authz가 빠지면 apisix가 실제로
|
||||||
|
요구하는 커스텀 인증 플러그인 없이 배포되는 회귀이기 때문이다.
|
||||||
|
|
||||||
|
apply-published-tags.py·apply_for_image()는 건드리지 않는다 — 이 스크립트가
|
||||||
|
끝난 뒤의 published.json이 "원래 그런 내용이었던 것"처럼 보이게만 한다.
|
||||||
|
|
||||||
|
사용
|
||||||
|
----
|
||||||
|
python3 scripts/build/resolve-apisix-keycloak-authz-tag.py --published /tmp/published.json
|
||||||
|
|
||||||
|
종료 코드
|
||||||
|
0 정상(교체·삭제·무변경 무관)
|
||||||
|
2 실행 오류(published.json 파싱 실패 등 — Docker Hub 조회 실패는 포함 안 함,
|
||||||
|
아래 참고)
|
||||||
|
"""
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
import urllib.error
|
||||||
|
import urllib.request
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_tags(repo):
|
||||||
|
"""Docker Hub 공개 API로 <namespace>/<repo>의 태그 전체를 가져온다.
|
||||||
|
{name: {tag_last_pushed, digest}} 로 반환."""
|
||||||
|
tags = {}
|
||||||
|
url = f"https://hub.docker.com/v2/repositories/{repo}/tags?page_size=100"
|
||||||
|
while url:
|
||||||
|
req = urllib.request.Request(url, headers={"Accept": "application/json"})
|
||||||
|
with urllib.request.urlopen(req, timeout=30) as resp:
|
||||||
|
doc = json.load(resp)
|
||||||
|
for r in doc.get("results") or []:
|
||||||
|
name = r.get("name")
|
||||||
|
if not name:
|
||||||
|
continue
|
||||||
|
tags[name] = {
|
||||||
|
"tag_last_pushed": r.get("tag_last_pushed"),
|
||||||
|
"digest": r.get("digest"),
|
||||||
|
}
|
||||||
|
url = doc.get("next")
|
||||||
|
return tags
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
ap = argparse.ArgumentParser(description=__doc__,
|
||||||
|
formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||||
|
ap.add_argument("--published", required=True, metavar="FILE",
|
||||||
|
help="hardened-containers의 published.json — 제자리에서 수정한다")
|
||||||
|
ap.add_argument("--repo", default="paasup/apisix",
|
||||||
|
help="Docker Hub 리포지토리 (namespace/repo, 기본 paasup/apisix)")
|
||||||
|
ap.add_argument("--suffix", default="-keycloak-authz",
|
||||||
|
help="찾을 태그 접미사 (기본 -keycloak-authz)")
|
||||||
|
ap.add_argument("--registry", default="docker.io",
|
||||||
|
help="레지스트리 호스트 (기본 docker.io)")
|
||||||
|
args = ap.parse_args()
|
||||||
|
|
||||||
|
published_path = Path(args.published)
|
||||||
|
try:
|
||||||
|
doc = json.loads(published_path.read_text())
|
||||||
|
except Exception as e:
|
||||||
|
print(f"::error::published.json 을 읽지 못했다: {e}", file=sys.stderr)
|
||||||
|
return 2
|
||||||
|
|
||||||
|
images = doc.get("images") or {}
|
||||||
|
entry = images.get("apisix")
|
||||||
|
if entry is None:
|
||||||
|
print("apisix: published.json 에 없다 — 건너뛴다")
|
||||||
|
return 0
|
||||||
|
if entry.get("gate") != "pass":
|
||||||
|
print(f"apisix: gate={entry.get('gate')!r} — 건너뛴다")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
base_tag = entry.get("tag")
|
||||||
|
if not base_tag:
|
||||||
|
print("::warning::apisix: published.json 항목에 tag 가 없다 — 건너뛴다",
|
||||||
|
file=sys.stderr)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
candidate = f"{base_tag}{args.suffix}"
|
||||||
|
|
||||||
|
try:
|
||||||
|
tags = fetch_tags(args.repo)
|
||||||
|
except (urllib.error.URLError, TimeoutError, json.JSONDecodeError) as e:
|
||||||
|
print(f"::warning::apisix: Docker Hub 태그 조회 실패({e}) — "
|
||||||
|
f"이번 실행에서는 기존 published.json 항목({base_tag})을 그대로 둔다",
|
||||||
|
file=sys.stderr)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if candidate in tags:
|
||||||
|
found = tags[candidate]
|
||||||
|
images["apisix"] = {
|
||||||
|
"ref": f"{args.registry}/{args.repo}:{candidate}",
|
||||||
|
"tag": candidate,
|
||||||
|
"digest": found.get("digest") or entry.get("digest"),
|
||||||
|
"gate": "pass",
|
||||||
|
}
|
||||||
|
published_path.write_text(json.dumps(doc))
|
||||||
|
print(f"apisix: keycloak-authz 변형 발견 — {candidate} 로 대체")
|
||||||
|
else:
|
||||||
|
del images["apisix"]
|
||||||
|
published_path.write_text(json.dumps(doc))
|
||||||
|
print(f"::warning::apisix: {candidate} 가 아직 없다 — "
|
||||||
|
f"이번 실행에서 apisix 를 건너뛴다(순정 태그로 되돌리지 않는다)",
|
||||||
|
file=sys.stderr)
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
Reference in New Issue
Block a user