pfSense.psm1
<#
.Synopsis pfSense management functions built for pfSense version 2.x .DESCRIPTION Haven't been able to find another API, or command line management for pfSense .NOTES It runs on Linux guys.... there shouldn't be a need for these functions... .COMPONENT Security, Networking, Firewall .FUNCTIONALITY pfSense task automation and scriptability #> #region Prerequisites # All modules require the core <# Great news! The core module is now installed automatically when installed from PSGallery #> #endregion #================================================= MEAT! =========================================================# #region Connection functions Function Connect-pfSense { <# .DESCRIPTION Authenticates to a pfSense server and returns the session variable #> [CmdLetBinding()] Param ( [Parameter( Mandatory = $true, Position = 0, HelpMessage = 'Hostname of pfSesense server' )] [Alias('HostName')] [String] $Server, [Parameter( Mandatory = $true, Position = 1, HelpMessage = 'Credentials for administering pfSense' )] [PSCredential] $Credential, [Switch] $NoTLS, # Not recommended [Switch] $IgnoreCertificateErrors ) Begin { # Debugging for scripts $Script:boolDebug = $PSBoundParameters.Debug.IsPresent # Is -Force set? # TODO: use to avoid asking if we should ignore self-signed web certs $Script:boolForce = $PSBoundParameters.Force.IsPresent $sessionParams = @{} # pfSense requires TLS1.2 This is not an available security protocol in Invoke-WebRequest by default # TODO: use available function (Set-WebSecurityProtocol) If ([Net.ServicePointManager]::SecurityProtocol -notmatch 'TLS12' -and -not $NoTLS) { [Net.ServicePointManager]::SecurityProtocol += [Net.SecurityProtocolType]::TLS12 } <# .NOTE: might be a good idea to add this to your $profile. Default is SSLv3 for Posh web commands!!! # Security protocols for web calls. removes SSL3 and TLS1.0 [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::TLS11 [Net.ServicePointManager]::SecurityProtocol += [Net.SecurityProtocolType]::TLS12 ...Just a suggestion #> # TODO: use available function (Set-WebCertificatePolicy) # Warn the user user that security will be degraded, and ask if they would like to proceed. # Check if they have the proper version of core use the function to Set-WebCertificatePolicy # Require that core be updated # Add a note on how to revert the security policy back to the original, without restarting PowerShell If ($IgnoreCertificateErrors) { if ($PSVersionTable.PSEdition -eq 'Core') { # ICertificatePolicy is deprecated in PWSH Core. IWR now has an option... $sessionParams.Add('SkipCertificateCheck',$true) } else { Try { Add-Type -TypeDefinition @' using System.Net; using System.Security.Cryptography.X509Certificates; public class InSecureWebPolicy : ICertificatePolicy { public bool CheckValidationResult(ServicePoint sPoint, X509Certificate cert,WebRequest wRequest, int certProb) { return true; } } '@ $pol = [System.Net.ServicePointManager]::CertificatePolicy [System.Net.ServicePointManager]::CertificatePolicy = New-Object -TypeName InSecureWebPolicy <# .NOTE: There is a timeout value to using this option. At the end of this function the policy is returned to its original configuration. PowerShell takes a little time, almost like cache, to recognize the reversion. Therefore this option is only good for fast scripting, and not for coding on the command line. It is recommended that you import the cert into your trusted certificates store #> } Catch {} } } } Process { # Variables $uri = 'https://{0}/index.php' -f $Server $pfWebSession = $null $retObject = @() $dictOptions = @{ host = $Server NoTLS = $([bool] $NoTLS) IgnoreCertificateErrors = $([bool] $IgnoreCertificateErrors) } If ($NoTLS) { # highway to tha Danger Zone!!! $uri = $uri -Replace "^https:", 'http:' Invoke-DebugIt -Console -Message '[WARNING]' -Value 'Insecure option selected (no TLS)' -Color 'Yellow' } Invoke-DebugIt -Console -Message '[INFO]' -Value $uri.ToString() $request = Invoke-WebRequest @sessionParams -Uri $uri $webCredential = @{login = 'Login' usernamefld = $Credential.GetNetworkCredential().UserName passwordfld = $Credential.GetNetworkCredential().Password __csrf_magic = $($request.InputFields[0].Value) } Invoke-WebRequest @sessionParams -Uri $uri -Body $webCredential -Method Post -SessionVariable pfWebSession | Out-Null $retObject += $pfWebSession $retObject += $dictOptions $retObject } End { if ($pol) { [System.Net.ServicePointManager]::CertificatePolicy = $pol } } } #endregion #region User functions Function Add-pfSenseUser { <# .Synopsis Adds a new user via pfSense user management page .DESCRIPTION Great for automating the turn up of new remote users .EXAMPLE $Creds = Get-Credential $pfs = Connect-pfSense -Server firewall.local -Credential $Creds Add-pfSenseUser -Session $pfs -Server firewall.local -UserName 'player1' -Password 'MySecretPassword' -FullName 'Player One' Creates a user account on the pfSense firewall named "firewall.local" .NOTES For the certificate, you'll need to get the CA's reference ID. This is located in the page source code of either the CA itself, or on the Add User Management Page. This can be found by visiting one of these pages, right-click and select view page source, the perfrom a search for caref. I'll write something to get this later... an example of this: 4813b1f414fec <div> <select class="form-control" name="caref" id="caref"> <option value="4813b1f414fec">pfSenseCertificateAuthority</option> </div> #> [CmdLetBinding()] [CmdletBinding(DefaultParameterSetName = 'NoCert')] Param ( [Parameter(Mandatory = $true, Position = 0, HelpMessage = 'Valid/active websession to server' )] [PSObject] $Session, [Parameter(Mandatory = $true, Position = 1, HelpMessage = 'User name' )] [String] $UserName, [Parameter(Mandatory = $true, Position = 2, HelpMessage = 'Password for the user' )] [Alias('Password')] [String] $UserPass, [Parameter(Mandatory = $true, Position = 3, HelpMessage = 'Display name for the user' )] [String] $FullName, [Parameter(ParameterSetName = 'Certificate')] [Switch] $Certificate, [Parameter(Mandatory = $false, ParameterSetName = "NoCert")] [Parameter(Mandatory = $true, ParameterSetName = "Certificate", HelpMessage = 'Name of the CA' )] [String] $CA, [Int] $KeyLength = 2048, [Int] $LifeTime = 3650, [ValidateSet('sha1', 'sha224', 'sha256', 'sha384', 'sha512')] [String] $DigestAlgorithm = 'sha256', [Switch] $Quiet # No output upon completion ) Begin { # Debugging for scripts $Script:boolDebug = $PSBoundParameters.Debug.IsPresent $Password = $UserPass $sessionParams = @{} if ($session.IgnoreCertificateErrors -and $PSVersionTable.PSEdition -eq 'Core') { $sessionParams.Add('SkipCertificateCheck',$true) } } Process { # Variables $Server = $Session.host [bool] $NoTLS = $Session.NoTLS [Microsoft.PowerShell.Commands.WebRequestSession] $webSession = $Session[0] $uri = 'https://{0}/system_usermanager.php' -f $Server If ($NoTLS) { # highway to tha Danger Zone!!! $uri = $uri -Replace "^https:", 'http:' Invoke-DebugIt -Console -Message '[WARNING]' -Value 'Insecure option selected (no TLS)' -Color 'Yellow' } Invoke-DebugIt -Console -Message '[INFO]' -Value $uri.ToString() # pfSense requires a lot of magic.... ++ foreach POST $request = Invoke-WebRequest @sessionParams -Uri $uri -Method Get -WebSession $webSession $dictPostData = @{ __csrf_magic = $($request.InputFields[0].Value) usernamefld = $UserName passwordfld1 = $Password passwordfld2 = $Password descr = $FullName utype = 'user' save = 'Save' # Needed for version >= 2.4.4 dashboardcolumns = 2 webguicss = 'pfSense.css' } # Change the utype to 'system' to create a protected system user $dictCertData = @{ # Extra form fields when requesting a certificate for the user showcert = 'yes' name = "$($UserName)_cert" caref = $CA keylen = $KeyLength digest_alg = $DigestAlgorithm lifetime = $LifeTime } If ($Certificate) { # Should we request a cert from the CA? $dictPostData += $dictCertData } # submit/post the form to the server $uri += '?act=new' Invoke-DebugIt -Console -Message '[INFO]' -Value ('Post URI: {0}' -f $uri) Try { $rawRet = Invoke-WebRequest @sessionParams -Uri $uri -Method Post -Body $dictPostData -WebSession $webSession -EA Stop | Out-Null If ($rawRet.StatusCode -eq 200 -and -not $Quiet) { Invoke-DebugIt -Console -Message 'Success' -Force -Color 'Green' ` -Value ('User: {0}, created successfully!' -f $FullName) } } Catch { Write-Error -Message 'Something went wrong submitting the form' } } End { } } Function Get-pfSenseUser { [CmdLetBinding()] Param ( [Parameter(Mandatory = $true, Position = 0, HelpMessage = 'Valid/active websession to server' )] [PSObject] $Session, [AllowNull()] [Parameter(Position = 1)] [String] $UserName, [Switch] $CertInfo, [Switch] $Detail ) Begin { # Debugging for scripts $Script:boolDebug = $PSBoundParameters.Debug.IsPresent Function Script:Where-Deleteable { param ( [Object] [Parameter(Mandatory = $true, ValueFromPipeline = $true, HelpMessage = "Data to filter")] $InputObject ) process { if ($InputObject.title -match 'Delete user') { $InputObject } } } $sessionParams = @{} if ($session.IgnoreCertificateErrors -and $PSVersionTable.PSEdition -eq 'Core') { $sessionParams.Add('SkipCertificateCheck',$true) } } Process { # Variables $objUsers = @() $objUsersDetail = @() #--------------------------------------------------------------------------------------# $Server = $Session.host [bool] $NoTLS = $Session.NoTLS [Microsoft.PowerShell.Commands.WebRequestSession] $webSession = $Session[0] $uri = 'https://{0}/system_usermanager.php' -f $Server If ($NoTLS) { # highway to tha Danger Zone!!! $uri = $uri -Replace "^https:", 'http:' Invoke-DebugIt -Console -Message '[WARNING]' -Value 'Insecure option selected (no TLS)' -Color 'Yellow' } Invoke-DebugIt -Console -Message '[INFO]' -Value $uri.ToString() # pfSense requires a lot of magic.... ++ foreach POST $request = Invoke-WebRequest @sessionParams -Uri $uri -Method Get -WebSession $webSession # Get a list of deletable users. $users = $request.Links | Where-Deleteable # Note: can't delete yourself # Build an array with usernames and IDs, which can be deleted by the current user. Foreach ($user in $users) { $uname = $user.href.Split(';').Replace('&', '').Trim() -match 'username' $uid = $user.href.Split(';').Replace('&', '').Trim() -match 'userid' $objBuilder = New-Object -TypeName PSObject $objBuilder | Add-Member -MemberType NoteProperty -Name 'Username' -Value $($uname.Split('=')[1]) $objBuilder | Add-Member -MemberType NoteProperty -Name 'UserID' -Value $($uid.Split('=')[1]) If ($CertInfo) { $userEditUri = $uri + ('?act=edit&userid={0}' -f $($uid.Split('=')[1])) $userReq = Invoke-WebRequest @sessionParams -Uri $userEditUri -WebSession $webSession -Method Get $cert = $userReq.ParsedHtml.frames.document.body.outerHTML.Split("`n") | Where-Object { $_ -match "Remove this certificate association" } If ($cert) { #$certName = '' $boolCert = $true } Else { #$certName = $null $boolCert = $false } $objBuilder | Add-Member -MemberType NoteProperty -Name 'Cert' -Value $boolCert #$objBuilder | Add-Member -MemberType NoteProperty -Name 'CertName' -Value $certName } $objUsers += $objBuilder } If ($Detail) { $tempFile = $env:TEMP + '\' + [guid]::NewGuid().guid + '.xml' [xml] $xmlFile = Backup-pfSenseConfig -Session $Session -OutputXML Foreach ($user in $xmlFile.pfsense.system.user) { # Cert info if exists $objCert = $xmlFile.pfsense.cert | ? { $_.refid -eq $user.cert } $objCA = $xmlFile.pfsense.ca | ? { $_.refid -eq $objCert.caref } $uid = $objUsers | ? { $_.username -eq $user.name } | % { $_.userid } $objCrl = $xmlFile.pfsense.crl | ? { $_.caref -eq $objCA.refid } $objBuilder = New-Object -TypeName PSObject $objBuilder | Add-Member -MemberType NoteProperty -Name 'Username' -Value $user.name $objBuilder | Add-Member -MemberType NoteProperty -Name 'System_UID' -Value $user.uid $objBuilder | Add-Member -MemberType NoteProperty -Name 'UserID' -Value $uid $objBuilder | Add-Member -MemberType NoteProperty -Name 'FullName' -Value $user.descr.'#cdata-section' $objBuilder | Add-Member -MemberType NoteProperty -Name 'Expiration' -Value $user.expires $objBuilder | Add-Member -MemberType NoteProperty -Name 'User_Type' -Value $user.scope $objBuilder | Add-Member -MemberType NoteProperty -Name 'Cert' -Value $objCert.descr.'#cdata-section' $objBuilder | Add-Member -MemberType NoteProperty -Name 'Cert_ID' -Value $user.cert $objBuilder | Add-Member -MemberType NoteProperty -Name 'CA' -Value $objCA.descr.'#cdata-section' $objBuilder | Add-Member -MemberType NoteProperty -Name 'CA_ID' -Value $objCA.refid $objBuilder | Add-Member -MemberType NoteProperty -Name 'CRL' -Value $objCrl.descr.'#cdata-section' $objBuilder | Add-Member -MemberType NoteProperty -Name 'CRL_ID' -Value $objCrl.refid $objUsersDetail += $objBuilder } If ($UserName) { Try { $objUsersDetail | Where-Object { $_.Username -eq $UserName } } Catch { Write-Host -ForegroundColor Red "Username $UserName not found" $objUsersDetail } } Else { $objUsersDetail } } Else { If ($UserName) { Try { $objUsers | Where-Object { $_.Username -eq $UserName } } Catch { Write-Host -ForegroundColor Red "Username $UserName not found" $objUsers } } Else { $objUsers } } } End { Remove-Variable -Name xmlFile -Force -ErrorAction SilentlyContinue -WarningAction SilentlyContinue [GC]::Collect() } } Function Remove-pfSenseUser { [CmdLetBinding()] Param ( [Parameter(Mandatory = $true, Position = 0, HelpMessage = 'Valid/active websession to server' )] [PSObject] $Session, [Parameter(Mandatory = $true, Position = 1, HelpMessage = 'User name' )] [String] $UserName, [Switch] $RevokeCert, [Switch] $Quiet ) Begin { # Debugging for scripts $Script:boolDebug = $PSBoundParameters.Debug.IsPresent $sessionParams = @{} if ($session.IgnoreCertificateErrors -and $PSVersionTable.PSEdition -eq 'Core') { $sessionParams.Add('SkipCertificateCheck',$true) } } Process { # Variables $Server = $Session.host [bool] $NoTLS = $Session.NoTLS [Microsoft.PowerShell.Commands.WebRequestSession] $webSession = $Session[0] $uri = 'https://{0}/system_usermanager.php' -f $Server If ($NoTLS) { # highway to tha Danger Zone!!! $uri = $uri -Replace "^https:", 'http:' Invoke-DebugIt -Console -Message '[WARNING]' -Value 'Insecure option selected (no TLS)' -Color 'Yellow' } Invoke-DebugIt -Console -Message '[INFO]' -Value $uri.ToString() # pfSense requires a lot of magic.... ++ foreach POST $request = Invoke-WebRequest @sessionParams -Uri $uri -Method Get -WebSession $webSession # Get a list of deletable users. $objUser = Get-pfSenseUser -Session $Session -Detail -UserName $UserName # Get the ID of the username to be deleted. Try { [bool] (!($objUser.UserID -eq $null)) Invoke-DebugIt -Console -Message '[INFO]' -Value ('User ID found: {0}' -f $objUser.UserID) } Catch { Write-Error -Message ` 'Failed to get the user ID for the username provided. Check the username, and try again' return } If ($RevokeCert) { Revoke-pfSenseUserCert -Session $Session -UserName $UserName -Reason 'Cessation of Operation' } # Dictionary submitted as body in our POST request $dictPostData = @{ __csrf_magic = $($request.InputFields[0].Value) 'delete_check[]' = $($objUser.UserID) 'dellall' = 'dellall' } Try { $rawRet = Invoke-WebRequest @sessionParams -Uri $uri -Method Post -Body $dictPostData -WebSession $webSession -EA Stop | Out-Null If ($rawRet.StatusCode -eq 200 -and -not $Quiet) { Invoke-DebugIt -Console -Message 'Success' -Force -Color 'Green' ` -Value ('User: {0}, deleted successfully!' -f $UserName) } } Catch { Write-Error -Message 'Something went wrong submitting the form' } } End { } } Function Export-pfSenseUserCert { [CmdLetBinding()] Param ( [Parameter(Mandatory = $true, Position = 0, HelpMessage = 'Valid/active websession to server' )] [PSObject] $Session, [Parameter(Mandatory = $true, Position = 1, HelpMessage = 'User name' )] [String] $UserName, [Parameter(Position = 2)] [ValidateSet('Cert', 'Key', 'P12')] [String] $CertAction = 'Cert', [Parameter(Position = 3)] [ValidateScript({ try { $Folder = Get-Item $($_ | Split-Path -Parent) -ErrorAction Stop } catch [System.Management.Automation.ItemNotFoundException] { Throw [System.Management.Automation.ItemNotFoundException] "${_} Maybe there are network issues?" } if ($Folder.PSIsContainer) { $True } else { Throw [System.Management.Automation.ValidationMetadataException] "The path '${_}' is not a container." } })] [String] $FilePath ) Begin { # Debugging for scripts $Script:boolDebug = $PSBoundParameters.Debug.IsPresent function Script:Extract-CertTableData { # Code from Lee Holmes (Modified) # http://www.leeholmes.com/blog/2015/01/05/extracting-tables-from-powershells-invoke-webrequest/ param ( [Parameter(Mandatory)] [Microsoft.PowerShell.Commands.HtmlWebResponseObject] $WebRequest, [Parameter(Mandatory)] [int] $TableNumber ) $tables = @($WebRequest.ParsedHtml.getElementsByTagName("TABLE")) $table = $tables[$TableNumber] $titles = @() $rows = @($table.Rows) Foreach ($row in $rows) { $cells = @($row.Cells) If ($cells[0].tagName -eq "TH") { $titles = @($cells | ForEach-Object {("" + $_.InnerText).Trim() }) Continue } #If we haven't found any table headers, make up names "P1", "P2", etc. If (-not $titles) { $titles = @(1..($cells.Count + 2) | ForEach-Object { "P$_" }) } $resultObject = [Ordered] @{} For ($intCounter = 0; $intCounter -lt $cells.Count; $intCounter++) { $title = $titles[$intCounter] If (-not $title) { Continue } if ($intCounter -eq 4) { $resultObject['UserID'] = (("" + $cells[$intCounter].InnerHTML).Trim() -split 'id=' -split '"')[6] } else { $resultObject[$title] = ("" + $cells[$intCounter].InnerText).Trim() } } [PSCustomObject] $resultObject } } $sessionParams = @{} if ($session.IgnoreCertificateErrors -and $PSVersionTable.PSEdition -eq 'Core') { $sessionParams.Add('SkipCertificateCheck',$true) } } Process { # Variables $Server = $Session.host [bool] $NoTLS = $Session.NoTLS [Microsoft.PowerShell.Commands.WebRequestSession] $webSession = $Session[0] $uri = 'https://{0}/system_certmanager.php' -f $Server If ($NoTLS) { # highway to tha Danger Zone!!! $uri = $uri -Replace "^https:", 'http:' Invoke-DebugIt -Console -Message '[WARNING]' -Value 'Insecure option selected (no TLS)' -Color 'Yellow' } Invoke-DebugIt -Console -Message '[INFO]' -Value $uri.ToString() # Get the page contents so we can parse the table. We'll need the iterated ID based on the web table. $request = Invoke-WebRequest @sessionParams -Uri $uri -Method Get -WebSession $webSession $objTable = Extract-CertTableData -WebRequest $request -TableNumber 0 $userID = $objTable | Where-Object { $_.Name -match $UserName } | Select-Object -ExpandProperty UserID #$userId = Get-pfSenseUser -Session $Session -UserName $UserName -Detail | Select-Object -ExpandProperty Cert_ID Switch ($CertAction) { Key { $uri += ('?act=key&id={0}' -f $userID) $fExt = 'key' Break } P12 { $uri += ('?act=p12&id={0}' -f $userID) $fExt = 'p12' Break } Default { $uri += ('?act=exp&id={0}' -f $userID) $fExt = 'crt' Break } } If (!$FilePath) { [String] $FilePath = ('{0}\{1}_pfSenseUserCertificate.{2}' -f $($PWD.Path), $UserName, $fExt) } Invoke-DebugIt -Console -Message '[INFO]' -Value ('Export path = {0}' -f $FilePath) -Force Invoke-DebugIt -Console -Message '[INFO]' -Value ('URI = {0}' -f $uri.ToString()) $exRequest = Invoke-WebRequest @sessionParams -Uri $uri -Method Get -WebSession $webSession ConvertFrom-HexToFile -HexString $exRequest.Content -FilePath $FilePath } End { } } Function Revoke-pfSenseUserCert { Param ( [Parameter(Mandatory = $true, Position = 0, HelpMessage = 'Valid/active websession to server' )] [PSObject] $Session, [Parameter(Mandatory = $true, Position = 1, HelpMessage = 'User name' )] [String] $UserName, [ValidateSet('No Status (default)', 'Unspecified', 'Key Compromise', 'CA Compromise', 'Affiliation Change', 'Superseded', 'Cessation of Operation', 'Certificate Hold' )] [String] $Reason = 'Unspecified', [Switch] $Quiet ) Begin { $sessionParams = @{} if ($session.IgnoreCertificateErrors -and $PSVersionTable.PSEdition -eq 'Core') { $sessionParams.Add('SkipCertificateCheck',$true) } } Process { # Variables $Server = $Session.host [bool] $NoTLS = $Session.NoTLS [Microsoft.PowerShell.Commands.WebRequestSession] $webSession = $Session[0] $user = Get-pfSenseUser -Session $Session -Detail -UserName $UserName $dictReason = @{ 'No Status (default)' = '-1' 'Unspecified' = 0 'Key Compromise' = 1 'CA Compromise' = 2 'Affiliation Changed' = 3 'Superseded' = 4 'Cessation of Operation' = 5 'Certificate Hold' = 6 } If ($user.count -gt 1 -or $user -eq $null) { Write-Error -Message ('Failed to get username {0}' -f $UserName) Return } If (!$user.CRL_ID) { Write-Error -Message ('No CRL for {0}' -f $UserName) Return } $uri = 'https://{0}/system_crlmanager.php?act=edit&id={1}' -f $Server, $user.CRL_ID If ($NoTLS) { # highway to tha Danger Zone!!! $uri = $uri -Replace "^https:", 'http:' Invoke-DebugIt -Console -Message '[WARNING]' -Value 'Insecure option selected (no TLS)' -Color 'Yellow' } Invoke-DebugIt -Console -Message '[INFO]' -Value $uri.ToString() # pfSense requires a lot of magic.... ++ foreach POST $request = Invoke-WebRequest @sessionParams -Uri $uri -Method Get -WebSession $webSession # Dictionary submitted as body in our POST request $dictPostData = @{ __csrf_magic = $($request.InputFields[0].Value) certref = $($user.Cert_ID) crlreason = $($dictReason["$Reason"]) submit = 'Add' id = $($user.CRL_ID) act = 'addcert' crlref = $($user.CRL_ID) } Try { $rawRet = Invoke-WebRequest @sessionParams -Uri $uri -Method Post -Body $dictPostData -WebSession $webSession -EA Stop | Out-Null If ($rawRet.StatusCode -eq 200 -and -not $Quiet) { Invoke-DebugIt -Console -Message 'Success' -Force -Color 'Green' ` -Value ('Certificate: {0}, revoked successfully!' -f $UserName) } } Catch { Write-Error -Message 'Something went wrong submitting the form' } } End { } } Function Restore-pfSenseUserCert { <# Un-Revoke: Remove a user's certificate from a CRL #> Param ( [Parameter(Mandatory = $true, Position = 0, HelpMessage = 'Valid/active websession to server' )] [PSObject] $Session, [Parameter(Mandatory = $true, Position = 1, HelpMessage = 'User name' )] [String] $UserName ) Begin { } Process { } End { } } #endregion #region System functions Function Backup-pfSenseConfig { <# .Synopsis Backup your pfSense firewall .DESCRIPTION Long description .EXAMPLE $Creds = Get-Credential $pfs = Connect-pfSense -Server firewall.local -Credential $Creds Backup-pfSenseConfig -Server firewall.local -Session $pfs #> [CmdLetBinding()] Param ( [Parameter( Mandatory = $true, Position = 0, HelpMessage = 'Valid/active websession to server' )] [PSObject] $Session, [Parameter(Position = 1)] [Switch] $OutputXML, [Parameter(Position = 1)] [ValidateScript({ try { $Folder = Get-Item $($_ | Split-Path -Parent) -ErrorAction Stop } catch [System.Management.Automation.ItemNotFoundException] { Throw [System.Management.Automation.ItemNotFoundException] "${_} Maybe there are network issues?" } if ($Folder.PSIsContainer) { $True } else { Throw [System.Management.Automation.ValidationMetadataException] "Invalid path '${_}'." } })] [String] $FilePath = ('{0}\{1}_pfSenseBackup.xml' -f $($PWD.Path), $(Get-Date -UFormat '%Y%m%d_%H%M%S')), [Parameter(Position = 2, ParameterSetName = 'ToDisk')] [String] $EncryptPassword ) Begin { # Debugging for scripts $Script:boolDebug = $PSBoundParameters.Debug.IsPresent $sessionParams = @{} if ($session.IgnoreCertificateErrors -and $PSVersionTable.PSEdition -eq 'Core') { $sessionParams.Add('SkipCertificateCheck',$true) } } Process { # Variables $Server = $Session.host [bool] $NoTLS = $Session.NoTLS [Microsoft.PowerShell.Commands.WebRequestSession] $webSession = $Session[0] $uri = 'https://{0}/diag_backup.php' -f $Server If ($NoTLS) { # highway to tha Danger Zone!!! $uri = $uri -Replace "^https:", 'http:' Invoke-DebugIt -Console -Message '[WARNING]' -Value 'Insecure option selected (no TLS)' -Color 'Yellow' } Invoke-DebugIt -Console -Message '[INFO]' -Value $uri.ToString() # pfSense requires a lot of magic.... ++ foreach POST $request = Invoke-WebRequest @sessionParams -Uri $uri -Method Get -WebSession $webSession $dictPostData = @{ __csrf_magic = $($request.InputFields[0].Value) donotbackuprrd = 'yes' download = 'Download configuration as XML' } If ($EncryptPassword) { $dictSecurity = @{ encrypt_password = "$EncryptPassword" encrypt_password_confirm = "$EncryptPassword" encrypt = "yes" } $dictPostData += $dictSecurity Invoke-DebugIt -Console -Message '[INFO]' -Value 'Encryption password set' } Try { $rawRequest = Invoke-WebRequest @sessionParams -Uri $uri -Method Post -Body $dictPostData -WebSession $webSession -EA Stop } Catch { Write-Error -Message 'Something went wrong submitting the form' } If ($rawRequest) { If ($OutputXML) { $Encoder = [System.Text.Encoding]::ASCII $retVal = $Encoder.GetString($rawRequest.Content) $retVal } Else { Invoke-DebugIt -Console -Message '[INFO]' -Value ('Output file: {0}' -f $FilePath) ConvertFrom-HexToFile -HexString $rawRequest.Content -FilePath $FilePath } } Else { Write-Error -Message 'Failed to read the output file' } } End { } } Function Restore-pfSenseConfig { } Function Add-pfSenseStaticRoute { } Function Get-pfSenseStaticRoute { } Function Remove-pfSenseStaticRoute { } Function Add-pfSenseGateway { } Function Get-pfSenseGateway { } Function Remove-pfSenseGateway { } Function Get-pfSenseCa { [CmdLetBinding()] Param ( [Parameter(Mandatory = $true, Position = 0, HelpMessage = 'Valid/active websession to server' )] [PSObject] $Session, [Parameter(Mandatory = $false, Position = 1, HelpMessage = 'CA name or ID' )] [String] $Name ) Begin { # Debugging for scripts $Script:boolDebug = $PSBoundParameters.Debug.IsPresent } Process { # Variables $errorActionSilent = 'SilentlyContinue' $objOfHolding = @() # Export the server config to XML object [xml] $objXmlFile = Backup-pfSenseConfig -Session $Session -OutputXML $objXmlCrl = $objXmlFile.pfsense.crl # Don't want the cert info to be displayed by default... too messy. [String[]] $defaultDisplaySet = 'CA', 'CA_ID', 'Serial', 'CRL' $defaultDisplayPropertySet = New-Object System.Management.Automation.PSPropertySet( 'DefaultDisplayPropertySet', [string[]]$defaultDisplaySet ) $PSStandardMembers = [System.Management.Automation.PSMemberInfo[]]@($defaultDisplayPropertySet) # Iterate thru the CAs and return their infos... Foreach ($objCA in $objXmlFile.pfsense.ca) { $objBuilder = New-Object -TypeName PSObject $objBuilder | Add-Member -MemberType NoteProperty -Name 'CA' -Value $objCA.descr.'#cdata-section' $objBuilder | Add-Member -MemberType NoteProperty -Name 'CA_ID' -Value $objCA.refid $objBuilder | Add-Member -MemberType NoteProperty -Name 'Cert' -Value $objCA.crt $objBuilder | Add-Member -MemberType NoteProperty -Name 'Key' -Value $objCA.prv $objBuilder | Add-Member -MemberType NoteProperty -Name 'Serial' -Value $objCA.serial $crl = $objXmlCrl | Where-Object { $_.caref -eq $objCA.refid } $objBuilder | Add-Member -MemberType NoteProperty -Name 'CRL' -Value $crl.descr.'#cdata-section' $objBuilder | Add-Member -MemberType NoteProperty -Name 'CRL_ID' -Value $crl.refid $objBuilder.PSObject.TypeNames.Insert(0, 'CA Information') $objBuilder | Add-Member -MemberType MemberSet PSStandardMembers $PSStandardMembers # Add the builder object to our array object $objOfHolding += $objBuilder } # Returning data... we're done with the work now If ($Name) { $objOfHolding | ? { $_.CA -eq $Name -or $_.CA_ID -eq $Name } } Else { $objOfHolding } # Clean up Remove-Variable objXmlFile -Force -ErrorAction $errorActionSilent -WarningAction $errorActionSilent } End { [GC]::Collect() } } Function Export-pfSenseCa { [CmdLetBinding()] Param ( [Parameter(Mandatory = $True, Position = 0, HelpMessage = 'Valid/active websession to server' )] [PSObject] $Session, [Parameter(Mandatory = $True, Position = 1, HelpMessage = 'CA name or ID' )] [String] $Name, [Parameter(Position = 2)] [ValidateScript({ try { $Folder = Get-Item $($_ | Split-Path -Parent) -ErrorAction Stop } catch [System.Management.Automation.ItemNotFoundException] { Throw [System.Management.Automation.ItemNotFoundException] "${_} Maybe there are network issues?" } if ($Folder.PSIsContainer) { $True } else { Throw [System.Management.Automation.ValidationMetadataException] "Invalid path '${_}'." } })] [String] $FilePath = ('{0}\pfSenseCA.cer' -f $($PWD.Path)) ) Try { $CA = Get-pfSenseCa -Session $Session -Name $Name $Cert = ConvertFrom-Base64 -InputString $CA.Cert $Cert | Out-File -Encoding ascii -FilePath $FilePath } Catch { Write-Error -Message ('CA {0} not found!' -f $Name) } } Function Export-pfSenseCrl { [CmdLetBinding()] Param ( [Parameter(Mandatory = $True, Position = 0, HelpMessage = 'Valid/active websession to server' )] [PSObject] $Session, [Parameter(Mandatory = $True, Position = 1, HelpMessage = 'CRL name or ID' )] [String] $Name, [Parameter(Position = 2)] [ValidateScript({ try { $Folder = Get-Item $($_ | Split-Path -Parent) -ErrorAction Stop } catch [System.Management.Automation.ItemNotFoundException] { Throw [System.Management.Automation.ItemNotFoundException] "${_} Maybe there are network issues?" } if ($Folder.PSIsContainer) { $True } else { Throw [System.Management.Automation.ValidationMetadataException] "Invalid path '${_}'." } })] [String] $FilePath = ('{0}\pfSenseCA.crl' -f $($PWD.Path)) ) Try { $CRL = Get-pfSenseCrl -Session $Session -Name $Name $Cert = ConvertFrom-Base64 -InputString $CRL.Data $Cert | Out-File -Encoding ascii -FilePath $FilePath } Catch { Write-Error -Message ('CRL {0} not found!' -f $Name) } } Function Get-pfSenseCrl { [CmdLetBinding()] Param ( [Parameter(Mandatory = $true, Position = 0, HelpMessage = 'Valid/active websession to server' )] [PSObject] $Session, [Parameter(Mandatory = $false, Position = 1, HelpMessage = 'CRL name or ID' )] [String] $Name ) Begin { # Debugging for scripts $Script:boolDebug = $PSBoundParameters.Debug.IsPresent } Process { # Variables $errorActionSilent = 'SilentlyContinue' $objOfHolding = @() # Export the server config to XML object [xml] $objXmlFile = Backup-pfSenseConfig -Session $Session -OutputXML $objXmlCa = $objXmlFile.pfsense.ca # Don't want the cert info to be displayed by default... too messy. [String[]] $defaultDisplaySet = 'CRL', 'CRL_ID', 'Method', 'CA' $defaultDisplayPropertySet = New-Object System.Management.Automation.PSPropertySet( 'DefaultDisplayPropertySet', [string[]]$defaultDisplaySet ) $PSStandardMembers = [System.Management.Automation.PSMemberInfo[]]@($defaultDisplayPropertySet) # Iterate thru the CAs and return their infos... Foreach ($objCRL in $objXmlFile.pfsense.crl) { $objBuilder = New-Object -TypeName PSObject $objBuilder | Add-Member -MemberType NoteProperty -Name 'CRL' -Value $objCRL.descr.'#cdata-section' $objBuilder | Add-Member -MemberType NoteProperty -Name 'CRL_ID' -Value $objCRL.refid $objBuilder | Add-Member -MemberType NoteProperty -Name 'Data' -Value $objCRL.text.'#cdata-section' $objBuilder | Add-Member -MemberType NoteProperty -Name 'Serial' -Value $objCRL.serial $objBuilder | Add-Member -MemberType NoteProperty -Name 'LifeTime' -Value $objCRL.lifetime $objBuilder | Add-Member -MemberType NoteProperty -Name 'Method' -Value $objCRL.method $ca = $objXmlCa | Where-Object { $_.refid -eq $objCRL.caref } $objBuilder | Add-Member -MemberType NoteProperty -Name 'CA' -Value $ca.descr.'#cdata-section' $objBuilder | Add-Member -MemberType NoteProperty -Name 'CA_ID' -Value $ca.refid $objBuilder.PSObject.TypeNames.Insert(0, 'CRL Information') $objBuilder | Add-Member -MemberType MemberSet PSStandardMembers $PSStandardMembers # Add the builder object to our array object $objOfHolding += $objBuilder } # Returning data... we're done with the work now If ($Name) { $objOfHolding | ? { $_.CRL -eq $Name -or $_.CRL_ID -eq $Name } } Else { $objOfHolding } # Clean up Remove-Variable objXmlFile -Force -ErrorAction $errorActionSilent -WarningAction $errorActionSilent } End { [GC]::Collect() } } #endregion #region Firewall functions Function Add-pfSenseFirewallRule { } Function Get-pfSenseFirewallRule { } Function Remove-pfSenseFirewallRule { } Function Add-pfSenseNatRule { } Function Get-pfSenseNatRule { } Function Remove-pfSenseNatRule { } #endregion #region Snort functions #endregion |