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
# 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

View File

@ -1,35 +1,42 @@
import * as Promise from 'bluebird';
import * as Bluebird 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);
export const execAsync: (args: string) => Bluebird<string> = Bluebird.promisify(
childProcess.exec,
);
function clearIptablesRule(rule: string): Promise<void> {
return execAsync(`iptables -D ${rule}`).return();
function applyIptablesArgs(args: string): Bluebird<void> {
return Bluebird.all([
execAsync(`iptables ${args}`),
execAsync(`ip6tables ${args}`),
]).return();
}
function clearAndAppendIptablesRule(rule: string): Promise<void> {
return clearIptablesRule(rule)
.catchReturn(null)
.then(() => execAsync(`iptables -A ${rule}`))
.return();
function clearIptablesRule(rule: string): Bluebird<void> {
return applyIptablesArgs(`-D ${rule}`);
}
function clearAndInsertIptablesRule(rule: string): Promise<void> {
function clearAndAppendIptablesRule(rule: string): Bluebird<void> {
return clearIptablesRule(rule)
.catchReturn(null)
.then(() => execAsync(`iptables -I ${rule}`))
.return();
.then(() => applyIptablesArgs(`-A ${rule}`));
}
function clearAndInsertIptablesRule(rule: string): Bluebird<void> {
return clearIptablesRule(rule)
.catchReturn(null)
.then(() => applyIptablesArgs(`-I ${rule}`));
}
export function rejectOnAllInterfacesExcept(
allowedInterfaces: string[],
port: number,
): Promise<void> {
): Bluebird<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 =>
Bluebird.each(allowedInterfaces, iface =>
clearAndInsertIptablesRule(
`INPUT -p tcp --dport ${port} -i ${iface} -j ACCEPT`,
),
@ -41,11 +48,10 @@ export function rejectOnAllInterfacesExcept(
.catch(() =>
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`)
.catchReturn(null)
.then(() => clearIptablesRule(`INPUT -p tcp --dport ${port} -j DROP`))

View File

@ -10,13 +10,19 @@ describe 'iptables', ->
stub(iptables, 'execAsync').returns(Promise.resolve())
iptables.rejectOnAllInterfacesExcept(['foo', 'bar'], 42)
.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 -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('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 ->
iptables.execAsync.restore()
@ -28,7 +34,7 @@ describe 'iptables', ->
Promise.resolve()
iptables.rejectOnAllInterfacesExcept(['foo', 'bar'], 42)
.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 -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')
@ -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 -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('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 ->
iptables.execAsync.restore()