完整的Windows与Linux服务器系统安全加固实践和检测工具(等保2.0)

 

0x00 前言简述

本人从事安全运维方面的工作(PS:曾经做过等保等方面的安全服务),由于最近单位在做等保测评,所以自然而然的与信安的测评人员一起对接相关业务系统的检查,在做主机系统测评检查时发现了系统中某些配置不符合等保要求,需要对不满足要求的主机做进一步整改,好在我们众多的系统基本都是运行在虚拟机上搭建的kubernetes集群中,这样一来就可以尽可能减少加固系统给应用带来的影响,我们可以一台一台加固更新。

在这样环境的驱动下不得不将通宵熬夜,我准备好了枸杞和保温杯,当然也把测试环境也准备了一套,并将以前写的安全加固脚本进行重新整理,根据当前业务服务器系统版本进行更新和测试。(我还年轻,我还可加班!)

我们企业内部主要有WindowsServer2019、CentOS7、以及Ubuntu20.04三类操作系统,可以看出操作系统版本都还是比较新的,所以在下面的安全配置核查脚本以及安全加固脚本主要针对上述的三个版本的操作系统。

Linux 系统中采用Shell脚本、WiindowsServer系统中采用的是PowerShell脚本。

Github 项目地址: https://github.com/WeiyiGeek/SecOpsDev/blob/master/OS-%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/Linux/

WindowsSever2019 安全配置策略基线配置核查效果图

系统基础信息

等保主机测评项

系统补丁信息

系统补丁信息

WindowsSever2019 主机测评项安全加固效果图

等保主机测评项

等保主机测评项

主机测评加固

温馨提示: 下面脚本有点长,需要有一定的耐心哟。


0x01 WindowServer2019 配置核查与安全加固

Windows Server 安全配置策略基线加固项

  • 系统账号策略
  • 系统事件审核策略
  • 系统组策略安全选项策略
  • 注册表相关安全策略
  • 防火墙服务相关安全策略
  • 针对于系统暂无办法通过注册表以及组策略配置的安全加固项
  • 从微软安全中心拉取服务器安全补丁列表信息与本地已打补丁做比较

废话不多,上才艺(脚本 )

Windows Server 安全配置策略基线检测脚本
WindowsSecurityBaseLine.ps1

#######################################################
# @Author: WeiyiGeek
# @Description:  Windows Server 安全配置策略基线检测脚本
# @Create Time:  2019年5月6日 11:04:42
# @Last Modified time: 2021-11-15 11:06:31
# @E-mail: master@weiyigeek.top
# @Blog: https://www.weiyigeek.top
# @wechat: WeiyiGeeker
# @Github: https://github.com/WeiyiGeek/SecOpsDev/tree/master/OS-操作系统/Windows/
# @Version: 1.8
# @Runtime: Server 2019 / Windows 10
#######################################################

<#
.SYNOPSIS
Windows Server 安全配置策略基线检测脚本 (脚本将会在Github上持续更新)

.DESCRIPTION
Windows Server 操作系统配置策略核查 (符合等保3级的关键检查项)

.EXAMPLE
WindowsSecurityBaseLine.ps1 -Executor WeiyiGeek -MsrcUpdate False
- Executor : 脚本执行者
- MsrcUpdate : 是否在线拉取微软安全中心的服务器安全补丁列表信息(建议一台主机拉取好之后将WSUSList.json和WSUSListId.json拷贝到当前脚本同级目录下)
.NOTES
注意:不同的版本操作系统以下某些关键项可能会不存在会有一些警告(需要大家提交issue,共同完成)。
#>
[Cmdletbinding()]
param(
  [Parameter(Mandatory=$true)][String]$Executor,
  [Boolean]$MsrcUpdate
)

# * 文件输出默认为UTF-8格式
$PSDefaultParameterValues['Out-File:Encoding'] = 'utf8'


################################################################################################################################
# **********************#
# * 全局公用工具依赖函数  *  
# **********************#
Function F_IsCurrentUserAdmin
{ 
<#
.SYNOPSIS
F_IsCurrentUserAdmin 函数:全局公用工具依赖。
.DESCRIPTION
判断当前运行的powershell终端是否管理员执行,返回值 true 或者 false
.EXAMPLE
F_IsCurrentUserAdmin
#>
  $user = [Security.Principal.WindowsIdentity]::GetCurrent(); 
  (New-Object Security.Principal.WindowsPrincipal $user).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator) 
} 

function F_Logging {
<#
.SYNOPSIS
F_Logging 日志输出函数
.DESCRIPTION
用于输出脚本执行结果并按照不同的日志等级输出显示到客户终端上。
.EXAMPLE
F_Logging -Level [Info|Warning|Error] -Msg "测试输出字符串"
#>
  param (
    [Parameter(Mandatory=$true)]$Msg,
    [ValidateSet("Info","Warning","Error")]$Level
  )

  switch ($Level) {
    Info { 
      Write-Host "[INFO] ${Msg}" -ForegroundColor Green;
    }
    Warning {
      Write-Host "[WARN] ${Msg}" -ForegroundColor Yellow;
    }
    Error { 
      Write-Host "[ERROR] ${Msg}" -ForegroundColor Red;
    }
    Default {
      Write-Host "[*] F_Logging 日志 Level 等级错误`n Useage: F_Logging -Level [Info|Warning|Error] -Msg '测试输出字符串'" -ForegroundColor Red;
    }
  }
}

function F_Tools {
<#
.SYNOPSIS
F_Tools 检测对比函数
.DESCRIPTION
验证判断传入的字段是否与安全加固字段一致
.EXAMPLE
F_Tools -Key "ItemDemo" -Value "2" -Operator "eq" -DefaultValue "1"  -Msg "对比ItemDemo字段值与预设值"
#>
  param (
    [Parameter(Mandatory=$true)][String]$Key,
    [Parameter(Mandatory=$true)]$Value,
    [Parameter(Mandatory=$true)]$DefaultValue,
    [String]$Msg,
    [String]$Operator
  )

  if ( $Operator -eq  "eq" ) {
    if ( $Value -eq $DefaultValue ) {
      $Result = @{"$($Key)"="[合格项]|$($Value)|$($DefaultValue)|$($Msg)-【符合】等级保护标准."}
      Write-Host "$($Key)"=" [合格项]|$($Value)|$($DefaultValue)|$($Msg)-【符合】等级保护标准." -ForegroundColor White
      return $Result
    } else {
      $Result = @{"$($Key)"="[异常项]|$($Value)|$($DefaultValue)|$($Msg)-【不符合】等级保护标准."}
      Write-Host "$($Key)"=" [异常项]|$($Value)|$($DefaultValue)|$($Msg)-【不符合】等级保护标准." -ForegroundColor red
      return $Result
    }

  } elseif ($Operator -eq  "ne" ) {

    if ( $Value -ne $DefaultValue ) {
      $Result = @{"$($Key)"="[合格项]|$($Value)|$($DefaultValue)|$($Msg)-【符合】等级保护标准."}
      Write-Host "$($Key)"=" [合格项]|$($Value)|$($DefaultValue)|$($Msg)-【符合】等级保护标准." -ForegroundColor White
      return $Result
    } else {
      $Result = @{"$($Key)"="[异常项]|$($Value)|$($DefaultValue)|$($Msg)-【不符合】等级保护标准."}
      Write-Host "$($Key)"=" [异常项]|$($Value)|$($DefaultValue)|$($Msg)-【不符合】等级保护标准." -ForegroundColor red
      return $Result
    }

  } elseif ($Operator -eq  "le") {

    if ( $Value -le $DefaultValue ) {
      $Result = @{"$($Key)"="[合格项]|$($Value)|$($DefaultValue)|$($Msg)-【符合】等级保护标准."}
      Write-Host "$($Key)"=" [合格项]|$($Value)|$($DefaultValue)|$($Msg)-【符合】等级保护标准." -ForegroundColor White
      return $Result
    } else {
      $Result = @{"$($Key)"="[异常项]|$($Value)|$($DefaultValue)|$($Msg)-【不符合】等级保护标准."}
      Write-Host "$($Key)"=" [异常项]|$($Value)|$($DefaultValue)|$($Msg)-【不符合】等级保护标准." -ForegroundColor red
      return $Result
    }

  } elseif ($Operator -eq "ge") {

    if ( $Value -ge $DefaultValue ) {
      $Result =  @{"$($Key)"="[合格项]|$($Value)|$($DefaultValue)|$($Msg)-【符合】等级保护标准."}
      Write-Host "$($Key)"=" [合格项]|$($Value)|$($DefaultValue)|$($Msg)-【符合】等级保护标准." -ForegroundColor White
      return $Result
    } else {
      $Result = @{"$($Key)"="[异常项]|$($Value)|$($DefaultValue)|$($Msg)-【不符合】等级保护标准."}
      Write-Host "$($Key)"=" [异常项]|$($Value)|$($DefaultValue)|$($Msg)-【不符合】等级保护标准." -ForegroundColor red
      return $Result
    }
  }
}

function F_GetRegPropertyValue {
  param (
    [Parameter(Mandatory=$true)][String]$Key,
    [Parameter(Mandatory=$true)][String]$Name,
    [Parameter(Mandatory=$true)][String]$Operator,
    [Parameter(Mandatory=$true)]$DefaultValue,
    [Parameter(Mandatory=$true)][String]$Msg
  )

  try {
    $Value = Get-ItemPropertyValue -Path "Registry::$Key" -ErrorAction Ignore -WarningAction Ignore -Name $Name
    $Result = F_Tools -Key "Registry::$($Name)" -Value $Value -Operator $Operator -DefaultValue $DefaultValue  -Msg $Msg
    return $Result
  } catch {
   $Result = @{"Registry::$($Name)"="[异常项]|$($Key)中$($Name)不存在该项|$($DefaultValue)|$($Msg)"}
   Write-Host $Result.Values -ForegroundColor Red
   return $Result
  }
}

Function F_UrlRequest {
  param (
    [Parameter(Mandatory=$true)][String]$Msrc_api
  )
  Write-Host "[-] $($Msrc_api)" -ForegroundColor Gray
  $Response=Invoke-WebRequest -Uri "$($Msrc_api)"
  Return ConvertFrom-Json -InputObject $Response
}

################################################################################################################################
#
# * 操作系统基础信息记录函数 * #
#
# - 系统信息记录函数 - #
$SysInfo = @{}
# - Get-Computer 命令使用 
# Tips :在 Server 2019 以及 Windows 10 以下系统无该命令
# $Item = 'WindowsProductName','WindowsEditionId','WindowsInstallationType','WindowsCurrentVersion','WindowsVersion','WindowsProductId','BiosManufacturer','BiosFirmwareType','BiosName','BiosVersion','BiosBIOSVersion','BiosSeralNumber','CsBootupState','OsBootDevice','BiosReleaseDate','CsName','CsAdminPasswordStatus','CsManufacturer','CsModel','OsName','OsType','OsProductType','OsServerLevel','OsArchitecture','CsSystemType','OsOperatingSystemSKU','OsVersion','OsBuildNumber','OsSerialNumber','OsInstallDate','OsSystemDevice','OsSystemDirectory','OsCountryCode','OsCodeSet','OsLocaleID','OsCurrentTimeZone','TimeZone','OsLanguage','OsLocalDateTime','OsLastBootUpTime','CsProcessors','OsBuildType','CsNumberOfProcessors','CsNumberOfLogicalProcessors','OsMaxNumberOfProcesses','OsTotalVisibleMemorySize','OsFreePhysicalMemory','OsTotalVirtualMemorySize','OsFreeVirtualMemory','OsInUseVirtualMemory','OsMaxProcessMemorySize','CsNetworkAdapters','OsHotFixes'
# - Systeminfo 命令使用(通用-推荐)
$Item = 'Hostname','OSName','OSVersion','OSManufacturer','OSConfiguration','OS Build Type','RegisteredOwner','RegisteredOrganization','Product ID','Original Install Date','System Boot Time','System Manufacturer','System Model','System Type','Processor(s)','BIOS Version','Windows Directory','System Directory','Boot Device','System Locale','Input Locale','Time Zone','Total Physical Memory','Available Physical Memory','Virtual Memory: Max Size','Virtual Memory: Available','Virtual Memory: In Use','Page File Location(s)','Domain','Logon Server','Hotfix(s)','Network Card(s)'
Function F_SysInfo {
  # - 当前系统及计算机相关信息 (Primary)
  # Server 2019 以及 Windows 10 适用
  # $Computer = Get-ComputerInfo
  $Computer = systeminfo.exe /FO CSV /S $env:COMPUTERNAME |Select-Object -Skip 1 | ConvertFrom-CSV -Header $Item
  foreach( $key in $Item) {
    $SysInfo += @{"$($key)"=$Computer.$key}
  }
  # - 通用设置针对采用`systeminfo.exe`命令方式
  $SysInfo += @{"WindowsProductName"="$($SysInfo.OSName)"}
  $SysInfo.OsVersion=($Sysinfo.OSVersion -split " ")[0]
  $SysInfo += @{"CsSystemType"=($Sysinfo."System Type" -split " ")[0]}

  # - 当前系统 PowerShell 版本信息以及是否为虚拟机
  $SysInfo += @{"PSVersion"=$PSVersionTable.PSEdition+"-"+$PSVersionTable.PSVersion}

  # - 验证当前计算机产品及其版本 (Primary)
  $Flag = $SysInfo.WindowsProductName -match  "Windows 8.1|Windows 10|Server 2008|Server 2012|Server 2016|Server 2019"
  $ProductName = "$($Matches.Values)"
  if ( $ProductName.Contains("Windows")) {
    $SysInfo += @{"ProductType"="Client"}
    $SysInfo += @{"ProductName"=$ProductName}
    $SysInfo += @{"WindowsVersion"=Get-ItemPropertyValue -Path 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -Name ReleaseId}
  } else {
    $SysInfo += @{"ProductType"="Server"}
    $SysInfo += @{"ProductName"=$ProductName}
  }

  # - 验证当前计算机产品是是物理机还是虚拟机 (Primary)
  $ComputerType = get-wmiobject win32_computersystem
  if ($ComputerType.Manufacturer -match "VMware"){
    $SysInfo += @{"ComputerType"="虚拟机 - $($ComputerType.Model)"}
  } else {
    $SysInfo += @{"ComputerType"="物理机 - $($ComputerType.Model)"}
  }

  # # - 当前计算机温度值信息记录 (WINDOWSERVER2019支持)
  # Get-CimInstance -Namespace ROOT/WMI -Class MSAcpi_ThermalZoneTemperature | % { 
  #   $currentTempKelvin = $_.CurrentTemperature / 10 
  #   $currentTempCelsius = $currentTempKelvin - 273.15 
  #   $currentTempFahrenheit = (9/5) * $currentTempCelsius + 32 
  #   $Temperature += "InstanceName: " + $_.InstanceName+ " ==>> " +  $currentTempCelsius.ToString() + " 摄氏度(C);  " + $currentTempFahrenheit.ToString() + " 华氏度(F) ; " + $currentTempKelvin + "开氏度(K) `n" 
  # }
  # $SysInfo += @{"Temperature"=$Temperature}

  return $SysInfo
}


#
# * - 计算机Mac及IP地址信息函数 * #
#
#  * 系统网络及适配器信息变量 * #
$SysNetAdapter = @{}
function F_SysNetAdapter {
  # - 计算机Mac及IP地址信息
  $Adapter = Get-NetAdapter | Sort-Object -Property LinkSpeed
  foreach ( $Item in $Adapter) {
    $IPAddress = (Get-NetIPAddress -AddressFamily IPv4 -InterfaceIndex $Item.ifIndex).IPAddress
    $SysNetAdapter += @{"$($Item.MacAddress)"="$($Item.Status) | $($Item.Name) | $($IPAddress) | $($Item.LinkSpeed) | $($Item.InterfaceDescription)"}
  }
  return $SysNetAdapter
}


#
# * - 计算机系统磁盘与空间剩余查询函数 * #
#
# - 系统磁盘与空间剩余信息 - #
$SysDisk = @{}
function F_SysDisk {
  # - 计算机磁盘信息
  $Disk = Get-Disk
  foreach ( $Item in $Disk) {
    $SysDisk += @{"$($Item.SerialNumber)"="$($Item.Number) | $($Item.FriendlyName) | $($Item.HealthStatus)| $($Item.Size / [math]::Pow(1024,3)) GB | $($Item.PartitionStyle) |$($Item.OperationalStatus)"}
  }
  $Drive = Get-PSDrive -PSProvider FileSystem | Sort-Object -Property Name
  $Drive | % {
    $Free = [Math]::Round( $_.Free / [math]::pow(1024,3),2 )
    $Used = [Math]::Round( $_.Used / [math]::pow(1024,3),2 )
    $Total = [Math]::Ceiling($Free + $Used)
    $SysDisk += @{"FileSystem::$($_.Name)"="$($_.Name) | Free: $($Free) GB | Used: $($Used) GB | Total: $($Total) GB"}
  }
  return $SysDisk
}


#
# * 系统账号检查函数  * #
#
# - 系统账户信息变量 - # 
$SysAccount = @{}
Function F_SysAccount {
  # - 账户检查
  $Account = Get-WmiObject -Class Win32_UserAccount | Select-Object Name,AccountType,Caption,SID
  Write-Host "* 当前系统存在的 $($Account.Length) 名账户 : $($Account.Name)" -ForegroundColor Green
  if($Acount.Length -ge 4 -and ($Account.sid  | Select-String -Pattern "^((?!(-500|-501|-503|-504)).)*$")) {
    $Result = @{"SysAccount"="[异常项]-系统中存在其他账号请检查: $($Account.Name)"}
    $SysAccount += $Result
  }else{
    $Result = @{"SysAccount"="[合格项]-系统中无多余其他账号";}
    $SysAccount += $Result
  }
  return $SysAccount
}

#
# * 系统账号策略配置核查函数  * #
#
# - 系统账号策略 - #
$SysAccountPolicy = @{
  # + 密码最短留存期
  "MinimumPasswordAge" = @{operator="le";value=1;msg="密码最短留存期"}
  # + 密码最长留存期
  "MaximumPasswordAge" = @{operator="le";value=90;msg="密码最长留存期"}
  # + 密码长度最小值
  "MinimumPasswordLength" = @{operator="ge";value=14;msg="密码长度最小值"}
  # + 密码必须符合复杂性要求
  "PasswordComplexity" = @{operator="eq";value=1;msg="密码必须符合复杂性要求策略"}
  # + 强制密码历史 N个记住的密码
  "PasswordHistorySize" = @{operator="ge";value=3;msg="强制密码历史个记住的密码"}
  # + 账户登录失败锁定阈值N次数
  "LockoutBadCount" = @{operator="le";value=6;msg="账户登录失败锁定阈值次数"}
  # + 账户锁定时间(分钟)
  "ResetLockoutCount" = @{operator="ge";value=15;msg="账户锁定时间(分钟)"}
  # + 复位账户锁定计数器时间(分钟)
  "LockoutDuration" = @{operator="ge";value=15;msg="复位账户锁定计数器时间(分钟)"}
  # + 下次登录必须更改密码
  "RequireLogonToChangePassword" = @{operator="eq";value=0;msg="下次登录必须更改密码"}
  # + 强制过期
  "ForceLogoffWhenHourExpire" = @{operator="eq";value=0;msg="强制过期"}
  # + 当前管理账号登陆名称
  "NewAdministratorName" = @{operator="ne";value='"Administrator"';msg="当前系统默认管理账号登陆名称策略"}
  # + 当前来宾用户登陆名称
  "NewGuestName" = @{operator="ne";value='"Guest"';msg="当前系统默认来宾用户登陆名称策略"}
  # + 管理员是否被启用
  "EnableAdminAccount" = @{operator="eq";value=1;msg="管理员账户停用与启用策略"}
  # + 来宾用户是否启用
  "EnableGuestAccount" = @{operator="eq";value=0;msg="来宾账户停用与启用策略"}
  # + 指示是否使用可逆加密来存储密码一般禁用(除非应用程序要求超过保护密码信息的需要)
  "ClearTextPassword" = @{operator="eq";value=0;msg="指示是否使用可逆加密来存储密码 (除非应用程序要求超过保护密码信息的需要)"}
  # + 启用时此设置允许匿名用户查询本地LSA策略(0关闭)
  "LSAAnonymousNameLookup" = @{operator="eq";value=0;msg="启用时此设置允许匿名用户查询本地LSA策略 (0关闭)"}
  # + 检查结果存放的空数组
  "CheckResults" = @()
  }
Function F_SysAccountPolicy {
  $Count = $Config.Count
  for ($i=0;$i -lt $Count; $i++){
    $Line = $Config[$i] -split " = "
    if ($SysAccountPolicy.ContainsKey("$($Line[0])")) {
      $Result = F_Tools -Key "SysAccountPolicy::$($Line[0])" -Value $Line[1] -Operator $SysAccountPolicy["$($Line[0])"].Operator -DefaultValue $SysAccountPolicy["$($Line[0])"].Value  -Msg "系统账号策略配置-$($SysAccountPolicy["$($Line[0])"].Msg)"
      $SysAccountPolicy['CheckResults'] += $Result
    }
    if ( $Line[0] -eq "[Event Audit]" ) { break;}
  }
  return $SysAccountPolicy['CheckResults']
}



#
# * 系统事件审核策略配置核查函数  * #
#
# - 系统事件审核策略 - #
$SysEventAuditPolicy  = @{
  # + 审核系统事件(0) [成功(1)、失败(2)] (3)
  AuditSystemEvents = @{operator="eq";value=3;msg="审核系统事件"}
  # + 审核登录事件 成功、失败
  AuditLogonEvents = @{operator="eq";value=3;msg="审核登录事件"}
  # + 审核对象访问 成功、失败
  AuditObjectAccess = @{operator="eq";value=3;msg="审核对象访问"}
  # + 审核特权使用 失败
  AuditPrivilegeUse = @{operator="ge";value=2;msg="审核特权使用"}
  # + 审核策略更改 成功、失败
  AuditPolicyChange = @{operator="eq";value=3;msg="审核策略更改"}
  # + 审核账户管理 成功、失败
  AuditAccountManage = @{operator="eq";value=3;msg="审核账户管理"}
  # + 审核过程追踪 失败
  AuditProcessTracking = @{operator="ge";value=2;msg="审核过程追踪"}
  # + 审核目录服务访问 失败
  AuditDSAccess = @{operator="ge";value=2;msg="审核目录服务访问"}
  # + 审核账户登录事件 成功、失败
  AuditAccountLogon = @{operator="eq";value=3;msg="审核账户登录事件"}
  # + 检查结果存放的空数组
  CheckResults = @()
}
function F_SysEventAuditPolicy {
  $Count = $Config.Count
  for ($i=0;$i -lt $Count; $i++){
    $Line = $Config[$i] -split " = "
    if ( $Line[0] -eq "[Registry Values]" ) { break;}
    if ($SysEventAuditPolicy.ContainsKey("$($Line[0])")) {
      $Result = F_Tools -Key "SysEventAuditPolicy::$($Line[0])" -Value $Line[1] -Operator $SysEventAuditPolicy["$($Line[0])"].Operator -DefaultValue $SysEventAuditPolicy["$($Line[0])"].Value  -Msg "系统账号策略配置-$($SysEventAuditPolicy["$($Line[0])"].Msg)"
      $SysEventAuditPolicy['CheckResults'] += $Result
    }
  }

  return $SysEventAuditPolicy['CheckResults']
}

#
# * 操作系统用户权限管理策略检查  * #
#
# - 组策略用户权限管理策略 - #
$SysUserPrivilegePolicy = @{
# + 操作系统本地关机策略安全
SeShutdownPrivilege = @{operator="eq";value='*S-1-5-32-544';msg="操作系统本地关机策略"}
# + 操作系统远程关机策略安全
SeRemoteShutdownPrivilege = @{operator="eq";value='*S-1-5-32-544';msg="操作系统远程关机策略"}
# + 取得文件或其他对象的所有权限策略
SeProfileSingleProcessPrivilege = @{operator="eq";value='*S-1-5-32-544';msg="取得文件或其他对象的所有权限策略"}
# + 从网络访问此计算机策略
SeNetworkLogonRight = @{operator="eq";value='*S-1-5-32-544,*S-1-5-32-545,*S-1-5-32-551';msg="从网络访问此计算机策略"}
CheckResults = @()
}

Function F_SysUserPrivilegePolicy {
  # - 策略组用户权限配置
  $Hash = $SysUserPrivilegePolicy.Clone()  # 巨坑之处
  foreach ( $Name in $Hash.keys) {
    if ( $Name.Equals("CheckResults")){ continue; }
    $Line = ($Config | Select-String $Name.toString()) -split " = "
    $Result = F_Tools -Key "SysUserPrivilegePolicy::$($Line[0])" -Value $Line[1] -Operator $SysUserPrivilegePolicy["$($Line[0])"].Operator -DefaultValue $SysUserPrivilegePolicy["$($Line[0])"].Value  -Msg "策略组用户权限配置-$($SysUserPrivilegePolicy["$($Line[0])"].Msg)"
    $SysUserPrivilegePolicy['CheckResults'] += $Result
  }
  return $SysUserPrivilegePolicy['CheckResults']
}

#
# * 操作系统策略组安全选项权限配置检查 * #
# 
# - 组策略安全选项策略 - #
$SysSecurityOptionPolicy = @{
  # - 帐户:使用空密码的本地帐户只允许进行控制台登录(启用),注意此设置不影响使用域帐户的登录。(0禁用|1启用)
  LimitBlankPasswordUse = @{operator="eq";value="MACHINE\System\CurrentControlSet\Control\Lsa\LimitBlankPasswordUse=4,1";msg="帐户-使用空密码的本地帐户只允许进行控制台登录(启用)"}

  # - 交互式登录: 不显示上次登录用户名值(启用)
  DontDisplayLastUserName = @{operator="eq";value="MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\DontDisplayLastUserName=4,1";msg="交互式登录-不显示上次登录用户名值(启用)"}
  # - 交互式登录: 登录时不显示用户名
  DontDisplayUserName = @{operator="eq";value="MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\DontDisplayUserName=4,1";msg="交互式登录: 登录时不显示用户名"}
  # - 交互式登录: 锁定会话时显示用户信息(不显示任何信息)
  DontDisplayLockedUserId = @{operator="eq";value="MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\DontDisplayLockedUserId=4,3";msg="交互式登录: 锁定会话时显示用户信息(不显示任何信息)"}
  # - 交互式登录: 无需按 CTRL+ALT+DEL(禁用)
  DisableCAD = @{operator="eq";value="MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\DisableCAD=4,0";msg="交互式登录-无需按CTRL+ALT+DEL值(禁用)"}
  # - 交互式登录:计算机不活动限制值为600秒或更少
  InactivityTimeoutSecs = @{operator="le";value="MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\InactivityTimeoutSecs=4,600";msg="交互式登录-计算机不活动限制值为600秒或更少"}
  # - 交互式登录: 计算机帐户阈值此策略设置确定可导致计算机重启的失败登录尝试次数
  MaxDevicePasswordFailedAttempts = @{operator="le";value="MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\MaxDevicePasswordFailedAttempts=4,10";msg="交互式登录: 此策略设置确定可导致计算机重启的失败登录尝试次数"}
  # - 交互式登录: 试图登录的用户的消息标题
  LegalNoticeCaption = @{operator="eq";value='MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\LegalNoticeCaption=1,"安全登陆"';msg="交互式登录: 试图登录的用户的消息标题"}
  # - 交互式登录: 试图登录的用户的消息文本
  LegalNoticeText = @{operator="eq";value='MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\LegalNoticeText=7,请谨慎的操作服务器中数据,您所有操作将被记录审计';msg="交互式登录: 试图登录的用户的消息文本"}

  # - Microsoft网络客户端: 将未加密的密码发送到第三方 SMB 服务器(禁用)
  EnablePlainTextPassword = @{operator="eq";value="MACHINE\System\CurrentControlSet\Services\LanmanWorkstation\Parameters\EnablePlainTextPassword=4,0";msg="Microsoft网络客户端-将未加密的密码发送到第三方 SMB 服务器(禁用)"}
  # - Microsoft网络服务器:暂停会话前所需的空闲时间数量值为15分钟或更少但不为0
  AutoDisconnect = @{operator="eq";value="MACHINE\System\CurrentControlSet\Services\LanManServer\Parameters\AutoDisconnect=4,15";msg="Microsoft网络服务器-暂停会话前所需的空闲时间数量值为15分钟"}

  # - 网络安全: 再下一次改变密码时不存储LAN管理器哈希值(启用)
  NoLMHash = @{operator="eq";value="MACHINE\System\CurrentControlSet\Control\Lsa\NoLMHash=4,1";msg="网络安全-在下一次改变密码时不存储LAN管理器哈希值(启用)"}

  # - 网络访问: 不允许SAM账户的匿名枚举值为(启用)
  RestrictAnonymousSAM = @{operator="eq";value="MACHINE\System\CurrentControlSet\Control\Lsa\RestrictAnonymousSAM=4,1";msg="网络访问-不允许SAM账户的匿名枚举值为(启用)"}
  # - 网络访问:不允许SAM账户和共享的匿名枚举值为(启用)
  RestrictAnonymous = @{operator="eq";value="MACHINE\System\CurrentControlSet\Control\Lsa\RestrictAnonymous=4,1";msg="网络访问-不允许SAM账户和共享的匿名枚举值为(启用)"}

  # - 关机:设置确定是否可以在无需登录 Windows 的情况下关闭计算机(禁用)
  ClearPageFileAtShutdown = @{operator="eq";value="MACHINE\System\CurrentControlSet\Control\Session Manager\Memory Management\ClearPageFileAtShutdown=4,0";msg="关机-设置确定是否可以在无需登录 Windows 的情况下关闭计算机(禁用)"}

  "CheckResults" = @()
}
Function F_SysSecurityOptionPolicy {
  $Hash = $SysSecurityOptionPolicy.Clone()  # 巨坑之处
  foreach ( $Name in $Hash.keys) {
    if ( $Name.Equals("CheckResults")){ continue; }
    $Flag = $Config | Select-String $Name.toString() 
    $Value = $SysSecurityOptionPolicy["$($Name)"].Value -split ","
    if ( $Flag ) {
      $Line = $Flag -split ","
      $Result = F_Tools -Key "SysSecurityOptionPolicy::$($Name)" -Value $Line[1] -Operator $SysSecurityOptionPolicy["$($Name)"].Operator -DefaultValue $Value[1] -Msg "策略组安全选项配置-$($SysSecurityOptionPolicy["$($Name)"].Msg)"
      $SysSecurityOptionPolicy['CheckResults'] += $Result
    } else {
      $Result = @{"SysSecurityOptionPolicy::$($Name)"="[异常项]|未配置|$($Value[1])|策略组安全选项配置-$($SysSecurityOptionPolicy["$($Name)"].Msg)-【不符合】等级保护标准."}
      $SysSecurityOptionPolicy['CheckResults'] += $Result
    }
  }
  return $SysSecurityOptionPolicy['CheckResults']
}


#
# * 操作系统注册表相关配置检查函数  * #
#
# - 注册表相关安全策略  -
$SysRegistryPolicy = @{
# + 屏幕自动保护程序
ScreenSaveActive = @{regname="HKEY_CURRENT_USER\Control Panel\Desktop";name="ScreenSaveActive";operator="eq";value=1;msg="系统基配核查-屏幕自动保护程序策略"}
# + 屏幕恢复时使用密码保护
ScreenSaverIsSecure = @{regname="HKEY_CURRENT_USER\Control Panel\Desktop";name="ScreenSaverIsSecure";operator="eq";value=1;msg="系统基配核查-屏幕恢复时使用密码保护策略"}
# + 屏幕保护程序启动时间
ScreenSaveTimeOut = @{regname="HKEY_CURRENT_USER\Control Panel\Desktop";name="ScreenSaveTimeOut";operator="le";value=600;msg="系统基配核查-屏幕保护程序启动时间策略"}

# + 禁止全部驱动器自动播放
DisableAutoplay  = @{regname="HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer";name="DisableAutoplay";regtype="DWord";operator="eq";value=1;msg="禁止全部驱动器自动播放"}
NoDriveTypeAutoRun = @{regname="HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer";name="NoDriveTypeAutoRun";regtype="DWord";operator="eq";value=255;msg="禁止全部驱动器自动播放"}

# - 检查关闭默认共享盘
restrictanonymous = @{regname="HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa";name="restrictanonymous";operator="eq";value=1;msg="系统网络基配核查-关闭默认共享盘策略"}
restrictanonymoussam = @{regname="HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa";name="restrictanonymoussam";regtype="DWord";operator="eq";value=1;msg="不允许SAM账户的匿名枚举值为(启用)"}

# - 禁用磁盘共享(SMB)
AutoShareWks = @{regname="HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\lanmanserver\parameters";name="AutoShareWks";regtype="DWord";operator="eq";value=0;msg="关闭禁用默认共享策略-Server2012"}
AutoShareServer = @{regname="HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\lanmanserver\parameters";name="AutoShareServer";regtype="DWord";operator="eq";value=0;msg="关闭禁用默认共享策略-Server2012"}

# - 系统、应用、安全、PS日志查看器大小设置(此处设置默认的两倍配置-建议一定通过日志采集平台采集系统日志比如ELK)
EventlogSystemMaxSize = @{regname="HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\System";name="MaxSize";operator="ge";value=41943040;msg="系统基日志配核查-系统日志查看器大小设置策略"}
EventlogApplicationMaxSize = @{regname="HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\Application";name="MaxSize";operator="ge";value=41943040;msg="系统日志基配核查-应用日志查看器大小设置策略"}
EventlogSecurityMaxSize = @{regname="HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\Security";name="MaxSize";operator="ge";value=41943040;msg="系统日志基配核查-安全日志查看器大小设置策略"}
EventlogPSMaxSize = @{regname="HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\Windows PowerShell";name="MaxSize";operator="ge";value=31457280;msg="系统日志基配核查-PS日志查看器大小设置策略"}

# - 防火墙相关操作设置(开启、协议、服务)
DomainEnableFirewall  = @{regname='HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\SharedAccess\Parameters\FirewallPolicy\DomainProfile';name='EnableFirewall';regtype="DWord";operator="eq";value=1;msg="开启域网络防火墙"}
StandardEnableFirewall = @{regname='HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\SharedAccess\Parameters\FirewallPolicy\StandardProfile';name='EnableFirewall';regtype="DWord";operator="eq";value=1;msg="开启专用网络防火墙"}
PPEnableFirewall = @{regname='HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\SharedAccess\Parameters\FirewallPolicy\PublicProfile';name='EnableFirewall';regtype="DWord";operator="eq";value=1;msg="开启公用网络防火墙"}


# - 结果存储
CheckResults=@()
}
Function F_SysRegistryPolicy { 
  $Registry=  $SysRegistryPolicy.Clone()
  foreach ( $item in $Registry.keys) {
    if ( $item -eq "CheckResults" ){ continue;}
    $Result = F_GetRegPropertyValue -Key $SysRegistryPolicy.$item.regname -Name $SysRegistryPolicy.$item.name -Operator $SysRegistryPolicy.$item.operator -DefaultValue $SysRegistryPolicy.$item.value -Msg $SysRegistryPolicy.$item.msg
    $SysRegistryPolicy['CheckResults'] += $Result
  }
  return $SysRegistryPolicy['CheckResults']
}

#
# * 操作系统服务及运行程序检查函数  * #
#
$SysProcessServicePolicy = @{"CheckResults"=@()}
function F_SysProcessServicePolicy {
  # + 检测系统及用户开机启动项
  $SysAutoStart = Get-Item -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run'
  $SysAutoStart.GetValueNames() | % { 
    $res += "$($_)#$($SysAutoStart.GetValue($_)) "
  }
  $Result = @{"SysProcessServicePolicy::SysAutoStart"=$res}
  $SysProcessServicePolicy['CheckResults'] += $Result

  $UserAutoStart = Get-Item -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Run'
  $UserAutoStart.GetValueNames() | % { 
    $res += "$($_)#$($SysAutoStart.GetValue($_)) "
  }
  $Result = @{"SysProcessServicePolicy::UserAutoStart"=$res}
  $SysProcessServicePolicy['CheckResults'] += $Result

  # + 否启用远程桌面服务
  $RDPStatus = (Get-Service -Name "TermService").Status
  # if ($RDP -eq "0" -and $RDPStatus -eq "Running" ) {
  #   $Result = @{"SysProcessServicePolicy::RDPStatus"="当前系统【已启用】远程桌面服务."}
  # } else {
  #   $Result = @{"SysProcessServicePolicy::RDPStatus"="当前系统【未启用】远程桌面服务."}
  # }
  if ($RDPStatus -eq "Running" ) {
    $Result = F_GetRegPropertyValue -Key 'HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Terminal Server' -Name 'fDenyTSConnections' -Operator "eq" -DefaultValue 0 -Msg "是否将远程桌面服务禁用"
  } else {
    $Result = @{"SysProcessServicePolicy::RDPStatus"="当前系统【未启用】远程桌面服务."}
  }
  $SysProcessServicePolicy['CheckResults'] += $Result
  # - 否启用NTP服务来同步时钟
  # $NTP = F_GetReg -Key 'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpServer' -Name 'Enabled'
  # if ( $NTP -eq "1") {
  #   $Result = @{"SysProcessServicePolicy::NtpServerEnabled"="[合格项]|$NTP|1|系统基础配置核查-启用NTP服务同步时钟策略-【符合】等级保护标准."}
  # } else {
  #   $Result = @{"SysProcessServicePolicy::NtpServerEnabled"="[异常项]|$NTP|1|系统基础配置核查-启用NTP服务同步时钟策略-【不符合】等级保护标准."}
  # }
  $Result = F_GetRegPropertyValue -Key 'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpServer' -Name 'Enabled' -Operator "eq" -DefaultValue 1 -Msg "是否启用NTP服务同步时钟策略"
  $SysProcessServicePolicy['CheckResults'] += $Result


  # - 是否修改默认的远程桌面端口
  $RDP1 = Get-Item 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp\' | % {$_.GetValue("PortNumber")}
  $RDP2 = Get-Item 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\Wds\rdpwd\Tds\tcp\' | % {$_.GetValue("PortNumber")} 
  if ( $RDP1 -eq $RDP2 -and $RDP2 -ne "3389") {
    $Result = @{"SysProcessServicePolicy::RDPPort"="[合格项]|$RDP1|除3389以外的端口|系统基础配置核查-默认的远程桌面端口已修改-【符合】等级保护标准."}
  } else {
    $Result = @{"SysProcessServicePolicy::RDPPort"="[异常项]|$RDP1|除3389以外的端口|系统基础配置核查-默认的远程桌面端口未修改-【不符合】等级保护标准."}
  }
  $SysProcessServicePolicy['CheckResults'] += $Result
}


#
# * 操作系统安全检测函数 * 
#
# * 微软Windows服务器安全补丁列表信息 * #
$Msrc_api = "https://api.msrc.microsoft.com/sug/v2.0/zh-CN/affectedProduct?%24orderBy=releaseDate+desc&%24filter=productFamilyId+in+%28%27100000010%27%29+and+severityId+in+%28%27100000000%27%2C%27100000001%27%29+and+%28releaseDate+gt+2020-01-14T00%3A00%3A00%2B08%3A00%29+and+%28releaseDate+lt+2021-05-22T23%3A59%3A59%2B08%3A00%29"
$SysWSUSList = @{}
$SysWSUSListId = @()
$AvailableWSUSList = @{}
function F_SysSecurityPolicy {

  # - 系统补丁验证
  if ( $MsrcUpdate -or ! (Test-Path -Path .\WSUSList.json) ) {
    $MSRC_JSON = F_UrlRequest -Msrc_api $Msrc_api
    $MSRC_JSON.value | % { 
      $id = $_.id;
      $product = $_.product;
      $articleName = $_.kbArticles.articleName | Get-Unique;
      $fixedBuildNumber = $_.kbArticles.fixedBuildNumber | Get-Unique;
      $severity = $_.severity;
      $impact = $_.impact;
      $baseScore = $_.baseScore;
      $cveNumber = $_.cveNumber | Get-Unique;
      $releaseDate = $_.releaseDate
      $SysWSUSList += @{"$($id)"=@{"product"=$product;"articleName"=$articleName;"fixedBuildNumber"=$fixedBuildNumber;"severity"=$severity;"impact"=$impact;"baseScore"=$baseScore;"cveNumber"=$cveNumber;"releaseDate"=$releaseDate}}
    }
    while ($MSRC_JSON.'@odata.nextLink'.length) {
      $MSRC_JSON = F_UrlRequest -Msrc_api $MSRC_JSON.'@odata.nextLink'
      $MSRC_JSON.value | % { 
        $id = $_.id;
        $product = $_.product;
        $articleName = $_.kbArticles.articleName | Get-Unique;
        $fixedBuildNumber = $_.kbArticles.fixedBuildNumber | Get-Unique;
        $severity = $_.severity;
        $impact = $_.impact;
        $baseScore = $_.baseScore;
        $cveNumber = $_.cveNumber | Get-Unique;
        $releaseDate = $_.releaseDate
        $SysWSUSList += @{"$($id)"=@{"product"=$product;"articleName"=$articleName;"fixedBuildNumber"=$fixedBuildNumber;"severity"=$severity;"impact"=$impact;"baseScore"=$baseScore;"cveNumber"=$cveNumber;"releaseDate"=$releaseDate }}
      }
    }
    Write-Host "[-] 已从 Microsoft 安全响应中心获取更新 $($MSRC_JSON.'@odata.count') 条补丁信息!" -ForegroundColor Green
    Write-Host "[-] 正在将获取的更新 $($MSRC_JSON.'@odata.count') 条补丁信息写入到本地 WSUSList.json 文件之中!" -ForegroundColor Green
    $SysWSUSList | ConvertTo-Json | Out-File WSUSList.json -Encoding utf8
    $SysWSUSListId = $SysWSUSList.keys
    $SysWSUSList.keys | ConvertTo-Json | Out-File WSUSListId.json -Encoding utf8
  } else {
    # 从本地读取JSON文件存储的补丁信息。
    if (Test-Path -Path .\WSUSList.json) {
      $SysWSUSList = Get-Content -Raw -Encoding UTF8 .\WSUSList.json | ConvertFrom-Json
      $SysWSUSListId  = Get-Content -Raw -Encoding UTF8 .\WSUSListId.json | ConvertFrom-Json
      Write-Host "[-] 已从本地 WSUSList.json 文件获得 $($SysWSUSListId.count) 条补丁信息!" -ForegroundColor Green
    } else {
      Write-Host "[-] 本地未能找到存放补丁信息的 WSUSList.json 文件! 请采用 -Update True 标记从Microsoft 安全响应中心获取更新" -ForegroundColor Red
      break
      exit
    }
  }

  # 获取当前系统版本可用的补丁列表
  $AvailableWSUSListId = @() 
  if ($SysInfo.ProductType -eq "Client") {
    Write-Host "[-] Desktop Client" -ForegroundColor Gray
    foreach ($KeyName in $SysWSUSListId) {
      if(($SysWSUSList."$KeyName".product -match $SysInfo.ProductName) -and ($SysWSUSList."$KeyName".product -match $SysInfo.WindowsVersion) -and ($SysWSUSList."$KeyName".product -match ($SysInfo.CsSystemType -split " ")[0])) {
        if (($SysWSUSList."$KeyName".fixedBuildNumber -match $SysInfo.OsVersion) -or ($SysWSUSList."$KeyName".fixedBuildNumber.length -eq 0 )) {
          $AvailableWSUSList."$KeyName" = $SysWSUSList."$KeyName"
          $AvailableWSUSListId += "$KeyName"
        }
      }
    }
  } else {
    Write-Host "[-] Windows Server" -ForegroundColor Gray
    foreach ($KeyName in $SysWSUSListId) {
      if(($SysWSUSList."$KeyName".product -match $SysInfo.ProductName) -and ($SysWSUSList."$KeyName".product -match $SysInfo.ProductName)) {
        $AvailableWSUSList."$KeyName" = $SysWSUSList."$KeyName"
        $AvailableWSUSListId += "$KeyName"
      }
    }
  }
  Write-Host $SysInfo.ProductName $SysInfo.WindowsVersion ($SysInfo.CsSystemType -split " ")[0] $SysInfo.OsVersion
  Write-Host "[-] 已从梳理出适用于当前 $($SysInfo.ProductType) 系统版本的 $($AvailableWSUSList.count) 条补丁信息!`n" -ForegroundColor Green

  # 已安装的补丁
  $InstallWSUSList = @{}
  $msg = @()
  foreach ($id in $AvailableWSUSListId) {
    if( $SysInfo.'Hotfix(s)' -match $AvailableWSUSList."$id".articleName ) {
      $InstallWSUSList."$id" = $SysWSUSList."$id"
      $msg += "[+]" + $SysWSUSList."$id".product + $SysWSUSList."$id".fixedBuildNumber + " " +  $SysWSUSList."$id".articleName + "(" + $SysWSUSList."$id".cveNumber   + ")" + $SysWSUSList."$id".severity  + $SysWSUSList."$id".baseScore + "`n"
    } 
  }
  Write-Host "[-] $($SysInfo.'Hotfix(s)') ,共 $($AvailableWSUSList.count) 条漏洞补丁信息!`n$($msg)" -ForegroundColor Green

  # 未安装的补丁
  $NotInstallWSUSList = @{}
  $msg = @()
  foreach ($id in $AvailableWSUSListId) {
    if(-not($InstallWSUSList."$id")) {
     $NotInstallWSUSList."$id" = $SysWSUSList."$id"
     $msg += "[+]" + $SysWSUSList."$id".product + $SysWSUSList."$id".fixedBuildNumber + " " + $SysWSUSList."$id".articleName + "(" + $SysWSUSList."$id".cveNumber + ")" + $SysWSUSList."$id".severity + $SysWSUSList."$id".baseScore + "`n"
    }
  }
  Write-Host "[-] 未安装 $($NotInstallWSUSList.count) 条漏洞补丁信息,共 $($AvailableWSUSList.count) 条漏洞补丁信息!`n$($msg)" -ForegroundColor red
}

