背景

每次新装服务器,配置基础环境,总是各种翻看笔记,各种复制粘贴,实在太烦琐了,于是写了个通用的一键初始化脚本,并发布到网站,方便调用,正所谓懒惰是技术进步的最大源动力。

脚本使用方法

安装curl

如果已有curl,可忽略此步骤。

1
apt-get -y install curl

示例1

创建用户yanyong,设置FQDNblog.yanyong.cc,设置ssh端口23422,设置阿里源,设置中国上海时区Asia/Shanghai

1
2
3
4
5
6
curl -fsSL https://raw.yanyong.cc/sh/debian10-init.sh | bash -s -- \
    --fqdn=blog.yanyong.cc \
    --user=yanyong \
    --port=23422 \
    --source=ali \
    --tz=Asia/Shanghai

执行结果,脚本已更新,此图可能与实际返回结果有所不同。

示例2

创建用户testuser,成功创建用户会触发禁用root远程登录和禁用密码验证登录,如有需要启用root远程登录或密码验证登录,查看可选操作

1
curl -fsSL https://raw.yanyong.cc/sh/debian10-init.sh | bash -s user=testuser

示例3

apt-get dist-upgrade更新系统。

1
curl -fsSL https://raw.yanyong.cc/sh/debian10-init.sh | bash -s distupgrade 

如果跨版本更新系统,例如当前是debian 9,需要传入--source=...参数修改成debian 10的源,此示例为香港源

1
curl -fsSL https://raw.yanyong.cc/sh/debian10-init.sh | bash -s distupgrade source=hk

特别说明

  1. 查看帮助:curl -fsSL https://raw.yanyong.cc/sh/debian10-init.sh | bash -s help。参数的小横线--可以省略,如user=yanyong等同于--user=yanyong,参数顺序无要求。

  2. 创建用户时,默认设置用户密码为随机生成的16位字符串,生成密钥对,保存在创建的用户家目录的.ssh目录下,并设置密钥密码为随机生成的8位字符串。也可以指定密码--password=PASSWORD(用户密码),--keyPasswd=PASSWORD(密钥密码)。

  3. hostname无需指定,取FQDN的最左边记录值为主机名(如FQDN为www.example.com,则hostname为www)。

  4. APT源设置,--source=cn中科大源--source=hk香港源--source=us美国源--source=ali阿里源--source=aws亚马逊源。修改源,会相应的修改NTP地址,已自动选择,无须指定。

  5. 查看所有可用时区:timedatectl list-timezones

  6. 初始化脚本每次执行,无论有没有参数,默认都有以下处理:

    • apt-get upgrade更新系统。
    • 安装man, sudo, vim, net-tools等。
    • 自定义profile文件,优化历史命令记录,设置命令别名,如ll, lt等。
    • 如果成功创建用户,就会禁用root远程登录和禁用密码验证登录。
    • 启用iptables防火墙,开启ping,开启ssh的端口,开启80和443端口。

可选操作

如有需要,启用root远程登录。

1
sudo sed -ri 's/^(PermitRootLogin )no$/\1yes/' /etc/ssh/sshd_config && sudo systemctl reload ssh

如有需要,启用密码验证登录。

1
sudo sed -ri 's/^(PasswordAuthentication )no$/\1yes/' /etc/ssh/sshd_config && sudo systemctl reload ssh

如有需要,关闭iptables防火墙。

1
2
3
sudo iptables -P INPUT ACCEPT
sudo iptables -F INPUT  # 可选
: | sudo tee /etc/iptables/rules.v4  # 可选,重启后也不会打开防火墙

脚本内容

以下内容可能不是最新版本,最新版本见raw链接:[raw]

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
#!/usr/bin/env bash
# Author: [email protected]
set -e
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
SCRIPTNAME="${0##*/}"
ARGS="$*"


usage(){
    echo "Usage: /PATH/SCRIPTNAME [[--]fqdn=FQDN] [[--]port=PORT] [ [--]user=USERNAME [[--]passwd=PASSWORD] [[--]keyPasswd=PASSWORD] ] [[--]source={cn|CN|hk|HK|us|US|ali|ALI|aws|AWS}] [[--]tz=Asia/Shanghai] [[--]help]"
}

setCN(){
    mirror=http://mirrors.ustc.edu.cn/debian/
    security=http://mirrors.ustc.edu.cn/debian-security
    ntp='0.cn.pool.ntp.org 1.cn.pool.ntp.org 2.cn.pool.ntp.org 3.cn.pool.ntp.org'
}
setHK(){
    mirror=http://ftp.hk.debian.org/debian/
    #security=http://ftp.hk.debian.org/debian-security
    security=http://security.debian.org/debian-security
    ntp='0.hk.pool.ntp.org 1.hk.pool.ntp.org 2.hk.pool.ntp.org 3.hk.pool.ntp.org'
}
setUS(){
    mirror=http://ftp.us.debian.org/debian/
    security=http://security.debian.org/debian-security
    ntp='0.us.pool.ntp.org 1.us.pool.ntp.org 2.us.pool.ntp.org 3.us.pool.ntp.org'
}
setALI(){
    mirror=http://mirrors.aliyun.com/debian/
    security=http://mirrors.aliyun.com/debian-security/  # must .../debian-security/ 
    ntp='0.cn.pool.ntp.org 1.cn.pool.ntp.org 2.cn.pool.ntp.org 3.cn.pool.ntp.org'
}
setAWS(){
    mirror=http://cdn-aws.deb.debian.org/debian/
    security=http://security.debian.org/debian-security
    ntp='0.amazon.pool.ntp.org 1.amazon.pool.ntp.org 2.amazon.pool.ntp.org 3.amazon.pool.ntp.org'
}

getArguments(){
for i in $ARGS; do
    case $i in
        --fqdn=?*|fqdn=?*)
            eval ${i#--}
            ;;
        --port=?*|port=?*)
            eval ${i#--}
            ;;
        --user=?*|user=?*)
            eval ${i#--}
            ;;
        --passwd=?*|passwd=?*)
            eval ${i#--}
            ;;
        --keyPasswd=?*|keyPasswd=?*)
            eval ${i#--}
            ;;
        --source=?*|source=?*)
            eval ${i#--}
            { [[ $source = cn || $source = CN ]] && setCN; } || \
            { [[ $source = hk || $source = HK ]] && setHK; } || \
            { [[ $source = us || $source = US ]] && setUS; } || \
            { [[ $source = ali || $source = ALI ]] && setALI; } || \
            { [[ $source = aws || $source = AWS ]] && setAWS; }
            ;;
        --tz=?*|tz=?*)
            eval ${i#--}
            ;;
        --help|help)
            usage
            exit 0
            ;;
        *)
            usage
            exit 3
            ;;
    esac
done
if [[ -z $user ]] && ( [[ -z $passwd ]] && [[ -z $keyPasswd ]] ); then  # 不传入用户名参数,不创建用户
    createuser=false
elif [[ -z $user ]] && ( [[ -n $passwd ]] || [[ -n $keyPasswd ]] ); then  # 只传入密码参数无用户名参数,返回脚本用法并退出
    usage; exit 4
fi
}

setDefaultVariables(){
    #full_domain="${fqdn:-www.example.com}"
    full_domain="${fqdn}"
    my_hostname=${full_domain%%.*}
    my_ip=`hostname -I | awk '{print $1}'`
    checkPort=`awk '/^Port/{print $2}' /etc/ssh/sshd_config`
    current_port="${checkPort:-22}"
    #ssh_port="${port:-$current_port}"
    ssh_port="${port}"
    #new_user="${user:-example}"
    new_user="${user}"
    password16="${passwd:-`tr -dc '[:alnum:]' < /dev/urandom | head -c 16`}"
    password8="${keyPasswd:-`tr -dc '[:digit:][:lower:]' < /dev/urandom | head -c 8`}"
    #debian_mirror="${mirror:-http://mirrors.ustc.edu.cn/debian/}"
    #debian_security="${security:-http://mirrors.ustc.edu.cn/debian-security}"
    #ntp_servers="${ntp:-0.cn.pool.ntp.org 1.cn.pool.ntp.org 2.cn.pool.ntp.org 3.cn.pool.ntp.org}"
    debian_mirror="${mirror}"
    debian_security="${security}"
    ntp_servers="${ntp}"
    #time_zone="${tz:-Asia/Shanghai}"
    time_zone="${tz}"
}

setGlobalVariables(){
    _fqdn="${full_domain}"
    _hostname=${my_hostname}
    _ip=${my_ip}
    _currentport="${current_port}"
    _sshport="${ssh_port}"
    _user="${new_user}"
    _password="${password16}"
    _passwd_key="${password8}"
    _source="${debian_mirror}"
    _source_security="${debian_security}"
    _ntp="${ntp_servers}"
    _tz="${time_zone}"
}

