2017年4月5日水曜日

Chrome extentionを使い、iframe内外でデータ通信

◆ 目的
Webページ(親ページ)とそこからiframeで読み込んだ子ページ間でデータの授受を行う。
ただし、iframe内のコンテンツは親ページのドメイン外に存在する任意のサイトとする。



◆ 課題
解決する課題は2つある。
1. クロスドメイン制約への対応
XMLHttpRequestを投げる際などにも必ず気に掛ける点だろう。
Same Origin Policy(同一生成元ポリシー)を回避しなければならない。

今回は任意の外部サイトをiframeに取り込むことを前提としているため、
CORS(Cross-Origin Resource Sharing)もJSONP(JSON with padding)も今回は使えない。

2. iframe内での読み込み不許可ヘッダへの対応
HTTPのレスポンスヘッダに、iframe内からWebページが読み込まれるのを防止するオプション(X-Frame-Options)が付与されていればiframe内で表示はできない。



◆ 解決策
1. postMessageの利用
クロスドメイン制約への対応として、HTML5で用意されたpostMessageの利用を思いつく。

(親サイト)
<iframe id="ifrm" src="外部サイト"></iframe>

<script type="text/javascript">
window.onload = function() {
  var ifrm = document.getElementById('ifrm').contentWindow
  ifrm.postMessage("hello", '外部サイト')
};
</script>

(外部サイト)
<script type="text/javascript">
window.addEventListener('message', function(event) {
    alert(event.data)
}, false);
</script>

しかし、Chromeが許可しない。
※Safariでは警告なく、実施できた。

(エラー例)
Failed to execute 'postMessage' on 'DOMWindow': The target origin provided ('http://親サイト') does not match the recipient window's origin ('http://外部サイト').


2. iframeを使わない
iframeを使う前提を変え、コンテンツをダウンロードし、外部サイトを親側で再現させる。つまり、前提を変える。
が、しかし、JavaScriptが生成する動的ページのリソースの管理は困難があるため、やはり、iframeは使いたい。前提は戻す。


3. Google Chromeの拡張機能の利用
Chromeに限定されるが、Chromeの拡張機能を使えば対応ができそうである。
データの共有は、バックグラウンドで動作させるスクリプト内でセッションストレージを使えばいいだろう。
また、ヘッダの書き換えも実施できる。



◆ 拡張コード概要
chrome拡張を利用することにした。
拡張コードを有効にするには、最低限以下の3つのファイルを用意し、それらを適当なフォルダに入れ、Chromeブラウザの拡張機能からインポートすれば良い。

○ マニフェスト ファイル
拡張機能に関する情報を与える。

○ コンテンツ スクリプト
ブラウザで表示させるページで読み込むjsとは別空間で実行させるjsである。
このファイルは親サイトと、iframe内の外部サイト、両方に読み込まれる。
空間は分かれているため、コンテンツスクリプト内で利用しているjQueryなどのライブラリがサイトで利用しているバージョンと異なっていても問題は起きない。

○ バックグラウンド スクリプト
Chromeのバックエンド側で処理させるjsである。
表示コンテンツには取り込まれないが、コンテント ファイルとの間でメッセージ通信ができる。


簡易図で表すと下記のような感じである。
   parent
+----------+
|          |← contentScripts.js
|  iframe  |                       
|  +----+  |
|  |    |  |
|  |    |←-|-- contentScripts.js
|  +----+  |
|          |
+----------+
background.js

contentScripts.js、background.jsの名称はmanifest.json内で指定する。



◆ 試験
1. iframe内でマウスを操作。マウスオーバしたタグ要素に色がつくようにしている。
  そのタグ要素でクリックすると、タグ名がセッションストレージへ保存される。

2. 親側でiframe外の要素をクリックする。
  iframeで取得した要素がalert表示されれば成功である。



◆ コード例
○ manifest.json


○ contentScripts.js


○ background.js



