This commit is contained in:
ROTTLER Tamas 2021-08-26 00:43:00 +02:00
commit e165b427fe
13 changed files with 438 additions and 0 deletions

3
README.md Normal file
View File

@ -0,0 +1,3 @@
== pf6
iptables management for IPv4 + IPv6

6
debian/README vendored Normal file
View File

@ -0,0 +1,6 @@
The Debian Package pf6
----------------------------
This program loads different iptables rulesets from its config file.
-- ROTTLER Tamas <tom@bitfit.hu> Tue, 17 Feb 2015 22:54:04 +0100

5
debian/changelog vendored Normal file
View File

@ -0,0 +1,5 @@
pf6 (1) unstable; urgency=low
* Initial Release.
-- ROTTLER Tamas <tom@bitfit.hu> Tue, 17 Feb 2015 14:20:04 +0100

1
debian/compat vendored Normal file
View File

@ -0,0 +1 @@
9

14
debian/control vendored Normal file
View File

@ -0,0 +1,14 @@
Source: pf6
Section: utils
Priority: optional
Maintainer: ROTTLER Tamas <tom@bitfit.hu>
Build-Depends: debhelper (>= 9)
Standards-Version: 3.9.5
Homepage: http://www.bitfit.hu/
Package: pf6
Architecture: all
Depends: perl, iptables
Description: iptables/ip6tables ruleset loader
This program loads different iptables/ip6tables rulesets from a configuration
file.

28
debian/copyright vendored Normal file
View File

