SSHトンネリングに関する備忘録

やりたかったこと

実験サーバ上で動作するQEMU仮想マシンの画面を、作業用PCのVNC Viewerで表示する。

作業環境

実験サーバ
作業PC

試してみたこと

  • QEMU起動時のオプションに-vnc 192.168.1.20:0を指定してみた
    仮想マシンは起動せず。そもそもVNCサーバのIPアドレスは指定できないっぽい?(要調査)

  • 実験サーバにVNCで接続し、そこでVNCクライアントを起動して127.0.0.1へ接続してみた
    → 一応これでも接続は可能だが、二重に接続する手間が煩雑。

解決策

SSHを利用し、作業用PCのループバックデバイス上のポートから実験サーバのVNCポートに向けてトンネリングを行った。
以下のように-L オプションを指定することでポートフォワーディングを行うことが可能。

$ ssh -L 5901:127.0.0.1:5901 192.168.1.20


このように、PC側のループバックデバイスの5901ポートを叩くことで、サーバを経由して(サーバ側の)127.0.0.1:5901へ接続することができるようになる。

…のだが、ここを理解するのに時間がかかった。
ssh -L (ローカル側ポート):(宛先アドレス):(宛先ポート) (SSH接続先のアドレス)と指定することで、「SSH接続先のサーバを経由して」「-Lオプションで指定した宛先へ」接続することが可能。
そのため、宛先アドレスを127.0.0.1とすることで「サーバを経由して127.0.0.1へ接続する」すなわち、サーバのループバックデバイスへ転送することが可能となる。

ともあれ、以上のようにトンネリングを行ったあとでPC上のVNC Viewerから127.0.0.1:5901へ接続することで、本来PC側からは見えないはずである実験サーバ上のVNCサーバに接続することができるようになる。

Flaskで簡単なWebアプリを作成して公開した話

お久しぶりです。人生最後の夏休みは北海道に旅行に行ったり部屋の模様替えをしたりでいろいろ満喫してました。
そんな中で、ヲタ芸やってる身内から「イベントの時の得点集計が面倒でたまらないのでどうにかできない?」との連絡をいただいて、"夏休みの宿題"感覚でWebアプリを個人開発したのでその記録をここに残しておきます。

何作ったの

https://scorer.rewind-memory.com/ ←これ
具体的なユースケースなどはトップページにあるので気になった人はぜひ見てみてください。

インフラまわり

今回の開発はかなり予算が限られている(ソフトウェア導入の実験的な要素の方が大きい)ので、何よりも安価になるようなサーバ構成にしました。
そこそこ使い慣れているAWSのEC2は無料枠を使い潰してしまったため、技術的な検証の意味も含めてGCPピンチケサーバCompute Engineを採用し、極力無料枠に収まるようにしています。 RDSインスタンスを停止し忘れて5000円の請求が飛んできそうになった話はまたいつか。

さらに、CircleCIを用いたCI/CDにも対応しているため、gitへのコードプッシュ後迅速にデプロイを可能としました。これで開発がめちゃくちゃ楽になった。

今後利用者数が増加して処理能力が不足した場合はサーバの増強やロードバランサの設置などを行っていきたいと考えています。

ミドルウェア・バックエンド

タイトルにもある通り、フレームワークにはFlaskを採用しました。というのも、LaravelやDjangoなどは機能が充実している分メモリ使用量が大きくなるかも?と考えたためです(個人的にPythonでのアプリ開発をしてみたかったという理由もそこそこある)。
同様の理由で、データベースもMySQLPostgreSQLなどではなくSQLiteを採用しています。今後利用者が増えてきたりした場合は移行してもいいかなという考えです。

さて、機能のコアとなるスコアデータの管理ですが、DB内にJSON形式の文字列で格納する方法を取ることによって複数の要素を同一のテーブルにて管理することを可能にしました。(RDBの設計的にはあまりよくないのかも?)
その他のDB設計に関しては、スコアブック内に複数のスコアが存在するといった単純な一対多の設計になっているので詳細は割愛します。

フロントエンド

PCとスマホの両方から利用される可能性を想定して、Bootstrapのグリッドシステムを利用したレスポンシブ設計を行っています。
Vueあたりを利用したSPAにしても良かったんですが、Flaskのテンプレートエンジン(Jinja)を使って値を直接埋め込みたかったため採用は見送りました(LaravelだとVueがデフォルトのUIとして選択できるのでこういうときに便利だったりする)。

