#!/usr/bin/env bash

#---------------------------------------------------------------------------------
# SecurityScan.sh

# Do simple security scan of system and put results in Markdown file SecurityScan.md.

# Requires sudo permission.

# For testing purposes, currently this script reports a lot of normal things
# as problems.  When the script is ready to be used, those items will
# be removed.


# The MIT License (MIT)
# Copyright (c) 2022 Bill Dietrich
# 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.


#---------------------------------------------------------------------------------

# One of: apt dnf eopkg dpkg rpm
export PKGMANAGER="apt"


#---------------------------------------------------------------------------------

export mdfilename="SecurityScan.md"
export tmpfilename="SecurityScanTemp.txt"

export FALSE=0
export TRUE=1

export HASSYSTEMD=$FALSE
systemctl 2>>/dev/null >/dev/null
if [ $? == 0 ]
then
    HASSYSTEMD=$TRUE
fi
# echo HASSYSTEMD $HASSYSTEMD


#---------------------------------------------------------------------------------

function Out {
    echo $1 >>"$mdfilename"
}

function OutListStart {
    echo '<ul>' $1 >>"$mdfilename"
}

function OutListItem {
    #echo '*' $1 >>"$mdfilename"
    echo '<li>' $1 '</li>' >>"$mdfilename"
}

function OutListEnd {
    echo '</ul>' $1 >>"$mdfilename"
    echo >>"$mdfilename"
}

function OutCode {
    echo '`' $1 '`' >>"$mdfilename"
}

function OutListItemCode {
    #echo '* ``' $1 '``' >>"$mdfilename"
    echo '<li><code>' $1 '</code></li>' >>"$mdfilename"
}

function OutHorizontalRule {
    echo '---' >>"$mdfilename"
}

function OutTempFileAsLinesCode {
    #while read -r textline
    #do
    #    OutCode "$textline"
    #    Out ""
    #done <$tmpfilename
    echo '<pre><code>' >>"$mdfilename"
    while read -r textline
    do
        echo "$textline" >>"$mdfilename"
    done <$tmpfilename
    echo '</code></pre>' >>"$mdfilename"
    echo >> "$mdfilename"
}

function OutTempFileAsListCode {
    OutListStart
    while read -r textline
    do
        OutListItemCode "$textline"
        Out ""
    done <$tmpfilename
    OutListEnd
}


#---------------------------------------------------------------------------------

# Note: arg is plain names, not regexs
function FindInstalledApps {
    rm -f $tmpfilename
    touch $tmpfilename

    for appname in "$1"
    do
        if test -f "/bin/$appname"
        then
            echo "/bin/$appname" >>$tmpfilename
        fi
        if test -f "/usr/bin/$appname"
        then
            echo "/usr/bin/$appname" >>$tmpfilename
        fi
        if test -f "/usr/local/bin/$appname"
        then
            echo "/usr/local/bin/$appname" >>$tmpfilename
        fi
        if test -f "/opt/$appname"
        then
            echo "/opt/$appname" >>$tmpfilename
        fi
    done
}


# Note: arg is regexs
function FindInstalledImages {
    rm -f $tmpfilename
    touch $tmpfilename

    snap list 2>>/dev/null | grep -i -E "$1" >>$tmpfilename

    flatpak list 2>>/dev/null | grep -i -E "$1" >>$tmpfilename

    # NOT TESTED
    sudo docker images 2>>/dev/null | grep -i -E "$1" >>$tmpfilename
}


# Note: arg is regexs
function FindInstalledPackages {
    rm -f $tmpfilename
    touch $tmpfilename
    case "$PKGMANAGER" in
    "apt")
        apt list 2>>/dev/null | grep -i -E "$1" | grep installed >$tmpfilename
        ;;
    "dnf")
        # NOT TESTED
        dnf list --installed 2>>/dev/null | grep -i -E "$1" >$tmpfilename
        ;;
    "eopkg")
        # NOT TESTED
        eopkg list-installed 2>>/dev/null | grep -i -E "$1" >$tmpfilename
        ;;
    "dpkg")
        dpkg-query --show 2>>/dev/null | grep -i -E "$1" >$tmpfilename
        ;;
    "rpm")
        # NOT TESTED
        rpm -qa 2>>/dev/null | grep -i -E "$1" >$tmpfilename
        ;;
    *)
        echo "Unknown package manager '$PKGMANAGER'"
        exit 4
        ;;
    esac
}


