タグ: Linux Page 18 of 25

ChatGPTによるシェルスクリプト(パッケージの自動更新)の問題点と修正。

この記事の更に続きとなります。

以前作成したスクリプトに問題点があったので修正します。

問題点詳細

アップデートするパッケージによってはコンフィグを残すか否かをアップデート中に聞いてきます。

このプロンプトが表示されるとスクリプトは停止。処理を手動で中断して実行する羽目になります。

これを回避するため、以下のように修正いたしました。

修正差分

-    aptitude -y full-upgrade | tee $upgraded_packages >/dev/null
+    DEBIAN_FRONTEND=noninteractive aptitude -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" full-upgrade | tee $upgraded_packages >/dev/null

修正した処理の詳細

※chatGPTの回答なので信頼性は各自ご判断ください。

  1. DEBIAN_FRONTEND=noninteractive : DEBIAN_FRONTEND 環境変数に noninteractive を設定し、aptitude コマンドを非対話モードで実行します。これにより、パッケージの更新時に表示される確認メッセージを自動的に承認することができます。
  2. aptitude -y : aptitude コマンドに -y オプションを指定して、すべての質問に対して自動的に yes を返答するようにします。
  3. -o Dpkg::Options::="--force-confdef" : dpkg コマンドに --force-confdef オプションを指定して、パッケージがインストールされる際に必要なデフォルトの設定値を使用するように指定します。
  4. -o Dpkg::Options::="--force-confold" : dpkg コマンドに --force-confold オプションを指定して、既存のコンフィグファイルを保持するように指定します。
  5. full-upgrade : aptitude コマンドの full-upgrade オプションを指定して、すべてのパッケージを最新バージョンに更新します。
  6. | tee $upgraded_packages >/dev/null : 更新されたパッケージのリストを一時ファイルに保存します。パイプライン演算子 | を使用して、aptitude コマンドの出力を tee コマンドに送信し、同時に一時ファイルに書き込むように指定します。>/dev/null を追加することで、標準出力を無効化し、結果を表示しないようにします。

修正後のスクリプト全文

#!/bin/bash

# インストールされているパッケージの一覧を取得して別ファイルに出力します。
now1=$(date +%Y%m%d)
dpkg-query -W > installed_packages_$now1.txt

# aptitude updateを行います。
aptitude update

# updateの結果:
if aptitude search '~U' | grep -q '^i'; then
    # 対象パッケージ,変更前バージョン,変更後のバージョン を記入した日付付きのファイルを作成。
    now1=$(date +%Y%m%d)
    upgraded_packages=$(mktemp)

    # パッケージのキャッシュをクリアした上でパッケージアップグレードを実施。
    aptitude clean
    DEBIAN_FRONTEND=noninteractive aptitude -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" full-upgrade | tee $upgraded_packages >/dev/null

    # パッケージ一覧からの差分を別ファイルで作成。(実行日の日付を付与)
    new_packages=$(mktemp)
    dpkg-query -W > $new_packages
    diff -u installed_packages_$now1.txt $new_packages > package_diff_$now1.txt

   # 新しいパッケージ名を取得
   DIFF_FILE="package_diff_$now1.txt"
   NEW_PACKAGES=$(grep -E "^\+[^+]" $DIFF_FILE | awk '{print $1}' | cut -c 2-)

   # 変更されたパッケージの数と、新しいバージョンのパッケージ名のリストを表示
   UPDATED_PACKAGES=$(echo "$NEW_PACKAGES" | wc -l)
   echo "$UPDATED_PACKAGES 件のパッケージに変更がありました。以下のパッケージが更新されました:"
   echo "$NEW_PACKAGES"

    # checkrestartを実行して結果を取得
    now=$(date +%Y%m%d)
    checkrestart_output=$(checkrestart)

    # サービスを再起動する必要のあるプロセスを抽出してファイルに出力
    restart_services=$(echo "$checkrestart_output" | awk '/^(These are the systemd services|These are the initd scripts)/{flag=1;next}/^$/{flag=0}flag' | awk '{print $NF}' | sort -u)

    if [[ -n "$restart_services" ]]; then
        # ファイル名に日付を追加
        now2=$(date +%Y%m%d)
        filename="restart_services_$now2.txt"

        echo "以下のサービスを再起動してください:" >> "$filename"
        echo "$checkrestart_output" | awk '/^(These are the systemd services|These are the initd scripts)/{flag=1;next}/^$/{flag=0}flag' | grep -v "restart$" >> "$filename"
        echo "$checkrestart_output" | awk '/^(These are the systemd services|These are the initd scripts)/{flag=1;next}/^$/{flag=0}flag' | grep "restart$" >> "$filename"

        echo "以下のサービスを再起動してください:"
        echo "$checkrestart_output" | awk '/^(These are the systemd services|These are the initd scripts)/{flag=1;next}/^$/{flag=0}flag' | grep -v "restart$"
        echo "$checkrestart_output" | awk '/^(These are the systemd services|These are the initd scripts)/{flag=1;next}/^$/{flag=0}flag' | grep "restart$"


    else
        echo "再起動するサービスはありません"
    fi
fi

これでコンフィグを残すか否かのアップデートでも停止することなく動きましたが、肝心の

  • どのパッケージがコンフィグを残すか否か求められたか
  • その差分は何か

などが不明。なので、改善点はまだまだです。

ChatGPTによるシェルスクリプト。(外為レートの表示と記録)

概要

定額制のLightsailを利用しているとはいえ、ドルベースで請求されるので相場観は気になる数字です。

そこで、ドル円の相場をスクリプトベースで取得することにしました。

