Hamutaro

오늘 오전 30분은 없었습ㄴㅣ다....

잠깐의 자습시간을 조금 가지구..? 버추얼을 했구용

바로 들어가기~~

 


오늘의 순서

교시 무엇
2교시 경계 기반 보안의 한계 · Zero Trust · 5대 원칙
3교시 점수로 판단하는 함수 (PDP)
4교시 PDP 와 PEP · ZT 성숙도 점수 · A사 적용 방안 문서
5교시 왜 도커인가 · 이미지와 컨테이너 · 같이 설치하기
6교시 nginx 서버 켜고 접속 기록 읽기
7교시 내 페이지 연결 · 두 서버 · 올리기

 


 

2교시 경계 기반 보안의 한계 · Zero Trust · 5대 원칙

오늘 들어온 일

A사 보안 담당이 보낸 메시지:

「월요일 아침 정산 포털 기록을 보니 새벽에 정산 내역이 대량으로 조회됐습니다. 방화벽은 아무것도 막지 않았습니다. 무슨 일이고 왜 통과됐나요?」

이 교시에서는 접근 기록에서 의심스러운 줄을 찾고, 5대 원칙의 어느 칸이 비어 있었는지 확인했다.

또 기록만으로 말할 수 있는 것과 없는 것을 구분했다.

왜 필요한가

지난주에 본 방화벽 · VPN 은 접속이 시작되는 곳에서 확인하는 방식이었다.

한 번 통과한 뒤에 그 계정이 무엇을 하는지는 따로 확인하지 않았다.

로그인이 성공한 요청 안에도 정상과 이상이 섞여 있기 때문에 어떤 항목을 확인해야 하는지 알아야 규칙으로 만들 수 있었다.

또 기록에 적힌 값과 그 값으로 추정한 내용을 구분해야 과장 없이 보고할 수 있었다.

2.1 경계 기반 보안과 Zero Trust

경계 기반 보안

회사 네트워크 안은 신뢰하고 밖은 의심하는 방식이다.

방화벽 · VPN 이라는 경계를 통과하면 내부 자원에 넓게 접근할 수 있다.

Zero Trust

누구도 기본적으로 신뢰하지 않고, 접근할 때마다 확인한다는 보안 원칙이다.

미국 NIST의 SP 800-207 문서에서 이 구조를 정리했다.

자원

보호할 대상을 뜻한다.

같은 포털 안에서도 정산 내역 조회와 계정 권한 변경은 서로 다른 자원이다.

횡이동

침입한 뒤 내부에서 다른 서버로 옮겨 가면서 공격 범위를 넓히는 것을 말한다.

경계 기반 보안에서는 내부 이동을 막기 어렵다.

기준 경계 기반 보안 Zero Trust
신뢰 전제 경계 안으로 들어오면 신뢰한다 안팎 구분 없이 기본적으로 신뢰하지 않는다
검증 시점 로그인할 때 한 번 접근할 때마다, 세션 중에도 반복
접근 범위 인증되면 내부에 넓게 접근 업무에 필요한 자원에만 최소한으로 접근

Zero Trust 가 모든 이상 행동을 막는 것은 아니다.

정상 계정 · 정상 기기 · 평소 시간에 자기 업무 시스템에서 자료 수만 건을 내려받는 경우처럼 접근 자체는 정상인데 행동이 이상할 수도 있다.

이런 경우는 접근을 허용할지 정하는 것만으로는 찾기 어렵고, 사용 기록을 계속 분석하는 이상탐지가 따로 필요하다.

실제 서비스로는 Cloudflare Access가 사내 앱마다 접근 정책을 걸고 사용자 신원과 기기 상태를 확인한다.

Microsoft Entra 조건부 액세스는 회사 기기가 보안 기준을 지킨 기기일 때만 접속하도록 정책을 만들 수 있다.

2.2 5대 원칙 — 요청 하나를 다섯 칸으로 읽기

Zero Trust 는 제품 하나가 아니라 확인할 항목의 묶음이다.

요청 하나를 다섯 칸으로 나누면 어느 부분이 부족했는지 확인할 수 있다.

ID

이 사용자가 진짜 본인인지 확인하는 항목이다.

MFA는 비밀번호 외에 휴대폰 승인 같은 인증 수단을 하나 더 확인하는 방식이다.

Device

접속하는 기기가 회사 보안 기준을 지키는지 확인한다.

등록된 기기인지, 백신 · 패치 상태가 괜찮은지 등을 확인한다.

Network

요청이 필요한 자원에만 접근하는지 확인한다.

마이크로 세그멘테이션은 네트워크를 작은 구역으로 나누고 구역 사이의 통신을 제한하는 방식이다.

Application

사용자가 해당 앱의 기능을 사용할 권한이 있는지 확인한다.

Policy

시간 · 위치 · 위험도에 따라 정책을 다르게 적용한다.

예를 들어 새벽 로그인이나 해외 IP 접속이라면 추가 인증을 요구할 수 있다.

칸 묻는 것 확인하는 예 대표 제품
ID 이 사용자가 진짜 본인인가 비밀번호 + MFA Microsoft Entra ID
Device 기기가 회사 기준을 지키나 등록 기기 · 백신 · 패치 · 디스크 암호화 Microsoft Intune
Network 필요한 자원에만 닿나 구역을 나누고 구역 사이 통신 제한 방화벽 구역 규칙
Application 이 앱의 권한이 있나 앱마다 따로 접근 정책 Cloudflare Access
Policy 상황에 따라 정책이 바뀌나 해외 IP · 새벽 로그인이면 추가 인증 Microsoft Entra 조건부 액세스

문서마다 항목 이름이나 개수가 조금씩 다를 수 있지만 이번 과정에서는 위 다섯 가지로 구분했다.

요청 하나가 한 항목을 통과해도 다른 항목에서 막힐 수 있다.

어느 항목에서 문제가 생겼는지 확인하면 어디를 고쳐야 하는지도 알 수 있다.

A사 재무팀 junho의 정상적인 정산 조회를 다섯 항목으로 보면 다음과 같았다.

칸 이 요청에서 확인한 것 결과
ID 계정 junho, 로그인 방식 비밀번호 + MFA 정상
Device 기기 NB-0522 는 junho 의 등록 기기이고 보안 상태 정상 정상
Network 사내 대역 10.20.3.14 에서 정산 포털로 접속 정상
Application junho 는 정산 내역(SETTLE) 담당 정상
Policy 낮 14시, 평소 조회량 12건 정상

2.3 기록으로 단정할 수 있는 것과 없는 것

확인된 사실은 기록에 실제로 적힌 값이다.

시각 · 계정 · 기기 · 출발 IP · 건수 · 결과 등이 여기에 해당했다.

추정은 기록을 보고 짐작한 내용이다.

예를 들어 「본인이 아닐 것이다」는 기록에 직접 적힌 값이 아니므로 추정이다.

출발 IP가 사내 대역이라는 것은 사내망에서 접속했다는 사실일 뿐, 그 기기가 회사 등록 기기라는 뜻은 아니다.

기기 등록 여부는 별도의 기록을 확인해야 한다.

「로그인 성공」이라는 기록에도 계정 확인, 비밀번호 확인, MFA 여부가 섞여 있기 때문에 무엇을 확인하고 성공한 것인지 따로 봐야 했다.

기록만으로 알 수 없는 것은 모른다고 쓰고, 어떤 자료를 추가로 확인해야 하는지 같이 적는 것이 중요했다.

2교시 한눈에

하려는 일 보는 곳
의심 줄 찾기 시각 · 기기 · 자원 · 건수 · 로그인 방식을 등록 정보와 같은 계정의 다른 줄에 대조
이상해 보이지만 정상인 줄 거르기 등록 정보의 설명
한 줄씩은 정상인데 모으면 이상한 줄 맡은 자원과 대조하고 같은 계정의 반복 횟수와 건수를 합산
요청이 어느 칸에서 걸리는지 ID → Device → Network → Application → Policy 순서
정상 로그인인데 걸린다면 Policy 칸
단정할 수 있는지 기록에 적힌 값만 사실, 나머지는 추정

 

3교시 점수로 판단하는 함수 (PDP)

오늘 들어온 일

A사 보안 담당이 보낸 메시지:

「2교시에 본 새벽 사건 같은 요청을 앞으로는 자동으로 거르고 싶습니다. 요청이 들어올 때마다 허용 · 추가 확인 · 거부를 정하는 규칙을 코드로 만들어 주세요.」

이번에는 점수로 판단하는 파이썬 함수를 읽고, 요청을 한꺼번에 판정했다.

왜 필요한가

로그인이 성공한 요청을 사람이 하나씩 읽기는 어렵다.

규칙을 코드로 만들면 모든 요청을 같은 기준으로 바로 판정할 수 있다.

