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

Using Pode as a Service in Windows, MacOSX and Linux (Systemd) #1421

Open
wants to merge 46 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
f264e07
First drop
mdaneri Oct 16, 2024
d42b58d
Merge branch 'develop' into service
mdaneri Oct 16, 2024
f965cbf
Integrated in Pode
mdaneri Oct 16, 2024
2791504
update
mdaneri Oct 17, 2024
d60d1cc
Update to linux, Mac
mdaneri Oct 17, 2024
3c3a5cd
fix Mac detection
mdaneri Oct 17, 2024
430ea27
FIx Mac service
mdaneri Oct 17, 2024
b87561e
Fix Windows
mdaneri Oct 18, 2024
6c51a48
Fix tests
mdaneri Oct 18, 2024
0091321
Remove settingsfile with unregister
mdaneri Oct 18, 2024
e29752e
fixes
mdaneri Oct 18, 2024
99f5109
Merge branch 'develop' into service
mdaneri Oct 19, 2024
9d90c78
Add UAC support
mdaneri Oct 19, 2024
2ca3621
FIx Mac service
mdaneri Oct 19, 2024
c3073d2
Merge remote-tracking branch 'upstream/develop' into service
mdaneri Oct 20, 2024
65b28d6
Merge remote-tracking branch 'upstream/develop' into service
mdaneri Oct 21, 2024
1f55857
Update Pode.psd1
mdaneri Oct 21, 2024
97d8e3c
fix linux 1
mdaneri Oct 21, 2024
734f6b3
fixes
mdaneri Oct 21, 2024
36e14cc
fix
mdaneri Oct 21, 2024
9e76fbb
fix service path
mdaneri Oct 21, 2024
5c0c33d
fixes
mdaneri Oct 21, 2024
1e0cf3b
Add logs
mdaneri Oct 22, 2024
b19643d
Update Service.ps1
mdaneri Oct 22, 2024
8a314ea
Code completed
mdaneri Oct 22, 2024
00c5252
remove spaces between function Register-PodeService and header
mdaneri Oct 22, 2024
302bb09
reinstated create user
mdaneri Oct 22, 2024
a67b3c3
fixes
mdaneri Oct 22, 2024
afcd5c1
fixes
mdaneri Oct 22, 2024
9326e32
fix the example
mdaneri Oct 22, 2024
88246e8
Merge branch 'develop' into service
mdaneri Oct 23, 2024
78e50f2
minor log fixex
mdaneri Oct 23, 2024
d79fe5f
Merge remote-tracking branch 'upstream/develop' into service
mdaneri Oct 23, 2024
fc3e1c3
MacOS improvements
mdaneri Oct 23, 2024
b113821
Merge remote-tracking branch 'upstream/develop' into service
mdaneri Oct 27, 2024
49b45c0
Merge branch 'develop' into service
mdaneri Oct 28, 2024
2e6b153
Merge branch 'develop' into service
mdaneri Oct 30, 2024
96cc8a3
Merge remote-tracking branch 'upstream/develop' into service
mdaneri Nov 2, 2024
b3579ea
Merge branch 'develop' into service
mdaneri Nov 3, 2024
6cc2da7
Merge branch 'develop' into service
mdaneri Nov 3, 2024
756853e
Merge branch 'develop' into service
mdaneri Nov 6, 2024
87254b3
add suspend ,resume
mdaneri Nov 17, 2024
ebf36a2
Improvements
mdaneri Nov 17, 2024
c11eb7f
revert to net8
mdaneri Nov 17, 2024
4996642
improvements
mdaneri Nov 17, 2024
93ca614
Added comments
mdaneri Nov 17, 2024
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -266,3 +266,5 @@ examples/PetStore/data/PetData.json
packers/choco/pode.nuspec
packers/choco/tools/ChocolateyInstall.ps1
docs/Getting-Started/Samples.md
examples/HelloService/*_svcsettings.json
examples/HelloService/svc_settings
2 changes: 2 additions & 0 deletions Pode.sln
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{41F81369-868
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pode", "src\Listener\Pode.csproj", "{772D5C9F-1B25-46A7-8977-412A5F7F77D1}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PodeMonitor", "src\PodePwshMonitor\PodeMonitor.csproj", "{A927D6A5-A2AC-471A-9ABA-45916B597EB6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down
55 changes: 55 additions & 0 deletions docs/Hosting/PortsBelow1024.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Using Ports Below 1024

#### Introduction

Traditionally in Linux, binding to ports below 1024 requires root privileges. This is a security measure, as these low-numbered ports are considered privileged. However, running applications as the root user poses significant security risks. This article explores methods to use these privileged ports with PowerShell (`pwsh`) in Linux, without running it as the root user.
There are different methods to achieve the goals.
Reverse Proxy is the right approach for a production environment, primarily if the server is connected directly to the internet.
The other solutions are reasonable after an in-depth risk analysis.

#### Using a Reverse Proxy

A reverse proxy like Nginx can listen on the privileged port and forward requests to your application running on an unprivileged port.

**Configuration:**

* Configure Nginx to listen on port 443 and forward requests to the port where your PowerShell script is listening.
* This method is widely used in web applications for its additional benefits like load balancing and SSL termination.

#### iptables Redirection

Using iptables, you can redirect traffic from a privileged port to a higher, unprivileged port.

**Implementation:**

* Set up an iptables rule to redirect traffic from, say, port 443 to a higher port where your PowerShell script is listening.
* `sudo iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 8080`

**Benefits:**

* This approach doesn't require changing the privileges of the PowerShell executable or script.

#### Using `setcap` Command

The `setcap` utility can grant specific capabilities to an executable, like `pwsh`, enabling it to bind to privileged ports.

**How it Works:**

* Run `sudo setcap 'cap_net_bind_service=+ep' $(which pwsh)`. This command sets the `CAP_NET_BIND_SERVICE` capability on the PowerShell executable, allowing it to bind to any port below 1024.

**Security Consideration:**

* This method enhances security by avoiding running PowerShell as root, but it still grants significant privileges to the PowerShell process.

#### Utilizing Authbind

Authbind is a tool that allows a non-root user to bind to privileged ports.

**Setup:**

* Install Authbind, configure it to allow the desired port, and then start your PowerShell script using Authbind.
* For instance, `authbind --deep pwsh yourscript.ps1` allows the script to bind to a privileged port.

**Advantages:**

* It provides a finer-grained control over port access and doesn't require setting special capabilities on the PowerShell binary itself.
121 changes: 117 additions & 4 deletions docs/Hosting/RunAsService.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,121 @@
# Service
# Using Pode as a Service

Rather than having to manually invoke your Pode server script each time, it's best if you can have it start automatically when your computer/server starts. Below you'll see how to set your script to run as either a Windows or a Linux service.
Pode now provides built-in functions to easily manage services across platforms (Windows, Linux, macOS). These functions allow you to register, start, stop, query, and unregister Pode services in a cross-platform way.

## Windows
## Registering a Service

You can register a Pode-based service using the `Register-PodeService` function, which will create the necessary service files and configurations for your system.

#### Example:
```powershell
Register-PodeService -Name "HelloService" -Description "Example Pode Service" -ParameterString "-Verbose" -Start
```

This command registers a service named "HelloService" and starts it immediately after registration. The service runs your Pode script with the specified parameters.

### `Register-PodeService` Parameters

The `Register-PodeService` function provides several parameters to customize your service registration across Windows, Linux, and macOS:

- **`-Name`** *(string)*:
The name of the service to register.
**Mandatory**.

- **`-Description`** *(string)*:
A brief description of the service. Defaults to "This is a Pode service."

- **`-DisplayName`** *(string)*:
The display name for the service (Windows only). Defaults to "Pode Service($Name)".

- **`-StartupType`** *(string)*:
Specifies the startup type of the service ('Automatic' or 'Manual'). Defaults to 'Automatic'.

- **`-ParameterString`** *(string)*:
Additional parameters to pass to the worker script when the service is run. Defaults to an empty string.

- **`-LogServicePodeHost`** *(switch)*:
Enables logging for the Pode service host.

- **`-ShutdownWaitTimeMs`** *(int)*:
Maximum time in milliseconds to wait for the service to shut down gracefully before forcing termination. Defaults to 30,000 milliseconds.

- **`-UserName`** *(string)*:
Specifies the username under which the service will run by default is the current user.

- **`-Start`** *(switch)*:
A switch to start the service immediately after registration.

- **`-Password`** *(securestring)*:
A secure password for the service account (Windows only). If omitted, the service account will be 'NT AUTHORITY\SYSTEM'.

- **`-SecurityDescriptorSddl`** *(string)*:
A security descriptor in SDDL format, specifying the permissions for the service (Windows only).

- **`-SettingsPath`** *(string)*:
Specifies the directory to store the service configuration file (`<name>_svcsettings.json`). If not provided, a default directory is used.

- **`-LogPath`** *(string)*:
Specifies the path for the service log files. If not provided, a default log directory is used.

---

## Starting a Service

Once a service is registered, you can start it using the `Start-PodeService` function.

#### Example:
```powershell
Start-PodeService -Name "HelloService"
```

This returns $true if the service is started successfully, $false otherwise.

## Stopping a Service

To stop a running Pode service, you can use the `Stop-PodeService` function.

#### Example:
```powershell
Stop-PodeService -Name "HelloService"
```
This returns $true if it was stopped, $false otherwise.

## Querying a Service

To check the status of a service (whether it's running or stopped), use the `Get-PodeService` function.

#### Example:
```powershell
Get-PodeService -Name "HelloService"
```

This returns a hashtable with the service name and status.
```powershell
Name Value
---- -----
Status Running
Pid 17576
Name HelloService
```

## Unregistering a Service

When you're done with a service, you can unregister it using the `Unregister-PodeService` function. You can also forcefully stop and remove a service using the `-Force` parameter.

#### Example:
```powershell
Unregister-PodeService -Name "HelloService" -Force
```

This returns $true if it was unregistered successfully, $false otherwise.



# Alternative Methods for Windows and Linux

If you prefer to manually manage Pode as a service or if you're working in an environment where the Pode functions are unavailable, you can still use the traditional methods for managing services on Windows and Linux.

#### Windows (NSSM):

To run your Pode server as a Windows service, we recommend using the [`NSSM`](https://nssm.cc) tool. To install on Windows you can use Chocolatey:

Expand Down Expand Up @@ -45,7 +158,7 @@ nssm stop $name
nssm remove $name confirm
```

## Linux
#### Linux (systemd):

To run your Pode server as a Linux service you just need to create a `<name>.service` file at `/etc/systemd/system`. The following is example content for an example `pode-server.service` file, which run PowerShell Core (`pwsh`), as well as you script:

Expand Down
167 changes: 167 additions & 0 deletions examples/HelloService/HelloService.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
<#
.SYNOPSIS
PowerShell script to register, start, stop, query, and unregister a Pode service, with a basic server setup.

.DESCRIPTION
This script manages a Pode service named 'Hello Service' with commands to register, start, stop, query,
and unregister the service. Additionally, it sets up a Pode server that listens on port 8080 and includes
a simple GET route that responds with 'Hello, Service!'.

The script checks if the Pode module exists locally and imports it; otherwise, it imports Pode from the system.

To test the Pode server's HTTP endpoint:
Invoke-RestMethod -Uri http://localhost:8080/ -Method Get
# Response: 'Hello, Service!'

.PARAMETER Register
Registers the 'Hello Service' with Pode.

.PARAMETER Unregister
Unregisters the 'Hello Service' from Pode. Use with the -Force switch to forcefully unregister the service.

.PARAMETER Force
Used with the -Unregister parameter to forcefully unregister the service.

.PARAMETER Start
Starts the 'Hello Service'.

.PARAMETER Stop
Stops the 'Hello Service'.

.PARAMETER Query
Queries the status of the 'Hello Service'.

.EXAMPLE
Register the service:
./HelloService.ps1 -Register

.EXAMPLE
Start the service:
./HelloService.ps1 -Start

.EXAMPLE
Query the service:
./HelloService.ps1 -Query

.EXAMPLE
Stop the service:
./HelloService.ps1 -Stop

.EXAMPLE
Unregister the service:
./HelloService.ps1 -Unregister -Force

.LINK
https://github.com/Badgerati/Pode/blob/develop/examples/HelloService/HelloService.ps1

.NOTES
Author: Pode Team
License: MIT License
#>

[CmdletBinding(DefaultParameterSetName = 'Inbuilt')]
param(
[Parameter( ParameterSetName = 'Inbuilt')]
[int]
$Port = 8080,

[Parameter(Mandatory = $true, ParameterSetName = 'Register')]
[switch]
$Register,

[Parameter(Mandatory = $true, ParameterSetName = 'Unregister')]
[switch]
$Unregister,

[Parameter( ParameterSetName = 'Unregister')]
[switch]
$Force,

[Parameter( ParameterSetName = 'Start')]
[switch]
$Start,

[Parameter( ParameterSetName = 'Stop')]
[switch]
$Stop,

[Parameter( ParameterSetName = 'Query')]
[switch]
$Query,

[Parameter( ParameterSetName = 'Suspend')]
[switch]
$Suspend,

[Parameter( ParameterSetName = 'Resume')]
[switch]
$Resume
)
try {
# Get the path of the script being executed
$ScriptPath = (Split-Path -Parent -Path (Split-Path -Parent -Path $MyInvocation.MyCommand.Path))
# Get the parent directory of the script's path
$podePath = Split-Path -Parent -Path $ScriptPath

# Check if the Pode module file exists in the specified path
if (Test-Path -Path "$($podePath)/src/Pode.psm1" -PathType Leaf) {
# If the Pode module file exists, import it
Import-Module "$($podePath)/src/Pode.psm1" -Force -ErrorAction Stop
}
else {
# If the Pode module file does not exist, import the Pode module from the system
Import-Module -Name 'Pode' -MaximumVersion 2.99 -ErrorAction Stop
}
}
catch {
# If there is any error during the module import, throw the error
throw
}


if ( $Register.IsPresent) {
Register-PodeService -Name 'Hello Service2' -ParameterString "-Port $Port" # -Password (ConvertTo-SecureString 'Pata2Pata1' -AsPlainText -Force)
exit
}
if ( $Unregister.IsPresent) {
Unregister-PodeService -Name 'Hello Service2' -Force:$Force
exit
}
if ($Start.IsPresent) {
Start-PodeService -Name 'Hello Service2'
exit
}

if ($Stop.IsPresent) {
Stop-PodeService -Name 'Hello Service2'
exit
}

if ($Suspend.IsPresent) {
Suspend-PodeService -Name 'Hello Service2'
exit
}

if ($Resume.IsPresent) {
Resume-PodeService -Name 'Hello Service2'
exit
}

if ($Query.IsPresent) {
Get-PodeService -Name 'Hello Service2'
exit
}

# Start the Pode server
Start-PodeServer {
New-PodeLoggingMethod -File -Name 'errors' -MaxDays 4 -Path './logs' | Enable-PodeErrorLogging

# Add an HTTP endpoint listening on localhost at port 8080
Add-PodeEndpoint -Address localhost -Port $Port -Protocol Http

# Add a route for GET requests to the root path '/'
Add-PodeRoute -Method Get -Path '/' -ScriptBlock {
# Send a text response with 'Hello, world!'
Write-PodeTextResponse -Value 'Hello, Service!'
}
}
Loading
Loading