레이블이 network인 게시물을 표시합니다. 모든 게시물 표시
레이블이 network인 게시물을 표시합니다. 모든 게시물 표시

2018-06-03

2018년 22번재 주 - P2P Network

이 포스팅은 그냥 지난 한 주간 읽었던 것들을 정리하는 포스트입니다. 그냥 예전에 봤던 글 중 나중에 필요한데 뭐였는지 기억 안 나는 글들이 있어서 쓰기 시작했습니다.
보통 하는 일과 관련된 글들이 올라오겠지만 딱히 정해둔 주제는 없고, 그때그때 관심 있었던 것을 읽었기 때문에 지난주에 쓰인 글일 수도 있고 몇 년 전에 쓰인 글일 수도 있습니다.

지난주에 이어 이번 주는 P2P를 주제로 발표했다. 이번에도 발표자료를 만들면서 참고한 자료를 공유하도록 하겠다.


Chord: a scalable peer-to-peer lookup protocol for internet applications

Tapestry: A Resilient Global-scale Overlay for Service Deployment

Pastry: Scalable, decentralized object location and routing for large-scale peer-to-peer systems

A Scalable Content-Addressable Network

P2P 네트워크 중에서도 네트워크 토폴로지가 특정한 규칙에 의해서 구성되는 네트워크를 structured 네트워크라고 말한다. Structured network는 unstructured network에 비해 특정 노드에 부하가 걸리거나 의존하는 것을 방지하면서 잘 분산된 네트워크를 구성할 수 있다. 위의 4 논문은 각기 Chord, Tapestry, Pastry, Content Addressable Network(a.k.a. CAN)라는 대표적인 structured 네트워크 오버레이를 제시하는 논문이다.

Kademlia: A Peer-to-Peer Information System Based on the XOR Metric

Kademlia는 위의 네 논문을 개선하는 네트워크 오버레이를 제시한다. BitTorrent, eDonkey, 이더리움 등 많은 P2P 시스템이 kademlia 방식을 이용한다. 실질적으로 P2P 네트워크에서 가장 많이 사용되는 방식이다.
Kademlia의 가장 큰 특징은 xor distance를 사용하는 것이다. Xor distance는 노드 아이디의 공통된 prefix가 길수록 가까운 노드로 판단하는 방식이다. 이 방식은 triangle inequality를 만족하며, 각 노드가 작은 라우팅 테이블만으로 전체 네트워크를 효율적으로 연결할 수 있다.
게다가 거리가 대칭적이기 때문에 TCP로 연결해야 하는 시스템에서도 사용하기 좋다.

Understanding churn in peer-to-peer networks

Churn이라는 것은 새 노드가 네트워크에 참여하거나 네트워크에서 노드가 빠져나가는 변동성을 말한다. 위 논문은 다양한 p2p 네트워크에서 네트워크 상태를 조사해서 각 네트워크가 어떤 churn을 가지는지 분석했다.
사실 churn 자체는 서비스의 종류마다 다 다를 것이다. 이 논문에서 주목할 점은 그 churn을 어떻게 분석했는지 그 방법론에 대한 것이라고 생각한다.

Low-Resource Eclipse Attacks on Ethereum’s Peer-to-Peer Network

이더리움에 가할 수 있는 eclipse attack에 관해 정리한 논문이다. 자세한 내용은 전에 쓴 적이 있으니 참고 바란다.

Eclipse Attacks on Bitcoin’s Peer-to-Peer Network

이 논문은 비트코인에서 할 수 있는 eclipse attack에 관해 정리한 논문이다. 이더리움은 2대의 머신으로 공격할 수 있었던 반면 비트코인에서는 약 400개의 머신이 필요하다. 아직 비트코인에서 이보다 적은 수로 공격할 수 있는 방법은 나오지 않았다.

BLAKE2: simpler, smaller, fast as MD5

Cryptographic hash function 중 하나인 BLAKE2를 소개하는 논문이다. SHA-3 finalist였던 blake의 개선판으로 blake보다 더 적은 메모리로 빠르게 돌아간다.

The BLAKE2 Cryptographic Hash and Message Authentication Code (MAC)

Codechain에서는 MD5SHA-1을 이용하는 일반적인 HMAC 대신에 BLAKE2를 HMAC으로 사용한다. 이와 관련하여 이론적 분석이 있을 것이라고 생각하고 보기 시작했는데, HMAC에 관한 내용은 별로 없고, BLAKE2의 알고리즘과 구현에 관한 내용이 대부분이었다.

O(1) Block Propagation

이건 내가 발표한 내용이 아니라 양준모 님의 발표에 있던 비트코인의 Graphene의 아이디어를 줬던 제안이다. 트랜잭션은 언제나 전파하는 비트코인의 특성상 대부분 트랜잭션이 이미 전파돼있을 거라고 가정하고, IBLT라는 자료구조를 이용해서 없는 트랜잭션을 빠르게 확인할 수 있다는 것이 메인 아이디어다.

Invertible Bloom Lookup Tables

이 논문이 위에서 말한 IBLT를 제시하는 논문이다.

2018-05-27

2018년 21번째 주 - Consensus algorithm

이 포스팅은 그냥 지난 한 주간 읽었던 것들을 정리하는 포스트입니다. 그냥 예전에 봤던 글 중 나중에 필요한데 뭐였는지 기억 안 나는 글들이 있어서 쓰기 시작했습니다.
 보통 하는 일과 관련된 글들이 올라오겠지만 딱히 정해둔 주제는 없고, 그때그때 관심 있었던 것을 읽었기 때문에 지난주에 쓰인 글일 수도 있고 몇 년 전에 쓰인 글일 수도 있습니다.

회사에서 이런 거 하느라 바빠서 한동안 다른 글은 읽을 시간이 없네요. 발표는 몇 주 동안 계속할 거라서 당분간은 발표자료 만들면서 참고했던 자료들을 공유할 것 같습니다.


Impossibility of distributed consensus with one faulty process

분산 환경에서 합의 알고리즘을 말할 때 빼놓을 수 없는 논문이다. 합의 알고리즘에 필요한 기본적인 속성은 크게 safety와 liveness가 있다. Safety는 잘못된 합의가 이루어지지 않는다는 것이고, liveness는 언젠가는 합의가 반드시 이루어진다는 것이다. 위 논문은 비동기 네트워크에서 하나의 fail-stop failure 노드만 있어도 이 합의가 이루어질 수 없다는 것을 보였다. 이를 FLP impossibility라고 부른다.

Consensus in the presence of partial synchrony

첫 번째 논문이 비동기 네트워크에서 safety와 liveness를 보장하는 합의 알고리즘이 불가능하다는 FLP impossibility를 보였다. 그래서 이 논문은 partial synchronous model을 만들어 특정한 가정 아래서 safety와 liveness를 같이 보장하는 합의 알고리즘을 만들 수 있게 해줬다. Partial synchronous model은 메시지가 언젠가는 도착하는 것을 보장하지만 그 도착하는 시간이 언제인지 알 수 없는 모델이다.

The Byzantine generals problem

Byzantine failure라는 용어의 어원이 된 논문이다. 이 논문 이전 논문은 기껏해야 메시지가 드랍되는 omission failure 정도밖에 가정하지 않았다. 하지만 이 논문 이후로 악의적인 참여자에 의해 시스템이 공격당하는 경우도 고려하게 됐다.

Reaching Agreement in the Presence of Faults

어떤 시스템이 f개의 byzantine failure node가 있을 때 3f + 1개의 노드로 구성된 네트워크에서 byzantine fault tolerant(a.k.a. BFT)한 합의가 가능하다는 것을 증명한 논문이다. 3f + 1은 BFT 시스템을 구성하기 위한 최소한의 노드 수이기도 하다.

Practical Byzantine fault tolerance

위 논문은 BFT 시스템을 Network File System에서 적용했다. 내가 아는 한은 BFT 알고리즘을 인터넷 규모에서 실제로 사용하는 시스템에 적용한 최초의 논문이고, 여기서 사용된 알고리즘들이 현재 블록체인에 사용되는 BFT 계열 합의 알고리즘들의 근간이 됐다.

Blockchain Consensus Protocols in the Wild

블록체인에서 유명한 합의 알고리즘들을 partial synchronous model에서 safety와 liveness, 그리고 얼마나 많은 byzantine failure node를 버틸 수 있는지 분석했다.

Byzantine Consensus Algorithm

텐더민트에서 사용하는 합의 알고리즘에 대해서 설명한 위키 페이지다. 텐더민트는 BFT에 stake 개념을 섞어 블록체인에서 사용할 수 있도록 만들었다. 특히 여기서 말하는 Proof of Lock Change(a.k.a. PoLC) 개념은 잘 이해해두는 것이 좋다. 보통 BFT 계열 합의 알고리즘을 최적화하려는 사람들이 PoLC에서 문제가 생겨 safety와 liveness에 문제가 생긴다.

Casper the Friendly Finality Gadget

현재 이더리움에서 진행 중인 프로젝트 중 하나로 PoW로 생성된 블록체인에 safety, 블록체인 용어로는 finality를 보장하기 위한 프로토콜이다. 기본적인 방법은 텐더민트에서 사용된 개념과 유사하다. 하지만 충분한 투표를 모으지 못하면 새 블록이 생성되지 않는 텐더민트와는 다르게 캐스퍼는 finality를 보장하지 못 할뿐 블록은 계속 생성된다.

Hot-Stuff the Linear, Optimal-Resilience, One-Message BFT Devil

캐스퍼에 영향을 받아 쓰인 논문으로 캐스퍼를 더 일반화시켰다. 캐스퍼는 블록 생성과 투표와 완전 별개로 진행된다. 하지만 Hot-stuff에서는 블록의 헤더에 투표를 모은다. 다만 이 투표는 반드시 포함해야 하는 것이 아니고 투표가 없어도 블록은 생성된다.

2018-05-24