이 규칙을 결정하는 부분이 PDP이다.

3.0 파일 만들기

이번부터 파일은 터미널에서 vim으로 만들었다.

먼저 위치를 확인했다.

pwd

결과가 network_zt 폴더가 아니라면 이동했다.

cd network_zt

vim으로 파일을 만드는 순서는 다음과 같았다.

순서 입력 하는 일
① vim 파일이름 파일을 연다
② i 입력 모드로 들어간다
③ 붙여넣기 코드를 붙여 넣는다
④ Esc → :wq → Enter 저장하고 나간다

키가 제대로 동작하지 않으면 한/영 상태를 확인하고 Esc를 먼저 누른 뒤 명령을 입력했다.

붙여넣기 과정에서 공백이 계속 밀리면 :q!로 저장하지 않고 나와 touch로 파일을 만든 뒤 VS Code에서 수정할 수도 있었다.

3.1 PDP 와 점수 판단

PDP

Policy Decision Point

요청을 받아 허용 · 추가 확인 · 거부를 결정하는 곳이다.

이번에는 파이썬 함수 하나가 이 역할을 했다.

필수 조건

하나라도 어긋나면 점수와 관계없이 바로 거부하는 조건이다.

예:

  • 재직 중인 계정인지
  • 등록된 기기인지

위험 점수

이상한 신호마다 점수를 더해 합계로 판단하는 방식이다.

0점에서 시작했다.

추가 확인

비밀번호만으로 부족할 때 인증을 한 번 더 요구하는 단계이다.

허용과 거부 사이의 단계라고 볼 수 있다.

최소권한

업무에 필요한 만큼만 권한을 주는 원칙이다.

더 중요한 자원일수록 기준도 더 엄격하게 정할 수 있다.

구분 정산 내역 조회 계정 권한 변경
필수 조건 재직 중 · 등록 기기 재직 중 · 등록 기기 · 역할이 account_admin
기기 보안 상태 이상 +40 3-1에서 정함
MFA 안 함 +20 3-1에서 정함
해외에서 접속 +30 3-1에서 정함
새벽 접속 +20 보지 않음
20건 이상 조회 +30 보지 않음
결정 50점 이상 거부 · 20점 이상 추가확인 · 그 아래 허용 50점 이상 거부 · 10점 이상 추가확인 · 그 아래 허용

ACCOUNTS.get(req["user"])를 사용하면 계정 정보를 찾고 없을 경우 None을 받을 수 있었다.

reasons.append()로 판단 이유를 리스트에 추가했다.

return "거부", reasons처럼 값 두 개를 한 번에 리턴할 수도 있었다.

함수는 return을 만나면 바로 끝나기 때문에 필수 조건에서 거부되면 아래의 점수 계산은 실행되지 않았다.

3.2 판단 함수

ACCOUNTS = {
    "seoyeon": {"employed": True,  "device": "NB-0417", "role": "sales"},
    "junho":   {"employed": True,  "device": "NB-0522", "role": "finance"},
    "gaeun":   {"employed": True,  "device": "NB-0188", "role": "account_admin"},
    "dami":    {"employed": False, "device": "NB-0333", "role": "sales"},
}

정산 내역 조회 요청을 판단하는 함수는 다음 구조였다.

def decide_settlement(req):
    """정산 내역 조회 요청을 판단해 (결정, 이유 목록)을 리턴한다"""

    account = ACCOUNTS.get(req["user"])

    if account is None or not account["employed"]:
        return "거부", ["재직 중인 계정이 아님"]

    if req["device"] != account["device"]:
        return "거부", ["이 계정에 등록된 기기가 아님"]

    score = 0
    reasons = []

    if req["health"] != "ok":
        score = score + 40
        reasons.append("기기 보안 상태 이상 (+40)")

    if req["country"] != "KR":
        score = score + 30
        reasons.append("해외에서 접속 (+30)")

    if req["hour"] < 6:
        score = score + 20
        reasons.append("새벽 접속 (+20)")

    if req["mfa"] == False:
        score = score + 20
        reasons.append("MFA 안 함 (+20)")

    if req["count"] >= 20:
        score = score + 30
        reasons.append("대량 조회 (+30)")

    if score >= 50:
        return "거부", reasons

    if score >= 20:
        return "추가확인", reasons

    return "허용", reasons

흐름은 다음과 같았다.

요청
  |
  v
[필수 조건 검사]
  |
  ├─ 어긋남 → 거부
  |
  v
[위험 점수 계산]
  |
  ├─ 50점 이상 → 거부
  ├─ 20점 이상 → 추가확인
  └─ 그 외 → 허용

3.3 요청 한 건 따라가기

요청:

seoyeon
등록 기기 NB-0417
기기 상태 ok
국가 KR
시각 2시
MFA 안 함
40건 조회
단계 검사 요청의 값 결과 누적 점수
1 재직 중인 계정인가 employed = True 통과 0
2 등록 기기인가 NB-0417 통과 0
3 기기 보안 상태 ok 0점 0
4 접속 국가 KR 0점 0
5 새벽인가 2시 +20 20
6 MFA 했는가 False +20 40
7 20건 이상 조회인가 40건 +30 70
8 결정 70점 ≥ 50 거부 70

결과는 다음과 같았다.

(
    "거부",
    [
        "새벽 접속 (+20)",
        "MFA 안 함 (+20)",
        "대량 조회 (+30)"
    ]
)

한 가지 신호만 보고 바로 거부하는 것이 아니라 여러 위험 신호가 겹칠수록 점수가 올라갔다.

모두 정상이면 0점이기 때문에 허용됐다.

3교시 한눈에

하려는 일 코드
요청의 계정 찾기 ACCOUNTS.get(req["user"])
필수 조건으로 바로 거부 return "거부", [이유]
점수 쌓기 score = score + 40
이유 추가 reasons.append()
합계로 결정 50점 이상 거부 · 20점 이상 추가확인
요청 목록 판정 for req in REQUESTS:
결정별 건수 세기 counts[decision] = counts[decision] + 1
이유별 건수 세기 .get(이유, 0) + 1

 

4교시 PDP 와 PEP · ZT 성숙도 점수 · A사 적용 방안 문서

오늘 들어온 일

A사 대표가 보낸 메시지:

「우리 회사의 지금 보안 수준이 어느 정도인지 점수로 알려 주시고, Zero Trust 도입 방안을 문서 하나로 정리해 주세요. 임원 보고에 씁니다.」

이번에는 PDP와 PEP의 역할을 구분하고 직원 정보를 이용해 Zero Trust 성숙도 점수를 계산했다.

왜 필요한가

3교시에서 만든 함수는 결정만 했다.

그 결정을 실제 요청에 적용해 통과시키거나 막는 역할도 필요했고, 그 역할이 PEP였다.

회사 보안 수준도 단순히 좋다 · 나쁘다로 말하는 것보다 점수로 나타내면 무엇부터 개선해야 하는지 확인하기 쉬웠다.

4.1 PDP 와 PEP

PEP

Policy Enforcement Point

PDP가 내린 결정을 실제로 적용하는 곳이다.

요청을 허용하거나 차단하고, 필요한 경우 추가 인증을 요구한다.

예:

  • 프록시
  • 방화벽
  • API 게이트웨이

PDP와 PEP를 나누면 정책을 한 곳에서 수정해도 여러 PEP가 같은 규칙을 사용할 수 있었다.

결정 기록도 한 곳에 모을 수 있었다.

기본 거부

허용 목록에 없는 것은 모두 거부하는 방식이다.

최소권한은 기본적으로 아무 권한도 없는 상태에서 필요한 권한만 추가하는 방식으로 볼 수 있다.

사용자 --요청--> PEP --요청 정보--> PDP
                   ^               |
                   |               |
                   +--- 결정 -------+

허용       → 앱으로 전달
추가확인   → MFA 요구
거부       → 차단하고 로그 기록

이번에 만든 decide_settlement 함수가 PDP 역할을 했다.

4.2 ZT 성숙도 점수

성숙도는 회사가 Zero Trust를 어느 정도 도입했는지를 나타냈다.

이번에는 다섯 항목에 각각 20점을 주고 총 100점으로 계산했다.

항목 만점 계산 5대 원칙
MFA 적용률 20 MFA 사용 직원 ÷ 전체 직원 × 20 ID
기기 보안 점검 비율 20 점검 통과 기기 사용자 ÷ 전체 직원 × 20 Device
최소권한 준수율 20 초과 권한 없는 직원 ÷ 전체 직원 × 20 Application
마이크로 세그멘테이션 20 적용했으면 20, 아니면 0 Network
위험 기반 인증 20 적용했으면 20, 아니면 0 Policy

예를 들어 직원 12명 중 6명이 MFA를 사용한다면 다음처럼 계산할 수 있었다.

