AsciiDoc と Antora でマニュアルサイトを構築しました

弊社 LMS WebClass には、学生の学習履歴を可視化する「学習記録ビューア」という機能があります。学習記録ビューアを使い始めるために必要な権限設定、プラグインによる機能拡張もあり、ユーザーの方に使っていただくためにはドキュメントの充実が必須です。学習記録ビューア専用のマニュアルサイトを以前から公開していたのですが、先日プチリニューアル(内部的にはなかなか大きな更新)しました。その際、サイト構築にドキュメントサイトジェネレータ Antora を使用することになりました。Antora 使用事例として興味を持つ人もいるかなと思い、構築プロセスを公開します。

今回リニューアルしたマニュアルサイトはこちらです。

Antora とは

  • Antora は、AsciiDoc からドキュメントサイトを生成するツール
  • デモサイト
  • Antora のドキュメントサイト 自体も Antora で生成されている
  • 複数のリポジトリにコミットされている AsciiDoc から一つのサイトを生成できる
  • Git ブランチやタグからドキュメントの「バージョン」を規定できる
    • 例えば “v1”, “v2” のようなタグまたはブランチを作り、バージョン毎にドキュメントを管理する
    • サイトにアクセスした人は見たいバージョンを切り替えることができるようになる

リニューアル前の状況

  • マニュアル本文は全て AsciiDoc で書いている
    • 1 ページ 1 adoc ファイル
  • サイトの外殻は Pug で書いていた。Pug を HTML に変換する際、AsciiDoc を Asciidoctor (Asciidoctor.js) で HTML に変換し埋め込んでいた
  • Asciidoctor PDF を使って、AsciiDoc を PDF に変換し、サイトからダウンロードできるようにしている
  • 社内でセルフホストしている GitLab でバージョン管理

課題

  • マニュアル本文は AsciiDoc を編集するので楽だが、ナビゲーションメニューを更新するために Pug を編集する必要があり、面倒くささは拭いきれなかった
    • ドキュメントの充実のためには更新のしやすさは必須
  • 検索機能がなかった
  • 目次がページのタイトル下に表示されるが、下にスクロールすると見えなくなるので、文の多いページだと全体が俯瞰しづらかった

リニューアル後

構成

  • マニュアルリポジトリには、複数ある製品のマニュアル AsciiDoc ファイルが全て登録されている
  • マニュアルサイトリポジトリには、antora-playbook.yml という設定ファイルが登録されている
    • antora-playbook.yml に AsciiDoc が登録されているリポジトリやバージョン情報を記述することで、Antora のコマンド一発でサイトが生成できるようになる
  • CI/CD
    1. マニュアルリポジトリで AsciiDoc ファイルを変更しコミットする
    2. GitLab CI の Multi-project pipelines によりマニュアルサイトリポジトリのパイプラインが起動
    3. マニュアルサイトリポジトリのパイプライン内でサイト生成ジョブ実行
    4. サイト生成完了後、Manual Job でデプロイ可能となる

リニューアル前の課題は解決したか

  • ナビゲーションメニュー等の高頻度で更新される部分は全て、 Antora の設定ファイル及び AsciiDoc を編集することで変更できるため、マニュアル更新が楽になった
  • 検索機能を簡単に付けることができた
    • 実装の詳細は後述
  • ページをスクロールしても常に目次が表示されるようになった

多言語化

こちらを参考に、バージョン切り替えの仕組みを使って言語切り替えを実現しました。

antora-playbook.yml:

content:
  sources:
  - url: https://git.lan.datapacific.co.jp/webclass/manual-integrated-portfolio.git
    start_paths:
      - integrated-portfolio/admin/ja
      - integrated-portfolio/admin/en

この方法だと、逆に通常のバージョン切り替えができなくなります。言語毎に別サイトを生成してもよかったかもしれません。

検索機能

  • Antora Lunr Extension を使っている
  • Antora コマンドによるサイト生成時に検索用インデックスも生成される
    • サイト表示後はオフライン検索できる

antora-playbook.yml:

antora:
  extensions:
    - require: '@antora/lunr-extension'
      languages: [ja]

