Skip to content

Instantly share code, notes, and snippets.

@Hashbrown777
Last active September 7, 2023 14:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Hashbrown777/7fe5e2c48dfd0aada4c821b73ab764de to your computer and use it in GitHub Desktop.
Save Hashbrown777/7fe5e2c48dfd0aada4c821b73ab764de to your computer and use it in GitHub Desktop.
Multiple wireguard connexions with an open port on each (azirevpn)
./single.ps1 bob paris
./private.ps1 steve newyork
./private.ps1 greg seattle
./private.ps1 emma newyork
<#
Will create
a wireguard tunnel to paris using bob's credentials in the init [non-]namespace,
a tunnel to seattle with the interface name "seattle" using greg's config under the namespace "greg",
and two tunnels to newyork (interfaces both called 'newyork') using options from steve and emma (under namespaces of the same name respectively)
All will have an open port to the outside world, printed to the console, accessible by connecting to the wireguard server's IP at that port!
#>
//expamle. With this you could run `./wireguard.ps1 steve paris`
{
"servers" : {
"paris" : {
"key": "...=",
"address": [
"5.4.3.2",
"12345"
]
},
"newyork" : {
"key": "...=",
"address": [
"4.1.3.2",
"12345"
]
},
"seattle" : {
"key": "...=",
"address": [
"4.1.5.0",
"52341"
]
}
},
"clients" : {
"bob" : {
"key" : "...=",
"address" : [
"1.2.3.4",
"32"
]
},
"steve" : {
"key" : "...=",
"address" : [
"1.2.3.5",
"32"
]
},
"greg" : {
"key" : "...=",
"address" : [
"1.0.3.5",
"32"
]
},
"emma" : {
"key" : "...=",
"address" : [
"1.0.4.5",
"32"
]
}
},
"dns": [
"1.1.1.1",
"8.8.1.1"
]
}
#using a bunch of zip files containing wg .conf files,
#generates a config.json housing the different client and server configurations separately for recombination at connexion-time.
#Ran 'once' on a non-lockeddown machine,
#where each zip has a differing 'client' [Interface] portion duplicated in all of their respective .confs,
#which individually make up the same _set_ of 'server' .confs repeating the [Peer]s,
#to DNS query the globally adressable domain names such that config.json only contanis IPs.
cd $PSScriptRoot
#your home subnet
$subnet = '10.0.0.0/24'
#rename provider-given user configs to your app use-cases
$rename = @{
frog = 'init'
log = 'deluge'
pog = 'baka'
jog = 'gaze'
}
#which zips to use and where the 'client' name can be extracted
$zips = 'AzireVPN-(.*).zip'
#what the wireguard configuration files therein are called, and where to extract the 'server' name from
$configs = 'azirevpn-.*-(.*).conf'
#parsing rules
$parameters = '^(\S+)\s*=\s*(\S.*\S)\s*$'
$IPV6 = '^([a-f0-9]{0,4}:?){1,8}(/\d+)?$'
$servers = @{}
$clients = @{}
$dns = @{}
$subnet = $subnet -split '/'
$subnet = (
([IPAddress]$subnet[0]).Address,
([UInt32]::MaxValue -shr 32 - $subnet[1])
)
Get-ChildItem '*.zip' `
| %{
if (!($_.Name -match $zips)) {
return
}
#client Interface
$zip = $Matches[1]
if ($rename[$zip]) {
$zip = $rename[$zip]
}
Expand-Archive $_ -DestinationPath .
Get-ChildItem '*.conf' `
| %{
if (!($_.Name -match $configs)) {
return
}
#server Peer
$config = $Matches[1]
try {
$privateKey = $NULL
$address = $NULL
$publicKey = $NULL
$endpoint = $NULL
Get-Content $_ `
| %{
if (!($_ -match $parameters)) {
return
}
$key = $Matches[1]
$values = $Matches[2] -split '\s*,\s*' `
| ?{ $_ -notmatch $IPV6 }
switch -exact ($key) {
'PrivateKey' { $privateKey = $values }
'PublicKey' { $publicKey = $values }
'Address' {
$address = $values -split '/'
}
'Endpoint' {
$values = $values -split ':'
$endpoint = (
((Resolve-DnsName ($values[0]) | ?{ $_.Section -eq 'Answer'}).IPAddress),
$values[1]
)
}
'DNS' {
$values `
| %{
#use the VPN's DNS entry so long as it is not coincidentally in our home's declared subnet
if ((([IPAddress]$_).Address -band $subnet[1]) -ne $subnet[0]) {
$dns[$_] = $True
}
}
}
'AllowedIPs' {}
default { "Ignoring $name $config $_" | Write-Host }
}
}
if (!($privateKey -and $publicKey -and $endpoint)) {
throw 'Missing value'
}
if (!$clients[$zip]) {
$clients[$zip] = [PSCustomObject]@{
key = $privateKey
address = $address
}
}
elseif ($clients[$zip].key -ne $privateKey) {
throw "private key mismatch against $($clients[$zip].key)"
}
elseif (($clients[$zip].address -join '/') -ne ($address -join '/')) {
throw "address mismatch against $($clients[$zip].address)"
}
if (!$servers[$config]) {
$servers[$config] = [PSCustomObject]@{
key = $publicKey
address = $endpoint
}
}
elseif ($servers[$config].key -ne $publicKey) {
throw "public key mismatch against $($servers[$config].key)"
}
elseif (($servers[$config].address -join ':') -ne ($endpoint -join ':')) {
throw "endpoint mismatch against $($servers[$config].address -join ':')"
}
}
catch {
"$zip $config $_" | Write-Error
}
Remove-Item $_
}
}
[PSCustomObject]@{
servers = $servers
clients = $clients
dns = $dns.Keys
} `
| ConvertTo-Json -Depth 3 `
| Out-File -Encoding ascii 'config.json'
#!/bin/pwsh
#Using config.json, opens multiple wireguard connexions at the same time, going through the one real interface;
#à la `./multi.ps1 up bob,steve,greg paris,newyork`
#practically equivalent to calling
# `./single.ps1 bob paris; ./single.ps1 steve newyork -extra 1; ./single.ps1 greg newyork -extra 2`
#but functionally uses namespaces instead of *relying* on routes, firewalls, and wg-quick.
#NB how the client configs are paired with the servers' in turn, the first acts as the gateway, and missing server descriptions are duplicated
#Current state is saved to multi.json and can be taken down with `./multi.ps1 down`
#
#Doesn't work :| even with a single pair and ufw completely disabled, the wg interface never comes up
Param($command)
cd $PSScriptRoot
$namespace = 'physical'
$interface = 'eno1'
$port = 40000
$protocol = 'udp'
Function up { Param($clients, $servers)
if (Test-Path 'multi.json') {
'Already up' | Write-Error
Exit 1
}
$config = Get-Content 'config.json' | ConvertFrom-Json
$servers = [System.Collections.Queue]@(
$servers -split ',' `
| %{
($server = $config.servers.($_))
if (!$server) {
'Servers:'
($config.servers | Get-Member -MemberType NoteProperty).Name -replace '^',"`t"
if ($_) {
"$_ not found"
}
Exit 1
}
}
)
$clients = $clients -split ',' `
| &{
Begin {
$output = [PSCustomObject]@{}
}
Process {
if (!($client = $config.clients.($_))) {
'Clients:'
($config.clients | Get-Member -MemberType NoteProperty).Name -replace '^',"`t"
if ($_) {
"$_ not found"
}
Exit 1
}
if ($servers.Count -gt 1) {
$server = $servers.Dequeue()
}
else {
$server = $servers.Peek()
}
$output `
| Add-Member `
-MemberType NoteProperty `
-Name $_ `
-Value ([PSCustomObject]@{
private = $client.key
public = $server.key
client = $client.address
server = $server.address
listen = $port++
})
}
End {
return $output
}
}
$clients | ConvertTo-Json -Depth 3 >'multi.json'
($clients | Get-Member -MemberType NoteProperty).Name `
| %{
$wireguard = $clients.($_)
sudo ./preup.ps1 `
-interface $interface `
-server ($wireguard.server -join ':') `
-protocol $protocol `
-port $wireguard.port
}
run ip netns add $namespace
($clients | Get-Member -MemberType NoteProperty).Name `
| %{
$wireguard = $clients.($_)
run ip -n $namespace link add $_ type wireguard
run ip -n $namespace link set $_ netns 1
mkfifo private.key
$key = $wireguard.private >private.key &
run wg set $_ `
listen-port $wireguard.listen `
private-key private.key `
peer $wireguard.public `
endpoint ($wireguard.server -join ':') `
allowed-ips '0.0.0.0/0,::/0'
run ip addr add ($wireguard.client -join '/') dev $_
$key | Receive-Job -Wait -AutoRemoveJob
}
rm -f private.key
run ip link set $interface down
run ip link set $interface netns $namespace
# run ip netns exec $namespace dhcpcd -b $interface
run ip -n $namespace link set $interface up
($clients | Get-Member -MemberType NoteProperty).Name `
| %{
run ip link set $_ up
run ufw enable
run ufw deny in on $_
sudo ./postup.ps1 `
-wireguard $_ `
-interface $interface `
-client $clients.($_).client[0] `
-protocol $protocol `
-default:($_ -eq ($clients | Get-Member -MemberType NoteProperty)[0].Name)
}
}
switch -exact ($command) {
'up' { up @args }
'down' {
run ip -n $namespace link set $interface down
run ip -n $namespace link set $interface netns 1
$clients = Get-Content 'multi.json' | ConvertFrom-Json
($clients | Get-Member -MemberType NoteProperty).Name | %{
$client = $clients.($_)
sudo ./predown.ps1
run ip link del $_
sudo ./postdown.ps1 `
-interface $interface `
-server $client.server[0] `
-default:($_ -eq ($clients | Get-Member -MemberType NoteProperty)[0].Name)
}
run ip netns del $namespace
# run dhcpcd -b $interface
run ip link set $interface up
rm 'multi.json'
}
'exec' {
sudo exec ip netns exec $namespace sudo -E -u "#$(id -u)" -g "#$(id -g)" -- @args
}
default { "$($MyInvocation.MyCommand.Name) up|down|exec" | Write-Error }
}
#!/bin/pwsh
Param($interface, $server, $wireguard, $default)
cd $PSScriptRoot
if ($default) {
#ip route add default dev $interface
# 'Releasing DNS...'
# (Get-Content 'config.json' | ConvertFrom-Json).dns `
# | %{
# "`t$_"
# ip route delete $_/32 dev $wireguard
# }
# "...from $wireguard"
'8.8.8.8','1.1.1.1' -replace '^','nameserver ' >/etc/resolv.conf
}
$filter = @("on $wireguard")
if (!$default) {
$gateway = (ip -j route show 0.0.0.0/0 dev $interface | ConvertFrom-Json)[0].gateway
"Removing route to $server" | Write-Host -NoNewline
ip route del $server via $gateway dev $interface
" via $gateway"
$filter += ,[Regex]::Escape($server)
}
$filter = "(\s|^)($($filter -join '|'))(\s|$)"
"Removing firewall rules..."
ufw status numbered `
| %{
$_ = $_ -split '(?<=^\[[ \d]+\])\s+'
if ($_[1] -match $filter) {
"`t" + $_[1] | Write-Host
$_[0] -replace '\D',''
}
} `
| Sort-Object -Descending `
| %{
'y' | ufw delete $_ >$NULL
}
"Done"
rm "$wireguard.conf" "$wireguard.port"
#!/bin/pwsh
Param($wireguard, $interface, $client, $protocol, $default)
cd $PSScriptRoot
$token = '...'
$user = 'lounge'
$port = 8080
if ($default) {
"logoutput: /var/log/sockd.log
internal: 0.0.0.0 port = $port
external: $wireguard
user.notprivileged: $user
clientmethod: none
socksmethod: none
client pass {
from: 10.0.0.0/24 to: 0.0.0.0/0
log: error
}
socks pass {
from: 0.0.0.0/0 to: 0.0.0.0/0
command: bind connect udpassociate
log: error
}
socks pass {
from: 0.0.0.0/0 to: 0.0.0.0/0
command: bindreply udpreply
log: error
}" >/etc/danted.conf
"Starting proxy on $port" | Write-Host -NoNewline
service danted restart
" forwarding to $wireguard"
#sudo ip route add default dev $wireguard
# 'Forcing DNS...'
(Get-Content 'config.json' | ConvertFrom-Json).dns `
| %{
# "`t$_" | Write-Host
# ip route add $_/32 dev $wireguard
'nameserver ' + $_
} >/etc/resolv.conf
# "...via $wireguard"
}
"Deny everything in on $wireguard"
ufw deny in on $wireguard >$NULL
$token = "Authorization: Bearer $token"
$json = 'Content-Type: application/json'
$api = 'https://api.azirevpn.com/v2/portforward'
$data = [PSCustomObject]@{
expires_in = $NULL
hidden = $False
internal_ipv4 = $client
} `
| ConvertTo-Json
#it's the same as the server ip..
#curl `
# --silent `
# --interface $wireguard `
# --dns-interface $wireguard `
# 'https://ifconfig.me/ip' `
#| tee "$wireguard.ip"
"Allowing" | Write-Host -NoNewline
for (
$request=[System.Collections.Stack]@('POST','GET');
$request.Count;
) {
if ($port = (
curl `
--silent `
--interface $wireguard `
--dns-interface $wireguard `
--request $request.Pop() `
--url $api `
--header $token `
--header $json `
--data $data `
| ConvertFrom-Json
).data.port) {
" incomming $port" | Write-Host -NoNewline
ufw insert 1 allow in on $wireguard from any to any port $port >$NULL
$port >"$wireguard.port"
break
}
}
" on $wireguard"
#!/bin/pwsh
Param()
cd $PSScriptRoot
#!/bin/pwsh
Param($interface, $server, $protocol, $port, $default)
cd $PSScriptRoot
'Allowing...'
"`toutgoing VPN $($server -replace ':.*$','')/$protocol"
ufw `
insert 1 `
allow out `
on $interface `
from any `
to ($server -replace ':',' port ' -split ' ') `
proto $protocol `
>$NULL
"`tincoming VPN to $port"
ufw `
insert 1 `
allow in `
on $interface `
from ($server -replace ':.*$','') `
to any `
port $port `
proto $protocol `
>$NULL
"...connexions on $interface"
if (!$default) {
$gateway = (ip -j route show 0.0.0.0/0 dev $interface | ConvertFrom-Json)[0].gateway
"Forcing $($server -replace ':.*$','')" | Write-Host -NoNewline
ip route add ($server -replace ':.*$') via $gateway dev $interface
" via $gateway"
}
#!/bin/pwsh
#Using config.json, opens a single wireguard connexion forced through the real interface, even if a current wireguard interface is up.
#`./private.ps1 up bob paris` to connect to paris wg server using bob's client configuration
#It creates a namespace bob under which the paris interface is the default route and accepts an open port
#passing a number for `-extra` offsets the listen port
#teardown would be `private.ps1 down bob`
#execute a command inside the namespace with `private.ps1 exec bob ...`
#
#only functions if there is an existing wg-quick tunnel up, using the same fwmark (single.ps1)
Param($_command)
cd $PSScriptRoot
$public = 'eno1'
$port = 40000
$protocol = 'udp'
$token = '...'
$fwMark = 0xca6c
Function run() {
'# ' + ($args -join ' ')
sudo @args
if (!$?) {
Exit 1
}
}
Function up { Param($client, $server, $extra = 0)
$private = $server
$namespace = $client
if (Test-Path "$namespace.json") {
throw $namespace
}
$config = Get-Content 'config.json' | ConvertFrom-Json
if ($config.servers.($server)) {
$server = $config.servers.($server)
}
else {
'Servers:'
($config.servers | Get-Member -MemberType NoteProperty).Name -replace '^',"`t"
if ($server) {
"$server not found"
}
Exit 1
}
if ($config.clients.($client)) {
$client = $config.clients.($client)
}
else {
'Clients:'
($config.clients | Get-Member -MemberType NoteProperty).Name -replace '^',"`t"
if ($client) {
"$client not found"
}
Exit 1
}
$script:port += $extra
run ufw `
insert 1 `
allow out `
on $public `
from any `
to $server.address[0] `
port $server.address[1] `
proto $protocol
run ufw `
insert 1 `
allow in `
on $public `
from $server.address[0] `
to any `
port $port `
proto $protocol
$gateway = (ip -j route show 0.0.0.0/0 dev $public | ConvertFrom-Json)[0].gateway
run ip route add $server.address[0] via $gateway dev $public
if ($namespace -notin (ip netns list | %{ $_ -replace '\s.*$','' })) {
run ip netns add $namespace
run ip -n $namespace link set lo up
run mkdir -p "/etc/netns/$namespace"
($config.dns -replace '^','nameserver ') | sudo tee "/etc/netns/$namespace/resolv.conf"
}
$config = [PSCustomObject]@{
interface = $private
server = $server.address[0]
open = $NULL
}
$config | ConvertTo-Json >"$namespace.json"
run ip link add $private type wireguard
run ip link set $private netns $namespace
run ip -n $namespace addr `
add ($client.address -join '/') `
dev $private
mkfifo private.key
$key = $client.key >private.key &
run ip netns exec $namespace `
wg set $private `
listen-port $port `
fwmark $fwMark `
private-key private.key `
peer $server.key `
endpoint ($server.address -join ':') `
allowed-ips '0.0.0.0/0,::/0'
$key | Receive-Job -Wait -AutoRemoveJob
rm -f private.key
run ip -n $namespace link set $private up
run ip -n $namespace route add default dev $private
$token = "Authorization: Bearer $token"
$json = 'Content-Type: application/json'
$api = 'https://api.azirevpn.com/v2/portforward'
$data = [PSCustomObject]@{
expires_in = $NULL
hidden = $False
internal_ipv4 = $client.address[0]
} `
| ConvertTo-Json
for (
$request = [System.Collections.Stack]@('POST','GET');
$request.Count -and !$config.open;
$config.open = (
sudo ip netns exec $namespace `
curl `
--silent `
--request $request.Pop() `
--url $api `
--header $token `
--header $json `
--data $data `
| ConvertFrom-Json
).data.port
) {}
if ($config.open) {
$config.open
$config | ConvertTo-Json >"$namespace.json"
}
else {
'Failed to open port!'
}
}
Function down { Param($namespace)
$config = Get-Content "$namespace.json" -ErrorAction Stop | ConvertFrom-Json
run ip -n $namespace link set $config.interface down
run ip -n $namespace link del $config.interface
$gateway = (ip -j route show 0.0.0.0/0 dev $public | ConvertFrom-Json)[0].gateway
run ip route del $config.server via $gateway dev $public
$filter = "(\s|^)(on $($config.interface)|$([Regex]::Escape($config.server)))(\s|$)"
sudo ufw status numbered `
| %{
$_ = $_ -split '(?<=^\[[ \d]+\])\s+'
if ($_[1] -match $filter) {
$_[0] -replace '\D',''
}
} `
| Sort-Object -Descending `
| %{
'y' | sudo ufw delete $_ >$NULL
}
rm "$namespace.json"
}
Function exec { Param($namespace)
sudo ip netns exec $namespace sudo -u $env:USER @args
}
Function info { Param($namespace)
exec $namespace ip a
''
exec $namespace sudo wg
''
ip netns pids $namespace
exec $namespace ss -lptn
}
Function port { Param($namespace, $port)
"nohup
socat
tcp-listen:$port,fork,reuseaddr
exec:'ip netns exec $namespace socat STDIO tcp-connect:127.0.0.1:$port',nofork
</dev/null >/dev/null 2>&1 &" `
-replace "'","\'" `
-replace '(?<=\n)\s*([^\s][^\n]*[^\s])\s*(?=\n)','$$''$1''' `
-replace '\n',' ' `
| sudo bash
}
Switch -Exact ($_command) {
'up' { up @args }
'down' { down @args }
'exec' { exec @args }
'info' { info @args }
'port' { port @args }
Default { "$($MyInvocation.MyCommand.Name) up|down|exec" | Write-Error }
}
#!/bin/pwsh
#this shouldn't ever be ran really, just once on system set-up
Param($interface = 'eno1')
cd $PSScriptRoot
'Resetting firewall'
sudo ufw --force reset >$NULL
sudo ufw --force enable >$NULL
'Deny everything' | Write-Host -NoNewline
sudo ufw deny in on $interface >$NULL
sudo ufw deny out on $interface >$NULL
" on $interface"
"Specifically allow incomming..."
"...SSH"
sudo ufw insert 1 allow in on $interface to any port 22 proto tcp >$NULL
'...NetBIOS, SMB, LLMNR'
sudo ufw insert 1 allow in on $interface to any port 137,138 proto udp >$NULL
sudo ufw insert 1 allow in on $interface to any port 137,139 proto tcp >$NULL
sudo ufw insert 1 allow in on $interface to any port 445 proto tcp >$NULL
sudo ufw insert 1 allow in on $interface to any port 5355 proto udp >$NULL
sudo ufw insert 1 allow in on $interface to any port 5355 proto tcp >$NULL
'...SOCKS&HTTP/S proxies, RDP'
sudo ufw insert 1 allow in on $interface to any port 8080,8081 proto tcp >$NULL
sudo ufw insert 1 allow in on $interface to any port 3389 proto tcp >$NULL
'...Minecraft'
sudo ufw insert 1 allow in on $interface to any port 19132 proto udp >$NULL
sudo ufw insert 1 allow in on $interface to any port 25565 proto tcp >$NULL
"connexions on $interface"
#!/bin/pwsh
#Using config.json, opens a single wireguard connexion forced through the real interface, even if a current wireguard interface is up.
#`./single.ps1 bob paris` to connect to paris wg server using bob's client configuration
#passing a number for `-extra` offsets the listen port & makes the interface not set up default routes to itself
#teardown would be `wg-quick down ./bob.conf`
#
#inbound traffic reaches through only if extra=0, which obviously doesn't work multiple times (use private.ps1)
Param($client, $server, $extra = 0, $interface = 'eno1', $protocol = 'udp', $port = 40000, $fwMark = 0xca6c)
cd $PSScriptRoot
$wireguard = "./$client.conf"
if (Test-Path $wireguard) {
throw "$config exists"
}
$config = Get-Content 'config.json' | ConvertFrom-Json
if (!($client = $config.clients.($client))) {
'Clients:'
($config.clients | Get-Member -MemberType NoteProperty).Name -replace '^',"`t"
Exit
}
if (!($server = $config.servers.($server))) {
'Servers:'
($config.servers | Get-Member -MemberType NoteProperty).Name -replace '^',"`t"
Exit
}
$port += $extra
"[Interface]
PrivateKey = $($client.key)
Address = $($client.address -join '/')
PreUp = $($PSScriptRoot)/preup.ps1 -interface $interface -port $port -server $($server.address -join ':') -protocol $protocol -default:$(!$extra)
PostUp = $($PSScriptRoot)/postup.ps1 -interface $interface -wireguard %i -client $($client.address[0]) -protocol $protocol -default:$(!$extra)
PreDown = $($PSScriptRoot)/predown.ps1
PostDown = $($PSScriptRoot)/postdown.ps1 -interface $interface -wireguard %i -server $($server.address[0]) -default:$(!$extra)
ListenPort = $port
Table = $(('off','auto')[!$extra])
FwMark = $fwMark
[Peer]
PublicKey = $($server.key)
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = $($server.address -join ':')" >$wireguard
sudo wg-quick up $wireguard
if (!$?) {
rm $wireguard
}
@Hashbrown777
Copy link
Author

Hashbrown777 commented Sep 6, 2023

#!/bin/pwsh
cd $PSScriptRoot

$_ = 'init','hkg'
$client,$server = $_
./single.ps1 @_
pause

$extra = 0
(
	('pub' ,'sto'),
	('baka','tor'),
	('gaze','ams')
) `
| %{
	''
	$client,$server = $_
	$_ -join ' '
	./private.ps1 up @_ (++$extra)
	pause
	
	$_config = "deluge/$client/core.conf"
	$config = Get-Content -Raw $_config
	
	$port = ($config | Select-String '"daemon_port"\s*:\s*(\d+)\s*,').Matches.Groups.Value[1]
	"Linking 127.0.0.1:$port"
	./private.ps1 port $_[0] $port
	
	$port = (Get-Content "$client.json" | ConvertFrom-Json).open
	"Setting open $port"
	$config -replace '(?<="listen_ports"\s*:\s*\[)[^\]]*(?=\])',"$port,$port" >$_config
}

@Hashbrown777
Copy link
Author

#!/bin/pwsh
cd $PSScriptRoot

Get-ChildItem '*.json' `
| %{
	if ($_.Name -eq 'config.json') {
		return
	}
	$config = $_ | Get-Content | ConvertFrom-Json
	
	$_.Name -replace '.json$',"`t" | Write-Host -NoNewline
	curl 'https://ports.yougetsignal.com/check-port.php' `
		-s `
		-H 'content-type: application/x-www-form-urlencoded; charset=UTF-8' `
		--data-raw "remoteAddress=$($config.server)&portNumber=$($config.open)" `
	| ag '(?<=alt=")Closed|Open' -o
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment