脚本参考

推送 ssh 公钥

基于 sshpass

sshpass 是一个 Linux 命令行工具,用于在非交互式的 SSH 会话中提供密码,以避免在脚本或自动化过程中手动输入密码。

通常,当你通过 SSH 连接到远程计算机时,需要手动输入密码来验证你的身份。但在脚本或自动化过程中,这不是很方便,因为你需要手动输入密码,这会打破自动化的流程。sshpass 可以避免这个问题,它可以自动提供 SSH 连接所需的密码,从而使得在脚本或自动化过程中连接到远程计算机更加方便。

需要注意的是,使用 sshpass 可能会存在安全风险,因为密码明文保存在脚本中。如果你需要在脚本中使用 sshpass,请确保只有有权访问脚本的人可以访问这些密码。

方案一

  • 通过变量传入IP
#!/bin/bash
TARGET_IP_LIST="
10.0.0.18
10.0.0.28
"
PUBLIC_KEY_PATH="/root/.ssh/id_rsa"

#dpkg -L sshpass &> /dev/null || apt -y install sshpass
rpm -q sshpass &> /dev/null || yum -y install sshpass

#[ -f /root/.ssh/id_rsa ] || ssh-keygen -f /root/.ssh/id_rsa -P ''
if [ ! -f ${PUBLIC_KEY_PATH} ]; then
  ssh-keygen -f ${PUBLIC_KEY_PATH} -P ''
fi

export SSHPASS=123
for IP in ${TARGET_IP_LIST}; do
    {
        sshpass -e ssh-copy-id -o StrictHostKeyChecking=no ${IP}
    }&
done
wait

该shell脚本中for循环中的 {}& 和外面的 wait 分别是什么意思

在这个脚本中,{}&分别是两个不同的符号,具有不同的含义。

{}是一种将多个命令组合成一个单元的方法。在这个脚本中,{}sshpass命令与ssh-copy-id命令组合在一起,这样这两个命令就可以在一个子shell中并行执行。

&是一种在后台执行命令的方法。在这个脚本中,&用于将多个{}中的子shell放入后台执行。这样做可以加快脚本的执行速度。

wait命令则是一种等待后台任务完成的方法。在这个脚本中,wait命令将会等待所有的子shell完成,然后再继续执行脚本中的其它命令。

为什么要加 wait?不加 wait 不行吗

如果不加wait命令,那么脚本将会立即继续执行,不会等待子shell完成。这样可能会导致一些问题,例如:

  • 如果脚本后面的命令需要使用子shell中创建的文件或者数据,那么这些命令可能会因为在子shell还未完成的情况下执行而失败。
  • 如果某个子shell执行失败,而脚本没有等待所有子shell完成就继续执行后续命令,那么可能会导致后续命令执行失败或者出现意料之外的错误。

因此,加上wait命令可以确保所有子shell都已经执行完毕后再继续执行后续命令,避免出现上述问题。

方案二

  • 将IP列表赋值到文件中传入IP
  • 您可以通过将IP列表保存在单独的文件中,然后在shell脚本中读取该文件来实现从外部文件传入IP列表。以下是示例脚本:
#!/bin/bash
PUBLIC_KEY_PATH="/root/.ssh/id_rsa"
IP_LIST_FILE="$1"

## 检查是否提供IP列表文件作为参数
if [ -z "$IP_LIST_FILE" ]; then
    echo "IP列表文件未作为参数提供!"
    exit 1
fi

## 检查IP列表文件是否存在
if [ ! -f "$IP_LIST_FILE" ]; then
    echo "IP列表文件不存在!"
    exit 1
fi

## 检查是否安装了sshpass
if ! command -v sshpass &> /dev/null; then
    echo "sshpass未安装!请先安装。"
    exit 1
fi

## 检查公钥是否存在,如果不存在,则生成一个
if [ ! -f ${PUBLIC_KEY_PATH} ]; then
  ssh-keygen -f ${PUBLIC_KEY_PATH} -P ''
fi

export SSHPASS=123
while read -r IP; do
    {
        sshpass -e ssh-copy-id -o StrictHostKeyChecking=no "$IP"
    }&
done < "$IP_LIST_FILE"
wait
## cat host_list.conf 
10.0.0.18
10.0.0.28


## bash 1.sh host_list.conf
...

[root@8 ~]# ssh 10.0.0.18
Last login: Fri Mar 31 11:34:46 2023 from 10.0.0.201
[root@18 ~]# logout
Connection to 10.0.0.18 closed.
[root@8 ~]# ssh 10.0.0.28
Last login: Fri Mar 31 11:34:49 2023 from 10.0.0.201
[root@28 ~]# 

基于 expect

expect 是一个 Linux 命令行工具,可以用来自动化和自动化交互式命令行应用程序。

它的主要作用是在非交互式的脚本中模拟人工交互,使得脚本可以与交互式应用程序进行交互,例如 Telnet、FTP、SSH 和 MySQL 等。

通过 expect,你可以编写脚本来模拟交互式命令行的操作,例如输入用户名和密码、选择菜单选项、输入命令等。在脚本中使用 expect,可以自动化这些交互式的操作,避免手动输入。

需要注意的是,使用 expect 可能会存在安全风险,因为密码明文保存在脚本中。如果你需要在脚本中使用 expect,请确保只有有权访问脚本的人可以访问这些密码。

#!/bin/bash
## #********************************************************************
#Author:            xiangzheng
#QQ:                767483070
#Date:              2022-04-20
#FileName:         push_ssh_key_expect.sh
#URL:               https://www.xiangzheng.vip
#Email:             rootroot25@163.com
#Description:      批量推送公钥到远程主机以实现ssh免密登录
#Copyright (C):     2022 All rights reserved
#********************************************************************