export PID
# Note: arg is plain names, not regexs
function FindRunningProcesses {
    rm -f $tmpfilename
    touch $tmpfilename

    for appname in $1
    do
        PID=`pidof $appname`
        if [ $? == 0 ]
        then
            echo "$appname" >>$tmpfilename
        fi
    done
}


export STATUS
# Note: arg is plain names, not regexs
function FindRunningSystemdUnits {
    #echo $1
    rm -f $tmpfilename
    touch $tmpfilename

    if [ "$HASSYSTEMD" == "$FALSE" ]; then return; fi

    for appname in $1
    do
        #echo $appname
        STATUS=`sudo systemctl is-active "$appname"`
        #echo $? $STATUS
        if [ "$STATUS" == "active" ]
        then
            echo "$appname" >>$tmpfilename
        fi
    done
}


#---------------------------------------------------------------------------------

sudo --validate

set -o verbose


#---------------------------------------------------------------------------------

echo >"$mdfilename"
if [ $? != 0 ]
then
	echo "Can't write to file '$mdfilename'"
	read -p 'OK? '
	exit 2
fi


#---------------------------------------------------------------------------------

Out '# Security Scan'
Out ""

Out "These are observations and suggestions, not necessarily insecure things or mistakes you should fix."
Out ""

#---------------------------------------------------------------------------------

OutHorizontalRule
Out ""
Out '## Applications'
Out ""


#---------------------------------------------------------------------------------

Out '### Remote-Control Applications'
Out ""


export ANYFOUND=$FALSE


APPLIST='bash vnc vino x2go remmina tftp-server telnet-server rsh-server xinetd rclone rsync dropbox megasync sshd xrdp odrive nextcloud xpra vinagre krfb nomachine teamviewer anydesk guacamole-server guacd gnome-user-share mate-user-share vsftpd samba apache2 httpd nginx lighttpd openssh-server'

FindInstalledApps $APPLIST
if test -s $tmpfilename
then
    ANYFOUND=$TRUE
    Out "The following applications are installed.  Potentially each could let an attacker control your computer.  Make sure each application is configured securely."
    Out ""
    OutTempFileAsListCode
fi


EGREPLIST='^bash/|^snapd|^ungoogled-chromium|vnc|^vino|x2go|remmina|tftp-server|telnet-server|rsh-server|xinetd|^rclone|^rsync|dropbox|megasync|^sshd|^xrdp|odrive|nextcloud|^xpra|vinagre|krfb|nomachine|teamviewer|anydesk|guacamole-server|guacd|gnome-user-share|mate-user-share|vsftpd|^samba/|^apache2/|^httpd/|^nginx|^lighttpd/|openssh-*server'

FindInstalledImages "$EGREPLIST"
if test -s $tmpfilename
then
    ANYFOUND=$TRUE
    Out "The following Snap/Flatpak/Docker images are installed.  Potentially each could let an attacker control your computer.  Make sure each application is configured securely."
    Out ""
    OutTempFileAsListCode
fi

FindInstalledPackages "$EGREPLIST"
if test -s $tmpfilename
then
    ANYFOUND=$TRUE
    Out "The following packages are installed.  Potentially each could let an attacker control your computer.  Make sure each package or application is configured securely."
    Out ""
    OutTempFileAsListCode
fi

if [ $ANYFOUND == $FALSE ]
then
    Out "None found."
    Out ""
fi


#---------------------------------------------------------------------------------

Out '### Apps that backup/sync to somewhere else'
Out ""

Out "[not done yet]"
Out ""


#---------------------------------------------------------------------------------

Out '### Apps/services that handle incoming traffic'
Out ""

Out "[not done yet]"
Out ""


#---------------------------------------------------------------------------------

Out '### Repositories you are using'
Out ""


rm -f $tmpfilename
touch $tmpfilename


# dpkg ???
apt-add-repository --list 2>>/dev/null >>$tmpfilename
cat /etc/pacman.conf 2>>/dev/null >>$tmpfilename        # NOT TESTED
dnf repolist --enabled 2>>/dev/null >>$tmpfilename        # NOT TESTED
zypper repos 2>>/dev/null >>$tmpfilename        # NOT TESTED
flatpak remotes 2>>/dev/null >>$tmpfilename
# Snap ???
docker info | grep Registry 2>>/dev/null >>$tmpfilename        # NOT TESTED