Safety & Liveness - FLP impossibility으로 보는 블록체인

블록체인이 유행하면서 블록체인의 수만큼 다양한 합의 알고리즘이 나오고 있다. 이는 어째서일까? 애초에 왜 다양한 블록체인이 나오고 있는 것일까? 이는 근본적으로 합의 알고리즘이 무엇을 할 수 있고 무엇을 할 수 없는가에서 기인한다. 좋은 합의 알고리즘은 무엇을 보장해야 할까?

FLP impossibility

우선 아무 문제 없는 두 노드가 서로 다른 값으로 합의하면 안 된다. 다른 값을 합의했다는 것은 블록체인 관점에서 보면 같은 높이에 서로 다른 블록이 생성됐다는 것이다. 이런 특성을 분산 시스템에서는 consensus의 safety라고 말한다.

또한, 합의가 언젠가는 이루어져야 한다. 분산 시스템에서 합의는 노드 간의 메시지를 주고받으며 각 노드의 상태를 변경시키며 이루어진다. 이때 문제없는 노드들은 무한 루프에 빠지지 않고 반드시 상태 변경이 종료돼야 한다. 모든 노드가 문제없이 합의를 할 수 있으면 이 시스템은 liveness가 보장된다고 말한다.

쉽게 풀어 말하면 safety는 "문제없는 노드 사이에서는 잘못된 합의가 이루어지지 않는다"라는 것이고, liveness는 "문제없는 노드들은 반드시 합의를 한다"라는 것이다. 문제는 byzantine failure가 아닌 fail-stop failure가 하나만 있어도 safety와 liveness를 둘 다 만족하는 합의 알고리즘이 존재할 수 없다. 이를 FLP impossibility 혹은 FLP theorem이라고 한다. 따라서 합의 알고리즘을 선택한다는 것은 사실상 safety와 liveness 중 무엇을 선택하고 무엇을 버릴까 하는 문제이다.

Liveness over Safety

비트코인이 사용하는 합의 알고리즘은 사토시 나카모토가 처음 제안하였기 때문에 nakamoto consensus라고도 불린다. Nakamoto consensus는 언제나 더 어려운 문제를 푼 체인이 있으면 그 체인을 유효한 체인으로 판단한다. 즉, 지금 있는 체인보다 더 긴 체인을 만들 해시 파워만 있으면 언제든지 현재 합의된 블록을 다른 블록으로 대체할 수 있다. 이런 방식을 블록체인에서는 finality가 보장되지 않는다고 말하고 FLP impossibility에서는 liveness를 위해서 safety를 포기했다고 말한다.

Liveness를 중시하는 nakamoto consensus에서 출발한 합의 알고리즘들은 한정적인 상황에서 safety를 보장할 방법을 추가하는 방식으로 발전해갔다. 이더리움에서 구현되고 있는 Casper the Friendly Finality Gadget이 대표적이다. Casper는 기존의 PoW로 liveness를 보장하며 블록을 생성하지만 50 블록마다 투표하여 safety가 보장되는 지점을 만든다.

Safety over Liveness

이와 반대로 safety를 중시하는 합의 알고리즘도 있다. 전통적으로 분산 시스템에서 연구되던 PBFT에 기반한 BFT 계열 합의 알고리즘들이 이쪽에 속한다. Cosmos에서 사용하는 Tendermint가 대표적인 safety를 보장하는 BFT 알고리즘이다.

Tendermint는 하나의 라운드가 propose, prevote, precommit 3개의 단계로 나누어진다. 이 중 prevote와 precommit 스텝에서 각각 2/3+1개 이상 모아야 합의가 이루어진다. 합의에 2/3+1개 이상의 동의가 필요하기 때문에 어떤 상황에서도 서로 다른 두 블록이 동시에 생성되는 일은 없다. 하지만 전송한 메시지가 시간 안에 도달하는 것을 보장하지 못 하는 비동기 네트워크에서는 합의가 이루어지지 않아 블록이 생성되지 않을 수 있다. 따라서 liveness는 보장되지 않는다.

FLP impossibility가 증명했듯이 safety가 보장되는 경우 어떤 방법을 사용해도 비동기 네트워크에서 liveness를 보장할 수 없다. 그래서 BFT 계열에서는 다른 네트워크 모델에서 liveness가 보장됨을 증명한다.

비동기 네트워크 모델에서는 메시지가 전송되는 것이 보장되는 시간이 없다. 이는 메시지가 무한한 시간 후에 도착하는 것도 가정해야 하고 다시 말해서 전송한 메시지가 도착하지 않는 것을 가정해야 한다. 그렇다고 해서 정해진 시간 안에 메시지 전달이 보장되는 동기 네트워크 모델을 사용할 수는 없다. 이는 인터넷 규모의 네트워크에서는 비현실적인 가정이고, 이런 가정에서 liveness를 증명하는 것은 아무 의미 없기 때문이다. 그래서 tendermint는 정해진 시간 안에 메시지가 도달하는 것이 보장되지만 그 정해진 시간을 알 수 없다는 partial synchronous network model을 사용한다.

Partial synchronous network는 정해진 시간 내에 메시지가 도착하는 것이 보장되는 모델이다. 다만 이 정해진 시간이 무엇인지 노드는 알지 못한다. 이는 꽤나 현실적인 모델이다. 현실의 네트워크도 omission failure가 발생하지 않는 한 언젠가는 메시지가 도착하기 때문이다.

BFT 계열의 합의 알고리즘은 블록 생성을 위해 2번의 투표를 모아야 한다. 비록 partial synchronous network model에서는 언젠가 합의될 것이 보장되지만, 최악의 경우 몇번의 라운드 동안 새 블록이 생성되지 않는 경우도 생긴다. 이는 TPS 저하를 초래한다.

BFT 계열 알고리즘은 이런 문제를 해결하기 위한 방향으로 발전해왔다. 2018년 3월에 발표된 Hot-stuff이라는 프로토콜이 대표적이다. Hot-stuff에서 블록은 validator들의 투표를 포함한다. 이 투표를 commit-certificate(a.k.a. CC)이라고 한다. Hot-stuff은 기존의 BFT 계열의 알고리즘과 다르게 CC가 없는 블록도 생성될 수 있다. 그저 이 블록들은 finality가 보장되지 않을 뿐이다. 이 CC가 없는 블록들은 뒤에 CC가 있는 블록의 finality가 보장되면 그때 finality가 보장된다. 시간 당 블록 생성량을 올리기 위해서 safety를 어느 정도 포기하는 것이다.

마치며

이상으로 liveness의 극단에 있는 nakamoto consensus에서부터 safety에 극단에 있는 tendermint까지 블록체인에서 사용되는 다양한 합의 알고리즘을 알아봤다. 중요한 것은 safety와 liveness는 trade-off이지 둘 중 누군가가 더 우월한 특성은 아니라는 것이다. 자신이 구현하려는 서비스의 종류에 따라 safety와 liveness가 어느 정도 필요한지를 판단하여 알맞은 합의 알고리즘을 사용하는 블록체인을 선택해야 한다.

2018-05-02

2 phase commit

Two Phase Commit(a.k.a. 2PC)은 distributed system에서 atomic commit을 보장하는 프로토콜이다. 2PC는 나름 많은 에러 상황을 버티고, 괜찮은 성능을 보이기 때문에 많이 사용된다.

2PC에서 노드는 한 개의 coordinator와 여러 개의 cohort로 나누어진다. Coordinator는 commit 할 transaction을 만드는 노드고, cohort들은 coordinator가 보낸 transaction을 commit 하거나 revert 한다. 2PC는 이때 fail 하지 않은 모든 cohort가 같은 상태를 유지하도록 하는 것이다. 즉, fail 하지 않은 모든 노드는 다 같이 commit 하거나 revert 한다.

이때 coordinator를 어떻게 선정할지는 2PC의 영역이 아니다. 고정된 coordinator를 계속 사용할 수도 있고, 차례대로 돌아가면서 coordinator가 될 수도 있고, 별도의 leader election을 사용하여 coordinator를 선정할 수도 있다. 2PC는 어떻게든 coordinator가 선정된 뒤의 일이다.

2PC는 이름 그대로 2가지 phase로 나누어져 있다. 첫 번째 phase는 voting phase라고 부르고, 두 번째 phase는 commit phase라고 불린다. 각 phase의 시작은 coordinator가 보내는 메시지로 시작한다.

Voting phase에서 coordinator는 commit 하고 싶은 transaction을 commit 할지 말지 투표하는 요청을 cohort에게 보낸다. VOTE 메시지를 받은 cohort들은 이 transaction을 바로 commit 하지 않는다. 해당 transaction을 커밋할 수 있으면 YES 메시지를, 없으면 NO 메시지를 coordinator에게 보낸다.

Voting phase에서 coordinator가 quorum 이상의 YES 메시지나 NO 메시지를 모으면 commit phase를 시작한다. 이때 일부 cohort에 문제가 생겨서 더 진행되지 않는 것을 방지하기 위해서 일정 시간 응답을 주지 않는 cohort는 NO 메시지를 보냈다고 가정한다.

이때 quorum을 얼마로 잡는가에 따라서 시스템의 consistency modelresilience가 결정된다. 예를 들어 N개의 coordinator가 있는 시스템에서 N개의 YES 메시지를 모아야 한다면, 하나의 failure도 용납하지 않는 resilient 하지 않지만, strong consistency를 보장하는 시스템이 된다. Quorum이 얼마가 되어야 하는지는 정해지지 않았다. 하지만 non-partition 상황에서 consistency를 보장하기 위해서는 최소 N/2 이상의 YES 메시지를 모아야 한다.

