본문 바로가기
IBM - old/IBM APIC

[DataPower]Cloud 에 있는 WAS 와 Legacy 에 있는 DB 연결을 위한 IBM DataPower Gateway 보안 구성

by freeman98 2017. 5. 29.

안녕하세요 이정운 입니다.

지난번에 테스트 해서 공유드린 내용의 2부 겪으로 Cloud 상에 있는 WAS 와 Legacy 에 있는 DB 연결을 좀 더 안정적으로 수행하기 위해서 IBM DataPower Gateway 가 할 수 있는 추가 방안을 모색해서 테스트 해보고 그 내용을 공유드립니다.

요구사항 : Cloud 에 WAS 를 구성하고 기존 Legacy 에 DB 를 둘 예정인데 이를 보안적으로 보완하기 위한 구성
구성 : WAS on Cloud --> IBM DataPower Gateway on Legacy -> DB on Legacy 
          - WAS 에서 REST/JSON 형태로 SQL 구문을 IBM DataPower Gateway 로 전달하면 DataPower 가 SQL 구문을 직접 DB 에 던져 수행후 그 결과를 JSON 으로 반환
차이 : WAS 에서 Application 변경이 필요하지만 DB 호출 및 응답을 IBM DataPower Gateway 가 대신 수행하므로 Legacy 에 있는 DB 가 직접 Cloud 로 노출되지 않음     
          IBM DataPower Gateway 에서 요청을 위임 받으므로 해당 솔루션이 Secure Gateway 로서 가지고 있는 다양한 보안/정책등을 통해 보다 강화된 보안/제어 가능


#1) WAS 에서 REST/JSON 형태로 request 를 보낼때 그 안에 SQL 구문을 하단과 같은 형태로 보낸다고 가정

{ "SQL" : "select * from IF_MRO_S where SITE_CD='00001'" }


#2) IBM DataPower Gateway 에서 DB 직접 호출을 위한 SQL 데이터 소스 작성

IBM DataPower Gateway 는 DB 접속을 위한 다양한 SQL 데이터 소스를 작성 가능하며 하단과 같이 DB 접속 정보를 넣어 줍니다.
(IBM DataPower Gateway 는 Oracle, IBM DB2, MS SQL, Sybase 등 다양한 DB 를 ODBC 형태로 지원 가능하며 테스트는 간단하게 Oracle 로 수행했습니다.)


#3) IBM DataPower Gateway 에서 HTTPS 요청을 받고 DB 를 호출할 Multi-Protocol Gateway(MPGW) 작성

요청을 받았을때 DB 작업 수행을 할 새로운 MPGW 를 하나 작성합니다. MPGW 는 하단과 같이 HTTP, HTTPS, JMS, FTP 등 다양한 프로토콜을 받아서 처리 및 다양한 프로토콜로 변환 가능한 서비스 중의 하나입니다.


MPGW 가 보유한 다양한 프로토콜 핸들러 중에서 HTTPS 핸들러를 선택합니다.


특정 포트로 서비스 할 수 있는 HTTPS 핸들러 설정을 작성합니다. (SSL 인증서가 필요하며 IBM DataPower 에서는 Crpyto Tool(암호 도구) 을 이용해서 사용자 SSL 인증서와 키를 직접 생성 가능합니다.)

HTTPS 핸들러 작성이 완료되었으면 실제 서비스를 수행(DB 수행) 하기 위해서 MPGW 정책을 작성합니다.


IBM DataPower Gateway 의 정책 디자이너에서 하단과 같이 정책을 생성합니다.

① 들어오는 모든 URL 대상으로 MPGW 정책 수행
Match
URL : *

② IBM DataPower Gateway 에서 직접 DB 작업을 수행후에 바로 응답을 보낼거라 응답 규칙을 만들지 않을것이라는 설정
Set Variable
service/mpgw/skip-backside = 1

③ Header 값의 contecnt-type 변경
Set Variable
service/set-request-header/content-type = application/json

④ 입력받은 JSON 을 내부에서 처리하기 위한 xml 형태의 JSONx 로 변환
Convert Query Params to XML
json

⑤ 입력받은 JSON 의 SQL 부분의 값을 가져와 미리 만들어둔 SQL 데이터 소스를 통해서 SQL query 수행
(XSLT 로 샘플을 작성했으며 Javascript 기반인 Gatewayscript 를 사용해서 작성도 가능합니다.)
sqlSelect01.xslt
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:dp="http://www.datapower.com/extensions"
    xmlns:json="http://www.ibm.com/xmlns/prod/2009/jsonx"
    xmlns="http://www.w3.org/2005/Atom"
    exclude-result-prefixes="dp"  >

<xsl:template match="json:string[@name]">
  <xsl:variable name="query">
       <xsl:value-of select="."/>
  </xsl:variable>

 <xsl:variable name="result" select="dp:sql-execute('OracleDS01',$query)" />
 <xsl:copy-of select="$result" />

 </xsl:template>
 </xsl:stylesheet>