updateOS(){
  if [[ -n $_source ]]; then
      if [[ ! -f /etc/apt/sources.list.origin ]]; then cp /etc/apt/sources.list{,.origin}; fi
      cat > /etc/apt/sources.list <<- EOF
deb $_source buster main
deb-src $_source  buster main
deb $_source_security buster/updates main
deb-src $_source_security buster/updates main
deb $_source buster-updates main
deb-src $_source buster-updates main
EOF

      sed -ri 's/^#?(NTP=).*$/\1'"$_ntp"'/' /etc/systemd/timesyncd.conf  # 直接替换,懒得各种判断
      systemctl restart systemd-timesyncd
  fi
      apt-get update && apt-get -y upgrade
      apt-get -y install man sudo vim net-tools
}

setHostname(){
  if [[ -n $_hostname ]]; then
    hostname $_hostname
    echo $_hostname > /etc/hostname
    if ! grep -q "$_ip $_fqdn $_hostname" /etc/hosts; then
        echo "$_ip $_fqdn $_hostname" >> /etc/hosts
    fi
  fi
}

setProfile(){
if [[ ! -f /etc/profile.d/myProfile.sh ]]; then

    tee /etc/profile.d/myProfile.sh <<- EOF
export HISTFILESIZE=20000
export HISTTIMEFORMAT="%F %T"
export HISTCONTROL="ignoreboth"
alias ls='ls --color=auto'
alias ll='ls -l --color=auto'
alias lt='ls -alt --color=auto'
alias grep='grep --color=auto'
alias cp='cp -i'
alias mv='mv -i'
alias rm='rm -i'
umask 022
export TMOUT=3600
EOF

fi
}

setTimezone(){
  if [[ -n $_tz ]]; then
    timedatectl set-timezone $_tz
    #sed -ri 's/^#+(NTP=).*$/\1'"$_ntp"'/' /etc/systemd/timesyncd.conf
    #systemctl restart systemd-timesyncd
  fi
}

createUser(){
  if [[ ! $createuser = false ]]; then
    if ! id -u $_user &> /dev/null; then
        iscreate=true
        useradd -m $_user -s /bin/bash
        eval chpasswd <<< "$_user:$_password"
        usermod -aG sudo $_user

        if [[ ! -f /etc/sudoers.d/nopasswd ]]; then
            echo -e '%sudo\tALL=(ALL:ALL) NOPASSWD: ALL' > /etc/sudoers.d/nopasswd
        fi

        mkdir /home/$_user/.ssh
        chown $_user:$_user /home/$_user/.ssh
        chmod 700 /home/$_user/.ssh
        su - $_user -c "ssh-keygen -qf /home/$_user/.ssh/id_rsa.$_user -t rsa -N $_passwd_key"
        cat /home/$_user/.ssh/id_rsa.$_user.pub > /home/$_user/.ssh/authorized_keys
        chown $_user:$_user /home/$_user/.ssh/authorized_keys
        chmod 600 /home/$_user/.ssh/authorized_keys
    else
        iscreate=false
        echo -en "\033[33m[Warning] \033[0m"
        echo "User: $_user exist!"
    fi
  else
    iscreate=false
  fi
}

setSSH(){
    if [[ ! -f /etc/ssh/sshd_config.origin ]]; then cp /etc/ssh/sshd_config{,.origin}; fi

    if grep -Eq '^PermitRootLogin (yes|no)$' /etc/ssh/sshd_config; then
        sed -ri -e 's/^(PermitRootLogin )yes$/\1no/' /etc/ssh/sshd_config
    else
        echo 'PermitRootLogin no' >> /etc/ssh/sshd_config
    fi

    if grep -Eq '^PasswordAuthentication (yes|no)$' /etc/ssh/sshd_config; then
        sed -ri -e 's/^(PasswordAuthentication )yes$/\1no/' /etc/ssh/sshd_config
    else
        echo 'PasswordAuthentication no' >> /etc/ssh/sshd_config
    fi

    if [[ -n $_sshport ]]; then  # 如果指定ssh端口
        if [[ ! $_sshport = $_currentport ]]; then  # 如果端口不一致
            if grep -q '^Port [0-9]\+' /etc/ssh/sshd_config; then  # 如果配置文件有指定Port
                sed -ri 's/^Port [0-9]+/Port '"$_sshport"'/' /etc/ssh/sshd_config
            else  # 如果配置文件没有指定Port,比如默认22端口的情况
                echo "Port $_sshport" >> /etc/ssh/sshd_config
            fi
        fi
    fi

    systemctl reload ssh
}

