아파치를 Docker로 돌리고 싶지만 사정상 서버에 직접 설치해서 운영 중이다. 이번에 다른 목적으로 WEB 서버 6대가 추가되었는데, 6개의 콘솔을 열어서 설정파일을 수정하고 재기동하는건 말도 안 되게 귀찮기 때문에 Bash 스크립트를 작성했다.

ssh 키 쌍 생성하여 패스워드 입력 없이 서버 연결 설정

# 1.키 쌍 생성
$ ssh-keygen -t rsa
# Enter file in which to save the key : 엔터 (기본 경로 ~/.ssh 사용)
# Enter passphrase : 엔터 (패스워드 없이 사용)
# Enter same passphrase again : 엔터

# 2. 생성된 공개키를 대상 서버에 복사
$ ssh-copy -id -p 22 username@remote.server.ip

# 3. 연결 확인
$ ssh -p 22 username@remote.server.ip

ssh 연결하여 명령 실행

# 한 개의 명령 실행
$ ssh -p 22 username@remote.server.ip "touch test"

# 여러 개의 명령 실행(세미콜론으로 구분)
$ ssh -p 22 username@remote.server.ip "touch test; mv test tset"

스크립트 작성

  1. 설정 파일의 수정이 있을 때 파일명.YYYYMMDD 형태로 파일을 백업한다.
  2. conf.d의 ssl.conf, vhost.conf, workers.properties 위주로 수정한다.

스크립트 작성의 전제 조건이다.

#!/bin/bash

APACHE_CONFD_PATH=/etc/httpd/conf.d
DATE=`date +%Y%m%d`
# 오늘 날짜로 수정된 파일명
MODIFIED_CONF_LIST=`ls -1 ${APACHE_CONFD_PATH} | grep ${DATE} | sed 's/\.[^.]*$//`
# 대상 서버 목록
SERVERS="192.168.0.2 192.168.0.3 192.168.0.4 192.168.0.5 192.168.0.6"

# 수정된 파일별로 반복
for CONF in ${MODIEFIED_CONF_LIST}
do
    # 대상 서버별로 파일 전송
    for SERVER in ${SERVERS}
    do
        scp -P 22 ${APACHE_CONFD_PATH}/${CONF} apache@${SERVER}:${APACHE_CONFD_PATH}/${CONF}
        scp -P 22 ${APACHE_CONFD_PATH}/${CONF}.${DATE} apache@${SERVER}:${APACHE_CONFD_PATH}/${CONF}.${DATE}
    done
done

# 아파치 재기동
for SERVER in ${SERVERS}
do
    ssh -n -p 22 apache@${SERVER} "sudo /home/apache/apache_restart.sh"
    sleep 5
done

sudo /home/apache/apache_restart.sh

 

기존에는 apache_stop.shapache_start.sh를 따로 실행해서 재기동을 했으나, 한 번의 명령으로 재기동 하기 위해 각 서버에 apache_restart.sh를 추가 작성했다.

#!/bin/bash

# 아파치 종료
sh /home/apache/apache_stop.sh

while true
do
    # 프로세스에 httpd가 없으면(내가 실행한 grep 하나만 나오면) 탈출
    if [ `ps -ef | grep -c httpd` -eq 1 ]
    then
        break
    fi

    sleep 1
done

# 아파치 시작
sh /home/apache/apache_start.sh

연계 파일을 생성할 때 마지막 줄에 긴 공백이 들어가서 상대방이 적재 시 오류가 발생한다고 확인 요청이 왔다.

ByteBuffer buffer = ByteBuffer.allocate(data.length() * 3); //실제로는 숫자가 조금 다르다

 

ByteBuffer에 담을 데이터들은 data 변수에 들어가 있는데, 대충 한글이니 2바이트 등해서 주석에 왜 이렇게 계산을 하는지 잔뜩 적어놓았다. 물론 딱 맞는 숫자는 아니었고 무조건 미사용 공간이 생길 수 밖에 없는 구조다. 하지만 찾아보니 ByteBuffer를 allocate로 할당하면 그 capacity는 줄일 수가 없어서 꽉 안 채우면 미사용 공간이 공백으로 바뀌어 파일이 생성되는 것이었다.

