You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

169 lines
4.3 KiB
PowerShell

#Requires -Modules powershell-yaml
$ErrorActionPreference = 'Stop'
function Test-IptablesChain {
param (
[string]$Chain,
[string]$Table = 'nat'
)
$output = iptables -t $Table -S $Chain
$reference = '-N {0}' -f $Chain
#check $output in case $Chain has no rules
#otherwise check the first line with $output[0]
$output.Count -gt 0 `
-and ($output -eq $reference `
-or `
$output[0] -eq $reference)
}
function Add-IptablesChain {
param (
[string]$Chain,
[string]$Table = 'nat'
)
if(-not (Test-IptablesChain -Chain $Chain)) {
iptables -t $Table -N $Chain
}
}
function Test-IptablesRule {
param (
[string]$Chain,
[string]$Table = 'nat',
[array]$Rule
)
$argument_list = @(
'-t'
$Table
'-C'
$Chain
)+$Rule
$check_splat = @{
FilePath = 'iptables'
ArgumentList = $argument_list
Wait = $true
PassThru = $true
}
$check = Start-Process @check_splat
Write-Output ($check.ExitCode -eq 0)
}
function Add-IptablesRule {
param (
[string]$Chain,
[string]$Table = 'nat',
[array]$Rule
)
$argument_list = @(
'-t'
$Table
'-A'
$Chain
)+$Rule
$add_splat = @{
FilePath = 'iptables'
ArgumentList = $argument_list
Wait = $true
PassThru = $true
}
if(-not (Test-IptablesRule -Chain $Chain -Rule $Rule)) {
$add = Start-Process @add_splat
if(0 -ne $add.ExitCode) {
Write-Error 'Adding iptables rule failed'
}
}
}
# setup SWARM-NAT chain
$chain = 'SWARM-NAT'
Write-Output ('Create chain {0}' -f $chain)
Add-IptablesChain -Chain $chain
Add-IptablesRule -Chain 'PREROUTING' -Rule '-m','addrtype','--dst-type','LOCAL','-j',$chain
foreach($yaml in (Get-ChildItem -Filter '*.yml')) {
Write-Output ('Processing {0}' -f $yaml)
$definition = Get-Content -Path $yaml -Raw | ConvertFrom-Yaml
foreach($port in $definition.services.Values.ports) {
$nat = @{
protocol = $null
public_ip = $null
public_port = $null
internal_port = $null
}
if($port.Count -eq 4) {
#long form
$published_splitted = $port.published -split ':'
$nat.protocol = $port.protocol
$nat.public_ip = $published_splitted[0]
$nat.public_port = $port.target
$nat.internal_port = $published_splitted[1]
} else {
#short form
$ports_splitted = $port -split ':'
$nat.public_ip = $ports_splitted[0]
$nat.public_port = $ports_splitted[2]
$nat.internal_port = $ports_splitted[1]
}
if(!$nat.protocol) {
#this is also Docker's default
$nat.protocol = 'tcp'
}
if($nat.Values -contains $null) {
#if $nat doesn't conatain all needed attributes skip the port
$error_message = 'Skipping port, because $nat contains $null: {0}' `
-f ($nat | ConvertTo-Json)
Write-Error -Message $error_message `
-ErrorAction Continue
continue
}
if($nat.internal_port -ne $nat.public_port) {
Write-Output ('Additional NAT rule required, because published {0} and target {1} differ' `
-f $nat.internal_port,$nat.public_port)
$nat.protocol
$nat.public_ip
$nat.public_port
$nat.internal_port
}
}
}
#TODO: port from bash
bridge=$(
docker network inspect docker_gwbridge \
--format '{{(index .Containers "ingress-sbox").IPv4Address}}' \
| cut -d'/' -f1
)
internal_port=30000
public_port=30001
destination='145.239.119.128'
rule="-p tcp -m tcp --destination ""${destination}"" --dport ""${public_port}""
-j DNAT --to-destination ""${bridge}:${internal_port}"""
if ! iptables -t nat -C SWARM-NAT $rule > /dev/null; then
Write-Output "Add rule for NAT from ${destination}:${public_port} to ${bridge}:${internal_port}"
iptables -t nat -A SWARM-NAT $rule
else
Write-Output "Rule for NAT from ${destination}:${public_port} to ${bridge}:${internal_port} already exists"
fi