Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add verbosity profile, ngrok tunnel configuration #577

Merged
merged 1 commit into from
Oct 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions powershell/DevolutionsGateway/DevolutionsGateway.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
FunctionsToExport = @(
'Find-DGatewayConfig', 'Enter-DGatewayConfig', 'Exit-DGatewayConfig',
'Set-DGatewayConfig', 'Get-DGatewayConfig',
'New-DGatewayNgrokConfig', 'New-DGatewayNgrokTunnel',
'Set-DGatewayHostname', 'Get-DGatewayHostname',
'New-DGatewayListener', 'Get-DGatewayListeners', 'Set-DGatewayListeners',
'Get-DGatewayPath', 'Get-DGatewayRecordingPath',
Expand Down
152 changes: 147 additions & 5 deletions powershell/DevolutionsGateway/Public/DGateway.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,125 @@ class DGatewaySubscriber {
}
}

class DGatewayNgrokTunnel {
[string] $Proto
[string] $Metadata
[string[]] $AllowCidrs
[string[]] $DenyCidrs

# HTTP tunnel
[string] $Domain
[System.Nullable[System.Single]] $CircuitBreaker
[System.Nullable[System.Boolean]] $Compression

# TCP tunnel
[string] $RemoteAddr

DGatewayNgrokTunnel() { }
}

class DGatewayNgrokConfig {
[string] $AuthToken
[System.Nullable[System.UInt32]] $HeartbeatInterval
[System.Nullable[System.UInt32]] $HeartbeatTolerance
[string] $Metadata
[string] $ServerAddr
[PSCustomObject] $Tunnels

DGatewayNgrokConfig() { }
}

function New-DGatewayNgrokTunnel() {
[CmdletBinding(DefaultParameterSetName = 'http')]
param(
[Parameter(Mandatory = $false, ParameterSetName = 'http',
HelpMessage = "HTTP tunnel")]
[switch] $Http,

[Parameter(Mandatory = $false, ParameterSetName = 'tcp',
HelpMessage = "TCP tunnel")]
[switch] $Tcp,

[Parameter(Mandatory = $false,
HelpMessage = "User-defined metadata that appears when listing tunnel sessions with ngrok")]
[string] $Metadata,

[ValidateScript({
$_ -match '^((\d{1,3}\.){3}\d{1,3}\/\d{1,2}|([\dA-Fa-f]{0,4}:){2,7}[\dA-Fa-f]{0,4}\/\d{1,3})$'
})]
[Parameter(Mandatory = $false,
HelpMessage = "Reject connections that do not match the given CIDRs")]
[string[]] $AllowCidrs,

[ValidateScript({
$_ -match '^((\d{1,3}\.){3}\d{1,3}\/\d{1,2}|([\dA-Fa-f]{0,4}:){2,7}[\dA-Fa-f]{0,4}\/\d{1,3})$'
})]
[Parameter(Mandatory = $false,
HelpMessage = "Reject connections that match the given CIDRs")]
[string[]] $DenyCidrs,

[ValidateScript({
$_ -match '^(\*\.)?([a-zA-Z0-9](-?[a-zA-Z0-9])*\.)*[a-zA-Z]{2,}$'
})]
[Parameter(Mandatory = $false, ParameterSetName = 'http',
HelpMessage = "Any valid domain or hostname previously registered with ngrok")]
[string] $Domain,

[ValidateRange(0.0, 1.0)]
[Parameter(Mandatory = $false, ParameterSetName = 'http',
HelpMessage = "Reject requests when 5XX responses exceed this ratio")]
[System.Single] $CircuitBreaker,

[Parameter(Mandatory = $false, ParameterSetName = 'http',
HelpMessage = "Use gzip compression on HTTP responses")]
[System.Boolean] $Compression,

[ValidateScript({
$_ -match '^([a-zA-Z0-9](-?[a-zA-Z0-9])*\.)*[a-zA-Z]{2,}:\d{1,5}$'
})]
[Parameter(Mandatory = $false, ParameterSetName = 'tcp',
HelpMessage = "The remote TCP address and port to bind. For example: remote_addr: 2.tcp.ngrok.io:21746")]
[string] $RemoteAddr
)

$tunnel = [DGatewayNgrokTunnel]::new()

if ($Tcp) {
$tunnel.Proto = "tcp"
} else {
$tunnel.Proto = "http"
}

$properties = [DGatewayNgrokTunnel].GetProperties() | ForEach-Object { $_.Name }
foreach ($param in $PSBoundParameters.GetEnumerator()) {
if ($properties -Contains $param.Key) {
$tunnel.($param.Key) = $param.Value
}
}

$tunnel
}

function New-DGatewayNgrokConfig() {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string] $AuthToken
)

$ngrok = [DGatewayNgrokConfig]::new()
$ngrok.AuthToken = $AuthToken
$ngrok
}

