apacheからnginxへの移行時の証明書周りの設定

歴史的な理由もあり学科のweb周りはapacheが元気に稼働しているケースが多い。

とはいえもうapacheの面倒を見るよりはnginxに移行したほうが何かと便利そうなので、現在apacheからnginxへの乗り換えを仕込んでいる。

先週の台風の際に学科システムへのダメージを減らすために、学科システムを一時的に停止させ、クラウドにおいてあるwebの予備サーバーを使うことになった。その時にタイミングを合わせて試しにnginxでwebを構築しようとしたところ、ちょっとSSL周りで詰まったのでそのメモ。

apache側では次のように証明書を指定している。

SSLCertificateChainFileはいわゆる中間証明書。

SSLCertificateFile /etc/pki/tls/private/ie.u-ryukyu.ac.jp/ie.u-ryukyu.ac.jp.cer
SSLCertificateKeyFile /etc/pki/tls/private/ie.u-ryukyu.ac.jp/ie.u-ryukyu.ac.jp.key
SSLCertificateChainFile /etc/pki/tls/private/ie.u-ryukyu.ac.jp/nii-odca3sha2ct.cer

ググったところ SSLCertificateChainFileに対応するnginxの設定項目は無いらしい。

どうもnginxの場合はSSL証明書と中間証明書を連結させたファイルを作る必要がある模様。

blog.steven266.de

ということでこんな感じのコマンドを実行して連結させたpemファイルを生成する。

$cat ie.u-ryukyu.ac.jp.cer nii-odca3sha2ct.cer > ssl.pem

生成したpemファイルをnginxのserverコンテキスト内に指定する。

    ssl_certificate /etc/pki/tls/private/ie.u-ryukyu.ac.jp/ssl.pem;
    ssl_certificate_key /etc/pki/tls/private/ie.u-ryukyu.ac.jp/ie.u-ryukyu.ac.jp.key;

こうするといい感じに動く。証明書周り難しいね...。

ansibleでpackageのinstall時にloopは使わないほうが良い

教えてもらったので。

ansibleでyum,dnf, apt を使っていくつかのpackageをまとめてインストールする際に、癖でloop(with_items)を使っていたのだけど、これは非推奨らしい。

というのも

 - name: install dnf packages
   become: yes
   dnf:
    name:
      - "{{ item }}"

     state: latest
  loop:
    - mariadb
    - mariadb-server
    - MySQL-python3
    - python3-libselinux

みたいに書いてしまうと、これはこのコマンドを実行していることと等価になる。

sudo dnf install mariadb
sudo dnf install mariadb-server
sudo dnf install MySQL-python3
sudo dnf install python3-libselinux

そんな事しなくても sudo dnf install mariadb mariadb-server MySQL-python3 python3-libselinuxってまとめられるよね。こっちのほうが効率いいよねということで、loopを使うやり方は非推奨とのこと。

じゃあ実際にはどう書けばいいのかというと、usageを見たところ次のような世界観らしい。

- name: ensure a list of packages installed
  yum:
    name: "{{ packages }}"
  vars:
    packages:
    - httpd
    - httpd-tools

- name: Install a list of packages
  yum:
    name:
      - nginx
      - postgresql
      - postgresql-server
    state: present

- name: install the latest version of Apache and MariaDB
  dnf:
    name:
      - httpd
      - mariadb-server
    state: latest

- name: Install a list of packages
  apt:
    pkg:
    - foo
    - foo-tools

ということでnamesの後ろに複数リストで与えるか、 {{ packages }}を使えとのこと。

debug情報を見たところnamesで複数指定する方法とpackagesを使うやり方は内部的には同じみたいですね。-vvvで見たところ両方次の様なjsonが表示されていました。好きな方を選べという感じっぽい。

