Shibboleth IdP+MFA Serverで多要素認証(Shibboleth IdP設定+確認編)

こんにちは、サイオステクノロジー技術部の小山です。

Shibboleth IdP+MFA Serverの後編です。

前回でMFA Server側は設定出来たので、今回はShibboleth IdP側の設定を行います。

Shibboleth IdPに必要な設定

Shibboleth IdPでは、多要素認証の出し分けのため下記2つの設定を行います。

・MFA Server用の認証フロー作成

・認証フローの出し分け(内部はパスワード、外部はパスワード+MFA)

MFA Server用の認証フロー作成

パスワード+MFAの認証を行うため、MFA Server用の認証フローを作成します。

Shibboleth IdPの認証フローは、下記のような設定ファイルで構成されています。

・xxxx-authn-flow.xml
・xxxx-authn-beans.xml
・xxxx-authn-config.xml

今回はMFA利用時の認証先をMFA Serverに切り替えるため、通常のパスワード認証フローを参考にMFA Server用のLDAP認証フローを作成していきます。

・作業対象:Shibboleth IdPサーバー

MFA Server認証フロー用のディレクトリを作成します。

# cd /opt/shibboleth-idp/
# mkdir flows/authn/Password2

パスワード認証フローをコピーします。その後、読み込むべきbeansファイル名を置換します。

# cp system/flows/authn/password-authn-flow.xml flows/authn/Password2/mfas-authn-flow.xml
# cp system/flows/authn/password-authn-beans.xml flows/authn/Password2/mfas-authn-beans.xml
# sed -i 's/password-authn-beans.xml/mfas-authn-beans.xml/' flows/authn/Password2/mfas-authn-flow.xml

beansファイルがMFA用の設定ファイルを参照するよう置換します。

# sed -i 's/password-authn-config.xml/passwordmfa-authn-config.xml/' flows/authn/Password2/mfas-authn-beans.xml 

パスワード認証フロー用の設定ファイルをコピーし、読み込むべき設定ファイル名を置換します。

# cp conf/authn/password-authn-config.xml conf/authn/passwordmfa-authn-config.xml
# sed -i 's/ldap-authn-config.xml/ldapmfa-authn-config.xml/' conf/authn/passwordmfa-authn-config.xml

general-authn.xmlにフローを登録します。

# vim conf/authn/general-authn.xml
...
         <bean id="authn/Password" parent="shibboleth.AuthenticationFlow"
                 p:passiveAuthenticationSupported="true"
                 p:forcedAuthenticationSupported="true" />
 
+        <bean id="authn/Password2" parent="shibboleth.AuthenticationFlow"
+                p:passiveAuthenticationSupported="true"
+                p:forcedAuthenticationSupported="true" />
+
         <bean id="authn/Duo" parent="shibboleth.AuthenticationFlow"
                 p:forcedAuthenticationSupported="true"
                 p:nonBrowserSupported="false">
             <!--
             The list below should be changed to reflect whatever locally- or
             community-defined values are appropriate to represent MFA. It is
             strongly advised that the value not be specific to Duo or any
             particular technology.
             -->

MFAServer用の設定パラメータを作成します。
idp.authn.LDAP~のパラメータ名の末尾に、”2″を追加します。

# cp conf/authn/ldap-authn-config.xml conf/authn/ldapmfa-authn-config.xml
# sed -i -r 's/(idp.authn.LDAP[^:]+)([:}])/\12\2/' conf/authn/ldapmfa-authn-config.xml 

ldap.propertiesに上記で作成したMFAServer用のパラメータを追加します。
LDAPのタイムアウト値は、Azure上で設定した双方向OTP有効時間に合わせ、長めに設定します。

# vim conf/ldap.properties 
# LDAP authentication configuration, see authn/ldap-authn-config.xml
# Note, this doesn't apply to the use of JAAS

