コンテンツへスキップ

目的
数多のIPがあり複数のパスワード候補の中で、どのパスワードがssh接続できるかを知りたい

問題点
sshpassコマンドで for文を回すとなぜか一回の処理で終わる

解決方法
ssh コマンドに -n オプションを追加して解決した。

参考
https://tech.kurojica.com/archives/36145/

コード例

#!/bin/sh

success_log="success.log"
failure_log="failure.log"
passwords=('password1' 'password2' 'password3')

# ログファイルを初期化
> $success_log
> $failure_log

while read -r ip_address; do
    echo "Attempting login to $ip_address..."

    success=false

    # 3つの異なるパスワードを試行
    for password in "${passwords[@]}"; do
        sshpass -p "$password" ssh -o StrictHostKeyChecking=no -p 32122 -n user@$ip_address "echo 'SSH connection successful!'"
        
        # スクリプトの実行結果に基づいてログを分ける
        if [ $? -eq 0 ]; then
            success=true
            echo "Success: $ip_address (Password: $password)" >> $success_log
            break  # ログイン成功したらループを抜ける
        fi
    done

    # パスワード試行がすべて失敗した場合
    if [ "$success" = false ]; then
        echo "Failure: $ip_address (All Passwords Failed)" >> $failure_log
    fi

done < iplist.txt

echo "Script execution completed."


簡単な解説
passwordsに判別したいパスワードを入力します
その結果は以下に吐き出されます。
success_log="success.log"
failure_log="failure.log"

Success: xxx.xxx.xxx.xxx (Password: password1)

シェルスクリプトでIPを引数にしたいとき、nslookupやdigを使用する時がある。
その結果からIPだけの文字列に整形をするのだが、IP以外にも切り取られてしまうことがある。

例えば、以下のコマンドの場合、digコマンドからIPを表示されない場合がある。

dig -4 $i | grep "IN" | grep -v ';' | awk '{print $5}'

どうにかならないか考えたところ、以下サイトが参考になった。


個人的にありがたかったのが、grepのオプション -o で、grepが一致した箇所だけを表示する。
それを踏まえて、dig のオプション -4でIPV4にしぼり、正規表現で3ケタの0-9までの文字列にしぼる。

dig -4 $i | grep "IN" | grep -Eo '[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}'


上記のコマンドで現在は安定してIPを取得できている

Zabbixでホストを大量に抱えていると一気にアラート発生したときに確認に苦労する。
例えば、「aaa.comでアラートが発生しました」とアラートが発生したら、
まず、踏み台サーバーのコンソールにログインし、aaa.comのIPをnslookupで取得する。
次に取得したIPでssh接続。原因を調べて解決。のような流れ。

今回は、nslookupでドメインからIPを取得する流れが面倒だったため、/etc/hostsファイルに追記することで、
ドメインでssh接続できるシェルスクリプトを考えてみた。

mkhosts.sh

#!/bin/bash
header='Content-Type:application/json-rpc'
apiurl='http://000.000.000/zabbix/api_jsonrpc.php'
json='{"jsonrpc": "2.0","method": "user.login","params": {"user": "zabbixのユーザー名","password": "Zabbixのパスワード"},"id": 1,"auth": null}'

zbxauth=$(curl -sS -X POST -H "${header}" -d "${json}" ${apiurl} | jq -r ".result")
echo $zbxauth

json='{"jsonrpc": "2.0","method": "host.get","params": {"output": ["hostid","host"]},"id": 2,"auth": "'$zbxauth'"}'
echo $json | jq

hostlist=`curl -sS -X POST -H "${header}" -d "${json}" ${apiurl} | jq | grep "host"| grep -v "hostid" | awk -F: '{print $2}' | sed "s/\"/""/g" `

for i in $hostlist 
do

#任意でnslookupの結果をIPのみに整形する
iplist=`nslookup $i | grep "Address" | grep -v "\#53" |  awk -F: '{print $2}'` 
echo "$iplist $i"| sed 's/^[ \t]*//' >> /etc/hosts 
done

全体の流れとして、ZabbixのAPIからホスト名を取得し整形する。
次に取得したホスト名を引数にしてfor文でnslookupをかけてIPを取得する変数をつくる
最後に、echoで「IP ホスト名」の形にしたものを、/etc/hosts ファイルの最終行に追記する。
mkhosts.sh 実行後、/etc/hostsファイルを確認すると、
IP ホスト名
となっているはず。
その後、

