텐센트 클라우드 라이트하우스 방화벽 규칙 동적 관리 API 구축

텐센트 클라우드의 라이트하우스 서비스는 방화벽 규칙을 API로 제어할 수 있어, 외부 접속이 필요한 순간에만 특정 포트를 개방하는 자동화 시스템을 구축할 수 있다. 이 글에서는 PHP 기반의 동적 방화벽 관리 엔드포인트를 만드는 방법을 살펴본다.

문제 상황

운영 환경에서는 보안을 위해 SSH(22), MySQL(3306), Redis(6379) 등의 포트를 기본적으로 차단하는 것이 일반적이다. 하지만 긴급한 원격 작업이나 데이터베이스 관리가 필요할 때마다 솔에 접속해 규칙을 수동으로 변경하는 것은 번거롭다.

해결책으로 다음과 같은 접근법들이 있지만, API 기반 자동화가 가장 유연하다:

  • 고정 IP 허용: 대부분의 사용자에게 해당하지 않음
  • 프록시 서버 활용: 추가 인프라 비용 발생
  • 동적 API 호출: 필요한 시점에만 포트 개방, 작업 완료 후 자동 차단

핵심 설계

목표는 쿼리 파라미터에 따라 방화벽 규칙을 즉시 변경하는 REST 스타일 엔드포인트다. 예를 들어 /firewall?ssh=open 호출 시 22번 포트를 현재 IP로만 허용하고, /firewall?ssh=close로 닫는 방식이다.

구현

텐센트 클라우드 SDK를 설치한다.

composer require tencentcloud/lighthouse

실제 구현에서는 요청 검증, IP 추출, 규칙 동적 생성을 분리해 처리한다. 환경 변수로 민감 정보를 관리하고, 규칙 플릿을 배열로 분리해 재사용성을 높인다.

<?php
require_once 'vendor/autoload.php';

use TencentCloud\Common\Credential;
use TencentCloud\Common\Exception\TencentCloudSDKException;
use TencentCloud\Common\Profile\ClientProfile;
use TencentCloud\Common\Profile\HttpProfile;
use TencentCloud\Lighthouse\V20200324\LighthouseClient;
use TencentCloud\Lighthouse\V20200324\Models\ModifyFirewallRulesRequest;

/* 환경 설정 */
$region = getenv('TX_REGION') ?: 'ap-seoul';
$secretId = getenv('TX_SECRET_ID');
$secretKey = getenv('TX_SECRET_KEY');
$machineId = getenv('TX_INSTANCE_ID');

/* 요청 파라미터 파싱 */
$sshFlag = $_GET['ssh'] ?? '';
$dbFlag = $_GET['db'] ?? '';
$clientIp = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';

if (empty($secretId) || empty($secretKey)) {
    http_response_code(500);
    exit('Credential not configured');
}

/* 상태 해석 */
$isSshOpen = in_array(strtolower($sshFlag), ['1', 'open', 'yes', 'true']);
$isDbOpen = in_array(strtolower($dbFlag), ['1', 'open', 'yes', 'true']);

/* 규칙이 명시되지 않은 경우 */
if ($sshFlag === '' && $dbFlag === '') {
    http_response_code(400);
    exit('No rule specified');
}

/* 기본 규칙 템플릿 */
$baseRules = [
    [
        'Action' => 'ACCEPT',
        'AppType' => 'HTTP',
        'CidrBlock' => '0.0.0.0/0',
        'FirewallRuleDescription' => 'Web server HTTP',
        'Port' => '80',
        'Protocol' => 'TCP'
    ],
    [
        'Action' => 'ACCEPT',
        'AppType' => 'HTTPS',
        'CidrBlock' => '0.0.0.0/0',
        'FirewallRuleDescription' => 'Web server HTTPS',
        'Port' => '443',
        'Protocol' => 'TCP'
    ],
    [
        'Action' => 'ACCEPT',
        'AppType' => 'ICMP',
        'CidrBlock' => '0.0.0.0/0',
        'FirewallRuleDescription' => 'Network diagnostics',
        'Port' => 'ALL',
        'Protocol' => 'ICMP'
    ]
];

/* 동적 규칙 생성 */
$dynamicRules = [];

if ($sshFlag !== '') {
    $dynamicRules[] = [
        'Action' => 'ACCEPT',
        'AppType' => 'SSH',
        'CidrBlock' => $isSshOpen ? $clientIp . '/32' : '127.0.0.1/32',
        'FirewallRuleDescription' => 'Secure shell access',
        'Port' => '22',
        'Protocol' => 'TCP'
    ];
}

if ($dbFlag !== '') {
    $dynamicRules[] = [
        'Action' => 'ACCEPT',
        'AppType' => 'MySQL',
        'CidrBlock' => $isDbOpen ? $clientIp . '/32' : '127.0.0.1/32',
        'FirewallRuleDescription' => 'Database access',
        'Port' => '3306',
        'Protocol' => 'TCP'
    ];
}

/* API 통신 */
try {
    $credential = new Credential($secretId, $secretKey);
    
    $httpConfig = new HttpProfile();
    $httpConfig->setEndpoint('lighthouse.tencentcloudapi.com');
    
    $profile = new ClientProfile();
    $profile->setHttpProfile($httpConfig);
    
    $apiClient = new LighthouseClient($credential, $region, $profile);
    
    $requestPayload = new ModifyFirewallRulesRequest();
    $requestPayload->InstanceId = $machineId;
    $requestPayload->FirewallRules = array_merge($baseRules, $dynamicRules);
    
    $response = $apiClient->ModifyFirewallRules($requestPayload);
    
    header('Content-Type: application/json');
    echo $response->toJsonString();

} catch (TencentCloudSDKException $err) {
    http_response_code(502);
    echo json_encode([
        'error' => $err->getMessage(),
        'code' => $err->getErrorCode()
    ]);
}

주요 개선 포인트

원본 코드 대비 다음과 같이 구조를 개선했다:

  • 환경 변수 분리: 하드코딩된 인증 정보 제거, getenv()로 외부화
  • IP 동적 바인딩: 0.0.0.0/0 전체 개방 대신 요청자 IP만 /32로 허용
  • 규칙 템플릉화: 고정 규칙과 동적 규칙을 분리해 가독성 향상
  • 상태 값 유연 처리: 1, open, yes 등 다양한 입력 형태 수용
  • 에러 응답 표준화: HTTP 상태 코드와 JSON 에러 본문 반환

보안 고려사항

이 엔드포인트를 운영 환경에 배포할 때는 반드시 다음을 적용해야 한다:

  1. API 키 관리: AWS Secrets Manager, HashiCorp Vault, 또는 텐센트 KMS 연동
  2. 접근 제한: Basic Auth, API Key, 또는 내부 VPN 전용 접근 설정
  3. 요청 로깅: 누가 언제 어 규칙을 변경했는지 감사 기록 보관
  4. Rate Limiting: 동일 IP의 과도한 호출 차단
  5. 자동 만료: at 파라미터로 예약 복원 기능 추가 권장

확장 방향

현재 구조를 더 발전시키면 다음과 같은 기능을 추가할 수 있다:

  • Webhook 연동: 규칙 변경 시 Slack/Discord 알림 전송
  • 스케줄링: Cron으로 일정 시간 후 자동 차단 (예: 2시간 후 SSH 차단)
  • 다중 인스턴스 지원: instance 파라미터로 대상 서버 동적 지정
  • 규칙 스냅샷: 변경 전 규칙 업으로 원릭 복원 기능

태그: PHP

9월 22일 20:50에 게시됨