DreamerDreamのブログ

夢想家の夢です。〜揚げたてのモヤっとしたものをラフレシアと共に〜

Djangoの使い方⑩サイトをhttps(SSL)サイトにする

以前記事から随分と時間が経ってしまいましたが、DjangoでWebサイトを構築しました。

dreamerdream.hateblo.jp

 

 

Webサイトの紹介はしませんが、今回はそのDjangoで作ったWebサイトをhttpsSSL)化します。

f:id:DreamerDream:20190221105409p:plain

 

ここ最近ではセキュリティーの関係でhttpプロトコルでのサイト接続は推奨されておらず、httpsSSL)として公開するのが望まれています。

 

しかしながら、https化するには「証明書」というものの発行が必用で、その証明書は結構高価なものなのです。

無料でなんとか利用出来ないか?と調べると

1.自前で用意する「オレオレ証明書」というものを利用する

2.無料の証明書発行機関を利用する

という2通りの方法があることが解ります。

1の方法は以前にラズパイサーバーで試しました。

dreamerdream.hateblo.jp

ですので、Apacheでのssl設定方法はなんとなーく解っているつもりでしたが・・・これがなかなか苦戦しました。

 

ひとまずしなければいけないことは

SSLが使う443ポートにアクセスできるようにすること。

これらの設定が済んでいることとして話を進めていきます。

SSLは無料証明書発行機関「Let's encrypt」を利用させていただきます。

Let's encryptは無料で90日間のSSL証明書を発行してくれる機関で、セキュアな通信を推進している有志の団体が提供してくれているものです。

Let's Encrypt - Wikipedia

え?90日間だけ?

と思われるかもしれませんが、実際には自動更新するというスクリプトを組んで運用する場合が多いようで、更新さえ出来ていれば半永久的に使える。という便利なもので、その更新さえも自動化出来るパッケージが提供されています。

今回はその便利で定評なcertbotクライアントをインストールして証明書の発行から更新まで行うことにします。

 

<参考リンク>

CentOS 7 + Apache 2.4 に Let’s Encrypt の証明書を導入する手順 | WEB ARCH LABO

とりあえずepelパッケージをインストール

yum install epel-release

したけど

No package epel-release available.

と怒られた?

ん?epel?何か聞覚えがるなー

と思ってブログを調べてみたら

 

dreamerdream.hateblo.jp

なんと!以前にここでインストールしてました!

んで、通常使用しない設定をしていたのでそこを変更。

nano /etc/yum.repos.d/epel.repo

の中身のenabled=0と上の記事でしていたのを

enabled=1

と変更するだけ。

 

epelがインストールされていたので、cerbotをインストール

$ sudo yum install certbot python-certbot-apache

f:id:DreamerDream:20190219133246p:plain

Complete!

無事にインストール出来ました!

 

では、早速証明書を取得しましょう。

sudo certbot certonly --webroot -w [ルートディレクトリ] -d [サイトアドレス]

 とするらしいです。

メールアドレス? 入力[メールアドレス]

同意書 同意なら[A]

メール送信? 必用なら[Y]

と入力すれば証明書が発行されー

To fix these errors, please make sure that your domain name was
entered correctly and the DNS A/AAAA record(s) for that domain
contain(s) the right IP address.

てないっ!?????

 

サクッと通るはずが何かエラーが出ました。

え?なんで??

 

調べましたところ、

<参考>

話題の無料SSL証明書!Let's Encryptで証明書の取得に失敗したときに確認すること | それからデザイン スタッフブログ

要は、「ルートディレクトリ」に指定したとこにcertbotが.well-knownというディレクトリを作って、その中身をWeb経由で発行機関が確認するそうな。

なるほど!ルートディレクトリは指定したけど、サイトがDjangoなどのフレームワークで運用してる場合、通常は不要なディレクトリにアクセス不可とするから(そういう設定にしてるから)アクセスして確認したいファイルにアクセスが出来ないってことですね!

よく見るとcertbotの吐いたレポートに

Domain: [アドレス]
Type: unauthorized
Detail: Invalid response from