if test -s $tmpfilename
then
    OutTempFileAsLinesCode
else
    Out "None found."
    Out ""
fi


#---------------------------------------------------------------------------------

OutHorizontalRule
Out ""
Out '## Networking'
Out ""


#---------------------------------------------------------------------------------

Out '### Ports that have listeners'
Out ""

rm -f $tmpfilename
touch $tmpfilename

ss -tulpH 2>>/dev/null >$tmpfilename
if test -s $tmpfilename -a $? == 0
then
    #ss -tulp | sed 's/[0-9]* *[0-9]* *//' >$tmpfilename
    ss -tulp >$tmpfilename
    Out "The following network ports have a process listening on them.  Potentially each could let an attacker attack your computer.  Make sure each listener is desired and is configured securely."
    Out ""
    OutTempFileAsLinesCode

else
    netstat -tulp 2>>/dev/null >$tmpfilename
    if test -s $tmpfilename -a $? == 0
    then
        Out "The following network ports have a process listening on them.  Potentially each could let an attacker attack your computer.  Make sure each listener is desired and is configured securely."
        Out ""
        OutTempFileAsLinesCode
    else
        Out "None found."
        Out ""
    fi
fi


# CHECK RDP PORTS (3389 for LAN, 3390 for WAN) SPECIALLY ???
# CHECK Telnet PORT (23, 992) SPECIALLY ???


Out "Some standard listeners are: mdns (port 5353), DNS (port 53), ipp (printing, port 631)."
Out ""


#---------------------------------------------------------------------------------

Out '### Network file-shares'
Out ""

rm -f $tmpfilename
touch $tmpfilename

# type fuse.gvfsd-fuse is used for many things, including network file-shares
findmnt -n -t ext4,cifs,nfs,nfs4,afs,ceph 2>>/dev/null >$tmpfilename
if test -s $tmpfilename -a $? == 0
then
    findmnt -t ext4 2>>/dev/null >$tmpfilename
    Out "The following network file-shares are mounted.  Potentially each could let an attacker attack your computer.  Make sure each file-share is desired and is configured securely."
    Out ""
    OutTempFileAsLinesCode
else
    Out "None found."
    Out ""
fi


# see if "smbclient -L localhost" returns anything ?
# ls /var/lib/samba/usershares ?
# sudo smbstatus
# gio mount -l


#---------------------------------------------------------------------------------

Out '### Outbound VPN'
Out ""

rm -f $tmpfilename
touch $tmpfilename

export ISUSINGOUTBOUNDVPN=$FALSE
export ISUSINGWIREGUARD=$FALSE

sudo wg show interfaces 2>>/dev/null >$tmpfilename
if test -s $tmpfilename -a $? == 0
then
    ISUSINGOUTBOUNDVPN=$TRUE
    ISUSINGWIREGUARD=$TRUE
    Out "WireGuard VPN protocol in use."
    Out ""
else
    # could do "ip tuntap" or "ip tunnel show" ?
    ip route 2>>/dev/null | grep default | grep tun >$tmpfilename
    if test -s $tmpfilename -a $? == 0
    then
        ISUSINGOUTBOUNDVPN=$TRUE
        Out "It looks like you are using an outbound VPN.  Make sure it is configured securely."
        Out ""
        OutTempFileAsLinesCode
    else
        Out "None in use."    
        Out ""
    fi
fi

if [ $ISUSINGOUTBOUNDVPN == $TRUE ]
then
    Out "You can test the VPN by going to [https://browserleaks.com/ip](https://browserleaks.com/ip) or a similar site.  The location shown should be the location of your VPN server.  Also click the 'Run DNS Leak Test' button in that web page."
    Out ""
fi


#---------------------------------------------------------------------------------

Out '### Port-based firewall'
Out ""

export ANYFOUND=$FALSE

# Note: case-sensitive
export APPLIST='ufw gufw firewalld bash'


FindRunningProcesses "$APPLIST"
if test -s $tmpfilename
then
    ANYFOUND=$TRUE
    Out "The following port-based firewalls are running (as processes):"
    Out ""
    OutTempFileAsListCode
fi