⑥ SQL querty 수행 결과를 XML 에서 JSON 으로 포멧 변환
xmltojson.xslt
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:func="http://exslt.org/functions"
    xmlns:xalan="http://xml.apache.org/xslt"
    xmlns:json="http://www.ibm.com/xmlns/prod/2009/jsonx"
    xmlns:regexp="http://exslt.org/regular-expressions"
    xmlns:dp="http://www.datapower.com/extensions"
    xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:dpquery="http://www.datapower.com/param/query"   
    extension-element-prefixes="dp"
    exclude-result-prefixes="dp"      >

    <xsl:template match="/*[node()]">           
        <xsl:text>{</xsl:text>
        <xsl:apply-templates select="." mode="detect" />
        <xsl:text>}</xsl:text>
    </xsl:template>
    <xsl:template match="*" mode="detect">
        <xsl:choose>
            <xsl:when test="name(preceding-sibling::*[1]) = name(current()) and name(following-sibling::*[1]) != name(current())">
                <xsl:apply-templates select="." mode="obj-content" />
                <xsl:text>]</xsl:text>
                <xsl:if test="count(following-sibling::*[name() != name(current())]) &gt; 0">, </xsl:if>
            </xsl:when>
            <xsl:when test="name(preceding-sibling::*[1]) = name(current())">
                <xsl:apply-templates select="." mode="obj-content" />
                <xsl:if test="name(following-sibling::*) = name(current())">, </xsl:if>
            </xsl:when>
            <xsl:when test="following-sibling::*[1][name() = name(current())]">
                <xsl:text>"</xsl:text><xsl:value-of select="name()"/><xsl:text>" : [</xsl:text>
                <xsl:apply-templates select="." mode="obj-content" /><xsl:text>, </xsl:text>   
            </xsl:when>
            <xsl:when test="count(./child::*) > 0 or count(@*) > 0">
                <xsl:text>"</xsl:text><xsl:value-of select="name()"/>" : <xsl:apply-templates select="." mode="obj-content" />
                <xsl:if test="count(following-sibling::*) &gt; 0">, </xsl:if>
            </xsl:when>
            <xsl:when test="count(./child::*) = 0">
                <xsl:text>"</xsl:text><xsl:value-of select="name()"/>" : "<xsl:apply-templates select="."/><xsl:text>"</xsl:text>
                <xsl:if test="count(following-sibling::*) &gt; 0">, </xsl:if>
            </xsl:when>
        </xsl:choose>
    </xsl:template>
    <xsl:template match="*" mode="obj-content">
        <xsl:text>{</xsl:text>
        <xsl:apply-templates select="@*" mode="attr" />
        <xsl:if test="count(@*) &gt; 0 and (count(child::*) &gt; 0 or text())">, </xsl:if>
        <xsl:apply-templates select="./*" mode="detect" />
        <xsl:if test="count(child::*) = 0 and text() and not(@*)">
            <xsl:text>"</xsl:text><xsl:value-of select="name()"/>" : "<xsl:value-of select="text()"/><xsl:text>"</xsl:text>
        </xsl:if>
        <xsl:if test="count(child::*) = 0 and text() and @*">
            <xsl:text>"text" : "</xsl:text><xsl:value-of select="text()"/><xsl:text>"</xsl:text>
        </xsl:if>
        <xsl:text>}</xsl:text>
        <xsl:if test="position() &lt; last()">, </xsl:if>
    </xsl:template>
    <xsl:template match="@*" mode="attr">
        <xsl:text>"</xsl:text><xsl:value-of select="name()"/>" : "<xsl:value-of select="."/><xsl:text>"</xsl:text>
        <xsl:if test="position() &lt; last()">,</xsl:if>
    </xsl:template>
    <xsl:template match="node/@TEXT | text()" name="removeBreaks">
        <xsl:param name="pText" select="normalize-space(.)"/>
        <xsl:choose>
            <xsl:when test="not(contains($pText, '&#xA;'))"><xsl:copy-of select="$pText"/></xsl:when>             
            <xsl:otherwise>
                <xsl:value-of select="concat(substring-before($pText, '&#xD;&#xA;'), ' ')"/>
                <xsl:call-template name="removeBreaks">
                    <xsl:with-param name="pText" select="substring-after($pText, '&#xD;&#xA;')"/>
                </xsl:call-template>
            </xsl:otherwise>     
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>


위와 같이 하면 MPGW 정책 작성을 하실 수 있습니다. 이후 MPGW 의 응답 유형 타입과 요청 유형 타입을 둘다 JSON 으로 설정하고 해당 설정을 저장 합니다.

해당 설정이 정상적으로 완료되면 하단과 같이 지금 만든 MPGW 서비스가 on 되어서 작동 상태로 변경된 것을 확인 가능합니다.




#4) WAS 에서 테스트 수행하는 것을 가정하여 간단하게 테스트를 진행 

JSON 형태로 SQL 을 HTTPS 로 요청하게 되면 SSL 을 통해서 하단과 같이 IBM DataPower Gateway 가 실제 DB 호출을 수행한 후 결과값을 JSON 형태로 보내주는 것을 확인 가능합니다.




추가 #1 : 이전 방식보다 MPGW 를 사용하는 방식이 보안적으로 더 높은 구성이 가능하다는 이유는 Secure Gateway 로서의 IBM DataPower Gateway 의 기능을 모두다 활용할 수 있기 때문입니다. 예를 들어 MPGE 정책의 맨 앞단에 하단과 같은 gatewayscript 를 추가하여 사전에 지정된 IP 만 해당 요청을 수행하게 할 수 있습니다.

validIP.js
var sm = require ('service-metadata');
//var hm = require('header-metadata');
//var clientIP = hm.current.get('X-Client-IP');
var clientIP = sm.getVar('var://service/transaction-client');
var allowIP = session.parameters.allowIP;

if (clientIP != allowIP) {
   session.reject(clientIP + " is not allowed.");
}else {
}

MPGW 정책 예시

지정된 IP 가 아닌 다른 곳에서 호출한 테스트 결과



web-rest-policy01.zip





#9) 참고자료

Creating an SQL data source

Multi-Protocol Gateway

Gateway programming model and GatewayScript

Variables




댓글