カテゴリー別アーカイブ: web

Google Chrome 59のHeadlessモードを使ってみる

Headless modeを内蔵したGoogle Chrome 59がリリースされたので、Python + Seleniumで使ってみました。

これまでheadlessと言えば、Xvfbを使って仮想的なディスプレイに作画することで実現していました。しかしGoogle Chrome 59以降ではChrome自体にheadless modeが内蔵され、Xvfbを使わなくてもディスプレイを持たないCLI環境でWebページのテストなどをできるようになりました。

そこでUbuntu 17.04上でGoogle ChromeのHeadlessモードを試してみました。

Chromeをインストール

Ubuntu 17.04にはChromeのパッケージがない1ので、まずChromeのダウンロードページからdebパッケージをダウンロードします。

Chromeはいくつか依存パッケージがあるので、それらのパッケージをインストールした後にdpkgコマンドでインストールします。

$ sudo apt install libappindicator1 libindicator7
$ sudo dpkg -i google-chrome-stable_current_amd64.deb
$ which google-chrome-stable
/usr/bin/google-chrome-stable

Seleniumとchromedriverをインストール

Seleniumは問題ありませんが、Ubuntu 17.04にあるchromedriveはChrome 59をサポートしていません。そこで最新のChromeDriverをダウインロードしてインストールします。

$ sudo apt install python3-selenium
$ cd ~/bin
$ unzip ~/Download/chromedriver_linux64.zip
$ ./chromedriver -v
ChromeDriver 2.30.477691 (6ee44a7247c639c0703f291d320bdf05c1531b57)

Headless modeを試してみる。

Pythonで次のようなスクリプトを書いて、Chromeをheadless modeで使えるか試してみました。

#!/usr/bin/python3

import os

from selenium import webdriver
from selenium.webdriver.chrome.options import Options

CHROME_BIN = "/usr/bin/google-chrome-stable"
CHROME_DRIVER = os.path.expanduser('~/bin/chromedriver')

options = Options()
options.binary_location = CHROME_BIN
options.add_argument('--headless')
#options.add_argument('--remote-debugging-port=9222')

driver = webdriver.Chrome(CHROME_DRIVER, chrome_options=options)

#driver.get("chrome://settings/help")
#driver.save_screenshot("/tmp/help.png")
driver.get("https://www.yahoo.co.jp")
driver.save_screenshot("/tmp/yahoo.png")
print(driver.title)
driver.quit()

このスクリプトを実行してYahoo!のページタイトルYahoo! JAPANとスクリーンショットが作成されていれば成功です。

画面のサイズ指定ができない

これまでWindowsサイズをset_window_size()で指定していましたが、これがあると次のようなエラーを引き起こします。

selenium.common.exceptions.WebDriverException: Message: unknown error: cannot get automation extension
from unknown error: page could not be found: chrome-extension://aapnijgdinlhnhlmodcfapnahmbfebeb/_generated_background_page.html
  (Session info: headless chrome=59.0.3071.86)
  (Driver info: chromedriver=2.30.477691 (6ee44a7247c639c0703f291d320bdf05c1531b57),platform=Linux 4.10.0-21-generic x86_64)

解決策がないかとググってChromeDriver error “unknown error: cannot get automation extension”を見つけたのですが、私の環境では

options.add_argument("disable-extensions");
options.add_argument("--start-maximized");

というオプションを追加しても解決せず、set_window_size()を削除する必要がありました。

脚注

  1. UbuntuにはChromeのパッケージはありませんが、オープンソース版のChromiumがあります。 

WordPress REST APIを使った投稿と更新

WordPressのREST API(v2)を使用して記事の投稿と更新をしてみました。どうやってOAuth POSTをPHPからするか少し悩みましたが、分かってみると簡単にWordPressのREST APIを使用出来ました。

セットアップ

WordPress REST APIを使うには、REST APIのプラグインと認証プラグインをインストールして有効にしておく必要があります。

REST APIプラグイン

REST API(v2)プラグインは、通常のプラグインと同様にWordPressのダッシュボードからプラグインを検索してインストールします。

Application Passwordを使用する場合

Application Passwordは、ネット上を生のパスワードが流れます。 ローカル環境で使用するときには簡単で便利ですが、本番環境では必ず通信経路が暗号化されたHTTPS(SSL/TSL)で接続する必要があります。

Application Passwordを使用する場合は、OAuthプラグインと同様にApplication Passwordプラグインのページからアーカイブをダウンロードしてインストールします。

プラグインを有効にしたら、WordPressのダッシュボードから「ユーザー > あなたのプロフィール」と進んで、ページの最後の方にある「Application Passwords」でパスワードを自動生成します。このパスワードは再表示と修正はできないので、表示されたら安全な所に記録しておきます。修正や再確認が必要なときは、「Revoke」ボタンで現在のパスワードを無効にして再生成することになります。