まとめ

マニュアルサイトに必要な機能は全て揃い、マニュアル更新のハードルはかなり下がりました。これから一番大切なコンテンツ改善を進めていきます。

Docker で検証用 CAS サーバ

CAS との連携を確認するのに、Docker でサクッと建てようと思ったら思いのほか手を焼いたのでメモ。

やりたいこと

CAS でSSOするサービス側モジュールの検査のために、とりあえずのCASサーバを立てて使いたい。細かい設定はいらない。

昔は仮想マシンに Tomcat 入れて動かしていましたが、今ならもう Docker でサクッと立つはず。。。

コンテナイメージの配布状況

本家のドキュメント、この素っ気なさからしていやな予感。

本家のコンテナイメージと、ビルドソース

いろいろ試した結果、配布されているコンテナイメージをそのまま何とか動かしても思ったように扱えない。

このコンテナイメージは SRC1 をビルドしたもので、SRC1 は gradle 実行環境と SRC2 をコンテナイメージの中に展開して gradle でビルドをしている。ただし、SRC1 は SRC2 のビルドオプションを最小限しか設定しておらず、実用性に難がある。

SRC2 が本命だが、SRC2 には gradle のビルド環境構築手続きが含まれていない。それを書くと結局は SRC1 を引っ張り出すことになるので、SRC1 を調整してカスタムイメージをビルドすることにする。

念のために Vagrant も探してみたけどいまいちでしたので、やっぱりコンテナのビルド。

予備知識、前提

はまりポイント、特に Java のビルド環境についてあまり経験の無い方は、ビルドオプションを調整するところかと思います(私がそうでした)。

ビルド環境の用意

まずは github.com/apereo/cas-webapp-docker をクローンしてビルドの準備をします。

git clone https://github.com/apereo/cas-webapp-docker.git
cd cas-webapp-docker
# git reset --hard a2d664fd006d86f0bb09d65c2ffb0eeb25018fdf

README.md の SSL セクションを実行します。ここはローカル開発用と割り切って、鍵もPASSもそのままです。

keytool -genkeypair -alias cas -keyalg RSA -keypass changeit \
        -storepass changeit -keystore ./thekeystore \
        -dname "CN=cas.example.org,OU=Example,OU=Org,C=AU" \
        -ext SAN="dns:example.org,dns:localhost,ip:127.0.0.1"

sudo su root -c " echo '127.0.0.1 cas.example.org' >> /etc/hosts"

実際に使うにはまだ設定が必要ですが、念のために一度ビルドして動かしてみます。

./build.sh 6.4
./run.sh 6.4

うまくいけば、コンソール出力に大きく「READY」と表示されます。(ここのコードブロックでの表示では形が崩れているかもしれません)。

2021-12-21 05:02:57,689 INFO [org.apereo.cas.web.CasWebApplication] - <>
2021-12-21 05:02:57,689 INFO [org.apereo.cas.web.CasWebApplication] - <

  ____  _____    _    ______   __
 |  _ \| ____|  / \  |  _ \ \ / /
 | |_) |  _|   / _ \ | | | \ V / 
 |  _ <| |___ / ___ \| |_| || |  
 |_| \_\_____/_/   \_\____/ |_|  

>
2021-12-21 05:02:57,689 INFO [org.apereo.cas.web.CasWebApplication] - <>
2021-12-21 05:20:05,860 INFO [org.apereo.cas.web.CasWebApplication] - <Ready to process requests @ [2021-12-21T05:20:05.859Z]>
2021-12-21 05:20:05,916 INFO [org.apereo.cas.services.AbstractServicesManager] - <Loaded [0] service(s) from [InMemoryServiceRegistry].>

特に、コードブロックにコピーしたログの一番最後の行、 [org.apereo.cas.services.AbstractServicesManager] - <Loaded [0] service(s) from [InMemoryServiceRegistry].> というのがポイントです。これは、Application ServiceRegistry に登録がないよ、と言っています。後で改善します。

