👋

CentOS 7.9でApache 2.4のHTTP/2対応

2023/09/18に公開

CentOS 7.9でApache 2.4のHTTP/2対応の設定手順を説明します。
本番環境でDocker導入済みの場合は、Apache 2.4のDockerイメージをpullしてきてHTTP/2を有効化した方が簡単だと思います。
本手順は、Docker未導入のCentOS 7.9サーバでのApache 2.4導入例として捉えてください。

DockerでCentOS 7.9を起動

説明のため、CentOS 7.9のDockerイメージを使用します。

docker run --name http2 -d centos:7.9.2009 /bin/sh -c "while :; do sleep 10; done"
# systemctlコマンドを使用できるようにしたい場合は、
# 上記のコマンドではなく、以下のコメントアウトしているコマンドを使用する。
# docker run --privileged --name http2 -d centos:7.9.2009 /sbin/init
# ただし、本番環境では--privilegedオプションは権限が強く危険なので注意が必要とのこと。

docker exec -it http2 /bin/bash
yum update -y

自己署名サーバ証明書の生成

yum install -y openssl
yum install -y vim
touch ./generate-cert.sh
chmod 770 ./generate-cert.sh
vim ./generate-cert.sh

自己署名サーバ証明書の作成手順は、以下のサイトをほぼそのまま参考にしました。
https://oji-cloud.net/2020/09/25/post-5540/

./generate-cert.sh
CA_DIR=./pki/CA
CA_NAME="local-CA"
mkdir -p "${CA_DIR}/private/"
mkdir -p "${CA_DIR}/certs/"
# CA
echo ''
echo '-----'
echo '[[[ CA ]]]'
echo '-----'
# CAの秘密鍵(RSA 2048 bit)の生成
openssl genrsa 2048 > "${CA_DIR}/private/${CA_NAME}.key"
# CAのCSR(署名アルゴリズムsha256)の生成
openssl req -new -key "${CA_DIR}/private/${CA_NAME}.key" -sha256 -out "${CA_DIR}/${CA_NAME}.csr"
# CAのCSR, CAの秘密鍵 からCAのCA証明書生成
openssl x509 -days 1095 -in "${CA_DIR}/${CA_NAME}.csr" -req -signkey "${CA_DIR}/private/${CA_NAME}.key" -out "${CA_DIR}/certs/${CA_NAME}.pem"

TLS_DIR=./pki/tls
SERVER_NAME="local-server"
mkdir -p "${TLS_DIR}/private/"
mkdir -p "${TLS_DIR}/certs/"
# サーバ
echo ''
echo '-----'
echo '[[[ Server ]]]'
echo '-----'
# サーバの秘密鍵(RSA 2048 bit)の生成
openssl genrsa 2048 > "${TLS_DIR}/private/${SERVER_NAME}.key"
# サーバのCSR(署名アルゴリズムsha256)の生成
openssl req -new -key "${TLS_DIR}/private/${SERVER_NAME}.key" -sha256 -out "${TLS_DIR}/${SERVER_NAME}.csr"
# サーバのCSR, CAの秘密鍵, CAのCA証明書 からサーバのサーバ証明書生成
## openssl caコマンド実行時に
## - CAデータベースのディレクトリとしてCA_DIRを使用する
## - CAの署名policyとしてpolicy_anythingを使用する
## ように設定ファイルを作成。
## 設定ファイルはCentOS 7.9.2009 (Core)のOpenSSL 1.0.2k-fipsの/etc/pki/tls/openssl.cnfファイルを元にした。
cat > ./ca.conf <<'EOF'
####################################################################
[ ca ]
default_ca      = CA_default            # The default ca section

####################################################################
[ CA_default ]
EOF

echo dir = "'${CA_DIR}'" >> ./ca.conf

cat >> ./ca.conf <<'EOF'
certs           = $dir/certs            # Where the issued certs are kept
crl_dir         = $dir/crl              # Where the issued crl are kept
database        = $dir/index.txt        # database index file.
#unique_subject = no                    # Set to 'no' to allow creation of
                                        # several ctificates with same subject.
new_certs_dir   = $dir/newcerts         # default place for new certs.