Coordinator가 quorum 이상의 YES 메시지를 받았으면 cohort들에게 COMMIT 메시지를 보내고, quorum 이상의 NO 메시지를 받았으면 cohort 들에게 ROLLBACK 메시지를 보낸다. cohort는 COMMIT 메시지를 받았으면 voting phase에서 받았던 transaction을 커밋하고, ROLLBACK 메시지를 받았으면 그 transaction을 버린다. COMMIT이든 ROLLBACK이든 메시지를 처리하고 나면 cohort는 coordinator에게 처리했다는 메시지를 보낸다. Coordinator가 cohort들에게 처리가 끝났다는 메시지를 받으면 commit phase가 끝난다.

위의 과정을 거쳐 2PC가 진행된다. 앞서 말했듯이 2PC는 괜찮은 resilience를 보이면서, 성능도 나쁘지 않기 때문에 많이 사용된다. 특히 atomic commit을 지원하는 프로토콜 중에서는 가장 적은 메시지 수로 commit 될 수 있는 프로토콜이다.

하지만 2PC에는 심각한 문제가 하나 있다. 2PC는 VOTE 메시지를 보낸 coordinator가 죽어서 COMMIT이나 ROLLBACK 메시지를 보내지 못하면 YES 메시지를 보낸 cohort가 안전하게 상태를 회복할 방법이 없다. 이는 YES 메시지를 보낸 cohort의 상태가 undefined이기 때문이다. 이에 관해서는 다음에 기회가 되면 three phase commit을 설명하면서 자세히 얘기하도록 하겠다.

2018-04-19

Byzantine Fault Tolerance 시스템에서 N = 3f + 1인 이유

분산환경 시스템에서는 다른 노드가 보낸 메시지가 정상적이라고 보장할 수 없다. 이때 잘못된 노드가 모두에게 틀린 메시지를 보낸다면 문제가 쉽게 풀린다. 틀린 메시지를 보내는 노드를 차단하면 된다.

하지만 일부 노드에게는 잘못된 메시지를 보내고, 일부 노드에게는 제대로 된 메시지를 보내는 경우는 문제 상황을 찾기 힘들다. 분산 시스템에서 각 노드는 다른 노드의 상태를 모르기 때문이다. 이런 식으로 일부 노드에게만 틀린 메시지를 보내는 노드를 가정하는 모델을 byzantine failure model이라고 부른다.

Byzantine failure model은 네트워크에서 가장 풀기 어려운 모델임과 동시에 실제 네트워크에서 반드시 해결해야 하는 문제다. 특히 다른 노드를 신뢰할 수 없는 p2p에서는 반드시 Byzantine failure model을 가정하고 예외 상황을 처리해야 한다.

그렇다고 인증된 노드만으로 구성된 분산 시스템이라고 byzantine failure model을 가정하지 않아도 된다는 것은 아니다. 노드 자체는 신뢰할 수 있는 사람이 관리하더라도 해킹당했을 수도 있고, 버그로 잘못된 메시지를 보낼 수도 있고, 하드웨어에 문제가 발생할 수도 있다.

Byzantine failure model에서도 정상적으로 돌아가는 시스템을 byzantine fault tolerance (a.k.a. BFT)라고 말한다. 당연히 BFT라고 해도 무한히 많은 faulty 노드에 대해서 동작하지는 않는다. 그래서 보통 어떤 시스템이 BFT라고 말할 때 전체 노드 중 몇 개의 노드에 문제가 있을 때까지 동작하는지를 같이 말한다. 예를 들어 N = 5f라고 말하면, 전체 노드 중 1/5가 byzantine failure일 때 정상 동작하는 시스템이고 N = 3f + 1이라고 말하면, 전체 노드 중 1/3이 byzantine failure일 때까지는 문제없이 돌아가는 시스템을 말한다.

같은 BFT라고 한다면, 감당할 수 있는 faulty 노드의 비율이 더 큰 시스템이 더 안전한 시스템인 것은 당연하다. 하지만 N = 3f + 1. 즉, 1/3 이상의 byzantine failure를 가정하는 시스템은 없다. 이는 1/3이 이론상으로 가질 수 있는 최대치이기 때문이다.

어떤 노드가 byzantine failure일 때 발생할 수 있는 문제 상황은 크게 두 가지다. 첫 번째 문제 상황은 메시지를 보내지 않는 것이다. 즉, 분산 시스템이 N개의 노드 중 byzantine failure f개가 정상적으로 동작하기 위해서는 N - f개의 메시지만으로 합의가 이루어져야 한다. 이것을 다른 말로 quorum을 위해서 N - f개의 노드가 필요하다고 말한다.

두 번째 문제 상황은 byzantine failure인 노드가 악의적으로 다른 메시지를 보내는 것이다. 극단적으로는 quorum을 이룬 N - f개의 노드 중에서 f개가 byzantine failure가 보낸 메시지인 경우를 생각해볼 수 있다. 이 경우도 정상 동작해야 하므로 (N - f) - f 개의 메시지가 byzantine failure인 노드가 보낸 f개의 메시지보다 많아야 한다.

위의 두 경우를 대비하기 위해서 (N - f) - f > f. 즉, N > 3f이므로 f개의 byzantine failure인 노드가 있을 때 최소 3f개보다 많은 노드가 있어야 byzantine fault tolerance한 시스템이고, 이때 가장 작은 N은 3f + 1이다. 따라서 3f + 1개의 노드로 구성된 시스템에서 견딜 수 있는 faulty 노드의 최대 수는 f다.

2018-04-14

2018년 15번째 주

이 포스팅은 그냥 지난 한 주간 읽었던 것들을 정리하는 포스트입니다. 그냥 예전에 봤던 글 중 나중에 필요한데 뭐였는지 기억 안 나는 글들이 있어서 쓰기 시작했습니다.

보통 하는 일과 관련된 글들이 올라오겠지만 딱히 정해둔 주제는 없고, 그때그때 관심 있었던 것을 읽었기 때문에 지난주에 쓰인 글일 수도 있고 몇 년 전에 쓰인 글일 수도 있습니다.


실제로 git을 사용하면서 단순히 커맨드를 외워서 사용하는 사람들을 많이 봤다. 보통 그 이유로 크게 두 가지를 든다. 첫 번째로 git의 mental model이 복잡하다는 것이다. git에서 변경된 내용은 크게 다음 상태 중 하나가 된다.

  1. 리모트에 존재하는 상태
  2. 로컬 브랜치에 있는 상태
  3. 브랜치에 머지되지 않았지만 add 돼 있는 상태
  4. 변경은 있지만 add 되지는 않은 상태
  5. stash에 들어있는 상태
  6. 예전에 커밋했었지만 지금은 브랜치로 따라갈 수 없는 상태

이 중에서 내가 수정했던 내용이 어떤 상태인지 모르는 것이 헷갈리게 하는 첫 번째 이유다.
하지만 반대로 왜 이렇게 많은 상태를 가지게 됐을지 생각해보면 git을 사용하는 데 도움이 된다. 이것들은 전부 그냥 추가된 것이 아니다. 애초에 git을 처음 만든 사람은 Linus Torvalds다. 그의 성격상 쓸모없는 것은 추가되지 않았다. 전부 제각각의 목적을 가지고 있다. 이 목적을 이해하는 것이 중요한데 아쉽게도 글로 잘 설명할 자신이 없다. 사실 이걸 이해하는 가장 빠르고 확실한 방법은 svn을 써보는 것이다. 쓰다 보면 불편한 부분들이 자주 생기는데, git에서는 위에서 말한 것들을 이용해 이를 쉽고 빠르게 해결할 수 있다.

사람들이 git을 어려워하는 두 번째 이유는 명령어가 복잡하다는 것이다. 이건 어쩔 수 없다. 사실 git의 명령어는 규칙성 없이 만들어졌다. 그래서 외우는 수밖에 없다. 하지만 어떤 상황에서 어떤 명령어를 써야 한다는 식으로 외우면 끝이 없다. 그보다는 각 명령어가 어떤 상태와 연관이 있는지를 보는 것이 좋다. git 명령어는 대부분 변화된 내용을 위의 상태 중 하나로 만들거나, 특정 상태로 로컬에 있는 파일을 변경하는 것이다. 따라서 현재 파일이 어떤 상태에 있고 명령어 이후 어떤 상태로 된다는 것을 인지하고 있으면 조금은 더 쉽게 적응할 수 있다.

News is bad for you – and giving up reading it will make you happier

뉴스가 사람의 정신건강에 해롭다는 기사다. 사람들이 흔히 정보를 얻으려고 뉴스를 읽지만, 실제로는 별 도움도 안 되고 시간만 낭비하게 하고 사람의 창의성을 없애고 수동적으로 만든다는 것이다.

Introduction to zkSNARKs

이더리움의 최근 움직임 중에서 zero-knowledge proof인 zkSNARKs을 도입하고자 하는 움직임이 있다. 위 자료는 이더리움 소속으로, 솔리디티 창시자 중 하나이며 zkSNARKs를 구횬하고 있는 Christian Reitwiessner이 zkSNARKs에 대해 간단히 소개한 발표자료다. 자료 이름은 zkSNARKs이지만 대부분의 내용을 zero-knwledge proof가 무엇인지에 설명하는 데 쓰고 있으므로 zero-knowledge proof가 무엇인지 알고 싶은 사람들이 보기에도 좋은 자료다.

RSS is undead

RSS는 이미 수명이 끝났고, 다시 살아날 리 없다는 글이다. 지난번에 소개했던 It's Time for an RSS Revival이나 Why RSS Still Beats Facebook and Twitter for Tracking News의 반대편에 있는 입장이다.

RSS는 소규모 사이트의 글을 구독하는 데는 적절하지만, 대형 사이트의 특정 카테고리의 글을 구독하는 데는 적당한 UX를 제공하지 못하고, 퍼블리셔 입장에서 사용자의 패턴을 파악할 수 없을뿐더러 광고수익을 얻을 수 없기 때문에 사용자와 퍼블리셔 양쪽에게 손해라는 것이다.

What is a “box model”? (UI Element layout)

