Convert iptables module to typescript

Change-type: patch
Signed-off-by: Cameron Diver <cameron@resin.io>
This commit is contained in:
Cameron Diver 2018-05-03 12:42:51 +01:00
parent 352a5684b3
commit 5622ddce94
No known key found for this signature in database
GPG Key ID: 69264F9C923F55C1
3 changed files with 52 additions and 47 deletions

View File

@ -1,25 +0,0 @@
Promise = require 'bluebird'
childProcess = Promise.promisifyAll(require('child_process'))
clearAndAppendIptablesRule = (rule) ->
childProcess.execAsync("iptables -D #{rule}")
.catchReturn()
.then ->
childProcess.execAsync("iptables -A #{rule}")
clearAndInsertIptablesRule = (rule) ->
childProcess.execAsync("iptables -D #{rule}")
.catchReturn()
.then ->
childProcess.execAsync("iptables -I #{rule}")
exports.rejectOnAllInterfacesExcept = (allowedInterfaces, port) ->
# We delete each rule and create it again to ensure ordering (all ACCEPTs before the REJECT/DROP).
# This is especially important after a supervisor update.
Promise.each allowedInterfaces, (iface) ->
clearAndInsertIptablesRule("INPUT -p tcp --dport #{port} -i #{iface} -j ACCEPT")
.then ->
clearAndAppendIptablesRule("INPUT -p tcp --dport #{port} -j REJECT")
.catch ->
# On systems without REJECT support, fall back to DROP
clearAndAppendIptablesRule("INPUT -p tcp --dport #{port} -j DROP")

32
src/lib/iptables.ts Normal file
View File

@ -0,0 +1,32 @@
import * as Promise from 'bluebird';
import * as childProcess from 'child_process';
// The following is exported so that we stub it in the tests
export const execAsync = Promise.promisify(childProcess.exec);
function clearAndAppendIptablesRule(rule: string): Promise<void> {
return execAsync(`iptables -D ${rule}`)
.catchReturn(null)
.then(() => execAsync(`iptables -A ${rule}`))
.return();
}
function clearAndInsertIptablesRule(rule: string): Promise<void> {
return execAsync(`iptables -D ${rule}`)
.catchReturn(null)
.then(() => execAsync(`iptables -I ${rule}`))
.return();
}
export function rejectOnAllInterfacesExcept(
allowedInterfaces: string[],
port: number,
): Promise<void> {
// We delete each rule and create it again to ensure ordering (all ACCEPTs before the REJECT/DROP).
// This is especially important after a supervisor update.
return Promise.each(allowedInterfaces, (iface) => clearAndInsertIptablesRule(`INPUT -p tcp --dport ${port} -i ${iface} -j ACCEPT`))
.then(() => clearAndAppendIptablesRule(`INPUT -p tcp --dport ${port} -j REJECT`))
// On systems without REJECT support, fall back to DROP
.catch(() => clearAndAppendIptablesRule(`INPUT -p tcp --dport ${port} -j DROP`))
.return();
}

View File