round(6 / 12 * 20)

결과는 10점이다.

4.3 파일 만들기

network_zt 폴더 안에서 vim으로 파일을 만들었다.

vim 파일이름

순서는 다음과 같았다.

vim 파일이름
→ i
→ 내용 입력
→ Esc
→ :wq
→ Enter

touch로 파일을 만들고 VS Code에서 작성하는 방법도 사용할 수 있었다.

저장한 내용은 cat으로 확인했다.

cat 파일이름

4교시 한눈에

하려는 일 방법
결정하는 곳 / 막는 곳 PDP / PEP
성숙도 점수 항목당 20점 × 5
허용 목록에 없는 것 기본 거부
새 파일 만들기 vim 또는 touch
저장 내용 확인 cat 파일이름

 

5교시 왜 도커인가 · 이미지와 컨테이너 · 같이 설치하기

왜 필요한가

프로그램을 PC에 직접 설치하면 PC마다 버전과 설정이 달라질 수 있다.

삭제한 뒤에도 기존 파일이나 설정이 남을 수 있다.

이런 차이 때문에 「내 PC에서는 되는데 다른 PC에서는 안 된다」는 문제가 생긴다.

도커는 프로그램과 필요한 파일을 하나로 묶어서 어느 PC에서든 같은 환경으로 실행할 수 있게 해 준다.

[도커 없이]

PC-A: nginx 1.18
PC-B: nginx 1.24
PC-C: nginx 1.18 + 예전 파일

→ PC마다 환경이 다름


[도커 사용]

PC-A
PC-B → nginx:1.28 이미지 사용
PC-C

→ 같은 환경

5.1 도커 없이 설치하는 것과 도커로 실행하는 것

직접 설치하면 버전이나 설정이 서로 달라질 수 있다.

도커에서는 같은 이미지를 받아 실행하기 때문에 여러 PC에서 같은 환경을 만들 수 있었다.

컨테이너를 지울 때도 컨테이너 하나만 지우면 됐다.

5.2 이미지와 컨테이너 · Docker Hub · 엔진

이미지

실행할 프로그램과 필요한 파일을 하나로 묶어 둔 것이다.

이미지는 읽기 전용이다.

nginx:1.28

처럼 이름:태그 형태로 사용했다.

태그는 버전을 나타냈다.

컨테이너

이미지를 실제로 실행한 것이다.

같은 이미지로 여러 개의 컨테이너를 만들 수 있었다.

레지스트리

이미지를 모아 두고 내려받을 수 있게 해 주는 서버이다.

Docker Hub가 대표적인 공개 레지스트리이다.

이미지를 받을 때는 registry-1.docker.io 같은 이름을 DNS로 IP 주소로 바꾸고 인터넷을 통해 이미지를 내려받았다.

Docker Engine

이미지를 받고 컨테이너를 만들어 실행하는 도커의 핵심 프로그램이다.

데몬이라고도 부른다.

Windows에서는 Docker Desktop이 Docker Engine을 설치하고 실행했다.

Docker Desktop은 WSL 2 위에서 엔진을 실행했다.

Docker Client

터미널에서 입력하는 docker 명령이다.

사용자의 명령을 Docker Engine에 전달했다.

Docker Hub
    |
    | pull
    v
nginx:1.28 이미지
    |
    ├── 컨테이너 1
    └── 컨테이너 2

이미지는 한 번 받아 두면 여러 컨테이너를 만들 수 있었다.

컨테이너를 삭제해도 이미지는 남아 있기 때문에 다음에 다시 사용할 수 있었다.

5.3 nginx 기반

이번 실습은 nginx:1.28 이미지를 사용했다.

nginx는 웹 서버 프로그램이다.

브라우저에서 요청이 들어오면 파일을 응답하고 접속 기록을 남긴다.

설정에 따라 다른 서버로 요청을 넘겨 주는 프록시 역할도 할 수 있다.

직접 서버 코드를 만들지 않아도 서버 실행 · 로그 확인 · 종료 등을 연습할 수 있었다.

5.4 Docker 설치

Windows에서는 Docker Desktop을 설치했다.

WSL 2가 없다면 다음 명령으로 설치할 수 있었다.

wsl --install

이미 설치되어 있다면 업데이트할 수도 있었다.

wsl --update

Docker Desktop을 실행한 뒤 Engine running 상태가 될 때까지 기다렸다.

설치 확인은 다음 명령으로 했다.

docker --version

첫 컨테이너는 다음 명령으로 실행했다.

docker run hello-world

이 명령을 실행하면 다음 순서로 동작했다.

docker 명령
    |
    v
Docker Engine
    |
    | 이미지가 없으면
    v
Docker Hub
    |
    | 이미지 다운로드
    v
Docker Engine
    |
    | 컨테이너 생성
    v
hello-world 컨테이너

5교시 한눈에

하려는 일 명령
설치 확인 docker --version
첫 컨테이너 docker run hello-world
내려받은 이미지 docker images
컨테이너 기록 docker ps -a
이미지 이름 이름:태그

 

6교시 nginx 서버 켜고 접속 기록 읽기

왜 필요한가

서버를 켰는데 접속이 안 되는 경우에는 서버가 실제로 실행 중인지, 포트가 연결됐는지, 로그가 남았는지 순서대로 확인해야 한다.

이번에는 docker ps, docker logs, docker exec 등을 사용했다.

컨테이너는 기본적으로 외부에서 바로 접근할 수 없기 때문에 내 PC의 포트와 컨테이너의 포트를 연결해야 했다.

6.1 포트를 연결해야 접속이 된다

브라우저
http://127.0.0.1:8080
        |
        v
내 PC 8080
        |
        | -p 8080:80
        v
컨테이너 nginx 80

nginx는 컨테이너 안의 80번 포트를 사용했다.

내 PC의 8080번으로 들어온 요청을 컨테이너의 80번으로 넘기도록 연결했다.

-p 내PC포트:컨테이너포트

이런 방식은 포트 포워딩이라고도 한다.

포트를 연결하지 않으면 컨테이너가 실행 중이어도 브라우저에서 접속할 수 없다.

주소 읽기

127.0.0.1

내 PC 자신을 가리킨다.

0.0.0.0

이 PC가 가진 모든 주소를 의미한다.

80/tcp

80번 포트를 TCP 방식으로 사용한다는 뜻이다.

6.2 접속 기록 한 줄 읽기

nginx는 요청을 처리할 때마다 로그를 한 줄씩 남겼다.

예:

172.17.0.1 - - [19/Oct/2026:14:31:05 +0000] "GET / HTTP/1.1" 200 615
부분 뜻
172.17.0.1 요청을 보낸 쪽의 주소
[19/Oct/2026:14:31:05 +0000] 요청을 받은 시각
"GET / HTTP/1.1" 요청 방식 · 주소 · 프로토콜
200 응답 번호
615 응답 크기

200은 요청이 성공했다는 뜻이다.

404는 요청한 파일이나 주소가 없다는 뜻이다.

서버를 실행한 직후 docker logs를 확인하면 접속 기록이 없을 수도 있다.

아직 브라우저에서 요청을 보내지 않았기 때문이다.

docker run
   |
   v
docker logs
→ 시작 메시지만 있음
   |
   v
브라우저 접속
   |
   v
docker logs
→ GET 요청 기록 생성

브라우저가 favicon.ico를 요청했는데 파일이 없으면 404가 나올 수 있었다.

이 경우는 정상적인 상황이라 넘어갔다.

새로고침 후 304가 나오기도 했다.

304는 바뀐 내용이 없으니 저장해 둔 화면을 그대로 사용하라는 의미이다.

6.3 Docker 명령

nginx 서버를 실행했다.

docker run -d --name web -p 8080:80 nginx:1.28

실행 중인 컨테이너를 확인했다.

docker ps

로그를 확인했다.

docker logs web

컨테이너 내부에 들어갔다.

docker exec -it web sh

나올 때는 다음 명령을 사용했다.

exit

컨테이너를 멈췄다.

docker stop web

멈춘 컨테이너를 삭제했다.

docker rm web
명령 하는 일
docker run -d --name web -p 8080:80 nginx:1.28 서버 실행
docker ps 실행 중인 컨테이너 확인
docker logs web 로그 확인
docker exec -it web sh 컨테이너 안으로 들어가기
docker stop web 컨테이너 정지
docker rm web 컨테이너 삭제

-d는 컨테이너를 백그라운드로 실행했다.

--name은 컨테이너 이름을 지정했다.

-p는 포트를 연결했다.

-it는 컨테이너 안의 터미널을 직접 사용할 수 있게 했다.

6교시 한눈에