サイトのデザインに関してはド素人でセンスもないので大目にみてほしい。それでもある程度まとまった見た目になるのはさすがBootstrap、って感じです。
あとはスコアブック共有画面で利用したQRCode.jsのライブラリ、めちゃくちゃ便利ですね。フロントエンドでcanvas要素にサクッとQRコードを生成できるのでオススメです。

まとめ

2週間弱と短めの開発期間で作成したため不具合がまだまだ多く残ってるかと思います。もしバグや不具合を発見した場合は不具合報告フォームから報告していただけると対応できるのでありがたいです。
ヲタ芸の採点以外にも色々な箇所で使えると思いますので、皆さんも是非一度利用してみてください。僕が泣いて喜びます。

OSPFによる動的ルーティング環境を構築してみた

最近、ルーティング周りの理解が浅く感じたためネットワークの勉強をしています。せっかくイイ感じのルータとL3SWを持っているので、そこそこ理論を頭に入れたうえで実践してみました。

主な使用機材

  • ルータ
    • NEC Univerge IX 2015
    • NEC Univerge IX 2105
  • L3スイッチ

実験環境NW構成

概ね以下の図の通り。
f:id:KLag:20220314235626p:plain
今回の目的は、各L3機器にスタティックルートを設定せずにPC1 <-> PC2間で通信を行うことです。OSPFによる動的ルーティングを利用します。

下準備

まず、3560X <-> IX2015間の通信を行うため、LANケーブルを接続したポート(Gi0/5)をVLAN 2のアクセスポートとして指定します。

---3560X---
Switch(config)# interface GigabitEthernet0/5
Switch(config-if)# switchport access vlan 2

コンフィグの流し込み

各機器に対して、自分が直接つながっているネットワークのアドレスと、それに紐づけるOSPFエリアの値を流し込んでいきます。

---IX2105---
Router(config)# ip router ospf 1
Router(config)# network 192.168.11.0/24 area 0

---IX2015---
Router(config)# ip router ospf 1
Router(config)# network 192.168.30.0/24 area 0
Router(config)# network 192.168.22.0/24 area 0

---3560X---
Switch(config)# router ospf 1
Switch(config)# network 192.168.11.0 0.0.0.255 area 0
Switch(config)# network 192.168.30.0 0.0.0.255 area 0

設定自体はこれでおしまいです。各ルータでshow ip routeを叩いてルートが反映されているかを確認してみます("O"で示される行のエントリがOSPFによって生成された経路情報となります)。

IX2105

f:id:KLag:20220315003648p:plain

IX2015

f:id:KLag:20220315003741p:plain

3560X

f:id:KLag:20220315003614p:plain

さらに、これらの設定を行っている間にWiresharkを用いてパケットキャプチャした結果、次のようにOSPFのリンク状態更新(LS Update)パケットや、リンク状態確認応答(LS Acknowledge)パケットを捕まえることができました。
あわせて、10秒間隔で各ルータからOSPFルータを探索するためのHelloパケットが流されているのもわかります。なお、これらのパケットの宛先224.0.0.5はOSPFルータを指すIPv4マルチキャストアドレスです。
f:id:KLag:20220315004445p:plain

疎通確認

Pingで確認。

192.168.11.0/24 <=> 192.168.30.0/24

f:id:KLag:20220315005428p:plain

192.168.11.0/24 <=> 192.168.22.0/24

f:id:KLag:20220315005810p:plain

traceroute PC2 => PC1

f:id:KLag:20220315010030p:plain このとおり、各L3機器で正しくルーティングされていることがわかります。

おわりに

今後は、各経路への重み付けをしてみたり、RIPでの動的ルーティングも実験してみようかなとも考えています。
また、OSPFパケットに関してもまだ確認できていないもの(リンク状態更新要求など)の確認や、既存のOSPFネットワークに新たなルータを導入した際の挙動確認なども行っていきたいです。

暗黙的なリターンに関する雑記

Rustの勉強中に「暗黙的なリターン」に関して気になったので、複数の言語で同様の実装をした場合にどのような動作をするのか実験してみた。

実験環境

OS : Windows 10
RAM: 32GB
処理系は各章ごとに記述する。

Rust

Rustでは関数内に明示的なreturn文が存在しない場合、「その関数内の最後の式」を暗黙的な返却値とする。例えば、以下のようなコードをコンパイル・実行すると"5"がターミナルに出力される。

fn main(){
    let i =  return5();
    println!("{}", i);  // 5
}

fn return5()->i32{
    5
}

Java

以下のようなTest.javaを作成し、javac 11.0.8でコンパイルした。

public class Test{
    public static void main(String[] args){
        int i = return5();
        System.out.println(i);
    }
    public static int return5(){
        5
    }
}

結果、「セミコロンがない」というコンパイルエラーで弾かれた。
また、5;というようにセミコロンをつけた場合でも「文ではない」というコンパイルエラーが発生した。

Python

次のようなコードを実行した。Python 3.8.5を利用。

def return5():
    5

i = return5()
print(i)    # None

シンタックスエラーとはならず、iにはNoneが格納された。

PHP

php 8.0.0にて、以下のコードを実行した。

<? php
    function return5(){
        5
    }
    $i = return5();
    echo  "i = ".$i;
?>

こちらもシンタックスエラーとなった。なお、セミコロンを付与した場合、ターミナルには何も出力されなかった。

Go

Go 1.17.1にて実行。

package main

import "fmt"

func main(){
    i := return5()
    fmt.Println(i)
}

func return5() int{
    5
}

結果、「5が評価されたのに未使用である」「関数内にreturn文がない」という2つのエラーが発生。

C

ソースコードは以下の通り。GCC 8.3.0でコンパイルした。

#include <stdio.h>

int return5(){ 5; }

int main()
{
    int i = return5();
    printf("%d\n", i); // 1
}

どこにも定義されていない1が出力された。
この現象に関する詳細な解説は以下の記事を参照のこと。

qiita.com

このように非voidで返り値を持たない関数の動作はC言語では定義されておらず、出力されている 1 はアキュムレータレジスタの値であり、これは環境によって異なるとのこと。
試しにMacのclang12.0.0でコンパイルしてみたところ、出力結果は0となった。

追加実験

アキュムレータレジスタの値が返却されているかどうかを確認するためgcc -g3 test.c -o test.exeデバッグ用の実行ファイルを作成し、gdb test.exeでデバッガにかけてみた。
レジスタの値はprintf()関数が実行される行で確認した。

(gdb) info register
rax            0x1                 1
rbx            0x8                 8
rcx            0x1                 1
rdx            0x1c48f0            1853680
(以下略)

また、アキュムレータレジスタで返却値のやり取りが行われているかどうかを確認す流ために、return5()関数の内容をreturn 5;に変更した上で同様にデバッガを走らせると、次のような結果を得た。

(gdb) info register
rax            0x5                 5
rbx            0x8                 8
rcx            0x1                 1
rdx            0x1c48f0            1853680
(以下略)

この結果から、返却値のやり取りがアキュムレータレジスタで行われていることが確認できた。
なお、デバッガ出力のRAXがアキュムレータレジスタに相当する。

おまけ・レジスタの名前に関する話

x86_64アーキテクチャの汎用レジスタはデータのサイズによって以下のように名前が変わる。(ただし、レジスタ自体は1つ。)
以下はアキュムレータ(Accumulator)レジスタの例。

  • AH, AL(A High, A Lowの略?): それぞれAXの上位8bitと下位8bitの領域
  • AX (A eXtended): AHとALを含む16bitの領域
  • EAX (Extended AX): AXを下位16bitとする32bitの領域
  • RAX (Register AXの略?): EAXを下位32bitとする64bitの領域

さいごに

Google先生に聞いたところ、RubyとかSwiftも関数ブロック内の最後の式が暗黙的に関数の返却値になる仕様とのこと。 さらに、LISP言語に端を発する仕様であることを考えると、かなり古くからある言語仕様のようです。

LAMP環境をソースからビルドしてみた(PHP編)

 先日投稿したApache編に引き続いて、今回はPHPをソースインストールしていきます。作業環境その他諸々は前回と同じなので割愛します。

PHPソースの入手

 PHPの公式サイトからソースを取得してきます。そういえば、ファイルのダウンロードといえばwgetがありましたね。なんで前回忘れてたんだろ。

$ wget https://www.php.net/distributions/php-8.1.1.tar.gz
$ wget https://www.php.net/distributions/php-8.1.1.tar.gz.asc

 前回同様署名を検証し、アーカイブを展開していきます。

