core: 支持 linux 设置普通账号

This commit is contained in:
bin456789
2026-05-29 07:25:16 +08:00
parent 74824ef7b4
commit bd48e70776
4 changed files with 683 additions and 165 deletions
+7 -5
View File
@@ -12,9 +12,11 @@ jobs:
os: [ubuntu-latest, windows-latest]
include:
- os: ubuntu-latest
command: sudo bash reinstall.sh --debug --password 123@@@
command: sudo bash reinstall.sh --debug --username x --password x
command_no_username_option: sudo bash reinstall.sh --debug
- os: windows-latest
command: ./reinstall.bat --debug --password 123@@@
command: ./reinstall.bat --debug --username x --password x
command_no_username_option: ./reinstall.bat --debug
runs-on: ${{ matrix.os }}
steps:
- run: |
@@ -35,9 +37,9 @@ jobs:
# ${{ matrix.command }} arch
# ${{ matrix.command }} gentoo
${{ matrix.command }} netboot.xyz
${{ matrix.command }} dd --img=https://download.opensuse.org/tumbleweed/appliances/openSUSE-MicroOS.x86_64-SelfInstall.raw.xz
${{ matrix.command }} windows --image-name='Windows Server blah' --iso https://aka.ms/HCIReleaseImage --username administrator
${{ matrix.command_no_username_option }} netboot.xyz
${{ matrix.command }} dd --img=https://cloud.debian.org/images/cloud/sid/daily/latest/debian-sid-nocloud-amd64-daily.tar.xz
${{ matrix.command }} windows --image-name='Windows Server blah' --iso https://aka.ms/HCIReleaseImage
${{ matrix.command }} reset
+115 -29
View File
@@ -24,13 +24,9 @@ d-i mirror/country string manual
# d-i mirror/http/hostname string deb.debian.org
# B.4.5. 帐号设置
d-i passwd/make-user boolean false
# 注意如果用 ssh key 后面还要删除密码
# d-i passwd/root-password password ''
# d-i passwd/root-password-again password ''
# d-i passwd/root-password-crypted password ''
# kali 需要下面这行,否则会提示输入用户名
d-i passwd/root-login boolean true
# kali 需要设置这行,否则会提示输入用户名
# 因为 kali installer initrd 内置了一个 preseed.cfg,设置了 passwd/root-login false
# d-i passwd/root-login boolean true
# B.4.6. 时钟与时区设置
d-i time/zone string Asia/Shanghai
@@ -121,6 +117,19 @@ d-i grub-installer/force-efi-extra-removable boolean true
# debian 11+ 才有 websocketd
# debian 9 sshd_config 不支持 Include
# di 环境下 /etc/passwd 设置了 root 的家目录是 /,不是常见的 /root
# 我们不修改它,防止出问题
# di 环境下 锁定用户将无法登录 ssh
# passwd/root-password-crypted 是 ! 开头时会提示输入密码
# 因此这个值设成 *
# https://salsa.debian.org/installer-team/user-setup/-/blob/1.109/user-setup-ask?ref_type=tags#L35
# screen 需要设置 +s 权限,否则普通用户无法 attach 到 root 的 screen 会话
# 有 /cdrom/simple-cdd 才安装 simple-cdd-profiles
# 不然安装时 control 脚本会报错:
# Loading simple-cdd-profiles failed for unknown reasons
@@ -133,6 +142,9 @@ d-i grub-installer/force-efi-extra-removable boolean true
# 此时还没有配置源,anna-install 会在配置完源后再安装
d-i preseed/early_command string true; \
for str in $(grep -wo "extra_[^ ]*" /proc/cmdline | sed 's/^extra_//'); do eval "$str"; done; \
username=${username:-root}; \
ssh_port=${ssh_port:-22}; \
web_port=${web_port:-80}; \
di(){ \
echo "d-i $*" >/tmp/selections.cfg; \
@@ -150,6 +162,11 @@ d-i preseed/early_command string true; \
cp -f /etc/screenrc.bak /etc/screenrc; \
}; \
chmod +s /usr/bin/screen; \
screen -x root/ -X multiuser on; \
screen -x root/ -X acladd "$username"; \
if [ "$hold" = 1 ]; then \
di auto-install/enable boolean false; \
di debconf/priority select low; \
@@ -160,7 +177,9 @@ d-i preseed/early_command string true; \
echo 'Option 1. View logs:'; \
echo ' tail -fn+1 /var/log/syslog'; \
echo 'Option 2. Attach to the installer:'; \
echo ' TERM=screen screen -xp1'; \
echo ' TERM=screen screen -x root/ -p 1'; \
echo 'Option 3. Attach to the root shell if you are not root:'; \
echo ' TERM=screen screen -x root/ -p 2'; \
} >>/etc/motd; \
mem=$(grep ^MemTotal: /proc/meminfo | { read -r _ y _; echo "$((y / 1024))"; }); \
if command -v websocketd && [ "$mem" -ge 400 ]; then \
@@ -170,10 +189,7 @@ d-i preseed/early_command string true; \
fi; \
sleep 5; \
done; \
if [ -z "$web_port" ]; then \
web_port=80; \
fi; \
run_as_service_with_screen websocketd --port 80 --loglevel=fatal --staticdir=/tmp \
run_as_service_with_screen websocketd --port "$web_port" --loglevel=fatal --staticdir=/tmp \
sh -c "tail -fn+0 /var/log/syslog | tr '\r' '\n' | grep -Fiv -e password -e token" ; \
fi; \
fi; \
@@ -182,25 +198,64 @@ d-i preseed/early_command string true; \
di finish-install/reboot_in_progress note; \
fi; \
if [ -s /configs/ssh_keys ]; then \
di passwd/root-password-crypted password "''"; \
mkdir -p /home; \
chmod 755 /home; \
if [ "$username" = "root" ]; then \
uid=0; \
scope=root; \
user_home=/; \
di passwd/root-login boolean true; \
di passwd/make-user boolean false; \
else \
di passwd/root-password-crypted password "$(cat /configs/password-linux-sha512)"; \
uid=1000; \
scope=user; \
user_home=/home/$username; \
di passwd/root-login boolean false; \
di passwd/make-user boolean true; \
di passwd/user-fullname string "$username"; \
di passwd/username string "$username"; \
fi; \
mkdir -p /etc/ssh; \
true >/etc/ssh/sshd_config; \
if [ -s /configs/ssh_keys ]; then \
(umask 077; mkdir -p /.ssh; cat /configs/ssh_keys >/.ssh/authorized_keys); \
password_hash_for_initrd=''; \
password_hash_for_preseed='*'; \
else \
echo "PermitRootLogin yes" >>/etc/ssh/sshd_config; \
password_hash_for_initrd=$(cat /configs/password-linux-sha512); \
password_hash_for_preseed=$(cat /configs/password-linux-sha512); \
fi; \
if [ -n "$ssh_port" ] && ! [ "$ssh_port" = 22 ]; then \
echo "Port $ssh_port" >>/etc/ssh/sshd_config; \
fi; \
grep -qs ^root: /etc/shadow || echo "root:$(cat /configs/password-linux-sha512):1:0:99999:7:::" >>/etc/shadow; \
di passwd/$scope-password-crypted password "$password_hash_for_preseed"; \
grep -qs ^$username: /etc/passwd || echo "$username:*:$uid:$uid:$username:$user_home:/bin/sh" >>/etc/passwd; \
grep -qs ^$username: /etc/group || echo "$username:*:$uid:" >>/etc/group; \
grep -qs ^$username: /etc/shadow || echo "$username:$password_hash_for_initrd:1:0:99999:7:::" >>/etc/shadow; \
grep -qs ^nogroup: /etc/group || echo "nogroup:*:65534:" >>/etc/group; \
grep -qs ^sshd: /etc/passwd || echo "sshd:*:100:65534::/run/sshd:/bin/false" >>/etc/passwd; \
mkdir -p /etc/ssh/; \
true >/etc/ssh/sshd_config; \
if [ -s /configs/ssh_keys ]; then \
( \
umask 077; \
mkdir -p "$user_home/.ssh"; \
cat /configs/ssh_keys >"$user_home/.ssh/authorized_keys"; \
); \
chown "$username:$username" "$user_home"; \
chown "$username:$username" "$user_home/.ssh"; \
chown "$username:$username" "$user_home/.ssh/authorized_keys"; \
echo "PasswordAuthentication no" >>/etc/ssh/sshd_config; \
else \
if [ "$username" = root ]; then \
echo "PermitRootLogin yes" >>/etc/ssh/sshd_config; \
fi; \
fi; \
if ! [ "$ssh_port" = 22 ]; then \
echo "Port $ssh_port" >>/etc/ssh/sshd_config; \
fi; \
mkdir -p /run/sshd; \
chmod 0755 /run/sshd; \
ssh-keygen -A; \
@@ -230,7 +285,11 @@ d-i preseed/early_command string true; \
# efi 分区大小未改变时,不会被格式化,因此需要手动删除旧系统的 efi 文件
# os-prober 卡太久,因此跳过
d-i partman/early_command string true; \
eval "$(grep -o 'extra_confhome=[^ ]*' /proc/cmdline | sed 's/^extra_//')"; \
for str in $(grep -wo "extra_[^ ]*" /proc/cmdline | sed 's/^extra_//'); do eval "$str"; done; \
username=${username:-root}; \
ssh_port=${ssh_port:-22}; \
web_port=${web_port:-80}; \
postinst=/var/lib/dpkg/info/bootstrap-base.postinst; \
cp $postinst $postinst.orig; \
@@ -276,6 +335,9 @@ d-i partman/early_command string true; \
# debian 9 tar 不支持 --strip-components
d-i preseed/late_command string true; \
for str in $(grep -wo "extra_[^ ]*" /proc/cmdline | sed 's/^extra_//'); do eval "$str"; done; \
username=${username:-root}; \
ssh_port=${ssh_port:-22}; \
web_port=${web_port:-80}; \
if [ "$elts" = 1 ]; then sed -i "s|deb\.freexian\.com/extended-lts|$deb_mirror|" /target/etc/apt/sources.list; fi; \
@@ -283,19 +345,43 @@ d-i preseed/late_command string true; \
in-target systemctl enable ssh; \
if [ -s /configs/ssh_keys ]; then \
(umask 077; mkdir -p /target/root/.ssh; cat /configs/ssh_keys >/target/root/.ssh/authorized_keys); \
in-target passwd -d root; \
if [ "$username" = root ]; then \
user_home=/root; \
else \
user_home=/home/$username; \
fi; \
if [ -s /configs/ssh_keys ]; then \
( \
umask 077; \
mkdir -p "/target/$user_home/.ssh"; \
cat /configs/ssh_keys >"/target/$user_home/.ssh/authorized_keys"; \
); \
in-target passwd -d -l "$username"; \
in-target chown "$username:$username" "$user_home"; \
in-target chown "$username:$username" "$user_home/.ssh"; \
in-target chown "$username:$username" "$user_home/.ssh/authorized_keys"; \
echo "PasswordAuthentication no" >/target/etc/ssh/sshd_config.d/01-passwordauthentication.conf || \
echo "PasswordAuthentication no" >>/target/etc/ssh/sshd_config; \
else \
if [ "$username" = root ]; then \
echo "PermitRootLogin yes" >/target/etc/ssh/sshd_config.d/01-permitrootlogin.conf || \
echo "PermitRootLogin yes" >>/target/etc/ssh/sshd_config; \
fi; \
fi; \
if [ -n "$ssh_port" ] && ! [ "$ssh_port" = 22 ]; then \
echo "Port $ssh_port" >/target/etc/ssh/sshd_config.d/01-change-ssh-port.conf || \
if ! [ "$ssh_port" = 22 ]; then \
echo "Port $ssh_port" >/target/etc/ssh/sshd_config.d/01-port.conf || \
echo "Port $ssh_port" >>/target/etc/ssh/sshd_config; \
fi; \
if ! [ "$username" = root ]; then \
printf '%s\n' "$username ALL=(ALL) NOPASSWD:ALL" >"/target/etc/sudoers.d/99-$username"; \
chmod 0440 "/target/etc/sudoers.d/99-$username"; \
fi; \
if ls /configs/frpc.* >/dev/null 2>&1; then \
mkdir -p /target/usr/local/bin; \
mkdir -p /target/usr/local/etc/frpc; \
+160 -57
View File
@@ -102,6 +102,7 @@ Usage: $reinstall_____ anolis 7|8|23
reset
Options: For Linux/Windows:
[--username USERNAME]
[--password PASSWORD]
[--ssh-key KEY]
[--ssh-port PORT]
@@ -1942,15 +1943,29 @@ verify_os_name() {
}
verify_os_args() {
# 必备参数
case "$distro" in
dd) [ -n "$img" ] || error_and_exit "dd need --img" ;;
redhat) [ -n "$img" ] || error_and_exit "redhat need --img" ;;
dd) [ -n "$img" ] || error_and_exit "dd need --img." ;;
redhat) [ -n "$img" ] || error_and_exit "redhat need --img." ;;
windows) [ -n "$image_name" ] || error_and_exit "Install Windows need --image-name." ;;
esac
# 用户名/密码/证书相关
case "$distro" in
netboot.xyz | windows) [ -z "$ssh_keys" ] || error_and_exit "not support ssh key for $distro" ;;
netboot.xyz)
[ -z "$username" ] || error_and_exit "not support set username for $distro."
[ -z "$password" ] || error_and_exit "not support set password for $distro."
[ -z "$ssh_keys" ] || error_and_exit "not support set ssh key for $distro."
;;
windows)
[ -z "$ssh_keys" ] || error_and_exit "not support set ssh key for $distro."
;;
esac
# 不能同时使用证书和密码
if [ -n "$password" ] && [ -n "$ssh_keys" ]; then
error_and_exit "Cannot set both password and ssh key."
fi
}
get_cmd_path() {
@@ -2341,30 +2356,28 @@ trim() {
}
assert_username_valid() {
if ! msg=$(is_username_valid); then
error_and_exit "$msg"
fi
}
is_username_valid() {
# https://learn.microsoft.com/windows-hardware/customize/desktop/unattend/microsoft-windows-shell-setup-useraccounts-localaccounts-localaccount-name
# 不能为 none [ ] / \ : | < > + = ; , ? * % @
# 账号为空,则使用 Administrator
# 账号为空
if [ -z "$username" ]; then
echo "Username: Will use the built-in Administrator account in ISO language."
return 0
error_and_exit "Username: Can not be empty."
fi
# 账号为 none
if [ "$(to_lower <<<"$username")" = none ]; then
echo "Username: Do not use the name \"NONE\", this is a restricted username."
return 1
error_and_exit "Username: Can not be 'none'."
fi
# 账号包含非法字符
if grep -q '[][/\:|<>+=;,?*%@]' <<<"$username"; then
echo "Username: Do not use any of the following characters: / \ [ ] : | < > + = ; , ? * % @"
return 1
error_and_exit "Username: Do not use any of the following characters: / \ [ ] : | < > + = ; , ? * % @"
fi
}
# trans.sh 有同名方法
is_administrator_username() {
username_in_lower=$(to_lower <<<"$1")
# 如果输入以下用户名则忽略,并使用系统内置的 Administrator 账号
# 防止系统有两个不同语言的 Administrator 账号而造成困扰
@@ -2376,27 +2389,38 @@ is_username_valid() {
администратор \
järjestelmänvalvoja \
rendszergazda; do
if [ "$(to_lower <<<"$username")" = "$builtin_username" ]; then
echo "Username: Will use the built-in Administrator account in ISO language."
unset username
if [ "$username_in_lower" = "$builtin_username" ]; then
return 0
fi
done
return 1
}
prompt_username() {
info "prompt username"
warn false "Leave blank to use Administrator"
warn false "不填写则使用 Administrator"
if [ "$distro" = windows ]; then
default_username=administrator
else
default_username=root
fi
warn false "Set username, leave blank to use $default_username"
warn false "设置用户名,不填写则使用 $default_username"
IFS= read -r -p "Username: " username
username="$(printf "%s" "$username" | trim)"
if [ -z "$username" ]; then
username=$default_username
fi
assert_username_valid
}
prompt_password() {
info "prompt password"
warn false "Leave blank to use a random password."
warn false "不填写则使用随机密码"
warn false "Set password, leave blank to use a random password."
warn false "设置密码,不填写则使用随机密码"
while true; do
IFS= read -r -p "Password: " password
if [ -n "$password" ]; then
@@ -3626,7 +3650,7 @@ EOF
# 2. 删除 debian busybox 无法识别的语法
# 3. 删除 apk 语句
# 4. debian 11/12 initrd 无法识别 > >
# 5. debian 11/12 initrd 无法识别 < <
# 5. debian 11/12 initrd 无法识别 < < ,注意可能分两行写
# 6. debian 11 initrd 无法识别 set -E
# 7. debian 11 initrd 无法识别 trap ERR
# 8. debian 9 initrd 无法识别 ${string//find/replace}
@@ -3637,11 +3661,16 @@ EOF
-e "s/> >/$replace/" \
-e "s/< </$replace/" \
-e "s/\. <\(/$replace/" \
-e "s/< \\\\/$replace/" \
-e "s/ <\(/$replace/" \
-e "s/^[[:space:]]*apk[[:space:]]/$replace/" \
-e "s/^[[:space:]]*trap[[:space:]]/$replace/" \
-e "s/\\$\{.*\/\/.*\/.*\}/$replace/" \
-e "/^[[:space:]]*set[[:space:]]/s/E//" \
$initrd_dir/trans.sh
# 测试魔改后的 trans.sh 有没有语法问题
bash -n $initrd_dir/trans.sh
}
get_disk_drivers() {
@@ -4499,9 +4528,7 @@ while true; do
shift 2
;;
--user | --username)
if ! [ "$distro" = windows ]; then
error_and_exit "$1 is only supported for installing Windows."
fi
[ -n "$2" ] || error_and_exit "Need value for $1"
username="$(printf "%s" "$2" | trim)"
assert_username_valid
shift 2
@@ -4682,7 +4709,7 @@ done
verify_os_args
# 用户名
if [ "$distro" = windows ] && [ -z "$username" ]; then
if ! is_netboot_xyz && [ -z "$username" ]; then
prompt_username
fi
@@ -4964,54 +4991,130 @@ fi
info 'info'
echo "$distro $releasever"
case "$distro" in
windows) username=${username:-administrator} ;;
netboot.xyz) username= ;;
dd | *) username=root ;;
esac
ssh_port=${ssh_port:-22}
rdp_port=${rdp_port:-3389}
web_port=${web_port:-80}
if [ -n "$username" ]; then
if [ "$distro" = netboot.xyz ]; then
:
elif [ "$distro" = alpine ] && [ "$hold" = 1 ]; then
info "Alpine Live OS"
echo "Username: $username"
if [ -n "$ssh_keys" ]; then
echo "Public Key: $ssh_keys"
else
echo "Password: $password"
fi
if [ -n "$ssh_port" ]; then
echo "SSH Port: $ssh_port"
fi
fi
if is_netboot_xyz; then
echo 'Reboot to start netboot.xyz.'
elif is_alpine_live; then
echo 'Reboot to start Alpine Live OS.'
elif is_use_dd; then
elif [ "$distro" = fnos ]; then
info "While Install (View Logs)"
echo "Username: $username"
if [ -n "$ssh_keys" ]; then
echo "Public Key: $ssh_keys"
else
echo "Password: $password"
fi
echo "SSH Port: $ssh_port"
echo "WEB Port: $web_port"
info "After Install"
echo "安装后不会开启 SSH 服务。"
echo "你需要尽快到 http://IP:5666 配置账号密码。"
echo
echo "SSH Service is disabled after installation."
echo "You need to config the username and password on http://IP:5666 as soon as possible."
elif [ "$distro" = windows ]; then
info "While Install (View Logs)"
echo "Username: $username"
echo "Password: $password"
echo "SSH Port: $ssh_port"
echo "WEB Port: $web_port"
info "After Install"
if is_administrator_username "$username"; then
echo "Username: $username (Depends on Windows iso's language)"
else
echo "Username: $username"
fi
echo "Password: $password"
echo "RDP Port: $rdp_port"
elif [ "$distro" = dd ]; then
info "While Install (View Logs)"
echo "Username: $username"
if [ -n "$ssh_keys" ]; then
echo "Public Key: $ssh_keys"
else
echo "Password: $password"
fi
echo "SSH Port: $ssh_port"
echo "WEB Port: $web_port"
info "After Install"
if [ -n "$cloud_data" ]; then
echo "Cloud Data: $cloud_data"
echo "Cloud Data Files: $cloud_data_files"
fi
show_dd_password_tips
echo 'Reboot to start DD.'
elif [ "$distro" = fnos ]; then
echo "Special note for FNOS:"
echo "Reboot to start the installation."
echo "SSH login is disabled when installation completed."
echo "You need to config the account and password on http://SERVER_IP:5666 as soon as possible."
echo
echo "飞牛 OS 注意事项:"
echo "重启后开始安装。"
echo "安装完成后不支持 SSH 登录。"
echo "你需要尽快在 http://SERVER_IP:5666 配置账号密码。"
else
echo "Reboot to start the installation."
echo "Username: [Depends on image]"
echo "Public Key: [Depends on image]"
echo "Password: [Depends on image]"
echo "SSH Port: [Depends on image]"
fi
else
# 普通 linux
info "While Install (View Logs)"
echo "Username: $username"
if [ -n "$ssh_keys" ]; then
echo "Public Key: $ssh_keys"
else
echo "Password: $password"
fi
echo "SSH Port: $ssh_port"
echo "WEB Port: $web_port"
info "After Install"
echo "Username: $username"
if [ -n "$ssh_keys" ]; then
echo "Public Key: $ssh_keys"
else
echo "Password: $password"
fi
echo "SSH Port: $ssh_port"
fi
if is_in_windows; then
echo
echo 'You can run this command to reboot:'
echo 'shutdown /r /t 0'
fi
echo
echo "If you want to revert all changes made by this script, run \"$reinstall_____ reset\""
if [ "$distro" = netboot.xyz ]; then
echo '重启后进入 netboot.xyz。'
echo "或者现在运行 \"$reinstall_____ reset\" 以清除该引导项。"
echo
echo 'Reboot to start netboot.xyz.'
echo "Or run \"$reinstall_____ reset\" now to clear this boot entry."
echo
elif [ "$distro" = alpine ] && [ "$hold" = 1 ]; then
echo '重启后进入 Alpine Live OS。'
echo "或者现在运行 \"$reinstall_____ reset\" 以清除该引导项。"
echo
echo 'Reboot to start Alpine Live OS.'
echo "Or run \"$reinstall_____ reset\" now to clear this boot entry."
echo
else
warn false '警告:重装会清除主硬盘的所有数据,包括所有分区!'
echo '重启后开始重装。'
echo "或者现在运行 \"$reinstall_____ reset\" 以取消重装。"
echo
warn false 'Warning: Reinstalling will erase all data on the main disk, including all partitions!'
echo 'Reboot to start the reinstallation.'
echo "Or run \"$reinstall_____ reset\" now to cancel the reinstallation."
fi
echo
+393 -66
View File
@@ -46,8 +46,24 @@ warn() {
error_and_exit() {
error "$@"
echo "Run '/trans.sh' to retry." >&2
echo "Run '/trans.sh alpine' to install Alpine Linux instead." >&2
if is_have_cmd sudo; then
sudo_='sudo '
elif is_have_cmd doas; then
sudo_='doas '
else
sudo_=
fi
echo "Run '$sudo_/trans.sh' to retry." >&2
echo "Run '$sudo_/trans.sh alpine' to install Alpine Linux instead." >&2
# 解除锁定,允许用户登录处理故障
# passwd -u "$username" >/dev/null
# 用不着,因为 alpine 锁定账户后无法登录 ssh
# 因此不会锁定
exit 1
}
@@ -292,9 +308,6 @@ setup_nginx() {
wget $confhome/logviewer.html -O /logviewer.html
wget $confhome/logviewer-nginx.conf -O /etc/nginx/http.d/default.conf
if [ -z "$web_port" ]; then
web_port=80
fi
sed -i "s/@WEB_PORT@/$web_port/gi" /etc/nginx/http.d/default.conf
# rc-service -q nginx start
@@ -310,10 +323,6 @@ setup_websocketd() {
wget $confhome/logviewer.html -O /tmp/index.html
apk add coreutils
if [ -z "$web_port" ]; then
web_port=80
fi
pkill websocketd || true
# websocketd 遇到 \n 才推送,因此要转换 \r 为 \n
websocketd --port "$web_port" --loglevel=fatal --staticdir=/tmp \
@@ -424,6 +433,16 @@ extract_env_from_cmdline() {
fi
done < <(xargs -n1 </proc/cmdline | grep "^${prefix}_" | sed "s/^${prefix}_//")
done
# 如果空白则设置默认值
if [ "$distro" = windows ]; then
username=${username:-administrator}
else
username=${username:-root}
fi
ssh_port=${ssh_port:-22}
rdp_port=${rdp_port:-3389}
web_port=${web_port:-80}
}
ensure_service_started() {
@@ -1553,6 +1572,7 @@ install_alpine() {
printf '\n' | chroot /os setup-ntp || true
# 设置公钥
add_user_if_need /os
if is_need_set_ssh_keys; then
set_ssh_keys_and_del_password /os
fi
@@ -1795,21 +1815,69 @@ install_nixos() {
nix_swap="swapDevices = [{ device = \"/swapfile\"; size = $swap_size; }];"
fi
# keys
if is_need_set_ssh_keys; then
nix_ssh_keys_or_PermitRootLogin="
services.openssh.settings.PasswordAuthentication = false;
users.users.root.openssh.authorizedKeys.keys = [
nix_user_keys_fragment="
openssh.authorizedKeys.keys = [
$(del_comment_lines </configs/ssh_keys | del_empty_lines | quote_line | add_space 2)
];
"
fi
# root user
if [ "$username" = root ]; then
if is_need_set_ssh_keys; then
nix_users="
users.users.$username = {
$(echo "$nix_user_keys_fragment" | add_space 2)
};
"
else
nix_ssh_keys_or_PermitRootLogin='services.openssh.settings.PermitRootLogin = "yes";'
nix_users=""
fi
else
# normal user
# https://nixos.org/manual/nixos/stable/#sec-user-management
nix_users=$(
cat <<EOF
users.users.$username = {
isNormalUser = true;
home = "/home/$username";
extraGroups = [
"wheel"
"networkmanager"
];
$(echo "$nix_user_keys_fragment" | add_space 2)
};
security.sudo.extraRules = [
{ users = [ "$username" ]; commands = [ { command = "ALL"; options = [ "NOPASSWD" ]; } ]; }
];
EOF
)
fi
# openssh
nix_openssh="
services.openssh = {
enable = true;
$(
{
if is_need_change_ssh_port; then
nix_ssh_ports="services.openssh.ports = [ $ssh_port ];"
echo "ports = [ $ssh_port ];"
fi
if is_need_set_ssh_keys; then
echo 'settings.PasswordAuthentication = false;'
fi
if [ "$username" = root ] && ! is_need_set_ssh_keys; then
echo 'settings.PermitRootLogin = "yes";'
fi
} | add_space 2
)
};
"
# frpc
if ls /configs/frpc.* >/dev/null 2>&1; then
nix_frpc=$(
if false; then
@@ -1856,9 +1924,8 @@ $nix_bootloader
$nix_swap
$nix_substituters
boot.kernelParams = [ $(get_ttys console= | quote_word) ];
services.openssh.enable = true;
$nix_ssh_keys_or_PermitRootLogin
$nix_ssh_ports
$nix_users
$nix_openssh
$nix_frpc
$(cat /tmp/nixos_network_config.nix)
###################################################
@@ -1906,7 +1973,7 @@ EOF
# 设置密码
if ! is_need_set_ssh_keys; then
echo "root:$(get_password_linux_sha512)" | nixos-enter --root /os -- \
printf '%s\n' "$username:$(get_password_linux_sha512)" | nixos-enter --root /os -- \
/run/current-system/sw/bin/chpasswd -e
fi
@@ -2042,12 +2109,13 @@ basic_init() {
fi
# 公钥/密码
add_user_if_need "$os_dir"
if is_need_set_ssh_keys; then
set_ssh_keys_and_del_password $os_dir
change_ssh_conf_for_root_key_login $os_dir
change_ssh_conf_for_key_login $os_dir
else
change_root_password $os_dir
change_ssh_conf_for_root_password_login $os_dir
change_user_password $os_dir
change_ssh_conf_for_password_login $os_dir
fi
# 下载 fix-eth-name.service
@@ -2119,6 +2187,10 @@ EOF
if [ "$(uname -m)" = aarch64 ]; then
pkgs="$pkgs archlinuxarm-keyring"
fi
if ! [ "$username" = root ]; then
pkgs="$pkgs sudo"
fi
pacstrap -K $os_dir $pkgs
# dns
@@ -2270,6 +2342,10 @@ EOF
chroot $os_dir emerge sys-fs/dosfstools
fi
if ! [ "$username" = root ]; then
chroot $os_dir emerge app-admin/sudo
fi
# firmware + microcode
if fw_pkgs=$(get_ucode_firmware_pkgs) && [ -n "$fw_pkgs" ]; then
chroot $os_dir emerge $fw_pkgs
@@ -3179,7 +3255,7 @@ modify_windows() {
# 5. 设置用户密码永不过期
# Azure 的 Windows 实例,初始用户的密码也是永不过期的
# 管理员账号默认不会过期
if [ -n "$username" ]; then
if ! is_administrator_username "$username"; then
cat <<EOF >$os_dir/windows-set-user-password-never-expires.bat
wmic useraccount where name="$username" set passwordexpires=false || ^
powershell -NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass -Command "Set-LocalUser -Name '$username' -PasswordNeverExpires \$true"
@@ -3451,6 +3527,10 @@ remove_or_disable_cloud_init() {
rm -f $os_dir/etc/cloud/cloud.cfg.rpmsave
;;
zypper)
# 防止删除 cloud-init 时自动删除 sudo
if ! [ "$username" = root ]; then
sed -i '/^sudo$/d' "$os_dir/var/lib/zypp/AutoInstalled"
fi
# 加上 -u 才会删除依赖
chroot $os_dir zypper remove -y -u cloud-init cloud-init-config-suse
;;
@@ -3562,6 +3642,7 @@ EOF
# find_and_mount /boot
# find_and_mount /boot/efi
# fedora 的 fstab 还有 /home /var,因此用 mount -a
# 不然无法往 /home/$username 写入 ssh 公钥
chroot $os_dir mount -a
cp_resolv_conf $os_dir
@@ -3823,7 +3904,7 @@ EOF
chroot $os_dir zypper remove -y --force-resolution $origin_kernel
fi
if $need_password_workaround; then
chroot $os_dir passwd -d root
chroot $os_dir passwd -d -l root
fi
fi
@@ -3860,7 +3941,7 @@ EOF
# 在这里修改密码,而不是用cloud-init,因为我们的默认密码太弱
is_password_plaintext && sed -i 's/enforce=everyone/enforce=none/' $os_dir/etc/security/passwdqc.conf
change_root_password $os_dir
change_user_password $os_dir
is_password_plaintext && sed -i 's/enforce=none/enforce=everyone/' $os_dir/etc/security/passwdqc.conf
# 下载仓库,选择 profile
@@ -4045,19 +4126,125 @@ create_swap() {
fi
}
set_ssh_keys_and_del_password() {
os_dir=$1
info 'set ssh keys'
del_user_password_and_lock() {
local os_dir=$1
local username=$2
# 添加公钥
(
umask 077
mkdir -p $os_dir/root/.ssh
cat /configs/ssh_keys >$os_dir/root/.ssh/authorized_keys
)
# 锁定用户后 ssh 能否登录
# alpine ×
# 其它系统 √
# root 空密码,不锁定 root,其它用户用 su - root 能否切换到 root
# alpine ×
# 其它系统 √
# centos 7 不支持一行命令同时 -d -l
# passwd: Only one of -l, -u, -d, -S may be specified.
# 删除密码
chroot $os_dir passwd -d root
chroot "$os_dir" passwd -d "$username"
# 锁定用户
if ! [ -e "$os_dir/etc/alpine-release" ]; then
chroot "$os_dir" passwd -l "$username"
fi
# alpine 锁定用户无法登录 ssh
# 因为 alpine 默认不开启 pam
# 其他系统默认开启
# 不开启 pam 的话,锁定用户无法登录 ssh
# 开启 pam 后可以
# alpine 是通过安装 openssh-server-pam 开启 pam
# 不需要设置 UsePAM yes 也无法识别 UsePAM yes
# localhost:~# sshd -G | grep -i pam
# /etc/ssh/sshd_config line 88: Unsupported option UsePAM
}
set_ssh_keys_and_del_password() {
local os_dir=$1
info 'set ssh keys'
if [ "$username" = root ]; then
local user_home="/root"
else
local user_home="/home/$username"
fi
# 添加公钥
if true; then
(
umask 077
mkdir -p "$os_dir/$user_home/.ssh"
cat /configs/ssh_keys >"$os_dir/$user_home/.ssh/authorized_keys"
)
# 注意要用 chroot,否则 uid/gid 是 alpine live os 下的 uid/gid
chroot "$os_dir" chown "$username:$username" "$user_home"
chroot "$os_dir" chown "$username:$username" "$user_home/.ssh"
chroot "$os_dir" chown "$username:$username" "$user_home/.ssh/authorized_keys"
else
(
# 如果日后添加 bsd 无法 chroot 时可以这样
umask 077
read -r owner group < \
<(awk -F: -v user="$username" '$1==user {print $3,$4}' "$os_dir/etc/passwd")
install -D \
-m 600 \
-o "$owner" \
-g "$group" \
/configs/ssh_keys \
"$os_dir/$user_home/.ssh/authorized_keys"
)
fi
# 删除密码/锁定用户
del_user_password_and_lock "$os_dir" "$username"
# debian 云镜像 /etc/shadow 的 root 条目为
# root:!unprovisioned:20591:0:99999:7:::
# 首次开机会停在设置 root 密码界面,且阻塞 ssh 服务
# 因此这里手动清空 root 密码并锁定
if ! [ "$username" = root ] && is_have_cmd_on_disk "$os_dir" systemd-firstboot; then
del_user_password_and_lock "$os_dir" root
fi
}
_is_ssh_kv_effective() {
local os_dir=$1
local key=$2
local value=$3
# centos 7 不支持 -G
if res=$(chroot "$os_dir" sshd -G 2>/dev/null || chroot "$os_dir" sshd -T 2>/dev/null); then
printf "%s\n" "$res" | grep -Fxiq "$key $value"
else
error_and_exit "Failed to verify sshd config."
fi
}
is_ssh_kv_effective() {
local os_dir=$1
local key=$2
local value=$3
if _is_ssh_kv_effective "$os_dir" "$key" "$value"; then
return 0
fi
# centos 7 设置 prohibit-password sshd -T 会显示成 without-password
if [ "$(echo "$key" | to_lower)" = "permitrootlogin" ] && {
[ "$(echo "$value" | to_lower)" = "prohibit-password" ] ||
[ "$(echo "$value" | to_lower)" = "without-password" ]
}; then
if _is_ssh_kv_effective "$os_dir" "permitrootlogin" "prohibit-password" ||
_is_ssh_kv_effective "$os_dir" "permitrootlogin" "without-password"; then
return 0
fi
fi
return 1
}
change_ssh_conf_if_different() {
@@ -4079,7 +4266,7 @@ change_ssh_conf_if_different() {
# PasswordAuthentication no
# 0. 如果已经有这个配置,则不修改,避免不必要的改动
if chroot "$os_dir" sshd -G | grep -Fxiq "$key $value"; then
if is_ssh_kv_effective "$os_dir" "$key" "$value"; then
return
fi
@@ -4109,19 +4296,25 @@ change_ssh_conf_if_different() {
echo "$key $value" >>$os_dir/etc/ssh/sshd_config
fi
fi
# 验证是否成功
if ! is_ssh_kv_effective "$os_dir" "$key" "$value"; then
error_and_exit "Failed to set sshd config $key $value."
fi
}
change_ssh_conf_for_root_key_login() {
change_ssh_conf_for_key_login() {
local os_dir=$1
# 目前脚本只用 root ,不需要设置这个
# change_ssh_conf_if_different "$os_dir" PasswordAuthentication no
change_ssh_conf_if_different "$os_dir" PasswordAuthentication no
# 这个也不需要设置,默认就是 prohibit-password
# change_ssh_conf_if_different "$os_dir" PermitRootLogin prohibit-password
# centos 7 PermitRootLogin 默认是 yes,而不是 prohibit-password
if [ "$username" = root ]; then
change_ssh_conf_if_different "$os_dir" PermitRootLogin prohibit-password
fi
}
change_ssh_conf_for_root_password_login() {
change_ssh_conf_for_password_login() {
local os_dir=$1
# opensuse 16/tumbleweed 安装 openssh-server-config-rootlogin
@@ -4137,7 +4330,10 @@ change_ssh_conf_for_root_password_login() {
# PasswordAuthentication 默认是 yes
# 但某些发行版会在 sshd_config.d 里设置 PasswordAuthentication no
change_ssh_conf_if_different "$os_dir" PasswordAuthentication yes
if [ "$username" = root ]; then
change_ssh_conf_if_different "$os_dir" PermitRootLogin yes
fi
}
change_ssh_port() {
@@ -4147,10 +4343,117 @@ change_ssh_port() {
change_ssh_conf_if_different "$os_dir" Port "$ssh_port"
}
change_root_password() {
os_dir=$1
# 暂时用不着
add_user_if_need_for_alpine() {
local os_dir=$1
info 'change root password'
if ! grep -q "^$username:" "$os_dir/etc/passwd"; then
# -a Create admin user. Add to wheel group and set up doas
# -u Unlock the user automatically (eg. creating the user non-interactively
# with an ssh key for login)
if is_need_set_ssh_keys; then
chroot "$os_dir" setup-user -a -u -k "$(cat /configs/ssh_keys)" "$username"
else
chroot "$os_dir" setup-user -a -u "$username"
change_user_password $os_dir
fi
fi
}
add_user_if_need() {
local os_dir=$1
# 添加用户
if ! grep -q "^$username:" "$os_dir/etc/passwd"; then
# debian 推荐使用 adduser 而不是 useradd
# https://manpages.debian.org/trixie/passwd/useradd.8.en.html
# useradd is a low level utility for adding users.
# On Debian, administrators should usually use adduser(8) instead.
# adduser 会从 /etc/adduser.conf 读取默认要添加的组
# 然而通常这个值是空白
# alpine
if is_have_cmd_on_disk "$os_dir" adduser &&
chroot "$os_dir" adduser --help 2>&1 | grep -Fq -- BusyBox; then
chroot "$os_dir" adduser --disabled-password "$username"
# debian/ubuntu
elif is_have_cmd_on_disk "$os_dir" adduser &&
chroot "$os_dir" adduser --help 2>&1 | grep -Fq -- '--disabled-password'; then
chroot "$os_dir" adduser --disabled-password --comment '' "$username"
# el
elif is_have_cmd_on_disk "$os_dir" adduser &&
chroot "$os_dir" adduser --help 2>&1 | grep -Fq -- '--password'; then
chroot "$os_dir" adduser --password ! "$username"
# arch/gentoo 默认没有 adduser
else
chroot "$os_dir" useradd -m "$username"
fi
fi
# 添加到 wheel/sudo 组
if ! [ "$username" = root ]; then
if [ -e "$os_dir/etc/alpine-release" ]; then
# alpine
# https://github.com/alpinelinux/alpine-conf/blob/master/setup-user.in#L168
# 安装 doas
chroot "$os_dir" apk add doas doas-sudo-shim
mkdir -p "$os_dir/etc/doas.d"
# 添加用户到组
chroot "$os_dir" addgroup "$username" wheel
# doas: 添加 wheel 组
local file="$os_dir/etc/doas.d/20-wheel.conf"
local content="permit persist :wheel"
if ! grep -q "^$content" "$file" 2>/dev/null; then
echo "$content" >>"$file"
fi
# doas: 添加单个用户 nopass
echo "permit nopass $username" >"$os_dir/etc/doas.d/99-$username.conf"
else
# 通常用 wheel 组
# debian/ubuntu 没有 wheel 组,只有 sudo 组
# aws lightsail 上测试默认用户加入了哪些组
# debian admin : admin adm dialout cdrom floppy sudo audio dip video plugdev
# ubuntu ubuntu : ubuntu adm cdrom sudo dip lxd
# almalinux ec2-user : ec2-user adm systemd-journal
# opensuse ec2-user : ec2-user
# 添加用户到组
for group in \
wheel sudo \
adm dialout cdrom floppy audio dip video plugdev lxd systemd-journal; do
if grep -q "^$group:" "$os_dir/etc/group"; then
# chroot "$os_dir" addgroup "$username" "$group"
chroot "$os_dir" usermod -aG "$group" "$username"
fi
done
# sudo: gentoo 安装 sudo 后也没有 /etc/sudoers.d
if ! [ -d "$os_dir/etc/sudoers.d" ]; then
install -d -m 0750 "$os_dir/etc/sudoers.d"
fi
# sudo: 添加单个用户 NOPASSWD
# https://wiki.archlinux.org/title/Sudo#Sudoers_default_file_permissions
local file="$os_dir/etc/sudoers.d/99-$username"
printf '%s\n' "$username ALL=(ALL) NOPASSWD:ALL" >"$file"
chmod 0440 "$file"
fi
fi
}
change_user_password() {
local os_dir=$1
info 'change user password'
if is_password_plaintext; then
pam_d=$os_dir/etc/pam.d
@@ -4184,13 +4487,13 @@ change_root_password() {
# 分两行写,不然遇到错误不会终止
plaintext=$(get_password_plaintext)
echo "root:$plaintext" | chroot $os_dir chpasswd
printf '%s\n' "$username:$plaintext" | chroot $os_dir chpasswd
if $has_pamd_chpasswd; then
mv $pam_d/chpasswd.orig $pam_d/chpasswd
fi
else
echo "root:$(get_password_linux_sha512)" | chroot $os_dir chpasswd -e
printf '%s\n' "$username:$(get_password_linux_sha512)" | chroot $os_dir chpasswd -e
fi
}
@@ -4460,6 +4763,7 @@ chroot_apt_autoremove() {
del_default_user() {
os_dir=$1
local user
while read -r user; do
if grep ^$user':\$' "$os_dir/etc/shadow"; then
echo "Deleting user $user"
@@ -4593,18 +4897,20 @@ install_fnos() {
mount_pseudo_fs /os
# 更改密码
if false; then
if is_need_set_ssh_keys; then
set_ssh_keys_and_del_password $os_dir
else
change_root_password $os_dir
change_user_password $os_dir
fi
fi
# ssh root 登录,测试用
if false; then
if is_need_set_ssh_keys; then
change_ssh_conf_for_root_key_login $os_dir
change_ssh_conf_for_key_login $os_dir
else
change_ssh_conf_for_root_password_login $os_dir
change_ssh_conf_for_password_login $os_dir
fi
chroot $os_dir systemctl enable ssh
fi
@@ -5756,6 +6062,26 @@ is_nt_ver_ge() {
[ "$orig" = "$sorted" ]
}
# reinstall.sh 有同名方法
is_administrator_username() {
username_in_lower=$(printf "%s" "$1" | to_lower)
for builtin_username in \
administrator \
administrador \
administrateur \
administratör \
администратор \
järjestelmänvalvoja \
rendszergazda; do
if [ "$username_in_lower" = "$builtin_username" ]; then
return 0
fi
done
return 1
}
get_cloud_vendor() {
# busybox blkid 不显示 sr0 的 UUID
apk add lsblk
@@ -7218,26 +7544,26 @@ EOF
/tmp/autounattend.xml
# 账号密码
if [ -n "$username" ]; then
# 普通账号
password_base64=$(get_password_windows_user_base64)
xmlstarlet ed -L -N x="urn:schemas-microsoft-com:unattend" \
-d "//x:AdministratorPassword" \
/tmp/autounattend.xml
sed -i \
-e "s|%enable_administrator%|0|" \
-e "s|%user_username%|$username|" \
-e "s|%user_password%|$password_base64|" \
/tmp/autounattend.xml
else
if is_administrator_username "$username"; then
# Administrator
password_base64=$(get_password_windows_administrator_base64)
xmlstarlet ed -L -N x="urn:schemas-microsoft-com:unattend" \
-d "//x:LocalAccounts" \
/tmp/autounattend.xml
sed -i \
-e "s|%enable_administrator%|1|" \
-e "s|%administrator_password%|$password_base64|" \
-e "s|%enable_administrator%|1|gi" \
-e "s|%administrator_password%|$password_base64|gi" \
/tmp/autounattend.xml
else
# 普通账号
password_base64=$(get_password_windows_user_base64)
xmlstarlet ed -L -N x="urn:schemas-microsoft-com:unattend" \
-d "//x:AdministratorPassword" \
/tmp/autounattend.xml
sed -i \
-e "s|%enable_administrator%|0|gi" \
-e "s|%user_username%|$username|gi" \
-e "s|%user_password%|$password_base64|gi" \
/tmp/autounattend.xml
fi
@@ -7838,13 +8164,14 @@ if is_need_change_ssh_port; then
fi
# 设置密码,添加开机启动 + 开启 ssh 服务
add_user_if_need /
if is_need_set_ssh_keys; then
set_ssh_keys_and_del_password /
# 目前脚本只用 root,不需要设置这个
# change_ssh_conf_if_different / PasswordAuthentication no
change_ssh_conf_for_key_login /
printf '\n' | setup-sshd
else
change_root_password /
change_user_password /
change_ssh_conf_for_password_login /
printf '\nyes' | setup-sshd
fi