3.5. OpenID Connect를 사용하여 OpenShift에서 JBoss EAP 애플리케이션 보안

외부 OpenID 공급자를 사용하여 인증을 위임하려면 JBoss EAP 기본 OpenID Connect(OIDC) 클라이언트를 사용합니다. OIDC는 JBoss EAP와 같은 클라이언트가 OpenID 공급자가 수행한 인증을 기반으로 사용자 ID를 확인할 수 있는 ID 계층입니다.

elytron-oidc-client 하위 시스템 및 elytron-oidc-client Galleon 계층은 JBoss EAP에서 기본 OIDC 클라이언트를 제공하여 OpenID 공급자와 연결합니다. JBoss EAP는 OpenID 공급자 구성에 따라 애플리케이션의 가상 보안 도메인을 자동으로 생성합니다.

elytron-oidc-client 하위 시스템을 다음과 같은 세 가지 방법으로 구성할 수 있습니다.

  • 배포에 oidc.json 추가
  • CLI 스크립트를 실행하여 elytron-oidc-client 하위 시스템을 구성합니다.
  • OpenShift에서 JBoss EAP 서버 시작 시 elytron-oidc-client 하위 시스템을 구성하는 환경 변수를 정의합니다.
참고

다음 절차에서는 OIDC로 애플리케이션을 보호하기 위해 환경 변수를 사용하여 elytron-oidc-client 하위 시스템을 구성하는 방법을 설명합니다.

3.5.1. JBoss EAP의 OpenID Connect 구성

OpenID 공급자를 사용하여 애플리케이션을 보호하면 보안 도메인 리소스를 로컬로 구성할 필요가 없습니다. elytron-oidc-client 하위 시스템은 JBoss EAP에서 기본 OpenID Connect(OIDC) 클라이언트를 제공하여 OpenID 공급자와 연결합니다. JBoss EAP는 OpenID 공급자 구성에 따라 애플리케이션의 가상 보안 도메인을 자동으로 생성합니다.

중요

Red Hat build of Keycloak과 함께 OIDC 클라이언트를 사용합니다. JSON 웹 토큰(JWT)인 액세스 토큰을 사용하도록 구성할 수 있고 RS256, RS384, ES256, ES384 또는 ES512 서명 알고리즘을 사용하도록 구성할 수 있는 경우 다른 OpenID 공급자를 사용할 수 있습니다.

OIDC 사용을 활성화하려면 elytron-oidc-client 하위 시스템 또는 애플리케이션 자체를 구성할 수 있습니다. JBoss EAP는 다음과 같이 OIDC 인증을 활성화합니다.

  • 애플리케이션을 JBoss EAP에 배포할 때 elytron-oidc-client 하위 시스템은 배포를 검사하여 OIDC 인증 메커니즘이 필요한지 감지합니다.
  • 하위 시스템에서 elytron-oidc-client 하위 시스템 또는 애플리케이션 배포 설명자 중 하나에서 배포에 대한 OIDC 구성을 감지하면 JBoss EAP는 애플리케이션에 대한 OIDC 인증 메커니즘을 활성화합니다.
  • 하위 시스템이 두 위치에서 OIDC 구성을 감지하면 elytron-oidc-client 하위 시스템 secure-deployment 속성의 구성이 애플리케이션 배포 설명자의 구성보다 우선합니다.

3.5.2. OpenID Connect로 보안된 애플리케이션 생성

웹 애플리케이션을 생성하려면 필수 종속성 및 디렉터리 구조가 포함된 Maven 프로젝트를 생성합니다. 로그인한 사용자의 주체 및 특성에서 얻은 사용자 이름을 반환하는 서블릿이 포함된 웹 애플리케이션을 생성합니다. 로그인한 사용자가 없는 경우 서블릿은 "NO AUTHENTICATED USER" 텍스트를 반환합니다.

사전 요구 사항

프로세스

  1. mvn 명령을 사용하여 Maven 프로젝트를 설정합니다. 명령은 프로젝트에 대한 디렉터리 구조와 pom.xml 구성 파일을 생성합니다.

    구문

    $ mvn archetype:generate \
    -DgroupId=${group-to-which-your-application-belongs} \
    -DartifactId=${name-of-your-application} \
    -DarchetypeGroupId=org.apache.maven.archetypes \
    -DarchetypeArtifactId=maven-archetype-webapp \
    -DinteractiveMode=false

    예제

    $ mvn archetype:generate \
    -DgroupId=com.example.app \
    -DartifactId=simple-webapp-example \
    -DarchetypeGroupId=org.apache.maven.archetypes \
    -DarchetypeArtifactId=maven-archetype-webapp \
    -DinteractiveMode=false

  2. 애플리케이션 루트 디렉터리로 이동합니다.

    구문

    $ cd <name-of-your-application>

    예제

    $ cd simple-webapp-example

  3. 생성된 pom.xml 파일의 내용을 다음 텍스트로 바꿉니다.

    <?xml version="1.0" encoding="UTF-8"?>
    
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
    
      <groupId>com.example.app</groupId>
      <artifactId>simple-webapp-example</artifactId>
      <version>1.0-SNAPSHOT</version>
      <packaging>war</packaging>
    
      <name>simple-webapp-example Maven Webapp</name>
      <!-- FIXME change it to the project's website -->
      <url>http://www.example.com</url>
    
      <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <version.maven.war.plugin>3.3.2</version.maven.war.plugin>
        <version.eap.plugin>1.0.0.Final-redhat-00014</version.eap.plugin>
        <version.server>8.0.0.GA-redhat-00009</version.server>
        <version.bom.ee>${version.server}</version.bom.ee>
      </properties>
    
    
      <repositories>
        <repository>
            <id>jboss</id>
            <url>https://maven.repository.redhat.com/ga/</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
      </repositories>
    
      <pluginRepositories>
        <pluginRepository>
            <id>jboss</id>
            <url>https://maven.repository.redhat.com/ga/</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
      </pluginRepositories>
    
      <dependencyManagement>
        <dependencies>
          <dependency>
            <groupId>org.jboss.bom</groupId>
            <artifactId>jboss-eap-ee-with-tools</artifactId>
            <version>${version.bom.ee}</version>
            <type>pom</type>
            <scope>import</scope>
          </dependency>
        </dependencies>
      </dependencyManagement>
    
      <dependencies>
        <dependency>
          <groupId>jakarta.servlet</groupId>
          <artifactId>jakarta.servlet-api</artifactId>
          <scope>provided</scope>
        </dependency>
        <dependency>
          <groupId>org.wildfly.security</groupId>
          <artifactId>wildfly-elytron-auth-server</artifactId>
        </dependency>
      </dependencies>
    
    <build>
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>${version.maven.war.plugin}</version>
            </plugin>
            <plugin>
                <groupId>org.jboss.eap.plugins</groupId>
                <artifactId>eap-maven-plugin</artifactId>
                <version>${version.eap.plugin}</version>
                <configuration>
                    <channels>
                        <channel>
                            <manifest>
                                <groupId>org.jboss.eap.channels</groupId>
                                <artifactId>eap-8.0</artifactId>
                            </manifest>
                        </channel>
                    </channels>
                    <feature-packs>
                        <feature-pack>
                            <location>org.jboss.eap:wildfly-ee-galleon-pack</location>
                        </feature-pack>
                        <feature-pack>
                            <location>org.jboss.eap.cloud:eap-cloud-galleon-pack</location>
                        </feature-pack>
                    </feature-packs>
                    <layers>
                        <layer>cloud-server</layer>
                        <layer>elytron-oidc-client</layer>
                    </layers>
                    <galleon-options>
                            <jboss-fork-embedded>true</jboss-fork-embedded>
                    </galleon-options>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>package</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    </project>
    참고
  4. Java 파일을 저장할 디렉터리를 만듭니다.

    구문

    $ mkdir -p src/main/java/<path_based_on_artifactID>

    예제

    $ mkdir -p src/main/java/com/example/app

  5. 새 디렉터리로 이동합니다.

    구문

    $ cd src/main/java/<path_based_on_artifactID>

    예제

    $ cd src/main/java/com/example/app

  6. 다음 콘텐츠를 사용하여 SecuredServlet.java 파일을 생성합니다.

    package com.example.app;
    
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.security.Principal;
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.Iterator;
    import java.util.List;
    import java.util.Set;
    
    import jakarta.servlet.ServletException;
    import jakarta.servlet.annotation.WebServlet;
    import jakarta.servlet.http.HttpServlet;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    import org.wildfly.security.auth.server.SecurityDomain;
    import org.wildfly.security.auth.server.SecurityIdentity;
    import org.wildfly.security.authz.Attributes;
    import org.wildfly.security.authz.Attributes.Entry;
    /**
     * A simple secured HTTP servlet. It returns the user name and
     * attributes obtained from the logged-in user's Principal. If
     * there is no logged-in user, it returns the text
     * "NO AUTHENTICATED USER".
     */
    
    @WebServlet("/secured")
    public class SecuredServlet extends HttpServlet {
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            try (PrintWriter writer = resp.getWriter()) {
    
            	Principal user = req.getUserPrincipal();
            	SecurityIdentity identity = SecurityDomain.getCurrent().getCurrentSecurityIdentity();
            	Attributes identityAttributes = identity.getAttributes();
            	Set <String> keys = identityAttributes.keySet();
            	String attributes = "<ul>";
    
            	for (String attr : keys) {
            		attributes += "<li> " +  attr + " : " + identityAttributes.get(attr).toString() + "</li>";
            	}
    
            	attributes+="</ul>";
            	writer.println("<html>");
            	writer.println("  <head><title>Secured Servlet</title></head>");
            	writer.println("  <body>");
            	writer.println("    <h1>Secured Servlet</h1>");
            	writer.println("    <p>");
            	writer.print(" Current Principal '");
            	writer.print(user != null ? user.getName() : "NO AUTHENTICATED USER");
            	writer.print("'");
            	writer.print(user != null ? "\n" + attributes : "");
            	writer.println("    </p>");
            	writer.println("  </body>");
            	writer.println("</html>");
            }
        }
    
    }
  7. 애플리케이션 리소스를 보호하도록 애플리케이션의 web.xml 을 구성합니다.

    예제

    <?xml version="1.0" encoding="UTF-8"?>
    
    <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
        metadata-complete="false">
    
        <security-constraint>
            <web-resource-collection>
                <web-resource-name>secured</web-resource-name>
                <url-pattern>/secured</url-pattern>
            </web-resource-collection>
    
            <auth-constraint>
                <role-name>Users</role-name>
            </auth-constraint>
        </security-constraint>
    
        <login-config>
            <auth-method>OIDC</auth-method>
        </login-config>
    
        <security-role>
            <role-name>*</role-name>
        </security-role>
    </web-app>

    이 예에서는 사용자가 역할이 있는 사용자만 애플리케이션에 액세스할 수 있습니다.

3.5.3. OpenShift에 애플리케이션 배포

JBoss EAP 애플리케이션 개발자는 OpenID Connect 하위 시스템을 사용하는 OpenShift에 애플리케이션을 배포하고 Red Hat build of Keycloak 서버와 통합할 수 있습니다. 아래 절차에 따라 애플리케이션을 배포합니다.

사전 요구 사항

다음 구성을 사용하여 OpenShift에서 Red Hat build of Keycloak 서버를 구성했습니다. 자세한 내용은 Red Hat build of Keycloak Operator 를 참조하십시오.

  • Cryostat라는 영역을 생성합니다.
  • demo 라는 사용자를 만듭니다.
  • demo 라는 사용자의 암호를 설정합니다. 임시OFF 로 전환하고 암호 설정을 클릭합니다. 확인 프롬프트에서 암호 설정을 클릭합니다.
  • Users 라는 역할을 생성합니다.
  • 사용자 demo 사용자 역할을 할당합니다.
  • 클라이언트 역할 필드에서 JBoss EAP에 대해 구성한 realm-management 를 선택합니다.
  • create-client 역할을 클라이언트 realm-management 에 할당합니다.

프로세스

  1. 애플리케이션 코드를 Git 리포지토리에 배포합니다.
  2. OIDC 구성이 포함된 보안을 생성합니다.

    1. 다음 콘텐츠를 사용하여 oidc-secret.yaml 이라는 파일을 생성합니다.

      apiVersion: v1
      kind: Secret
      metadata:
        name: oidc-secret
      type: Opaque
      stringData:
        OIDC_PROVIDER_NAME: rh-sso
        OIDC_USER_NAME: demo
        OIDC_USER_PASSWORD: demo
        OIDC_SECURE_DEPLOYMENT_SECRET: mysecret
    2. 다음 명령을 사용하여 보안을 생성합니다.

      $ oc apply -f oidc-secret.yaml
  3. 다음 콘텐츠를 사용하여 helm.yaml 이라는 파일을 생성합니다.

    build:
      uri: [URL TO YOUR GIT REPOSITORY]
    deploy:
      envFrom:
    	- secretRef:
        	  name: oidc-secret
  4. JBoss EAP Helm 차트를 사용하여 예제 애플리케이션을 배포합니다.

    $ helm install eap-oidc-test-app -f helm.yaml jboss-eap/eap8
  5. oidc-secret.yaml 파일에 환경 변수를 추가하여 OIDC 공급자 URL 및 애플리케이션 호스트 이름을 구성합니다.

    yaml
    stringData:
      ...
      OIDC_HOSTNAME_HTTPS: <host of the application>
      OIDC_PROVIDER_URL: https://<host of the SSO provider>/realms/JBossEAP

    OIDC_HOSTNAME_HTTPS 의 값은 다음 출력에 해당합니다.

    echo $(oc get route eap-oidc-test-app --template='{{ .spec.host }}')

    OIDC_PROVIDER_URL 값은 다음 출력에 해당합니다.

    echo https://$(oc get route sso --template='{{ .spec.host }}')/realms/JBossEAP

    OIDC_HOSTNAME_HTTP(S) 가 설정되지 않은 경우 경로 검색 시도가 수행됩니다. 경로 검색을 활성화하려면 OpenShift 사용자가 경로 리소스를 나열할 수 있어야 합니다. 예를 들어 routeview 역할을 생성하고 view 사용자와 연결하려면 다음 oc 명령을 사용합니다.

    $ oc create role <role-name> --verb=list --resource=route
    
    $ oc adm policy add-role-to-user <role-name> <user-name> --role-namespace=<your namespace>
  6. oc apply -f oidc-secret.yaml 을 사용하여 시크릿을 업데이트합니다.
  7. 애플리케이션을 다시 배포하여 OpenShift에서 새 환경 변수를 사용하는지 확인합니다.

    $ oc rollout restart deploy eap-oidc-test-app