ssh ホスト名

で対象のホストに接続できると思うので確認してみてほしい。

参考
https://www.zabbix.com/documentation/4.4/en/manual/api
https://tech-mmmm.blogspot.com/2019/02/zabbix-api.html
https://orebibou.com/ja/home/201607/20160712_003/
https://ex1.m-yabe.com/archives/3306

bashでスクリプトを作るとき、スクリプト内のリダイレクト先を下記のように指定すると
そのスクリプトがあるディレクトリにファイルが作成される。

echo "test" > test

しかし、cronでそのスクリプトを実行するとtestファイルは、
rootディレクトリに作成されてしまう。
これに気が付くまで

そのため、cronを実施する場合、リダイレクトしたいディレクトリのパスを毎回記載する必要があった。

echo "test" > 対象ディレクトリ/test

パスが長ければ長いほど手間がかかるため、楽が出来ないかと探したところdirnameコマンドを知った。
dirnameコマンドはファイルが設置されている、パスを取得を取得できるコマンド。

dirname /etc/fail2ban
/etc

また、$0 は実行スクリプトを取得できる。それとpwdとサブシェルを合わせると絶対パスを取得できる。

BASE_DIR=$(cd $(dirname $0); pwd) 
echo "test" > ${BASE_DIR}/test

上記を用いるとcronからスクリプトを実行しても、
実行スクリプトと同じ階層からリダイレクトしたファイルを取得できた。

参考
[bash] 実行スクリプトの絶対パスの取得
https://qiita.com/koara-local/items/2d67c0964188bba39e29

ブログのスパム攻撃とか変なアクセスが気になるので、日本とgoogle以外のIPをブロックするスクリプトを作りました。
テスト環境でうまくいっているみたいなので、アップ
ipblker

#!/bin/bash
#ssコマンドで15個までのIPを取得します。
ip=`/usr/sbin/ss -t | awk -F : '{print$2}' | awk '{print$2}' | grep -v Peer | sort | uniq | head -n 15`
echo "$ip"
echo "$ip" > /root/ipblk/ip_listold

#whitelistに記録されているIPを除外しip_listoldとして吐き出します。
for i in `cat /root/ipblk/whitelist`
do
cat /root/ipblk/ip_listold | grep -v "$i" > /root/ipblk/ip_listold1
cat /root/ipblk/ip_listold1 > /root/ipblk/ip_listold
cat /root/ipblk/ip_listold
cat /root/ipblk/ip_listold | wc -l
echo ""
echo "$i"
echo ""
done

#ip_listoldから日本とgoogleのIPをwhitelistに追加します
#それ以外をブロックし、blacklistに追加します
ipl=`cat /root/ipblk/ip_listold`
for i in `echo "$ipl"`
do

curl -kfsS https://ipinfo.io/"$i"/country >> /root/ipblk/ip_country
if [[ "$?" -eq 0 ]]; then
  echo "終了コードは0です 1"
   if [[ `tail -n 1 /root/ipblk/ip_country` = JP ]]; then
      echo "nonblock_JP"
      echo "$i"
      echo "$i" >> /root/ipblk/whitelist
      continue
   else
     echo 日本ではない
    fi
elif [[ "$?" -ne 0 ]]; then
  echo "終了コードは0以外です 1"
  continue
fi

curl -kfsS https://ipinfo.io/"$i"/ | grep org | awk '{print$3}' >> /root/ipblk/ip_org
if [[ "$?" -eq 0 ]]; then
  echo "終了コードは0です 2"
   if [[ `tail -n 1 /root/ipblk/ip_org` = "Google" ]] && [[ `host "$i" | awk -F . '{print$7}'` = "googlebot" ]]; then
      echo "nonblock_Google"
      echo "$ip"
      echo "$ip" >> /root/ipblk/whitelist
       continue
   else
      echo googleではない
   fi

  elif [[ "$?" -ne 0 ]]; then
      echo "終了コードは0以外です 2"
  continue
fi

