QA@IT

FreeradiusーOpenLDAP でのパスワード有効期限の設定について

11339 PV

VPNの認証サーバーを構築しようとしています。
以下についてご教示頂けないでしょうか。

FreeRADIUSとopenLDAPはyumでインストールしました。

構成:

 PC(Windows7) → CiscoASAルータ → Freeradius(3.0.13) → OpenLDAP(2.4.44)

できること:

 OpenLDAPで登録したユーザーとパスワードを使用して、PC(Windows7)からのVPN認証はOK  

困っていること:

 OpenLDAPの ppolicy でパスワードの有効期限(maxPwdage)を設定しているが、
 パスワードの有効期限が切れた状態でもVPN認証ができてしまう。

知りたいこと:

 FreeradiusからOpenLDAPへの接続では OpenLDAPの ppolicy は効かない(無視される)?

   →ldapsearchコマンドなどでは "password expire" となり、ppolicy が効いていることは確認済み。

 ppolicy 以外で、パスワード有効期限切れの際にVPN認証できないようにする方法はありますか?

FreeRADIUSの各設定ファイルの変更点を記載いたします。

・/etc/raddb/radiusd.conf
    auth = yes 
    auth_badpass = yes
    auth_goodpass = yes

・/etc/raddb/users
    #DEFAULT        Framed-Protocol == PPP
    #       Framed-Protocol = PPP,
    #       Framed-Compression = Van-Jacobson-TCP-IP

    #DEFAULT        Hint == "CSLIP"
    #       Framed-Protocol = SLIP,
    #       Framed-Compression = Van-Jacobson-TCP-IP

    #DEFAULT        Hint == "SLIP"
    #       Framed-Protocol = SLIP

    DEFAULT         Auth-Type = LDAP
                    Fall-Through = Yes

・/etc/raddb/dictionary
    VALUE         Auth-Type               LDAP    5

・/etc/raddb/sites-enabled/default
            ldap

            Auth-Type LDAP {
                    ldap
            }

            ldap

・/etc/raddb/mods-enabled/ldap
            server = 'server01.radius.local'

            port = 389

            identity = 'cn=admin,dc=radius,dc=local'
            password = password

            base_dn = 'dc=radius,dc=local'

追加で必要な情報がありましたら取得しますので、言ってください。

追記:2018/4/6

パスワード変更画面の出力方法について、スクリプトの書き方がわからなかったため、/etc/raddb/mods-available/mschapのlocal_cpwの指定をDB(mariaDB)を使用する方法で設定しました。

local_cpw = "%{sql:UPDATE radcheck set value='%{MS-CHAP-New-NT-Password}' where username='%{SQL-User-Name}' and attribute='NT-Password'}"

パスワード変更画面が表示されるようになりましたが、パスワードの更新ができませんでした。
radiusdのデバッグ結果には以下のエラーが出力されました。

rlm_sql (sql): Reserved connection (12)
(5) mschap: Executing query: UPDATE radcheck set value='0xf57febdc33059efac3c4305c1f58bb1e' where username='testuser01' and attribute='NT-Password'
(5) mschap: ERROR: rlm_sql_mysql: ERROR 1142 (UPDATE command denied to user 'radius'@'localhost' for table 'radcheck'): 42000
(5) mschap: ERROR: SQL query failed: server error
rlm_sql (sql): Released connection (12)
(5) mschap: EXPAND %{sql:UPDATE radcheck set value='%{MS-CHAP-New-NT-Password}' where username='%{SQL-User-Name}' and attribute='NT-Password'}
(5) mschap:    -->
(5) mschap: ERROR: Local MS-CHAPv2 password change - xlat didn't give any result, assuming failure
(5) mschap: ERROR: Password change failed
(5)     [mschap] = reject
(5)   } # authenticate = reject
(5) Failed to authenticate the user

radcheckテーブルにレコードがないため、updateで失敗しているものと推測していますが、
openldapで認証する場合には、やはりスクリプトを使用するしか方法がないでしょうか。

  • 追加の質問(2018/4/6)に回答しました。 -

回答

ご存知かもしれませんが、RADIUSのLDAP認証方法に通常の方式とbind方式とがありますよね。

  • 通常の方式: RADIUSサーバーがLDAP(データベース)からパスワード等の属性を引き出して自分でユーザーを認証する。
  • bind方式: RADIUSサーバが自分で認証するかわりにLDAPのbindを行ない、LDAP(データベース)の認証をあたかも自分の認証であるかのように見せる。