Web을 비롯하여 현대의 많은 UI framework들이 box model을 사용하기 때문에 프론트엔드 개발을 하려면 한 번 읽어두는 것이 좋다.

The Science of The Job Search, Part III: 61% of “Entry-Level” Jobs Require 3+ Years of Experience

경력 같은 신입을 원하는 건 우리나라뿐 아니라 전세계 공통인듯하다.

A fork on Github is no fork

Git은 decentralized 된 version control system이기 때문에 fork를 하면 어디서 fork 해왔는지와 완전히 독립적인 repository로 존재할 수 있다. 하지만 Github의 fork는 그 모든 설정과 권한이 원본 repository에 종속적이기 때문에 진짜 fork가 아니라는 글이다.
원칙적으로 맞기는 하지만 Github Enterprise나 private repository 유료 모델을 생각해보면 어쩔 수 없는 선택이었다고 생각한다.

Uninhabited types are ZSTs despite partial initialization being allowed in safe code.

Rust 1.24.0 이후 생긴 memory safety가 깨지는 버그다.

Rust는 무한루프나 내부에서 exit 하기 때문에 return 되지 않는 함수를 표현하기 위해서 uninhabited type을 도입했다. 이 uninhabited type으로 product type을 만들면 이 타입도 uninhabited type이 돼서 크기가 0인 type으로 계산되는데, 이 product type의 다른 field를 접근하는 게 가능해서 주소 계산이 잘못되는 버그가 발생했다. Rust에 zst(zero size type)가 도입됐을 때부터 메모리 주소 계산을 잘못 하는문제가 발생하지 않을까 생각했는데 정말로 터져버렸다.

현재 두 가지 해결 방안 중에서 어느 쪽으로 해결할지 논의 중이라고 하니까 다음 버전인 1.26.0에서는 수정될 것으로 보인다.

Top 10 Bugs in the C++ Projects of 2017

소프트웨어 분석 회사인 viva64에서 2017년 동안 발견했던 버그 중 가장 인상 깊은 버그 10개를 발표하였다. uninitialized memory부터 type error나 단순 실수까지 다양하게 있다.

Don’t Give Away Historic Details About Yourself

자기 정보를 쉽게 제공하지 말라는 글. 특히 우리나라 사람들은 주민등록번호 유출에조차 익숙해져서 그런지 자기 정보를 적는데 아무런 망설임이 없다.

Why Does "=" Mean Assignment?

수학에서는 보통 대입에는 :=를 사용하고 =는 비교에 사용한다. 근데 프로그래밍에서는 왜 =를 대입에 사용하게 됐는지에 관한 글이다. 뭐 지금은 다들 =를 대입연산자로 사용하고, ==를 비교연산자로 사용하는 데 익숙하기 때문에 별 의미는 없지만, 그냥 재미로 읽어봤다.

2018-04-07

2018년 14번째 주

이 포스팅은 그냥 지난 한 주간 읽었던 것들을 정리하는 포스트입니다. 그냥 예전에 봤던 글 중 나중에 필요한데 뭐였는지 기억 안 나는 글들이 있어서 쓰기 시작했습니다.
 보통 하는 일과 관련된 글들이 올라오겠지만 딱히 정해둔 주제는 없고, 그때그때 관심 있었던 것을 읽었기 때문에 지난주에 쓰인 글일 수도 있고 몇 년 전에 쓰인 글일 수도 있습니다.

Why you should pick strong consistency, whenever possible

지난번 글에서 CP와 AP 중에서 CP를 AP보다 더 선호해야 한다고 썼었다. 이렇게 생각한 사람이 나만 있는 건 아닌 것 같다. 구글 클라우드 플랫폼 팀에서 발표한 Spanner라는 데이터베이스는 external consistency를 보장한다. 이 포스트는 Spanner가 external consistency를 사용한 이유에 관한 글인데 제목은 strong consistency라고 나와 있지만, 이는 strong consistency가 더 일반적으로 사용되는 용어이기 때문에 제목을 이렇게 쓴 것이지 Spanner는 언제나 최신 데이터를 읽을 것을 보장하는 external consistency를 보장한다.

MantisTek GK2's Keylogger Is A Warning Against Cheap Gadgets

중국 키보드에서 키로거가 검출됐다고 한다. 개인적으로 전자제품 살 때 알리익스프레스를 많이 사용한다. 같은 사양의 제품을 10분의 1도 안 되는 가격으로 살 수 있기 때문이다. 그때마다 친구들과 이거 전부 해킹당하고 있는거 아닌가라는 농담 하면서 구매하고 몇일은 외부로 나가는 네트워크를 감시하고 그랬는데 실제로 키로거 하는 제품이 있었다.

There's a biological reason you're bored at work

회사 생활에 질리고 권태감이 드는 게 생물학적으로 당연하다고 한다. 사람의 뇌는 언제나 새로운 것을 추구하도록 진화됐다고 한다. 이는 사람뿐만 아니라 동물들에게도 마찬가지다. 동물들도 주어진 먹이를 먹는 것보다 숨겨진 음식을 찾아서 먹는 것을 더 좋아한다. 숨겨진 것을 찾아내고 새로운 것을 알아가면 뇌가 자극받아 도파민을 분비하고 이게 기쁨을 느끼게 한다는 것이다.

문제는 현대의 회사 시스템은 이런 도전 정신을 자극할만한 것이 없다는 것이다. 그래서 결국 주기적으로 팀을 옮기거나 조금 더 적극적인 경우 회사를 옮기거나 회사 생활에서 못 받는 자극을 취미에서 찾거나 하는 것 같다.

개인적으로는 이런 권태감을 개인 프로젝트로 푸는 편이다. 특히 회사 일과 같은 것을 하지 않기 위해서 개인 프로젝트를 할 때는 회사에서 쓰는 것과 다른 특성의 언어를 사용하여 회사 일과 상관없는 분야를 한다.

Known-planitext attack

KPA라고 불리는 암호학 공격기법이다. 이름 그대로 공격자가 암호화되기 전의 평문을 알고 있는 암호문이 몇 개 있을 때, 이를 기반으로 어떤 알고리즘으로 암호화된 것인지, 혹은 알고리즘을 이미 알고 있다면, 어떤 키로 암호화됐는지 찾아내는 공격방법이다. 찾아낸 알고리즘이나 키를 이용하여 후에 들어오는 암호문을 복호화하는 것이 목적이다.

It's Time for an RSS Revival

지난번에 소개했던 Why RSS Still Beats Facebook and Twitter for Tracking News와 비슷한 글이다. 최근 페이스북에서 터진 이슈 때문에 많은 사람이 대안을 찾고 있어 RSS에 주목하는 글이 많이 나오고 있는 듯하다.

Metcalfe's law

네트워크의 효용성은 노드 간 컨넥션 수가 높을 수록 높으므로 노드 수의 제곱에 비례하게 된다는 법칙이다. 하지만 현실적으로는 모든 노드가 서로 컨넥션을 갖지 않는다. 따라서 크기가 작은 네트워크에서는 메칼프의 법칙이 성립하지만, 크기가 커지면 성립하지 않는다. 일반적으로 노드 수가 많은 네트워크는 complete graph도 아니고 각 컨넥션이 모두 같은 가치를 갖는것도 아니기 때문이라고 한다.

2018-03-29

[보안] Alice와 Bob

네트워크 프로토콜을 설명하는 글을 읽으면 AliceBob, Carol, Eve, Mallory 등 많은 이름이 등장한다. 보통 이 이름들은 일정한 규칙을 가지고 부여되기 때문에 각 이름이 무슨 의미를 가지는지 안다면 그 프로토콜이 무엇을 어떻게 풀려고 하는지 조금은 더 이해하기 쉬워진다. 이번 글에서는 많이 네트워크 프로토콜에서 많이 사용되는 이름들이 무슨 의미를 가지는지 간략하게 정리해보았다.

네트워크 참여자 - Alice, Bob

보통 AliceBob은 서로 통신하려는 사람을 의미한다. 이때 통신하는 메시지는 보통 암호화하여 통신하는 메시지를 의미하는데 비대칭 키를 사용할지, 대칭 키를 사용할지는 그때그때 다르다.

다자 간 통신 참여자 - Carol, Dave, Erin, Frank, Gray

CarolDaveAliceBob과 함께 통신에 참여하는 경우 사용된다. CarolDave 말고도 CharlieDavid 같은 이름을 사용하기도 하지만 보통 CarolDave를 많이 사용한다.

5명 이상의 참여자가 필요한 프로토콜을 묘사할 때는, E, F, G로 시작하는 이름들을 사용한다. 보통 Erin, Frank, Gray 등의 이름이 사용되는데 어떤 이름을 사용할지는 딱히 정해져 있지 않다. 다른 용도로 사용되는 EveFaythe, Grace 등을 제외하고 아무 이름이나 사용된다.

공격자 - Eve, Mallory, Oscar, Trudy

Eve는 보통 네트워크를 감시하여 패킷을 도청하는 공격자를 의미한다. 다만 Eve는 능동적으로 공격을 하지는 않고 AliceBob이 주고받는 대화를 도청하여 무슨 대화를 주고받는지 알아내는 공격자에게 붙이는 이름이다. 이름의 유래는 eavesdrop에서 나왔다.

Trudy라는 이름은 Eve보다 조금 더 적극적인 공격자를 지칭할 때 사용된다. AliceBob 사이에 끼어들어 AliceBob의 메시지를 가로채 변조하여 보내거나 지우고 자신이 보내고 싶은 메시지를 보내는 공격자를 의미한다. 이름은 intruder에서 유래했다.

조금 더 포괄적인 공격자를 부르는 이름으로 Mallory가 있다. 이름의 유래는 malicious라는 단어에서 나왔다. EveTrudy와 구분 없이 공격자는 전부 합쳐 Mallory라고 부르기도 하므로 Mallory라는 이름이 나오면 이 사람이 어떤 공격을 하는지는 상황에 맞게 해석해야 한다. 이와 비슷한 의미로 사용되는 Oscar라는 이름도 있다.

