90 lines
3.4 KiB
Python
Executable File
90 lines
3.4 KiB
Python
Executable File
#!/usr/bin/python3
|
|
"""Write a candidate vpn-router.json to the path given on the command line.
|
|
|
|
Called from postinst, which hands the result to ucf(1). ucf compares it with
|
|
/etc/vpn-router/vpn-router.json and decides what to do about local changes, so
|
|
this script never writes to /etc and never has to worry about destroying an
|
|
administrator's edits.
|
|
|
|
The candidate is built in three layers, each overriding the one before:
|
|
|
|
1. the shipped defaults
|
|
2. the current contents of /etc/vpn-router/vpn-router.json, if it exists
|
|
3. any debconf answer that was actually given
|
|
|
|
Layer 2 is what makes reconfiguration safe: a setting the administrator edited
|
|
by hand, and that debconf has nothing to say about, is carried through
|
|
unchanged, so ucf only sees a diff where something really changed. An empty
|
|
answer means "leave alone", which is why clearing the pre-shared key from the
|
|
debconf database after installation does not blank it on the next
|
|
dpkg-reconfigure.
|
|
|
|
Answers arrive as VPN_ROUTER_* environment variables and are converted to the
|
|
type of the setting they fill: comma-separated lists become arrays, "true" and
|
|
"false" become booleans and digits become integers.
|
|
"""
|
|
|
|
import json
|
|
import os
|
|
import sys
|
|
|
|
sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)))
|
|
|
|
import vpnrouter
|
|
|
|
# environment variable -> (section, option)
|
|
ANSWERS = {
|
|
'VPN_ROUTER_PLATFORM': ('general', 'platform'),
|
|
'VPN_ROUTER_MODE': ('general', 'mode'),
|
|
'VPN_ROUTER_INT_ADDR': ('local', 'int_addr'),
|
|
'VPN_ROUTER_EXTERNAL_INTERFACE': ('interfaces', 'external'),
|
|
'VPN_ROUTER_INTERNAL_INTERFACE': ('interfaces', 'internal'),
|
|
'VPN_ROUTER_LOCAL_FQDN': ('wan', 'local_fqdn'),
|
|
'VPN_ROUTER_LOCAL_ID_MODE': ('wan', 'local_id_mode'),
|
|
'VPN_ROUTER_LOCAL_CIDRS': ('local', 'cidrs'),
|
|
'VPN_ROUTER_INT_GATEWAY_IP': ('local', 'int_gateway_ip'),
|
|
'VPN_ROUTER_REMOTE_ADDRS': ('remote', 'addrs'),
|
|
'VPN_ROUTER_REMOTE_ID': ('remote', 'id'),
|
|
'VPN_ROUTER_REMOTE_CIDRS': ('remote', 'cidrs'),
|
|
'VPN_ROUTER_PSK': ('remote', 'psk'),
|
|
'VPN_ROUTER_P2S_ENABLED': ('p2s', 'enabled'),
|
|
'VPN_ROUTER_P2S_ADDRESS_POOL': ('p2s', 'address_pool'),
|
|
'VPN_ROUTER_P2S_CA_NAME': ('p2s', 'ca_name'),
|
|
'VPN_ROUTER_WG_ENABLED': ('wireguard', 'enabled'),
|
|
'VPN_ROUTER_WG_ADDRESS': ('wireguard', 'address'),
|
|
'VPN_ROUTER_WG_LISTEN_PORT': ('wireguard', 'listen_port'),
|
|
}
|
|
|
|
|
|
def main():
|
|
if len(sys.argv) != 2:
|
|
print('usage: generate-config OUTPUT', file=sys.stderr)
|
|
return 2
|
|
output = sys.argv[1]
|
|
|
|
cfg = vpnrouter.load_config()
|
|
|
|
for variable, (section, option) in ANSWERS.items():
|
|
value = os.environ.get(variable)
|
|
if not value:
|
|
continue
|
|
# A value that does not convert is kept as a string for validate() to report.
|
|
default = vpnrouter.SCHEMA[section][option]
|
|
if isinstance(default, bool):
|
|
value = value == 'true'
|
|
elif isinstance(default, int):
|
|
value = int(value) if value.isdigit() else value
|
|
elif isinstance(default, list):
|
|
value = [item.strip() for item in value.split(',') if item.strip()]
|
|
cfg[section][option] = value
|
|
|
|
with open(output, 'w', encoding='utf-8') as handle:
|
|
json.dump(cfg, handle, indent=2)
|
|
handle.write('\n')
|
|
os.chmod(output, 0o600)
|
|
return 0
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|