DjangoシングルサインオンとMicrosoft Active Directory

開始する


Python + Djangoで企業で使用するためのWebアプリケーションを開発する必要がありました。 そして、私が解決しなければならなかった最初の問題は、サイトでの透過的な認証またはシングルサインオン(SSO)でした。

同社はMicrosoft Active Directoryに基づくディレクトリサービスを広く使用しており、現在ではほとんどすべての企業アプリケーションでWindows認証を使用でき、ログイン/パスワードを絶えず入力する必要がないため、新しいアプリケーションは既存の状況を満たし、上記の可能性を実現する必要がありました透過的な「ユーザー認証。

DjangoにSSOを実装する問題について多くの記事が書かれていますが、必要なものを実装するには比較的長い時間がかかりました。 したがって、情報の長い検索とその作業スキームへの組み立てから一部のユーザーを救うために、Active Directoryアカウントを使用してDjangoアプリケーションで透過的な認証を行う方法に関するマニュアルを提供します。

だから私たちは持っています:


する必要がある:


多数の公開された記事と説明を検討した結果、次の2つの主要な手順を完了することで目的の結果を達成できることが明らかになりました。

手順1:Kerberosを使用して透過認証を構成する


Kerberosプロトコルを使用して、Windows ADネットワークにSSOの原則を実装できることは明らかです。 したがって、構成の最初の段階の主なタスクは、Linux + ApacheにKerberosをインストールし、Windows ADドメインコントローラーとの通信をセットアップすることです。

LinuxサーバーにKerberosをインストールして構成する


/ etc / hosts、 srv-appの /etc/resolv.conf、DC-1のDNSを構成します

srv-appで、 / etc / hostsに追加します。

192.168.7.105 srv-app.company.ru 

srv-appで、 / etc / resolv.confを変更します(実際には、このファイルはCentOS7のNetworkManagerによって生成されるため、/ etc / sysconfig / network-scripts / ifcfg-eth0を変更する必要があります)。

  search company.ru nameserver 192.168.7.110 

DC-1ドメインコントローラーの場合:


Kerberosを使用するためのモジュールをインストールする


  [root@srv-app ~]# yum install mod_auth_kerb #    apache [root@srv-app ~]# yum install krb5-workstation #       kerberos 

/etc/krb5.confファイルを編集してKerberosを構成します

  [logging] default = FILE:/var/log/krb5libs.log kdc = FILE:/var/log/krb5kdc.log admin_server = FILE:/var/log/kadmind.log [libdefaults] dns_lookup_realm = false ticket_lifetime = 24h renew_lifetime = 7d forwardable = true rdns = false default_realm = COMPANY.RU default_ccache_name = KEYRING:persistent:%{uid} [realms] COMPANY.RU = { kdc = 192.168.7.110 admin_server = 192.168.7.110 } [domain_realm] .company.ru = COMPANY.RU company.ru = COMPANY.RU 

構成ファイルの内容に関する簡単な説明:


srv-appコンピューターでkerberosの動作をいくつか確認します

前に、ドメインコントローラーで、パスワードP @ ssw0rdを使用してユーザーsrv-apacheを作成しました。 kinitユーティリティを使用してCDにログインしてみましょう。

  [root@dsrv-app ~]# kinit svc-apache@COMPANY.RU Password for admin@COMPANY.RU: **** 

エラーがない場合は、持っているチケットを見てみましょう。

  [root@srv-app ~]# klist Ticket cache: FILE:/tmp/krb5cc_0 Default principal: srv-apache@COMPANY.RU Ticket cache: KEYRING:persistent:0:0 Default principal: svc-apache@COMPANY.RU Valid starting Expires Service principal 20.12.2015 16:12:59 21.12.2015 02:12:59 krbtgt/COMPANY.RU@COMPANY.RU renew until 27.12.2015 16:12:55 

したがって、kerberosを使用してCDにログインし、切断して削除します
受け取ったチケット:

  [root@srv-app ~]# kdestroy 

