Initial commit
This commit is contained in:
commit
d8f1ac024e
66
Functions/Connect-GraylogService.ps1
Normal file
66
Functions/Connect-GraylogService.ps1
Normal file
@ -0,0 +1,66 @@
|
||||
function Connect-GraylogService {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Connects to the Graylog server.
|
||||
.DESCRIPTION
|
||||
Connects to the Graylog server using the provided credentials.
|
||||
.PARAMETER Credential
|
||||
The credentials to use to connect to the Graylog server.
|
||||
.OUTPUTS
|
||||
None, the session is stored in the global variable $GraylogSession.
|
||||
.EXAMPLE
|
||||
Connect-GraylogService
|
||||
Connects to the Graylog server using the stored credentials.
|
||||
.EXAMPLE
|
||||
Connect-GraylogService -Credential (Get-Credential)
|
||||
Connects to the Graylog server using the provided credentials prompting for both the username and password.
|
||||
.EXAMPLE
|
||||
Connect-GraylogService -Credential (Get-Credential -UserName "ab123456")
|
||||
Connects to the Graylog server using the provided credentials, prompting for just a password.
|
||||
.EXAMPLE
|
||||
$Credential = [PSCredential]::new("ab123456", (ConvertTo-SecureString "Password123" -AsPlainText -Force))
|
||||
Connect-GraylogService -Credential $Credential
|
||||
Connects to the Graylog server using the provided credentials without prompting for any input. (Other methods of creating a PSCredential object can be used)
|
||||
.NOTES
|
||||
The session is stored in the global variable $GraylogSession and is used for subsequent requests to the Graylog server.
|
||||
#>
|
||||
[Alias("Connect-Graylog")]
|
||||
param (
|
||||
[Parameter()]
|
||||
[PSCredential]
|
||||
$Credential
|
||||
)
|
||||
if ((Test-Session)) { return } # If the session is still valid, don't create a new one
|
||||
if (-NOT $Credential) {
|
||||
try {
|
||||
$Credential = Get-Secret Graylog_Credential -ErrorAction Stop
|
||||
} catch {
|
||||
try {
|
||||
Write-Host "The Graylog Credential secret is missing, prompting for input..."
|
||||
$Credential = Get-Credential -Message "Enter your Graylog credentials (the same way as you would via the web service)"
|
||||
$Credential | Set-Secret Graylog_Credential -ErrorAction Stop
|
||||
} catch {
|
||||
throw "Failed to set the Graylog Credential secret: $_"
|
||||
}
|
||||
}
|
||||
}
|
||||
$Request = @{
|
||||
Method = "POST"
|
||||
URI = "$(Get-Secret Graylog_BaseURI -AsPlainText)/api/system/sessions"
|
||||
Body = ConvertTo-Json @{
|
||||
host = $BaseURI.Authority
|
||||
username = $Credential.Username.Split("@")[0]
|
||||
password = $Credential.GetNetworkCredential().Password
|
||||
}
|
||||
ContentType = "application/json"
|
||||
UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML; like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0"
|
||||
Headers = @{ "X-Requested-By" = "XMLHttpRequest" }
|
||||
SessionVariable = "GraylogSession"
|
||||
}
|
||||
# Use a regular Invoke-RestMethod for the initial sign-in request to avoid any issues with the WebSession
|
||||
try { $null = Invoke-RestMethod @Request }
|
||||
catch { throw $_ }
|
||||
|
||||
$GraylogSession | ConvertFrom-GraylogSession | Set-Secret Graylog_Session
|
||||
}
|
||||
# Export-ModuleMember -Function Connect-Graylog
|
56
Functions/ConvertFrom-GraylogSession.ps1
Normal file
56
Functions/ConvertFrom-GraylogSession.ps1
Normal file
@ -0,0 +1,56 @@
|
||||
function ConvertFrom-GraylogSession {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Converts a WebRequestSession object to a JSON string.
|
||||
.DESCRIPTION
|
||||
Converts a WebRequestSession object to a JSON string.
|
||||
.PARAMETER InputObject
|
||||
The WebRequestSession object to convert to a JSON string.
|
||||
.OUTPUTS
|
||||
A JSON string containing the properties of the WebRequestSession object.
|
||||
.EXAMPLE
|
||||
ConvertFrom-Session -WebSession $global:GraylogSession
|
||||
Converts the WebRequestSession object to a JSON string.
|
||||
.EXAMPLE
|
||||
$global:GraylogSession | ConvertFrom-Session
|
||||
Converts the WebRequestSession object to a JSON string.
|
||||
#>
|
||||
param (
|
||||
[Parameter(Mandatory, ValueFromPipeline)]
|
||||
[Microsoft.PowerShell.Commands.WebRequestSession]
|
||||
$InputObject
|
||||
)
|
||||
|
||||
try { $local:Graylog_BaseURI = Get-Secret Graylog_BaseURI -AsPlainText }
|
||||
catch { Initialize-ServiceVault }
|
||||
# $Output = @{
|
||||
# Headers = $InputObject.Headers
|
||||
# Cookies = [Collections.Generic.List[PSCustomObject]]::new()
|
||||
# UseDefaultCredentials = $InputObject.UseDefaultCredentials
|
||||
# Credentials = $InputObject.Credentials
|
||||
# Certificates = $InputObject.Certificates
|
||||
# UserAgent = $InputObject.UserAgent
|
||||
# Proxy = $InputObject.Proxy
|
||||
# MaximumRedirection = $InputObject.MaximumRedirection
|
||||
# MaximumRetryCount = $InputObject.MaximumRetryCount
|
||||
# RetryIntervalInSeconds = $InputObject.RetryIntervalInSeconds
|
||||
# }
|
||||
$Output = $InputObject
|
||||
if ($InputObject.Cookies.Count) {
|
||||
# GetAllCookies is only available in PowerShell Core :c
|
||||
$InputObject.Cookies.GetCookies($local:Graylog_BaseURI) | ForEach-Object {
|
||||
$Output.Cookies.Add(
|
||||
[PSCustomObject]@{
|
||||
Name = $_.Name
|
||||
Value = $_.Value
|
||||
Domain = $_.Domain
|
||||
Path = $_.Path
|
||||
Expires = $_.Expires
|
||||
Secure = $_.Secure
|
||||
HttpOnly = $_.HttpOnly
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return ConvertTo-Json $Output
|
||||
}
|
52
Functions/ConvertTo-GraylogSession.ps1
Normal file
52
Functions/ConvertTo-GraylogSession.ps1
Normal file
@ -0,0 +1,52 @@
|
||||
|
||||
function ConvertTo-GraylogSession {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Converts a JSON string to a WebRequestSession object.
|
||||
.DESCRIPTION
|
||||
Converts a JSON string to a WebRequestSession object.
|
||||
.PARAMETER InputObject
|
||||
The JSON string to convert to a WebRequestSession object.
|
||||
.OUTPUTS
|
||||
A WebRequestSession object containing the properties from the JSON string.
|
||||
.EXAMPLE
|
||||
ConvertTo-Session -InputObject $Json
|
||||
Converts the JSON string to a WebRequestSession object.
|
||||
.EXAMPLE
|
||||
$Json | ConvertTo-Session
|
||||
Converts the JSON string to a WebRequestSession object.
|
||||
.NOTES
|
||||
This function is used to convert a JSON string to a WebRequestSession object.
|
||||
#>
|
||||
param (
|
||||
[Parameter(Mandatory, ValueFromPipeline)]
|
||||
[string]
|
||||
$InputObject
|
||||
)
|
||||
|
||||
$Object = ConvertFrom-Json $InputObject
|
||||
$Session = [Microsoft.PowerShell.Commands.WebRequestSession]::new()
|
||||
$Object.Headers.PSObject.Properties | ForEach-Object { $Session.Headers.Add($_.Name, $_.Value) }
|
||||
$Session.UseDefaultCredentials = $Object.UseDefaultCredentials
|
||||
$Session.Credentials = $Object.Credentials
|
||||
$Session.Certificates = $Object.Certificates
|
||||
$Session.UserAgent = $Object.UserAgent
|
||||
$Session.Proxy = $Object.Proxy
|
||||
$Session.MaximumRedirection = $Object.MaximumRedirection
|
||||
try { $Session.MaximumRetryCount = $Object.MaximumRetryCount } catch {} # MaximumRetryCount is only available in PowerShell Core
|
||||
try { $Session.RetryIntervalInSeconds = $Object.RetryIntervalInSeconds } catch {} # RetryIntervalInSeconds is only available in PowerShell Core
|
||||
if ($Object.Cookies) {
|
||||
$Object.Cookies | ForEach-Object {
|
||||
$Cookie = [Net.Cookie]::new()
|
||||
$Cookie.Name = $_.Name
|
||||
$Cookie.Value = $_.Value
|
||||
$Cookie.Domain = $_.Domain
|
||||
$Cookie.Path = $_.Path
|
||||
$Cookie.Expires = $_.Expires
|
||||
$Cookie.Secure = $_.Secure
|
||||
$Cookie.HttpOnly = $_.HttpOnly
|
||||
$Session.Cookies.Add($Cookie)
|
||||
}
|
||||
}
|
||||
return $Session
|
||||
}
|
26
Functions/Disconnect-GraylogService.ps1
Normal file
26
Functions/Disconnect-GraylogService.ps1
Normal file
@ -0,0 +1,26 @@
|
||||
function Disconnect-GraylogService {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Disconnects from the Graylog server.
|
||||
.DESCRIPTION
|
||||
Disconnects from the Graylog server by deleting the session using the current WebSession data. By default, the stored credentials are not removed.
|
||||
.PARAMETER Force
|
||||
An optional switch to force the removal of the stored credentials as well as the session.
|
||||
.OUTPUTS
|
||||
None
|
||||
.EXAMPLE
|
||||
Disconnect-GraylogService
|
||||
Disconnects from the Graylog server.
|
||||
.NOTES
|
||||
Garbage collection is called after the session is deleted to ensure that the session is removed from memory.
|
||||
#>
|
||||
[Alias("Disconnect-Graylog")]
|
||||
param (
|
||||
[Parameter()]
|
||||
[Switch]
|
||||
$Force
|
||||
)
|
||||
Invoke-GraylogRequest DELETE "/api/system/sessions"
|
||||
Remove-Secret Graylog_Session
|
||||
if ($Force) { Remove-Secret Graylog_Credential }
|
||||
}
|
31
Functions/Get-GraylogStreamId.ps1
Normal file
31
Functions/Get-GraylogStreamId.ps1
Normal file
@ -0,0 +1,31 @@
|
||||
function Get-GraylogStreamId {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Gets the ID of a log stream in Graylog.
|
||||
.DESCRIPTION
|
||||
Gets the ID of a log stream in Graylog using the provided log name.
|
||||
.PARAMETER LogName
|
||||
The name of the log stream to get the ID for, the log name has to be an exact match for a known log stream.
|
||||
.OUTPUTS
|
||||
The ID of the log stream if it exists, otherwise null.
|
||||
.EXAMPLE
|
||||
Get-GraylogStreamId -LogName "Windows Security"
|
||||
Gets the ID of the log stream with the name "Windows Security" (645b6fdd9d3689589cdce1bc).
|
||||
#>
|
||||
param (
|
||||
[Parameter(Mandatory)]
|
||||
[ValidateSet("Infoblox", "Infoblox DNS", "Windows Application", "Windows Security", "Windows System")]
|
||||
[string]
|
||||
$LogName
|
||||
)
|
||||
|
||||
# TODO: Use Secret Management module to get the Graylog API URI
|
||||
if ($null -eq $global:GraylogStreams) {
|
||||
$Response = Invoke-GraylogRequest GET "/streams"
|
||||
$global:GraylogStreams = $Response.Streams
|
||||
}
|
||||
|
||||
$Stream = $global:GraylogStreams.Where{$_.Title -eq $LogName}
|
||||
if ($null -eq $Stream) { return $null }
|
||||
return $Stream.Id
|
||||
}
|
41
Functions/Initialize-GraylogServiceVault.ps1
Normal file
41
Functions/Initialize-GraylogServiceVault.ps1
Normal file
@ -0,0 +1,41 @@
|
||||
function Initialize-GraylogServiceVault {
|
||||
try {
|
||||
$null = Get-SecretVault "Graylog" -ErrorAction Stop
|
||||
} catch {
|
||||
try {
|
||||
$null = Register-SecretVault "Graylog" -ModuleName "Microsoft.PowerShell.SecretStore" -ErrorAction Stop
|
||||
} catch {
|
||||
throw "Failed to create the Graylog secret vault: $_"
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$null = Get-Secret Graylog_BaseURI -ErrorAction Stop
|
||||
} catch {
|
||||
try {
|
||||
do {
|
||||
Write-Host "The Graylog Base URI secret is missing, prompting for input..."
|
||||
$local:Graylog_Host = [string](Read-Host -Prompt "Enter Graylog Host (e.g. graylog.example.com)")
|
||||
$local:Graylog_Port = [int](Read-Host -Prompt "Enter Graylog Port (e.g. 80, 443, 9000, etc)")
|
||||
$local:Graylog_IsHTTPS = [bool]$Host.UI.PromptForChoice("Graylog Base URI - Is the application served over HTTPS?", "(Is there a lock symbol when you visit the app?)", @("&No", "&Yes"), 0)
|
||||
$local:Graylog_Protocol = if ($local:Graylog_IsHTTPS) { "https" } else { "http" }
|
||||
$local:Graylog_BaseURI = "${local:Graylog_Protocol}://${local:Graylog_Host}:${local:Graylog_Port}"
|
||||
$local:Graylog_IsBaseURICorrect = [bool]$Host.UI.PromptForChoice("Graylog Base URI - Is the following URI correct?", $local:Graylog_BaseURI, @("&No", "&Yes"), 0)
|
||||
} while (-NOT $local:Graylog_IsBaseURICorrect)
|
||||
$null = $local:Graylog_BaseURI | Set-Secret Graylog_BaseURI -ErrorAction Stop
|
||||
} catch {
|
||||
throw "Failed to set the Graylog Base URI secret: $_"
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$null = Get-Secret Graylog_Credential -ErrorAction Stop
|
||||
} catch {
|
||||
try {
|
||||
Write-Host "The Graylog Credential secret is missing, prompting for input..."
|
||||
Get-Credential -Message "Enter your Graylog credentials (the same way as you would via the web service)" | Set-Secret Graylog_Credential -ErrorAction Stop
|
||||
} catch {
|
||||
throw "Failed to set the Graylog Credential secret: $_"
|
||||
}
|
||||
}
|
||||
}
|
61
Functions/Invoke-GraylogRequest.ps1
Normal file
61
Functions/Invoke-GraylogRequest.ps1
Normal file
@ -0,0 +1,61 @@
|
||||
function Invoke-GraylogRequest {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Invokes a request to the Graylog API.
|
||||
.DESCRIPTION
|
||||
Invokes a request to the Graylog API using the provided parameters and HTTP method.
|
||||
.PARAMETER Method
|
||||
The HTTP method to use for the request (default: GET, available: DELETE, GET, HEAD, OPTIONS, POST, PUT, PATCH).
|
||||
.PARAMETER Path
|
||||
The path to the API endpoint to invoke the request to.
|
||||
.PARAMETER Body
|
||||
The (optional) body of the request to send to the API endpoint, which should be provided as a JSON string (or eqivalent object for other formats).
|
||||
.PARAMETER ContentType
|
||||
The content type of the request body (default: application/json).
|
||||
.OUTPUTS
|
||||
The response from the Graylog API.
|
||||
.EXAMPLE
|
||||
Invoke-GraylogRequest GET "/api/system/sessions"
|
||||
.EXAMPLE
|
||||
Invoke-GraylogRestRequest -Method GET -Path "/api/system/sessions"
|
||||
Invokes a GET request to the /api/system/sessions endpoint in the Graylog API using the global WebSession object.
|
||||
.EXAMPLE
|
||||
Invoke-GraylogRestRequest -Method POST -Path "/api/system/sessions" -Body @{
|
||||
host = $BaseURI.Authority
|
||||
username = $Credential.Username.Split("@")[0]
|
||||
password = $Credential.GetNetworkCredential().Password
|
||||
}
|
||||
Invokes a POST request to the /api/system/sessions endpoint in the Graylog API using the global WebSession object and the provided body.
|
||||
.NOTES
|
||||
This function is used to invoke requests to the Graylog API using the provided parameters.
|
||||
The Method, Path, and WebSession parameters are required, while the Body, ContentType, Headers, and UserAgent parameters are optional.
|
||||
#>
|
||||
[Alias("igsr")]
|
||||
param (
|
||||
[Parameter(Mandatory)]
|
||||
[ValidateSet("DELETE", "GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH")]
|
||||
[string]
|
||||
$Method,
|
||||
|
||||
[Parameter(Mandatory)]
|
||||
[string]
|
||||
$Path,
|
||||
|
||||
[Parameter()]
|
||||
[string]
|
||||
$Body,
|
||||
|
||||
[Parameter()]
|
||||
[string]
|
||||
$ContentType = "application/json"
|
||||
)
|
||||
$Session = Get-Secret Graylog_Session Graylog -AsPlainText -ErrorAction Stop
|
||||
$Request = @{
|
||||
Method = $Method
|
||||
URI = "$(Get-Secret Graylog_BaseURI -AsPlainText)/api/$($Path.TrimStart('/api'))"
|
||||
WebSession = (ConvertTo-GraylogSession $Session)
|
||||
ContentType = $ContentType
|
||||
}
|
||||
if ($Body) { $Request.Body = $Body }
|
||||
Invoke-RestMethod @Request
|
||||
}
|
58
Functions/Receive-GraylogSearchJob.ps1
Normal file
58
Functions/Receive-GraylogSearchJob.ps1
Normal file
@ -0,0 +1,58 @@
|
||||
function Receive-GraylogSearchJob {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Retrieves the results of a search job in Graylog.
|
||||
.DESCRIPTION
|
||||
Retrieves the results of a search job in Graylog using the provided SearchId, QueryId, and Filter
|
||||
Alternatively, a PSCustomObject containing the SearchId, QueryId, and FilterId can be provided to retrieve the results.
|
||||
.PARAMETER Job
|
||||
A PSCustomObject containing the SearchId, QueryId, and FilterId of the search job. This parameter cannot be used with the other *Id parameters.
|
||||
.PARAMETER SearchId
|
||||
The ID of the search job to retrieve the results for.
|
||||
.PARAMETER QueryId
|
||||
The ID of the query to retrieve the results for.
|
||||
.PARAMETER FilterId
|
||||
The ID of the filter to retrieve the results for.
|
||||
.OUTPUTS
|
||||
The results of the search job.
|
||||
.EXAMPLE
|
||||
Receive-GraylogSearchJob -Job $Job
|
||||
Retrieves the results of the search job using the provided PSCustomObject containing the SearchId, QueryId, and FilterId.
|
||||
.EXAMPLE
|
||||
Receive-GraylogSearchJob -SearchId "..." -QueryId "..." -FilterId ".."
|
||||
Retrieves the results of the search job with the provided SearchId, QueryId, and FilterId.
|
||||
.NOTES
|
||||
The SearchId, QueryId, and FilterId are used to retrieve the results of the search job using the Receive-GraylogJob function.
|
||||
These can either be provided as the Job parameter, or as individual parameters.
|
||||
#>
|
||||
param (
|
||||
[Parameter(Mandatory, ParameterSetName="ByPSCustomObject", ValueFromPipeline, ValueFromRemainingArguments)]
|
||||
[PSCustomObject]
|
||||
$Job,
|
||||
|
||||
[Parameter(Mandatory={-NOT $Job}, ParameterSetName="ById")]
|
||||
[string]
|
||||
$SearchId,
|
||||
|
||||
[Parameter(Mandatory={-NOT $Job}, ParameterSetName="ById")]
|
||||
[string]
|
||||
$QueryId,
|
||||
|
||||
[Parameter(Mandatory={-NOT $Job}, ParameterSetName="ById")]
|
||||
[string]
|
||||
$FilterId
|
||||
)
|
||||
if ($PSCmdlet.ParameterSetName -eq "ByPSCustomObject") {
|
||||
$SearchId = $Job.SearchId
|
||||
$QueryId = $Job.QueryId
|
||||
$FilterId = $Job.FilterId
|
||||
}
|
||||
$Body = ConvertTo-Json @{
|
||||
global_override = @{ keep_queries = @($QueryId) }
|
||||
parameter_bindings = @{}
|
||||
}
|
||||
$Response = Invoke-GraylogRequest POST "/views/search/$SearchId/execute" $Body
|
||||
$Data = $Response.results.$QueryId.search_types.$FilterId
|
||||
if ($Data.total_results -eq 0) { throw "No results found for the search job '$SearchId'." }
|
||||
return $Data.messages.message
|
||||
}
|
151
Functions/Search-Graylog.ps1
Normal file
151
Functions/Search-Graylog.ps1
Normal file
@ -0,0 +1,151 @@
|
||||
function Search-Graylog {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Queries Graylog for log data using the provided query and log stream name.
|
||||
.DESCRIPTION
|
||||
Query Graylog for log data using the provided query and log stream name. The query should contain the key:value pairs to search for, and the key should be the CASE-SENSITIVE field name to search within.
|
||||
.PARAMETER Query
|
||||
The search query to execute, should contain the key:value pairs to search for.
|
||||
Example: "EventID:4740 && TargetUserName:ab123456" will search for all account lockouts for the user ab123456.
|
||||
(Note that the LogId would need to be changed to the unique identifier for the Active Directory log stream in the above example)
|
||||
.PARAMETER TimeSpan
|
||||
The time span to search within, defaults to 7 days.
|
||||
.PARAMETER LogName
|
||||
The name of the log stream to search within, defaults to "Windows Security".
|
||||
.PARAMETER Limit
|
||||
The maximum number of results to return, defaults to 150.
|
||||
.PARAMETER SortField
|
||||
The field to sort the results by, defaults to "timestamp".
|
||||
.PARAMETER SortOrder
|
||||
The order to sort the results by, defaults to "desc".
|
||||
.PARAMETER AsJob
|
||||
An optional switch to run the search job as a background job.
|
||||
.PARAMETER Detailed
|
||||
An optional switch to return the search job details instead of the results (only does anything when -AsJob is not used).
|
||||
.OUTPUTS
|
||||
A PSCustomObject containing the SearchId, QueryId, and FilterId of the search job.
|
||||
.EXAMPLE
|
||||
Start-GraylogJob -Query "EventID:4740 && TargetUserName:ab123456"
|
||||
Starts a search job for all account lockouts for the user ab123456.
|
||||
.NOTES
|
||||
A identifier for various parts of the search job (SearchId, QueryId, FilterId) are generated and returned in a PSCustomObject, which can be used to retrieve the results of the search job.
|
||||
The SearchId, QueryId, and FilterId are used to retrieve the results of the search job using the Receive-GraylogJob function.
|
||||
#>
|
||||
[Alias("Search-Graylog")]
|
||||
param (
|
||||
[Parameter(Mandatory)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string]
|
||||
$Query,
|
||||
|
||||
[Parameter()]
|
||||
[TimeSpan]
|
||||
$TimeSpan = [TimeSpan]::FromDays(7),
|
||||
|
||||
[Parameter()]
|
||||
[ValidateSet("Infoblox", "Infoblox DNS", "Windows Application", "Windows Security", "Windows System")]
|
||||
[string]
|
||||
$LogName = "Windows Security",
|
||||
|
||||
[Parameter()]
|
||||
[int]
|
||||
$Limit = 150,
|
||||
|
||||
[Parameter()]
|
||||
[string]
|
||||
$SortField = "timestamp",
|
||||
|
||||
[Parameter()]
|
||||
[ValidateSet("desc", "asc")]
|
||||
[string]
|
||||
$SortOrder = "desc",
|
||||
|
||||
[Parameter()]
|
||||
[switch]
|
||||
$AsJob,
|
||||
|
||||
[Parameter()]
|
||||
[switch]
|
||||
$Detailed
|
||||
)
|
||||
|
||||
$LogId = Get-GraylogLogStreamId -LogName $LogName
|
||||
if ($null -eq $LogId) { throw "The log stream '$LogName' does not exist." }
|
||||
$SearchId = [String]::Join('', [GUID]::NewGUID().GUID.Replace("-", "")[0..23]) # Generate a unique identifier for the search
|
||||
$QueryId = [GUID]::NewGUID().GUID.ToString() # Generate a unique identifier for the query
|
||||
$FilterId = [GUID]::NewGUID().GUID.ToString() # Generate a unique identifier for the filter
|
||||
|
||||
$Request = @{
|
||||
Method = "POST"
|
||||
Path = "/views/search"
|
||||
Body = ConvertTo-JSON -Depth 7 @{
|
||||
id = $SearchId
|
||||
queries = @(
|
||||
@{
|
||||
id = $QueryId
|
||||
query = @{
|
||||
type = "elasticsearch"
|
||||
query_string = $Query
|
||||
}
|
||||
timerange = @{
|
||||
type = "relative"
|
||||
from = $TimeSpan.TotalSeconds
|
||||
}
|
||||
filter = @{
|
||||
type = "or"
|
||||
filters = @(@{
|
||||
type = "stream"
|
||||
id = $LogId
|
||||
})
|
||||
}
|
||||
filters = @()
|
||||
search_types = @(@{
|
||||
id = $FilterId
|
||||
query = $null
|
||||
timerange = $null
|
||||
offset = 0
|
||||
streams = @()
|
||||
decorators = @()
|
||||
type = "messages"
|
||||
limit = $Limit
|
||||
filters = @()
|
||||
sort = @(@{
|
||||
field = $SortField.ToLower()
|
||||
order = $SortOrder.ToUpper()
|
||||
})
|
||||
})
|
||||
}
|
||||
)
|
||||
parameters = @()
|
||||
}
|
||||
}
|
||||
$QuerySuccess = $true
|
||||
try { $null = Invoke-GraylogRequest @Request }
|
||||
catch { $QuerySuccess = $false }
|
||||
|
||||
if ($AsJob) {
|
||||
return [PSCustomObject]@{
|
||||
Success = $QuerySuccess
|
||||
SearchId = $SearchId
|
||||
QueryId = $QueryId
|
||||
FilterId = $FilterId
|
||||
Request = $Request
|
||||
}
|
||||
} else {
|
||||
$RetrievalSuccess = $true
|
||||
try { $Data = Receive-GraylogSearchJob -SearchId $SearchId -QueryId $QueryId -FilterId $FilterId }
|
||||
catch { $RetrievalSuccess = $false }
|
||||
if (-NOT $Detailed) { return $Data }
|
||||
else {
|
||||
return [PSCustomObject]@{
|
||||
QuerySuccess = $QuerySuccess
|
||||
RetrievalSuccess = $RetrievalSuccess
|
||||
Data = $Data
|
||||
SearchId = $SearchId
|
||||
QueryId = $QueryId
|
||||
FilterId = $FilterId
|
||||
Request = $Request
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
45
Functions/Test-GraylogSession.ps1
Normal file
45
Functions/Test-GraylogSession.ps1
Normal file
@ -0,0 +1,45 @@
|
||||
function Test-GraylogSession {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Tests the current session to the Graylog server.
|
||||
.DESCRIPTION
|
||||
Tests the current session to the Graylog server to ensure that it is still valid.
|
||||
.PARAMETER SkipSecretCheck
|
||||
An optional switch to skip the check for the required secrets.
|
||||
.PARAMETER SkipSessionCheck
|
||||
An optional switch to skip the check for the session secret.
|
||||
.OUTPUTS
|
||||
True if the session is valid, otherwise false.
|
||||
.EXAMPLE
|
||||
Test-GraylogSession
|
||||
Tests the current session to the Graylog server.
|
||||
.NOTES
|
||||
This function is used to test the current session to the Graylog server to ensure that it is still valid.
|
||||
#>
|
||||
param (
|
||||
[Parameter()]
|
||||
[Switch]
|
||||
$SkipSecretCheck,
|
||||
|
||||
[Parameter()]
|
||||
[Switch]
|
||||
$SkipSessionCheck
|
||||
)
|
||||
|
||||
if (-NOT $SkipSecretCheck) {
|
||||
try { $null = Get-Secret Graylog_BaseURI -ErrorAction Stop }
|
||||
catch { Write-Error "The Graylog BaseURI secret is missing, try running Initialize-GraylogServiceVault?"; return $false }
|
||||
try { $null = Get-Secret Graylog_Credential -ErrorAction Stop }
|
||||
catch { Write-Error "The Graylog Credential secret is missing, try running Initialize-GraylogServiceVault?"; return $false }
|
||||
try { $null = Get-Secret Graylog_Session -ErrorAction Stop }
|
||||
catch { Write-Error "The Graylog Session secret is missing, try running Connect-GraylogService?"; return $false }
|
||||
if ($SkipSessionCheck) { return $true }
|
||||
}
|
||||
|
||||
if (-NOT $SkipSessionCheck) {
|
||||
try { return (Invoke-GraylogRequest GET "/system/sessions").is_valid }
|
||||
catch { return $false }
|
||||
}
|
||||
|
||||
throw "-SkipSecretCheck and -SkipSessionCheck are mutually exclusive."
|
||||
}
|
149
PSGraylog.psd1
Normal file
149
PSGraylog.psd1
Normal file
@ -0,0 +1,149 @@
|
||||
#
|
||||
# Module manifest for module 'PSGraylog'
|
||||
#
|
||||
# Generated by: Nathan Windisch <n@wnd.sh>
|
||||
#
|
||||
# Generated on: 2024-07-24
|
||||
#
|
||||
|
||||
@{
|
||||
|
||||
# Script module or binary module file associated with this manifest.
|
||||
RootModule = 'PSGraylog.psm1'
|
||||
|
||||
# Version number of this module.
|
||||
ModuleVersion = '0.0.1'
|
||||
|
||||
# Supported PSEditions
|
||||
# CompatiblePSEditions = @()
|
||||
|
||||
# ID used to uniquely identify this module
|
||||
GUID = 'a145661b-ef85-4b71-8fa3-cafbb7e210a5'
|
||||
|
||||
# Author of this module
|
||||
Author = 'Nathan Windisch <n@wnd.sh>'
|
||||
|
||||
# Company or vendor of this module
|
||||
CompanyName = 'wnd.sh'
|
||||
|
||||
# Copyright statement for this module
|
||||
Copyright = '(c) Nathan Windisch <n@wnd.sh>. All rights reserved.'
|
||||
|
||||
# Description of the functionality provided by this module
|
||||
Description = 'A PowerShell interface for Graylog'
|
||||
|
||||
# Minimum version of the PowerShell engine required by this module
|
||||
PowerShellVersion = '5.1'
|
||||
|
||||
# Name of the PowerShell host required by this module
|
||||
# PowerShellHostName = ''
|
||||
|
||||
# Minimum version of the PowerShell host required by this module
|
||||
# PowerShellHostVersion = ''
|
||||
|
||||
# Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
|
||||
# DotNetFrameworkVersion = ''
|
||||
|
||||
# Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
|
||||
# ClrVersion = ''
|
||||
|
||||
# Processor architecture (None, X86, Amd64) required by this module
|
||||
# ProcessorArchitecture = ''
|
||||
|
||||
# Modules that must be imported into the global environment prior to importing this module
|
||||
RequiredModules = @("Microsoft.PowerShell.SecretManagement", "Microsoft.PowerShell.SecretStore")
|
||||
|
||||
# Assemblies that must be loaded prior to importing this module
|
||||
# RequiredAssemblies = @()
|
||||
|
||||
# Script files (.ps1) that are run in the caller's environment prior to importing this module.
|
||||
# ScriptsToProcess = @()
|
||||
|
||||
# Type files (.ps1xml) to be loaded when importing this module
|
||||
# TypesToProcess = @()
|
||||
|
||||
# Format files (.ps1xml) to be loaded when importing this module
|
||||
# FormatsToProcess = @()
|
||||
|
||||
# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
|
||||
NestedModules = @(
|
||||
"Functions\Connect-GraylogService.ps1",
|
||||
"Functions\Disconnect-GraylogService.ps1",
|
||||
"Functions\Get-GraylogStreamId.ps1",
|
||||
"Functions\Initialize-GraylogServiceVault.ps1",
|
||||
"Functions\Invoke-GraylogRequest.ps1",
|
||||
"Functions\Receive-GraylogSearchJob.ps1",
|
||||
"Functions\Search-Graylog.ps1",
|
||||
"Functions\Test-GraylogSession.ps1",
|
||||
"Functions\ConvertFrom-GraylogSession.ps1",
|
||||
"Functions\ConvertTo-GraylogSession.ps1"
|
||||
)
|
||||
|
||||
# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
|
||||
FunctionsToExport = @(
|
||||
"Connect-GraylogService",
|
||||
"Disconnect-GraylogService",
|
||||
"Get-GraylogStreamId",
|
||||
"Initialize-GraylogServiceVault",
|
||||
"Invoke-GraylogRequest",
|
||||
"Receive-GraylogSearchJob",
|
||||
"Search-Graylog",
|
||||
"Test-GraylogSession",
|
||||
"ConvertFrom-GraylogSession",
|
||||
"ConvertTo-GraylogSession"
|
||||
)
|
||||
|
||||
# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
|
||||
CmdletsToExport = @()
|
||||
|
||||
# Variables to export from this module
|
||||
VariablesToExport = @()
|
||||
|
||||
# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
|
||||
AliasesToExport = @("igsr", "Connect-Graylog", "Disconnect-Graylog")
|
||||
|
||||
# DSC resources to export from this module
|
||||
# DscResourcesToExport = @()
|
||||
|
||||
# List of all modules packaged with this module
|
||||
# ModuleList = @()
|
||||
|
||||
# List of all files packaged with this module
|
||||
# FileList = @()
|
||||
|
||||
# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
|
||||
PrivateData = @{
|
||||
PSData = @{
|
||||
# Tags applied to this module. These help with module discovery in online galleries.
|
||||
# Tags = @()
|
||||
|
||||
# A URL to the license for this module.
|
||||
# LicenseUri = ''
|
||||
|
||||
# A URL to the main website for this project.
|
||||
# ProjectUri = ''
|
||||
|
||||
# A URL to an icon representing this module.
|
||||
# IconUri = ''
|
||||
|
||||
# ReleaseNotes of this module
|
||||
# ReleaseNotes = ''
|
||||
|
||||
# Prerelease string of this module
|
||||
# Prerelease = ''
|
||||
|
||||
# Flag to indicate whether the module requires explicit user acceptance for install/update/save
|
||||
# RequireLicenseAcceptance = $false
|
||||
|
||||
# External dependent modules of this module
|
||||
# ExternalModuleDependencies = @()
|
||||
|
||||
} # End of PSData hashtable
|
||||
} # End of PrivateData hashtable
|
||||
|
||||
# HelpInfo URI of this module
|
||||
# HelpInfoURI = ''
|
||||
|
||||
# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
|
||||
# DefaultCommandPrefix = ""
|
||||
}
|
2
PSGraylog.psm1
Normal file
2
PSGraylog.psm1
Normal file
@ -0,0 +1,2 @@
|
||||
Import-Module "$PSScriptRoot/Functions/Initialize-GraylogServiceVault.ps1"
|
||||
$null = Initialize-GraylogServiceVault
|
28
README.md
Normal file
28
README.md
Normal file
@ -0,0 +1,28 @@
|
||||
# PSGraylog
|
||||
*A PowerShell interface for Graylog*
|
||||
|
||||
# Getting Started
|
||||
1. First, install and import the module from your local PSGallery repo.
|
||||
You should be prompted to set up your Graylog host, and your credentials.
|
||||
(You can always re-run this with `Initialize-GraylogServiceVault`)
|
||||
```pwsh
|
||||
TODO: How to install from a repo
|
||||
Install-Module PSGraylog
|
||||
Import-Module PSGraylog
|
||||
```
|
||||
2. Then, connect to Graylog.
|
||||
```pwsh
|
||||
Connect-GraylogService
|
||||
```
|
||||
3. Finally, run a query (the default for the -LogName parameter is 'Windows Security', which is (in my environment, anyways) Active Directory logs):
|
||||
```pwsh
|
||||
$Query = "EventID:4740 && TargetUsername:ab123456"
|
||||
Search-Graylog $Query
|
||||
```
|
||||
4. If you want to re-use the data, you can use the -AsJob parameter to return a GraylogSearchJob object.
|
||||
This object contains various identifers used to locate the search query, and is much quicker then
|
||||
re-running the query.
|
||||
```pwsh
|
||||
$Job = Search-Graylog $Query -AsJob
|
||||
$Job | Receive-GraylogSearchJob
|
||||
```
|
Loading…
Reference in New Issue
Block a user