본문 바로가기

개발/BACK

통제망 환경에서 외부에서 내부망 API 호출하는 서버 아키텍처 설계

728x90

통제망 환경에서 외부 클라이언트가 내부망의 API를 안전하게 호출할 수 있도록 서버 아키텍처를 설계하는 방법을 설명합니다. DMZ 구간, 리버스 프록시, API Gateway, 방화벽 규칙 등을 활용한 보안 아키텍처를 단계별로 구현합니다.

 

 

 


1. 🔐 통제망 환경 개요 및 요구사항

💡 통제망 환경의 특징

통제망 환경은 높은 보안 수준이 요구되는 네트워크 환경입니다. 내부망의 API 서버는 외부에서 직접 접근할 수 없으며, DMZ 구간을 통한 간접 접근만 허용됩니다. 이를 통해 내부망의 보안을 유지하면서도 외부 클라이언트가 필요한 API를 호출할 수 있도록 해야 합니다.

📋 요구사항

  • 외부 접근 차단: 내부망 API 서버는 외부에서 직접 접근 불가
  • 간접 접근 허용: DMZ 구간을 통한 간접 접근만 허용
  • 인증/인가: API 호출 시 인증 및 권한 검증 필수
  • 로깅 및 모니터링: 모든 API 호출에 대한 로깅 및 모니터링
  • 트래픽 제어: DDoS 공격 방지를 위한 Rate Limiting
  • SSL/TLS: 모든 통신은 암호화 필수

🏗️ 네트워크 구간

1. 외부망 (Internet): 클라이언트가 위치한 공인 네트워크

2. DMZ 구간 (Demilitarized Zone): 외부와 내부망 사이의 완충 구간

3. 내부망 (Intranet): 실제 API 서버가 위치한 보호된 네트워크

4. 방화벽: 각 구간 사이의 트래픽을 제어하는 보안 장비


2. 🏛️ 서버 아키텍처 설계

💡 전체 아키텍처 개요

외부 클라이언트가 내부망 API를 호출하기 위한 3-Tier 아키텍처를 설계합니다. 각 구간은 방화벽으로 분리되어 있으며, DMZ 구간의 리버스 프록시가 외부 요청을 받아 내부망으로 전달합니다.

📊 아키텍처 다이어그램

┌─────────────────────────────────────────────────────────┐
│                    외부망 (Internet)                      │
│  ┌──────────────┐                                        │
│  │   클라이언트  │                                        │
│  │  (외부 사용자) │                                        │
│  └──────┬───────┘                                        │
└─────────┼────────────────────────────────────────────────┘
          │ HTTPS (443)
          │
┌─────────▼────────────────────────────────────────────────┐
│              방화벽 1 (외부 → DMZ)                        │
│  - 포트 443 (HTTPS)만 허용                                │
│  - IP 화이트리스트 적용                                    │
└─────────┬────────────────────────────────────────────────┘
          │
┌─────────▼────────────────────────────────────────────────┐
│                    DMZ 구간                                │
│  ┌──────────────────────────────────────────────┐        │
│  │  리버스 프록시 (Nginx)                        │        │
│  │  - SSL/TLS 종료                                │        │
│  │  - 로드 밸런싱                                  │        │
│  │  - Rate Limiting                               │        │
│  └──────────────┬───────────────────────────────┘        │
│                 │                                          │
│  ┌──────────────▼──────────────┐                         │
│  │  API Gateway (Spring Cloud)  │                         │
│  │  - 인증/인가 처리            │                         │
│  │  - 라우팅                    │                         │
│  │  - 로깅                      │                         │
│  └──────────────┬──────────────┘                         │
└─────────────────┼─────────────────────────────────────────┘
                  │ HTTP (8080)
                  │
┌─────────────────▼─────────────────────────────────────────┐
│              방화벽 2 (DMZ → 내부망)                      │
│  - 포트 8080 (HTTP)만 허용                                │
│  - DMZ 서버 IP만 허용                                     │
└─────────────────┬─────────────────────────────────────────┘
                  │
┌─────────────────▼─────────────────────────────────────────┐
│                    내부망 (Intranet)                       │
│  ┌──────────────────────────────────────────────┐        │
│  │  API 서버 1 (Spring Boot)                    │        │
│  │  - 비즈니스 로직                             │        │
│  │  - 데이터베이스 접근                         │        │
│  └──────────────────────────────────────────────┘        │
│  ┌──────────────────────────────────────────────┐        │
│  │  API 서버 2 (Spring Boot)                    │        │
│  │  - 비즈니스 로직                             │        │
│  │  - 데이터베이스 접근                         │        │
│  └──────────────────────────────────────────────┘        │
│  ┌──────────────────────────────────────────────┐        │
│  │  데이터베이스 서버                           │        │
│  └──────────────────────────────────────────────┘        │
└───────────────────────────────────────────────────────────┘

🔑 주요 구성 요소

  • 리버스 프록시 (Nginx): 외부 요청을 받아 내부로 전달, SSL/TLS 종료
  • API Gateway: 인증/인가, 라우팅, 로깅, Rate Limiting
  • 내부 API 서버: 실제 비즈니스 로직을 처리하는 백엔드 서버
  • 방화벽: 각 구간 간 트래픽 제어 및 보안 정책 적용

3. 🛡️ DMZ 구간 구성

💡 DMZ 구간의 역할

DMZ 구간은 외부와 내부망 사이의 완충 구간으로, 외부 요청을 받아 내부망으로 전달하는 역할을 합니다. DMZ에 위치한 서버는 최소한의 서비스만 실행하여 공격 표면을 최소화합니다.

📋 DMZ 서버 구성

1. 리버스 프록시 (Nginx): 외부 요청을 받아 내부 API Gateway로 전달

2. API Gateway: 인증/인가 처리 및 라우팅

3. 로깅 서버: 모든 요청/응답 로깅

4. 모니터링 도구: 서버 상태 모니터링

🔒 DMZ 보안 정책

  • 최소 권한 원칙: 필요한 서비스만 실행
  • 정기 업데이트: 보안 패치 정기 적용
  • 접근 제어: SSH 등 관리 포트는 특정 IP만 허용
  • 로그 모니터링: 이상 징후 실시간 감지

4. 🔄 리버스 프록시 설정 (Nginx)

💡 Nginx 리버스 프록시 설정

Nginx를 리버스 프록시로 사용하여 외부 HTTPS 요청을 받아 내부 API Gateway로 전달합니다. SSL/TLS 종료, Rate Limiting, 로깅 등의 기능을 설정합니다.

📄 nginx.conf

# /etc/nginx/nginx.conf

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

events {
    worker_connections 1024;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    
    # 로그 포맷
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';
    
    access_log /var/log/nginx/access.log main;
    
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    
    # Gzip 압축
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
    
    # Rate Limiting 설정
    limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
    limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
    
    # 업스트림 서버 (API Gateway)
    upstream api_gateway {
        least_conn;
        server 192.168.10.10:8080 max_fails=3 fail_timeout=30s;
        server 192.168.10.11:8080 max_fails=3 fail_timeout=30s backup;
    }
    
    # HTTP 서버 (HTTPS로 리다이렉트)
    server {
        listen 80;
        server_name api.example.com;
        
        # HTTP를 HTTPS로 리다이렉트
        return 301 https://$server_name$request_uri;
    }
    
    # HTTPS 서버
    server {
        listen 443 ssl http2;
        server_name api.example.com;
        
        # SSL 인증서 설정
        ssl_certificate /etc/nginx/ssl/api.example.com.crt;
        ssl_certificate_key /etc/nginx/ssl/api.example.com.key;
        
        # SSL 보안 설정
        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';
        ssl_prefer_server_ciphers on;
        ssl_session_cache shared:SSL:10m;
        ssl_session_timeout 10m;
        
        # 보안 헤더
        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
        add_header X-Frame-Options "SAMEORIGIN" always;
        add_header X-Content-Type-Options "nosniff" always;
        add_header X-XSS-Protection "1; mode=block" always;
        
        # Rate Limiting 적용
        limit_req zone=api_limit burst=20 nodelay;
        limit_conn conn_limit 10;
        
        # 요청 크기 제한
        client_max_body_size 10M;
        client_body_timeout 60s;
        
        # API Gateway로 프록시
        location / {
            proxy_pass http://api_gateway;
            proxy_http_version 1.1;
            
            # 프록시 헤더 설정
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header X-Forwarded-Host $server_name;
            
            # 타임아웃 설정
            proxy_connect_timeout 60s;
            proxy_send_timeout 60s;
            proxy_read_timeout 60s;
            
            # 버퍼 설정
            proxy_buffering on;
            proxy_buffer_size 4k;
            proxy_buffers 8 4k;
            proxy_busy_buffers_size 8k;
        }
        
        # Health Check 엔드포인트
        location /health {
            access_log off;
            return 200 "healthy\n";
            add_header Content-Type text/plain;
        }
    }
}

🔧 Nginx 설치 및 실행

# Ubuntu/Debian
sudo apt-get update
sudo apt-get install nginx

# 설정 파일 복사
sudo cp nginx.conf /etc/nginx/nginx.conf

# SSL 인증서 설정 (Let's Encrypt 또는 자체 서명 인증서)
sudo mkdir -p /etc/nginx/ssl
sudo cp api.example.com.crt /etc/nginx/ssl/
sudo cp api.example.com.key /etc/nginx/ssl/

# 설정 검증
sudo nginx -t

# Nginx 재시작
sudo systemctl restart nginx
sudo systemctl enable nginx

# 상태 확인
sudo systemctl status nginx

5. 🚪 API Gateway 구현

💡 Spring Cloud Gateway 구현

Spring Cloud Gateway를 사용하여 인증/인가, 라우팅, 로깅 등의 기능을 구현합니다. API Gateway는 DMZ 구간에 위치하며, 내부망의 API 서버로 요청을 전달합니다.

📦 build.gradle

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.1.0'
    id 'io.spring.dependency-management' version '1.1.0'
}

ext {
    set('springCloudVersion', "2022.0.3")
}

dependencies {
    // Spring Cloud Gateway
    implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
    
    // JWT 인증
    implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
    implementation 'io.jsonwebtoken:jjwt-impl:0.11.5'
    implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5'
    
    // Redis (Rate Limiting용)
    implementation 'org.springframework.boot:spring-boot-starter-data-redis-reactive'
    
    // 로깅
    implementation 'net.logstash.logback:logstash-logback-encoder:7.3'
    
    // Actuator (모니터링)
    implementation 'org.springframework.boot:spring-boot-starter-actuator'
}

dependencyManagement {
    imports {
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
    }
}

⚙️ application.yml

server:
  port: 8080

