#Liberty

[WebSphere] Liberty Cluster: End-to-End 구축 및 구성 가이드

Summary: WebSphere Liberty Profile(WLP)의 Collective 및 Cluster 기능을 활용한 인프라 구축 가이드입니다. Controller 구성부터 Member 조인, 클러스터링 설정 및 트러블슈팅까지의 전체 과정을 다룹니다.

WebSphere Application Server Liberty Profile(WLP)은 경량화된 구조와 확장성 덕분에 채택률이 높아지고 있습니다. 본 포스트는 WLP Collective와 Clustering 기능을 사용하여 확장 가능한 토폴로지를 구축하는 방법을 단계별로 정리한 엔지니어링 노트입니다.

이 시리즈는 다음 순서로 진행됩니다.

  • How to Create and Configure WebSphere Liberty Cluster End-to-End (Current)
  • How to Deploy Application in WebSphere Liberty Cluster
  • How to Setup Front-End Web Server for WebSphere Liberty Cluster

1. Topology Architecture

이 가이드에서는 Collective Controller 1대와 Collective/Cluster Member 2대로 구성된 토폴로지를 구현합니다. 프론트엔드에는 IBM HTTP Server(IHS)가 배치되며 별도의 배포 서버가 존재하는 구조입니다.

2. Prerequisites & WLP Installation

본 가이드는 WLP 17.0.2CentOS Linux 7.3 환경을 기준으로 작성되었습니다. 설치 전 지원되는 Java 버전이 설치되어 있는지 확인이 필요합니다.

Check System Environment

$> cat /etc/redhat-release
CentOS Linux release 7.3.1611 (Core)

$> ./java -version
java version "1.8.0"
Java(TM) SE Runtime Environment (build pxa6480sr4fp5-20170421_01(SR4 FP5))
IBM J9 VM (build 2.8, JRE 1.8.0 Linux amd64-64 Compressed References 20170419_344392)

Base Installation (Machine: 02)

먼저 Controller가 될 머신(Machine: 02)에 WLP를 설치하고 필요한 기능을 추가한 뒤, 이를 패키징하여 Member 서버들(Machine: 03, 04)로 배포하는 전략을 사용합니다.

# Create directory
$> sudo mkdir -p /opt/ibm

# Change ownership
$> sudo chown -R wasadmin:wasgrp /opt/ibm

# Install WLP
$> java -jar wlp-17.0.0.2-all.jar --acceptLicense /opt/ibm

# Verify version
$> cd /opt/ibm/wlp/bin
$> ./productInfo version
Product name: WebSphere Application Server
Product version: 17.0.0.2

Install Required Features

Collective, Cluster, SSL, JMX Connector 등의 필수 기능을 설치합니다.

$> ./installUtility install collectiveController-1.0 collectiveMember-1.0 clusterMember-1.0 websocket-1.1 restConnector-2.0 ssl-1.0 localConnector-1.0 adminCenter-1.0

Package and Distribute

설치된 환경을 wlp_install.jar로 패키징하여 다른 노드에 복제합니다.

# Create defaultServer for packaging context
$> ./server create

# Package server including all binaries
$> ./server package defaultServer --archive=/tmp/wlp_install.jar --include=all
Server defaultServer package complete in /tmp/wlp_install.jar.

생성된 wlp_install.jar를 Machine 03, 04로 전송한 후 동일하게 설치를 진행합니다.

# On Machine 03 & 04
$> sudo mkdir -p /opt/ibm
$> chown -R wasadmin:wasgrp /opt/ibm
$> java -jar wlp_install.jar --acceptLicense /opt/ibm

3. Setup Collective Controller (Machine: 02)

Controller 서버(wlpCntlr)를 생성하고 구성을 초기화합니다.

# Create server
$> ./server create wlpCntlr

# Initialize Collective Controller
$> ./collective create wlpCntlr --keystorePassword=<password> --createConfigFile=/opt/ibm/wlp/usr/servers/wlpCntlr/wlpcntlr_include.xml

위 명령어를 수행하면 인증서 생성 및 wlpcntlr_include.xml 설정 파일이 생성됩니다. 이후 server.xml과 include 파일을 다음과 같이 수정합니다.

Configuration: server.xml (Controller)

<server description="CollectiveController">
    <featureManager>
        <feature>adminCenter-1.0</feature>
        <feature>websocket-1.1</feature>
        <feature>restConnector-1.0</feature>
        <feature>localConnector-1.0</feature>
    </featureManager>

    <!-- Include generated config -->
    <include location="${server.config.dir}/wlpcntlr_include.xml" />

    <httpEndpoint id="defaultHttpEndpoint" httpPort="9080" httpsPort="9443" host="*" />
</server>

Configuration: wlpcntlr_include.xml

자동 생성된 파일에서 quickStartSecurity 부분을 본인의 계정 정보로 수정합니다.

<quickStartSecurity userName="wasadmin" userPassword="{xor}EncryptedPassword..." />

Firewall Configuration

CentOS 방화벽에서 9080, 9443 포트를 허용해야 합니다.

$> sudo firewall-cmd --zone=public --permanent --add-port=9443/tcp
$> sudo firewall-cmd --zone=public --permanent --add-port=9080/tcp
$> sudo firewall-cmd --reload

Start Controller

$> ./server start wlpCntlr