◆ コード解説
1点、解説を加えておく。
contentScript.jsは親子両方に読み込まれるため、共通処理以外のjsコードは、親子用で条件を加えている。
// iframe用
if (window != parent) {
  ~snip~
} // 親用
else{
  ~snip~
}

2017年3月4日土曜日

ChatHub(LINE上で動作する、Q&Aを中心としたSNSサービス)


LINE上で動作する、お互いに顔までは知らない関係の、身近なコミュニティ内で、匿名のまま情報交換ができるQ&Aを中心としたSNSサービス、ChatHubを作成した。最近はやりのbotの一つである。





























※内部でAI的な要素を使ったおもしろい仕組みを導入している。
  技術詳細に関しては随時公開予定。



2017年1月10日火曜日

Cのselect、epollを使ったI/O多重化


複数のファイルディスクリプタを監視し、その中のいずれが入出力可能な状態であるかを確認するCのシステムコールとしては、select、epoll、kqueue(BSD系)が有名だろう。
ここでは、selectとepollを使い、1プロセス1スレッドでのイベント駆動によるI/O多重化コードを備忘録として残しておく。

(参考)
最近ではselectではなく、epoll、kqueueが使われることが多い。
selectは待ち受けられるファイルディスクリプタの数に上限があり、またパフォーマンス問題も存在するためである。
I/Oからの入力に応じて発生するイベントを処理するライブラリとしてはlibevent, libev, libuvなどがあるがこの内部でもepollやkqueueが利用されている。
※libevはlibeventの速度改善、FDの制限撤廃の対応がされた改良版である。
※libuvはnode.js用のためにlibevをベースに開発されたライブラリである。



(select.c)


(epoll.c)


$ gcc ファイル名

$ ./a.out /dev/tty /dev/input/mouse0 10
hello
/dev/tty: hello

no input after 10 seconds

