カテゴリー: Linux Page 1 of 62

攻撃ログの傾向とスペルミス。

自分のサーバへのアクセスログ。いつもの挨拶の他、「これはさすがに」と思うものがあったのでご紹介です。

ログ抜粋

例によってIPアドレスやホスト名を無害化(ダミーデータへの置き換え)を行っています。テロリストに名前を与える気はないからです。

[Fri May 22 00:01:58.357561 2026] [security2:error] [pid 563988:tid 133631884367552] [client 192.0.2.10:13346] [client 192.0.2.10] ModSecurity: Multipart parsing error: Multipart: No boundaries found in payload. [hostname "example.com"] [uri "/"] [unique_id "ag8eZi5y5sdCQ_cRAbbcQAAAAFc"]
[Fri May 22 00:01:58.357663 2026] [security2:error] [pid 563989:tid 133632756782784] [client 192.0.2.10:13348] [client 192.0.2.10] ModSecurity: Multipart parsing error: Multipart: No boundaries found in payload. [hostname "example.com"] [uri "/projects/"] [unique_id "ag8eZuLCu5fVcQiqqP37FAAAAIo"]
[Fri May 22 00:02:00.980490 2026] [security2:error] [pid 563989:tid 133632656135872] [client 192.0.2.10:53854] [client 192.0.2.10] ModSecurity: Multipart parsing error: Multipart: No boundaries found in payload. [hostname "example.com"] [uri "/"] [unique_id "ag8eaOLCu5fVcQiqqP37HQAAAIw"]
[Fri May 22 00:22:32.285606 2026] [security2:error] [pid 563989:tid 133632790353600] [client 192.0.2.20:48723] [client 192.0.2.20] ModSecurity: Access denied with code 404 (phase 1). Pattern match "^[\\\\d.]+(:\\\\d+)?$" at REQUEST_HEADERS:Host. [file "/usr/share/modsecurity-crs/coreruleset/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf"] [line "53"] [id "10004"] [msg "[CUSTOM RULE] Host header is a numeric IP address (incl port). Blocked immediately."] [tag "application-attack"] [tag "PROTOCOL_VIOLATION/INVALID_HREQ"] [hostname "203.0.113.50"] [uri "/"] [unique_id "ag8jOOLCu5fVcQiqqP3_ewAAAIY"]
[Fri May 22 00:22:38.213267 2026] [security2:error] [pid 563989:tid 133632647743168] [client 192.0.2.20:56013] [client 192.0.2.20] ModSecurity: Access denied with code 404 (phase 1). Operator EQ matched 0 at REQUEST_HEADERS. [file "/usr/share/modsecurity-crs/coreruleset/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf"] [line "62"] [id "10005"] [msg "[CUSTOM RULE] Missing Host Header. Blocked immediately."] [hostname "example.com"] [uri "/"] [unique_id "ag8jPuLCu5fVcQiqqP3_gwAAAI0"]
[Fri May 22 00:36:55.821987 2026] [security2:error] [pid 563989:tid 133632370915008] [client 192.0.2.30:38808] [client 192.0.2.30] ModSecurity: Access denied with code 404 (phase 1). Pattern match "^[\\\\d.]+(:\\\\d+)?$" at REQUEST_HEADERS:Host. [file "/usr/share/modsecurity-crs/coreruleset/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf"] [line "53"] [id "10004"] [msg "[CUSTOM RULE] Host header is a numeric IP address (incl port). Blocked immediately."] [tag "application-attack"] [tag "PROTOCOL_VIOLATION/INVALID_HREQ"] [hostname "203.0.113.50"] [uri "/.env"] [unique_id "ag8ml-LCu5fVcQiqqP0CpgAAAJU"]
[Fri May 22 02:56:01.000854 2026] [security2:error] [pid 563988:tid 133632387700416] [client 192.0.2.40:46164] [client 192.0.2.40] ModSecurity: Access denied with code 404 (phase 1). Operator EQ matched 0 at REQUEST_HEADERS. [file "/usr/share/modsecurity-crs/coreruleset/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf"] [line "62"] [id "10005"] [msg "[CUSTOM RULE] Missing Host Header. Blocked immediately."] [hostname "example.com"] [uri "/"] [unique_id "ag9HMS5y5sdCQ_cRAbbtWQAAAEw"]
[Fri May 22 03:18:21.209460 2026] [security2:error] [pid 563989:tid 133632396093120] [client 192.0.2.50:53678] [client 192.0.2.50] ModSecurity: Warning. Matched phrase "Mozlila" at REQUEST_HEADERS:User-Agent. [file "/usr/share/modsecurity-crs/coreruleset/rules/REQUEST-913-SCANNER-DETECTION.conf"] [line "56"] [id "913100"] [msg "Found User-Agent associated with security scanner"] [data "Matched Data: Mozlila found within REQUEST_HEADERS:User-Agent: Mozlila/5.0 (Linux; Android 7.0; SM-G892A Bulid/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/60.0.3112.107 Moblie Safari/537.36"] [severity "CRITICAL"] [hostname "example.com"] [uri "/"] [unique_id "ag9MbeLCu5fVcQiqqP0kggAAAJI"]
[Fri May 22 03:18:51.242531 2026] [security2:error] [pid 563989:tid 133632396093120] [client 192.0.2.50:58448] [client 192.0.2.50] ModSecurity: Warning. Matched phrase ".env" at REQUEST_FILENAME. [file "/usr/share/modsecurity-crs/coreruleset/rules/REQUEST-930-APPLICATION-ATTACK-LFI.conf"] [line "145"] [id "930130"] [msg "Restricted File Access Attempt"] [data "Matched Data: .env found within REQUEST_FILENAME: /.env.live"] [severity "CRITICAL"] [hostname "example.com"] [uri "/.env.live"] [unique_id "ag9Mi-LCu5fVcQiqqP0kmwAAAJI"]
[Fri May 22 03:18:51.349415 2026] [security2:error] [pid 563989:tid 133632379307712] [client 192.0.2.50:58432] [client 192.0.2.50] ModSecurity: Warning. String match within "..." at TX:extension. [file "/usr/share/modsecurity-crs/coreruleset/rules/REQUEST-920-PROTOCOL-ENFORCEMENT.conf"] [line "1105"] [id "920440"] [msg "URL file extension is restricted by policy"] [data ".old"] [severity "CRITICAL"] [hostname "example.com"] [uri "/.env.old"] [unique_id "ag9Mi-LCu5fVcQiqqP0knQAAAJQ"]
[Fri May 22 03:19:17.202887 2026] [security2:error] [pid 563988:tid 133632807139008] [client 192.0.2.50:48772] [client 192.0.2.50] ModSecurity: Warning. Matched phrase ".gitlab-ci.yml" at REQUEST_FILENAME. [file "/usr/share/modsecurity-crs/coreruleset/rules/REQUEST-930-APPLICATION-ATTACK-LFI.conf"] [line "145"] [id "930130"] [msg "Restricted File Access Attempt"] [data "Matched Data: .gitlab-ci.yml found within REQUEST_FILENAME: /.gitlab-ci.yml"] [severity "CRITICAL"] [hostname "example.com"] [uri "/.gitlab-ci.yml"] [unique_id "ag9MpS5y5sdCQ_cRAbbvZQAAAEM"]

