def generate(data): render(charon_conf_file, 'ipsec/charon.tmpl', data, trim_blocks=True) if data["ipsec_l2tp"]: remove_confs(delim_ipsec_l2tp_begin, delim_ipsec_l2tp_end, ipsec_secrets_file) # old_umask = os.umask(0o077) # render(ipsec_secrets_file, 'ipsec/ipsec.secrets.tmpl', data, trim_blocks=True) # os.umask(old_umask) ## Use this method while IPSec CLI handler won't be overwritten to python write_ipsec_secrets(data) old_umask = os.umask(0o077) # Create tunnels directory if does not exist if not os.path.exists(ipsec_ra_conn_dir): os.makedirs(ipsec_ra_conn_dir) render(ipsec_ra_conn_file, 'ipsec/remote-access.tmpl', data, trim_blocks=True) os.umask(old_umask) remove_confs(delim_ipsec_l2tp_begin, delim_ipsec_l2tp_end, ipsec_conf_file) # old_umask = os.umask(0o077) # render(ipsec_conf_file, 'ipsec/ipsec.conf.tmpl', data, trim_blocks=True) # os.umask(old_umask) ## Use this method while IPSec CLI handler won't be overwritten to python write_ipsec_conf(data) else: if os.path.exists(ipsec_ra_conn_file): remove_confs(delim_ipsec_l2tp_begin, delim_ipsec_l2tp_end, ipsec_ra_conn_file) remove_confs(delim_ipsec_l2tp_begin, delim_ipsec_l2tp_end, ipsec_secrets_file) remove_confs(delim_ipsec_l2tp_begin, delim_ipsec_l2tp_end, ipsec_conf_file)
def generate(ntp): # bail out early - looks like removal from running config if ntp is None: return None render(config_file, 'ntp/ntp.conf.tmpl', ntp) return None
def generate(relay): # bail out early - looks like removal from running config if relay is None: return None render(config_file, 'dhcpv6-relay/config.tmpl', relay) return None
def generate(bgp): # bail out early - looks like removal from running config if not bgp: return None render(config_file, 'frr/bgp.frr.tmpl', bgp) return None
def set(self): """ Configure interface as DHCPv6 client. The dhclient binary is automatically started in background! Example: >>> from vyos.ifconfig import Interface >>> j = Interface('eth0') >>> j.dhcp.v6.set() """ # better save then sorry .. should be checked in interface script # but if you missed it we are safe! if self.options['dhcpv6_prm_only'] and self.options['dhcpv6_temporary']: raise Exception( 'DHCPv6 temporary and parameters-only options are mutually exclusive!' ) render(self._conf_file, 'dhcp-client/ipv6.tmpl', self.options, trim_blocks=True) return self._cmd( 'systemctl restart dhcp6c@{ifname}.service'.format(**self.options))
def generate(config): # skip all checks if flow-accounting was removed if not config['flow-accounting-configured']: return True # Calculate all necessary values if config['buffer-size']: # circular queue size config['plugin_pipe_size'] = int(config['buffer-size']) * 1024**2 else: config['plugin_pipe_size'] = default_plugin_pipe_size * 1024**2 # transfer buffer size # recommended value from pmacct developers 1/1000 of pipe size config['plugin_buffer_size'] = int(config['plugin_pipe_size'] / 1000) # Prepare a timeouts string timeout_string = '' for timeout_type, timeout_value in config['netflow']['timeout'].items(): if timeout_value: if timeout_string == '': timeout_string = "{}{}={}".format(timeout_string, timeout_type, timeout_value) else: timeout_string = "{}:{}={}".format(timeout_string, timeout_type, timeout_value) config['netflow']['timeout_string'] = timeout_string render(uacctd_conf_path, 'netflow/uacctd.conf.tmpl', { 'templatecfg': config, 'snaplen': default_captured_packet_size, })
def generate(openvpn): interface = openvpn['intf'] directory = os.path.dirname(get_config_name(interface)) # we can't know in advance which clients have been removed, # thus all client configs will be removed and re-added on demand ccd_dir = os.path.join(directory, 'ccd', interface) if os.path.isdir(ccd_dir): rmtree(ccd_dir, ignore_errors=True) if openvpn['deleted'] or openvpn['disable']: return None # create config directory on demand directories = [] directories.append(f'{directory}/status') directories.append(f'{directory}/ccd/{interface}') for onedir in directories: if not os.path.exists(onedir): os.makedirs(onedir, 0o755) chown(onedir, user, group) # Fix file permissons for keys fix_permissions = [] fix_permissions.append(openvpn['shared_secret_file']) fix_permissions.append(openvpn['tls_key']) # Generate User/Password authentication file if openvpn['auth']: with open(openvpn['auth_user_pass_file'], 'w') as f: f.write('{}\n{}'.format(openvpn['auth_user'], openvpn['auth_pass'])) # also change permission on auth file fix_permissions.append(openvpn['auth_user_pass_file']) else: # delete old auth file if present if os.path.isfile(openvpn['auth_user_pass_file']): os.remove(openvpn['auth_user_pass_file']) # Generate client specific configuration for client in openvpn['client']: client_file = os.path.join(ccd_dir, client['name']) render(client_file, 'openvpn/client.conf.tmpl', client) chown(client_file, user, group) # we need to support quoting of raw parameters from OpenVPN CLI # see https://phabricator.vyos.net/T1632 render(get_config_name(interface), 'openvpn/server.conf.tmpl', openvpn, formater=lambda _: _.replace(""", '"')) chown(get_config_name(interface), user, group) # Fixup file permissions for file in fix_permissions: chmod_600(file) return None
def generate(nat): render(iptables_nat_config, 'firewall/nftables-nat.tmpl', nat, trim_blocks=True, permission=0o755) return None
def generate(mdns): if mdns is None: return None if mdns['disabled']: print('Warning: mDNS repeater will be deactivated because it is disabled') return None render(config_file, 'mdns-repeater/mdns-repeater.tmpl', mdns) return None
def generate(https): if https is None: return None if 'server_block_list' not in https or not https['server_block_list']: https['server_block_list'] = [default_server_block] render(config_file, 'https/nginx.default.tmpl', https, trim_blocks=True) return None
def generate(dhcp): if not dhcp or dhcp['disabled']: return None # Please see: https://phabricator.vyos.net/T1129 for quoting of the raw parameters # we can pass to ISC DHCPd render(config_file, 'dhcp-server/dhcpd.conf.tmpl', dhcp, formater=lambda _: _.replace(""", '"')) return None
def generate(rtradv): if not rtradv['interfaces']: return None render(config_file, 'router-advert/radvd.conf.tmpl', rtradv, trim_blocks=True) # adjust file permissions of new configuration file if os.path.exists(config_file): os.chmod(config_file, S_IRUSR | S_IWUSR | S_IRGRP) return None
def generate(c): if c == None: return None conf = '/etc/rsyslog.d/vyos-rsyslog.conf' render(conf, 'syslog/rsyslog.conf.tmpl', c, trim_blocks=True) # eventually write for each file its own logrotate file, since size is # defined it shouldn't matter conf = '/etc/logrotate.d/vyos-rsyslog' render(conf, 'syslog/logrotate.tmpl', c, trim_blocks=True)
def generate(dyndns): # bail out early - looks like removal from running config if dyndns['deleted']: return None render(config_file, 'dynamic-dns/ddclient.conf.tmpl', dyndns) # Config file must be accessible only by its owner os.chmod(config_file, S_IRUSR | S_IWUSR) return None
def generate(dns): # bail out early - looks like removal from running config if dns is None: return None render(config_file, 'dns-forwarding/recursor.conf.tmpl', dns, trim_blocks=True, user='******', group='pdns') return None
def generate(igmp_proxy): # bail out early - looks like removal from running config if igmp_proxy is None: return None # bail out early - service is disabled, but inform user if igmp_proxy['disable']: print('Warning: IGMP Proxy will be deactivated because it is disabled') return None render(config_file, 'igmp-proxy/igmpproxy.conf.tmpl', igmp_proxy) return None
def generate(login): # calculate users encrypted password for user in login['add_users']: if user['password_plaintext']: user['password_encrypted'] = crypt(user['password_plaintext'], METHOD_SHA512) user['password_plaintext'] = '' # remove old plaintext password and set new encrypted password env = os.environ.copy() env['vyos_libexec_dir'] = '/usr/libexec/vyos' call("/opt/vyatta/sbin/my_set system login user '{name}' " "authentication plaintext-password '{password_plaintext}'". format(**user), env=env) call("/opt/vyatta/sbin/my_set system login user '{name}' " "authentication encrypted-password '{password_encrypted}'". format(**user), env=env) else: try: if getspnam( user['name']).sp_pwdp == user['password_encrypted']: # If the current encrypted bassword matches the encrypted password # from the config - do not update it. This will remove the encrypted # value from the system logs. # # The encrypted password will be set only once during the first boot # after an image upgrade. user['password_encrypted'] = '' except: pass if len(login['radius_server']) > 0: render(radius_config_file, 'system-login/pam_radius_auth.conf.tmpl', login, trim_blocks=True) uid = getpwnam('root').pw_uid gid = getpwnam('root').pw_gid os.chown(radius_config_file, uid, gid) chmod_600(radius_config_file) else: if os.path.isfile(radius_config_file): os.unlink(radius_config_file) return None
def generate(snmp): # # As we are manipulating the snmpd user database we have to stop it first! # This is even save if service is going to be removed call('systemctl stop snmpd.service') config_files = [ config_file_client, config_file_daemon, config_file_access, config_file_user ] for file in config_files: rmfile(file) if snmp is None: return None # Write client config file render(config_file_client, 'snmp/etc.snmp.conf.tmpl', snmp) # Write server config file render(config_file_daemon, 'snmp/etc.snmpd.conf.tmpl', snmp) # Write access rights config file render(config_file_access, 'snmp/usr.snmpd.conf.tmpl', snmp) # Write access rights config file render(config_file_user, 'snmp/var.snmpd.conf.tmpl', snmp) return None
def generate(wifi): interface = wifi['intf'] # always stop hostapd service first before reconfiguring it call(f'systemctl stop hostapd@{interface}.service') # always stop wpa_supplicant service first before reconfiguring it call(f'systemctl stop wpa_supplicant@{interface}.service') # Delete config files if interface is removed if wifi['deleted']: if os.path.isfile(hostapd_conf.format(**wifi)): os.unlink(hostapd_conf.format(**wifi)) if os.path.isfile(wpa_suppl_conf.format(**wifi)): os.unlink(wpa_suppl_conf.format(**wifi)) return None if not wifi['mac']: # http://wiki.stocksy.co.uk/wiki/Multiple_SSIDs_with_hostapd # generate locally administered MAC address from used phy interface with open('/sys/class/ieee80211/{}/addresses'.format(wifi['phy']), 'r') as f: # some PHYs tend to have multiple interfaces and thus supply multiple MAC # addresses - we only need the first one for our calculation tmp = f.readline().rstrip() tmp = EUI(tmp).value # mask last nibble from the MAC address tmp &= 0xfffffffffff0 # set locally administered bit in MAC address tmp |= 0x020000000000 # we now need to add an offset to our MAC address indicating this # subinterfaces index tmp += int(findall(r'\d+', interface)[0]) # convert integer to "real" MAC address representation mac = EUI(hex(tmp).split('x')[-1]) # change dialect to use : as delimiter instead of - mac.dialect = mac_unix_expanded wifi['mac'] = str(mac) # render appropriate new config files depending on access-point or station mode if wifi['op_mode'] == 'ap': render(hostapd_conf.format(**wifi), 'wifi/hostapd.conf.tmpl', wifi) elif wifi['op_mode'] == 'station': render(wpa_suppl_conf.format(**wifi), 'wifi/wpa_supplicant.conf.tmpl', wifi) return None
def generate(pptp): if not pptp: return None render(pptp_conf, 'accel-ppp/pptp.config.tmpl', pptp, trim_blocks=True) if pptp['local_users']: render(pptp_chap_secrets, 'accel-ppp/chap-secrets.tmpl', pptp, trim_blocks=True) os.chmod(pptp_chap_secrets, S_IRUSR | S_IWUSR | S_IRGRP) else: if os.path.exists(pptp_chap_secrets): os.unlink(pptp_chap_secrets)
def generate(ipoe): if not ipoe: return None render(ipoe_conf, 'accel-ppp/ipoe.config.tmpl', ipoe, trim_blocks=True) if ipoe['auth_mode'] == 'local': render(ipoe_chap_secrets, 'accel-ppp/chap-secrets.ipoe.tmpl', ipoe) os.chmod(ipoe_chap_secrets, S_IRUSR | S_IWUSR | S_IRGRP) else: if os.path.exists(ipoe_chap_secrets): os.unlink(ipoe_chap_secrets) return None
def generate(regdom): print("Changing the wireless regulatory domain requires a system reboot.") if regdom['deleted']: if os.path.isfile(config_80211_file): os.unlink(config_80211_file) if os.path.isfile(config_crda_file): os.unlink(config_crda_file) return None render(config_80211_file, 'wifi/cfg80211.conf.tmpl', regdom) render(config_crda_file, 'wifi/crda.tmpl', regdom) return None
def generate(l2tp): if not l2tp: return None render(l2tp_conf, 'accel-ppp/l2tp.config.tmpl', l2tp, trim_blocks=True) if l2tp['auth_mode'] == 'local': render(l2tp_chap_secrets, 'accel-ppp/chap-secrets.tmpl', l2tp) os.chmod(l2tp_chap_secrets, S_IRUSR | S_IWUSR | S_IRGRP) else: if os.path.exists(l2tp_chap_secrets): os.unlink(l2tp_chap_secrets) return None
def generate(data): vrrp_groups, sync_groups = data # Remove disabled groups from the sync group member lists for sync_group in sync_groups: for member in sync_group["members"]: g = list(filter(lambda x: x["name"] == member, vrrp_groups))[0] if g["disable"]: print("Warning: ignoring disabled VRRP group {0} in sync-group {1}".format(g["name"], sync_group["name"])) # Filter out disabled groups vrrp_groups = list(filter(lambda x: x["disable"] is not True, vrrp_groups)) render(VRRP.location['config'], 'vrrp/keepalived.conf.tmpl', {"groups": vrrp_groups, "sync_groups": sync_groups}) render(VRRP.location['daemon'], 'vrrp/daemon.tmpl', {}) return None
def generate(sstp): if not sstp: return None # accel-cmd reload doesn't work so any change results in a restart of the daemon render(sstp_conf, 'accel-ppp/sstp.config.tmpl', sstp, trim_blocks=True) if sstp['local_users']: render(sstp_chap_secrets, 'accel-ppp/chap-secrets.tmpl', sstp, trim_blocks=True) os.chmod(sstp_chap_secrets, S_IRUSR | S_IWUSR | S_IRGRP) else: if os.path.exists(sstp_chap_secrets): os.unlink(sstp_chap_secrets) return sstp
def generate(lldp): # bail out early - looks like removal from running config if lldp is None: return # generate listen on interfaces for intf in lldp['interface_list']: tmp = '' # add exclamation mark if interface is disabled if intf['disable']: tmp = '!' tmp += intf['name'] lldp['options']['listen_on'].append(tmp) # generate /etc/default/lldpd render(config_file, 'lldp/lldpd.tmpl', lldp) # generate /etc/lldpd.d/01-vyos.conf render(vyos_config_file, 'lldp/vyos.conf.tmpl', lldp)
def generate(wwan): # set up configuration file path variables where our templates will be # rendered into intf = wwan['intf'] config_wwan = f'/etc/ppp/peers/{intf}' config_wwan_chat = wwan['chat_script'] script_wwan_pre_up = f'/etc/ppp/ip-pre-up.d/1010-vyos-wwan-{intf}' script_wwan_ip_up = f'/etc/ppp/ip-up.d/1010-vyos-wwan-{intf}' script_wwan_ip_down = f'/etc/ppp/ip-down.d/1010-vyos-wwan-{intf}' config_files = [config_wwan, config_wwan_chat, script_wwan_pre_up, script_wwan_ip_up, script_wwan_ip_down] # Always hang-up WWAN connection prior generating new configuration file call(f'systemctl stop ppp@{intf}.service') if wwan['deleted']: # Delete PPP configuration files for file in config_files: if os.path.exists(file): os.unlink(file) else: # Create PPP configuration files render(config_wwan, 'wwan/peer.tmpl', wwan) # Create PPP chat script render(config_wwan_chat, 'wwan/chat.tmpl', wwan) # generated script file must be executable # Create script for ip-pre-up.d render(script_wwan_pre_up, 'wwan/ip-pre-up.script.tmpl', wwan, permission=0o755) # Create script for ip-up.d render(script_wwan_ip_up, 'wwan/ip-up.script.tmpl', wwan, permission=0o755) # Create script for ip-down.d render(script_wwan_ip_down, 'wwan/ip-down.script.tmpl', wwan, permission=0o755) return None
def generate(salt): if not salt: return None render(config_file, 'salt-minion/minion.tmpl', salt, user=salt['user'], group=salt['group']) if not os.path.exists(master_keyfile): if salt['master_key']: req = PoolManager().request('GET', salt['master_key'], preload_content=False) with open(master_keyfile, 'wb') as f: while True: data = req.read(1024) if not data: break f.write(data) req.release_conn() chown(master_keyfile, salt['user'], salt['group']) return None
def generate(relay): if relay is None: return None config_dir = os.path.dirname(config_file) config_filename = os.path.basename(config_file) active_configs = [] for config in fnmatch.filter(os.listdir(config_dir), config_filename + '*'): # determine prefix length to identify service instance prefix_len = len(config_filename) active_configs.append(config[prefix_len:]) # sort our list active_configs.sort() # delete old configuration files for id in active_configs[:]: if os.path.exists(config_file + id): os.unlink(config_file + id) # If the service is disabled, we can bail out here if relay['disabled']: print( 'Warning: UDP broadcast relay service will be deactivated because it is disabled' ) return None for r in relay['instances']: # Skip writing instance config when it's disabled if r['disabled']: continue # configuration filename contains instance id file = config_file + str(r['id']) render(file, 'bcast-relay/udp-broadcast-relay.tmpl', r) return None
def set(self): """ Configure interface as DHCP client. The dhclient binary is automatically started in background! Example: >>> from vyos.ifconfig import Interface >>> j = Interface('eth0') >>> j.dhcp.v4.set() """ if not self.options['hostname']: # read configured system hostname. # maybe change to vyos hostd client ??? with open('/etc/hostname', 'r') as f: self.options['hostname'] = f.read().rstrip('\n') render(self.options['options_file'], 'dhcp-client/daemon-options.tmpl', self.options) render(self.options['conf_file'], 'dhcp-client/ipv4.tmpl', self.options) return self._cmd('systemctl restart dhclient@{ifname}.service'.format( **self.options))