로그(messages.log)에서 CWWKX6011I: The collective controller is ready 메시지를 확인합니다. Admin Center(https://hostname:9443/adminCenter/) 접속도 가능해야 합니다.

4. Setup Collective & Cluster Members

Machine 03과 04에서 멤버 서버를 생성하고 Controller에 Join 시킵니다.

Create & Join Member (Machine: 03)

# Create Server
$> ./server create wlpSrv01

# Join Collective
$> ./collective join wlpSrv01 \
  --host=waslibctlr01 \
  --port=9443 \
  --user=wasadmin \
  --password=<password> \
  --keystorePassword=<password> \
  --createConfigFile=/opt/ibm/wlp/usr/servers/wlpSrv01/wlpsrv01_include.xml

SSL Handshake 과정에서 인증서를 신뢰하겠냐는 프롬프트에 y를 입력합니다.

Configuration: server.xml (Member)

Member 서버의 server.xml에 Cluster 기능을 추가하고, Controller가 배포 관리를 할 수 있도록 remoteFileAccess를 설정합니다.

<server description="Cluster Member">
    <featureManager>
        <feature>webProfile-7.0</feature>
        <feature>restConnector-1.0</feature>
        <feature>localConnector-1.0</feature>
        <!-- Added for Clustering -->
        <feature>clusterMember-1.0</feature>
    </featureManager>

    <include location="${server.config.dir}/wlpsrv01_include.xml" />

    <!-- Define Cluster Name -->
    <clusterMember name="wlpCluster"/>

    <httpEndpoint id="defaultHttpEndpoint" httpPort="9081" httpsPort="9444" host="*" />

    <!-- Write Access for Controller -->
    <remoteFileAccess>
        <writeDir>${server.config.dir}</writeDir>
    </remoteFileAccess>
</server>

Machine: 04 (wlpSrv02)에 대해서도 위 과정을 동일하게 반복합니다.

Security Considerations (LTPA)

클러스터 환경에서 세션 공유 및 보안을 위해 모든 멤버는 동일한 LTPA 키를 사용해야 합니다. 한 서버에서 생성된 ltpa.keys 파일을 다른 멤버 서버들의 동일한 경로(${server.ouput.dir}/resources/security/)로 복사합니다.

5. Start Members & Verification

각 노드에서 멤버 서버를 시작합니다.

$> ./server start wlpSrv01  # On Machine 03
$> ./server start wlpSrv02  # On Machine 04

로그 파일에서 다음 메시지들을 확인하여 정상 구동을 검증합니다.

  • CWWKX8112I: Collective Repository에 호스트 정보 게시 성공.
  • CWWKX7400I: ClusterMember MBean 활성화 (클러스터 조인 성공).

6. Troubleshooting Notes

설정 과정에서 자주 발생하는 오류와 해결 방법입니다.

  • CWWKX0229E (401 Unauthorized / 403 Forbidden)
    collective join 시 인증 실패. quickStartSecurity의 계정 정보가 일치하는지 확인하십시오. 403 에러의 경우 해당 사용자가 administrator-role을 가지고 있는지 확인해야 합니다.
  • CWWKS9582E (SSL unresolved)
    IIOP 보안 설정 시 SSL 참조 오류. server.xml에 SSL 구성 및 KeyStore 정의가 명확한지 확인하십시오.
  • CWWKO0221E / CWWKS9580E (Port in use)
    한 호스트에 여러 인스턴스를 띄울 경우 JMS 포트(7276)나 IIOP 포트(2809) 충돌이 발생할 수 있습니다. wasJmsEndpointiiopEndpoint 설정을 통해 포트를 변경해야 합니다.

Next Step: 클러스터 구성이 완료되었습니다. 다음 포스트에서는 이 클러스터에 애플리케이션을 배포하는 방법을 다룹니다.

Open Stream →
#tomcat

[Tomcat] 주요 설정 파일(server.xml, web.xml) 완벽 분석 및 필수 튜닝 포인트

Apache Tomcat의 동작을 제어하는 핵심 설정 파일들은 모두 /conf 디렉토리에 위치합니다. 가장 중요한 server.xml(포트, 커넥터)과 web.xml(세션, MIME)의 역할과 자주 사용하는 설정 변경 가이드를 정리합니다.

1. 설정 파일 개요 (Configuration Overview)

Tomcat 설치 경로 내 conf 디렉토리에는 다음과 같은 주요 XML 파일들이 존재합니다.

파일명 주요 역할 및 설명
server.xml Tomcat의 메인 설정 파일입니다.
- HTTP/AJP 포트 설정
- 가상 호스트(Host) 및 Context 설정
- 쓰레드 풀(Thread Pool) 관리
web.xml 모든 웹 애플리케이션의 기본 속성을 정의합니다.
- 세션 타임아웃(Session Timeout) 설정
- MIME 타입 정의, 기본 서블릿 매핑
context.xml 웹 애플리케이션의 Context 설정을 담당합니다.
- JNDI 리소스(DB Connection Pool) 설정 시 주로 사용
tomcat-users.xml Tomcat Manager/Admin 페이지 접근 계정을 관리합니다.

2. server.xml 주요 설정 가이드

엔지니어가 가장 빈번하게 수정하는 파일입니다. 포트 충돌 해결이나 인코딩 처리를 위해 필수적으로 확인해야 합니다.

1) HTTP 포트 변경 및 인코딩 설정

기본 8080 포트를 80으로 변경하거나, 한글 깨짐 방지를 위해 URIEncoding을 설정합니다.

<!-- 기본 설정 -->
<Connector port="8080" protocol="HTTP/1.1" 
           connectionTimeout="20000" 
           redirectPort="8443" />

<!-- 튜닝 설정 예시 -->
<Connector port="80" protocol="HTTP/1.1" 
           connectionTimeout="20000" 
           redirectPort="8443"
           URIEncoding="UTF-8" />
Tip: GET 방식으로 넘어오는 파라미터의 한글이 깨진다면 URIEncoding="UTF-8" 속성이 누락되었을 가능성이 큽니다.

2) AJP 포트 설정 (웹 서버 연동 시)

Apache HTTP Server와 연동할 때 사용하는 AJP 프로토콜 포트입니다. 사용하지 않는다면 주석 처리하여 보안을 강화하는 것이 좋습니다.

<!-- 기본값 8009 -->
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

3. web.xml 주요 설정 가이드

모든 애플리케이션에 적용되는 전역(Global) 설정을 담당합니다.

1) 세션 타임아웃 (Session Timeout)

사용자 세션 만료 시간을 설정합니다. (단위: 분)

<session-config>
    <session-timeout>30</session-timeout>
</session-config>

2) 디렉토리 리스팅 비활성화 (보안)

특정 디렉토리 접근 시 파일 목록이 노출되는 것을 방지하기 위해 listings 값을 false로 설정해야 합니다.

<servlet>
    <servlet-name>default</servlet-name>
    <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
    <init-param>
        <param-name>debug</param-name>
        <param-value>0</param-value>
    </init-param>
    <init-param>
        <param-name>listings</param-name>
        <param-value>false</param-value> <!-- true면 파일 목록 노출됨 -->
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

4. tomcat-users.xml (Manager App)

http://localhost:8080/manager 페이지에 접속하여 GUI로 애플리케이션을 배포하거나 중지하려면 계정 설정이 필요합니다.

<tomcat-users>
  <!-- 역할 정의 -->
  <role rolename="manager-gui"/>
  <role rolename="admin-gui"/>
  
  <!-- 사용자 정의 -->
  <user username="admin" password="password" roles="manager-gui,admin-gui"/>
</tomcat-users>
주의: Manager App은 강력한 권한을 가지므로, 운영 환경에서는 외부 IP 접근을 차단하거나 해당 기능을 비활성화하는 것을 권장합니다.

Next Step:
설정 파일 수정 후에는 반드시 Tomcat을 재기동해야 적용됩니다. 다음 단계에서는 실제 운영 환경에서 성능을 최적화하기 위한 JVM 메모리 설정(Heap Size)과 GC 튜닝 방법에 대해 알아보겠습니다.

Open Stream →
#Liberty

[IHS/Liberty] 보안 취약점 조치: X-Powered-By 헤더 숨김 및 정보 노출 방지 가이드

IBM HTTP Server(IHS)와 WebSphere Liberty 환경에서 X-Powered-By 헤더(예: Servlet/3.1) 노출을 차단하는 방법을 정리합니다. 보안 강화를 위해 웹 서버(IHS) 단에서의 필터링과 WAS(Liberty) 단에서의 생성 금지 설정을 모두 적용하는 것을 권장합니다.

0. 배경 및 전략 (Context)

보안 취약점 조치 시, 정보 노출 방지는 다계층 방어(Defense in Depth)가 중요합니다.

계층 역할 및 중요성
1. IHS (Web Server) [필수] 최전방 방어선. 백엔드 WAS가 무엇이든 상관없이 클라이언트로 나가는 모든 응답에서 헤더를 강제 삭제합니다.
2. Liberty (WAS) [권장] 소스 차단. 내부망에서 WAS로 직접 접속하는 경우나 웹 서버 설정을 우회하는 경우를 대비해 헤더 생성 자체를 막습니다.

Test Environment

  • Web Server: IBM HTTP Server v9.0 (Apache 2.4 Base)
  • WAS: WebSphere Liberty Core 20.0.x

1. IBM HTTP Server (IHS) 설정

Apache 기반인 IHS에서는 mod_headers 모듈을 사용하여 응답 헤더를 제어합니다.

httpd.conf 수정

설정 파일(httpd.conf)을 열어 아래 내용을 적용합니다.

# 1. 모듈 로드 확인 (주석 해제 필수)
LoadModule headers_module modules/mod_headers.so

# 2. 헤더 제거 설정 (Global 영역 또는 VirtualHost 내부에 작성)
<IfModule mod_headers.c>
    # 보안 조치: 기술 스택 정보 숨김
    Header unset X-Powered-By
    
    # (선택) 추가적인 정보 노출 헤더 차단
    Header unset X-AspNet-Version
    Header unset X-Runtime
</IfModule>

# 3. 서버 버전 정보 최소화 (OS 정보 등 숨김)
ServerTokens Prod
Tip: 설정 후에는 반드시 ./apachectl -t로 문법을 검사하고 재기동(restart 또는 graceful)해야 합니다.

2. WebSphere Liberty 설정

Liberty는 server.xml 파일 하나로 대부분의 설정을 처리합니다. webContainer 요소를 추가하거나 수정하여 헤더 생성을 비활성화합니다.

server.xml 수정

<server description="Liberty Server">

    <!-- Feature Manager (기본 설정) -->
    <featureManager>
        <feature>servlet-3.1</feature>
    </featureManager>

    <!-- [보안 조치] X-Powered-By 헤더 비활성화 속성 추가 -->
    <webContainer disableXPoweredBy="true" />

</server>

Liberty는 동적 설정을 지원하므로 파일 저장 시 즉시 반영되지만, 운영 환경에서는 확실한 적용을 위해 서버 재기동을 권장합니다.


3. 검증 (Verification)

curl 명령어를 사용하여 조치 전후의 응답 헤더를 비교합니다.

조치 전 (Before)

HTTP/1.1 200 OK
X-Powered-By: Servlet/3.1
Server: IBM_HTTP_Server/9.0.5...
...

조치 후 (After)

curl -I http://localhost:80/
HTTP/1.1 200 OK
Server: IBM_HTTP_Server   <-- (Prod 설정으로 버전 숨김)
Content-Type: text/html
...                       <-- (X-Powered-By 헤더 삭제됨)

Next Step:
헤더 조치가 완료되었다면, HTTP 메소드 제한(GET, POST 외 차단)SSL/TLS 프로토콜 버전(TLS 1.2 Only) 설정을 통해 웹 서비스 보안을 한 단계 더 강화해 보십시오.

Open Stream →
#apache

[Apache/IHS] 서버 성능 튜닝의 핵심: MaxRequestWorkers 계산법 및 MPM 설정 완벽 가이드

"사용자가 몰리면 서버가 응답이 없어요." 이런 문제의 90%는 동시 접속자 처리 설정인 MPM(Multi-Processing Module) 튜닝으로 해결됩니다. 물리 메모리 한계 내에서 최대 성능을 끌어내는 MaxRequestWorkers 설정법과 ServerLimit의 관계를 단계별로 정리합니다.

0. 튜닝의 핵심 공식 (The Formula)

튜닝은 '감'으로 하는 것이 아닙니다. 메모리 부족으로 인한 스왑(Swap) 발생을 막는 것이 최우선 목표이며, 이는 정확한 계산에서 시작됩니다.

MaxRequestWorkers = (총 RAM - OS/DB 사용 RAM) / (Apache 프로세스 1개의 평균 메모리)