spring:
  application:
    name: api-gateway
  
  cloud:
    gateway:
      routes:
        # 사용자 API
        - id: user-api
          uri: http://192.168.20.10:8081
          predicates:
            - Path=/api/users/**
          filters:
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 10
                redis-rate-limiter.burstCapacity: 20
            - StripPrefix=1
        
        # 주문 API
        - id: order-api
          uri: http://192.168.20.11:8082
          predicates:
            - Path=/api/orders/**
          filters:
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 5
                redis-rate-limiter.burstCapacity: 10
            - StripPrefix=1
        
        # 상품 API
        - id: product-api
          uri: http://192.168.20.12:8083
          predicates:
            - Path=/api/products/**
          filters:
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 20
                redis-rate-limiter.burstCapacity: 40
            - StripPrefix=1
      
      globalcors:
        cors-configurations:
          '[/**]':
            allowedOrigins: "*"
            allowedMethods:
              - GET
              - POST
              - PUT
              - DELETE
              - OPTIONS
            allowedHeaders: "*"
            allowCredentials: true

  # Redis 설정
  data:
    redis:
      host: 192.168.10.20
      port: 6379
      password: ${REDIS_PASSWORD}
      timeout: 2000ms

# JWT 설정
jwt:
  secret: ${JWT_SECRET}
  expiration: 3600000  # 1시간

# 로깅
logging:
  level:
    org.springframework.cloud.gateway: DEBUG
    org.springframework.web: INFO
  pattern:
    console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"

🔐 JWT 인증 필터

package com.example.gateway.filter;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;

@Slf4j
@Component
public class JwtAuthenticationFilter extends AbstractGatewayFilterFactory {
    
    @Value("${jwt.secret}")
    private String jwtSecret;
    
    public JwtAuthenticationFilter() {
        super(Config.class);
    }
    
    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();
            
            // Health check 엔드포인트는 인증 제외
            if (request.getURI().getPath().equals("/health")) {
                return chain.filter(exchange);
            }
            
            // Authorization 헤더 확인
            String authHeader = request.getHeaders().getFirst("Authorization");
            
            if (authHeader == null || !authHeader.startsWith("Bearer ")) {
                return onError(exchange, "Missing or invalid Authorization header", HttpStatus.UNAUTHORIZED);
            }
            
            String token = authHeader.substring(7);
            
            try {
                // JWT 토큰 검증
                SecretKey key = Keys.hmacShaKeyFor(jwtSecret.getBytes(StandardCharsets.UTF_8));
                Claims claims = Jwts.parserBuilder()
                        .setSigningKey(key)
                        .build()
                        .parseClaimsJws(token)
                        .getBody();
                
                // 사용자 정보를 헤더에 추가
                ServerHttpRequest modifiedRequest = request.mutate()
                        .header("X-User-Id", claims.getSubject())
                        .header("X-User-Role", claims.get("role", String.class))
                        .build();
                
                log.info("JWT 인증 성공: User ID = {}", claims.getSubject());
                
                return chain.filter(exchange.mutate().request(modifiedRequest).build());
                
            } catch (Exception e) {
                log.error("JWT 인증 실패: {}", e.getMessage());
                return onError(exchange, "Invalid JWT token", HttpStatus.UNAUTHORIZED);
            }
        };
    }
    
    private Mono onError(ServerWebExchange exchange, String message, HttpStatus status) {
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(status);
        response.getHeaders().add("Content-Type", "application/json");
        
        String errorBody = String.format("{\"error\": \"%s\"}", message);
        return response.writeWith(
                Mono.just(response.bufferFactory().wrap(errorBody.getBytes()))
        );
    }
    
    public static class Config {
        // 필터 설정 (필요시 추가)
    }
}

📄 Gateway 설정 클래스

package com.example.gateway.config;

import com.example.gateway.filter.JwtAuthenticationFilter;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class GatewayConfig {
    
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder, JwtAuthenticationFilter jwtFilter) {
        return builder.routes()
                .route("user-api", r -> r
                        .path("/api/users/**")
                        .filters(f -> f
                                .filter(jwtFilter.apply(new JwtAuthenticationFilter.Config()))
                                .stripPrefix(1))
                        .uri("http://192.168.20.10:8081"))
                
                .route("order-api", r -> r
                        .path("/api/orders/**")
                        .filters(f -> f
                                .filter(jwtFilter.apply(new JwtAuthenticationFilter.Config()))
                                .stripPrefix(1))
                        .uri("http://192.168.20.11:8082"))
                
                .build();
    }
}

6. 🔒 보안 설정 및 방화벽 규칙

💡 방화벽 규칙 설정

각 구간 사이의 트래픽을 제어하는 방화벽 규칙을 설정합니다. 최소 권한 원칙에 따라 필요한 포트와 IP만 허용합니다.

🛡️ 방화벽 1: 외부 → DMZ

# iptables 규칙 예제 (외부 → DMZ)

# 기본 정책: 모든 트래픽 차단
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT

# Loopback 허용
iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT

# ESTABLISHED, RELATED 연결 허용
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

# HTTPS (443) 허용 (특정 IP만 허용하는 경우)
iptables -A INPUT -p tcp --dport 443 -s 0.0.0.0/0 -j ACCEPT

# 또는 특정 IP만 허용
# iptables -A INPUT -p tcp --dport 443 -s 203.0.113.0/24 -j ACCEPT

# SSH (22) - 특정 IP만 허용 (관리용)
iptables -A INPUT -p tcp --dport 22 -s 192.0.2.0/24 -j ACCEPT

# ICMP 허용 (ping)
iptables -A INPUT -p icmp -j ACCEPT

# 로깅 (차단된 패킷)
iptables -A INPUT -j LOG --log-prefix "BLOCKED: "
iptables -A INPUT -j DROP

🛡️ 방화벽 2: DMZ → 내부망

# iptables 규칙 예제 (DMZ → 내부망)

# 기본 정책: 모든 트래픽 차단
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT

# Loopback 허용
iptables -A INPUT -i lo -j ACCEPT

# ESTABLISHED, RELATED 연결 허용
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

# HTTP (8080) - DMZ 서버 IP만 허용
iptables -A INPUT -p tcp --dport 8080 -s 192.168.10.10 -j ACCEPT
iptables -A INPUT -p tcp --dport 8080 -s 192.168.10.11 -j ACCEPT

# 데이터베이스 포트 (3306) - 내부 API 서버만 허용
iptables -A INPUT -p tcp --dport 3306 -s 192.168.20.10 -j ACCEPT
iptables -A INPUT -p tcp --dport 3306 -s 192.168.20.11 -j ACCEPT

# Redis 포트 (6379) - DMZ 서버만 허용
iptables -A INPUT -p tcp --dport 6379 -s 192.168.10.10 -j ACCEPT

# SSH (22) - 관리자 IP만 허용
iptables -A INPUT -p tcp --dport 22 -s 192.168.1.0/24 -j ACCEPT

# 로깅
iptables -A INPUT -j LOG --log-prefix "INTERNAL-BLOCKED: "
iptables -A INPUT -j DROP

🔐 추가 보안 설정

1. IP 화이트리스트: 특정 IP만 API 호출 허용

2. API 키 인증: 클라이언트별 API 키 발급 및 검증

3. Rate Limiting: DDoS 공격 방지를 위한 요청 제한

4. 로깅 및 모니터링: 모든 요청/응답 로깅 및 이상 징후 감지

5. 정기 보안 점검: 취약점 스캔 및 보안 패치 적용


7. 🛠️ 실제 구현 예제

💡 클라이언트 예제 코드

외부 클라이언트에서 내부망 API를 호출하는 예제 코드입니다. JWT 토큰을 포함하여 인증된 요청을 보냅니다.

📄 JavaScript 클라이언트 예제

// API 클라이언트 예제

const API_BASE_URL = 'https://api.example.com';

// JWT 토큰 저장
let jwtToken = localStorage.getItem('jwt_token');

// API 호출 함수
async function callInternalAPI(endpoint, method = 'GET', data = null) {
    const headers = {
        'Content-Type': 'application/json',
    };
    
    // JWT 토큰이 있으면 헤더에 추가
    if (jwtToken) {
        headers['Authorization'] = `Bearer ${jwtToken}`;
    }
    
    const options = {
        method: method,
        headers: headers,
    };
    
    if (data && method !== 'GET') {
        options.body = JSON.stringify(data);
    }
    
    try {
        const response = await fetch(`${API_BASE_URL}${endpoint}`, options);
        
        // 토큰 만료 시 재인증
        if (response.status === 401) {
            await refreshToken();
            return callInternalAPI(endpoint, method, data);
        }
        
        if (!response.ok) {
            throw new Error(`HTTP error! status: ${response.status}`);
        }
        
        return await response.json();
    } catch (error) {
        console.error('API 호출 실패:', error);
        throw error;
    }
}

// 토큰 갱신
async function refreshToken() {
    // 토큰 갱신 로직
    const newToken = await authenticate();
    jwtToken = newToken;
    localStorage.setItem('jwt_token', newToken);
}

// 사용 예제
async function getUserInfo(userId) {
    const userInfo = await callInternalAPI(`/api/users/${userId}`);
    console.log('사용자 정보:', userInfo);
}

async function createOrder(orderData) {
    const order = await callInternalAPI('/api/orders', 'POST', orderData);
    console.log('주문 생성:', order);
}

📄 cURL 테스트 예제

# 1. JWT 토큰 발급 (인증 서버)
curl -X POST https://api.example.com/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username": "user", "password": "password"}'

# 응답: {"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}

# 2. 내부망 API 호출 (사용자 정보 조회)
curl -X GET https://api.example.com/api/users/123 \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

# 3. 내부망 API 호출 (주문 생성)
curl -X POST https://api.example.com/api/orders \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{"productId": 456, "quantity": 2}'

# 4. Health Check
curl https://api.example.com/health

📊 트래픽 흐름도

1. 클라이언트 요청
   ↓
   HTTPS (443) + JWT 토큰
   ↓
2. 방화벽 1 (외부 → DMZ)
   - 포트 443 허용
   - IP 화이트리스트 검증
   ↓
3. Nginx 리버스 프록시 (DMZ)
   - SSL/TLS 종료
   - Rate Limiting
   - 로깅
   ↓
   HTTP (8080) + JWT 토큰
   ↓
4. API Gateway (DMZ)
   - JWT 토큰 검증
   - 인증/인가 처리
   - 라우팅 결정
   ↓
   HTTP (8080)
   ↓
5. 방화벽 2 (DMZ → 내부망)
   - 포트 8080 허용
   - DMZ 서버 IP만 허용
   ↓
6. 내부 API 서버 (내부망)
   - 비즈니스 로직 처리
   - 데이터베이스 접근
   ↓
7. 응답 반환 (역순)

✅ 마무리

통제망 환경에서 외부 클라이언트가 내부망 API를 안전하게 호출할 수 있도록 서버 아키텍처를 설계했습니다. DMZ 구간, 리버스 프록시, API Gateway, 방화벽 규칙 등을 활용하여 보안을 유지하면서도 필요한 기능을 제공할 수 있습니다.

주요 포인트:

  • 3-Tier 아키텍처: 외부망 → DMZ → 내부망 구조로 보안 강화
  • 리버스 프록시: Nginx로 SSL/TLS 종료 및 로드 밸런싱
  • API Gateway: 인증/인가, 라우팅, Rate Limiting 처리
  • 방화벽 규칙: 최소 권한 원칙에 따른 포트 및 IP 제한
  • JWT 인증: 토큰 기반 인증으로 보안 강화

💡 참고: 실제 운영 환경에서는 보안 정책에 따라 추가적인 보안 조치(IP 화이트리스트, API 키 인증, WAF 등)를 적용해야 합니다. 또한 정기적인 보안 점검과 모니터링이 필수적입니다.

 

카카오톡 오픈채팅 링크

https://open.kakao.com/o/seCteX7h


 

728x90