$ gpg --verify php-8.1.1.tar.gz.asc
$ gzip -d php-8.1.1.tar.gz
$ tar xvf php-8.1.1.tar

依存関係の解決

 こちらも前回同様、インストールしていくうえで必要なパッケージなどを準備していく必要があります。何が必要なのかは、公式ドキュメントにすべて示されています。
PHP: Unix システムへのインストール - Manual

autoconf

 autoconfはconfigureファイルを自動で生成してくれるツールです。これからインストールするPHPのバージョンは8.1.1なので、バージョン2.64以上を選択します。とりあえず今回は最新版をインストールします。

$ wget http://ftp.gnu.org/gnu/autoconf/autoconf-latest.tar.xz
$ xz -d autoconf-latest.tar.xz
$ tar xvf autoconf-latest.tar
$ cd autoconf-latest
$ ./configure
(略)
GNU M4 1.4.6 or later is required; 1.4.16 or newer is recommended.

 autoconfはM4言語のマクロなので、M4をインストールする必要があります。先にこちらをインストールしましょう。

$ cd
$ wget http://ftp.gnu.org/gnu/m4/m4-latest.tar.xz
$ xz -d m4-latest.tar.xz
$ tar xvf m4-latest.tar
$ cd m4-latest
$ ./configure
$ sudo make
$ sudo make install

 これでOK。もう一度autoconfのソースフォルダに戻って作業を再開します。

$ ./configure
$ sudo make
$ sudo make install
$ autoconf --version
autoconf (GNU Autoconf) 2.71
(略)

automake

 こちらはMakefileを自動で生成してくれるツールです。autoconfと同様の方法でインストールします。

$ wget https://ftp.gnu.org/gnu/automake/automake-1.16.5.tar.xz 
$ xz -d automake-1.16.5.tar.xz
$ tar xvf automake-1.16.5.tar
$ cd automake-1.16.5
$ ./configure
$ sudo make
$ sudo make install
$ automake --version
automake (GNU automake) 1.16.5
(略)

libtool

 各種ファイルを自動で作成してくれる上記の2ツールとは異なり、libtoolはプラットフォーム間の違いを吸収してくれるインタフェースの役割を持っています。
 インストール方法はこれまで通りです。

$ wget https://ftpmirror.gnu.org/libtool/libtool-2.4.6.tar.gz
$ gzip -d libtool-2.4.6.tar.gz
$ tar xvf libtool-2.4.6.tar
$ cd libtool-2.4.6
$ ./configure
$ sudo make
$ sudo make install
$ libtool --version
libtool --version
libtool (GNU libtool) 2.4.6
(略)

Re2c

 Re2cはLexer(lexical analyzer:字句解析器)を自動で生成するツールです。
 Lexerは与えられた文字列をトークンとして分割する役割を持ち、処理系においては後述するParserと組み合わせて利用します。

$ wget https://re2c.org/https://github.com/skvadrik/re2c/releases/download/2.2/re2c-2.2.tar.xz
$ xz -d re2c-2.2.tar.xz
$ tar xvf re2c-2.2.tar
$ cd re2c-2.2
$ ./configure
$ sudo make
$ sudo make install
$ re2c --version
re2c 2.2

Bison

 BisonはParser(構文解析器)を生成するツールです。
 Lexerと同様、Parserもソースコードの処理時に使用されますが、こちらは複数のトークンを処理し、構文木の構造を作成する役割を持ちます。

$ wget https://ftp.gnu.org/gnu/bison/bison-3.8.tar.xz
$ xz -d bison-3.8.tar.xz
$ tar xvf bison-3.8.tar
$ cd bison-3.8
$ ./configure
$ sudo make
$ sudo make install
$ bison --version
bison (GNU Bison) 3.8
(略)

Configure実行

 必要なライブラリ等がそろったので、PHPのソースディレクトリに移動して./configureを叩きます。

