bc3dd6c535
Deploys a fully preconfigured OPNsense appliance into a Hyper-V lab in a single execution, re-running safely because every stage detects the current state before it acts. Main script: - Hyper-V platform detection and installation, exiting 3010 only when the hypervisor itself needs a restart - Random /20 block selection out of a private base network, divided into /24 networks whose VLAN tag is the third octet of their own network address - Zone based roles, with five server zones paired by index to five client zones, plus Management, Infrastructure, DMZ, Storage and Guest - Generated OPNsense config.xml delivered on a FAT32 VHDX at conf/config.xml - Generation 2 virtual machine with secure boot disabled and a LAN trunk carrying VLANs 1-4094 - Marker scoped teardown via RemoveExistingDeployment Toolkit functions: - Save-ToolkitModule, Install-HyperVPlatform, Test-PendingReboot - Expand-CompressedFile, Get-OPNSenseInstallationMedia - Get-HyperVStorageLocation, Get-HostUpstreamDNSConfiguration - New-RandomPassword, New-OPNSensePasswordHash - New-OPNSenseNetworkPlan, New-OPNSenseConfigurationDocument, Save-OPNSenseConfigurationDocument, New-OPNSenseConfigurationDisk - Initialize-OPNSenseVirtualSwitch, New-OPNSenseVirtualMachine, Remove-OPNSenseDeployment Configuration document covers interfaces, VLANs, Kea DHCPv4 scopes with PXE options, Unbound, outbound NAT, six firewall aliases and an ordered rule set that grants management full reach, allows the jump hosts over well known management ports, forces name resolution to approved resolvers, and pairs the client and server zones. Bundles 7-Zip, because the tar.exe included with Windows cannot read a raw bzip2 stream, and BCrypt.Net-Next for the appliance password hash. docs: Add readme with execution flow and generated per function reference docs: Add design specification under .ai/specification Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
177 lines
7.7 KiB
PowerShell
177 lines
7.7 KiB
PowerShell
#region New-RandomPassword
|
|
Function New-RandomPassword
|
|
{
|
|
<#
|
|
.SYNOPSIS
|
|
Generates a random password using a cryptographically secure random number generator.
|
|
|
|
.DESCRIPTION
|
|
The generated password always contains at least one character from each of the requested character sets.
|
|
Characters that are easily confused with one another are excluded by default so that the password remains usable from a console session.
|
|
|
|
.PARAMETER Length
|
|
The number of characters that the generated password will contain.
|
|
|
|
.PARAMETER IncludeSymbols
|
|
Include symbol characters within the generated password.
|
|
|
|
.PARAMETER IncludeAmbiguousCharacters
|
|
Include the characters that are easily confused with one another, such as "O", "0", "l", "1", and "I".
|
|
|
|
.PARAMETER ContinueOnError
|
|
Ignore failures.
|
|
|
|
.EXAMPLE
|
|
$NewRandomPasswordResult = New-RandomPassword -Length 24 -IncludeSymbols -Verbose
|
|
|
|
Write-Output -InputObject ($NewRandomPasswordResult)
|
|
|
|
.NOTES
|
|
The password is returned as a plain string because it has to be written into a configuration document and reported to the operator.
|
|
|
|
.LINK
|
|
https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.randomnumbergenerator
|
|
#>
|
|
|
|
[CmdletBinding()]
|
|
Param
|
|
(
|
|
[Parameter(Mandatory=$False)]
|
|
[ValidateRange(8, 128)]
|
|
[Alias('L')]
|
|
[System.Int32]$Length,
|
|
|
|
[Parameter(Mandatory=$False)]
|
|
[Alias('IS')]
|
|
[Switch]$IncludeSymbols,
|
|
|
|
[Parameter(Mandatory=$False)]
|
|
[Alias('IAC')]
|
|
[Switch]$IncludeAmbiguousCharacters,
|
|
|
|
[Parameter(Mandatory=$False)]
|
|
[Alias('COE')]
|
|
[Switch]$ContinueOnError
|
|
)
|
|
|
|
Try
|
|
{
|
|
[System.String]$CmdletName = $MyInvocation.MyCommand.Name
|
|
|
|
$WriteLogMessage.Invoke(0, @("Function `'$($CmdletName)`' is beginning. Please Wait..."))
|
|
|
|
#region Set default parameter value(s)
|
|
Switch ($True)
|
|
{
|
|
{($Length -le 0)}
|
|
{
|
|
[System.Int32]$Length = 20
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Define the character sets
|
|
$CharacterSetList = New-Object -TypeName 'System.Collections.Generic.List[System.String]'
|
|
$CharacterSetList.Add('ABCDEFGHIJKLMNOPQRSTUVWXYZ')
|
|
$CharacterSetList.Add('abcdefghijklmnopqrstuvwxyz')
|
|
$CharacterSetList.Add('0123456789')
|
|
|
|
Switch ($IncludeSymbols.IsPresent)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$CharacterSetList.Add('!#%*+-=?@_')
|
|
}
|
|
}
|
|
|
|
Switch ($IncludeAmbiguousCharacters.IsPresent)
|
|
{
|
|
{($_ -eq $False)}
|
|
{
|
|
For ($CharacterSetListIndex = 0; $CharacterSetListIndex -lt $CharacterSetList.Count; $CharacterSetListIndex++)
|
|
{
|
|
$CharacterSetList[$CharacterSetListIndex] = $CharacterSetList[$CharacterSetListIndex] -ireplace '[OoIl01]', ''
|
|
}
|
|
}
|
|
}
|
|
|
|
[System.String]$CombinedCharacterSet = $CharacterSetList -Join ''
|
|
#endregion
|
|
|
|
#region Define the secure random number scriptblock
|
|
$RandomNumberGenerator = [System.Security.Cryptography.RandomNumberGenerator]::Create()
|
|
|
|
[ScriptBlock]$GetSecureRandomNumber = {
|
|
Param
|
|
(
|
|
[System.Int32]$MaximumValue
|
|
)
|
|
|
|
[Byte[]]$RandomBytes = New-Object -TypeName 'System.Byte[]' -ArgumentList (4)
|
|
|
|
$Null = $RandomNumberGenerator.GetBytes($RandomBytes)
|
|
|
|
[System.Int64]$RandomValue = [System.BitConverter]::ToUInt32($RandomBytes, 0)
|
|
|
|
Write-Output -InputObject ([System.Int32]($RandomValue % $MaximumValue))
|
|
}
|
|
#endregion
|
|
|
|
#region Generate the password
|
|
$PasswordCharacterList = New-Object -TypeName 'System.Collections.Generic.List[System.Char]'
|
|
|
|
For ($CharacterSetListIndex = 0; $CharacterSetListIndex -lt $CharacterSetList.Count; $CharacterSetListIndex++)
|
|
{
|
|
[System.String]$CharacterSet = $CharacterSetList[$CharacterSetListIndex]
|
|
|
|
$PasswordCharacterList.Add($CharacterSet[$GetSecureRandomNumber.InvokeReturnAsIs($CharacterSet.Length)])
|
|
}
|
|
|
|
For ($PasswordCharacterIndex = $PasswordCharacterList.Count; $PasswordCharacterIndex -lt $Length; $PasswordCharacterIndex++)
|
|
{
|
|
$PasswordCharacterList.Add($CombinedCharacterSet[$GetSecureRandomNumber.InvokeReturnAsIs($CombinedCharacterSet.Length)])
|
|
}
|
|
|
|
For ($ShuffleIndex = $PasswordCharacterList.Count - 1; $ShuffleIndex -gt 0; $ShuffleIndex--)
|
|
{
|
|
[System.Int32]$SwapIndex = $GetSecureRandomNumber.InvokeReturnAsIs($ShuffleIndex + 1)
|
|
|
|
[System.Char]$SwapCharacter = $PasswordCharacterList[$ShuffleIndex]
|
|
|
|
$PasswordCharacterList[$ShuffleIndex] = $PasswordCharacterList[$SwapIndex]
|
|
$PasswordCharacterList[$SwapIndex] = $SwapCharacter
|
|
}
|
|
|
|
[System.String]$Password = -Join ($PasswordCharacterList.ToArray())
|
|
#endregion
|
|
|
|
$WriteLogMessage.Invoke(0, @("A random password containing $($Password.Length) character(s) was generated."))
|
|
|
|
Write-Output -InputObject ($Password)
|
|
}
|
|
Catch
|
|
{
|
|
$ErrorRecord = $_
|
|
|
|
Switch ($ContinueOnError.IsPresent)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$WriteLogMessage.Invoke(2, @("[Message: $($ErrorRecord.Exception.Message)] [LineNumber: $($ErrorRecord.InvocationInfo.ScriptLineNumber)] [Code: $($ErrorRecord.InvocationInfo.Line.Trim())]"))
|
|
}
|
|
|
|
{($_ -eq $False)}
|
|
{
|
|
Throw
|
|
}
|
|
}
|
|
}
|
|
Finally
|
|
{
|
|
$Null = Try {$RandomNumberGenerator.Dispose()} Catch {}
|
|
|
|
$WriteLogMessage.Invoke(0, @("Function `'$($CmdletName)`' is completed."))
|
|
}
|
|
}
|
|
#endregion
|