1. 3단계 계산법: 내 서버의 한계값 찾기

Step 1: Apache 프로세스 평균 메모리 측정

먼저, 현재 구동 중인 httpd(또는 apache2) 프로세스 하나가 실제로 사용하는 메모리(RSS)의 평균을 구합니다.

# SSH 접속 후 실행 (결과 단위: MB)
ps -ylC httpd --sort:rss | awk '{sum+=$8; ++n} END {print "Average RSS: " sum/n/1024 " MB"}'

(예시 결과: 45.5 MB)

Step 2: Apache 가용 RAM 산정

서버의 전체 메모리에서 OS와 다른 애플리케이션(DB 등)이 사용하는 메모리를 제외합니다.

# 전체 메모리 확인
free -m

(예시: 16GB 서버에서 OS/DB가 6GB 사용 중 -> Apache용 가용 메모리 10GB (10,240 MB))

Step 3: 최종 설정값 도출

위에서 구한 값을 공식에 대입합니다.

  • 계산: 10,240 MB / 45.5 MB = 225.05
  • 결론: 소수점은 버리고 225MaxRequestWorkers 값으로 선정합니다.

2. 보이지 않는 벽: Limit 지시어의 이해

MaxRequestWorkers 값만 높인다고 끝이 아닙니다. 이 값은 상위 제한(Hard Limit) 설정인 ServerLimitThreadLimit 안에서만 유효합니다.

  • 규칙: MaxRequestWorkers ≤ (ServerLimit × ThreadsPerChild)

만약 계산된 값이 기본 한계(보통 ServerLimit 16)를 초과한다면, 반드시 설정 파일에 ServerLimit을 명시해야 합니다.


3. 튜닝 전략: 안정성 vs 효율성

Event/Worker MPM을 사용할 때, 성능을 높이는 방향은 두 가지입니다.

구분 ServerLimit 증가 (프로세스 ↑) ThreadsPerChild 증가 (스레드 ↑)
안정성 높음 (하나가 죽어도 나머지는 생존) 낮음 (스레드 하나가 죽으면 프로세스 전체 다운)
메모리 효율 낮음 (독립 메모리 필요) 높음 (메모리 공유)
권장 ✅ 적극 권장 ⚠️ 신중한 접근 필요 (보통 25~64 고정)

4. 최종 설정 예시 (httpd.conf)

위의 계산 결과(MaxRequestWorkers 1000 가정)를 바탕으로 한 Event MPM 최종 설정 예시입니다.

<IfModule mpm_event_module>
    # 1. 스레드 수는 안정적인 값으로 고정 (25)
    ThreadsPerChild         25

    # 2. 필요한 프로세스 수 계산 (1000 / 25 = 40)
    # 기본값(16)보다 크므로 반드시 명시해야 함
    ServerLimit             40

    # 3. 목표 동시 처리 수 (40 * 25 = 1000)
    MaxRequestWorkers       1000

    # 4. 기타 프로세스 관리 옵션
    StartServers            4
    MinSpareThreads         75
    MaxSpareThreads         250
    MaxConnectionsPerChild  0
</IfModule>
Check Point: 설정을 마친 후에는 반드시 apachectl -t 또는 httpd -t 명령어로 문법 오류가 없는지 확인하고 재기동해야 합니다.
Open Stream →
#mcp

[Node.js] Windows npm 실행 오류 해결: PowerShell 보안 정책(PSSecurityException) 설정 가이드

Windows 환경에서 npm 명령어를 실행할 때 발생하는 "이 시스템에서 스크립트를 실행할 수 없으므로..."(PSSecurityException) 오류를 해결합니다. PowerShell의 실행 정책(Execution Policy)을 이해하고, 보안을 유지하면서 npm을 사용할 수 있도록 RemoteSigned 정책을 적용하는 방법을 정리합니다.

1. 문제 현상 (Issue)

Node.js 설치 후 터미널(VS Code 또는 PowerShell)에서 npm 명령어를 실행하면 다음과 같은 빨간색 에러 메시지가 출력되며 실행이 차단됩니다.

에러 메시지

npm : 이 시스템에서 스크립트를 실행할 수 없으므로 C:\Program Files\nodejs\npm.ps1 파일을 로드할 수 없습니다.
위치 줄:1 문자:1
+ npm install
+ ~~~
    + CategoryInfo          : 보안 오류: (:) [], PSSecurityException
    + FullyQualifiedErrorId : UnauthorizedAccess

2. 원인 분석 (Root Cause)

이것은 npm의 문제가 아니라 Windows PowerShell의 보안 정책 때문입니다.

  • Restricted (기본값): Windows 클라이언트 OS의 기본 정책으로, 모든 스크립트 파일(.ps1)의 실행을 차단합니다.
  • npm 명령어는 내부적으로 npm.ps1 스크립트를 실행하려다 이 정책에 막히게 됩니다.

3. 해결 방법 (Solution)

보안 정책을 변경하여 스크립트 실행을 허용해야 합니다. 보안과 편의성의 균형을 위해 RemoteSigned 정책을 현재 사용자(CurrentUser)에게만 적용하는 것을 권장합니다.

Step 1: 현재 정책 확인

PowerShell을 실행하고 아래 명령어를 입력합니다.

Get-ExecutionPolicy

결과가 Restricted로 나온다면 실행이 불가능한 상태입니다.

Step 2: 정책 변경 (권장 설정)

PowerShell을 관리자 권한으로 실행한 뒤, 아래 명령어를 입력합니다.

Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
정책 설명:
  • RemoteSigned: 로컬에서 생성한 스크립트는 실행 허용, 인터넷에서 다운로드한 스크립트는 서명된 것만 허용 (가장 권장되는 설정).
  • -Scope CurrentUser: 시스템 전체가 아닌 현재 로그인한 사용자에게만 적용하여 보안 리스크 최소화.

변경 확인 메시지가 나오면 Y (예)를 입력하여 승인합니다.


4. 대안 및 팁 (Alternatives)

방법 A: 일회성 허용 (임시)

정책을 영구적으로 바꾸기 부담스럽다면, 현재 열려있는 창에서만 허용할 수 있습니다.