하려는 일 명령
서버 켜기 docker run -d --name 이름 -p 내PC포트:컨테이너포트 이미지:태그
켜진 것 보기 docker ps
끈 것까지 보기 docker ps -a
로그 보기 docker logs 이름
컨테이너 안에 들어가기 docker exec -it 이름 sh
컨테이너 끄기 docker stop 이름
컨테이너 삭제 docker rm 이름
실행 중인 상태로 삭제 docker rm -f 이름

 

7교시 내 페이지 연결 · 두 서버 · 올리기

왜 필요한가

컨테이너 안에서 직접 수정한 파일은 컨테이너를 삭제하면 같이 사라진다.

웹 페이지의 index.html이나 설정 파일처럼 계속 보관해야 하는 파일은 내 PC에 두고 컨테이너가 읽도록 연결하는 것이 좋다.

서버를 여러 개 실행할 때는 서로 다른 포트를 사용하면 한 PC에서도 여러 서버를 띄울 수 있다.

읽기 전용으로 연결하면 컨테이너가 내 파일을 수정하지 못하게 할 수 있다.

내 폴더를 컨테이너에 연결

Git Bash에서는 다음과 같이 실행했다.

MSYS_NO_PATHCONV=1 docker run -d --name 이름 -p 포트:80 -v "$(pwd)/폴더:/usr/share/nginx/html:ro" nginx:1.28

-v를 사용해 내 PC의 폴더와 컨테이너의 폴더를 연결했다.

내 PC 폴더
      |
      | -v
      v
/usr/share/nginx/html
      |
      v
nginx

:ro는 read only를 뜻했다.

컨테이너가 연결된 파일을 읽을 수는 있지만 수정하지는 못했다.

최소권한과 비슷한 방식으로 볼 수 있었다.

서버 두 대 실행

한 PC에서 서버를 두 개 실행하려면 내 PC 쪽 포트를 다르게 연결했다.

내 PC 8080 → 컨테이너 1의 80
내 PC 8081 → 컨테이너 2의 80

컨테이너 안의 nginx는 둘 다 80번을 사용할 수 있었다.

바깥에서 접근할 때 사용하는 내 PC의 포트만 다르게 지정했다.

브라우저에서는 다음처럼 각각 접근할 수 있었다.

http://127.0.0.1:8080
http://127.0.0.1:8081

여러 컨테이너를 한 번에 정리할 수도 있었다.

docker rm -f 이름1 이름2

7교시 한눈에

하려는 일 명령
내 폴더를 서버에 연결 -v "$(pwd)/폴더:/usr/share/nginx/html:ro"
컨테이너 안에서 바꾼 파일 컨테이너를 지우면 같이 사라짐
서버 두 대 실행 내 PC 포트를 8080 · 8081처럼 다르게 연결
읽기 전용 연결 :ro
한꺼번에 정리 docker rm -f 이름1 이름2

 

 

 

오늘은 도커를 깔고 해보는 과정을 가졌는데, 오랜만에 해본 것 같아요

사실 딱 한번..? 두번 밖에 안해봤는데 자세하게 알려주셔서 그런가 좋았습니다.

오늘 사실 너무 피곤해요,,, 어제 어디 놀러갔다왔는데 진짜 피곤... 하지만 ?? 수업은 열심이 들었다...

그럼 아됴

안녕하세요1!!!!!!

벌써 금요일입니다.

정말 일주일이 정말 빠르게 지나간 것 같아요.

그럼 모닝 30 레츠고.

1. 왜 이걸 하는지 찾아봅니다

1.1 서버에 접속하면 VS Code 가 없습니다. 보안 담당자는 서버의 설정 파일을 어떻게 고칠까요?

>

보안 담당자는 SSH로 리눅스 서버에 원격 접속한 뒤 vim이나 nano 같은 터미널 기반 텍스트 편집기를 사용하여 설정 파일을 수정한다. vim은 GUI가 없는 서버에서도 사용할 수 있고, 별도의 그래픽 편집기를 설치하지 않아도 설정 파일을 관리할 수 있어 서버 운영에 유용하다. 예를 들어 sudo vim /etc/ssh/sshd_config 명령으로 SSH 서버 설정 파일을 열어 수정할 수 있다.

vim 의 모드와 키를 익힙니다

왜 중요한가 — vim 은 처음 열면 글자를 쳐도 입력되지 않아서 많은 사람이 여기서 막힙니다. 모드 세 개와 저장 · 종료 키만 알면 서버에서 파일을 고치고 나올 수 있습니다.

vim(빔 · 터미널 안에서 쓰는 글 편집기)은 마우스 없이 키보드로만 씁니다. 키가 하는 일을 정하는 상태, 즉 모드(mode)가 세 가지 있습니다.

모드들어가는 법이때 키가 하는 일화면에서 보이는 것

일반 모드 (Normal mode · 명령을 내리는 상태) vim 을 열면 처음 여기 · 다른 모드에서 Esc 키가 명령이 된다 — 이동 · 지우기 · 복사 왼쪽 아래가 비어 있다
입력 모드 (Insert mode · 글자를 입력하는 상태) 일반 모드에서 i 키가 글자로 입력된다 왼쪽 아래 -- INSERT --
명령줄 모드 (Command-line mode · 저장 · 종료 명령을 치는 상태) 일반 모드에서 : 저장 · 종료 · 바꾸기 명령을 친다 왼쪽 아래 : 와 친 글자

아래 키는 모두 일반 모드에서 칩니다. : 로 시작하는 명령은 끝에 Enter 를 누릅니다.

키하는 일

i 입력 모드로 들어간다 (커서 앞에 글자가 들어간다)
Esc 일반 모드로 돌아온다
:w 저장한다 (write)
:q 나간다 (quit). 고친 뒤 저장하지 않았으면 나가지지 않는다
:wq 저장하고 나간다
:q! 저장하지 않고 강제로 나간다. 고친 내용은 사라진다
dd 커서가 있는 줄 하나를 지운다
yy · p 줄을 복사한다 · 커서 아래 줄에 붙여 넣는다
u 방금 한 것을 되돌린다
/단어 단어를 찾는다. n 을 누르면 다음 것으로 간다
gg · G 맨 위 줄로 · 맨 아래 줄로 간다
:%s/옛/새/g 파일 전체에서 옛 을 새 로 바꾼다

방향키로 커서를 움직여도 됩니다.

키가 먹지 않을 때 — 아래 둘 중 하나입니다.

  • 키보드가 한글 상태이면 i 가 ㅑ 로 입력되어 아무 일도 일어나지 않습니다. 한/영 키로 영문으로 바꿉니다.
  • 입력 모드(-- INSERT --)에서 :wq 를 치면 글자로 입력됩니다. 먼저 Esc 를 누릅니다.

어떻게 해도 못 나가겠으면 Esc 를 두 번 누르고 :q! Enter 를 입력합니다.


오늘의 순서

교시 무엇
2교시 클라우드 · SaaS · 회사 방화벽이 못 보는 접속
3교시 망 구성도 읽고 고치기 · DMZ
4교시 구역 규칙을 판정하는 코드 읽기와 만들기
5교시 지연 · 지터 · 패킷 손실
6교시 코드로 세 숫자 계산하고 판정하기
7교시 tracert 로 느려진 구간 찾기 · 5일 차 품질 진단 리포트

 

2교시 — 클라우드 · SaaS · 회사 방화벽이 못 보는 접속

왜 필요한가

서버와 업무 도구가 클라우드로 옮겨 가면서 어디까지 업체가 관리하고 어디부터 내가 관리하는지가 서비스마다 달라졌다.

내가 관리하는 범위가 좁을수록 사용할 수 있는 보안 설정도 줄어들었다.

그래서 서비스를 확인할 때는 먼저 IaaS, PaaS, SaaS 중 어떤 형태인지 구분해야 했다.

직원이 집에서 SaaS 에 바로 접속하면 회사 방화벽을 지나지 않았다.

이 경우 회사 방화벽 로그에는 접속 기록이 남지 않았고, 보안팀은 SaaS 서비스가 제공하는 로그인 기록과 활동 기록을 확인해야 했다.

2.1 클라우드 서비스의 세 형태

클라우드

내 건물이 아니라 업체의 서버를 인터넷으로 빌려 쓰는 방식이었다.

IaaS

Infrastructure as a Service

서버와 저장소, 네트워크를 빌리는 형태였다.

운영체제 설치 · 패치 · 보안 설정은 직접 관리했다.

PaaS

Platform as a Service

운영체제와 실행 환경까지 업체가 관리하는 형태였다.

회사는 코드와 데이터를 관리했다.

SaaS

Software as a Service

완성된 서비스를 계정만 받아 사용하는 형태였다.

회사는 계정 · 권한 · 데이터를 관리했다.

형태 내가 맡는 것 방화벽 규칙을 직접 걸 수 있나
IaaS 운영체제 · 보안 설정 · 코드 · 데이터 걸 수 있다
PaaS 코드 · 데이터 업체가 정한 범위 안에서만 걸 수 있다
SaaS 계정 · 권한 · 데이터 걸 자리가 없다