+#############################################################################################################################
+# GENERAL
+#############################################################################################################################
+
## Authenticator strategy, either anonSearchAuthenticator, bindSearchAuthenticator, directAuthenticator, adAuthenticator
idp.authn.LDAP.authenticator = directAuthenticator

## Connection properties ##
idp.authn.LDAP.ldapURL = ldap://ldapserver.example.com:389
idp.authn.LDAP.useStartTLS = false
idp.authn.LDAP.useSSL = false
# Time in milliseconds that connects will block
idp.authn.LDAP.connectTimeout = PT3S
# Time in milliseconds to wait for responses
idp.authn.LDAP.responseTimeout = PT3S

## SSL configuration, either jvmTrust, certificateTrust, or keyStoreTrust
#idp.authn.LDAP.sslConfig = certificateTrust
## If using certificateTrust above, set to the trusted certificate's path
idp.authn.LDAP.trustCertificates = %{idp.home}/credentials/ldap-server.crt
## If using keyStoreTrust above, set to the truststore path
#idp.authn.LDAP.trustStore = %{idp.home}/credentials/ldap-server.truststore

## Return attributes during authentication
idp.authn.LDAP.returnAttributes = *

## DN resolution properties ##

# Search DN resolution, used by anonSearchAuthenticator, bindSearchAuthenticator
# for AD: CN=Users,DC=example,DC=org
idp.authn.LDAP.baseDN = ou=users,dc=example,dc=com
idp.authn.LDAP.subtreeSearch = false
idp.authn.LDAP.userFilter = (uid={user})
# bind search configuration
# for AD: idp.authn.LDAP.bindDN=adminuser@domain.com
idp.authn.LDAP.bindDN = cn=manager,dc=example,dc=com
idp.authn.LDAP.bindDNCredential = secret

# Format DN resolution, used by directAuthenticator, adAuthenticator
# for AD use idp.authn.LDAP.dnFormat=%s@domain.com
idp.authn.LDAP.dnFormat = uid=%s,ou=users,dc=example,dc=com
+
+
+#############################################################################################################################
+# MFA
+#############################################################################################################################
+
+idp.authn.LDAP.authenticator2 = directAuthenticator
+idp.authn.LDAP.ldapURL2 = ldap://mfaserver.example.com:389
+idp.authn.LDAP.useStartTLS2 = false
+idp.authn.LDAP.useSSL2 = false
+idp.authn.LDAP.connectTimeout2 = PT240S
+idp.authn.LDAP.responseTimeout2 = PT240S
+#idp.authn.LDAP.sslConfig2 = certificateTrust
+idp.authn.LDAP.trustCertificates2 = %{idp.home}/credentials/ldap-server.crt
+#idp.authn.LDAP.trustStore2 = %{idp.home}/credentials/ldap-server.truststore
+idp.authn.LDAP.returnAttributes2 = *
+idp.authn.LDAP.baseDN2 = ou=users,dc=example,dc=com
+idp.authn.LDAP.subtreeSearch2 = false
+idp.authn.LDAP.userFilter2 = (uid={user})
+idp.authn.LDAP.bindDN2 = cn=manager,dc=example,dc=com
+idp.authn.LDAP.bindDNCredential2 = secret
+idp.authn.LDAP.dnFormat2 = uid=%s,ou=users,dc=example,dc=com
+
+
+#############################################################################################################################
+# RESOLVER
+#############################################################################################################################