Set-ExecutionPolicy RemoteSigned -Scope Process

방법 B: Command Prompt (cmd) 사용

PowerShell이 아닌 일반 명령 프롬프트(cmd)Git Bash에서는 해당 보안 정책의 영향을 받지 않으므로, 별도 설정 없이 npm을 사용할 수 있습니다.


5. 검증 (Verification)

설정 후 다시 버전을 확인하거나 명령어를 실행하여 에러가 사라졌는지 확인합니다.

npm --version

Next Step:
이제 npm이 정상 작동하므로, npm install을 통해 필요한 패키지를 설치하거나 npx create-react-app 등의 프로젝트 생성 명령어를 실행해 보십시오.

Open Stream →
#apache

[Apache] 보안 취약점 조치: 서버 버전 정보 숨기기 (ServerTokens, ServerSignature)

Apache 웹 서버 운영 시 기본적으로 노출되는 서버 버전(Version), 운영체제(OS), 모듈(Module) 정보를 숨겨 보안성을 높이는 방법을 정리합니다. httpd.conf 파일의 ServerTokensServerSignature 지시어를 최적화하여 정보 유출 취약점을 조치합니다.

0. 배경 및 취약점 (Context)

공격자는 Banner Grabbing 기법을 통해 대상 서버의 구체적인 버전 정보를 수집하고, 해당 버전에 알려진 취약점(CVE)을 이용해 공격을 시도합니다. 따라서 서버 정보 노출을 최소화하는 것은 보안 강화(Hardening)의 첫걸음입니다.


1. 필수 설정 (Basic Configuration)

Apache 설정 파일(httpd.conf 또는 security.conf)에서 다음 두 가지 지시어를 찾아 수정하거나 추가합니다.

1) 헤더 정보 제한 (ServerTokens)

HTTP 응답 헤더의 Server 필드에 표시되는 정보량을 제어합니다.

  • 기본값 (Full): Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips PHP/7.0 ... (모두 노출)
  • 권장값 (Prod): Apache (제품명만 노출)
# Server 헤더에 제품명(Apache)만 표시
ServerTokens Prod

2) 에러 페이지 서명 제거 (ServerSignature)

404 Not Found, 403 Forbidden 등 에러 페이지 하단에 표시되는 서버 정보를 제어합니다.

  • 기본값 (On): 에러 메시지 하단에 서버 버전과 포트 정보가 표시됨
  • 권장값 (Off): 하단 서명 라인을 제거함
# 에러 페이지 하단에 서버 정보 숨김
ServerSignature Off

2. 심화 설정 (Advanced Configuration)

위의 ServerTokens Prod 설정을 적용해도 Server: Apache라는 정보는 여전히 남습니다. 보안 감사를 위해 이 헤더조차 아예 삭제하고 싶다면 mod_headers 모듈을 사용해야 합니다.

Pre-check: 이 설정을 사용하려면 LoadModule headers_module modules/mod_headers.so 라인의 주석이 해제되어 있어야 합니다.
<IfModule mod_headers.c>
    # Server 헤더 자체를 응답에서 제거 (권장)
    Header unset Server
    
    # 또는 다른 이름으로 위장 (Security by Obscurity)
    # Header set Server "MySecureServer"
</IfModule>

3. 설정 적용 및 검증 (Verification)

서비스 재기동

# Syntax 검사
apachectl -t

# 서비스 재기동 (CentOS/RHEL)
systemctl restart httpd

# 서비스 재기동 (Ubuntu/Debian)
systemctl restart apache2

적용 확인 (curl)

curl -I 옵션을 사용하여 응답 헤더만 조회해 봅니다.

# 명령 실행
curl -I http://localhost

# [Before]
HTTP/1.1 200 OK
Server: Apache/2.4.6 (CentOS) ... (취약)

# [After 1 - Prod 적용]
Server: Apache

# [After 2 - Header unset 적용]
(Server 헤더가 아예 보이지 않음)

Next Step:
서버 정보 숨김 조치가 완료되었다면, 추가적인 보안 강화를 위해 X-Content-Type-Options, X-Frame-Options 등 보안 헤더 적용을 검토해 보십시오.

Open Stream →
#EAP7

[JBoss EAP 7] 데이터소스 패스워드 암호화 완벽 가이드: Password Vault 구성 및 적용 (Windows/Linux)

JBoss EAP 7.x 환경에서 평문으로 저장되는 데이터소스 비밀번호를 보호하기 위해 Password Vault를 구성하는 방법을 정리합니다. Windows와 Linux 환경, 그리고 Standalone과 Domain 모드 각각에 대한 설정법을 포괄하며, KeyStore 생성부터 VAULT 문자열 적용까지의 전체 프로세스를 다룹니다.

1. 프로세스 개요 (Process Overview)

Vault 구성은 크게 4단계로 진행됩니다.

  1. KeyStore 생성: 암호화 키를 저장할 물리적 파일(.keystore) 생성
  2. Vault 초기화 및 암호화: 비밀번호를 암호화하고 JBoss 설정에 필요한 파라미터 생성
  3. JBoss Vault 설정: 생성된 KeyStore와 JBoss를 연동 (standalone.xml 또는 host.xml)
  4. 데이터소스 적용: 평문 비밀번호를 암호화된 문자열(${VAULT::...})로 교체

2. KeyStore 생성 (Generate KeyStore)

Java의 keytool을 사용하여 JCEKS 형식의 키스토어를 생성합니다.

Windows 환경

keytool.exe -genseckey ^
  -alias "vault" ^
  -storetype "jceks" ^
  -keyalg "AES" ^
  -keysize "128" ^
  -storepass "passw0rd" ^
  -keypass "passw0rd" ^
  -validity "7300" ^
  -keystore "D:\app\was\JBoss\vault\vault.keystore"

Linux 환경

./keytool -genseckey \
  -alias "vault" \
  -storetype "jceks" \
  -keyalg "AES" \
  -keysize "128" \
  -storepass "passw0rd" \
  -keypass "passw0rd" \
  -validity "7300" \
  -keystore "/app/was/JBoss/vault/vault.keystore"

3. 비밀번호 암호화 실행 (Vault Tool)

JBoss `bin` 디렉토리에 있는 vault 스크립트를 사용하여 실제 DB 패스워드를 암호화합니다.

Windows (vault.bat)

vault.bat --keystore "D:\app\was\JBoss\vault\vault.keystore" ^
  --keystore-password "passw0rd" ^
  --alias "vault" ^
  --vault-block "vb" ^
  --attribute "dbpw" ^
  --sec-attr "RealDBPassword!" ^
  --enc-dir "D:\app\was\JBoss\vault" ^
  --iteration "44" ^
  --salt "JBo72ssv"

Linux (vault.sh)

./vault.sh --keystore "/app/was/JBoss/vault/vault.keystore" \
  --keystore-password "passw0rd" \
  --alias "vault" \
  --vault-block "vb" \
  --attribute "dbpw" \
  --sec-attr "RealDBPassword!" \
  --enc-dir "/app/was/JBoss/vault" \
  --iteration "44" \
  --salt "JBo72ssv"
중요 (Result Check):
명령어 실행 결과에서 아래 두 가지 값을 반드시 기록해 두어야 합니다.
1. Vault Configuration: KEYSTORE_PASSWORD 값 (예: MASK-1234abcd...)
2. Vault Block: 실제 사용될 암호화 문자열 (예: ${VAULT::vb::dbpw::1})

4. JBoss Vault 구성 등록 (Configuration)

JBoss가 위에서 만든 KeyStore를 인식할 수 있도록 설정합니다. XML을 직접 수정하거나 CLI를 사용할 수 있습니다.

Standalone Mode (standalone.xml)

<vault> 섹션을 추가합니다. (일반적으로 <extensions> 뒤쪽)

<vault>
    <vault-option name="KEYSTORE_URL" value="D:\app\was\JBoss\vault\vault.keystore"/>
    <vault-option name="KEYSTORE_PASSWORD" value="MASK-xxxx"/> <!-- Vault 실행 결과값 -->
    <vault-option name="KEYSTORE_ALIAS" value="vault"/>
    <vault-option name="SALT" value="JBo72ssv"/>
    <vault-option name="ITERATION_COUNT" value="44"/>
    <vault-option name="ENC_FILE_DIR" value="D:\app\was\JBoss\vault\"/>
</vault>

Domain Mode (host.xml 권장)

도메인 모드에서는 물리적 파일 경로(KeyStore 위치)가 서버마다 다를 수 있으므로, 각 서버의 host.xml에 설정을 추가하는 것이 일반적입니다.

<!-- host.xml의 <management> 또는 <vault> 영역 -->
<vault>
    <vault-option name="KEYSTORE_URL" value="/app/was/JBoss/vault/vault.keystore"/>
    <vault-option name="KEYSTORE_PASSWORD" value="MASK-xxxx"/>
    <vault-option name="KEYSTORE_ALIAS" value="vault"/>
    <vault-option name="SALT" value="JBo72ssv"/>
    <vault-option name="ITERATION_COUNT" value="44"/>
    <vault-option name="ENC_FILE_DIR" value="/app/was/JBoss/vault/"/>
</vault>

5. 데이터소스 비밀번호 적용

마지막으로 데이터소스 설정 파일(standalone.xml 또는 domain.xml)에서 평문 비밀번호를 Vault 문자열로 교체합니다.

<datasource jndi-name="java:jboss/datasources/ExampleDS" ...>
    ...
    <security>
        <user-name>dbuser</user-name>
        <!-- 암호화된 문자열 적용 -->
        <password>${VAULT::vb::dbpw::1}</password>
    </security>
</datasource>

Next Step:
설정이 완료되었다면 JBoss를 재기동하고 관리 콘솔에서 Test Connection을 수행하여 DB 연결이 정상적으로 이루어지는지 확인하십시오.

Open Stream →
#EAP6

[JBoss EAP 6] 도메인 모드 보안 강화: Vault를 이용한 Datasource 패스워드 암호화 및 무중단 적용 전략

JBoss EAP 6.4 도메인 모드(Domain Mode)에서 평문으로 저장된 데이터소스 비밀번호를 보호하기 위해 Vault를 구성하는 방법을 정리합니다. host.xmldomain.xml의 설정 분리 원칙을 이해하고, 이중화된 환경에서 서비스 중단 없이 적용하는 순차적 배포 전략을 다룹니다.

0. 배경 및 아키텍처 (Context)

보안 감사 요건 충족을 위해 DB 패스워드 암호화는 필수입니다. 도메인 모드에서는 다음과 같은 설정 분리가 필요합니다.

  • host.xml: 물리적인 Keystore 파일의 위치와 암호화 키 해독 설정 (Vault 선언)
  • domain.xml: 실제 암호화된 문자열을 사용하는 데이터소스 설정 (Vault 참조)

1. Vault 환경 준비 (Keystore 생성)

암호화 키를 저장할 Java Keystore를 생성합니다. JDK의 keytool을 사용합니다.

# Keystore 생성 (알고리즘: DESede 또는 AES)
keytool -genkey -alias vault -keyalg DESede -keystore vault.keystore -keysize 168 -storepass [키스토어_비밀번호] -validity 3650
Tip: 생성된 vault.keystore 파일은 마스터(Master)와 슬레이브(Slave) 서버 모두 동일한 경로(예: EAP_HOME/vault/)에 복사해 두어야 합니다.

2. Vault 초기화 및 비밀번호 암호화

JBoss에서 제공하는 스크립트를 사용하여 Vault를 초기화하고, 실제 DB 패스워드를 암호화된 문자열로 변환합니다.

대화형 모드 실행 (Interactive)

$JBOSS_HOME/bin/vault.sh

위 명령 실행 후 옵션 0 (Store a secured attribute)을 선택하여 진행하거나, 아래와 같이 CLI 파라미터로 한 번에 실행할 수도 있습니다.