Oauthを使用する場合

OAuthを使用する場合は、OAuthプラグインのGitHubページからアーカイブを取得して、OAuthプラグインをインストールします。

プラグインのインストールして有効にしたら、OAuthキーを取得しておきます。OAuthキーの取得方法は、WORDPRESS REST APIのOAUTHトークンを取得を参照して下さい。

OAuthライブラリ

プログラムからOAuth認証を使用するにはライブラリーが必要です。

言語 ライブラリ名
Ruby Oauth
PHP HTTP_OAuth
Pyhton Requests-OAuthlib

記事の投稿と更新サンプル

Application Passwordでのサンプル

Application Passwordは普通のBASIC認証と同じなので、curlコマンドにユーザ名1とアプリケーションパスワードを引数として渡すことで簡単にAPIを叩くことができます。

#!/bin/bash

read -r -d '' json << _EOM_
  {
    "title": "Post new draft with cat 2",
    "content": "TEST<br/>TEST",
    "categories": [2],
    "status": "draft"
  }
_EOM_

curl -X POST \
     -u "user:applicatopn_password" \
     -H "Content-Type: application/json" \
     -d "$json" \
     https://MY_WORDPRESS_SITE/wp-json/wp/v2/posts

-uオプションを指定しないでも、エンコード済みのAuthorizationヘッダー2を加えてもAPIにアクセスできます。

curl -X POST \
     -H "Authorization: Basic dXNlcjphcHBsaWNhdG9wbl9wYXNzd29yZA==" \
     -H "Content-Type: application/json" \
     -d "$json" \
     https://books.nosuz.jp/wp-json/wp/v2/posts

BASIC認証方法は、パスワードをBase64でエンコードしているだけなので簡単にデコードして読み取ることができます。

$ echo dXNlcjphcHBsaWNhdG9wbl9wYXNzd29yZA==|base64 -d
user:applicatopn_password

OAuthでのサンプル

OAuthのサンプルは、PHPとそのOAuthライブラリHTTP_OAuthを使用しました。ライブラリのインストール方法は、WORDPRESS REST APIのOAUTHトークンを取得を参照して下さい。

PHPからOAuth認証でPOSTする時に、HTTP_OAuthライブラリにデータを指定する方法が分からず少し困りました。しかしHTTP_Request2のインスタンスにデータをセットし、それをHTTP_OAuthに渡すことでOAuth認証でPOSTできました。

require_once('HTTP/OAuth/Consumer.php');

$oauth = new HTTP_OAuth_Consumer(
    $oauth_secret->client_key,
    $oauth_secret->client_secret,
    $oauth_secret->access_key,
    $oauth_secret->access_secret
);

# POST OAuth
$http_request = new HTTP_Request2();
$http_request->setHeader("Content-Type: application/json");
$http_request->setBody(json_encode($article));

$consumer_request = new HTTP_OAuth_Consumer_Request();
$consumer_request->accept($http_request);
$oauth->accept($consumer_request);
$result = $oauth->sendRequest($post_endpoint)->getResponse();

これを使ってWordPressに記事を投稿と更新するプログラムを書いてみました。このプログラムは、HTTP_OAuthライブラリの他にテンプレートエンジンのTwigを使用しています。

phpのビルトインサーバを起動させて

php -S localhost:8080

http://localhost:8080/wp-post.phpにアクセスします。最初にアクセスした時には新規投稿となり、結果には記事のIDが埋め込まれているので次からは更新となります。

参照と脚注

  1. Application PasswordによるBASIC認証でのユーザ名は、WordPressのユーザ名でありApplication Passwordを登録した時のアプリケーション名ではありません。これを勘違いしていて私はだいぶ時間をロスしました。

  2. Authorizationヘッダーは、ユーザ名とパスワードを:でつないでBase64でエンコードしただけです。

    $ echo -n 'user:applicatopn_password' | base64
    dXNlcjphcHBsaWNhdG9wbl9wYXNzd29yZA==
    

WordPress REST APIのOAuthトークンを取得

WordPress REST APIで使用するOAuthトークンを取得するPHPプログラムを作成した。プログラムを作成したと言っても、TwitterのOAuthを取得するプログラムからエンドポイントを変更しただけでWordPress用のOAuthトークンを取得できました。

WordPress REST APIは、ユーザを認証する三種類(通常のパスワード、アプリケーションパスワード、OAuth)の方法をサポートしています。この内通常のパスワードとアプリケーションパスワードを使った方法は、通信経路が暗号化されていないHTTP://というサイトではパスワードが丸見えになってしまい危険で使用できません。

そのような場合には、OAuthを使ったユーザ認証を使用します。このOAuthを使った認証では、自分と相手のみが知っている鍵(トークン)を使って署名を作成し、その署名だけを送ります。そのため通信が覗かれても署名に使われたトークンを知られることがなく安全です。

