diff options
| author | Paul Buetow <paul@buetow.org> | 2025-05-06 10:59:02 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2025-05-06 10:59:02 +0300 |
| commit | 2ad4a74bd0ba2cdc76e0d54e34d195a7675430e1 (patch) | |
| tree | 7c76983ec6b243cfb67b4bf0922d98d8462f1ee6 | |
| parent | 9e9cac5bce3b89970001938899b1f872cc6d2da1 (diff) | |
add more comments
| -rw-r--r-- | wireguardmeshgenerator.rb | 56 |
1 files changed, 49 insertions, 7 deletions
diff --git a/wireguardmeshgenerator.rb b/wireguardmeshgenerator.rb index 08aa0eb..dfc4e21 100644 --- a/wireguardmeshgenerator.rb +++ b/wireguardmeshgenerator.rb @@ -1,4 +1,7 @@ #!/usr/bin/env ruby +# This script is a Wireguard mesh configuration generator and manager. +# It provides options to generate, install, and clean Wireguard configurations +# for a set of hosts specified in a YAML configuration file. require 'English' require 'fileutils' @@ -8,7 +11,13 @@ require 'yaml' require 'optparse' +# KeyTool is a utility class for managing WireGuard keys. +# It ensures the presence of required directories and files for public/private keys +# and preshared keys (PSKs). If keys are missing, it generates them using the `wg` tool. class KeyTool + # Initializes the KeyTool instance. + # Ensures the `wg` tool is available and required directories exist. + # Generates public/private keys if they are missing. def initialize(myself) raise 'Wireguard tool not found' unless system('which wg > /dev/null 2>&1') @@ -29,7 +38,7 @@ class KeyTool def pub = File.read(@pubkey_path).strip def priv = File.read(@privkey_path).strip - # Preshared key + # Retrieves or generates a preshared key (PSK) for communication with a peer. def psk(peer) psk_path = "#{@psk_dir}/#{[@myself, peer].sort.join('_')}.key" gen_psk!(psk_path) unless File.exist?(psk_path) @@ -38,8 +47,10 @@ class KeyTool private + # Generates a preshared key (PSK) and writes it to the specified path. def gen_psk!(psk_path) = File.write(psk_path, `wg genpsk`) + # Generates a private key and its corresponding public key. def gen_privpub! privkey = IO.popen('wg genkey', 'r+', &:read) IO.popen('wg pubkey', 'r+') do |io| @@ -51,8 +62,12 @@ class KeyTool end end +# PeerSnippet is a Struct that represents the configuration for a WireGuard peer. PeerSnippet = Struct.new(:myself, :peer, :domain, :wgdomain, :allowed_ips, :endpoint, :keepalive) do + # Converts the PeerSnippet instance into a WireGuard peer configuration string. + # This includes the public key, preshared key, allowed IPs, endpoint, and + # keepalive settings. def to_s keytool = KeyTool.new(myself) <<~PEER_CONF @@ -66,12 +81,16 @@ PeerSnippet = Struct.new(:myself, :peer, :domain, :wgdomain, PEER_CONF end + # Generates the endpoint configuration string for the peer. + # If the peer is behind NAT, a comment is returned instead. def endpoint_str return '# Due to NAT no Endpoint configured' if endpoint == :behind_nat "Endpoint = #{endpoint}:56709" end + # Generates the PersistentKeepalive configuration string for the peer. + # If keepalive is not enabled, a comment is returned instead. def keepalive_str return '# No KeepAlive configured' unless keepalive @@ -79,6 +98,8 @@ PeerSnippet = Struct.new(:myself, :peer, :domain, :wgdomain, end end +# WireguardConfig is a configuration generator for WireGuard mesh networks. +# It generates configuration files for WireGuard interfaces and peers. WireguardConfig = Struct.new(:myself, :hosts) do def to_s keytool = KeyTool.new(myself) @@ -93,12 +114,17 @@ WireguardConfig = Struct.new(:myself, :hosts) do CONF end + # Cleans up generated directories and files. + # Removes the `dist` and `keys` directories if they exist. def clean! %w[dist keys].select { |dir| Dir.exist?(dir) }.each do |dir| FileUtils.rm_r(dir) end end + # Generates the WireGuard configuration file for the current host. + # Creates the necessary directory structure and writes the configuration + # to `wg0.conf`. def generate! dist_dir = "dist/#{myself}/etc/wireguard" puts "Generating #{dist_dir}/wg0.conf" @@ -108,19 +134,22 @@ WireguardConfig = Struct.new(:myself, :hosts) do private + # Generates the address configuration for the current host. + # For OpenBSD, it returns a placeholder comment. Otherwise, it returns the + # IP address as that option isn't supported on OpenBSD. def address return '# No Address = ... for OpenBSD here' if hosts[myself]['os'] == 'OpenBSD' "Address = #{hosts[myself]['wg0']['ip']}" end - # The `peers` method generates a list of peer configurations for a WireGuard mesh network. - # It determines the appropriate endpoint and keepalive settings for each peer. + # Generates a list of peer configurations for the WireGuard mesh network. + # Excludes peers specified in the `exclude_peers` list and the current host itself. + # Determines the appropriate endpoint and keepalive settings for each peer. def peers exclude = hosts[myself].fetch('exclude_peers', []).append(myself) # Check if the current host is in the local area network (LAN). in_lan = hosts[myself].key?('lan') - # Filter out excluded peers and map the remaining hosts to PeerSnippet objects. hosts.reject { exclude.include?(_1) }.map do |peer, data| # Determine if the peer is in the LAN. peer_in_lan = data.key?('lan') @@ -134,6 +163,9 @@ WireguardConfig = Struct.new(:myself, :hosts) do end end +# InstallConfig is a utility class for managing the installation, +# configuration, and restarting of Wireguard on a remote host. It uses SSH and +# SCP for remote operations. InstallConfig = Struct.new(:myself, :hosts) do def initialize(myself, hosts) @myself = myself @@ -147,12 +179,15 @@ InstallConfig = Struct.new(:myself, :hosts) do @conf_dir = data['ssh']['conf_dir'] end + # Uploads the Wireguard configuration file to the remote host. def upload! wg0_conf = "dist/#{@myself}/etc/wireguard/wg0.conf" scp(wg0_conf) self end + # Installs the Wireguard configuration file on the remote host. + # Ensures the configuration directory exists and has the correct permissions. def install! puts "Installing Wireguard config on #{@myself}" ssh <<~SH @@ -165,7 +200,8 @@ InstallConfig = Struct.new(:myself, :hosts) do SH end - def restart! + # Reloads the Wireguard service on the remote host and displays its status. + def reload! puts "Reloading Wireguard on #{@myself}" ssh <<~SH #{@sudo_cmd} #{@reload_cmd} @@ -175,12 +211,14 @@ InstallConfig = Struct.new(:myself, :hosts) do private + # Uploads a file to the remote host using SCP. def scp(src, dst = '.') puts "Uploading #{src} to #{@fqdn}:#{dst}" - raise "Upload #{srd} to #{@fqdn}:#{dst} failed" unless + raise "Upload #{src} to #{@fqdn}:#{dst} failed" unless Net::SCP.upload!(@fqdn, @ssh_user, src, dst) end + # Executes a shell command on the remote host using SSH. def ssh(cmd) File.write('cmd.sh', <<~SH) and scp('cmd.sh') #!/bin/sh @@ -202,6 +240,7 @@ end begin options = { hosts: [] } OptionParser.new do |opts| + opts.banner = 'Usage: wireguardmeshgenerator.rb [options]' opts.on('--generate', 'Generate Wireguard configs') do options[:generate] = true end @@ -220,8 +259,11 @@ begin conf['hosts'].keys.select { options[:hosts].empty? || options[:hosts].include?(_1) } .each do |host| + # Generate Wireguard configuration for the hostreload! WireguardConfig.new(host, conf['hosts']).generate! if options[:generate] - InstallConfig.new(host, conf['hosts']).upload!.install!.restart! if options[:install] + # Install Wireguard configuration for the host. + InstallConfig.new(host, conf['hosts']).upload!.install!.reload! if options[:install] + # Clean Wireguard configuration for the host. WireguardConfig.new(host, conf['hosts']).clean! if options[:clean] end rescue StandardError => e |