この状態でブラウザで https://cas.example.org:8443/cas/login を開くとログイン画面を表示できるはずですが、実際には証明書が INVALID なために警告が表示されてしまいます。以下のコマンドで証明書を吐き出して許可リストに加えるか、ブラウザで開いたときに強制的に表示させます。

keytool -export -alias cas -keystore thekeystore -rfc -file cas-self.cert

ログイン画面が表示されると、casuser アカウントで認証は成功するはずです。

アプリケーションサーバの連携確認

アプリケーションサーバにこのCASサーバを登録するには、以下を確認してください。

  • ブラウザは、CASサーバとアプリケーションサーバのどちらにもアクセスできること
  • アプリケーションサーバがこのCASサーバに 8443:tcp でアクセスできること
  • アプリケーションサーバの hosts ファイルにも cas.example.org を足しておくこと
  • アプリケーションサーバの CAS 連携モジュールは証明書チェックを無視する設定になっていること

特に、PHPのCURLについては以下の設定を使います。

curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);

アプリケーションサーバでCAS認証しようとすると、CASサーバにリダイレクトされますが、以下の表示になると思います。

また、CASサーバのログでは試した直後には以下のログが出ているはずです。

2021-12-21 04:13:02,682 ERROR [org.apereo.cas.services.web.support.RegisteredServiceResponseHeadersEnforcementFilter] - <Service unauthorized>

つまり、許可サービスリストの登録が0で、いま試したアプリケーションサーバもはじかれている状態になります。

Docker のカスタマイズと Application ServiceRegistry の設定

ここからは、チェックアウトしているDocker のビルドキットをカスタマイズして、Application ServiceRegistry を設定し直したコンテナイメージを作り直していきます。

まずは2つファイルを新規追加します。1つは cas サーバの起動設定ファイルで、JSONリポジトリを有効に指定します。1つはテスト用アプリケーションサーバの定義ファイルで、http://192.168.56.??/ で動作することを前提としています。

etc/cas/config/cas.properties

cas.server.name=https://cas.example.org:8443
cas.server.prefix=${cas.server.name}/cas

logging.config=file:/etc/cas/config/log4j2.xml

# for json services ( need build option
cas.service-registry.core.init-from-json=true
cas.service-registry.json.watcher-enabled=true
cas.service-registry.json.location=file:/etc/cas/services
#cas.service-registry.yaml.location=file:/etc/cas/services

# cas.authn.accept.enabled=false

etc/cas/services/test-2.json

{
     "@class" : "org.apereo.cas.services.RegexRegisteredService",
            "id":2,
            "serviceId":"http://192.168.*",
            "name":"DEV",
            "description":"dev",
            "allowedToProxy":true,
            "enabled":true,
            "ssoEnabled":true,
            "anonymousAccess":false,
            "allowedAttributes":["uid", "mail"]
            "extraAttributes":{
                "someCustomAttribute":"Custom attribute value"
            },
            "evaluationOrder":2
}

続いて、gradle のビルド定義ファイルを置き換えます。ちょっと面倒なのですが、以下の build.grade ファイルを取得して、手元のファイルを置き換えてしまいます。

その上で、L930 くらいにある dependencies のセクションに ServiceRegistry の JOSN ストレージモジュールを足します。${project.'cas.version'} の変数も、このまま書きます。

dependencies {
    /**
     * CAS dependencies and modules may be listed here.
     *
     * ...
     */
     ...

    /* add */
    implementation "org.apereo.cas:cas-server-support-json-service-registry:${project.'cas.version'}"
}

続いて、Dockerfile を編集します。2カ所はファイルコピー操作を追加、gradle のビルドコマンドは、./gradlew dependencies コマンドを追加しています。2ステップに分解していますが、まとめてもかまいません。

FROM centos:centos7

MAINTAINER Apereo Foundation

ENV PATH=$PATH:$JRE_HOME/bin
ARG cas_version

RUN yum -y install wget tar unzip git \
    && yum -y clean all