# CLI 파라미터 예시
$JBOSS_HOME/bin/vault.sh \
--keystore vault.keystore \
--keystore-password [키스토어_비밀번호] \
--alias vault \
--vault-block ds_vault \
--attribute db_password \
--sec-attr [실제_DB_비밀번호] \
--enc-dir /path/to/vault/ \
--iteration 120 \
--salt 1234abcd

결과 확인: 출력되는 VAULT::ds_vault::db_password::1 형태의 문자열과, 화면에 표시되는 XML 설정 블록을 복사해 둡니다.


3. host.xml 설정 (Vault 선언)

물리적인 파일 경로를 인식해야 하는 모든 호스트 컨트롤러(Master, Slave)host.xml 파일을 수정합니다.

<!-- host.xml의 <management> 섹션 내, 또는 <vault> 섹션 -->
<vault>
    <vault-option name="KEYSTORE_URL" value="/path/to/vault/vault.keystore"/>
    <vault-option name="KEYSTORE_PASSWORD" value="MASK-..."/> <!-- 마스킹된 비밀번호 -->
    <vault-option name="KEYSTORE_ALIAS" value="vault"/>
    <vault-option name="SALT" value="1234abcd"/>
    <vault-option name="ITERATION_COUNT" value="120"/>
    <vault-option name="ENC_FILE_DIR" value="/path/to/vault/" />
</vault>

4. domain.xml 설정 (Datasource 적용)

이제 도메인 컨트롤러의 domain.xml에서 데이터소스 비밀번호 부분을 위에서 생성한 Vault 문자열로 대체합니다.

<datasource ...>
    <security>
        <user-name>dbuser</user-name>
        <password>${VAULT::ds_vault::db_password::1}</password>
    </security>
</datasource>

5. 운영 환경 적용 전략 (Deployment Strategy)

이중화(HA)된 운영 환경에서 서비스 중단을 방지하기 위해, host.xml 설정과 domain.xml 설정을 분리하여 적용하는 전략이 필요합니다.

Scenario A: 무중단 순차 적용 (Rolling Update)

Host Controller(HC) 설정인 host.xml은 재기동이 필요하지만, domain.xml 변경은 리로드(Reload)로 반영될 수 있음을 이용합니다.

  1. Slave 서버(Server2) 작업:
    • Slave HC 중지
    • Slave host.xml에 Vault 설정 추가 및 vault.keystore 파일 배포
    • Slave HC 재기동 (이때 Server2 인스턴스들도 재기동됨)
  2. Master 서버(Server1) 작업:
    • Master HC 중지 (도메인 컨트롤러 중지)
    • Master host.xml에 Vault 설정 추가 및 vault.keystore 파일 배포
    • Master HC 재기동
  3. Datasource 변경:
    • Master가 기동된 상태에서 domain.xml의 데이터소스 패스워드를 ${VAULT::...}로 변경
    • JBoss CLI 또는 콘솔에서 Datasource Disable/Enable 수행 (또는 reload)

Scenario B: 전체 중단 후 적용 (Cold Restart)

가장 안전하고 확실한 방법입니다. 유지보수 시간(Maintenance Window)이 확보되었을 때 권장합니다.

  1. 모든 JBoss 프로세스(Master/Slave) 종료
  2. 모든 서버의 host.xml 수정 및 키스토어 배포
  3. Master 서버의 domain.xml 수정
  4. Master 기동 → Slave 기동 순서로 전체 재시작

6. 검증 (Verification)

  • 서버 기동 로그(server.log)에 Vault 관련 에러(PBOX000...)가 없는지 확인합니다.
  • 관리 콘솔에서 Datasource의 Test Connection을 수행하여 DB 연결이 정상적인지 확인합니다.
Open Stream →
#Liberty

[WebSphere Liberty] securityUtility로 SSL 인증서 생성 및 AES 패스워드 암호화 설정 가이드

WebSphere Liberty의 securityUtility 도구를 사용하여 자체 서명된(Self-Signed) SSL 인증서를 생성하고, 보안성을 높이기 위해 Keystore 비밀번호를 AES로 암호화하여 설정하는 방법을 정리합니다.

0. 배경 및 시나리오 (Context)

Liberty 서버는 기본적으로 개발 편의를 위해 SSL 설정을 자동화하지만, 운영 환경에서는 명시적인 인증서 관리와 비밀번호 보안이 필수적입니다. securityUtility를 사용하면 인증서 생성과 비밀번호 암호화(Encoding)를 동시에 수행할 수 있습니다.

Test Environment

  • Middleware: WebSphere Liberty Profile (WLP)
  • Server Name: s11, s12

1. SSL 인증서 생성 및 암호화 (Create Certificate)

securityUtility createSSLCertificate 명령어를 사용하여 키스토어(PKCS12)를 생성합니다. 이때 --passwordEncoding=aes 옵션을 사용하여 설정 파일에 들어갈 비밀번호를 암호화합니다.

명령어 구문

# 구문: securityUtility createSSLCertificate --server=[서버명] --password=[키패스워드] --validity=[유효기간일수] --passwordEncoding=aes --passwordKey=[암호화키]

cd $WLP_HOME/bin
./securityUtility createSSLCertificate --server=s11 --password=passw0rd --validity=7300 --passwordEncoding=aes --passwordKey=passw0rd

실행 결과

키 저장소 /sw/was/WebSphere/wlp/usr/servers/s11/resources/security/key.p12을(를) 작성하는 중입니다.

서버 s11에 대한 SSL 인증서를 작성했습니다. 
이 인증서는 CN=testwas11,OU=s11을(를) 사용하여 SubjectDN으로 작성되었습니다.
Tip: 여기서 생성된 키스토어 파일(key.p12)은 usr/servers/[서버명]/resources/security/ 경로에 저장됩니다.

2. 서버 설정 적용 (server.xml)

위에서 생성된 인증서를 서버가 사용하도록 server.xml을 수정합니다. 이때 비밀번호 부분에 {aes}... 로 시작하는 암호화된 문자열을 입력해야 합니다.