감시자 - Walter, Wendy

Walter 혹은 Wendy라는 이름은 참여자를 감시하는 합법적인 감시자를 의미한다. 이들이 하는 일은 Mallory를 비롯한 공격자들과 비슷하지만, WendyWalter라는 이름이 나오면 AliceBob이 악의적인 사용자가 된다. 하지만 검열 감시를 피하는 것을 목적으로 하는 프로토콜을 설명할 때도 WalterWendy라는 이름을 사용한다. 예를 들면 검열하는 정부나 기업을 Wendy라고 이름 붙이고 이를 피하기 위한 참여자를 AliceBob이라고 이름하기도 한다.

WalterWendy는 크게 구분 없이 사용되는데 굳이 구분하면 Wendy는 허락되지 않은 정보를 주고받을 때 패킷을 검열하여 보내지 않도록 하는 역할을 하지만 Walter는 패킷을 검열하지는 않고 감시하는 역할만 할 때 사용된다. 보통 메시지를 감시하는 사람이 검열도 하기 때문인지 Wendy라는 이름이 더 많이 사용된다.

신뢰할만한 제삼자 - Trent, Ted

지금까지 설명한 이름들은 AliceBob의 통신을 방해하는 역할에 주어지는 이름이었다. 하지만 지금 설명할 TrentAliceBob의 통신을 도와주는 역할을 한다. 구체적으로 어떤 역할이 있지는 않고, AliceBob의 통신을 도와주는 역할을 한다. AliceBob이 통신할 shared secret을 생성, 관리해주기도 하고, 어떨 때는 AliceBob이 직접 통신하는 것이 아닌 대신 통신할 수 있는 암호화 된 채널을 만들어주기도 한다. 비대칭 키를 사용하는 경우, AliceBob의 public key를 저장하는 저장소를 Trent라고 부르기도 한다. Trent 대신 Ted라고 부르기도 하며 이름의 유래는 trust다.

2018-03-26

이더리움과 Eclipse attack

p2p 네트워크는 많은 취약점을 가지고 있는데 대표적인 것이 Eclipse attack이다. Eclipse attack은 네트워크 전체를 공격하는 것이 아니라 목표로 하는 노드의 Routing table을 공격하여 목표로 하는 노드와 전체 네트워크 사이에 악의적인 노드를 집어넣는 공격이다. Routing table을 공격하는 방법이기 때문에 routing-table poisoning이라고도 불린다.

이더리움도 p2p 네트워크를 사용하여 메시지를 주고받기 때문에 eclipse attack이 가능하리라 생각은 했는데 지난 3월 1일 발표 된 페이퍼에 따르면 단 2개의 노드만으로 하나의 노드를 완전히 고립시키는 것이 가능하다고 한다. 이 페이퍼는 올해 1월 진행됐던 바운티 프로그램에서 나온 문제점들을 정리한 페이퍼로 크게 세 가지 공격방법으로 나눌 수 있다.

우선 첫 번째 문제는 이해하기 위해 이더리움이 p2p 네트워크를 어떻게 관리하는지 이해해야 한다. 네트워크 그래프 구성에 가장 중요한 것은 다른 노드를 어떻게 찾을 것인가 하는 것이다. 이를 흔히 node discovery라고 하는데 이더리움은 node discovery를 위해 DHT(Distributed Hash Table) 프로토콜 중 하나인 Kademlia의 일부를 수정해서 사용한다.
Kademlia가 다른 DHT와 다른 가장 큰 특징은 노드 간의 거리를 XOR distance로 측정한다는 것이다. XOR distance의 거리는 symmetric 하므로 노드 아이디만 알고 있으면, 노드 A가 생각하는 노드 B까지의 거리나, 노드 B가 생각하는 노드 A까지의 거리나, 노드 C가 생각하는 노드 A와 노드 B 사이의 거리가 같다. 따라서 각 노드는 자신이 알고 있는 노드 중에서 자신과 가까운 노드들과만 통신하면 적은 연결 수로도 큰 네트워크를 구성할 수 있다는 장점이 있다. Kademlia 페이퍼에는 대략 노드의 개수를 N이라고 할 때 각 노드는 O(log(N))개의 연결만 유지하면 된다고 말한다.
이더리움이 Eclipse attack에 취약했던 근본적인 원인이 여기에 있다. Kademlia 프로토콜에서는 거리를 노드 아이디를 기반으로 구하는데, 어떤 노드의 아이디가 무엇인지 네트워크 안에 있는 모든 노드가 같은 값으로 알고 있어야 한다. 그래야 Kademlia가 주장하는 효율성을 가져올 수 있다. 문제는 분산 환경에서 각자의 아이디를 동의하는 방법이 없기 때문에 연결을 요청하는 노드가 자신의 public key를 알려주면 그 public key를 노드 아이디를 Keccak hashing 한 값을 노드 아이디로 준다. 즉, 각 노드가 자신의 아이디를 정하여 알려주는 것이다. 따라서 하나의 머신에서 여러 개의 노드 아이디를 생성해낼 수 있고, 악의적인 사용자가 하나의 머신을 여러 개의 노드인 것처럼 꾸미는 Sybil attack이 가능해진다.
이를 막기 위해 IP 주소와 노드 아이디를 기억하여 하나의 주소에서 여러 개의 키를 사용 못 하게 하는 것이 필요하지만, 아직 구현된 솔루션은 존재하지 않는다.

두 번째 공격 방법은 서버의 시간을 조정하는 것이다. 이더리움은 replay-attack을 막기 위해서 메시지에 보낸 시간을 적는다. 그리고 받은 메시지의 시간이 현재 시각과 20초 이상 차이가 나면 replay-attack으로 받은 메시지로 가정하고 무시한다. 문제는 replay-attack인지 판단하기 위해 메시지에 적힌 시간을 사용할 뿐, 메시지를 보낸 실제 시간은 알 수 없다는 것이다. 예를 들어 두 노드 사이에 시간이 20초 이상 난다면, replay-attack이 아닌 정상적인 메시지도 절대 처리되지 않는다.
현대 서버 환경에서 이럴 일은 거의 없다. 보통 서버 시간을 수동으로 설정하지 않고 NTP(Network Time Protocol)를 이용해 주기적으로 동기화하기 때문이다. 따라서 모든 서버의 시간은 길어봐야 몇 초 이내의 오차를 가질 것이라고 가정해도 된다.
하지만 이는 정상적인 상황에서만 가능한 가정이다. 만약 희생자가 될 노드가 사용할 NTP 서버를 해킹하거나 NTP 메시지를 가로채 동기화하지 못 하는 상황을 만들면 어떻게 될까. 아니면 조금 더 적극적으로 NTP 메시지를 가로채 실제 시간과 20초 이상 앞으로 당기면 어떻게 될까.
이 경우 희생자 노드는 네트워크상의 모든 노드와 시간 차이가 나게 될 테니, 다른 노드들은 모두 이 노드에서 보내는 메시지를 무시할 것이다. 그렇게 되면 희생자 노드는 네트워크에서 잊히게 되고 결국 희생자와 통신하는 것은 NTP 서버를 해킹했던 공격자 노드밖에 남지 않게 된다.
이는 메시지에 시간을 적는 방식으로 replay-attack을 막으려고 했던 이더리움 프로토콜의 문제다. 이런 공격을 막기 위해서는 세션별 혹은 메시지 별로 nonce를 사용하여 sign 하는 방법을 이용해야 한다. 하지만 이에 관한 솔루션도 아직 이더리움에는 구현된 것은 없는 것으로 보인다. 따라서 이더리움 노드를 돌리고 있는 서버는 시간이 잘 동기화되고 있는지 주기적으로 확인하는 방법밖에 없다.

마지막 공격 방법은 일종의 타이밍 어택이다. 이더리움은 네트워크에 너무 많은 리소스를 사용하지 않기 위해 통신할 최대 연결 수를 정해놓고 있다. 현재 최대 연결 수는 25개로 25개 이상의 연결이 맺어지면 더는 새 연결을 요청하거나 받아주지 않는다.
반대로 말해서 어떤 노드의 연결을 25개 점유할 수 있으면 이 노드는 더 이상 새 연결을 요청하지도 받아주지도 않을 것이다. 그렇다면 이것은 어떻게 가능할까?
간단하다. 연결이 0개인 순간을 노리면 된다. 어떤 서버라도 서버가 처음 시작했을 때는 연결이 0개인 상태가 된다. 이 타이밍을 노려서 25개의 연결을 먼저 맺으면 된다. 그렇다면 서버가 재시작되는 순간은 어떻게 찾을까.
하드웨어 문제든 OS 업데이트든 이더리움 클라이언트를 업데이트하든 서버가 재시작될 이유는 많이 있다. 하지만 조금 더 공격적으로 DoS 공격을 하여 이더리움 클라이언트를 죽이는 방법도 있다. 보통 이더리움 마이닝 노드는 서버가 종료되면 바로 다시 시작하게 돼 있기 때문에 DoS 공격으로 서버를 죽이고 나면 얼마 지나지 않아 서버가 재시작할 것이고 이때 25개 이상의 연결을 빠르게 요청하면 된다.
이를 막는 방법은 앞에서 설명한 두 방법보다 쉽다. 앞의 두 방법은 프로토콜 자체를 바꿔야 하지만 이 공격은 구현체를 수정하는 것만으로 쉽게 막을 수 있다. 전체 연결 개수 제한과 별개로 외부에서 들어오는 연결의 개수를 제한하는 것이다. 즉, 최소 몇 개의 연결은 내가 요청하는 노드와 연결이 되도록 하면 누군가 다른 사람에 의해 내 연결이 독점 당할 위험은 사라진다. 이 솔루션은 2월에 릴리즈 된 go-ethereum 1.8 버전부터 적용된 것으로 보인다.

