243 lines
6.2 KiB
V
243 lines
6.2 KiB
V
// ©AngelaMos | 2026
|
|
// main.v
|
|
|
|
module main
|
|
|
|
import os
|
|
import flag
|
|
import src.config
|
|
import src.models
|
|
import src.parser
|
|
import src.analyzer
|
|
import src.generator
|
|
import term
|
|
import src.display
|
|
|
|
fn main() {
|
|
if os.args.len < 2 {
|
|
cmd_help()
|
|
exit(config.exit_usage_error)
|
|
}
|
|
command := os.args[1]
|
|
remaining := if os.args.len > 2 { os.args[2..] } else { []string{} }
|
|
|
|
match command {
|
|
'load', 'display' {
|
|
cmd_load(remaining)
|
|
}
|
|
'analyze' {
|
|
cmd_analyze(remaining)
|
|
}
|
|
'optimize' {
|
|
cmd_optimize(remaining)
|
|
}
|
|
'harden' {
|
|
cmd_harden(remaining)
|
|
}
|
|
'export' {
|
|
cmd_export(remaining)
|
|
}
|
|
'diff' {
|
|
cmd_diff(remaining)
|
|
}
|
|
'version', '--version', '-v' {
|
|
cmd_version()
|
|
}
|
|
'help', '--help', '-h' {
|
|
cmd_help()
|
|
}
|
|
else {
|
|
eprintln('unknown command: ${command}')
|
|
eprintln('Run "${config.app_name} help" for usage')
|
|
exit(config.exit_usage_error)
|
|
}
|
|
}
|
|
}
|
|
|
|
fn cmd_load(args []string) {
|
|
if args.len == 0 {
|
|
eprintln('usage: ${config.app_name} load <file>')
|
|
exit(config.exit_usage_error)
|
|
}
|
|
rs := load_ruleset(args[0]) or {
|
|
eprintln('${err}')
|
|
exit(config.exit_parse_error)
|
|
}
|
|
display.print_banner()
|
|
display.print_summary(rs)
|
|
display.print_rule_table(rs)
|
|
}
|
|
|
|
fn cmd_analyze(args []string) {
|
|
if args.len == 0 {
|
|
eprintln('usage: ${config.app_name} analyze <file>')
|
|
exit(config.exit_usage_error)
|
|
}
|
|
rs := load_ruleset(args[0]) or {
|
|
eprintln('${err}')
|
|
exit(config.exit_parse_error)
|
|
}
|
|
display.print_banner()
|
|
display.print_summary(rs)
|
|
|
|
println(term.bold(' Conflict Analysis'))
|
|
conflicts := analyzer.analyze_conflicts(rs)
|
|
display.print_findings(conflicts)
|
|
|
|
println(term.bold(' Optimization Suggestions'))
|
|
optimizations := analyzer.suggest_optimizations(rs)
|
|
display.print_findings(optimizations)
|
|
}
|
|
|
|
fn cmd_optimize(args []string) {
|
|
if args.len == 0 {
|
|
eprintln('usage: ${config.app_name} optimize <file>')
|
|
exit(config.exit_usage_error)
|
|
}
|
|
rs := load_ruleset(args[0]) or {
|
|
eprintln('${err}')
|
|
exit(config.exit_parse_error)
|
|
}
|
|
display.print_banner()
|
|
|
|
println(term.bold(' Optimization Suggestions'))
|
|
findings := analyzer.suggest_optimizations(rs)
|
|
display.print_findings(findings)
|
|
}
|
|
|
|
fn cmd_harden(args []string) {
|
|
mut fp := flag.new_flag_parser(args)
|
|
fp.application(config.app_name)
|
|
fp.description('Generate a hardened firewall ruleset')
|
|
services_str := fp.string('services', `s`, config.default_services.join(','), 'comma-separated list of services to allow')
|
|
iface := fp.string('iface', `i`, config.default_iface, 'public-facing network interface')
|
|
format_str := fp.string('format', `f`, 'iptables', 'output format (iptables or nftables)')
|
|
fp.finalize() or {
|
|
eprintln('${err}')
|
|
exit(config.exit_usage_error)
|
|
}
|
|
|
|
services := services_str.split(',').map(it.trim_space()).filter(it.len > 0)
|
|
out_format := match format_str.to_lower() {
|
|
'iptables' {
|
|
models.RuleSource.iptables
|
|
}
|
|
'nftables' {
|
|
models.RuleSource.nftables
|
|
}
|
|
else {
|
|
eprintln('invalid format: ${format_str} (use iptables or nftables)')
|
|
exit(config.exit_usage_error)
|
|
models.RuleSource.iptables
|
|
}
|
|
}
|
|
|
|
display.print_banner()
|
|
output := generator.generate_hardened(services, iface, out_format)
|
|
println(output)
|
|
}
|
|
|
|
fn cmd_export(args []string) {
|
|
mut fp := flag.new_flag_parser(args)
|
|
fp.application(config.app_name)
|
|
fp.description('Export ruleset in a different format')
|
|
format_str := fp.string('format', `f`, 'nftables', 'output format (iptables or nftables)')
|
|
remaining := fp.finalize() or {
|
|
eprintln('${err}')
|
|
exit(config.exit_usage_error)
|
|
[]string{}
|
|
}
|
|
|
|
if remaining.len == 0 {
|
|
eprintln('usage: ${config.app_name} export <file> --format <iptables|nftables>')
|
|
exit(config.exit_usage_error)
|
|
}
|
|
|
|
rs := load_ruleset(remaining[0]) or {
|
|
eprintln('${err}')
|
|
exit(config.exit_parse_error)
|
|
}
|
|
|
|
out_format := match format_str.to_lower() {
|
|
'nftables' {
|
|
models.RuleSource.nftables
|
|
}
|
|
'iptables' {
|
|
models.RuleSource.iptables
|
|
}
|
|
else {
|
|
eprintln('invalid format: ${format_str}')
|
|
exit(config.exit_usage_error)
|
|
models.RuleSource.iptables
|
|
}
|
|
}
|
|
|
|
output := generator.export_ruleset(rs, out_format)
|
|
println(output)
|
|
}
|
|
|
|
fn cmd_diff(args []string) {
|
|
if args.len < 2 {
|
|
eprintln('usage: ${config.app_name} diff <file1> <file2>')
|
|
exit(config.exit_usage_error)
|
|
}
|
|
left := load_ruleset(args[0]) or {
|
|
eprintln('${err}')
|
|
exit(config.exit_parse_error)
|
|
}
|
|
right := load_ruleset(args[1]) or {
|
|
eprintln('${err}')
|
|
exit(config.exit_parse_error)
|
|
}
|
|
display.print_banner()
|
|
display.print_diff(left, right)
|
|
}
|
|
|
|
fn cmd_version() {
|
|
println('${config.app_name} v${config.version}')
|
|
}
|
|
|
|
fn cmd_help() {
|
|
println('${config.app_name} v${config.version} - Firewall Rule Engine')
|
|
println('')
|
|
println('USAGE:')
|
|
println(' ${config.app_name} <command> [options]')
|
|
println('')
|
|
println('COMMANDS:')
|
|
println(' load <file> Load and display a ruleset')
|
|
println(' analyze <file> Run conflict detection and optimization analysis')
|
|
println(' optimize <file> Show optimization suggestions')
|
|
println(' harden Generate a hardened ruleset')
|
|
println(' export <file> Convert ruleset between iptables/nftables formats')
|
|
println(' diff <f1> <f2> Compare two rulesets')
|
|
println(' version Show version')
|
|
println(' help Show this help')
|
|
println('')
|
|
println('HARDEN OPTIONS:')
|
|
println(' -s, --services Services to allow (default: ssh,http,https)')
|
|
println(' -i, --iface Public interface (default: eth0)')
|
|
println(' -f, --format Output format: iptables or nftables (default: iptables)')
|
|
println('')
|
|
println('EXPORT OPTIONS:')
|
|
println(' -f, --format Target format: iptables or nftables (default: nftables)')
|
|
println('')
|
|
println('EXAMPLES:')
|
|
println(' ${config.app_name} load rules.txt')
|
|
println(' ${config.app_name} analyze /etc/iptables.rules')
|
|
println(' ${config.app_name} harden -s ssh,http,https -f nftables')
|
|
println(' ${config.app_name} export rules.txt -f nftables')
|
|
println(' ${config.app_name} diff old.rules new.rules')
|
|
}
|
|
|
|
fn load_ruleset(path string) !models.Ruleset {
|
|
if !os.exists(path) {
|
|
return error('file not found: ${path}')
|
|
}
|
|
content := os.read_file(path) or { return error('cannot read file: ${path}') }
|
|
fmt := parser.detect_format(content)!
|
|
return match fmt {
|
|
.iptables { parser.parse_iptables(content)! }
|
|
.nftables { parser.parse_nftables(content)! }
|
|
}
|
|
}
|