summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2025-05-06 10:59:02 +0300
committerPaul Buetow <paul@buetow.org>2025-05-06 10:59:02 +0300
commit2ad4a74bd0ba2cdc76e0d54e34d195a7675430e1 (patch)
tree7c76983ec6b243cfb67b4bf0922d98d8462f1ee6
parent9e9cac5bce3b89970001938899b1f872cc6d2da1 (diff)
add more comments
-rw-r--r--wireguardmeshgenerator.rb56
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