|
|
|
#Requires -Modules powershell-yaml
|
|
|
|
param (
|
|
|
|
[string]$Chain = $env:CHAIN,
|
|
|
|
[string]$Stacks = $env:STACKS
|
|
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
|
|
|
|
function Start-IptablesProcess {
|
|
|
|
param (
|
|
|
|
[array]$ArgumentList
|
|
|
|
)
|
|
|
|
|
|
|
|
$splat = @{
|
|
|
|
FilePath = 'iptables'
|
|
|
|
ArgumentList = $ArgumentList
|
|
|
|
Wait = $true
|
|
|
|
PassThru = $true
|
|
|
|
}
|
|
|
|
|
|
|
|
Start-Process @splat
|
|
|
|
}
|
|
|
|
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 = Start-IptablesProcess -ArgumentList $argument_list
|
|
|
|
|
|
|
|
Write-Output ($check.ExitCode -eq 0)
|
|
|
|
}
|
|
|
|
function Add-IptablesRule {
|
|
|
|
param (
|
|
|
|
[string]$Chain,
|
|
|
|
[string]$Table = 'nat',
|
|
|
|
[array]$Rule
|
|
|
|
)
|
|
|
|
|
|
|
|
$argument_list = @(
|
|
|
|
'-t'
|
|
|
|
$Table
|
|
|
|
'-I'
|
|
|
|
$Chain
|
|
|
|
)+$Rule
|
|
|
|
|
|
|
|
if(-not (Test-IptablesRule -Chain $Chain -Rule $Rule)) {
|
|
|
|
$add = Start-IptablesProcess -ArgumentList $argument_list
|
|
|
|
if(0 -eq $add.ExitCode) {
|
|
|
|
Write-Output $true
|
|
|
|
} else {
|
|
|
|
Write-Error 'Adding iptables rule failed'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
function Get-DockerIngressAddress {
|
|
|
|
param (
|
|
|
|
[string]$BridgeDevice = 'docker_gwbridge'
|
|
|
|
)
|
|
|
|
|
|
|
|
$bridge = docker network inspect $BridgeDevice | ConvertFrom-Json
|
|
|
|
$bridge.Containers.{ingress-sbox}.IPv4Address -replace '/.*'
|
|
|
|
}
|
|
|
|
|
|
|
|
# setup SWARM-NAT chain
|
|
|
|
Write-Output ('Create chain {0}' -f $Chain)
|
|
|
|
Add-IptablesChain -Chain $Chain
|
|
|
|
$chain_rule = @(
|
|
|
|
'-m','addrtype'
|
|
|
|
'--dst-type','LOCAL'
|
|
|
|
'-j',$Chain
|
|
|
|
)
|
|
|
|
Add-IptablesRule -Chain 'PREROUTING' -Rule $chain_rule
|
|
|
|
Add-IptablesRule -Chain 'OUTPUT' -Rule $chain_rule
|
|
|
|
|
|
|
|
|
|
|
|
$ingress_address = Get-DockerIngressAddress
|
|
|
|
|
|
|
|
|
|
|
|
foreach($yaml in (Get-ChildItem -Path $Stacks -Include '*.yml','*.yaml' -Recurse)) {
|
|
|
|
Write-Output ('Processing {0}' -f $yaml)
|
|
|
|
$definition = Get-Content -Path $yaml -Raw | ConvertFrom-Yaml
|
|
|
|
|
|
|
|
foreach($port in $definition.services.Values.ports) {
|
|
|
|
$nat = @{
|
|
|
|
protocol = $null
|
|
|
|
ip = $null
|
|
|
|
port = $null
|
|
|
|
published_port = $null
|
|
|
|
}
|
|
|
|
|
|
|
|
if($port.Count -eq 4) {
|
|
|
|
#long form
|
|
|
|
$published_splitted = $port.published -split ':'
|
|
|
|
|
|
|
|
$nat.protocol = $port.protocol
|
|
|
|
$nat.ip = $published_splitted[0]
|
|
|
|
$nat.port = $port.target
|
|
|
|
$nat.published_port = $published_splitted[1]
|
|
|
|
} else {
|
|
|
|
#short form
|
|
|
|
$all_splitted = $port -split ':'
|
|
|
|
$port_splitted = $all_splitted[2] -split '/'
|
|
|
|
|
|
|
|
$nat.protocol = $port_splitted[1]
|
|
|
|
$nat.ip = $all_splitted[0]
|
|
|
|
$nat.port = $port_splitted[0]
|
|
|
|
$nat.published_port = $all_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.published_port -ne $nat.port) {
|
|
|
|
Write-Output ('Additional NAT rule required, because published_port {0} and target {1} differ' `
|
|
|
|
-f $nat.published_port,$nat.port)
|
|
|
|
Write-Output ('Add rule for {0}:{1}' -f $nat.ip,$nat.port)
|
|
|
|
|
|
|
|
$rule = @(
|
|
|
|
'-p','tcp'
|
|
|
|
'-m','tcp'
|
|
|
|
'--destination',$nat.ip
|
|
|
|
'--dport',$nat.port
|
|
|
|
'-j','DNAT'
|
|
|
|
'--to-destination','"{0}:{1}"' -f $ingress_address,$nat.published_port
|
|
|
|
)
|
|
|
|
Add-IptablesRule -Chain $Chain -Rule $rule
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|