ただしこのOAuthに使用するトークンを取得するには、WordPressを使用するサイトと何度か通信する必要があります。手動でやり取りしてトークンを取得することも可能ですが、Webサーバとブラウザがあればとても簡単です。

そこで今回は、OAuthトークンを取得するプログラムをPHPで作成し、PHPが持っているビルトインサーバを使用して走らすことでWordPress REST APIで使用するOAuthトークンを取得しました。わざわざApacheなどの本格的サーバを準備すること無くテスト用OAuthトークンを簡単に取得出来ました。

準備

WordPressのプラグインをインストール

WordPressにOAuthプラグインをインストールします。またREST APIのプラグインはOAuthトークンを取得する時に必要ないと思いますが、動作確認の時に必要なので合わせてインストールしておきます。

WordPress REST API(Version 2)は、WordPressのダッシュボード「プラグインを追加」で検索すると見つかるので、それをインストールします。登録済みOAuthプラグインに無いようなので、OAuthプラグインのページからアーカイブをダウンロードしてインストールします。

どちらもプラグインをインストールした後は、忘れずにインストールしたプラグインを 有効 にしておきます。

REST APIが有効になっているかは、次のコマンドで確認できます。

$ curl https://tech.nosuz.jp/wp-json/wp/v2/posts

これで投稿が取得できればREST APIに関しては問題ありません。

PHPとOAuthライブラリをインストール

手元のパソコンでは、Ubuntu 15.10が動いています。

まずパッケージからPHPをインストールします。

$ sudo apt install php5-cli php-pear

普通のphp5はApacheなどもインストールされてしまうので、php5-cliをインストールします。またPHPのライブラリをインストールするためにpearもインストールします。

次はOAuthライブラリのインストールですが、HTTP_OAuth1はα版とのことでインストールする時にバージョンを指定する必要があります。

$ sudo pear install HTTP_OAuth
Failed to download pear/HTTP_OAuth within preferred state "stable", latest release is version 0.3.2, stability "alpha", use "channel://pear.php.net/HTTP_OAuth-0.3.2" to install
install failed

$ sudo pear install HTTP_OAuth-0.3.2

PHPでOAuthトークンを取得するプログラム

このライブラリを使ってOAuthトークンを取得するプログラムget-oauth-token.phpを作成しました2

OAuthトークンの取得

アプリケーションの登録

まずOAuthを使用するアプリケーションを、WordPressのダッシュボードにある「ユーザー > Application」で登録します。

application

AddNewApplication

「Add New」ボタンをクリックして、アプリケーションの名前と簡単な説明、callbackアドレスを記入して「Add Consumer」ボタンをクリックすれば完了です。

EditApplication

今回手元のPHPビルトインサーバ機能を使用するので、callbackは次のように記入しました。このようにcallback先のURLは自分がアクセスできればネットに公開されていなくても大丈夫です。

http://localhost:8080/get-oauth-token.php

登録されるとOAuth Credentialsが表示されるので、Client KeyとSecretをコピーしてJSON形式で別のファイルに保管しておきます。これらの値は、プログラムにハードコードしないでファイルから読み込んで使用します。

OAuthトークンを取得

PHPビルトインサーバを起動して、上で作成したOAuthトークンを取得するプログラムへのアクセスを待ちます。ビルトインサーバを起動するには、OAuthを取得するプログラムが有るディレクトリに移動し、そこで次のコマンドを実行します。

$ php -S localhost:8080

次にブラウザからhttp://localhost/get-oauth-token.phpにアクセスすると、WordPressのユーザ認証ページにリダイレクトされます。ここで「Authorize」をクリックすると取得したOAuthトークンが表示されます。

authorize

このトークンは後で使用するので、安全なファイルに記録しておきます。

参照と脚注

  1. HTTP_OAuthを使用したのは、PHPのOAuthライブラリーを検索した時に結果の最初の方に有ったからです。まだα版ということですが、特に問題はありませんでした。

  2. このWordPress用のOAuthトークンを取得するプログラムは、APIのエンドポイントURLをWordPress用に修正したのみでTwitterのOAuthトークンを取得するプログラムと全く同じです。

ApacheでOCSP Staplingを有効にしてみた

Apacheでは設定を二行加えるだけでOCSP Staplingを有効にできた。

HTTPSによるアクセスでは、通信を暗号化するためにサイトの証明書が必要です。

この証明書には有効期間が有りますが、有効期間内であっても証明書が失効している可能性が有ります。そのため証明書が失効していないか確認する必要が有ります。

証明書が有効か確認するには、CRLとOCSPという二種類が有ります1。この二種類の方法の内、現在はレスポンスのが速いという利点2から主にOCSPが使用されています。

ただしOCSPにも次のような問題点が有ります3

  • OCSP応答が得られない場合にサーバ証明書の失効検証を正しく行わないままSSL通信を許可してしまう可能性がある。
  • OCSPを使った場合には、あるサイトにアクセスがあったことをOCSPレスポンダも知り得てしまう。