ok: [test-fedora] => {
    "changed": false,
    "invocation": {
        "module_args": {
            "allow_downgrade": false,
            "autoremove": false,
            "bugfix": false,
            "conf_file": null,
            "disable_excludes": null,
            "disable_gpg_check": false,
            "disable_plugin": [],
            "disablerepo": [],
            "download_dir": null,
            "download_only": false,
            "enable_plugin": [],
            "enablerepo": [],
            "exclude": [],
            "install_repoquery": true,
            "install_weak_deps": true,
            "installroot": "/",
            "list": null,
            "lock_timeout": 30,
            "name": [
                "mariadb",
                "mariadb-server",
                "MySQL-python3",
                "python3-libselinux"
            ],
            "releasever": null,
            "security": false,
            "skip_broken": false,
            "state": "latest",
            "update_cache": false,
            "update_only": false,
            "validate_certs": true
        }
    },
    "msg": "Nothing to do",
    "rc": 0,
    "results": []
}

macのopenコマンドでURLっぽいファイルを指定したときの挙動

並行で学科のWordPressからコンテンツをダウンロードするくんを書いていた所、ミスでカレントディレクトリにhttp:/みたいなディレクトリを生成してしまった。

$ ls
http:/

$ tree
.
└── http:
    └── anatofuz.net
        └── test.html

2 directories, 1 file

こんな感じのディレクトリ構成になっている。

ここでtest.htmlを開いてみようとmacのopenコマンドを利用した所おもしろい挙動になった。

$ open http://anatofuz.net/test.html
http://anatofuz.net/test.html?
[0]     cancel
[1]     Open the file http://anatofuz.net/test.html
[2]     Open the URL  http://anatofuz.net/test.html

Which did you mean?

Which did you mean?が表示されたあとは入力の待機状態になっていて、1と押すとファイルが、2と押すとwebのURLが開かれるようになっていた。

普段ならまず出てこないケースなので結構面白かったですね。ちなみにvimだと何も聞かれずにファイルを開きに行ってました。

大乱闘障害復旧バトル2020/08/01-2020/08/02

ということで学科システムに障害が発生していました。具体的に言うと学科内から外に行けない、一部VMに学内からでもアクセスできないなどでした。

この障害は突然発生したものではなく琉大工学部で定期的に発生する計画停電によるものです。 原因としては3つあるL3スイッチのうち1つが故障。その結果としてネットワークが死んだという感じでした。 今回の計画停電はエレベーターも破壊したのでなにか良からぬ事をした可能性があります。

これはそのときの解決のログです(記憶力が無いので間違ってるかも...)

2020/08/02

8:50

停電発生

id:anatofuzが学科サイトを静的にダウンロードするツールをテストしていたところ、突如ホストの応答が無くなる。 mattermostに「停電したわw」的な内容を書き込む。

だいたい同時刻

id:unimarimoが悪い予感がしたらしく、停電対応でサーバー室に入る。 時既に遅しで既にサーバーは死んでいたらしい。この時に新規システムにつないでたケーブルを抜くのを忘れる。

9:40

mattermostにフラグを貼る

f:id:anatofuz:20200803205109j:plain

16:00

id:anatofuzが17時に復電しそうだなと思いサーバー室に到着。時既に遅しで既に復電しており、エアコンが付いていない30度の部屋の中サーバーが元気に稼働している。

基幹システムはa,b,c,dの4台あるが、bとcだけなぜか立ち上がっていた。新規システムはつないでいた2台が起動していた。

とりあえず新規システムを落としつつエアコンをいれる。このときサーバー室から変な音がする。

予備のエアコンも稼働して冷えたのでaとd起動する。起動した所livbirtdが立ち上がってないので一旦全員落とす。その後相変わらずlivbirtdが立ち上がってないのでsystemctlで立ち上げる。

16:25

kvmが立ち上がったのでVMを立ち上げていく。

立ち上げたが踏み台サーバーにssh出来ないなどの報告が出る。よくよく見ると学科のwebサイトも見れないわwifiが外に行けないわの症状が出ている。

