Create a gist now

Instantly share code, notes, and snippets.

@zanloy /lxc-nat.rb
Last active Mar 12, 2016

What would you like to do?
LXC script to setup NAT rules based on a config file
#!/usr/bin/env ruby
#
## lxc-nat.rb
#
# Simple ruby script to create port-forwarding rules based on a static table.
#
## Configuration
#
# Create /etc/lxc/lxc-nat.yml with rules such as the following:
# **Note:** interface (will assume eth0), source ip, and destination port (will
# assume source port) are all optional.
#
# forwards:
# - source:
# ip: 10.0.0.1
# port: 80
# destination:
# name: www
# port: 80
# - source:
# port: 3306
# destination:
# name: mysql_server
# - proto: udp
# source:
# interface: eth1
# port: 53
# destination:
# name: dns_server
#
# The config file is standard YAML and the syntax is available online.
require 'optparse'
require 'yaml'
options = {
noop: false,
flush: false,
configfile: '/etc/lxc/lxc-nat.yml'
}
OptionParser.new do |opts|
opts.on('-f', '--flush') { options[:flush] = true }
opts.on('-n', '--noop') { options[:noop] = true }
opts.on('-c', '--config=CONFIG') { |cf| options[:configfile] = cf }
end.parse!
forwards = []
containers = {}
lxc_output = `lxc-ls -f -F name,ipv4 | tail -n +3`.split("\n")
lxc_output.each do |line|
cols = line.split(/\s+/)
containers[cols[0]] = cols[1]
end
config = YAML.load_file(options[:configfile])
config['forwards'].each do |forward|
# TODO: verify all fields exist and valid
src_iface = forward['source']['interface']
src_ip = forward['source']['ip']
src_port = forward['source']['port']
proto = forward['proto']
lxc_name = forward['destination']['name']
lxc_port = forward['destination']['port']
proto ||= 'tcp'
src_iface ||= 'eth0' unless src_ip
lxc_port ||= src_port
if containers.include?(lxc_name)
cmd = "iptables -t nat -A lxc-nat"
cmd += " -i #{src_iface}" if src_iface
cmd += " -d #{src_ip}" if src_ip
cmd += " -p #{proto}" if proto
cmd += " --dport #{src_port}"
cmd += " -j DNAT --to #{containers[lxc_name]}:#{lxc_port}"
forwards << cmd
else
puts "WARNING: Forward specified in configfile doesn't match any containers: #{lxc_name}"
end
end
def runcmd ( cmd, noop )
if noop
puts cmd
else
system(cmd)
end
end
runcmd('iptables -t nat -F lxc-nat', options[:noop])
runcmd('iptables -t nat -D PREROUTING -j lxc-nat', options[:noop])
runcmd('iptables -t nat -X lxc-nat', options[:noop])
unless options[:flush]
runcmd('iptables -t nat -N lxc-nat', options[:noop])
runcmd('iptables -t nat -A PREROUTING -j lxc-nat', options[:noop])
forwards.each do |f|
runcmd(f, options[:noop])
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment