checking in before i do some major restructuring of wifi stuff in the xml/xsd
This commit is contained in:
parent
3e33abe0a6
commit
edc78ea18e
10
aif.xsd
10
aif.xsd
@ -347,8 +347,6 @@
|
|||||||
</xs:element>
|
</xs:element>
|
||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
<xs:attribute name="auto" type="xs:boolean" use="optional" default="true"/>
|
<xs:attribute name="auto" type="xs:boolean" use="optional" default="true"/>
|
||||||
<xs:attribute name="defaultGateway" type="xs:boolean"
|
|
||||||
use="optional" default="false"/>
|
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
<xs:unique name="uniq_ipv4_route">
|
<xs:unique name="uniq_ipv4_route">
|
||||||
<xs:selector xpath="aif:route"/>
|
<xs:selector xpath="aif:route"/>
|
||||||
@ -377,8 +375,6 @@
|
|||||||
<!-- https://datatracker.ietf.org/doc/draft-ietf-mif-dhcpv6-route-option/
|
<!-- https://datatracker.ietf.org/doc/draft-ietf-mif-dhcpv6-route-option/
|
||||||
expired. Shame, that. -->
|
expired. Shame, that. -->
|
||||||
<xs:attribute name="auto" type="xs:boolean" use="optional" default="true"/>
|
<xs:attribute name="auto" type="xs:boolean" use="optional" default="true"/>
|
||||||
<xs:attribute name="defaultGateway" type="xs:boolean"
|
|
||||||
use="optional" default="false"/>
|
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
<xs:unique name="uniq_ipv6_route">
|
<xs:unique name="uniq_ipv6_route">
|
||||||
<xs:selector xpath="aif:route"/>
|
<xs:selector xpath="aif:route"/>
|
||||||
@ -443,6 +439,12 @@
|
|||||||
<xs:simpleContent>
|
<xs:simpleContent>
|
||||||
<xs:extension base="xs:token">
|
<xs:extension base="xs:token">
|
||||||
<xs:attribute name="type" use="optional" default="psk">
|
<xs:attribute name="type" use="optional" default="psk">
|
||||||
|
<!-- TODO: change this to sub-elements. <psk> or a <radius> thinger. -->
|
||||||
|
<!-- <psk raw="false">PSK_HERE</psk> -->
|
||||||
|
<!-- or e.g. wpa_passphrase test testingpsk -->
|
||||||
|
<!-- <psk raw="true">
|
||||||
|
124153ff24015a16d1993323b1840f3e6309ae24c07df7007d9fff8cff22f74c
|
||||||
|
</psk> -->
|
||||||
<xs:simpleType>
|
<xs:simpleType>
|
||||||
<xs:restriction base="xs:token">
|
<xs:restriction base="xs:token">
|
||||||
<xs:enumeration value="psk"/>
|
<xs:enumeration value="psk"/>
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
|
import binascii
|
||||||
import ipaddress
|
import ipaddress
|
||||||
import os
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
import re
|
import re
|
||||||
##
|
##
|
||||||
|
from passlib.crypto.digest import pbkdf2_hmac
|
||||||
from pyroute2 import IPDB
|
from pyroute2 import IPDB
|
||||||
##
|
##
|
||||||
import aif.utils
|
import aif.utils
|
||||||
@ -47,7 +49,22 @@ def convertIpTuples(addr_xmlobj):
|
|||||||
return((addr, net, gw))
|
return((addr, net, gw))
|
||||||
|
|
||||||
|
|
||||||
def convertWifiCrypto(crypto_xmlobj):
|
def convertPSK(ssid, passphrase):
|
||||||
|
try:
|
||||||
|
passphrase = passphrase.encode('utf-8').decode('ascii').strip('\r').strip('\n')
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
raise ValueError('passphrase must be an ASCII string')
|
||||||
|
if len(ssid) > 32:
|
||||||
|
raise ValueError('ssid must be <= 32 characters')
|
||||||
|
if not 7 < len(passphrase) < 64:
|
||||||
|
raise ValueError('passphrase must be >= 8 and <= 32 characters')
|
||||||
|
raw_psk = pbkdf2_hmac('sha1', str(passphrase), str(ssid), 4096, 32)
|
||||||
|
hex_psk = binascii.hexlify(raw_psk)
|
||||||
|
str_psk = hex_psk.decode('utf-8')
|
||||||
|
return(str_psk)
|
||||||
|
|
||||||
|
|
||||||
|
def convertWifiCrypto(crypto_xmlobj, ssid):
|
||||||
crypto = {'type': crypto_xmlobj.find('type').text.strip()}
|
crypto = {'type': crypto_xmlobj.find('type').text.strip()}
|
||||||
# if crypto['type'] in ('wpa', 'wpa2', 'wpa3'):
|
# if crypto['type'] in ('wpa', 'wpa2', 'wpa3'):
|
||||||
if crypto['type'] in ('wpa', 'wpa2'):
|
if crypto['type'] in ('wpa', 'wpa2'):
|
||||||
@ -61,7 +78,8 @@ def convertWifiCrypto(crypto_xmlobj):
|
|||||||
creds = crypto_xmlobj.find('creds')
|
creds = crypto_xmlobj.find('creds')
|
||||||
crypto['auth'] = {'type': creds.attrib.get('type', 'psk').strip()}
|
crypto['auth'] = {'type': creds.attrib.get('type', 'psk').strip()}
|
||||||
if crypto['auth']['type'] == 'psk':
|
if crypto['auth']['type'] == 'psk':
|
||||||
crypto['auth']['psk'] = creds.text
|
crypto['auth']['passphrase'] = creds.text.strip('\r').strip('\n')
|
||||||
|
crypto['auth']['psk'] = convertPSK(ssid, creds.text)
|
||||||
# TODO: enterprise support
|
# TODO: enterprise support
|
||||||
return(crypto)
|
return(crypto)
|
||||||
|
|
||||||
@ -211,3 +229,8 @@ class BaseConnection(object):
|
|||||||
if addrset not in self.routes[addrtype]:
|
if addrset not in self.routes[addrtype]:
|
||||||
self.routes[addrtype].append(addrset)
|
self.routes[addrtype].append(addrset)
|
||||||
return()
|
return()
|
||||||
|
|
||||||
|
def _writeConnCfg(self, chroot_base = None):
|
||||||
|
# Dummy method.
|
||||||
|
pass
|
||||||
|
return()
|
||||||
|
@ -274,6 +274,7 @@ class Wireless(Connection):
|
|||||||
def __init__(self, iface_xml):
|
def __init__(self, iface_xml):
|
||||||
super().__init__(iface_xml)
|
super().__init__(iface_xml)
|
||||||
self.connection_type = 'wireless'
|
self.connection_type = 'wireless'
|
||||||
|
self.packages.add('wpa_supplicant')
|
||||||
self._initCfg()
|
self._initCfg()
|
||||||
self._initConnCfg()
|
self._initConnCfg()
|
||||||
|
|
||||||
@ -291,8 +292,7 @@ class Wireless(Connection):
|
|||||||
self._cfg['BASE']['AP'] = bssid
|
self._cfg['BASE']['AP'] = bssid
|
||||||
crypto = self.xml.find('encryption')
|
crypto = self.xml.find('encryption')
|
||||||
if crypto:
|
if crypto:
|
||||||
self.packages.add('wpa_supplicant')
|
crypto = aif.network._common.convertWifiCrypto(crypto, self.xml.attrib['essid'])
|
||||||
crypto = aif.network._common.convertWifiCrypto(crypto)
|
|
||||||
# if crypto['type'] in ('wpa', 'wpa2', 'wpa3'):
|
# if crypto['type'] in ('wpa', 'wpa2', 'wpa3'):
|
||||||
if crypto['type'] in ('wpa', 'wpa2'):
|
if crypto['type'] in ('wpa', 'wpa2'):
|
||||||
# TODO: WPA2 enterprise
|
# TODO: WPA2 enterprise
|
||||||
|
19
aif/network/networkd.conf.j2
Normal file
19
aif/network/networkd.conf.j2
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# Generated by AIF-NG.
|
||||||
|
{%- for section_name, section_items in cfg.items() %}
|
||||||
|
{%- if section_items|isList %}
|
||||||
|
{#- We *only* use lists-of-dicts because they should always render to their own sections.
|
||||||
|
INI doesn't support nesting, thankfully. #}
|
||||||
|
{%- for i in section_items %}
|
||||||
|
[{{ section_name }}]
|
||||||
|
{%- for k, v in i.items() %}
|
||||||
|
{{ k }}={{ v }}
|
||||||
|
{%- endfor %}
|
||||||
|
{% endfor %}
|
||||||
|
{%- else %}
|
||||||
|
{#- It's a single-level dict. #}
|
||||||
|
[{{ section_name }}]
|
||||||
|
{%- for k, v in section_items.items() %}
|
||||||
|
{{ k }}={{ v }}
|
||||||
|
{%- endfor %}
|
||||||
|
{%- endif %}
|
||||||
|
{% endfor %}
|
@ -1,8 +1,157 @@
|
|||||||
import ipaddress
|
import os
|
||||||
import socket
|
|
||||||
##
|
##
|
||||||
# We have to use Jinja2 because while there are ways to *parse* an INI with duplicate keys
|
# We have to use Jinja2 because while there are ways to *parse* an INI with duplicate keys
|
||||||
# (https://stackoverflow.com/a/38286559/733214), there's no way to *write* an INI with them using configparser.
|
# (https://stackoverflow.com/a/38286559/733214), there's no way to *write* an INI with them using configparser.
|
||||||
# So we use Jinja2 logic.
|
# So we use Jinja2 logic.
|
||||||
import jinja2
|
import jinja2
|
||||||
|
##
|
||||||
|
import aif.utils
|
||||||
|
import aif.network._common
|
||||||
|
|
||||||
|
|
||||||
|
class Connection(aif.network._common.BaseConnection):
|
||||||
|
def __init__(self, iface_xml):
|
||||||
|
super().__init__(iface_xml)
|
||||||
|
self.provider_type = 'systemd-networkd'
|
||||||
|
self.packages = set()
|
||||||
|
self.services = {
|
||||||
|
('/usr/lib/systemd/system/systemd-networkd.service'): ('etc/systemd/system/'
|
||||||
|
'multi-user.target.wants/'
|
||||||
|
'systemd-networkd.service'),
|
||||||
|
('/usr/lib/systemd/system/systemd-networkd.service'): ('etc/systemd/system/'
|
||||||
|
'dbus-org.freedesktop.network1.service'),
|
||||||
|
('/usr/lib/systemd/system/systemd-networkd.socket'): ('etc/systemd/system/'
|
||||||
|
'sockets.target.wants/systemd-networkd.socket'),
|
||||||
|
('/usr/lib/systemd/system/systemd-networkd.socket'): ('etc/systemd/system/'
|
||||||
|
'network-online.target.wants/'
|
||||||
|
'systemd-networkd-wait-online.service'),
|
||||||
|
# We include these *even if* self.auto['resolvers'][*] are false.
|
||||||
|
('/usr/lib/systemd/system/systemd-resolved.service'): ('etc/systemd/system/'
|
||||||
|
'dbus-org.freedesktop.resolve1.service'),
|
||||||
|
('/usr/lib/systemd/system/systemd-resolved.service'): ('etc/systemd/'
|
||||||
|
'system/multi-user.target.wants/'
|
||||||
|
'systemd-resolved.service')}
|
||||||
|
self._wpasupp = {}
|
||||||
|
self._initJ2()
|
||||||
|
|
||||||
|
def _initCfg(self):
|
||||||
|
if self.device == 'auto':
|
||||||
|
self.device = aif.network._common.getDefIface(self.connection_type)
|
||||||
|
self._cfg = {'Match': {'Name': self.device},
|
||||||
|
'Network': {'Description': ('A {0} profile for {1} '
|
||||||
|
'(generated by AIF-NG)').format(self.connection_type,
|
||||||
|
self.device),
|
||||||
|
'DefaultRouteOnDevice': ('true' if self.is_defroute else 'false'),
|
||||||
|
# This (may) get modified by logic below.
|
||||||
|
'IPv6AcceptRA': 'false',
|
||||||
|
'LinkLocalAddressing': 'no'}}
|
||||||
|
if self.domain:
|
||||||
|
self._cfg['Network']['Domains'] = self.domain
|
||||||
|
if self.resolvers:
|
||||||
|
self._cfg['Network']['DNS'] = [str(ip) for ip in self.resolvers]
|
||||||
|
if all((self.auto['addresses']['ipv4'], self.auto['addresses']['ipv6'])):
|
||||||
|
self._cfg['Network']['IPv6AcceptRA'] = 'true'
|
||||||
|
self._cfg['Network']['LinkLocalAddressing'] = 'ipv6'
|
||||||
|
self._cfg['Network']['DHCP'] = 'yes'
|
||||||
|
elif self.auto['addresses']['ipv4'] and not self.auto['addresses']['ipv6']:
|
||||||
|
self._cfg['Network']['DHCP'] = 'ipv4'
|
||||||
|
elif (not self.auto['addresses']['ipv4']) and self.auto['addresses']['ipv6']:
|
||||||
|
self._cfg['Network']['IPv6AcceptRA'] = 'true'
|
||||||
|
self._cfg['Network']['LinkLocalAddressing'] = 'ipv6'
|
||||||
|
self._cfg['Network']['DHCP'] = 'ipv6'
|
||||||
|
else:
|
||||||
|
self._cfg['Network']['DHCP'] = 'no'
|
||||||
|
if any((self.auto['addresses']['ipv4'], self.auto['routes']['ipv4'], self.auto['resolvers']['ipv4'])):
|
||||||
|
t = 'ipv4'
|
||||||
|
self._cfg['DHCPv4'] = {'UseDNS': ('true' if self.auto['resolvers'][t] else 'false'),
|
||||||
|
'UseRoutes': ('true' if self.auto['routes'][t] else 'false')}
|
||||||
|
if any((self.auto['addresses']['ipv6'], self.auto['routes']['ipv6'], self.auto['resolvers']['ipv6'])):
|
||||||
|
t = 'ipv6'
|
||||||
|
self._cfg['Network']['IPv6AcceptRA'] = 'true'
|
||||||
|
self._cfg['DHCPv6'] = {'UseDNS': ('true' if self.auto['resolvers'][t] else 'false')}
|
||||||
|
for t in ('ipv4', 'ipv6'):
|
||||||
|
if self.addrs[t]:
|
||||||
|
if t == 'ipv6':
|
||||||
|
self._cfg['Network']['LinkLocalAddressing'] = 'ipv6'
|
||||||
|
if 'Address' not in self._cfg.keys():
|
||||||
|
self._cfg['Address'] = []
|
||||||
|
for addr, net, gw in self.addrs[t]:
|
||||||
|
a = {'Address': '{0}/{1}'.format(str(addr), str(net.prefixlen))}
|
||||||
|
self._cfg['Address'].append(a)
|
||||||
|
if self.routes[t]:
|
||||||
|
if 'Route' not in self._cfg.keys():
|
||||||
|
self._cfg['Route'] = []
|
||||||
|
for route, net, gw in self.routes[t]:
|
||||||
|
r = {'Gateway': str(gw),
|
||||||
|
'Destination': '{0}/{1}'.format(str(route), str(net.prefixlen))}
|
||||||
|
self._cfg['Route'].append(r)
|
||||||
|
if self._cfg['Network']['IPv6AcceptRA'] == 'true':
|
||||||
|
self._cfg['Network']['LinkLocalAddressing'] = 'ipv6'
|
||||||
|
if 'IPv6AcceptRA' not in self._cfg.keys():
|
||||||
|
self._cfg['IPv6AcceptRA'] = {'UseDNS': ('true' if self.auto['resolvers']['ipv6'] else 'false')}
|
||||||
|
self._initConnCfg()
|
||||||
|
return()
|
||||||
|
|
||||||
|
def _initJ2(self):
|
||||||
|
self.j2_env = jinja2.Environment(loader = jinja2.FileSystemLoader(searchpath = './'))
|
||||||
|
self.j2_env.filters.update(aif.utils.j2_filters)
|
||||||
|
self.j2_tpl = self.j2_env.get_template('networkd.conf.j2')
|
||||||
|
return()
|
||||||
|
|
||||||
|
def writeConf(self, chroot_base):
|
||||||
|
cfgroot = os.path.join(chroot_base, 'etc', 'systemd', 'network')
|
||||||
|
cfgfile = os.path.join(cfgroot, self.id)
|
||||||
|
os.makedirs(cfgroot, exist_ok = True)
|
||||||
|
os.chown(cfgroot, 0, 0)
|
||||||
|
os.chmod(cfgroot, 0o0755)
|
||||||
|
with open(cfgfile, 'w') as fh:
|
||||||
|
fh.write(self.j2_tpl.render(cfg = self._cfg))
|
||||||
|
os.chmod(cfgfile, 0o0644)
|
||||||
|
os.chown(cfgfile, 0, 0)
|
||||||
|
self._writeConnCfg(chroot_base)
|
||||||
|
return()
|
||||||
|
|
||||||
|
|
||||||
|
class Ethernet(Connection):
|
||||||
|
def __init__(self, iface_xml):
|
||||||
|
super().__init__(iface_xml)
|
||||||
|
self.connection_type = 'ethernet'
|
||||||
|
self._initCfg()
|
||||||
|
|
||||||
|
|
||||||
|
class Wireless(Connection):
|
||||||
|
def __init__(self, iface_xml):
|
||||||
|
super().__init__(iface_xml)
|
||||||
|
self.connection_type = 'wireless'
|
||||||
|
self.packages.add('wpa_supplicant')
|
||||||
|
self.services['src'] = 'dest'
|
||||||
|
self._initCfg()
|
||||||
|
|
||||||
|
def _initConnCfg(self):
|
||||||
|
self._wpasupp['ssid'] = self.xml.attrib['essid']
|
||||||
|
hidden = aif.utils.xmlBool(self.xml.attrib.get('hidden', 'false'))
|
||||||
|
if hidden:
|
||||||
|
self._wpasupp['scan_ssid'] = 1
|
||||||
|
try:
|
||||||
|
bssid = self.xml.attrib.get('bssid').strip()
|
||||||
|
except AttributeError:
|
||||||
|
bssid = None
|
||||||
|
if bssid:
|
||||||
|
bssid = aif.network._common.canonizeEUI(bssid)
|
||||||
|
self._cfg['BASE']['AP'] = bssid
|
||||||
|
crypto = self.xml.find('encryption')
|
||||||
|
if crypto:
|
||||||
|
crypto = aif.network._common.convertWifiCrypto(crypto, self._cfg['BASE']['ESSID'])
|
||||||
|
# if crypto['type'] in ('wpa', 'wpa2', 'wpa3'):
|
||||||
|
if crypto['type'] in ('wpa', 'wpa2'):
|
||||||
|
# TODO: WPA2 enterprise
|
||||||
|
self._cfg['BASE']['Security'] = 'wpa'
|
||||||
|
# if crypto['type'] in ('wep', 'wpa', 'wpa2', 'wpa3'):
|
||||||
|
if crypto['type'] in ('wpa', 'wpa2'):
|
||||||
|
self._cfg['BASE']['Key'] = crypto['auth']['psk']
|
||||||
|
return()
|
||||||
|
|
||||||
|
def _writeConnCfg(self, chroot_base):
|
||||||
|
cfgroot = os.path.join(chroot_base, 'etc', 'wpa_supplicant')
|
||||||
|
cfgbase = os.path.join(cfgroot, 'wpa_supplicant.conf')
|
||||||
|
cfgfile = os.path.join(cfgroot, self.id)
|
||||||
|
@ -145,7 +145,7 @@ class Wireless(Connection):
|
|||||||
if crypto:
|
if crypto:
|
||||||
self.packages.add('wpa_supplicant')
|
self.packages.add('wpa_supplicant')
|
||||||
self._cfg['wifi-security'] = {}
|
self._cfg['wifi-security'] = {}
|
||||||
crypto = aif.network._common.convertWifiCrypto(crypto)
|
crypto = aif.network._common.convertWifiCrypto(crypto, self._cfg['wifi']['ssid'])
|
||||||
# if crypto['type'] in ('wpa', 'wpa2', 'wpa3'):
|
# if crypto['type'] in ('wpa', 'wpa2', 'wpa3'):
|
||||||
if crypto['type'] in ('wpa', 'wpa2'):
|
if crypto['type'] in ('wpa', 'wpa2'):
|
||||||
# TODO: WPA2 enterprise
|
# TODO: WPA2 enterprise
|
||||||
|
14
aif/utils.py
14
aif/utils.py
@ -57,6 +57,20 @@ def isPowerofTwo(n):
|
|||||||
return(isPowerOf2)
|
return(isPowerOf2)
|
||||||
|
|
||||||
|
|
||||||
|
# custom Jinja2 filters
|
||||||
|
def j2_isDict(value):
|
||||||
|
return(isinstance(value, dict))
|
||||||
|
|
||||||
|
|
||||||
|
def j2_isList(value):
|
||||||
|
return(isinstance(value, list))
|
||||||
|
|
||||||
|
|
||||||
|
j2_filters = {'isDict': j2_isDict,
|
||||||
|
'isList': j2_isList}
|
||||||
|
# end custom Jinja2 filters
|
||||||
|
|
||||||
|
|
||||||
def kernelCmdline(chroot_base = '/'):
|
def kernelCmdline(chroot_base = '/'):
|
||||||
cmds = {}
|
cmds = {}
|
||||||
chroot_base = pathlib.PosixPath(chroot_base)
|
chroot_base = pathlib.PosixPath(chroot_base)
|
||||||
|
@ -574,32 +574,35 @@ LVM (LVs, in particular), however, aren't consecutive. There *is* no concept of
|
|||||||
=== "How do I specify packages from the AUR?"
|
=== "How do I specify packages from the AUR?"
|
||||||
You'd have to https://wiki.archlinux.org/index.php/Makepkg[build the package(s)^], https://wiki.archlinux.org/index.php/Pacman/Tips_and_tricks#Custom_local_repository[set up a repository^], serve it via e.g. https://www.nginx.com/[nginx^], and add it as a repo (`/aif/pacman/repos/repo`) first. Then you can specify the package as normal as a `/aif/pacman/software/package` item.
|
You'd have to https://wiki.archlinux.org/index.php/Makepkg[build the package(s)^], https://wiki.archlinux.org/index.php/Pacman/Tips_and_tricks#Custom_local_repository[set up a repository^], serve it via e.g. https://www.nginx.com/[nginx^], and add it as a repo (`/aif/pacman/repos/repo`) first. Then you can specify the package as normal as a `/aif/pacman/software/package` item.
|
||||||
|
|
||||||
=== "Why aren't the network settings in <network> being applied during install?"
|
=== "Why can't the network settings in <network> be applied during install?"
|
||||||
Simply put, a logical race condition. In order for probably 90+% of AIF-NG deploys to bootstrap, they fetch their XML configuration via a network URI (rather than a file URI). This means it needs a network connection that pre-exists in the *install environment* (LiveCD, LiveUSB, PXE/iPXE, etc.) before it even knows what network configuration you want the *persistent environment* to have.
|
Simply put, a logical race condition. In order for probably 90+% of AIF-NG deploys to bootstrap, they fetch their XML configuration via a network URI (rather than a file URI). This means it needs a network connection that pre-exists in the *install environment* (LiveCD, LiveUSB, PXE/iPXE, etc.) before it even knows what network configuration you want the *persistent environment* to have.
|
||||||
|
|
||||||
Granted, this is a moot point if you're using a *`file://`* URI for the XML configuration, but this is not a very flexible means regardless. If demand increases for this, future releases may include this functionality.
|
Granted, this is a moot point if you're using a *`file://`* URI for the XML configuration, but this is not a very flexible means regardless. The installation host itself is outside the scope of AIF-NG.
|
||||||
|
|
||||||
If you desire the configuration to be applied *during* the install, you can do it yourself in an `/aif/scripts/pre/script` or `/aif/scripts/pkg/script` script. The fetched XML file can be found at `/var/tmp/AIF.xml` in the install environment. (Alternatively, configure the network yourself procedurally using one of those scripts).
|
If you desire the configuration to be applied *during* the install, you can do it yourself in an `/aif/scripts/pre/script` or `/aif/scripts/pkg/script` script. The fetched XML file can be found at `/var/tmp/AIF.xml` in the install environment.
|
||||||
|
|
||||||
If you wish to SSH into the install environment to check the status/progress of the install, it is recommended that you set up a static lease (if using DHCP) or use SLAAC (if using IPv6) beforehand and configure your install environment beforehand. Remember, AIF-NG only *installs* Arch Linux; it tries very hard to *not* interact with the install environment.
|
If you wish to SSH into the install environment to check the status/progress of the install, it is recommended that you set up a static lease (if using DHCP) or use SLAAC (if using IPv6) beforehand and configure your install environment beforehand. Remember, AIF-NG only *installs* Arch Linux; it tries very hard to *not* interact with the install environment.
|
||||||
|
|
||||||
=== "Why isn't enabling/disabling automatic DNS resolvers working?"
|
=== "Why isn't enabling/disabling automatic DNS resolvers/routes/addresses working?"
|
||||||
This is going to be highly unpredictable based on the networking provider you choose. This is a limitation of underlying network provider intercompatibility, resolver libraries, and technology architecture. This may be changed in the future, but because of how DNS servers are handled via DHCP/RDNSS and glibc (and the fact that IPv4 resolver addresses can serve IPv6 -- e.g. AAAA -- records and vice versa) and inherent limitations in some network providers like netctl, I wouldn't hold your breath.
|
This is going to be highly unpredictable based on the networking provider you choose. This is a limitation of underlying network provider intercompatibility, resolver libraries, there being no way to tell DHCP/DHCP6/SLAAC clients to *only* fetch information about a network and *not* assign a lease, and technology architecture. This may be changed in the future, but because of how DNS servers are handled via DHCP/RDNSS and glibc (and the fact that IPv4 resolver addresses can serve IPv6 -- e.g. AAAA -- records and vice versa) and inherent limitations in some network providers like netctl, I wouldn't hold your breath.
|
||||||
|
|
||||||
=== "I'm using netctl as my network provider, and-"
|
=== "I'm using netctl as my network provider, and-"
|
||||||
I'ma let you finish, but netctl is a *really* simple network provider. I mean REALLY simple. As such, a lot of things (like mixing auto DNS and non-auto addressing) don't work at all feasibly, and probably might not ever. It's great for simple and flat configurations (i.e. all static everything, all automatic everything, etc.) and I even use it on my own machines where I can, but it just simply doesn't make allowances for more complex setups. (This is why init scripts were replaced by systemd for init, remember? Script-and-shell-based utilities, such as netctl -- seriously, the entire thing's written in Bash -- just can't handle more complex jobs reliably.)
|
I'ma let you finish, but netctl is a *really* simple network provider. I mean REALLY simple. As such, a lot of things don't work at all feasibly, and probably might not ever. It's great for simple and flat configurations (i.e. all static everything, all automatic everything, etc.) and I even use it on my own machines where I can, but it just simply doesn't make allowances for more complex setups. (This is why init scripts were replaced by systemd for init, remember? Script-and-shell-based utilities, such as netctl -- seriously, the entire thing's written in Bash -- just can't handle more complex jobs reliably.)
|
||||||
|
|
||||||
If you need more advanced functionality but don't want a lot of cruft or bloat, I recommend `networkd` as your network provider. It requires no extra packages (other than wpa_supplicant, if you're using wireless) because it's part of the systemd package (which is part of the most basic install of Arch) and handles more advanced configurations a lot more reliably.
|
If you need more advanced functionality but don't want a lot of cruft or bloat, I recommend `networkd` as your network provider. It requires no extra packages (other than wpa_supplicant, if you're using wireless) because it's part of the systemd package (which is part of the most basic install of Arch) and handles more advanced configurations a lot more reliably.
|
||||||
|
|
||||||
=== "How do I specify WEP for a wireless network?"
|
=== "How do I specify WEP for a wireless network?"
|
||||||
You can't. WEP's pretty broken. I understand some legacy networks may still use it, but I'm incredibly uncomfortable supporting it.
|
You can't. WEP's pretty broken. I understand some legacy networks may still use it, but I'm incredibly uncomfortable supporting it.
|
||||||
|
|
||||||
If absolutely necessary, you can manually configure it yourself via a `/aif/scripts/post/script` script.
|
If absolutely necessary, you can manually configure it yourself via a `/aif/scripts/post/script` script (or just configure it once you boot the newly-installed system).
|
||||||
|
|
||||||
=== "How do I connect to a WPA2 Enterprise network?"
|
==== "Then why do you allow connecting to open wireless networks in the config?"
|
||||||
|
Because captive portals are a thing. *Authing* to them, however; that's out of my scope.
|
||||||
|
|
||||||
|
=== "How do I configure connecting to a WPA2 Enterprise network?"
|
||||||
You can't, currently; support is only stubbed out for now. If absolutely necessary, you can manually configure it yourself via a `/aif/scripts/post/script` script.
|
You can't, currently; support is only stubbed out for now. If absolutely necessary, you can manually configure it yourself via a `/aif/scripts/post/script` script.
|
||||||
|
|
||||||
This hopefully will be changed in the future, however, as I'm interested in adding support. For now, WPA/WPA2 PSK only are considered supported.
|
This hopefully will be changed in the future, however, as I'm interested in adding support. For now, open and WPA/WPA2 PSK only are considered supported.
|
||||||
|
|
||||||
== Bug Reports/Feature Requests
|
== Bug Reports/Feature Requests
|
||||||
NOTE: It is possible to submit a bug or feature request without registering in my bugtracker. One of my pet peeves is needing to create an account/register on a bugtracker simply to report a bug! The following links only require an email address to file a bug (which is necessary in case I need any further clarification from you or to keep you updated on the status of the bug/feature request -- so please be sure to use a valid email address).
|
NOTE: It is possible to submit a bug or feature request without registering in my bugtracker. One of my pet peeves is needing to create an account/register on a bugtracker simply to report a bug! The following links only require an email address to file a bug (which is necessary in case I need any further clarification from you or to keep you updated on the status of the bug/feature request -- so please be sure to use a valid email address).
|
||||||
|
@ -130,11 +130,11 @@
|
|||||||
</ipv6>
|
</ipv6>
|
||||||
</addresses>
|
</addresses>
|
||||||
<routes>
|
<routes>
|
||||||
<ipv4 defaultGateway="true" auto="true">
|
<ipv4 auto="true">
|
||||||
<route gateway="192.168.1.1">10.1.1.0/24</route>
|
<route gateway="192.168.1.1">10.1.1.0/24</route>
|
||||||
<route gateway="10.1.1.4">172.16.1.20/32</route>
|
<route gateway="10.1.1.4">172.16.1.20/32</route>
|
||||||
</ipv4>
|
</ipv4>
|
||||||
<ipv6 defaultGateway="false" auto="true"/>
|
<ipv6 auto="true"/>
|
||||||
</routes>
|
</routes>
|
||||||
<resolvers>
|
<resolvers>
|
||||||
<ipv4 auto="false"/>
|
<ipv4 auto="false"/>
|
||||||
|
82
extras/genPSK.py
Executable file
82
extras/genPSK.py
Executable file
@ -0,0 +1,82 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import binascii
|
||||||
|
import getpass
|
||||||
|
import sys
|
||||||
|
##
|
||||||
|
# from passlib.utils import pbkdf2 # deprecated
|
||||||
|
from passlib.crypto.digest import pbkdf2_hmac
|
||||||
|
|
||||||
|
|
||||||
|
def pskGen(ssid, passphrase):
|
||||||
|
# raw_psk = pbkdf2.pbkdf2(str(passphrase), str(ssid), 4096, 32) # deprecated
|
||||||
|
raw_psk = pbkdf2_hmac('sha1', str(passphrase), str(ssid), 4096, 32)
|
||||||
|
hex_psk = binascii.hexlify(raw_psk)
|
||||||
|
str_psk = hex_psk.decode('utf-8')
|
||||||
|
return(str_psk)
|
||||||
|
|
||||||
|
|
||||||
|
def parseArgs():
|
||||||
|
def essidchk(essid):
|
||||||
|
essid = str(essid)
|
||||||
|
if len(essid) > 32:
|
||||||
|
raise argparse.ArgumentTypeError('The maximum length of an ESSID is 32 characters')
|
||||||
|
return(essid)
|
||||||
|
|
||||||
|
def passphrasechk(passphrase):
|
||||||
|
if passphrase:
|
||||||
|
is_piped = False
|
||||||
|
passphrase = str(passphrase)
|
||||||
|
if passphrase == '-':
|
||||||
|
if sys.stdin.isatty():
|
||||||
|
raise argparse.ArgumentTypeError(('[STDIN] You specified a passphrase to be entered but did not '
|
||||||
|
'provide one via a pipe.'))
|
||||||
|
else:
|
||||||
|
is_piped = True
|
||||||
|
try:
|
||||||
|
# WPA-PSK only accepts ASCII for passphrase.
|
||||||
|
raw_pass = sys.stdin.read().encode('utf-8').decode('ascii').strip('\r').strip('\n')
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
raise argparse.ArgumentTypeError('[STDIN] WPA-PSK passphrases must be an ASCII string')
|
||||||
|
if not 7 < len(passphrase) < 64:
|
||||||
|
raise argparse.ArgumentTypeError(('{0}WPA-PSK passphrases must be no shorter than 8 characters'
|
||||||
|
' and no longer than 63 characters. '
|
||||||
|
'Please ensure you have provided the '
|
||||||
|
'correct passphrase.').format(('[STDIN] ' if is_piped else '')))
|
||||||
|
return(passphrase)
|
||||||
|
|
||||||
|
args = argparse.ArgumentParser(description = 'Generate a PSK from a passphrase')
|
||||||
|
args.add_argument('-p', '--passphrase',
|
||||||
|
dest = 'passphrase',
|
||||||
|
default = None,
|
||||||
|
type = passphrasechk,
|
||||||
|
help = ('If specified, use this passphrase (otherwise securely interactively prompt for it). '
|
||||||
|
'If "-" (without quotes), read from stdin (via a pipe). '
|
||||||
|
'WARNING: THIS OPTION IS INSECURE AND MAY EXPOSE THE PASSPHRASE GIVEN '
|
||||||
|
'TO OTHER PROCESSES ON THIS SYSTEM'))
|
||||||
|
args.add_argument('ssid',
|
||||||
|
metavar = 'ESSID',
|
||||||
|
type = essidchk,
|
||||||
|
help = ('The ESSID (network name) to use for this passphrase. '
|
||||||
|
'(This is required because WPA-PSK uses it to salt the key derivation)'))
|
||||||
|
return(args)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
args = parseArgs().parse_args()
|
||||||
|
if not args.passphrase:
|
||||||
|
args.passphrase = getpass.getpass(('Please enter the passphrase for '
|
||||||
|
'network "{0}" (will NOT echo back): ').format(args.ssid))
|
||||||
|
args.passphrase = args.passphrase.encode('utf-8').decode('ascii').strip('\r').strip('\n')
|
||||||
|
if not 7 < len(args.passphrase) < 64:
|
||||||
|
raise ValueError(('WPA-PSK passphrases must be no shorter than 8 characters'
|
||||||
|
' and no longer than 63 characters. '
|
||||||
|
'Please ensure you have provided the correct passphrase.'))
|
||||||
|
psk = pskGen(args.ssid, args.passphrase)
|
||||||
|
print('PSK for network "{0}": {1}'.format(args.ssid, psk))
|
||||||
|
return()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
Loading…
Reference in New Issue
Block a user