## 此脚本在每台主机执行即可实现相互的ssh免密登录


## 远程主机和本机的IP
IP_LIST="
10.0.0.8
10.0.0.100
10.0.0.101
10.0.0.102
"

#远程主机和本机的密码
PASS="123"

#dpkg -L expect &> /dev/null || apt -y install expect
rpm -q expect &> /dev/null || yum -y install expect

#生成公钥私钥对,默认不为私钥创建密码,需要创建密码的话在-P后面的""中指定密码
#ssh-keygen -f /root/.ssh/id_rsa -t rsa -P ""


#判断公钥是否存在
[ -f ~/.ssh/id_rsa.pub ] || { echo '公钥不存在 请存放在 ~/.ssh/id_rsa.pub下 退出' ; exit 3; }


#使用 for 以IP变量的方式将公钥推送到远程主机组
for IP in ${IP_LIST};do
expect <<EOF
set timeout 20
spawn ssh-copy-id -i /root/.ssh/id_rsa.pub root@${IP}
expect { 
    "*yes/no*" { send "yes\n";exp_continue }
    "*password*" { send "${PASS}\n" }
}
expect eof
EOF
echo ${IP} push succeed
done

#使用 while read 以配置文件的方式将公钥推送到远程主机组
#while read IP ;do
#expect <<EOF
#set timeout 20
#spawn ssh-copy-id -i /root/.ssh/id_rsa.pub root@${IP}
#expect {
## "*yes/no*" { send "yes\n";exp_continue }
## "*password*" { send "${PASS}\n" }
#}
#expect eof
#EOF
#echo ${IP} push succeed
#done < hosts.txt

MySQL 数据库备份

基于 mysqldump

全库备份

#!/bin/bash
TIME=`date +%F_%H-%M-%S`
DIR=/backup/all_bak_${TIME}
#PASS=passwd
[ -d "$DIR" ] || mkdir $DIR && cd $DIR

#databases
mysqldump -uroot -F --single-transaction --source-data=2 -q -A | gzip > ${DIR}/all_backup_${TIME}.sql.gz

#log_bin
mkdir log_bin_backup
cp -a /data/mysql_bin log_bin_backup/

#config
mkdir config
cp -a /etc/my.cnf.d/mariadb-server.cnf config/
#!/bin/bash
## 定义 MySQL 登录信息
MYSQL_USER="root"
MYSQL_PASSWORD="password"

## 定义备份文件保存目录
BACKUP_DIR="/var/backups/mysql"

## 定义备份文件名,使用日期作为后缀
BACKUP_FILE="$BACKUP_DIR/mysql-$(date +%Y-%m-%d).sql"

## 确保备份目录存在
mkdir -p $BACKUP_DIR

## 使用 mysqldump 备份所有数据库到指定的文件中
mysqldump -u $MYSQL_USER -p$MYSQL_PASSWORD --all-databases > $BACKUP_FILE

## 删除旧的备份文件,只保留最近7天的备份
find $BACKUP_DIR -name "mysql-*.sql" -type f -mtime +7 -delete

分库备份

#!/bin/bash
TIME=`date +%F_%H-%M-%S`
DIR=/backup/branch_${TIME}
#PASS=passwd
[ -d "$DIR" ] || mkdir $DIR && cd $DIR

for DB in `mysql -uroot -e 'show databases' | grep -Ev "^Database|.*schema$"`;
do
mysqldump -F --single-transaction --source-data=2 -q -B $DB | gzip > branch_${DB}_${TIME}.sql.gz
done

指定库备份

#!/bin/bash
TIME=`date +%F_%H-%M-%S`
DIR=/backup
DB=hellodb  #指定数据库
PASS=12345
mysqldump -uroot -p "$PASS" -F –E –R --triggers  --single-transaction --source-data=2 -q  -B $DB | gzip > ${DIR}/${DB}_${TIME}.sql.gz

创建证书

基于 openssl

#!/bin/bash
. /etc/init.d/functions
declare -A CERT_INFO
CERT_INFO=([subject0]="/O=heaven/CN=ca.god.com" \
           [keyfile0]="cakey.pem" \
           [crtfile0]="cacert.pem" \
           [key0]=2048 \
           [expire0]=3650 \
           [serial0]=0    \
           [subject1]="/C=CN/ST=liaoning/L=shenyang/O=Central.Hospital/CN=master.xiangzheng.org" \
           [keyfile1]="master.key" \
           [crtfile1]="master.crt" \
           [key1]=2048 \
           [expire1]=365
           [serial1]=1 \
           [csrfile1]="master.csr" \
           [subject2]="/C=CN/ST=liaoning/L=shenyang/O=Central.Hospital/CN=slave.xiangzheng.org" \
           [keyfile2]="slave.key" \
           [crtfile2]="slave.crt" \
           [key2]=2048 \
           [expire2]=365 \
           [serial2]=2 \
           [csrfile2]="slave.csr"   )

COLOR="echo -e \\E[1;32m"
END="\\E[0m"
DIR=/data
cd $DIR 



for i in {0..2};do
    if [ $i -eq 0 ] ;then
        openssl req  -x509 -newkey rsa:${CERT_INFO[key${i}]} -subj ${CERT_INFO[subject${i}]} \
            -set_serial ${CERT_INFO[serial${i}]} -keyout ${CERT_INFO[keyfile${i}]} -nodes \
	    -days ${CERT_INFO[expire${i}]}  -out ${CERT_INFO[crtfile${i}]} &>/dev/null
        
    else 
        openssl req -newkey rsa:${CERT_INFO[key${i}]} -nodes -subj ${CERT_INFO[subject${i}]} \
            -keyout ${CERT_INFO[keyfile${i}]}   -out ${CERT_INFO[csrfile${i}]} &>/dev/null

        openssl x509 -req -in ${CERT_INFO[csrfile${i}]}  -CA ${CERT_INFO[crtfile0]} \
	    -CAkey ${CERT_INFO[keyfile0]}  -set_serial ${CERT_INFO[serial${i}]}  \
	    -days ${CERT_INFO[expire${i}]} -out ${CERT_INFO[crtfile${i}]} &>/dev/null
    fi
    $COLOR"**************************************生成证书信息**************************************"$END
    openssl x509 -in ${CERT_INFO[crtfile${i}]} -noout -subject -dates -serial
    echo 
done
chmod 600 *.key
action "证书生成完成"
$COLOR"**************************************生成证书文件如下**************************************"$END
echo "证书存放目录: "$DIR
echo "证书文件列表: "`ls $DIR`

部署应用

安装 JDK

#!/bin/bash
## #********************************************************************
#Author:		xiangzheng
#QQ: 			767483070
#Date: 			2021-07-07
#FileName:		install_jdk.sh
#URL: 			llinux.cn
#Description:		The test script
#Copyright (C): 	2021 All rights reserved
#********************************************************************

JDK_FILE="/usr/local/src/jdk-8u333-linux-x64.tar.gz"
JDK_TARGET="/usr/local"
JDK_UN_NAME="jdk1.8.0_333" # jdk 版本

if  $(java -version &> /dev/null); then
    echo "JDK 已经安装 退出"    
    exit
elif [ ! -f "$JDK_FILE" ]; then
    echo "$JDK_FILE 文件不存在 退出"
    exit 
fi

tar xvf $JDK_FILE  -C $JDK_TARGET

ln -s $JDK_TARGET/$JDK_UN_NAME $JDK_TARGET/jdk 

cat >  /etc/profile.d/jdk.sh <<EOF
export JAVA_HOME=$JDK_TARGET/jdk
export JRE_HOME=\$JAVA_HOME/jre
export CLASSPATH=\$JAVA_HOME/lib/:\$JRE_HOME/lib/
export PATH=\$PATH:\$JAVA_HOME/bin
EOF

.  /etc/profile.d/jdk.sh

java -version &> /dev/null && echo "${JDK_FILE%%.*} 安装完成" || { echo "${JDK_FILE%%.*} 安装失败" ; exit ; }

安装 Harbor

#!/bin/bash
## #********************************************************************
#Author:	     	xiangzheng
#QQ: 			    767483070
#Date: 		     	2022-03-24
#FileName:		    install_harbor.sh
#URL: 		    	https://www.xiangzheng.vip
#Email: 		    rootroot25@163.com
#Description:		The test script
#Copyright (C): 	2022 All rights reserved
#********************************************************************

#需将 docker-compose 和 harbor 文件放到和此脚本同级的目录下

DOCKER_COMPOSE_VERSION="2.3.3"
HARBOR_HOSTNAME="harbor.xiangzheng.vip"
HARBOR_ADMIN_PASSWORD="666666"
HARBOR_VERSION="2.3.5"

docker --version &> /dev/null || { echo "docker未安装 退出" ; exit; }

apt -y install python3
#dnf -y install python3


install_docker-compose(){
    chmod +x docker-compose-linux-x86_64 
    mv docker-compose-linux-x86_64 /usr/bin/docker-compose
    docker-compose --version &> /dev/null
        if [ $? -eq 0 ];then
            echo "docker-compose 安装完成" ; sleep 3
        else
            echo "docker-compose 安装失败 退出" ; exit 3
        fi
}

install_harbor(){
    mkdir -p /apps && tar xf harbor-offline-installer-v${HARBOR_VERSION}.tgz -C /apps/
    mv /apps/harbor/harbor.yml.tmpl /apps/harbor/harbor.yml
    sed -ri.bak "s|(hostname: )(.*)|\1${HARBOR_HOSTNAME}|" /apps/harbor/harbor.yml
    sed -ri "s|(harbor_admin_password: )(.*)|\1${HARBOR_ADMIN_PASSWORD}|" /apps/harbor/harbor.yml
    sed -ri 's|^(https:)|#\1|' /apps/harbor/harbor.yml
    sed -ri 's|(.*)(port: 443)|#\1\2|' /apps/harbor/harbor.yml
    sed -ri 's|(.*)(certificate: .*)|#\1\2|' /apps/harbor/harbor.yml
    sed -ri 's|(.*)(private_key: .*)|#\1\2|' /apps/harbor/harbor.yml
    /apps/harbor/install.sh
cat > /lib/systemd/system/harbor.service <<EOF
[Unit]
Description=Harbor
Requires=docker.service
After=docker.service systemd-networkd.service systemd-resolved.service
Documentation=http://github.com/vmware/harbor

[Service]
Type=simple
Restart=on-failure
RestartSec=5
ExecStart=/usr/bin/docker-compose -f /apps/harbor/docker-compose.yml up
ExecStop=/usr/bin/docker-compose -f /apps/harbor/docker-compose.yml down
ExecReload=/bin/kill -s HUP \$MAINPID

[Install]
WantedBy=multi-user.target
EOF

    systemctl daemon-reload
    systemctl enable --now harbor.service
    systemctl is-active harbor.service &> /dev/null && echo "harbor 安装成功" || echo "harbor 安装失败"
}

install_docker-compose

install_harbor

安装 Docker

#!/bin/bash
## #********************************************************************
#Author:	     	xiangzheng
#QQ: 			    767483070
#Date: 		     	2022-06-15
#FileName:		    init_docker.sh
#URL: 		    	https://www.xiangzheng.vip
#Email: 		    rootroot25@163.com
#Description:		针对 Ubuntu 或 centos 一键安装docker
#Copyright (C): 	2022 All rights reserved
#********************************************************************

