From e165b427fe46f27630e9f631cd9c6e40355a41a0 Mon Sep 17 00:00:00 2001 From: ROTTLER Tamas Date: Thu, 26 Aug 2021 00:43:00 +0200 Subject: [PATCH] first --- README.md | 3 + debian/README | 6 + debian/changelog | 5 + debian/compat | 1 + debian/control | 14 +++ debian/copyright | 28 +++++ debian/docs | 0 debian/install | 2 + debian/pf6.init | 12 ++ debian/rules | 32 +++++ debian/source/format | 1 + pf6 | 272 +++++++++++++++++++++++++++++++++++++++++++ pf6.conf | 62 ++++++++++ 13 files changed, 438 insertions(+) create mode 100644 README.md create mode 100644 debian/README create mode 100644 debian/changelog create mode 100644 debian/compat create mode 100644 debian/control create mode 100644 debian/copyright create mode 100644 debian/docs create mode 100644 debian/install create mode 100755 debian/pf6.init create mode 100755 debian/rules create mode 100644 debian/source/format create mode 100755 pf6 create mode 100644 pf6.conf diff --git a/README.md b/README.md new file mode 100644 index 0000000..2d4d072 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +== pf6 + +iptables management for IPv4 + IPv6 diff --git a/debian/README b/debian/README new file mode 100644 index 0000000..24238f2 --- /dev/null +++ b/debian/README @@ -0,0 +1,6 @@ +The Debian Package pf6 +---------------------------- + +This program loads different iptables rulesets from its config file. + + -- ROTTLER Tamas Tue, 17 Feb 2015 22:54:04 +0100 diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..4983b70 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +pf6 (1) unstable; urgency=low + + * Initial Release. + + -- ROTTLER Tamas Tue, 17 Feb 2015 14:20:04 +0100 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..ec63514 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +9 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..ebe0b3c --- /dev/null +++ b/debian/control @@ -0,0 +1,14 @@ +Source: pf6 +Section: utils +Priority: optional +Maintainer: ROTTLER Tamas +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. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..4e6cfbc --- /dev/null +++ b/debian/copyright @@ -0,0 +1,28 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: pf6 +Source: + +Files: * +Copyright: 2015 ROTTLER Tamas +License: GPL-3.0+ + +Files: debian/* +Copyright: 2015 ROTTLER Tamas +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 . + . + On Debian systems, the complete text of the GNU General + Public License version 3 can be found in "/usr/share/common-licenses/GPL-3". diff --git a/debian/docs b/debian/docs new file mode 100644 index 0000000..e69de29 diff --git a/debian/install b/debian/install new file mode 100644 index 0000000..9aa509d --- /dev/null +++ b/debian/install @@ -0,0 +1,2 @@ +pf6 /usr/sbin +pf6.conf /etc diff --git a/debian/pf6.init b/debian/pf6.init new file mode 100755 index 0000000..e8c4c04 --- /dev/null +++ b/debian/pf6.init @@ -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 diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..ce15cce --- /dev/null +++ b/debian/rules @@ -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) + + + + diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..89ae9db --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/pf6 b/pf6 new file mode 100755 index 0000000..0ed9824 --- /dev/null +++ b/pf6 @@ -0,0 +1,272 @@ +#!/usr/bin/perl +# Author: ROTTLER Tamas +# 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 = ) { + 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: diff --git a/pf6.conf b/pf6.conf new file mode 100644 index 0000000..63c179f --- /dev/null +++ b/pf6.conf @@ -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.