#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