docker --version &> /dev/null && { echo "docker已经安装 退出" ; exit; }

install_docker_for_ubuntu(){
    apt-get -y install \
        apt-transport-https \
        ca-certificates \
        curl \
        gnupg \
        lsb-release
        
    curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
    
    echo \
      "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://mirrors.aliyun.com/docker-ce/linux/ubuntu/ \
      $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
      
    apt update
    
    # 可指定安装版本
    apt -y install docker-ce=5:20.10.16~3-0~ubuntu-focal docker-ce-cli=5:20.10.16~3-0~ubuntu-focal
    #apt -y install docker-ce=5:19.03.15~3-0~ubuntu-focal docker-ce-cli=5:19.03.15~3-0~ubuntu-focal
    
    systemctl is-active docker &>/dev/null && echo "docker安装成功" || { echo "docker安装失败 退出" ; exit; }
}

install_docker_for_centos(){
. /etc/init.d/functions

yum install -y yum-utils
yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo \
&& action "docker-ce.repo文件下载成功" || { action "docker-ce.repo文件下载失败 退出" false ; exit; }

mkdir -p /etc/docker/
cat > /etc/docker/daemon.json <<EOF
{
  "registry-mirrors": ["https://jqm0rnhf.mirror.aliyuncs.com"]
}
EOF
action "docker加速准备完成"

## 可指定安装版本
yum install -y docker-ce-20.10.10 docker-ce-cli-20.10.10 containerd.io

systemctl enable --now docker

systemctl is-active docker &>/dev/null && action "docker安装成功" ||  action "docker安装失败" false ; exit;
}

#install_docker_for_ubuntu
install_docker_for_centos

扫描网段

  • -c1 表示发送一次 ping 请求并等待回复,然后退出程序。即只发送一个 ICMP 包。
  • -W1 表示设置等待回复的超时时间为 1 秒。如果在 1 秒内没有接收到回复,那么程序将退出。

扫描单个网段

#!/bin/bash
NET=172.16.0
for IP in {1..254};do
    {
    ping -c1 -W1 $NET.$IP &> /dev/null && echo "$NET.$IP is online"  
    }&
done
wait

扫描多个网段

#!/bin/bash
NET1=172.16.10
NET2=172.16.20
NET3=172.16.30
for IP in {1..254};do
    {
    ping -c1 -W1 $NET1.$IP &> /dev/null && echo "$NET1.$IP is online";\
    ping -c1 -W1 $NET2.$IP &> /dev/null && echo "$NET2.$IP is online";\
    ping -c1 -W1 $NET3.$IP &> /dev/null && echo "$NET2.$IP is online"  
    }&
done
wait

扫描部分IP

  • 创建IP列表文件,下面的脚本通过位置变量传参,因此文件名任意
## vim ip_list.txt
172.16.20.1
172.16.20.200
172.16.20.201
172.16.20.180
172.16.20.181
172.16.20.222
172.16.20.223
172.16.10.1
172.16.10.2
172.16.30.199
172.16.30.200
172.16.30.201
  • scan_ip.sh
#!/bin/bash
IP_LIST_FILE="$1"
while read -r IP
do
    {
        ping -c1 -W1 $IP &> /dev/null && echo "$IP is online"
    }&
done < "${IP_LIST_FILE}"
wait
  • 执行测试
## ./scan_ip.sh /data/scripts/ip_list.txt
172.16.20.180 is online
172.16.20.181 is online

FTP上传和下载文件

交互式

  • ftp_upload_download.sh
#!/bin/bash

### 匿名登录 ###
PROXY_HOST="172.16.66.208"
PROXY_PORT="12081"
FTP_HOST="172.16.66.202"
#FTP_HOST="gyh7lb7om5wvxsifwefq675za3m73spilf5lebaum5kjc652gaugikad.onion"
FTP_USER="anonymous"
FTP_PASS="123"
SUB_DIR="testdir/"

### 非匿名登录 ###
#PROXY_HOST="172.16.66.208"
#PROXY_PORT="12081"
#FTP_HOST="172.16.66.213"
#FTP_USER="ftpuser"
#FTP_PASS="ftppass"

function upload (){
## 读取要上传的文件
read -p "Please enter the file to upload to ${FTP_HOST}: " LOCAL_FILE

## 上传文件
echo "Uploading file..."
if [[ -n "${PROXY_HOST}" && -n "${PROXY_PORT}" ]]; then
    # 使用代理服务器
    curl --socks5 ${PROXY_HOST}:${PROXY_PORT} \
    --upload-file ${LOCAL_FILE} \
    ftp://${FTP_HOST}/${SUB_DIR} \
    --user ${FTP_USER}:${FTP_PASS} \
    -s
else
    # 不使用代理服务器
    curl --upload-file ${LOCAL_FILE} \
    ftp://${FTP_HOST}/${SUB_DIR} \
    --user ${FTP_USER}:${FTP_PASS} \
    -s
fi
echo "File uploaded."
}


function download (){
## 读取要下载的文件
read -p "Please enter the file to download from ${FTP_HOST}: " REMOTE_FILE

## 下载文件
echo "Downloading file..."
if [[ -n "${PROXY_HOST}" && -n "${PROXY_PORT}" ]]; then
    # 使用代理服务器
    curl --socks5 "${PROXY_HOST}:${PROXY_PORT}" \
    --user "${FTP_USER}:${FTP_PASS}" \
    "ftp://${FTP_HOST}/${SUB_DIR}${REMOTE_FILE}" \
    -o ${REMOTE_FILE} \
    -s
else
    # 不使用代理服务器
    curl --user "${FTP_USER}:${FTP_PASS}" \
    "ftp://${FTP_HOST}/${SUB_DIR}${REMOTE_FILE}" \
    -o ${REMOTE_FILE} \
    -s
fi
echo "File downloaded."
}

