From 3c68b7a7aec80590980d2f86edc3d43aa7d3649f Mon Sep 17 00:00:00 2001 From: "kihwan.song" Date: Thu, 18 Jun 2026 11:33:43 +0900 Subject: [PATCH] =?UTF-8?q?=EB=8F=84=EB=A9=94=EC=9D=B8=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EA=B3=B5=ED=86=B5=20=EC=8A=A4=ED=81=AC=EB=A6=BD?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- create-domain/action.yml | 182 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 create-domain/action.yml diff --git a/create-domain/action.yml b/create-domain/action.yml new file mode 100644 index 0000000..c13b813 --- /dev/null +++ b/create-domain/action.yml @@ -0,0 +1,182 @@ +name: 도메인 생성 +description: nginx-proxy-manager에 프록시 호스트를 등록하고 Let's Encrypt SSL을 적용 (멱등성 보장) + +inputs: + npm-host: + required: true + description: nginx-proxy-manager 주소 (npm-api.conf의 NGINX_IP) + npm-port: + required: false + default: "81" + description: nginx-proxy-manager 관리 API 포트 (npm-api.conf의 NGINX_PORT) + npm-user: + required: true + description: NPM 로그인 이메일 (npm-api.conf의 API_USER) + npm-password: + required: true + description: NPM 로그인 비밀번호 (npm-api.conf의 API_PASS) + domain: + required: true + description: 생성할 도메인 (예: app.example.com) + forward-host: + required: true + description: 프록시가 바라볼 대상 호스트/IP + forward-port: + required: true + description: 프록시가 바라볼 대상 포트 + websockets: + required: false + default: "true" + description: 웹소켓 지원 활성화 + +outputs: + host-id: + description: 생성되었거나 이미 존재하는 프록시 호스트 ID + value: ${{ steps.create.outputs.host-id }} + created: + description: 이번 실행에서 새로 생성했으면 true, 이미 있었으면 false + value: ${{ steps.create.outputs.created }} + +runs: + using: composite + steps: + - name: jq 설치 확인 + shell: bash + run: | + set -eu + + # jq가 이미 있으면 건너뜁니다. + if command -v jq >/dev/null 2>&1; then + exit 0 + fi + + # 패키지 매니저에 따라 jq를 설치합니다. + if command -v apt-get >/dev/null 2>&1; then + apt-get update && apt-get install -y jq + elif command -v apk >/dev/null 2>&1; then + apk add --no-cache jq + elif command -v yum >/dev/null 2>&1; then + yum install -y jq + else + echo "jq 설치 실패: 지원하지 않는 러너 이미지" + exit 1 + fi + + - name: 프록시 호스트 생성 + id: create + shell: bash + env: + NPM_HOST: ${{ inputs.npm-host }} + NPM_PORT: ${{ inputs.npm-port }} + NPM_USER: ${{ inputs.npm-user }} + NPM_PASS: ${{ inputs.npm-password }} + DOMAIN: ${{ inputs.domain }} + FORWARD_HOST: ${{ inputs.forward-host }} + FORWARD_PORT: ${{ inputs.forward-port }} + WEBSOCKETS: ${{ inputs.websockets }} + run: | + set -eu + + # 개발서버 전용 고정값 + BASE="http://${NPM_HOST}:${NPM_PORT}/api" # NPM_SCHEME=http 고정 + FORWARD_SCHEME="http" # 대상 프로토콜 http 고정 + EMAIL="$NPM_USER" # Let's Encrypt 이메일 = NPM 계정 + + # --------------------------------------------------------------- + # 1. 토큰 발급 (로그인) + # --------------------------------------------------------------- + TOKEN=$(curl -fsS -X POST "${BASE}/tokens" \ + -H "Content-Type: application/json" \ + -d "$(jq -nc --arg i "$NPM_USER" --arg s "$NPM_PASS" \ + '{identity:$i, secret:$s}')" \ + | jq -r '.token') + + if [ -z "$TOKEN" ] || [ "$TOKEN" = "null" ]; then + echo "NPM 토큰 발급 실패: 자격증명을 확인하세요" + exit 1 + fi + AUTH="Authorization: Bearer ${TOKEN}" + + # --------------------------------------------------------------- + # 2. 도메인 중복 체크 (2번째 실행 대응) + # --------------------------------------------------------------- + HOST_ID=$(curl -fsS "${BASE}/nginx/proxy-hosts" -H "$AUTH" \ + | jq -r --arg d "$DOMAIN" \ + 'map(select(.domain_names | index($d))) | .[0].id // empty') + + if [ -n "$HOST_ID" ]; then + echo "이미 존재하는 도메인입니다: ${DOMAIN} (host id=${HOST_ID})" + echo "host-id=${HOST_ID}" >> "$GITHUB_OUTPUT" + echo "created=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + + # --------------------------------------------------------------- + # 3. Let's Encrypt 인증서 발급 — 인증서도 멱등 처리 + # --------------------------------------------------------------- + # 같은 도메인의 기존 인증서가 있으면 재사용합니다. + CERT_ID=$(curl -fsS "${BASE}/nginx/certificates" -H "$AUTH" \ + | jq -r --arg d "$DOMAIN" \ + 'map(select(.provider=="letsencrypt" and (.domain_names | index($d)))) | .[0].id // empty') + + if [ -z "$CERT_ID" ]; then + echo "Let's Encrypt 인증서 발급 중: ${DOMAIN}" + CERT_ID=$(curl -fsS -X POST "${BASE}/nginx/certificates" \ + -H "$AUTH" -H "Content-Type: application/json" \ + -d "$(jq -nc --arg d "$DOMAIN" --arg e "$EMAIL" \ + '{provider:"letsencrypt", + domain_names:[$d], + meta:{letsencrypt_email:$e, letsencrypt_agree:true, dns_challenge:false}}')" \ + | jq -r '.id') + + if [ -z "$CERT_ID" ] || [ "$CERT_ID" = "null" ]; then + echo "인증서 발급 실패: ${DOMAIN} (도메인 DNS가 NPM을 가리키는지 확인하세요)" + exit 1 + fi + else + echo "기존 인증서 재사용: ${DOMAIN} (cert id=${CERT_ID})" + fi + + # --------------------------------------------------------------- + # 4. 프록시 호스트 생성 (SSL 강제 + HTTP/2 고정 적용) + # --------------------------------------------------------------- + echo "프록시 호스트 생성 중: ${DOMAIN} -> ${FORWARD_SCHEME}://${FORWARD_HOST}:${FORWARD_PORT}" + + WS=$([ "$WEBSOCKETS" = "true" ] && echo true || echo false) + + PAYLOAD=$(jq -nc \ + --arg d "$DOMAIN" \ + --arg fh "$FORWARD_HOST" \ + --arg fp "$FORWARD_PORT" \ + --arg fs "$FORWARD_SCHEME" \ + --argjson cert "$CERT_ID" \ + --argjson ws "$WS" \ + '{domain_names:[$d], + forward_host:$fh, + forward_port:($fp|tonumber), + forward_scheme:$fs, + certificate_id:$cert, + ssl_forced:true, + http2_support:true, + hsts_enabled:false, + hsts_subdomains:false, + caching_enabled:false, + allow_websocket_upgrade:$ws, + block_exploits:true, + access_list_id:0, + advanced_config:"", + locations:[], + meta:{}}') + + HOST_ID=$(curl -fsS -X POST "${BASE}/nginx/proxy-hosts" \ + -H "$AUTH" -H "Content-Type: application/json" \ + -d "$PAYLOAD" | jq -r '.id') + + if [ -z "$HOST_ID" ] || [ "$HOST_ID" = "null" ]; then + echo "프록시 호스트 생성 실패: ${DOMAIN}" + exit 1 + fi + + echo "도메인 생성 완료: ${DOMAIN} (host id=${HOST_ID})" + echo "host-id=${HOST_ID}" >> "$GITHUB_OUTPUT" + echo "created=true" >> "$GITHUB_OUTPUT"