IaaS → PaaS → SaaS 로 갈수록 내가 직접 관리할 수 있는 범위가 줄어들었다.

실제 서비스로 보면 다음과 같았다.

형태 대표 서비스 회사가 하는 일의 예
IaaS AWS EC2 · Google Compute Engine · Azure Virtual Machines 가상 서버를 만들고 운영체제와 보안 설정을 관리했다
PaaS Vercel · Heroku · Azure App Service 코드를 올리고 환경 설정과 데이터를 관리했다
SaaS Google Docs · Microsoft 365 · Slack 직원 계정 · 권한 · 공유 범위를 관리했다

한 회사에서 세 가지 형태를 함께 사용하는 경우도 많았다.

예를 들면 회사 홈페이지는 Vercel, 사내 DB는 AWS EC2, 업무 문서는 Google Docs 를 사용할 수 있었다.

2.2 회사 방화벽을 지나지 않는 접속

예전에는 다음과 같은 경로로 인터넷에 접속했다.

사내 PC → 사내망 → 회사 방화벽 → 인터넷

모든 접속이 회사 방화벽을 지나기 때문에 한 곳에서 기록하고 통제할 수 있었다.

재택근무에서는 다음과 같이 접속할 수도 있었다.

집 PC → 인터넷 → SaaS 서비스

이 경우 회사 망과 회사 방화벽을 지나지 않았다.

따라서 회사 방화벽 로그에는 해당 접속이 남지 않았다.

가시성

가시성(visibility)은 무슨 일이 일어나는지 보안팀이 확인할 수 있는 정도를 뜻했다.

접속이 회사 방화벽을 지나지 않으면 가시성이 떨어졌다.

이때는 SaaS 서비스가 남기는 기록을 확인해야 했다.

SaaS 에서는 주로 두 가지 기록을 볼 수 있었다.

  • 로그인 기록: 누가 · 언제 · 어느 IP 에서 로그인했는지
  • 활동 기록: 로그인한 뒤 무엇을 열람하거나 다운로드했는지

직원이 집에서 Google Docs 나 Slack 에 로그인하면 회사 방화벽을 지나지 않았다.

따라서 방화벽 로그가 아니라 Google Workspace 같은 서비스의 로그인 기록과 활동 기록을 확인해야 했다.

퇴사 예정자 계정이 새벽에 낯선 IP 에서 로그인했다면 계정 도용을 먼저 의심할 수 있었다.

2교시 한눈에

하려는 일 보는 곳
서비스 형태 구분 운영체제까지 직접 관리하면 IaaS · 코드만 관리하면 PaaS · 계정만 관리하면 SaaS
사내에서 접속한 기록 회사 방화벽 로그
집 · 외부에서 SaaS 에 접속한 기록 SaaS 의 로그인 기록
조치 보고 확인된 것과 확인하지 못한 것을 나눠 작성했다

 

3교시 — 망 구성도 읽고 고치기 · DMZ

왜 필요한가

고객사 환경을 처음 확인할 때는 먼저 망 구성도를 확인했다.

구성도를 보면 어떤 서버가 인터넷에 공개되어 있고 어떤 서버가 내부망에 있는지 확인할 수 있었다.

웹 서버 같은 공개 서버가 내부망 안에 있는지 별도의 DMZ 에 있는지에 따라 사고가 발생했을 때 피해 범위가 달라질 수 있었다.

3.1 구성도를 읽는 순서

구성도

장비와 장비 사이의 연결을 그린 그림이었다.

구역

방화벽이 네트워크를 나눈 영역이었다.

이번에는 다음 세 구역을 사용했다.

  • outside : 인터넷
  • DMZ
  • inside : 내부망

DMZ

Demilitarized Zone

외부에 공개해야 하는 서버만 따로 배치한 중간 구역이었다.

웹 서버가 공격을 받더라도 내부 서버로 접근하려면 방화벽을 한 번 더 지나야 했다.

도식에서 사용한 기호

모양 뜻
INTERNET 인터넷
FW 방화벽
INSIDE 내부망
( WEB ) 해당 구역에 있는 서버
WEB 웹 서버
DB 데이터베이스 서버
──── 연결선
┬ · │ · └ 연결이 갈라지는 부분

구성도는 왼쪽에서 오른쪽으로 선을 따라 읽었다.

INTERNET ──── FW ──── INSIDE ( PC · FILE )

이 구성은 인터넷에서 오는 통신이 방화벽을 지나 내부망으로 들어가고, 내부망에는 직원 PC 와 파일 서버가 있다는 뜻이었다.

공개 서버는 없었다.

연결이 갈라지는 구성은 다음처럼 읽었다.

INTERNET ──── FW ──┬── DMZ    ( MAIL )
                   └── INSIDE ( PC )

방화벽에서 길이 두 개로 나뉘었고, 메일 서버는 DMZ 에 있었으며 직원 PC 는 내부망에 있었다.

공개 서버가 내부 서버와 같은 구역에 있다면 DMZ 가 없는 구성이었다.

방화벽의 개수만 보고 DMZ 여부를 판단하면 안 됐다.

회사 홈페이지 서버나 메일 서버처럼 인터넷에서 접속해야 하는 서버는 DMZ 에 두고, DB 서버나 파일 서버는 내부망에 두는 방식으로 구성했다.

클라우드에서도 같은 원칙을 적용할 수 있었다.

예를 들어 AWS EC2 에 만든 웹 서버는 443번 포트만 외부에 열고, DB 서버는 인터넷에서 직접 접근할 수 없도록 구성할 수 있었다.

경보와 로그 읽기

경보(alert)는 보안 장비가 이상한 통신을 발견했을 때 발생시키는 알림이었다.

로그는 다음 순서로 읽었다.

번호 → 시각 → 출발지 → 목적지 → 포트 → 결과

permit은 허용을 의미했고 deny는 차단을 의미했다.

3교시 한눈에

하려는 일 보는 곳
공개 서버가 어느 구역인지 구성도에서 WEB 이 들어 있는 구역
DMZ 가 있는지 WEB 과 DB 가 다른 구역에 있는지
해킹 시 번지는 경로 WEB → DB 사이의 방화벽
로그가 이상한지 출발지 → 목적지가 가면 안 되는 경로인지
고친 구성도 남기기 ASCII 로 구성도를 작성했다

 

4교시 — 구역 규칙을 판정하는 코드 읽기와 만들기

왜 필요한가

고객이 요구사항대로 방화벽 규칙이 설정됐는지 확인해 달라고 하면 눈으로만 확인하는 것보다 코드로 요청을 넣어 판정하는 것이 정확했다.

이전에는 IP 주소와 포트를 기준으로 방화벽 규칙을 판정했고, 이번에는 outside · DMZ · inside 같은 구역을 이용해 규칙을 판정했다.

값 두 개를 리턴하고 받기

함수에서는 값을 쉼표로 이어 두 개 이상 리턴할 수 있었다.

def two():
    return "permit", 1

a, n = two()

print(a, n)
print(two())

결과는 다음과 같았다.

permit 1
('permit', 1)

값을 변수 두 개로 받으면 각각 나눠서 저장됐다.

변수 하나로 받으면 튜플 하나로 저장됐다.

기본 정책

기본 정책은 규칙에 일치하는 항목이 없을 때 사용하는 기본 동작이었다.

이번 코드에서는 같은 구역끼리의 통신, 내부망에서 출발하는 통신, 인터넷으로 나가는 통신은 허용하고 나머지는 차단하도록 했다.

튜플

규칙 하나는 다음과 같은 튜플로 표현했다.

(1, "outside", "DMZ", 443, "permit")

튜플 안의 값을 번호로 꺼낼 수도 있었다.

rule[0]
rule[4]

rule[0]은 규칙 번호였고 rule[4]는 동작이었다.

한 번에 여러 변수로 나눠 받을 수도 있었다.

no, r_src, r_dst, r_port, action = rule

4교시 한눈에

하려는 일 코드
규칙 하나가 요청과 맞는지 match_rule(rule, src, dst, port)
규칙표를 위에서부터 판정 judge(src, dst, port)
허용되는 포트만 모으기 allowed_ports(src, dst, ports)
값 두 개 리턴 · 받기 return a, b · x, y = f()

 

5교시 — 지연 · 지터 · 패킷 손실

왜 필요한가

사용자가 인터넷이 느리다고 말하는 것만으로는 정확한 원인을 확인하기 어려웠다.

따라서 느린 현상을 숫자로 표현해야 했다.

이번에는 다음 세 가지를 확인했다.

  1. 지연
  2. 지터
  3. 패킷 손실

5.1 세 가지 숫자

지연

Latency