cat <<EOF
1) upload
2) download
3) exit
EOF

read -p "please enter a number (1-3): " NUM

case ${NUM} in
1)
    upload
    ;;
2)
    download
    ;;
*)
    echo exit && exit 3
esac

非互式

  • ftp_upload_download.sh
#!/bin/bash

PROXY_HOST=""
PROXY_PORT=""
FTP_HOST=""
FTP_USER=""
FTP_PASS=""
SUB_DIR=""

show_help () {
    cat <<EOF
Usage:
  $0 [option...]

Options:
  -u, --upload              upload mode
  -d, --download            download mode
  -h, --host     <string>   FTP server address [required]
  -a, --username <string>   non-anonymous login username
  -p, --password <string>   non-anonymous login password
  -s, --subdir   <string>   sub directory on FTP server
  -x, --proxy    <string>   proxy server <host>:<port>
  -f, --file     <string>   filename to upload or download [required for upload/download modes]

Examples:
  $0 -u -h 172.16.66.202 -f test.txt
  $0 -d -h 172.16.66.213 -a ftpuser -p 'myftppass' -s testdir/ -f tor.jpg
  $0 -u -h gyh7lb7om5wvxsifwefq675za3m73spilf5lebaum5kjc652gaugikad.onion -x 172.16.66.208:12081 -f secrets.txt
EOF
}

if [[ "$1" == "-h" || "$1" == "--help" ]]; then
    show_help
    exit 0
fi

while [[ $# -gt 0 ]]; do
    key="$1"
    case $key in
        -u|--upload) # upload mode
        MODE="upload"; shift
        ;;
        -d|--download) # download mode
        MODE="download"; shift
        ;;
        -h|--host) # FTP server address
        FTP_HOST="$2"; shift 2
        ;;
        -a|--username) # non-anonymous login username
        FTP_USER="$2"; shift 2
        ;;
        -p|--password) # non-anonymous login password
        FTP_PASS="$2"; shift 2
        ;;
        -s|--subdir) # sub directory on FTP server
        SUB_DIR="$2/"; shift 2
        ;;
        -x|--proxy) # proxy server <host>:<port>
        PROXY="$2"; shift 2
        ;;
        -f|--file) # filename to upload or download
        FILE="$2"; shift 2
        ;;
        -*) # unknown options
        echo -e "Unknown option: $1\nTry '$0 --help' for more information." >&2; exit 1
        ;;
        *) # positional arguments
        echo -e "Unknown argument: $1\nTry '$0 --help' for more information." >&2; exit 1
        ;;
    esac
done

## make sure required options are set
if [[ -z "$FTP_HOST" ]]; then
    echo "FTP host not defined! Use -h or --host option to specify." >&2
    echo "Try '$0 --help' for more information." >&2
    exit 1
fi

if [[ "$MODE" == "upload" ]]; then
    if [[ -z "$FILE" ]]; then
        echo "No file specified for upload! Use -f or --file option to specify." >&2
        echo "Try '$0 --help' for more information." >&2
        exit 1
    fi
    # upload file
    if [[ -n "$PROXY" ]]; then
        # use proxy server
        curl --socks5-hostname "$PROXY" \
        --upload-file "$FILE" \
        ftp://"$FTP_HOST"/"$SUB_DIR" \
        --user "$FTP_USER:$FTP_PASS" #-s
    else
        # no proxy
        curl --upload-file "$FILE" \
        ftp://"$FTP_HOST"/"$SUB_DIR" \
        --user "$FTP_USER:$FTP_PASS" #-s
    fi
elif [[ "$MODE" == "download" ]]; then
    if [[ -z "$FILE" ]]; then
        echo "No file specified for download! Use -f or --file option to specify." >&2
        echo "Try '$0 --help' for more information." >&2
        exit 1
    fi
    # download file
    if [[ -n "$PROXY" ]]; then
        # use proxy server
        curl --socks5-hostname "$PROXY" \
        --user "$FTP_USER:$FTP_PASS" \
        "ftp://$FTP_HOST/$SUB_DIR$FILE" \
        -o "$FILE" #-s
    else
        # no proxy
        curl --user \
        "$FTP_USER:$FTP_PASS" \
        "ftp://$FTP_HOST/$SUB_DIR$FILE" \
        -o "$FILE" #-s
    fi
fi

测试文档

  • 匿名用户
## 匿名用户通过代理上传
./ftp_upload_download.sh -u -h 172.16.66.202 -x 172.16.66.208:12081 -f fstab -s testdir/ -a anonymous -p 123

./ftp_upload_download.sh -u -h gyh7lb7om5wvxsifwefq675za3m73spilf5lebaum5kjc652gaugikad.onion -x 172.16.66.208:12081 -f fstab -s testdir/ -a anonymous -p 123

## 匿名用户通过代理下传
./ftp_upload_download.sh -d -h 172.16.66.202 -x 172.16.66.208:12081 -f fstab -s testdir/ -a anonymous -p 123

./ftp_upload_download.sh -d -h gyh7lb7om5wvxsifwefq675za3m73spilf5lebaum5kjc652gaugikad.onion -x 172.16.66.208:12081 -f fstab -s testdir/ -a anonymous -p 123


## 匿名用户不通过代理上传
./ftp_upload_download.sh -u -h 172.16.66.202 -f fstab -s testdir/ -a anonymous -p 123


## 匿名用户不通过代理下载
./ftp_upload_download.sh -d -h 172.16.66.202 -f fstab -s testdir/ -a anonymous -p 123
  • 非匿名用户
## 非匿名用户通过代理上传
./ftp_upload_download.sh -u -h 172.16.66.213 -x 172.16.66.208:12081 -f fstab -a ftpuser -p ftppass