# LDAP attribute configuration, see attribute-resolver.xml
# Note, this likely won't apply to the use of legacy V2 resolver configurations
idp.attribute.resolver.LDAP.ldapURL = %{idp.authn.LDAP.ldapURL}
idp.attribute.resolver.LDAP.connectTimeout = %{idp.authn.LDAP.connectTimeout:PT3S}
idp.attribute.resolver.LDAP.responseTimeout = %{idp.authn.LDAP.responseTimeout:PT3S}
idp.attribute.resolver.LDAP.baseDN = %{idp.authn.LDAP.baseDN:undefined}
idp.attribute.resolver.LDAP.bindDN = %{idp.authn.LDAP.bindDN:undefined}
idp.attribute.resolver.LDAP.bindDNCredential = %{idp.authn.LDAP.bindDNCredential:undefined}
idp.attribute.resolver.LDAP.useStartTLS = %{idp.authn.LDAP.useStartTLS:true}
idp.attribute.resolver.LDAP.trustCertificates = %{idp.authn.LDAP.trustCertificates:undefined}
idp.attribute.resolver.LDAP.searchFilter = (uid=$resolutionContext.principal)

# LDAP pool configuration, used for both authn and DN resolution
#idp.pool.LDAP.minSize = 3
#idp.pool.LDAP.maxSize = 10
#idp.pool.LDAP.validateOnCheckout = false
#idp.pool.LDAP.validatePeriodically = true
#idp.pool.LDAP.validatePeriod = PT5M
#idp.pool.LDAP.prunePeriod = PT5M
#idp.pool.LDAP.idleTime = PT10M
#idp.pool.LDAP.blockWaitTime = PT3S
#idp.pool.LDAP.failFastInitialize = false

認証方式出し分けの設定

Shibboleth IdPが内部と外部で認証方式を出し分け出来るように設定します。

・作業対象:Shibboleth IdPサーバー

idp.propertiesで認証方式【MFA】を使用するように変更します。

※ここで指定する【MFA】とは、後述のmfa-authn-config.xmlを使用するための設定です。MFA Serverを指定しているわけではありません。

# vim conf/idp.properties
 # Regular expression matching login flows to enable, e.g. IPAddress|Password
-idp.authn.flows= Password
+idp.authn.flows= MFA

IPアドレスによって、通常の認証フローか、MFA Server用の認証フローか判別する設定を行います。
mfa-authn-config.xmlで以下のように記載します。

# vim conf/authn/mfa-authn-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
       xmlns:context="https://www.springframework.org/schema/context"
       xmlns:util="https://www.springframework.org/schema/util"
       xmlns:p="https://www.springframework.org/schema/p"
       xmlns:c="https://www.springframework.org/schema/c"
       xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
                           https://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
                           https://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd"

       default-init-method="initialize"
       default-destroy-method="destroy">

    <!--
    This is a map of transition rules that guide the behavior of the MFA flow
    and controls how factors are sequenced, skipped, etc. The key of each entry
    is the name of the step/flow out of which control is passing. The starting
    rule has an empty key.

    Each entry is a bean inherited from "shibboleth.authn.MFA.Transition". Per
    the Javadoc for net.shibboleth.idp.authn.MultiFactorAuthenticationTransition:

        p:nextFlow (String)
            - A flow to run if the previous step signaled a "proceed" event, for simple
                transitions.

        p:nextFlowStrategy (Function<ProfileRequestContext,String>)
            - A function to run if the previous step signaled a "proceed" event, for dynamic
                transitions. Returning null ends the MFA process.

        p:nextFlowStrategyMap (Map<String,Object> where Object is String or Function<ProfileRequestContext,String>)
            - Fully dynamic way of expressing control paths. Map is keyed by a previously
                signaled event and the value is a flow to run or a function to
                return the flow to run. Returning null ends the MFA process.

    When no rule is provided, there's an implicit "null" that ends the MFA flow
    with whatever event was last signaled. If the "proceed" event from a step is
    the final event, then the MFA process attempts to complete itself successfully.
    -->
    <util:map id="shibboleth.authn.MFA.TransitionMap">
        <!-- First rule runs the IPAddress login flow. -->
        <entry key="">
            <bean parent="shibboleth.authn.MFA.Transition" p:nextFlowStrategy-ref="checkSecondFactor" />
        </entry>

        <!-- An implicit final rule will return whatever the final flow returns. -->
    </util:map>

    <!-- Example script to see if second factor is required. -->
    <bean id="checkSecondFactor" parent="shibboleth.ContextFunctions.Scripted" factory-method="inlineScript"
        p:customObject-ref="shibboleth.HttpServletRequest">
        <constructor-arg>
            <value>
            <![CDATA[
                logger = Java.type("org.slf4j.LoggerFactory").getLogger("net.shibboleth.idp.attribute");
                nextFlow = "authn/Password2";
                allowIp = "10.8.20."

                logger.debug( '[MFA] clientIP: '+ custom.remoteAddr );

                // Chack IPAddress
                if (custom.remoteAddr.startsWith(allowIp)) {
                    nextFlow = "authn/Password";
                    logger.debug( '[MFA] client IP is internal IP.' );
                } else {
                    logger.debug( '[MFA] client IP is external IP.' );
                }

                logger.debug( '[MFA] Next Flow is ' + nextFlow );
                nextFlow;   // pass control to second factor or end with the first
            ]]>
            </value>
        </constructor-arg>
    </bean>