FreeradiusからOpenLDAPへの接続では OpenLDAPの ppolicy は効かない(無視される)?

ppolicyはOpenLDAP側の機能なので、bind方式のときは効き、通常方式のときは効かないはずです。

ppolicy 以外で、パスワード有効期限切れの際にVPN認証できないようにする方法はありますか?

僕はやったことはないのでただの思い付きですが、例えばLDAP側にユーザー毎の有効期限切れの日時の属性を持たせておけるなら、それをRADIUSのExpiration属性にマッピングし、RADIUS側でパスワード有効期限切れをチェックすることもできそうな気がします。(通常の方式を使う場合の話)。

設定の修正案
設定を以下のように直して試してみるとよいのではないかと思います。うまく行かないかもしれませんが、radiusd -Xでデバッグログを見て調査するとよいです。(bind方式を使う場合の話)。

/etc/raddb/users

    #DEFAULT        Framed-Protocol == PPP
    #       Framed-Protocol = PPP,
    #       Framed-Compression = Van-Jacobson-TCP-IP

    #DEFAULT        Hint == "CSLIP"
    #       Framed-Protocol = SLIP,
    #       Framed-Compression = Van-Jacobson-TCP-IP

    #DEFAULT        Hint == "SLIP"
    #       Framed-Protocol = SLIP

    #DEFAULT         Auth-Type = LDAP
    #                Fall-Through = Yes

/etc/raddb/sites-enabled/default

authorize {
  ...
  ldap
  if ((ok || updated) && User-Password) {
    update control {
      Auth-Type := LDAP
    }
  }
  ...
}

authenticate {
  ...
  Auth-Type LDAP {
    ldap
  }
  ...
}
...
post-auth {
  ...
#  ldap
  ...
}

追記(2018/03/24)
通常の方式を使う場合ですが、こちらでやってみたらうまく行ったので、設定例を書いておきます。RADIUSのExpiration属性へのマッピングは行わず、ppolicyの属性pwdChangedTimeとpwdMaxAgeを見て直接パスワード有効期限切れをチェックしています。タイムスタンプ関連でmysqlを使っているのでsqlモジュールも必要です。(ちなみに上のbind方式もこちらではうまく行きました。)

/usr/local/etc/raddb/dictionary

ATTRIBUTE       pwdMaxAge       3000    integer
ATTRIBUTE       pwdChangedTime  3001    string
ATTRIBUTE       pwdChangedTime1 3002    integer
ATTRIBUTE       pwdExpires      3003    date
ATTRIBUTE       pwdCurrent      3004    date

/usr/local/etc/raddb/mods-enabled/ldap

  update {
    ...
    control:pwdChangedTime          := 'pwdChangedTime'
    ...
  }

/usr/local/etc/raddb/sites-enabled/default

authorize {
  ...
  -ldap
  update control {
    pwdMaxAge := "%{ldap:ldap:///ou=Policies,dc=example,dc=com?pwdMaxAge?one?(cn=passwordDefault)}"
    pwdChangedTime1 := "%{sql:SELECT UNIX_TIMESTAMP(CONVERT_TZ(STR_TO_DATE('%{control:pwdChangedTime}', '%%Y%%m%%d%%H%%i%%sZ'), '+0:00', '+9:00'))}"
    pwdExpires := "%{expr:%{control:pwdChangedTime1} + %{control:pwdMaxAge}}"
    pwdCurrent := "%{expr:%l}"
  }
  if (control:pwdCurrent > control:pwdExpires) {
    reject
  }
  ...
}

authenticate {
  ...
#  Auth-Type LDAP {
#    ldap
#  }
  ...
}

参考にしたスレ
http://lists.freeradius.org/pipermail/freeradius-users/2012-February/059147.html

Operational属性について
LDAP側でユーザー毎のOperational属性を持っていて、その中にpwdChangedTime属性があるので、RADIUS側でパスワード有効期限切れのチェックができます。

引用元
http://www.zytrax.com/books/ldap/ch6/ppolicy.html

Operational Attributes
The ppolicy module uses a number of operational attributes in the user entry to indicate account status and to allow the administrator to unlock the account following a lockout condition.
pwdChangedTime
pwdChangedTime last-password-change-time
Read only attribute. This attribute indicates the last time the password for the entry was changed.