./ftp_upload_download.sh -u -h gyh7lb7om5wvxsifwefq675za3m73spilf5lebaum5kjc652gaugikad.onion -x 172.16.66.208:12081 -f fstab -a ftpuser -p ftppass


## 非匿名用户通过代理下传
./ftp_upload_download.sh -d -h 172.16.66.213 -x 172.16.66.208:12081 -f fstab -a ftpuser -p ftppass

## 非匿名用户不通过代理上传
./ftp_upload_download.sh -u -h 172.16.66.213 -f fstab -a ftpuser -p ftppass

## 非匿名用户不通过代理下载
./ftp_upload_download.sh -d -h 172.16.66.213 -f fstab -a ftpuser -p ftppass

1

## http可以访问
curl --socks5-hostname "172.16.66.208:12081" "http://gyh7lb7om5wvxsifwefq675za3m73spilf5lebaum5kjc652gaugikad.onion"


## 好了
curl --socks5-hostname "172.16.66.208:12081" --upload-file fstab ftp://gyh7lb7om5wvxsifwefq675za3m73spilf5lebaum5kjc652gaugikad.onion/testdir/ --user anonymous:111


./ftp_upload_download.sh -u -h gyh7lb7om5wvxsifwefq675za3m73spilf5lebaum5kjc652gaugikad.onion -x 172.16.66.208:12081 -f fstab -s testdir/ -a anonymous -p 123

一键安装 Docker

docker_install.sh

#!/bin/bash

ubuntu_function() {
    local docker_version="5:20.10.24~3-0~ubuntu-*"
    if command -v docker &>/dev/null; then
        echo "Docker is already installed."
    else
        apt-get update
        apt-get -y install apt-transport-https ca-certificates curl software-properties-common
        curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
        add-apt-repository "deb [arch=amd64] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable"
        apt-get -y update
        apt-get -y install docker-ce=${docker_version}
	configure_docker_accelerator
    fi

}

centos_function() {
    echo "This is CentOS!"
}

other_function() {
    echo "This is another distribution!"
}

configure_docker_accelerator() {
    if [ ! -f /etc/docker/daemon.json ]; then
        mkdir -p /etc/docker
        touch /etc/docker/daemon.json
    fi

    echo '{
      "registry-mirrors": ["https://jqm0rnhf.mirror.aliyuncs.com"]
    }' | sudo tee /etc/docker/daemon.json > /dev/null

    systemctl restart docker

    echo "Docker image accelerator configured successfully."
}

if [ -f /etc/os-release ]; then
    source /etc/os-release
    case "$ID" in
        ubuntu)
            ubuntu_function
            ;;
        centos)
            centos_function
            ;;
        *)
            other_function
            ;;
    esac
else
    echo "Unable to determine the Linux distribution."
fi

批量上传镜像

方法一

#!/bin/bash

## 定义镜像仓库地址和名称
repository="172.16.0.120:30002/darknet-target"

## 获取镜像列表
images=(
    $(docker images | grep www | grep -Ev "(k8s|ubuntu)" | awk '{print  $1 ":" $2 }')
)

## 循环处理每个镜像
for image in "${images[@]}"
do
  # 添加标签
  docker tag "$image" "$repository/$(basename "$image")"

  # 推送到镜像仓库
  docker push "$repository/$(basename "$image")"
done
  • 此脚本将遍历 images 数组中定义的镜像列表,并为每个镜像添加标签,然后将其推送到指定的镜像仓库。脚本中的 repository 变量定义了目标镜像仓库的地址和名称,你可以根据需要进行修改。
  • 请注意,你需要在运行脚本之前已经登录到目标镜像仓库。如果尚未登录,请在脚本中添加适当的 docker login 命令以进行登录。

方法二

#/bin/bash
IP="10.0.0.10:30002"
REPO="/tor-target"
SOURCE_IMAGES=$(docker images | grep "$REPO" | awk '{print $1 ":" $2}')

for IMAGE in $SOURCE_IMAGES
do
    #echo "docker tag $IMAGE ${IP}${REPO}/$(basename ${IMAGE})"
    #echo "docker push ${IP}${REPO}/$(basename ${IMAGE})"
    docker tag $IMAGE ${IP}${REPO}/$(basename ${IMAGE})
    docker push ${IP}${REPO}/$(basename ${IMAGE})
    #docker rmi ${IP}${REPO}/$(basename ${IMAGE})
done

遇到的问题

unauthorized: unauthorized to access repository: darknet-target/tor_da, action: push: unauthorized to access repository: darknet-target/tor_da, action: push

这个错误表示你的用户没有足够的权限来推送镜像到指定的镜像仓库。为了解决这个问题,你可以尝试以下几个步骤:

  1. 确保你已经使用正确的凭据登录到目标镜像仓库。运行以下命令登录到镜像仓库:

    docker login 172.16.0.120:30002

    在登录时,你需要提供正确的用户名和密码或其他凭据。

  2. 检查你登录的用户是否具有推送(push)镜像到指定仓库的权限。你可能需要联系仓库管理员或拥有适当权限的用户,以获得推送镜像的权限。

  3. 验证目标镜像仓库的名称是否正确。确保在上传时指定了正确的镜像仓库名称和路径。可以通过运行以下命令检查已登录用户的仓库列表:

    docker repository ls
  4. 如果你使用的是私有镜像仓库,并且你在 docker push 命令中使用了带有仓库名称的标签,请确保仓库名称正确且与你的仓库设置一致。有时候,这个错误可能是由于标签中的仓库名称拼写错误或者与你的仓库设置不匹配所导致的。

请按照上述步骤检查和修复相关问题,并确保你具有足够的权限来推送镜像到指定的镜像仓库。如有需要,请联系你的镜像仓库管理员获取更多帮助和支持。

批量构建和推送镜像

