diff --git a/src/lib/iptables.ts b/src/lib/iptables.ts index 0f3d3836..1fd45da6 100644 --- a/src/lib/iptables.ts +++ b/src/lib/iptables.ts @@ -42,20 +42,19 @@ export function rejectOnAllInterfacesExcept( ): Bluebird { // 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 ( - Bluebird.each(allowedInterfaces, iface => - clearAndInsertIptablesRule( - `INPUT -p tcp --dport ${port} -i ${iface} -j ACCEPT`, + return Bluebird.each(allowedInterfaces, iface => + clearAndInsertIptablesRule( + `INPUT -p tcp --dport ${port} -i ${iface} -j ACCEPT`, + ), + ) + .then(() => + clearAndAppendIptablesRule( + `OUTPUT -p tcp --sport ${port} -m state --state ESTABLISHED -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`), - ) - ); + .then(() => + clearAndAppendIptablesRule(`INPUT -p tcp --dport ${port} -j REJECT`), + ); } export function removeRejections(port: number): Bluebird { diff --git a/test/06-iptables.spec.ts b/test/06-iptables.spec.ts index b522cb6b..fae510aa 100644 --- a/test/06-iptables.spec.ts +++ b/test/06-iptables.spec.ts @@ -8,56 +8,6 @@ describe('iptables', async () => { it('calls iptables to delete and recreate rules to block a port', async () => { stub(iptables, 'execAsync').returns(Bluebird.resolve('')); - await iptables.rejectOnAllInterfacesExcept(['foo', 'bar'], 42); - expect((iptables.execAsync as sinon.SinonStub).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', - ); - (iptables.execAsync as sinon.SinonStub).restore(); - }); - - it("falls back to blocking the port with DROP if there's no REJECT support", async () => { - stub(iptables, 'execAsync').callsFake(cmd => { - if (/REJECT$/.test(cmd)) { - return Bluebird.reject(new Error()); - } else { - return Bluebird.resolve(''); - } - }); - await iptables.rejectOnAllInterfacesExcept(['foo', 'bar'], 42); expect((iptables.execAsync as sinon.SinonStub).callCount).to.equal(16); expect(iptables.execAsync).to.be.calledWith( @@ -72,18 +22,18 @@ describe('iptables', async () => { 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 OUTPUT -p tcp --sport 42 -m state --state ESTABLISHED -j ACCEPT', + ); + expect(iptables.execAsync).to.be.calledWith( + 'iptables -A OUTPUT -p tcp --sport 42 -m state --state ESTABLISHED -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', - ); expect(iptables.execAsync).to.be.calledWith( 'ip6tables -D INPUT -p tcp --dport 42 -i foo -j ACCEPT', ); @@ -96,19 +46,18 @@ describe('iptables', async () => { 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 OUTPUT -p tcp --sport 42 -m state --state ESTABLISHED -j ACCEPT', + ); + expect(iptables.execAsync).to.be.calledWith( + 'ip6tables -A OUTPUT -p tcp --sport 42 -m state --state ESTABLISHED -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', - ); - (iptables.execAsync as sinon.SinonStub).restore(); }); });