NTP
고친 사람 github-actions[bot]
NTP 는 네트워크에 연결된 컴퓨터들이 같은 시각을 가리키도록 시계를 맞춰 줍니다. 시간 서버에 지금 몇 시냐고 묻고, 그 답이 오가는 데 걸린 시간을 걷어 내 내 시계가 얼마나 어긋났는지 알아냅니다. 알아낸 어긋남은 한 번에 뛰어넘지 않고 시계가 가는 속도를 바꿔 서서히 메웁니다.
쉽고 빠른 이해
NTP 는 컴퓨터 시계를 시간 서버의 시각에 맞추는 프로토콜입니다. 서버마다 시계가 제각각이면 장애가 났을 때 로그를 시각순으로 늘어놓을 수 없는데, 그걸 막아 줍니다.
컴퓨터 시계는 혼자 두면 하루에 몇 초씩 어긋납니다. 인증서 유효 기간 검사도, 여러 서버의 로그 대조도, 시각으로 ID 를 만드는 방식도 모두 시계가 맞다는 전제 위에 서 있습니다.
어떻게 도는가:
- 서버에 한 번 묻고 답을 받습니다. 그 한 왕복에서 시각 네 개가 모입니다. 내가 보낸 시각, 서버가 받은 시각, 서버가 답한 시각, 내가 받은 시각입니다
- 네 시각을 더하고 빼면 왕복에 걸린 시간과, 내 시계가 서버 시계보다 얼마나 앞서거나 뒤진지가 각각 나옵니다
- 어긋남이 작으면 시계 속도를 조금 바꿔 서서히 메우고, 크면 시각을 한 번에 옮깁니다
대가도 있습니다. 가는 길과 오는 길이 똑같이 걸린다고 가정하는데, 한쪽만 느리면 그 차이의 절반이 그대로 오차로 남습니다. 기본 형태에는 상대를 확인하는 장치가 없어서 중간에서 시각을 바꿔치기할 수도 있습니다.
상세
NTP(Network Time Protocol, 네트워크 시간 프로토콜)는 컴퓨터가 시간 서버에 시각을 물어 자기 시계를 맞추는 프로토콜입니다. 이 절은 시계가 왜 어긋나는지, 물어서 얻은 시각으로 오차를 어떻게 계산하는지, 그 오차를 시계에 어떻게 반영하는지를 차례로 봅니다.
전화로 시각을 묻는 것과 비슷합니다. 다만 상대가 "세 시 정각"이라고 말한 순간과 내가 그 말을 들은 순간 사이에 시간이 흐릅니다. NTP 가 하는 일의 절반은 그 흐른 시간을 걷어 내는 것입니다.
컴퓨터 시계가 어긋나는 이유
컴퓨터 안에는 수정 진동자가 들어 있습니다. 운영체제는 그 진동을 세어 시각을 만듭니다. 진동 수는 온도와 전압에 따라 미세하게 달라집니다.
그래서 어떤 컴퓨터의 시계는 조금씩 빨라지고 어떤 컴퓨터의 시계는 조금씩 늦어집니다. 이렇게 시계가 저 혼자 흘러가며 벌어지는 것을 클록 드리프트라고 부릅니다.
하루에 몇 초 어긋나는 정도라면 사람에게는 별일이 아닙니다. 그런데 소프트웨어는 시각을 기준으로 판단하는 일이 많습니다. TLS(Transport Layer Security, 전송 계층 보안) 인증서의 유효 기간 검사, 여러 서버의 로그를 시각순으로 늘어놓는 장애 분석, 캐시 만료 판정이 모두 시계에 매여 있습니다.
시계가 뒤로 감기면 더 아픕니다. 시각으로 고유 ID를 만드는 방식은 같은 번호를 두 번 내놓지 않으려고 시계가 뒤로 간 순간 발급을 멈춰 버립니다.
UDP 123 번 포트로 오가는 메시지
NTP 메시지는 UDP(User Datagram Protocol, 사용자 데이터그램 프로토콜) 위에 실려 123 번 포트로 오갑니다. 왜 UDP 일까요. 시각 요청은 한 번에 하나씩 짧게 오가고, 답이 안 오면 다음 차례에 다시 물으면 그만이라 연결을 맺어 둘 이유가 없습니다.
TCP(Transmission Control Protocol, 전송 제어 프로토콜)의 재전송 장치는 여기서 오히려 해롭습니다. 시각 요청은 패킷을 내보내는 그 순간의 시각을 담아야 맞는데, TCP 가 뒤에서 다시 보내 주면 담긴 시각과 실제로 나간 시각이 어긋납니다.
패킷 머리의 첫 네 바이트에는 짧은 필드 여섯이 붙어 있습니다.
packet-beta 0-1: "LI" 2-4: "VN" 5-7: "Mode" 8-15: "Stratum" 16-23: "Poll" 24-31: "Precision"
숫자는 비트 자리입니다. 맨 앞의 LI(Leap Indicator, 윤초 표시)가 두 비트, 그다음 VN(Version Number, 판 번호)과 Mode 가 각각 세 비트를 쓰고, 나머지 셋이 여덟 비트씩 차지합니다. 각 필드가 담는 것은 아래와 같습니다.
| 필드 | 담는 것 |
|---|---|
| LI | 이달 마지막 분에 윤초가 끼어들거나 빠지는지 알린다 |
| VN | 몇 번째 판의 NTP 인지. 지금 쓰는 것은 4 다 |
| Mode | 이 패킷이 요청인지 응답인지. 클라이언트는 3, 서버는 4 를 적는다 |
| Stratum | 기준이 되는 시계에서 몇 다리 건너왔나 |
| Poll | 이 컴퓨터가 얼마 간격으로 묻고 있나 |
| Precision | 이 컴퓨터의 시계가 시각을 얼마나 잘게 잴 수 있나 |
그 뒤로는 64비트짜리 타임스탬프 네 칸이 이어집니다. 그중 셋이 다음 절에서 오차를 계산하는 재료가 됩니다.
시각 네 개로 오차를 구하는 법
클라이언트는 요청을 보내면서 보낸 시각을 패킷에 적어 둡니다. 서버는 그 시각을 그대로 되돌려 주면서 자기가 받은 시각과 답하는 시각을 함께 실어 보냅니다. 클라이언트가 응답을 받는 순간까지 세면 시각 네 개가 모입니다.
sequenceDiagram
participant 클라이언트
participant 서버
클라이언트->>서버: 요청 · 보낸 시각 t1 을 담는다
Note over 서버: t2 에 받아서 t3 에 답한다
서버-->>클라이언트: 응답 · t1 · t2 · t3 을 담는다
Note over 클라이언트: t4 에 받는다 · 시각 넷이 모였다
이 넷으로 두 값을 뽑습니다. 하나는 왕복에 걸린 시간이고, 다른 하나는 두 시계가 벌어진 정도입니다. 앞의 것을 지연, 뒤의 것을 오프셋이라고 부릅니다.
지연은 전체 왕복 시간에서 서버가 붙들고 있던 시간을 빼면 나옵니다. 오프셋은 가는 길에서 잰 차이와 오는 길에서 잰 차이를 평균 낸 값입니다. 숫자를 넣어 보면 이렇습니다.
t1 = 10.000 // 요청을 보낸 시각
t2 = 10.020 // 서버가 받은 시각
t3 = 10.021 // 서버가 답한 시각
t4 = 10.045 // 응답을 받은 시각
지연 = (t4-t1) - (t3-t2) // 0.044초
오프셋 = ((t2-t1)+(t3-t4)) / 2 // -0.002초
오프셋이 음수라는 것은 내 시계가 서버 시계보다 앞서 있다는 뜻입니다. 2밀리초만큼 빨리 가고 있으니 그만큼 늦춰야 합니다. 이 계산은 요청 한 번, 응답 한 번으로 끝납니다.
대칭 경로라는 가정
오프셋 계산은 한 가지를 당연하게 여깁니다. 요청이 가는 데 걸린 시간과 응답이 오는 데 걸린 시간이 같다는 것입니다. 이 가정이 있어야 왕복 시간의 절반을 한쪽 길의 시간으로 볼 수 있습니다.
실제 라우팅은 그렇게 얌전하지 않습니다. 가는 길과 오는 길이 다른 라우터를 거치기도 하고, 한쪽 방향만 혼잡으로 밀리기도 합니다. 그 차이의 절반이 고스란히 오차로 남습니다.
그래서 NTP 는 비대칭 경로에 특히 약합니다. 왕복이 아무리 짧아도 양쪽이 비대칭이면 오차가 줄지 않습니다. 이 가정을 피하려면 중간 장비가 직접 시각을 찍어 주는 방식으로 가야 합니다.
계층 — 기준 시계에서 떨어진 거리
시각의 원본은 원자시계, 또는 GPS(Global Positioning System, 위성 위치 확인 시스템) 위성이 실어 보내는 시각을 받아 쓰는 수신기 같은 물리 장비가 가지고 있습니다. 모든 컴퓨터가 그 장비에 직접 연결될 수는 없으니, NTP 는 서버를 사슬처럼 이어 시각을 아래로 흘려보냅니다. 이 사슬의 단계를 계층이라고 부릅니다. 프로토콜을 쌓아 올린 네트워크 계층과는 다른 뜻입니다.
flowchart TD
S0["계층 0 · 원자시계 · GPS 수신기"]
S1["계층 1 · 기준 장비에 직접 붙은 서버"]
S2["계층 2 · 계층 1 에 맞춘 서버"]
S3["계층 3 · 계층 2 에 맞춘 컴퓨터"]
S0 --> S1
S1 --> S2
S2 --> S3
기준 장비 자신이 계층 0 이고, 거기에 직접 붙은 서버가 계층 1 입니다. 계층 1 에 맞춘 서버는 계층 2 가 되고, 한 다리 건널 때마다 숫자가 하나씩 올라갑니다. 숫자가 클수록 원본에서 멀고, 다리마다 조금씩 붙은 오차를 물려받습니다.
숫자는 15 까지만 쓸 수 있습니다. 16 은 "아직 시각을 못 맞췄다"는 표시로 따로 떼어 두었습니다. 사슬이 끝없이 길어지는 것과, 어긋난 서버가 남에게 시각을 나눠 주는 것을 함께 막는 장치입니다.
여러 서버의 답을 견주어 고르는 절차
서버 하나만 믿으면 그 서버가 틀렸을 때 확인할 방법이 없습니다. 그래서 클라이언트는 보통 서버 여럿에게 같은 질문을 던집니다.
답이 모이면 서로 견줍니다. 다수가 가리키는 구간에서 혼자 크게 벗어난 답은 버립니다. 남은 답들 가운데 흔들림이 적은 쪽에 무게를 더 실어 하나의 값으로 합칩니다.
여기서 흔들림은 같은 서버에 되풀이해 물었을 때 오프셋이 들쭉날쭉한 정도를 말하고, 지터라고 부릅니다. 지터가 큰 서버는 경로가 붐비거나 멀다는 뜻이라 덜 믿습니다.
오차를 시계에 반영하는 두 방법
오차를 알아냈다고 해서 시계를 곧바로 그 값만큼 옮기면 안 됩니다. 시각이 갑자기 뛰면 그사이에 찍힌 로그 타임스탬프의 순서가 뒤집히고, 시각 차이로 시간을 재던 코드가 음수를 보게 됩니다.
그래서 NTP 구현은 두 가지 방법을 나눠 씁니다. 시계 속도를 조금 바꿔 서서히 따라잡는 것을 슬루, 시각을 한 번에 옮기는 것을 스텝이라고 합니다.
flowchart TD
A["오프셋을 구했다"] --> B{"오차가 스텝 문턱보다 작나"}
B -->|작다| C["슬루 · 시계 속도를 바꿔 메운다"]
B -->|크다| D{"오차가 1000초를 넘나"}
D -->|아니다| E["스텝 · 시각을 한 번에 옮긴다"]
D -->|그렇다| F["맞추기를 멈추고 사람을 부른다"]
스텝 문턱의 기본값은 0.125초입니다. 그보다 작은 오차는 슬루로 메웁니다. 시계가 빨리 가고 있으면 잠시 느리게, 늦게 가고 있으면 잠시 빠르게 돌려서 시각이 뛰지 않게 합니다.
오차가 1000초를 넘으면 이야기가 달라집니다. 이쯤 되면 시계가 어긋난 것이 아니라 무언가 크게 잘못됐다고 보고, 맞추기를 아예 멈춘 채 사람의 판단을 기다립니다.
묻는 간격을 늘리고 줄이는 기준
클라이언트가 얼마나 자주 묻는지는 고정이 아닙니다. 방금 켠 컴퓨터는 자기 시계가 얼마나 빨리 어긋나는지 모르니 자주 물어봐야 합니다. 몇 번 물어보고 나면 어긋나는 속도를 알게 되어 예측할 수 있습니다.
그래서 시계가 안정되면 간격을 늘리고, 다시 흔들리기 시작하면 줄입니다. 간격은 2의 거듭제곱 초로 잡고, 16초에서 36시간 사이에서 움직입니다. 실무에서 흔히 보는 값은 64초에서 1024초 사이입니다.
간격을 늘리는 것은 서버 부담 때문입니다. 공개 시간 서버 하나에 수만 대가 붙는 일이 흔한데, 모두가 짧은 간격으로 물으면 서버가 질문만 받다 끝납니다.
시각을 보내 준 쪽을 믿게 하는 장치
기본 형태의 NTP 패킷은 아무 보호 없이 오갑니다. 중간에 끼어든 쪽이 시각을 바꿔 보내면 클라이언트는 그대로 받아들입니다. 시각을 조금씩 앞으로 밀면 만료된 인증서를 되살릴 수도 있어서 중간자 공격의 표적이 됩니다.
오래된 대비책은 서버와 클라이언트가 같은 열쇠를 미리 나눠 갖고 패킷에 메시지 인증 코드를 붙이는 것입니다. 열쇠를 사람이 손으로 나눠야 해서, 아는 사이끼리 쓰는 방식입니다.
NTS(Network Time Security, 네트워크 시각 보안)는 이 열쇠 나누기를 TLS 로 먼저 처리한 뒤 NTP 패킷을 그 열쇠로 보호합니다. 모르는 공개 서버와도 쓸 수 있습니다.
반대 방향의 문제도 있습니다. 응답이 요청보다 훨씬 큰 명령이 서버에 열려 있으면, 출발지 주소를 속인 요청을 잔뜩 보내 엉뚱한 곳으로 응답을 쏟아붓게 만들 수 있습니다. NTP 증폭 공격이 이 수법을 썼고, 그래서 요즘 서버는 그런 명령을 기본으로 닫아 둡니다.
SNTP 와 PTP
SNTP(Simple Network Time Protocol, 간이 네트워크 시간 프로토콜)는 같은 패킷 형식을 쓰되 서버 고르기와 시계를 서서히 맞추는 절차를 생략한 갈래입니다. 서버 하나에 물어 받은 값을 바로 쓰는 쪽에 가깝고, 임베디드 기기처럼 계산 여력이 적은 쪽이 씁니다.
PTP(Precision Time Protocol, 정밀 시각 프로토콜)는 반대 방향입니다. 경로 위의 스위치가 패킷이 지나간 시각을 직접 찍어 주어 대칭 경로 가정에 기대지 않습니다. 대신 그렇게 해 주는 장비가 필요해서 한 건물 안의 망에서 씁니다.
관련 항목
NTP 패킷 머리에 담기는 필드
Leap Indicator · stratum · 폴링 간격 · 루트 지연 · 루트 분산 · 참조 식별자 · NTP 타임스탬프 · 정밀도
NTP 를 실어 나르는 전송·네트워크 프로토콜
UDP · IP · 포트 번호 · 데이터그램 · 브로드캐스트 · 멀티캐스트 · TCP
NTP 가 오차를 재고 시계를 고치는 동작
오프셋 · 왕복 시간 · 지터 · 슬루 · 스텝 · 시계 보정 · 주파수 오차
NTP 가 다루는 시각과 시계 개념
UTC · 에포크 · 벽시계 · 단조 시계 · 시스템 클록 · 타임스탬프 · 클록 드리프트 · 클록 스큐 · 윤초 · 수정 진동자 · 시간대
NTP 를 구현한 프로그램
ntpd · chrony · systemd-timesyncd · OpenNTPD · w32time
시각을 맞추는 데 NTP 대신 쓰는 다른 수단
SNTP · PTP · Roughtime · 원자시계 · 전파 시계 · GPS 수신기
NTP 시각을 지키거나 노리는 보안 수단과 공격
NTS · 메시지 인증 코드 · Autokey · NTP 증폭 공격 · UDP 반사 공격 · 중간자 공격 · 방화벽
NTP 를 규정하는 표준 문서
RFC 5905 · RFC 4330 · RFC 8915 · IETF · IANA
정확한 시각에 기대는 시스템 기능
인증서 · 스노우플레이크 · 분산 추적 · Kerberos · 캐시 · JWT · 로그 타임스탬프
NTP 서버를 공개로 내주는 서비스와 운영 방식
NTP Pool · 윤초 스미어 · 시간 서버 · 계층 1 서버 · 시간 동기화
NTP 가 속하는 상위 분류
다른 이름: Network Time Protocol · 네트워크 시간 프로토콜