#!/bin/bash

function login() {
  echo "start login harbor"
  docker login -u $harbor_username -p $harbor_password $harbor
}
function build() {
  local role=$1
  docker build -f "Dockerfile_${role}" -t "${harbor}/${respository}/${role}:${version}" .
}

function push() {
  local role=$1
  docker push "${harbor}/${respository}/${role}:${version}"
}

function main() {
  login
  for role in "${roles[@]}"
  do
    #build ${role}
    push ${role}
  done
}

harbor="172.16.0.120:30002"
harbor_username="admin"
harbor_password="Harbor12345"
respository="tor-target"

roles=("da" "relay" "exit" "client" "onion")
version="4.7.13"
main

在 Bash 脚本中,local 关键字用于声明一个本地变量,这意味着该变量只在当前函数的作用域内有效。在这种情况下,local role=$1 表示将作为参数传递给函数的第一个参数赋值给本地变量 role

在这个脚本中,push 函数被定义为接受一个参数 role。使用 local 关键字声明的变量仅在 push 函数内部可见,这样做的目的是确保在函数执行期间不会意外修改或覆盖脚本中其他地方定义的全局变量 role

制作以及调试镜像

make_image.sh

#!/bin/bash
IMAGE='systemwire:v1.0'
#NAME='systemwire'
NAME='test'

build_image() {
    docker build -t $IMAGE .
}

run_container() {
    #docker run -d -p 5000:5000 --name test --rm $IMAGE
    docker run -d --name $NAME --rm $IMAGE
}

enter_container() {
    docker exec -it test bash
}

stop_container() {
    docker stop test
}

case "$1" in
    build)
        build_image
        ;;
    run)
        run_container
        ;;
    stop)
        stop_container
        ;;
    exec)
        enter_container
        ;;
    *)
        echo "Usage: $0 {build|run|exec|stop}"
esac

查看实时网络流量

#!/bin/bash

while [ True ];do
    i=0
    for eth in $(cat /proc/net/dev | sed -e '1,2d'|cut -d: -f1);do

        RX[$i]=$(cat /proc/net/dev | grep $eth | tr : " " | awk '{print $2}')
        TX[$i]=$(cat /proc/net/dev | grep $eth | tr : " " | awk '{print $10}')
        let i=$i+1
    done

    sleep 1
    clear
    i=0
    for eth in $(cat /proc/net/dev | sed -e '1,2d'|cut -d: -f1);do
        RXnext[$i]=$(cat /proc/net/dev | grep $eth | tr : " " | awk '{print $2}')
        TXnext[$i]=$(cat /proc/net/dev | grep $eth | tr : " " | awk '{print $10}')
        let i=$i+1
    done

    i=0
    echo  -e  "\t RX `date +%k:%M:%S` TX"    
    for eth in $(cat /proc/net/dev | sed -e '1,2d'|cut -d: -f1);do
        RX=$((${RXnext[$i]}-${RX[$i]}))
        TX=$((${TXnext[$i]}-${TX[$i]}))
         
        if [[ $RX -lt 1024 ]];then
        RX="${RX}B/s"
        elif [[ $RX -gt 1048576 ]];then
        RX=$(echo $RX | awk '{print $1/1048576 "MB/s"}')
        else
        RX=$(echo $RX | awk '{print $1/1024 "KB/s"}')
        fi
         
        if [[ $TX -lt 1024 ]];then
        TX="${TX}B/s"
        elif [[ $TX -gt 1048576 ]];then
        TX=$(echo $TX | awk '{print $1/1048576 "MB/s"}')
        else
        TX=$(echo $TX | awk '{print $1/1024 "KB/s"}')
        fi
         
        echo -e "$eth \t $RX   $TX "
        let i=$i+1
    done
done

离线安装pip与netmiko

#!/bin/bash

## 目录路径
PIP_SCRIPT="get-pip.py"
PIP_PACKAGE_DIR="pip_offline"
NETMIKO_PACKAGE_DIR="netmiko_package"

## 函数:检查命令是否存在
command_exists() {
    command -v "$1" >/dev/null 2>&1
}

## 函数:安装pip
install_pip() {
    if [ -f "$PIP_SCRIPT" ] && [ -d "$PIP_PACKAGE_DIR" ]; then
        python2 "$PIP_SCRIPT" --no-index --find-links="$PIP_PACKAGE_DIR"
    else
        echo "$PIP_SCRIPT or $PIP_PACKAGE_DIR not found."
        exit 1
    fi
}

## 函数:安装netmiko
install_netmiko() {
    if [ -d "$NETMIKO_PACKAGE_DIR" ]; then
        pip2 install --no-index --find-links="$NETMIKO_PACKAGE_DIR" netmiko==2.4.2
    else
        echo "$NETMIKO_PACKAGE_DIR directory not found."
        exit 1
    fi
}

## 检查并安装pip
if command_exists pip; then
    echo "pip is already installed."
else
    echo "pip is not installed. Installing pip..."
    install_pip
fi

## 检查并安装netmiko
if python2 -c "import netmiko" &>/dev/null; then
    echo "netmiko is already installed."
else
    echo "netmiko is not installed. Installing netmiko..."
    install_netmiko
fi

echo "Setup completed."

批量域名解析测试

check_domains.sh

#!/bin/bash