2018-03-25

CAP theorem

CAP theorem은 분산 스토리지는 consistency(a.k.a. C), availability(a.k.a A), partition tolerance(a.k.a. P)를 동시에 만족시킬 수 없다는 것이다. 여기서 C, A, P는 각자 일반적으로 사용되는 용어와 다른 용어로 사용되기 때문에 CAP theorem을 이해하려면 각자가 정의하는 것을 이해하는 것이 중요하다.

C는 모든 read operation이 최신 데이터를 받는 것을 보장하는 것이다. C를 보장하는 시스템은 만약 최신 데이터를 돌려줄 것을 보장하지 못한다면 에러를 돌려줘야 한다. 개인적으로 분산 스토리지를 구현할 때 C, A, P 중 가장 구현하기 어려운 특성은 C라고 생각한다.

A는 모든 operation이 에러가 아닌 데이터를 돌려주는 것이다. 이때 돌려주는 값은 최신 값이 아니어도 상관없다. 심지어 eventual consistencyA를 보장하는 시스템에서는 실제로 존재할 수 없는 데이터 조합이 생길 수도 있다.

P는 partition 상황에서도 시스템이 정상 동작해야 한다는 것이다. 여기서 시스템이 정상 동작한다는 것이 언제나 최신 데이터를 보장하거나 에러가 아닌 값을 준다는 것이 아니다. 그것은 CA가 보장하는 것이고 partition 상황에서도 partition이 아닌 상황과 같은 것을 보장하면 P를 보장한다고 할 수 있다.

근데 여기서 partition은 정말 네트워크 레이어에 문제가 생겨 물리적으로 다른 망이 구성되는 상황을 말하는 것이 아니다. partition은 일부 메시지가 전달되지 않는 상황도 포함된다. 이는 분산환경에서 매우 흔히 발생하는 일이고 P를 포기한다는 것은 결국, 분산 환경을 포기한다는 말이 되기 때문에 분산 데이터 스토리지를 만들 때는 결국 CPAP 중 하나를 선택해야 한다.

개인적으로 생각하기에 CPAP 중 구현하기 더 어려운 것은 CP라고 생각된다. 모든 노드가 언제나 같은 상태를 유지하게 하는 것은 생각보다 어렵고 비싸다. 게다가 CP는 근본적으로 AP보다 latency와 throughput이 떨어진다.

하지만 이런 문제에도 불구하고 데이터 스토리지를 사용하는 application을 선택하는 입장에서는 AP보다 CP를 선호해야 한다고 생각한다. CP 대신 AP를 선택하는 이유는 가용성 때문이다. 가용성은 분명 중요하다. 하지만 스토리지의 가용성과 application 전체의 가용성은 다른 말이다. 데이터 스토리지가 정상적으로 동작하고 있다고 하더라도, 데이터의 일관성이 보장되지 못해서 잘못된 값을 돌려주었다면 이 application은 정상적으로 동작했다고 말할 수 있을까.

이런 일을 방지하기 위해서 AP인 스토리지를 사용하는 로직에는 데이터를 체크하는 복잡한 로직이 들어간다. 복잡한 로직은 높은 버그 가능성과 같은 의미이다. application 로직이 복잡해져서 버그가 발생했고 이를 고치기 위해 서비스를 종료했다면 이 application의 가용성은 떨어지게 된다. 게다가 버그로 인해 잘못된 응답을 준 경우는 오히려 처리하기 쉽다. 만약 버그로 인해 잘못된 데이터가 스토리지에 저장됐다면 이는 잡기도 어려울뿐더러, 고치기도 어렵다.

APCP보다 latency와 throughput이 떨어지기 때문에 성능이 필요한 application을 위해서는 AP인 스토리지를 선택해야 주장하는 사람들도 있다. 하지만 성능도 가용성과 마찬가지로 스토리지의 성능은 스토리지를 사용하는 application의 성능과는 별개다. AP인 스토리지를 사용하여 CP인 스토리지를 사용할 때보다 높은 성능을 보여준다는 것을 주장하기 위해서는 데이터 스토리지의 처리 시간이 application 처리 시간의 대부분을 차지한다는 것을 증명해야 한다. 여기서 측정하는 시간은 단순히 데이터 스토리지에 요청한 operation의 처리속도가 아니라, 네트워크 전송 속도를 제외한 데이터 스토리지 내부의 소요 시간이어야 한다.

설령 스토리지가 처리 속도에 충분한 영향을 준다고 하더라도 AP인 데이터 스토리지를 선택하기 전에 중요한 것이 처리 속도인지, 스토리지를 바꾸지 않으면 처리 속도를 올릴 방법이 없는지 고민해봐야 한다.

2018-03-04

2018년 9번째 주

 이 포스팅은 그냥 지난 한 주간 읽었던 것들을 정리하는 포스트입니다. 그냥 예전에 봤던 글 중 나중에 필요한데 뭐였는지 기억 안 나는 글들이 있어서 쓰기 시작했습니다.
 보통 하는 일과 관련된 글들이 올라오겠지만 딱히 정해둔 주제는 없고, 그때그때 관심 있었던 것을 읽었기 때문에 지난주에 쓰인 글일 수도 있고 몇 년 전에 쓰인 글일 수도 있습니다.

Lifetime Safety: Preventing Leaks and Dangling

 Herb Sutter와 Neil MacIntosh가 쓴 C++에서 memory leakdangling pointer를 어떻게 없앨 수 있는지에 관한 글이다. 일반적으로 C++의 포인터는 매우 강력하기 때문에, memory leak이나 dangling pointer를 없애기 위해서 C++의 기능을 일부 제한하거나 새로운 문법을 추가하거나 한다. 하지만 이 글에서는 언어를 바꾸지 않으면서 런타임 오버헤드 없이 컴파일 타임에 분석할 수 있는 알고리즘을 제시한다.
 특히 이 알고리즘은 프로그램 전체를 분석하는 것이 아니라 함수 단위, 정확히는 블록 단위로 적용할 수 있고, 변수 재사용 등 많은 스타일 가이드에서 권하지 않지만 실제로는 많이 사용되는 패턴들에 대해서도 고려돼있기 때문에 레거시 코드에 적용하기도 좋다.
 기계적인 작업이기 때문에 툴을 만드는 것이 가장 좋을 것이다. 하지만 툴을 만들 여유가 없더라도 포인터나 레퍼런스를 어떻게 써야 안전한지 보여주는 좋은 글이기 때문에 일단 읽어보는 것을 추천한다.

GitHub DDoS 공격당함

 지난 2018년 2월 28일, GitHubDistributed Denial-of-Service(a.k.a. DDoS) 공격을 당해 약 10분 정도 서비스가 멈췄었다. 이 공격은 memcached를 이용한 공격으로 중국의 0kee team이 찾은 Deluge라는 기법을 이용한 공격이었다.
 Deluge는 다른 서버에 설치된 memcached에 데이터를 요청할 때 source IP를 목표가 되는 서버의 IP로 수정하여 보내, memcached 서버가 목표가 된 서버로 데이터를 보내게 만드는 공격이다.
 이런 부류의 공격을 IP spoofing이라고 하는데, 이는 Internet protocol(a.k.a. IP)OSI 7 layer에서 말하는 session layer에 관한 부분이 없기 때문에 생기는 근본적인 취약점을 이용한 것이기 때문에, 네트워크 프로토콜을 IP 위에 올린다면, 프로토콜을 작성하는 차원에서 세션 처리를 신경 쓰지 않았다면 근본적으로 막을 방법은 없다.
 IP spoofing 자체는 흔하게 사용되는 공격법이지만 memcached를 사용하는 Deluge는 그중에서도 큰 획을 그었다. 단순히 GitHub을 공격했기 때문이 아니라 적은 패킷으로 많은 데이터를 공격하게 할 수 있기 때문이다. 이 비율을 bandwidth amplification factor라고 하는데 Deluge는 지금까지 알려진 IP spoofing 공격 중에서 가장 높다.

2018-02-21

Diffie-Hellman Key Exchange - 공개된 정보만으로 secret key 만들기

 네트워크상의 두 노드가 암호화된 통신을 하기 위해선 먼저 두 노드가 어떤 암호화 방식으로 어떤 키를 이용해서 암호화할지 합의해야 한다. 보통 암호화 방식은 사용하는 애플리케이션에 따라 고정된 방식을 사용하거나 두 노드가 처음 통신을 시작할 때 암호화하지 않은 패킷을 이용해 합의하거나 한다. 이후 패킷은 양쪽 노드밖에 모르는 암호키를 이용해 암호화할 것이기 때문에 암호화 방식은 암호화되지 않은 방식으로 합의를 해도 안전하다. 하지만 어떤 키를 사용할지는 암호화되지 않은 방식으로 합의해선 안 된다. 키가 공개되면, 이 비밀키를 이용해서 제삼자가 패킷을 위조할 수 있기 때문이다. 그렇다면 이 비밀키는 어떻게 안전하게 교환할 수 있을까?
 이에 대한 해답으로 나온 것 중 하나가 Diffie-Hellman key exchange(a.k.a. DH)다. 사실 이외에도 다른 방법들이 많이 있지만, 개인적으로 생각하기에 가장 범용적으로 안전하게 사용할 수 있는 것은 DH라고 생각한다. 또한, 이후 이것에 대해 많은 변종이 나왔지만, DH만 이해하면 나머지는 이해하는 데 별문제 되지 않는다. 그렇다면 DH는 어떻게 동작할까?

 우선 DH가 성립하기 위해서는 특별한 수학적 성질을 만족하는 generator가 필요하다. 이 generator는 하나의 입력을 받아 하나의 출력을 내뱉는다. 이 generator가 g라고 하고, 입력 x에 대해서 출력 Y를 내뱉는 Y=g(x)가 있을 때, x로부터 Y를 가지고 오는 것은 빠르고 쉽게 계산할 수 있지만, Y로부터 x를 가지고 오는 것은 어려운 일이어야 한다. 즉, 수학적으로는 역함수가 없는 함수여야 하고, 결괏값의 스페이스가 매우 커서 brute-force로 찾는 것이 매우 힘들어야 한다. 사실 이외에도 만족해야 할 수학적 성질이 여러 개 있지만 이번 포스팅에서는 그에 대한 설명은 생략하고 넘어가겠다.

 DH가 처음으로 제시한 방법은 generator로 modular exponentiation을 사용했다. modular exponentiation에서는 입력을 x라고 했을 때, 출력 Y를 다음과 같이 계산한다. Y:=bxmodm. 이때 bm은 적절한 수학적 성질을 만족하는 값으로, bm의 pair가 generator g가 된다.
 이후 여러 변종이 나왔고, 요새 많이 사용되는 것은 elliptic curve를 이용하는 Elliptic-curve Diffie–Hellman(a.k.a. ECDH)이다. 이 경우 사용하는 곡선의 종류가 generator g가 된다.

 어떤 generator를 사용할지 결정되고 나면 DH는 다음의 과정을 거쳐서 진행된다.


