Skip to content
This repository has been archived by the owner on Apr 4, 2024. It is now read-only.

Commit

Permalink
tkt-77574: Add property localhost_ip (#884)
Browse files Browse the repository at this point in the history
* Add property localhost_ip

This property will allow a user to specify which address to assign during creation if assign_localhost is True and vnet is False.

- Add new method to generate unused IP's
- Alias localhost IP's during creation
- Check for active jail IP's and warn the user if they have a jail already using this IP as it may be fatal

FreeNAS Ticket: #77574

* Address review

* Simplify the code, make it only relevant to localhost generation

* Allow setting localhost_ip back to none

* Remove old artifacts, correct changing property value in start

* Write etc/hosts if generation of localhost_ip happens during start
  • Loading branch information
Brandon Schneider committed Mar 7, 2019
1 parent 47d7c28 commit 1bf088a
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 9 deletions.
25 changes: 25 additions & 0 deletions iocage_lib/ioc_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
import re
import shlex
import glob
import netifaces
import ipaddress

import iocage_lib.ioc_exceptions
import iocage_lib.ioc_exec
Expand Down Expand Up @@ -932,3 +934,26 @@ def is_tty():

def lowercase_set(values):
return set([v.lower() for v in values])


def gen_unused_lo_ip():
"""Best effort to try to allocate a localhost IP for a jail"""
interface_addrs = netifaces.ifaddresses('lo0')
inuse = [ip['addr'] for ips in interface_addrs.values() for ip in ips
if ip['addr'].startswith('127')]

for ip in ipaddress.IPv4Network('127.0.0.0/8'):
if str(ip) == '127.0.0.0':
continue

if str(ip) not in inuse:
return str(ip)

logit(
{
'level': 'EXCEPTION',
'message': 'An unused RFC5735 compliant localhost address could'
' not be allocated.\nIf you wish to use a non-RFC5735 compliant'
' address, please manually set the localhost_ip property.'
}
)
55 changes: 53 additions & 2 deletions iocage_lib/ioc_create.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,7 @@ def _create_jail(self, jail_uuid, location):
_callback=self.callback,
silent=self.silent)

disable_localhost = False
for prop in self.props:
key, _, value = prop.partition("=")
is_true = iocage_lib.ioc_common.check_truthy(value)
Expand Down Expand Up @@ -461,6 +462,39 @@ def _create_jail(self, jail_uuid, location):
_callback=self.callback,
silent=self.silent)
config['vnet'] = 1
elif key == 'assign_localhost' and is_true:
if iocage_lib.ioc_common.lowercase_set(
iocage_lib.ioc_common.construct_truthy(
'vnet'
)
) & iocage_lib.ioc_common.lowercase_set(self.props):
iocage_lib.ioc_common.logit({
'level': 'WARNING',
'message': 'assign_localhost only applies to shared'
' IP jails, disabling!'
},
_callback=self.callback,
silent=self.silent)
disable_localhost = True

if disable_localhost:
self.props = [p for p in self.props if not p.startswith(
'assign_localhost') and not p.startswith('localhost_ip')]
if not self.thickconfig:
try:
del config['assign_localhost']
except KeyError:
# They may not have specified this
pass

try:
del config['localhost_ip']
except KeyError:
# They may not have specified this
pass
else:
config['assign_localhost'] = 0
config['localhost_ip'] = 0

try:
value, config = iocjson.json_check_prop(key, value, config)
Expand Down Expand Up @@ -528,8 +562,25 @@ def _create_jail(self, jail_uuid, location):

for line in _etc_hosts.readlines():
if line.startswith("127.0.0.1"):
if config.get('assign_localhost'):
line = '127.0.1.1\t\tlocalhost' \
if config.get(
'assign_localhost'
) and not config.get('vnet'):
l_ip = config.get('localhost_ip', 'none')
l_ip = l_ip if l_ip != 'none' else \
iocage_lib.ioc_common.gen_unused_lo_ip()
config['localhost_ip'] = l_ip
iocjson.json_write(config)

# If they are creating multiple jails, we want
# this aliased before starting the jail
su.run(
[
'ifconfig', 'lo0', 'alias',
f'{l_ip}/32'
]
)