編集 履歴 (8)
  • 質問文に設定ファイルの内容が追記されていたので回答の後半部分をそれに合わせて書き直しました。 -
  • 通常の方式の場合の設定例を追記しておきました。 -
  • タイムゾーンの変換してなかったので修正しました。 -
  • 認証はmschapv2を使用するので、通常方式の認証となるようです。
    記載いただきました通常方式の設定をする際は、bind方式の設定例の方は、特に設定しなくて良いしょうか。
    -
  • はい、それなら通常方式ですね。ちなみにusersファイルの設定は初期状態に戻してください。Auth-Typeはradiusdに自動判定させます。LDAP側から読むパスワードはmschapv2互換である必要があります(http://deployingradius.com/docを参照。認証がうまく行ったらパスワード有効期限切れのチェックを追加してください。その際mysqlが使えるようにしてください。 -
  • 互換性が記載されたURLが字数制限で切れてしまったようなので書き直します。http://deployingradius.com/documents/protocols/compatibility.html -
  • 教えて頂いた設定でppolicyが効くようになりました。
    パスワードの有効期限切れた後にVPNクライアントでVPNログインした際「パスワードを変更してください」の様なメッセージが表示され、新しいパスワードに変更できるようにすることは可能でしょうか。
    -

パスワードの有効期限切れた後にVPNクライアントでVPNログインした際「パスワードを変更してください」の様なメッセージが表示され、新しいパスワードに変更できるようにすることは可能でしょうか。

マニュアルのPassword changes > Localのやり方でできそうな気はしますが、Cisco ASAルーターとの連携ができるかはよく分かりません。試してみるとよいかもしれません。

マニュアルのURL
https://github.com/FreeRADIUS/freeradius-server/blob/v3.0.x/doc/modules/mschap.rst

追記(2018/4/5)

マニュアルに書いてある設定ですが、どのファイルに書けば良いかわかりますでしょうか。

まずステップ1として、radiusdからCiscoASAルータにAccess-Rejectを返すさいにパスワードが有効期限切れだと伝える設定が必要ですが、マニュアルには下の例が載っています。

update control {
  # U == user
  # e == expired
  SMB-Account-Ctrl-Text := '[Ue]'
}

僕はこれはraddb/sites-enabled/defaultに書くイメージなのかなと思って読みました(あくまで僕の主観です)。ただこれは一つの例というかヒントであって必ずしもこれに合わせる必要はない気がします。ta513さんが都合がいいようなやり方にすればよいのではと思います。

次にステップ2として、CiscoASAルータからradiusdにAccess-Requestが来てパスワード変更要求があったときの設定が必要ですが、これはpasschangeセクションで、mschapモジュールの設定ファイル内です。元々コメントになっているはずです。

raddb/mods-enabled/mschap

  passchange {
    ...
#    local_cpw = "%{exec:/path/to/script %{mschap:User-Name} %{MS-CHAP-New-Cleartext-Password}}"
    ...
  }

mschapのパスワード変更はRADIUSプロトコルでサポートされているものですので、念のためRFCの参照先を書いておきます。見ておくとよいかもしれません。

https://tools.ietf.org/html/rfc2548

追記(2018/4/7)

radcheckテーブルにレコードがないため、updateで失敗しているものと推測していますが、
openldapで認証する場合には、やはりスクリプトを使用するしか方法がないでしょうか。

  • エラーの原因はta513さんの推測通りだと思います。これは言わずもがなではあるのですが、ユーザーがRDB上に存在するならRDB上のパスワードを変更しますが、LDAP上に存在するならLDAP上のパスワードを変更します。LDAP上のパスワード変更にはスクリプトを書く必要があるかというと、通常は何らかのスクリプト的なものは書くことになると思います。

  • パスワード有効期限切れをクライアント(CiscoASAルータ)に通知し、パスワード変更を促したいときには、SMB-Account-Ctrl-Text属性のexpiredフラグを立てる必要があり、クライアントからパスワード変更要求が来て、DB (file OR sql OR ldap)内のパスワードの更新が成功したときにはexpiredフラグを落とす必要があることから、何らかのスクリプト的な処理は必要だと思います。ただやり方によってはFreeRADIUSのunlangで書けるかもしれません。

  • マニュアルに載っている例を見ると%{sql:...}展開を使う場合もRDBのストアドプロシージャの中でパスワードの更新とともにSMB-Account-Ctrl-Text属性の更新もしています(expiredフラグを落とす必要があるため)。なのでDBとしてsqlを使う場合も結局スクリプト的なものは必要なわけですよね。つまりスクリプトを書く必要があるのはDBとしてldapを使う場合だけの話でもないと思います。

  • マニュアルには%{sql:...}展開と%{exec:...}展開が例として載っていますが、たぶん%{ldap: ...}展開も使えるのではないでしょうか。%{ldap: ...}展開が使えるならスクリプトではなくFreeRADIUSのunlangで何とかなるかもしれません。ただこれはLDAPクエリなので更新はできないかもしれませんが。

  • %{exec:...}展開を使う場合は、スクリプトを書いて、その中でOpenLDAPのクライアント側のコマンドを叩くというのが僕の抱いているイメージです。これはそんなに難しい話でもないとは思うのですが、SMB-Account-Ctrl-Text属性をどうするかは微妙な点のような気がします。

  • %{exec:...}展開を使う場合、スクリプトを起動するときにパスワードを引数で渡すのはセキュリティ的に気持ち悪い気がします。なので%{exec:...}展開よりは%{sql:...}展開の方がベターな気はします。単なる思いつきですが、もしそういうことが可能だとして、RDBのストアドプロシージャの中でLDAPの更新ができればいいかもしれませんが。

  • あと僕はよく知らないのですが、CiscoASAルータとOpenLDAPを直に連携させた方が話が簡単になるってことはありませんか。もしそれでta513さんがやりたいことができるなら、間にFreeRADIUSを挟まなくてもよい気がします。それができないから質問されているのだったらごめんなさい。

編集 履歴 (4)
  • マニュアルに書いてある設定ですが、どのファイルに書けば良いかわかりますでしょうか。 -
  • 設定ファイルについて追記しました。 -
  • クイックに回答頂きありがとうございます。
    local_cpwに指定するscriptなのですが、何を指定すれば良いでしょうか。
    独自にスクリプトを組む必要がありますか?
    -
  • スクリプトを組む必要があると思います。 -
  • スクリプトでldappasswdを実行してパスワードを更新する方式で進めることにしました。
    ただ、ldappasswdでパスワード更新をするとハッシュの方式がデフォルトでsshaとなるため、認証ができなくなりました。そのため、cleartextでパスワードを格納するように変更したいと考えております。
    -
  • 設定は/etc/openldap/slapd.dに階層構造で格納されており、ldapaddでldifファイルから追加するしかないかと思っているのですが、ldapaddでの設定方法がわからなく困っております。ハッシュの方式の設定方法をご存知でしたら、ご教示いただきたいです。 -
  • すみません。ldapmodifyでolcPasswordHashを設定したらCLEARTEXTで格納できました。 -
  • パスワードの変更機能ですが、execでシェルスクリプトを実行させることで、実現する事ができました。
    また、以下のサイトを参考にし、mariadbを使わずにパスワード有効期限の計算をするように変更し、シェルの中でShadowLastChangeをldapmodifyで更新し、パスワード更新後にパスワード更新日も更新されるようにしました。
    -
  • 参考URL http://lists.freeradius.org/pipermail/freeradius-users/2012-February/059160.html -
  • あと、追加で教えていただきたいのですが、FreeRADIUSの同時接続ユーザー数はいくつかわかりますでしょうか。また、設定の変更方法などもご存知でしたら、教えていただきたいです。 -
  • 基本ステートレスなので制限は特にないと思います。UDPでradiusdにリクエストが飛んできて、UDPでリプライを返したら終わりで、radiusdは接続とかセッションのようなものは維持してないです。 -
  • 例外としてはEAPがあって、EAPセッションがありますけど、たぶんEAPは使ってないですよね? -
  • セッションは関係ないんですね。EAPは使っていないです。
    ログイン処理の同時実行数などもないですかね?
    -
  • そうですね。関係ないです。ログイン処理の同時実行数はeapのmax_sessionsで設定しますが、EAPを使ってないのでそれも関係ないと思います。
    リクエスト数についてはradiusd.confにmax_requestsという設定があり、これは全体に共通ですが、通常はデフォルトのままでよいと思います。
    -
  • ご回答ありがとうございます。
    要件を満たすサーバーを作ることができました。
    いろいろと聞いてしまいましたが、ご協力いただけたことを本当に感謝いたします。
    -

 間違えて投稿

編集 履歴 (1)
ウォッチ

この質問への回答やコメントをメールでお知らせします。