AliceBobEve
1GGG
2ab
3A=G(a)B=G(b)
4BAA, B
5s=B(a)s=A(b)

 우선 위의 표를 설명하자면, AliceBob은 서로 통신하기 원하는 사용자고, EveAliceBob 사이에 무슨 내용을 통신하는지 알기 원하는 도청자다. AliceBob은 공개된 공간에 있기 때문에 둘 사이의 통신하는 내용은 제삼자인 Eve도 알 수 있다. 그리고 위의 표에서 푸른색으로 표시된 문자는 자신이 생성한 데이터고, 붉은색으로 표시된 문자는 다른 사람이 생성하여 통신을 통해서 듣게 된 데이터다. 또한, 대문자와 소문자도 다른 의미를 가지는데, 대문자는 외부에 공개해도 되는 public 한 데이터고, 소문자는 자신만 알고 있어야 하는 private 한 데이터다. 따라서 붉은색 데이터는 모두 Eve도 알게 되고, 붉은색 데이터와 푸른색 데이터를 조합하여 AliceBob은 알지만, Eve는 모르는 데이터를 만드는 것이 DH의 목적이다.

1GGG

 우선 첫 단계는 어떤 generator를 사용할지 결정하는 단계다. generator를 결정하는 단계도 여러 방법으로 구현할 수 있지만 그다지 중요한 내용은 없으니 이번 포스트에서는 결정됐다고 가정하고 넘어가도록 하겠다.

2ab

 다음 단계에서 AliceBob은 각자 private key를 만든다. 이 private key를 어떻게 만드는가는 DH에서 중요한 요소가 아니지만, replay 공격을 막기 위해 private key는 랜덤하게 생성하는 것이 좋다.

3A=G(a)B=G(b)

 그리고 AliceBob은 generator G를 이용해서 각자 public key를 생성한다. 여기까지 진행되면 AliceBob은 자신의 private key와 public key만 알고 있고, Eve는 아무 키도 모르고 있는 상태가 된다.

4BAA, B

 다음 단계는 AliceBob이 각자 자신의 public key를 말하는 단계다. 이 단계를 지나면 AliceBob의 public key 두 개는 Eve를 포함한 모든 사람이 알고 있는 정보가 된다.

5s=B(a)s=A(b)

 다음 단계에서 AliceBob의 public key를 기반으로 generator를 만들어 자신의 private key를 사용하여 secret key를 만들고, BobAlice의 public key를 기반으로 generator를 만들어 자신의 private key를 사용하여 secret key를 만든다. 이때 public key를 사용해서 generator를 만드는 것은 어떤 generator를 사용하는가에 따라 다르다. modular exponentiation을 사용하는 경우 m는 기존의 generator의 m을 그대로 사용하고, 새로 받은 public key를 b로 사용한다. elliptic curve를 사용하는 경우 타원 곡선은 그대로 사용하고, public key를 시작점으로 계산한다.
 어쨌든 전달받은 public key를 기반으로 generator를 만들어 자신의 private key를 input으로 만들면, 그 결과 나온 secret key는 놀랍게도 AliceBob이 같은 값을 가지게 된다. 이는 아까 설명을 생략했던 generator의 몇 가지 수학적 성질 중 하나 덕분에 가능한 것이다. 이 secret key s를 알기 위해서는 최소 한 개의 private key가 필요하므로 Eve는 secret key s를 알 수 없다. 이제 AliceBob은 공유하지만, Eve는 모르는 값이 생겼으니, 이후 메시지는 전부 secret key s를 이용해서 암호화하면 된다.

 이상이 Diffie-Hellman Key Exchange를 이용하여 두 노드가 안전하게 비밀키를 만들어 공유하는 방법을 설명하였다. 사실 DH를 직접 구현해야 할 경우는 거의 없다. 하지만 네트워크 통신의 기본이 되는 내용이고, 많은 프로토콜이 컨넥션을 시작하는 단계에서 DH를 사용하기 때문에 서버 프로그래머라면 알아 두는 것이 좋다.

2017-12-05

Raft - leader election

 Raft에서는 모든 결정을 leader가 한다. 클라이언트의 모든 요청은 리더를 통해서만 가능하고, 새로운 로그를 추가하는 것도 새로운 노드가 추가되거나 기존의 노드를 지우는 것도 리더를 통해서 결정된다. leader의 명령을 따르는 노드들은 follower라고 하는데 follower들은 leader의 명령을 그대로 따른다. Follower는 leader가 보낸 명령에 따라 자신의 상태를 변경하고, 새로운 클라이언트가 접속하면, 클라이언트에게 어떤 노드가 리더인지 알려준다. Raft에서는 의도적으로 follower가 할 수 있는 일이 별로 없도록 만들었고 덕분에 프로토콜을 단순하게 만들 수 있었다.

 Leader인 노드는 일정 주기로 follower들에 heartbeat을 보낸다. follower들은 leader의 heartbeat을 듣고 있다가 일정 시간 동안 heartbeat을 듣지 못하면 leader가 죽었다고 생각하고 자신을 후보로 추천하며 다른 노드들에 자신을 leader로 뽑아달라고 RequestVote 요청을 보낸다. 이렇게 자신을 RequestVote 요청을 받은 노드를 candidate이라고 부른다. RequestVote를 받은 노드는 현재 자신의 상태를 보고 candidate이 더 최신 상태라면 새 leader를 지지하는 응답을 보내고, 그렇지 않으면 거절하는 응답을 보낸다. 반 이상의 노드가 자신을 지지한다고 응답하면 이 candidate은 leader가 된다. RequestVote를 보내고 일정 시간 동안 leader가 되지 못한 candidate은 다시 한번 모든 노드에게 RequestVote를 보낸다. 이때 얼마 만에 다시 RequestVote를 보낼지는 특정 범위 내에서 랜덤하게 결정된다. 랜덤한 timeout을 사용한다는 것은 Raft를 효율적으로 동작하게 하는데 매우 중요하다. 만약 고정된 시간을 사용한다면 모든 후보가 자기 자신에게 투표하라고 주장하며 선거가 끝나지 않을 수 있다. Candidate이 더 최신인지 아닌지는 term과 lastLogIndex를 보고 결정한다.

 Term은 leader의 재임 기간이다. Leader가 바뀌면 term이 바뀐다. 하지만 모든 term에 leader가 존재하지는 않는다. Term은 단조 증가하는 숫자로 서버별로 자신의 term을 저장하고 있다. Raft에서 메시지를 보낼 때는 언제나 자신이 생각하는 term을 함께 보내고, 메시지를 받은 노드는 메시지에 들어있는 term과 자신의 term 중 더 큰 값을 자신의 term으로 만든다. 혹은 RequestVote를 보낼 때도 term을 증가시키고 보낸다.

 Message에 들어있는 term이 자신의 term보다 크다는 것은 candidate이 자신보다 더 최신의 상태라는 것이다. 따라서 자신의 상태를 바로 follower로 바꾸고 candidate을 지지한다는 응답을 보낸다. 이것은 자신이 leader였을 때도 마찬가지다. 이런 경우 네트워크 등의 문제로 다른 노드가 자신의 heartbeat을 듣지 못했다는 것이고, 이 경우 다른 노드들이 이미 state를 진행했을 수 있기 때문에 새 leader의 상태를 따른다. 반대로 요청의 term이 자신의 term보다 낮다면, 이 요청은 그냥 무시하면 된다.

 자신과 같은 term의 RequestVote 메시지가 메시지에 있는 lastLogIndex를 본다. lastLogIndex가 자신의 log index보다 작다면 이 candidate은 지지하지 않는다. 이는 partition 상황에서도 consistency를 유지하기 위함이다. 이는 다음에 log replication을 설명하며 자세히 설명하도록 하겠다.

2017-11-29