setIptables(){
    echo 'iptables-persistent iptables-persistent/autosave_v4 boolean true' | debconf-set-selections
    echo 'iptables-persistent iptables-persistent/autosave_v6 boolean false' | debconf-set-selections
    apt-get -y install iptables-persistent

    if ! iptables -C INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT &> /dev/null; then iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT; fi
    if ! iptables -C INPUT -i lo -j ACCEPT &> /dev/null; then iptables -A INPUT -i lo -j ACCEPT; fi
    if ! iptables -C INPUT -p icmp -m state --state NEW --icmp-type echo-request -j ACCEPT &> /dev/null; then iptables -A INPUT -p icmp -m state --state NEW --icmp-type echo-request -j ACCEPT; fi

    if [[ -n $_sshport ]]; then  # 如果指定ssh端口
        if [[ ! $_sshport = $_currentport ]]; then  # 如果端口不一致,删除当前规则,添加新规则
          if iptables -C INPUT -p tcp -m state --state NEW -m tcp --dport "$_currentport" -j ACCEPT &> /dev/null; then iptables -D INPUT -p tcp -m state --state NEW -m tcp --dport "$_currentport" -j ACCEPT; fi
          if ! iptables -C INPUT -p tcp -m state --state NEW -m tcp --dport "$_sshport" -j ACCEPT &> /dev/null; then iptables -A INPUT -p tcp -m state --state NEW -m tcp --dport "$_sshport" -j ACCEPT; fi
        else  # 如果端口一致,检测是否有防火墙规则,无则添加
          if ! iptables -C INPUT -p tcp -m state --state NEW -m tcp --dport "$_currentport" -j ACCEPT &> /dev/null; then iptables -A INPUT -p tcp -m state --state NEW -m tcp --dport "$_currentport" -j ACCEPT; fi
        fi
    else  # 如果不指定ssh端口,检测是否有防火墙规则,无则添加
        if ! iptables -C INPUT -p tcp -m state --state NEW -m tcp --dport "$_currentport" -j ACCEPT &> /dev/null; then iptables -A INPUT -p tcp -m state --state NEW -m tcp --dport "$_currentport" -j ACCEPT; fi
    fi

    if ! iptables -C INPUT -p tcp -m state --state NEW -m multiport --dports http,https -j ACCEPT &> /dev/null; then iptables -A INPUT -p tcp -m state --state NEW -m multiport --dports http,https -j ACCEPT; fi

    iptables -P INPUT DROP

    netfilter-persistent save
    if [[ ! -f /etc/iptables/rules.v4.origin ]]; then cp /etc/iptables/rules.v4{,.origin}; fi
    #iptables-restore < /etc/iptables/rules.v4.origin  # run the script multiple times
}

printInfo(){
    runScriptDate=`date`
    echo '**************************************** End ****************************************'
    echo "Output -> /root/$SCRIPTNAME.log"

    if $iscreate; then
        cat >> /root/$SCRIPTNAME.log <<- EOF
$runScriptDate
Arguments: $ARGS
------------------------------------------------------------------------------------------
User Name: $_user
User Password: $_password
SSH Port: $_sshport
Private Key(id_rsa.$_user):
$(cat /home/$_user/.ssh/id_rsa.$_user)
Password(id_rsa.$_user): $_passwd_key
FQDN: $_fqdn
Hostname: $_hostname
APT Source: $_source
TimeZone: $_tz
NTP: $_ntp
------------------------------------------------------------------------------------------

EOF
    else
        cat >> /root/$SCRIPTNAME.log <<- EOF
$runScriptDate
Arguments: $ARGS
------------------------------------------------------------------------------------------
SSH Port: $_sshport
FQDN: $_fqdn
Hostname: $_hostname
APT Source: $_source
TimeZone: $_tz
NTP: $_ntp
------------------------------------------------------------------------------------------

EOF
    fi

    chmod 400 /root/$SCRIPTNAME.log
    str=`sed -n "/^${runScriptDate}$/,$ p" /root/$SCRIPTNAME.log`
    echo -e "\033[31m${str}\033[0m"
}

main(){
    getArguments
    setDefaultVariables
    setGlobalVariables
    updateOS
    setHostname
    setProfile
    setTimezone
    createUser
    setSSH
    setIptables
    printInfo
}

main

参考链接

https://www.debian.org/mirror/list

https://www.ntppool.org/zone/@

https://en.wikipedia.org/wiki/Regular_expression 注:需要爱国才可以打开的链接

https://www.wikiwand.com/en/Regular_expression 注:爱国与否都可以打开的链接

https://man7.org/linux/man-pages/man1/bash.1.html

https://www.gnu.org/software/bash/manual/html_node/Pattern-Matching.html