| #!/bin/sh |
| |
| # YesLogin |
| # Copyright 2015-2024 Rivoreo |
| # |
| # Permission is hereby granted, free of charge, to any person obtaining |
| # a copy of this software and associated documentation files (the |
| # "Software"), to deal in the Software without restriction, including |
| # without limitation the rights to use, copy, modify, merge, publish, |
| # distribute, sublicense, and/or sell copies of the Software, and to |
| # permit persons to whom the Software is furnished to do so, subject to |
| # the following conditions: |
| # |
| # The above copyright notice and this permission notice shall be included |
| # in all copies or substantial portions of the Software. |
| # |
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
| # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
| # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
| # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
| # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| |
| |
| set -e |
| |
| setup_dir="${0%/*}" |
| if [ "$setup_dir" != "$0" ] && [ "$setup_dir" != . ] && [ "$setup_dir" != "$PWD" ] && [ ! "$setup_dir" -ef . ]; then |
| echo "Error: You must run this program under the directory where it resides" 1>&2 |
| exit 1 |
| fi |
| unset setup_dir |
| |
| if [ -z "$HOME" ] || [ ! -d "$HOME" ]; then |
| echo "Error: Home directory didn't exist, please check the HOME environment variable" 1>&2 |
| exit 1 |
| fi |
| |
| replace_chattr= |
| replace_reboot= |
| replace_tar= |
| replace_sbin_init= |
| configure_sudo_io_logging= |
| install_procd= |
| install_update_blacklist= |
| install_chflags= |
| remove_utempter= |
| remove_cloud_init= |
| bind_protection= |
| harden= |
| reinstall_config_files= |
| |
| for a in "$@"; do case "$a" in |
| --replace-chattr) |
| replace_chattr=1 |
| ;; |
| --replace-chattr-without-backup) |
| replace_chattr=2 |
| ;; |
| --replace-reboot) |
| replace_reboot=1 |
| ;; |
| --replace-reboot-with-reminder) |
| replace_reboot=2 |
| ;; |
| --replace-tar) |
| replace_tar=1 |
| ;; |
| --replace-sbin-init) |
| replace_sbin_init=1 |
| ;; |
| --configure-sudo-io-logging) |
| configure_sudo_io_logging=1 |
| ;; |
| --install-procd) |
| install_procd=1 |
| ;; |
| --install-update-blacklist) |
| install_update_blacklist=1 |
| ;; |
| --install-chflags) |
| install_chflags=1 |
| ;; |
| --remove-utempter) |
| remove_utempter=1 |
| ;; |
| --remove-cloud-init) |
| remove_cloud_init=1 |
| ;; |
| --bind-protection) |
| bind_protection=1 |
| ;; |
| --harden) |
| harden=1 |
| ;; |
| --reinstall-config-files) |
| reinstall_config_files=1 |
| ;; |
| -h|--help) |
| cat << EOT |
| Usage: $0 [<options>] |
| |
| Options: |
| --replace-chattr[-without-backup] |
| --replace-reboot[-with-reminder] |
| --replace-tar |
| --replace-sbin-init |
| --configure-sudo-io-logging |
| --install-procd |
| --install-update-blacklist |
| --install-chflags |
| --remove-utempter |
| --remove-cloud-init |
| --bind-protection |
| --harden |
| --reinstall-config-files |
| |
| EOT |
| exit 0 |
| ;; |
| --) |
| break |
| ;; |
| -*) |
| printf "Error: Unknown option '%s'\\n" "$a" 1>&2 |
| exit 255 |
| ;; |
| *) |
| printf "Warning: Extra operand '%s'\\n" "$a" 1>&2 |
| ;; |
| esac done |
| |
| if [ -n "$harden" ] && [ "$replace_chattr" != 2 ]; then |
| echo "Error: Hardening requires '--replace-chattr-without-backup'" 1>&2 |
| exit 255 |
| fi |
| |
| need_fix_path= |
| seen_usr_local_bin= |
| seen_usr_local_sbin= |
| seen_usr_bin= |
| seen_usr_sbin= |
| seen_bin= |
| seen_sbin= |
| ORIG_IFS="$IFS" |
| IFS=: |
| for d in $PATH; do case "$d" in |
| /usr/local/bin) |
| seen_usr_local_bin=1 |
| if [ -n "$seen_usr_bin" ]; then |
| printf 'Warning: Inappropriate PATH (%s): /usr/local/bin is placed after /usr/bin, fixing\n' "$PATH" 1>&2 |
| export PATH="/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin" |
| seen_usr_local_sbin=1 |
| need_fix_path=1 |
| break |
| fi |
| ;; |
| /usr/local/sbin) |
| seen_usr_local_sbin=1 |
| if [ -n "$seen_usr_sbin" ]; then |
| printf 'Warning: Inappropriate PATH (%s): /usr/local/sbin is placed after /usr/sbin, fixing\n' "$PATH" 1>&2 |
| export PATH="/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin" |
| seen_usr_local_bin=1 |
| need_fix_path=1 |
| break |
| fi |
| ;; |
| /usr/bin) |
| seen_usr_bin=1 |
| ;; |
| /usr/sbin) |
| seen_usr_sbin=1 |
| ;; |
| /bin) |
| if [ -z "$seen_usr_bin" ]; then |
| printf 'Warning: Inappropriate PATH (%s): /bin has seen before /usr/bin, fixing\n' "$PATH" 1>&2 |
| export PATH="/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin" |
| seen_usr_local_bin=1 |
| seen_usr_local_sbin=1 |
| need_fix_path=1 |
| break |
| fi |
| seen_bin=1 |
| ;; |
| /sbin) |
| if [ -z "$seen_usr_sbin" ]; then |
| printf 'Warning: Inappropriate PATH (%s): /sbin has seen before /usr/sbin, fixing\n' "$PATH" 1>&2 |
| export PATH="/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin" |
| seen_usr_local_bin=1 |
| seen_usr_local_sbin=1 |
| need_fix_path=1 |
| break |
| fi |
| seen_sbin=1 |
| ;; |
| esac done |
| IFS="$ORIG_IFS" |
| if [ -z "$seen_usr_local_sbin" ]; then |
| export PATH="/usr/local/sbin:$PATH" |
| need_fix_path=1 |
| fi |
| if [ -z "$seen_usr_local_bin" ]; then |
| export PATH="/usr/local/bin:$PATH" |
| need_fix_path=1 |
| fi |
| unset seen_usr_local_bin |
| unset seen_usr_local_sbin |
| unset seen_usr_bin |
| unset seen_usr_sbin |
| unset seen_bin |
| unset seen_sbin |
| |
| KERNEL_NAME="`uname -s`" |
| |
| is_gnu() { |
| [ -x /lib/ld-linux.so.2 ] || [ -x /lib/ld-linux.so.3 ] || [ -x /lib/ld-linux-armhf.so.3 ] || [ -f /lib64/ld-linux-x86-64.so.2 ] && return |
| getconf GNU_LIBC_VERSION 2> /dev/null | grep -q ^glibc |
| } |
| |
| . fileflags/which.sh || true |
| |
| [ -x /usr/bin/cattr ] && chattr() { cattr "$@"; } |
| |
| if [ -n "$replace_chattr" ]; then |
| for f in chattr lsattr; do |
| if [ "`dd bs=1 count=9 if=/usr/bin/$f 2> /dev/null`" = "#!/bin/sh" ]; then |
| eval "${f}_replaced=1" |
| else |
| eval "${f}_replaced=" |
| fi |
| done |
| if [ -n "$chattr_replaced" ] && [ -n "$lsattr_replaced" ]; then |
| echo "chattr(1) and lsattr(1) appear already been replaced" 1>&2 |
| replace_chattr= |
| fi |
| fi |
| |
| [ "$KERNEL_NAME" = Linux ] && ! which chflags > /dev/null && chflags() { sh fileflags/chflags.ext2ioctl.sh "$@"; } |
| |
| set_immutable() { |
| set_immutable_ok= |
| chflags_output="`chflags simmutable \"$@\" 2>&1`" && set_immutable_ok=1 |
| chattr_output="`chattr +i -- \"$@\" 2>&1`" && set_immutable_ok=1 |
| [ -z "$set_immutable_ok" ] && printf %s\\n%s\\n "$chflags_output" "$chattr_output" 1>&2 |
| unset chflags_output chattr_output |
| [ -n "$set_immutable_ok" ] |
| } |
| |
| set_append_only() { |
| set_append_only_ok= |
| chflags_output="`chflags sappend \"$@\" 2>&1`" && set_append_only_ok=1 |
| chattr_output="`chattr +a -- \"$@\" 2>&1`" && set_append_only_ok=1 |
| [ -z "$set_append_only_ok" ] && printf %s\\n%s\\n "$chflags_output" "$chattr_output" 1>&2 |
| unset chflags_output chattr_output |
| [ -n "$set_append_only_ok" ] |
| } |
| |
| install_program() { |
| [ -f "$2" ] && [ -x "$2" ] && cmp -s -- "$2" "$1" && return |
| rm -rf -- "$2" |
| cp -- "$1" "$2" |
| chmod -- 755 "$2" |
| [ "$KERNEL_NAME" = Linux ] && chcon -- system_u:object_r:shell_exec_t:s0 "$2" > /dev/null 2>&1 || true |
| } |
| |
| if perl_program_path="`which perl 2> /dev/null`"; then |
| if [ "`dd \"if=$perl_program_path\" bs=11 count=1 2> /dev/null`" = "#!/bin/bash" ]; then |
| eval "`grep -E '^REAL_PERL=["/a-zA-Z0-9._-]+\$' \"$perl_program_path\"`" |
| if [ -f "$REAL_PERL" ] && [ -x "$REAL_PERL" ]; then |
| printf "Found real perl at '%s' from existing perl wrapper\\n" "$REAL_PERL" 1>&2 |
| perl_path="${REAL_PERL%/*}" |
| export REAL_PERL |
| else |
| perl_path= |
| REAL_PERL= |
| fi |
| else |
| [ -f /var/log/perl.log ] || touch /var/log/perl.log |
| chmod 666 /var/log/perl.log |
| set_append_only /var/log/perl.log |
| |
| perl_path=/usr/lib/perl5 |
| for d in /usr/lib/perl5 /usr/lib/perl /usr/lib64/perl5 /usr/lib64/perl /usr/lib32/perl5 /usr/lib32/perl /usr/local/lib/perl5 /usr/local/lib/perl /opt/perl5 /opt/perl; do |
| [ -d "$d" ] && perl_path="$d" && break |
| done |
| [ -d "$perl_path" ] || mkdir "$perl_path" |
| mv "$perl_program_path" "$perl_path/" |
| export REAL_PERL="$perl_path/perl" |
| sed -E "s ^REAL_PERL=.+ REAL_PERL=$REAL_PERL " perl > "$perl_program_path" |
| chmod 755 "$perl_program_path" |
| set_immutable "$perl_program_path" || true |
| fi |
| [ -n "$harden" ] && set_immutable "$REAL_PERL" |
| else |
| perl_path= |
| REAL_PERL= |
| fi |
| |
| [ -d /var/log/noshell ] || mkdir /var/log/noshell |
| chmod 1733 /var/log/noshell |
| |
| [ -f /var/log/shd.log ] || touch /var/log/shd.log |
| chmod 666 /var/log/shd.log || true |
| set_append_only /var/log/shd.log |
| |
| for f in "$HOME/.bashrc" "$HOME/.profile"; do |
| [ -f "$f" ] && continue |
| if [ -e "$f" ]; then |
| printf "Warning: %s isn't a regular file, recreating\\n" "$f" 1>&2 |
| rm -f "$f" |
| fi |
| true >> "$f" |
| done |
| |
| need_source_system_bashrc= |
| if [ ! -f "$HOME/.bashrc" ]; then |
| printf 'Warning: %s is missing! Fixing\n' "$HOME/.bashrc" 1>&2 |
| need_source_system_bashrc=1 |
| elif ! grep -Fq -e /etc/bash/bashrc -e /etc/bash.bashrc -e /etc/bashrc "$HOME/.bashrc"; then |
| printf 'Warning: Missing reference to system bashrc from %s. Fixing\n' "$HOME/.bashrc" 1>&2 |
| need_source_system_bashrc=1 |
| elif ! grep -Eq "^PROMPT_COMMAND='.+ /dev/shm" "$HOME/.bashrc" && grep -Fq PROMPT_COMMAND= "$HOME/.bashrc"; then |
| printf 'Warning: PROMPT_COMMAND redefined in %s\n' "$HOME/.bashrc" 1>&2 |
| fi |
| if [ -n "$need_source_system_bashrc" ]; then |
| for f in /etc/bash/bashrc /etc/bash.bashrc /etc/bashrc ""; do |
| if [ -n "$f" ]; then |
| [ -f "$f" ] || continue |
| printf '\nsource %s\n' "$f" >> "$HOME/.bashrc" |
| else |
| echo "Warning: Cannot find system bashrc file in any known location" 1>&2 |
| printf 'Failed to fix %s\n' "$HOME/.bashrc" 1>&2 |
| fi |
| break |
| done |
| fi |
| |
| append_uniq_line() { |
| grep --fixed-strings --line-regexp --quiet -- "$1" "$2" && return |
| printf %s\\n "$1" >> "$2" |
| } |
| |
| chmod u+rw,a+x "$HOME" |
| if [ -n "$need_fix_path" ]; then |
| append_uniq_line "export PATH=\"$PATH\"" "$HOME/.profile" |
| [ -f "$HOME/.bash_profile" ] && append_uniq_line "export PATH=\"$PATH\"" "$HOME/.bash_profile" |
| fi |
| if [ -n "$reinstall_config_files" ] || [ ! -f "$HOME/.shdrc" ]; then |
| cp shdrc "$HOME/.shdrc" |
| chmod 640 "$HOME/.shdrc" |
| fi |
| source_shdrc_code='[ -z "$SHELL_DEF" ] && [ -r "$HOME/.shdrc" ] && dd if="$HOME/.shdrc" of=/dev/null bs=1 count=1 > /dev/null 2>&1 && . "$HOME/.shdrc"' |
| append_uniq_line "$source_shdrc_code" "$HOME/.bashrc" |
| append_uniq_line "$source_shdrc_code" "$HOME/.profile" |
| [ -f "$HOME/.bash_profile" ] && append_uniq_line "$source_shdrc_code" "$HOME/.bash_profile" |
| [ -f /etc/ssh/sshd_config ] && sftp_server_program=`sed -En 's/^[[:space:]]*Subsystem[[:space:]]+sftp[[:space:]]+//p' /etc/ssh/sshd_config` || sftp_server_program=/usr/lib/openssh/sftp-server |
| for f in "$HOME/.blacklist" "$HOME/.bash_history" "$HOME/.ash_commands" "$HOME/.login.log"; do |
| if [ ! -h "$f" ]; then |
| [ -e "$f" ] || continue |
| [ -f "$f" ] && continue |
| fi |
| rm -rf "$f" |
| done |
| if [ -n "$harden" ]; then |
| set_immutable "$HOME/.shdrc" "$HOME/.bashrc" |
| printf '%s and %s has been made immutable, you will have to undo this before editing these files\n' "$HOME/.shdrc" "$HOME/.bashrc" 1>&2 |
| [ -f "$HOME/.blacklist" ] || true >> "$HOME/.blacklist" |
| set_append_only "$HOME/.blacklist" |
| fi |
| |
| if [ -n "$reinstall_config_files" ] || [ ! -f /etc/rshrc ]; then |
| rm -rf /etc/rshrc |
| cp rshrc /etc/rshrc |
| chmod 644 /etc/rshrc |
| fi |
| install_program noshellhere /bin/noshellhere |
| if rm -f /bin/mbash; then |
| sed -E "s#^SFTP_SERVER_PROGRAM=.+#SFTP_SERVER_PROGRAM=$sftp_server_program#" mbash > /bin/mbash |
| chmod 755 /bin/mbash |
| [ "$KERNEL_NAME" = Linux ] && chcon system_u:object_r:shell_exec_t:s0 /bin/mbash > /dev/null 2>&1 || true |
| elif [ ! -f /bin/mbash ] || [ ! -x /bin/mbash ]; then |
| echo "Failed to remove non-regular or non-executable file /bin/mbash" 1>&2 |
| exit 1 |
| fi |
| [ -n "$harden" ] && set_immutable /etc/rshrc /bin/noshellhere /bin/mbash |
| |
| for f in /usr/bin/script /usr/bin/telnet /usr/bin/sudo /usr/bin/fakeroot-ng; do |
| [ -x "$f" ] || printf "Warning: %s not found\\n" "$f" |
| done 1>&2 |
| |
| tmp_piao= |
| rm -f /tmp/true |
| printf '#!/bin/sh\nexit 0\n' > /tmp/true |
| chmod 755 /tmp/true |
| if ! /tmp/true; then |
| echo "Warning: Cannot execute program under '/tmp'!" 1>&2 |
| echo "Please make sure that '/tmp/' isn't mounted with 'noexec'" 1>&2 |
| tmp_piao=1 |
| fi |
| rm -f /tmp/true |
| |
| if grep -Fqs /dev/shm /usr/bin/fakeroot-ng; then |
| echo "Warning: /usr/bin/fakeroot-ng uses /dev/shm" 1>&2 |
| fi |
| |
| if [ -d /dev/shm ] && ! grep -Eq 'PROMPT_COMMAND=.+ /dev/shm' /etc/bash/bashrc /etc/bash.bashrc /etc/bashrc > /dev/null 2>&1; then |
| echo "Warning: /dev/shm is not guarded from bash(1)" 1>&2 |
| fi |
| |
| for u in pischi dudu ad1tz systemd; do |
| uid_and_shell="`grep ^$u: /etc/passwd | cut -s -d : -f 3,7`" || continue |
| [ -z "$uid_and_shell" ] && continue |
| if printf %s\\n "${uid_and_shell%%:*}" | grep -Eq '^0+$'; then |
| echo "Warning: user account '$u' exists with UID 0" 1>&2 |
| fi |
| shell="${uid_and_shell#*:}" |
| [ -z "$shell" ] && shell=/bin/sh |
| case "$shell" in |
| */bin/noshellhere|*/bin/mbash) |
| ;; |
| */bin/false) |
| ;; |
| */bin/nologin|*/sbin/nologin) |
| ;; |
| *) |
| printf "Warning: user account '%s' exists with a likely non-audited shell '%s'\\n" $u "$shell" 1>&2 |
| ;; |
| esac |
| unset shell |
| done |
| unset u uid_and_shell |
| |
| if is_gnu; then |
| [ ! -f /var/log/btmp ] && touch /var/log/btmp && chmod 600 /var/log/btmp |
| [ -f /var/log/wtmp ] || touch /var/log/wtmp |
| fi |
| |
| if systemctl is-enabled --quiet basic.target > /dev/null 2>&1; then |
| systemctl disable --now systemd-tmpfiles-clean systemd-tmpfiles-clean.timer || true |
| systemctl mask systemd-tmpfiles-clean systemd-tmpfiles-clean.timer || true |
| fi |
| |
| if [ -n "$replace_chattr" ]; then |
| for p in ch ls; do |
| f=${p}attr |
| eval "[ -n \"\$${f}_replaced\" ]" && continue |
| [ -f /usr/bin/$f ] && [ -x /usr/bin/$f ] || continue |
| new_name=${p%?}attr |
| if [ ! -h /usr/bin/$new_name ] && [ -f /usr/bin/$new_name ] && [ -s /usr/bin/$new_name ]; then |
| if [ -s /usr/bin/$f ] && ! cmp /usr/bin/$f /usr/bin/$new_name; then |
| echo "Error: Both /usr/bin/$f and /usr/bin/$new_name exist" 1>&2 |
| exit 1 |
| fi |
| rm -f /usr/bin/$f |
| else |
| rm -rf /usr/bin/$new_name |
| mv /usr/bin/$f /usr/bin/$new_name |
| fi |
| cp $f /usr/bin/$f |
| chmod 755 /usr/bin/$f |
| cattr +i /usr/bin/$f || chflags simmutable /usr/bin/$f |
| done |
| chattr() { cattr "$@"; } |
| fi |
| |
| if [ -n "$replace_reboot" ]; then |
| if [ "$replace_reboot" = 2 ]; then |
| replacement_reboot_program=reboot.reminder |
| else |
| replacement_reboot_program=reboot |
| fi |
| reboot_program=/usr/sbin/reboot |
| is_already_replaced= |
| for f in /usr/sbin/reboot /sbin/reboot /usr/sbin/shutdown /sbin/shutdown; do |
| [ -f $f ] || continue |
| [ -x $f ] || continue |
| [ -h $f ] && continue |
| if cmp -s $f $replacement_reboot_program; then |
| reboot_program=$f |
| is_already_replaced=1 |
| break |
| fi |
| if rm -f $f. mv $f $f. && chmod 0 $f.; then |
| [ -n "$harden" ] && set_immutable $f. |
| reboot_program=$f |
| break |
| fi > /dev/null 2>&1 |
| done |
| if [ -z "$is_already_replaced" ]; then |
| rm -f $reboot_program |
| cp $replacement_reboot_program $reboot_program |
| chmod 755 $reboot_program |
| fi |
| [ -n "$harden" ] && set_immutable $reboot_program |
| dir=${reboot_program%/*} |
| reboot_program=${reboot_program##*/} |
| for f in reboot halt poweroff shutdown; do |
| [ $f != $reboot_program ] && ln -sf $reboot_program $dir/$f |
| done |
| fi |
| |
| if [ -n "$replace_tar" ]; then |
| standard_tar_path= |
| for f in /usr/bin/tar /bin/tar; do [ -x $f ] && standard_tar_path=$f && break; done |
| if [ -n "$standard_tar_path" ]; then |
| if tar_in_path="`which tar`" && [ "$tar_in_path" != $standard_tar_path ]; then |
| i=0 |
| while [ -h "$tar_in_path" ] && tar_in_path="`readlink -- \"$tar_in_path\"`" && [ $i -lt 16 ] |
| do i=$((i+1)); done |
| [ ! -h "$tar_in_path" ] && [ "$tar_in_path" != $standard_tar_path ] && chmod a-x -- "$tar_in_path" |
| fi |
| if [ "`which tar`" = $standard_tar_path ]; then |
| if [ "`dd if=$standard_tar_path bs=2 count=1 2> /dev/null`" != "#!" ] && |
| tar_version="`tar --version 2> /dev/null`" && |
| if printf %s "$tar_version" | grep -Fq -e " (GNU tar" -e " Free Software Foundation"; then |
| new_path=${standard_tar_path%/tar}/gnutar |
| elif printf %s "$tar_version" | grep -q "^bsdtar "; then |
| new_path=${standard_tar_path%/tar}/bsdtar |
| else |
| false |
| fi |
| then |
| if [ $standard_tar_path -ef $new_path ] && { [ ! -h $new_path ] || [ -h $standard_tar_path ]; }; then |
| rm -f $standard_tar_path |
| else |
| mv $standard_tar_path $new_path |
| fi |
| cat > $standard_tar_path << EOF |
| #!/bin/bash |
| [ "\${1#-}" = "\$1" ] && exec -a tar $new_path |
| exec -a tar $new_path "\$@" |
| EOF |
| chmod 755 $standard_tar_path |
| set_immutable $standard_tar_path |
| [ -n "$harden" ] && set_immutable $new_path |
| else |
| echo "tar(1) implementation not supported, skip replacing" 1>&2 |
| [ -z "$tar_version" ] && set_immutable $standard_tar_path |
| fi |
| else |
| echo "tar(1) in PATH is not $standard_tar_path, skip replacing" 1>&2 |
| fi |
| else |
| echo "Neither /usr/bin/tar nor /bin/tar available, skip replacing" 1>&2 |
| fi |
| fi |
| |
| if [ -n "$replace_sbin_init" ]; then |
| if [ "$KERNEL_NAME" != Linux ]; then |
| echo "Replacing /sbin/init requires a Linux-based operating system, skip replacing" 1>&2 |
| elif [ -x /sbin/init ]; then |
| if link_target="`readlink --canonicalize /sbin/init`" && [ "${link_target%/systemd}" != "$link_target" ]; then |
| if rm -f /sbin/init; then |
| canonicalized_init_path="`readlink --canonicalize /sbin/init 2> /sbin/init`" |
| cat > /sbin/init << EOF |
| #!/bin/sh |
| case "\$0" in |
| "\`readlink --canonicalize /sbin/init\`") |
| ;; |
| "$canonicalized_init_path") |
| ;; |
| /sbin/init) |
| ;; |
| *) |
| echo "Must be run as /sbin/init" 1>&2 |
| exit 1 |
| ;; |
| esac |
| if [ \$\$ != 1 ]; then |
| echo "Must be run as PID 1" 1>&2 |
| exit 1 |
| fi |
| kernel_version="\`uname -r\`" |
| chmod a-rw /dev/shm |
| rm -f /dev/kmsg |
| [ -h /var/shm ] || ln -s run/shm /var/ |
| rm -f "/lib/modules/\$kernel_version/kernel/net/bpfilter/bpfilter.ko" |
| rm -f "/lib/modules/\$kernel_version/kernel/net/bpfilter/bpfilter.ko"* |
| exec "$link_target" --show-status "\$@" |
| EOF |
| chmod 755 /sbin/init |
| chcon system_u:object_r:init_exec_t:s0 /sbin/init || true |
| set_immutable /sbin/init |
| else |
| echo "Failed to replace /sbin/init" |
| fi |
| else |
| echo "/sbin/init isn't a symbolic link to systemd, skip replacing" 1>&2 |
| fi |
| else |
| echo "/sbin/init not executable, skip replacing /sbin/init" 1>&2 |
| fi |
| fi |
| |
| if [ -n "$configure_sudo_io_logging" ]; then |
| if [ ! -x "`which sudo`" ] || [ ! -x "`which visudo`" ]; then |
| echo "Configuring Sudo I/O logging is requested, but Sudo didn't appear to be |
| correctly installed" 1>&2 |
| exit 1 |
| fi |
| sudoers=/etc/sudoers |
| if [ -f /etc/sudoers ]; then |
| sudoers=/etc/sudoers |
| elif [ -f /usr/local/etc/sudoers ]; then |
| sudoers=/usr/local/etc/sudoers |
| else |
| echo "Configuring Sudo I/O logging is requested, but I couldn't find the sudoers |
| file in usual locations" 1>&2 |
| exit 1 |
| fi |
| append_or_set_words() { |
| if [ -n "$new_value" ]; then |
| eval "$1=" |
| append_value="$new_value" |
| elif [ -z "$append_value" ]; then |
| return |
| fi |
| while new_value="${append_value#[ ]}" && [ "$new_value" != "$append_value" ] |
| do append_value="$new_value" |
| done |
| while new_value="${append_value%[ ]}" && [ "$new_value" != "$append_value" ] |
| do append_value="$new_value" |
| done |
| append_value="${append_value#\"}" |
| append_value="${append_value%\"}" |
| eval "$1=\"\$$1 |
| \$append_value\"" |
| } |
| new_sudoers="`mktemp`" |
| trap 'rm -f "$new_sudoers"' EXIT |
| chmod 600 "$new_sudoers" |
| if echo "Defaults iolog_flush" | visudo -cq -f -; then |
| payload="Defaults log_input |
| Defaults log_output |
| Defaults !compress_io |
| Defaults iolog_flush |
| Defaults log_year |
| Defaults !set_utmp" |
| else |
| payload="Defaults log_input |
| Defaults log_output |
| Defaults !compress_io |
| Defaults log_year |
| Defaults !set_utmp" |
| fi |
| printf %s\\n "$payload" | while read -r line; do |
| grep --fixed-strings --line-regexp --quiet "$line" "$sudoers" || exit |
| done && payload= |
| i=0 |
| last_defaults_line= |
| has_env_reset= |
| has_always_set_home= |
| env_keep= |
| while read -r line; do |
| i=$((i+1)) |
| case "$line" in |
| Defaults[\ \ ]*) |
| append_value= |
| new_value= |
| last_defaults_line=$i |
| option_name="${line#Defaults[ ]}" |
| option_name=${option_name#${option_name%%[\!a-z]*}} |
| if [ "${option_name%%+=*}" != "$option_name" ]; then |
| append_value="${option_name#*+=}" |
| option_name=${option_name%%+=*} |
| elif [ "${option_name%%=*}" != "$option_name" ]; then |
| new_value="${option_name#*=}" |
| option_name=${option_name%%=*} |
| fi |
| option_name=${option_name%${option_name##*[a-z]}} |
| case $option_name in |
| env_reset|always_set_home) |
| eval "has_$option_name=1" |
| ;; |
| !env_reset|!always_set_home) |
| eval "has_$option_name=" |
| ;; |
| env_keep) |
| append_or_set_words env_keep |
| ;; |
| esac |
| ;; |
| esac |
| done < "$sudoers" |
| for i in env_reset always_set_home; do |
| eval "value=\$has_$i" |
| [ -n "$value" ] && continue |
| echo "Defaults $i" | visudo -cq -f - || continue |
| payload="Defaults $i |
| $payload" |
| done |
| add_env_keep= |
| for e in SYSTEMD_PAGER SYSTEMD_URLIFY SSH_CLIENT SSH_CONNECTION; do |
| for k in $env_keep; do [ $k = $e ] && continue 2; done |
| [ -n "$add_env_keep" ] && add_env_keep="$add_env_keep $e" || add_env_keep="$e" |
| done |
| [ -n "$add_env_keep" ] && payload="$payload |
| Defaults env_keep += \"$add_env_keep\"" |
| if [ -n "$last_defaults_line" ]; then |
| i=0 |
| while read -r line; do |
| i=$((i+1)) |
| printf %s\\n "$line" |
| if [ $i = $last_defaults_line ]; then |
| echo |
| printf %s\\n "$payload" |
| fi |
| done |
| else |
| cat |
| printf %s\\n "$payload" |
| fi < "$sudoers" > "$new_sudoers" |
| if ! visudo -c -f "$new_sudoers"; then |
| echo "Error configuring Sudo I/O logging" 1>&2 |
| rm -f "$new_sudoers" |
| exit 1 |
| fi |
| cat "$new_sudoers" > "$sudoers" |
| for f in "$sudoers" "$sudoers".d/*; do |
| [ -h "$f" ] && continue |
| [ -e "$f" ] || break |
| [ -f "$f" ] || continue |
| no_tty_users="`sed -En 's/^Defaults:(icinga|wp\\-toolkit)[[:space:]]+!requiretty\$/\\1/p' \"$f\"`" || continue |
| [ -z "$no_tty_users" ] && continue |
| for u in $no_tty_users; do |
| if grep -Eq "^Defaults:$u[[:space:]]+!log_input\$" "$f" && \ |
| grep -Eq "^Defaults:$u[[:space:]]+!log_output\$" "$f"; then |
| continue |
| fi |
| sed -E 's/^((Defaults:(icinga|wp\-toolkit)[[:space:]]+)!requiretty)$/\1\ |
| \2!log_input\ |
| \2!log_output/' "$f" > "$new_sudoers" |
| visudo -cq -f "$new_sudoers" || continue 2 |
| cat "$new_sudoers" > "$f" |
| [ "$f" != "$sudoers" ] && set_immutable "$f" |
| break |
| done |
| done |
| rm -f "$new_sudoers" |
| trap - EXIT |
| if sudoreplay --no-resize --list > /dev/null 2>&1; then |
| echo "Be sure to use option '--no-resize' when replaying I/O logs to prevent |
| sudoreplay(8) from messing your terminal" 1>&2 |
| fi |
| fi |
| |
| if [ -n "$install_procd" ]; then |
| if [ "$KERNEL_NAME" = Linux ]; then |
| install_program procd /usr/sbin/procd |
| if systemctl is-enabled --quiet basic.target > /dev/null 2>&1; then |
| cat > /etc/systemd/system/procd.service << EOF |
| [Unit] |
| Description=Process state daemon |
| ConditionPathIsReadWrite=/proc |
| |
| [Service] |
| Type=simple |
| ExecStart=/usr/sbin/procd |
| Restart=on-failure |
| RestartSec=10 |
| |
| [Install] |
| WantedBy=multi-user.target |
| EOF |
| if ! systemctl enable --now procd; then |
| # systemd too old to support '--now'? |
| systemctl enable procd |
| if ! systemctl start procd; then |
| echo "Failed to start procd via systemd, configure the service in other way?" 1>&2 |
| fi |
| fi |
| else |
| echo "/usr/sbin/procd installed, please manually configure a service to start it |
| on boot" 1>&2 |
| fi |
| else |
| echo "procd is available for Linux only, skip installing" 1>&2 |
| fi |
| fi |
| |
| if [ -n "$install_update_blacklist" ]; then |
| install_program update-blacklist /usr/bin/update-blacklist |
| [ -n "$harden" ] && set_immutable /usr/bin/update-blacklist |
| if ! crontab="`crontab -l`" || ! printf %s\\n "$crontab" | grep -Eq '^[0-9*].+ update\-blacklist$'; then |
| if ! printf %s "$crontab" | grep -Eq "^PATH[[:space:]]*="; then |
| crontab="PATH=/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin |
| $crontab" |
| fi |
| rand="`dd if=/dev/urandom count=1 bs=4 2> /dev/null | cksum`" |
| rand=${rand%% *} |
| if ! printf '%s\n%s */2 * * * update-blacklist\n' "$crontab" $((rand%60)) | crontab -; then |
| echo "Failed to install crontab, trying again without preserving previous content" 1>&2 |
| printf '%s\n%s */2 * * * update-blacklist\n' \ |
| PATH=/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin $((rand%60)) | crontab - |
| rm -f piao-crontab |
| if printf %s\\n "$crontab" > piao-crontab; then |
| echo "Problematic crontab with previous content has been saved to 'piao-crontab'" |
| else |
| echo "Failed to save problematic crontab with previous content" |
| fi 1>&2 |
| fi |
| fi |
| fi |
| |
| if [ -n "$install_chflags" ]; then |
| if [ "$KERNEL_NAME" = Linux ]; then |
| [ -d /usr/share/fileflags ] || mkdir /usr/share/fileflags |
| chmod 755 /usr/share/fileflags || true |
| if rm -f /usr/share/fileflags/functions; then |
| { |
| sh fileflags/build-function.sh |
| [ -n "$REAL_PERL" ] && printf '\nPERL5=%s\n' "$REAL_PERL" |
| } > /usr/share/fileflags/functions |
| chmod 644 /usr/share/fileflags/functions |
| elif [ ! -f /usr/share/fileflags/functions ]; then |
| echo "Failed to remove non-regular file /usr/share/fileflags/functions" 1>&2 |
| exit 1 |
| fi |
| if [ -d /usr/share/bash-completion/completions ]; then |
| cp fileflags/bash_completion.d/chflags /usr/share/bash-completion/completions/ |
| chmod 644 /usr/share/bash-completion/completions/chflags |
| elif [ -d /etc/bash/bash_completion.d ]; then |
| cp fileflags/bash_completion.d/chflags /etc/bash/bash_completion.d/ |
| chmod 644 /etc/bash/bash_completion.d/chflags |
| else |
| [ -d /etc/bash_completion.d ] || mkdir /etc/bash_completion.d |
| cp fileflags/bash_completion.d/chflags /etc/bash_completion.d/ |
| chmod 644 /etc/bash_completion.d/chflags |
| fi |
| [ -n "$harden" ] && set_immutable /usr/share/fileflags/functions |
| else |
| echo "This Unix-shell-based chflags(1) implementation is available for Linux only, skip installing" 1>&2 |
| fi |
| fi |
| |
| if [ -n "$remove_utempter" ]; then |
| for f in /usr/local/libexec/utempter/utempter /usr/libexec/utempter/utempter /usr/lib/*/utempter/utempter /usr/lib/utempter/utempter; do |
| [ ! -h "$f" ] && [ ! -f "$f" ] && continue |
| if rm -f "$f" > "$f" || rm -f "$f"; then |
| printf 'Removed %s\n' "$f" 1>&2 |
| mkdir "$f" && chmod 0 "$f" && set_immutable "$f" |
| fi |
| done |
| fi |
| |
| if [ -n "$remove_cloud_init" ]; then |
| for f in \ |
| /usr/bin/cloud-init* \ |
| /usr/share/doc/cloud-init \ |
| /usr/libexec/cloud-init \ |
| /usr/lib/cloud-init \ |
| /usr/lib/python*/site-packages/cloudinit \ |
| /usr/lib/python*/site-packages/cloud_init-* \ |
| /usr/lib/systemd/system/cloud-init* \ |
| /lib/systemd/system/cloud-config.* \ |
| /lib/systemd/system/cloud-final.service \ |
| /usr/lib/systemd/system-generators/cloud-init-generator \ |
| /lib/systemd/system-generators/cloud-init-generator \ |
| /etc/init.d/cloud-init* \ |
| /etc/rc*.d/*cloud-init* \ |
| /etc/systemd/system/cloud-init.target.wants \ |
| /etc/systemd/system/*.wants/cloud-init* \ |
| /etc/systemd/system/*.wants/cloud-config.service \ |
| /etc/systemd/system/*.wants/cloud-final.service \ |
| /usr/share/bash-completion/completions/cloud-init |
| do case "$f" in |
| */lib/systemd/system/*.*|*/lib/systemd/system-generators/*-generator) |
| [ -f "$f" ] || continue |
| rm -f "$f" |
| f="/etc/${f##*/lib/}" |
| [ -d "${f%/*}" ] || mkdir -p "${f%/*}" |
| ln -sf /dev/null "$f" |
| ;; |
| /etc/rc*.d/*|*.wants/*) |
| rm -f "$f" |
| ;; |
| *) |
| if [ -h "$f" ] || [ -f "$f" ]; then |
| [ -h "$f" ] || [ -w "$f" ] || continue |
| if rm -f "$f" > "$f" || rm -f "$f"; then |
| printf "Removed '%s'\\n" "$f" 1>&2 |
| mkdir -m 0 "$f" && set_immutable "$f" |
| fi |
| elif [ -d "$f" ]; then |
| [ -w "$f" ] || continue |
| if rm -rf "$f"; then |
| printf "Removed '%s'\\n" "$f" 1>&2 |
| chmod 0 "$f" > "$f" && set_immutable "$f" |
| fi |
| fi |
| ;; |
| esac done |
| systemctl disable --now cloud-init || true |
| systemctl mask cloud-init || true |
| fi |
| |
| if [ -n "$bind_protection" ]; then |
| if [ "$KERNEL_NAME" = Linux ]; then |
| [ -d /lib/init ] || mkdir -p /lib/init |
| install_program bindfiles /lib/init/bindfiles |
| enabled_bindfiles= |
| if systemctl is-enabled --quiet basic.target > /dev/null 2>&1; then |
| cat > /etc/systemd/system/bindfiles.service << EOF |
| [Unit] |
| Description=Bind mount important operating system files to protect them from being renamed or removed |
| Documentation=man:mount(8) |
| DefaultDependencies=off |
| Conflicts=shutdown.target |
| After=local-fs.target |
| |
| [Service] |
| Type=oneshot |
| ExecStart=/lib/init/bindfiles |
| |
| [Install] |
| WantedBy=sysinit.target |
| EOF |
| fi |
| systemctl enable --now bindfiles || systemctl enable bindfiles && enabled_bindfiles=1 |
| if [ ! -h /etc/init.d ] && [ -d /etc/init.d ] && [ ! -h /etc/rcS.d ] && [ -d /etc/rcS.d ]; then |
| cat > /etc/init.d/bindfiles << EOF |
| #!/bin/sh |
| ### BEGIN INIT INFO |
| # Provides: bindfiles |
| # Required-Start: \$local_fs mountall |
| # Required-Stop: |
| # Default-Start: S |
| # Default-Stop: |
| # Short-Description: Bind protection |
| # Description: Bind mount important operating system files to protect |
| # them from being renamed or removed |
| ### END INIT INFO |
| |
| |
| case "\$1" in |
| start) |
| exec /lib/init/bindfiles |
| ;; |
| stop) |
| # No-op |
| ;; |
| restart|reload|force-reload) |
| echo "Action \$1 not supported" 1>&2 |
| exit 3 |
| ;; |
| *) |
| printf 'Usage: %s {start|stop}\\n' "\$0" 1>&2 |
| exit 3 |
| ;; |
| esac |
| true |
| EOF |
| chmod 755 /etc/init.d/bindfiles |
| update-rc.d defaults bindfiles && enabled_bindfiles=1 |
| fi |
| /lib/init/bindfiles || echo "/lib/init/bindfiles failed" 1>&2 |
| [ -z "$enabled_bindfiles" ] && echo "Please manually configure a service to start /lib/init/bindfiles on boot" 1>&2 |
| else |
| echo "Bind protection is available for Linux only, skip installing" 1>&2 |
| fi |
| fi |
| |
| set +e |
| |
| should_reload_systemd= |
| for f in /etc/systemd/system/myservice.service /usr/lib/systemd/system/myservice.service /lib/systemd/system/myservice.service /etc/systemd/system/auto_script.service; do |
| [ -h "$f" ] && continue |
| [ -f "$f" ] || continue |
| printf "Warning: Found suspicious systemd unit file '%s', removing\\n" "$f" 1>&2 |
| if rm -f "$f" > "$f" || rm -f "$f"; then |
| mkdir -m 0 "$f" && set_immutable "$f" |
| should_reload_systemd=1 |
| fi |
| done |
| [ -n "$should_reload_systemd" ] && systemctl daemon-reload |
| |
| for f in "$HOME/.cfg" "$HOME/.ccfg" "$HOME/.md" /var/tmp/systemd-private-f7732edae7f9494cb9b8001dfdds81816-cups.service-Y4Kr3n /usr/bin/mslog /usr/bin/.ICE-unix /var/tmp/.ICE-unix; do |
| [ -h "$f" ] && continue |
| if [ -d "$f" ]; then |
| printf "Warning: Found suspicious directory '%s'\\n" "$f" 1>&2 |
| elif [ -f "$f" ]; then |
| # A regular file could already be created on the path by |
| # install-dos-vvnnmm-proxy.sh from fuck-vvnnmm, to protect |
| # the path from being reused |
| set_immutable "$f" |
| fi |
| done |
| |
| for f in /usr/local/lib/libextrasshd.so /usr/bin/player "$HOME/rcu_tasked" "$HOME/rcu_shader"; do |
| [ -h "$f" ] && continue |
| if [ -f "$f" ]; then |
| printf "Warning: Found suspicious regular file '%s'\\n" "$f" 1>&2 |
| elif [ -d "$f" ]; then |
| # A directory could already be created on the path by |
| # install-dos-vvnnmm-proxy.sh from fuck-vvnnmm, to protect |
| # the path from being reused |
| set_immutable "$f" |
| fi |
| done |
| |
| if [ -f /usr/bin/sshd ]; then |
| if ! [ /usr/bin/sshd -ef /usr/sbin/sshd ] && [ "`dd if=/usr/bin/sshd bs=2 count=1 2> /dev/null`" = "#!" ]; then |
| echo "Warning: Found suspicious regular file '/usr/bin/sshd', removing" 1>&2 |
| rm -f /usr/bin/sshd > /usr/bin/sshd || rm -f /usr/bin/sshd |
| mkdir -m 0 /usr/bin/sshd && set_immutable /usr/bin/sshd |
| fi |
| elif [ ! -h /usr/bin/sshd ] && [ -d /usr/bin/sshd ]; then |
| # A directory could already be created on the path by |
| # install-dos-vvnnmm-proxy.sh from fuck-vvnnmm, to protect the path |
| # from being reused |
| set_immutable /usr/bin/sshd |
| fi |
| |
| if [ -d /www/server/panel/plugin/clear ] || [ -f /www/server/panel/script/log_cleanup.py ]; then |
| # Fuck BT clear plugin as it may remove important log files |
| echo "Warning: Found directory for BT clear plugin" 1>&2 |
| for f in clear_main.py upgrade.sh repair.sh install.sh config.json /www/server/panel/script/log_cleanup.py; do |
| [ "${f#/}" = "$f" ] && f="/www/server/panel/plugin/clear/$f" |
| [ -h "$f" ] || [ -f "$f" ] || continue |
| rm -f "$f" |
| mkdir -m 0 "$f" |
| set_immutable "$f" |
| done |
| fi |
| |
| mask_securetmp_files() { |
| rm -f /usr/testDSK |
| mkdir /usr/testDSK |
| chmod 0 /usr/testDSK |
| set_immutable /usr/testDSK |
| rm -rf /usr/tmp.secure |
| chmod 0 /usr/tmp.secure > /usr/tmp.secure |
| set_immutable /usr/tmp.secure |
| } |
| |
| if [ -f /usr/tmpDSK ] && [ -s /usr/tmpDSK ]; then |
| # Fuck cPanel securetmp |
| echo "Warning: Found /usr/tmpDSK, truncating" 1>&2 |
| true > /usr/tmpDSK |
| mask_securetmp_files |
| if grep -q ^/usr/tmpDSK /etc/fstab; then |
| rm -f /etc/fstab.new |
| sed -E -e /tmpDSK/d -e '/^\/tmp[[:space:]]+\/var\/tmp/d' /etc/fstab > /etc/fstab.new && mv /etc/fstab.new /etc/fstab |
| fi |
| if grep -Eq "^(/usr/tmpDSK|/dev/loop[0-9]+) /tmp " /proc/mounts; then |
| [ "${0#/tmp/}" != "$0" ] && rm -rf "${0%/setup.sh}" |
| umount -l /tmp/ |
| fi |
| systemctl is-enabled --quiet securetmp && systemctl disable --now securetmp |
| if grep -q "^export TMPDIR=/var/spool/logrotate/tmp" /etc/cron.daily/logrotate; then |
| rm -rf /etc/cron.daily/logrotate.new && |
| sed '/^export TMPDIR=\/var\/spool\/logrotate\//d' /etc/cron.daily/logrotate > /etc/cron.daily/logrotate.new && |
| mv /etc/cron.daily/logrotate.new /etc/cron.daily/logrotate && |
| set_immutable /etc/cron.daily/logrotate |
| rm -rf /var/spool/logrotate |
| fi |
| if loop_device="`losetup --associated /usr/tmpDSK`" && [ -n "$loop_device" ]; then |
| loop_device="${loop_device%%:*}" |
| cat /dev/urandom > "$loop_device" & |
| printf 'Trashing %s that associated with /usr/tmpDSK\n' "$loop_device" 1>&2 |
| fi |
| set_immutable /usr/tmpDSK > /usr/tmpDSK |
| elif [ -n "$tmp_piao" ]; then |
| mask_securetmp_files |
| if set_immutable /usr/tmpDSK > /usr/tmpDSK && rm -f /usr/tmpDSK > /dev/null 2>&1; then |
| echo "Warning: /usr/tmpDSK can be removed after being made immutable" 1>&2 |
| fi |
| fi |
| |
| if [ "$replace_chattr" = 2 ] || { [ -n "$harden" ] && [ -n "$install_chflags" ] && [ "$KERNEL_NAME" = Linux ]; }; then |
| rm -f /usr/bin/cattr |
| fi |