fix(setup): 실험대에서 35편을 끝까지 밟고 어긋난 명령·결과 31건을 고친다

test-server 를 비우고 다시 세운 뒤 Setup 기록 35편(virtualization 9 ·
keycloak-session-store 26)을 문서에 적힌 명령 그대로 쳤다. 어긋난 자리를
기록과 SSOT 양쪽에 실측과 함께 넣었다.

막히던 것
- 04 의 인증서 경로가 live/hyeonworks.com 이라 nginx 가 [emerg] 로 안 떴다.
  실제 계보는 live/auth.hyeonworks.com 이고 「문제가 생기면」은 진단이 거꾸로였다
- 인증서가 와일드카드가 아니다. SAN 이 auth·app1·app2 셋뿐이라 그 밖의 이름은
  TLS 에서 끊기고 curl 이 exit 60 · %{http_code} 000 을 낸다. SSOT 안에서
  두 문단이 서로 어긋나 있었다
- A-7 14번 ①이 kc-lab-1 에서 여섯 줄 다 실패하는데 마지막 date 만 「차단」을 찍는다

검사가 실패할 수 없던 자리
- B-1 의 세션 키 고르기는 앞 단계가 $KEY 를 채워 둬서 루프가 한 건도 못 맞혀도
  통과한다. KEY= 로 비우고 키마다 1/0 을 찍게 바꿨다
- k3s-agent 유닛의 sed -i 는 패턴에 $HOME 이 들어 있어 아무 줄도 안 바꾼 채 성공한다

certbot
- renew --dry-run 의 종료 코드는 성공도 0, 실패도 0, 다른 사유의 실패는 1 이다.
  본문의 renew failure(s) 로만 판정할 수 있다