8.8.8.8にpingを飛ばした所外に行けてなさそう。

17:00

id:unimarimoと合流。仲良くサーバーの調子を見る。 学内ネットからだと学科サイトが見れそう。内向きDNSは元気そう。

systemctlでstatusを確認していたらnetwrokが一部立ち上がってない。startしても変化が見られない。

ぐむ〜と言いながら色々試したがダメそう。エレベーターが動いてなかったので「電気が完全に復旧してないのでは?」みたいな話になる。工学部周辺が停電という話だったので総情(琉大のネットワークの大本を管理している場所)が停電していたら外にいけないよね〜と話し、一旦帰宅することに。

階段を降り1Fにつくとエレベーターに「故障しました」のガムテープが。ひょっとしたら電気は完全に復旧している?などと思いながらおなか空いたので一旦家帰ってご飯。

18:00

ご飯食べながら色々考えていたが、そもそも総情のオンプレで配信している総情のページは見えているのだからやはり電気は復旧している。電気がだめじゃなくて我々が悪そう.......。

外に行けないサーバーのことを「内向的な性格」などと呼びつつ大学に戻る。

18:50

内向きが解決できるかと思っていたがなんかアクセスできなくなっている。 大学戻った所4Fのwifiが死んでいた。

先日UTMのアップグレードをしたこともありUTMを疑うが、UTMは見る限り元気に稼働している。 VMの状態を確認するなら有線持ってくるか〜と言いながらUTMの下に目を向けると、L3スイッチ3つのうちの1つが息をしていないことが発覚。 f:id:anatofuz:20200803204750j:plain

ラックの後ろに回ると動いていないL3スイッチのランプが赤色に光っている… しかも焦げたような匂いが漂っている。

f:id:anatofuz:20200803204814j:plain

型番からググってマニュアルを見ると、PS OKが赤色の場合は出力が停止しましたとのことらしい。障害じゃん…。

念のために電源を抜き差ししても変わらず。こいつが原因そうだったので電源を抜いて業者さんとの連絡用のMLに投稿。翌日の9時から作業をすることに。

f:id:anatofuz:20200803205243j:plain

22:40

業者さんから「火災防止でスイッチのケーブル抜いといて」と言われる。抜いたか覚えてなかったのでid:unimarimoに頼んで抜いてもらう。

実は既に抜いていたのでただサーバー室まで歩いて行かせたムーブをした。

2020/08/03

9:10

業者さんとこんにちは。 コンソールケーブルは持ってきていただいた…。申し訳ない。

9:30~10:00

スイッチの電源を交換したりしたところ、相変わらず赤ランプだったので物理障害が確定。代替機の手配をしてもらいつつ今後の方針を決める。

L3スイッチ3つのうちに生き残った2つのスイッチに空いているポートがあったので、configを空いているポートに移す作業をすることになる。

その前に他のネットワーク機器が大丈夫かを調べた所、故障したスイッチに接続していた各研究室のフロアスイッチ及びEPS室に置いてあるスイッチがpingが通らないことが発覚。これらも治す必要がある。

またwifiアクセスポイントも赤色点灯しておりエラーが発生している。どうもwifiが全体的に調子が悪いらしい。

10:30?~

L3スイッチに接続していたフロアスイッチやルームスイッチの様子を見に行く。どうも自己防衛かなんかで落ちていたらしい。エレベーターやL3スイッチ、フロアスイッチまで障害が発生しているので計画停電で電気業者がなにかやらかした説が出てきた。一部スイッチの調子を見に行くためにid:unimarimoが脚立を借りに行く。ショムニっぽい。

各スイッチにはコンソール接続してconfigure terminalしたあとにshutdownno shutdownをl3に接属していたport-channleで実行して解決して回る。

研究室はこのコロナ騒ぎで人がいないのでマスターキーを借りる。しばらくしたらエレベーターの修理で使うらしいので別のマスターキーを借りに行くなどをした。