패킷을 보낸 뒤 응답이 돌아올 때까지 걸리는 시간이었다.

실무에서는 왕복 시간인 RTT를 주로 확인했다.

단위는 ms를 사용했다.

지터

Jitter

응답 시간이 얼마나 일정하지 않은지를 나타내는 값이었다.

이번 수업에서는 이웃한 두 응답 시간의 차이의 평균으로 계산했다.

패킷 손실

Packet Loss

보낸 패킷 가운데 응답이 오지 않은 비율이었다.

손실된 데이터는 다시 전송해야 했다.

예를 들어 응답 시간이 다음과 같았다.

50ms
52ms
120ms
48ms

평균은 67.5ms였다.

평균만 보면 크게 문제가 없어 보일 수 있었지만 세 번째 응답이 120ms로 크게 튀었다.

화상회의나 음성 통화 같은 실시간 서비스에서는 패킷이 일정한 간격으로 도착해야 하기 때문에 평균 지연이 낮더라도 지터가 크면 끊김이 발생할 수 있었다.

지연이 일정하게 큰 경우에는 조금 늦게 들릴 수 있었고, 지터나 손실이 크면 음성이나 화면이 끊길 수 있었다.

대용량 파일 다운로드가 느린 경우에는 지연보다는 대역폭을 먼저 확인했다.

온라인 게임에서 사용하는 「핑 30」이라는 표현도 RTT가 약 30ms라는 뜻이었다.

5.2 ping 으로 직접 측정하기

Windows에서는 다음 명령을 사용했다.

ping -n 10 8.8.8.8

10번의 요청을 보내 각각의 응답 시간을 확인했다.

결과에서는 다음 항목을 확인했다.

  • 각각의 응답 시간
  • 평균 지연
  • 패킷 손실
  • 최소 시간
  • 최대 시간

응답이 없는 경우에는 요청 시간이 만료됐다는 메시지가 나왔고, 이를 패킷 손실 한 번으로 볼 수 있었다.

5교시 한눈에

확인하는 것 방법
지연 응답 시간의 평균
지터 이웃한 두 값의 차이의 평균
손실 못 받은 개수 ÷ 보낸 개수 × 100
직접 측정 ping -n 10 주소

 

6교시 — 코드로 세 숫자 계산하고 판정하기

왜 필요한가

회선이 여러 개이고 측정값이 많아지면 직접 계산하기 어려웠다.

5교시에서 계산한 지연 · 지터 · 손실을 코드로 계산하도록 만들었다.

계산한 숫자에 기준을 적용해 화상회의에 적합한 회선인지도 판정했다.

abs()

abs()는 숫자의 절댓값을 구했다.

print(abs(5))
print(abs(-5))
print(abs(31 - 30))
print(abs(29 - 31))

결과는 다음과 같았다.

5
5
1
2

지터를 계산할 때 이웃한 두 값 중 어느 값이 더 큰지 신경 쓰지 않고 차이를 계산할 수 있었다.

독스트링

함수 이름 아래에 따옴표 세 개로 설명을 작성하면 함수 전체를 설명하는 독스트링을 만들 수 있었다.

def average(nums):
    """응답 시간 리스트의 평균을 구한다.

    입력: 응답 시간 리스트 nums
    리턴: 평균 값
    """

    return sum(nums) / len(nums)

help()를 사용하면 독스트링을 확인할 수 있었다.

print(average([30, 31, 29]))
help(average)

주석은 코드 한 줄을 설명하는 데 사용했고, 독스트링은 함수 전체의 역할 · 입력 · 반환값을 설명하는 데 사용했다.

지터 계산

다음과 같은 반복 범위를 사용할 수 있었다.

range(1, len(got))

0번째 값에는 바로 앞 값이 없기 때문에 1번부터 반복했다.

현재 값과 바로 앞 값을 이용해 차이를 계산했다.

got[i]
got[i - 1]

응답이 없는 ping 값은 None으로 표현했다.

None

응답이 있는 값만 사용하려면 다음처럼 확인했다.

if v != None:
    got.append(v)

화상회의 판정 기준

이번 수업에서 사용한 기준은 다음과 같았다.

항목 기준
평균 지연 150ms 이하
지터 30ms 이하
손실률 1% 이하

세 기준을 모두 만족해야 적합으로 판정했다.

하나라도 기준을 넘으면 부적합으로 판정했다.

어떤 값이 기준을 넘었는지 확인하면 문제의 원인도 좁힐 수 있었다.

값 세 개를 한 번에 리턴할 수도 있었다.

return delay, jitter, loss

받을 때도 세 개의 변수로 나눠 받을 수 있었다.

delay, jitter, loss = measure(...)

여러 원인을 한 줄로 합칠 때는 join()을 사용할 수 있었다.

", ".join(reasons)

예를 들어 다음 리스트가 있다면

["지연", "지터"]

다음과 같은 문자열이 만들어졌다.

지연, 지터

6교시 한눈에

하려는 일 코드
응답이 있는 값만 모으기 if v != None: got.append(v)
이웃한 차이 계산 abs()
세 기준을 모두 확인 비교식을 and로 연결
여러 원인을 한 줄로 만들기 ", ".join(reasons)

 

7교시 — tracert 로 느려진 구간 찾기 · 5일 차 품질 진단 리포트

왜 필요한가

ping으로 지연이 크다는 것을 확인했다면 다음에는 어느 구간에서 지연이 증가하는지 찾아야 했다.

문제가 회사 내부인지 통신사인지 SaaS 업체인지에 따라 문제를 해결해야 하는 주체가 달라졌다.

이때 tracert를 사용할 수 있었다.

7.1 tracert 로 느려진 구간 확인하기

tracert는 목적지까지 거쳐 가는 장비를 순서대로 보여 주고 각 장비까지의 응답 시간을 확인할 수 있는 명령이었다.

tracert google.com

각 장비 하나를 홉이라고 불렀다.

내 PC
  ↓
1번 홉 : 공유기
  ↓
2번 홉 : 통신사 장비
  ↓
3번 홉 : 다른 라우터
  ↓
...
  ↓
목적지 서버

각 홉의 시간은 바로 앞 홉에서 그 장비까지 걸린 시간이 아니었다.

내 PC 에서 해당 장비까지 갔다가 돌아오는 왕복 시간이었다.

tracert 결과 읽는 방법

시간이 처음 크게 증가한 홉을 찾았다.

그 이후 홉도 계속 높은 값을 유지하면 처음 증가한 홉과 바로 앞 홉 사이의 구간에서 문제가 발생했을 가능성이 있었다.

예를 들어 다음과 같았다.

1번 홉     1ms
2번 홉     3ms
3번 홉     5ms
4번 홉    80ms
5번 홉    85ms
6번 홉    82ms

3번 홉까지는 정상이었지만 4번 홉부터 지연이 크게 증가했다.

따라서 3번과 4번 사이의 구간을 의심할 수 있었다.

반대로 한 홉에서만 시간이 크게 증가했다가 다음 홉에서 다시 낮아진다면 실제 네트워크 장애가 아닐 수도 있었다.

1번 홉     1ms
2번 홉     3ms
3번 홉   100ms
4번 홉     7ms
5번 홉     8ms

3번 장비가 tracert 응답만 늦게 보낸 것일 수 있었다.

실제 패킷 전달 자체는 정상일 가능성이 있었다.

7.2 순서대로 좁혀 가는 진단

SaaS 접속이 느리다는 문제가 발생했다면 다음 순서로 확인할 수 있었다.

  1. ping으로 지연 · 지터 · 손실을 확인했다.
  2. tracert로 어느 구간에서 지연이 증가하는지 확인했다.
  3. 문제가 회사 내부라면 방화벽 · 프록시 로그를 확인했다.
  4. 회사 내부가 정상이라면 통신사 또는 SaaS 업체에 측정 결과와 함께 확인을 요청했다.
  5. SaaS 제공자의 상태 페이지에 장애 공지가 있는지도 확인했다.

이 과정을 통해 단순히 「인터넷이 느리다」라고 판단하는 것이 아니라 지연 · 지터 · 손실을 숫자로 확인하고, tracert를 이용해 문제가 발생한 구간까지 좁힐 수 있었다.

 

 

 

---

 

와..드디어 금요일이 찾아왔어요,,

내일은 여행을 가기때문에 집에가면 바로 짐싸구 호다닥 자야될 것 같아요

힘든 수업 와중에 소소한 즐거움,,^____^

그럼 아됴

안녕하세요.

오늘 목표는 감자 이론 다끝내구 C언어까지 한번 다 해보는거에요.... 과연..!

그럼 모닝 30 레츠고.

오늘 모닝30은 그냥 VSCode 한글화하고 확장프로그램 추가 설치가 끝이였어용..