すべてが機能する場合は、さらに設定するために、認証サービス用のkrb5.keytabファイルを作成する必要があります
Apacheおよびmod_auth_kerb。

Windowsドメインコントローラーでのキータブの生成

ktpass.exeコマンドを使用して、 DC-1ドメインコントローラーでキータブを生成できます。

  ktpass.exe /princ HTTP/srv-app.company.ru@COMPANY.RU /mapuser svc-apache@COMPANY.RU /crypto ALL /ptype KRB5_NT_PRINCIPAL /mapop set /pass P@ssw0rd /out c:\share\keytab 


その結果、ファイルc:\ share \ keytabを取得します。これはsrv-appにコピーし、/ etc / krb5.keytabという名前を付ける必要があります。 次に、httpdサーバーを実行しているアカウントのユーザーにこのファイルへのアクセスを提供する必要があります。 私たちの場合、それはapacheです。 Apacheがこのファイルを読み取るために、すべてのユーザーにファイルの読み取りを許可します。

  chmod a+r /etc/krb5.keytab 

キータブが次のように機能するかどうかを確認します。

1. ktutilの使用:

  [root@srv-app ~]# ktutil ktutil: rkt /etc/krb5.keytab ktutil: list slot KVNO Principal ---- ---- --------------------------------------------------------------------- 1 3 HTTP/srv-app.company.ru@COMPANY.RU 2 3 HTTP/srv-app.company.ru@COMPANY.RU 3 3 HTTP/srv-app.company.ru@COMPANY.RU 4 3 HTTP/srv-app.company.ru@COMPANY.RU 5 3 HTTP/srv-app.company.ru@cCOMPANY.RU ktutil: q 


2. kvnoの使用:

  #   KDC [root@srv-app ~]# kinit svc-apache Password for svc-apache@COMPANY.RU: #     <b>HTTP/srv-app.company.ru@COMPANY.RU</b>         keytab [root@srv-app ~]# kvno HTTP/srv-app.company.ru@COMPANY.RU HTTP/srv-app.company.ru@COMPANY.RU: kvno = 3 #   [root@srv-app ~]# kdestroy 

Apacheを構成する

以下は/etc/httpd/conf.d/company_main.confファイルです。このファイルには、「/」URIにアクセスするときにKerberos認証を構成するための構成手順が含まれています。

  <Location "/"> # Kerberos authentication: AuthType Kerberos AuthName "SRV-APP auth" KrbMethodNegotiate on KrbMethodK5Passwd off KrbServiceName HTTP/srv-app.company.ru@COMPANY.RU KrbAuthRealms COMPANY.RU Krb5Keytab /etc/krb5.keytab KrbLocalUserMapping On Require valid-user </Location> 

KrbMethodK5Passwdをオフに設定することに注意を払いたいです。 指定された設定は、サイトの指定されたセクションに入ると、 シングルサインオンテクノロジーを使用してKerberos認証が実行されるという事実につながります。 認証に失敗すると、すぐに「401 Unautorized」エラーが発生します。 ただし、設定をKrbMethodK5Passwd onに変更すると、 シングルサインオン認証に失敗した後、名前とパスワードによる認証が試行されます。

さらに、ドキュメント化されていないもう1つの利点を活用します。KrbLocalUserMappingOnを設定すると、登録ユーザー名がREMOTE_USER変数に配置されます( KrbLocalUserMapping Offの場合、REMOTE_USERにはユーザー名@ COMPANY RUが含まれます)。

mod_auth_kerbモジュール設定の詳細については、 こちらをご覧ください

Windowsワークステーションからのシングルサインオン(SSO)

Active Directoryに保存されている企業アカウントを使用してLinuxで公開されたマシンのポータルページにアクセスできるようにするために、LinuxでのKerberos認証の設定に関するすべての作業が行われたことをもう一度繰り返します。さらに、このエントリは「透明」である必要があります"、パスワードを要求せずに、シングルサインオン(SSO)テクノロジを使用して実現します。このテクノロジは、Windows 7以降およびInternet Explorer(およびMozilla Firefox)でサポートされています。

