CentOS7でOpenldap構築 - 6.パスワード有効期限切れ通知スクリプト

OpenldapでLDAP環境を構築した際の、LDAPアカウント パスワード運用について、
・パスワード有効期限切れのスクリプト
・パスワード間違いでアカウントロックした際の解除法
上記を紹介します。

　スクリプトについて各自の環境で動かなかったらごめんなさい。。その場合bashやpythonの基本的なことがわかる方は修正してください。

## パスワード有効期限切れ通知スクリプト
　パスワード有効期限切れをメールで通知するスクリプトの構成です。
warn-password-expiration.sh を実行することで、各スクリプトをインクルードしつつ処理を実施します。

構成: 
/スクリプト配置ディレクトリ
  ├ listup-password-expire.py (パスワード期限切れユーザリストアップ)
  ├ send-mail.py (メール送信スクリプト)
  ├ warn-password-expiration.sh (処理実行メインスクリプト)
  └ warn-password-expiration_mail.txt (メール本文のフォーマット)

python-ldapというモジュールをインストールしておきます。

```
yum install python-ldap
```

各スクリプトを配置します。内容について記載します。
#### listup-password-expire.py
　パスワード期限切れのLDAPユーザをリストアップします。パスワード有効期限を90日間に設定しています。LDAPサーバのIPを記載する箇所を修正してください。
　LDAPアカウントの各属性の値を取得していますが、アカウントにその属性が設定されていない場合、エラーとなります。

```python
#!/usr/bin/python
# -*- encoding=utf8 -*-

import os, sys, datetime, codecs, ldap
from datetime import datetime as dt
sys.stdout = codecs.lookup('utf_8')[-1](sys.stdout)

# LDAP接続情報
expireDay = 90
shortlyDay = 7
ldap_uri = u'ldap://LDAPサーバIP/'
base_dn = u'dc=test,dc=org'

# 関数：アカウントのパスワード有効期限が迫っている場合に、残りの時間を返す
def password_expire_shortly ( dn,entry,expireDay,shortlyDay ):
    nowTime = dt.now() - datetime.timedelta(hours=9)
    pwdChangedTimeStr = entry['pwdChangedTime'][0]
    pwdChangedTime = dt.strptime(pwdChangedTimeStr, '%Y%m%d%H%M%SZ')
    pwdExpireTime  = pwdChangedTime + datetime.timedelta(days=expireDay)
    # パスワードの有効期限が迫っているかをチェック（すでに有効期限切れは含まない）
    untilExpirationTime   = pwdExpireTime - nowTime
    if ( datetime.timedelta(days=0) < untilExpirationTime
         and untilExpirationTime < datetime.timedelta(days=shortlyDay)):
        return untilExpirationTime
    else:
        return None

# パスワードが設定されているLDAPアカウントを検索
lo = ldap.initialize(ldap_uri)
lo.set_option(ldap.OPT_NETWORK_TIMEOUT, 10.0)
lo.simple_bind_s()
filterstr = u'(&(objectClass=posixAccount)(pwdChangedTime=*))'
attrlist  = ['+', '*']
result    = lo.search_s(base_dn, ldap.SCOPE_SUBTREE, filterstr, attrlist, )

# パスワードの有効期限が迫っている利用者情報を表示
for dn,entry in result:
    untilExpirationTime = password_expire_shortly ( dn,entry,expireDay,shortlyDay )
    if untilExpirationTime is not None:
        sn        = entry.get ('sn',[''])[0]
        givenName = entry.get ('givenName',[''])[0]
        mail      = entry.get ('mail',[''])[0]
        print u'%s %s;%s;%s' % (unicode(sn,'utf-8'), unicode(givenName,'utf-8'), mail, untilExpirationTime)
```

#### send_mail.py
　メール送信部分のスクリプトです。SMTPサーバはlocalhostとしています。

```python
#!/usr/bin/python
# -*- encoding=utf8 -*-

import sys, smtplib
from email.MIMEText import MIMEText
from email.Header import Header
from email.Utils import formatdate

argvs = sys.argv
argc = len(argvs)

if (argc != 4):
    print 'Usage: # %s from to subject' % argvs[0]
    quit()

# メールの文字コード、アドレス、件名の設定
charset = 'cp932'
from_address = argvs[1]
to_address   = argvs[2]
subject = unicode(argvs[3], 'utf-8')

# 標準入力からメール本文を取得
text = ''
for line in sys.stdin:
    text = text + line
text = unicode(text, 'utf-8')

# 送信メールの作成
msg = MIMEText(text.encode(charset), 'plain', charset)
msg['Subject'] = Header(subject, charset)
msg['From'] = from_address
msg['To'] = to_address
msg['Date'] = formatdate(localtime=True)

# メールの送信
smtp = smtplib.SMTP('localhost')
smtp.sendmail(from_address, to_address, msg.as_string())
smtp.close()
```

#### warn-password-expiration.sh 
　パスワード有効期限切れ通知のメインスクリプトになります。送信元アドレスを修正します。