- --dry-run 은 staging 서버를 쓰는데 renewal/*.conf 의 account= 는 운영 계정을
  가리킨다. 실패한 dry-run 이 staging 계정을 하나 더 만들어 다음 실행이 계속 멎는다
- 훅을 755 로 놓고 시뮬레이션이 성공해도 Running deploy-hook command 는 안 나온다.
  certbot 2.1.0 에는 --run-deploy-hooks 도 없다
- 강제 갱신은 실제로 쳤고 서빙까지 닿았다. serial 06F3E0EF…1373 → 065547…3DF1,
  notAfter Dec 3 → Dec 16, nginx worker 2629 4712 → 4745 4754

독자가 칠 수 있는 형태로
- 안 되는 형태가 번호 붙은 단계에 앉아 있던 8곳을 뒤집고, 되는 형태를 ①로 올렸다
- 랩 안에서 공개 이름을 치는 curl 65줄에 --resolve 를 붙였다. 붙인 형태를 실제로
  쳐서 문서가 적은 값과 같은지 확인했다
- 힙독·sed -i·echo >>·&&·|| 를 편집기 + 파일 리스팅 + 분할 형태로 바꿨다
- 닫는 코드펜스가 빠져 뒤 200여 줄의 블록 종류가 뒤집혀 있던 곳을 포함해 3곳을 고쳤다

관문: check_body PASS · check_prose error 0 · check_evidence 두 프로젝트 문제 없음 ·
verify-tech-log-tree error 0 · verify-project-layout error 0 · 코드펜스 전수 0건

남은 것: B-0 주입은 keycloak-pattern 저장소의 소스를 고치고 이미지를 다시 구워야
해서 안 했다(unknown).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
DongHyeonka
2026-09-17 19:38:12 +09:00
co-authored by Claude Opus 5
parent 4a457afde9
commit 32e39e20aa
150 changed files with 6066 additions and 269 deletions
@@ -0,0 +1,13 @@
=== 03 층별 확인 — 이제 밖에서 닿는다 ===
--- 층 ③ 밖에서 도메인으로 (문서: 04 이후엔 301, 이 단계에선 404) ---
HTTP/1.1 302 Found
Server: nginx/1.22.1
Date: Thu, 17 Sep 2026 07:07:31 GMT
Connection: keep-alive
--- 값만 뽑는 형태 ---
302 https://auth.hyeonworks.com/admin/
--- 층 ④ TLS 이후 (아직 인증서가 없다) ---
000
=== app1 도 닿나 (B층·C층이 쓸 경로) ===
app1 200
app1 root 200
@@ -0,0 +1,10 @@
=== 같은 엣지에 세 가지로 친다 — 무엇이 코드를 가르나 ===
① Host 없이 엣지 IP 로 : 000
404
② Host: auth 로 엣지 IP 에 : 302 https://auth.hyeonworks.com/admin/
③ 밖에서 도메인으로 : 302 https://auth.hyeonworks.com/admin/
④ 밖에서 도메인 + /realms/master: 200
=== Keycloak 이 무엇을 보고 그 주소를 만드나 ===
KC_HOSTNAME=https://auth.hyeonworks.com
KC_HOSTNAME_STRICT=true
@@ -0,0 +1,40 @@
=== 01 의 미검증: cloud-init packages 에 certbot 이 있었나 ===
--- 저장소 템플릿
10:hostname: kc-lab-__NODE__
14:users:
15: - name: donghyeon
22: # (22.4.2 on the guests) rejects it and prints the whole users.0 block with
40:packages:
--- 엣지 게스트가 실제로 받은 user-data
19:packages: [curl, nftables]
---
#cloud-config
hostname: kc-lab-edge
fqdn: kc-lab-edge
manage_etc_hosts: true
users:
- name: donghyeon
groups: [sudo]
shell: /bin/bash
sudo: ['ALL=(ALL) NOPASSWD:ALL']
ssh_authorized_keys:
- ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINW0f8garKmfO93vd66yl1t1JtTKN68eRh8K6NuMpBzk test-server -> kc-lab
- ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGhGllPm3iLvB93ITk36Ep0TtAXgBqZZtzvhIwLK8T4j donghyeon@donghyeon-960XGK
ssh_pwauth: false
package_update: true
packages: [curl, nftables]
=== 저장소 템플릿의 packages 절 ===
ssh_pwauth: false
package_update: true
packages:
- curl
- nftables
# kc-lab-edge only. The k3s nodes do not need these, and the edge does not need
# anything else — nginx terminates TLS and certbot renews the certificate, both
# inside this disposable guest.
# - nginx
# - certbot
# - python3-certbot-dns-cloudflare
@@ -0,0 +1,19 @@
=== 01 확인 ① ===
Id Name State
-----------------------------
13 kc-lab-1 running
15 kc-lab-edge running
16 kc-lab-2 running
kc-lab-1
PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
=== 01 확인 ② ===
kc-lab-edge
enp1s0 UP 192.168.122.10/24 metric 100
status: done
=== 01 확인 ③ 스크린샷 ===
Screenshot saved to /tmp/kc1.ppm, with type of image/png
/tmp/kc1.ppm: PNG image data, 720 x 400, 8-bit/color RGB, non-interlaced
@@ -0,0 +1,42 @@
=== 08 「지우기 전에 먼저 본다」 네 값 ===
Id Name State
-----------------------------
13 kc-lab-1 running
15 kc-lab-edge running
16 kc-lab-2 running
--- 볼륨
Name Path
----------------------------------------------------------------------
base.qcow2 /var/lib/libvirt/images/base.qcow2
kc-lab-1.qcow2 /var/lib/libvirt/images/kc-lab-1.qcow2
kc-lab-2.qcow2 /var/lib/libvirt/images/kc-lab-2.qcow2
kc-lab-edge.qcow2 /var/lib/libvirt/images/kc-lab-edge.qcow2
seed-kc-lab-1.iso /var/lib/libvirt/images/seed-kc-lab-1.iso
seed-kc-lab-2.iso /var/lib/libvirt/images/seed-kc-lab-2.iso
seed-kc-lab-edge.iso /var/lib/libvirt/images/seed-kc-lab-edge.iso
--- 예약
<dhcp>
<range start='192.168.122.2' end='192.168.122.254'/>
<host mac='52:54:00:aa:bb:10' name='kc-lab-edge' ip='192.168.122.10'/>
<host mac='52:54:00:aa:bb:11' name='kc-lab-1' ip='192.168.122.11'/>
<host mac='52:54:00:aa:bb:12' name='kc-lab-2' ip='192.168.122.12'/>
</dhcp>
</ip>
</network>
--- 디스크
/dev/nvme0n1p3 226G 18G 197G 9% /
=== 08 이 지키려는 /etc/letsencrypt 가 어느 기계에 있나 ===
--- lab host
ls: cannot open directory '/etc/letsencrypt/': Permission denied
certbot: /usr/bin/certbot
--- kc-lab-edge (03·04 가 nginx·certbot 을 옮겨 둔 곳)
total 16
drwxr-xr-x 3 root root 4096 Sep 17 07:11 .
drwxr-xr-x 66 root root 4096 Sep 17 04:33 ..
-rw-r--r-- 1 root root 207 Nov 12 2021 cli.ini
drwxr-xr-x 5 root root 4096 Sep 17 04:33 renewal-hooks
certbot: /usr/bin/certbot
@@ -0,0 +1,55 @@
=== 09 · lsmod | grep kvm ===
kvm_intel 524288 11
kvm 1490944 6 kvm_intel
irqbypass 16384 1 kvm
=== 09 · 코어 수가 16 인가 8 인가 ===
8
CPU(s): 8
Model name: 11th Gen Intel(R) Core(TM) i5-1135G7 @ 2.40GHz
Thread(s) per core: 2
Core(s) per socket: 4
=== 09 · systemctl status libvirtd.socket ===
● libvirtd.socket - libvirt legacy monolithic daemon socket
Loaded: loaded (/usr/lib/systemd/system/libvirtd.socket; enabled; preset: disabled)
Active: active (running) since Thu 2026-09-03 19:00:35 KST; 1 week 6 days ago
Invocation: e330532b350d4d79ad7f31f7c6512a6c
Triggers: ● libvirtd.service
Listen: /run/libvirt/libvirt-sock (Stream)
Sep 03 19:00:35 test-server systemd[1]: Listening on libvirt legacy monolithic daemon socket.
=== 01 · virsh vol-info 와 net-dumpxml --inactive ===
Name: kc-lab-1.qcow2
Type: file
Capacity: 20.00 GiB
Allocation: 5.40 GiB
---
Name: seed-kc-lab-1.iso
Type: file
Capacity: 370.00 KiB
Allocation: 372.00 KiB
---
<bridge name='virbr0' stp='on' delay='0'/>
<range start='192.168.122.2' end='192.168.122.254'/>
<host mac='52:54:00:aa:bb:10' name='kc-lab-edge' ip='192.168.122.10'/>
<host mac='52:54:00:aa:bb:11' name='kc-lab-1' ip='192.168.122.11'/>
<host mac='52:54:00:aa:bb:12' name='kc-lab-2' ip='192.168.122.12'/>
=== 01 · cloud-init status --long ===
status: done
boot_status_code: enabled-by-generator
last_update: Thu, 17 Sep 2026 04:24:22 +0000
detail:
DataSourceNoCloud [seed=/dev/vdb][dsmode=net]
=== 볼륨 일곱의 용량·할당 ===
base.qcow2 Capacity=3.00 GiB Allocation=323.25 MiB
kc-lab-1.qcow2 Capacity=20.00 GiB Allocation=5.40 GiB
kc-lab-2.qcow2 Capacity=20.00 GiB Allocation=3.85 GiB
kc-lab-edge.qcow2 Capacity=10.00 GiB Allocation=316.26 MiB
seed-kc-lab-1.iso Capacity=370.00 KiB Allocation=372.00 KiB
seed-kc-lab-2.iso Capacity=370.00 KiB Allocation=372.00 KiB
seed-kc-lab-edge.iso Capacity=370.00 KiB Allocation=372.00 KiB
@@ -0,0 +1,39 @@
=== 03 ① 배포된 두 파일이 문서 판인가 ===
1
1
(문서는 0 과 1 이 나와야 맞다고 적는다)
=== 03 ② lab_edge 테이블 실물 ===
table ip lab_edge {
chain prerouting {
type nat hook prerouting priority dstnat; policy accept;
iifname "tailscale0" tcp dport { 80, 443 } dnat to 192.168.122.10
}
chain forward {
type filter hook forward priority filter - 10; policy accept;
ip daddr 192.168.122.10 tcp dport { 80, 443 } ct state new accept
}
}
=== 03 ③ guest_input 체인 — 구멍이 들어가 있나 ===
table ip libvirt_network {
chain guest_input {
oif "virbr0" ip daddr 192.168.122.10 tcp dport { 80, 443 } ct state new counter packets 44 bytes 2640 accept
oif "virbr0" ip daddr 192.168.122.10 tcp dport { 80, 443 } ct state new counter packets 0 bytes 0 accept
oif "virbr0" ip daddr 192.168.122.0/24 ct state established,related counter packets 141705 bytes 2375116597 accept
oif "virbr0" counter packets 5 bytes 300 reject
oif "virbr0" ip daddr 192.168.122.10 tcp dport { 80, 443 } ct state new counter packets 10 bytes 600 accept
}
}
=== 03 ④ 출발지를 덮는 것이 무엇인가 ===
# Warning: table ip filter is managed by iptables-nft, do not touch!
# Warning: table ip nat is managed by iptables-nft, do not touch!
# Warning: table ip6 filter is managed by iptables-nft, do not touch!
# Warning: table ip6 nat is managed by iptables-nft, do not touch!
# Warning: table ip mangle is managed by iptables-nft, do not touch!
# Warning: table ip6 mangle is managed by iptables-nft, do not touch!
122: meta l4proto tcp ip saddr 192.168.122.0/24 ip daddr != 192.168.122.0/24 counter packets 308 bytes 18480 masquerade to :1024-65535
123: meta l4proto udp ip saddr 192.168.122.0/24 ip daddr != 192.168.122.0/24 counter packets 39 bytes 2964 masquerade to :1024-65535
124: ip saddr 192.168.122.0/24 ip daddr != 192.168.122.0/24 counter packets 0 bytes 0 masquerade
@@ -0,0 +1,45 @@
=== tailscale 이 만든 체인 ===
# Warning: table ip filter is managed by iptables-nft, do not touch!
# Warning: table ip nat is managed by iptables-nft, do not touch!
# Warning: table ip6 filter is managed by iptables-nft, do not touch!
# Warning: table ip6 nat is managed by iptables-nft, do not touch!
# Warning: table ip mangle is managed by iptables-nft, do not touch!
# Warning: table ip6 mangle is managed by iptables-nft, do not touch!
2: chain ts-input {
4: iifname "tailscale0" counter packets 1168848 bytes 1390274528 accept
6: ip saddr 100.115.92.0/23 iifname != "tailscale0" counter packets 0 bytes 0 return
7: ip saddr 100.64.0.0/10 iifname != "tailscale0" counter packets 530 bytes 29680 drop
10: chain ts-forward {
11: iifname "tailscale0" counter packets 360 bytes 33127 xt target "MARK"
13: ip saddr 100.64.0.0/10 oifname "tailscale0" counter packets 0 bytes 0 drop
14: oifname "tailscale0" counter packets 265 bytes 95483 accept
19: counter packets 3492404 bytes 4065830972 jump ts-input
24: counter packets 590543 bytes 5494319628 jump ts-forward
28: chain ts-postrouting {
34: counter packets 332016 bytes 24132162 jump ts-postrouting
38: chain ts-input {
40: iifname "tailscale0" counter packets 1 bytes 104 accept
44: chain ts-forward {
45: iifname "tailscale0" counter packets 0 bytes 0 xt target "MARK"
47: oifname "tailscale0" counter packets 0 bytes 0 accept
52: counter packets 435 bytes 159766 jump ts-input
57: counter packets 0 bytes 0 jump ts-forward
61: chain ts-postrouting {
67: counter packets 81 bytes 6580 jump ts-postrouting
152: iifname "tailscale0" tcp dport { 80, 443 } dnat to 192.168.122.10
=== iptables-nft 쪽 nat POSTROUTING ===
-P PREROUTING ACCEPT
-P INPUT ACCEPT
-P OUTPUT ACCEPT
-P POSTROUTING ACCEPT
-N ts-postrouting
-A POSTROUTING -j ts-postrouting
-A ts-postrouting -m mark --mark 0x40000/0xff0000 -j MASQUERADE
=== tailscale 의 SNAT 설정 ===
"AdvertiseTags": null,
"AdvertiseRoutes": null,
"AdvertiseServices": null,
"NoSNAT": false,
"Advertise": false
@@ -0,0 +1,19 @@
=== 02 · API 서버 인증서 SAN ===
X509v3 Subject Alternative Name:
DNS:kubernetes, DNS:kubernetes.default, DNS:kubernetes.default.svc, DNS:kubernetes.default.svc.cluster.local, DNS:localhost, DNS:kc-lab-1, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1, IP Address:192.168.122.11, IP Address:10.43.0.1, IP Address:192.168.122.11
=== 02 · journalctl -u k3s (서버) 끝 6줄 ===
Sep 17 07:37:32 kc-lab-1 k3s[1024]: I0917 07:37:32.358989 1024 reconciler_common.go:299] "Volume detached for volume \"kube-api-access-49x4f\" (UniqueName: \"kubernetes.io/projected/b2a26eb4-c671-4681-b67f-77e47966b53b-kube-api-access-49x4f\") on node \"kc-lab-1\" DevicePath \"\""
Sep 17 07:37:32 kc-lab-1 k3s[1024]: I0917 07:37:32.501306 1024 scope.go:122] "RemoveContainer" containerID="21a239d442f2d69328ebf52e5b29e6706a93f62c622ae201284313a983839424"
Sep 17 07:37:32 kc-lab-1 k3s[1024]: I0917 07:37:32.962363 1024 kubelet_volumes.go:161] "Cleaned up orphaned pod volumes dir" podUID="b2a26eb4-c671-4681-b67f-77e47966b53b" path="/var/lib/kubelet/pods/b2a26eb4-c671-4681-b67f-77e47966b53b/volumes"
Sep 17 07:40:08 kc-lab-1 k3s[1024]: time="2026-09-17T07:40:08Z" level=info msg="COMPACT compactRev=9376 targetCompactRev=9685 currentRev=10685"
Sep 17 07:40:08 kc-lab-1 k3s[1024]: time="2026-09-17T07:40:08Z" level=info msg="COMPACT deleted 380 rows from 309 revisions in 9.152746ms - compacted to 9685/10685"
Sep 17 07:40:08 kc-lab-1 k3s[1024]: time="2026-09-17T07:40:08Z" level=info msg="COMPACT compacted from 9376 to 9685 in 1 transactions over 9ms"
=== 02 · journalctl -u k3s-agent (에이전트) 끝 6줄 ===
Sep 17 07:42:16 kc-lab-2 k3s[2644]: E0917 07:42:16.919786 2644 conn.go:353] "Error on socket receive" err="read tcp 127.0.0.1:10250->127.0.0.1:55428: use of closed network connection"
Sep 17 07:42:17 kc-lab-2 k3s[2644]: E0917 07:42:17.005337 2644 conn.go:353] "Error on socket receive" err="read tcp 127.0.0.1:10250->127.0.0.1:55436: use of closed network connection"
Sep 17 07:42:17 kc-lab-2 k3s[2644]: E0917 07:42:17.092765 2644 conn.go:353] "Error on socket receive" err="read tcp 127.0.0.1:10250->127.0.0.1:55450: use of closed network connection"
Sep 17 07:42:24 kc-lab-2 k3s[2644]: E0917 07:42:24.627959 2644 conn.go:353] "Error on socket receive" err="read tcp 127.0.0.1:10250->127.0.0.1:40132: use of closed network connection"
Sep 17 07:42:25 kc-lab-2 k3s[2644]: E0917 07:42:25.072360 2644 conn.go:353] "Error on socket receive" err="read tcp 127.0.0.1:10250->127.0.0.1:40148: use of closed network connection"
Sep 17 07:42:26 kc-lab-2 k3s[2644]: E0917 07:42:26.574554 2644 conn.go:353] "Error on socket receive" err="read tcp 127.0.0.1:10250->127.0.0.1:40158: use of closed network connection"
@@ -0,0 +1,7 @@
=== 04 3단계 ④ 권한과 바이트 수 (값은 안 본다) ===
ls: cannot access '/etc/letsencrypt/cloudflare.ini': No such file or directory
wc: /etc/letsencrypt/cloudflare.ini: No such file or directory
=== 04 3단계 ⑤ 토큰이 살아 있고 권한 범위가 맞는지 (응답의 상태 문자열만) ===
awk: cannot open /etc/letsencrypt/cloudflare.ini (No such file or directory)
{"success":false,"errors":[{"code":6003,"message":"Invalid request headers","error_chain":[{"code":6111,"message":"Invalid format for Authorization header"}]}],"messages":[],"result":null}
@@ -0,0 +1,19 @@
=== 랩 호스트의 인증서 ===
Saving debug log to /var/log/letsencrypt/letsencrypt.log
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Found the following certs:
Certificate Name: auth.hyeonworks.com
Serial Number: 6f3e0ef4d1bb03de58130eaad1176101373
Key Type: ECDSA
Identifiers: auth.hyeonworks.com app1.hyeonworks.com app2.hyeonworks.com
Expiry Date: 2026-12-03 11:29:17+00:00 (VALID: 77 days)
Certificate Path: /etc/letsencrypt/live/auth.hyeonworks.com/fullchain.pem
Private Key Path: /etc/letsencrypt/live/auth.hyeonworks.com/privkey.pem
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
=== renewal 설정의 authenticator (04 의 오랜 미검증) ===
zsh:1: no matches found: /etc/letsencrypt/renewal/*.conf
=== 토큰이 살아 있나 (상태 문자열만) ===
{"success":false,"errors":[{"code":6003,"message":"Invalid request headers","error_chain":[{"code":6111,"message":"Invalid format for Authorization header"}]}],"messages":[],"result":null}
@@ -0,0 +1,11 @@
=== 04 의 오랜 미검증: authenticator 가 무엇인가 ===
--- 문서 형태 (글로브가 sudo 앞에서 펼쳐진다)
zsh:1: no matches found: /etc/letsencrypt/renewal/*.conf
--- sudo 안에서 펼치게 고친 형태
/etc/letsencrypt/renewal/auth.hyeonworks.com.conf:authenticator = dns-cloudflare
/etc/letsencrypt/renewal/auth.hyeonworks.com.conf:dns_cloudflare_credentials = /etc/letsencrypt/cloudflare.ini
/etc/letsencrypt/renewal/auth.hyeonworks.com.conf:server = https://acme-v02.api.letsencrypt.org/directory
=== 토큰 검증 (값은 안 찍는다) ===
토큰 길이 0자
{"success":false,"errors":[{"code":6003,"message":"Invalid request headers","error_chain":[{"code":6111,"message":"Invalid format for Authorization header"}]}],"messages":[],"result":null}
@@ -0,0 +1,18 @@
=== 호스트 → 엣지로 인증서를 옮긴다 (값은 화면에 안 나온다) ===
Found the following certs:
Certificate Name: auth.hyeonworks.com
Serial Number: 6f3e0ef4d1bb03de58130eaad1176101373
Key Type: ECDSA
Domains: auth.hyeonworks.com app1.hyeonworks.com app2.hyeonworks.com
Expiry Date: 2026-12-03 11:29:17+00:00 (VALID: 77 days)
Certificate Path: /etc/letsencrypt/live/auth.hyeonworks.com/fullchain.pem
Private Key Path: /etc/letsencrypt/live/auth.hyeonworks.com/privkey.pem
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
=== 엣지에서 파일 권한 확인 ===
total 4
-rw-r--r-- 1 root root 692 Sep 3 01:47 README
lrwxrwxrwx 1 root root 43 Sep 4 12:29 cert.pem -> ../../archive/auth.hyeonworks.com/cert3.pem
lrwxrwxrwx 1 root root 44 Sep 4 12:29 chain.pem -> ../../archive/auth.hyeonworks.com/chain3.pem
lrwxrwxrwx 1 root root 48 Sep 4 12:29 fullchain.pem -> ../../archive/auth.hyeonworks.com/fullchain3.pem
lrwxrwxrwx 1 root root 46 Sep 4 12:29 privkey.pem -> ../../archive/auth.hyeonworks.com/privkey3.pem
@@ -0,0 +1,7 @@
=== live 아래에 무엇이 있나 ===
README
auth.hyeonworks.com
=== 04 5단계를 문서 그대로 (live/hyeonworks.com) 쳤을 때 ===
2026/09/17 07:47:15 [emerg] 2530#2530: cannot load certificate "/etc/letsencrypt/live/hyeonworks.com/fullchain.pem": BIO_new_file() failed (SSL: error:80000002:system library::No such file or directory:calling fopen(/etc/letsencrypt/live/hyeonworks.com/fullchain.pem, r) error:10000080:BIO routines::no such file)
nginx: configuration file /etc/nginx/nginx.conf test failed
@@ -0,0 +1,13 @@
=== 경로를 live/auth.hyeonworks.com 으로 고친다 ===
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
RELOADED
=== 엣지가 443 을 듣나 ===
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=2547,fd=5),("nginx",pid=1065,fd=5))
LISTEN 0 511 0.0.0.0:443 0.0.0.0:* users:(("nginx",pid=2547,fd=10),("nginx",pid=1065,fd=10))
=== 밖에서 ===
https://auth.hyeonworks.com/realms/master 400
https://app1.hyeonworks.com/ 400
http://auth.hyeonworks.com/ 301
@@ -0,0 +1,22 @@
=== 400 의 본문과 머리 ===
HTTP/2 400
server: nginx/1.22.1
date: Thu, 17 Sep 2026 07:47:28 GMT
content-type: text/plain; charset=utf-8
400 Bad Request: malformed Host header
=== TLS 협상 자체는 되나 ===
http_version=2 ssl_verify=0 code=400
--- HTTP/1.1 로 강제하면
code=400
=== 엣지 로그 ===
192.168.122.1 - - [17/Sep/2026:07:47:28 +0000] "GET /realms/master HTTP/2.0" 400 38 "-" "curl/8.5.0"
192.168.122.1 - - [17/Sep/2026:07:47:28 +0000] "GET /realms/master HTTP/2.0" 400 38 "-" "curl/8.5.0"
192.168.122.1 - - [17/Sep/2026:07:47:28 +0000] "GET /realms/master HTTP/1.1" 400 49 "-" "curl/8.5.0"
---
2026/09/17 04:27:39 [notice] 1065#1065: using inherited sockets from "5;6;"
2026/09/17 05:39:03 [error] 1106#1106: *31 connect() failed (113: No route to host) while connecting to upstream, client: 192.168.122.1, server: _, request: "GET /realms/master HTTP/1.1", upstream: "http://192.168.122.12:80/realms/master", host: "auth.hyeonworks.com"
2026/09/17 05:40:37 [error] 1106#1106: *38 connect() failed (113: No route to host) while connecting to upstream, client: 192.168.122.1, server: _, request: "GET /realms/master HTTP/1.1", upstream: "http://192.168.122.12:80/realms/master", host: "auth.hyeonworks.com"
2026/09/17 05:46:21 [error] 1106#1106: *47 connect() failed (111: Connection refused) while connecting to upstream, client: 192.168.122.1, server: _, request: "GET /realms/master HTTP/1.1", upstream: "http://192.168.122.12:80/realms/master", host: "auth.hyeonworks.com"
2026/09/17 05:48:45 [error] 1106#1106: *54 connect() failed (111: Connection refused) while connecting to upstream, client: 192.168.122.1, server: _, request: "GET /realms/master HTTP/1.1", upstream: "http://192.168.122.12:80/realms/master", host: "auth.hyeonworks.com"
@@ -0,0 +1,15 @@
=== 층 ① 엣지에서 Traefik 에 직접 (Host 헤더를 손으로) ===
200
=== 층 ② 엣지의 443 에 엣지 자신이 (localhost) ===
400
=== 층 ② 엣지의 80 (리다이렉트 확인) ===
301 -> https://\auth.hyeonworks.com\/realms/master
=== 지금 깔린 443 블록의 Host 관련 줄 ===
7: listen 80 default_server;
13: listen 443 ssl http2 default_server;
21: proxy_pass http://k3s_traefik;
23: proxy_set_header Host \$host;
24: proxy_set_header X-Forwarded-Host \$host;
@@ -0,0 +1,11 @@
23: proxy_set_header Host $host;
24: proxy_set_header X-Forwarded-Host $host;
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
RELOADED
=== 밖에서 ===
https://auth.hyeonworks.com/realms/master 400
https://app1.hyeonworks.com/ 200
https://app2.hyeonworks.com/ 302
http://auth.hyeonworks.com/ 301
@@ -0,0 +1,23 @@
=== auth 의 400 본문 ===
HTTP/2 200
server: nginx/1.22.1
date: Thu, 17 Sep 2026 07:47:56 GMT
content-type: application/json;charset=UTF-8
content-length: 602
cache-control: no-cache
referrer-policy: no-referrer
strict-transport-security: max-age=31536000; includeSubDomains
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
x-robots-tag: none
=== 다른 경로도 400 인가 ===
/ 302
/realms/master 200
/realms/keycloak-patterns 200
/admin/ 302
=== 엣지에서 Traefik 에 Host 를 주고 직접 (대조군) ===
http 200
http+XFP-https 200
@@ -0,0 +1,7 @@
=== certbot 의 진짜 종료 코드 ===
certbot exit=0
11
=== 인증서만 옮기면 따라오지 않는 둘을 마저 옮긴다 ===
ls: cannot access '/etc/letsencrypt/accounts/*/*/*': No such file or directory
81 /etc/letsencrypt/cloudflare.ini
@@ -49,3 +49,11 @@
43-dominfo-vs-config.txt 같은 순간의 dominfo(5242880) 와 dumpxml --inactive(4194304). 바꾼 것이 들어갔는지는 --inactive 가 답한다
44-host-prep-checks.txt 00 의 확인 명령 전부 — vmx, kvm 모듈 셋, 저장소, virsh 12.7.0, libvirtd.socket, libvirt 그룹, default 네트워크, sudo 없는 virsh list
45-libvirt-uri-mechanism.txt rc 파일은 비대화형 ssh 에서 안 읽힌다. 그런데도 되는 까닭은 ~/.config/libvirt/libvirt.conf 의 uri_default
48 cloud-init 의 packages 에 certbot 이 없다는 것 (게스트 user-data + 저장소 템플릿)
49 01 의 확인 ①②③ 을 다시 친 결과
50 08 의 철거 전 네 값과, 인증서가 어느 기계에 있는지
51 (철거는 실행하지 않았다 — 파일 없음)
52 09 의 lsmod·코어 수·libvirtd.socket, 01 의 vol-info·net-dumpxml·cloud-init status
53 sudo 가 열린 뒤 doc 03 의 미검증 셋 — 배포 파일 상태·guest_input 중복·masquerade 찾기
54 출발지를 덮는 것이 Tailscale 이라는 근거 (ts-forward MARK → ts-postrouting MASQUERADE)
55 doc 02 의 API 서버 SAN 과 두 journalctl