ただし、すべてがスムーズに進むためには、そのような入力が行われるワークステーションで、次の設定を行う必要があります。

  1. ログインしているサイト(この場合、メニューを使用してsrv-app.company.ruをローカルイントラネットサイトに入力する必要があります)
    Internet Explorer: ツール->インターネットオプション->セキュリティ->ローカルイントラネット->ホスト->詳細
  2. IEでは、次のオプションを有効にする必要があります(原則として、デフォルトで有効になっています)。
    • [ツール]-> [インターネットオプション]-> [詳細]-> [セキュリティ]-> [ビルトインWindowsチェックを許可する] = [オン]
    • ツール->インターネットオプション->セキュリティ->ローカルイントラネット->このゾーンのセキュリティレベル->その他->ユーザー認証->イントラネットゾーンのみでの自動ネットワークログオン

  3. また、IPアドレスでサイトにアクセスしようとすると、SSOは機能しません。 必ずドメイン名srv-app.company.ruを使用してください!

ステージ2. Djangoでのユーザー認証


したがって、最初の段階で実行された作業の結果、次の結果が得られました。


ただし、Apacheサーバーがユーザーを承認したという事実にもかかわらず、Djangoアプリケーションについてはまだ不明であるため、ユーザーを認証/承認し、Djangoでセッションを使用するためのメカニズム全体は未使用のままです。

RemoteUserBackendを使用する


特にそのような目的のために、Djangoには、IISやApacheなどの外部アプリケーションによってすでに認証されているユーザーのシステムに認証メカニズムを含む簡単なソリューションがあります(手順1:mod_authnz_ldap、CAS、Cosign、WebAuth、mod_auth_sspi、mod_auth_krbと同様の方法を使用します)。

公式djangoproject.org Webサイトの説明によると、このようにDjangoで透過的な認証と承認を使用するには、次の手順に従ってRemoteUserBackendメカニズムを使用するだけで十分です。