そこでこの問題を回避するために、RFC6066ではコンテンツを配信するサーバがOCSPレスポンダを兼ねる(キャッシュする)ことができる方法が提案されています4。これによりOCSPレスポンダに関するどちらの問題点も解消できます。

なおこのRFC6066の証明書状態リクエスト(TLS Certificate Status Request)は、OCSP staplingと一般に呼ばれています5

OCSP staplingを有効にするApacheの設定

OCSP staplingの説明が長くなりましたが、ApacheでOCSP staplingを有効にする設定はいたって簡単です6

UbuntuのapacheパッケージでApacheをインストールしたならば、SSLのグローバル設定である/etc/apache2/mods-enabled/ssl.confに次の二行を追加します。

SSLUseStapling		On
SSLStaplingCache        shmcb:${APACHE_RUN_DIR}/ssl_stapling(32768)

設定はこれだけです。

設定を変更したらapacheを再起動します。

$ sudo apachectl configtest
Syntax OK

$ sudo service apache2 restart

OCSP staplingが有効か確認

OCSP staplingが有効になっているかは、opensslコマンドを使用して確認します。

OCSP staplingが有効になっている場合は、次のようにOCSP Response Dataを含む結果が得られます。

$ openssl s_client -connect www.example.jp:443 -status -servername www.example.jp < /dev/null | head
depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X1
verify error:num=20:unable to get local issuer certificate
verify return:0
CONNECTED(00000003)
OCSP response: 
======================================
OCSP Response Data:
    OCSP Response Status: successful (0x0)
    Response Type: Basic OCSP Response
    Version: 1 (0x0)
    Responder Id: C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X1
    Produced At: Dec 23 14:51:00 2015 GMT
    Responses:
DONE

もし次のようにOCSP response: no response sentとなった場合は、OCSP staplingが有効になっていません。

$ openssl s_client -connect www.example.jp:443 -status -servername www.example.jp < /dev/null | head
depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X1
verify error:num=20:unable to get local issuer certificate
verify return:0
DONE
CONNECTED(00000003)
OCSP response: no response sent
---
Certificate chain
 0 s:/CN=www.nosuz.jp
   i:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X1
 1 s:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X1
   i:/O=Digital Signature Trust Co./CN=DST Root CA X3
---
Server certificate

参照と脚注

Let’s Encryptから証明書を取得して常時HTTPS化

  • Let’ EncryptからHTTPS(暗号化)通信に必要な証明書を無料で発行してもらいました。
  • HTTPSが有効になるようにApacheの設定を変更して、暗号化されていることを示す鍵アイコンが表示されることを確認しました。
  • HTTPでのリクエストをHTTPSに転送して常時HTTPS化しました。
  • HTTPSの証明書には有効期限があるので、自動的に更新できるようにしました。

  • 証明書の取得
  • Apacheの設定
  • 証明書の自動更新

HTTPSはWebの通信を暗号化するために使われていると一般的には思われています。しかし実際には暗号化だけでなく、Webサイトを運営する会社・組織の身元を保証する1ためにも使われています。

そのため身元を保証してもらいたい会社・組織は審査を受ける必要が有り、時間とそれなりのお金をかけて証明書を発行してもらっていました。その結果としてユーザは安心してオンラインで買い物をしたり預金を確認できます。

一方、身元の保証は必要なく単純に安全な通信のためにHTTPSを使いたいという場合には、これまでのやり方は非常に手間がかかりハードルが高いものです。そこで誰でもHTTPSの暗号化機能を手軽に使えるようにするためLet’ Encryptプロジェクトが、Webサイトの身元は保証しないけど暗号化に使用できる証明書を機械的かつ無料で発行する実験が開始されました。

会社・組織の身元保証が無いので商業利用向きではありませんが、個人のWebサーバでも簡単にHTTPSを使って安全な通信を行えるようになりました。

ここではHTTPSに必要な証明書の取得方法からサーバの設定、証明書の更新方法について説明します。

証明書の取得

HTTPSの証明書は、専用のプログラムletsencryptを使用して取得します。

letsencryptをインストール

letsencryptは、Debianなどパッケージとして配布されている場合も有りますが、Ubuntuではまだパッケージになっていなのでgitコマンドを使ってプログラムを取得します2

$ sudo apt-get install git # gitをまずインストールする。
$ git clone https://github.com/letsencrypt/letsencrypt
$ cd letsencrypt

HTTPSの証明書を取得

letsencrypt-autoコマンドを実行してHTTPSの証明書を取得します。

WebサーバにApacheを使用している場合は、次のコマンドを実行します3

ただしこのコマンドを実行するとサーバが一時的に停止します。ダウンタイムを最小にするには、--apacheの代わりに--webrootオプション(webroot mode)を使用します4

