parsing and writing WireGuard configuration files (comment preserving)
Parsing and writing WireGuard configuration files (comment preserving)
WireGuard config files are ini-style. Since all "Peer" sections have the same name, these files cannot be parsed and modified by most libraries handling configuration files. Most existing libraries are not able to preserve or even add comments when modifying a config file. "wgconfig" was created to work with WireGuard configuration files and to preserve comments.
Install using PyPi:
pip3 install wgconfig
Read and parse the existing WireGuard configuration file 'wg0.conf' located in '/etc/wireguard':
import wgconfig
wc = wgconfig.WGConfig('wg0')
wc.read_file()
print('INTERFACE DATA:', , wc.get_interface())
print('LIST OF PEERS:', wc.get_peers())
print('ALL PEER DATA:', wc.get_peers(keys_only=False))
Add a new peer with a comment line before the peer section:
wc.add_peer('801mgm2JhjTOCxfihEknzFJGYxDvi+8oVYBrWe3hOWM=', '# Newly added peer')
Add an attribute to that peer:
wc.add_attr('801mgm2JhjTOCxfihEknzFJGYxDvi+8oVYBrWe3hOWM=', 'Endpoint', 'wg.example.com:51820', '# Added for demonstration purposes')
Write the changes to disk. Comments that were present when reading the file are preserved.
wc.write_file()
Please see below for more detailed usage information.
Create a new WireGuard configuration file as '/root/wgtest.conf':
import wgconfig
wc = wgconfig.WGConfig('/root/wgtest.conf')
# Add attribute to Interface section (denoted by 'None')
wc.add_attr(None, 'PrivateKey', '6FYKQKEtGFAb5HSwyj5cQl3wgS1E9d6SqVjdVksOn2s=')
# Save to disk
wc.write_file()
# Access the data
print('INTERFACE DATA:', wc.get_interface())
print('LIST OF PEERS (there are no peers yet):', wc.get_peers())
print('ALL PEER DATA (there are no peers yet):', wc.get_peers(keys_only=False))
The module also contains simple wrappers around the wg command (contained in WireGuard tools) to generate and manage keys:
import wgconfig.wgexec as wgexec
# Create a new WireGuard private key
private_key = wgexec.generate_privatekey()
More information and examples can be found here:
Returns attributes and values (including wgconfig-internal ones) of the Interface section as a dictionary
Notes:
get_interface()
method (see further below) instead.Returns attributes and values (including wgconfig-internal ones) of all peers as a nested dictionary
Notes:
get_peers()
method (see further below) instead.__init__(file)
Initializes the instance
Parameters:
Examples:
wc = wgconfig.WGConfig('wg0')
wc = wgconfig.WGConfig('/etc/wireguard/wg0.conf')
read_file()
Reads the WireGuard config file from disk into memory
write_file(file)
Writes a WireGuard config file from memory to file
Parameters:
Examples:
wc.write_file()
wc.write_file('wg0')
wc.write_file('/etc/wireguard/wg0.conf')
initialize_file(leading_comment)
Empties the file and adds the interface section header
Parameters:
Examples:
wc.initialize_file()
wc.initialize_file('# Here comes the Interface section:')
get_interface(include_details)
Returns a dictionary of the attributes and values of the interface section
Parameters:
Examples:
ifdata = wc.get_interface()
get_peers(keys_only, include_disabled, include_details)
Returns a list of peers or - if selected - a dictionary including peers' data
Parameters:
Examples:
peers = wc.get_peers()
peerdata = wc.get_peers(keys_only=False)
get_peer(key, include_details)
Returns the data of the peer with the given (public) key
Parameters:
Examples:
peerdata = wc.get_peer('801mgm2JhjTOCxfihEknzFJGYxDvi+8oVYBrWe3hOWM=')
peerdata = wc.get_peer('801mgm2JhjTOCxfihEknzFJGYxDvi+8oVYBrWe3hOWM=', include_details=True)
Notes:
read_file()
before attempting to get data out of a filepeers
property if you want to retrieve the data of all peersadd_peer(key, leading_comment)
Adds a new peer with the given (public) key
Parameters:
Examples:
wc.add_peer('801mgm2JhjTOCxfihEknzFJGYxDvi+8oVYBrWe3hOWM=')
wc.add_peer('801mgm2JhjTOCxfihEknzFJGYxDvi+8oVYBrWe3hOWM=', '# Here comes the Interface section:')
del_peer(key)
Removes the peer with the given (public) key
Note: Comment lines immediately before the Peer section are removed, too.
Parameters:
Examples:
wc.del_peer('801mgm2JhjTOCxfihEknzFJGYxDvi+8oVYBrWe3hOWM=')
add_attr(key, attr, value, leading_comment, append_as_line)
Adds an attribute/value pair to the given peer ('None' for adding an interface attribute)
Parameters:
Examples:
wc.add_attr(None, 'ListenPort', '51820')
wc.add_attr('801mgm2JhjTOCxfihEknzFJGYxDvi+8oVYBrWe3hOWM=', 'AllowedIPs', '0.0.0.0/0')
wc.add_attr('801mgm2JhjTOCxfihEknzFJGYxDvi+8oVYBrWe3hOWM=', 'AllowedIPs', '0.0.0.0/0', '# Allow all IPv4 addresses', append_as_line=True)
del_attr(self, key, attr, value, remove_leading_comments)
Removes an attribute/value pair from the given peer ('None' for adding an interface attribute); set 'value' to 'None' to remove all values
Parameters:
Examples:
wc.del_attr(None, 'ListenPort')
wc.del_attr('801mgm2JhjTOCxfihEknzFJGYxDvi+8oVYBrWe3hOWM=', 'AllowedIPs')
wc.del_attr('801mgm2JhjTOCxfihEknzFJGYxDvi+8oVYBrWe3hOWM=', 'AllowedIPs', '0.0.0.0/0')
wc.del_attr('801mgm2JhjTOCxfihEknzFJGYxDvi+8oVYBrWe3hOWM=', 'AllowedIPs', '0.0.0.0/0', remove_leading_comments=False)
disable_peer(self, key)
Disables the peer with the given (public) key by prepending #!
to all lines in a peer section
Parameters:
Examples:
wc.disable_peer('801mgm2JhjTOCxfihEknzFJGYxDvi+8oVYBrWe3hOWM=')
enable_peer(self, key)
Enables the peer with the given (public) key by removing #!
from all lines in a peer section
Parameters:
Examples:
wc.enable_peer('801mgm2JhjTOCxfihEknzFJGYxDvi+8oVYBrWe3hOWM=')
get_peer_enabled(self, key)
Checks whether the peer with the given (public) key is enabled
Parameters:
Examples:
wc.get_peer_enabled('801mgm2JhjTOCxfihEknzFJGYxDvi+8oVYBrWe3hOWM=')
In case you encounter any bugs, please report the expected behavior and the actual behavior so that the issue can be reproduced and fixed.
Clone this repo to your local machine using https://github.com/towalink/wgconfig.git
Install the module temporarily to make it available in your Python installation:
pip3 install -e <path to root of "src" directory>
Call "pytest" to run the unit tests:
pytest <path to root of "test" directory>