enum VerbosityProfile {
Default
Debug
Tls
All
Quiet
}

class DGatewayConfig {
[System.Nullable[Guid]] $Id
[string] $Hostname
Expand All @@ -159,7 +278,28 @@ class DGatewayConfig {
[DGatewayListener[]] $Listeners
[DGatewaySubscriber] $Subscriber

[DGatewayNgrokConfig] $Ngrok

[string] $LogDirective
[string] $VerbosityProfile
}

Function Remove-NullObjectProperties {
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline, Mandatory)]
[object[]] $InputObject
)
process {
foreach ($OldObj in $InputObject) {
$NonNullProperties = $OldObj.PSObject.Properties.Name.Where( { -Not [string]::IsNullOrEmpty($OldObj.$_) })
$NewObj = $OldObj | Select-Object $NonNullProperties
$NewObj.PSObject.Properties | Where-Object { $_.TypeNameOfValue.EndsWith('PSCustomObject') } | ForEach-Object {
$NewObj."$($_.Name)" = $NewObj."$($_.Name)" | Remove-NullObjectProperties
}
$NewObj
}
}
}

function Save-DGatewayConfig {
Expand All @@ -172,10 +312,8 @@ function Save-DGatewayConfig {

$ConfigPath = Find-DGatewayConfig -ConfigPath:$ConfigPath
$ConfigFile = Join-Path $ConfigPath $DGatewayConfigFileName

$Properties = $Config.PSObject.Properties.Name
$NonNullProperties = $Properties.Where( { -Not [string]::IsNullOrEmpty($Config.$_) })
$ConfigData = $Config | Select-Object $NonNullProperties | ConvertTo-Json
$ConfigClean = $Config | ConvertTo-Json -Depth 4 | ConvertFrom-Json # drop class type info
$ConfigData = $ConfigClean | Remove-NullObjectProperties | ConvertTo-Json -Depth 4

[System.IO.File]::WriteAllLines($ConfigFile, $ConfigData, $(New-Object System.Text.UTF8Encoding $False))
}
Expand Down Expand Up @@ -203,7 +341,11 @@ function Set-DGatewayConfig {
[string] $DelegationPublicKeyFile,
[string] $DelegationPrivateKeyFile,

[DGatewaySubProvisionerKey] $SubProvisionerPublicKey
[DGatewaySubProvisionerKey] $SubProvisionerPublicKey,

[DGatewayNgrokConfig] $Ngrok,

[VerbosityProfile] $VerbosityProfile
)

$ConfigPath = Find-DGatewayConfig -ConfigPath:$ConfigPath
Expand Down
54 changes: 54 additions & 0 deletions powershell/pester/Config.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Describe 'Devolutions Gateway config' {

Context 'Fresh environment' {
It 'Creates basic configuration' {
Remove-Item (Join-Path $ConfigPath 'gateway.json') -ErrorAction SilentlyContinue | Out-Null
Set-DGatewayConfig -ConfigPath:$ConfigPath -Hostname 'gateway.local'
$(Get-DGatewayConfig -ConfigPath:$ConfigPath).Hostname | Should -Be 'gateway.local'
}
Expand Down Expand Up @@ -43,6 +44,59 @@ Describe 'Devolutions Gateway config' {
Set-DGatewayConfig -ConfigPath:$ConfigPath -RecordingPath $RecordingPath
$(Get-DGatewayConfig -ConfigPath:$ConfigPath).RecordingPath | Should -Be $RecordingPath
}

It 'Sets log verbosity profile' {
Set-DGatewayConfig -ConfigPath:$ConfigPath -VerbosityProfile 'All'
$(Get-DGatewayConfig -ConfigPath:$ConfigPath).VerbosityProfile | Should -Be 'All'
Set-DGatewayConfig -ConfigPath:$ConfigPath -VerbosityProfile 'Default'
$(Get-DGatewayConfig -ConfigPath:$ConfigPath).VerbosityProfile | Should -Be 'Default'
Set-DGatewayConfig -ConfigPath:$ConfigPath -VerbosityProfile 'Tls'
$(Get-DGatewayConfig -ConfigPath:$ConfigPath).VerbosityProfile | Should -Be 'Tls'
{ Set-DGatewayConfig -ConfigPath:$ConfigPath -VerbosityProfile 'yolo' } | Should -Throw
}

It 'Sets ngrok configuration' {
$AuthToken = '4nq9771bPxe8ctg7LKr_2ClH7Y15Zqe4bWLWF9p'
$Metadata = '{"serial": "00012xa-33rUtz9", "comment": "For customer alan@example.com"}'
$HeartbeatInterval = 15
$HeartbeatTolerance = 5
$ngrok = New-DGatewayNgrokConfig -AuthToken $AuthToken
$ngrok.Metadata = $Metadata
$ngrok.HeartbeatInterval = $HeartbeatInterval
$ngrok.HeartbeatTolerance = $HeartbeatTolerance
$httpTunnelParams = @{
Http = $true
Metadata = "c6481452-6f5d-11ee-b962-0242ac120002"
AllowCidrs = @("0.0.0.0/0")
Domain = "gateway.ngrok.io"
CircuitBreaker = 0.5
Compression = $true
}
$tcpTunnelParams = @{
Tcp = $true
AllowCidrs = @("0.0.0.0/0")
RemoteAddr = "7.tcp.ngrok.io:20560"
}
$httpTunnel = New-DGatewayNgrokTunnel @httpTunnelParams
$tcpTunnel = New-DGatewayNgrokTunnel @tcpTunnelParams
$ngrok.Tunnels = [PSCustomObject]@{
"http-endpoint" = $httpTunnel
"tcp-endpoint" = $tcpTunnel
}
Set-DGatewayConfig -ConfigPath:$ConfigPath -Ngrok $ngrok
$(Get-DGatewayConfig -ConfigPath:$ConfigPath).Ngrok.AuthToken | Should -Be $AuthToken
$(Get-DGatewayConfig -ConfigPath:$ConfigPath).Ngrok.Metadata | Should -Be $Metadata
$(Get-DGatewayConfig -ConfigPath:$ConfigPath).Ngrok.HeartbeatInterval | Should -Be $HeartbeatInterval
$(Get-DGatewayConfig -ConfigPath:$ConfigPath).Ngrok.HeartbeatTolerance | Should -Be $HeartbeatTolerance
$Tunnels = $(Get-DGatewayConfig -ConfigPath:$ConfigPath).Ngrok.Tunnels
$Tunnels.'http-endpoint'.Proto | Should -Be 'http'
$Tunnels.'http-endpoint'.Domain | Should -Be $httpTunnel.Domain
$Tunnels.'http-endpoint'.Metadata | Should -Be $httpTunnel.Metadata
$Tunnels.'http-endpoint'.AllowCidrs | Should -Be $httpTunnel.AllowCidrs
$Tunnels.'tcp-endpoint'.Proto | Should -Be 'tcp'
$Tunnels.'tcp-endpoint'.RemoteAddr | Should -Be $tcpTunnel.RemoteAddr
$Tunnels.'tcp-endpoint'.AllowCidrs | Should -Be $tcpTunnel.AllowCidrs
}
}
}
}
13 changes: 7 additions & 6 deletions powershell/pester/ImportCertificate.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,32 @@ Describe 'Devolutions Gateway certificate import' {
BeforeAll {
$DummyPrivateKey = Join-Path $TestDrive 'dummy.key'
New-Item -Path $DummyPrivateKey -Value 'dummy'

$ConfigPath = Join-Path $TestDrive 'Gateway'
}

It 'Smoke' {
ForEach ($certFile in Get-ChildItem -Path ./ImportCertificate/WellOrdered) {
(Get-Item -Path ".\ImportCertificate\WellOrdered\*.crt") | ForEach-Object {
$CertFile = $_.FullName
$expected = Get-Content -Path $certFile

Import-DGatewayCertificate -ConfigPath:$ConfigPath -CertificateFile $certFile -PrivateKeyFile $DummyPrivateKey

$resultingFile = Join-Path $TestDrive 'Gateway' 'server.crt'
$resultingFile = Join-Path $ConfigPath 'server.crt'
$result = Get-Content -Path $resultingFile

$result | Should -Be $expected
}
}

It 'Sorting' {
ForEach ($unorderedCertFile in Get-ChildItem -Path ./ImportCertificate/Unordered) {
$wellOrderedCertFile = Join-Path './ImportCertificate/WellOrdered' $unorderedCertFile.Name
(Get-Item -Path ".\ImportCertificate\Unordered\*.crt") | ForEach-Object {
$unorderedCertFile = $_.FullName
$wellOrderedCertFile = $_.FullName -Replace 'Unordered', 'WellOrdered'
$expected = Get-Content -Path $wellOrderedCertFile

Import-DGatewayCertificate -ConfigPath:$ConfigPath -CertificateFile $unorderedCertFile -PrivateKeyFile $DummyPrivateKey

$resultingFile = Join-Path $TestDrive 'Gateway' 'server.crt'
$resultingFile = Join-Path $ConfigPath 'server.crt'
$result = Get-Content -Path $ResultingFile

$result | Should -Be $expected
Expand Down
3 changes: 1 addition & 2 deletions powershell/run-tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ Import-Module Pester
Push-Location -Path $(Join-Path $PSScriptRoot 'pester')

try {
Invoke-Pester .
Invoke-Pester . -Show All
} finally {
Pop-Location
}