<server description="SSL Server">

    <!-- 1. SSL 기능 활성화 -->
    <featureManager>
        <feature>transportSecurity-1.0</feature>
    </featureManager>

    <!-- 2. Keystore 정의 (비밀번호는 암호화된 값 사용) -->
    <keyStore id="defaultKeyStore" 
              location="key.p12"
              password="{aes}AJS+VEek/Fgo/zp46z8cuIUMTbnMM7sJVmPPbT49n4s6" />

</server>

3. 암호화 키 등록 (bootstrap.properties)

server.xml에 적힌 {aes} 비밀번호를 서버가 복호화하려면, 암호화할 때 사용했던 Key를 서버에 알려주어야 합니다. 이 설정은 bootstrap.properties 파일에 저장합니다.

  • 파일 위치: usr/servers/[서버명]/bootstrap.properties
# securityUtility 실행 시 --passwordKey 옵션에 넣었던 값
wlp.password.encryption.key=passw0rd
주의: 이 설정이 누락되면 서버 기동 시 CWWKS1704E: 비밀번호를 복호화할 수 없습니다. 에러가 발생합니다.

4. 인증서 검증 (Verification)

생성된 키스토어 파일이 정상적인지, 유효기간은 맞는지 확인하기 위해 JDK에 포함된 keytool 명령어를 사용합니다.

검증 명령어

# keytool -list -v -keystore [파일경로] -storetype PKCS12 -storepass [비밀번호]
./keytool -list -v -keystore /sw/was/WebSphere/wlp/usr/servers/s12/resources/security/key.p12 -storetype PKCS12 -storepass passw0rd

출력 결과 분석

키 저장소 유형: PKCS12
키 저장소 제공자: SUN

별칭 이름: default
생성 날짜: 2024. 6. 12.
항목 유형: PrivateKeyEntry
인증서 체인 길이: 2

# 유효기간 확인
적합한 시작 날짜: Wed Jun 12 16:47:57 KST 2024 
종료 날짜: Tue Jun 07 16:47:57 KST 2044 (약 20년)

# 소유자 및 서명 알고리즘 확인
소유자: CN=testwas11, OU=s12, O=ibm, C=us
서명 알고리즘 이름: SHA256withRSA
주체 공용 키 알고리즘: 2048비트 RSA 키

Next Step:
자체 서명 인증서(Self-Signed)는 브라우저에서 경고가 발생하므로, 운영 환경에서는 CSR을 생성하여 공인 인증기관(CA)의 서명을 받은 후 keytool -import 명령어로 교체해야 합니다.

Open Stream →
#EAP7

[JBoss EAP 7] 보안 헤더 숨기기: Server 및 X-Powered-By 정보 노출 방지 (Undertow Filter 설정)

JBoss EAP 7.4 (Undertow)의 HTTP 응답 헤더에 노출되는 서버 버전 정보(Server: JBoss-EAP/7, X-Powered-By: JSP/2.3)를 제거하거나 변경하여 보안성을 강화하는 방법을 정리합니다. CLI 명령어를 이용한 JSP 설정 변경과 필터(Filter) 적용 방법을 다룹니다.

1. 문제 현상 (Issue)

기본 설정 상태에서 JBoss EAP 7.4는 응답 헤더에 구체적인 미들웨어 정보를 노출합니다. 이는 공격자에게 서버 정보를 제공하는 단서가 되므로 보안 취약점으로 분류됩니다.

노출되는 헤더 예시

HTTP/1.1 200 OK
X-Powered-By: Undertow/1
X-Powered-By: JSP/2.3
Server: JBoss-EAP/7
...

2. 해결 방법 (Resolution Plan)

조치는 크게 두 단계로 나뉩니다. ① JSP 엔진이 자동으로 붙이는 헤더 비활성화, ② Undertow 필터를 사용하여 Server 헤더 덮어쓰기입니다.

Step 1: X-Powered-By (JSP) 비활성화

서블릿 컨테이너 설정에서 JSP 엔진이 해당 헤더를 생성하지 못하도록 설정합니다.

[JBoss CLI 명령어]

# JSP 설정의 x-powered-by 속성을 false로 변경
/subsystem=undertow/servlet-container=default/setting=jsp:write-attribute(name=x-powered-by,value=false)

# 설정 적용을 위한 리로드 (필요시)
reload
확인: 관리 콘솔에서도 Configuration > Subsystems > Undertow > Servlet Container > JSP 항목에서 X-Powered-By 체크박스가 해제된 것을 확인할 수 있습니다.

Step 2: Server 헤더 변경/삭제 (Undertow Filter)

Server 헤더는 엔진 레벨에서 붙는 경우가 많아 아예 삭제가 어렵다면, 필터(Filter)를 통해 무의미한 값으로 덮어쓰는(Override) 방식을 사용합니다.

[JBoss CLI 명령어]

# 1. 헤더 변경용 필터 생성 (이름: server-header, 값: "Apache" 또는 빈 값)
/subsystem=undertow/configuration=filter/response-header=server-header:add(header-name="Server", header-value="Apache")

# 2. X-Powered-By (Undertow) 헤더 제거 필터 생성 (필요 시)
/subsystem=undertow/configuration=filter/response-header=x-powered-by-header:add(header-name="X-Powered-By", header-value="Unknown")

# 3. 생성한 필터를 기본 호스트(default-host)에 적용
/subsystem=undertow/server=default-server/host=default-host/filter-ref=server-header:add
/subsystem=undertow/server=default-server/host=default-host/filter-ref=x-powered-by-header:add
Tip: header-value에 빈 값("")을 넣거나 일반적인 웹 서버 이름("Webserver")을 넣어 공격자에게 혼동을 줄 수 있습니다.

3. 조치 결과 확인 (Verification)

설정 적용 후 curl 명령어나 브라우저 개발자 도구를 통해 응답 헤더가 변경되었는지 확인합니다.

변경 전후 비교

헤더(Header) 변경 전 (Before) 변경 후 (After)
Server JBoss-EAP/7 Apache (설정한 값)
X-Powered-By JSP/2.3, Undertow/1 (삭제됨) 또는 Unknown
Security Header Verification

[그림] 조치 후 헤더 정보 노출 테스트 결과

Open Stream →