검증

  1. 브라우저에서 https://<eap-oidc-test-app route>/ 로 이동합니다.

    Red Hat build of Keycloak 로그인 페이지로 리디렉션됩니다.

  2. 보안 서블릿에 액세스합니다.
  3. 다음 인증 정보로 로그인합니다.

    username: demo
    password: demo

    보안 주체 ID가 포함된 페이지가 나타납니다.

3.5.4. 환경 변수 기반 구성

이러한 환경 변수를 사용하여 OpenShift 이미지에서 JBoss EAP OIDC 지원을 구성합니다.

표 3.1. 환경 변수

환경 변수레거시 SSO 환경 변수설명필수 항목기본값

OIDC_PROVIDER_NAME

없음. SSO_* 환경 변수가 사용되는 경우 "rh-sso" 이름이 내부적으로 설정됩니다.

OIDC_PROVIDER_NAME 변수를 사용할 때 rh-sso 로 설정해야 합니다.

제공됨

 

OIDC_PROVIDER_URL

$SSO_URL/realms/$SSO_REALM

공급자의 URL입니다.

제공됨

 

OIDC_USER_NAME

SSO_USERNAME

동적 클라이언트 등록에는 토큰을 받으려면 사용자 이름이 필요합니다.

제공됨

 

OIDC_USER_PASSWORD