configure: error: in `/home/klag/php-8.1.1':
configure: error: The pkg-config script could not be found or is too old.  Make sure it
is in your PATH or set the PKG_CONFIG environment variable to the full
path to pkg-config.

pkg-config

エラーで「pkg-configがない」と言われてしまったので、インストールしていきます。例によって、wgetして展開してconfigure, make, make installします。

$ wget https://pkgconfig.freedesktop.org/releases/pkg-config-0.29.2.tar.gz
$ gzip -d pkg-config-0.29.2.tar.gz
$ tar xvf pkg-config-0.29.2.tar 
$ cd pkg-config-0.29.2
$ ./configure --with-internal-glib
$ make
$ sudo make install
$ pkg-config --version
0.29.2

 オプションとして--with-internal-glibを指定していますが、これはglibが入っていない場合にpkg-config側にバンドルされているglibを使用してビルドを行うためのものです。

 再びPHPのconfigureを行います。

configure: error: Package requirements (libxml-2.0 >= 2.9.0) were not met:

No package 'libxml-2.0' found

libxml2

 今度はlibxml2がないとのこと。インストールしましょう。

$ wget ftp://xmlsoft.org/libxml2/libxml2-2.9.12.tar.gz
$ gzip -d libxml2-2.9.12.tar.gz
$ tar xvf libxml2-2.9.12.tar
$ cd libxml2-2.9.12
$ ./configure
$ make 
$ sudo make install

 再度configureするも、次はsqlite3がないと言われました。

configure: error: Package requirements (sqlite3 >= 3.7.7) were not met:

No package 'sqlite3' found

sqlite3

 DBはMySQLを使うのでサポートは不要なのですが、--without-sqlite3オプションをつけても同じ内容のエラーが出てしまうのでインストールしておきます。

$ wget https://www.sqlite.org/2022/sqlite-autoconf-3370200.tar.gz 
$ gzip -d sqlite-autoconf-3370200.tar.gz 
$ tar xvf sqlite-autoconf-3370200.tar
$ cd sqlite-autoconf-3370200
$ ./configure
$ make
$ sudo make install
$ sqlite3 --version
3.37.2 (略)

 これでOK。
 ここで気が付いたのですが、LAMP環境でPHPを使うにはApache2との連携を行う必要があるとのこと。
 参照:PHP: Apache 2.x (Unixシステム用) - Manual
 いままでは./configureだけを実行していたのですが、次回以降は以下のコマンドを実行していきます。

./configure --with-apxs2=/usr/local/apache2/bin/apxs --with-pdo-mysql

 APXSはApacheの拡張モジュールを管理する実行形式ファイルです。これを使ってPHPとの連携をするための設定を行ってくれるものであると思われます。
 PDOはPhp Data Objectsの略で、PHPからDBを利用する際に利用されるものです。今回はMySQL用のものを利用していきます。

 無事configureが通ったので、make, make installしていきます。
 ソースの量が結構あるのでmakeに時間がかかりました。終わったらmake installを叩きます。こちらはすぐに完了しました。

動作確認

PHPApacheの連携ができているかの確認も兼ねて、/usr/local/apache2/htdocs以下に次のような内容のphpinfo.phpを作成します。

<?php
    phpinfo();
?>

 これで(サーバのIPアドレス)/phpinfo.phpにアクセスしたところ、PHPのソース自体が表示されてしまいました。
 公式ドキュメントを確認し、Apacheが.phpファイルをPHPソースとして認識できるように以下のような設定をhttpd.confに追加しました。

<FilesMatch \.php$>
    SetHandler application/x-httpd-php
</FilesMatch>

 Apacheを再起動してページをリロードしたところ、無事phpinfoが表示されました。 f:id:KLag:20220114005510p:plain

まとめ

 前回の作業の経験から、ある程度スムーズに進むようになってきました。ただ、前回に比べてエラーが多く発生し、その解決に時間がかかってしまいました。
 また、他のアプリケーションとの連携を行う際に必要な設定に関して多少は理解が深まったと感じています。
 次回はMySQLのインストールを行っていきます。ではまた。

Cisco CatalystスイッチにSSHで接続する

かねてからCisco製品が欲しい欲しいと言ってましたが、先日ついにCatalyst 3560Xスイッチを購入しました。
2960シリーズでもよかったんですけど、L3スイッチでVLAN間ルーティングしてみるのも楽しそうだなと思ったので3560Xを選択。結果、想像以上にデカいものが届きました。

作業開始

例によってシリアルコンソールケーブルをコンソールポートに挿し込んでscreenで接続していきます。設定の度にコンソールケーブルをつないでいては面倒が過ぎるので、ここからSSHで設定ができるようにしていきます。

IPアドレスの設定

初めに、スイッチ自体のIPアドレスを設定します。管理用のインタフェースVlan1に設定すればいいらしい。

Switch>en
Switch#configure terminal
Switch(config)#interface vlan1
Switch(config-if)#ip address aaa.bbb.ccc.ddd 255.255.255.0
Switch(config-if)#no shutdown

設定を確認するときは、ユーザモードまたは特権モードでshow interface vlan1を実行すればOK。IPアドレスInternet Address is aaa.bbb.ccc.ddd/eeで表示されるはず。
お好みで他ホストからPingするなりして疎通を確認しましょう。

SSH有効化の準備

さっそくSSHサーバを有効化していきたいところですが、その前にユーザ名とドメイン名、vtyポートの設定をしておく必要があります。
ここではvtyポートを5つ作成し、その他の設定は例として適当な文字列を記載しておきます。

Switch(config)#username klag password hoge
Switch(config)#line vty 0 4
Switch(config-line)#login local
Switch(config-line)#exit
Switch(config)#ip domain-name fuga.local

SSHサーバ有効化

最後に、SSHサーバを有効化していきます。SSHバージョン2、4096bit RSA鍵を利用します。

Switch(config)#crypto key generate rsa
(略)
How many bits in the modules [512]: 4096
Switch(config)#ip ssh version 2

おまけ:SSHクライアントの設定

以上でSSHサーバは有効になりましたが、いつものようにssh klag@aaa.bbb.ccc.dddで接続しようとしたらエラー発生。内容は以下の通り。

$ ssh klag@aaa.bbb.ccc.ddd
Unable to negotiate with aaa.bbb.ccc.ddd port 22: no matching cipher found. Their offer: aes128-cbc,3des-cbc,aes192-cbc,aes256-cbc

どうやら暗号方式が異なるためにネゴシエーションに失敗しているようです。ということで、-cオプションで接続時に使用する暗号方式を指定してあげます。

$ ssh klag@aaa.bbb.ccc.ddd -c aes256-cbc

ただ、毎度毎度オプションを指定するのも面倒なので、configファイルに次のように登録しておきました。

Host Catalyst
    HostName aaa.bbb.ccc.ddd
    User klag
    Ciphers +aes256-cbc

これで無事接続できるようになりました。よかったね!

LAMP環境をソースからビルドしてみた(Apache編)

 先日、某企業様の選考エントリー課題として、「LAMP環境のソースインストール」というものをいただきました。
 僕としても初めての試みということもあり、せっかくなので今回から「LAMP環境をソースからビルドしてみた」シリーズとして連載記事にその記録を残していこうと思います。
 また、根本的な理解を深めるという側面もあるので、基本的に公式ドキュメントなどの一次情報を参考にして作業を進めていきます。(英語文献を読むのも頑張っていきたい...)
Compiling and Installing - Apache HTTP Server Version 2.4

作業環境

  • OS: Ubuntu Server 20.04 (@VBox VM)
  • RAM: 8GB
  • NIC: Bridge Adaptor

ソースコードの入手・展開

 まず初めに、Apacheのサイトでこれからビルドしていくソースコードを入手してきます。

$ curl https://dlcdn.apache.org//httpd/httpd-2.4.51.tar.gz -o httpd-2.4.51.tar.gz

 SHA256ハッシュで整合性をチェックします。

$ curl https://downloads.apache.org/httpd/httpd-2.4.51.tar.gz.sha256 -o httpd-2.4.51.tar.gz.sha256
$ sha256sum -c httpd-2.4.51.tar.gz.sha256
httpd-2.4.51.tar.gz: OK

 gzip, tarで展開して、さっそくワーキングディレクトリを移動していきます。

$ gzip -d httpd-2.4.51.tar.gz
$ tar xvf httpd-2.4.51.tar
$ cd httpd-2.4.51

依存関係の解決

 ソースをただ持ってきてmakeを叩いただけではインストールはできません。公式ドキュメントのRequirementsにも書いてある通り、Apacheを動かすために外部パッケージの設定を行っていく必要があります。

APRAPR_Util

The mission of the Apache Portable Runtime (APR) project is to create and maintain software libraries that provide a predictable and consistent interface to underlying platform-specific implementations.
引用: https://apr.apache.org/
(APRの使命は、プラットフォーム特有の実装にとらわれない一貫性を提供するライブラリを作成し、維持することです。)

 この文章からもわかる通り、APRApacheを動かすために必要なランタイムです。READMEファイルを見た限り、原子性を満たす操作やメモリ・プロセスの管理、ファイル・ネットワークの入出力などを行っているようです。
 こちらもApache本体の時と同じように、ソースとハッシュを持ってきて整合性をチェックし、展開していきます。

APR
$ curl https://dlcdn.apache.org//apr/apr-1.7.0.tar.gz -o apr-1.7.0.tar.gz
$ curl https://downloads.apache.org/apr/apr-1.7.0.tar.gz.sha256 -o apr-1.7.0.tar.gz.sha256
$ sha256sum -c apr-1.7.0.tar.gz.sha256
apr-1.7.0.tar.gz: OK
$ gzip -d apr-1.7.0.tar.gz
$ tar xvf apr-1.7.0.tar
APR-Util
$ curl https://dlcdn.apache.org//apr/apr-util-1.6.1.tar.gz -o apr-util-1.6.1.tar.gz
$ curl https://downloads.apache.org/apr/apr-util-1.6.1.tar.gz.sha256 -o apr-util-1.6.1.tar.gz.sha256
$ sha256sum apr-util-1.6.1.tar.gz.sha256
apr-util-1.6.1.tar.gz: OK
$ gzip -d apr-util-1.6.1.tar.gz
$ tar xvf apr-util-1.6.1.tar

 あとは、これらのソースを~/httpd-2.4.51/srclib内に移動すればOK。ただし、ディレクトリ名のバージョンを表す数字は消しておきます。

$ mv apr-1.7.0/ httpd-2.4.51/srclib/apr
$ mv apr-util-1.6.1/ httpd-2.4.51/srclib/apr-util

PCRE (Perl-Compatible Regular Expressions)

 名前の通り、Perl互換の正規表現を扱うためのパッケージです。github上で公開されている最新バージョンを持ってきます。

$ curl -L https://github.com/PhilipHazel/pcre2/releases/download/pcre2-10.39/pcre2-10.39.tar.gz -o pcre2-10.39.tar.gz
$ curl -L https://github.com/PhilipHazel/pcre2/releases/download/pcre2-10.39/pcre2-10.39.tar.gz.sig -o pcre2-10.39.tar.gz.sig

 PCREのパッケージはGPG鍵で署名されているので、検証は以下のように行いました。

$ gpg --verify pcre2-10.39.tar.gz.sig 
gpg: assuming signed data in 'pcre2-10.39.tar.gz'
gpg: Signature made Fri Oct 29 16:07:03 2021 UTC using RSA key 45F68D54BBE23FB3039B46E59766E084FB0F43D8
gpg: Can't check signature: No public key

 公開鍵がないと怒られたので、公開鍵を取得してきてからもう一度署名を検証します。

$ gpg --keyserver pgp.nic.ad.jp --recv-key 45F68D54BBE23FB3039B46E59766E084FB0F43D8
$ gpg --verify pcre2-10.39.tar.gz.sig
...
gpg: Good signature from "Philip Hazel <ph10@hermes.cam.ac.uk>" [unknown]
...

 検証が完了したら、いままでと同様に展開していきます。

$ gzip -d pcre2-10.39.tar.gz
$ tar xvf pcre2-10.39.tar

 展開が完了したら、PCREのREADMEに書いてある通りにインストールしていきます。/usr/local/ 以下のファイル操作でPermission Deniedされることがあるので、sudoをつけてmakeしていきます。

$ cd pcre2-10.39
$ ./configure
$ sudo make
$ sudo make install 

ANSI-Cコンパイラとビルドツール

$ make -v
GNU Make 4.2.1
(略)
$ gcc -v
(略)
gcc version 9.3.0 (Ubuntu 9.3.0-17ubuntu1~20.04)

正確な時刻

 systemd-timesyncdをインストールしてNTPで時刻設定を行います。
 私の環境ではルータ(192.168.11.1)がNTPサーバの役割も受け持っているので、そこに同期する形で時刻を合わせていきます。

$ sudo apt install systemd-timesyncd
$ sudo vim /etc/systemd/timesyncd.conf 
(↓のようにConfファイルを編集する)
[Time]
NTP=192.168.11.1
$ sudo systemctl unmask systemctl-timesyncd
$ sudo systemctl enable systemd-timesyncd
$ sudo systemctl start systemd-timesyncd
$ sudo timedatectl set-ntp true  // NTPでの同期を有効化
$ sudo timedatectl set-timezone Asia/Tokyo // タイムゾーンをUTC+9に
$ timedatectl
(略)
                Time zone: Asia/Tokyo (JST, +0900)
System clock synchronized: yes
              NTP service: active

ソースツリーの構築とビルド(エラーとの闘い)

 ここまででインストールに必要なライブラリやパッケージの導入や、事前に済ませておくべき設定はすべて完了しました。これにてようやくApache本体のインストールに取り掛かっていきます。
 はじめに、ソースツリーをUbuntu向けに構成していきます。

$ ./configure

 実行すると作業状況などのログがダーーーーっと流れていきます。不足しているパッケージなどがある場合は最後の行に出力されます。成功した場合はバージョンやインストール先、システムのコンパイラ情報などが表示されているはず。
 無事終了したら、makeでビルドしていきます。
 これでうまくいくかと思いきや、エラー発生。APR関係の処理のあたりでexpat.hがないと怒られてしまいました。 というわけでこちらもソースからインストール。

$ curl -L https://github.com/libexpat/libexpat/releases/download/R_2_4_2/expat-2.4.2.tar.gz -o expat.tar.gz 
$ gzip -d expat.tar.gz
$ tar xvf expat.tar
$ cd expat
$ ./configure
$ sudo make
$ sudo make install

 今度こそ上手く行くかと思ったのですが、PCREまわりでエラー発生。ここでとても詰まりました。
 結論から言ってしまうと、PCRE2ではなくPCREをインストールする必要があったということがわかりました。公式サイトにEnd of lifeなどと書いてあったので、何も考えずに最新版を入れてしまったのが間違いでした。
 ということで、PCRE2をアンインストールしてからPCREを入れなおします。

$ cd pcre2-10.39
$ sudo make uninstall
$ cd
$ curl -L -o pcre-8.45.tar.gz https://sourceforge.net/projects/pcre/files/pcre/8.45/pcre-8.45.tar.gz/download
$ gzip -d pcre-8.45.tar.gz
$ tar xvf pcre-8.45.tar
$ cd pcre-8.45
$ ./configure
$ sudo make
$ sudo make install

これでOK。もう一度httpdディレクトリにもどって作業を進めていきます。

$ ./configure --with-pcre=/usr/local
$ sudo make
$ sudo make install

お疲れさまでした。これにてインストールは終了です。

httpdの起動

 インストールも終わったので、さっそくApacheを動かしていきましょう。 公式ドキュメントによると、apachectlで起動できるらしい。PATHを通してからやっていきます。

$ export PATH=$PATH:/usr/local/apache2/bin
$ sudo apachectl -k start
/usr/local/apache2/bin/httpd: error while loading shared libraries: libpcre.so.1: cannot open shared object file: No such file or directory

 ライブラリが読み込めてないとのこと。調べてみたら、新たなライブラリを追加したときはldconfigでキャッシュとかリンキングをしなおしてあげる必要があるらしい。初めて使うコマンドでした。
気を取り直してもう一回。

$ sudo apachectl -k start
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using (サーバのIPv6アドレス). Set the 'ServerName' directive globally to suppress this message

 どうやらうまく動いてはいるのですが、サーバ名が指定されてないと警告が表示されました。毎度毎度このメッセージが出るのはうっとうしいので、httpd.confにサーバ名を設定します。
 あとはapachectl -k stopしてから再起動すれば、なにも表示されないでコマンドが通るはず。
 サーバにHTTPでアクセスしたら見慣れた画面とは違うページが表示されはしたものの、無事動作が確認できました。"It works!"の文字にここまで感動したのは初めてかもしれない。
f:id:KLag:20211227044259p:plain

まとめ

 sudo apt install apache2で済むようなことが、ソースコードからビルドするととても大変だということが身にしみてわかりました。これから毎日aptに感謝しながら生活したいと思います。
 それ以上に、今までLinuxを利用する中で触ってこなかった/usr/local/lib/usr/local/includeといったディレクトリにシステム全体で共有されるライブラリとかヘッダファイルが置いてあることや、単純にApache一つをとっても様々なライブラリとの依存関係があることが理解できました。
 また、今までソースからビルドする方法に関しては皆目見当がつかなかったのですが、今回の構築を通じて大まかな流れに関しては掴めたかなと思います。
 今回はここまでです。次回はここにPHPをインストールしていきたいと思います。ではまた。