Infosec403

iptables_발표자료.html

26.09 KB · text/html · 업로드 2026-07-23 13:52

<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>iptables — 핵심 요약 & 실습</title>
<style>
  :root{
    --bg:#f5f5f7;
    --bg-deep:#e8e8ed;
    --ink:#1d1d1f;
    --ink-soft:#6e6e73;
    --blue:#0071e3;
    --blue-deep:#0058b0;
    --card:#ffffff;
    --line:rgba(0,0,0,0.08);
    --code-bg:#1d1d1f;
    --code-line:#2c2c2e;
    --green:#59d499;
    --orange:#f5a623;
    --pink:#ff6482;
    --purple:#bf8cff;
    --cyan:#6fd6ff;
    --radius:22px;
  }

  *{ box-sizing:border-box; }
  html,body{
    margin:0; padding:0; height:100%;
    background:var(--bg);
    color:var(--ink);
    font-family:-apple-system, BlinkMacSystemFont, "SF Pro Display", "SF Pro Text",
      "Helvetica Neue", Arial, "Apple SD Gothic Neo", "Malgun Gothic", sans-serif;
    overflow:hidden;
    -webkit-font-smoothing:antialiased;
  }

  #deck{ position:relative; width:100vw; height:100vh; }

  .slide{
    position:absolute; inset:0;
    display:flex; flex-direction:column; justify-content:center; align-items:center;
    padding:6vh 8vw;
    opacity:0;
    transform:translateY(24px) scale(0.985);
    transition:opacity .55s cubic-bezier(.2,.8,.2,1), transform .55s cubic-bezier(.2,.8,.2,1);
    pointer-events:none;
  }
  .slide.active{ opacity:1; transform:translateY(0) scale(1); pointer-events:auto; }

  .content{ width:100%; max-width:1000px; }

  .eyebrow{
    font-size:14px; font-weight:600; letter-spacing:.04em;
    text-transform:uppercase; color:var(--blue); margin-bottom:14px;
  }

  h1.big{
    font-size:clamp(38px, 5.6vw, 68px);
    font-weight:700; letter-spacing:-0.02em;
    margin:0 0 10px 0; line-height:1.08;
  }

  h2.title{
    font-size:clamp(26px, 3.6vw, 42px);
    font-weight:700; letter-spacing:-0.015em;
    margin:0 0 26px 0; line-height:1.15;
  }

  p.sub{
    font-size:clamp(15px, 1.8vw, 21px);
    color:var(--ink-soft); font-weight:400; margin:0; line-height:1.55;
  }

  .grid{ display:grid; grid-template-columns:repeat(2, 1fr); gap:16px; margin-top:8px; }
  .grid.cols-3{ grid-template-columns:repeat(3,1fr); }
  .grid.cols-4{ grid-template-columns:repeat(4,1fr); }
  .grid.cols-1{ grid-template-columns:1fr; }

  .card{
    background:var(--card); border:1px solid var(--line); border-radius:18px;
    padding:20px 22px; box-shadow:0 1px 2px rgba(0,0,0,0.04);
  }
  .card h3{ margin:0 0 8px 0; font-size:18px; font-weight:600; letter-spacing:-0.01em; }
  .card p{ margin:0; font-size:14.5px; color:var(--ink-soft); line-height:1.55; }
  .card .tag{
    display:inline-block; font-size:12px; font-weight:700; color:var(--blue);
    background:rgba(0,113,227,.09); border-radius:20px; padding:3px 10px;
    margin-bottom:10px; letter-spacing:.01em;
  }
  .card.ok .tag{ color:#1e9e5a; background:rgba(30,158,90,.1); }
  .card.no .tag{ color:#e0433d; background:rgba(224,67,61,.1); }

  table.compare{
    width:100%; border-collapse:separate; border-spacing:0;
    background:var(--card); border-radius:18px; overflow:hidden;
    box-shadow:0 1px 2px rgba(0,0,0,0.04); border:1px solid var(--line);
  }
  table.compare th, table.compare td{
    padding:13px 18px; text-align:left; font-size:14.5px; border-bottom:1px solid var(--line);
  }
  table.compare th{
    background:var(--bg-deep); font-weight:600; font-size:12.5px; color:var(--ink-soft);
    text-transform:uppercase; letter-spacing:.03em;
  }
  table.compare tr:last-child td{ border-bottom:none; }
  table.compare td.emph{ font-weight:600; color:var(--ink); }
  table.compare td.accent{ color:var(--blue); font-weight:600; }

  .terminal{
    background:var(--code-bg); border-radius:16px; overflow:hidden;
    box-shadow:0 20px 45px -18px rgba(0,0,0,0.45), 0 1px 0 rgba(255,255,255,0.04) inset;
    margin-top:10px; width:100%;
  }
  .terminal-bar{
    display:flex; align-items:center; gap:8px; padding:10px 14px;
    background:var(--code-line); border-bottom:1px solid rgba(255,255,255,0.06);
  }
  .dot{ width:11px; height:11px; border-radius:50%; }
  .dot.red{ background:#ff5f57; }
  .dot.yellow{ background:#febc2e; }
  .dot.green{ background:#28c840; }
  .terminal-title{
    flex:1; text-align:center; font-size:12px; color:#98989d;
    font-family:ui-monospace, "SF Mono", Menlo, monospace; margin-right:52px;
  }
  pre.code{
    margin:0; padding:18px 20px;
    font-family:ui-monospace, "SF Mono", "Menlo", "Consolas", monospace;
    font-size:14.5px; line-height:1.6; color:#e6e6e6;
    overflow-x:auto; white-space:pre;
  }
  .c-cmt{ color:#7d7d80; }
  .c-flag{ color:var(--cyan); }
  .c-val{ color:var(--orange); }
  .c-str{ color:var(--green); }
  .c-key{ color:var(--pink); font-weight:600; }
  .c-mod{ color:var(--purple); }

  .caption{ margin-top:10px; font-size:13.5px; color:var(--ink-soft); }
  .caption b{ color:var(--ink); font-weight:600; }

  .test-row{
    display:flex; align-items:center; gap:10px; margin-top:12px;
    font-size:14.5px; color:var(--ink); background:var(--card);
    border:1px solid var(--line); border-radius:14px; padding:12px 16px;
  }
  .test-row .badge{
    flex-shrink:0; font-size:11.5px; font-weight:700; letter-spacing:.03em;
    padding:3px 9px; border-radius:20px; color:#fff;
  }
  .badge.pass{ background:#1e9e5a; }
  .badge.block{ background:#e0433d; }

  ul.clean{ list-style:none; margin:0; padding:0; }
  ul.clean li{
    position:relative; padding-left:22px; margin-bottom:11px;
    font-size:16px; line-height:1.5; color:var(--ink);
  }
  ul.clean li::before{
    content:""; position:absolute; left:0; top:9px;
    width:7px; height:7px; border-radius:50%; background:var(--blue);
  }
  ul.clean li b{ font-weight:600; }
  ul.clean li span.mono{
    font-family:ui-monospace, "SF Mono", monospace; background:rgba(0,0,0,0.06);
    border-radius:6px; padding:1px 7px; font-size:.92em;
  }

  .flow{ display:flex; align-items:center; justify-content:center; gap:14px; margin-top:10px; flex-wrap:wrap; }
  .flow .step{
    background:var(--card); border:1px solid var(--line); border-radius:16px;
    padding:18px 22px; text-align:center; min-width:150px;
    box-shadow:0 1px 2px rgba(0,0,0,0.04);
  }
  .flow .step .k{ font-size:11.5px; font-weight:700; color:var(--blue); text-transform:uppercase; letter-spacing:.04em; margin-bottom:6px; }
  .flow .step .v{ font-size:18px; font-weight:600; }
  .flow .step .d{ font-size:12.5px; color:var(--ink-soft); margin-top:4px; }
  .flow .arrow{ font-size:20px; color:var(--ink-soft); }

  #hud{ position:fixed; left:0; right:0; bottom:26px; display:flex; justify-content:center; align-items:center; gap:8px; z-index:10; }
  .pip{ width:7px; height:7px; border-radius:50%; background:rgba(0,0,0,0.18); transition:all .3s ease; cursor:pointer; }
  .pip.active{ background:var(--blue); width:22px; border-radius:4px; }

  #counter{
    position:fixed; top:26px; right:34px; font-size:13px; color:var(--ink-soft);
    font-weight:600; letter-spacing:.02em; z-index:10; font-family:ui-monospace, "SF Mono", monospace;
  }
  #brand{ position:fixed; top:26px; left:34px; font-size:13px; font-weight:700; color:var(--ink-soft); letter-spacing:.02em; z-index:10; }
  #brand b{ color:var(--ink); }

  .navzone{ position:fixed; top:0; bottom:0; width:14%; z-index:5; cursor:pointer; }
  #zone-prev{ left:0; }
  #zone-next{ right:0; }

  ::-webkit-scrollbar{ display:none; }
</style>
</head>
<body>

<div id="brand"><b>iptables</b> · 핵심 요약 & 실습</div>
<div id="counter">01 / 13</div>

<div id="deck">

  <!-- 0. 방화벽이란? -->
  <section class="slide active">
    <div class="content">
      <div class="eyebrow">정의</div>
      <h2 class="title">방화벽이란?</h2>
      <ul class="clean" style="margin-bottom:22px;">
        <li>네트워크를 통과하는 패킷을 검사하여 미리 정한 정책에 따라 <b>허용하거나 차단하는 기능(또는 시스템)</b></li>
        <li>리눅스에서는 <b>Netfilter</b>가 방화벽 기능을 담당 — 즉, <b>Netfilter가 실제 방화벽</b></li>
        <li><b>iptables</b>는 Netfilter에게 "이렇게 동작해라"고 규칙을 등록하는 <b>관리 프로그램</b></li>
      </ul>
      <div class="card">
        <span class="tag">Netfilter</span>
        <h3>리눅스 커널에 내장된 방화벽 프레임워크</h3>
        <p>패킷 필터링, 상태 기반(Stateful) 패킷 검사, NAT(Network Address Translation) 등의 기능을 제공한다. iptables와 nftables는 이러한 Netfilter 기능을 설정·관리하기 위한 사용자 공간 도구다.</p>
      </div>
    </div>
  </section>

  <!-- 1. Title -->
  <section class="slide">
    <div class="content" style="text-align:center;">
      <div class="eyebrow" style="justify-content:center; display:flex;">LINUX FIREWALL</div>
      <h1 class="big">iptables</h1>
      <p class="sub">미리 정의된 보안 정책에 따라 트래픽을 검사하고 허용·차단하는 보안 시스템</p>
      <div class="flow" style="margin-top:52px;">
        <div class="step"><div class="k">What</div><div class="v">Table</div><div class="d">무슨 일을 할 것인가</div></div>
        <div class="arrow">→</div>
        <div class="step"><div class="k">When</div><div class="v">Chain</div><div class="d">언제 처리할 것인가</div></div>
        <div class="arrow">→</div>
        <div class="step"><div class="k">How</div><div class="v">Rule</div><div class="d">어떻게 처리할 것인가</div></div>
      </div>
    </div>
  </section>

  <!-- 5. 구조: Table/Chain/Rule -->
  <section class="slide">
    <div class="content">
      <div class="eyebrow">iptables의 구조</div>
      <h2 class="title">Table → Chain → Rule</h2>
      <div class="grid cols-3" style="margin-bottom:18px;">
        <div class="card"><span class="tag">WHAT</span><h3>Table</h3><p>목적별 작업 공간 — 허용/차단, NAT, 패킷 수정 등</p></div>
        <div class="card"><span class="tag">WHEN</span><h3>Chain</h3><p>패킷이 처리되는 시점 — 들어올 때, 나갈 때, 전달될 때</p></div>
        <div class="card"><span class="tag">HOW</span><h3>Rule</h3><p>실제 동작 정의 — 조건에 맞으면 허용/차단/변환</p></div>
      </div>
      <p class="sub" style="margin-bottom:14px;">패킷을 처리할 목적에 따라 여러 <b style="color:var(--ink)">테이블</b>로 나뉘고, 각 테이블 안에는 패킷이 검사받는 타이밍인 <b style="color:var(--ink)">체인</b>이 있으며, 체인에는 실제 동작을 정의하는 <b style="color:var(--ink)">규칙</b>이 순서대로 등록된다.</p>
      <div class="terminal">
        <div class="terminal-bar"><div class="dot red"></div><div class="dot yellow"></div><div class="dot green"></div><div class="terminal-title">bash</div></div>
        <pre class="code"><span class="c-key">iptables</span> <span class="c-flag">-A</span> INPUT <span class="c-flag">-p</span> tcp <span class="c-flag">--dport</span> <span class="c-val">22</span> <span class="c-flag">-j</span> ACCEPT   <span class="c-cmt"># filter(기본) · INPUT · 22번 허용</span></pre>
      </div>
    </div>
  </section>

  <!-- 5-1. 구조 상세: 패킷 처리 흐름 -->
  <section class="slide">
    <div class="content">
      <div class="eyebrow">iptables의 구조 · 상세</div>
      <h2 class="title">패킷 한 개가 처리되는 순서</h2>
      <div class="flow" style="margin-bottom:22px;">
        <div class="step"><div class="k">Step 1</div><div class="v">Table 진입</div><div class="d">목적에 맞는 테이블로</div></div>
        <div class="arrow">→</div>
        <div class="step"><div class="k">Step 2</div><div class="v">Chain 통과</div><div class="d">해당 시점의 체인으로</div></div>
        <div class="arrow">→</div>
        <div class="step"><div class="k">Step 3</div><div class="v">Rule 순차 검사</div><div class="d">위 → 아래로 하나씩</div></div>
        <div class="arrow">→</div>
        <div class="step"><div class="k">Step 4</div><div class="v">Target 실행</div><div class="d">일치 시 동작 확정</div></div>
      </div>
      <table class="compare">
        <tr><th>계층</th><th>질문</th><th>의미</th></tr>
        <tr><td class="emph">Table</td><td>무슨 일을 할 것인가?</td><td>허용/차단, NAT, 패킷 수정 등 목적별 작업 공간</td></tr>
        <tr><td class="emph">Chain</td><td>언제 처리할 것인가?</td><td>들어올 때 · 나갈 때 · 전달될 때 등 처리 시점</td></tr>
        <tr><td class="emph">Rule</td><td>어떻게 처리할 것인가?</td><td>조건에 맞는 패킷을 허용·차단·주소 변경하는 실제 동작</td></tr>
      </table>
      <p class="caption" style="margin-top:14px;">☆ 하나의 체인에는 여러 Rule이 존재하며, <b>가장 먼저 조건이 일치한 Rule</b>에서 처리가 끝난다 — 그 아래 Rule은 검사되지 않는다.</p>
    </div>
  </section>

  <!-- 4. 역할 & DROP vs REJECT -->
  <section class="slide">
    <div class="content">
      <div class="eyebrow">동작 비교</div>
      <h2 class="title">DROP vs REJECT</h2>
      <p class="sub" style="margin-bottom:16px;">규칙에 따라 패킷을 <b style="color:var(--ink)">ACCEPT · DROP · REJECT</b> 중 하나로 처리한다</p>
      <table class="compare">
        <tr><th>항목</th><th>DROP</th><th>REJECT</th></tr>
        <tr><td class="emph">동작</td><td>조용히 버림</td><td>거부 응답 전송</td></tr>
        <tr><td class="emph">상대방 경험</td><td>타임아웃까지 대기</td><td>즉시 거부 확인</td></tr>
        <tr><td class="emph">보안성</td><td>포트 존재 은닉</td><td>차단 사실 노출</td></tr>
        <tr><td class="emph">적합한 곳</td><td class="accent">외부 공개 서버</td><td class="accent">내부망 · 개발 환경</td></tr>
      </table>
    </div>
  </section>

  <!-- 6. 5대 테이블 -->
  <section class="slide">
    <div class="content">
      <div class="eyebrow">테이블</div>
      <h2 class="title">5가지 테이블</h2>
      <table class="compare">
        <tr><th>테이블</th><th>역할</th><th>주 사용 사례</th></tr>
        <tr><td class="emph">filter</td><td>패킷 허용/차단</td><td class="accent">방화벽 설정 (가장 많이 사용)</td></tr>
        <tr><td class="emph">nat</td><td>주소 변환</td><td>포트 포워딩, 공유기 NAT</td></tr>
        <tr><td class="emph">mangle</td><td>패킷 헤더 수정</td><td>QoS, TTL 변경, 패킷 마킹</td></tr>
        <tr><td class="emph">raw</td><td>conntrack 제어</td><td>성능 최적화, 특수 환경</td></tr>
        <tr><td class="emph">security</td><td>SELinux 연동</td><td>보안 정책 적용</td></tr>
      </table>
    </div>
  </section>

  <!-- 7. 체인 -->
  <section class="slide">
    <div class="content">
      <div class="eyebrow">체인 (Chain)</div>
      <h2 class="title">패킷이 지나가는 경로</h2>
      <div class="grid">
        <div class="card">
          <span class="tag">filter 테이블</span>
          <h3>INPUT / OUTPUT / FORWARD</h3>
          <p>외부→서버, 서버→외부, 경유(라우팅) 패킷을 각각 검사</p>
        </div>
        <div class="card">
          <span class="tag">nat 테이블</span>
          <h3>PREROUTING / POSTROUTING / OUTPUT</h3>
          <p>라우팅 전 목적지 변경(DNAT), 라우팅 후 출발지 변경(SNAT)</p>
        </div>
      </div>
      <p class="sub" style="margin-top:20px;">※ 라우팅은 커널이 수행 — iptables는 그 패킷을 검사·허용·차단·수정(NAT)할 뿐</p>
    </div>
  </section>

  <!-- 8. 규칙 구성 + 순서 -->
  <section class="slide">
    <div class="content">
      <div class="eyebrow">규칙 (Rule)</div>
      <h2 class="title">Match + Target, 그리고 순서</h2>
      <div class="terminal">
        <div class="terminal-bar"><div class="dot red"></div><div class="dot yellow"></div><div class="dot green"></div><div class="terminal-title">조건 → 동작</div></div>
        <pre class="code"><span class="c-key">iptables</span> <span class="c-flag">-A</span> INPUT <span class="c-flag">-p</span> tcp <span class="c-flag">-s</span> <span class="c-val">192.168.1.100</span> <span class="c-flag">--dport</span> <span class="c-val">22</span> <span class="c-flag">-j</span> ACCEPT</pre>
      </div>
      <div class="terminal" style="margin-top:12px;">
        <div class="terminal-bar"><div class="dot red"></div><div class="dot yellow"></div><div class="dot green"></div><div class="terminal-title">위 → 아래 순서로 검사</div></div>
        <pre class="code"><span class="c-key">iptables</span> <span class="c-flag">-A</span> INPUT <span class="c-flag">-p</span> tcp <span class="c-flag">--dport</span> <span class="c-val">22</span> <span class="c-flag">-j</span> ACCEPT
<span class="c-key">iptables</span> <span class="c-flag">-A</span> INPUT <span class="c-flag">-j</span> DROP                <span class="c-cmt"># SSH만 허용, 나머지 차단</span></pre>
      </div>
    </div>
  </section>

  <!-- 9. Match/Target -->
  <section class="slide">
    <div class="content">
      <div class="eyebrow">자주 쓰는 옵션</div>
      <h2 class="title">Match & Target</h2>
      <div class="grid">
        <div class="card">
          <h3>조건 (Match)</h3>
          <ul class="clean" style="margin-top:10px;">
            <li><span class="mono">-p tcp/udp/icmp</span></li>
            <li><span class="mono">-s / -d</span> 출발지 · 목적지 IP</li>
            <li><span class="mono">--dport / --sport</span> 포트</li>
            <li><span class="mono">-m conntrack --ctstate</span> 연결 상태</li>
          </ul>
        </div>
        <div class="card">
          <h3>동작 (Target)</h3>
          <ul class="clean" style="margin-top:10px;">
            <li><span class="mono">ACCEPT</span> 허용</li>
            <li><span class="mono">DROP</span> 조용히 버림</li>
            <li><span class="mono">REJECT</span> 거부 응답</li>
            <li><span class="mono">DNAT / SNAT / MASQUERADE</span> 주소 변환</li>
          </ul>
        </div>
      </div>
    </div>
  </section>

  <!-- 10. 기본 스크립트 + NAT -->
  <section class="slide">
    <div class="content">
      <div class="eyebrow">실전 예시</div>
      <h2 class="title">기본 방화벽 & 포트 포워딩</h2>
      <div class="terminal">
        <div class="terminal-bar"><div class="dot red"></div><div class="dot yellow"></div><div class="dot green"></div><div class="terminal-title">filter 테이블</div></div>
        <pre class="code"><span class="c-cmt"># 기존 연결 허용</span>
<span class="c-key">iptables</span> <span class="c-flag">-A</span> INPUT <span class="c-mod">-m</span> state <span class="c-flag">--state</span> ESTABLISHED,RELATED <span class="c-flag">-j</span> ACCEPT
<span class="c-cmt"># SSH / HTTP 허용</span>
<span class="c-key">iptables</span> <span class="c-flag">-A</span> INPUT <span class="c-flag">-p</span> tcp <span class="c-flag">--dport</span> <span class="c-val">22</span> <span class="c-flag">-j</span> ACCEPT
<span class="c-key">iptables</span> <span class="c-flag">-A</span> INPUT <span class="c-flag">-p</span> tcp <span class="c-flag">--dport</span> <span class="c-val">80</span> <span class="c-flag">-j</span> ACCEPT
<span class="c-cmt"># 나머지 전부 차단</span>
<span class="c-key">iptables</span> <span class="c-flag">-A</span> INPUT <span class="c-flag">-j</span> DROP</pre>
      </div>
      <div class="terminal" style="margin-top:12px;">
        <div class="terminal-bar"><div class="dot red"></div><div class="dot yellow"></div><div class="dot green"></div><div class="terminal-title">nat 테이블 · 포트 포워딩</div></div>
        <pre class="code"><span class="c-key">iptables</span> <span class="c-mod">-t</span> nat <span class="c-flag">-A</span> PREROUTING <span class="c-flag">-p</span> tcp <span class="c-flag">--dport</span> <span class="c-val">2222</span> \
  <span class="c-flag">-j</span> DNAT <span class="c-flag">--to-destination</span> <span class="c-val">192.168.1.100:22</span></pre>
      </div>
    </div>
  </section>

  <!-- 11. 실습 1: 특정 포트만 허용 -->
  <section class="slide">
    <div class="content">
      <div class="eyebrow">실습 01</div>
      <h2 class="title">80 · 8080 포트만 허용</h2>
      <div class="terminal">
        <div class="terminal-bar"><div class="dot red"></div><div class="dot yellow"></div><div class="dot green"></div><div class="terminal-title">iptables</div></div>
        <pre class="code"><span class="c-key">iptables</span> <span class="c-flag">-A</span> INPUT <span class="c-mod">-m</span> state <span class="c-flag">--state</span> ESTABLISHED,RELATED <span class="c-flag">-j</span> ACCEPT
<span class="c-key">iptables</span> <span class="c-flag">-A</span> INPUT <span class="c-flag">-p</span> tcp <span class="c-flag">--dport</span> <span class="c-val">80</span>   <span class="c-flag">-j</span> ACCEPT
<span class="c-key">iptables</span> <span class="c-flag">-A</span> INPUT <span class="c-flag">-p</span> tcp <span class="c-flag">--dport</span> <span class="c-val">8080</span> <span class="c-flag">-j</span> ACCEPT
<span class="c-key">iptables</span> <span class="c-flag">-P</span> INPUT DROP</pre>
      </div>
      <div class="test-row"><span class="badge pass">PASS</span> 8080에서 실행한 Python 웹서버 → Host IP 접속 성공</div>
      <div class="test-row"><span class="badge block">BLOCK</span> 그 외 포트 접속 → connection timed out</div>
    </div>
  </section>

  <!-- 12. 실습 2: SSH 특정 IP만 허용 -->
  <section class="slide">
    <div class="content">
      <div class="eyebrow">실습 02</div>
      <h2 class="title">SSH를 특정 IP에만 허용</h2>
      <div class="terminal">
        <div class="terminal-bar"><div class="dot red"></div><div class="dot yellow"></div><div class="dot green"></div><div class="terminal-title">iptables</div></div>
        <pre class="code"><span class="c-key">iptables</span> <span class="c-flag">-A</span> INPUT <span class="c-flag">-s</span> <span class="c-val">172.16.X.X</span> <span class="c-flag">-p</span> tcp <span class="c-flag">--dport</span> <span class="c-val">22</span> <span class="c-flag">-j</span> ACCEPT
<span class="c-key">iptables</span> <span class="c-flag">-A</span> INPUT <span class="c-flag">-p</span> tcp <span class="c-flag">--dport</span> <span class="c-val">22</span> <span class="c-flag">-j</span> DROP</pre>
      </div>
      <div class="test-row"><span class="badge pass">PASS</span> 허용된 PC에서 SSH 접속 성공</div>
      <div class="test-row"><span class="badge block">BLOCK</span> 그 외 PC에서 SSH 접속 차단</div>
      <p class="caption" style="margin-top:16px;">규칙 순서 원칙: <b>허용(ACCEPT)을 먼저, 차단(DROP)을 나중에</b> — 위에서부터 검사되기 때문</p>
    </div>
  </section>

  <!-- 13. 실습 3: 카운터 & 초기화 -->
  <section class="slide">
    <div class="content">
      <div class="eyebrow">실습 03</div>
      <h2 class="title">규칙 확인 · 카운터 · 초기화</h2>
      <div class="terminal">
        <div class="terminal-bar"><div class="dot red"></div><div class="dot yellow"></div><div class="dot green"></div><div class="terminal-title">확인</div></div>
        <pre class="code"><span class="c-cmt"># 규칙 및 패킷/바이트 카운터 확인</span>
<span class="c-key">iptables</span> <span class="c-flag">-L</span> <span class="c-flag">-n</span> <span class="c-flag">-v</span>

<span class="c-cmt"># 번호와 함께 목록 → 특정 규칙 삭제</span>
<span class="c-key">iptables</span> <span class="c-flag">-L</span> <span class="c-flag">--line-numbers</span> <span class="c-flag">-n</span>
<span class="c-key">iptables</span> <span class="c-flag">-D</span> INPUT <span class="c-val">&lt;번호&gt;</span></pre>
      </div>
      <div class="terminal" style="margin-top:12px;">
        <div class="terminal-bar"><div class="dot red"></div><div class="dot yellow"></div><div class="dot green"></div><div class="terminal-title">전체 초기화</div></div>
        <pre class="code"><span class="c-key">iptables</span> <span class="c-flag">-F</span>
<span class="c-key">iptables</span> <span class="c-flag">-X</span>
<span class="c-key">iptables</span> <span class="c-flag">-P</span> INPUT   ACCEPT
<span class="c-key">iptables</span> <span class="c-flag">-P</span> OUTPUT  ACCEPT
<span class="c-key">iptables</span> <span class="c-flag">-P</span> FORWARD ACCEPT</pre>
      </div>
      <p class="caption">pkts/bytes 증가 확인 → 규칙 삭제 확인 → 전체 초기화 후 <span class="mono" style="font-family:ui-monospace, monospace;">iptables -L -n -v</span>로 최종 확인</p>
    </div>
  </section>

</div>

<div id="hud"></div>

<div class="navzone" id="zone-prev"></div>
<div class="navzone" id="zone-next"></div>

<script>
  const slides = document.querySelectorAll('.slide');
  const total = slides.length;
  let idx = 0;

  const hud = document.getElementById('hud');
  slides.forEach((_, i) => {
    const p = document.createElement('div');
    p.className = 'pip' + (i === 0 ? ' active' : '');
    p.addEventListener('click', () => go(i));
    hud.appendChild(p);
  });
  const pips = hud.querySelectorAll('.pip');
  const counter = document.getElementById('counter');

  function pad(n){ return String(n).padStart(2,'0'); }

  function render(){
    slides.forEach((s, i) => s.classList.toggle('active', i === idx));
    pips.forEach((p, i) => p.classList.toggle('active', i === idx));
    counter.textContent = pad(idx+1) + ' / ' + pad(total);
  }

  function go(i){
    idx = Math.max(0, Math.min(total - 1, i));
    render();
  }
  function next(){ go(idx + 1); }
  function prev(){ go(idx - 1); }

  document.getElementById('zone-next').addEventListener('click', next);
  document.getElementById('zone-prev').addEventListener('click', prev);

  window.addEventListener('keydown', (e) => {
    if (['ArrowRight','ArrowDown',' ','PageDown'].includes(e.key)) { next(); e.preventDefault(); }
    if (['ArrowLeft','ArrowUp','PageUp'].includes(e.key)) { prev(); e.preventDefault(); }
    if (e.key === 'Home') go(0);
    if (e.key === 'End') go(total-1);
  });

  let touchX = null;
  window.addEventListener('touchstart', e => touchX = e.touches[0].clientX);
  window.addEventListener('touchend', e => {
    if (touchX === null) return;
    const dx = e.changedTouches[0].clientX - touchX;
    if (dx > 50) prev();
    if (dx < -50) next();
    touchX = null;
  });

  render();
</script>
</body>
</html>