Merge pull request #871 from balena-io/867-block-ipv6

Apply iptables rules to ipv6
This commit is contained in:
CameronDiver 2019-02-14 09:38:48 +00:00 committed by GitHub
commit a7b709bf34
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 44 additions and 18 deletions

View File

@ -46,4 +46,10 @@ if [ ! -z "${BALENA_ROOT_CA}" ]; then
fi fi
fi fi
# Mount the host kernel module path onto the expected location
# We need to do this as busybox doesn't support using a custom location
ln -s /mnt/root/lib/modules /lib/modules
# Now load the ip6_tables kernel module, so we can do filtering on ipv6 addresses
modprobe ip6_tables
exec node /usr/src/app/dist/app.js exec node /usr/src/app/dist/app.js

View File

@ -1,35 +1,42 @@
import * as Promise from 'bluebird'; import * as Bluebird from 'bluebird';
import * as childProcess from 'child_process'; import * as childProcess from 'child_process';
// The following is exported so that we stub it in the tests // The following is exported so that we stub it in the tests
export const execAsync = Promise.promisify(childProcess.exec); export const execAsync: (args: string) => Bluebird<string> = Bluebird.promisify(
childProcess.exec,
);
function clearIptablesRule(rule: string): Promise<void> { function applyIptablesArgs(args: string): Bluebird<void> {
return execAsync(`iptables -D ${rule}`).return(); return Bluebird.all([
execAsync(`iptables ${args}`),
execAsync(`ip6tables ${args}`),
]).return();
} }
function clearAndAppendIptablesRule(rule: string): Promise<void> { function clearIptablesRule(rule: string): Bluebird<void> {
return clearIptablesRule(rule) return applyIptablesArgs(`-D ${rule}`);
.catchReturn(null)
.then(() => execAsync(`iptables -A ${rule}`))
.return();
} }
function clearAndInsertIptablesRule(rule: string): Promise<void> { function clearAndAppendIptablesRule(rule: string): Bluebird<void> {
return clearIptablesRule(rule) return clearIptablesRule(rule)
.catchReturn(null) .catchReturn(null)
.then(() => execAsync(`iptables -I ${rule}`)) .then(() => applyIptablesArgs(`-A ${rule}`));
.return(); }
function clearAndInsertIptablesRule(rule: string): Bluebird<void> {
return clearIptablesRule(rule)
.catchReturn(null)
.then(() => applyIptablesArgs(`-I ${rule}`));
} }
export function rejectOnAllInterfacesExcept( export function rejectOnAllInterfacesExcept(
allowedInterfaces: string[], allowedInterfaces: string[],
port: number, port: number,
): Promise<void> { ): Bluebird<void> {
// We delete each rule and create it again to ensure ordering (all ACCEPTs before the REJECT/DROP). // 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. // This is especially important after a supervisor update.
return ( return (
Promise.each(allowedInterfaces, iface => Bluebird.each(allowedInterfaces, iface =>
clearAndInsertIptablesRule( clearAndInsertIptablesRule(
`INPUT -p tcp --dport ${port} -i ${iface} -j ACCEPT`, `INPUT -p tcp --dport ${port} -i ${iface} -j ACCEPT`,
), ),
@ -41,11 +48,10 @@ export function rejectOnAllInterfacesExcept(
.catch(() => .catch(() =>
clearAndAppendIptablesRule(`INPUT -p tcp --dport ${port} -j DROP`), clearAndAppendIptablesRule(`INPUT -p tcp --dport ${port} -j DROP`),
) )
.return()
); );
} }
export function removeRejections(port: number): Promise<void> { export function removeRejections(port: number): Bluebird<void> {
return clearIptablesRule(`INPUT -p tcp --dport ${port} -j REJECT`) return clearIptablesRule(`INPUT -p tcp --dport ${port} -j REJECT`)
.catchReturn(null) .catchReturn(null)
.then(() => clearIptablesRule(`INPUT -p tcp --dport ${port} -j DROP`)) .then(() => clearIptablesRule(`INPUT -p tcp --dport ${port} -j DROP`))

View File

@ -10,13 +10,19 @@ describe 'iptables', ->
stub(iptables, 'execAsync').returns(Promise.resolve()) stub(iptables, 'execAsync').returns(Promise.resolve())
iptables.rejectOnAllInterfacesExcept(['foo', 'bar'], 42) iptables.rejectOnAllInterfacesExcept(['foo', 'bar'], 42)
.then -> .then ->
expect(iptables.execAsync.callCount).to.equal(6) expect(iptables.execAsync.callCount).to.equal(12)
expect(iptables.execAsync).to.be.calledWith('iptables -D INPUT -p tcp --dport 42 -i foo -j ACCEPT') 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 -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 -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 -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 -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 -A INPUT -p tcp --dport 42 -j REJECT')
expect(iptables.execAsync).to.be.calledWith('ip6tables -D INPUT -p tcp --dport 42 -i foo -j ACCEPT')
expect(iptables.execAsync).to.be.calledWith('ip6tables -I INPUT -p tcp --dport 42 -i foo -j ACCEPT')
expect(iptables.execAsync).to.be.calledWith('ip6tables -D INPUT -p tcp --dport 42 -i bar -j ACCEPT')
expect(iptables.execAsync).to.be.calledWith('ip6tables -I INPUT -p tcp --dport 42 -i bar -j ACCEPT')
expect(iptables.execAsync).to.be.calledWith('ip6tables -D INPUT -p tcp --dport 42 -j REJECT')
expect(iptables.execAsync).to.be.calledWith('ip6tables -A INPUT -p tcp --dport 42 -j REJECT')
.then -> .then ->
iptables.execAsync.restore() iptables.execAsync.restore()
@ -28,7 +34,7 @@ describe 'iptables', ->
Promise.resolve() Promise.resolve()
iptables.rejectOnAllInterfacesExcept(['foo', 'bar'], 42) iptables.rejectOnAllInterfacesExcept(['foo', 'bar'], 42)
.then -> .then ->
expect(iptables.execAsync.callCount).to.equal(8) expect(iptables.execAsync.callCount).to.equal(16)
expect(iptables.execAsync).to.be.calledWith('iptables -D INPUT -p tcp --dport 42 -i foo -j ACCEPT') 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 -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 -D INPUT -p tcp --dport 42 -i bar -j ACCEPT')
@ -37,5 +43,13 @@ describe 'iptables', ->
expect(iptables.execAsync).to.be.calledWith('iptables -A 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 -D INPUT -p tcp --dport 42 -j DROP')
expect(iptables.execAsync).to.be.calledWith('iptables -A INPUT -p tcp --dport 42 -j DROP') expect(iptables.execAsync).to.be.calledWith('iptables -A INPUT -p tcp --dport 42 -j DROP')
expect(iptables.execAsync).to.be.calledWith('ip6tables -D INPUT -p tcp --dport 42 -i foo -j ACCEPT')
expect(iptables.execAsync).to.be.calledWith('ip6tables -I INPUT -p tcp --dport 42 -i foo -j ACCEPT')
expect(iptables.execAsync).to.be.calledWith('ip6tables -D INPUT -p tcp --dport 42 -i bar -j ACCEPT')
expect(iptables.execAsync).to.be.calledWith('ip6tables -I INPUT -p tcp --dport 42 -i bar -j ACCEPT')
expect(iptables.execAsync).to.be.calledWith('ip6tables -D INPUT -p tcp --dport 42 -j REJECT')
expect(iptables.execAsync).to.be.calledWith('ip6tables -A INPUT -p tcp --dport 42 -j REJECT')
expect(iptables.execAsync).to.be.calledWith('ip6tables -D INPUT -p tcp --dport 42 -j DROP')
expect(iptables.execAsync).to.be.calledWith('ip6tables -A INPUT -p tcp --dport 42 -j DROP')
.then -> .then ->
iptables.execAsync.restore() iptables.execAsync.restore()