FindRunningSystemdUnits "$APPLIST"
if test -s $tmpfilename
then
    ANYFOUND=$TRUE
    Out "The following port-based firewalls are running (as systemd units):"
    Out ""
    OutTempFileAsListCode
fi

if [ $ANYFOUND == $TRUE ]
then
    Out "You should check their settings to make sure they're what you want."
    Out ""
else
    Out "None found.  It is a good idea to install and enable a firewall, perhaps gufw."
    Out ""
fi


#---------------------------------------------------------------------------------

Out '### Application-based firewall'
Out ""

export ANYFOUND=$FALSE

# Note: case-sensitive
export APPLIST='cron opensnitch opensnitchd portmaster'


FindRunningProcesses "$APPLIST"
if test -s $tmpfilename
then
    ANYFOUND=$TRUE
    Out "The following application-based firewalls are running (as processes)."
    Out ""
    OutTempFileAsListCode
fi


FindRunningSystemdUnits "$APPLIST"
if test -s $tmpfilename
then
    ANYFOUND=$TRUE
    Out "The following application-based firewalls are running (as systemd units):"
    Out ""
    OutTempFileAsListCode
fi


if [ $ANYFOUND == $TRUE ]
then
    Out "You should check their settings to make sure they're what you want."
    Out ""
else
    Out "None found."
    Out ""
fi


#---------------------------------------------------------------------------------

Out '### Security Modules and Controls'
Out ""


# NOT TESTED WITH SELINUX INSTALLED
export STATUS
STATUS=`sestatus 2>>/dev/null | grep 'Current Mode:'`
#echo $? $STATUS
if [ $? == 0 ]
then
    case "$STATUS" in
        "Current Mode: enabled")
            Out "SELinux is enabled."
            Out ""
            ;;
        "Current Mode: permissive")
            Out "SELinux is operating in permissive mode."
            Out ""
            ;;
        "Current Mode: disabled")
            Out "SELinux is disabled."
            Out ""
            ;;
        *)
            Out "SELinux unknown mode."
            Out ""
            ;;
    esac
else
    Out "SELinux is not installed."
    Out ""
fi


# NOT TESTED WITH FIREJAIL INSTALLED
firejail --list 2>>/dev/null >$tmpfilename
if [ $? == 0 ]
then
    Out "Firejail is installed.  The following profiles are defined:"
    Out ""
    OutTempFileAsLinesCode
else
    Out "Firejail is not installed."
    Out ""
fi


sudo aa-status 2>>/dev/null >$tmpfilename
if [ $? == 0 ]
then
    Out "AppArmor is installed.  The status is:"
    Out ""
    OutTempFileAsLinesCode
else
    Out "AppArmor is not installed."
    Out ""
fi



#---------------------------------------------------------------------------------

Out '### IPv6'
Out ""


# NOT TESTED WITH IPv6 ENABLED
ip -6 addr 2>>/dev/null >$tmpfilename
if test -s $tmpfilename
then
    Out "IPv6 is enabled:"
    Out ""
    OutTempFileAsLinesCode
    grep -i 'fe80:' $tmpfilename 2>>/dev/null
    if [ $? == 0 ]
    then
        Out "IPv6 is using a link-local (LAN-only, not routable) address."
        Out ""
    else
        Out "IPv6 is using a public (world-routable) address."
        Out ""
    fi
else
    Out "IPv6 is disabled, or IP command not installed."
    Out ""
fi


#---------------------------------------------------------------------------------

Out '### DNS'
Out ""


if [ $ISUSINGWIREGUARD == $TRUE ]
then
    Out "Using WireGuard VPN protocol; this implies you are using the VPN's DNS, which is good."
    Out ""
else
    ip route 2>>/dev/null >$tmpfilename
    if test -s $tmpfilename
    then
        Out "Routing table:"
        Out ""
        OutTempFileAsLinesCode
        ip route | sed '2,$d' | cut -d ' ' -f3 >$tmpfilename
        Out "Primary default DNS address is:"
        Out ""
        OutTempFileAsLinesCode
        export DNSADDRFIRSTOCTET
        DNSADDRFIRSTOCTET=`cut -d '.' -f1 $tmpfilename`
        #echo $DNSADDRFIRSTOCTET
        case "$DNSADDRFIRSTOCTET" in
            "10")
                Out "Probably this means you are using the VPN's DNS, which is good."
                Out ""
                ;;
            *)
                if [ $ISUSINGOUTBOUNDVPN == $TRUE ]
                then
                    Out "Probably you are using an outbound VPN, but not using VPN's DNS, which may be a mistake.  Usually it's best to use the VPN's DNS."
                    Out ""
                fi
                ;;
            # MAYBE COMMENT ON DNS IS/ISN'T YOUR ISP'S VPN, IF CAN DETECT ?
        esac
    else
        Out "IP command not installed."
        Out ""
    fi