certificate     = $dir/cacert.pem       # The CA certificate
serial          = $dir/serial           # The current serial number
crlnumber       = $dir/crlnumber        # the current crl number
                                        # must be commented out to leave a V1 CRL
crl             = $dir/crl.pem          # The current CRL
private_key     = $dir/private/cakey.pem# The private key
RANDFILE        = $dir/private/.rand    # private random number file

x509_extensions = usr_cert              # The extentions to add to the cert

# Comment out the following two lines for the "traditional"
# (and highly broken) format.
name_opt        = ca_default            # Subject Name options
cert_opt        = ca_default            # Certificate field options

# Extension copying option: use with caution.
# copy_extensions = copy

# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs
# so this is commented out by default to leave a V1 CRL.
# crlnumber must also be commented out to leave a V1 CRL.
# crl_extensions        = crl_ext

default_days    = 365                   # how long to certify for
default_crl_days= 30                    # how long before next CRL
default_md      = sha256                # use SHA-256 by default
preserve        = no                    # keep passed DN ordering

# A few difference way of specifying how similar the request should look
# For type CA, the listed attributes must be the same, and the optional
# and supplied fields are just that :-)
EOF

echo policy = policy_anything >> ./ca.conf

cat >> ./ca.conf <<'EOF'
# For the 'anything' policy
# At this point in time, you must list all acceptable 'object'
# types.
[ policy_anything ]
countryName             = optional
stateOrProvinceName     = optional
localityName            = optional
organizationName        = optional
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ usr_cert ]

# These extensions are added when 'ca' signs a request.

# This goes against PKIX guidelines but some CAs do it and some software
# requires this to avoid interpreting an end user certificate as a CA.

basicConstraints=CA:FALSE

# Here are some examples of the usage of nsCertType. If it is omitted
# the certificate can be used for anything *except* object signing.

# This is OK for an SSL server.
# nsCertType                    = server

# For an object signing certificate this would be used.
# nsCertType = objsign

# For normal client use this is typical
# nsCertType = client, email

# and for everything including object signing:
# nsCertType = client, email, objsign

# This is typical in keyUsage for a client certificate.
# keyUsage = nonRepudiation, digitalSignature, keyEncipherment

# This will be displayed in Netscape's comment listbox.
nsComment                       = "OpenSSL Generated Certificate"

# PKIX recommendations harmless if included in all certificates.
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer

# This stuff is for subjectAltName and issuerAltname.
# Import the email address.
# subjectAltName=email:copy
# An alternative to produce certificates that aren't
# deprecated according to PKIX.
# subjectAltName=email:move

# Copy subject details
# issuerAltName=issuer:copy

#nsCaRevocationUrl              = http://www.domain.dom/ca-crl.pem
#nsBaseUrl
#nsRevocationUrl
#nsRenewalUrl
#nsCaPolicyUrl
#nsSslServerName

# This is required for TSA certificates.
# extendedKeyUsage = critical,timeStamping
EOF

## CAデータベースの中身を作成
touch "${CA_DIR}/index.txt"
echo 01 > "${CA_DIR}/serial"
mkdir -p "${CA_DIR}/newcerts"
## サーバのCSR, CAの秘密鍵, CAのCA証明書 からサーバのサーバ証明書生成
openssl ca -config "./ca.conf" -days 1095 -in "${TLS_DIR}/${SERVER_NAME}.csr" -out "${TLS_DIR}/certs/${SERVER_NAME}.pem" -keyfile "${CA_DIR}/private/${CA_NAME}.key" -cert "${CA_DIR}/certs/${CA_NAME}.pem"
./generate-cert.sh
# [[[ CA ]]]
# [[Server]]
# のそれぞれでCA、サーバそれぞれの証明書作成用のCSRの情報を聞かれるので、適当に入力。
# ただし、サーバのSCRについての以下の質問:
# Common Name (eg, your name or your server's hostname) []:
# に対しては、「localhost」と正しくドメイン名を入力しておく必要あり。
# (このドメイン名でサーバ証明書が生成されるため。)

# 生成したキーや証明書をApacheで使用するためにコピー
cp /pki/tls/certs/local-server.pem /etc/pki/tls/certs/localhost.crt
cp /pki/tls/private/local-server.key /etc/pki/tls/private/localhost.key
cp /pki/CA/certs/local-CA.pem /etc/pki/CA/certs/ca.crt