# Download Azul Java, verify the hash, and install \
RUN set -x; \
    java_version=11.0.3; \
    zulu_version=11.31.11-ca; \
    java_hash=20218b15ae5ef1318aed1a3d5dde3219; \
    cd / \
    && wget http://cdn.azul.com/zulu/bin/zulu$zulu_version-jdk$java_version-linux_x64.tar.gz \
    && echo "$java_hash  zulu$zulu_version-jdk$java_version-linux_x64.tar.gz" | md5sum -c - \
    && tar -zxvf zulu$zulu_version-jdk$java_version-linux_x64.tar.gz -C /opt \
    && rm zulu$zulu_version-jdk$java_version-linux_x64.tar.gz \
    && ln -s /opt/zulu$zulu_version-jdk$java_version-linux_x64/ /opt/java-home;

# Download the CAS overlay project \
RUN cd / \
    && git clone --depth 1 --single-branch -b $cas_version https://github.com/apereo/cas-overlay-template.git cas-overlay \
    && mkdir -p /etc/cas \
    && mkdir -p cas-overlay/bin;

COPY thekeystore /etc/cas/
COPY bin/*.* cas-overlay/
COPY etc/cas/config/*.* /cas-overlay/etc/cas/config/
COPY etc/cas/services/*.* /cas-overlay/etc/cas/services/
# 追記
COPY build.gradle /cas-overlay/build.gradle

RUN chmod 750 cas-overlay/gradlew \
    && chmod 750 cas-overlay/*.sh \
    && chmod 750 /opt/java-home/bin/java;

EXPOSE 8080 8443

WORKDIR /cas-overlay

ENV JAVA_HOME /opt/java-home
ENV PATH $PATH:$JAVA_HOME/bin:.

# 書き換え
RUN mkdir -p ~/.gradle  && echo "org.gradle.daemon=false" >> ~/.gradle/gradle.properties
RUN ./gradlew dependencies && ./gradlew clean build --parallel \
    && rm -rf /root/.gradle

# 追記
RUN mkdir -p /etc/cas/config && mkdir -p /etc/cas/services && cp etc/cas/config/* /etc/cas/config/ && cp etc/cas/services/* /etc/cas/services/

CMD ["/cas-overlay/run-cas.sh"]

あらためてビルドと実行を行います。

./build.sh 6.4
./run.sh 6.4

もし JSON のサービス定義ファイルをロードできていれば、「READY」の後に以下のログが出ます。 [org.apereo.cas.services.AbstractServicesManager] - <Loaded [1] service(s) from [JsonServiceRegistry].> となっていれば、JSON のアプリケーション定義をロードするのに成功しています。

2021-12-21 09:07:28,348 INFO [org.apereo.cas.web.CasWebApplication] - <Ready to process requests @ [2021-12-21T09:07:28.342Z]>
2021-12-21 09:07:28,780 INFO [org.apereo.cas.services.AbstractServicesManager] - <Loaded [1] service(s) from [JsonServiceRegistry].>

アプリケーション定義ファイルのURL正規表現と、アプリケーションのURLがマッチしていれば、SSO認証したときに無事に CAS のログイン画面が表示されるはずです。パスワード認証に成功すると、CASサーバはチケットを発行してアプリケーションにリダイレクトします。ログは以下が残ります。

2021-12-21 09:39:39,055 INFO [org.apereo.cas.DefaultCentralAuthenticationService] - <Granted service ticket [ST-11-Li75-erdbw7sPZ5vdnW5QG1IcAY-66a57e874b0d] for service [http://192.168.56.76/login.php?auth_mode=CAS] and principal [casuser]>
2021-12-21 09:39:39,055 INFO [org.apereo.inspektr.audit.support.Slf4jLoggingAuditTrailManager] - <Audit trail record BEGIN
=============================================================
WHO: casuser
WHAT: {service=http://192.168.56.76/login.php?auth_mode=CAS, return=ST-11-Li75-erdbw7sPZ5vdnW5QG1IcAY-66a57e874b0d}
ACTION: SERVICE_TICKET_CREATED
APPLICATION: CAS
WHEN: Tue Dec 21 09:39:39 UTC 2021
CLIENT IP ADDRESS: 172.17.0.1
SERVER IP ADDRESS: 172.17.0.2
=============================================================

アプリケーションサーバがリダイレクトURLより認証チケットを受け取ると、アプリケーションサーバからCASサーバにチケットのValidationの問い合わせを行います。Validation に成功すると、CASサーバには以下のログが残り、アプリケーションサーバではユーザ名など属性情報を受け取ります。

2021-12-21 09:39:39,159 INFO [org.apereo.inspektr.audit.support.Slf4jLoggingAuditTrailManager] - <Audit trail record BEGIN
=============================================================
WHO: casuser
WHAT: {ticket=ST-11-Li75-erdbw7sPZ5vdnW5QG1IcAY-66a57e874b0d, service=http://192.168.56.76/login.php?auth_mode=CAS}
ACTION: SERVICE_TICKET_VALIDATE_SUCCESS
APPLICATION: CAS
WHEN: Tue Dec 21 09:39:39 UTC 2021
CLIENT IP ADDRESS: 172.17.0.1
SERVER IP ADDRESS: 172.17.0.2
=============================================================

今後

Java のビルドツールはあまり使ったことがないので、gradle の設定ファイルの記入位置を見つけるのに思いのほか時間がかかりました。

ですが、これですぐに検証サーバが用意できます。CASサーバのスタブを書くまでの間、クライアントのライブラリ更新やリファクタリングなどで頼もしく動いてくれるのを期待します。

Mac OS monterey 12.0.1

Mac OS monterey が 10 月 25 日にリリースされました。
WebClass への影響は Safari を除いてありません。

最新版の Safari では、一部の pdf を資料取り込みをすると、教材が読み込まれない問題が生じています(Blog:Safari15)。現在調査中で、詳細が分かり次第、webclass.jp で safari 15 のブラウザリリース報告をさせていただきます。

Release notes

リリースノートは以下に記載されています。

https://developer.apple.com/documentation/macos-release-notes/macos-12_0_1-release-notes

機能の追加に関しては、以下に記載されています。

https://www.apple.com/macos/monterey/features/

気になった変更点

プロキシ自動設定ファイル での HTTP 廃止

セキュリティの観点より、HTTP から HTTPS への移行が進んでいます。ブラウザでも、HTTPS をデフォルトとして扱うアプリケーションが増えています。その流れを汲み、プロキシ自動設定ファイルでも HTTP を廃止し、HTTPS のみを扱うように変更されました。

今後について

現在、monterey の 12.1 の Beta 版が公開されています。

リリースノートは以下の通りです。

ple.com/documentation/macos-release-notes/macos-12_1-release-notes

変更がまだ加わる可能性がありますが、正式版がリリースされ次第、また確認します。

Windows 11

Windows 11 が10月にリリースされました。
WebClassへの影響に関する調査結果としては、WebClassの推奨ブラウザであるMicrosoft Edge, Google Chrome, FireFoxに関して、Windows 11 でもインストール可能であり、大きな変更もありませんでした。
また、WebClassの基本的な操作や表示についても問題はありませんでした。
そのため、Windows 11 へのアップグレード後もこれまで通りWebClassを使用することできます。

Release notes

更新履歴は以下です
https://support.microsoft.com/ja-jp/topic/windows-11-%E6%9B%B4%E6%96%B0%E5%B1%A5%E6%AD%B4-a19cd327-b57f-44b9-84e0-26ced7109ba9

新機能については以下のページで紹介されています
https://docs.microsoft.com/ja-jp/windows/whats-new/windows-11-whats-new

システム要件は以下です
https://docs.microsoft.com/ja-jp/windows/whats-new/windows-11-requirements

気になった変更点

WebClassへの直接の影響は無いですが、いくつか気になった変更点を説明します。

Internet Explorerの廃止

Windows 11 では、 Internet Explorer 11は標準でインストールされておらず、新たにインストールすることも出来ませんでした。

TPM 2.0が必須になった

Windows 11のシステム要件として、 PCが TPM 2.0 に対応していることが新たに追加されました。また、TPM 2.0 に対応していてもそれを有効にする設定になっていないとWindows 11へアップグレードすることはできません。

お使いのPCがWindow 11 のシステム要件を満たすかどうかをチェックするアプリが以下のページからダウンロード可能です。
https://www.microsoft.com/ja-jp/windows/windows-11#pchealthcheck

以下のページでは、TPM 2.0 を有効にする方法について説明されています。
https://support.microsoft.com/ja-jp/windows/pc-%E3%81%A7-tpm-2-0-%E3%82%92%E6%9C%89%E5%8A%B9%E3%81%AB%E3%81%99%E3%82%8B-1fd5a332-360d-4f46-a1e7-ae6b0c90645c

エクスプローラーのホームタブがシンプルになった

エクスプローラーにて、ファイルを扱う基本操作を行う「ホーム」タブがシンプルになりました。
よく使われる操作のみが常時表示されており、Windows 10 と比べて、文字での説明もほとんどなくなっています。

Windows 11
WIndows 11のエクスプローラー

Windows 10
Windows 10 のエクスプローラー

デフォルトブラウザの変更方法が複雑になった

Windows 11では、デフォルトのブラウザを変更する場合、「設定」→「アプリ」→「規定のアプリ」と選択し、さらにファイル形式(HTM, HTMLなど)や通信プロトコル(HTTP, HTTPSなど)に対して1つずつデフォルトのブラウザを選択していく必要があります。
Windows 10 では、「Webブラウザー」という項目で一括で設定することができました。

Windows 11 のデフォルトブラウザの設定画面
デフォルトのブラウザ設定

まとめ

Windows 11の変更点に関して、WebClassへの直接の影響は無いですが、
人によっては、アップグレード前にTPM 2.0の有効化が必要であったり、アップグレード後のデフォルトブラウザの変更方法が変わっているため注意が必要となっています。

ウェブ・セキュリティ基礎試験のススメ

PHPカンファレンス2021をきっかけに、ウェブ・セキュリティ基礎試験に興味を持ちました。先日受験して合格することができましたが、とても良い試験だと思いましたので、ここでWebエンジニア向けに布教してみます。

どんな試験?

ウェブ・セキュリティ基礎試験では、Webエンジニア必読と言われる「体系的に学ぶ 安全なWebアプリケーションの作り方 第2版」からそのまま出題されます。Webエンジニアとして必要なセキュリティに関する基礎的な知識があるかを問われます。

誰に受験をお勧めできるか?

Webエンジニアとして、セキュリティを保つためにやるべき事、やってはならない事を知らないと、危険なシステムを作ってしまう可能性があります。また、開発チーム全員が基本的な知識水準を保たないと、コードレビューも大変です。なので、もし以下に自分は該当するなと思った人には、この試験を受験し勉強することをお勧めします。

  • 自分の開発するWebアプリケーションで、どこが脆弱になりやすいポイントなのか説明できない。
  • 一般的な脆弱性の原因と対策を一通り説明できない。

勉強方法は?

主教材「体系的に学ぶ 安全なWebアプリケーションの作り方 第2版」を読み込むのが基本になります。特に、各脆弱性の原因と影響範囲、対策をすべて納得した状態で覚え込むのが大事です。

副教材として「Webブラウザセキュリティ Webアプリケーションの安全性を支える仕組みを整理する」が良かったです。HTTPやCookieなどの基本的な知識が既にあるなら、この本から読んでもいいかもしれない。Webを構成する各領域における関心事が整理されているため、納得しながら理解を深めることができました。

その他合格のためのTips:

  • 受験者の合格期を読むと分かりますが、章によって出題される問題数に偏りがあります。
  • 試験は1時間ですが、結構時間が余ります。何回か見直すつもりでどんどん進めると良いかと思います。
  • 少なくとも自分の受験時は、教材の各脆弱性まとめ欄に書かれている「影響を受けるページ」をよく聞かれ、悩んだ覚えがあります。あまり重視していませんでした😢

最後に

さて、無事合格でき、知識の棚卸しができたのはいいのですが。脆弱性のトレンドは変化していきます。今後も知識をアップデートし続けることが必要です。このブログでは各ブラウザのリリースノートを追っていますが、これもキャッチアップ方法の一つになります。実務につながる勉強は楽しいものですし、今後も積極的に取り組んでいきたいです。