@ -1,43 +1,41 @@
Promise = require 'bluebird'
iptables = require '../src/lib/iptables'
childProcess = require('child_process')
m = require 'mochainon'
{ stub } = m.sinon
{ expect } = m.chai
describe 'iptables', ->
it 'calls iptables to delete and recreate rules to block a port', ->
stub(childProcess, 'execAsync').returns(Promise.resolve())
stub(iptables, 'execAsync').returns(Promise.resolve())
iptables.rejectOnAllInterfacesExcept(['foo', 'bar'], 42)
.then ->
expect(childProcess.execAsync.callCount).to.equal(6)
expect(childProcess.execAsync).to.be.calledWith('iptables -D INPUT -p tcp --dport 42 -i foo -j ACCEPT')
expect(childProcess.execAsync).to.be.calledWith('iptables -I INPUT -p tcp --dport 42 -i foo -j ACCEPT')
expect(childProcess.execAsync).to.be.calledWith('iptables -D INPUT -p tcp --dport 42 -i bar -j ACCEPT')
expect(childProcess.execAsync).to.be.calledWith('iptables -I INPUT -p tcp --dport 42 -i bar -j ACCEPT')
expect(childProcess.execAsync).to.be.calledWith('iptables -D INPUT -p tcp --dport 42 -j REJECT')
expect(childProcess.execAsync).to.be.calledWith('iptables -A INPUT -p tcp --dport 42 -j REJECT')
expect(iptables.execAsync.callCount).to.equal(6)
expect(iptables.execAsync).to.be.calledWith('iptables -D INPUT -p tcp --dport 42 -i foo -j ACCEPT')
expect(iptables.execAsync).to.be.calledWith('iptables -I INPUT -p tcp --dport 42 -i foo -j ACCEPT')
expect(iptables.execAsync).to.be.calledWith('iptables -D INPUT -p tcp --dport 42 -i bar -j ACCEPT')
expect(iptables.execAsync).to.be.calledWith('iptables -I INPUT -p tcp --dport 42 -i bar -j ACCEPT')
expect(iptables.execAsync).to.be.calledWith('iptables -D INPUT -p tcp --dport 42 -j REJECT')
expect(iptables.execAsync).to.be.calledWith('iptables -A INPUT -p tcp --dport 42 -j REJECT')
.then ->
childProcess.execAsync.restore()
iptables.execAsync.restore()
it "falls back to blocking the port with DROP if there's no REJECT support", ->
stub(childProcess, 'execAsync').callsFake (cmd) ->
stub(iptables, 'execAsync').callsFake (cmd) ->
if /REJECT$/.test(cmd)
Promise.reject()
else
Promise.resolve()
iptables.rejectOnAllInterfacesExcept(['foo', 'bar'], 42)
.then ->
expect(childProcess.execAsync.callCount).to.equal(8)
expect(childProcess.execAsync).to.be.calledWith('iptables -D INPUT -p tcp --dport 42 -i foo -j ACCEPT')
expect(childProcess.execAsync).to.be.calledWith('iptables -I INPUT -p tcp --dport 42 -i foo -j ACCEPT')
expect(childProcess.execAsync).to.be.calledWith('iptables -D INPUT -p tcp --dport 42 -i bar -j ACCEPT')
expect(childProcess.execAsync).to.be.calledWith('iptables -I INPUT -p tcp --dport 42 -i bar -j ACCEPT')
expect(childProcess.execAsync).to.be.calledWith('iptables -D INPUT -p tcp --dport 42 -j REJECT')
expect(childProcess.execAsync).to.be.calledWith('iptables -A INPUT -p tcp --dport 42 -j REJECT')
expect(childProcess.execAsync).to.be.calledWith('iptables -D INPUT -p tcp --dport 42 -j DROP')
expect(childProcess.execAsync).to.be.calledWith('iptables -A INPUT -p tcp --dport 42 -j DROP')
expect(iptables.execAsync.callCount).to.equal(8)
expect(iptables.execAsync).to.be.calledWith('iptables -D INPUT -p tcp --dport 42 -i foo -j ACCEPT')
expect(iptables.execAsync).to.be.calledWith('iptables -I INPUT -p tcp --dport 42 -i foo -j ACCEPT')
expect(iptables.execAsync).to.be.calledWith('iptables -D INPUT -p tcp --dport 42 -i bar -j ACCEPT')
expect(iptables.execAsync).to.be.calledWith('iptables -I INPUT -p tcp --dport 42 -i bar -j ACCEPT')
expect(iptables.execAsync).to.be.calledWith('iptables -D INPUT -p tcp --dport 42 -j REJECT')
expect(iptables.execAsync).to.be.calledWith('iptables -A INPUT -p tcp --dport 42 -j REJECT')
expect(iptables.execAsync).to.be.calledWith('iptables -D INPUT -p tcp --dport 42 -j DROP')
expect(iptables.execAsync).to.be.calledWith('iptables -A INPUT -p tcp --dport 42 -j DROP')
.then ->
childProcess.execAsync.restore()
iptables.execAsync.restore()