</beans>

設定が終わったらShibboleth IdPを再起動してください。

これで、Shibboleth IdPの設定も完了しました。

動作確認

最後に、内部ネットワークと外部ネットワークからそれぞれ動作確認を行います。

・作業対象:クライアントPC

内部ネットワークから

内部ネットワークからSPにアクセスし、Shibboleth IdPのログイン画面にリダイレクトされます。

ユーザー名とパスワードを入力して、loginボタンをクリックします。

mfashib30

パスワード認証だけで認証成功します。

mfashib31

Shibboleth IdPのデバッグログを見ると、authn/Password(パスワード認証)が選択されていました。

2018-04-26 02:19:34,864 - DEBUG [net.shibboleth.idp.attribute:5] - [MFA] clientIP: 10.8.20.6
2018-04-26 02:19:34,917 - DEBUG [net.shibboleth.idp.attribute:10] - [MFA] client IP is internal IP.
2018-04-26 02:19:34,927 - DEBUG [net.shibboleth.idp.attribute:13] - [MFA] Next Flow is authn/Password

外部ネットワークから

外部ネットワークからSPにアクセスし、Shibboleth IdPのログイン画面にリダイレクトされます。

ユーザー名とパスワードを入力して、loginボタンをクリックします。

mfashib30

そのまま認証成功とはならず、SMSでOTPを受信します。

そのままOTP入力して返信します。

mfashib32

OTP返信後、Shibboleth IdPの認証画面から遷移し、認証に成功します。

mfashib31

Shibboleth IdPのデバッグログを見ると、authn/Password2(パスワード認証+多要素認証)が選択されていることがわかります。

2018-04-26 02:35:36,551 - DEBUG [net.shibboleth.idp.attribute:5] - [MFA] clientIP: xx.xx.xx.xx(<-外部のIP)
2018-04-26 02:35:36,632 - DEBUG [net.shibboleth.idp.attribute:12] - [MFA] client IP is external IP.
2018-04-26 02:35:36,635 - DEBUG [net.shibboleth.idp.attribute:15] - [MFA] Next Flow is authn/Password2(<-パスワード認証+多要素認証のフロー)

と、いうことで期待通りの動作となりました!

おわりに

MFA ServerとShibboleth IdPを使った多要素認証環境を構築することができました。

実際に動かして分かりましたが、Shibboleth IdPのログイン画面でログイン中、OTPを返信するまで、(OTP返信を待っているため)画面が応答しなくなっているように見えてしまいました。

mfashib30

↑この画面のログイン処理が応答しなくなっているかのような挙動になります。

ログイン画面に注釈を入れるなど、Shibboleth IdP側はもう少し考慮が必要そうです。

以上です。

ご覧いただきありがとうございます! この投稿はお役に立ちましたか?

役に立った 役に立たなかった

2人がこの投稿は役に立ったと言っています。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です