## 检查是否提供了文件
if [ $# -eq 0 ]; then
    echo "请提供一个包含域名的文件作为参数"
    exit 1
fi

## 读取域名列表文件
domain_file=$1

## 检查文件是否存在
if [ ! -f "$domain_file" ]; then
    echo "文件 $domain_file 不存在"
    exit 1
fi

## 遍历域名列表
while IFS= read -r domain; do
    # 跳过空行和以#开头的注释行
    if [[ -z "$domain" || "$domain" =~ ^# ]]; then
        continue
    fi

    # 使用nslookup测试域名解析并提取IP地址
    result=$(nslookup "$domain" 2>/dev/null)

    # 根据nslookup返回的状态码判断是否能解析
    if [ $? -eq 0 ]; then
        # 提取IP地址
        ip=$(echo "$result" | awk '/^Address: / {print $2}')
        echo "$domain 能被解析,IP地址: $ip"
    #else
    #    echo "$domain 无法解析"
    fi
done < "$domain_file"

domains.txt

ftp.harbin2025.com
repo.harbin2025.com
docs.harbin2025.com
alerts.harbin2025.com
results.harbin2025.com
standings.harbin2025.com
refreshments.harbin2025.com
streaming.harbin2025.com
liveblog.harbin2025.com
photo.harbin2025.com
admin.harbin2025.com
test.harbin2025.com
pay.harbin2025.com
dev.harbin2025.com
db.harbin2025.com
download.harbin2025.com
backup.harbin2025.com
devops.harbin2025.com
chat.harbin2025.com

要批量测试一批域名是否能被解析,你可以编写一个简单的Shell脚本,利用nslookupdig工具进行DNS查询。下面是一个使用nslookup工具的示例脚本。

脚本内容

#!/bin/bash

## 检查是否提供了文件
if [ $# -eq 0 ]; then
    echo "请提供一个包含域名的文件作为参数"
    exit 1
fi

## 读取域名列表文件
domain_file=$1

## 检查文件是否存在
if [ ! -f "$domain_file" ]; then
    echo "文件 $domain_file 不存在"
    exit 1
fi

## 遍历域名列表
while IFS= read -r domain; do
    # 跳过空行和以#开头的注释行
    if [[ -z "$domain" || "$domain" =~ ^# ]]; then
        continue
    fi

    # 使用nslookup测试域名解析
    nslookup "$domain" > /dev/null 2>&1

    # 根据nslookup返回的状态码判断是否能解析
    if [ $? -eq 0 ]; then
        echo "$domain 能被解析"
    else
        echo "$domain 无法解析"
    fi
done < "$domain_file"

使用说明

  1. 将域名列表保存在一个文本文件中(例如 domains.txt),每个域名占一行。

    • 文件示例:
      google.com
      example.com
      nonexistdomain.xyz
  2. 将上述脚本保存为文件(例如 check_domains.sh)。

  3. 赋予脚本执行权限:

    chmod +x check_domains.sh
  4. 运行脚本并传递域名文件作为参数:

    ./check_domains.sh domains.txt

脚本工作原理

  • 脚本首先检查是否传入了域名文件作为参数。
  • 它会逐行读取域名列表,跳过空行和注释(以 # 开头的行)。
  • 使用 nslookup 工具来检查每个域名是否能够解析。如果 nslookup 返回成功(状态码为0),则输出该域名可以解析;否则,输出该域名无法解析。

其他注意事项

  • 如果你想使用 dig 工具,可以将 nslookup 替换为 dig,例如:
    dig +short "$domain"
    然后根据返回的结果判断是否能够解析。

这个脚本非常简单且实用,适用于常见的批量域名解析测试场景。如果你有更复杂的需求,可以根据需要进一步修改脚本。

nmap扫描网段

好的,使用 nmap 来扫描一个网段中的主机是否开放 22 端口会更高效且功能更强大。下面是一个使用 nmap 的 Shell 脚本示例:

脚本:check_ssh_ports_with_nmap.sh

#!/bin/bash

## 设置要扫描的网段(可以根据需求修改)
network="192.168.1"  # 例如,192.168.1.0/24网段

## 扫描的端口
port=22

## 扫描网段中的主机,使用 nmap 检查 22 端口是否开放
nmap -p $port --open ${network}.0/24 -oG - | awk '/22\/open/ {print $2 " 的 22 端口开放"}'

说明:

  1. network="192.168.1":扫描的网段。你可以根据需要修改成你自己想扫描的网段,如 10.0.0192.168.0 等。

  2. port=22:指定要检查的端口,默认是 22,即 SSH 端口。

  3. nmap -p $port --open ${network}.0/24

    :使用

    nmap

    扫描指定网段的 IP,检测端口是否开放:

    • -p $port:指定扫描的端口(这里是 22)。
    • --open:只显示开放的端口。
    • ${network}.0/24:扫描整个 192.168.1.0/24 网段,检查该网段中哪些主机的 22 端口开放。
  4. -oG -:将 nmap 输出以 “grepable” 格式输出,便于后续处理。

  5. awk '/22\/open/ {print $2 " 的 22 端口开放"}':使用 awk 处理 nmap 输出,只打印开放了 22 端口的主机 IP。

执行:

  1. 将脚本保存为一个文件,例如 check_ssh_ports_with_nmap.sh
  2. 给脚本执行权限:chmod +x check_ssh_ports_with_nmap.sh
  3. 执行脚本:./check_ssh_ports_with_nmap.sh

示例输出:

192.168.1.5 的 22 端口开放
192.168.1.20 的 22 端口开放
192.168.1.45 的 22 端口开放

安装 nmap

  • 如果你的系统没有安装

    nmap

    ,可以通过以下命令安装:

    • 在 Ubuntu/Debian 上:sudo apt-get install nmap
    • 在 CentOS/RHEL 上:sudo yum install nmap
    • 在 macOS 上:brew install nmap

说明:

  • 使用 nmap 来扫描比手动使用 nc 更为高效,尤其是在大规模网络中,因为 nmap 会并行扫描多个目标。
  • 如果你需要扫描特定的 IP 范围,可以将网段替换为具体的 IP 地址,例如:192.168.1.1-254

这样你就能更方便、高效地扫描网段内哪些机器的 22 端口开放了。