curl -kfsS https://ipinfo.io/"$i"/ip >> /root/ipblk/ip_ip
if [[ "$?" -eq 0 ]]; then
  echo "終了コードは0です 3"
   if [[ `tail -n 1 /root/ipblk/ip_ip` = "127.0.0.1" ]]; then
      echo "nonblock_loopback"
      continue
   else
      echo `tail -n 1 /root/ipblk/ip_country`
      echo /sbin/iptables -I INPUT -s $i -j DROP
      echo iptables -I INPUT -s $i -j DROP >> /root/ipblk/blacklist
   fi
  elif [[ "$?" -ne 0 ]]; then
      echo "終了コードは0以外です 3"
      echo 127.0.0.1ではない
  continue
fi

done

と、 ipinfo.io 様からIPの情報を取得しています。
後半の行でIPをブロックしますが、
テストを兼ねてechoを付けていますので、
実際に実行される場合は、echoを外してください。

echo iptables -I INPUT -s $i -j DROP
iptables -I INPUT -s $i -j DROP

次に上記のスクリプトをcronに設置します。

cd /var/spool/cron/crontabs/
vim root
*/1 * * * * /root/ipblk/ipblker

エラー通知のみ受信したい場合
*/1 * * * * /root/ipblk/ipblker 1> /dev/null

全ての通知を受信しない場合
*/1 * * * * /root/ipblk/ipblker >/dev/null 2>$1

cronのログ自体を確認したい場合

vim /etc/rsyslog.d/50-default.conf

#cron.*                          /var/log/cron.log
コメントされている場合外してあげます。
cron.*                          /var/log/cron.log


テスト用としてcronを1分に一回と設定していますが、
ipinfo.ioの1日に取得できるIPは無料だと1000回までなので、
それを超えない設定にします。
今回の場合、1回につき最大IPを15個まで取得できるように設定しているので、
30分に一回、ipblkerを実行します。

*/30 * * * * /root/ipblk/ipblker 1> /dev/null

何かエラー通知あれば、確認して調整していく予定です。

サーバー監視において、高負荷が上がった場合、
サーバーにssh接続できないことがあるからcronで対応することがあります。
その条件として何かしらのプロセス数を用いることが多いからテンプレートを作成しました。
#!/bin/bash
ps=`ps -ef | grep <監視したいプロセス> | wc -l`
date=`date '+%Y/%m/%d - %H:%M:%S'`

if [[ $ps -gt <プロセス数> ]]; then
echo $date ; <実行内容>

else
  echo $date  "プロセス数:$ps"

fi

cronとあわせる場合
1 * * * * /usr/local/bin/test >> /var/log/test_log

elseの結果を /var/log/test_log から確認した場合、
2021/10/02 - 13:22:01 プロセス数:1

やけにサイトが重かったりメールが送信できなかったりしたときにサーバー内の接続状況を確認すると、同じIPから大量の接続があったりしますよね。そのようなときはIPをブロックしますが、IPによってはブロックできないことってありますよね。

私の場合、毎回IPの情報を確認してからブロックしています。しかしIPの数によっては大変なので今回はシェルスクリプトで一回で作業を済ますコードを書いてみました。

コードを書く際に至って、https://ipinfo.io を借りています。1日1000件まではIPの情報を取得できるらしい。

ipblocker.sh

#/bin/bash
ip=`ss -t | awk -F : '{print$2}' | awk '{print$2}' | grep -v Peer | sort | uniq`
for i in `echo $ip`
do
cn=`curl https://ipinfo.io/"$i" | grep country | awk '{print$2}'`
echo $i
echo $cn
if [ "$cn" = "\"JP\"," ]; then
  echo "nonblock_JP"
  elif [ `curl https://ipinfo.io/"$i" | grep org | awk '{print$3}'` = "Google" ]; then
    echo "nonblock_Google"
  elif [ `curl https://ipinfo.io/"$i" | grep ip | awk '{print$2}'` = "\"127.0.0.1\"," ]; then
    echo "nonblock_loopback"
  else
      echo "iptables -I INPUT -s $i -j DROP"
      iptables -I INPUT -s $i -j DROP
      fi
done

コードの内容としては、ss コマンドでIPを取得してから、
日本とGoogle、ループバックに関するIP以外だったらブロックする内容になっています。

ブロックした内容は iptables -nL とかで確認してみてください。

サーバー立てるたびに鍵認証設定するの面倒だからね。
ssh-keygen時にパスワードを聞かれず、最後に秘密鍵を
表示してくれるスクリプトです。

makekey.sh