SSO_PASSWORD

동적 클라이언트 등록에는 토큰을 받으려면 사용자 암호가 필요합니다.

제공됨

 

OIDC_SECURE_DEPLOYMENT_SECRET

SSO_SECRET

secure-deployment 하위 시스템과 인증 서버 클라이언트 모두 알려져 있습니다.

없음

 

OIDC_SECURE_DEPLOYMENT_PRINCIPAL_ATTRIBUTE

SSO_PRINCIPAL_ATTRIBUTE

보안 주체 이름 값을 구성합니다.

없음

rh-sso 의 기본값은 sub (ID 토큰)입니다.

일반적인 값: preferred_username.

OIDC_SECURE_DEPLOYMENT_ENABLE_CORS

SSO_ENABLE_CORS

Single Sign-On 애플리케이션에 대해 CORS를 활성화합니다.

없음

기본값은 False입니다.

OIDC_SECURE_DEPLOYMENT_BEARER_ONLY

SSO_BEARER_ONLY

전달자 토큰만 허용하고 로깅을 지원하지 않는 배포입니다.

없음

기본값은 False입니다.

OIDC_PROVIDER_SSL_REQUIRED

NONE

기본값은 private 및 local 주소와 같은 external이지만 https는 지원하지 않습니다.

없음

외부

OIDC_PROVIDER_TRUSTSTORE

SSO_TRUSTSTORE

realm trustore 파일을 지정합니다. 설정되지 않은 경우 어댑터는 HTTPS 요청을 처리할 때 신뢰 관리자를 사용할 수 없습니다.

없음

 

OIDC_PROVIDER_TRUSTSTORE_DIR

SSO_TRUSTSTORE_DIR

영역 신뢰 저장소를 찾을 디렉터리입니다. 설정되지 않은 경우 어댑터는 HTTPS 요청을 처리할 때 신뢰 관리자를 사용할 수 없습니다.

없음

 

OIDC_PROVIDER_TRUSTSTORE_PASSWORD

SSO_TRUSTSTORE_PASSWORD

realm truststore 암호를 지정합니다. 설정되지 않은 경우 어댑터는 HTTPS 요청을 처리할 때 신뢰 관리자를 사용할 수 없습니다.

없음

 

OIDC_PROVIDER_TRUSTSTORE_CERTIFICATE_ALIAS

SSO_TRUSTSTORE_CERTIFICATE_ALIAS

realm trustore 별칭을 지정합니다. 클라이언트를 등록하려면 인증 서버와 상호 작용해야 합니다.

없음

 

OIDC_DISABLE_SSL_CERTIFICATE_VALIDATION

SSO_DISABLE_SSL_CERTIFICATE_VALIDATION

클라이언트를 등록하기 위해 인증 서버와 상호 작용할 때 인증서 검증을 비활성화합니다.

없음

 

OIDC_HOSTNAME_HTTP

HOSTNAME_HTTP

비보안 경로에 사용되는 호스트 이름입니다.

없음

경로가 검색됩니다.

OIDC_HOSTNAME_HTTPS

HOSTNAME_HTTPS

보안 경로에 사용되는 호스트 이름입니다.

없음

보안 경로가 검색됩니다.

NONE

SSO_PUBLIC_KEY

Single Sign-On 영역의 공개 키입니다. 이 옵션은 사용되지 않으며 공개 키는 OIDC 하위 시스템에서 자동으로 검색됩니다.

없음

설정되어 있으면 이 옵션이 무시되고 있다는 경고가 표시됩니다.