터미널 위치도 오른쪽으로 변경하기 있었는데, 저는 아래가 더 편해서 그대루 납뒀답니다.. 그럼.. 시작!


오늘의 순서

교시 무엇
2교시 VPN 이 필요한 이유 · 터널 · 암호화
3교시 Site-to-Site VPN · Remote Access VPN
4교시 VPN 접속 로그 · VPN 의 한계
5교시 방화벽이 하는 일 · 규칙 읽기
6교시 허용 목록과 차단 목록 · 규칙 순서
7교시 규칙표 직접 쓰기 · 4일 차 방화벽 규칙서 완성

 

2교시 — VPN 이 필요한 이유 · 터널 · 암호화

왜 필요한가

재택근무 · 출장 중인 직원도 회사 업무 포털 · 메일 서버를 사용해야 했다.

하지만 회사 서버에 인터넷 어디서든 접속할 수 있도록 허용하면 누구나 접속을 시도할 수 있었다.

집 · 카페에서 회사까지는 공용 인터넷이기 때문에 중간 장비나 같은 와이파이에 연결된 사람이 내용을 볼 수도 있었다.

VPN 은 이 두 문제를 해결하기 위해 사용했다.

회사 서버는 VPN 을 거친 사람에게만 열고, 회사 VPN 은 보통 터널링과 암호화를 함께 사용해 오가는 내용을 숨겼다.

2.1 터널링 — 패킷을 다른 패킷 안에 넣어 보냈다

여기 나오는 말

VPN (Virtual Private Network)

공용 인터넷 위에서 회사 망에 직접 연결된 것처럼 통신하게 해 주는 기술이었다.

터널링 (tunneling)

원래 패킷을 통째로 다른 패킷 안에 넣어 보내는 방식이었다.

10/12 에 배운 캡슐화처럼 패킷을 한 번 더 감싸는 방식이었다.

터널링만 하면 패킷을 감쌀 뿐 내용이 숨겨지지는 않았고, 실제 내용을 숨기는 것은 암호화였다.

암호화 (encryption)

중간에서 보더라도 읽을 수 없는 형태로 내용을 바꾸는 것이었다. 키를 가진 쪽에서만 원래 내용으로 복호화할 수 있었다.

VPN 게이트웨이

회사 쪽에서 VPN 연결을 맺는 장비였다.

바깥 헤더를 제거하고 암호화된 내용을 복호화한 뒤 원래 패킷을 사내망으로 전달했다.

VPN 주소

VPN 에 접속한 PC 가 VPN 게이트웨이에서 받은 사내망 주소였다.

공인 IP

인터넷에서 사용하는 주소였다.

네트워크 어댑터

PC 가 네트워크에 연결될 때 사용하는 장치나 설정이었다.

랜 카드 · 와이파이처럼 실제 장치도 있었고, VPN 프로그램이 만드는 가상 어댑터도 있었다.

집 PC ─[원래 패킷: 출발 10.8.0.21 → 도착 10.10.20.5]
      │
      │ ① 원래 패킷을 암호화
      │ ② 바깥 헤더 추가
      │    출발 211.36.10.7
      │    → 도착 203.0.113.1
      ▼
공용 인터넷
      │
      │ 중간 라우터는 바깥 헤더만 확인
      ▼
회사 VPN 게이트웨이
      │
      │ ③ 바깥 헤더 제거
      │ ④ 복호화 후 사내망으로 전달
      ▼
업무 포털 10.10.20.5
구분 VPN 없이 VPN 사용
중간 라우터가 보는 도착지 업무 포털 서버 주소 회사 VPN 게이트웨이 주소
안쪽 내용 · 사내 주소 암호화하지 않으면 보임 암호화되어 보이지 않음
회사 서버가 보는 출발지 집 공인 IP VPN 주소
내 PC 의 어댑터 랜 · 와이파이 랜 · 와이파이 + VPN 가상 어댑터

VPN 을 사용하지 않으면 내 PC 가 인터넷을 통해 회사 서버와 직접 통신했다.

VPN 을 사용하면 원래 패킷을 암호화한 뒤 바깥 패킷으로 감싸 VPN 게이트웨이까지 전달했다.

VPN 게이트웨이에서는 바깥 헤더를 제거하고 복호화한 뒤 원래 패킷을 사내망으로 전달했다.

VPN 에 연결하면 PC 에 가상 어댑터가 하나 추가되는 것도 확인했다.

내 컴퓨터
├─ 와이파이 어댑터 → 일반 인터넷
└─ VPN 가상 어댑터 → 암호화된 터널 → 회사망

가상 어댑터는 실제 부품을 추가하는 것이 아니라 VPN 프로그램이 소프트웨어로 만든 장치였다.

VPN 을 연결하면 이 어댑터에 회사에서 받은 VPN 주소가 할당됐다.

딕셔너리 안의 딕셔너리

딕셔너리 안에 또 다른 딕셔너리가 있을 때 키를 두 번 사용해 값을 꺼낼 수 있었다.

outer = {"dst": "203.0.113.1", "data": {"dst": "10.10.20.5"}}

print(outer["dst"])
print(outer["data"])
print(outer["data"]["dst"])
203.0.113.1
{'dst': '10.10.20.5'}
10.10.20.5
쓰는 것 뜻
d["data"] 바깥 딕셔너리에서 data 값을 꺼냈다
d["data"]["dst"] 안쪽 딕셔너리에서 dst 값을 다시 꺼냈다

 

3교시 — Site-to-Site VPN · Remote Access VPN

왜 필요한가

VPN 은 연결 대상에 따라 Site-to-Site VPN 과 Remote Access VPN 으로 나뉜다는 것을 알게 됐다.

본사와 지점처럼 망과 망을 연결할 수도 있었고, 직원의 노트북 한 대를 회사 망에 연결할 수도 있었다.

3.1 망과 망의 연결인가, 기기 한 대의 연결인가

Site-to-Site VPN

망과 망을 연결하는 VPN 이었다.

본사와 지점의 VPN 게이트웨이가 서로 연결을 맺고 계속 유지했다.

Remote Access VPN

직원 PC 한 대를 회사 망에 연결하는 VPN 이었다.

PC 에 VPN 클라이언트를 설치하고 사용자가 직접 접속했다.

주소 풀

Remote Access VPN 에 접속한 PC 에 VPN 게이트웨이가 나눠 주는 주소 범위였다.

예:

10.8.0.0/24
구분 Site-to-Site VPN Remote Access VPN
연결 대상 망 ↔ 망 PC 한 대 ↔ 회사 망
연결 시작 VPN 게이트웨이 직원 PC 의 VPN 클라이언트
직원이 하는 일 없음 직접 프로그램을 실행하고 로그인
연결 유지 계속 유지 접속할 때만 유지
회사 서버가 보는 출발지 지점 PC 의 주소 VPN 주소
사용 예 본사 ↔ 지점 재택근무 · 출장

VPN 의 종류를 구분할 때는 장소보다 연결 대상을 봐야 한다는 것을 알게 됐다.

집이나 호텔에서 사용한다고 해서 무조건 Remote Access VPN 은 아니었다.

VPN 의 반대편이 회사 VPN 게이트웨이인지 먼저 확인해야 했다.

3.2 로그의 출발지 주소로 어디서 왔는지 확인했다

회사 방화벽이나 서버 로그에는 출발지 주소가 남았다.

이 주소가 어느 네트워크 범위에 포함되는지를 확인하면 어디에서 들어온 통신인지 구분할 수 있었다.

범위 뜻
10.8.0.0/24 Remote Access VPN 으로 들어온 직원
192.168.20.0/24 Site-to-Site VPN 으로 연결된 부산 지점
10.10.0.0/16 본사 PC · 서버

VPN 게이트웨이에는 누가 · 언제 · 어느 공인 IP 에서 접속했고 어떤 VPN 주소를 받았는지가 기록된다는 것도 알게 됐다.


 

4교시 — VPN 접속 로그 · VPN 의 한계

왜 필요한가

VPN 게이트웨이는 누가 · 언제 · 어디에서 접속했는지 로그를 남겼다.

비밀번호를 훔친 공격자가 VPN 에 로그인해도 이 접속 기록에 흔적이 남았다.

하지만 VPN 은 처음 들어올 때 사용자를 확인한 뒤 사내망에서는 다시 확인하지 않는다는 한계도 있었다.

VPN 계정이 탈취되면 공격자도 회사 내부망에 들어온 것처럼 행동할 수 있었다.

4.1 VPN 접속 로그에서 확인한 것

무차별 대입

한 계정에 여러 비밀번호를 반복해서 입력하는 공격이었다.

계정 잠금

로그인에 일정 횟수 이상 실패하면 일정 시간 로그인을 막는 기능이었다.

비밀번호 스프레이

여러 계정에 흔히 사용하는 비밀번호를 한두 번씩 시도하는 공격이었다.