buffer.flip();
byte[] bufferData = new byte[buffer.limit()];
buffer.get(bufferData);

 

flip()으로 읽기모드로 전환하면 position(시작)이 0으로, limit(끝)이 실제 쓴 데이터의 끝으로 변경되어, 이 상태로 복사하면 limit까지의 데이터만 복사되고, 뒤에 미사용 공간은 복사되지 않는다. 따라서 byte array를 생성할 때 길이를 buffer.limit()으로 설정하고, buffer.get()으로 버퍼에 담긴 데이터를 복사하면 된다.

 

문제가 있다면 다음에 buffer를 넘기는 메소드가 ByteBuffer 타입을 요구하는데, 위에서 생성한 bufferData는 byte array다.

//String foo = bar.foobar(buffer);
String foo = bar.foobar(ByteBuffer.wrap(bufferData);

 

이때 ByteBuffer.wrap(byte[])을 사용하면, 이미 존재하는 byte array를 ByteBuffer로 감싸기 때문에 예시로 쓴 foobar 메소드에서 타입 에러 없이 동작하게 된다. 이렇게 수정하여 연계 파일을 생성했을 때, 상대방의 연계 파일 적재 오류를 해결했다.

기본적으로 JBoss EAP 7.4에서 운영 중에 있으나, 특정 서비스의 요구사항으로 JBoss EAP 8.0을 설치한 서버가 있다. standalone.xml의 설정 과정에서 datasource 서브시스템의 security 절을 동일하게 사용하고, security 서브시스템으로 username과 password를 관리하려 했으나 8.0에서는 security 서브시스템을 지원하지 않고, elytron 서브시스템을 사용해야한다.

<subsystem xmlns="urn:jboss:domain:datasources:7.0">
  <datasources>
    <datasource jndi-name="java:/fooDs" pool-name="fooDs" enabled="true" use-java-context="true">
      <connection-url>...</connection-url>
      <driver>oracle</driver>
      <!-- 생략 -->
      <security>
        <security-domain>fooDs</security-domain>
      </security>
    </datasource>
  </datasources>
</subsystem>

<subsystem xmlns="urn:jboss:domain:security:2.0">
  <security-domains>
    <security-domain name="fooDs" cache-type="default">
      <authentication>
        <login-module code="org.picketbox.datasource.security.SecureIdentityLoginModule" flag="required">
          <module-option name="username" value="foo"/>
          <module-option name="password" value="bar"/>
        </login-module>
      </authentication>
    </security-domain>
  </security-domains>
</subsystem>

 

7.4에서 쓰던 설정이고, 그대로 8.0에 옮겼을 때는 앞에서 말했듯이 오류가 난다.

/subsystem=elytron/credential-store=fooDs-credstore:add( \
    path=fooDs-credstore.cs, \
    relative-to=jboss.server.config.dir, \
    create=true, \
    credential-reference={clear-text="5dOaAVafCSd;12345678;100} \
)

/subsystem=elytron/credential-store=fooDs-credstore:add-alias(\
    alias=fooDs-username, \
    secret-value="foo" \
)

/subsystem=elytron/credential-store=fooDs-credstore:add-alias(\
    alias=fooDs-password, \
    secret-value="bar" \
)

 

우선 security를 elytron으로 변경하기 위해 jboss-cli에서 elytron credential store를 생성한 뒤에, store에 username과 password를 각각 저장해준다.

<security>
  <credential-reference>
    <store>fooDs-credstore</store>
    <alias>fooDs-username</alias>
    <clear-text-credential-reference>
      <store>fooDs-credstore</store>
      <alias>fooDs-password</alias>
    </clear-text-credential-reference>
  </credential-reference>
</security>

 

설정한 datasource에서 security 구문만 바꿔줬더니 오류가 난다.

<security>
  <credential-reference store="fooDs-credstore" alias="fooDs-username"/>
  <credential-reference store="fooDs-credstore" alias="fooDs-username"/>
</security>

 

store랑 alias를 credential-reference의 속성으로 넣어봐도 오류가 난다.

<subsystem xmlns="urn:jboss:domain:datasources:7.1">

 

datasources 서브시스템을 7.0에서 7.1로 올리면 store를 사용할 수 있다고 해서 변경하였으나 오류가 난다.

<security>
  <user-name>foo</user-name>
  <credential-reference store="fooDs-credstore" alias="fooDs-username"/>
</security>

 

다시 datasources 서브시스템을 7.0으로 바꾸고 username을 user-name 태그로 변경하니 성공했다.

/subsystem=datasources/data-source=fooDs:test-connection-in-pool
{
    "outcome" => "success",
    "result" => [ture]
}

 

username은 평문으로 쓰고, password만 elytron credential store에 등록해서 설정하면 되는듯하다.

'DevOps > WEB.WAS' 카테고리의 다른 글

[Apache] referer에 따른 Directory 접근 차단  (0) 2025.04.14
[JBoss] JBWEB002004 오류 발생  (0) 2025.01.09

서비스 운영 중 일부 js 파일 등 static content의 노출로 인한 취약점 해결을 위해서 찾아보았다. 사용자의 직접 접근 요청을 차단하고, 설정한 referer의 경우에만 Directory에 접근하여 기능이 정상적으로 동작하도록 한다.

SetEnvIf Referer ^http(s)?:\/\/ allow_access
...
<Directory "/foo/bar">
    Require all denied
    Require env allow_access
</Directory>

 

Apache의 mod_setenvif 를 참고하면 SetEnvIf 지시어로 환경변수를 설정할 수 있다. 첫번째 인자는 attribute로 HTTP 요청 헤더나 Remote_Host, Remote_Addr 등을 지정하고, 두번째 인자로 정규표현식을 지정하는데 regex가 attribute에 대응하면 된다. 세번째 인자는 변수명이다.
 
따라서, 설정한 값은 HTTP 요청 헤더 중 Referer 헤더가 http:// 또는 https://로 시작하면 allow_access가 참이 된다. 그리고 VirtualHost에서 Directory 지시자로 지정한 경로(서버 기준의 절대경로)에 allow_access 대응했을 경우에만 접근이 가능하다. 목적은 직접 접근 요청 차단이어서 해결 완료(실제 서버에는 Referer 정규표현식을 더 상세하게 작성함).

'DevOps > WEB.WAS' 카테고리의 다른 글

[JBoss] EAP 7.4 → 8.0 datasource의 security 설정 문제  (1) 2025.05.02
[JBoss] JBWEB002004 오류 발생  (0) 2025.01.09

일년에 몇 번 안 쓰는 기능을 쓰다보니 오류 때문에 진행이 안 된다고 연락이 왔다.

JBWEB002004: More than the maximum number of request parameters (GET plus POST) for a single request (10000) were detected. Any parameters beyond this limit have been ignored. To change this limit, set the maxParameterCount attribute on the Connector.

 

처음에는 제대로 안 읽고 single request 뒤의 (10000)에만 집중해서 WEB 서버에 접속해 Apache의 ssl.conf 파일을 여니 LimitRequestFields가 딱 10000이었다. 문제는 스테이징 서버도 동일 설정 값이어서 운영 서버에서 오류가 나는게 이상한 상황이었지만, 일단 10000에 꽂혔으니 늘려봤다. 당연히 결과는 허탕.

<system-properties>
    ...
    <property name="org.apache.tomcat.util.http.Parameters.MAX_COUNT" value="20000"/>
</system-properties>

 

찾아보니 JBoss의 standalone.xml에 위 내용을 추가해보라고 해서 추가하니 잘 됐다. 기본값이 10000이었다.

+ Recent posts