マウス操作
/dev/input/mouse0: (


(補足)
readで指定したバッファサイズ以上のデータがあった場合、ループでepoll_waitの処理へ戻るとはどのような動作をするか分かるだろうか。
残りのデータを引き続き読み込めると判断されるた、待ち状態にはならない。
しかし、次のようにイベント条件を指定した場合は一度しかイベントは発火しない。
ev.events = EPOLLIN | EPOLLONESHOT

イベントを継続させる場合には、再度epoll_ctlを同じepfdとEPOLL_CTL_MOD値を渡して呼べばよい。
epoll_ctl(epfd, EPOLL_CTL_MOD, fd1, &ev)

2017年1月6日金曜日

Rubyで深さ優先探索(DFS)と幅優先探索(BFS)

グラフ操作の基本、深さ優先探索(DFS: Depth First Search)と幅優先探索(BFS: Breadth First Search)のアルゴリズムをRubyで書く。


◆ DFS


◆ BFS

2016年9月25日日曜日

日本語文字列の自然なソート

手元にあった海外製のJavaで書かれたソフトで日本語の文字列をソートすると、
漢字部分が無秩序、訳わからない並びになった。
Unicodeの並びに沿わせていないからであろうか?

※JavaはUnicodeの文字コードでUTF-16の符号化方式である。
 つまり、StringクラスはUTF-16形式で扱うわけである。
 char型は符号なし16ビット整数である。Characterクラスはこのラッパ。

【参考:一般的な文字コード(文字集合)と符号化方式】
 -文字コード-   -符号化(エンコード)方式-
 JIS X 0201   8bit符号
              7bit符号

 JIS X 0208   EUC-JP

              ISO-2022-JP
              Shift_JIS

 Unicode      UTF-16

              UTF-32
              UTF-8


しかし、ASCII部分、ひらがな、カタカナ、は問題なく、Unicodeに沿っているように見える。なぜ漢字にだけ違和感を感じるのだろうか。
少し調べたところ、Unicodeの漢字は部首順に並べられているようである。規則性を感じなかったのはそのためである。部首単位でまとまるためそれが統一性を感じることも逆にあるだろうが。

国や言語固有の文化を反映した自然だと思われる並びにするにはJavaであればjava.text.Collatorクラスを使うと解決することが分かった。日本語に限らず、他言語にも対応している。英語圏発のソフトウェアはこのあたりへ考え及ばせること難しいかもしれないが考慮入れていただきたい点の一つである。



(脱線1)
文字列比較をする際にはCollator#compare()メソッドを使えばいいが、繰り返しソートする場合は性能面からCollationKeyを利用したビット単位比較をさせた方がいい。

(脱線2)
Javaと文字列の話ついでに、もう一つ。
charの単位は1文字ではない。適切な区切りを得るためにはBreakIteratorクラスあたりを使うこと。

なぜかというと、UTF-16はBMP(Basic Multilingual Plane)以外の面の文字を表すためにサロゲートペア(surrogate pair)や、あと複数のパーツで1文字を表現する結合文字を存在しているからである。濁点、半濁点との結合などである。

(脱線3)
Rubyの1.8系までは少しおもしろい取り組みをしている。

JavaやPythonなどはシステムの内部コードをUnicodeの文字コードに統一するUCS(Universal Code Set)方式を取っている。そのためUnicodeに含まれない文字を扱いたければ、ライブラリを使うか、自力でchar単位で処理するしかない。Unicodeだけで問題あるのかというと、このセットに含まれない文字は実は結構あるらしい。

一方、Ruby1.8系までは8bitの列に符号化方式の情報をセットにしたものを文字列だと定義する、CSI(Code Set Independent)方式を採用している。文字コードはUnicodeではなく、ただのバイナリでしかない。どのような文字コードでも直接保存できるが、実装、最適化あたりの処理は複雑にならざるを得ないだろうと容易に想像できる。
ちなみに、Ruby1.9系以降のディフォルトのエンコーディング方式はUTF-8である。


〜参考〜
http://ruby-doc.org/core-2.3.1/String.html
http://magazine.rubyist.net/?0025-Ruby19_m17
http://d.hatena.ne.jp/nishiohirokazu/20141107/1415286729




2016年8月24日水曜日

MQTT理解(publisher/subscriber: ruby利用、broker: Bluemixサービス)


◆ 目的
IoTやM2Mで使われるpublisherとsubscriberモデルの軽量なメッセージプロトコルであるMQTTの動作をrubyクライアントを使い確認する。rubyのmqttライブラリはこちらを使う。

brokerとなるサーバは構築の手間を省くため、IBM Bluemix上にあるサービス、Internet of Things Platform を利用する。



◆ publisher接続仕様
IBMが提供するIoTのbrokerの作法の詳細はこちら
以下ではコードを書く上で必要なところを抽出してまとめる。

(フォーマット)
● 認証(ユーザ)     : use-token-auth(固定値)
     (パスワード)   : デバイス登録時に払い出されるトークン

● Client ID      : d:{org_id}:{device_type}:{device_id}

● Topic(event)   : iot-2/evt/{event_id}/fmt/{format}
       (command) : iot-2/cmd/{command_id}/fmt/{format}
                   今回はeventトピック(commandではない)のフォーマットを利用する

{}の各変数はそれぞれ以下の意味である。
org_id           : brokerが払い出し(接続先エンドポイントのURIに付与される)
device_type      : 任意のものを作成
                   ここではラズベリーパイを使うことを想定しraspiとする
device_id        : デバイス単位で固有の値を任意に作成

event_id         : 任意のものを作成(ここではtype01とする)
command_id       : 任意のものを作成
format           : jsonとする

下記変数部分は文字列ワイルドカード文字(+)を使用することができる。
device_type、device_id、event_id、format



◆ subscriber接続仕様
(フォーマット)
● 認証(ユーザ)      : APIキー(broker管理画面で作成)
     (パスワード)    : APIトークン(broker管理画面で作成)

● Client ID       : a:{org_id}:{app_id}

● Topic(event)    : iot-2/type/{device_type}/id/{device_id}/evt/{event_id}/fmt/{format}
     (command)    : iot-2/type/{device_type}/id/{device_id}/cmd/{command_id}/fmt/{format}

{}の各変数はそれぞれ以下の意味である。
app_id            : 任意のものを作成(ここではapp01とする)

下記変数部分は文字列ワイルドカード文字(+)を使用することができる。
device_type、device_id、cmd_id、format



◆ publisherコード



◆ subscriberコード



◆ 試験
1.
$ ./subscriber.rb
待ち

2.
$ ./publisher.rb

3.
subscriber.rbのプロンプトへ以下が出力される。
"iot-2/type/android/id/12345/evt/type01/fmt/json"
"{\"d\":{\"test_data\":10}}"



2016年5月9日月曜日

OpenLDAPを利用したディレクトリサーバの構築(OLC対応版)


OpenLDAP2.4系がOLC(On-Line Configuration)を利用する設定方式に変わっている。
その仕様にあわせたサーバの構築手順(Red Hat 7、CentOS 7 用)をまとめておく。

-目次-
【設計・簡易構成案】
【インストールから設定】
【試験ユーザの登録】
【TLS(SSL)化】
【その他、設定検討事項項目】



【設計・簡易構成案】
今回、模擬的に作成するツリー構成は以下とする。

dc=jp
    dc=example
        cn=root          ・・・特権アカウント

        ou=managers      ・・・管理アカウントディレクトリ
            cn=readmgr
            cn=writemgr

        ou=users         ・・・ユーザディレクトリ
            cn=user1
            cn=user2

        ou=groups        ・・・グループディレクトリ
            cn=group1
            cn=group2


dc(domain component)としてjp, exampleを上位に配置する。
その配下に全権限をもたせたrootアカウントを作成し、
また各種ou(organization unit)を並行に配置する。

managersでは役割に特化した(権限を絞った)管理者アカウントを作成する。
具体的には、users、groupsディレクトリ配下を参照、または更新ができるアカウントをそれぞれ用意する。

利用者アカウントの管理はusersの下で行う。ここで各ユーザが追加、削除、参照される。
オブジェクトクラスはinetOrgPersonを利用する。

各アカウントが所属する組織はgroups下で作成し、ユーザを各グループへmemberとして登録する。
オブジェクトクラスはgroupOfNamesを利用する。


~補足~
(オブジェクトクラスの決め方)
今回ユーザの管理にinetOrgPersonというオブジェクトクラスを選んだ。
ただし、用途次第で最適なオブジェクトクラスは異なるため慎重に選択するべきである。

例えば今回はユーザの属性の一つとして、メールアドレスを登録させることを想定したため、inetOrgPersonを選んだ。
mail属性をディフォルトの補足属性としてもっているためである。

オブジェクトクラスの属性を調べるにはこのあたりのファイルを開けばよい。
/etc/openldap/schema/*.schema


ユーザ管理であってもサーバのログインアカウントなどでであればposixAccountの方がいいかもしれない。

objectclass ( 1.3.6.1.1.1.2.0 NAME 'posixAccount'
        DESC 'Abstraction of an account with POSIX attributes'
        SUP top AUXILIARY
        MUST ( cn $ uid $ uidNumber $ gidNumber $ homeDirectory )
        MAY ( userPassword $ loginShell $ gecos $ description ) )


またそれにあわせてグループもposixGroupの方がいいだろう。

objectclass ( 1.3.6.1.1.1.2.2 NAME 'posixGroup'
        DESC 'Abstraction of a group of accounts'
        SUP top STRUCTURAL
        MUST ( cn $ gidNumber )
        MAY ( userPassword $ memberUid $ description ) )

※オブジェクトクラスは継承環境があるため、上位クラス(SUP)の必須、補足属性の定義にも影響される。


(ツリーの構成)
ツリーの設計として、グループの下にユーザディレクトリを掘っていくような案も考えられる。
広さよりも、深さをより優先する設計という意味である。
深さ優先のメリットとしては、検索のベースを下げられ、ロック粒度を下げられる点があるだろう。
しかし、グループ属性が流動的であれば、そことディレクトリを共有し密に紐づくユーザの管理が煩雑になるデメリットもある。



【インストールから設定】
● パッケージのインストール
$ sudo yum install openldap-servers openldap-clients


● 初期設定
DB設定はサンプル例を利用する。
$ sudo cp -iv /usr/share/openldap-servers/DB_CONFIG.example /var/lib/ldap/DB_CONFIG

$ sudo chown -v ldap.ldap /var/lib/ldap/DB_CONFIG


● 起動・自動起動設定
$ sudo systemctl enable slapd

$ sudo systemctl start slapd

$ sudo systemctl status slapd


● スキーマの登録
要・不要の判断を簡略するためさしあたりすべてを登録しておく。
ldap rootアカウントはこの段階で未設定のため、"-Y EXTERNAL"オプションにより、認証をldap環境外の認証(OSローカル)に任せる。
$ find /etc/openldap/schema/*.ldif -exec sudo ldapadd -Y EXTERNAL -H ldapi:/// -f {} \;

スキーマが登録されたことを確認する。
$ sudo slapcat -b cn=config | grep "cn=schema,cn=config"

slapcatコマンドの代わりにldapsearchでconfigを参照しても同じ結果が得られる。
$ sudo ldapsearch -H ldapi:// -LLL -Q -Y EXTERNAL -b "cn=config" dn


● suffixの設定
ldapデータベースのルートとなるディレクトリを指定する。
suffixで指定したノード以下でしかオペレーションはできない、などの制約を発生させることができる。
suffixの設定であり、この段階でツリー構造ができるわけではない。
※コマンドを打った後に、ldifを貼り付け、Ctrl+Dで抜ける。

$ sudo ldapmodify -vv -Y EXTERNAL -H ldapi:///
dn: olcDatabase={2}hdb,cn=config
changetype: modify
replace: olcSuffix
olcSuffix: dc=example,dc=jp

$ sudo slapcat -n 0 | grep -i ^olcSuffix


● パスワード暗号化方式の決定
パスワードのハッシュ化方式を指示する。

{CRYPT}はcrypt(3)を利用することを表す。
$5$はSHA256形式のハッシュ化を、"%.8s"の部分は8文字のランダムなソルトを付与することを意味する。
$6$にするとSHA516形式となる。

$ sudo ldapmodify -Y EXTERNAL -H ldapi:///
dn: cn=config
changetype: modify
replace: olcPasswordHash
olcPasswordHash: {CRYPT}

dn: cn=config
changetype: modify
replace: olcPasswordCryptSaltFormat
olcPasswordCryptSaltFormat: $5$%.8s

$ sudo slapcat -b cn=config -a "(cn=config)" | grep Password


● ldap rootのdnとpwの設定
ldap用rootのパスワードをまず生成する。
schemeとcrypt-saltは先ほどldapコンフィグで指定したものを利用する。
$ slappasswd -h {CRYPT} -c '$5$%.8s' -s "testrootpass"
{CRYPT}$5$0O1V・・・ メモしておく

RootDNはSuffixで指定したノード以下でなければならない。
$ sudo ldapmodify -vv -Y EXTERNAL -H ldapi:///
dn: olcDatabase={2}hdb,cn=config
changetype: modify
replace: olcRootDN
olcRootDN: cn=root,dc=example,dc=jp

dn: olcDatabase={2}hdb,cn=config
changetype: modify
replace: olcRootPW
olcRootPW: {CRYPT}$5$0O1V・・・

$ sudo slapcat -n 0 | grep -i ^olcRoot..


● ツリーの構築
ldap rootアカウントが使えるようになっているはずであるのでここからはこのアカウントを使い操作する。

$ ldapadd -x -vv -H ldap://127.0.0.1 -D cn=writemgr,dc=example,dc=jp -w 'tmp_write_pass'
dn: dc=example,dc=jp
objectClass: top
objectClass: dcObject
objectClass: organization
o: example

dn: cn=root,dc=example,dc=jp
objectClass: organizationalRole
cn: root
description: Directory Manager

dn: ou=users,dc=example,dc=jp
objectClass: organizationalUnit
ou: users

dn: ou=groups,dc=example,dc=jp
objectClass: organizationalUnit
ou: groups

dn: ou=managers,dc=example,dc=jp
objectClass: organizationalUnit
ou: managers


$ ldapsearch -x -vv -H ldap://127.0.0.1 -D cn=root,dc=example,dc=jp -w 'tmp_root_pass' -b "dc=example,dc=jp"



● 管理者アカウント(root以外)の登録
参照用、更新用に専用のアカウントを作成する。
$ ldapadd -x -vv -H ldap://127.0.0.1 -D cn=root,dc=example,dc=jp -w 'tmp_root_pass'
dn: cn=readmgr,ou=managers,dc=example,dc=jp
objectClass: inetOrgPerson
mail: readmgr@example.jp
sn: readmgr

dn: cn=writemgr,ou=managers,dc=example,dc=jp
objectClass: inetOrgPerson
mail: writemgr@example.jp
sn: writemgr

パスワードはコマンドで投入する。
$ ldappasswd -H ldap://127.0.0.1 -D cn=root,dc=example,dc=jp -w 'tmp_root_pass' "cn=readmgr,ou=managers,dc=example,dc=jp" -s 'tmp_read_pass'

$ ldappasswd -H ldap://127.0.0.1 -D cn=root,dc=example,dc=jp -w 'tmp_root_pass' "cn=writemgr,ou=managers,dc=example,dc=jp" -s 'tmp_write_pass'

アクセスリストの設定をまだ行っていないため、この段階では意図した権限にはなっていない。


● アクセスリストの設定
各種DBがあり、それぞれにふさわしいフィルタを設定する必要がある。

$ sudo ls /etc/openldap/slapd.d/cn=config/
~snip~
olcDatabase={0}config.ldif
olcDatabase={1}monitor.ldif
olcDatabase={2}hdb.ldif
~snip~


(olcDatabase={0}config.cn=config、設定管理エントリ)
設定しない。

(olcDatabase={1}monitor、モニタ機能)
設定しない。

The monitor backend to slapd(8) is not an actual database; if enabled, it is automatically generated and dynamically maintained by slapd with information about the running status of the daemon.


(olcDatabase={2}、ユーザデータDB)
$ sudo ldapmodify -vv -Y EXTERNAL -H ldapi:///
dn: olcDatabase={2}hdb,cn=config
changetype: modify
replace: olcAccess
olcAccess: to attrs=userPassword,shadowLastChange
              by dn=cn=root,dc=example,dc=jp write
              by anonymous auth
              by self write
              by * none
olcAccess: to dn.subtree=dc=example,dc=jp
              by dn=cn=readmgr,ou=managers,dc=example,dc=jp read

              by dn=cn=writemgr,ou=managers,dc=example,dc=jp write
olcAccess: to *
              by dn=cn=root,dc=example,dc=jp write

※適用順番には意味がある。変えると意図しない動作になる。

フィルタを削除する場合はldifを以下のようにする。
olcAccess:{数字}は適用するDBの数字ではなく、アクセスリストの番号である。
0番で入れれば、先頭が、設定されている数以上の数字を入れれば最後尾のものが対象となる。

dn: olcDatabase={2}hdb,cn=config
changetype: modify
delete: olcAccess
olcAccess: {0}



【試験ユーザの登録】
● 一般ユーザ、グループの登録
usersツリー配下に試験アカウントを登録する。

rootアカウントは利用せず、書き込み権限のあるwritemgrアカウントを使う。

$ ldapadd -x -vv -H ldap://127.0.0.1 -D cn=writemgr,ou=managers,dc=example,dc=jp -w 'tmp_write_pass'
dn: uid=tmp_user01,ou=users,dc=example,dc=jp
objectClass: inetOrgPerson
mail:tmp_user01@example.jp
uid: tmp_user01
cn: tmp_user01
sn: tmp

dn: uid=tmp_user02,ou=users,dc=example,dc=jp
objectClass: inetOrgPerson
mail:tmp_user02@example.jp
uid: tmp_user02
cn: tmp_user02
sn: tmp

dn: cn=tmp_group01,ou=groups,dc=example,dc=jp
objectClass: groupOfNames
cn: tmp_group01
member: uid=tmp_user01,ou=users,dc=example,dc=jp

dn: cn=tmp_group02,ou=groups,dc=example,dc=jp
objectClass: groupOfNames
cn: tmp_group02
member: uid=tmp_user02,ou=users,dc=example,dc=jp


作成したユーザのパスワード(userPasswordアトリビュート)を追加する。
$ ldappasswd -H ldap://127.0.0.1 -D cn=writemgr,dc=managers,dc=example,dc=jp -w 'tmp_write_pass' uid=tmp_user01,ou=users,dc=example,dc=jp -s 'tmp_user_pass01'

$ ldappasswd -H ldap://127.0.0.1 -D cn=writemgr,dc=managers,dc=example,dc=jp -w 'tmp_write_pass' uid=tmp_user02,ou=users,dc=example,dc=jp -s 'tmp_user_pass02'


readmgrアカウントで適切に追加されているか参照してみる。
$ ldapsearch -x -vv -H "ldap://127.0.0.1" -D "cn=readmgr,ou=managers,dc=example,dc=jp" -w 'tmp_read_pass' -b ou=users,dc=example,dc=jp dn

$ ldapsearch -x -vv -H "ldap://127.0.0.1" -D "cn=readmgr,ou=managers,dc=example,dc=jp" -w 'tmp_read_pass' -b ou=users,dc=example,dc=jp "(&(objectclass=inetOrgPerson)(mail=tmp_user01@example.jp))"



【TLS(SSL)化】
● 自己証明書の作成
暗号化通信目的にTLS(SSL)を使えるよう、CA証明局の構築とCA証明書を作成する(さしあたり自己証明書)。
$ filename="openssl_x509_`date +%F`"

$ openssl genrsa 2048 > ${filename}.key

$ openssl req -new -x509 -out $filename.crt -key $filename.key -days 365 -sha256 -subj "/C=jp/ST=tokyo/L=example-ku/O=example Corp./CN=ldap.example.jp"

$ ls -la $filename.key $filename.crt

$ openssl x509 -text -noout -in $filename.crt

$ sudo cp $filename.key /etc/openldap/certs/server.key

$ sudo cp $filename.crt /etc/openldap/certs/server.crt

$ sudo chown ldap.ldap /etc/openldap/certs/server.key

$ sudo chown ldap.ldap /etc/openldap/certs/server.crt


● 証明書の指定
作成したcrt、keyファイルを設定ファイルに指定する。
$ sudo ldapmodify -Y EXTERNAL -H ldapi:///
dn: cn=config
changetype: modify
replace: olcTLSCertificateFile
olcTLSCertificateFile: /etc/openldap/certs/server.crt

dn: cn=config
changetype: modify
replace: olcTLSCertificateKeyFile
olcTLSCertificateKeyFile: /etc/openldap/certs/server.key

dn: cn=config
changetype: modify
delete: olcTLSCACertificatePath

$ sudo slapcat -b cn=config -a "(cn=config)" | grep -i TLS


● スキーマの追加
$ sudo vi /etc/sysconfig/slapd
SLAPD_URLS="ldapi:/// ldaps:/// ldap:///"

$ sudo systemctl restart slapd


● ldapクライアントの修正
認証局より認証されていないサーバ証明書であるためクライアントが受け入れを拒否する。
それを許可する指定をする。
$ echo -e "TLS_REQCERT allow" >> /etc/openldap/ldap.conf



【その他、設定検討事項項目】
● ログレベルの設定
$ sudo ldapmodify -vv -Y EXTERNAL -H ldapi:///
dn: cn=config
changetype: modify
replace: olcLogLevel
olcLogLevel: config stats

$ sudo slapcat -n 0 |grep ^olcLogLevel

ログを確認する。
$ sudo journalctl -u slapd -f