#
# * 杂类检测函数 * 
#
$OtherCheck = @{}
function F_OtherCheckPolicy {
  # - 当前系统已安装的软件
  $Product = Get-WmiObject -Class Win32_Product | Select-Object -Property Name,Version,IdentifyingNumber | Sort-Object Name | Out-String
  $OtherCheck += @{"Product"="$($Product)"}

  # - 当前系统最近访问文件或者目录
  $Recent = (Get-ChildItem ~\AppData\Roaming\Microsoft\Windows\Recent).Name
  $OtherCheck += @{"Recent"="$($Recent)"}
  return $OtherCheck
}


function Main() {
<#
.SYNOPSIS
main 函数程序执行入口
.DESCRIPTION
调用上述编写的相关检测脚本
.EXAMPLE
main
#>

$ScanStartTime = Get-date -Format 'yyyy-M-d H:m:s'
F_Logging -Level Info -Msg "#################################################################################"
F_Logging -Level Info -Msg "- @Desc: Windows Server 安全配置策略基线检测脚本  [将会在Github上持续更新-star]"
F_Logging -Level Info -Msg "- @Author: WeiyiGeek"
F_Logging -Level Info -Msg "- @Blog: https://www.weiyigeek.top"
F_Logging -Level Info -Msg "- @Github: https://github.com/WeiyiGeek/SecOpsDev/tree/master/OS-操作系统/Windows"
F_Logging -Level Info -Msg "#################################################################################`n"

F_Logging -Level Info -Msg "[*] Windows Server 安全配置策略基线检测脚本已启动."
F_Logging -Level Info -Msg "[*] 脚本执行: $($Executor), 是否在线拉取微软安全中心的服务器安全补丁列表信息: $($MsrcUpdate)`n"
# 1.判断当前运行的powershell终端是否管理员执行
F_Logging -Level Info -Msg "[-] 正在检测当前运行的PowerShell终端是否管理员权限...`n"
$flag = F_IsCurrentUserAdmin
if (!($flag)) {
  F_Logging -Level Error -Msg "[*] 脚本执行发生错误,请使用管理员权限运行该脚本..例如: Start-Process powershell -Verb runAs...."
  F_Logging -Level Warning -Msg "[*] 正在退出执行该脚本......"
  return
}
F_Logging -Level Info -Msg "[*] PowerShell 管理员权限检查通过...`n"

# 2.当前系统策略配置文件导出 (注意必须系统管理员权限运行) 
F_Logging -Level Info -Msg "[-] 正在导出当前系统策略配置文件 config.cfg......`n"
secedit /export /cfg config.cfg /quiet
start-sleep 3
if ( -not(Test-Path -Path config.cfg)) {
  F_Logging -Level Error -Msg "[*] 当前系统策略配置文件 config.cfg 不存在,请检查......`n"
  F_Logging -Level Warning -Msg "[*] 正在退出执行该脚本......"
  return
} else { 
  Copy-Item -Path config.cfg -Destination config.cfg.bak -Force
}
$Config = Get-Content -path config.cfg

# 3.系统相关信息以及系统安全组策略检测
F_Logging -Level Info -Msg "[-] 当前系统信息一览"
$SysInfo = F_SysInfo
$SysInfo

F_Logging -Level Info -Msg "[-] 当前系统网络信息一览"
$SysNetAdapter = F_SysNetAdapter
$SysNetAdapter

F_Logging -Level Info -Msg "[-] 当前系统磁盘信息一览"
$SysDisk = F_SysDisk
$SysDisk

F_Logging -Level Info -Msg "[-] 当前系统账户信息一览"
$SysAccount = F_SysAccount
$SysAccount

F_Logging -Level Info -Msg "[-] 当前系统安全策略信息一览"
$SysAccountPolicy.CheckResults = F_SysAccountPolicy
$SysEventAuditPolicy.CheckResults = F_SysEventAuditPolicy
$SysUserPrivilegePolicy.CheckResults = F_SysUserPrivilegePolicy
$SysSecurityOptionPolicy.CheckResults = F_SysSecurityOptionPolicy
$SysRegistryPolicy.CheckResults = F_SysRegistryPolicy
$SysProcessServicePolicy.CheckResults = F_SysProcessServicePolicy

F_Logging -Level Info -Msg "[-] 当前系统杂类信息一览"
$OtherCheck = F_OtherCheckPolicy
$OtherCheck.Values

F_Logging -Level Info -Msg "[-] 当前系统安全补丁情况信息一览"
F_SysSecurityPolicy

# 4.程序执行完毕
$ScanEndTime = Get-date -Format 'yyyy-M-d H:m:s'
F_Logging -Level Info -Msg "- Windows Server 安全配置策略基线检测脚本已执行完毕......`n开始时间:${ScanStartTime}`n完成时间: ${ScanEndTime}"
}
Main

Windows Server 安全配置策略基线加固脚本
WindowsSecurityReinforce.ps1

## ----------------------------------------- ##
# @Author: WeiyiGeek
# @Description:  Windows Server 安全配置策略基线加固脚本
# @Create Time:  2019年5月6日 11:04:42
# @Last Modified time: 2021-11-15 11:06:31
# @E-mail: master@weiyigeek.top
# @Blog: https://www.weiyigeek.top
# @wechat: WeiyiGeeker
# @Github: https://github.com/WeiyiGeek/SecOpsDev/tree/master/OS-操作系统/Windows/
# @Version: 1.8
# @Runtime: Server 2019 / Windows 10
## ----------------------------------------- ##
# 脚本主要功能说明:
# (1) Windows 系统安全策略相关基础配置
# (2) Windows 默认共享关闭、屏保、超时时间以及WSUS补丁更新。
# (3) Windows 等保主机测评项安全加固配置
## ----------------------------------------- ##

# * 文件输出默认为UTF-8格式
# $PSDefaultParameterValues['Out-File:Encoding'] = 'utf8'

<#
.SYNOPSIS
Windows Server 安全配置策略基线加固脚本 (脚本将会在Github上持续更新)

.DESCRIPTION
Windows Server 操作系统配置策略 (符合等保3级的关键检查项)
- 系统账号策略 
- 系统事件审核策略
- 系统组策略安全选项策略
- 注册表相关安全策略
- 防火墙服务相关安全策略
- 针对于系统暂无办法通过注册表以及组策略配置的安全加固项

.EXAMPLE
WindowsSecurityReinforce.ps1

.NOTES
注意:不同的版本操作系统以下某些关键项可能会不存在会有一些警告(需要大家提交issue,共同完成)。
#>

# - 系统账号策略 - #
$SysAccountPolicy = @{
  # + 密码最短留存期
  "MinimumPasswordAge" = @{operator="eq";value=1;msg="密码最短留存期"}
  # + 密码最长留存期
  "MaximumPasswordAge" = @{operator="eq";value=90;msg="密码最长留存期"}
  # + 密码长度最小值
  "MinimumPasswordLength" = @{operator="ge";value=14;msg="密码长度最小值"}
  # + 密码必须符合复杂性要求
  "PasswordComplexity" = @{operator="eq";value=1;msg="开启密码符合复杂性要求策略"}
  # + 强制密码历史 N个记住的密码
  "PasswordHistorySize" = @{operator="ge";value=3;msg="强制密码历史N个记住的密码"}
  # + 账户登录失败锁定阈值N次数
  "LockoutBadCount" = @{operator="eq";value=6;msg="账户登录失败锁定阈值次数"}
  # + 账户锁定时间(分钟)
  "ResetLockoutCount" = @{operator="ge";value=15;msg="账户锁定时间(分钟)"}
  # + 复位账户锁定计数器时间(分钟)
  "LockoutDuration" = @{operator="ge";value=15;msg="复位账户锁定计数器时间(分钟)"}
  # + 下次登录必须更改密码
  "RequireLogonToChangePassword" = @{operator="eq";value=0;msg="下次登录必须更改密码"}
  # + 强制过期
  "ForceLogoffWhenHourExpire" = @{operator="eq";value=1;msg="强制过期"}
  # + 当前管理账号登陆名称
  "NewAdministratorName" = @{operator="ne";value='"Admin"';msg="更改当前系统管理账号登陆名称为Admin策略"}
  # + 当前来宾用户登陆名称
  "NewGuestName" = @{operator="ne";value='"Guester"';msg="更改当前系统来宾用户登陆名称为Guester策略"}
  # + 管理员是否被启用
  "EnableAdminAccount" = @{operator="eq";value=1;msg="管理员账户停用与启用策略"}
  # + 来宾用户是否启用
  "EnableGuestAccount" = @{operator="eq";value=0;msg="来宾账户停用与启用策略"}
  # + 指示是否使用可逆加密来存储密码一般禁用(除非应用程序要求超过保护密码信息的需要)
  "ClearTextPassword" = @{operator="eq";value=0;msg="指示是否使用可逆加密来存储密码 (除非应用程序要求超过保护密码信息的需要)"}
  # + 启用时此设置允许匿名用户查询本地LSA策略(0关闭)
  "LSAAnonymousNameLookup" = @{operator="eq";value=0;msg="启用时此设置允许匿名用户查询本地LSA策略 (0关闭)"}
}

# - 系统事件审核策略 - #
$SysEventAuditPolicy  = @{
  # + 审核系统事件(0) [成功(1)、失败(2)] (3)
  AuditSystemEvents = @{operator="eq";value=3;msg="审核系统事件"}
  # + 审核登录事件 成功、失败
  AuditLogonEvents = @{operator="eq";value=3;msg="审核登录事件"}
  # + 审核对象访问 成功、失败
  AuditObjectAccess = @{operator="eq";value=3;msg="审核对象访问"}
  # + 审核特权使用 失败
  AuditPrivilegeUse = @{operator="ge";value=2;msg="审核特权使用"}
  # + 审核策略更改 成功、失败
  AuditPolicyChange = @{operator="eq";value=3;msg="审核策略更改"}
  # + 审核账户管理 成功、失败
  AuditAccountManage = @{operator="eq";value=3;msg="审核账户管理"}
  # + 审核过程追踪 失败
  AuditProcessTracking = @{operator="ge";value=2;msg="审核过程追踪"}
  # + 审核目录服务访问 失败
  AuditDSAccess = @{operator="ge";value=2;msg="审核目录服务访问"}
  # + 审核账户登录事件 成功、失败
  AuditAccountLogon = @{operator="eq";value=3;msg="审核账户登录事件"}
}

# - 系统组策略安全选项策略 - #
$SysSecurityOptionPolicy = @{
  # - 帐户:使用空密码的本地帐户只允许进行控制台登录(启用),注意此设置不影响使用域帐户的登录。(0禁用|1启用)
  LimitBlankPasswordUse = @{operator="eq";value="MACHINE\System\CurrentControlSet\Control\Lsa\LimitBlankPasswordUse=4,1";msg="帐户-使用空密码的本地帐户只允许进行控制台登录(启用)"}
  # - 交互式登录: 不显示上次登录用户名值(启用)
  DontDisplayLastUserName = @{operator="eq";value="MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\DontDisplayLastUserName=4,1";msg="交互式登录-不显示上次登录用户名值(启用)"}
  # - 交互式登录: 登录时不显示用户名
  DontDisplayUserName = @{operator="eq";value="MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\DontDisplayUserName=4,1";msg="交互式登录: 登录时不显示用户名"}
  # - 交互式登录: 锁定会话时显示用户信息(不显示任何信息)
  DontDisplayLockedUserId = @{operator="eq";value="MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\DontDisplayLockedUserId=4,3";msg="交互式登录: 锁定会话时显示用户信息(不显示任何信息)"}
  # - 交互式登录: 无需按 CTRL+ALT+DEL(禁用)
  DisableCAD = @{operator="eq";value="MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\DisableCAD=4,0";msg="交互式登录-无需按CTRL+ALT+DEL值(禁用)"}
  # - 交互式登录:计算机不活动限制值为600秒或更少
  InactivityTimeoutSecs = @{operator="eq";value="MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\InactivityTimeoutSecs=4,600";msg="交互式登录-计算机不活动限制值为600秒或更少"}
  # - 交互式登录: 计算机帐户阈值此策略设置确定可导致计算机重启的失败登录尝试次数
  MaxDevicePasswordFailedAttempts = @{operator="le";value="MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\MaxDevicePasswordFailedAttempts=4,10";msg="交互式登录: 此策略设置确定可导致计算机重启的失败登录尝试次数"}
  # - 交互式登录: 试图登录的用户的消息标题
  LegalNoticeCaption = @{operator="eq";value='MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\LegalNoticeCaption=1,"安全登陆"';msg="交互式登录: 试图登录的用户的消息标题"}
  # - 交互式登录: 试图登录的用户的消息文本
  LegalNoticeText = @{operator="eq";value='MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\LegalNoticeText=7,请谨慎的操作服务器中数据,您所有操作将被记录审计';msg="交互式登录: 试图登录的用户的消息文本"}

  # - Microsoft网络客户端: 将未加密的密码发送到第三方 SMB 服务器(禁用)
  EnablePlainTextPassword = @{operator="eq";value="MACHINE\System\CurrentControlSet\Services\LanmanWorkstation\Parameters\EnablePlainTextPassword=4,0";msg="Microsoft网络客户端-将未加密的密码发送到第三方 SMB 服务器(禁用)"}
  # - Microsoft网络服务器:暂停会话前所需的空闲时间数量值为15分钟或更少但不为0
  AutoDisconnect = @{operator="15";value="MACHINE\System\CurrentControlSet\Services\LanManServer\Parameters\AutoDisconnect=4,15";msg="Microsoft网络服务器-暂停会话前所需的空闲时间数量值为15分钟"}

  # - 网络安全: 再下一次改变密码时不存储LAN管理器哈希值(启用)
  NoLMHash = @{operator="eq";value="MACHINE\System\CurrentControlSet\Control\Lsa\NoLMHash=4,1";msg="网络安全-在下一次改变密码时不存储LAN管理器哈希值(启用)"}

  # - 网络访问: 不允许SAM账户的匿名枚举值为(启用)
  RestrictAnonymousSAM = @{operator="eq";value="MACHINE\System\CurrentControlSet\Control\Lsa\RestrictAnonymousSAM=4,1";msg="网络访问-不允许SAM账户的匿名枚举值为(启用)"}
  # - 网络访问:不允许SAM账户和共享的匿名枚举值为(启用)
  RestrictAnonymous = @{operator="eq";value="MACHINE\System\CurrentControlSet\Control\Lsa\RestrictAnonymous=4,1";msg="网络访问-不允许SAM账户和共享的匿名枚举值为(启用)"}

  # - 关机:设置确定是否可以在无需登录 Windows 的情况下关闭计算机(禁用)
  ClearPageFileAtShutdown = @{operator="eq";value="MACHINE\System\CurrentControlSet\Control\Session Manager\Memory Management\ClearPageFileAtShutdown=4,0";msg="关机-设置确定是否可以在无需登录 Windows 的情况下关闭计算机(禁用)"}
}

# - 操作系统组策略用户权限管理策略 - #
$SysUserPrivilegePolicy = @{
  # + 操作系统本地关机策略安全
  SeShutdownPrivilege = @{operator="eq";value='*S-1-5-32-544';msg="操作系统本地关机策略"}
  # + 操作系统远程关机策略安全
  SeRemoteShutdownPrivilege = @{operator="eq";value='*S-1-5-32-544';msg="操作系统远程关机策略"}
  # + 取得文件或其他对象的所有权限策略
  SeProfileSingleProcessPrivilege = @{operator="eq";value='*S-1-5-32-544';msg="取得文件或其他对象的所有权限策略"}
  # + 从网络访问此计算机策略
  SeNetworkLogonRight = @{operator="eq";value='*S-1-5-32-544,*S-1-5-32-545,*S-1-5-32-551';msg="从网络访问此计算机策略"}
}

# - 注册表相关安全策略  -
$SysRegistryPolicy = @{
  # + 屏幕自动保护程序
  ScreenSaveActive = @{reg="HKEY_CURRENT_USER\Control Panel\Desktop";name="ScreenSaveActive";regtype="String";value=1;operator="eq";msg="开启屏幕自动保护程序策略"}
  # + 屏幕恢复时使用密码保护
  ScreenSaverIsSecure = @{reg="HKEY_CURRENT_USER\Control Panel\Desktop";name="ScreenSaverIsSecure";regtype="String";value=1;operator="eq";msg="开启屏幕恢复时使用密码保护策略"}
  # + 屏幕保护程序启动时间
  ScreenSaveTimeOut = @{reg="HKEY_CURRENT_USER\Control Panel\Desktop";name="ScreenSaveTimeOut";regtype="String";value=600;operator="le";msg="开启屏幕保护程序启动时间策略"}

  # + 禁止全部驱动器自动播放
  DisableAutoplay  = @{reg="HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer";name="DisableAutoplay";regtype="DWord";operator="eq";value=1;msg="禁止全部驱动器自动播放"}
  NoDriveTypeAutoRun = @{reg="HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer";name="NoDriveTypeAutoRun";regtype="DWord";operator="eq";value=255;msg="禁止全部驱动器自动播放"}

  # + 限制IPC共享(禁止SAM帐户和共享的匿名枚举)
  restrictanonymous = @{reg="HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa";name="restrictanonymous";regtype="DWord";operator="eq";value=1;msg="不允许SAM账户和共享的匿名枚举值为(启用)"}
  restrictanonymoussam = @{reg="HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa";name="restrictanonymoussam";regtype="DWord";operator="eq";value=1;msg="不允许SAM账户的匿名枚举值为(启用)"}

  # + 禁用磁盘共享(SMB)
  AutoShareWks = @{reg="HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\lanmanserver\parameters";name="AutoShareWks";regtype="DWord";operator="eq";value=0;msg="关闭禁用默认共享策略-Server2012"}
  AutoShareServer = @{reg="HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\lanmanserver\parameters";name="AutoShareServer";regtype="DWord";operator="eq";value=0;msg="关闭禁用默认共享策略-Server2012"}

  # + 系统、应用、安全、PS日志查看器大小(单位字节)设置(此处设置默认的两倍配置-建议一定通过日志采集平台采集系统日志比如ELK)
  EventlogSystemMaxSize = @{reg="HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\System";name="MaxSize";regtype="DWord";operator="ge";value=41943040;msg="系统基日志配核查-系统日志查看器大小设置策略"}
  EventlogApplicationMaxSize = @{reg="HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\Application";name="MaxSize";regtype="DWord";operator="ge";value=41943040;msg="系统日志基配核查-应用日志查看器大小设置策略"}
  EventlogSecurityMaxSize = @{reg="HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\Security";name="MaxSize";regtype="DWord";operator="ge";value=41943040;msg="系统日志基配核查-安全日志查看器大小设置策略"}
  EventlogPSMaxSize = @{reg="HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\Windows PowerShell";name="MaxSize";regtype="DWord";operator="ge";value=31457280;msg="系统日志基配核查-PS日志查看器大小设置策略"}

  # + 远程桌面开启与关闭
  fDenyTSConnections = @{reg='HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Terminal Server';name='fDenyTSConnections';regtype="DWord";operator="eq";value=0;msg="是否禁用远程桌面服务-1则为禁用"}
  UserAuthentication = @{reg='HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp';name='UserAuthentication ';regtype="DWord";operator="eq";value=1;msg="只允许运行带网络级身份验证的远程桌面的计算机连接"}
  RDPTcpPortNumber = @{reg='HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp';name='PortNumber';regtype="DWord";operator="eq";value=39393;msg="远程桌面服务端口RDP-Tcp非3389"}
  TDSTcpPortNumber = @{reg='HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\Wds\rdpwd\Tds\tcp';name='PortNumber';regtype="DWord";operator="eq";value=39393;msg="远程桌面服务端口TDS-Tcp非3389"}

  # + 防火墙相关操作设置(开启、协议、服务)
  DomainEnableFirewall  = @{reg='HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\SharedAccess\Parameters\FirewallPolicy\DomainProfile';name='EnableFirewall';regtype="DWord";operator="eq";value=1;msg="开启域网络防火墙"}
  StandardEnableFirewall = @{reg='HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\SharedAccess\Parameters\FirewallPolicy\StandardProfile';name='EnableFirewall';regtype="DWord";operator="eq";value=1;msg="开启专用网络防火墙"}
  PPEnableFirewall = @{reg='HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\SharedAccess\Parameters\FirewallPolicy\PublicProfile';name='EnableFirewall';regtype="DWord";operator="eq";value=1;msg="开启公用网络防火墙"}

  # + 源路由欺骗保护
  DisableIPSourceRouting = @{reg='HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\Tcpip\Parameters';name='DisableIPSourceRouting';regtype="DWord";operator="eq";value=2;msg="源路由欺骗保护"}

  # + 碎片攻击保护
  EnablePMTUDiscovery = @{reg='HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\Tcpip\Parameters';name='EnablePMTUDiscovery';regtype="DWord";operator="eq";value=1;msg="碎片攻击保护"}

  # 【TCP/IP 协议栈的调整可能会引起某些功能的受限,管理员应该在进行充分了解和测试的前提下进行此项工作】
  # + 防SYN洪水攻击: 
  # SynAttackProtect = @{reg='HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters';name='EnablePMTUDiscovery';regtype="DWord";operator="eq";value=1;msg="设置防syn洪水攻击"}
  # TcpMaxHalfOpen = @{reg='HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters';name='TcpMaxHalfOpen';regtype="DWord";operator="eq";value=500;msg="允许的最大半开连接数"}
  # TcpMaxHalfOpenRetried = @{reg='HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters';name='TcpMaxHalfOpenRetried';regtype="DWord";operator="eq";value=400;msg="处于至少已发送一次重传的 SYN_RCVD 状态中的TCP连接数"}
  # + 防止DDOS攻击保护 
  # EnableICMPRedirect = @{reg='HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\Tcpip\Parameters';name='EnableICMPRedirect';regtype="DWord";operator="eq";value=0;msg="防止DDOS攻击保护,不启用 ICMP 重定向"}

  # + 启用并正确配置WSUS(自定义WSUS地址)启用 (一般中大企业都会有自己的WSUS补丁服务器),你需要将下述http://wsus.weiyigeek.top改为企业中自建的地址
  # 启用策略组“配置自动更新”
  AUOptions = @{reg="HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU";name="AUOptions";regtype="DWord";operator="eq";value=3;msg="自动下载并计划安装(4)-建议设置3自动下载并通知安装"}
  AutomaticMaintenanceEnabled = @{reg="HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU";name="AutomaticMaintenanceEnabled";regtype="DWord";operator="eq";value=1;msg="启用自动维护"}
  NoAutoUpdate = @{reg="HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU";name="NoAutoUpdate";regtype="DWord";operator="eq";value=0;msg="关闭无自动更新设置"}
  ScheduledInstallDay = @{reg="HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU";name="ScheduledInstallDay";regtype="DWord";operator="eq";value=7;msg="计划安装日期为每周六"}
  ScheduledInstallTime = @{reg="HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU";name="ScheduledInstallTime";regtype="DWord";operator="eq";value=1;msg="计划安装时间为凌晨1点"}
  # 启用策略组(指定Intranet Microsoft更新服务位置)
  UseWUServer = @{reg="HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU";name="UseWUServer";regtype="DWord";operator="eq";value=1;msg="指定Intranet Microsoft更新服务补丁服务器"}
  WUServer = @{reg="HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate";name="WUServer";regtype="String";value="http://wsus.weiyigeek.top";operator="eq";msg="设置检测更新的intranet更新服务"}
  WUStatusServer = @{reg="HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate";name="WUStatusServer";regtype="String";value="http://wsus.weiyigeek.top";operator="eq";msg="设置Intranet统计服务器"}
  UpdateServiceUrlAlternate = @{reg="HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate";name="UpdateServiceUrlAlternate";regtype="String";value="http://wsus.weiyigeek.top";operator="eq";msg="设置备用下载服务器"}
}