$ ./letsencrypt-auto --apache

実行すると必要なパッケージがインストールされ、証明書を取得するために必要な質問をいくつかされます。質問に答えてしばらく(1分程度)待つとHTTPS証明書が/etc/letsencrupt/live/に保存されます。

もしかすると、証明書を取得するときにHTTPSポートが開いている必要があったかもしれません。その場合は、次の「HTTPSポート(443)を公開」を参照してください。

証明書を取得するときに質問される項目は次の3つです。

  1. 現在のApacheがホストしているドメインの内で証明書に含めるドメイン名。
  2. 連絡先のメールアドレス。
  3. Let’s Encryptを使用するための決まりに同意するか。

1.で選択したドメイン名は、全て同じ証明書を使ってHTTPS通信が行えます(SAN Certificate?)。また-dオプションで事前に対象のドメイン名を指定しておくことができます。

複数のサーバ名を指定すると「複数のサーバ名はサポートしていない」というようなエラーが表示されますが、問題なく使えています。

2.と3.の質問は、それぞれ--email--agree-tosオプションで事前に指定しておけます。

オプションの詳しい説明は、

$ ./letsencrypt-auto --help all

で取得できます。説明が表示されるのが遅いので、次のようにリダイレクトを使って説明を保存しておくと後で参照しやすくなって便利だと思います。

$ ./letsencrypt-auto --help all > help.txt

--apacheオプションは、証明書の取得の他にインストールもするということですが、具体的に何をしているのか私には分かりませんでした5。Apacheの設定を変えてくれるということもありませんでしたし、インストール機能がないwebrootでも証明書は同じ場所に保存されました。

またletsencrypt-autoを実行すると、/var/log/letsencrypt/letsencrypt.logにログが記録されます。

HTTPSポート(443)を公開

HTTPSで通信するには、HTTPS用のポートに外部からアクセスできる必要が有ります。

HTTPSポートを開くためには、次のルールをiptablesの設定に追加します。既にHTTP用のルールが書かれていると思うので、その後ろあたりに追加するのが良いと思います。また二番目のようにmultiportモジュールを使ってHTTPポートとHTTPSポートをまとめてルールにすることもできます。

iptables -A INPUT -p tcp --syn -m state --state NEW --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --syn -m state --state NEW --dport 443 -j ACCEPT

# or

iptables -A INPUT -p tcp --syn -m state --state NEW -m multiport --dports 80,443 -j ACCEPT

iptablesの設定はサイトを守る上で非常に重要ですので、十分注意してください。

Apacheの設定

HTTPSの証明書を取得できたら、その証明書を使ってHTTPS通信ができるようにApacheの設定を変更します。

SSLモジュールを有効化

HTTPS通信をするには、ApacheのSSLモジュールを有効にします。

次のようにしてSSLモジュールを有効にしますが、もしかするとletsencrypt-auto --apacheが既にモジュールを有効にしているかもしれません。もちろん既にsslモジュールが有効になっているときにこのコマンドを実行してしまっても問題はありません。

$ sudo a2enmod ssl
$ sudo a2ensite default-ssl # optional

a2ensite default-ssl/etc/apache2/sites-enabledにSSL用の雛形default-sslが作成されますが、000-default.confに設定を加えても問題ありません。

Apacheの設定変更

実際にHTTPSで通信するにするには、<VirtualHost *:80/>に加えて<VirtualHost *:443/>を加えます。

例えば、私の場合はVirtualHostなので、次のような内容を設定に追加しました。

<VirtualHost *:443>
    # これまでの設定内容をコピーする。

    SSLEngine		on
    SSLProtocol		all -SSLv2 -SSLv3

    # Apache 2.4.7まで
    SSLCertificateFile    /etc/letsencrypt/live/_MY_DOMAIN_NAME_/cert.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/_MY_DOMAIN_NAME_/privkey.pem
    SSLCertificateChainFile /etc/letsencrypt/live/_MY_DOMAIN_NAME_/chain.pem

    # Apache 2.4.8以降
    # SSLCertificateChainFileが廃止になった。
    #SSLCertificateFile    /etc/letsencrypt/live/_MY_DOMAIN_NAME_/fullchain.pem
    #SSLCertificateKeyFile /etc/letsencrypt/live/_MY_DOMAIN_NAME_/privkey.pem

    # 使用したSSLプロトコルと暗号化方式をログに記録することもできます。
    #CustomLog logs/ssl_request_log \ "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
</VirtualHost>

その他の設定オプションは、/etc/apache2/sites-available/default-ssl.confを参照して設定に追加します。

また/etc/letsencrypt/options-ssl-apache.confに書かれているようなグローバル設定は、/etc/apache2/mods-enabled/ssl.confで設定します。

設定を変更したら、次のコマンドで設定の書き方に問題がないか確認しておきます。

$ sudo apachectl configtest