Apacheのインストール

次にApacheをインストールします。

yum install -y wget
# Apacheの最新版をyumで取得できるよう設定
yum install -y epel-release
pushd /etc/yum.repos.d && wget https://repo.codeit.guru/codeit.el$(rpm -q --qf "%{VERSION}" $(rpm -q --whatprovides redhat-release)).repo && popd
# 最新版になっていることを確認(私が実行した時点ではVersion:2.4.57)
yum info httpd
# インストール
yum install -y httpd
yum install -y mod_http2 mod_ssl
httpd -v

結果は以下のとおり。

Server version: Apache/2.4.57 (codeit)
Server built:   Apr  6 2023 17:31:51
# 設定ファイルをコピーしてバックアップをとっておく。
cp /etc/httpd/conf.d/ssl.conf /etc/httpd/conf.d/ssl.conf.bak
vim /etc/httpd/conf.d/ssl.conf

以下のディレクティブの行を確認して、設定値の場所に、
サーバ証明書と秘密鍵のファイルが存在していることを確認。

/etc/httpd/conf.d/ssl.conf
SSLCertificateFile /etc/pki/tls/certs/localhost.crt
SSLCertificateKeyFile /etc/pki/tls/private/localhost.key

ファイルの末尾の方の<VirtualHost *:443>セクションに以下のとおり追記。

/etc/httpd/conf.d/ssl.conf
<VirtualHost *:443>
  # (省略)
  # <VirtualHost>セクション内の末尾に以下を追記

  # HTTP2
  Protocols h2 http/1.1

  # LogLevel debug
</VirtualHost>

本来は、以下の設定も有効にする必要があります。
しかし、既にファイル

  • /etc/httpd/conf.modules.d/00-ssl.conf
  • /etc/httpd/conf.d/ssl.conf
  • /etc/httpd/conf.modules.d/10-h2.conf

の中で設定が有効になっているので、省略します。

/etc/httpd/conf/httpd.conf
# Listenの設定は他の箇所でデフォルトで設定済みなので省略
Listen 443

# SSLの有効化設定は他の箇所でデフォルトで設定済みなので省略
SSLEngine on

# Moduleの読み込みも他の箇所で実行済みなので省略

# SSL
LoadModule ssl_module modules/mod_ssl.so

# HTTP2
LoadModule http2_module modules/mod_http2.so

curlで動作確認

最後に、curlでApacheとHTTP/2で通信できることを確認します。

# curl最新版をインストールするためにyumのリポジトリを設定
vim /etc/yum.repos.d/city-fan.repo
/etc/yum.repos.d/city-fan.repo
[CityFan]
name=City Fan Repo
baseurl=http://www.city-fan.org/ftp/contrib/yum-repo/rhel$releasever/$basearch/
enabled=1
gpgcheck=0
yum update -y
yum install -y curl
curl --version

結果は以下のとおり。

curl 8.2.1 (x86_64-redhat-linux-gnu) libcurl/8.2.1 NSS/3.79 zlib/1.2.7 libpsl/0.20.2 (+libidn2/2.3.2) libssh2/1.11.0 nghttp2/1.56.0 OpenLDAP/2.4.44
Release-Date: 2023-07-26
Protocols: dict file ftp ftps gopher gophers http https imap imaps ldap ldaps mqtt pop3 pop3s rtsp scp sftp smb smbs smtp smtps telnet tftp
Features: alt-svc AsynchDNS GSS-API HSTS HTTP2 HTTPS-proxy IPv6 Kerberos Largefile libz NTLM NTLM_WB PSL SPNEGO SSL UnixSockets

通信確認用のHTMLファイルを作成します。

vim /var/www/html/index.html
/var/www/html/index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>title</title>
    <!-- <link rel="stylesheet" href="style.css">
    <script src="script.js"></script> -->
  </head>
  <body>
    Hello, World!
  </body>
</html>
# Apacheサーバの起動
httpd
# curlでApacheとHTTP/2通信が可能か確認
curl --http2-prior-knowledge --cacert /etc/pki/CA/certs/ca.crt -v https://localhost/ -o "tmp.txt"

出力結果に以下の行が出力されていれば、
HTTP/2で通信成功です。

< HTTP/2 200

Discussion