################################################################################################################################
# **********************#
# * 全局公用工具依赖函数  *  
# **********************#
function F_Logging {
<#
.SYNOPSIS
F_Logging 函数全局工具
.DESCRIPTION
用于输出脚本执行结果并按照不同的日志等级输出显示到客户终端上。
.EXAMPLE
F_Logging -Level [Info|Warning|Error] -Msg "测试输出字符串"
#>
  param (
    [Parameter(Mandatory=$true)]$Msg,
    [ValidateSet("Info","Warning","Error")]$Level
  )

  switch ($Level) {
    Info { 
      Write-Host "[INFO] ${Msg}" -ForegroundColor Green;
    }
    Warning {
      Write-Host "[WARN] ${Msg}" -ForegroundColor Yellow;
    }
    Error { 
      Write-Host "[ERROR] ${Msg}" -ForegroundColor Red;
    }
    Default {
      Write-Host "[*] F_Logging 日志 Level 等级错误`n Useage: F_Logging -Level [Info|Warning|Error] -Msg '测试输出字符串'" -ForegroundColor Red;
    }
  }
}


Function F_IsCurrentUserAdmin
{ 
<#
.SYNOPSIS
F_IsCurrentUserAdmin 函数:全局公用工具依赖。
.DESCRIPTION
判断当前运行的powershell终端是否管理员执行,返回值 true 或者 false
.EXAMPLE
F_IsCurrentUserAdmin
#>
  $user = [Security.Principal.WindowsIdentity]::GetCurrent(); 
  (New-Object Security.Principal.WindowsPrincipal $user).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator) 
} 


function F_Detection {
<#
.SYNOPSIS
F_Detection 函数: 全局公用工具依赖。
.DESCRIPTION
函数用于检测 config.cfg 关键项是否匹配并返回相应的结果,返回值 1 或者 0。
.EXAMPLE
F_Detection -Value $Value -Operator $Operator -DefaultValue $DefaultValue
#>
  param (
    [Parameter(Mandatory=$true)]$Value,
    [Parameter(Mandatory=$true)]$Operator,
    [Parameter(Mandatory=$true)]$DefaultValue  
  )
  if ( $Operator -eq "eq" ) {
    if ( $Value -eq "$DefaultValue" ) {return 1;} else { return 0;}
  } elseif ($Operator -eq  "ne" ) {
    if ( $Value -ne $DefaultValue ) {return 1;} else { return 0;}
  } elseif ($Operator -eq  "le") {
    if ( $Value -le $DefaultValue ) {return 1;} else { return 0;}
  } elseif ($Operator -eq "ge") {
    if ( $Value -ge $DefaultValue ) {return 1;} else { return 0;}
  }
}


function F_GetRegPropertyValue {
<#
.SYNOPSIS
F_GetRegPropertyValue 函数: 全局公用工具依赖函数。
.DESCRIPTION
函数用于获取指定键与值并与预定义的进行对比,正常返回结果为1或者0,如果键不存在则返回NotExist。
.EXAMPLE
An example
#>
  param (
    [Parameter(Mandatory=$true)][String]$Key,
    [Parameter(Mandatory=$true)][String]$Name,
    [Parameter(Mandatory=$true)][String]$Operator,
    [Parameter(Mandatory=$true)]$DefaultValue
  )

  try {
    $Value = Get-ItemPropertyValue -Path "Registry::$Key" -Name $Name -ErrorAction Ignore -WarningAction Ignore 
    $Result = F_Detection -Value $Value -Operator $Operator -DefaultValue $DefaultValue
    return $Result
  } catch {
    F_Logging -Level Warning -Msg "[*] $Key - $Name - NotExist"
    return 'NotExist'
  }
}



function F_SeceditReinforce() {
<#
.SYNOPSIS
F_SeceditReinforce 函数:实现系统策略组配置项对比和修改。
.DESCRIPTION
针对 config.cfg 安全配置项进行检测并修改,主要涉及系统账号策略设置、系统事件审核策略设置、系统组策略安全选项配置、操作系统组用户权限管理策略配置
.EXAMPLE
F_SeceditReinforce
#>
  # - 系统账号策略设置
  $Hash = $SysAccountPolicy.Clone()
  foreach ( $Name in $Hash.keys ) {
    $Flag = $Config | Select-String -AllMatches -Pattern "^$($Name.toString())"
    if ($Flag) {
      F_Logging -Level Info -Msg "[*] Update - $Name"
      $Line = $Flag -split " = "
      $Result = F_Detection -Value $Line[1] -Operator $SysAccountPolicy["$($Line[0])"].operator -DefaultValue $SysAccountPolicy["$($Line[0])"].value
      $NewLine = $Line[0] + " = " + $SysAccountPolicy["$($Line[0])"].value
      # - 在不匹配时进行关键项替换配置
      if ( -not($Result) -or $Line[0] -eq "NewGuestName" -or $Line[0] -eq "NewAdministratorName" ) {
          write-host "    $Flag -->> $NewLine"
        # 此处采用正则进行匹配系统账号策略相关项,防止后续
        $SecConfig = $SecConfig -replace "$Flag", "$NewLine" 
      }
    } else {
      F_Logging -Level Info -Msg "[+] Insert - $Name"
      $NewLine = $Name + " = " + $SysAccountPolicy["$Name"].value
      Write-Host "    $NewLine "
      # - 在不存在该配置项时进行插入
      $SecConfig = $SecConfig -replace "\[System Access\]", "[System Access]`n$NewLine"
    }
  }

  # - 系统事件审核策略设置
  $Hash = $SysEventAuditPolicy.Clone()
  foreach ( $Name in $Hash.keys ) {
    $Flag = $Config | Select-String $Name.toString()
    if ($Flag) {
      F_Logging -Level Info -Msg "[*] Update - $Name"
      $Line = $Flag -split " = "
      $Result = F_Detection -Value $Line[1] -Operator $SysEventAuditPolicy["$($Line[0])"].operator -DefaultValue $SysEventAuditPolicy["$($Line[0])"].value
      $NewLine = $Line[0] + " = " + $SysEventAuditPolicy["$($Line[0])"].value
      # - 在不匹配时进行关键项替换配置
      if (-not($Result)) {
        $SecConfig = $SecConfig -replace "$Flag", "$NewLine" 
      }
    } else {
      F_Logging -Level Info -Msg "[+] Insert - $Name"
      $NewLine = $Name + " = " + $SysEventAuditPolicy["$Name"].value
      Write-Host "  $NewLine"
      # - 在不存在该配置项时进行插入
      $SecConfig = $SecConfig -replace "\[Event Audit\]", "[Event Audit] `n$NewLine"
    }
  }

  # - 系统组策略安全选项配置 - #
  $Hash = $SysSecurityOptionPolicy.Clone()
  foreach ( $Name in $Hash.keys ) {
    $Flag = $Config | Select-String $Name.toString()
    if ($Flag) {
      F_Logging -Level Info -Msg "[*] Update - $Name"
      # 源字符串
      $Line = $Flag -split "="
      # 目标字符串
      $Value = $SysSecurityOptionPolicy["$($Name)"].value -split "="
      $Result = F_Detection -Value $Line[1] -Operator $SysSecurityOptionPolicy["$($Name)"].operator -DefaultValue $Value[1] 
      $NewLine = $Line[0] + "=" + $Value[1]
      if (-not($Result)) {
        $SecConfig = $SecConfig -Replace ([Regex]::Escape("$Flag")),"$NewLine" 
      }
    } else {
      F_Logging -Level Info -Msg "[+] Insert - $Name"
      $NewLine = $SysSecurityOptionPolicy["$Name"].value
      Write-Host "   $NewLine"
      # 不采用正则匹配原字符串(值得学习)
      $SecConfig = $SecConfig -Replace ([Regex]::Escape("[Registry Values]")),"[Registry Values]`n$NewLine"
    }
  }

  # - 操作系统组用户权限管理策略配置
  $Hash = $SysUserPrivilegePolicy.Clone()
  foreach ( $Name in $Hash.keys ) {
    $Flag = $Config | Select-String $Name.toString()
    if ($Flag) {
      F_Logging -Level Info -Msg "[*] Update - $Name"
      $Line = $Flag -split " = "
      $Result = F_Detection -Value $Line[1] -Operator $SysUserPrivilegePolicy["$($Line[0])"].operator -DefaultValue $SysUserPrivilegePolicy["$($Line[0])"].value
      $NewLine = $Line[0] + " = " + $SysUserPrivilegePolicy["$($Line[0])"].value
      if (-not($Result)) {
        $SecConfig = $SecConfig -Replace ([Regex]::Escape("$Flag")), "$NewLine" 
      }
    } else {
      F_Logging -Level Info -Msg "[+] Insert - $Name"
      $NewLine = $Name + " = " + $SysUserPrivilegePolicy["$Name"].value
      Write-Host "    $NewLine"
      $SecConfig = $SecConfig -Replace ([Regex]::Escape("[Privilege Rights]")),"[Privilege Rights]`n$NewLine"
    }
  }
   # 将生成的本地安全组策略配置输到`secconfig.cfg`,【坑】非常注意文件编码格式为UTF16-LE,此时需要添加-Encoding参数并指定为string
   $SecConfig | Out-File secconfig.cfg -Encoding string
}


function F_SysRegistryReinforce()  {
<#
.SYNOPSIS
F_SysRegistryReinforce 函数针对于注册表中系统相关配置。
.DESCRIPTION
针对操作系统注册表安全配置项与SysRegistryPolicy哈希表的键值进行检测与设置。
.EXAMPLE
F_SysRegistryReinforce 
#>
  # - 满足等级保护相关基础配置
  $Hash = $SysRegistryPolicy.Clone()
  foreach ( $Name in $Hash.keys ) {
    $Result = F_GetRegPropertyValue -Key $SysRegistryPolicy.$Name.reg -Name $SysRegistryPolicy.$Name.name -Operator $SysRegistryPolicy.$Name.operator -DefaultValue $SysRegistryPolicy.$Name.value
    F_Logging -Level Info -Msg "Get-ItemProperty -Path Registry::$($SysRegistryPolicy.$Name.reg)"
    if ( $Result -eq 'NotExist' ){
      # - 判断注册表项是否存在不存在则创建
      if (-not(Test-Path -Path "Registry::$($SysRegistryPolicy.$Name.reg)")){
        F_Logging -Level Info -Msg "正在创建 $($SysRegistryPolicy.$Name.reg) 注册表项......"
        New-Item -Path "registry::$($SysRegistryPolicy.$Name.reg)" -Force
      }
      # - 可能的枚举值包括"String、ExpandString、Binary、DWord、MultiString、QWord、Unknown"
      New-ItemProperty -Path "Registry::$($SysRegistryPolicy.$Name.reg)" -Name $SysRegistryPolicy.$Name.name -PropertyType $SysRegistryPolicy.$Name.regtype -Value $SysRegistryPolicy.$Name.value
    } elseif ( $Result -eq 0 ) {
      Set-ItemProperty -Path "Registry::$($SysRegistryPolicy.$Name.reg)" -Name $SysRegistryPolicy.$Name.name -Value $SysRegistryPolicy.$Name.value
    }
  }
}


function F_ServiceManager() {
<#
.SYNOPSIS
F_ServiceManager 函数:针对于系统中相关服务管理操作
.DESCRIPTION
主要对系统中某些服务进行停止禁用
.EXAMPLE
F_ServiceManager -Name server -Operator restart -StartType Automatic
#>
  param (
    [Parameter(Mandatory=$true)]$Name,
    [ValidateSet("Start","Stop","Restart")]$Operator,
    [ValidateSet("Automatic","Manual","Disabled","Boot","System")]$StartType
  )
  # - 验证服务是否存在
  F_Logging -Level Info -Msg "正在对 $Name 服务进行操作管理......."
  $ServiceStatus = (Get-Service $Name -ErrorAction SilentlyContinue).Status
  if( -not($ServiceStatus.Length) ) {
    F_Logging -Level Error -Msg "$Name Service is not exsit with current system!!!!!!"
    return
  }

  # - 根据$Operator操作服务启动、停止、重启
  switch ($Operator) {
    Start { 
      if ( "$ServiceStatus" -ne "Running" ) {
        F_Logging -Level Info -Msg "正在启动 $Name 服务";Start-Service -Name $Name -Force
      }
    }
    Stop { 
      if ( "$ServiceStatus" -ne "Stopped" ) {
        F_Logging -Level Warning -Msg "正在停止 $Name 服务";Stop-Service -Name $Name -Force
      }
    }
    Restart {
      F_Logging -Level Warning -Msg "正在重启 $Name 服务";Restart-Service -Name $Name -Force
    }
    Default { F_Logging -Level Info -Msg "未对 $Name 服务做任何操作!" }
  }


  # - 根据$StartType设置服务启动类型
  switch ($StartType) {
    Automatic { Set-Service -Name $Name -StartupType Automatic}
    Manual { Set-Service -Name $Name -StartupType Manual }
    Disabled { Set-Service -Name $Name -StartupType Disabled}
    Boot { Set-Service -Name $Name -StartupType Boot}
    System {Set-Service -Name $Name -StartupType System }
    Default {F_Logging -Level Info -Msg "未对 $Name 服务做任何自启配置操作!"}
  }
}



Function F_ExtentionReinforce() {
<#
.SYNOPSIS
F_ExtentionReinforce 函数:针对于系统暂无办法通过注册表以及组策略配置的将在此处执行。
.DESCRIPTION
执行系统加固的相关命令,其中保护PowerShell或者cmd相关命令
.EXAMPLE
F_ExtentionReinforce 
#>
  # [+] 禁用共享服务以及删除当前主机中所有共享

  F_ServiceManager -Name lanmanserver -Operator Stop -StartType Manual
  (gwmi -class win32_share).delete()
  # 方式2.(Get-WmiObject -class win32_share).delete()

  # [+] 启用windows防火墙以及防火墙相关
  netsh advfirewall set allprofiles state on
  # 启用、或者禁用文件和打印机共享(回显请求 - ICMPv4-In) 根据需求而定
  # Enable-NetFirewallRule -Name FPS-ICMP4-ERQ-In
  Disable-NetFirewallRule  -Name FPS-ICMP4-ERQ-In
  Get-NetFirewallRule -Name "CustomSecurity-Remote-Desktop-Port" -ErrorAction SilentlyContinue
  if ($?) {
    # 允许其它主机访问 Remote-Desktop-Port 的39393端口。
    New-NetFirewallRule -Name "CustomSecurity-Remote-Desktop-Port" -DisplayName "CustomSecurity-Remote-Desktop-Port" -Description "CustomSecurity-Remote-Desktop-Port" -Direction Inbound -LocalPort 39393 -Protocol TCP -Action Allow -Enabled True
  }
}

# Function F_SensitiveFile() {
# <#
# .SYNOPSIS
# F_SensitiveFile 函数:针对于系统中相关服务的敏感文件检测。(后续扩充)
# .DESCRIPTION
# 针对 config.cfg 安全配置项进行检测并修改
# .EXAMPLE
# F_SensitiveFile 
# #>
#   $SensitiveFile = @("%systemroot%\system32\inetsrv\iisadmpwd")
#   if (Test-Path -Path $SensitiveFile[$i]) {
#     # 1.删除具有任何文件扩展名的文件
#     Remove-Item C:\Test\*.* # == Del C:\Test\*.*
#     Remove-Item -Path C:\Test\file.txt -Force 

#     # 2.删除特殊条件的文件或者目录
#     Remove-Item -Path C:\temp\DeleteMe -Recurse # 递归删除子文件夹中的文件
#     }
# }

function Main {
<#
.SYNOPSIS
main 函数程序执行入口
.DESCRIPTION
调用上述编写的相关检测加固函数
.EXAMPLE
main
#>
F_Logging -Level Info -Msg "#################################################################################"
F_Logging -Level Info -Msg "- @Desc: Windows Server 安全配置策略基线加固脚本 [将会在Github上持续更新-star]"
F_Logging -Level Info -Msg "- @Author: WeiyiGeek"
F_Logging -Level Info -Msg "- @Blog: https://www.weiyigeek.top"
F_Logging -Level Info -Msg "- @Github: https://github.com/WeiyiGeek/SecOpsDev/tree/master/OS-操作系统/Windows"
$StartTime = Get-date -Format 'yyyy-M-d H:m:s'
F_Logging -Level Info -Msg "#################################################################################`n"

# 1.当前系统策略配置文件导出 (注意必须系统管理员权限运行) 
F_Logging -Level Info -Msg "- 正在检测当前运行的PowerShell终端是否管理员权限...`n"
$flag = F_IsCurrentUserAdmin
if (!($flag)) {
  F_Logging -Level Error -Msg "- 脚本执行发生错误,请使用管理员权限运行该脚本..例如: Start-Process powershell -Verb runAs....`n"
  F_Logging -Level Warning -Msg "- 正在退出执行该脚本......`n"
  return
}

# 2.导出当前系统策略配置文件后验证文件是否存在以及原始配置文件备份。
secedit /export /cfg config.cfg /quiet
start-sleep 3
if ( -not(Test-Path -Path config.cfg) ) {
  F_Logging -Level Error -Msg "- 当前系统策略配置文件 config.cfg 不存在,请检查......"
  F_Logging -Level Warning -Msg "- 正在退出执行该脚本......"
  return
} else { 
  Copy-Item -Path config.cfg -Destination config.cfg.bak -Force
}
$Config = Get-Content -path config.cfg
$SecConfig = $Config.Clone()

# 3.进行系统策略配置安全加固
F_SeceditReinforce

# 4.当系统策略配置安全加固完成后将生成的secconfig.cfg导入进系统策略中。
secedit /configure /db secconfig.sdb /cfg secconfig.cfg

# 5.进行系统注册表相关配置安全加固
F_SysRegistryReinforce

# 6.系统扩展相关配置安全加固
F_ExtentionReinforce

# 7.程序执行完毕
$EndTime = Get-date -Format 'yyyy-M-d H:m:s'
F_Logging -Level Info -Msg "- 该操作系统安全加固已完毕......`n开始时间:${StartTime}`n完成时间: ${EndTime}"
}
Main

至此,Windows 安全基线配置核查和安全加固脚本分享完毕。


0x02 CentOS7 系统初始化安全加固

描述: 适用于企业内部 CentOS7 系列服务器操作系统初始化、系统安全加固脚本,内容包含了,网络初始化设置,软件更新源替换以及内核版本升级 ,时间时区初始化设置 系统安全加固(符合等保三级主机测评项) 安全运维设置 、系统内核参数优化、 常用软件安装等 一系列的操作直接开箱即用, 将跑过该脚本的机器可以克隆成为作为线上生产环境的基线模板。

CentOS7 安全加固效果

CentOS7 TLS Security Initiate
CentOS7-InitializeSecurity.sh

#!/bin/bash
# @Author: WeiyiGeek
# @Description: CentOS7 TLS Security Initiate
# @Create Time:  2019年5月6日 11:04:42
# @Last Modified time: 2021-11-15 11:06:31
# @E-mail: master@weiyigeek.top
# @Blog: https://www.weiyigeek.top
# @wechat: WeiyiGeeker
# @Github: https://github.com/WeiyiGeek/SecOpsDev/tree/master/OS-操作系统/Linux/
# @Version: 3.2
## ----------------------------------------- ##
# 脚本主要功能说明:
# (1) CentOS7系统初始化操作包括IP地址设置、基础软件包更新以及安装加固。
# (2) CentOS7系统容器以及JDK相关环境安装。
# (3) CentOS7系统中异常错误日志解决。
# (4) CentOS7系统中常规服务安装配置,加入数据备份目录。
## ----------------------------------------- ##

## 系统全局变量定义
# [系统配置]
HOSTNAME=SecurityServerTemplate
EXECTIME=$(date +%Y%m%d-%m%S)

# [网络配置]
IPADDR=192.168.1.2
NETMASK=225.255.255.0
GATEWAY=192.168.1.1
DNSIP=("223.5.5.5" "223.6.6.6")
SSHPORT=20211