http://[アドレス]/.well-known/acme-challenge/e(何らかの暗号キー)ep8:

と出ています。

通常の静的WebページであればApahe設定でDocumentRoot以下に.well-known/acme~~~というディレクトリを作れば外部からアクセスできるので問題無いようです。 

 

ということで、[サイトURL]/.well-known/~~ディレクトリにアクセス出来るようにしましょう。

dreamerdream.hateblo.jp

以前に書いたconf設定に任意のディレクトリを作り+.well-knownというディレクトリにアクセス許可を与えます。

(/var/www/django に指定した場合)

(上ブログのex.</etc/httpd/conf.modules.d/extra/vhost-draemon.conf>に 追記した場合)

Alias /.well-known /var/www/django/.well-known
<Directory /var/www/django/.well-known>
Require all granted
</Directory>

 

上の発行コマンド[ルートディレクトリ]に.well-knownを除いたディレクトリ名を 指定して実行!

sudo certbot certonly --webroot -w/var/www/django/ -d [アカウント]

とすると

f:id:DreamerDream:20190219203058p:plain

 今度は成功です!

 

sudo ls -l /etc/letsencrypt/live/[アドレス]

とすると証明書含む5種類のファイルが確認できます。

  • cert.pem
  • chain.pem
  • fullchain.pem
  • privkey.pem
  • README

これらはシンボリックリンクで、証明書が更新されても名前が変わらないからそのままApacheに一度これを設定すれば更新の度に書き換えなくても良い仕様なのだそうです。

 

/etc/httpd/conf.d/ssl.conf

に先ほど確認した証明書を追記します。ファイルがなければopen-sslの導入が必用です。(オリジナルの項目は念のためコメントアウトで残しておきます)

SSL設定ふぁいるに上で出来たシンボリックリンクを設定します。

</etc/httpd/conf.d/ssl.conf>

#SSLCertificateFile /etc/pki/tls/certs/localhost.crt
SSLCertificateFile /etc/letsencrypt/live/[サーバーアドレス]/cert.pem

 

#SSLCertificateKeyFile /etc/pki/tls/private/localhost.key

SSLCertificateKeyFile /etc/letsencrypt/live/[サーバーアドレス]/privkey.pem

 

#SSLCertificateChainFile /etc/pki/tls/certs/server-chain.crt

SSLCertificateChainFile /etc/letsencrypt/live/[サーバーアドレス]/chain.pem

 

OK!

これでhttps:[サーバーアドレス]で接続しますと!

f:id:DreamerDream:20190220121647p:plain


 

 

えええ???

 

調べて見たらわかりましたよ。

Djangoで開発したWebアプリケーションをLet's EncryptでSSL対応する(Apache編) | Gyobot Blog

 

SSLで公開するバーチャルホストの設定ファイル(上の.well-knownディレクトリの公開設定をしたconfファイル)にSSLEngineを追加する必用があったようです。

ex.</etc/httpd/conf.modules.d/extra/vhost-draemon.conf>

まずはポートを80からSSLポートの443に変更して

<VirtualHost *:443>

以下を追加

SSLEngine On
SSLCertificateFile /etc/letsencrypt/live/[サーバーアドレス]/cert.pem
SSLCertificateKeyFile /etc/letsencrypt/live/[サーバーアドレス]/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/[サーバーアドレス]/chain.pem

以上!

https://〜〜〜にアクセスすると〜、ちゃんと通りました!

 

 

じゃあ今までのhttpアクセスで動いていたサイトは紹介サイトなどのリンクがあれば全部書き直さないといけないのか?というとhttps接続先へ「リダイレクト」という方法で転送することが出来ます。

方法はHTMLファイルに書いてクライアントから飛ばす方法とApacheで設定してサーバーで飛ばす方法がありますが、サーバーからのほうが確実。(クライアントに委ねる場合はブラウザの環境次第では飛ばないことがあるらしい。)

ですので、

Apacheに同じアドレスで80ポートで接続できるhttp接続してきた時用のconfファイルを作って(静的ページでOK)