ここから気になった攻撃ログを見てみましょう。

謎のパースエラー:WAFや脆弱性スキャナの「ご挨拶」

  • 該当ログの傾向:
    • Multipart parsing error: No boundaries found in payload.
  • 攻撃者の意図:

これは攻撃者がファイルアップロード(Multipart形式)を模したリクエストを送っているものの、データ構造が壊れている(Boundaryがない)ためにWAF(ModSecurity)が激怒している状態です。
手動の攻撃ではなく、雑に作られた自動スキャンツール(ボット)が、手当たり次第に脆弱性を探す「偵察フェーズ」としてパケットを送りつけてきたと考えられます。

IPアドレス直打ちでのアクセス:無差別スキャンの証拠

  • 該当ログの傾向:
    • Host header is a numeric IP address / Missing Host Header.
  • 攻撃者の意図:
    通常、ユーザーは「example.com」というドメイン名でアクセスしますが、このボットはドメイン名ではなくサーバーの「生のIPアドレス」を直接指定、あるいはHostヘッダーを空にしてアクセスしています。
    これは「特定のサイトを狙った攻撃」ではなく、「インターネット全体に存在するIPアドレスへ、適当に片っ端から攻撃を打つ」タイプの無差別スキャン(IPスキャン)を行っている明確な証拠です。

「.env」の執拗な探索:お宝(環境変数)の窃盗狙い

  • 該当ログの傾向:
    • /.env, /.env.live, /.env.local, /.env.old, /.env.test
  • 攻撃者の意図:

Webアプリケーションの設定ファイルである .env(環境変数ファイル)を狙っています。
ここには「データベースのパスワード」「AWSやstripeなどのAPI秘密鍵」といった、一発でシステムが崩壊するレベルの情報が書かれています。
ご丁寧に .env だけでなく .env.old や .env.test などの「バックアップっぽいファイル名」まで推測して探している点です。

開発者がうっかり残した過去の遺物を探す、攻撃者の「ずる賢さ」がよく出ています。

「.gitlab-ci.yml」の探索:開発環境のソースコード狙い

  • 該当ログの傾向:
    • /.gitlab-ci.yml
  • 攻撃者の意図:

CI/CD(自動ビルド・デプロイ)の設定ファイルです。ここを覗かれると、システムの内部構造や、ソースコードが保管されているリポジトリの場所、デプロイ用の認証情報などが漏洩します。開発環境のセキュリティ設定が甘いサイトを見つけ出そうとしています。

「Mozlila」という偽装ミス

  • 該当ログの傾向:
    • Matched phrase "Mozlila" at REQUEST_HEADERS:User-Agent.
  • 攻撃者の意図とオチ:

攻撃者は、普通のブラウザ(Mozilla)からのアクセスに見せかけようとUser-Agentを偽装しています。しかし、よく見ると Mozlila(ロとリが逆:モズリラ) になっています。
これはチープなハッキングツールの作成者がスペルミスをしたまま配布しているケースで、ModSecurityのシグネチャ(検出ルール)に「チープなスキャナ特有の文字列」として一発で見破られてブロックされています。

というよりも、この「モズリラ」を紹介したくてこの記事を書いたまであります。

まとめ

もちろん、これらのログは抜粋であり、その数十倍。サイトによっては数百・数千倍のログがあることは日常茶飯事。それでも

  • 相手がどのような攻撃を試みようとしているのか?
  • そのために何を防ぐか?

と、「ログに残る攻撃があるうちは」対策するのは嗜みだと思いました。真に巧妙な攻撃者はログをいかに残さないかに全力を挙げるわけですし。

NextcloudのタスクとiOSのリマインダを認識させる

Nextdloudを更に統合プラットフォームとして使うため、以下の手順が必要でした。

環境

Nextcloud側

  • Ver 33
  • Nextdloud Task (標準アプリ)
  • Ubuntu 24.04
  • PHP-FPM 8.3
  • Apache 2.4
  • MySQL 8
  • ※二要素認証あり
  • ※外部からアクセスできる環境にあること

iOS側

  • iOS 26.42
  • iPhone Air

さっくりとした手順

  1. Nextdloud側でアプリパスワードを作ります。
  2. iOS側でアカウントを競ってします。