@ -0,0 +1,28 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: pf6
Source: <url://example.com>
Files: *
Copyright: 2015 ROTTLER Tamas <tom@bitfit.hu>
License: GPL-3.0+
Files: debian/*
Copyright: 2015 ROTTLER Tamas <tom@bitfit.hu>
License: GPL-3.0+
License: GPL-3.0+
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
.
This package is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
.
On Debian systems, the complete text of the GNU General
Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".

0
debian/docs vendored Normal file
View File

2
debian/install vendored Normal file
View File

@ -0,0 +1,2 @@
pf6 /usr/sbin
pf6.conf /etc

12
debian/pf6.init vendored Executable file
View File

@ -0,0 +1,12 @@
#!/bin/sh
### BEGIN INIT INFO
# Provides: pf6 firewall iptables ip6tables
# Required-Start: $syslog $remote_fs
# Required-Stop: $syslog $remote_fs
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: pf6 iptables firewall
# Description: pf6 iptables/6 firewall initialization
### END INIT INFO
[ -x /usr/sbin/pf6 ] && exec /usr/sbin/pf6 "$*"
exit 0

32
debian/rules vendored Executable file
View File

@ -0,0 +1,32 @@
#!/usr/bin/make -f
# See debhelper(7) (uncomment to enable)
# output every command that modifies files on the build system.
#DH_VERBOSE = 1
# see EXAMPLES in dpkg-buildflags(1) and read /usr/share/dpkg/*
DPKG_EXPORT_BUILDFLAGS = 1
include /usr/share/dpkg/default.mk
# see FEATURE AREAS in dpkg-buildflags(1)
#export DEB_BUILD_MAINT_OPTIONS = hardening=+all
# see ENVIRONMENT in dpkg-buildflags(1)
# package maintainers to append CFLAGS
#export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic
# package maintainers to append LDFLAGS
#export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed
# main packaging script based on dh7 syntax
%:
dh $@
# debmake generated override targets
# This is example for Cmake (See http://bugs.debian.org/641051 )
#override_dh_auto_configure:
# dh_auto_configure -- \
# -DCMAKE_LIBRARY_PATH=$(DEB_HOST_MULTIARCH)

1
debian/source/format vendored Normal file
View File

@ -0,0 +1 @@
3.0 (native)

272
pf6 Executable file
View File

@ -0,0 +1,272 @@
#!/usr/bin/perl
# Author: ROTTLER Tamas <tom@bitfit.hu>
# 2014-12-05 - release
# 2014-12-11 - looking for config in FindBin::RealBin
# - cleanup: detect tables with policy
# - cleanup: no ip6 nat table for kernels < 3.7
# 2015-02-17 - debian package
# - configuration now in /etc/pf6.conf
use strict; use warnings;
use Getopt::Long;
sub usage {
print STDERR "Usage: $0 [OPTIONS..] ACTION\n".
" OPTIONS:\n".
" --verbose|-v print commands\n".
" --dry-run only show ip(6)tables commands\n".
" --ipv4|-4 IPv4 only\n".
" --ipv6|-6 IPv6 only\n".
" ACTION:\n".
" start|RULESET load given ruleset\n".
" stop unload all rules, set ACCEPT policies\n".
" restart|force-reload (re)load the 'start' ruleset\n".
" status print status (always running)\n";
exit 1;
}
my $self = {
config_fn => "/etc/pf6.conf",
config => '',
iptables => '/sbin/iptables',
ip6tables => '/sbin/ip6tables',
cleanup_tables => [qw(filter nat mangle raw)],
verbose => 0,
dryrun => 0
};
sub iptables {
my %opts = @_;
$opts{ip} = '46' if !$opts{ip} or $opts{ip} !~ /[46]/;
my @ipts;
if ($opts{ip} =~ /4/) {
push @ipts, $self->{iptables};
}
if ($opts{ip} =~ /6/) {
push @ipts, $self->{ip6tables};
}
my @output;
for my $ipt (@ipts) {
my $cline = "$ipt $opts{cmd}";
if (!$opts{query}) {
print "pf6 CMD: $cline\n" if $self->{verbose};
next if $self->{dryrun};
}
@output = `$cline 2>&1`;
if ($? > 0) {
my $ec = $? >> 8;
print STDERR "pf6 ERR: exit code $ec on:\n$cline\n" .
join('', @output);
return 0, @output if $opts{output};
return 0;
}
}
return 1, @output if $opts{output};
return 1;
}
sub kernel_older {
my ($vmin) = @_;
my $output = qx(uname -sr);
if ($output !~ /^Linux\s+(\d+)\.(\d+)(\.(\d+))?/) {
return 0; # unexpexted output
}
my @v = ($1, $2, $4);
my @m = split /\./, $vmin;
for (my $i = 0; $i <= $#v and $i <= $#m; $i++) {
if ($v[$i] < $m[$i]) {
return 1;
}
}
return 0;
}
sub grepchains {
my %opts = @_;
my @output = iptables(
cmd => "-n -t $opts{table} -L",
query => 1,
output => 1,
ip => $opts{ip},
);
my @chains;
for my $line (@output) {
next unless $line =~ /Chain (\S+) \((policy)?/;
push @chains, {
name => $1,
policy => $2 ? 1 : 0
};
}
return @chains;
}
sub cleanup {
my %opts = @_;
if ($opts{ip} ne '4' and $opts{ip} ne '6') {
print "pf6: cleanup: invalid ip protocol\n";
return;
}
print "pf6: cleanup ip$opts{ip}..\n";
my $error = 0;
my $tables = $self->{cleanup_tables};
for my $table (@$tables) {
if ($opts{ip} eq '6' and kernel_older('3.7') and $table eq 'nat') {
# no ip6 nat table before linux 3.7
next;
}
my @chains = grepchains(
table => $table,
ip => $opts{ip}
);
# clean up first
for my $chain (@chains) {
iptables(
cmd => "-t $table -F $chain->{name}",
ip => $opts{ip}
);
if ($chain->{policy}) {
iptables(
cmd => "-t $table -P $chain->{name} ACCEPT",
ip => $opts{ip}
);
}
}
# now there are no references to the chains
for my $chain (@chains) {
next if $chain->{policy};
my $success = iptables(
cmd => "-t $table -X $chain->{name}",
ip => $opts{ip}
);
$error++ if !$success;
}
}
}
sub loadconf {
open(CF, '<', $self->{config_fn})
or die "Could not open $self->{config_fn}: $!\n";
my @cf;
while (my $line = <CF>) {
chomp $line;
next if $line !~ /\S+/;
next if $line =~ /^\s*#/;
if ($line =~ /^\s+(.*)/) {
# lines beginning with \s continue last line
push @cf, '' if !scalar @cf;
$cf[-1] .= $1;
} else {
push @cf, $line;
}
}
close CF;
$self->{config} = \@cf;
}
sub find_ruleset {
my ($ruleset) = @_;
my $in_ruleset = 0;
my @rules;
for (my $i = 0; $i < scalar @{$self->{config}}; $i++) {
my $line = $self->{config}->[$i];
if (!$in_ruleset) {
if ($line =~ /^R>\s*$ruleset\s*$/) {
$in_ruleset = 1;
}
} else {
if ($line =~ /^R>/) {
$in_ruleset = 0;
$i--; # rerun line, perhaps it's us again
} else {
push @rules, $line;
}
}
}
return @rules;
}
sub activate_rules {
my %opts = @_;
my ($good, $bad) = (0, 0);
for my $r (@{$opts{rules}}) {
if ($r =~ s/^PRINT>(.*)//) {
print "$1\n";
next;
}
my $ip = '46';
if ($r =~ s/^([46])>\s*//) {
$ip = $1;
}
$ip =~ s/6// if $opts{ip} eq '4';
$ip =~ s/4// if $opts{ip} eq '6';
next if $ip eq '';
my $success = iptables(
cmd => $r,
ip => $ip
);
if ($success) {
$good++;
} else {
$bad++;
}
}
return $good, $bad;
}
my ($ipv4, $ipv6) = (0, 0);
GetOptions(
"verbose|v" => \$self->{verbose},
"dry-run" => \$self->{dryrun},
"ipv4|4" => \$ipv4,
"ipv6|6" => \$ipv6,
) or usage;
if (!$ipv4 and !$ipv6) {
($ipv4, $ipv6) = (1, 1);
}
if ($self->{dryrun}) {
print "pf6: DRY RUN!\n";
$self->{verbose} = 1;
}
loadconf;
my $action = $ARGV[0] or usage;
# these are the same for us:
$action = 'start' if $action eq 'restart' or $action eq 'force-reload';
if ($action eq 'stop') {
cleanup(ip => '4') if $ipv4;
cleanup(ip => '6') if $ipv6;
exit 0;
} if ($action eq 'status') {
print "pf6 status is meaningless.\n";
exit 0;
}
my @rules = find_ruleset($action);
if (!scalar @rules) {
print STDERR "Ruleset '$action' not defined.\n";
exit 1;
}
cleanup(ip => '4') if $ipv4;
cleanup(ip => '6') if $ipv6;
print "pf6: loading ruleset '$action'..\n";
my ($good, $bad) = activate_rules(
rules => \@rules,
ip => ($ipv4 ? '4' : '') . ($ipv6 ? '6' : '')
);
if ($bad) {
print "pf6: $good rules ok, $bad rules FAILED.\n";
exit 1;
}
print "pf6: $good rules ok.\n";
exit 0;
__END__
# vim: set tabstop=4 shiftwidth=4 expandtab smarttab:

62
pf6.conf Normal file
View File

@ -0,0 +1,62 @@
# pf6.conf syntax:
# - empty lines allowed; only full line comments beginning with '#'
# - lines beginning with whitespace continue last line
# - ruleset begin: 'R>ruleset'
# - rules only for IPv4 begin with '4>', IPv6 with '6>'
# - if no 4/6 given, rule is executed for both
# - lines beginning with 'PRINT>' are only printed
# ---%<--- delete this block
R> start
-P INPUT ACCEPT
-P OUTPUT ACCEPT
-P FORWARD ACCEPT
# --->%--- until here and change the ruleset name 'start2' -> 'start', and
# customize the rules for your needs...
R> start2
#-----------------------------------------------------------------------------
-N established
-A established -m state --state ESTABLISHED -j ACCEPT
-A established -m state --state RELATED -j ACCEPT
#-----------------------------------------------------------------------------
-N inbase
-A inbase -i lo -j ACCEPT
4> -A inbase -p icmp -j ACCEPT
6> -A inbase -p icmpv6 -j ACCEPT
-A inbase -p tcp --dport ssh -j ACCEPT
-A inbase -p tcp --dport domain -j ACCEPT
-A inbase -p udp --dport domain -j ACCEPT
-A inbase -p udp --sport ntp -j ACCEPT
-A inbase -p udp --dport ntp -j ACCEPT
-A inbase -p tcp --dport auth -j REJECT
#-----------------------------------------------------------------------------
-N input
#-A input -p tcp --dport http -j ACCEPT
#-----------------------------------------------------------------------------
-A INPUT -i lo -j ACCEPT
-A INPUT -j established
-A INPUT -j inbase
-A INPUT -j input
-P INPUT DROP
-P OUTPUT ACCEPT
-P FORWARD DROP
##############################################################################
##############################################################################
R> ssh
-P INPUT DROP
-P OUTPUT DROP
-P FORWARD DROP
-A INPUT -i lo -j ACCEPT
-A OUTPUT -o lo -j ACCEPT
-A INPUT -p tcp --dport 22 -j ACCEPT
-A OUTPUT -p tcp --sport 22 -j ACCEPT
PRINT>
PRINT> SSH only ruleset (stateless) loaded
PRINT>
# end.