소개
Corner Shop 주문 애플리케이션은 비공개 PostgreSQL 데이터베이스에서 레코드를 읽고 생성합니다. 페이지는 작동하지만 Visitor도 데이터베이스에 직접 연결할 수 있습니다. 따라서 애플리케이션이 작동한다고 해서 데이터베이스 경계가 안전한 것은 아닙니다.
실제 연결을 비교하고 준비된 Gateway에서 전달을 제한하면서 주문과 공개 상태 서비스를 유지합니다. 기본 터미널 사용 능력만 필요합니다. 모든 인물과 기록은 합성 데이터입니다. 세 터미널 노드는 별도 네트워크 네임스페이스를 가진 컨테이너이며 하나의 인스턴스 커널을 공유합니다. 독립적인 VM이 아닙니다.
주문과 네트워크 경로 추적하기
이 단계에서는 실제 주문을 만들고 데이터베이스로 향하는 두 경로를 비교합니다. 애플리케이션은 고객이 사용하는 프로그램이고 데이터베이스는 기록 저장소입니다. Order Page를 열고 seed: Green tea를 찾은 뒤 Order reference에 practice-1을 입력하고 Create tea order를 누르세요. 새 행은 화면에만 표시되는 것이 아니라 PostgreSQL에 저장됩니다.
네트워크 지도의 IP 주소는 네트워크 목적지를 식별합니다. 포트는 해당 주소의 서비스를 식별합니다. PostgreSQL은 172.28.20.10:5432에서 수신하고 별도의 공개 상태 서비스는 8001을 사용합니다. App Client(172.28.10.20)는 데이터베이스가 필요하지만 Visitor(172.28.10.30)는 공개 상태만 필요합니다.
App Client로 전환하세요. 준비된 tcp-probe는 실제 TCP 연결을 열며 최대 3초 기다립니다. 두 인수는 목적지 주소와 포트입니다.
tcp-probe 172.28.20.10 5432
TCP OPEN이 표시됩니다. 이는 도달성을 증명하며 기록 읽기 권한을 증명하지는 않습니다. psql은 PostgreSQL 쿼리 클라이언트입니다. 다음 명령은 이번 실행에만 연습용 비밀번호를 제공합니다. 따옴표 안 연결 문자열은 서버, 포트, 사용자, 데이터베이스, 3초 연결 제한을 묶습니다. -c는 따옴표 안 SQL을 실행합니다. SELECT는 orders에서 지정 열을 읽고 ORDER BY는 정렬합니다. 세미콜론은 SQL 문을 끝냅니다.
PGPASSWORD=training-only psql "host=172.28.20.10 port=5432 user=shopapp dbname=shop connect_timeout=3" -c "SELECT id, item FROM orders ORDER BY id;"
결과에서 practice-1과 Green tea를 찾으세요. 의도적으로 알려진 자격 증명은 그대로 두어 네트워크 변화만 관찰합니다.
Visitor로 전환하고 두 명령을 반복하세요.
tcp-probe 172.28.20.10 5432
PGPASSWORD=training-only psql "host=172.28.20.10 port=5432 user=shopapp dbname=shop connect_timeout=3" -c "SELECT id, item FROM orders ORDER BY id;"
둘 다 성공합니다. 불필요한 클라이언트가 연결해 합성 주문을 읽을 수 있습니다. 잘못된 비밀번호라면 PostgreSQL에 도달한 뒤 인증 오류가 발생하며 네트워크 포트는 여전히 노출됩니다.
경로는 노드가 패킷을 어디로 보낼지 알려줍니다. Visitor에서 데이터베이스로 향하는 준비된 경로를 확인하세요.
ip route get 172.28.20.10
중요한 필드는 via 172.28.10.254(Gateway)와 src 172.28.10.30(Visitor)입니다. App Client는 같은 게이트웨이를 다른 출발지로 사용합니다. 데이터베이스의 반환 경로는 Gateway의 다른 인터페이스인 172.28.20.254를 가리킵니다. 두 클라이언트 모두 데이터베이스 망에 직접 연결된 인터페이스가 없습니다. 경로는 준비된 기반 시설이므로 변경하지 마세요. 이 단계는 실제 주문 기록으로 확인하며 보고서는 제출하지 않습니다.
애플리케이션의 데이터베이스 경로만 허용하기
이 단계에서는 Gateway를 통과하는 패킷을 필터링합니다. Gateway로 전환하세요. iptables는 커널의 패킷 필터 규칙을 수정합니다. 체인은 순서가 있는 목록으로 처음 일치하는 결정이 적용됩니다. FORWARD는 노드를 통과하는 트래픽을, INPUT은 노드 자체로 향하는 트래픽을 처리합니다. 환경은 FORWARD를 실습 소유 체인인 LAB_FORWARD에 연결합니다. 초기의 광범위한 허용 규칙이 노출을 발생시킵니다.
현재 규칙을 확인하세요. -n은 숫자 주소를 유지하고 -v는 패킷 수를 포함하며 -L은 지정 체인을 나열합니다.
iptables -nvL LAB_FORWARD
이 터미널의 LAB_FORWARD만 수정하세요. 호스트 규칙, 경로, 자격 증명, 서비스를 유지하세요. 규칙은 즉시 적용되고 이번 실습 세션 동안 유지됩니다. 광범위한 규칙을 네 가지 결정으로 바꿉니다. 일치하지 않은 패킷은 준비된 상위 체인이 거부합니다.
-F는 지정 체인을 비웁니다. 규칙을 추가하는 동안 전달 서비스가 잠시 차단되지만 터미널과 Order Page 인터페이스는 계속 접근할 수 있습니다. 오타가 나면 이 비우기부터 시작하여 아래 네 규칙을 모두 다시 입력하세요.
iptables -F LAB_FORWARD
응답도 돌아와야 합니다. 예를 들어 데이터베이스의 응답은 App Client로 향하므로 목적지는 더 이상 데이터베이스 포트가 아닙니다. -A는 규칙을 끝에 추가하고 -m conntrack는 연결 추적을 사용하며 --ctstate ESTABLISHED,RELATED는 이미 허용된 연결의 응답이나 관련 트래픽을 식별합니다. -j ACCEPT는 일치하는 패킷을 허용합니다.
iptables -A LAB_FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
이제 애플리케이션의 새 연결을 허용합니다. -s는 출발지, -d는 목적지, -p tcp는 TCP, --dport는 목적지 포트와 일치시킵니다. 단일 주소는 정확히 그 노드를 뜻합니다.
iptables -A LAB_FORWARD -s 172.28.10.20 -d 172.28.20.10 -p tcp --dport 5432 -j ACCEPT
애플리케이션 예외 뒤에 일반적인 데이터베이스 거부를 배치합니다. -s가 없으므로 다른 출발지에 적용됩니다. REJECT --reject-with tcp-reset은 무응답으로 시간 초과를 기다리게 하는 대신 TCP 연결을 명시적으로 거부합니다.
iptables -A LAB_FORWARD -d 172.28.20.10 -p tcp --dport 5432 -j REJECT --reject-with tcp-reset
마지막으로 두 클라이언트의 별도 공개 상태 포트를 유지합니다. 이 서비스를 허용해도 데이터베이스 포트는 열리지 않습니다.
iptables -A LAB_FORWARD -d 172.28.20.10 -p tcp --dport 8001 -j ACCEPT
결과를 나열하세요.
iptables -nvL LAB_FORWARD
순서는 응답, 애플리케이션 데이터베이스 접근, 일반 데이터베이스 거부, 공개 상태 접근입니다. 거부가 애플리케이션 예외보다 앞서면 상점도 차단됩니다. 일치하는 트래픽이 실제 Gateway를 통과할 때만 패킷 수가 증가합니다. 규칙 존재만으로 성공을 증명할 수 없습니다. Order Page를 새로 고치세요. App Client는 OPEN, Visitor는 BLOCKED, 공개 상태는 PUBLIC STATUS OK이고 주문은 계속 로드되어야 합니다.
보안과 상점 다시 시험하기
이 단계에서는 연결 표를 확인하고 또 하나의 실제 주문을 완료합니다. App Client에서 새 연결을 만드세요.
tcp-probe 172.28.20.10 5432
TCP OPEN을 예상합니다. 같은 자격 증명으로 다시 읽으세요.
PGPASSWORD=training-only psql "host=172.28.20.10 port=5432 user=shopapp dbname=shop connect_timeout=3" -c "SELECT id, item FROM orders ORDER BY id;"
Visitor로 전환하세요.
tcp-probe 172.28.20.10 5432
TCP BLOCKED와 연결 거부 설명이 나와야 합니다. 이 명령은 의도적으로 실패 상태를 반환합니다. 조용히 폐기하면 대신 시간 초과가 발생합니다. 둘 다 새 연결을 막지만 시간 초과만으로 원인을 알 수는 없습니다. App Client도 실패한다면 Gateway에서 규칙 순서와 응답 처리를 확인하세요.
curl은 웹 리소스를 가져옵니다. --max-time 4는 전체 요청을 4초로 제한하고 -f는 HTTP 오류를 실패로 처리하며 -s는 진행 표시를 숨기고 -S는 오류를 계속 표시합니다. Visitor에서 별도의 공개 서비스를 확인하세요.
curl --max-time 4 -fsS http://172.28.20.10:8001/status.txt
PUBLIC STATUS OK를 예상합니다. 모든 전달 패킷을 막는 방법은 데이터베이스 경로를 닫더라도 이 요구를 충족하지 못합니다.
Order Page에서 practice-2를 만들고 두 연습 주문이 보이는지 확인하세요. 이는 수정 후 애플리케이션의 실제 데이터베이스 연결을 사용합니다. 다시 새로 고쳐도 Visitor는 차단되어야 합니다. Gateway 카운터는 문제 진단에 도움이 되지만 실제 증거는 성공한 주문과 허용·차단 연결 시험입니다. PostgreSQL을 멈추거나 비밀번호를 바꿔 방문자 쿼리 실패를 보안 수정처럼 보이게 하지 마세요.
요약
주문이 PostgreSQL에 들어가는 흐름을 추적하고 경로를 통해 접속하는 두 클라이언트를 비교했으며 순서 있는 전달 규칙을 적용하고 보안과 가용성을 시험했습니다. 비밀번호 오류와 TCP 연결 차단은 다릅니다. 노출 경로를 닫을 때도 정상 애플리케이션과 별도 서비스를 유지하세요.