Raft - 이해하기 쉬운 consensus algorithm

 분산 시스템을 구축할 때, 모든 노드가 독립적으로 돌아가는 시스템을 설계한 것이 아니라면, 공유된 상태를 합의하기 위한 모종의 방법이 필요하다. 이런 식으로 분산 환경에서 상태를 공유하는 알고리즘을 consensus algorithm이라고 한다.
 consensus algorithm 중에서 가장 유명한 알고리즘은 Paxos다. 하지만 Paxos는 구현은커녕 이해하는 것 자체가 어렵기 때문에, Paxos를 구현한 라이브러리가 거의 없었고, 일부 분산 시스템들이 내부적으로 사용하는 정도였다.
 그래서 분산 시스템을 구축할 때 현실적으로 사용할 수 있는 방법은 Zab algorithm을 사용하는 Zookeeper를 이용하는 것이었다. Zookeeper는 매우 훌륭하다. 사실 지금까지도 분산 환경에서 consensus를 위해 사용할 수 있는 검증 된 거의 유일한 라이브러리라고 말할 수도 있을 정도다. 하지만 Zookeeper는 메시지의 순서가 보장돼야 하기 때문에 반드시 TCP를 사용해야만 한다. 또한, Zab algorithm이 Zookeeper의 구현과 긴밀하게 엮여 있기 때문에 다른 구현을 만들기 힘들어 반드시 JVM을 띄워야 하는 문제가 있다.

 Raft는 구현체를 만들기 어렵다는 기존 consensus algorithm의 문제를 해결하기 위해 이해하기 쉬운 것을 최우선으로 설계된 consensus algorithm이다.
 Raft에서는 노드 간 공유될 state를 append-only state machine으로 본다. 따라서 노드 간에 상태가 합의됐다는 것은 이 state machine을 변경시키는 append command가 같은 순서로 적용됐다는 것을 의미한다. append command를 추가하는 것을 로그를 추가한다고 하고, 모든 노드가 같은 state를 가지게 하는 것을 log replication이라고 한다. 이때 어떤 append command를 추가할 것인지를 모든 노드가 일치한 정보를 가지는 것이 중요한데, Raft는 리더를 선출하여 모든 결정을 리더에게 위임하는 방법을 택했다. 이를 strong leader라고 하는데 덕분에 리더가 결정되고 나면 log replication은 매우 단순하게 진행된다. 이 leader election이 어떻게 되는지 다음 글에서 자세히 다뤄보도록 하겠다.

2016-04-08

Balanced graph와 unbalanced graph

 사람과 사람 사이의 관계를 그래프로 나타내보자. 그렇다면 사람은 node로 표현될 것이고, 두 사람이 서로를 알고 있다면 두 node 사이의 edge를 그릴 수 있다. 실제로는 내가 모르는 사람이 나를 알고 있을 수도 있고, 내가 아는 사람이 나를 모를 수도 있지만, 그런 복잡한 경우는 생각하지 않고 서로 간에 알고 있는 경우만을 고려하도록 하자.1)
 이때 두 사람은 친구이거나 적일 것이다. 현실적으로는 그냥 아는 사람이고 적인지 친구인지 별생각 없을 수도 있거나, 내가 친구라고 생각하는 사람이 나를 적으로 생각할 수 있지만, 이런 경우 문제가 복잡해지니 생각하지 않도록 하자.
 그러면 친구인 사람 사이의 edge에는 +사인을, 적인 사람 사이의 관계에는 -사인을 추가하여 signed undirected graph2)로 표현할 수 있다.

positive signed edgenegative signed edge

 사람 사이의 관계를 signed graph로 표현한다면, 이는 아래의 4가지 모습 중 하나일 것이다.

 만약 친구일 확률과 적일 확률이 똑같이 1/2로 랜덤하게 그래프를 생성하였다면, 모두가 친구로 생각하거나 모두가 적으로 생각할 확률은 각각 1/8이고, 한 명은 다른 두 명을 적이라고 생각하는데 두 사람은 서로 친구라고 생각하거나, 친구로 생각하는 두 사람이 서로 적일 확률은 각각 3/8으로 나타날 것이다.
 하지만 실제 인간관계를 그래프로 그렸을 때는 첫 번째나 세 번째 삼각형이 나올 확률은 기대했던 것보다 높게 나오고, 두 번째나 네 번째 삼각형이 나올 확률은 기대했던 것보다 낮게 나온다. 이 두 삼각형은 다른 삼각형으로 쉽게 변하기 때문이다.

 내가 친구라고 생각하는 두 사람이 서로를 적으로 생각하는 경우를 보자. 이 경우 나에게 압박이 가해져서 한쪽 관계를 끊거나, 적으로 돌아서기 쉽다. 3명이 서로를 모두 적으로 생각하는 경우도 쉽게 변한다. 이런 경우 공동의 적을 상대하기 위해 적이었던 두 사람이 손을 잡기 쉽다.

 이렇게 변하기 쉬운 삼각형을 unbalanced triangle이라고 부르고, 이런 삼각형이 있는 그래프를 unbalanced graph라고 부른다. unbalanced graph의 경우 외부에서 압력을 주지 않아도 쉽게 상태가 변한다.

 반대로 3명이 모두 친구이거나 공통의 적을 둔 친구 사이를 가지는 경우는 외부 압력이 없는 한 상태가 변하지 않는다.

이런 삼각형을 balanced triangle이라고 부르고, 이런 삼각형으로만 이루어진 그래프를 balanced graph라고 부른다.


1) 그래프 안의 모든 사람이 서로 알고 있는 경우. 즉, 모든 node와 연결된 edge를 가지는 경우를 complete graph라고 부른다. 실제 커뮤니티를 조사하면 대부분 서로 모르는 관계이기 때문에 complete graph가 되는 경우는 드물다. 하지만 문제를 단순화하기 위해 앞으로 나오는 그래프는 전부 complete graph라고 가정하도록 하겠다. incomplete graph의 balance에 대해서는 다른 글에서 설명하도록 하겠다.
2) undirected가 directed보다 많이 쓰이기 때문에, undirected는 간단하게 signed graph라고 하고, signed directed graph를 signed digraph라는 이름으로 부르기도 한다.

2016-03-10

OSI 모델은 무엇인가

 OSI 모델은 추상화를 위해 프로토콜을 여러 계층의 layer로 나누고, 각 layer에서 자신이 책임진 일만 하도록 설계한 네트워크 모델이다. 각 layer는 완전히 독립되어 있으며, 각 layer에서 수행해야 하는 일만 제대로 된다면 그 구현은 다른 구현으로 대체 될 수 있도록 설계되어야 한다.

OSI 7 layer

 OSI layer는 보통 7계층으로 나누어지기 때문에 OSI 7 layer라고 불리기도 한다. 각 layer의 이름은 가장 아래부터 physical layer, data link layer, network layer, transport layer, session layer, presentation layer, application layer다. 이를 다시 크게 둘로 나누어서 network layer까지의 아래 3개 layer는 media layer라고 하고, transport layer부터 위의 4개 layer를 host layer라고 한다. media layer는 네트워크상에서 원하는 머신을 찾아 데이터를 보내는 역할을 하고, host layer는 전송된 데이터를 안전하게 사용하는 방법에 대한 역할을 한다.

 그러면 이제부터 각 layer에 대해 자세한 설명을 할 것인데 명심해야 할 것은 어떤 네트워크 프로토콜을 만들 때 OSI 모델을 지키는 것이 필수적인 것은 아니라는 것이다. 실제로 많이 사용되는 네트워크 프로토콜 중에서도 완벽하게 OSI의 원칙에 따라 설계된 프로토콜도 거의 없고, 7개 계층을 전부 구현하여 사용하는 것도 본 적이 없다. 그냥 네트워크 프로토콜은 이러한 역할이 필요하고, 그것은 이러한 순서로 지켜지는 게 안전하다는 가이드 정도로 생각하는 것이 좋다.

physical layer

 우선 가장 아래 계층은 physical layer다. physical layer에서는 전달 매체를 통해 전달된 신호를 어떻게 0과 1로 바꾸는지를 담당한다. 데이터의 전달 매체에 따라서 0과 1이 반드시 bit일 필요도 없고, 사실 전기 신호일 필요도 없다. 정말 다양한 방법으로 구현될 수 있지만 보통은 프로그래머가 신경 쓸 필요 없는 수준의 이야기다.

data link layer

 두 번째 계층은 data link layer다. data link layer에서 전송되는 data의 단위는 frame이라고 부르는데, 이 계층에서는 오류 없는 온전한 frame이 전달되는 것을 보장하거나 데이터를 전달할 매체에 접근하는 것을 컨트롤 해주는 계층이다. 다만 최종목적지까지 전달되는 것은 아니고, 로컬 네트워크 내에서만 전달해주거나, 로컬 네트워크 사이를 연결해주는 역할만 한다. 오류가 없는 것을 보장하기 위해 에러를 감지하거나 고칠 수 있는 데이터를 프레임에 추가하기도 한다.

network layer

 세 번째 계층은 network layer다. network layer에서 다루는 data의 단위는 packet이다. 일반적으로 frame은 packet을 감싼 1 : 1 관계지만, 꼭 그렇다고 할 수는 없다. 다양한 이유로 data link layer에서 packet들을 모아서 하나의 frame으로 만들거나, 하나의 packet을 쪼개서 여러 개의 frame으로 만들거나 한다. 이 계층에서는 data가 목적지까지 도착하도록 routing 해준다. 하지만 목적지까지 제대로 도착한다거나, 순서대로 도착하는 것을 보장해주지는 않는다.

transport layer

 네 번째 계층은 transport layer다. transport layer는 최종 노드까지의 안전한 통신을 보장해서 이다음 layer부터는 data의 무결성을 신경 쓰지 않을 수 있도록 해준다. 에러 검출이나 order의 보장 등을 할 지 말지에 따라서 TP0부터 TP4까지 4개의 class로 분류된다.

session layer

 다섯 번째 계층은 session layer다. 두 node 사이의 인증을 해주거나, 연결이 끊어졌을 때 다시 연결을 해주거나 하는 것을 담당한다. SSH protocol처럼 지속적인 연결이 보장되어야 하는 프로토콜에서 사용된다.

presentation layer

 여섯 번째 계층은 presentation layer다. 교환되는 정보의 형태를 결정한다. endian을 결정하기도 하고, 전송할 data field의 크기를 결정하는 등 전송될 데이터의 모양을 만든다. 하지만 현실적으로 별도의 프로토콜 레이어로 구현되지 않고, 서버와 클라이언트 간의 암묵적인 약속으로 보장해준다.

application layer

 일곱 번째 계층은 application layer다. 이 계층의 프로토콜은 사용자가 네트워크를 이용해 실제로 하고자 하던 것. 즉, file을 주고받는다거나, 명령을 보낸다거나 하는 내용을 명시한다.