SSLで接続できるか確認

Apacheの設定が終わったら、サーバを再起動してHTTPSで接続できるか確認します。

$ sudo service apache2 restart

まずApacheがHTTPSのポートを見ているか確認します。ApacheがHTTPSポートで待っている場合は、次のように表示されます。何も出力がないときはポート番号に間違いがないか設定を再確認してください。

$ ss -lnt | grep 443
LISTEN     0      128                      :::443                     :::*

次にWebブラウザからWebページを開いてみます。この時URLの頭をhttp:からhttps:に変更してページにアクセスします。

Webページを開けない時は、ファイアウォールでHTTPSのポートが閉じられていないか確認してください。

HTTPを転送して常時HTTPS化

常時HTTPS化するかはサイト毎のポリシーですので必須ではありません。

常時HTTPS化するには、HTTPで接続を求めてきたリクエストを全てHTTPSにリダイレクトします。この例ではリダイレクトステータスをR=301で恒久的な移動を示す301にしています。

<VirtualHost *:80>
    ServerName www.nosuz.jp
    LogLevel warn
    #LogLevel warn rewrite:trace8
    CustomLog ${APACHE_LOG_DIR}/access.log
    ErrorLog ${APACHE_LOG_DIR}/error.log
    RewriteEngine On
    RewriteRule . https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</VirtualHost>

アドレスバーの鍵表示

HTTPSでアクセスすると多くのWebブラウザでは、通信が暗号化されていることを鍵アイコンが表示されます。

しかしHTTPSでアクセスしているのに鍵アイコンが表示されないことが有ります。この時は、画像やJavaScriptの読み込みなど一部にHTTPでのアクセスが使われています。

鍵アイコンを表示させるには、ページのソースを開いてhttp://となっている部分を探して、その部分をhttps://またはhtpを削除して単に//と変更します6

証明書の自動更新

Let’s Encryptが発行する証明書の有効期間は90日なので、最低三ヶ月に一度証明書を更新する必要が有ります。もちろんここも人が介在すること無く証明書の更新を自動化できます。

更新処理の自動化にはcronサービスを使用するのが簡単です。例えば次のようなエントリーをrootのcrontabに登録すると、奇数月7の15日3時21分に更新処理が行われます。

21 3 15 */2 * /_PATH_/letsencrypt-auto certonly --text --renew-by-default --webroot -w /var/www-root -d www.example.jp > /var/log/letsencrypt/renew-cert.log 2>1 && service apache2 restart

