mirror of
https://github.com/freedbygrace/PSOPNSenseAPI.git
synced 2026-07-27 12:19:12 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ef21e5d359 | |||
| fa5c666cbd | |||
| d5fe842cb7 | |||
| 52a422d6cc | |||
| ccfb37aaac | |||
| 28546595bd | |||
| 08e21843e0 | |||
| bfbc031a76 |
@@ -1,126 +0,0 @@
|
||||
name: Build and Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- 'docs/**'
|
||||
- '.github/**'
|
||||
- '!.github/workflows/build-and-release.yml'
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release:
|
||||
description: 'Create a release'
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: '6.0.x'
|
||||
|
||||
- name: Setup PowerShell
|
||||
uses: actions/setup-powershell@v1
|
||||
with:
|
||||
powershell-version: '7.2'
|
||||
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore src/PSOPNSenseAPI.sln
|
||||
|
||||
- name: Run tests
|
||||
run: pwsh -Command "./build/test.ps1 -Configuration Release"
|
||||
|
||||
- name: Build and package
|
||||
run: |
|
||||
pwsh -Command "./build/build-release.ps1 -Clean -Package"
|
||||
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: PSOPNSenseAPI
|
||||
path: output/PSOPNSenseAPI/
|
||||
|
||||
- name: Upload package artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: PSOPNSenseAPI-Package
|
||||
path: packages/*.zip
|
||||
|
||||
release:
|
||||
needs: build
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main' || github.event.inputs.release == 'true'
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup PowerShell
|
||||
uses: actions/setup-powershell@v1
|
||||
with:
|
||||
powershell-version: '7.2'
|
||||
|
||||
- name: Download build artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: PSOPNSenseAPI
|
||||
path: output/PSOPNSenseAPI/
|
||||
|
||||
- name: Download package artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: PSOPNSenseAPI-Package
|
||||
path: packages/
|
||||
|
||||
- name: Configure Git
|
||||
run: |
|
||||
git config --global user.name "GitHub Actions"
|
||||
git config --global user.email "actions@github.com"
|
||||
|
||||
- name: Create Release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
$version = Get-Date -Format "yyyy.MM.dd.HHmm"
|
||||
|
||||
# Update version in project file
|
||||
$projectFile = "src/PSOPNSenseAPI/PSOPNSenseAPI.csproj"
|
||||
$projectXml = [xml](Get-Content $projectFile)
|
||||
$versionNode = $projectXml.Project.PropertyGroup.Version
|
||||
$versionNode.InnerText = $version
|
||||
$projectXml.Save($projectFile)
|
||||
|
||||
# Update version in module manifest
|
||||
$manifestFile = "PSOPNSenseAPI/PSOPNSenseAPI.psd1"
|
||||
$manifestContent = Get-Content -Path $manifestFile -Raw
|
||||
$manifestContent = $manifestContent -replace "ModuleVersion\s*=\s*['`"].*['`"]", "ModuleVersion = '$version'"
|
||||
Set-Content -Path $manifestFile -Value $manifestContent
|
||||
|
||||
# Commit changes
|
||||
git add $projectFile
|
||||
git add $manifestFile
|
||||
git commit -m "Release version $version [skip ci]"
|
||||
git push
|
||||
|
||||
# Create release
|
||||
$packagePath = Get-ChildItem -Path "packages/*.zip" | Select-Object -First 1 -ExpandProperty FullName
|
||||
|
||||
gh release create "v$version" `
|
||||
--title "Release v$version" `
|
||||
--notes "Release version $version - $(Get-Date -Format 'yyyy-MM-dd')" `
|
||||
$packagePath
|
||||
shell: pwsh
|
||||
@@ -1,3 +1,5 @@
|
||||
.vs/
|
||||
bin/
|
||||
obj/
|
||||
*.zip
|
||||
output/*.zip
|
||||
|
||||
@@ -6,6 +6,8 @@ This component provides cmdlets for managing Tailscale VPN on OPNSense firewalls
|
||||
|
||||
The Tailscale Integration component allows you to install, configure, and manage Tailscale VPN on OPNSense firewalls. It provides cmdlets for enabling Tailscale, configuring subnet routes, and managing the Tailscale connection.
|
||||
|
||||
The module will automatically install the Tailscale plugin if it doesn't exist, making it easy to set up Tailscale on a new OPNSense firewall.
|
||||
|
||||
## Cmdlets
|
||||
|
||||
### Get-OPNSenseTailscaleStatus
|
||||
@@ -138,6 +140,53 @@ Apply-OPNSenseTailscaleChanges
|
||||
Apply-OPNSenseTailscaleChanges -Force
|
||||
```
|
||||
|
||||
### Connect-OPNSenseTailscale
|
||||
|
||||
Connects to the Tailscale network using an authentication key.
|
||||
|
||||
#### Parameters
|
||||
|
||||
- **AuthKey** - The Tailscale authentication key.
|
||||
- **InstallIfMissing** - Installs the Tailscale plugin if it's not already installed.
|
||||
- **EnableIfDisabled** - Enables Tailscale if it's disabled.
|
||||
- **StartIfStopped** - Starts the Tailscale service if it's stopped.
|
||||
- **SubnetRoutes** - The subnet routes to advertise to Tailscale.
|
||||
- **AdvertiseRoutes** - Whether to advertise routes to Tailscale.
|
||||
- **Force** - Suppresses the confirmation prompt.
|
||||
- **PassThru** - Returns the Tailscale status.
|
||||
|
||||
#### Examples
|
||||
|
||||
```powershell
|
||||
# Connect to Tailscale with an auth key
|
||||
Connect-OPNSenseTailscale -AuthKey "tskey-auth-abcdef123456"
|
||||
|
||||
# Connect to Tailscale with automatic installation and enablement
|
||||
Connect-OPNSenseTailscale -AuthKey "tskey-auth-abcdef123456" -InstallIfMissing -EnableIfDisabled -StartIfStopped
|
||||
|
||||
# Connect to Tailscale and advertise subnet routes
|
||||
Connect-OPNSenseTailscale -AuthKey "tskey-auth-abcdef123456" -SubnetRoutes "192.168.1.0/24","10.0.0.0/8" -AdvertiseRoutes
|
||||
```
|
||||
|
||||
### Disconnect-OPNSenseTailscale
|
||||
|
||||
Disconnects from the Tailscale network.
|
||||
|
||||
#### Parameters
|
||||
|
||||
- **Force** - Suppresses the confirmation prompt.
|
||||
- **PassThru** - Returns the Tailscale status.
|
||||
|
||||
#### Examples
|
||||
|
||||
```powershell
|
||||
# Disconnect from Tailscale
|
||||
Disconnect-OPNSenseTailscale
|
||||
|
||||
# Disconnect from Tailscale without confirmation
|
||||
Disconnect-OPNSenseTailscale -Force
|
||||
```
|
||||
|
||||
## Common Scenarios
|
||||
|
||||
### Installing and Configuring Tailscale
|
||||
@@ -159,6 +208,19 @@ Apply-OPNSenseTailscaleChanges -Force
|
||||
Disconnect-OPNSense
|
||||
```
|
||||
|
||||
### Simplified Installation and Connection
|
||||
|
||||
```powershell
|
||||
# Connect to the OPNSense firewall
|
||||
Connect-OPNSense -Server "https://firewall.example.com" -ApiKey "your_api_key" -ApiSecret "your_api_secret" -SkipCertificateCheck
|
||||
|
||||
# Connect to Tailscale with automatic installation and enablement
|
||||
Connect-OPNSenseTailscale -AuthKey "tskey-auth-abcdef123456" -InstallIfMissing -EnableIfDisabled -StartIfStopped -Force
|
||||
|
||||
# Disconnect from the firewall
|
||||
Disconnect-OPNSense
|
||||
```
|
||||
|
||||
### Configuring Tailscale as a Subnet Router
|
||||
|
||||
```powershell
|
||||
@@ -177,14 +239,8 @@ foreach ($interface in $interfaces) {
|
||||
}
|
||||
}
|
||||
|
||||
# Join the subnets with commas
|
||||
$advertisedRoutes = $subnets -join ","
|
||||
|
||||
# Enable Tailscale with route advertisement
|
||||
Enable-OPNSenseTailscale -AuthKey "tskey-auth-abcdef123456" -AdvertiseRoutes $advertisedRoutes -Force
|
||||
|
||||
# Apply the changes
|
||||
Apply-OPNSenseTailscaleChanges -Force
|
||||
# Connect to Tailscale with subnet route advertisement
|
||||
Connect-OPNSenseTailscale -AuthKey "tskey-auth-abcdef123456" -SubnetRoutes $subnets -AdvertiseRoutes -InstallIfMissing -Force
|
||||
|
||||
# Disconnect from the firewall
|
||||
Disconnect-OPNSense
|
||||
@@ -206,6 +262,26 @@ Apply-OPNSenseTailscaleChanges -Force
|
||||
Disconnect-OPNSense
|
||||
```
|
||||
|
||||
### Connecting and Disconnecting from Tailscale
|
||||
|
||||
```powershell
|
||||
# Connect to the OPNSense firewall
|
||||
Connect-OPNSense -Server "https://firewall.example.com" -ApiKey "your_api_key" -ApiSecret "your_api_secret" -SkipCertificateCheck
|
||||
|
||||
# Connect to Tailscale
|
||||
Connect-OPNSenseTailscale -AuthKey "tskey-auth-abcdef123456" -InstallIfMissing -EnableIfDisabled -StartIfStopped -Force
|
||||
|
||||
# Get Tailscale status
|
||||
$status = Get-OPNSenseTailscaleStatus
|
||||
Write-Output "Tailscale Status: $($status.Status)"
|
||||
|
||||
# Disconnect from Tailscale
|
||||
Disconnect-OPNSenseTailscale -Force
|
||||
|
||||
# Disconnect from the firewall
|
||||
Disconnect-OPNSense
|
||||
```
|
||||
|
||||
### Updating Tailscale Routes
|
||||
|
||||
```powershell
|
||||
@@ -253,6 +329,7 @@ Disconnect-OPNSense
|
||||
|
||||
- Tailscale requires the Tailscale plugin to be installed on the OPNSense firewall.
|
||||
- The `Install-OPNSenseTailscale` cmdlet will install the plugin if it's not already installed.
|
||||
- The `Connect-OPNSenseTailscale` cmdlet can automatically install the plugin, enable Tailscale, and start the service with the `-InstallIfMissing`, `-EnableIfDisabled`, and `-StartIfStopped` parameters.
|
||||
- Tailscale authentication keys can be generated from the Tailscale admin console.
|
||||
- Authentication keys are single-use and expire after a short period.
|
||||
- When advertising routes, ensure that the routes are valid and do not overlap with Tailscale's internal routes.
|
||||
@@ -261,3 +338,5 @@ Disconnect-OPNSense
|
||||
- The `ExitNode` option allows other Tailscale nodes to use the OPNSense firewall as an internet gateway.
|
||||
- Changes to Tailscale settings are not applied until you call `Apply-OPNSenseTailscaleChanges`.
|
||||
- Consider using the `-PassThru` parameter when updating Tailscale settings to verify the changes.
|
||||
- The `Connect-OPNSenseTailscale` cmdlet combines multiple steps (installation, enablement, and connection) into a single command.
|
||||
- The `Disconnect-OPNSenseTailscale` cmdlet disconnects from the Tailscale network but does not disable Tailscale.
|
||||
|
||||
@@ -11,6 +11,7 @@ The PSOPNSenseAPI solution is a Visual Studio solution that contains the source
|
||||
The solution contains the following projects:
|
||||
|
||||
- **PSOPNSenseAPI**: The main project containing the PowerShell module code.
|
||||
- **PSOPNSenseAPI.Tests**: The test project containing unit tests for the PowerShell module.
|
||||
|
||||
## Opening the Solution
|
||||
|
||||
@@ -56,19 +57,19 @@ If you encounter issues with the IPNetwork2 library, here are some common troubl
|
||||
|
||||
1. **Check the reference**: Ensure that the IPNetwork2 package is properly referenced in the project file.
|
||||
```xml
|
||||
<PackageReference Include="IPNetwork2" Version="2.6.618" />
|
||||
<PackageReference Include="IPNetwork2" Version="3.1.764" />
|
||||
```
|
||||
|
||||
2. **Verify the namespace**: The correct namespace for IPNetwork2 is `System.Net.IPNetwork`.
|
||||
2. **Verify the namespace**: The correct namespace for IPNetwork2 is `System.Net`.
|
||||
```csharp
|
||||
using System.Net.IPNetwork;
|
||||
using System.Net;
|
||||
```
|
||||
|
||||
3. **Check the class name**: The main class in IPNetwork2 is `IPNetwork`, not `IPNetwork2`.
|
||||
```csharp
|
||||
// Correct usage
|
||||
System.Net.IPNetwork.IPNetwork network = System.Net.IPNetwork.IPNetwork.Parse("192.168.1.0/24");
|
||||
|
||||
System.Net.IPNetwork network = System.Net.IPNetwork.Parse("192.168.1.0/24");
|
||||
|
||||
// Incorrect usage
|
||||
IPNetwork2 network = IPNetwork2.Parse("192.168.1.0/24");
|
||||
```
|
||||
@@ -100,7 +101,7 @@ When adding new features to the solution:
|
||||
2. Implement service classes in the `Services` directory
|
||||
3. Create PowerShell cmdlets in the `Cmdlets` directory
|
||||
4. Update the module manifest (`PSOPNSenseAPI.psd1`) to export the new cmdlets
|
||||
5. Add tests for the new features
|
||||
5. Add tests for the new features in the `PSOPNSenseAPI.Tests` project
|
||||
6. Update documentation
|
||||
|
||||
## Testing
|
||||
@@ -112,9 +113,14 @@ The solution includes unit tests that can be run from Visual Studio using the Te
|
||||
.\build\test.ps1
|
||||
```
|
||||
|
||||
## Continuous Integration
|
||||
## Manual Release Process
|
||||
|
||||
The project uses GitHub Actions for continuous integration. The CI workflow builds and tests the solution on every push to the main branch.
|
||||
The project uses a manual release process. To create a new release:
|
||||
|
||||
1. Update the version in the project file and module manifest
|
||||
2. Build the solution in Release mode
|
||||
3. Create a zip file of the module
|
||||
4. Create a GitHub release with the zip file attached
|
||||
|
||||
## Dependencies
|
||||
|
||||
@@ -123,10 +129,29 @@ The solution has the following dependencies:
|
||||
- PowerShellStandard.Library (5.1.1)
|
||||
- Newtonsoft.Json (13.0.3)
|
||||
- System.Net.Http (4.3.4)
|
||||
- IPNetwork2 (2.6.618)
|
||||
- IPNetwork2 (3.1.764)
|
||||
|
||||
## Notes
|
||||
|
||||
- The solution is configured to target both .NET Framework 4.7.2 and .NET Standard 2.0 to support both Windows PowerShell 5.1 and PowerShell 7+.
|
||||
- The module uses a versioning scheme of `yyyy.MM.dd.HHmm` for releases, which is automatically generated during the build process.
|
||||
- The module uses a versioning scheme of `yyyy.MM.dd.HHmm` for releases.
|
||||
- The solution includes XML documentation comments that are used to generate help content for the PowerShell cmdlets.
|
||||
- The module uses System.Reflection.Assembly::LoadBytes to load DLLs to avoid file lock issues.
|
||||
- DLLs are organized in a lib subfolder for neatness.
|
||||
|
||||
## Module Structure
|
||||
|
||||
The module has the following structure:
|
||||
|
||||
```
|
||||
PSOPNSenseAPI/
|
||||
├── lib/ # DLL files
|
||||
│ ├── Newtonsoft.Json.dll
|
||||
│ ├── PSOPNSenseAPI.dll
|
||||
│ ├── System.Net.IPNetwork.dll
|
||||
│ └── ...
|
||||
├── PSOPNSenseAPI.psd1 # Module manifest
|
||||
└── PSOPNSenseAPI.psm1 # Module script that loads DLLs
|
||||
```
|
||||
|
||||
The PSM1 file uses System.Reflection.Assembly::LoadBytes to load the DLLs from the lib folder, which avoids file lock issues that can occur when PowerShell loads DLLs directly.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@{
|
||||
RootModule = 'PSOPNSenseAPI.dll'
|
||||
ModuleVersion = '2025.04.14.2246'
|
||||
RootModule = 'PSOPNSenseAPI.psm1'
|
||||
ModuleVersion = '2025.04.14.2340'
|
||||
GUID = '1f0e4b77-cc7c-4a1e-b45a-d7c51a3c562e'
|
||||
Author = 'PSOPNSenseAPI Contributors'
|
||||
CompanyName = 'PSOPNSenseAPI'
|
||||
@@ -12,65 +12,97 @@
|
||||
CLRVersion = '4.0.0'
|
||||
FunctionsToExport = @()
|
||||
CmdletsToExport = @(
|
||||
'Connect-OPNSense',
|
||||
'Disconnect-OPNSense',
|
||||
'Get-OPNSenseInterface',
|
||||
'Set-OPNSenseInterface',
|
||||
'Restart-OPNSenseInterface',
|
||||
'Get-OPNSenseInterfaceStatistics',
|
||||
'Get-OPNSenseVLAN',
|
||||
'New-OPNSenseVLAN',
|
||||
'Set-OPNSenseVLAN',
|
||||
'Remove-OPNSenseVLAN',
|
||||
'Get-OPNSenseFirewallRule',
|
||||
'New-OPNSenseFirewallRule',
|
||||
'Set-OPNSenseFirewallRule',
|
||||
'Remove-OPNSenseFirewallRule',
|
||||
'Enable-OPNSenseFirewallRule',
|
||||
'Disable-OPNSenseFirewallRule',
|
||||
'Get-OPNSenseAlias',
|
||||
'New-OPNSenseAlias',
|
||||
'Set-OPNSenseAlias',
|
||||
'Remove-OPNSenseAlias',
|
||||
'Get-OPNSenseNATRule',
|
||||
'New-OPNSenseNATRule',
|
||||
'Set-OPNSenseNATRule',
|
||||
'Remove-OPNSenseNATRule',
|
||||
'Get-OPNSensePlugin',
|
||||
'Enable-OPNSensePlugin',
|
||||
'Disable-OPNSensePlugin',
|
||||
'Install-OPNSensePlugin',
|
||||
'Uninstall-OPNSensePlugin',
|
||||
'Get-OPNSenseUser',
|
||||
'New-OPNSenseUser',
|
||||
'Set-OPNSenseUser',
|
||||
'Remove-OPNSenseUser',
|
||||
'Get-OPNSenseFirmware',
|
||||
'Update-OPNSenseFirmware',
|
||||
'Upgrade-OPNSenseFirmware',
|
||||
'Restart-OPNSenseFirewall',
|
||||
'New-OPNSenseSubnetVLANs',
|
||||
'Get-OPNSenseDHCPServer',
|
||||
'Set-OPNSenseDHCPServer',
|
||||
'Get-OPNSenseDHCPStaticMapping',
|
||||
'New-OPNSenseDHCPStaticMapping',
|
||||
'Set-OPNSenseDHCPStaticMapping',
|
||||
'Remove-OPNSenseDHCPStaticMapping',
|
||||
'Get-OPNSenseCronJob',
|
||||
'New-OPNSenseCronJob',
|
||||
'Set-OPNSenseCronJob',
|
||||
'Remove-OPNSenseCronJob',
|
||||
'Enable-OPNSenseCronJob',
|
||||
'Disable-OPNSenseCronJob',
|
||||
'Get-OPNSenseTailscale',
|
||||
'Enable-OPNSenseTailscale',
|
||||
'Disable-OPNSenseTailscale',
|
||||
'Connect-OPNSenseTailscale',
|
||||
'Disconnect-OPNSenseTailscale',
|
||||
'Get-OPNSensePortForwardingRule',
|
||||
'New-OPNSensePortForwardingRule',
|
||||
'Set-OPNSensePortForwardingRule',
|
||||
'Remove-OPNSensePortForwardingRule'
|
||||
'ApplyOPNSenseFirewallChanges',
|
||||
'BackupOPNSenseConfig',
|
||||
'ConnectOPNSense',
|
||||
'ConnectOPNSenseTailscale',
|
||||
'ConvertToOPNSenseNetworkNotation',
|
||||
'DisableOPNSenseCronJob',
|
||||
'DisableOPNSenseFirewallRule',
|
||||
'DisableOPNSensePlugin',
|
||||
'DisableOPNSenseTailscale',
|
||||
'DisconnectOPNSense',
|
||||
'DisconnectOPNSenseTailscale',
|
||||
'EnableOPNSenseCronJob',
|
||||
'EnableOPNSenseFirewallRule',
|
||||
'EnableOPNSensePlugin',
|
||||
'EnableOPNSenseTailscale',
|
||||
'ExportOPNSenseConfig',
|
||||
'GetOPNSenseAlias',
|
||||
'GetOPNSenseConfigBackup',
|
||||
'GetOPNSenseConnection',
|
||||
'GetOPNSenseCronJob',
|
||||
'GetOPNSenseDHCPLease',
|
||||
'GetOPNSenseDHCPOption',
|
||||
'GetOPNSenseDHCPServer',
|
||||
'GetOPNSenseDHCPStaticMapping',
|
||||
'GetOPNSenseDNSForwarding',
|
||||
'GetOPNSenseDNSForwardingHost',
|
||||
'GetOPNSenseDNSOverride',
|
||||
'GetOPNSenseDNSServer',
|
||||
'GetOPNSenseFirewallRule',
|
||||
'GetOPNSenseFirmware',
|
||||
'GetOPNSenseGateway',
|
||||
'GetOPNSenseInterface',
|
||||
'GetOPNSenseInterfaceStatistics',
|
||||
'GetOPNSensePlugin',
|
||||
'GetOPNSensePortForwardingRule',
|
||||
'GetOPNSenseRoute',
|
||||
'GetOPNSenseSystemDNS',
|
||||
'GetOPNSenseTailscaleStatus',
|
||||
'GetOPNSenseUser',
|
||||
'GetOPNSenseVLAN',
|
||||
'ImportOPNSenseConfig',
|
||||
'InstallOPNSensePlugin',
|
||||
'InvokeOPNSenseNetworkCalculation',
|
||||
'NewOPNSenseAlias',
|
||||
'NewOPNSenseCronJob',
|
||||
'NewOPNSenseDHCPOption',
|
||||
'NewOPNSenseDHCPStaticMapping',
|
||||
'NewOPNSenseDNSForwardingHost',
|
||||
'NewOPNSenseDNSOverride',
|
||||
'NewOPNSenseFirewallRule',
|
||||
'NewOPNSenseGateway',
|
||||
'NewOPNSensePortForwardingRule',
|
||||
'NewOPNSenseRoute',
|
||||
'NewOPNSenseSubnetVLANs',
|
||||
'NewOPNSenseUser',
|
||||
'NewOPNSenseVLAN',
|
||||
'RemoveOPNSenseAlias',
|
||||
'RemoveOPNSenseCronJob',
|
||||
'RemoveOPNSenseDHCPLease',
|
||||
'RemoveOPNSenseDHCPOption',
|
||||
'RemoveOPNSenseDHCPStaticMapping',
|
||||
'RemoveOPNSenseDNSForwardingHost',
|
||||
'RemoveOPNSenseDNSOverride',
|
||||
'RemoveOPNSenseFirewallRule',
|
||||
'RemoveOPNSenseGateway',
|
||||
'RemoveOPNSensePortForwardingRule',
|
||||
'RemoveOPNSenseRoute',
|
||||
'RemoveOPNSenseUser',
|
||||
'RemoveOPNSenseVLAN',
|
||||
'RestartOPNSenseFirewall',
|
||||
'RestartOPNSenseInterface',
|
||||
'RestoreOPNSenseConfig',
|
||||
'SetOPNSenseAlias',
|
||||
'SetOPNSenseCronJob',
|
||||
'SetOPNSenseDHCPOption',
|
||||
'SetOPNSenseDHCPServer',
|
||||
'SetOPNSenseDHCPStaticMapping',
|
||||
'SetOPNSenseDNSForwarding',
|
||||
'SetOPNSenseDNSForwardingHost',
|
||||
'SetOPNSenseDNSServer',
|
||||
'SetOPNSenseFirewallRule',
|
||||
'SetOPNSenseGateway',
|
||||
'SetOPNSenseInterface',
|
||||
'SetOPNSensePortForwardingRule',
|
||||
'SetOPNSenseRoute',
|
||||
'SetOPNSenseSystemDNS',
|
||||
'SetOPNSenseUser',
|
||||
'SetOPNSenseVLAN',
|
||||
'UninstallOPNSensePlugin',
|
||||
'UpdateOPNSenseFirmware',
|
||||
'UpgradeOPNSenseFirmware'
|
||||
)
|
||||
VariablesToExport = @()
|
||||
AliasesToExport = @()
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# Load assemblies from the lib folder using System.Reflection.Assembly::LoadBytes
|
||||
# This avoids file lock issues when the module is loaded
|
||||
|
||||
$libPath = Join-Path $PSScriptRoot "lib"
|
||||
$dllFiles = Get-ChildItem -Path $libPath -Filter "*.dll"
|
||||
|
||||
foreach ($dll in $dllFiles) {
|
||||
try {
|
||||
$dllBytes = [System.IO.File]::ReadAllBytes($dll.FullName)
|
||||
$Null = [System.Reflection.Assembly]::Load($dllBytes)
|
||||
Write-Verbose "Attempting to load assembly: $($dll.FullName)"
|
||||
}
|
||||
catch {
|
||||
Write-Warning "Failed to load assembly $($dll.Name): $_"
|
||||
}
|
||||
}
|
||||
|
||||
# Export all cmdlets
|
||||
$cmdlets = [System.AppDomain]::CurrentDomain.GetAssemblies() |
|
||||
Where-Object { $_.FullName -like "*PSOPNSenseAPI*" } |
|
||||
ForEach-Object { $_.GetTypes() } |
|
||||
Where-Object { $_.IsPublic -and $_.IsClass -and $_.Name -like "*Cmdlet" } |
|
||||
ForEach-Object { $_.Name }
|
||||
|
||||
Export-ModuleMember -Cmdlet $cmdlets
|
||||
Binary file not shown.
@@ -0,0 +1,12 @@
|
||||
## What's New
|
||||
|
||||
### Added
|
||||
- Port forwarding management (Get, New, Set, Remove)
|
||||
- Added unit tests for interface management
|
||||
|
||||
### Changed
|
||||
- Updated IPNetwork2 library from version 2.6.618 to 3.1.764
|
||||
- Updated code to use the new namespace (System.Net) for IPNetwork2
|
||||
- Added support for .NET 8.0 and .NET 9.0
|
||||
- Improved XML documentation for all classes and methods
|
||||
- Made OPNSenseApiClient more testable by introducing an interface
|
||||
@@ -4,7 +4,7 @@
|
||||
<TargetFrameworks>netstandard2.0;net472</TargetFrameworks>
|
||||
<AssemblyName>PSOPNSenseAPI</AssemblyName>
|
||||
<RootNamespace>PSOPNSenseAPI</RootNamespace>
|
||||
<Version>2025.04.14.2246</Version>
|
||||
<Version>2025.04.14.2340</Version>
|
||||
<Authors>PSOPNSenseAPI Contributors</Authors>
|
||||
<Company>PSOPNSenseAPI</Company>
|
||||
<Description>PowerShell module for interacting with the OPNSense API to configure firewalls</Description>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
@{
|
||||
# Script module or binary module file associated with this manifest.
|
||||
RootModule = 'PSOPNSenseAPI.dll'
|
||||
RootModule = 'PSOPNSenseAPI.psm1'
|
||||
|
||||
# Version number of this module.
|
||||
ModuleVersion = '0.5.0'
|
||||
ModuleVersion = '2025.04.14.2340'
|
||||
|
||||
# Supported PSEditions
|
||||
CompatiblePSEditions = @('Desktop', 'Core')
|
||||
@@ -45,7 +45,7 @@
|
||||
# RequiredModules = @()
|
||||
|
||||
# Assemblies that must be loaded prior to importing this module
|
||||
RequiredAssemblies = @('System.Net.Http', 'Newtonsoft.Json', 'System.Net.IPNetwork')
|
||||
# RequiredAssemblies = @()
|
||||
|
||||
# Script files (.ps1) that are run in the caller's environment prior to importing this module.
|
||||
# ScriptsToProcess = @()
|
||||
@@ -64,127 +64,101 @@
|
||||
|
||||
# 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 = @(
|
||||
# Connection
|
||||
'Connect-OPNSense',
|
||||
'Disconnect-OPNSense',
|
||||
|
||||
# Firewall Rules
|
||||
'Get-OPNSenseFirewallRule',
|
||||
'New-OPNSenseFirewallRule',
|
||||
'Set-OPNSenseFirewallRule',
|
||||
'Remove-OPNSenseFirewallRule',
|
||||
'Enable-OPNSenseFirewallRule',
|
||||
'Disable-OPNSenseFirewallRule',
|
||||
'Apply-OPNSenseFirewallChanges',
|
||||
|
||||
# Aliases
|
||||
'Get-OPNSenseAlias',
|
||||
'New-OPNSenseAlias',
|
||||
'Set-OPNSenseAlias',
|
||||
'Remove-OPNSenseAlias',
|
||||
|
||||
# Interfaces
|
||||
'Get-OPNSenseInterface',
|
||||
'Set-OPNSenseInterface',
|
||||
'Get-OPNSenseVLAN',
|
||||
'New-OPNSenseVLAN',
|
||||
'Remove-OPNSenseVLAN',
|
||||
'New-OPNSenseSubnetVLANs',
|
||||
|
||||
# DNS
|
||||
'Get-OPNSenseDNSServer',
|
||||
'Set-OPNSenseDNSServer',
|
||||
'Get-OPNSenseDNSOverride',
|
||||
'New-OPNSenseDNSOverride',
|
||||
'Remove-OPNSenseDNSOverride',
|
||||
'Get-OPNSenseDNSForwarding',
|
||||
'Set-OPNSenseDNSForwarding',
|
||||
'Get-OPNSenseDNSForwardingHost',
|
||||
'New-OPNSenseDNSForwardingHost',
|
||||
'Set-OPNSenseDNSForwardingHost',
|
||||
'Remove-OPNSenseDNSForwardingHost',
|
||||
'Get-OPNSenseSystemDNS',
|
||||
'Set-OPNSenseSystemDNS',
|
||||
|
||||
# Configuration
|
||||
'Backup-OPNSenseConfig',
|
||||
'Restore-OPNSenseConfig',
|
||||
'Export-OPNSenseConfig',
|
||||
'Import-OPNSenseConfig',
|
||||
|
||||
# Plugins
|
||||
'Get-OPNSensePlugin',
|
||||
'Install-OPNSensePlugin',
|
||||
'Uninstall-OPNSensePlugin',
|
||||
'Enable-OPNSensePlugin',
|
||||
'Disable-OPNSensePlugin',
|
||||
|
||||
# Users
|
||||
'Get-OPNSenseUser',
|
||||
'New-OPNSenseUser',
|
||||
'Set-OPNSenseUser',
|
||||
'Remove-OPNSenseUser',
|
||||
|
||||
# Firmware
|
||||
'Get-OPNSenseFirmware',
|
||||
'Update-OPNSenseFirmware',
|
||||
'Upgrade-OPNSenseFirmware',
|
||||
|
||||
# System
|
||||
'Restart-OPNSenseFirewall',
|
||||
|
||||
# DHCP
|
||||
'Get-OPNSenseDHCPServer',
|
||||
'Set-OPNSenseDHCPServer',
|
||||
'Get-OPNSenseDHCPLease',
|
||||
'Remove-OPNSenseDHCPLease',
|
||||
'Get-OPNSenseDHCPStaticMapping',
|
||||
'New-OPNSenseDHCPStaticMapping',
|
||||
'Set-OPNSenseDHCPStaticMapping',
|
||||
'Remove-OPNSenseDHCPStaticMapping',
|
||||
'Get-OPNSenseDHCPOption',
|
||||
'New-OPNSenseDHCPOption',
|
||||
'Set-OPNSenseDHCPOption',
|
||||
'Remove-OPNSenseDHCPOption',
|
||||
|
||||
# Cron
|
||||
'Get-OPNSenseCronJob',
|
||||
'New-OPNSenseCronJob',
|
||||
'Set-OPNSenseCronJob',
|
||||
'Remove-OPNSenseCronJob',
|
||||
'Enable-OPNSenseCronJob',
|
||||
'Disable-OPNSenseCronJob',
|
||||
|
||||
# Tailscale
|
||||
'Get-OPNSenseTailscaleStatus',
|
||||
'Enable-OPNSenseTailscale',
|
||||
'Disable-OPNSenseTailscale',
|
||||
'Connect-OPNSenseTailscale',
|
||||
'Disconnect-OPNSenseTailscale',
|
||||
|
||||
# Network Utilities
|
||||
'Invoke-OPNSenseNetworkCalculation',
|
||||
'ConvertTo-OPNSenseNetworkNotation',
|
||||
|
||||
# Gateways and Routes
|
||||
'Get-OPNSenseGateway',
|
||||
'New-OPNSenseGateway',
|
||||
'Set-OPNSenseGateway',
|
||||
'Remove-OPNSenseGateway',
|
||||
'Get-OPNSenseRoute',
|
||||
'New-OPNSenseRoute',
|
||||
'Set-OPNSenseRoute',
|
||||
'Remove-OPNSenseRoute',
|
||||
|
||||
# Port Forwarding
|
||||
'Get-OPNSensePortForwardingRule',
|
||||
'New-OPNSensePortForwardingRule',
|
||||
'Set-OPNSensePortForwardingRule',
|
||||
'Remove-OPNSensePortForwardingRule'
|
||||
'ApplyOPNSenseFirewallChanges',
|
||||
'BackupOPNSenseConfig',
|
||||
'ConnectOPNSense',
|
||||
'ConnectOPNSenseTailscale',
|
||||
'ConvertToOPNSenseNetworkNotation',
|
||||
'DisableOPNSenseCronJob',
|
||||
'DisableOPNSenseFirewallRule',
|
||||
'DisableOPNSensePlugin',
|
||||
'DisableOPNSenseTailscale',
|
||||
'DisconnectOPNSense',
|
||||
'DisconnectOPNSenseTailscale',
|
||||
'EnableOPNSenseCronJob',
|
||||
'EnableOPNSenseFirewallRule',
|
||||
'EnableOPNSensePlugin',
|
||||
'EnableOPNSenseTailscale',
|
||||
'ExportOPNSenseConfig',
|
||||
'GetOPNSenseAlias',
|
||||
'GetOPNSenseConfigBackup',
|
||||
'GetOPNSenseConnection',
|
||||
'GetOPNSenseCronJob',
|
||||
'GetOPNSenseDHCPLease',
|
||||
'GetOPNSenseDHCPOption',
|
||||
'GetOPNSenseDHCPServer',
|
||||
'GetOPNSenseDHCPStaticMapping',
|
||||
'GetOPNSenseDNSForwarding',
|
||||
'GetOPNSenseDNSForwardingHost',
|
||||
'GetOPNSenseDNSOverride',
|
||||
'GetOPNSenseDNSServer',
|
||||
'GetOPNSenseFirewallRule',
|
||||
'GetOPNSenseFirmware',
|
||||
'GetOPNSenseGateway',
|
||||
'GetOPNSenseInterface',
|
||||
'GetOPNSenseInterfaceStatistics',
|
||||
'GetOPNSensePlugin',
|
||||
'GetOPNSensePortForwardingRule',
|
||||
'GetOPNSenseRoute',
|
||||
'GetOPNSenseSystemDNS',
|
||||
'GetOPNSenseTailscaleStatus',
|
||||
'GetOPNSenseUser',
|
||||
'GetOPNSenseVLAN',
|
||||
'ImportOPNSenseConfig',
|
||||
'InstallOPNSensePlugin',
|
||||
'InvokeOPNSenseNetworkCalculation',
|
||||
'NewOPNSenseAlias',
|
||||
'NewOPNSenseCronJob',
|
||||
'NewOPNSenseDHCPOption',
|
||||
'NewOPNSenseDHCPStaticMapping',
|
||||
'NewOPNSenseDNSForwardingHost',
|
||||
'NewOPNSenseDNSOverride',
|
||||
'NewOPNSenseFirewallRule',
|
||||
'NewOPNSenseGateway',
|
||||
'NewOPNSensePortForwardingRule',
|
||||
'NewOPNSenseRoute',
|
||||
'NewOPNSenseSubnetVLANs',
|
||||
'NewOPNSenseUser',
|
||||
'NewOPNSenseVLAN',
|
||||
'RemoveOPNSenseAlias',
|
||||
'RemoveOPNSenseCronJob',
|
||||
'RemoveOPNSenseDHCPLease',
|
||||
'RemoveOPNSenseDHCPOption',
|
||||
'RemoveOPNSenseDHCPStaticMapping',
|
||||
'RemoveOPNSenseDNSForwardingHost',
|
||||
'RemoveOPNSenseDNSOverride',
|
||||
'RemoveOPNSenseFirewallRule',
|
||||
'RemoveOPNSenseGateway',
|
||||
'RemoveOPNSensePortForwardingRule',
|
||||
'RemoveOPNSenseRoute',
|
||||
'RemoveOPNSenseUser',
|
||||
'RemoveOPNSenseVLAN',
|
||||
'RestartOPNSenseFirewall',
|
||||
'RestartOPNSenseInterface',
|
||||
'RestoreOPNSenseConfig',
|
||||
'SetOPNSenseAlias',
|
||||
'SetOPNSenseCronJob',
|
||||
'SetOPNSenseDHCPOption',
|
||||
'SetOPNSenseDHCPServer',
|
||||
'SetOPNSenseDHCPStaticMapping',
|
||||
'SetOPNSenseDNSForwarding',
|
||||
'SetOPNSenseDNSForwardingHost',
|
||||
'SetOPNSenseDNSServer',
|
||||
'SetOPNSenseFirewallRule',
|
||||
'SetOPNSenseGateway',
|
||||
'SetOPNSenseInterface',
|
||||
'SetOPNSensePortForwardingRule',
|
||||
'SetOPNSenseRoute',
|
||||
'SetOPNSenseSystemDNS',
|
||||
'SetOPNSenseUser',
|
||||
'SetOPNSenseVLAN',
|
||||
'UninstallOPNSensePlugin',
|
||||
'UpdateOPNSenseFirmware',
|
||||
'UpgradeOPNSenseFirmware'
|
||||
)
|
||||
|
||||
# Variables to export from this module
|
||||
VariablesToExport = '*'
|
||||
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 = @()
|
||||
|
||||
@@ -1,202 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using PSOPNSenseAPI.Models;
|
||||
|
||||
namespace PSOPNSenseAPI.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Service for managing port forwarding rules in OPNSense
|
||||
/// </summary>
|
||||
public class PortForwardingService : ServiceBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PortForwardingService"/> class
|
||||
/// </summary>
|
||||
/// <param name="apiClient">The API client to use for requests</param>
|
||||
public PortForwardingService(ApiClient apiClient) : base(apiClient)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all port forwarding rules
|
||||
/// </summary>
|
||||
/// <returns>A list of port forwarding rules</returns>
|
||||
public async Task<List<PortForwardingRule>> GetPortForwardingRulesAsync()
|
||||
{
|
||||
var response = await ApiClient.GetAsync("/firewall/nat/getRule");
|
||||
var responseContent = await response.Content.ReadAsStringAsync();
|
||||
var jsonResponse = JObject.Parse(responseContent);
|
||||
|
||||
if (jsonResponse["status"]?.ToString() != "ok")
|
||||
{
|
||||
throw new Exception($"Failed to get port forwarding rules: {jsonResponse["status_msg"]}");
|
||||
}
|
||||
|
||||
var rules = new List<PortForwardingRule>();
|
||||
var rulesData = jsonResponse["rows"];
|
||||
|
||||
foreach (var rule in rulesData)
|
||||
{
|
||||
var portForwardingRule = new PortForwardingRule
|
||||
{
|
||||
Uuid = rule["uuid"]?.ToString(),
|
||||
Enabled = rule["enabled"]?.ToString() == "1",
|
||||
Description = rule["descr"]?.ToString(),
|
||||
Interface = rule["interface"]?.ToString(),
|
||||
Protocol = rule["protocol"]?.ToString(),
|
||||
Source = rule["source"]?.ToString(),
|
||||
SourcePort = rule["source_port"]?.ToString(),
|
||||
Destination = rule["destination"]?.ToString(),
|
||||
DestinationPort = rule["destination_port"]?.ToString(),
|
||||
TargetIP = rule["target"]?.ToString(),
|
||||
TargetPort = rule["target_port"]?.ToString(),
|
||||
NatReflection = rule["natreflection"]?.ToString(),
|
||||
FilterRuleAssociation = rule["associated-rule-id"]?.ToString() != "none",
|
||||
Log = rule["log"]?.ToString() == "1"
|
||||
};
|
||||
|
||||
rules.Add(portForwardingRule);
|
||||
}
|
||||
|
||||
return rules;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a port forwarding rule by UUID
|
||||
/// </summary>
|
||||
/// <param name="uuid">The UUID of the rule to get</param>
|
||||
/// <returns>The port forwarding rule</returns>
|
||||
public async Task<PortForwardingRule> GetPortForwardingRuleAsync(string uuid)
|
||||
{
|
||||
var rules = await GetPortForwardingRulesAsync();
|
||||
return rules.Find(r => r.Uuid == uuid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new port forwarding rule
|
||||
/// </summary>
|
||||
/// <param name="rule">The rule to create</param>
|
||||
/// <returns>The UUID of the created rule</returns>
|
||||
public async Task<string> CreatePortForwardingRuleAsync(PortForwardingRule rule)
|
||||
{
|
||||
var ruleData = new
|
||||
{
|
||||
rule = new
|
||||
{
|
||||
enabled = rule.Enabled ? "1" : "0",
|
||||
descr = rule.Description,
|
||||
interface = rule.Interface,
|
||||
protocol = rule.Protocol,
|
||||
source = rule.Source,
|
||||
source_port = rule.SourcePort,
|
||||
destination = rule.Destination,
|
||||
destination_port = rule.DestinationPort,
|
||||
target = rule.TargetIP,
|
||||
target_port = rule.TargetPort,
|
||||
natreflection = rule.NatReflection,
|
||||
associated_rule_id = rule.FilterRuleAssociation ? "pass" : "none",
|
||||
log = rule.Log ? "1" : "0"
|
||||
}
|
||||
};
|
||||
|
||||
var response = await ApiClient.PostAsync("/firewall/nat/addRule", ruleData);
|
||||
var responseContent = await response.Content.ReadAsStringAsync();
|
||||
var jsonResponse = JObject.Parse(responseContent);
|
||||
|
||||
if (jsonResponse["status"]?.ToString() != "ok")
|
||||
{
|
||||
throw new Exception($"Failed to create port forwarding rule: {jsonResponse["status_msg"]}");
|
||||
}
|
||||
|
||||
// Apply changes
|
||||
await ApplyChangesAsync();
|
||||
|
||||
return jsonResponse["uuid"]?.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates an existing port forwarding rule
|
||||
/// </summary>
|
||||
/// <param name="rule">The rule to update</param>
|
||||
/// <returns>True if successful</returns>
|
||||
public async Task<bool> UpdatePortForwardingRuleAsync(PortForwardingRule rule)
|
||||
{
|
||||
if (string.IsNullOrEmpty(rule.Uuid))
|
||||
{
|
||||
throw new ArgumentException("Rule UUID cannot be null or empty", nameof(rule));
|
||||
}
|
||||
|
||||
var ruleData = new
|
||||
{
|
||||
rule = new
|
||||
{
|
||||
enabled = rule.Enabled ? "1" : "0",
|
||||
descr = rule.Description,
|
||||
interface = rule.Interface,
|
||||
protocol = rule.Protocol,
|
||||
source = rule.Source,
|
||||
source_port = rule.SourcePort,
|
||||
destination = rule.Destination,
|
||||
destination_port = rule.DestinationPort,
|
||||
target = rule.TargetIP,
|
||||
target_port = rule.TargetPort,
|
||||
natreflection = rule.NatReflection,
|
||||
associated_rule_id = rule.FilterRuleAssociation ? "pass" : "none",
|
||||
log = rule.Log ? "1" : "0"
|
||||
}
|
||||
};
|
||||
|
||||
var response = await ApiClient.PostAsync($"/firewall/nat/setRule/{rule.Uuid}", ruleData);
|
||||
var responseContent = await response.Content.ReadAsStringAsync();
|
||||
var jsonResponse = JObject.Parse(responseContent);
|
||||
|
||||
if (jsonResponse["status"]?.ToString() != "ok")
|
||||
{
|
||||
throw new Exception($"Failed to update port forwarding rule: {jsonResponse["status_msg"]}");
|
||||
}
|
||||
|
||||
// Apply changes
|
||||
await ApplyChangesAsync();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a port forwarding rule
|
||||
/// </summary>
|
||||
/// <param name="uuid">The UUID of the rule to delete</param>
|
||||
/// <returns>True if successful</returns>
|
||||
public async Task<bool> DeletePortForwardingRuleAsync(string uuid)
|
||||
{
|
||||
var response = await ApiClient.PostAsync($"/firewall/nat/delRule/{uuid}", null);
|
||||
var responseContent = await response.Content.ReadAsStringAsync();
|
||||
var jsonResponse = JObject.Parse(responseContent);
|
||||
|
||||
if (jsonResponse["status"]?.ToString() != "ok")
|
||||
{
|
||||
throw new Exception($"Failed to delete port forwarding rule: {jsonResponse["status_msg"]}");
|
||||
}
|
||||
|
||||
// Apply changes
|
||||
await ApplyChangesAsync();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the changes to the firewall
|
||||
/// </summary>
|
||||
/// <returns>True if successful</returns>
|
||||
private async Task<bool> ApplyChangesAsync()
|
||||
{
|
||||
var response = await ApiClient.PostAsync("/firewall/nat/apply", null);
|
||||
var responseContent = await response.Content.ReadAsStringAsync();
|
||||
var jsonResponse = JObject.Parse(responseContent);
|
||||
|
||||
return jsonResponse["status"]?.ToString() == "ok";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,289 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using PSOPNSenseAPI.Logging;
|
||||
using PSOPNSenseAPI.Models;
|
||||
|
||||
namespace PSOPNSenseAPI.Services
|
||||
{
|
||||
public class InterfaceService
|
||||
{
|
||||
private readonly OPNSenseApiClient _apiClient;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public InterfaceService(OPNSenseApiClient apiClient, ILogger logger)
|
||||
{
|
||||
_apiClient = apiClient;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<InterfaceListResponse> GetInterfacesAsync()
|
||||
{
|
||||
_logger.Information("Getting interfaces");
|
||||
|
||||
var endpoint = "interfaces/overview/searchInterfaces";
|
||||
return await _apiClient.GetAsync<InterfaceListResponse>(endpoint);
|
||||
}
|
||||
|
||||
public async Task<InterfaceDetailResponse> GetInterfaceDetailAsync(string interfaceName)
|
||||
{
|
||||
_logger.Information($"Getting interface {interfaceName}");
|
||||
|
||||
var endpoint = $"interfaces/overview/getInterface/{interfaceName}";
|
||||
return await _apiClient.GetAsync<InterfaceDetailResponse>(endpoint);
|
||||
}
|
||||
|
||||
public async Task<InterfaceStatisticsResponse> GetInterfaceStatisticsAsync()
|
||||
{
|
||||
_logger.Information("Getting interface statistics");
|
||||
|
||||
var endpoint = "diagnostics/interface/getInterfaceStatistics";
|
||||
return await _apiClient.GetAsync<InterfaceStatisticsResponse>(endpoint);
|
||||
}
|
||||
|
||||
public async Task<InterfaceUpdateResponse> UpdateInterfaceAsync(string interfaceName, InterfaceConfig interfaceConfig)
|
||||
{
|
||||
_logger.Information($"Updating interface {interfaceName}");
|
||||
|
||||
var endpoint = $"interfaces/overview/setInterface/{interfaceName}";
|
||||
var data = new { interface = interfaceConfig };
|
||||
return await _apiClient.PostAsync<InterfaceUpdateResponse>(endpoint, data);
|
||||
}
|
||||
|
||||
public async Task<InterfaceRestartResponse> RestartInterfaceAsync(string interfaceName)
|
||||
{
|
||||
_logger.Information($"Restarting interface {interfaceName}");
|
||||
|
||||
var endpoint = $"interfaces/overview/reconfigure/{interfaceName}";
|
||||
return await _apiClient.PostAsync<InterfaceRestartResponse>(endpoint);
|
||||
}
|
||||
|
||||
public async Task<VLANListResponse> GetVLANsAsync()
|
||||
{
|
||||
_logger.Information("Getting VLANs");
|
||||
|
||||
var endpoint = "interfaces/vlan/searchItem";
|
||||
return await _apiClient.GetAsync<VLANListResponse>(endpoint);
|
||||
}
|
||||
|
||||
public async Task<VLANResponse> GetVLANAsync(string uuid)
|
||||
{
|
||||
_logger.Information($"Getting VLAN with UUID {uuid}");
|
||||
|
||||
var endpoint = $"interfaces/vlan/getItem/{uuid}";
|
||||
return await _apiClient.GetAsync<VLANResponse>(endpoint);
|
||||
}
|
||||
|
||||
public async Task<VLANCreateResponse> CreateVLANAsync(VLANConfig vlan)
|
||||
{
|
||||
_logger.Information("Creating VLAN");
|
||||
|
||||
var endpoint = "interfaces/vlan/addItem";
|
||||
var data = new { vlan = vlan };
|
||||
return await _apiClient.PostAsync<VLANCreateResponse>(endpoint, data);
|
||||
}
|
||||
|
||||
public async Task<VLANUpdateResponse> UpdateVLANAsync(string uuid, VLANConfig vlan)
|
||||
{
|
||||
_logger.Information($"Updating VLAN with UUID {uuid}");
|
||||
|
||||
var endpoint = $"interfaces/vlan/setItem/{uuid}";
|
||||
var data = new { vlan = vlan };
|
||||
return await _apiClient.PostAsync<VLANUpdateResponse>(endpoint, data);
|
||||
}
|
||||
|
||||
public async Task<VLANDeleteResponse> DeleteVLANAsync(string uuid)
|
||||
{
|
||||
_logger.Information($"Deleting VLAN with UUID {uuid}");
|
||||
|
||||
var endpoint = $"interfaces/vlan/delItem/{uuid}";
|
||||
return await _apiClient.PostAsync<VLANDeleteResponse>(endpoint);
|
||||
}
|
||||
}
|
||||
|
||||
public class InterfaceListResponse
|
||||
{
|
||||
[JsonProperty("rows")]
|
||||
public List<InterfaceInfo> Interfaces { get; set; }
|
||||
}
|
||||
|
||||
public class InterfaceInfo
|
||||
{
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("description")]
|
||||
public string Description { get; set; }
|
||||
|
||||
[JsonProperty("ipaddr")]
|
||||
public string IpAddress { get; set; }
|
||||
|
||||
[JsonProperty("subnet")]
|
||||
public string SubnetMask { get; set; }
|
||||
|
||||
[JsonProperty("gateway")]
|
||||
public string Gateway { get; set; }
|
||||
|
||||
[JsonProperty("status")]
|
||||
public string Status { get; set; }
|
||||
}
|
||||
|
||||
public class InterfaceDetailResponse
|
||||
{
|
||||
[JsonProperty("interface")]
|
||||
public InterfaceDetail Interface { get; set; }
|
||||
}
|
||||
|
||||
public class InterfaceDetail
|
||||
{
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("description")]
|
||||
public string Description { get; set; }
|
||||
|
||||
[JsonProperty("ipaddr")]
|
||||
public string IpAddress { get; set; }
|
||||
|
||||
[JsonProperty("subnet")]
|
||||
public string SubnetMask { get; set; }
|
||||
|
||||
[JsonProperty("gateway")]
|
||||
public string Gateway { get; set; }
|
||||
|
||||
[JsonProperty("enable")]
|
||||
public string Enabled { get; set; }
|
||||
}
|
||||
|
||||
public class InterfaceConfig
|
||||
{
|
||||
[JsonProperty("description")]
|
||||
public string Description { get; set; }
|
||||
|
||||
[JsonProperty("ipaddr")]
|
||||
public string IpAddress { get; set; }
|
||||
|
||||
[JsonProperty("subnet")]
|
||||
public string SubnetMask { get; set; }
|
||||
|
||||
[JsonProperty("gateway")]
|
||||
public string Gateway { get; set; }
|
||||
|
||||
[JsonProperty("enable")]
|
||||
public string Enabled { get; set; }
|
||||
}
|
||||
|
||||
public class InterfaceUpdateResponse
|
||||
{
|
||||
[JsonProperty("result")]
|
||||
public string Result { get; set; }
|
||||
}
|
||||
|
||||
public class InterfaceRestartResponse
|
||||
{
|
||||
[JsonProperty("status")]
|
||||
public string Status { get; set; }
|
||||
}
|
||||
|
||||
public class InterfaceStatisticsResponse
|
||||
{
|
||||
[JsonProperty("statistics")]
|
||||
public Dictionary<string, InterfaceStatistics> Statistics { get; set; }
|
||||
}
|
||||
|
||||
public class InterfaceStatistics
|
||||
{
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("inpkts")]
|
||||
public string InPackets { get; set; }
|
||||
|
||||
[JsonProperty("outpkts")]
|
||||
public string OutPackets { get; set; }
|
||||
|
||||
[JsonProperty("inbytes")]
|
||||
public string InBytes { get; set; }
|
||||
|
||||
[JsonProperty("outbytes")]
|
||||
public string OutBytes { get; set; }
|
||||
}
|
||||
|
||||
public class VLANListResponse
|
||||
{
|
||||
[JsonProperty("rows")]
|
||||
public List<VLAN> Rows { get; set; }
|
||||
}
|
||||
|
||||
public class VLAN
|
||||
{
|
||||
[JsonProperty("uuid")]
|
||||
public string Uuid { get; set; }
|
||||
|
||||
[JsonProperty("if")]
|
||||
public string Interface { get; set; }
|
||||
|
||||
[JsonProperty("tag")]
|
||||
public string Tag { get; set; }
|
||||
|
||||
[JsonProperty("pcp")]
|
||||
public string Priority { get; set; }
|
||||
|
||||
[JsonProperty("descr")]
|
||||
public string Description { get; set; }
|
||||
}
|
||||
|
||||
public class VLANResponse
|
||||
{
|
||||
[JsonProperty("vlan")]
|
||||
public VLANDetail Vlan { get; set; }
|
||||
}
|
||||
|
||||
public class VLANDetail
|
||||
{
|
||||
[JsonProperty("if")]
|
||||
public string Interface { get; set; }
|
||||
|
||||
[JsonProperty("tag")]
|
||||
public string Tag { get; set; }
|
||||
|
||||
[JsonProperty("pcp")]
|
||||
public string Priority { get; set; }
|
||||
|
||||
[JsonProperty("descr")]
|
||||
public string Description { get; set; }
|
||||
}
|
||||
|
||||
public class VLANConfig
|
||||
{
|
||||
[JsonProperty("if")]
|
||||
public string Interface { get; set; }
|
||||
|
||||
[JsonProperty("tag")]
|
||||
public string Tag { get; set; }
|
||||
|
||||
[JsonProperty("pcp")]
|
||||
public string Priority { get; set; }
|
||||
|
||||
[JsonProperty("descr")]
|
||||
public string Description { get; set; }
|
||||
}
|
||||
|
||||
public class VLANCreateResponse
|
||||
{
|
||||
[JsonProperty("uuid")]
|
||||
public string Uuid { get; set; }
|
||||
}
|
||||
|
||||
public class VLANUpdateResponse
|
||||
{
|
||||
[JsonProperty("result")]
|
||||
public string Result { get; set; }
|
||||
}
|
||||
|
||||
public class VLANDeleteResponse
|
||||
{
|
||||
[JsonProperty("result")]
|
||||
public string Result { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -11,9 +11,9 @@ namespace PSOPNSenseAPI.Tests
|
||||
[TestClass]
|
||||
public class InterfaceServiceTests
|
||||
{
|
||||
private Mock<IOPNSenseApiClient> _mockApiClient;
|
||||
private Mock<ILogger> _mockLogger;
|
||||
private InterfaceService _interfaceService;
|
||||
private Mock<IOPNSenseApiClient>? _mockApiClient;
|
||||
private Mock<ILogger>? _mockLogger;
|
||||
private InterfaceService? _interfaceService;
|
||||
|
||||
[TestInitialize]
|
||||
public void Setup()
|
||||
@@ -43,11 +43,11 @@ namespace PSOPNSenseAPI.Tests
|
||||
}
|
||||
};
|
||||
|
||||
_mockApiClient.Setup(x => x.GetAsync<InterfaceListResponse>("interfaces/overview/searchInterfaces"))
|
||||
_mockApiClient!.Setup(x => x.GetAsync<InterfaceListResponse>("interfaces/overview/searchInterfaces"))
|
||||
.ReturnsAsync(response);
|
||||
|
||||
// Act
|
||||
var result = await _interfaceService.GetInterfacesAsync();
|
||||
var result = await _interfaceService!.GetInterfacesAsync();
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(result);
|
||||
@@ -78,11 +78,11 @@ namespace PSOPNSenseAPI.Tests
|
||||
}
|
||||
};
|
||||
|
||||
_mockApiClient.Setup(x => x.GetAsync<InterfaceDetailResponse>($"interfaces/overview/getInterface/{interfaceName}"))
|
||||
_mockApiClient!.Setup(x => x.GetAsync<InterfaceDetailResponse>($"interfaces/overview/getInterface/{interfaceName}"))
|
||||
.ReturnsAsync(response);
|
||||
|
||||
// Act
|
||||
var result = await _interfaceService.GetInterfaceDetailAsync(interfaceName);
|
||||
var result = await _interfaceService!.GetInterfaceDetailAsync(interfaceName);
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(result);
|
||||
@@ -114,11 +114,11 @@ namespace PSOPNSenseAPI.Tests
|
||||
Result = "saved"
|
||||
};
|
||||
|
||||
_mockApiClient.Setup(x => x.PostAsync<InterfaceUpdateResponse>($"interfaces/overview/setInterface/{interfaceName}", It.IsAny<object>()))
|
||||
_mockApiClient!.Setup(x => x.PostAsync<InterfaceUpdateResponse>($"interfaces/overview/setInterface/{interfaceName}", It.IsAny<object>()))
|
||||
.ReturnsAsync(response);
|
||||
|
||||
// Act
|
||||
var result = await _interfaceService.UpdateInterfaceAsync(interfaceName, interfaceConfig);
|
||||
var result = await _interfaceService!.UpdateInterfaceAsync(interfaceName, interfaceConfig);
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(result);
|
||||
|
||||
Reference in New Issue
Block a user