wifiアクセスポイントは再起動時はよくなったがしばらくするとエラーがでる。つらい

11:30~

スイッチの設定等は業者の方に頼みつつ、基幹システムの状況をリカバリしにいく。 radiusとかが立ち上がってないのを立ち上げていく。一部VMが立ちあがってもvirsh consoleでいけないので泣きながらshutdown。shutdownしても落ちないのでdestroy.........

wifiコンパネにアクセスしようとしたが相変わらずwifiが死んでいる。つらみ。

12:00~

疲れたのでお昼休憩。家帰ってご飯を食べる。

鮭を3切れ焼いた。

13::00~

頑張ってwifiアクセスポイントのwebコンパネにアクセスしようとする。 L3スイッチからVLANを切って有線でアクセスしようとしたが失敗する。

研究室のサーバーからアクセスしようとしたがこれも失敗。むむむ.....とか言ってたら突然wifiでアクセスできるようになった。webコンパネが表示しているスイッチなどの情報が少なすぎてウケるという感じ。まだどっかで障害が起きてそう。

基幹サーバー及びシステムともに、突然外部にいけるようになったりいけなくなったりする。 systemctlやnmcliなどを使っても原因が断定出来ない。基幹サーバーにtracerouteが入って無くて辛い気持ちになる

13:20~

業者さんが原因を特定。生き残ったL3スイッチ2台がともにActiveになっていたらしい。 いわゆるダブルアクト状態というもの。

L3スイッチは冗長化としてActive/Standbyの状態を持つ手法があるらしいが、両方Activeだとゲートウェイが2つ存在することになりコンフリクトするらしい。 この設定を正常にした所、ネットワークが改善され外に行けるようになった。

業者さんがスイッチの設定を書き換え、及び取り外しを行っている間に、相変わらずダメそうな各フロア/ルームスイッチを再起動して回る。

14:30

すべてのスイッチの設定を生存していたスイッチに移しスイッチ側の復旧は完了。

僕とid:unimarimoも基幹システムのmountやデーモンの障害を一通り解決し終了。 ネットワークでトラブっていたためか微妙にNFSが調子悪そう。まぁ治るか....。 代替機がきたタイミングで交換作業をしましょうという話になり今日は終了。

感想

ちょうど来月システム更新なんですが、システム更新前に障害が発生してウケるwという感じでした。 (今回代替機のL3スイッチは30日後に引き取られるスイッチになってしまった)

業者の方の対応力がすごくて、一緒にフロアスイッチを床から取り出す作業や、原因の特定、設定の書き換えまでしていただきました。ありがとうございます。

あとは一家に一台コンソールケーブルはあったほうがいいですね。シリアルケーブルも...。

あわせてよみたい

seeker-s-eye.blogspot.com

ie.u-ryukyu.ac.jp

流れに乗って @xtetsuji さんからの出題をやってみる

ということで流れに乗ってみました。

アルゴリズムそんな自信ないのでどうなんだろう.....。競プロerからなんか突っ込まれそう

gist.github.com

シグナルでプロセスが終了されるとぴえんするコマンド作った

最近YouTubeでPIENのプレイ動画をよく見るので作りました

pienの後に渡したプログラムがシグナルで死んだ場合はぴえんの歌が再生されます github.com

なんとなくRustの勉強も兼ねてRustで書いてます。

地味にbrewでもダウンロードできるようになってます。

$ brew tap anatofuz/pien
$ brew install anatofuz/pien

別途音源が必要なので音源配布サイトからダウンロードして、ホームディレクトリに.pien.mp3で置いておくと再生されます。

soundevotee.net

実装方法

音声を流すのはafplayを雑に叩くみたいな世界観です。そぼく.....

RustでのCLIの作り方は全然解ってなかったので、いいかんじのチュートリアルを読みながら勉強しました。

rust-cli.github.io