fi


#---------------------------------------------------------------------------------

OutHorizontalRule
Out ""
Out '## Devices'
Out ""

Out "[not done yet]"
Out ""


#---------------------------------------------------------------------------------

OutHorizontalRule
Out ""
Out '## Accounts'
Out ""

Out "[not done yet]"
Out ""


#---------------------------------------------------------------------------------

OutHorizontalRule
Out ""
Out '## Resources'
Out ""

Out "[not done yet]"
Out ""


#---------------------------------------------------------------------------------

OutHorizontalRule
Out ""
Out '## Advice'
Out ""

OutListStart


OutListItem "Are you using a password manager ?  You should."
Out ""
OutListItem "Are you using two-factor authentication on important online accounts (email, financial) ?  You should."
Out ""
OutListItem "Do you have backups of important files ?  You should."
Out ""


rm -f $tmpfilename
touch $tmpfilename
stat --printf=%y /var/lib/dpkg/status 2>>/dev/null | cut -d' ' -f1 >>$tmpfilename
stat --printf=%y /var/log/apt/history.log 2>>/dev/null | cut -d' ' -f1 >>$tmpfilename
stat --printf=%y /var/log/yum.log 2>>/dev/null | cut -d' ' -f1 >>$tmpfilename      # NOT TESTED
rpm -qa --last 2>>/dev/null | cut -d' ' -f1 >>$tmpfilename      # NOT TESTED

if test -s $tmpfilename
then
    OutListItem "You should keep your system software updated.  It looks like your last check or update was on"
    OutTempFileAsLinesCode
else
    OutListItem "You should keep your system software updated.  Can't tell when your last check or update occurred."
fi


blkid 2>>/dev/null | grep -i LUKS >$tmpfilename
if test -s $tmpfilename -a $? == 0
then
    OutListItem "It looks like you are using disk encryption, which is good."
else
    OutListItem "It look like your disk is not encrypted, so someone who steals your system could read your data."    
fi


OutListEnd


#---------------------------------------------------------------------------------

OutHorizontalRule
Out ""
Out '## Privacy'
Out ""

Out "[not done yet]"
Out ""


#---------------------------------------------------------------------------------

rm -f $tmpfilename

read -p 'OK?'
exit 0


#---------------------------------------------------------------------------------
# SAVED CODE:


function IsAppInstalled {
    if test -f "/usr/bin/$1"
    then
		return $TRUE
    fi

    return $FALSE
}


function IsPackageInstalled {
    case "$PKGMANAGER" in
    "apt")
        apt list 2>>/dev/null | grep -i -E $1 | grep installed >>/dev/null
        #echo apt result $?
        if [ $? == 0 ]
        then
            return $TRUE
        fi
        return $FALSE
        ;;
    *)
        echo "Unknown package manager '$PKGMANAGER'"
        exit 3
        ;;
    esac
}


while [ 0 == 1 ]
#for appname in bash vnc vino x2go remmina tftp-server telnet-server rsh-server xinetd xrdp xpra vinagre krfb nomachine teamviewer anydesk
do
    echo appname $appname
    IsAppInstalled $appname
    if [ $? == $TRUE ]
    then
        Out "Application '$appname' is installed.  Potentially this application could let an attacker control your computer.  Make sure the application is configured securely."
        Out ""
    fi
done


# TOO SLOW
while [ 0 == 1 ]
#for pkgname in ^bash/ vnc ^vino x2go ^remmina/ tftp-server telnet-server rsh-server xinetd ^xpra vinagre krfb nomachine teamviewer anydesk
do
    echo pkgname $pkgname
    IsPackageInstalled $pkgname
    #echo 'IsPackageInstalled' result $?
    if [ $? == $TRUE ]
    then
        Out "Package '$pkgname' is installed.  Potentially this application could let an attacker control your computer.  Make sure the application is configured securely."
        Out ""
    fi
done


#---------------------------------------------------------------------------------
