feat(fedora): support fedora (#1367)

* feat(fedora): support fedora

* fix(fedora): fix modular package scan

* fix(fedora): check needs-restarting, oval arch, add source link

Co-authored-by: MaineK00n <mainek00n.1229@gmail.com>
This commit is contained in:
maito1201
2022-02-09 09:30:44 +09:00
committed by GitHub
parent 2923cbc645
commit 1cfe155a3a
17 changed files with 454 additions and 72 deletions

116
scanner/fedora.go Normal file
View File

@@ -0,0 +1,116 @@
package scanner
import (
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/logging"
"github.com/future-architect/vuls/models"
)
// inherit OsTypeInterface
type fedora struct {
redhatBase
}
// NewFedora is constructor
func newFedora(c config.ServerInfo) *fedora {
r := &fedora{
redhatBase{
base: base{
osPackages: osPackages{
Packages: models.Packages{},
VulnInfos: models.VulnInfos{},
},
},
sudo: rootPrivFedora{},
},
}
r.log = logging.NewNormalLogger()
r.setServerInfo(c)
return r
}
func (o *fedora) checkScanMode() error {
return nil
}
func (o *fedora) checkDeps() error {
if o.getServerInfo().Mode.IsFast() {
return o.execCheckDeps(o.depsFast())
} else if o.getServerInfo().Mode.IsFastRoot() {
return o.execCheckDeps(o.depsFastRoot())
} else {
return o.execCheckDeps(o.depsDeep())
}
}
func (o *fedora) depsFast() []string {
if o.getServerInfo().Mode.IsOffline() {
return []string{}
}
// repoquery
return []string{"dnf-utils"}
}
func (o *fedora) depsFastRoot() []string {
if o.getServerInfo().Mode.IsOffline() {
return []string{}
}
// repoquery
return []string{"dnf-utils"}
}
func (o *fedora) depsDeep() []string {
return o.depsFastRoot()
}
func (o *fedora) checkIfSudoNoPasswd() error {
if o.getServerInfo().Mode.IsFast() {
return o.execCheckIfSudoNoPasswd(o.sudoNoPasswdCmdsFast())
} else if o.getServerInfo().Mode.IsFastRoot() {
return o.execCheckIfSudoNoPasswd(o.sudoNoPasswdCmdsFastRoot())
} else {
return o.execCheckIfSudoNoPasswd(o.sudoNoPasswdCmdsDeep())
}
}
func (o *fedora) sudoNoPasswdCmdsFast() []cmd {
return []cmd{}
}
func (o *fedora) sudoNoPasswdCmdsFastRoot() []cmd {
if !o.ServerInfo.IsContainer() {
return []cmd{
{"repoquery -h", exitStatusZero},
{"needs-restarting", exitStatusZero},
{"which which", exitStatusZero},
{"stat /proc/1/exe", exitStatusZero},
{"ls -l /proc/1/exe", exitStatusZero},
{"cat /proc/1/maps", exitStatusZero},
{"lsof -i -P -n", exitStatusZero},
}
}
return []cmd{
{"repoquery -h", exitStatusZero},
{"needs-restarting", exitStatusZero},
}
}
func (o *fedora) sudoNoPasswdCmdsDeep() []cmd {
return o.sudoNoPasswdCmdsFastRoot()
}
type rootPrivFedora struct{}
func (o rootPrivFedora) repoquery() bool {
return false
}
func (o rootPrivFedora) yumMakeCache() bool {
return false
}
func (o rootPrivFedora) yumPS() bool {
return false
}

View File

@@ -4,6 +4,7 @@ import (
"bufio"
"fmt"
"regexp"
"strconv"
"strings"
"github.com/future-architect/vuls/config"
@@ -16,19 +17,38 @@ import (
ver "github.com/knqyf263/go-rpm-version"
)
var releasePattern = regexp.MustCompile(`(.*) release (\d[\d\.]*)`)
// https://github.com/serverspec/specinfra/blob/master/lib/specinfra/helper/detect_os/redhat.rb
func detectRedhat(c config.ServerInfo) (bool, osTypeInterface) {
if r := exec(c, "ls /etc/fedora-release", noSudo); r.isSuccess() {
logging.Log.Warnf("Fedora not tested yet: %s", r)
return true, &unknown{}
if r := exec(c, "cat /etc/fedora-release", noSudo); r.isSuccess() {
fed := newFedora(c)
result := releasePattern.FindStringSubmatch(strings.TrimSpace(r.Stdout))
if len(result) != 3 {
logging.Log.Warnf("Failed to parse Fedora version: %s", r)
return true, fed
}
release := result[2]
ver, err := strconv.Atoi(release)
if err != nil {
logging.Log.Warnf("Failed to parse Fedora version: %s", release)
return true, fed
}
if ver < 32 {
logging.Log.Warnf("Versions prior to Fedora 32 are not supported, detected version is %s", release)
return true, fed
}
fed.setDistro(constant.Fedora, release)
return true, fed
}
}
if r := exec(c, "ls /etc/oracle-release", noSudo); r.isSuccess() {
// Need to discover Oracle Linux first, because it provides an
// /etc/redhat-release that matches the upstream distribution
if r := exec(c, "cat /etc/oracle-release", noSudo); r.isSuccess() {
re := regexp.MustCompile(`(.*) release (\d[\d\.]*)`)
result := re.FindStringSubmatch(strings.TrimSpace(r.Stdout))
result := releasePattern.FindStringSubmatch(strings.TrimSpace(r.Stdout))
if len(result) != 3 {
logging.Log.Warnf("Failed to parse Oracle Linux version: %s", r)
return true, newOracle(c)
@@ -41,37 +61,9 @@ func detectRedhat(c config.ServerInfo) (bool, osTypeInterface) {
}
}
// https://bugzilla.redhat.com/show_bug.cgi?id=1332025
// CentOS cloud image
if r := exec(c, "ls /etc/centos-release", noSudo); r.isSuccess() {
if r := exec(c, "cat /etc/centos-release", noSudo); r.isSuccess() {
re := regexp.MustCompile(`(.*) release (\d[\d\.]*)`)
result := re.FindStringSubmatch(strings.TrimSpace(r.Stdout))
if len(result) != 3 {
logging.Log.Warnf("Failed to parse CentOS version: %s", r)
return true, newCentOS(c)
}
release := result[2]
switch strings.ToLower(result[1]) {
case "centos", "centos linux":
cent := newCentOS(c)
cent.setDistro(constant.CentOS, release)
return true, cent
case "centos stream":
cent := newCentOS(c)
cent.setDistro(constant.CentOS, fmt.Sprintf("stream%s", release))
return true, cent
default:
logging.Log.Warnf("Failed to parse CentOS: %s", r)
}
}
}
if r := exec(c, "ls /etc/almalinux-release", noSudo); r.isSuccess() {
if r := exec(c, "cat /etc/almalinux-release", noSudo); r.isSuccess() {
re := regexp.MustCompile(`(.*) release (\d[\d\.]*)`)
result := re.FindStringSubmatch(strings.TrimSpace(r.Stdout))
result := releasePattern.FindStringSubmatch(strings.TrimSpace(r.Stdout))
if len(result) != 3 {
logging.Log.Warnf("Failed to parse Alma version: %s", r)
return true, newAlma(c)
@@ -91,8 +83,7 @@ func detectRedhat(c config.ServerInfo) (bool, osTypeInterface) {
if r := exec(c, "ls /etc/rocky-release", noSudo); r.isSuccess() {
if r := exec(c, "cat /etc/rocky-release", noSudo); r.isSuccess() {
re := regexp.MustCompile(`(.*) release (\d[\d\.]*)`)
result := re.FindStringSubmatch(strings.TrimSpace(r.Stdout))
result := releasePattern.FindStringSubmatch(strings.TrimSpace(r.Stdout))
if len(result) != 3 {
logging.Log.Warnf("Failed to parse Rocky version: %s", r)
return true, newRocky(c)
@@ -110,21 +101,66 @@ func detectRedhat(c config.ServerInfo) (bool, osTypeInterface) {
}
}
// https://bugzilla.redhat.com/show_bug.cgi?id=1332025
// CentOS cloud image
if r := exec(c, "ls /etc/centos-release", noSudo); r.isSuccess() {
if r := exec(c, "cat /etc/centos-release", noSudo); r.isSuccess() {
result := releasePattern.FindStringSubmatch(strings.TrimSpace(r.Stdout))
if len(result) != 3 {
logging.Log.Warnf("Failed to parse CentOS version: %s", r)
return true, newCentOS(c)
}
release := result[2]
switch strings.ToLower(result[1]) {
case "centos", "centos linux":
cent := newCentOS(c)
cent.setDistro(constant.CentOS, release)
return true, cent
case "centos stream":
cent := newCentOS(c)
cent.setDistro(constant.CentOS, fmt.Sprintf("stream%s", release))
return true, cent
case "alma", "almalinux":
alma := newAlma(c)
alma.setDistro(constant.Alma, release)
return true, alma
case "rocky", "rocky linux":
rocky := newRocky(c)
rocky.setDistro(constant.Rocky, release)
return true, rocky
default:
logging.Log.Warnf("Failed to parse CentOS: %s", r)
}
}
}
if r := exec(c, "ls /etc/redhat-release", noSudo); r.isSuccess() {
// https://www.rackaid.com/blog/how-to-determine-centos-or-red-hat-version/
// e.g.
// $ cat /etc/redhat-release
// CentOS release 6.5 (Final)
if r := exec(c, "cat /etc/redhat-release", noSudo); r.isSuccess() {
re := regexp.MustCompile(`(.*) release (\d[\d\.]*)`)
result := re.FindStringSubmatch(strings.TrimSpace(r.Stdout))
result := releasePattern.FindStringSubmatch(strings.TrimSpace(r.Stdout))
if len(result) != 3 {
logging.Log.Warnf("Failed to parse RedHat/CentOS version: %s", r)
return true, newCentOS(c)
}
release := result[2]
ver, err := strconv.Atoi(release)
if err != nil {
logging.Log.Warnf("Failed to parse RedHat/CentOS version number: %s", release)
return true, newCentOS(c)
}
if ver < 5 {
logging.Log.Warnf("Versions prior to RedHat/CentOS 5 are not supported, detected version is %s", release)
}
switch strings.ToLower(result[1]) {
case "fedora":
fed := newFedora(c)
fed.setDistro(constant.Fedora, release)
return true, fed
case "centos", "centos linux":
cent := newCentOS(c)
cent.setDistro(constant.CentOS, release)
@@ -519,7 +555,7 @@ func (o *redhatBase) isExecNeedsRestarting() bool {
// TODO zypper ps
// https://github.com/future-architect/vuls/issues/696
return false
case constant.RedHat, constant.CentOS, constant.Alma, constant.Rocky, constant.Oracle:
case constant.RedHat, constant.CentOS, constant.Alma, constant.Rocky, constant.Oracle, constant.Fedora:
majorVersion, err := o.Distro.MajorVersion()
if err != nil || majorVersion < 6 {
o.log.Errorf("Not implemented yet: %s, err: %+v", o.Distro, err)
@@ -698,7 +734,7 @@ func (o *redhatBase) rpmQf() string {
func (o *redhatBase) detectEnabledDnfModules() ([]string, error) {
switch o.Distro.Family {
case constant.RedHat, constant.CentOS, constant.Alma, constant.Rocky:
case constant.RedHat, constant.CentOS, constant.Alma, constant.Rocky, constant.Fedora:
//TODO OracleLinux
default:
return nil, nil

View File

@@ -225,6 +225,8 @@ func ParseInstalledPkgs(distro config.Distro, kernel models.Kernel, pkgList stri
osType = &oracle{redhatBase: redhatBase{base: base}}
case constant.Amazon:
osType = &amazon{redhatBase: redhatBase{base: base}}
case constant.Fedora:
osType = &fedora{redhatBase: redhatBase{base: base}}
default:
return models.Packages{}, models.SrcPackages{}, xerrors.Errorf("Server mode for %s is not implemented yet", base.Distro.Family)
}

View File

@@ -26,7 +26,7 @@ func isRunningKernel(pack models.Package, family string, kernel models.Kernel) (
}
return false, false
case constant.RedHat, constant.Oracle, constant.CentOS, constant.Alma, constant.Rocky, constant.Amazon:
case constant.RedHat, constant.Oracle, constant.CentOS, constant.Alma, constant.Rocky, constant.Amazon, constant.Fedora:
switch pack.Name {
case "kernel", "kernel-devel", "kernel-core", "kernel-modules", "kernel-uek":
ver := fmt.Sprintf("%s-%s.%s", pack.Version, pack.Release, pack.Arch)