계정마다 실패 횟수가 적기 때문에 계정 잠금을 피할 수 있었다.

확인한 것 의심할 수 있는 내용
한 계정의 로그인 실패가 반복됨 무차별 대입 또는 사용자 실수
같은 IP 에서 여러 계정이 실패 비밀번호 스프레이
여러 번 실패한 뒤 성공 비밀번호가 맞았을 가능성

리스트를 값으로 사용하는 딕셔너리도 사용했다.

seen = {}

for user in ["kim", "lee", "kim"]:
    users = seen.get("A", [])

    if user not in users:
        users.append(user)

    seen["A"] = users

print(seen, len(seen["A"]))
{'A': ['kim', 'lee']} 2

.get() 을 사용하면 키가 없을 때 기본값을 지정할 수 있었다.

not in 과 .append() 를 사용해 중복되지 않는 값만 리스트에 넣을 수도 있었다.

4.2 VPN 의 한계

VPN 게이트웨이는 처음 접속할 때 아이디와 비밀번호를 확인했다.

하지만 접속이 허용된 뒤에는 해당 PC 가 회사 내부에 있는 것처럼 동작했다.

[VPN 게이트웨이]
아이디 · 비밀번호 확인
          │
          ▼
        허용
          │
          ▼
사내망
├─ 업무 포털
├─ 관리 서버
└─ 고객 DB

그래서 VPN 접속 자체만 허용하는 것으로 끝나는 것이 아니라 내부에서 접근할 수 있는 서버와 포트도 제한해야 한다는 것을 알게 됐다.


 

5교시 — 방화벽이 하는 일 · 규칙 읽기

왜 필요한가

VPN 이 누가 회사망에 들어오는지를 확인했다면, 방화벽은 들어온 요청을 어디까지 보낼 것인지를 정했다.

방화벽은 규칙표를 위에서부터 읽으면서 요청을 허용하거나 차단했다.

5.1 첫 번째로 일치하는 규칙

방화벽 규칙에는 출발지 · 목적지 · 포트 · 동작이 들어갔다.

any 는 모든 값과 일치하는 의미로 사용했다.

번호 누가 어디로 포트 동작
1 직원 업무 포털 443 허용
2 직원 웹메일 443 허용
3 부산 지점 업무 포털 443 허용
4 업무 포털 고객 DB 3306 허용
5 any any any 차단

방화벽 규칙은 위에서부터 읽고 처음 일치한 규칙을 사용했다.

예를 들어 직원이 웹메일의 443번 포트에 접속하면 1번 규칙에서는 목적지가 일치하지 않았다.

2번 규칙에서는 출발지 · 목적지 · 포트가 모두 일치했기 때문에 허용됐다.

그 이후의 규칙은 확인하지 않았다.

5.2 막힌 연결을 패킷으로 확인했다

방화벽이 통신을 막을 때는 드롭과 거부 방식이 있다는 것을 알게 됐다.

방식 동작 Wireshark 에서 보이는 것
드롭 패킷을 버리고 응답하지 않음 SYN 재전송
거부 연결을 거절한다고 응답 RST

정상적인 TCP 연결은 다음과 같았다.

내 PC ── SYN ───────▶ 서버
내 PC ◀─ SYN/ACK ─── 서버
내 PC ── ACK ───────▶ 서버

방화벽이 SYN 을 드롭하면 다음과 같이 보였다.

내 PC ── SYN ──▶ [방화벽: 버림]
내 PC ◀─ 응답 없음
내 PC ── SYN ──▶ 다시 전송

Wireshark 에서는 다시 보낸 SYN 에 [TCP Retransmission] 이 표시됐다.

따라서

SYN 만 반복되고 SYN/ACK 가 없음 → 드롭 의심
SYN 바로 뒤에 RST → 거부 또는 닫힌 포트

으로 구분할 수 있었다.

방화벽 로그와 패킷을 함께 보면 같은 사건을 서로 다른 위치에서 확인할 수 있다는 것도 알게 됐다.


 

6교시 — 허용 목록과 차단 목록 · 규칙 순서

왜 필요한가

방화벽 규칙을 만드는 방식에는 허용 목록과 차단 목록이 있었다.

허용 목록은 허용할 통신만 적고 나머지를 차단했다.

차단 목록은 막을 통신만 적고 나머지를 허용했다.

구분 허용 목록 차단 목록
규칙에 적는 것 허용할 통신 차단할 통신
마지막 규칙 모두 차단 모두 허용
규칙에 없는 통신 차단 허용

허용 목록 방식이 업무에 필요한 접근만 허용하는 최소 권한 원칙과 연결된다는 것을 알게 됐다.

6.2 방화벽 정책을 밖에서 확인했다

방화벽 설정 파일을 직접 볼 수 없는 경우에는 포트별 접속을 시험해 정책을 확인할 수 있었다.

443번은 HTTPS 로 수업에 필요해서 열려 있어야 했다.

445번은 SMB 파일 공유 포트였고, 외부에서 사용할 일이 적어 차단되는 경우가 많았다.

3389번은 원격 데스크톱 포트였고 외부에 열려 있으면 위험할 수 있었다.

Wireshark 에서는 다음 필터를 사용했다.

tcp.port == 443 || tcp.port == 445 || tcp.port == 3389

|| 는 또는이라는 뜻이었다.

세 포트 가운데 하나라도 해당하면 화면에 표시됐다.

TCP 연결을 시작하는 SYN 만 확인할 때는 다음 필터를 사용할 수 있었다.

tcp.flags.syn == 1 && tcp.flags.ack == 0

재전송 패킷만 보고 싶을 때는 다음 필터를 사용할 수 있었다.

tcp.analysis.retransmission

이 필터를 이용하면 응답을 받지 못해 다시 전송한 패킷만 확인할 수 있었다.

6.3 규칙 순서

방화벽 규칙에서는 /32, /24, /16, /0 같은 CIDR 범위를 사용했다.

/32 → IP 주소 1개
/24 → 마지막 한 자리가 달라지는 범위
/16 → 뒤 두 자리가 달라지는 범위
/0  → 모든 IPv4 주소
대상 주소
직원 VPN 10.8.0.0/24
부산 지점 192.168.20.0/24
업무 포털 10.10.20.5/32
웹메일 10.10.20.6/32
고객 DB 10.10.30.7/32
관리 서버 10.10.20.9/32
보안팀 관리자 PC 10.10.40.10/32
모든 곳 0.0.0.0/0

CIDR 숫자가 클수록 범위가 좁아졌다.

/32 → 가장 좁음
/24
/16
/0 → 가장 넓음

넓은 규칙을 위에 두면 좁은 규칙에 맞는 요청도 넓은 규칙에 먼저 걸릴 수 있었다.

그래서 방화벽 규칙은 좁은 규칙을 위에 두고 넓은 규칙을 아래에 두는 것이 중요했다.

마지막에는 모두 차단 규칙을 두었다.


 

7교시 — 규칙표 직접 쓰기 · 4일 차 방화벽 규칙서 완성

5 · 6교시에서는 주어진 규칙을 읽고 요청이 허용되는지 차단되는지 직접 판단했다.

7교시에서는 요구사항을 보고 직접 규칙표를 만들고, 코드로 결과를 확인했다.

방화벽 규칙은 요청마다 처음부터 다시 확인해야 했다.

RULES = [
    (1, "A"),
    (2, "B"),
    (3, "A")
]

for req in ["A", "B"]:
    found = False

    for no, letter in RULES:
        if found == False:
            if letter == req:
                print(req, "→ 규칙", no)
                found = True
A → 규칙 1
B → 규칙 2

found = False 를 요청마다 다시 설정했다.

첫 번째로 맞는 규칙을 찾으면 found = True 로 바꿔 그 뒤 규칙은 사용하지 않았다.

실제 방화벽 규칙을 코드로 확인할 때는 IP 주소가 특정 네트워크 범위에 포함되는지도 확인했다.

ipaddress.ip_address(주소) in ipaddress.ip_network(망)

출발지 · 목적지 · 포트가 모두 맞아야 해당 규칙과 일치했다.

src_ok and dst_ok and port_ok

포트가 any 이거나 요청한 포트와 같으면 일치하도록 처리할 수도 있었다.

rport == "any" or rport == port

오늘 실습을 통해 VPN 으로 회사망에 들어오는 과정과 방화벽이 내부 접근을 제한하는 과정을 연결해서 확인했다.

또한 방화벽에서는 규칙 자체뿐만 아니라 규칙의 순서가 결과에 영향을 준다는 것을 알게 됐다.

 

 

 

오늘 너무 힘들어요

하지만? 내일 금요일이라는 사실,,,,,,,,,,,,,,,,,,,!!!!!!!!!!!!!!!!!!!!!

그럼 아됴

+ Recent posts