yum -y install openssh-server
ssh-keygen -N "" -f ~/.ssh/id_rsa
mv ~/.ssh/id_rsa.pub ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
sed -i 's/^PubkeyAuthentication.*$/PubkeyAuthentication yes/' /etc/ssh/sshd_config
sed -i 's/PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
systemctl restart sshd
cat ~/.ssh/id_rsa

これまでシェルでいくつかスクリプトを作ってきたけど、オプション要素がどれもなかった。
オプションをつけることは敷居が高そうだと思ったので手をつけなかったけど、
実際に検索したら思ったよりも簡単そうなのでためしてみた。

opt_test
#/bin/bash
while getopts sdt OPT
do
case $OPT in
s) ss -t ;;
d) df -h ;;
t) top ;;
esac
done

重要なのは、【getopts】コマンドで、その横、オプションとなる一文字を記入
今回の場合、s,d,tの計3つのオプションをつけたかったので、「sdt」と記載。
OPTの部分は変数を記載する。
結果的に opt_test -s とすれば ss -t  のコマンド、 opt_test -d とすれば、df -h が可能となりましたとさ。 

参考
https://atmarkit.itmedia.co.jp/ait/articles/2002/13/news025.html

最近LXCばかり触っていたのでホストからゲストOSを設定できるスクリプトをかいてみた。
ネットワークとSSH公開鍵認証の設定。sshは内容軽くいじれば他でも使いまわせそう。
OSはcentos7.9を想定

#network config
rm /var/lib/lxc/$1/rootfs/etc/sysconfig/network-scripts/ifcfg-eth0
lxc-attach -n $1 -- bash -c 'echo "DEVICE=eth0" >> /etc/sysconfig/network-scripts/ifcfg-eth0'
lxc-attach -n $1 -- bash -c 'echo "BOOTPROTO=none" >> /etc/sysconfig/network-scripts/ifcfg-eth0'
lxc-attach -n $1 -- bash -c 'echo "ONBOOT=yes" >> /etc/sysconfig/network-scripts/ifcfg-eth0'
lxc-attach -n $1 -- bash -c 'echo "NM_CONTROLLED=no" >> /etc/sysconfig/network-scripts/ifcfg-eth0'
lxc-attach -n $1 -- bash -c 'echo "TYPE=Ethernet" >> /etc/sysconfig/network-scripts/ifcfg-eth0'
lxc-attach -n $1 -- bash -c 'echo "IPADDR='$1'" >> /etc/sysconfig/network-scripts/ifcfg-eth0'
lxc-attach -n $1 -- bash -c 'echo "NETMASK='$2'" >> /etc/sysconfig/network-scripts/ifcfg-eth0'
lxc-attach -n $1 -- bash -c 'echo "GATEWAY='$3'" >> /etc/sysconfig/network-scripts/ifcfg-eth0'
lxc-attach -n $1 -- bash -c 'echo "DNS1=8.8.8.8" >> /etc/sysconfig/network-scripts/ifcfg-eth0'
lxc-attach -n $1 -- bash -c 'echo "DNS1=8.8.4.4" >> /etc/sysconfig/network-scripts/ifcfg-eth0'
lxc-attach -n $1 systemctl restart network
#ssh config
lxc-attach -n $1 -- yum -y install openssh-server
lxc-attach -n $1 -- ssh-keygen -N "" -f ~/.ssh/id_rsa
lxc-attach -n $1 mv ~/.ssh/id_rsa.pub ~/.ssh/authorized_keys
lxc-attach -n $1 chmod 600  ~/.ssh/authorized_keys
lxc-attach -n $1 -- sed -i 's/^PubkeyAuthentication.*$/PubkeyAuthentication yes/' /etc/ssh/sshd_config
lxc-attach -n $1 -- sed -i 's/PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
lxc-attach -n $1 systemctl restart sshd
lxc-attach -n $1 cat ~/.ssh/id_rsa

個人的に下記行の引数を展開できないことに苦労した。
lxc-attach -n $1 -- bash -c 'echo "IPADDR='$1'" >> /etc/sysconfig/network-scripts/ifcfg-eth0'

シングルクォーテーションって強制的にすべて文字列にするものだと思ってるから
echo以降は全て文字列しかできない。
でもぐぐったら、むしろ$1の場所をシングルクォーテーションでさらに囲ったら変数扱いとなった。
なぜ


参考
sed-iについて
https://www.usupi.org/sysad/197.html

lxc-attachについて
https://gihyo.jp/admin/serial/01/linux_containers/0008?page=4