```bash

#!/usr/bin/bash

MailSubject='パスワード有効期限のお知らせ'                          # 送信メールの件名
MailFrom='xxx@xxx.com'                                              # 送信メールの送信元アドレス
MailTemplate=$(dirname $0)'/warn-password-expiration-mail.txt'      # 送信メール本文のテンプレートファイル
PY_LISTUP_EXSHORT=$(dirname $0)'/listup-password-expire.py' # パスワード期限切れが迫っているアカウントをリストアップするスクリプト
PY_SEND_MAIL=$(dirname $0)'/send-mail.py'                           # メールを送信するスクリプト


# メール送信処理部
function send_mail () {
  user_name="${1}"
  mail_to="${2}"
  user_until_exp_time="${3}"

  echo "${user_name}（${mail_to}）へメールを送信します."
  mail_text=$(sed -e 's#%UserName%#'"${user_name}"'#' -e 's#%untilExpirationTime%#'"${user_until_exp_time}"'#' ${MailTemplate})

  echo "${mail_text}" | ${PY_SEND_MAIL} "${MailFrom}" "${mail_to}" "${MailSubject}"
}


# ユーザ毎に順にメールを送信する
${PY_LISTUP_EXSHORT} | while read line
do
  user_name=$(echo "${line}" | cut -d';' -f1)
  user_mail=$(echo "${line}" | cut -d';' -f2)
  user_until_exp_time=$(echo "${line}" | cut -d';' -f3)
  send_mail "${user_name}" "${user_mail}" "${user_until_exp_time}"
done
```

#### warn-password-expiration-mail.txt
　メールに送信する本文のフォーマットです。%で囲まれた部分は他のスクリプトで取得した値が格納されます。

```
%UserName% さん

インフラチームで管理している、LDAPアカウントのパスワード有効期限が残り少なくなっています。
パスワードの有効期限はパスワード更新時より90日となります。

        残りの有効期限：%untilExpirationTime%


下記のURLよりパスワードを更新してください。パスワードポリシーは以下となっております。
---
文字数8文字以上
過去2回のパスワードは利用不可
英大文字を1文字以上使用
英小文字を1文字以上使用
数字を1文字以上使用
---
        https://userminのあるサーバのIP:20000/

[変更方法]
1.上記URLの表示画面よりLDAPユーザ名、パスワードでログイン
↓
2.左メニュー「Login」より、「Change Password」をクリック
↓
3.現在のパスワード、新しいパスワードを入力し「Change Now」をクリック

よろしくお願いいたします。

（本メールに関するお問い合わせ先）
        お問い合わせは下記のメールアドレス宛てにお願いいたします。
                xxx@xxx.com

-- 以上 --

```

## パスワード間違いでアカウントロックした際の解除法
「n回ログインに失敗したらアカウントロックする。ロックしている時間はm分」というような設定を入れている場合、
すぐに解除してあげたいこともあると思います。その際の解除方法となります。

LDAPサーバ上での作業です。
※dn: cn=test-user,ou=user,dc=test,dc=orgがパスワードロックになった場合。

#### アカウントロックされているユーザの確認
```
ldapsearch -x -W -D "cn=admin,dc=test,dc=org" -b "dc=test,dc=org" pwdAccountLockedTime=* pwdAccountLockedTime
Enter LDAP Password:<ディレクトリマネージャのパスワードを入力>
※以下アカウントロックされているユーザが表示される
—
test user, user, test.org
dn: cn=test-user,ou=user,dc=test,dc=org
pwdAccountLockedTime: YYYYMMGGHHmmSS
—
```

#### アカウントロック解除
```
# ldifファイルを作成
vi /root/ldif/pwdAccountLockedTime.ldif
—
dn: cn=test-user,ou=user,dc=test,dc=org
changetype: modify
delete: pwdAccountLockedTime
—

# ロック解除設定を投入
ldapmodify -x -W -D "cn=admin,dc=test,dc=org" -f /root/ldif/pwdAccountLockedTime.ldif
Enter LDAP Password:<ディレクトリマネージャのパスワードを入力>
```

　以上でLDAP環境構築の連載記事も終わりになります。他にもバックアップスクリプトを作成したり、LAMでのユーザ追加手順など運用資料も用意するといいと思います。

## 関連記事
[・CentOS7でOpenldap構築 - 1.Openldapの仕組み](https://qiita.com/y-araki-qiita/items/0c954c3ae25d4ed9dbb8)
[・CentOS7でOpenldap構築 - 2.LDAPサーバ構築手順](https://qiita.com/y-araki-qiita/items/6b2dcbf1a39a969d8024)
[・CentOS7でOpenldap構築 - 3.LDAPクライアント設定手順](https://qiita.com/y-araki-qiita/items/b0f3b291011d1e55d95f)
[・CentOS7でOpenldap構築 - 4.LDAPディレクトリ構成例とACLの設定](https://qiita.com/y-araki-qiita/items/bc874aeb40ede9c5368c)
[・CentOS7でOpenldap構築 - 5.バックアップリストアテスト](https://qiita.com/y-araki-qiita/items/ad000141c3fccaef7167)
[・CentOS7でOpenldap構築 - 6.パスワード有効期限切れ通知スクリプト](https://qiita.com/y-araki-qiita/items/65103497c2ce0ad63d24)