参照と脚注

  1. Webデザイナーなら知っておくべき サーバ知識相談室

  2. Quick Start Guide – Let’s Encrypt

  3. Ngixサーバの場合は、--ngixオプションが将来使えるようになる予定です。しかし現在は、--webrootオプションを使うなど他の方法で証明書を取得する必要が有ります。

  4. webroot modeを使用すると、起動中のWebサーバを使用して証明書を取得します。そのため多少時間がかかる証明書を取得する過程でもサービスを停止しないですみます。ただしHTTPSを有効にするところでWebサーバの再起動が必要になります。

  5. Plugins – Let’s Encrypt

  6. URLがいきなり//で始まる場合は、現在のプロトコールでそのリソースにアクセスします。そのため<img src='//domain/img.png'>を含むページをHTTPで開いた場合はHTTPでimg.pngを取得し、HTTPSで開いた場合はHTTPSでimg.pngを取得します。

  7. crontabの月は時間や分とスタートが異なり1-12なので、*/2は1-12/*と同じ意味です。よって2で割り切れる順番の月、すなわち0番目1月、2番目3月…と奇数月にcrontabエントリーが実行されます。偶数月ならば、21 3 15 2-12/2 * commandとなります。

CGIプログラムが動かずにソースが表示される

設定は正しいのにCGIプログラムのソースが表示されてしまう原因は、mod_cgiがロードされていなかった。

Apacheを使ったWebサーバを新規にセットアップしたらCGIプログラムが動かないで、そのソースコードが表示されてしまう問題にぶつかりました。

まず最初に考えるのは、設定が間違っているのではないかということです。そこで既に動いているサーバから設定ファイルを持ってきて見ましたが、やはり新規サーバではソースコードが表示される問題が発生していまします。

ググっても有用なページを見つけることができずさんざん悩んでしましました。

仕方なしに一息入れて頭をリセットすると、設定ファイルに現れない設定が間違っていることに気が付きました。

ApacheでCGIを有効にする設定

見落としていたCGI設定のが何であったかという前に、CGIを有効にするApacheの設定を再度確認しておきましょう。

ApacheでCGIを有効にする設定にはに二種類あります1

  1. ScriptAliasを使う。
  2. HandlerとExecCGIを使う。

UbuntuのパッケージでApacheをインストールすると、設定ファイルは/etc/apache2/sites-enabled/000-default.confです。このファイルには最初から<VirtualHost>指定が書き込まれているので、この中にCGIの設定を挿入します。

ScriptAliasを使う。

ScriptAlias /url/ /abs/dir/

とすると、/abs/dirにある実行ファイルが全てCGIプログラムと認識されます。そして/url/cgiにアクセスすると、/abs/dir/cgiが実行されます。

/abs/dirは、通常のファイルとしてアクセスできないようにhtmlのDocumentRoot外に置きます。また/abs/dirの実行ファイルは全てCGIとして実行できてしまうので、CGIスクリプトのバックアップファイルが残っていたりしないよう注意する必要が有ります。

HandlerとExecCGIを使う。

特定の拡張子を持ったファイルのみをCGIプログラムとして実行したいときは、こちらの設定を使用します。

<Directory /var/www/html/cgi-dir>
  AllowOverride None
  Options +ExecCGI
  AddHandler cgi-script .rb .sh
</Directory>

# or

<Location /cgi-dir>
  AllowOverride None
  Options +ExecCGI
  AddHandler cgi-script .rb .sh
</Location>

この設定では、/var/www/html/cgi-dirにある*.rbまたは*.shというファイルがCGIプログラムとして認識されます。

その結果DocumentRootが/var/www/htmlだとすると、/cgi-dir/cgi.rbにアクセスすると/var/www/html/cgi-dir/cgi.rbが実行されます。

Handlerで指定されなかった/var/www/html/cgi-dirにあるファイルは、CGIプログラムとして実行されることはありません。しかし通常のファイルとしてアクセスすることが可能ですので、CGIスクリプトのバックアップファイルなどCGIプログラム以外のファイルを置かないように注意する必要が有ります。

500 Internal Server Error

Internal Server Errorと表示されるときには、次の点を確認します。

  • CGIプログラムにアクセスするURLに間違いがないか。
  • CGIプログラムの実行フラッグ(x)がセットされているか。
  • CGIプログラムにエラーがあって異常終了していないか。

CGIスクリプトが動かないでソースが表示されるときには、設定が間違っていてCGIプログラムと認識されていませんので、設定ファイルをもう一度確認します。

見落としがちなCGIを有効にする設定

設定ファイルに絶対間違いはないのにCGIプログラムが動かず、そのソースが表示されるときにはどうしたら良いんだろう?

その場合は、ApacheにCGIを実行するモジュールが組み込まれているか確認します。

$ sudo apache2ctl -M|grep cgi
 cgi_module (shared)

このコマンドでcgi_moduleが表示されない時には、ApacheにCGIモジュールが組み込まれていません。

UbuntuのApacheパッケージでCGIモジュールを読みこませるには、/etc/apache2/mods-available/cgi.loadmods-enabledにコピーまたはリンクを作成します。

$ cd /etc/apache2/mods-enabled
$ sudo ln -s ../mods-available/cgi.load .
$ ls -l | grep cgi
lrwxrwxrwx 1 root root 26 11月  3 11:58 cgi.load -> ../mods-available/cgi.load

これでApacheを再起動するとCGIプログラムが動くようになるはずです。

参照と注釈

  1. Apache Tutorial: CGI による動的コンテンツ – Apache2.0
    Apache2.0のドキュメントを参照したのが悪かった。最新のマニュアルを参照していれば、最初にmod_cgiをロードするように注意書きがあってハマることはなかった。

複数サイズのfaviconを作成するワンライナー

convertコマンドの-cloneオプションを使えばワンライナーでマルチサイズfaviconを作成できる。

以前faviconを作成する方法を書きました1が、convertコマンドの-cloneオプションを使えば、もっと簡単に複数サイズのデータを持ったicoファイルを作成できる2ことがわかりました。

faviconをワンライナーで作成

アイコンの元となる画像をfavicon.pngだとすると、次のコマンド一発で複数サイズfavicon.icoを作成できます3

convert favicon.png \
	\( -clone 0 -resize 16x16 \) \
	\( -clone 0 -resize 32x32 \) \
	\( -clone 0 -resize 48x48 \) \
	\( -clone 0 -resize 150x150 \) \
	-delete 0 favicon.ico

注意:シェルが解釈しないようにするため括弧をバックスラッシュ(円記号)でエスケープする。

このワンライナーが何をしているかというと…

  1. favicon.pngをlayer 0に読み込む。
  2. 括弧内を処理して、その結果を新しいlayerに置く。
  3. 括弧内では、-clone 0でlayer 0すなわちfavicon.pngをコピーして指定のサイズに縮小する。
  4. layer 0の元画像を削除して、favicon.icoに合成する。

色数を制限したいときには、-colorsオプションを加えます。例えば-colors 256とすると、256色に制限できます。

参照と注釈

Webブラウザのタブにオリジナルアイコンを表示

アイコン画像(16px X 16pxと32px X 32px)を用意して、GIMPまたはnetpbmを使用してfavicon.icoを作成する。作成したファイルをWebページのトップディレクトリーに保存する。

WebページをChromeなどで表示すると、タブやアドレスバーにサイト独自のアイコン(favicon)が表示されます。このサイトでも使用しているWordpressは、インストールしただけではfaviconが表示されません。そこでWordpressを使用したサイトでfaviconを表示するようにしてしてみました。

faviconとは

faviconを簡単に説明する1と、もともとは名前から想像できるようにMicrosoft Internel Explorer(IE)の「お気に入り」に登録された時に表示されるデコレーションアイコンでした。それが時代を経て現在のブラウザーでは、サイトを識別しやすいようにタブやアドレスバーにも表示されるようになりました。

favcionはIEの独自機能であったためにfaviconは標準化されていませんが、全てのブラウザーがWindowsのアイコンファイル形式(*.ico)をサポートしています。現在はWindowsアイコンファイルの他に、多くのブラウザーでGIPとPNG形式も使用できます2

しかしあえてGIFやPNGを選択する必要もないので、ここでは全てのブラウザーで表示できるWindowsアイコンファイルのfavicon.icoを作成します。

Linux環境でfaviconを作成

faviconを作成するWebサービスも有りますが、Linux(Ubuntu)環境で使用できるGIMPとnetpbmを使ってfaviconの作成方法を作成しました。

アイコン画像の準備

まずアイコンに使用する16×16ピクセルと32×32ピクセルのアイコン画像を用意します。アイコンの元となる画像がある場合は、GIMPやimagemagickパッケージのconvertなどで縮小します。

機械的に縮小したファイルをそのまま使うのでなく、すこし修正したほうが選り見やすいアイコンになります。絵心の必要な作業なので、機械的に縮小したものを使って不満があったら修正する程度で良いかと思います。

GIMPを利用してfaviconを作成

まずGIMPをインストールし、GIMPを起動します。

$ sudo apt-get install gimp
$ gimp &

GIMPを起動させたら、次のようにしてfavicon.icoを作成します。

  1. 「File」メニューの「Open…」を選択して、縮小した画像ファイルを開きます。GIMP-open
  2. 「File」メニューの「Open as Layers…」を選択して、残りの縮小した画像ファイルを開きます。GIMP-open-layers
  3. 「File」メニューの「Export AS …」を選択して、Export Imageダイアログを開きます。
  4. 表示されたダイアログの下の方に「Select File Type」があるので、「By Extension」または「Microsoft Windows icon」になっていることを確認します。GIMP-export
  5. Nameをfavicon.icoとして、「Export」ボタンをクリックしてアイコンファイルを保存します。

コマンドラインからfaviconを作成

コマンドラインからfaviconを作成するには、netpbmパッケージに含まれるコマンドを使用します。

$ sudo apt-get install netpbm

$ ppmtowinicon -o favicon.ico <(pngtopnm icon16.png|pnmquant 256) <(pngtopnm icon32.png|pnmquant 256)

# bashは、標準出力を普通のリダイレクトではなくファイルに見せかけてコマンドの引数に使えて便利。
# シェルがbashでない場合は、pnm形式の中間ファイルを作成する。

$ pngtopnm icon16.png|pnmquant 256 > icon16.pnm
$ pngtopnm icon32.png|pnmquant 256 > icon32.pnm
$ ppmtowinicon -o favicon.ico icon16.pnm icon32.pnm

pnmquantを通しているのは、Windowsのアイコンに使用できる色数が最大256色なので、色数を256以下に制限するためです。

Ubuntu 15.10 (Wily)でインストールされるnetpbmパッケージのバージョンは10.0ですが、今後netpbmが10.44より新しいバージョンに置き換えられた場合は、pngtopnmppmtowiniconが廃止になるのでpngtopampamtowiniconを使用します。

faviconを設置

作成したfavicon.icoは、http://site/favicon.icoでアクセスできるようにWebページのトップディレクトリーに置きます3

faviconを設置したWebページを開いて正しく表示されることを確認します。表示されない時は、サーバのアスセスログを確認してfavicon.icoにアクセスしているか・エラーが無く取得されているか確認します。

参照

  1. Favicon – Wikipedia

  2. GIFとPNG形式のアイコンファイルを使用するには、全てのページのHTMLファイルに次のリンクタグを入れてアイコンファイルを指定します。

    <link rel="shortcut icon" href="http://example.com/icons/favicon.png" />
    <link rel="icon" href="http://example.com/icons/favicon.png" />
    
  3. WordPressでは、テーマのヘッダーファイルに次のリンクタグを挿入して全てのページでfaviconを表示させることも可能です。

    <link rel="shortcut icon" href="http://example.com/icons/favicon.ico" type="image/vnd.microsoft.icon" />
    <link rel="icon" href="http://example.com/icons/favicon.ico" type="image/vnd.microsoft.icon" />