肝なのは外部コマンドの終了コードの取得ですね。 Rustの場合は外部コマンドの終了コードはいい感じにパターンマッチで取得できます。

doc.rust-lang.org

use std::process::Command;

let status = Command::new("mkdir")
                     .arg("projects")
                     .status()
                     .expect("failed to execute mkdir");

match status.code() {
    Some(code) => println!("Exited with status code: {}", code),
    None       => println!("Process terminated by signal")
}

この例だとNoneに分類されるやつらが全員シグナルで死んでるので、Noneの場合は音楽を再生すれば良いということになります。その為今回は入力で受け取ったコマンドの結果をパターンマッチし、シグナルで死んでたらafplayを実行するみたいな極めて単純な実装になってます。

こういう一発ネタみたいなコマンド作るのやはり楽しいですね。

apache -> nginx -> gitlab-ceのプロキシを止める

追記 2020/07/15

8080ポートはgitlabのrailsアプリケーションのポートなのだけど、ここに直接プロキシするのではなく、gitlabのHTTPのルーティングを行っているgitlab-workhorseの8181ポートに繋げないと駄目だった


自分が在籍している学科ではオンプレ上にgitlabを構築して運用している。

gitlab.ie.u-ryukyu.ac.jp

gitlabが動いているサーバーはオンプレ上に構築したKVMで動かしているVMなのだけど、歴史的理由でこのサーバーでは他にjenkinsやredmineなども動作している。

もともとredmine-> jenkins -> gitlabの順でサーバー上に構築されたこともあり、サーバー全体のwebサーバーはapacheで動いている。

問題がgitlabのinstallでgitlab-ceを使っていたのだけれど、gitlab-ceはデフォルトでgitlabにまつわるすべてのミドルウェアなどもインストールして構築してくれる。 具体的に言うとデータベースとしてPostgreSQL、webサーバーとしてnginxを同梱している。

現状の学科のgitlabは、このgitlab-ceに同梱されているnginxをポート8000番で起動させ、apacheからそのnginxにプロキシをしていた。つまりapache -> nginx -> gitlabの順でHTTPリクエストが伝搬 される。

具体的に見るとこういう感じだった。大変ですね。

$ lsof -i :8000
COMMAND   PID       USER   FD   TYPE    DEVICE SIZE/OFF NODE NAME
nginx    5769       root    9u  IPv4 131461357      0t0  TCP *:irdmi (LISTEN)
nginx    5770 gitlab-www    9u  IPv4 131461357      0t0  TCP *:irdmi (LISTEN)
nginx    5770 gitlab-www   19u  IPv4 131528575      0t0  TCP localhost:irdmi->localhost:52234 (ESTABLISHED)
nginx    5770 gitlab-www   20u  IPv4 131524330      0t0  TCP localhost:irdmi->localhost:52192 (ESTABLISHED)
nginx    5771 gitlab-www    9u  IPv4 131461357      0t0  TCP *:irdmi (LISTEN)
nginx    5771 gitlab-www   18u  IPv4 131526937      0t0  TCP localhost:irdmi->localhost:52196 (ESTABLISHED)
nginx    5771 gitlab-www   19u  IPv4 131530893      0t0  TCP localhost:irdmi->localhost:52244 (ESTABLISHED)
nginx    5772 gitlab-www    9u  IPv4 131461357      0t0  TCP *:irdmi (LISTEN)
nginx    5772 gitlab-www   15u  IPv4 131530209      0t0  TCP localhost:irdmi->localhost:52230 (ESTABLISHED)
nginx    5773 gitlab-www    9u  IPv4 131461357      0t0  TCP *:irdmi (LISTEN)
httpd    7873     apache   16u  IPv4 131524319      0t0  TCP localhost:52186->localhost:irdmi (CLOSE_WAIT)
httpd    7874     apache   16u  IPv4 131527717      0t0  TCP localhost:52192->localhost:irdmi (ESTABLISHED)
httpd    8583     apache   16u  IPv4 131528766      0t0  TCP localhost:52198->localhost:irdmi (CLOSE_WAIT)
httpd    8831     apache   16u  IPv4 131525112      0t0  TCP localhost:52164->localhost:irdmi (CLOSE_WAIT)
httpd    9433     apache   16u  IPv4 131524337      0t0  TCP localhost:52196->localhost:irdmi (ESTABLISHED)
httpd   10272     apache   16u  IPv4 131524317      0t0  TCP localhost:52184->localhost:irdmi (CLOSE_WAIT)
httpd   11736     apache   16u  IPv4 131529561      0t0  TCP localhost:52244->localhost:irdmi (ESTABLISHED)
httpd   24102     apache   16u  IPv4 131528566      0t0  TCP localhost:52230->localhost:irdmi (ESTABLISHED)
httpd   24103     apache   16u  IPv4 131525592      0t0  TCP localhost:52188->localhost:irdmi (CLOSE_WAIT)
httpd   24111     apache   16u  IPv4 131530228      0t0  TCP localhost:52234->localhost:irdmi (ESTABLISHED)
httpd   24166     apache   16u  IPv4 131526971      0t0  TCP localhost:52200->localhost:irdmi (CLOSE_WAIT)

