通过分析和删除不必要的软件包来强化 Ubuntu 22.04 服务器

通过分析和删除不必要的软件包来强化 Ubuntu 22.04 服务器

我读过 Jay Lacroix 的《精通 Ubuntu 服务器》一书,他建议删除所有不必要的软件包,以减少攻击面。具体来说,他建议运行apt-cache rdepends <package>以查明是否有其他软件包依赖于我们考虑删除的软件包。

我编写了一个 bash 脚本,列出了所有已安装软件包的依赖软件包,但是它花费了很长时间(在 Raspberry Pi 4、8GB 上需要 30 分钟以上),我想知道是否有更好、更快的解决方案。

#!/bin/bash

readarray -t packages < <(dpkg --get-selections | cut -f1)

for package in ${packages[@]};
do
    readarray -t dependents < <(apt-cache rdepends $package | sed -n '3,$s/^\s*//p')
    echo "-----------------------------------------------------------------------" | tee -a packages_and_depents.txt
    echo "${package} has these dependents on the system of max ${#dependents[@]}:" | tee -a packages_and_depents.txt
    echo "-----------------------------------------------------------------------" | tee -a packages_and_depents.txt
    for dependent in ${dependents[@]};
    do
        dpkg --get-selections $dependent 2>/dev/null | tee -a packages_and_depents.txt
    done
done

答案1

包系统dpkg为每个包都有一个字段,指示其优先事项

optional您可以将其用作初始过滤器,并且仅对归类为和extra(并省略requiredimportant和)的包运行脚本standard

此外,为每个包创建额外的数组并运行额外的for循环似乎是不必要的,而且肯定会消耗更多的计算能力。

因此我删除了第二个for循环并直接将其添加--installedapt-cache rdepends命令中。

可以通过修改脚本来实现,如下所示:

#!/bin/bash

# The command for this line is changed
readarray -t packages < <(dpkg-query -Wf '${Package}${Status}${Priority}\n' | sort -b -k5,5 -k1,1 | grep -v 'required\|important\|standard' | grep 'installed' | awk '{ print $1 }')

for package in ${packages[@]};
do
    echo "--------------------------------------------------------" | tee -a packages_and_depents.txt
    echo "${package} has these dependents installed on the system:" | tee -a packages_and_depents.txt
    echo "--------------------------------------------------------" | tee -a packages_and_depents.txt

    # 2nd for loop removed and replaced with `--installed` option
    apt-cache --installed rdepends "$package" | tail -n +3 | tee -a packages_and_depents.txt
done

另一个选择是更改整个脚本,因此它不显示所有反向依赖项,而是仅显示那些没有反向依赖项的包(那些可以删除的包)的名称。

此外,我认为您可以grep通过排除所有名称以lib(添加grep -v '^lib')开头的包来添加额外的排除项。

最后,可以改进演示,因此脚本在运行时提供视觉反馈,但最终报告仅写入输出文件。

这是我的最终版本的脚本:

#!/bin/bash

# The command for this line is changed
readarray -t packages < <(dpkg-query -Wf '${Package}${Status}${Priority}\n' | sort -b -k5,5 -k1,1 | grep -v 'required\|important\|standard' | grep -v '^lib' | grep 'installed' | awk '{ print $1 }')

# Write to file
echo "The following packages are not a dependency to any installed package:" > packages_no_depends.txt
# Write to screen
echo "Number of packages: [ ${#packages[@]} ] (priority optional/extra)"
echo ""

i=0
j=0

# Loop that only prints package names with NO reverse dependencies
for package in ${packages[@]};
do

    (( j++ ))
    echo -e "\033[1AProcessed packages: [ $i/$j ]"
    if [[ $(apt-cache --installed rdepends "$package" | tail -n +3 | wc -l) -eq 0 ]]
    then
        # Write to file
        echo "  $package" >> packages_no_depends.txt
        # Write to screen
        echo -e "\033[K  Package $package added to the list of non-dependencies\033[1A"
        (( i++ ))
    fi

done

# Final overview
echo -e "\033[K"
echo "STATUS"
echo "======"
echo "  Total packages scanned : $j"
echo "  Candidates for removal : $i"
echo "  Script execution time  : $SECONDS seconds"

参考光标移动的转义码

编辑:这个解决方案主要应该考虑布局和演示 - Raffa 的解决方案更为有效,因此请根据喜好将两者进行自己的组合。

根据 Raffa 的意见,这是我的最终脚本版本:

#!/bin/bash

# Change /path/to
dpkg_file="/path/to/packages_no_depends.txt"

# Write to file
echo "The following packages are not a dependency to any installed package:" > "$dpkg_file"
# Write to screen
echo "Scanning packages ..."

# Function to write non-dependencies to file
dpkg-query -Wf '${Package} ${Status}${Priority}\n' |
  grep -v 'required\|important\|standard' |
  grep 'installed' |
  awk '{ print $1 }' |
xargs apt-cache rdepends --installed |
  awk '! /Reverse Depends:/ {
    tp = $0
    n++
  }
  /Reverse Depends:/ {
    if (n == 1 && NR != 2) {
        print "  " p
    }
    n = 0
    p = tp
  }
  END {
    if (n == 0) {
        print "  " p
    }
  }' >> "$dpkg_file"

# Final overview
echo -e "\nSTATUS\n======"
echo "  Total packages scanned : $(dpkg-query -Wf '${Package}${Status}${Priority}\n' | grep -v 'required\|important\|standard' | grep 'installed' | wc -l)"
echo "  Candidates for removal : $(tail -n +2 $dpkg_file | wc -l)"
echo "  Script execution time  : $SECONDS seconds"

答案2

根据您在脚本中实现的工具,这应该是尽可能快的:

dpkg --get-selections |
cut -f1 |
xargs apt-cache rdepends --installed |
awk '! /Reverse Depends:/ {
    tp = $0
    n++
}

/Reverse Depends:/ {
    if (n == 1 && NR != 2) {
        print p
    }
    n = 0
    p = tp
}

END {
    if (n == 0) {
        print p
    }
}
'

它应该在输出中列出dpkg --get-selections | cut -f1系统上没有安装反向依赖项的软件包。

!!警告!!

切勿将上述命令的输出提供给软件包删除工具...在所有情况下都检查输出并手动处理。

答案3

这是我的最终实施来自@Raffa、@artur-meinild 和@Dan 的意见。

我在 Raspberry Pi 4 (8GB) 和 iMac (8GB、i5、SSD、2015) 上的 VM 上测试了该脚本,两个系统上的运行时间均为 1 秒左右。与我最初耗时超过 30 分钟的脚本相比,这是一个很大的改进。

谢谢大家!

#!/bin/bash

# Define output filename
filename=packages_no_dependents.txt

# Write heading to file
echo "The following packages are not a dependency to any installed package:" > $filename

# Get all installed packages that do not have a 'Priority' of 'required', 'important' or 'standard' and do not have packages that depend on them
dpkg-query -Wf '${Package} ${Status;-26}${Priority}\n' | grep -v 'required\|important\|standard' | grep 'installed' | awk '{ print $1 }' |
xargs apt-cache rdepends --installed |
awk '! /Reverse Depends:/ {
    tp = $0
    n++
}

/Reverse Depends:/ {
    if (n == 1 && NR != 2) {
        print p
    }
    n = 0
    p = tp
}

END {
    if (n == 0) {
        print p
    }
}
' | tee -a $filename

# Remove leading white-space in file
sed -i '3,$s/\s*//' $filename

答案4

我通常采用另一种方式:aptitude我将所有内容标记为“自动安装”,这将标记除必需软件包之外的所有软件包以供删除。然后,我会浏览要卸载的软件包列表,并手动添加那些我知道我会卸载的软件包直接地需要。

如果列表中的某些内容看起来有用,我可以按r获取反向依赖项列表,如果该列表中的某些内容看起来有用,则会将其标记为手动安装,而我之前查看的包仍然作为依赖项安装。

相关内容