参考:

【Linux】ドル円為替情報をwgetコマンドで取得する
https://www.mtioutput.com/entry/fx-info-wget

前提

  • Ubuntu 20.04で動作を確認しています。
  • 参考先にも書いてありますが、不正アクセスと間違われないよう実行頻度などはお気をつけください。
  • また、相場観を知るためだけにこのスクリプトを作成したことを前もって強調いたします。

ChatGPTへのお伺い

以下のように訊いてみました。

以下を満たすシェルスクリプトを書いてください。

1. 以下のコマンドを実行する
wget -O - -U "" http://www.gaitameonline.com/rateaj/getrate 2> /dev/null

2. コマンド実行時の日時を取得する。

3.USDJPYを含む以下を抽出する。(以下は1のコマンド抜粋)
{"high":"132.86","open":"132.86","bid":"132.54","currencyPairCode":"USDJPY","ask":"132.55","low":"132.49"}

4. 以下のように改行して並び替える。この時、:以外の記号を取り除く。
currencyPairCode:USDJPY
open:132.86
high:132.86
bid:132.68
ask:132.69
low:132.50

4. 次のように言葉を変えて表示する。
yyyy/mm/dd hh:mm 現在のドル円相場は以下の通りです。

open:132.86
high:132.86
bid:132.68
ask:132.69
low:132.50

作成結果

そこからいくつかの質疑応答や機能追加、そして手動による修正をを繰り返しました。

最終的にできあがったのがこちらです。

  • fx_info.sh
#!/bin/bash

# fx_info.shは、外国為替相場の情報を取得し、現在のレートを表示するシェルスクリプトです。
# USD/JPYをデフォルトの通貨ペアとして使用しますが、他の通貨ペアにも簡単に変更できます。
# また、同じディレクトリに相場.csvを作成。相場の履歴を追うことも可能です。
# 参照するURLは外為オンラインの情報です。

#### 変数定義ここから #####
# 通貨ペアを変数化する。
# USDJPY=ドル円 
# EURUSD=ユーロドル
# など、以下のURLに沿ったものを指定
currency_pair="USDJPY"
#### 変数定義ここまで ####