Nextcloud側でのアプリパスワードの設定

  1. 個人設定 > セキュリティに遷移します。
  2. デバイスとセッションの一番下、アプリ名というところに適当な名前を付けます。iOSリマインダー
  3. 新しいアプリパスワード作成をクリックします。このパスワードは一度しか表示されません。控えておきます。(一番手っ取り早いのはそのパスワードをコピーして、Nextdloud Talk等で貼り付けること。ただし、Nextdloud全てにアクセスできるパスワードです。設定後、速やかにTalkから削除しましょう。

iOS側での連携

  1. iPhoneの「設定」>「リマインダー(またはアカウント)」>「CalDAVアカウントを追加」の画面を開きます。
  2. 以下のように設定します。
    1. サーバ: 自分のNextdloudのドメイン
    2. アカウント:自分のNextdloudのアカウント
    3. パスワード:先ほど生成したアプリパスワード
    4. 設定:自分が覚えやすいもの
  3. 設定後「次へ」をタップして、エラーがないことを確認します。

連携の確認

Nextdloud側で適当なタスクを作成して、iOS側で表示されることを確認します。

iOSの「リマインダー」に、Nextdloudで設定したタスクが表示されることを確認します。

iOS側で適当なタスクを作成して、Nextdloud側で表示されることを確認します。

firewalldのゾーンと設定方法のケーススタディ

RHEL系Linuxに備わっているfirewalld。非常に柔軟で(比較的)直感的に使える仕組みだったのでメモを残します。

特に驚きだったのがzoneの概念。

ufwが基本的に「システム全体に対してポートを開けるか閉じるか」をシンプルに管理するのに対し、firewalldは「接続するネットワークの信頼度に応じて、ファイアウォールのルールを瞬時に切り替える」という柔軟性がありました。

そもそもfirewalldにおけるゾーンとは?

一言で言うと、ゾーンとは「接続元のネットワークやインターフェース(LANカードなど)の『信頼度』に応じたグループ分け」のことです。

従来の iptables などでは、「このIPアドレスからのこのポートへの通信を許可する」といった細かいルールを1つずつ書く必要があります。

一方、firewalld では以下のようなステップで考えます。

  1. あらかじめ「拒否」「自宅用」「パブリック(公共)」といった、ルールの異なる箱(ゾーン)を用意しておく。
  2. ネットワークインターフェース(例: eth0)や、特定のIPアドレスをその箱に割り当てます。

これにより、「カフェのWi-Fiに繋いだ時は『パブリック』ゾーンに切り替える」「会社のLANに繋いだ時は『社内』ゾーンに切り替える」といった管理が、一瞬でできるようになります。

代表的なプリセットゾーン

firewalld には、最初からいくつかのゾーンが用意されています。

ゾーン名信頼度主な用途・特徴
drop最低すべての受信パッケージを破棄します(応答すら返さない)。こちらからの送信は可能です。
blockすべての受信を拒否します。drop と違い、相手に「拒否しました」という通知(ICMP)を返します。
public低〜中デフォルトのゾーン。 不特定多数がいる公共のネットワーク用。自分が許可した通信(sshなど)だけを通します。
externalルーターとして使う場合の「外側(インターネット側)」用。マスカレード(NAT)が有効になります。
**home / internal**自宅や社内LANなど、周囲のコンピューターを信頼できる場合用。お互いの通信が少し緩く許可されています。
trusted最高すべてを許可します。完全に安全だと分かっているネットワーク専用です。

ゾーンの3つの重要なルール

インターフェースは必ずどこかのゾーンに属する

ネットワークカード(eth0wlan0 など)は、必ずいずれか1つのゾーンに紐付けられます。何も設定していない場合、自動的に public(デフォルトゾーン)に属します。

IPアドレス単位での割り当ても可能

「インターフェース全体は public だけど、上司のPCのIPアドレス(192.168.1.50)だけは trusted ゾーンとして扱う」といった柔軟な設定が可能です。

ルールはゾーンごとに設定する

Webサーバー(80番ポート)を開放したい」となったら、「public ゾーンに対して80番ポートを許可する」というように、ゾーンに対して設定を紐付けます。

もっと有り体に言うと

『ジョジョの奇妙な冒険』第5部における『マン・イン・ザ・ミラー』です。

ここには『スタンド力』は おれの許可なくしては入る事はできない
『おまえ本体』だけ入る事を許可した
ここにある物は全て命のない『物質』だけだ―――おまえとオレだけ!他に『生きてる物』はいない………

という「許可」と「許可しない」を、鏡の世界ではなく「ゾーン」ごとに決められる能力、と言っていいでしょう。

よく使う基本コマンド

ゾーンの状態を確認・操作するための、代表的な firewall-cmd コマンドです。

現在のデフォルトゾーンを確認する

firewall-cmd --get-default-zone

すべてのゾーンの設定を確認する

firewall-cmd --list-all-zones

特定のインターフェース(例: eth0)のゾーンを変更する

firewall-cmd --zone=home --change-interface=eth0

特定のゾーン(例: public)にサービス(例: http)を許可する

 firewall-cmd --zone=public --add-service=http --permanent

`

  • --permanent をつけた後は、設定を反映させるために以下が必要です
firewall-cmd --reload

Linuxサーバーを構築する際は、まず「このサーバーはどこに置かれていて、どのゾーンを適用すべきか」を考えていきましょう。

そのケーススタディを行っていきます。

ケーススタディ

各部署が持っているサーバを管理するためシステム部が一括でzabbixのエージェントを入れたいという状況。

  • システム部だけにzabbixサーバへのssh接続(ターミナル操作)と管理画面(Web管理画面)の閲覧と操作を許可
  • それ以外の部署が所属するNWには上記2つを拒否。
  • zabbixサーバそのものを司る大本のzabbixサーバはエージェントの通信を許可。
  • ただし、zabbixのエージェントは通るようにします。

Step 1: システム部専用ゾーンの作成と「IP」の紐付け

まずは 192.168.1.0/24 だけが所属する専用のゾーン illuso を作ります。

  • ゾーンの新規作成
sudo firewall-cmd --permanent --new-zone=illuso
  • 一度リロードして、OSに新しいゾーンを認識させる(重要)
sudo firewall-cmd --reload

作成したゾーンに「部内NWのIPセグメント」を紐付ける

sudo firewall-cmd --permanent --zone=illuso --add-source=192.168.1.0/24

Step 2: システム部専用ゾーンに「許可サービス」を追加

Step 1 で作ったゾーンに、ssh, http, https の鍵(許可)を配置します。

  • システム部専用ゾーンに対して SSH を許可
sudo firewall-cmd --permanent --zone=illuso --add-service=ssh
  • システム部専用ゾーンに対して HTTP を許可
sudo firewall-cmd --permanent --zone=illuso --add-service=http
  • システム部専用ゾーンに対して HTTPS を許可
sudo firewall-cmd --permanent --zone=illuso --add-service=https

Step 3: 上位監視サーバー専用のゾーン作成と設定

社内のZabbixサーバー(192.168.12.6)だけが所属する man-in-the-mirror を作り、Zabbix Agent用の 10050 ポートを許可します。

  • 上位サーバー用のゾーンを新規作成
sudo firewall-cmd --permanent --new-zone=man-in-the-mirror
  • 再度リロードして、新しいゾーンを認識させる
sudo firewall-cmd --reload
  • そのゾーンに「上位ZabbixのIP」を紐付ける
sudo firewall-cmd --permanent --zone=man-in-the-mirror --add-source=192.168.12.6
  • そのゾーンに 10050 ポート(tcp)の許可を与える
sudo firewall-cmd --permanent --zone=man-in-the-mirror --add-port=10050/tcp

Step 4: 全員に開くポートの追加 と public の掃除

誰からでも受け付ける Zabbix Server(10051)や SMTP(25)を public に設定し、同時に、先ほど部内限定へお引越しさせた不要なサービス(ssh, http, https)を public から削除します。

  • 自身の Zabbix Server ポート(10051)を全員に開放
sudo firewall-cmd --permanent --zone=public --add-port=10051/tcp
  • SMTP(25)を全員に開放
sudo firewall-cmd --permanent --zone=public --add-service=smtp
  • 【とても重要】どこからでも SSH できる状態を public から削除
sudo firewall-cmd --permanent --zone=public --remove-service=ssh
  • どこからでも HTTP できる状態を public から削除
sudo firewall-cmd --permanent --zone=public --remove-service=http
  • HTTPS も public から削除
sudo firewall-cmd --permanent --zone=public --remove-service=https

Step 5: 設定の最終反映と確認

ここまでの --permanent(次回起動用の設定)を、一気に本番環境へ反映(リロード)させます。

  • すべての設定を反映させます。
sudo firewall-cmd --reload

反映が完了したら、正しく設定できているか各ゾーンを覗いてみましょう。

  • 部内限定ゾーンの確認(sourcesにIP、servicesにssh http httpsがあること)
sudo firewall-cmd --zone=illuso --list-all
  • 上位Zabbix用ゾーンの確認(sourceにIP、portsに10050/tcpがあること)
sudo firewall-cmd --zone=man-in-the-mirror --list-all
  • 共通ゾーンの確認(servicesからssh, http, httpsが消え、zabbix-serverやsmtp、portsに10051/tcpがあること)
sudo firewall-cmd --zone=public --list-all

まとめ

以上、手順は多いものの

  • こいつは許可
  • こいつは拒否

を極めて柔軟に行えるのはRHEL系Linuxの持つ特権だと思いました。とはいえ、

この条件は「これから入ろうとする邪魔者」を拒否するには有効ですが、入ってしまったものを取り除くのは極めて厄介です。とくにウィルス(マルウェア)の除去は

『マン・イン・ザ・ミラー』オレだけが外に出る事を許可しろォォォォーーーーッ
うおおおががががが だが! ウイルスは許可しないィィィィィーーーッ
感染した部分は出る事は 許可しないィィィィィィィーーーッ!!

とはならないので注意が必要です。

(最後のこれが言いたいだけのエントリーを書き終えました)

Ubuntu24.04にfail2banの条件緩和。(ヘビーユースのWebサーバの問題点)

昨日設定したfailbanとの連携。ufw.aggressive。

結論から言うと「あまりにも閾値が低すぎて自分自身がロックアウトを喰らう」結果になりました。

何が起きたのか?

自分のIPからのアクセスが全てのサービスにつながらなくなった。

これはSSH接続はのみならずWeb閲覧でも弾かれるを意味します。

取り急ぎ、「確実にignoreipされている」場所からアクセスし、

sudo fail2ban-client status ufw

をしたところ、ものの見事にアクセスしていたIPアドレスが含まれています。

そのため、

sudo fail2ban-client set ufw unbaip IPアドレス

として条件を解除。

そんな中で見つけた挙動。Redminetneは比較的単純な通信が発生するため、

[ufw]
enabled = true
filter = ufw-aggressive
action = iptables-allports
logpath = /var/log/ufw.log
maxretry = 1
bantime = -1
# ignoreipには、自分自身のローカルホストと、巻き添えを防ぎたい大手検索エンジン(Googlebotなど)のIP帯を指定します

では、大量のretryが発生し、fail2banはご丁寧に「こいつは大量アクセスを繰り返している」としてban。更に、当然ながら、iptables-allportsが含まれているため、自分自身が締め出しを食らったという次第。

対処

「閾値の緩和」に尽きます。「人間の通常の作業は問題ないがbotが引っかかる」を目安に修正しました。この時の注意点は 対象サイトに過度にアクセスしないことに尽きます。iptablesと手を組んでいる以上、無効の怒りを買わないよう、接続はSSHのみにとどめます。

修正ファイル /etc/fail2ban/jail.local

[ufw]
enabled = true
filter = ufw-aggressive
action = iptables-allports
logpath = /var/log/ufw.log
# ↓ 1 から 30 〜 50 程度に大幅緩和(ボットは防げて人間は誤検知しない絶妙なライン)
maxretry = 50
# ↓ 10分〜15分(600〜900秒)の間に規定回数叩いたらアウトにする
findtime = 900
bantime = -1
# ignoreipは、自分が今アクセスしているIPを真っ先に入れます
ignoreip = 127.0.0.0/8 ::1 

設定後、

sudo fail2ban-client reload

を実行して設定完了。

設定終わって

  • Growi
  • Nextcloud

を操作して通常通りの作業が行えることを確認。

まとめ

今回、慌てずに作業できていたのが「どこからアクセスすれば安全か」を知っていたこと。

「頑固で融通が利かない門番」

を意図通りに動かすためには、門番が動く条件をきちっと動かす必要があるというお話でした。

Ubuntu24.04にufwとfail2banを設定(2026年の手順)

インターネット上にWebサーバーを公開すると、ものの数分で世界中から自動スキャンやブルートフォースアタック(総当たり攻撃)の嵐に見舞われます。
アクセス元のIPアドレスが固定されていれば接続元を絞れますが、「自宅や出先からリモートアクセスして作業する」場合、ファイアウォールの門を広く開けざるを得ません。

この記事では、そんな環境でもサーバーを鉄壁に守るため、UFW(ファイアウォール)とFail2ban(ログ監視型自動遮断ツール)を組み合わせ、不審者を検知した瞬間に「すべてのポートから永久追放(永久BAN)」する強力なネットワーク保護の設定手順を解説します。

動作環境

  • OS: Ubuntu 24.04 LTS

さっくりとした手順

  1. UFWの設定: 必要最低限のポート(SSH/Web)のみを許可
  2. Fail2banのインストール
  3. Fail2banの設定: UFWと連携し、不審なスキャンを一撃で永久BANする設定を追加

UFWの設定(SSHとWeb通信のみを有効化)

まずは不要なポートをすべて閉じ、必要な通信だけを通す基本的な防壁を作ります。

SSH接続の許可(過度な接続を制限)

単なる許可(allow)ではなく、短時間の連続アクセスを制限する limit を使うことで、ブルートフォースアタックの速度を鈍らせます。

sudo ufw limit proto tcp from any to any port 22

Web通信(HTTP / HTTPS)の許可

sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

UFWのログレベルを「中」に引き上げる

Ubuntuのデフォルト(low)では、UFWが自動で弾いた不審なアクセスのログが残りません。これではFail2banが検知できないため、ログレベルを medium に引き上げます。

sudo ufw logging medium

UFWを有効化して反映

※ この作業は何度やっても心臓に悪い作業です。

  • 別のターミナルを開けてロックアウトされないようにする
  • リモートコンソールできる環境にいる

を確実に確認してから作業を行います。

sudo ufw enable

注意: Command may disrupt existing ssh connections. Proceed with operation (y|n)? と聞かれたら、落ち着いて y を入力して続けます。

設定の確認

sudo ufw status verbose

以下のように、状態が「アクティブ(ロギング: on (medium))」になり、ルールが適用されていることを確認します。

状態: アクティブ
ロギング: on (medium)
Default: deny (incoming), allow (outgoing), deny (routed)
To                         Action      From
--                         ------      ----
22/tcp                     LIMIT       Anywhere                  
80/tcp                     ALLOW       Anywhere                  
443/tcp                    ALLOW       Anywhere                  
22/tcp (v6)                LIMIT       Anywhere (v6)             
80/tcp (v6)                ALLOW       Anywhere (v6)             
443/tcp (v6)               ALLOW       Anywhere (v6)             
  • 必須チェック:
    • ここで一度別ウィンドウを開き、サーバーへ新しいSSH接続ができるか必ずテストしてください。問題なければ sudo reboot で再起動し、再起動後も接続できることを確認します。

Fail2banのインストール

ログを監視して自動でブロックする必須ツールです。

sudo aptitude update && sudo aptitude install fail2ban

筆者の好みでaptitudeを用いています。

起動確認

systemctl status fail2ban.service

active (running) と表示されていればOKです。

3. Fail2banの設定(一撃永久BANの罠を張る)

① ufw-aggressive フィルターの作成

UFWの拒否ログ([UFW BLOCK])をFail2banに認識させるための判定ルールを作成します。
※Ubuntu 24.04環境の仕様に合わせ、フィルターファイル名は ufw-aggressive.confとします。

sudo tee /etc/fail2ban/filter.d/ufw-aggressive.conf > /dev/null << 'EOF'
[Definition]
failregex = \[UFW BLOCK\].+SRC=<HOST> DST
ignoreregex =
EOF

jail.local の作成・編集

Fail2banの挙動を定義するローカル設定ファイルを作成します。

以下のファイルを教義・信仰に沿ったエディタで編集します。

/etc/fail2ban/jail.local
[ufw]
enabled = true
filter = ufw-aggressive
action = iptables-allports
logpath = /var/log/ufw.log
maxretry = 1
bantime = -1
# ignoreipには、自分自身のローカルホストと、巻き添えを防ぎたい大手検索エンジン(Googlebotなど)のIP帯を指定します
ignoreip = 127.0.0.0/8 ::1 66.249.64.0/19

[sshd]
enabled = true
filter = sshd
mode = normal
port = 22
protocol = tcp
logpath = /var/log/auth.log
maxretry = 3
bantime = -1
ignoreip = 127.0.0.0/8 ::1
# ignoreipには、自分自身のローカルホストと、ある程度回線が固定されているIP帯を指定します。

この設定の「強さ」のポイント

  • bantime = -1:
    • 一度捕まった攻撃者は永久にBAN(解除なし)。
  • maxretry = 1(ufwセクション):
    • 許可していないポートへ1回でもスキャンを仕掛けてきたら即アウト。
  • action = iptables-allports:
    • 特定のポートだけでなく、すべてのポートからの通信を完全遮断します。
  • ignoreip = ... 66.249.64.0/19:
    • maxretry = 1 は強力すぎるため、Webサイトを巡回するGoogleのクローラー(Googlebot)を誤って永久追放しないよう、あらかじめホワイトリストで保護しています。

設定の反映と効果確認

設定が完了したら、Fail2banに読み込ませます。

sudo fail2ban-client reload

防御効果の確認

設定してわずか数分〜数十分。ログファイル(/var/log/fail2ban.log)を覗いてみると、その効果は一目瞭然です。

2026-05-17 07:14:26,476 fail2ban.filter         [1720]: INFO    [ufw] Found xxx.xxx.xxx.xxx
2026-05-17 07:14:26,623 fail2ban.actions        [1720]: NOTICE  [ufw] Ban xxx.xxx.xxx.xxx
2026-05-17 07:14:44,198 fail2ban.filter         [1720]: INFO    [ufw] Found yyy.yyy.yyy.yyy
2026-05-17 07:14:44,647 fail2ban.actions        [1720]: NOTICE  [ufw] Ban yyy.yyy.yyy.yyy

世界中から飛んでくる不審なアタックやスキャンを、Fail2banが次々と検知し、その場で息の根を止めて(Ban)くれているのが分かります。

現在の捕獲状況は、以下のコマンドでリアルタイムに確認できます。

sudo fail2ban-client status ufw

インターネットの荒波に晒されているサーバーであれば、10分も経たないうちに数十〜100以上の悪質なIPアドレスが Banned IP list: に積み上がっていきます。

もしうっかり味方や自分をBANしてしまったら?

万が一、設定ミスなどで必要なIPをBANしてしまった場合は、以下のコマンドで個別に救出(BAN解除)が可能です。

sudo fail2ban-client set ufw unbanip <解除したいIPアドレス>

まとめ

固定IPを持たない環境であっても、「UFWで怪しい動きを検知し、Fail2banですべてのポートを即座に塞ぐ」という二段構えを構築することで、驚くほど強固なサーバーへと進化させることができます。

筆者はかれこれ4年ほどvps運用を続けている中、重篤な攻撃に晒されていないのは基本であるこのufwとfail2banのおかげ。

休息も慈悲も与えぬ。何があってもだ。
No rest, no mercy. No matter what.

のレベルで攻撃者にいかなる躊躇も容赦もしないのがVPSサーバの基本です。

失敗記録:LVM物理ボリューム縮小とディスク切り詰めの挑戦。

注意事項

  • これは失敗した手順です。
  • なのでやってはいけないやつです。
  • あくまでも私の失敗したときの記録として残します。

何をやりたかったのか?

「KVMで作成したディスク(LVM)を500→200程度に切り詰めようとしたところ失敗した」。

環境

  • ホスト
    • Rocky Linux 8.6
    • KVM
  • ゲスト
    • Rocy Linux 9.7
    • シックボリュームで構築

1. ゲストOS内でのデータ整理

まず、ディスク容量を空けるために /home を削除・再作成し、使用量を削減しました。

  • 状態: 物理ボリューム(PV)500GB に対し、中身の合計(Root+Home+Swap)を 170GB 程度まで圧縮。

2. データの「前詰め」作業(pvmove)

LVMの「末尾」にあるデータを物理的にディスクの「先頭」へ移動させました。

  • コマンド: sudo pvmove --alloc anywhere /dev/vda2
  • 結果: pvdisplay -m にて、使用中セグメントが 0 ~ 73153 PE(約180GB圏内)に固まり、それ以降が FREE になったことを確認。

3. PVリサイズの試行(pvresize)

管理情報を 180GB に書き換えようと試みました。

  • コマンド:
sudo pvresize --setphysicalvolumesize 180G /dev/vda2
  • 結果: cannot resize to 46079 extents as later ones are allocated により失敗。
  • 考察: LVMの内部メタデータや、目に見えない微細なフラグが末尾に残っていた可能性。

4. ホスト側での物理コピー(dd による強行突破)

「データは前に寄せた」という事実に基づき、ホスト側から物理的に180GB 分だけを切り出す作戦を敢行。

  • 手順:
  1. ホスト側で新LV(180GB)を作成。
  2. dd コマンドで旧LVから 180GB 分を抽出コピー。
  3. lvrename を使い、VMが参照するターゲットを 180GB の新ディスクにすり替え。

5. 最終結果

  • 起動: 成功。
  • ログイン: 失敗。
  • 状況: virsh console 等で応答なし。
  • 結論: LVMおよびファイルシステムの整合性において、180GB という境界線で「管理情報の断裂」が発生。

教訓

  • LVMの末尾は聖域: pvmove でデータを寄せても、LVM自身の管理領域(Metadata Area)の整合性を保ったまま物理サイズを削るのは、OS稼働中や単純な dd では極めて困難である。
  • 切り詰めるなら「外から」より「中から」: 今回のように外部から dd で削る手法は、パーティションテーブルとLVMヘッダの整合性が 1 バイトでも狂うとシステム停止に直結する。

やはり、この手のリサイズは「新たにサーバを作成し(リサイズした上で)データを流し込む」という地道な主だが一番です。

RHEL9系でMySQLのrootパスワードを忘れてしまったときの再設定メモ

本手順は、MySQLのrootパスワードを紛失し、通常の方法でログインできなくなった場合に実施する「非常時用」のリカバリ手順です。

作業の前に

この作業は本来ならばあってはならない作業です。パスワード失念はセキュリティ事故の筆頭。ましてやWebシステムの神に等しいDBを司る通行証が消えた。なので、

  • 事前にアカウント情報を記したファイルを保存する
  • 適切な場所、適切なアクセス権で保管する

は必須ですが

往々にしてこの事故は起きます。なので、「マジで起きてしまった。取り敢えずの迅速な復旧」を望んでいる(つまり今回の私のような)方へのメモとなります。

この手順が笑える状況

  • 構築中
  • 検証作業中

のいずれかのみ。本番稼働中だったらまず笑えませんし、許可を得るための政治力・交渉力は甚大なものになります。筆者は「構築中」のパターンです。

環境

  • RHEL9系(RockyLinux9系)
  • MySQL 8

注記:RHEL 9系での注意点

  • 従来の mysqld_safe コマンドは廃止されているため、systemctl set-environment を使用して起動オプションを制御します。
  • MySQL 8.0以降は skip-grant-tables モード中でも FLUSH PRIVILEGES を実行しない限り ALTER USER コマンドが受け付けられない仕様となっています。

作業影響

  • 作業中にMySQLが止まる

これに尽きますが、「起きてしまったことは仕方ない。潔くサパッと止めてサクッと終わらせる」を心がけます。

さっくりとした手順

  1. MySQLサービスを停止します。
  2. 認証をスキップするための環境変数をセットします。
  3. 認証スキップ状態でMySQLを起動します。
  4. rootパスワードのリセットを行います。
  5. 認証を有効化してMySQLサービスを起動します。
  6. リセットされたパスワードでMySQLに入れることを確認します。

MySQLサービスの停止

  • MySQL停止
sudo systemctl stop mysqld
  • MySQL停止確認
systemctl status mysld

inactive(dead)を確認します。

認証をスキップするための環境変数をセットします。

  • 認証スキップのオプションを環境変数に一時セット
systemctl set-environment MYSQLD_OPTS="--skip-grant-tables --skip-networking"

(--skip-networkingを付けることで、作業中の外部接続を遮断し安全を確保します。)

認証スキップ状態でMySQLを起動します。

sudo systemctl start mysqld
  • MySQL起動確認
systemctl status mysld

active(running)を確認します。

rootパスワードのリセットを行います。

認証がスキップされている状態でログインし、権限テーブルを強制ロードしてからパスワードを書き換えます。

  • パスワードなしでrootログイン
mysql -u root

これでログインできたらひとまず成功です。ここからはSQL捜査を行います。

  • 権限テーブルをリロード (ALTER USER を実行可能にするために必須)
FLUSH PRIVILEGES;
  • パスワードの変更を実施します。
ALTER USER 'root'@'localhost' IDENTIFIED BY 'your_strong_password';
  • 変更を確定して終了
FLUSH PRIVILEGES;
exit

認証を有効化してMySQLサービスを起動します。

一時的な環境変数を削除し、通常の認証が有効な状態で再起動します。

  • MySQLを一旦停止
systemctl stop mysqld
  • MySQL停止確認
systemctl status mysld

inactive(dead)を確認します。

  • セットした環境変数を必ず解除 (これを忘れると誰でも入れる状態が続くため重要)
systemctl unset-environment MYSQLD_OPTS
  • 通常どおりサービスを起動
sudo systemctl start mysqld
  • MySQL起動確認
systemctl status mysld

リセットされたパスワードでMySQLに入れることを確認します。

mysql -u root -p

新しいパスワードでログインできたことを確認します。

まとめ

「起きないことが第一」とは言いますが、本当にくだらない理由でこういう事象は発生します。なので

  • 起きてしまったことは潔く認める
  • そこから原状の復旧を目指す。
  • 責任の追及とか誰がイモを引くかはその後で考える

の三段活用。“全裸大佐”が言う

過ちを気に病むことはない。ただ認めて、次の糧にすればいい

という「大人の特権」をフル活用しましょう。

レポジトリ一覧を確認するためのワンライナー。

RHEL系のサーバ設定で結構重要になってくる「サーバがどのレポジトリを使っているか?

これを簡単に調べるワンライナーです。

環境

Rocky Linux 9.10

ワンライナー

echo -e "|Repo-id|Repo-name|\n|---|---|" && dnf repolist -q | awk '$1 != "repo" && NF > 1 {id=$1; $1=""; sub(/^[ \t]+/, ""); print "|" id "|" $0 "|"}'

出力結果

Repo-idRepo-name
appstreamRocky Linux 9 - AppStream
baseosRocky Linux 9 - BaseOS
epelExtra Packages for Enterprise Linux 9 - x86_64
epel-cisco-openh264Extra Packages for Enterprise Linux 9 openh264 (From Cisco) - x86_64
extrasRocky Linux 9 - Extras
remi-modularRemi's Modular repository for Enterprise Linux 9 - x86_64
remi-safeSafe Remi's RPM repository for Enterprise Linux 9 - x86_64
zabbixZabbix Official Repository - x86_64
zabbix-non-supportedZabbix Official Repository (non-supported) - x86_64
zabbix-toolsZabbix Official Repository (tools) - x86_64

と、このままマークダウン記事として貼り付けられるようになっています。

仕組み

1. echo -e "|Repo-id|Repo-name|\n|---|---|"

Markdown形式の表の「見出し」を無理やり作っている部分です。

  • echo -e: 「バックスラッシュ記法」を有効にするオプションです。
  • \n: これがあることで、1行目の見出しと2行目の区切り線(|---|---|)の間で改行されます。

2. dnf repolist -q

システムのレポジトリ一覧を取得するコマンドです。

  • repolist: 有効なレポジトリの ID と名前を表示します。
  • -q (quiet): 「メタデータの期限切れ確認」などの余計なメッセージを非表示にし、純粋なリストの結果だけを出力します。

3. awk '$1 != "repo" && NF > 1 { ... }'

受け取ったテキストを1行ずつ加工しています。

要素意味
$1 != "repo"1番目の項目(Repo-id)が "repo" という文字列ではない行だけを処理する(見出し除外)。
NF > 1項目の数(Number of Fields)が1つより多い行=空行などを除外。
id=$11番目の項目(Repo-id)を変数 id にキープ。
$1=""1番目の項目をデータから消去。これで残りの $0(行全体)が Repo-name だけになります。
sub(/^[ \t]+/, "")1列目を消した後に残ってしまう「先頭の空白」を削除して綺麗にします。
print "|"...最後に、Markdownの枠組み | で囲って出力します。

まとめ

  1. echo で表の見た目を作り、
  2. dnf でデータを静かに(-q)呼び出し、
  3. awk で「いらない行(repo)」を捨てつつ、1列目(ID)とそれ以降(Name)をバラバラにして | で挟み直す。

という流れになっています。

iPhoneの画像データを自宅内のNextcloudに一括送信。

連休で家にいる中でないとできない作業でした。

事前準備

  1. Nextcloud(宅内)のセットアップができている。
  2. NextcloudサーバとNASをNFSでマウントしている。(書き取り可能になっている)
  3. iPhoneにNextcloudアプリを入れ、同じNW内にあるNextcloudと連携が取れている。

2,019年からのiPhoneデータ。相当に苦労しました。

自動アップロード設定画面を開く

Nextcloudアプリの自動アップロード機能を有効化するしていきます。

Nextcloudアプリ右下「その他(…)」 → 「設定」 → 「自動アップロード」

の画面を開きます。

カメラロールの自動アップロードを有効化

iPhoneの写真アプリ内の画像・動画をNextcloudへ自動送信するため「カメラロールをアップロード」をオンにします。

必要に応じて「新しい写真のみ」「すべての写真」などの範囲を選択します。(筆者は全ての写真を選択)

保存先フォルダを指定(重要)

誤ったフォルダを選ぶと整理が崩れるため、最も重要なステップです。

  1. 「リモートフォルダ」をタップ
  2. 事前に作成した NAS側の外部ストレージフォルダ を選択しました。

必要に応じて「カメラロール」専用フォルダをNextcloud側で作っておきましょう。

同期条件を設定して安定性を確保

筆者は「Wi-Fi使用時のみ」をオンにしました。というのも、このNextcloudは完全に宅内で運用するからです。

初回のみ:自動ロックの解除

これがハマりポイントでした。

iPhoneの

設定 > 画面表示と明るさ > 自動ロック

に選び「なし」に変更。その上で先のiPhoneのNextcloudアプリを開きっぱなしにしておきます。

可能であればワイヤレス充電でつけっぱなしにしておくと良いでしょう。

参考までに、宅内無線LAN環境、45,000枚ほどの画像の転送に10時間ほどかかりました。

※この作業が終わったら自動ロックの設定を元に戻すのを忘れないようにしましょう。

Ubuntu26.04でNextcloudをインストール。(Apache/MySQL/PHP8.5-FPM)

概要

  • Ubuntu 26.04
  • Apache

をインストールした状況で、「PHP-FPM」を稼働させた上でNextcloudをインストールしていくためのメモです。

この手順はゴールではなくスタートです

  • 本手順は「構築した」という始まりに過ぎません。
  • 「動く」手順ではありますが「初期設定」は以下が絡むため、これ以上に厄介です。
    • 初期設定
    • メール設定
    • redis設定
    • 各種セキュリティ
    • ログ設定
    • アプリのチューニング…
  • インターネット環境だろうとローカルだろうと、「データを取り扱う器」を構築した以上、データ保全という義務と責任がこれから重くのしかかります。
  • 本手順で「めんどくさい」と思った方は素直にOneDrive/GoogleDrive/Dropboxをお使いください。その方があなたもデータも幸せです。(実際、筆者が使ってるGoogle AI proなら月額2900円で5TBも利用可能です!)

再掲:Nextcloudというかサービス運営者に必要なのは「資格」ではなく「責任」です。

Ubuntu24.04のインストール時にも言いましたが、この理論は未だに私の中では真理です。

巷では「○○の資格があればこの運用は」的な話があるようですが:そもそも運用の方針を取り違えていると思います。

「救急戦隊ゴーゴーファイブ」に曰く

「資格? 馬鹿野郎、誰もそんなもの持ってねぇんだ! いいか、あるのは責任だけだ。戦う責任! あの子を傷つけちまった責任! そいつを果たすには、この地球を守るしかねぇんだ!」

私が言いたいことはこれに尽きます。

なぜ mod_php ではなく PHP-FPM を使うのか?

パフォーマンスとリソース効率を向上させるためです。

従来のmod_phpでは、PHPがApacheの全プロセスに組み込まれるため、画像ファイルのリクエストのようなPHPが不要な処理でもメモリを消費し、無駄が多くなりがちでした。

一方、PHP-FPMはPHPの処理をApacheから完全に独立させた専門のプロセスとして管理します。ApacheはPHPが必要なリクエストだけをPHP-FPMに中継するため、サーバー全体の動作が軽量かつ高速になります。

前提

  • OS: Ubuntu 26.04 LTS
  • → SSH接続できること。
  • ※root権限を持っていること。
  • この権限を持っていない場合、ここから先の設定はできません。
  • データベース: MySQL 8.0
  • Webサーバー: Apache 2.4
  • 実行ユーザーはwww-data
  • ホームディレクトリを /home/www-dataにしています。自分の環境に合わせてください。
  • ドメインとSSL/TLS証明書: 準備済みであること

筆者の好みでaptitudeを用いています。必要に応じてaptをご利用ください。

さっくりとはならない手順

  1. パッケージをインストールしていきます。
  2. PHP-FPMの設定を行います。
  3. PHPのパフォーマンス設定を行います。
  4. MySQLでDB設定を行います。
  5. NextcloudのDBを設定します。
  6. Apacheバーチャルホストの設定を行います。
  7. バーチャルホストの設定を有効化します。
  8. 設定の有効化とサービスの再起動を実施します。
  9. Webブラウザで初期インストールを行います。

必要なパッケージのインストール

PHP本体、PHP-FPM、Nextcloudが必要とする各種PHPモジュールをインストールします。

sudo aptitude install php php-fpm php-opcache php-pdo php-bcmath php-calendar php-ctype php-fileinfo php-ftp php-gd php-intl php-json php-mbstring php-mysql php-posix php-readline php-sockets php-bz2 php-tokenizer php-zip php-curl php-iconv php-xml php-imagick php-gmp php-apcu memcached

バージョンを確認します。

php -v

表示例

PHP 8.5.4 (cli) (built: Apr  1 2026 09:36:11) (NTS)
Copyright (c) The PHP Group
Built by Ubuntu
Zend Engine v4.5.4, Copyright (c) Zend Technologies
    with Zend OPcache v8.5.4, Copyright (c), by Zend Technologies

※Ubuntu 26.04はリポジトリを追加するまでもなくPHP8.5がインストールされます。※

PHP-FPMとApacheの連携設定

従来の mod_php を無効化し、PHP-FPMとの通信に必要な proxy_fcgi モジュールなどを有効化します。

  • mod_phpを無効化(もしインストールされていれば)
sudo a2dismod php
  • 必要なモジュールを有効化
sudo a2enmod proxy_fcgi setenvif header rewrite

PHPのパフォーマンス設定

Nextcloudのパフォーマンス向上のため、PHPのメモリ制限、OPcache、APCuを設定します。

  • php.ini の設定 (memory_limit)
sudo sed -i 's/memory_limit = .*/memory_limit = 512M/g' /etc/php//fpm/php.ini
  • OPcacheとAPCuの有効化

Nextcloud推奨の設定値を /etc/php//mods-available/ に作成・適用します。

  • OPcache設定
cat <<- __EOF__ | sudo tee /etc/php//mods-available/opcache.ini
opcache.enable=1
opcache.enable_cli=1
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.memory_consumption=128
opcache.save_comments=1
opcache.revalidate_freq=1
__EOF__
  • APCu設定
cat <<- __EOF__ | sudo tee /etc/php//mods-available/apcu.ini
[apcu]
apc.enabled=1 apc.shm_size=32M apc.ttl=7200 apc.enable_cli=1 apc.serializer=php __EOF__
  •  設定の有効化
sudo phpenmod opcache apcu

このphpenmodがハマりポイントでした。従来の ln -sではなく、専用コマンドを用いることでfpm / cli / apache-mod でも安定した運用が可能になります。

データベースの作成

Nextcloudが使用するMySQLデータベースと専用ユーザーを作成します。

  • MySQLにrootでログイン
mysql -u root -p

以下のSQLコマンドを実行します。YOUR_STRONG_PASSWORD は必ず強固なパスワードに変更してください。

CREATE DATABASE IF NOT EXISTS nextcloud CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
CREATE USER 'nextcloud'@'localhost' IDENTIFIED BY 'YOUR_STRONG_PASSWORD';
GRANT ALL PRIVILEGES ON nextcloud.* TO 'nextcloud'@'localhost';
FLUSH PRIVILEGES;
EXIT;

Nextcloudプログラムの配置

Nextcloud本体をダウンロードし、Webサーバーからアクセスできる場所に配置します。

  • 作業ディレクトリへ移動
cd /tmp && pwd

任意のディレクトリを指定します。

  • 最新版をダウンロードして展開
wget https://download.nextcloud.com/server/releases/latest.zip
unzip latest.zip
  • 展開したファイル一式をWeb公開用ディレクトリに移動
sudo mv nextcloud /home/www-data/
  • 所有者をWebサーバーの実行ユーザーに変更
sudo chown -R www-data:www-data /home/www-data/nextcloud

Apacheバーチャルホストの設定

Nextcloud用のApache設定ファイルを作成します。ここでPHP-FPMとの連携設定を組み込みます。

  • ログディレクトリの作成
sudo mkdir /var/log/nextcloud
  • ログディレクトリをwww-dataに修正。

これは、後のメンテナンス性を高めるためです。

sudo chown www-data:www-data /var/log/nextcloud
  • 設定ファイルの作成
/etc/apache2/sites-available/nextcloud.conf

を、teeで一気通貫で作ります。

# 【】内はご自身の環境に合わせてください
cat <<- __EOF__ | sudo tee /etc/apache2/sites-available/nextcloud.conf
<VirtualHost *:80>
    ServerName 【hoge.example.com】
    RewriteEngine On
    RewriteCond %{HTTPS} off
    RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</VirtualHost>

<VirtualHost *:443>
    ServerName 【hoge.example.com】
    DocumentRoot 【/home/www-data/nextcloud】

    CustomLog /var/log/nextcloud/nextcloud_access.log combined
    ErrorLog /var/log/nextcloud/nextcloud_error.log

    <Directory 【/home/www-data/nextcloud】>
        Options -MultiViews
        AllowOverride All
        Require all granted
    </Directory>

    # PHP-FPM連携設定
    <FilesMatch \.php$>
        # SetHandlerで、phpファイルのリクエストをPHP-FPMのソケットに渡す
        SetHandler "proxy:unix:/var/run/php/php8.5-fpm.sock|fcgi://localhost/"
    </FilesMatch>

    # --- SSL設定 ---
    SSLEngine on
    Protocols h2 http/1.1
    SSLCertificateFile 【/etc/certs/hoge.example.com.crt】
    SSLCertificateKeyFile 【/etc/private/hoge.example.com.key】
    # 中間証明書が別に提供されている場合はこちらを有効化
    # SSLCACertificateFile 【/etc/certs/hoge.example.com.CA.crt】

    # --- 推奨SSL/TLS設定 ---
    SSLProtocol             all -SSLv3 -TLSv1 -TLSv1.1
    SSLCipherSuite          ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
    SSLHonorCipherOrder     on
    SSLCompression          off
    SSLSessionTickets       off

    # --- セキュリティヘッダー ---
    Header always set Strict-Transport-Security "max-age=15552000; includeSubDomains"
    Header always set Referrer-Policy "no-referrer"
    Header always set X-Content-Type-Options "nosniff"
    Header always set X-Frame-Options "SAMEORIGIN"
    Header always set X-Permitted-Cross-Domain-Policies "none"
</VirtualHost>
__EOF__

※ こちらもMod_Securityによる連携は可能です。

設定の有効化とサービスの再起動

  • 作成したサイト設定を有効化
sudo a2ensite nextcloud.conf
  • 構文チェック
sudo apache2ctl configtest

Syntax OK と表示されることを確認

  • fpm/apacheサービスを再起動
sudo systemctl restart php8.5-fpm.service
sudo systemctl restart apache2.service
  • fpm/apache再起動確認
systemctl status php8.5-fpm.service
systemctl status apache2.service

active (running)と表示されていれば正常です。

Webブラウザでのセットアップ

最後に、Webブラウザで https://【設定したドメイン】 にアクセスし、画面の指示に従ってNextcloudの初期設定を完了させます。

  • 管理者ユーザーのユーザー名とパスワードを入力
  • データベース情報を入力
  • データベースのユーザー名: nextcloud
  • データベースのパスワード: データベースのパスワード
  • データベース名: nextcloud
  • データベースのホスト名: localhost (または localhost:3306)

これで、PHP-FPM上で動作するNextcloud環境の構築が完了します。

Page 1 of 62

Powered by WordPress & Theme by Anders Norén