1. Djangoプロジェクトの設定ファイルsettings.pyで、 django.contrib.auth.middleware.AuthenticationMiddlewareの直後にMIDDLEWARE_CLASSESリストにdjango.contrib.auth.middleware.RemoteUserMiddlewareを追加します。

 MIDDLEWARE_CLASSES = [ '...', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.RemoteUserMiddleware', '...', ] 

2.同じ場所で、 AUTHENTICATION_BACKENDSリストのModelBackendRemoteUserBackendに置き換えます(または、このリストをsettings.pyに追加します)。

 AUTHENTICATION_BACKENDS = [ 'django.contrib.auth.backends.RemoteUserBackend', ] 

これらの変更の結果、 RemoteUserMiddlewarerequest.META ['REMOTE_USER']を介してユーザー名を取得し、 RemoteUserBackendを使用してこのユーザーを自動的に認証および承認します 。 さらに、 RemoteUserBackendは、この権限を持つauth_userテーブルに新しいユーザーを追加し、Djangoの標準のアカウント管理メカニズムを使用します。

ただし、残念ながら、この方法ではActive Directoryから取得して、アプリケーションで必要な情報(first_name、last_name、メール、ADグループへの参加など)を使用することはできません。

django-auth-ldapを使用する


そのため、LDAPを使用してActive Directoryにアクセスする必要があります。 幸いなことに、 django-auth-ldapはこれらの目的に最適なDjangoアプリケーションです。 pipで標準的にインストールできます:

 pip install django-auth-ldap 

その後、前のセクションで追加したMIDDLEWARE_CLASSES 、つまり'django.contrib.auth.middleware.RemoteUserMiddleware'をsettings.pyから削除する必要があり、 AUTHENTICATION_BACKENDSのリストは次のようになります。

 AUTHENTICATION_BACKENDS = ( 'django_auth_ldap.backend.LDAPBackend', 'django.contrib.auth.backends.ModelBackend', ) 

さらに、次の構成パラメーターをsettings.pyに含める必要があります。

settings.py
 # Baseline LDAP configuration. AUTH_LDAP_SERVER_URI = "ldap://DC-1.COMPANY.ru" AUTH_LDAP_AUTHORIZE_ALL_USERS = True AUTH_LDAP_PERMIT_EMPTY_PASSWORD = True #          LDAP ( ) AUTH_LDAP_BIND_DN = "cn=svc-apache,cn=Users,dc=company,dc=ru" AUTH_LDAP_BIND_PASSWORD = "P@ssw0rd" #         OU Django    Users, #   login    sAMAccountName AUTH_LDAP_USER_SEARCH = LDAPSearchUnion( LDAPSearch("ou=Django,dc=company,dc=ru", ldap.SCOPE_SUBTREE, "(sAMAccountName=%(user)s)"), LDAPSearch("cn=Users,dc=company,dc=ru", ldap.SCOPE_SUBTREE, "(sAMAccountName=%(user)s)"), ) # Set up the basic group parameters. AUTH_LDAP_GROUP_SEARCH = LDAPSearch("ou=Groups,ou=Django,dc=company,dc=ru", ldap.SCOPE_SUBTREE, "(objectClass=group)" ) AUTH_LDAP_GROUP_TYPE = GroupOfNamesType(name_attr="cn") # Simple group restrictions # AUTH_LDAP_REQUIRE_GROUP -   DN   ,        #         #   ,             "active" AUTH_LDAP_REQUIRE_GROUP = "cn=active,ou=Groups,ou=Django,dc=company,dc=ru" # AUTH_LDAP_DENY_GROUP -   DN   ,         #      AUTH_LDAP_DENY_GROUP = "cn=disabled,ou=Groups,ou=Django,dc=company,dc=ru" # Populate the Django user from the LDAP directory. #      AD     Django AUTH_LDAP_USER_ATTR_MAP = { "first_name": "givenName", "last_name": "sn", "email": "mail" } #      AD     Django AUTH_LDAP_PROFILE_ATTR_MAP = { "employee_number": "employeeNumber" } #     is_active, is_staff  is_superuser     AD #  is_active   django_remote_auth_ldap          #      Django    AUTH_LDAP_REQUIRE_GROUP (.) AUTH_LDAP_USER_FLAGS_BY_GROUP = { "is_active": "cn=active,ou=Groups,ou=Django,dc=company,dc=ru", "is_staff": "cn=staff,ou=Groups,ou=Django,dc=company,dc=ru", "is_superuser": "cn=superuser,ou=Groups,ou=Django,dc=company,dc=ru" } #          AD AUTH_LDAP_PROFILE_FLAGS_BY_GROUP = { "is_awesome": "cn=awesome,ou=Groups,ou=Django,dc=company,dc=ru", } # This is the default, but I like to be explicit. AUTH_LDAP_ALWAYS_UPDATE_USER = True # Use LDAP group membership to calculate group permissions. AUTH_LDAP_FIND_GROUP_PERMS = True # Cache group memberships for an hour to minimize LDAP traffic AUTH_LDAP_CACHE_GROUPS = True AUTH_LDAP_GROUP_CACHE_TIMEOUT = 3600 


Django-auth-ldapは素晴らしいアプリです。 その助けを借りて、ADに関するDjangoのユーザープロフィールからほぼすべての情報を読み込むことができ、このユーザーが対応するDjangoモデルに参加しているグループを「プルアップ」することもできます。

ただし、django-auth-ldapを使用した認証の場合、ユーザーにユーザー名とパスワードを尋ねる必要がありますが、これは厳密には私たちには適していません。

ドキュメントには、 django-auth-ldapRemoteUserBackendを 「クロス」できると記載されていますが、

非LDAPユーザー
LDAPバックエンドには、権限に関するもう1つの機能があります。これは、認証されなかったユーザーの承認を処理する機能です。 たとえば、RemoteUserBackendを使用して、外部認証されたユーザーをDjangoユーザーにマッピングしている場合があります。 AUTH_LDAP_AUTHORIZE_ALL_USERSを設定することにより、LDAPBackendはこれらのユーザーを通常の方法でLDAPユーザーにマッピングし、認証情報を提供します。 これはAUTH_LDAP_MIRROR_GROUPSでは機能しないことに注意してください。 グループミラーリングは、認証ではなく承認の機能です。

しかし、これは、必要に応じて機能しません。 ユーザーは実際にログインできますが、これはまさにRemoteUserBackendを使用した場合とまったく同じです(前のセクションを参照)。 ADからの情報は、Djangoユーザープロファイルに自動的に読み込まれません。

もちろん、次の推奨事項を使用してこれを自分で行うことができます。

ユーザーの更新
デフォルトでは、マップされたすべてのユーザーフィールドは、ユーザーがログインするたびに更新されます。 これを無効にするには、AUTH_LDAP_ALWAYS_UPDATE_USERをFalseに設定します。 認証プロセスの外部でユーザーを取り込む必要がある場合(たとえば、ユーザーが初めてログインする前に関連するモデルオブジェクトを作成する場合)、django_auth_ldap.backend.LDAPBackend.populate_user()を呼び出すことができます。 LDAPBackendのインスタンスが必要になります。このインスタンスは自由に作成してください。 populate_user()は、ユーザーを返すか、ユーザーがLDAPで見つからなかった場合はNoneを返します。

 from django_auth_ldap.backend import LDAPBackend user = LDAPBackend().populate_user('alice') if user is None: raise Exception('No user named alice') 


django-remote-auth-ldapの使用


しかし、結局のところ、すべてがはるかに簡単です。 自転車はすでに発明されており、利用できるのは私たちだけです。 django-remote-auth-ldapアプリケーションはdjango_auth_ldapの小さなアドオンであり、ユーザーを認証し、認証中にADからユーザーのデータを追加の労力なしでロードできます。

django-remote-auth-ldapを標準としてインストールします(このアドインが機能するにはdjango-auth-ldapも必要です):

 pip install django-remote-auth-ldap 

次に、次の設定をsettings.pyに追加します。

 DRAL_CHECK_DOMAIN = False 

事実、 django-remote-auth-ldapはIISと連携するように設計されているようです。IISは、REMOTE_USER変数を「DOMAIN / username」の形式で設定しますが、ドメイン名がREMOTE_USERに入らないようにmod_auth_kerbを設定しました。 上記の設定により、 django-remote-auth-ldapは、REMOTE_USERにドメインなしのユーザー名が1つだけであると想定します。 まさに必要なものです。
繰り返しますが、 MIDDLEWARE_CLASSESAUTHENTICATION_BACKENDSの設定に関する推奨事項:

1. Djangoプロジェクトの設定ファイルsettings.pyで、 django.contrib.auth.middleware.AuthenticationMiddlewareの直後にMIDDLEWARE_CLASSESリストにdjango.contrib.auth.middleware.RemoteUserMiddlewareを追加します。

 MIDDLEWARE_CLASSES = [ '...', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.RemoteUserMiddleware', '...', ] 

2. AUTHENTICATION_BACKENDSは次のようになります。

 AUTHENTICATION_BACKENDS = [ 'django_remote_auth_ldap.backend.RemoteUserLDAPBackend', ] 

それだけです。Djangoで透過的な認証が設定されています。

Djangoアプリケーションに含まれるユーザーがActive Directoryで既に承認されている場合、次のようになります。

1. Apacheは Kerberosを使用してそれを認証し、アプリケーションのページにアクセスし、認証されたユーザーの名前をREMOTE_USERに書き込みます。
2. RemoteUserMiddlewareはREMOTE_USERの値を「認識」し、 django-remote-auth-ldapを使用してDjangoで指定されたユーザーの認証と承認を開始します
3. django-remote-auth-ldapは、 django-auth-ldapアプリケーションから継承したメソッドを使用してユーザーを認証および承認します。これにより、DjangoのActive Directoryから必要な情報が取得されます。

Source: https://habr.com/ru/post/J274931/


All Articles