RedirectMatch 301 .* https://[サーバーアドレス]/

と追記するだけ。これでこれまでのhttpのトップページに接続してきたクライアントをhttpsに回せます。

 

 

今度は更新処理

SSL証明書の有効は90日なので更新手続が必用になります。

更新は期限切れの30日前から出来るようで、一度証明書を取得した段階で

sudo certbot renew

をすると

No renewals were attempted.

と怒られます。

 

ですのでの「ドライラン」を実行して、実際には更新せず、更新できるか?を確認しましょう。 

sudo certbot renew --dry-run

すると

All renewal attempts failed. The following certs could not be renewed:

 

というエラーが吐かれました。これはいけない!

更新時用には80番ポートへアクセスするようなのでディレクト

</var/www/django/.well-known>

が見れるように忘れずApacheからconfの設定しておく必用があるようです。

 

更新時には作成時と同じ80ポートへアクセスしてくるので、conf設定で

Alias /.well-known /var/www/django/.well-known
<Directory /var/www/django/.well-known>
Require all granted
</Directory>

RedirectMatch 301 .* https://[サーバーアドレス]/

と、リダイレクト設定よりも先に.well-knownへのアクセスを許可するように設定します。

これでドライランを実行して

Congratulations, all renewals succeeded. The following certs have been renewed:

と表示されれば成功です!

 

 

と、・・・・ここまでしてから気が付いた

DjangoアプリをSSLに設定する場合、

同じアドレスでhttpを使う予定が特に無いなら、最初からCertbotがhttpアクセスしてきた時に.well_knownパスを読み出すためだけの静的なページをApacheで1つ設置するだけでいいってことだったんだ!

なーんだ。こんなに苦労しなくてよかったんだ。。orz

 

 

さて、気を取り直して自動更新の設定をします。

定期的な動作といえばcronですね。

sudo crontab -e

としたいところだけど

コチラのブログの警告のように

crontab -e は「絶対に」使ってはいけない - ろば電子が詰まつてゐる

間違ってeの隣のrで

crontab -r

としてしまうと、何の警告も成しにcronが消えるという取り返しのつかない事になるらしい。いろいろな予防策があるけど、

sudo crontab -l > crontab.back

としてバックアップを取っておくと良い。

これでホームディレクトリにバックアップが保存される。

リストアするには

sudo crontab crontab.back

これだけで安全対策になる。

このクセをつけておくと最悪の場合でも一度でもバックアップ取った状態にまではリストアできる。

cronには先のブログほぼそののまま、 

#certbot Let's encrypt renew
00 01 * * 2 root /usr/bin/certbot renew --post-hook "service httpd restart"

 

現在、certbot-auto ではなく、certbotになっていることに注意。

ひとまずこれで記述して期限切れの水曜日1時に更新できているか?確認してみることにする。

 

<追記>

上のように設定してたけど、もともとroot権限でcronする設定なので、

00 01 * * 3 /usr/bin/certbot renew --post-hook "service httpd restart" 2> /var/log/lets_encrypt_error.log &

として、追加で動作確認のためエラーログを残すようにした。

期限内に更新しようとしたら、ログに

Cert not yet due for renewal

と出ていたので一応正常にcron動作はしている様子。

 

 

なぜ1週間ごと?

certbotはあまり頻繁に更新しちゃダメだから期間を開けたいんだけど、1ヶ月ごとだと、もしサーバーの再起動とかでタイミングが悪いと更新できない期間があるかもしれないからとりあえず1週間ごとが妥当かな?それと更新時にネットワークエラーで更新できてなければ何度かチャンスがあるし他のエラーでもある程度対処出来るだろうし、という考えです。

 

今回は以上です。

 

 <追記>実際の更新時にエラーメールが来ました。

dreamerdream.hateblo.jp

 

 

<追記>SSLサイトにしたら必ず安全!

ということもありません。セキュリティーレベルのチェックをしてより安全な設定を施しましょう↓

dreamerdream.hateblo.jp

 

kampa.me