# 1. コマンドを実行してデータを取得し、標準エラー出力を/dev/nullにリダイレクトする。
data=$(wget -O - -U "" http://www.gaitameonline.com/rateaj/getrate 2> /dev/null)

# 2. 現在日時を取得する。
date=$(date '+%Y/%m/%d')
time=$(date '+%H:%M')

# 3. 指定された通貨ペアの情報を取得する。
currency_data=$(echo "$data" | grep -o '{"high":"[0-9]*\.[0-9]*","open":"[0-9]*\.[0-9]*","bid":"[0-9]*\.[0-9]*","currencyPairCode":"'$currency_pair'","ask":"[0-9]*\.[0-9]*","low":"[0-9]*\.[0-9]*"}')

# 4. 必要な情報を取り出し、並び替える。
currencyPairCode=$(echo "$currency_data" | grep -o 'currencyPairCode":"'$currency_pair'"' | cut -d ':' -f 2 | tr -d '"')
open=$(echo "$currency_data" | grep -o 'open":"[0-9]*\.[0-9]*"' | cut -d ':' -f 2 | tr -d '"')
high=$(echo "$currency_data" | grep -o 'high":"[0-9]*\.[0-9]*"' | cut -d ':' -f 2 | tr -d '"')
bid=$(echo "$currency_data" | grep -o 'bid":"[0-9]*\.[0-9]*"' | cut -d ':' -f 2 | tr -d '"')
ask=$(echo "$currency_data" | grep -o 'ask":"[0-9]*\.[0-9]*"' | cut -d ':' -f 2 | tr -d '"')
low=$(echo "$currency_data" | grep -o 'low":"[0-9]*\.[0-9]*"' | cut -d ':' -f 2 | tr -d '"')

# 5. 結果を表示する。
echo "$date $time 現在の${currencyPairCode}相場は以下の通りです。"
echo "始値:$open"
echo "高値:$high"
echo "売値:$bid"
echo "買値:$ask"
echo "安値:$low"

## 以下はファイルに書き込むための処理。

# ファイル名を${currencyPairCode}.csvとする。
filename="${currencyPairCode}.csv"

# ファイルが存在しない場合は、ファイルにヘッダーを書き込む。
if [ ! -e "$filename" ]; then
  echo "実行日,実行時刻,相場,始値,高値,売値,買値,安値" >> "$filename"
fi

# ファイルにデータを追記する。
echo "${date},${time},${currencyPairCode},${open},${high},${bid},${ask},${low}" >> "$filename"

スクリプト作成後の処理

chmod +x fx_info.sh

スクリプト実行結果

  • スクリプト実行
./fx_info.sh
  • 実行結果
2023/03/30 13:41 現在のUSDJPY相場は以下の通りです。
始値:132.86
高値:132.86
売値:132.56
買値:132.57
安値:132.45
  • 作成されたファイル閲覧
cat USDJPY.csv
  • ファイル内容
実行日,実行時刻,相場,始値,高値,売値,買値,安値
2023/03/30,13:29,USDJPY,132.86,132.86,132.60,132.61,132.45
2023/03/30,13:31,USDJPY,132.86,132.86,132.60,132.61,132.45
2023/03/30,13:41,USDJPY,132.86,132.86,132.56,132.57,132.45

これで、ブラウザを開くことなく情報を表示することができるようになりました。

Redmine脆弱性対応。(4.2.x→4.2.10へのバージョンアップ)

これを行うきっかけ

こちらの記事:

https://blog.redmine.jp/articles/redmine-security-scanner/

で運用中のRedmineに既知の脆弱性がないかをチェックできるWebサービスの存在を知ったからでした。

で、現在のRedmineのURLを調べてみたら

E。

何度見てもEです。多少自信はあったので、この結果はその鼻っ柱を打ち砕くものでした。

そこで、現在運用中のRedmineのバージョンアップを図り脆弱性に対応します。

環境

  • Ubuntu 20.04 (AWS Lightsailで稼働中)
  • 動かしていたRedmine:4.2.9
  • Apache 2.4 / mod-passangerでRubyアプリを使用(Ruby 2.7系)
  • MySQL 8.0.3

作業に備えての前提

  • 本手順では「使用するDBの削除」を伴います。作業の際には慎重に行って下さい。
  • Webサービスを止める/何も入っていないRedmineが途中でできるため、ユーザアクセスができない状況が発生します。
  • また、筆者環境はfiles配下をクラウドストレージにマウントしています。手順は自身の環境に合わせてください。

さっくりとした手順

  1. スナップショットのバックアップ
  2. DBのバックアップ
  3. redmineのディレクトリを一度mvでリネームしてバックアップ。
  4. apache停止
  5. redmineのDBを消す。
  6. redmineのDBを新たに作る。(ユーザは全て権限があるので問題なし)
  7. apache再開
  8. ディレクトリを再作成し、新しい空のRedmineを作る。
  9. themesとpluginを再配置。filesのシンボリックリンクを貼り替える。
  10. themesとpluginを再配置した状態でDBマイグレーション。
  11. DBリストア。

データバックアップ

万一に備え、AWSからインスタンスのスナップショットを取得します。

mysqlによるDBバックアップ

mysqldump -h localhost -u redmine -p --no-tablespaces --single-transaction redmine > redmine_backup.$(date +%Y%m%d).sql
# DB名やDBユーザは自信の環境に合わせます

データ退避

cd /var/lib
# Redmineが格納されているディレクトリの親ディレクトリに移動します

ls -ld redmine
# 退避対象のディレクトリがあることを確認します

sudo mv redmine redmine_$(date +%Y%m%d)

ls -ld redmine_$(date +%Y%m%d)

apache停止

ここでWebサービスを停止するのは、DBを削除するためです。

systemctl status apache2.service

sudo systemctl stop apache2.service

systemctl status apache2.service

DB削除

慎重に行って下さい。

sudo mysql -u root -p
show databases;
/* redmineのDBがあることを確認 */

drop database redmine;

show databases;
/* redmineのDBがないことを確認 */

CREATE DATABASE redmine character set utf8mb4;

show databases;
/* redmineのDBがあることを確認 */

exit

Redmine再インストール

ソースダウンロード

sudo mkdir /var/lib/redmine

sudo chown -R www-data:www-data /var/lib/redmine

sudo -u www-data svn co https://svn.redmine.org/redmine/branches/4.2-stable /var/lib/redmine
# 4.2系の最新安定版をダウンロードします

退避させたディレクトリからconfigファイルコピー

sudo cp -pi /var/lib/redmine_$(date +%Y%m%d)/config/database.yml /var/lib/redmine/config/database.yml

cat /var/lib/redmine/config/database.yml
#中身確認

sudo cp -pi /var/lib/redmine_$(date +%Y%m%d)/config/configuration.yml /var/lib/redmine/config/configuration.yml

cat /var/lib/redmine/config/configuration.yml
#中身確認

Redmineインストール

cd /var/lib/redmine

sudo -u www-data bundle install --without development test --path vendor/bundle

sudo -u www-data bundle exec rake generate_secret_token

sudo -u www-data RAILS_ENV=production bundle exec rake db:migrate

sudo -u www-data RAILS_ENV=production REDMINE_LANG=ja bundle exec rake redmine:load_default_data

apache再開

systemctl status apache2.service

sudo systemctl start apache2.service

systemctl status apache2.service

再開後の仮パスワード作成

対象のRedmineにアクセスします。

IDとパスワードがadmin / admin に戻っている状態のため、仮パスワードを発行します。

退避したディレクトリからデータを再配置

cd /var/lib/redmine_$(date +%Y%m%d)/plugins

sudo cp -pir ./* /var/lib/redmine/plugins/
# プラグイン一式をコピーします

cd /var/lib/redmine_$(date +%Y%m%d)/public/themes

sudo cp -pir ./* /var/lib/redmine/public/themes/
# テーマ一式をコピーします

シンボリックリンク貼り替え

これは筆者の環境が

  • logディレクトリとfilesディレクトリを別の場所にリンクを張っているための措置です。
  • それ以外の場合は上述した /var/lib/redmine_$(date +%Y%m%d)からfilesやlogをコピーして下さい。
cd /var/lib/redmine

sudo rm -rf files

sudo rm -rf log

sudo ln -sf /mnt/wasabi/redmine/files files
# wasabiクラウドストレージを利用しています。

sudo chown -h www-data:www-data files

sudo ln -sf /var/log/redmine log
# ログは/var/log配下で一括管理しています。

sudo chown -h www-data:www-data log

データ再マイグレーション

プラグイン再マイグレーション

cd /var/lib/redmine

sudo -u www-data bundle install

sudo -u www-data bundle exec rake redmine:plugins:migrate RAILS_ENV=production

apacheリスタート

systemctl status apache2.service

sudo systemctl restart apache2.service

systemctl status apache2.service

DBリストア

cd /hoge
# mysqldumpを行ったディレクトリ

mysql -h localhost -u redmine -p redmine < redmine_backup.$(date +%Y%m%d).sql
# パスワードはredmineインストール時に設定したDBユーザのものです

apacheリスタート

systemctl status apache2.service

sudo systemctl restart apache2.service

systemctl status apache2.service

動作確認

この状態でRedmineに管理者権限でログインします。手順通りなら

  • テーマ
  • 添付ファイル
  • プラグイン

などが正常に動いていると思います。

バージョンアップ後の診断

2023/03/18現在のRedmine4.2系の最新安定版:4.2.10がインストールされ、A+ と診断されました。

ChatGPTによるシェルスクリプト。(プロセスごとに消費メモリを表示)

検証中のLinuxサーバで

  • 外部公開する際にどのぐらいリソースを消費するのか?
  • 現在のサーバスペックで大丈夫か?
  • けど、Zabbix等の設定はめんどい

という状況は多々発生します。そこで、またもやChatGPTに

Linuxのメモリ使用量を調べるためのシェルスクリプトを書いてください。 要件は以下の通りです。

1. 稼働中のプロセスをpsで調べる
1. 複数のプロセスがあればそのRSSの合計値を足す
1. RSSの合計値を昇順で表示する としてください。

と聞いてみました。幾ばくかの質疑応答を繰り返し、以下、できあがりです。

Script内容

  • スクリプト: rsstotal.sh
#!/bin/bash

# 現在の日時を取得
now=$(date +"%Y/%m/%d %H:%M")

# 全てのプロセスのRSSの合計をKBで取得
processes_rss=$(ps -e -o rss=,comm= | awk '{a[$2]+=$1;} END {for(i in a) if(a[i]>0) printf("%d %s\n",a[i],i);}' | sort -rn)

# 全てのプロセスのRSSの合計をMBに変換して取得
total_rss_kb=$(echo "$processes_rss" | awk '{s+=$1} END {print s}')
total_rss_mb=$(echo "scale=2;$total_rss_kb/1024" | bc -l)

# メモリ使用量と各プロセスのRSSを表示
echo "$now 現在のメモリ使用量は $total_rss_mb MB です"
echo "各プロセスのRSSの使用量は:"
while read -r rss process; do
  rss_mb=$(echo "scale=2;$rss/1024" | bc -l)
  printf "%.2f MB - %s\n" $rss_mb "$process"
done <<< "$processes_rss"

記述後、

chmod +x rsstotal.sh

として実行権を付与します。

実行結果

2023/03/15 20:11 現在のメモリ使用量は 2238.71 MB です
各プロセスのRSSの使用量は:
738.42 MB - java
738.33 MB - node
101.75 MB - mongod
81.71 MB - Xorg
71.31 MB - slick-greeter
43.44 MB - npm
38.82 MB - sshd
38.53 MB - systemd-journal
30.06 MB - systemd
29.48 MB - pulseaudio
23.67 MB - nginx
22.06 MB - lightdm
20.05 MB - networkd-dispat
19.09 MB - NetworkManager
17.30 MB - bash
16.99 MB - dbus-daemon
12.56 MB - udisksd
12.37 MB - cups-browsed
11.76 MB - systemd-resolve
11.31 MB - ModemManager
9.30 MB - upowerd
9.12 MB - polkitd
9.00 MB - thermald
8.21 MB - cupsd
7.98 MB - systemd-logind
7.82 MB - accounts-daemon
7.78 MB - controller
7.60 MB - gvfsd
7.08 MB - systemd-udevd
6.99 MB - (sd-pam)
6.66 MB - at-spi2-registr
6.22 MB - at-spi-bus-laun
5.90 MB - systemd-timesyn
5.44 MB - gvfsd-fuse
5.33 MB - dconf-service
4.81 MB - bluetoothd
4.77 MB - wpa_supplicant
4.76 MB - redis-server
4.46 MB - rsyslogd
3.81 MB - avahi-daemon
3.43 MB - irqbalance
3.15 MB - awk
3.08 MB - ps
2.95 MB - rtkit-daemon
2.94 MB - cron
2.85 MB - sh
2.77 MB - rsync
1.85 MB - agetty

と表示されたのでうまくいっています。

こちらはnginxとgrowiサーバ。やはりというかjava(elasticsearch)がメモリを相当消費していました。

動作確認日

2023/03/15

連携:RedmineのディレクトリとWasabiバケット。

概要

クラウドストレージで作成したバケットは無事にマウントできるようになったので、Redmineの添付ファイルの保存先を切り替えます。

確認環境

  • Ubuntu 20.04
  • s3fsによりWasabiクラウドストレージのバケットがマウントされていること

サックリとした手順

  1. 保存先のディレクトリを作ります。
  2. Remineの添付ファイル一式をバケットにコピーします。
  3. 添付ファイルの保存先をシンボリックリンクに切り替えます。

詳細手順

マウントしたバケットにディレクトリを作成します。

sudo mkdir -p /mnt/wasabi/redmine
# 自分がマウントした環境に合わせます。

sudo chown www-data:www-data /mnt/wasabi/redmine

ls -ld /mnt/wasabi/redmine
# ファイルがあることと所有者がwww-dataであることを確認します。

Remineの添付ファイル一式をコピーします。

sudo -u www-data cp -pir /var/lib/redmine/files /mnt/wasabi/redmine
# Redmineのパスは自分の環境に合わせます。

シンボリックリンクを貼り替えます。

cd /var/lib/redmine
# 自分の環境に合わせます。

sudo mv files files.org
# 一時的に退避します。

sudo ln -s /mnt/wasabi/redmine/files files
# 自分がマウントした環境に合わせます。

sudo chown -h www-data:www-data files

ls -ld /var/lib/redmine/files
# filesの向き先がリンクを張った場所にあることとリンクの所有者がwww-dataであることを確認します

動作を確認します。

  1. Redmineの任意のチケットでファイルを添付します。
  2. 添付後、上記、マウントしたバケットの内容を確認してファイルがあることを確認します。

これで、AWSのRedmineでもファイルを大量に添付できるようになります。

fail2banの再設定。

概要

不正アクセスからサーバを保護するfail2ban。様々なルールが存在するため、チューニングの失敗によっては機能不全に陥ります。

そんなこんなで、ちょっとハマった出来事を記します。

確認実施環境

Ubuntu 20.04

fail2banはアンインストールできない場合があります。

apt-get --purge autoremove fail2ban

をやってもアンインストールできませんでした。

対処: dpkgのinfoファイル削除

sudo su -
# この作業は全て管理者権限で行った方がいいです

cd /var/lib/dpkg/info/

ls -l fail*
rm fail*
# fail2banのみのパッケージがあることを確認して消去します

apt-get --purge autoremove fail2ban
# この段階でようやくアンインストールできました

cd /etc/

rm -rf fail2ban
# fail2banの設定を変更します

と、dpkgのinfoファイルを削除して

ufwとうまく連携できません。

「なぜチューニングに失敗したのか」の理由です。ネットにあるfail2banの記事は大概がiptablesとの連携を前提としているため、Ubuntu系での標準ファイアウォールであるufwとうまく連携できませんでした。

なので、

  • 記事を鵜呑みにして設定するとエラーが発生してfail2banの起動に失敗する。
  • 再設定のためにアンインストールしようとすると上記問題が発生する

という経緯があります。

対処:ufwに即した設定変更

参考記事:
https://blog.fernvenue.com/archives/ufw-with-fail2ban/

前提:

まっさらな状態で(上記手段でアンインストールした上で)

sudo aptitude install fail2ban

を実行した状態とします。

jail.localを編集します。

協議・信仰に沿ったエディタで以下のファイルを編集(作成)します。

  • ファイル名 /etc/fail2ban/jail.local

○内容

[ufw]
enabled=true
filter=ufw.aggressive
action=iptables-allports
logpath=/var/log/ufw.log
maxretry=1
bantime=-1
ignoreip = 127.0.0.0/8 ::1
# ignoreipは任意の(自分のアクセス元)を指定ください

[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は任意の(自分のアクセス元)を指定ください
  • ファイル名 /etc/fail2ban/filter.d/ufw.aggressive.conf

○内容

[Definition]
failregex = [UFW BLOCK].+SRC=<HOST> DST
ignoreregex =

設定反映

systemctl enable fail2ban
systemctl start fail2ban
systemctl statsu fail2ban

これで、不審なアクセスは次回以降は有無を言わせずブロックする設定となります。

ChatGPTによるRubyスクリプト。(パスワード生成スクリプト)

ChatGPTを利用するようになってから、「こういう処理ができたら仕事の処理が楽になるのに」が現実的になりました。

概要

割と設定する機会があるパスワード。

  • キチッとランダムに
  • 桁数や出力数が指定可能で
  • ファイルに出力する必要があるか
  • または標準出力に表示させればいいのか

を設定するRubyスクリプトはできないものかとChatGPTにお伺い。幾度かの対話を繰り返し、ようやく想定通りの動きをするスクリプトができました。

openssl rand と異なり、

  • 大文字/小文字の利用
  • 数字や記号の混在

を細かく設定できるのがポイントです。

生成・編集したスクリプト

  • スクリプト名:password_generate.rb

前提

以下の環境で動作を確認しました。

  • Ubuntu 20.04 LTS
  • CentOS 7
  • Ruby 2.4 / 2.7.0

スクリプト内容

  • password_generate.rb
#このRubyスクリプトは、SecureRandomモジュールを使用してランダムなパスワードを生成するためのスクリプトです。
#パスワードの生成に関する様々な設定を変数で行うことができます。
#例えば、パスワードの長さ、数字やアルファベット、大文字や記号を使用するかどうかなどを指定することができます。
#スクリプトは、出力先がファイルか標準出力かを選択できます。
#ファイルに出力する場合は出力先のディレクトリやファイル名を指定することができます。

# モジュールを呼び出します 
require 'securerandom'
require 'fileutils'
require 'date'

### 変数はここからです
## 出力するパスワードの数(行数)を数字で指定します
num_passwords = 12
## 出力先を選択します
# ファイルに出力する場合は 1、標準出力に出力する場合は 0 に設定します
output_to_file = 1
# 出力先のディレクトリを指定します
output_directory = '/home/hoge/restore_password' 
# 出力ファイル名を名前+yyyymmdd形式.txtで指定します
# 最初の''部分の名前を任意の名前に入力ください
output_filename =  'mysql-restore.' + Time.now.strftime("%Y%m%d") + '.txt'
## パスワードの強度を指定します
# パスワード桁数を数字で指定します
password_length = 10
# 数字を用いる場合は1を、用いない場合は0を指定します
use_numbers = 1
# アルファベット小文字を用いる場合は1を、用いない場合は0を指定します
use_alphabet = 1
# アルファベット大文字を用いる場合は1を、用いない場合は0を指定します
use_uppercase = 1
# 記号を用いる場合は1を、用いない場合は0を指定します
use_symbols = 1
## パスワード生成用の文字種を定義します
# 必要に応じて使わない文字を削除することが可能です(lと|, 0とOなど)
characters = ''
characters += '0123456789' if use_numbers == 1
characters += 'abcdefghijklmnopqrstuvwxyz' if use_alphabet == 1
characters += 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' if use_uppercase == 1
characters += '!#$%&()*+,-./:;<=>?@[]^_`{|}~' if use_symbols == 1
### 変数はここまでです

# パスワード生成関数を定義します
def generate_password(length, characters)
  SecureRandom.random_bytes(length).unpack('C*').map do |byte|
    characters[byte % characters.length]
  end.join
end

## パスワードファイルを出力する場合(変数 output_to_fileを1にしたとき)の処理です
#出力先のパスを作成します
if output_to_file == 1
  output_path = File.join(output_directory, output_filename)
# 出力先のディレクトリが存在しない場合は作成します
  FileUtils.mkdir_p(output_directory)
# パスワードを出力します
  File.open(output_path, 'w') do |file|
    num_passwords.times do
      password = generate_password(password_length, characters)
      file.puts password
    end
  end
else

## 標準出力に出すだけの場合(変数 output_to_fileを0にしたとき)の処理です
# パスワードを標準出力に出力します
  num_passwords.times do
    password = generate_password(password_length, characters)
    puts password
  end
end

使い方

  1. 変数を環境に合わせて定義します。
  2. ruby password_genarate.rbを実行します。
  3. 定義したとおりパスワードファイルが出力されていたり、画面に表示されれば成功です。

今後の展望

  • 他のスクリプトで呼び出す(以前に紹介したMySQLバックアップ時にパスワードをかけるなど
  • 対話式で実行

など、使い勝手に合わせて修正していきたいです。

続・MySQLの自動バックアップ。(パスワードによる暗号化付与)

こちらの記事で挙げたRedmineなどのMySQLを実行するスクリプト。

この問題点を修正します。

問題点

  • むきだしのSQLファイルが平文で格納されてしまうのはセキュリティ的によろしくありません。
  • MySQLのバックアップ時に使うアカウントファイルが誰でも読み取れるのも問題です。

そこで、バックアップされたファイルにパスワードをかけることで簡単な防波堤を作ることにします。

前提

上記URLに併せます。

  1. MySQL dumpを行うDBにRELOAD権限があること。
  2. 次の環境で動作を確認しています。
  • Ubuntu 20.04
  • MySQL 8.0.32

実施した手順

さっくりとした手順

  1. バックアップディレクトリを作成します。
  2. DBにアクセスするためのアカウント情報を記したファイルを作成します。
  3. 開封パスワードを格納するディレクトリを作成します。
  4. バックアップスクリプトを作成します。
  5. crontabに登録します。

バックアップディレクトリを作成します。

sudo mkdir -p /home/backup/mysql
# 運用に合わせて指定ください。ファイルサーバや別パーティションにマウントしている方がサーバ事態の障害発生でも冗長化を持たせられます。

sudo chown -R hoge:hoge /home/backup/mysql
# ディレクトリの所有者をログインユーザに修正します

cd /home/backup/mysql && pwd
# 指定したディレクトリに移動します

DBにアクセスするためのアカウントファイルを作成します。

Cronによる自動実行を前提としているため、スクリプト実行時にDBユーザとパスワードを記したファイルを読み込むことでセキュリティのリスクを抑えます。

sudo mkdir -p /home/hoge/db_password
# 運用に合わせて指定ください。

cd /home/hoge/db_password && pwd
# 指定したディレクトリに移動します

以下の内容を教義・信仰に沿ったエディタで作成します。(【】内は取り除き、自分の設定に合わせます)

  • アカウントファイル内容
    • ファイル名:account.txt
[client]
user = 【RedmineのDBユーザ】
password = "【RedmineのDBユーザ用パスワード】"

その後、このファイルの読み取り権限を変更します。

chmod 400 account.txt

ls -l account.txt
# パーミッションが400であることを確認します

アカウントファイルでアクセスできることを確認

mysql --defaults-extra-file=【アカウントファイルを格納したディレクトリ】/account.txt

#MySQLのプロンプトが出れば成功です。exitで抜けます。

スクリプト作成

以下の内容を教義・信仰に沿ったエディタで作成します。

  • スクリプト内容
    • スクリプト名:pw_mysql_daily_backup.sh
#!/bin/bash

## 変数ここから ##
# SQLをバックアップするディレクトリ(保管先)を指定します。運用に合わせて指定ください。
backup_dir="/home/backup/mysql"
# 保持するバックアップの世代を日数で指定します。
keep_days=7
# ファイルに付与する日付/作業ディレクトリ名/バックアップファイル名を指定します。
current_date=$(date +%Y%m%d)
backup_name="redmine_mysql_${current_date}"
zip_file="redmine_mysql.${current_date}.zip"
# アカウントファイルを指定します。運用に合わせて指定ください。
credentials_file="$HOME/redmine/account.txt"
# パスワードを記録するファイル名を指定します。運用に併せてして指定ください。
password_dir="$HOME/restore_redmine"
password_file="${password_dir}/mysql-restore.$current_date.txt"
# redmineのデータベース名を指定します。
database_name=redmine
# バックアップ時に指定するオプションを指定します。
options="--defaults-extra-file=$credentials_file --no-tablespaces --single-transaction"
## 変数ここまで ##

## 処理ここから ##

# 1.アカウントファイルのパーミッションが400かどうかチェックします。
# 400以外は処理そのものを終了します。
permissions=$(stat -c "%a" "$credentials_file")
if [ "$permissions" != "400" ]; then
    echo "アカウントファイルのパーミッションは400である必要があります。"
    exit 1
fi

# 2.一時的なバックアップディレクトリを作成します。
mkdir "${backup_dir}/${backup_name}"

# 3. mysqldumpを実行してデータベースのバックアップを取ります。
mysqldump $options -h localhost $database_name > "${backup_dir}/${backup_name}/${backup_name}.sql"

# 4. パスワードによる暗号化を実施します。
password=$(openssl rand -base64 12)
cd "${backup_dir}/${backup_name}"
zip -r "${backup_dir}/${zip_file}" -P "$password" .
cd -

# 5. 一時的なバックアップディレクトリを削除します。
rm -rf "${backup_dir}/${backup_name}"

# 6. 解凍パスワードを指定ディレクトリに保存します。
echo $password > $password_file

# 7.パスワードの読み取り権限を600に変更します。
chmod 600 $password_file

# 8. 保持期間より古いバックアップファイルを削除します。
find "$backup_dir" -name "redmine_mysql.*.zip"  ! -type f -newermt "${keep_days} days ago" -delete
find "$password_dir" -name "*restore*.txt" ! -type f -newermt "${keep_days} days ago" -delete

## 処理ここまで

前回との修正点

  1. 変数と処理のセクションを明確化しています。
  2. アカウントファイルのパーミッションチェックを行い、400以外は処理を中止します。
  3. opensslで生成したパスワードで暗号化します。(このパスワードはランダムで生成されるので運用者は覚える必要がありません)
  4. 圧縮と同時に暗号化を行うので、gz形式からzip形式に変更しています。
  5. このパスワードを任意のディレクトリに転送します。
  • 実行権限の付与
chmod +x pw_mysql_daily_backup.sh

動作確認

cd 【スクリプトを格納したディレクトリ】 && pwd
bash pw_mysql_daily_backup.sh

以下を確認します。

  1. エラーなく実行できること
  2. バックアップ格納ディレクトリにredmine.sql.実行日付.zip形式でファイルが作成されること
  3. パスワードファイル格納ディレクトリにファイル名.実行日付.txt形式でファイルが作成されること
  4. unzip redmine.sql.実行日付.zipでファイル解凍時にパスワードを確認されること
  5. パスワードファイルで暗号化されたファイルを解凍することができること

Crontab設定

Cron登録

crontab -e

登録内容例

0 0 * * * /home/backup/mysql/pw_mysql_daily_backup.sh
# 実行時刻、頻度などは自分の運用形態に合わせます。
# また、既に平文でのバックアップスクリプトを設定している場合はコメントアウトして処理を外します。

Cron登録確認

sudo tail -20 /var/log/cron.log

操作時刻に

  • BEGIN EDIT
  • REPLACE
  • END EDIT

が表示されれば設定は完了です。

動作確認日

2023/02/18

検証:Ubuntu 20.04にRedmine 5.0のインストールと4.2へのダウングレード。

ふと思い立っての検証です。

あらまし

別サイトに記載しているRedmine4.2のインストール手順。

https://atelier.reisalin.com/projects/zettel/knowledgebase/articles/19

この手順で「Redmine 5.0を設定できるか?」と思い立ち、検証用のまっさらなUbuntu 20.04を用意しました。

前提

以下を設定しています。

  • インターネット回線に接続されていること
  • ドメインで名前解決できること
  • SSH接続が可能なこと

実施手順

上記のリンクの手順に沿いました。異なっている点は、Redmine 5.0をダウンロードするため、

sudo -u www-data svn co https://svn.redmine.org/redmine/branches/5.0-stable /home/www-data/redmine

としただけです。

無事にRedmine5.0が動き、以下の参照どおりにSSLを設定。

https://atelier.reisalin.com/projects/zettel/knowledgebase/articles/20

これで試しにと思いましたが、

プラグインとの兼ね合い

「どうしても使いたいプラグインがRedmine 5.0に対応していない」事情により継続利用は無理だと断念。特に

  • knowlegebase
  • redmine_issue_badge_plugin

の2つが利用できないのは非常に痛い状況でした。

Redmine 5.0→4.2へのダウングレード

そこで、インストールしたばかりのRedmine5.0を4.2に即座に戻すことにします。

注意点

この手順は、データが全く入っていない状況で可能な作業です。「こんな手法を採ったのがいる」程度に参照ください。

前提

  • 上記手順を元にRedmine 5.0がインストールされ
  • なおかつデータが何も入っていない
  • RedmineのDB名は「redmine」
  • apache2設定ファイルは稼働済み

さっくりとした手順

  1. apache2サービスを落とします。
  2. データベースをまるごと削除します。
  3. 同じ名前でDBを再作成します。
  4. プログラムを再配置します。
  5. apache2サービスを起動します。

apache2サービス停止

sudo systemctl stop apache2.service
#これを行わないと後述のDBが消去できません

mysqlでDBを再作成します。

sudo mysql -u root -p
DROP DATABASE redmine;
# DBを削除します

CREATE DATABASE redmine character set utf8mb4;
# DB "redmine" を再作成します

exit

Redmineプログラムを再配置します。

sudo rm -rf /home/www-data/redmine
# Redmineを配置したディレクトリごと削除します

sudo -u www-data svn co https://svn.redmine.org/redmine/branches/4.2-stable /home/www-data/redmine
# 設定したときと同じディレクトリに4.2を再配置します

Redmineのコンフィグを設定します。

sudo cp -pi /home/www-data/redmine/config/database.yml.example /home/www-data/redmine/config/database.yml

sudo vi /home/www-data/redmine/config/database.yml
# 教義・信仰に従ったエディタで編集してください。

database.yml 編集内容

production:
  adapter: mysql2
  database: redmine
  host: localhost
  username: redmine
  # rootからredmineに変更します
  password: "redmine用のパスワード"
  encoding: utf8mb4
# 本番環境(production)のみ設定を行います

Redmineのマイグレーションを行います。

cd /home/www-data/redmine/ && pwd
# /home/www-data/redmine/ (Redmineを配置したディレクトリ)であることを確認します

sudo -u www-data bundle install --without development test --path vendor/bundle

sudo -u www-data bundle exec rake generate_secret_token

sudo -u www-data RAILS_ENV=production bundle exec rake db:migrate

sudo -u www-data RAILS_ENV=production REDMINE_LANG=ja bundle exec rake redmine:load_default_data

apache2サービスを起動します

すでにapache上でRedmineを動かす手はずは整っており、プログラムの実行ディレクトリも同じ。ならば、設定ファイルは修正せずに済むという判断のもとに実行。

sudo apache2ctl configtest
# Syntax OK を確認します

sudo systemctl restart apache2.service

systemctl status apache2.service

サイトの表示を確認します。

http://設定したRedmineドメイン

でRedmineのトップページが表示されれば成功です。

検証段階だからこそ行えた手荒な手段でした。

MySQLの定期バックアップ、現状の運用に修正。

こちらの記事を2023年2月時点での運用に併せ、以下、修正しました。

概要

Redmineのメンテナンスの中で、「データベースのバックアップ」は非常に重要なものです。

そこで、改めて、シェルスクリプトとCronによるバックアップ手順を整理しました。

動作を確認した環境

  • Ubuntu 20.04 LTS
  • Redmine 4.2
  • MySQL 8.0.32

実施前提

  • MySQLに管理者権限でログインできること。
  • Redmine用のDBとDBユーザ、DBパスワードを把握していること。
  • また、DBサーバはローカルホストです。

確認した手順

  • Redmineが稼働しているUbuntuサーバのターミナル上での操作です。
  • MySQL以外は全て一般権限で実行します。

さっくりとした手順

  1. Redmineのデータベースユーザに権限を付与します。
  2. バックアップディレクトリを作成します。
  3. アカウントファイルを作成します。
  4. バックアップスクリプトを作成します。
  5. crontabに登録します。

データベース設定

管理者権限でMySQLにログインします。

mysql -u root -p

データベースのユーザ権限を変更します。

これを行わないとDump処理ができませんでした。

GRANT RELOAD ON *.* TO '【RedmineのDBユーザ】'@'localhost';
FLUSH PRIVILEGES;
EXIT

ディレクトリとスクリプト作成

バックアップディレクトリ作成

sudo mkdir -p /home/backup/mysql
# 運用に合わせて指定ください。ファイルサーバや別パーティションにマウントしている方がサーバ事態の障害発生でも冗長化を持たせられます。

sudo chown -R hoge:hoge /home/backup/mysql
# ディレクトリの所有者をログインユーザに修正します

cd /home/backup/mysql && pwd
# 指定したディレクトリに移動します

アカウントファイル作成

※このファイルを作成しないと、「安全ではない」とエラーが出ます。

以下の内容を教義・信仰に沿ったエディタで作成します。(【】内は取り除き、自分の設定に合わせます)

  • アカウントファイル内容
  • ファイル名:account.txt
[client]
user = 【RedmineのDBユーザ】
password = "【RedmineのDBユーザ用パスワード】"

アカウントファイルでアクセスできることを確認

mysql --defaults-extra-file=account.txt

MySQLのプロンプトが出れば成功です。exitで抜けます。

スクリプト作成

以下の内容を教義・信仰に沿ったエディタで作成します。

  • スクリプト内容
  • スクリプト名:mysql_daily_backup.sh
#!/bin/bash

# スクリプトの日付形式を定義します
date=$(date +"%Y%m%d")

# バックアップディレクトリを定義します
# 上記手順で示したディレクトリを指定してください
backup_dir="/home/backup/mysql"

# アカウントファイルを指定します
credentials_file="$backup_dir/account.txt"

# バックアップ時に指定するオプションを定義します
options="--defaults-extra-file=$credentials_file --no-tablespaces --single-transaction"

# バックアップファイル名を定義します
backup_file="$backup_dir/redmine.sql.$date.gz"

# バックアップを実行し、.gz形式でバックアップをします
mysqldump $options -h localhost redmine | gzip > $backup_file

# 10世代前の圧縮ファイルを削除します(運用に合わせて指定ください)
find $backup_dir -type f -name "redmine.sql.*.gz" -mtime +10 -delete
  • 実行権限の付与
chmod +x mysql_daily_backup.sh

動作確認

sh mysql_daily_backup.sh

以下を確認します。

  • エラーなく実行できること
  • redmine.sql.実行日付.gz形式でファイルが作成されること
  • gunzip redmine.sql.実行日付.gzでファイルが解凍できること

Crontab設定

Cron登録

crontab -e

登録内容例

0 0 * * * /home/backup/mysql/mysql_daily_backup.sh
# 実行時刻、頻度などは自分の運用形態に合わせます。

Cron登録確認

sudo tail -20 /var/log/cron.log

操作時刻に

  • BEGIN EDIT
  • REPLACE
  • END EDIT

が表示されれば設定は完了です。

動作確認日

2023/02/08

Page 18 of 25

Powered by WordPress & Theme by Anders Norén