line = f'{l_ip}\t\tlocalhost' \
' localhost.my.domain' \
f' {jail_uuid_short}\n'
else:
Expand Down
25 changes: 22 additions & 3 deletions iocage_lib/ioc_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -709,7 +709,7 @@ def __init__(self, location, checking_datasets, silent, callback):
@staticmethod
def get_version():
"""Sets the iocage configuration version."""
version = '21'
version = '22'

return version

Expand Down Expand Up @@ -1058,6 +1058,10 @@ def check_config(self, conf, default=False):
if not conf.get('assign_localhost'):
conf['assign_localhost'] = 0

# Version 22 keys
if not conf.get('localhost_ip'):
conf['localhost_ip'] = 'none'

if not default:
conf.update(jail_conf)

Expand Down Expand Up @@ -1377,7 +1381,8 @@ def check_default_config(self):
'vnet_interfaces': 'none',
'rtsold': 0,
'ip_hostname': 0,
'assign_localhost': 0
'assign_localhost': 0,
'localhost_ip': 'none'
}

try:
Expand Down Expand Up @@ -2286,7 +2291,8 @@ def json_check_prop(self, key, value, conf):
"allow_tun": truth_variations,
'rtsold': truth_variations,
'ip_hostname': truth_variations,
'assign_localhost': truth_variations
'assign_localhost': truth_variations,
'localhost_ip': ('string', )
}

zfs_props = {
Expand Down Expand Up @@ -2452,6 +2458,19 @@ def json_check_prop(self, key, value, conf):
IOCRCTL.validate_rctl_props(key, value)
elif key == 'cpuset':
IOCCpuset.validate_cpuset_prop(value)
elif key == 'localhost_ip':
if value != 'none':
try:
ipaddress.IPv4Address(value)
except ipaddress.AddressValueError as e:
iocage_lib.ioc_common.logit(
{
'level': 'EXCEPTION',
'message': f'Invalid IPv4 address: {e}'
},
_callback=self.callback,
silent=self.silent
)

return value, conf
else:
Expand Down
42 changes: 38 additions & 4 deletions iocage_lib/ioc_start.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ def __start_jail__(self):
debug_mode = True if os.environ.get(
'IOCAGE_DEBUG', 'FALSE') == 'TRUE' else False
assign_localhost = self.conf['assign_localhost']
localhost_ip = self.conf['localhost_ip']

if wants_dhcp:
if not bpf:
Expand Down Expand Up @@ -292,13 +293,46 @@ def __start_jail__(self):
if assign_localhost:
# Make sure this exists, jail(8) will tear it down if we don't
# manually do this.
if self.check_aliases('127.0.1.1', '4') != '127.0.1.1':
su.run(['ifconfig', 'lo0', 'alias', '127.0.1.1/32'])
if localhost_ip == 'none':
localhost_ip = iocage_lib.ioc_common.gen_unused_lo_ip()
self.set(f'localhost_ip={localhost_ip}')

with open(
f'{self.path}/root/etc/hosts', 'r'
) as _etc_hosts:
with iocage_lib.ioc_common.open_atomic(
f'{self.path}/root/etc/hosts', 'w') as etc_hosts:
# open_atomic will empty the file, we need these still.
for line in _etc_hosts.readlines():
if line.startswith('127.0.0.1'):
line = line.replace('127.0.0.1', localhost_ip)

etc_hosts.write(line)

if self.check_aliases(localhost_ip, '4') != localhost_ip:
su.run(['ifconfig', 'lo0', 'alias', f'{localhost_ip}/32'])
else:
active_jail_ips = json.loads(su.run(
['jls', '-n', 'ip4.addr', '--libxo=json'],
capture_output=True
).stdout)['jail-information']['jail']
active_jail_ips = [
ip.get('ip4.addr') for ip in active_jail_ips
]

if localhost_ip in active_jail_ips:
iocage_lib.ioc_common.logit({
"level": "WARNING",
"message": f' {self.uuid} is reusing a localhost'
' address, failure may occur!'
},
_callback=self.callback,
silent=self.silent)

if ip4_addr == 'none':
ip4_addr = '127.0.1.1'
ip4_addr = localhost_ip
else:
ip4_addr += ',127.0.1.1'
ip4_addr += f',{localhost_ip}'

if ip4_addr != 'none':
ip4_addr = self.check_aliases(ip4_addr, '4')
Expand Down

0 comments on commit 1bf088a

Please sign in to comment.