SecureTL은 더욱 안전한 TL이다.
Split Learning과 Secure Multi-Party Computation(MPC)을 결합하여 데이터 프라이버시를 보장하면서 협력 학습을 수행한다.
Orchestrator로 부터 받은 모델 Config 검증을 위해 WASM 샌드박스를 도입했다.
네트워크를 통한 원본 데이터 유출을 방지하기 위해 리눅스 커널 기능(namespace, seccomp-bpf)을 활용하여 프로세스 격리하고, 권한을 제어하였다.
기존 TL++은 모델 아키텍처를 models.py에서 하드코딩하고 있다.
Secure TL은 모델 아키텍처를 하드코딩하지 않는다. JSON config 파일 하나로 모델 구조 전체를 정의하고, 런타임에 동적으로 PyTorch 모델을 생성한다.
config/cnn_config.json에 레이어를 순서대로 정의한다.
[
{ "type": "conv2d", "args": [3, 64, 3], "kwargs": {"padding": 1} },
{ "type": "relu" },
{ "type": "maxpool2d", "args": [2, 2] },
{ "type": "flatten" },
{ "type": "linear", "args": [4096, 512] },
{ "type": "linear", "args": [512, 10] }
]TLpp/dynamic_model.py의 LayerBuilder클래스는 등록된 레이어 타입(conv2d, linear, relu, lstm, transformer_encoder_layer 등)을 args/kwargs로 인스턴스화한다. 코드 수정 없이 config만 바꾸면 다른 모델 아키텍처로 실험할 수 있다.
Orchestrator가 config를 로드한 뒤 cut_layer 인덱스를 기준으로 분할한다.
config/cnn_config.json
│
▼
┌─ node_config ──┐ ┌── orch_config ──┐
│ Layer 0~4 │ │ Layer 5~end │
└────────────────┘ └─────────────────┘
│ │
DynamicNodeNet DynamicOrchestratorNet
(각 Node에 전송) (Orchestrator 보유)
- Node 측:
config[:cut_layer]로DynamicNodeNet을 생성하여 로컬 데이터로 forward pass 수행 - Orchestrator 측:
config[cut_layer:]로 분류 레이어를 구성, activation을 받아 loss 계산 및 backward pass 수행
원본 데이터는 Node 밖으로 나가지 않으며, intermediate activation만 전송된다.
Orchestrator로부터 수신한 config JSON을 그대로 역직렬화하면 악의적 페이로드에 노출될 수 있다. 이를 방지하기 위해 Rust로 작성된 WebAssembly 모듈로 config를 사전 검증한다.
Orchestrator ──[JSON]──► Node
│
┌─────▼──────────┐
│ WASM validator │ (Rust → wasm32 컴파일)
└─────┬──────────┘
│
검증 통과 시에만
모델 생성 진행
Node는 wasmtime 런타임 위에서 WASM 바이너리를 실행하고, 레이어 타입/인자 구조를 검증한 뒤에만 모델을 생성한다.
WASM 모듈은 샌드박스 내에서 실행되므로 검증 과정 자체가 호스트 시스템에 영향을 줄 수 없다.
--isolated 플래그를 활성화하면 Node 하나가 3개의 격리된 프로세스로 분리된다.
각 프로세스는 Linux 커널 수준에서 서로 다른 보안 정책이 적용되어, 한 프로세스가 침해되더라도 다른 프로세스의 데이터에 접근하거나 외부로 유출할 수 없다.
┌─────────────────────────────────────────────────────────────────────┐
│ Node │
│ │
│ ┌───────────────────────┐ IPC (Queue) ┌────────────────────────┐ │
│ │ DataLoader │◄────────────►│ Trainer │ │
│ │ │ │ │ │
│ │ unshare(NEWNET) │ │ unshare(NEWNET) │ │
│ │ seccomp: │ │ seccomp: │ │
│ │ socket() ALL BLOCK │ │ AF_INET/AF_INET6 │ │
│ │ execve() BLOCK │ │ BLOCK │ │
│ │ │ │ AF_UNIX ALLOW │ │
│ │ send as numpy array │ │ (for torch IPC) │ │
│ └───────────────────────┘ └───────────┬────────────┘ │
│ IPC │ (Queue) │
│ ┌──────────────┴────────────┐ │
│ │ Networker │ │
│ │ │ │
│ │ seccomp: │ │
│ │ AF_INET ALLOW │ │
│ │ other sockets BLOCK │ │
│ │ execve() BLOCK │ │
│ │ PR_SET_NO_NEW_PRIVS │ │
│ │ │ │
│ │ Orchestrator/Helper comm │ │
│ └───────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
각 프로세스에는 두 계층의 커널 수준 보안이 적용된다.
Layer 1 — Network Namespace (unshare)
unshare(CLONE_NEWUSER | CLONE_NEWNET) 시스템 콜로 프로세스를 빈 네트워크 네임스페이스에 배치한다. 네트워크 인터페이스(lo, eth0 등)가 존재하지 않으므로 AF_INET/AF_INET6 소켓으로는 어떤 외부 연결도 성립할 수 없다. AF_UNIX 소켓은 파일시스템 기반이므로 네임스페이스 격리 후에도 정상 동작한다.
Layer 2 — Syscall 필터링 (seccomp-bpf)
prctl(PR_SET_SECCOMP) 로 BPF(Berkeley Packet Filter) 프로그램을 설치하여, 프로세스가 호출할 수 있는 시스템 콜 자체를 제한한다.
필터는 커널에서 실행되므로 사용자 공간에서 우회할 수 없다.
| 프로세스 | unshare(NEWNET) | socket 정책 | execve 정책 | 역할 |
|---|---|---|---|---|
| DataLoader | O | 전체 차단 (AF_INET, AF_INET6, AF_UNIX 모두) | 차단 | 데이터 읽기 전용. numpy 배열로 변환 후 Queue 전송 |
| Trainer | O | AF_INET/AF_INET6 차단, AF_UNIX 허용 | 허용 | PyTorch 학습. AF_UNIX는 torch IPC/CUDA에 필요 |
| Networker | X | AF_INET만 허용, 나머지 차단 | 차단 | Orchestrator/Helper와의 IPv4 소켓 통신 전담 |
DataLoader에는 가장 엄격한 정책(DataLoaderPolicy)이 적용된다. 모든 소켓 생성과 execve 시스템 콜이 차단되어, 데이터를 읽고 numpy 배열로 변환하는 것 외에는 아무 작업도 수행할 수 없다. torch 텐서 공유는 AF_UNIX 소켓 기반 파일 디스크립터를 사용하므로, numpy 배열로 변환하여 전송함으로써 소켓 없이도 IPC가 가능하다.
Trainer에는 TrainerPolicy가 적용된다. 외부 네트워크(AF_INET/AF_INET6)는 차단하되, torch의 multiprocessing IPC와 CUDA 드라이버가 요구하는 AF_UNIX 소켓은 허용한다.
Networker에는 NetworkerPolicy가 적용된다. Orchestrator/Helper 통신에 필요한 AF_INET 소켓만 허용하고, AF_INET6/AF_UNIX 등 나머지 소켓 family와 execve를 차단한다. IPC는 multiprocessing.Queue가 내부적으로 os.pipe()(pipe syscall)을 사용하므로 소켓이 불필요하다. 네트워크 통신이 핵심 역할이므로 네임스페이스 격리(unshare)는 적용하지 않되, PR_SET_NO_NEW_PRIVS로 권한 상승을 방지한다.
BPF 필터는 커널의 seccomp_data 구조체를 직접 검사하는 바이트코드로 구성된다.
seccomp_data {
nr (offset 0) ← syscall 번호
arch (offset 4) ← 아키텍처 (AUDIT_ARCH)
args[0] (offset 16) ← 첫 번째 인자 (예: socket family)
}
AF_INET 차단 필터 동작 흐름:
[0] 아키텍처 로드 (AUDIT_ARCH 검증)
[1] 아키텍처 불일치 → 프로세스 종료
[2] syscall 번호 로드
[3] socket() 아닌 경우 → ALLOW
[4] args[0] (socket family) 로드
[5] AF_INET → BLOCK (EPERM)
[6] AF_INET6 → BLOCK (EPERM)
[7] 그 외 → ALLOW
seccomp 필터는 아키텍처에 따라 syscall 번호가 다르다. ArchConfig 추상 클래스로 이를 분리하고, 런타임에 platform.machine()으로 자동 감지한다.
| 아키텍처 | AUDIT_ARCH | socket | execve | execveat |
|---|---|---|---|---|
| x86_64 | 0xC000003E |
41 | 59 | 322 |
| ARM64 (aarch64) | 0xC00000B7 |
198 | 221 | 281 |
격리된 프로세스 간 통신은 multiprocessing.Queue를 사용한다. 타입이 지정된 IPCMessage를 통해 데이터 요청, 배치 할당, forward/backward 결과 등을 교환한다.
TL++는 다중 계층 보안을 통해 데이터 프라이버시를 보장한다.
| 계층 | 기술 | 방어 대상 |
|---|---|---|
| 데이터 분리 | Split Learning | 원본 데이터 미전송, activation만 교환 |
| 암호학적 보호 | Additive Secret Sharing (3-Party MPC) | activation/gradient 도청 방지 |
| 네트워크 격리 | Linux Network Namespace (unshare) |
프로세스의 외부 네트워크 접근 차단 |
| 시스템 콜 제한 | seccomp-BPF | 위험한 시스템 콜 자체를 커널 수준에서 차단 |
| 권한 상승 방지 | PR_SET_NO_NEW_PRIVS |
프로세스 권한 상승 불가 |
| Config 검증 | WebAssembly (Rust) | 역직렬화 공격 방지 |
├── TLpp/
│ ├── orchestrator.py # 학습 코디네이터
│ ├── node.py # SecureNode / IsolatedNode
│ ├── helper.py # MPC share 교환 서버
│ ├── protocol.py # 바이너리 소켓 프로토콜 + secret sharing
│ ├── dynamic_model.py # JSON → PyTorch 모델 빌더
│ ├── data.py # 학습 데이터 로더
│ └── utils.py # BatchScheduler, GradientAggregator
├── isolation/
│ ├── dataloader.py # DataLoaderProcess (격리)
│ ├── trainer.py # TrainerProcess (격리)
│ ├── networker.py # NetworkerProcess (격리)
│ └── common/
│ ├── ipc.py # IPCChannel, IPCMessage
│ ├── warden.py # seccomp-BPF 필터 빌더/설치
│ ├── process_policy.py # 프로세스별 샌드박스 정책
│ └── architecture_config.py # 아키텍처별 syscall 번호
├── validator/
│ ├── validator.py # WASM 검증기 래퍼
│ └── config_validator/ # Rust 소스 + 컴파일된 .wasm
├── config/
│ └── cnn_config.json # CNN 모델 아키텍처 정의
├── Dockerfile
├── docker_manager.py
├── gui.py # gui
└── requirements.txt
pip install -r requirements.txt
pip install -e .주요 의존성: torch, torchvision, wasmtime (WASM 런타임), pyseccomp
플랫폼별 동작 방식
- Linux: 네이티브 실행.
unshare,seccomp등 커널 기능을 직접 사용한다.- macOS / Windows: Node 실행 시
docker_manager.py가 Docker를 자동 감지하여 Linux 컨테이너 내에서 실행한다. 이미지 빌드도 자동으로 처리되므로, Docker Desktop을 설치하고 실행한 상태에서 동일한 명령어를 사용하면 된다. Orchestrator와 Helper는 모든 플랫폼에서 네이티브로 실행된다.
# Orchestrator
python -m TLpp.orchestrator \
--host 192.168.0.4 --port 8080 \
--n_nodes 1
# Node (별도 터미널에서 n_nodes 수만큼 실행)
python -m TLpp.node \
--host 192.168.0.4 --port 80803-Party MPC를 활성화하여 activation과 gradient를 secret sharing으로 보호한다.
# Orchestrator
python -m TLpp.orchestrator \
--host [Orchestrator_IP] --port [Orchestrator_Port] \
--helper_host [Helper_IP] --helper_port [Helper_Port] \
--n_nodes [number of nodes] \
--secure
# Helper
python -m TLpp.helper \
--orch_host [Orchestrator_IP] --orch_port [Orchestrator_Port]\
--node_host [Helper_IP] --node_port [Helper_Port]\
--n_nodes [number of nodes]
# Node
python -m TLpp.node \
--orch_host [Orchestrator_IP] --orch_port [Orchestrator_Port]\
--node_host [Helper_IP] --node_port [Helper_Port]\
--secureNode를 3개의 격리된 프로세스로 분리하여 커널 수준 보안을 적용한다.
# Orchestrator + Helper는 Secure Mode와 동일
# Node (--isolated 추가)
python -m TLpp.node \
--orch_host [Orchestrator_IP] --orch_port [Orchestrator_Port]\
--helper_host [Helper_IP] --helper_port [Helper_Port]\
--secure \
--isolated