この構成であまり問題は無かったのだけれど、諸事情でgitlabのHTTPヘッダにAccess-Control-Allow-Originの設定をする必要が出た。現状の構成だとApache側でAccess-Control-Allow-Originを付与しておけば良いと思ったのだけれど、実際はapache -> nginxに伝搬される際に、nginx側でヘッダ情報が落とされることが判明した。さらにgitlab-ceが生成するnginxの設定ファイルでAccess-Control-Allow-Originを付与するのが結構めんどくさそうだった。

というわけでapache -> nginx -> gitlab-ceのプロキシを止めて、apache -> gitlab-ceに一本化したい。本来ならapacheやめてnginxにしたいのだけれど、redmineやjenkinsのプロキシ設定も変更する必要があり、やや工数的にめんどいので段階的にやることにした。

gitlab側の設定

まずはnginxを止めなければならない。

これらはgitlab側の設定ファイルgitlab.rbを編集することで停止できる。

若干違うけれど、gitlab公式の同梱されてないnginxを使う方法が参考になった。 docs.gitlab.com

とりあえずnginxの使用を止める

nginx['enable'] = false

この後に続く nginx['hoge']的な記述はすべてコメントアウトしておく。

末尾にこの2行を追加する(これは効果があるかどうかは不明)

gitlab_workhorse['listen_network'] = "tcp" 
gitlab_workhorse['listen_addr'] = "127.0.0.1:8181" 

apache

今まではこんな感じの設定だった

<VirtualHost *:443>
        ServerName gitlab.ie.u-ryukyu.ac.jp
        ProxyRequests off
        ProxyPass / https://127.0.0.1:8000/
        ProxyPassReverse / https://127.0.0.1:8000/
        SSLEngine on
        SSLProxyEngine on
        SSLProxyCheckPeerCN off
        SSLProxyCheckPeerName off
        SSLCertificateFile hoge.cer
        SSLCertificateKeyFile hoge.key
        SSLCertificateChainFile hoge.cer
</VirtualHost>

何も考えずにnginxにプロキシしていたが、apacheからgitlabに直接プロキシする場合はある程度設定を書き加える必要がある。

様々な人がブログエントリで設定例を書いていたが、最終的にはgitlab公式が出しているapacheの設定例が一番正しかった。

gitlab.com

httpsでやってることもあるので、この例の通りにやらないとリポジトリの中身が表示されないとか、LDAPログインした後にコールバックされないなどの悲しさが存在する。

ここまで設定したらgitrlabとapacheを再起動すればよい

gitlab-ctl reconfigure
gitlab-ctl restart
gitlab-ctl reconfigure
systemctl restart httpd