# [用户设置]
DefaultUser="WeiyiGeek"
ROOTPASS=WeiyiGeek  # 密码建议12位以上且包含数字、大小写字母以及特殊字符。
APPPASS=WeiyiGeek

# [SNMP配置]
SNMP_user=WeiyiGeek
SNMP_group=testgroup
SNMP_view=testview
SNMP_password=dont_use_public
SNMP_ip=127.0.0.1

# [配置备份目录]
BACKUPDIR=/var/log/.backups
if [ ! -d ${BACKUPDIR} ];then  mkdir -vp ${BACKUPDIR}; fi


## 名称: err 、info 、warning
## 用途:全局Log信息打印函数
## 参数: $@
log::err() {
  printf "[$(date +'%Y-%m-%dT%H:%M:%S')]: \033[31mERROR: $@ \033[0m\n"
}
log::info() {
  printf "[$(date +'%Y-%m-%dT%H:%M:%S')]: \033[32mINFO: $@ \033[0m\n"
}
log::warning() {
  printf "[$(date +'%Y-%m-%dT%H:%M:%S')]: \033[33mWARNING: $@ \033[0m\n"
}


## 名称: os::Network
## 用途: 操作系统网络配置相关脚本包括(IP地址修改)
## 参数: 无
os::Network(){
  log::info "[-] 操作系统网络配置相关脚本,开始执行....."

 # (1) 静态网络IP地址设置
tee /opt/network.sh <<'EOF'
#!/bin/bash
IPADDR="${1}"
NETMASK="${2}"
GATEWAY="${3}"
DEVNAME="ifcfg-ens192"
if [ "${4}" != "" ];then
  DEVNAME="ifcfg-${4}"
fi

if [[ $# -lt 3 ]];then
  echo -e "\e[32m[*] Usage: $0 IP-Address MASK Gateway \e[0m"
  echo -e "\e[32m[*] Usage: $0 192.168.1.99 255.255.255.0 192.168.1.1 \e[0m"
  exit 1
fi
NET_FILE="/etc/sysconfig/network-scripts/${DEVNAME}"
if [[ ! -f ${NET_FILE} ]];then
  log::err "[*] Not Found ${NET_FILE} File"
  exit 2
fi
cp ${NET_FILE}{,.bak}
sed -i -e 's/^ONBOOT=.*$/ONBOOT="yes"/' -e 's/^BOOTPROTO=.*$/BOOTPROTO="static"/' ${NET_FILE}
grep -q "^IPADDR=.*$" ${NET_FILE} &&  sed -i "s/^IPADDR=.*$/IPADDR=\"${IPADDR}\"/" ${NET_FILE} || echo "IPADDR=\"${IPADDR}\"" >> ${NET_FILE}
grep -q "^NETMASK=.*$" ${NET_FILE} &&  sed -i "s/^NETMASK=.*$/NETMASK=\"${NETMASK}\"/" ${NET_FILE} || echo "NETMASK=\"${NETMASK}\"" >> ${NET_FILE}
grep -q "^GATEWAY=.*$" ${NET_FILE} &&  sed -i "s/^GATEWAY=.*$/IPADDR=\"${GATEWAY}\"/" ${NET_FILE} || echo "GATEWAY=\"${GATEWAY}\"" >> ${NET_FILE}
EOF
chmod +x /opt/network.sh
/opt/network.sh ${IPADDR} ${NETMASK} ${GATEWAY}

# (2) 系统主机名与本地解析设置
sudo hostnamectl set-hostname ${HOSTNAME} 
# sed -i "s/127.0.1.1\s.\w.*$/127.0.1.1 ${NAME}/g" /etc/hosts
cp -a /etc/hosts  ${BACKUPDIR}/hosts.bak
grep -q "^\$(hostname -I)\s.\w.*$" /etc/hosts && sed -i "s/\$(hostname -I)\s.\w.*$/${IPADDR} ${HOSTNAME}" /etc/hosts || echo "${IPADDR} ${HOSTNAME}" >> /etc/hosts

# (3) 系统DNS域名解析服务设置
cp -a /etc/resolv.conf  ${BACKUPDIR}/resolv.conf.bak
for dns in  ${DNSIP[@]};do echo "nameserver ${dns}" >> /etc/resolv.conf;done

log::info "[*] network configure modifiy successful! restarting Network........."
service network restart && ip addr
}



## 名称: os::Software
## 用途: 操作系统软件包管理及更新源配置相关脚本
## 参数: 无
os::Software () {
  log::info "[-] 操作系统软件包管理及更新源配置相关脚本,开始执行....."
  cp -a /etc/yum.repos.d/CentOS-Base.repo ${BACKUPDIR}/CentOS-Base.repo

# (1) CentOS 软件仓库镜像源配置&&初始化更新
  log::info "[*] CentOS 软件仓库镜像源配置&&初始化更新 "
curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
curl -o /etc/yum.repos.d/CentOS-epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
sed -i "s#mirrors.cloud.aliyuncs.com#mirrors.aliyun.com#g" /etc/yum.repos.d/CentOS-Base.repo
rpm --import http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7
yum clean all && yum makecache
yum --exclude=kernel* update -y && yum upgrade -y &&  yum -y install epel*


# (2) CentOS 操作系统内核升级(可选)
  cp -a /etc/grub2.cfg ${BACKUPDIR}/grub2.cfg.kernelupdate.bak
  log::info "[*] CentOS 操作系统内核升级(可选) "
rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org
yum -y install https://www.elrepo.org/elrepo-release-7.el7.elrepo.noarch.rpm
yum --disablerepo="*" --enablerepo=elrepo-kernel repolist
yum --disablerepo="*" --enablerepo=elrepo-kernel list kernel*
# 内核安装,服务器里我们选择长期lt版本,安全稳定是我们最大的需求,除非有特殊的需求内核版本需求;
yum update -y --enablerepo=elrepo-kernel 
# 内核版本介绍, lt:longterm 的缩写长期维护版, ml:mainline 的缩写最新主线版本;
yum install -y --enablerepo=elrepo-kernel --skip-broken kernel-lt kernel-lt-devel kernel-lt-tools
# yum -y --enablerepo=elrepo-kernel --skip-broken install kernel-ml.x86_64 kernel-ml-devel.x86_64 kernel-ml-tools.x86_64
  log::warning "[*] 当前 CentOS 操作系统可切换的内核内核版本"
awk -F \' '$1=="menuentry " {print i++ " : " $2}' /etc/grub2.cfg
sudo grub2-set-default 0
#传统引导
# grub2-mkconfig -o /boot/grub2/grub.cfg
# grubby --default-kernel
reboot

# (3) 安装常用的运维软件
# 编译软件
yum install -y gcc gcc-c++ g++ make jq libpam-cracklib openssl-devel bzip2-devel
# 常规软件
yum install -y nano vim git unzip wget ntpdate dos2unix net-tools
yum install -y tree htop ncdu nload sysstat psmisc bash-completion fail2ban nfs-utils chrony
# 清空缓存和已下载安装的软件包
yum clean all

  log::info "[*] Software configure modifiy successful!Please Happy use........."
}


## 名称: os::TimedataZone
## 用途: 操作系统系统时间时区配置相关脚本
## 参数: 无
os::TimedataZone() {
  log::info "[*] 操作系统系统时间时区配置相关脚本,开始执行....."

# (1) 时区设置东8区
log::info "[*] 时区设置前的时间: $(date -R) "
timedatectl
cp -a /etc/localtime ${BACKUPDIR}/localtime.bak
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

# (2) 时间同步软件安装
grep -q "192.168.12.254" /etc/chrony.conf || sudo tee -a /etc/chrony.conf <<'EOF'
pool 192.168.12.254 iburst maxsources 1
pool 192.168.10.254 iburst maxsources 1
pool 192.168.4.254 iburst maxsources 1
pool ntp.aliyun.com iburst maxsources 4
keyfile /etc/chrony.keys
driftfile /var/lib/chrony/chrony.drift
logdir /var/log/chrony
maxupdateskew 100.0
rtcsync
makestep 1.0 3
#stratumweight 0.05
#noclientlog
#logchange 0.5
EOF
systemctl enable chronyd && systemctl restart chronyd && systemctl status chronyd -l

# 将当前的 UTC 时间写入硬件时钟 (硬件时间默认为UTC)
sudo timedatectl set-local-rtc 0
# 启用NTP时间同步:
timedatectl set-ntp yes
# 时间服务器连接查看
chronyc tracking
# 手动校准-强制更新时间
# chronyc -a makestep
# 硬件时钟(系统时钟同步硬件时钟 )
hwclock --systohc 
# 备用方案: 采用 ntpdate 进行时间同步 ntpdate 192.168.10.254

# (3) 重启依赖于系统时间的服务
sudo systemctl restart rsyslog.service crond.service

log::info "[*] Tie confmigure modifiy successful! restarting chronyd rsyslog.service crond.service........."
timedatectl
}


## 名称: os::Security
## 用途: 操作系统安全加固配置脚本(符合等保要求-三级要求)
## 参数: 无
os::Security () {
  log::info "[-] 操作系统安全加固配置(符合等保要求-三级要求)"


# (0) 系统用户及其终端核查配置
  log::info "[-] 锁定或者删除多余的系统账户以及创建低权限用户"
  # cat /etc/passwd | cut -d ":" -f 1 | tr '\n' ' '
defaultuser=(root bin daemon adm lp sync shutdown halt mail operator games ftp nobody systemd-network dbus polkitd sshd postfix chrony ntp rpc rpcuser nfsnobody)
for i in $(cat /etc/passwd | cut -d ":" -f 1,7);do
  flag=0; name=${i%%:*}; terminal=${i##*:}
  if [[ "${terminal}" == "/bin/bash" || "${terminal}" == "/bin/sh" ]];then
    log::warning "${i} 用户,shell终端为 /bin/bash 或者 /bin/sh"
  fi
  for j in ${defaultuser[@]};do
    if [[ "${name}" == "${j}" ]];then
      flag=1
      break;
    fi
  done
  if [[ $flag -eq 0 ]];then
    log::warning "${i} 非默认用户"
  fi
done
cp -a /etc/shadow ${BACKUPDIR}/'shadow-'${EXECTIME}.bak
passwd -l adm&>/dev/null 2&>/dev/null; passwd -l daemon&>/dev/null 2&>/dev/null; passwd -l bin&>/dev/null 2&>/dev/null; passwd -l sys&>/dev/null 2&>/dev/null; passwd -l lp&>/dev/null 2&>/dev/null; passwd -l uucp&>/dev/null 2&>/dev/null; passwd -l nuucp&>/dev/null 2&>/dev/null; passwd -l smmsplp&>/dev/null 2&>/dev/null; passwd -l mail&>/dev/null 2&>/dev/null; passwd -l operator&>/dev/null 2&>/dev/null; passwd -l games&>/dev/null 2&>/dev/null; passwd -l gopher&>/dev/null 2&>/dev/null; passwd -l ftp&>/dev/null 2&>/dev/null; passwd -l nobody&>/dev/null 2&>/dev/null; passwd -l nobody4&>/dev/null 2&>/dev/null; passwd -l noaccess&>/dev/null 2&>/dev/null; passwd -l listen&>/dev/null 2&>/dev/null; passwd -l webservd&>/dev/null 2&>/dev/null; passwd -l rpm&>/dev/null 2&>/dev/null; passwd -l dbus&>/dev/null 2&>/dev/null; passwd -l avahi&>/dev/null 2&>/dev/null; passwd -l mailnull&>/dev/null 2&>/dev/null; passwd -l nscd&>/dev/null 2&>/dev/null; passwd -l vcsa&>/dev/null 2&>/dev/null; passwd -l rpc&>/dev/null 2&>/dev/null; passwd -l rpcuser&>/dev/null 2&>/dev/null; passwd -l nfs&>/dev/null 2&>/dev/null; passwd -l sshd&>/dev/null 2&>/dev/null; passwd -l pcap&>/dev/null 2&>/dev/null; passwd -l ntp&>/dev/null 2&>/dev/null; passwd -l haldaemon&>/dev/null 2&>/dev/null; passwd -l distcache&>/dev/null 2&>/dev/null; passwd -l webalizer&>/dev/null 2&>/dev/null; passwd -l squid&>/dev/null 2&>/dev/null; passwd -l xfs&>/dev/null 2&>/dev/null; passwd -l gdm&>/dev/null 2&>/dev/null; passwd -l sabayon&>/dev/null 2&>/dev/null; passwd -l named&>/dev/null 2&>/dev/null


# (2) 用户密码设置和口令策略设置
log::info "[-]  配置满足策略的root管理员密码 "
echo "root:${ROOTPASS}" | chpasswd

log::info "[-] 配置满足策略的app普通用户密码(根据需求配置)"
groupadd application
useradd -m -s /bin/bash -c "application primary user" -g application app 
echo "root:${APPPASS}" | chpasswd

log::info "[-] 强制用户在下次登录时更改密码 "
chage -d 0 -m 0 -M 90 -W 15 root && passwd --expire root  
chage -d 0 -m 0 -M 90 -W 15 app && passwd --expire app
chage -d 0 -m 0 -M 90 -W 15 ${DefaultUser} && passwd --expire ${DefaultUser} 

log::info "[-] 用户口令复杂性策略设置 (密码过期周期0~90、到期前15天提示、密码长度至少15、复杂度设置至少有一个大小写、数字、特殊字符、密码三次不能一样、尝试次数为三次)"
# 相关修改文件备份
cp /etc/login.defs ${BACKUPDIR}/login.defs.bak;
cp /etc/pam.d/password-auth ${BACKUPDIR}/password-auth.bak
cp /etc/pam.d/system-auth ${BACKUPDIR}/system-auth.bak
egrep -q "^\s*PASS_MIN_DAYS\s+\S*(\s*#.*)?\s*$" /etc/login.defs && sed -ri "s/^(\s*)PASS_MIN_DAYS\s+\S*(\s*#.*)?\s*$/\PASS_MIN_DAYS  0/" /etc/login.defs || echo "PASS_MIN_DAYS  0" >> /etc/login.defs
egrep -q "^\s*PASS_MAX_DAYS\s+\S*(\s*#.*)?\s*$" /etc/login.defs && sed -ri "s/^(\s*)PASS_MAX_DAYS\s+\S*(\s*#.*)?\s*$/\PASS_MAX_DAYS  90/" /etc/login.defs || echo "PASS_MAX_DAYS  90" >> /etc/login.defs
egrep -q "^\s*PASS_WARN_AGE\s+\S*(\s*#.*)?\s*$" /etc/login.defs && sed -ri "s/^(\s*)PASS_WARN_AGE\s+\S*(\s*#.*)?\s*$/\PASS_WARN_AGE  15/" /etc/login.defs || echo "PASS_WARN_AGE  15" >> /etc/login.defs
egrep -q "^\s*PASS_MIN_LEN\s+\S*(\s*#.*)?\s*$" /etc/login.defs && sed -ri "s/^(\s*)PASS_MIN_LEN\s+\S*(\s*#.*)?\s*$/\PASS_MIN_LEN  15/" /etc/login.defs || echo "PASS_MIN_LEN  15" >> /etc/login.defs

egrep -q "^password\s.+pam_pwquality.so\s+\w+.*$" /etc/pam.d/password-auth && sed -ri '/^password\s.+pam_pwquality.so/{s/pam_pwquality.so\s+\w+.*$/pam_pwquality.so try_first_pass local_users_only retry=3 authtok_type=  minlen=15 ucredit=-1 lcredit=-1 dcredit=-1 ocredit=-1 difok=1 enforce_for_root/g;}' /etc/pam.d/password-auth
egrep -q "^password\s.+pam_unix.so\s+\w+.*$" /etc/pam.d/password-auth && sed -ri '/^password\s.+pam_unix.so/{s/pam_unix.so\s+\w+.*$/pam_unix.so sha512 shadow nullok try_first_pass use_authtok remember=3/g;}' /etc/pam.d/password-auth

egrep -q "^password\s.+pam_pwquality.so\s+\w+.*$" /etc/pam.d/system-auth && sed -ri '/^password\s.+pam_pwquality.so/{s/pam_pwquality.so\s+\w+.*$/pam_pwquality.so try_first_pass local_users_only retry=3 authtok_type=  minlen=15 ucredit=-1 lcredit=-1 dcredit=-1 ocredit=-1 difok=1 enforce_for_root/g;}' /etc/pam.d/system-auth
egrep -q "^password\s.+pam_unix.so\s+\w+.*$" /etc/pam.d/system-auth && sed -ri '/^password\s.+pam_unix.so/{s/pam_unix.so\s+\w+.*$/pam_unix.so sha512 shadow nullok try_first_pass use_authtok remember=3/g;}' /etc/pam.d/system-auth

log::info "[-] 存储用户密码的文件,其内容经过sha512加密,所以非常注意其权限"
# 解决首次登录配置密码时提示"passwd: Authentication token manipulation error"
touch /etc/security/opasswd && chown root:root /etc/security/opasswd && chmod 600 /etc/security/opasswd 


# (3) 设置用户sudo权限以及重要目录和文件的新建默认权限
log::info "[-] 用户sudo权限以及重要目录和文件的新建默认权限设置"
cp /etc/sudoers ${BACKUPDIR}/sudoers.bak
# 如CentOS安装时您创建的用户 WeiyiGeek 防止直接通过 sudo passwd 修改root密码(此时必须要求输入WeiyiGeek密码后才可修改root密码)
# Tips: Sudo允许授权用户权限以另一个用户(通常是root用户)的身份运行程序, 
# DefaultUser="weiyigeek"
sed -i "/# Allows members of the/i ${DefaultUser} ALL=(ALL) PASSWD:ALL" /etc/sudoers

# 此参数需要根据业务来定,否则在使用时候会出现某些权限不足导致程序安装报错
log::info "[-] 配置用户 umask 为022 "
cp -a /etc/profile ${BACKUPDIR}/profile
egrep -q "^\s*umask\s+\w+.*$" /etc/profile && sed -ri "s/^\s*umask\s+\w+.*$/umask 022/" /etc/profile || echo "umask 022" >> /etc/profile 
# log::info "[-] 设置用户目录创建默认权限, (初始为077比较严格)在未设置umask为027 则默认为077"
# egrep -q "^\s*umask\s+\w+.*$" /etc/csh.login && sed -ri "s/^\s*umask\s+\w+.*$/umask 022/" /etc/csh.login || echo "umask 022" >> /etc/csh.login
# egrep -q "^\s*umask\s+\w+.*$" /etc/csh.cshrc && sed -ri "s/^\s*umask\s+\w+.*$/umask 022/" /etc/csh.cshrc || echo "umask 022" >> /etc/csh.cshrc
# egrep -q "^\s*(umask|UMASK)\s+\w+.*$" /etc/login.defs && sed -ri "s/^\s*(umask|UMASK)\s+\w+.*$/UMASK 027/" /etc/login.defs || echo "UMASK 027" >> /etc/login.defs

log::info "[-] 设置或恢复重要目录和文件的权限(设置日志文件非全局可写)"
chmod 600 ~/.ssh/authorized_keys;
chmod 755 /etc;
chmod 755 /etc/passwd; 
chmod 755 /etc/shadow; 
chmod 755 /etc/security; 
chmod 644 /etc/group; 
chmod 644 /etc/services; 
chmod 750 /etc/rc*.d;
chmod 755 /var/log/messages;
chmod 775 /var/log/spooler;
chmod 775 /var/log/cron;
chmod 775 /var/log/secure;
chmod 775 /var/log/maillog;
chmod 775 /var/log/mail&>/dev/null 2&>/dev/null; 
chmod 775 /var/log/localmessages&>/dev/null 2&>/dev/null

log::info "[-] 删除潜在威胁文件 "
find / -maxdepth 3 -name hosts.equiv | xargs rm -rf
find / -maxdepth 3 -name .netrc | xargs rm -rf
find / -maxdepth 3 -name .rhosts | xargs rm -rf

# (4) SSHD 服务安全加固设置以及网络登陆Banner设置
log::info "[-] sshd 服务安全加固设置"
cp /etc/ssh/sshd_config ${BACKUPDIR}/sshd_config.bak
# 严格模式
sudo egrep -q "^\s*StrictModes\s+.+$" /etc/ssh/sshd_config && sed -ri "s/^(#)?\s*StrictModes\s+.+$/StrictModes yes/" /etc/ssh/sshd_config || echo "StrictModes yes" >> /etc/ssh/sshd_config
# 默认的监听端口更改
if [ -e ${SSHPORT} ];then export SSHPORT=20211;fi
sudo egrep -q "^\s*Port\s+.+$" /etc/ssh/sshd_config && sed -ri "s/^(#)?\s*Port\s+.+$/Port ${SSHPORT}/" /etc/ssh/sshd_config || echo "Port ${SSHPORT}" >> /etc/ssh/sshd_config
# 禁用X11转发以及端口转发
sudo egrep -q "^\s*X11Forwarding\s+.+$" /etc/ssh/sshd_config && sed -ri "s/^(#)?\s*X11Forwarding\s+.+$/X11Forwarding no/" /etc/ssh/sshd_config || echo "X11Forwarding no" >> /etc/ssh/sshd_config
sudo egrep -q "^\s*X11UseLocalhost\s+.+$" /etc/ssh/sshd_config && sed -ri "s/^(#)?\s*X11UseLocalhost\s+.+$/X11UseLocalhost yes/" /etc/ssh/sshd_config || echo "X11UseLocalhost yes" >> /etc/ssh/sshd_config
sudo egrep -q "^\s*AllowTcpForwarding\s+.+$" /etc/ssh/sshd_config && sed -ri "s/^(#)?\s*AllowTcpForwarding\s+.+$/AllowTcpForwarding no/" /etc/ssh/sshd_config || echo "AllowTcpForwarding no" >> /etc/ssh/sshd_config
sudo egrep -q "^\s*AllowAgentForwarding\s+.+$" /etc/ssh/sshd_config && sed -ri "s/^(#)?\s*AllowAgentForwarding\s+.+$/AllowAgentForwarding no/" /etc/ssh/sshd_config || echo "AllowAgentForwarding no" >> /etc/ssh/sshd_config
# 关闭禁用用户的 .rhosts 文件  ~/.ssh/.rhosts 来做为认证: 缺省IgnoreRhosts yes 
egrep -q "^(#)?\s*IgnoreRhosts\s+.+$" /etc/ssh/sshd_config && sed -ri "s/^(#)?\s*IgnoreRhosts\s+.+$/IgnoreRhosts yes/" /etc/ssh/sshd_config || echo "IgnoreRhosts yes" >> /etc/ssh/sshd_config
# 禁止root远程登录(推荐配置-根据需求配置)
egrep -q "^\s*PermitRootLogin\s+.+$" /etc/ssh/sshd_config && sed -ri "s/^\s*PermitRootLogin\s+.+$/PermitRootLogin no/" /etc/ssh/sshd_config || echo "PermitRootLogin no" >> /etc/ssh/sshd_config
# 登陆前后欢迎提示设置
egrep -q "^\s*(banner|Banner)\s+\W+.*$" /etc/ssh/sshd_config && sed -ri "s/^\s*(banner|Banner)\s+\W+.*$/Banner \/etc\/issue/" /etc/ssh/sshd_config || \
echo "Banner /etc/issue" >> /etc/ssh/sshd_config
log::info "[-] 远程SSH登录前后提示警告Banner设置"
# SSH登录前后提示警告Banner设置
sudo tee /etc/issue <<'EOF'
****************** [ 安全登陆 (Security Login) ] *****************
Authorized only. All activity will be monitored and reported.By Security Center.

EOF
# SSH登录后提示Banner
# 艺术字B格: http://www.network-science.de/ascii/
sudo tee /etc/motd <<'EOF'

################## [ 安全运维 (Security Operation) ] ####################
            __          __  _       _  _____           _    
            \ \        / / (_)     (_)/ ____|         | |   
            \ \  /\  / /__ _ _   _ _| |  __  ___  ___| | __
              \ \/  \/ / _ \ | | | | | | |_ |/ _ \/ _ \ |/ /
              \  /\  /  __/ | |_| | | |__| |  __/  __/   < 
                \/  \/ \___|_|\__, |_|\_____|\___|\___|_|\_\
                              __/ |                        
                              |___/              

Login success. Please execute the commands and operation data after carefully.By WeiyiGeek

EOF


# (5) 用户远程登录失败次数与终端超时设置 
log::info "[-] 用户远程连续登录失败5次锁定帐号5分钟包括root账号"
cp /etc/pam.d/sshd ${BACKUPDIR}/sshd.bak
cp /etc/pam.d/login ${BACKUPDIR}/login.bak

# 远程登陆
sed -ri "/^\s*auth\s+required\s+pam_tally2.so\s+.+(\s*#.*)?\s*$/d" /etc/pam.d/sshd 
sed -ri '2a auth required pam_tally2.so deny=5 unlock_time=300 even_deny_root root_unlock_time=300' /etc/pam.d/sshd 
# 宿主机控制台登陆(可选)
# sed -ri "/^\s*auth\s+required\s+pam_tally2.so\s+.+(\s*#.*)?\s*$/d" /etc/pam.d/login
# sed -ri '2a auth required pam_tally2.so deny=5 unlock_time=300 even_deny_root root_unlock_time=300' /etc/pam.d/login

log::info "[-] 设置登录超时时间为10分钟 "
egrep -q "^\s*(export|)\s*TMOUT\S\w+.*$" /etc/profile && sed -ri "s/^\s*(export|)\s*TMOUT.\S\w+.*$/export TMOUT=600\nreadonly TMOUT/" /etc/profile || echo -e "export TMOUT=600\nreadonly TMOUT" >> /etc/profile
egrep -q "^\s*.*ClientAliveInterval\s\w+.*$" /etc/ssh/sshd_config && sed -ri "s/^\s*.*ClientAliveInterval\s\w+.*$/ClientAliveInterval 600/" /etc/ssh/sshd_config || echo "ClientAliveInterval 600" >> /etc/ssh/sshd_config


# (6) 切换用户日志记录和切换命令更改名称为SU
log::info "[-] 切换用户日志记录和切换命令更改名称为SU "
cp -a /etc/rsyslog.conf  ${BACKUPDIR}/'rsyslog.conf-'${EXECTIME}.bak
egrep -q "^\s*authpriv\.\*\s+.+$" /etc/rsyslog.conf && sed -ri "s/^\s*authpriv\.\*\s+.+$/authpriv.*  \/var\/log\/secure/" /etc/rsyslog.conf || echo "authpriv.*  /var/log/secure" >> /etc/rsyslog.conf
egrep -q "^(\s*)SULOG_FILE\s+\S*(\s*#.*)?\s*$" /etc/login.defs && sed -ri "s/^(\s*)SULOG_FILE\s+\S*(\s*#.*)?\s*$/\SULOG_FILE  \/var\/log\/.history\/sulog/" /etc/login.defs || echo "SULOG_FILE  /var/log/.history/sulog" >> /etc/login.defs
egrep -q "^\s*SU_NAME\s+\S*(\s*#.*)?\s*$" /etc/login.defs && sed -ri "s/^(\s*)SU_NAME\s+\S*(\s*#.*)?\s*$/\SU_NAME  SU/" /etc/login.defs || echo "SU_NAME  SU" >> /etc/login.defs
mkdir -vp /usr/local/bin /var/log/.history
cp /usr/bin/su ${BACKUPDIR}/su.bak
mv /usr/bin/su /usr/bin/SU
chmod 777 /var/log/.history 


# (7) 用户终端执行的历史命令记录
log::info "[-] 用户终端执行的历史命令记录 "
egrep -q "^HISTSIZE\W\w+.*$" /etc/profile && sed -ri "s/^HISTSIZE\W\w+.*$/HISTSIZE=101/" /etc/profile || echo "HISTSIZE=101" >> /etc/profile
sudo tee /etc/profile.d/history-record.sh <<'EOF'
# 历史命令执行记录文件路径
LOGTIME=$(date +%Y%m%d-%H-%M-%S)
export HISTFILE="/var/log/.history/${USER}.${LOGTIME}.history"
if [ ! -f ${HISTFILE} ];then
  touch ${HISTFILE}
fi
chmod 600 /var/log/.history/${USER}.${LOGTIME}.history
# 历史命令执行文件大小记录设置
HISTFILESIZE=128
HISTTIMEFORMAT="%F_%T $(whoami)#$(who -u am i 2>/dev/null| awk '{print $NF}'|sed -e 's/[()]//g'):"
EOF


# (8) GRUB 安全设置
  log::info "[-] 系统 GRUB 安全设置(防止物理接触从grub菜单中修改密码) "
# Grub 关键文件备份
cp -a /etc/grub.d/00_header ${BACKUPDIR}/'00_header'${EXECTIME}.bak
cp -a /etc/grub.d/10_linux ${BACKUPDIR}/'10_linux'${EXECTIME}.bak
# 设置Grub菜单界面显示时间
sed -i -e 's|set timeout_style=${style}|#set timeout_style=${style}|g' -e 's|set timeout=${timeout}|set timeout=3|g' /etc/grub.d/00_header
# sed -i -e 's|GRUB_TIMEOUT_STYLE=hidden|#GRUB_TIMEOUT_STYLE=hidden|g' -e 's|GRUB_TIMEOUT=0|GRUB_TIMEOUT=3|g' /etc/default/grub
# grub 用户认证密码创建
sudo grub2-mkpasswd-pbkdf2
# 输入口令:
# Reeter password:n
PBKDF2 hash of your password is grub.pbkdf2.sha512.10000.A4A6B06EFAB660C11DD8EBC3BE73C5AB5D763ED937060477DB533B3E7D60F1DE66C3AC12DA795B46762AB8C4A1911B69B94FFCD88FB4499938150405DCB116F8.35D290F5B8D2677AEE5E8BAB4DB133206D417F99A26B14EAB8D0A5379DCD3632F40037388C9D2CA3001E0D6A8B74837549970EEEAEC3420CE38E2236DE1A8565
# 设置认证用户以及上面生成的password_pbkdf2认证密钥
tee -a /etc/grub.d/00_header <<'END'
cat <<'EOF'
# GRUB Authentication
set superusers="grub"
password_pbkdf2 grub grub.pbkdf2.sha512.10000.A4A6B06EFAB660C11DD8EBC3BE73C5AB5D763ED937060477DB533B3E7D60F1DE66C3AC12DA795B46762AB8C4A1911B69B94FFCD88FB4499938150405DCB116F8.35D290F5B8D2677AEE5E8BAB4DB133206D417F99A26B14EAB8D0A5379DCD3632F40037388C9D2CA3001E0D6A8B74837549970EEEAEC3420CE38E2236DE1A8565
EOF
END
# 设置进入正式系统不需要认证如进入单用户模式进行重置账号密码时需要进行认证。 (高敏感数据库系统不建议下述操作)
# 在 135 加入 -unrestricted ,例如
# 133 echo "menuentry $(echo "$title" | grub_quote)' ${CLASS} \$menuentry_id_option 'gnulinux-$version-$type-    $boot_device_id' {" | sed "s/^/$submenu_indentation/"
# 134   else
# 135 echo "menuentry --unrestricted '$(echo "$os" | grub_quote)' ${CLASS} \$menuentry_id_option 'gnulinux-simple-$boot_devic    e_id' {" | sed "s/^/$submenu_indentation/"
sed -i '/echo "$title" | grub_quote/ { s/menuentry /menuentry --user=grub /;}' /etc/grub.d/10_linux
sed -i '/echo "$os" | grub_quote/ { s/menuentry /menuentry --unrestricted /;}' /etc/grub.d/10_linux
# CentOS 方式更新GRUB从而生成boot启动文件
grub2-mkconfig -o /boot/grub2/grub.cfg


# (9) 记录安全事件日志
  log::info "[-] 记录安全事件日志"
touch /var/log/.history/adm&>/dev/null; chmod 755 /var/log/.history/adm
semanage fcontext -a -t security_t '/var/log/.history/adm'
restorecon -v '/var/log/.history/adm'&>/dev/null
egrep -q "^\s*\*\.err;kern.debug;daemon.notice\s+.+$" /etc/rsyslog.conf && sed -ri "s/^\s*\*\.err;kern.debug;daemon.notice\s+.+$/*.err;kern.debug;daemon.notice  \/var\/log\/.history\/adm/" /etc/rsyslog.conf || echo "*.err;kern.debug;daemon.notice  /var/log/.history/adm" >> /etc/rsyslog.conf


# (10) 配置自动屏幕锁定(适用于具备图形界面的设备), 非图形界面不需要执行
  log::info "[-] 对于有图形界面的系统配置10分钟屏幕锁定"
# gconftool-2 --direct \
# --config-source xml:readwrite:/etc/gconf/gconf.xml.mandatory \
# --type bool \
# --set /apps/gnome-screensaver/idle_activation_enabled true \
# --set /apps/gnome-screensaver/lock_enabled true \
# --type int \
# --set /apps/gnome-screensaver/idle_delay 10 \
# --type string \
# --set /apps/gnome-screensaver/mode blank-only


# (10) 关闭CentOS服务器中 SELINUX 以及防火墙端口放行
  log::info "[-] SELINUX 禁用以及系统防火墙规则设置 "
sed -i "s/SELINUX=enforcing/SELINUX=disabled/g" /etc/selinux/config 
firewall-cmd --zone=public --add-port=20211/tcp --permanent
firewall-cmd --zone=public --add-port=161/udp --permanent
firewall-cmd --reload
systemctl restart sshd
reboot
}


## 名称: os::Operation 
## 用途: 操作系统安全运维设置相关脚本
## 参数: 无
os::Operation () {
  log::info "[-] 操作系统安全运维设置相关脚本"

# (0) 禁用ctrl+alt+del组合键对系统重启 (必须要配置,我曾入过坑)
 log::info "[-] 禁用控制台ctrl+alt+del组合键重启"
mv /usr/lib/systemd/system/ctrl-alt-del.target ${BACKUPDIR}/'ctrl-alt-del.target-'${EXECTIME}.bak

# (1) 设置文件删除回收站别名
  log::info "[-] 设置文件删除回收站别名(防止误删文件) "
sudo tee -a  /etc/profile.d/alias.sh <<'EOF'
# User specific aliases and functions
# 删除回收站
# find ~/.trash -delete
# 删除空目录
# find ~/.trash -type d -delete
alias rm="sh /usr/local/bin/remove.sh"
EOF
sudo tee /usr/local/bin/remove.sh <<'EOF'
#!/bin/sh
# 定义回收站文件夹目录.trash
trash="/.trash"
deltime=$(date +%Y%m%d-%H-%M-%S)
TRASH_DIR="${HOME}${trash}/${deltime}"
# 建立回收站目录当不存在的时候
if [ ! -e ${TRASH_DIR} ];then
   mkdir -p ${TRASH_DIR}
fi
for i in $*;do
  if [ "$i" = "-rf" ];then continue;fi
    #定义秒时间戳
    STAMP=$(date +%s)
    #得到文件名称(非文件夹),参考man basename
    fileName=$(basename $i)
    #将输入的参数,对应文件mv至.trash目录,文件后缀,为当前的时间戳
    mv $i ${TRASH_DIR}/${fileName}.${STAMP}
done
EOF
sudo chmod +775 /usr/local/bin/remove.sh /etc/profile.d/alias.sh /etc/profile.d/history-record.sh
sudo chmod a+x /usr/local/bin/remove.sh /etc/profile.d/alias.sh /etc/profile.d/history-record.sh
source /etc/profile.d/alias.sh  /etc/profile.d/history-record.sh

}



## 名称: os::DisableService
## 用途: 禁用与设置操作系统中某些服务(需要根据实际环境进行)
## 参数: 无
os::DisableService () {
  log::info "[-] 禁用操作系统中某些服务(需要根据实际环境进行配置)"

  log::info "[-] 配置禁用telnet服务"
cp /etc/services ${BACKUPDIR}/'services-'${EXECTIME}.bak
egrep -q "^\s*telnet\s+\d*.+$" /etc/services && sed -ri "/^\s*telnet\s+\d*.+$/s/^/# /" /etc/services

  log::info "[-] 禁止匿名与root用户用户登录FTP"
if [ -f /etc/vsftpd/vsftpd.conf ];then
cp /etc/vsftpd/vsftpd.conf /etc/vsftpd/'vsftpd.conf-'`date +%Y%m%d`.bak
systemctl list-unit-files|grep vsftpd > /dev/null && sed -ri "/^\s*anonymous_enable\s*\W+.+$/s/^/#/" /etc/vsftpd/vsftpd.conf && echo "anonymous_enable=NO" >> /etc/vsftpd/vsftpd.conf
systemctl list-unit-files|grep vsftpd > /dev/null && echo "root" >> /etc/vsftpd/ftpusers
  log::info "[-] 限制FTP用户上传的文件所具有的权限"
systemctl list-unit-files|grep vsftpd > /dev/null && sed -ri "/^\s*write_enable\s*\W+.+$/s/^/#/" /etc/vsftpd/vsftpd.conf && echo "write_enable=NO" >> /etc/vsftpd/vsftpd.conf
systemctl list-unit-files|grep vsftpd > /dev/null && sed -ri "/^\s*ls_recurse_enable\s*\W+.+$/s/^/#/" /etc/vsftpd/vsftpd.conf && echo "ls_recurse_enable=NO" >> /etc/vsftpd/vsftpd.conf
systemctl list-unit-files|grep vsftpd > /dev/null && sed -ri "/^\s*anon_umask\s*\W+.+$/s/^/#/" /etc/vsftpd/vsftpd.conf && echo "anon_umask=077" >> /etc/vsftpd/vsftpd.conf
systemctl list-unit-files|grep vsftpd > /dev/null && sed -ri "/^\s*local_umask\s*\W+.+$/s/^/#/" /etc/vsftpd/vsftpd.conf && echo "local_umask=022" >> /etc/vsftpd/vsftpd.conf
  log::info "[-] 限制FTP用户登录后能访问的目录"
systemctl list-unit-files|grep vsftpd > /dev/null && sed -ri "/^\s*chroot_local_user\s*\W+.+$/s/^/#/" /etc/vsftpd/vsftpd.conf && echo "chroot_local_user=NO" >> /etc/vsftpd/vsftpd.conf
  log::info "[-] FTP Banner 设置"
systemctl list-unit-files|grep vsftpd > /dev/null && sed -ri "/^\s*ftpd_banner\s*\W+.+$/s/^/#/" /etc/vsftpd/vsftpd.conf && echo "ftpd_banner='Authorized only. All activity will be monitored and reported.'" >> /etc/vsftpd/vsftpd.conf

  log::info "[-] 限制不必要的服务 (根据实际环境配置)"
# systemctl disable rsh&>/dev/null 2&>/dev/null;systemctl disable talk&>/dev/null 2&>/dev/null;systemctl disable telnet&>/dev/null 2&>/dev/null;systemctl disable tftp&>/dev/null 2&>/dev/null;systemctl disable rsync&>/dev/null 2&>/dev/null;systemctl disable xinetd&>/dev/null 2&>/dev/null;systemctl disable nfs&>/dev/null 2&>/dev/null;systemctl disable nfslock&>/dev/null 2&>/dev/null
fi

  log::info "[-] 配置SNMP默认团体字"
if [ -f /etc/snmp/snmpd.conf ];then
cp /etc/snmp/snmpd.conf ${BACKUPDIR}/'snmpd.conf-'${EXECTIME}.bak
cat > /etc/snmp/snmpd.conf <<EOF
com2sec $SNMP_user  default    $SNMP_password   
group   $SNMP_group         v1           $SNMP_user
group   $SNMP_group         v2c          $SNMP_user
view    systemview      included        .1                      80
view    systemview      included        .1.3.6.1.2.1.1
view    systemview      included        .1.3.6.1.2.1.25.1.1
view    $SNMP_view        included        .1.3.6.1.4.1.2021.80
access  $SNMP_group         ""      any       noauth    exact  systemview none none
access  $SNMP_group         ""      any       noauth    exact  $SNMP_view   none none
dontLogTCPWrappersConnects yes
trapcommunity $SNMP_password
authtrapenable 1
trap2sink $SNMP_ip
agentSecName $SNMP_user
rouser $SNMP_user
defaultMonitors yes
linkUpDownNotifications yes
EOF
fi
}


## 名称: os::optimizationn
## 用途: 操作系统优化设置(内核参数)
## 参数: 无
os::Optimizationn () {
log::info "[-] 正在进行操作系统内核参数优化设置......."

# (1) 系统内核参数的配置(/etc/sysctl.conf)
log::info "[-] 系统内核参数的配置/etc/sysctl.conf"

# /etc/sysctl.d/99-kubernetes-cri.conf
egrep -q "^(#)?net.ipv4.ip_forward.*" /etc/sysctl.conf && sed -ri "s|^(#)?net.ipv4.ip_forward.*|net.ipv4.ip_forward = 1|g"  /etc/sysctl.conf || echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
# egrep -q "^(#)?net.bridge.bridge-nf-call-ip6tables.*" /etc/sysctl.conf && sed -ri "s|^(#)?net.bridge.bridge-nf-call-ip6tables.*|net.bridge.bridge-nf-call-ip6tables = 1|g" /etc/sysctl.conf || echo "net.bridge.bridge-nf-call-ip6tables = 1" >> /etc/sysctl.conf 
# egrep -q "^(#)?net.bridge.bridge-nf-call-iptables.*" /etc/sysctl.conf && sed -ri "s|^(#)?net.bridge.bridge-nf-call-iptables.*|net.bridge.bridge-nf-call-iptables = 1|g" /etc/sysctl.conf || echo "net.bridge.bridge-nf-call-iptables = 1" >> /etc/sysctl.conf
egrep -q "^(#)?net.ipv6.conf.all.disable_ipv6.*" /etc/sysctl.conf && sed -ri "s|^(#)?net.ipv6.conf.all.disable_ipv6.*|net.ipv6.conf.all.disable_ipv6 = 1|g" /etc/sysctl.conf || echo "net.ipv6.conf.all.disable_ipv6 = 1" >> /etc/sysctl.conf
egrep -q "^(#)?net.ipv6.conf.default.disable_ipv6.*" /etc/sysctl.conf && sed -ri "s|^(#)?net.ipv6.conf.default.disable_ipv6.*|net.ipv6.conf.default.disable_ipv6 = 1|g" /etc/sysctl.conf || echo "net.ipv6.conf.default.disable_ipv6 = 1" >> /etc/sysctl.conf
egrep -q "^(#)?net.ipv6.conf.lo.disable_ipv6.*" /etc/sysctl.conf && sed -ri "s|^(#)?net.ipv6.conf.lo.disable_ipv6.*|net.ipv6.conf.lo.disable_ipv6 = 1|g" /etc/sysctl.conf || echo "net.ipv6.conf.lo.disable_ipv6 = 1" >> /etc/sysctl.conf
egrep -q "^(#)?net.ipv6.conf.all.forwarding.*" /etc/sysctl.conf && sed -ri "s|^(#)?net.ipv6.conf.all.forwarding.*|net.ipv6.conf.all.forwarding = 1|g" /etc/sysctl.conf || echo "net.ipv6.conf.all.forwarding = 1"  >> /etc/sysctl.conf
egrep -q "^(#)?vm.max_map_count.*" /etc/sysctl.conf && sed -ri "s|^(#)?vm.max_map_count.*|vm.max_map_count = 262144|g" /etc/sysctl.conf || echo "vm.max_map_count = 262144"  >> /etc/sysctl.conf

tee -a /etc/sysctl.conf <<'EOF'
# 调整提升服务器负载能力之外,还能够防御小流量的Dos、CC和SYN攻击
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
# net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_fin_timeout = 60
net.ipv4.tcp_synack_retries = 1
net.ipv4.tcp_syn_retries = 1
net.ipv4.tcp_fastopen = 3

# 优化TCP的可使用端口范围及提升服务器并发能力(注意一般流量小的服务器上没必要设置如下参数)
net.ipv4.tcp_keepalive_time = 1200
net.ipv4.tcp_max_syn_backlog = 8192
net.ipv4.tcp_max_tw_buckets = 5000
net.ipv4.ip_local_port_range = 1024 65535

# 优化核套接字TCP的缓存区
net.core.netdev_max_backlog = 8192
net.core.somaxconn = 8192
net.core.rmem_max = 12582912
net.core.rmem_default = 6291456
net.core.wmem_max = 12582912
net.core.wmem_default = 6291456
EOF

# (2) Linux 系统的最大进程数和最大文件打开数限制
log::info "[-] Linux 系统的最大进程数和最大文件打开数限制 "
egrep -q "^\s*ulimit -HSn\s+\w+.*$" /etc/profile && sed -ri "s/^\s*ulimit -HSn\s+\w+.*$/ulimit -HSn 65535/" /etc/profile || echo "ulimit -HSn 65535" >> /etc/profile
egrep -q "^\s*ulimit -HSu\s+\w+.*$" /etc/profile && sed -ri "s/^\s*ulimit -HSu\s+\w+.*$/ulimit -HSu 65535/" /etc/profile || echo "ulimit -HSu 65535" >> /etc/profile
sed -i "/# End/i *  soft  nofile  65535" /etc/security/limits.conf
sed -i "/# End/i *  hard  nofile  65535" /etc/security/limits.conf
sed -i "/# End/i *  soft  nproc   65535" /etc/security/limits.conf
sed -i "/# End/i *  hard  nproc   65535" /etc/security/limits.conf
sysctl -p

# 需重启生效
reboot
}

## 名称: os::Swap
## 用途: Liunx 系统创建SWAP交换分区(默认2G)
## 参数: $1(几G)
os::Swap () {
  if [ -e $1 ];then
    sudo dd if=/dev/zero of=/swapfile bs=1024 count=2097152   # 2G Swap 分区 1024 * 1024 , centos 以 1000 为标准
  else
    number=$(echo "${1}*1024*1024"|bc)
    sudo dd if=/dev/zero of=/swapfile bs=1024 count=${number}   # 2G Swap 分区 1024 * 1024 , centos 以 1000 为标准
  fi

  sudo mkswap /swapfile && sudo swapon /swapfile
  if [ $(grep -c "/swapfile" /etc/fstab) -eq 0 ];then
sudo tee -a /etc/fstab <<'EOF'
/swapfile swap swap default 0 0
EOF
fi
sudo swapon --show && sudo free -h
}

## 名称: disk::Lvsmanager
## 用途: CentOS7 操作系统磁盘 LVS 逻辑卷添加与配置(扩容流程)
## 参数: 无
disk::lvsmanager () {
  echo "\n分区信息:"
  sudo df -Th
  sudo lsblk
  echo -e "\n 磁盘信息:"
  sudo fdisk -l
  echo -e "\n PV物理卷查看:"
  sudo pvscan
  echo -e "\n vgs虚拟卷查看:"
  sudo vgs
  echo -e "\n lvscan逻辑卷扫描:"
  sudo lvscan
  echo -e "\n 分区扩展"
  echo "CentOS \n lvextend -L +24G /dev/centos/root"
  echo "lsblk"
  echo -e "Centos \n # xfs_growfs /dev/mapper/centos-root"
}

至此 CentOS7 安全加固脚本完毕。


0x03 Ubuntu 系统初始化安全加固

描述: 适用于企业内部 Ubuntu 系列服务器操作系统初始化、系统安全加固脚本,内容包含了,网络初始化设置,软件更新源替换以及内核版本升级 ,时间时区初始化设置 系统安全加固(等保三级操作系统主机检查项) 安全运维设置 系统内核参数 常用软件安装等 一系列的操作直接开箱即用, 将跑过该脚本的机器可以克隆成为作为线上生产环境的基线模板。

脚本适用说明:
Ubuntu 20.04 (已测试)
Ubuntu 18.04 (部分适用)

Ubuntu 安全加固效果

Ubuntu等保测评

Ubuntu TLS Security Initiate
Ubuntu-InitializeSecurity.sh

#!/bin/bash
# @Author: WeiyiGeek
# @Description: Ubuntu TLS Security Initiate
# @Create Time:  2019年9月1日 16:43:33
# @Last Modified time: 2021-11-15 11:06:31
# @E-mail: master@weiyigeek.top
# @Blog: https://www.weiyigeek.top
# @wechat: WeiyiGeeker
# @Github: https://github.com/WeiyiGeek/SecOpsDev/tree/master/OS-操作系统/Linux/
# @Version: 3.2
#-------------------------------------------------#
# 脚本主要功能说明:
# (1) Ubuntu 系统初始化操作包括IP地址设置、基础软件包更新以及安装加固。
# (2) Ubuntu 系统容器以及JDK相关环境安装。
# (3) Ubuntu 系统中异常错误日志解决。
# (4) Ubuntu 系统常规服务安装配置,加入数据备份目录。
#-------------------------------------------------#

## 系统全局变量定义
HOSTNAME=SecurityServerTemplate
IP=192.168.1.2
GATEWAY=192.168.1.1
DNSIP=("223.5.5.5" "223.6.6.6")
SSHPORT=20211
DefaultUser="WeiyiGeek"
ROOTPASS=WeiyiGeek  # 密码建议12位以上且包含数字、大小写字母以及特殊字符。
APPPASS=WeiyiGeek

## 名称: err 、info 、warning
## 用途:全局Log信息打印函数
## 参数: $@
log::err() {
  printf "[$(date +'%Y-%m-%dT%H:%M:%S')]: \033[31mERROR: $@ \033[0m\n"
}
log::info() {
  printf "[$(date +'%Y-%m-%dT%H:%M:%S')]: \033[32mINFO: $@ \033[0m\n"
}
log::warning() {
  printf "[$(date +'%Y-%m-%dT%H:%M:%S')]: \033[33mWARNING: $@ \033[0m\n"
}


## 名称: os::Network
## 用途: 网络配置相关操作脚本包括(IP地址修改)
## 参数: 无
os::Network () {
  log::info "[-] 操作系统网络配置相关脚本,开始执行....."

# (1) IP地址与主机名称设置
sudo cp /etc/netplan/00-installer-config.yaml{,.bak}
mkdir /opt/init/
sudo tee /opt/init/network.sh <<'EOF'
#!/bin/bash
CURRENT_IP=$(hostname -I | cut -f 1 -d " ")
GATEWAY=$(hostname -I | cut -f 1,2,3 -d ".")
if [[ $# -lt 3 ]];then
  echo "Usage: $0 IP Gateway Hostname"
  exit
fi
echo "IP:${1} # GATEWAY:${2} # HOSTNAME:${3}"
sudo sed -i "s#${CURRENT_IP}#${1}#g" /etc/netplan/00-installer-config.yaml
sudo sed -i "s#${GATEWAY}.1#${2}#g" /etc/netplan/00-installer-config.yaml
sudo hostnamectl set-hostname ${3} 
sudo netplan apply
EOF
sudo chmod +x /opt/init/network.sh

# (2) 本地主机名解析设置
sed -i "s/127.0.1.1\s.\w.*$/127.0.1.1 ${HOSTNAME}/g" /etc/hosts
grep -q "^\$(hostname -I)\s.\w.*$" /etc/hosts && sed -i "s/\$(hostname -I)\s.\w.*$/${IPADDR} ${HOSTNAME}" /etc/hosts || echo "${IPADDR} ${HOSTNAME}" >> /etc/hosts

# (3) 系统DNS域名解析服务设置
cp -a /etc/resolv.conf{,.bak}
for dns in ${DNSIP[@]};do echo "nameserver ${dns}" >> /etc/resolv.conf;done

sudo /opt/init/network.sh ${IP} ${GATEWAY} ${HOSTNAME}
log::info "[*] network configure modifiy successful! restarting Network........."
}


## 名称: os::Software
## 用途: 操作系统软件包管理及更新源配置
## 参数: 无
os::Software () {
  log::info "[-] 操作系统软件包管理及更新源配置相关脚本,开始执行....."

# (1) 卸载多余软件,例如 snap 软件及其服务
sudo systemctl stop snapd snapd.socket #停止snapd相关的进程服务
sudo apt autoremove --purge -y snapd
sudo systemctl daemon-reload
sudo rm -rf ~/snap /snap /var/snap /var/lib/snapd /var/cache/snapd /run/snapd

# (2) 软件源设置与系统更新
sudo cp /etc/apt/sources.list{,.bak}
sudo tee /etc/apt/sources.list <<'EOF'
#阿里云Mirrors - Ubuntu
deb http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse

deb http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse

deb http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse

deb http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse

deb http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse
EOF

# (3) 内核版本升级以及常规软件安装
sudo apt autoclean && sudo apt update && sudo apt upgrade -y
sudo apt install -y nano vim git unzip wget ntpdate dos2unix net-tools tree htop ncdu nload sysstat psmisc bash-completion fail2ban  gcc g++ make jq nfs-common rpcbind libpam-cracklib

# (4) 代理方式进行更新
# sudo apt autoclean && sudo apt -o Acquire::http::proxy="http://proxy.weiyigeek.top/" update && sudo apt -o Acquire::http::proxy="http://proxy.weiyigeek.top" upgrade -y
# sudo apt install -o Acquire::http::proxy="http://proxy.weiyigeek.top/" -y nano vim git unzip wget ntpdate dos2unix net-tools tree htop ncdu nload sysstat psmisc bash-completion fail2ban
}


## 名称: os::TimedataZone
## 用途: 操作系统时间与时区同步配置
## 参数: 无
os::TimedataZone () {
  log::info "[*] 操作系统系统时间时区配置相关脚本,开始执行....."

# (1) 时间同步服务端容器(可选也可以用外部ntp服务器) : docker run -d --rm --cap-add SYS_TIME -e ALLOW_CIDR=0.0.0.0/0 -p 123:123/udp geoffh1977/chrony
echo "同步前的时间: $(date -R)"

# 方式1.Chrony 客户端配置
apt install -y chrony
grep -q "192.168.12.254" /etc/chrony/chrony.conf || sudo tee -a /etc/chrony/chrony.conf <<'EOF'
pool 192.168.10.254 iburst maxsources 1
pool 192.168.12.254 iburst maxsources 1
pool 192.168.4.254 iburst maxsources 1
pool ntp.aliyun.com iburst maxsources 4
keyfile /etc/chrony/chrony.keys
driftfile /var/lib/chrony/chrony.drift
logdir /var/log/chrony
maxupdateskew 100.0
rtcsync
# 允许跳跃式校时 如果在前 3 次校时中时间差大于 1.0s
makestep 1 3
EOF
systemctl enable chronyd && systemctl restart chronyd && systemctl status chronyd -l

# 方式2
# sudo ntpdate 192.168.10.254 || sudo ntpdate 192.168.12.215 || sudo ntpdate ntp1.aliyun.com

# 方式3
# echo 'NTP=192.168.10.254 192.168.4.254' >> /etc/systemd/timesyncd.conf
# echo 'FallbackNTP=ntp.aliyun.com' >> /etc/systemd/timesyncd.conf
# systemctl restart systemd-timesyncd.service

# (2) 时区与地区设置: 
sudo cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
sudo timedatectl set-timezone Asia/Shanghai
# sudo dpkg-reconfigure tzdata  # 修改确认
# sudo bash -c "echo 'Asia/Shanghai' > /etc/timezone" # 与上一条命令一样
# 将当前的 UTC 时间写入硬件时钟 (硬件时间默认为UTC)
sudo timedatectl set-local-rtc 0
# 启用NTP时间同步:
sudo timedatectl set-ntp yes
# 校准时间服务器-时间同步(推荐使用chronyc进行平滑同步)
sudo chronyc tracking
# 手动校准-强制更新时间
# chronyc -a makestep
# 系统时钟同步硬件时钟
# sudo hwclock --systohc
sudo hwclock -w

# (3) 重启依赖于系统时间的服务
sudo systemctl restart rsyslog.service cron.service
log::info "[*] Tie confmigure modifiy successful! restarting chronyd rsyslog.service crond.service........."
timedatectl
}


## 名称: os::Security
## 用途: 操作系统安全加固配置脚本(符合等保要求-三级要求)
## 参数: 无
os::Security () {
  log::info "正在进行->操作系统安全加固(符合等保要求-三级要求)配置"

# (0) 系统用户核查配置
  log::info "[-] 锁定或者删除多余的系统账户以及创建低权限用户"
userdel -r lxd
groupdel lxd
defaultuser=(root daemon bin sys games man lp mail news uucp proxy www-data backup list irc gnats nobody systemd-network systemd-resolve systemd-timesync messagebus syslog _apt tss uuidd tcpdump landscape pollinate usbmux sshd systemd-coredump _chrony)
for i in $(cat /etc/passwd | cut -d ":" -f 1,7);do
  flag=0; name=${i%%:*}; terminal=${i##*:}
  if [[ "${terminal}" == "/bin/bash" || "${terminal}" == "/bin/sh" ]];then
    log::warning "${i} 用户,shell终端为 /bin/bash 或者 /bin/sh"
  fi
  for j in ${defaultuser[@]};do
    if [[ "${name}" == "${j}" ]];then
      flag=1
      break;
    fi
  done
  if [[ $flag -eq 0 ]];then
    log::warning "${i} 非默认用户"
  fi
done
cp /etc/shadow /etc/'shadow-'`date +%Y%m%d`.bak
passwd -l adm&>/dev/null 2&>/dev/null; passwd -l daemon&>/dev/null 2&>/dev/null; passwd -l bin&>/dev/null 2&>/dev/null; passwd -l sys&>/dev/null 2&>/dev/null; passwd -l lp&>/dev/null 2&>/dev/null; passwd -l uucp&>/dev/null 2&>/dev/null; passwd -l nuucp&>/dev/null 2&>/dev/null; passwd -l smmsplp&>/dev/null 2&>/dev/null; passwd -l mail&>/dev/null 2&>/dev/null; passwd -l operator&>/dev/null 2&>/dev/null; passwd -l games&>/dev/null 2&>/dev/null; passwd -l gopher&>/dev/null 2&>/dev/null; passwd -l ftp&>/dev/null 2&>/dev/null; passwd -l nobody&>/dev/null 2&>/dev/null; passwd -l nobody4&>/dev/null 2&>/dev/null; passwd -l noaccess&>/dev/null 2&>/dev/null; passwd -l listen&>/dev/null 2&>/dev/null; passwd -l webservd&>/dev/null 2&>/dev/null; passwd -l rpm&>/dev/null 2&>/dev/null; passwd -l dbus&>/dev/null 2&>/dev/null; passwd -l avahi&>/dev/null 2&>/dev/null; passwd -l mailnull&>/dev/null 2&>/dev/null; passwd -l nscd&>/dev/null 2&>/dev/null; passwd -l vcsa&>/dev/null 2&>/dev/null; passwd -l rpc&>/dev/null 2&>/dev/null; passwd -l rpcuser&>/dev/null 2&>/dev/null; passwd -l nfs&>/dev/null 2&>/dev/null; passwd -l sshd&>/dev/null 2&>/dev/null; passwd -l pcap&>/dev/null 2&>/dev/null; passwd -l ntp&>/dev/null 2&>/dev/null; passwd -l haldaemon&>/dev/null 2&>/dev/null; passwd -l distcache&>/dev/null 2&>/dev/null; passwd -l webalizer&>/dev/null 2&>/dev/null; passwd -l squid&>/dev/null 2&>/dev/null; passwd -l xfs&>/dev/null 2&>/dev/null; passwd -l gdm&>/dev/null 2&>/dev/null; passwd -l sabayon&>/dev/null 2&>/dev/null; passwd -l named&>/dev/null 2&>/dev/null

# (2) 用户密码设置和口令策略设置
  log::info "[-]  配置满足策略的root管理员密码 "
echo  ${ROOTPASS} | passwd --stdin root

log::info "[-] 配置满足策略的app普通用户密码(根据需求配置)"
groupadd application
useradd -m -s /bin/bash -c "application primary user" -g application app 
echo ${APPPASS} | passwd --stdin app

  log::info "[-] 强制用户在下次登录时更改密码 "
chage -d 0 -m 0 -M 90 -W 15 root && passwd --expire root 
chage -d 0 -m 0 -M 90 -W 15 ${DefaultUser} && passwd --expire ${DefaultUser} 
chage -d 0 -m 0 -M 90 -W 15 app && passwd --expire app

  log::info "[-] 用户口令复杂性策略设置 (密码过期周期0~90、到期前15天提示、密码长度至少15、复杂度设置至少有一个大小写、数字、特殊字符、密码三次不能一样、尝试次数为三次)"
egrep -q "^\s*PASS_MIN_DAYS\s+\S*(\s*#.*)?\s*$" /etc/login.defs && sed -ri "s/^(\s*)PASS_MIN_DAYS\s+\S*(\s*#.*)?\s*$/\PASS_MIN_DAYS  0/" /etc/login.defs || echo "PASS_MIN_DAYS  0" >> /etc/login.defs
egrep -q "^\s*PASS_MAX_DAYS\s+\S*(\s*#.*)?\s*$" /etc/login.defs && sed -ri "s/^(\s*)PASS_MAX_DAYS\s+\S*(\s*#.*)?\s*$/\PASS_MAX_DAYS  60/" /etc/login.defs || echo "PASS_MAX_DAYS  90" >> /etc/login.defs
egrep -q "^\s*PASS_WARN_AGE\s+\S*(\s*#.*)?\s*$" /etc/login.defs && sed -ri "s/^(\s*)PASS_WARN_AGE\s+\S*(\s*#.*)?\s*$/\PASS_WARN_AGE  15/" /etc/login.defs || echo "PASS_WARN_AGE  15" >> /etc/login.defs
egrep -q "^\s*PASS_MIN_LEN\s+\S*(\s*#.*)?\s*$" /etc/login.defs && sed -ri "s/^(\s*)PASS_MIN_LEN\s+\S*(\s*#.*)?\s*$/\PASS_MIN_LEN  15/" /etc/login.defs || echo "PASS_MIN_LEN  15" >> /etc/login.defs

egrep -q "^password\s.+pam_cracklib.so\s+\w+.*$" /etc/pam.d/common-password && sed -ri '/^password\s.+pam_cracklib.so/{s/pam_cracklib.so\s+\w+.*$/pam_cracklib.so retry=3 minlen=15 ucredit=-1 lcredit=-1 dcredit=-1 ocredit=-1 difok=1/g;}' /etc/pam.d/common-password
egrep -q "^password\s.+pam_unix.so\s+\w+.*$" /etc/pam.d/common-password && sed -ri '/^password\s.+pam_unix.so/{s/pam_unix.so\s+\w+.*$/pam_unix.so obscure use_authtok try_first_pass sha512 remember=3/g;}' /etc/pam.d/common-password

  log::info "[-] 存储用户密码的文件,其内容经过sha512加密,所以非常注意其权限"
touch /etc/security/opasswd && chown root:root /etc/security/opasswd && chmod 600 /etc/security/opasswd 


# (3) 用户sudo权限以及重要目录和文件的权限设置
  log::info "[-] 用户sudo权限以及重要目录和文件的新建默认权限设置"
# 如uBuntu安装时您创建的用户 WeiyiGeek 防止直接通过 sudo passwd 修改root密码(此时必须要求输入WeiyiGeek密码后才可修改root密码)
# Tips: Sudo允许授权用户权限以另一个用户(通常是root用户)的身份运行程序, 
# DefaultUser="weiyigeek"
sed -i "/# Members of the admin/i ${DefaultUser} ALL=(ALL) PASSWD:ALL" /etc/sudoers


  log::info "[-] 配置用户 umask 为027 "
egrep -q "^\s*umask\s+\w+.*$" /etc/profile && sed -ri "s/^\s*umask\s+\w+.*$/umask 027/" /etc/profile || echo "umask 027" >> /etc/profile
egrep -q "^\s*umask\s+\w+.*$" /etc/bash.bashrc && sed -ri "s/^\s*umask\s+\w+.*$/umask 027/" /etc/bashrc || echo "umask 027" >> /etc/bash.bashrc
# log::info "[-] 设置用户目录创建默认权限, (初始为077比较严格)在未设置umask为027 则默认为077"
# egrep -q "^\s*(umask|UMASK)\s+\w+.*$" /etc/login.defs && sed -ri "s/^\s*(umask|UMASK)\s+\w+.*$/UMASK 022/" /etc/login.defs || echo "UMASK 022" >> /etc/login.defs

  log::info "[-] 设置或恢复重要目录和文件的权限"
chmod 755 /etc; 
chmod 777 /tmp; 
chmod 700 /etc/inetd.conf&>/dev/null 2&>/dev/null; 
chmod 755 /etc/passwd; 
chmod 755 /etc/shadow; 
chmod 644 /etc/group; 
chmod 755 /etc/security; 
chmod 644 /etc/services; 
chmod 750 /etc/rc*.d
chmod 600 ~/.ssh/authorized_keys

  log::info "[-] 删除潜在威胁文件 "
find / -maxdepth 3 -name hosts.equiv | xargs rm -rf
find / -maxdepth 3 -name .netrc | xargs rm -rf
find / -maxdepth 3 -name .rhosts | xargs rm -rf


# (4) SSHD 服务安全加固设置
log::info "[-] sshd 服务安全加固设置"
# 严格模式
sudo egrep -q "^\s*StrictModes\s+.+$" /etc/ssh/sshd_config && sed -ri "s/^(#)?\s*StrictModes\s+.+$/StrictModes yes/" /etc/ssh/sshd_config || echo "StrictModes yes" >> /etc/ssh/sshd_config
# 监听端口更改
if [ -e ${SSHPORT} ];then export SSHPORT=20211;fi
sudo egrep -q "^\s*Port\s+.+$" /etc/ssh/sshd_config && sed -ri "s/^(#)?\s*Port\s+.+$/Port ${SSHPORT}/" /etc/ssh/sshd_config || echo "Port ${SSHPORT}" >> /etc/ssh/sshd_config
# 禁用X11转发以及端口转发
sudo egrep -q "^\s*X11Forwarding\s+.+$" /etc/ssh/sshd_config && sed -ri "s/^(#)?\s*X11Forwarding\s+.+$/X11Forwarding no/" /etc/ssh/sshd_config || echo "X11Forwarding no" >> /etc/ssh/sshd_config
sudo egrep -q "^\s*X11UseLocalhost\s+.+$" /etc/ssh/sshd_config && sed -ri "s/^(#)?\s*X11UseLocalhost\s+.+$/X11UseLocalhost yes/" /etc/ssh/sshd_config || echo "X11UseLocalhost yes" >> /etc/ssh/sshd_config
sudo egrep -q "^\s*AllowTcpForwarding\s+.+$" /etc/ssh/sshd_config && sed -ri "s/^(#)?\s*AllowTcpForwarding\s+.+$/AllowTcpForwarding no/" /etc/ssh/sshd_config || echo "AllowTcpForwarding no" >> /etc/ssh/sshd_config
sudo egrep -q "^\s*AllowAgentForwarding\s+.+$" /etc/ssh/sshd_config && sed -ri "s/^(#)?\s*AllowAgentForwarding\s+.+$/AllowAgentForwarding no/" /etc/ssh/sshd_config || echo "AllowAgentForwarding no" >> /etc/ssh/sshd_config
# 关闭禁用用户的 .rhosts 文件  ~/.ssh/.rhosts 来做为认证: 缺省IgnoreRhosts yes 
egrep -q "^(#)?\s*IgnoreRhosts\s+.+$" /etc/ssh/sshd_config && sed -ri "s/^(#)?\s*IgnoreRhosts\s+.+$/IgnoreRhosts yes/" /etc/ssh/sshd_config || echo "IgnoreRhosts yes" >> /etc/ssh/sshd_config
# 禁止root远程登录(推荐配置-根据需求配置)
egrep -q "^\s*PermitRootLogin\s+.+$" /etc/ssh/sshd_config && sed -ri "s/^\s*PermitRootLogin\s+.+$/PermitRootLogin no/" /etc/ssh/sshd_config || echo "PermitRootLogin no" >> /etc/ssh/sshd_config
# 登陆前后欢迎提示设置
egrep -q "^\s*(banner|Banner)\s+\W+.*$" /etc/ssh/sshd_config && sed -ri "s/^\s*(banner|Banner)\s+\W+.*$/Banner \/etc\/issue/" /etc/ssh/sshd_config || \
echo "Banner /etc/issue" >> /etc/ssh/sshd_config
log::info "[-] 远程SSH登录前后提示警告Banner设置"
# SSH登录前警告Banner
sudo tee /etc/issue <<'EOF'
****************** [ 安全登陆 (Security Login) ] *****************
Authorized only. All activity will be monitored and reported.By Security Center.

EOF
# SSH登录后提示Banner
sed -i '/^fi/a\\n\necho "\\e[1;37;41;5m################## 安全运维 (Security Operation) ####################\\e[0m"\necho "\\e[32mLogin success. Please execute the commands and operation data carefully.By WeiyiGeek.\\e[0m"' /etc/update-motd.d/00-header

# (5) 用户远程登录失败次数与终端超时设置 
  log::info "[-] 用户远程连续登录失败5次锁定帐号5分钟包括root账号"
sed -ri "/^\s*auth\s+required\s+pam_tally2.so\s+.+(\s*#.*)?\s*$/d" /etc/pam.d/sshd 
sed -ri '2a auth required pam_tally2.so deny=5 unlock_time=300 even_deny_root root_unlock_time=300' /etc/pam.d/sshd 
# 宿主机控制台登陆(可选)
# sed -ri "/^\s*auth\s+required\s+pam_tally2.so\s+.+(\s*#.*)?\s*$/d" /etc/pam.d/login
# sed -ri '2a auth required pam_tally2.so deny=5 unlock_time=300 even_deny_root root_unlock_time=300' /etc/pam.d/login

  log::info "[-] 设置登录超时时间为10分钟 "
egrep -q "^\s*(export|)\s*TMOUT\S\w+.*$" /etc/profile && sed -ri "s/^\s*(export|)\s*TMOUT.\S\w+.*$/export TMOUT=600\nreadonly TMOUT/" /etc/profile || echo -e "export TMOUT=600\nreadonly TMOUT" >> /etc/profile
egrep -q "^\s*.*ClientAliveInterval\s\w+.*$" /etc/ssh/sshd_config && sed -ri "s/^\s*.*ClientAliveInterval\s\w+.*$/ClientAliveInterval 600/" /etc/ssh/sshd_config || echo "ClientAliveInterval 600" >> /etc/ssh/sshd_config


# (5) 切换用户日志记录或者切换命令更改(可选)
  log::info "[-] 切换用户日志记录和切换命令更改名称为SU "
egrep -q "^(\s*)SULOG_FILE\s+\S*(\s*#.*)?\s*$" /etc/login.defs && sed -ri "s/^(\s*)SULOG_FILE\s+\S*(\s*#.*)?\s*$/\SULOG_FILE  \/var\/log\/.history\/sulog/" /etc/login.defs || echo "SULOG_FILE  /var/log/.history/sulog" >> /etc/login.defs
egrep -q "^\s*SU_NAME\s+\S*(\s*#.*)?\s*$" /etc/login.defs && sed -ri "s/^(\s*)SU_NAME\s+\S*(\s*#.*)?\s*$/\SU_NAME  switch_user/" /etc/login.defs || echo "SU_NAME  switch_user" >> /etc/login.defs
mkdir -vp /var/log/.backups /usr/local/bin /var/log/.history
cp /usr/bin/su /var/log/.backups/su.bak
mv /usr/bin/su /usr/bin/SU
chmod 777 /var/log/.history 

# (6) 用户终端执行的历史命令记录
log::info "[-] 用户终端执行的历史命令记录 "
egrep -q "^HISTSIZE\W\w+.*$" /etc/profile && sed -ri "s/^HISTSIZE\W\w+.*$/HISTSIZE=101/" /etc/profile || echo "HISTSIZE=101" >> /etc/profile
# 方式1
sudo tee /etc/profile.d/history-record.sh <<'EOF'
# 历史命令执行记录文件路径
LOGTIME=$(date +%Y%m%d-%H-%M-%S)
export HISTFILE="/var/log/.history/${USER}.${LOGTIME}.history"
if [ ! -f ${HISTFILE} ];then
  touch ${HISTFILE}
fi
chmod 600 /var/log/.history/${USER}.${LOGTIME}.history
# 历史命令执行文件大小记录设置
HISTFILESIZE=128
HISTTIMEFORMAT="%F_%T $(whoami)#$(who -u am i 2>/dev/null| awk '{print $NF}'|sed -e 's/[()]//g'):"
EOF
# 方式2.未能成功(如后续有小伙伴成功了欢迎留言分享)
# sudo tee /usr/local/bin/history.sh <<'EOF'
# #!/bin/bash
# logfiletime=$(date +%Y%m%d-%H-%M-%S)
# # unalias "history"
# if [ $# -eq 0 ];then history;fi
# for i in $*;do
#   if [ "$i" = "-c" ];then 
#     mv ~/.bash_history > /var/log/.history/${logfiletime}.history
#     history -c
#     continue;
#   fi
# done
# alias history="source /usr/local/bin/history.sh"
# EOF

# (7) GRUB 安全设置 (需要手动设置请按照需求设置)
  log::info "[-] 系统 GRUB 安全设置(防止物理接触从grub菜单中修改密码) "
# Grub 关键文件备份
cp -a /etc/grub.d/00_header /var/log/.backups 
cp -a /etc/grub.d/10_linux /var/log/.backups 
# 设置Grub菜单界面显示时间
sed -i -e 's|GRUB_TIMEOUT_STYLE=hidden|#GRUB_TIMEOUT_STYLE=hidden|g' -e 's|GRUB_TIMEOUT=0|GRUB_TIMEOUT=3|g' /etc/default/grub
sed -i -e 's|set timeout_style=${style}|#set timeout_style=${style}|g' -e 's|set timeout=${timeout}|set timeout=3|g' /etc/grub.d/00_header
# 创建认证密码 (此处密码: WeiyiGeek)
sudo grub-mkpasswd-pbkdf2
# Enter password:
# Reenter password:
# PBKDF2 hash of your password is grub.pbkdf2.sha512.10000.21AC9CEF61B96972BF6F918D2037EFBEB8280001045ED32DFDDCC260591CC6BC8957CF25A6755904A7053E97940A9E4CD5C1EF833C1651C1BCF09D899BED4C7C.9691521F5BB34CD8AEFCED85F4B830A86EC93B61A31885BCBE3FEE927D54EFDEE69FA8B51DBC00FCBDB618D4082BC22B2B6BA4161C7E6B990C4E5CFC9E9748D7
# 设置认证用户以及password_pbkdf2认证
tee -a /etc/grub.d/00_header <<'END'
cat <<'EOF'
# GRUB Authentication
set superusers="grub"
password_pbkdf2 grub grub.pbkdf2.sha512.10000.21AC9CEF61B96972BF6F918D2037EFBEB8280001045ED32DFDDCC260591CC6BC8957CF25A6755904A7053E97940A9E4CD5C1EF833C1651C1BCF09D899BED4C7C.9691521F5BB34CD8AEFCED85F4B830A86EC93B61A31885BCBE3FEE927D54EFDEE69FA8B51DBC00FCBDB618D4082BC22B2B6BA4161C7E6B990C4E5CFC9E9748D7
EOF
END
# 设置进入正式系统不需要认证如进入单用户模式进行重置账号密码时需要进行认证。 (高敏感数据库系统不建议下述操作)
# 在191和193 分别加入--user=grub 和 --unrestricted
# 191       echo "menuentry --user=grub '$(echo "$title" | grub_quote)' ${CLASS} \$menuentry_id_option 'gnulinux-$version-$type-$boot_device_id' {" | sed "s/^/$submenu_indentation/"  # 如果按e进行menu菜单则需要用grub进行认证
# 192   else
# 193       echo "menuentry --unrestricted '$(echo "$os" | grub_quote)' ${CLASS} \$menuentry_id_option 'gnulinux-simple-$boot_device_id' {" | sed "s/^/$submenu_indentation/"          # 正常进入系统则不认证

sed -i '/echo "$title" | grub_quote/ { s/menuentry /menuentry --user=grub /;}' /etc/grub.d/10_linux
sed -i '/echo "$os" | grub_quote/ { s/menuentry /menuentry --unrestricted /;}' /etc/grub.d/10_linux

# Ubuntu 方式更新GRUB从而生成boot启动文件。
update-grub

# (8) 操作系统防火墙启用以及策略设置
  log::info "[-] 系统防火墙启用以及规则设置 "
systemctl enable ufw.service && systemctl start ufw.service && ufw enable
sudo ufw allow proto tcp to any port 20211
# 重启修改配置相关服务
systemctl restart sshd
}

## 名称: os::Operation 
## 用途: 操作系统安全运维设置
## 参数: 无
os::Operation () {
  log::info "[-] 操作系统安全运维设置相关脚本"

# (0) 禁用ctrl+alt+del组合键对系统重启 (必须要配置,我曾入过坑)
  log::info "[-] 禁用控制台ctrl+alt+del组合键重启"
mv /usr/lib/systemd/system/ctrl-alt-del.target ${BACKUPDIR}/'ctrl-alt-del.target-'${EXECTIME}.bak

# (1) 设置文件删除回收站别名
  log::info "[-] 设置文件删除回收站别名(防止误删文件) "
sudo tee -a  /etc/profile.d/alias.sh <<'EOF'
# User specific aliases and functions
# 删除回收站
# find ~/.trash -delete
# 删除空目录
# find ~/.trash -type d -delete
alias rm="sh /usr/local/bin/remove.sh"
EOF
sudo tee /usr/local/bin/remove.sh <<'EOF'
#!/bin/sh
# 定义回收站文件夹目录.trash
trash="/.trash"
deltime=$(date +%Y%m%d-%H-%M-%S)
TRASH_DIR="${HOME}${trash}/${deltime}"
# 建立回收站目录当不存在的时候
if [ ! -e ${TRASH_DIR} ];then
   mkdir -p ${TRASH_DIR}
fi
for i in $*;do
  if [ "$i" = "-rf" ];then continue;fi
    #定义秒时间戳
    STAMP=$(date +%s)
    #得到文件名称(非文件夹),参考man basename
    fileName=$(basename $i)
    #将输入的参数,对应文件mv至.trash目录,文件后缀,为当前的时间戳
    mv $i ${TRASH_DIR}/${fileName}.${STAMP}
done
EOF
sudo chmod +775 /usr/local/bin/remove.sh /etc/profile.d/alias.sh /etc/profile.d/history-record.sh
sudo chmod a+x /usr/local/bin/remove.sh /etc/profile.d/alias.sh /etc/profile.d/history-record.sh
source /etc/profile.d/alias.sh  /etc/profile.d/history-record.sh

# (2) 解决普通定时任务无法后台定时执行
log::info "[-] 解决普通定时任务无法后台定时执行 "
linenumber=`expr $(egrep -n "pam_unix.so\s$" /etc/pam.d/common-session-noninteractive | cut -f 1 -d ":") - 2`
sudo sed -ri "${linenumber}a session [success=1 default=ignore] pam_succeed_if.so service in cron quiet use_uid" /etc/pam.d/common-session-noninteractive

# (3) 解决 ubuntu20.04 multipath add missing path 错误
# 添加以下内容,sda视本地环境做调整
tee -a /etc/multipath.conf <<'EOF'
blacklist {
  devnode "^sda"
}
EOF
# 重启multipath-tools服务
sudo service multipath-tools restart
}

## 名称: os::optimizationn
## 用途: 操作系统优化设置(内核参数)
## 参数: 无
os::optimizationn () {
log::info "[-] 正在进行操作系统内核参数优化设置......."

# (1) 系统内核参数的配置(/etc/sysctl.conf)
log::info "[-] 系统内核参数的配置/etc/sysctl.conf"

# /etc/sysctl.d/99-kubernetes-cri.conf
egrep -q "^(#)?net.ipv4.ip_forward.*" /etc/sysctl.conf && sed -ri "s|^(#)?net.ipv4.ip_forward.*|net.ipv4.ip_forward = 1|g"  /etc/sysctl.conf || echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
# egrep -q "^(#)?net.bridge.bridge-nf-call-ip6tables.*" /etc/sysctl.conf && sed -ri "s|^(#)?net.bridge.bridge-nf-call-ip6tables.*|net.bridge.bridge-nf-call-ip6tables = 1|g" /etc/sysctl.conf || echo "net.bridge.bridge-nf-call-ip6tables = 1" >> /etc/sysctl.conf 
# egrep -q "^(#)?net.bridge.bridge-nf-call-iptables.*" /etc/sysctl.conf && sed -ri "s|^(#)?net.bridge.bridge-nf-call-iptables.*|net.bridge.bridge-nf-call-iptables = 1|g" /etc/sysctl.conf || echo "net.bridge.bridge-nf-call-iptables = 1" >> /etc/sysctl.conf
egrep -q "^(#)?net.ipv6.conf.all.disable_ipv6.*" /etc/sysctl.conf && sed -ri "s|^(#)?net.ipv6.conf.all.disable_ipv6.*|net.ipv6.conf.all.disable_ipv6 = 1|g" /etc/sysctl.conf || echo "net.ipv6.conf.all.disable_ipv6 = 1" >> /etc/sysctl.conf
egrep -q "^(#)?net.ipv6.conf.default.disable_ipv6.*" /etc/sysctl.conf && sed -ri "s|^(#)?net.ipv6.conf.default.disable_ipv6.*|net.ipv6.conf.default.disable_ipv6 = 1|g" /etc/sysctl.conf || echo "net.ipv6.conf.default.disable_ipv6 = 1" >> /etc/sysctl.conf
egrep -q "^(#)?net.ipv6.conf.lo.disable_ipv6.*" /etc/sysctl.conf && sed -ri "s|^(#)?net.ipv6.conf.lo.disable_ipv6.*|net.ipv6.conf.lo.disable_ipv6 = 1|g" /etc/sysctl.conf || echo "net.ipv6.conf.lo.disable_ipv6 = 1" >> /etc/sysctl.conf
egrep -q "^(#)?net.ipv6.conf.all.forwarding.*" /etc/sysctl.conf && sed -ri "s|^(#)?net.ipv6.conf.all.forwarding.*|net.ipv6.conf.all.forwarding = 1|g" /etc/sysctl.conf || echo "net.ipv6.conf.all.forwarding = 1"  >> /etc/sysctl.conf
egrep -q "^(#)?vm.max_map_count.*" /etc/sysctl.conf && sed -ri "s|^(#)?vm.max_map_count.*|vm.max_map_count = 262144|g" /etc/sysctl.conf || echo "vm.max_map_count = 262144"  >> /etc/sysctl.conf

tee -a /etc/sysctl.conf <<'EOF'
# 调整提升服务器负载能力之外,还能够防御小流量的Dos、CC和SYN攻击
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
# net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_fin_timeout = 60
net.ipv4.tcp_synack_retries = 1
net.ipv4.tcp_syn_retries = 1
net.ipv4.tcp_fastopen = 3

# 优化TCP的可使用端口范围及提升服务器并发能力(注意一般流量小的服务器上没必要设置如下参数)
net.ipv4.tcp_keepalive_time = 1200
net.ipv4.tcp_max_syn_backlog = 8192
net.ipv4.tcp_max_tw_buckets = 5000
net.ipv4.ip_local_port_range = 1024 65535

# 优化核套接字TCP的缓存区
net.core.netdev_max_backlog = 8192
net.core.somaxconn = 8192
net.core.rmem_max = 12582912
net.core.rmem_default = 6291456
net.core.wmem_max = 12582912
net.core.wmem_default = 6291456
EOF


# (2) Linux 系统的最大进程数和最大文件打开数限制
log::info "[-] Linux 系统的最大进程数和最大文件打开数限制 "
egrep -q "^\s*ulimit -HSn\s+\w+.*$" /etc/profile && sed -ri "s/^\s*ulimit -HSn\s+\w+.*$/ulimit -HSn 65535/" /etc/profile || echo "ulimit -HSn 65535" >> /etc/profile
egrep -q "^\s*ulimit -HSu\s+\w+.*$" /etc/profile && sed -ri "s/^\s*ulimit -HSu\s+\w+.*$/ulimit -HSu 65535/" /etc/profile || echo "ulimit -HSu 65535" >> /etc/profile

tee -a /etc/security/limits.conf <<'EOF'
# ulimit -HSn 65535
# ulimit -HSu 65535
*  soft  nofile  65535
*  hard  nofile  65535
*  soft  nproc   65535
*  hard  nproc   65535

# End of file
EOF
# sed -i "/# End/i *  soft  nproc   65535" /etc/security/limits.conf
# sed -i "/# End/i *  hard  nproc   65535" /etc/security/limits.conf
sysctl -p

# 需重启生效
reboot
}

## 名称: system::swap
## 用途: Liunx 系统创建SWAP交换分区(默认2G)
## 参数: $1(几G)
system::swap () {
  if [ -e $1 ];then
    sudo dd if=/dev/zero of=/swapfile bs=1024 count=2097152   # 2G Swap 分区 1024 * 1024 , centos 以 1000 为标准
  else
    number=$(echo "${1}*1024*1024"|bc)
    sudo dd if=/dev/zero of=/swapfile bs=1024 count=${number}   # 2G Swap 分区 1024 * 1024 , centos 以 1000 为标准
  fi

  sudo mkswap /swapfile && sudo swapon /swapfile
  if [ $(grep -c "/swapfile" /etc/fstab) -eq 0 ];then
sudo tee -a /etc/fstab <<'EOF'
/swapfile swap swap default 0 0
EOF
fi
sudo swapon --show && sudo free -h
}

## 名称: software::docker
## 用途: 软件安装之Docker安装
## 参数: 无
# 帮助: https://docs.docker.com/engine/install/ubuntu/
# Ubuntu Focal 20.04 (LTS)
# Ubuntu Bionic 18.04 (LTS)
# Ubuntu Xenial 16.04 (LTS)
function InstallDocker(){
  # 1.卸载旧版本 
  sudo apt-get remove docker docker-engine docker.io containerd runc

  # 2.更新apt包索引并安装包以允许apt在HTTPS上使用存储库
  sudo apt-get install -y \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg-agent \
    software-properties-common

  # 3.添加Docker官方GPG密钥 # -fsSL
  sudo curl  https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

  # 4.通过搜索指纹的最后8个字符进行密钥验证
  sudo apt-key fingerprint 0EBFCD88

  # 5.设置稳定存储库
  sudo add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable"

  # 6.Install Docker Engine 默认最新版本
  sudo apt-get update && sudo apt-get install -y docker-ce=5:20.10.7~3-0~ubuntu-focal docker-ce-cli=5:20.10.7~3-0~ubuntu-focal containerd.io docker-compose
  # - 强制IPv4
  # sudo apt-get -o Acquire::ForceIPv4=true  install -y docker-ce=5:19.03.15~3-0~ubuntu-focal docker-ce-cli=5:19.03.15~3-0~ubuntu-focal containerd.io docker-compose

  # 7.安装特定版本的Docker引擎,请在repo中列出可用的版本
  apt-cache madison docker-ce
  # docker-ce | 5:20.10.6~3-0~ubuntu-focal| https://download.docker.com/linux/ubuntu focal/stable amd64 Packages
  # docker-ce | 5:19.03.15~3-0~ubuntu-focal | https://download.docker.com/linux/ubuntu  xenial/stable amd64 Packages
  # 使用第二列中的版本字符串安装特定的版本,例如:5:18.09.1~3-0~ubuntu-xenial。
  # $sudo apt-get install docker-ce=<VERSION_STRING> docker-ce-cli=<VERSION_STRING> containerd.io

  #8.将当前用户加入docker用户组然后重新登陆当前用户使得低权限用户
  sudo gpasswd -a ${USER} docker
  # sudo gpasswd -a weiyigeek docker

  #9.加速器建立
  mkdir -vp /etc/docker/
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["https://xlx9erfu.mirror.aliyuncs.com"],
  "exec-opts": ["native.cgroupdriver=systemd"],
  "storage-driver": "overlay2",
  "log-driver": "json-file",
  "log-level": "warn",
  "log-opts": {
    "max-size": "100m",
    "max-file": "10"
  },
  "live-restore": true,
  "dns": ["192.168.10.254","223.6.6.6"]
}
EOF
  # "data-root":"/monitor/container",
  # "insecure-registries": ["harbor.weiyigeek.top"]
  # 9.自启与启动
  sudo systemctl daemon-reload
  sudo systemctl enable docker 
  sudo systemctl restart docker

  # 10.退出登陆生效
  exit
}

## 名称: disk::Lvsmanager
## 用途: uBuntu操作系统磁盘 LVS 逻辑卷添加与配置(扩容流程)
## 参数: 无
disk::Lvsmanager () {
  echo "\n分区信息:"
  sudo df -Th
  sudo lsblk
  echo -e "\n 磁盘信息:"
  sudo fdisk -l
  echo -e "\n PV物理卷查看:"
  sudo pvscan
  echo -e "\n vgs虚拟卷查看:"
  sudo vgs
  echo -e "\n lvscan逻辑卷扫描:"
  sudo lvscan
  echo -e "\n 分区扩展"
  echo "Ubuntu \n lvextend -L +74G /dev/ubuntu-vg/ubuntu-lv"
  echo "lsblk"
  echo -e "ubuntu general \n # resize2fs -p -F /dev/mapper/ubuntu--vg-ubuntu--lv"
}

至此 Ubuntu 安全加固脚本完毕。


0x04 补充说明

第一次投稿给安全客,心里还是有点小激动的,在投稿时让我想起曾经深夜挖洞的过往,不知不觉已过几年了,颇多的感慨,仰望众多大佬的脚步前行,勿忘初心、不骄不躁。

后续我将针对 数据库应用软件以及中间件方面的等保测评项进行搜寻整理,编写安全加固配脚本供大家使用,争取再搞一个系列。

文章中脚本如有错误,欢迎各位大佬指正。

(完)