diff options
Diffstat (limited to 'src')
-rwxr-xr-x | src/conf_mode/https.py | 87 | ||||
-rwxr-xr-x | src/conf_mode/ipsec-settings.py | 149 | ||||
-rwxr-xr-x | src/conf_mode/mdns_repeater.py | 44 | ||||
-rwxr-xr-x | src/conf_mode/protocols_bfd.py | 60 | ||||
-rwxr-xr-x | src/conf_mode/protocols_igmp.py | 65 | ||||
-rwxr-xr-x | src/conf_mode/protocols_mpls.py | 85 | ||||
-rwxr-xr-x | src/conf_mode/protocols_pim.py | 60 | ||||
-rwxr-xr-x | src/conf_mode/salt-minion.py | 113 | ||||
-rwxr-xr-x | src/conf_mode/service-ipoe.py | 202 | ||||
-rwxr-xr-x | src/conf_mode/service-pppoe.py | 297 | ||||
-rwxr-xr-x | src/conf_mode/service-router-advert.py | 59 | ||||
-rwxr-xr-x | src/conf_mode/system-login.py | 32 | ||||
-rwxr-xr-x | src/conf_mode/system-wifi-regdom.py | 41 | ||||
-rwxr-xr-x | src/conf_mode/tftp_server.py | 64 | ||||
-rwxr-xr-x | src/conf_mode/vpn-pptp.py | 151 | ||||
-rwxr-xr-x | src/conf_mode/vrf.py | 23 | ||||
-rwxr-xr-x | src/conf_mode/vrrp.py | 164 |
17 files changed, 348 insertions, 1348 deletions
diff --git a/src/conf_mode/https.py b/src/conf_mode/https.py index 889b62cf4..83a5f3602 100755 --- a/src/conf_mode/https.py +++ b/src/conf_mode/https.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2019 VyOS maintainers and contributors +# Copyright (C) 2019-2020 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -13,88 +13,22 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -# -# -import sys import os -from copy import deepcopy -import jinja2 +from sys import exit +from copy import deepcopy +from jinja2 import FileSystemLoader, Environment import vyos.defaults import vyos.certbot_util + from vyos.config import Config +from vyos.defaults import directories as vyos_data_dir from vyos import ConfigError config_file = '/etc/nginx/sites-available/default' -# Please be careful if you edit the template. -config_tmpl = """ - -### Autogenerated by https.py ### -# Default server configuration -# -server { - listen 80 default_server; - listen [::]:80 default_server; - server_name _; - return 301 https://$server_name$request_uri; -} - -{% for server in server_block_list %} -server { - - # SSL configuration - # -{% if server.address == '*' %} - listen {{ server.port }} ssl; - listen [::]:{{ server.port }} ssl; -{% else %} - listen {{ server.address }}:{{ server.port }} ssl; -{% endif %} - -{% for name in server.name %} - server_name {{ name }}; -{% endfor %} - -{% if server.certbot %} - ssl_certificate /etc/letsencrypt/live/{{ server.certbot_dir }}/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/{{ server.certbot_dir }}/privkey.pem; - include /etc/letsencrypt/options-ssl-nginx.conf; - ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; -{% elif server.vyos_cert %} - include {{ server.vyos_cert.conf }}; -{% else %} - # - # Self signed certs generated by the ssl-cert package - # Don't use them in a production server! - # - include snippets/snakeoil.conf; -{% endif %} - - # proxy settings for HTTP API, if enabled; 503, if not - location ~ /(retrieve|configure|config-file|image|generate|show) { -{% if server.api %} - proxy_pass http://localhost:{{ server.api.port }}; - proxy_buffering off; -{% else %} - return 503; -{% endif %} - } - - error_page 501 502 503 =200 @50*_json; - - location @50*_json { - default_type application/json; - return 200 '{"error": "Start service in configuration mode: set service https api"}'; - } - -} - -{% endfor %} -""" - default_server_block = { 'id' : '', 'address' : '*', @@ -193,10 +127,15 @@ def generate(https): if https is None: return None + # Prepare Jinja2 template loader from files + tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'https') + fs_loader = FileSystemLoader(tmpl_path) + env = Environment(loader=fs_loader, trim_blocks=True) + if 'server_block_list' not in https or not https['server_block_list']: https['server_block_list'] = [default_server_block] - tmpl = jinja2.Template(config_tmpl, trim_blocks=True) + tmpl = env.get_template('nginx.default.tmpl') config_text = tmpl.render(https) with open(config_file, 'w') as f: f.write(config_text) @@ -217,4 +156,4 @@ if __name__ == '__main__': apply(c) except ConfigError as e: print(e) - sys.exit(1) + exit(1) diff --git a/src/conf_mode/ipsec-settings.py b/src/conf_mode/ipsec-settings.py index e80c6caf0..1ebf6a5cd 100755 --- a/src/conf_mode/ipsec-settings.py +++ b/src/conf_mode/ipsec-settings.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018 VyOS maintainers and contributors +# Copyright (C) 2018-2020 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -13,22 +13,18 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -# -# -import sys import re import os -import jinja2 -import syslog as sl -import time -import vyos.config -import vyos.defaults +from time import sleep +from jinja2 import FileSystemLoader, Environment +from sys import exit +from vyos.config import Config +from vyos.defaults import directories as vyos_data_dir from vyos import ConfigError - ra_conn_name = "remote-access" charon_conf_file = "/etc/strongswan.d/charon.conf" ipsec_secrets_flie = "/etc/ipsec.secrets" @@ -42,55 +38,8 @@ delim_ipsec_l2tp_begin = "### VyOS L2TP VPN Begin ###" delim_ipsec_l2tp_end = "### VyOS L2TP VPN End ###" charon_pidfile = "/var/run/charon.pid" -l2pt_ipsec_conf = ''' -{{delim_ipsec_l2tp_begin}} -include {{ipsec_ra_conn_file}} -{{delim_ipsec_l2tp_end}} -''' - -l2pt_ipsec_secrets_conf = ''' -{{delim_ipsec_l2tp_begin}} -{% if ipsec_l2tp_auth_mode == 'pre-shared-secret' %} -{{outside_addr}} %any : PSK "{{ipsec_l2tp_secret}}" -{% elif ipsec_l2tp_auth_mode == 'x509' %} -: RSA {{server_key_file_copied}} -{% endif%} -{{delim_ipsec_l2tp_end}} -''' - -l2tp_ipsec_ra_conn_conf = ''' -{{delim_ipsec_l2tp_begin}} -conn {{ra_conn_name}} - type=transport - left={{outside_addr}} - leftsubnet=%dynamic[/1701] - rightsubnet=%dynamic - mark_in=%unique - auto=add - ike=aes256-sha1-modp1024,3des-sha1-modp1024,3des-sha1-modp1024! - dpddelay=15 - dpdtimeout=45 - dpdaction=clear - esp=aes256-sha1,3des-sha1! - rekey=no -{% if ipsec_l2tp_auth_mode == 'pre-shared-secret' %} - authby=secret - leftauth=psk - rightauth=psk -{% elif ipsec_l2tp_auth_mode == 'x509' %} - authby=rsasig - leftrsasigkey=%cert - rightrsasigkey=%cert - rightca=%same - leftcert={{server_cert_file_copied}} -{% endif %} - ikelifetime={{ipsec_l2tp_ike_lifetime}} - keylife={{ipsec_l2tp_lifetime}} -{{delim_ipsec_l2tp_end}} -''' - def get_config(): - config = vyos.config.Config() + config = Config() data = {"install_routes": "yes"} if config.exists("vpn ipsec options disable-route-autoinstall"): @@ -146,44 +95,12 @@ def get_config(): return data -### ipsec secret l2tp -def write_ipsec_secrets(c): - tmpl = jinja2.Template(l2pt_ipsec_secrets_conf, trim_blocks=True) - l2pt_ipsec_secrets_txt = tmpl.render(c) - old_umask = os.umask(0o077) - open(ipsec_secrets_flie,'w').write(l2pt_ipsec_secrets_txt) - os.umask(old_umask) - sl.syslog(sl.LOG_NOTICE, ipsec_secrets_flie + ' written') - -### ipsec remote access connection config -def write_ipsec_ra_conn(c): - tmpl = jinja2.Template(l2tp_ipsec_ra_conn_conf, trim_blocks=True) - ipsec_ra_conn_txt = tmpl.render(c) - 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) - sl.syslog(sl.LOG_NOTICE, ipsec_ra_conn_dir + " created") - - open(ipsec_ra_conn_file,'w').write(ipsec_ra_conn_txt) - os.umask(old_umask) - sl.syslog(sl.LOG_NOTICE, ipsec_ra_conn_file + ' written') ### Remove config from file by delimiter def remove_confs(delim_begin, delim_end, conf_file): os.system("sed -i '/"+delim_begin+"/,/"+delim_end+"/d' "+conf_file) -### Append "include /path/to/ra_conn" to ipsec conf file -def append_ipsec_conf(c): - tmpl = jinja2.Template(l2pt_ipsec_conf, trim_blocks=True) - l2pt_ipsec_conf_txt = tmpl.render(c) - old_umask = os.umask(0o077) - open(ipsec_conf_flie,'a').write(l2pt_ipsec_conf_txt) - os.umask(old_umask) - sl.syslog(sl.LOG_NOTICE, ipsec_conf_flie + ' written') - ### Checking certificate storage and notice if certificate not in /config directory def check_cert_file_store(cert_name, file_path, dts_path): if not re.search('^\/config\/.+', file_path): @@ -197,8 +114,6 @@ def check_cert_file_store(cert_name, file_path, dts_path): ret = os.system('cp -f '+file_path+' '+dts_path) if ret: raise ConfigError("L2TP VPN configuration error: Cannot copy "+file_path) - else: - sl.syslog(sl.LOG_NOTICE, file_path + ' copied to '+dts_path) def verify(data): # l2tp ipsec check @@ -231,21 +146,45 @@ def verify(data): raise ConfigError("L2TP VPN configuration error: \"vpn ipsec ipsec-interfaces\" must be specified.") def generate(data): - tmpl_path = os.path.join(vyos.defaults.directories["data"], "templates", "ipsec") + tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'ipsec') fs_loader = jinja2.FileSystemLoader(tmpl_path) - env = jinja2.Environment(loader=fs_loader) - - - charon_conf_tmpl = env.get_template("charon.tmpl") - charon_conf = charon_conf_tmpl.render(data) + env = jinja2.Environment(loader=fs_loader, trim_blocks=True) + tmpl = env.get_template('charon.tmpl') + config_text = tmpl.render(data) with open(charon_conf_file, 'w') as f: - f.write(charon_conf) + f.write(config_text) if data["ipsec_l2tp"]: remove_confs(delim_ipsec_l2tp_begin, delim_ipsec_l2tp_end, ipsec_conf_flie) - write_ipsec_secrets(data) - write_ipsec_ra_conn(data) + + tmpl = env.get_template('ipsec.secrets.tmpl') + l2pt_ipsec_secrets_txt = tmpl.render(c) + old_umask = os.umask(0o077) + with open(ipsec_secrets_flie,'w') as f: + f.write(l2pt_ipsec_secrets_txt) + os.umask(old_umask) + + tmpl = env.get_template('remote-access.tmpl') + ipsec_ra_conn_txt = tmpl.render(c) + 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) + + with open(ipsec_ra_conn_file,'w') as f: + f.write(ipsec_ra_conn_txt) + os.umask(old_umask) + + + tmpl = env.get_template('ipsec.conf.tmpl') + l2pt_ipsec_conf_txt = tmpl.render(c) + old_umask = os.umask(0o077) + with open(ipsec_conf_flie,'a') as f: + .write(l2pt_ipsec_conf_txt) + os.umask(old_umask) + append_ipsec_conf(data) else: if os.path.exists(ipsec_ra_conn_file): @@ -254,15 +193,15 @@ def generate(data): remove_confs(delim_ipsec_l2tp_begin, delim_ipsec_l2tp_end, ipsec_conf_flie) def restart_ipsec(): - os.system("ipsec restart >&/dev/null") + os.system('ipsec restart >&/dev/null') # counter for apply swanctl config counter = 10 while counter <= 10: if os.path.exists(charon_pidfile): - os.system("swanctl -q >&/dev/null") + os.system('swanctl -q >&/dev/null') break counter -=1 - time.sleep(1) + sleep(1) if counter == 0: raise ConfigError('VPN configuration error: IPSec is not running.') @@ -278,4 +217,4 @@ if __name__ == '__main__': apply(c) except ConfigError as e: print(e) - sys.exit(1) + exit(1) diff --git a/src/conf_mode/mdns_repeater.py b/src/conf_mode/mdns_repeater.py index cef735c0d..f738cc6a6 100755 --- a/src/conf_mode/mdns_repeater.py +++ b/src/conf_mode/mdns_repeater.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2017 VyOS maintainers and contributors +# Copyright (C) 2017-2020 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -13,45 +13,42 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -# -# -import sys import os -import jinja2 -import netifaces + +from sys import exit +from copy import deepcopy +from jinja2 import FileSystemLoader, Environment +from netifaces import ifaddresses, AF_INET from vyos.config import Config +from vyos.defaults import directories as vyos_data_dir from vyos import ConfigError config_file = r'/etc/default/mdns-repeater' -config_tmpl = """ -### Autogenerated by mdns_repeater.py ### -DAEMON_ARGS="{{ interfaces | join(' ') }}" -""" - default_config_data = { 'disabled': False, 'interfaces': [] } def get_config(): - mdns = default_config_data + mdns = deepcopy(default_config_data) conf = Config() - if not conf.exists('service mdns repeater'): + base = ['service', 'mdns', 'repeater'] + if not conf.exists(base): return None else: - conf.set_level('service mdns repeater') + conf.set_level(base) # Service can be disabled by user - if conf.exists('disable'): + if conf.exists(['disable']): mdns['disabled'] = True return mdns # Interface to repeat mDNS advertisements - if conf.exists('interface'): - mdns['interfaces'] = conf.return_values('interface') + if conf.exists(['interface']): + mdns['interfaces'] = conf.return_values(['interface']) return mdns @@ -69,8 +66,8 @@ def verify(mdns): # For mdns-repeater to work it is essential that the interfaces has # an IPv4 address assigned for interface in mdns['interfaces']: - if netifaces.AF_INET in netifaces.ifaddresses(interface).keys(): - if len(netifaces.ifaddresses(interface)[netifaces.AF_INET]) < 1: + if AF_INET in ifaddresses(interface).keys(): + if len(ifaddresses(interface)[AF_INET]) < 1: raise ConfigError('mDNS repeater requires an IPv6 address configured on interface %s!'.format(interface)) return None @@ -83,7 +80,12 @@ def generate(mdns): print('Warning: mDNS repeater will be deactivated because it is disabled') return None - tmpl = jinja2.Template(config_tmpl) + # Prepare Jinja2 template loader from files + tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'mdns-repeater') + fs_loader = FileSystemLoader(tmpl_path) + env = Environment(loader=fs_loader) + + tmpl = env.get_template('mdns-repeater.tmpl') config_text = tmpl.render(mdns) with open(config_file, 'w') as f: f.write(config_text) @@ -108,4 +110,4 @@ if __name__ == '__main__': apply(c) except ConfigError as e: print(e) - sys.exit(1) + exit(1) diff --git a/src/conf_mode/protocols_bfd.py b/src/conf_mode/protocols_bfd.py index 58f5b5a0e..9940c80c5 100755 --- a/src/conf_mode/protocols_bfd.py +++ b/src/conf_mode/protocols_bfd.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2019 VyOS maintainers and contributors +# Copyright (C) 2019-2020 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -13,39 +13,20 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -# -import sys -import jinja2 -import copy import os -import vyos.validate -from vyos import ConfigError +from sys import exit +from copy import deepcopy +from jinja2 import FileSystemLoader, Environment + from vyos.config import Config +from vyos.defaults import directories as vyos_data_dir +from vyos.validate import is_ipv6_link_local, is_ipv6 +from vyos import ConfigError config_file = r'/tmp/bfd.frr' -# Please be careful if you edit the template. -config_tmpl = """ -! -bfd -{% for peer in old_peers -%} - no peer {{ peer.remote }}{% if peer.multihop %} multihop{% endif %}{% if peer.src_addr %} local-address {{ peer.src_addr }}{% endif %}{% if peer.src_if %} interface {{ peer.src_if }}{% endif %} -{% endfor -%} -! -{% for peer in new_peers -%} - peer {{ peer.remote }}{% if peer.multihop %} multihop{% endif %}{% if peer.src_addr %} local-address {{ peer.src_addr }}{% endif %}{% if peer.src_if %} interface {{ peer.src_if }}{% endif %} - detect-multiplier {{ peer.multiplier }} - receive-interval {{ peer.rx_interval }} - transmit-interval {{ peer.tx_interval }} - {% if peer.echo_mode %}echo-mode{% endif %} - {% if peer.echo_interval != '' %}echo-interval {{ peer.echo_interval }}{% endif %} - {% if not peer.shutdown %}no {% endif %}shutdown -{% endfor -%} -! -""" - default_config_data = { 'new_peers': [], 'old_peers' : [] @@ -132,7 +113,7 @@ def get_bfd_peer_config(peer, conf_mode="proposed"): return bfd_peer def get_config(): - bfd = copy.deepcopy(default_config_data) + bfd = deepcopy(default_config_data) conf = Config() if not (conf.exists('protocols bfd') or conf.exists_effective('protocols bfd')): return None @@ -164,12 +145,12 @@ def verify(bfd): for peer in bfd['new_peers']: # IPv6 link local peers require an explicit local address/interface - if vyos.validate.is_ipv6_link_local(peer['remote']): + if is_ipv6_link_local(peer['remote']): if not (peer['src_if'] and peer['src_addr']): raise ConfigError('BFD IPv6 link-local peers require explicit local address and interface setting') # IPv6 peers require an explicit local address - if vyos.validate.is_ipv6(peer['remote']): + if is_ipv6(peer['remote']): if not peer['src_addr']: raise ConfigError('BFD IPv6 peers require explicit local address setting') @@ -208,18 +189,23 @@ def generate(bfd): if bfd is None: return None + # Prepare Jinja2 template loader from files + tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'frr-bfd') + fs_loader = FileSystemLoader(tmpl_path) + env = Environment(loader=fs_loader) + + tmpl = env.get_template('bfd.frr.tmpl') + config_text = tmpl.render(bfd) + with open(config_file, 'w') as f: + f.write(config_text) + return None def apply(bfd): if bfd is None: return None - tmpl = jinja2.Template(config_tmpl) - config_text = tmpl.render(bfd) - with open(config_file, 'w') as f: - f.write(config_text) - - os.system("sudo vtysh -d bfdd -f " + config_file) + os.system(f'vtysh -d bfdd -f {config_file}') if os.path.exists(config_file): os.remove(config_file) @@ -233,4 +219,4 @@ if __name__ == '__main__': apply(c) except ConfigError as e: print(e) - sys.exit(1) + exit(1) diff --git a/src/conf_mode/protocols_igmp.py b/src/conf_mode/protocols_igmp.py index dcbd9a0a8..0148b5dac 100755 --- a/src/conf_mode/protocols_igmp.py +++ b/src/conf_mode/protocols_igmp.py @@ -13,65 +13,19 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -# -import sys -import jinja2 -import copy import os -import vyos.validate + +from ipaddress import IPv4Address +from jinja2 import FileSystemLoader, Environment +from sys import exit from vyos import ConfigError from vyos.config import Config -from ipaddress import IPv4Address +from vyos.defaults import directories as vyos_data_dir config_file = r'/tmp/igmp.frr' -# Please be careful if you edit the template. -config_tmpl = """ -! -{% for iface in old_ifaces -%} -interface {{ iface }} -{% for group in old_ifaces[iface].gr_join -%} -{% if old_ifaces[iface].gr_join[group] -%} -{% for source in old_ifaces[iface].gr_join[group] -%} -no ip igmp join {{ group }} {{ source }} -{% endfor -%} -{% else -%} -no ip igmp join {{ group }} -{% endif -%} -{% endfor -%} -no ip igmp -! -{% endfor -%} -{% for iface in ifaces -%} -interface {{ iface }} -{% if ifaces[iface].version -%} -ip igmp version {{ ifaces[iface].version }} -{% else -%} -{# IGMP default version 3 #} -ip igmp -{% endif -%} -{% if ifaces[iface].query_interval -%} -ip igmp query-interval {{ ifaces[iface].query_interval }} -{% endif -%} -{% if ifaces[iface].query_max_resp_time -%} -ip igmp query-max-response-time {{ ifaces[iface].query_max_resp_time }} -{% endif -%} -{% for group in ifaces[iface].gr_join -%} -{% if ifaces[iface].gr_join[group] -%} -{% for source in ifaces[iface].gr_join[group] -%} -ip igmp join {{ group }} {{ source }} -{% endfor -%} -{% else -%} -ip igmp join {{ group }} -{% endif -%} -{% endfor -%} -! -{% endfor -%} -! -""" - def get_config(): conf = Config() igmp_conf = { @@ -132,7 +86,12 @@ def generate(igmp): if igmp is None: return None - tmpl = jinja2.Template(config_tmpl) + # Prepare Jinja2 template loader from files + tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'igmp') + fs_loader = FileSystemLoader(tmpl_path) + env = Environment(loader=fs_loader) + + tmpl = env.get_template('igmp.frr.tmpl') config_text = tmpl.render(igmp) with open(config_file, 'w') as f: f.write(config_text) @@ -157,4 +116,4 @@ if __name__ == '__main__': apply(c) except ConfigError as e: print(e) - sys.exit(1) + exit(1) diff --git a/src/conf_mode/protocols_mpls.py b/src/conf_mode/protocols_mpls.py index 5f2497660..514fe5efb 100755 --- a/src/conf_mode/protocols_mpls.py +++ b/src/conf_mode/protocols_mpls.py @@ -13,81 +13,17 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -# -import sys -import jinja2 -import copy import os -import vyos.validate -from vyos import ConfigError +from jinja2 import FileSystemLoader, Environment + from vyos.config import Config +from vyos.defaults import directories as vyos_data_dir +from vyos import ConfigError config_file = r'/tmp/ldpd.frr' -# Please be careful if you edit the template. -config_tmpl = """ -! -{% if mpls_ldp -%} -mpls ldp -{% if old_router_id -%} -no router-id {{ old_router_id }} -{% endif -%} -{% if router_id -%} -router-id {{ router_id }} -{% endif -%} -{% for neighbor_id in old_ldp.neighbors -%} -no neighbor {{neighbor_id}} password {{old_ldp.neighbors[neighbor_id].password}} -{% endfor -%} -{% for neighbor_id in ldp.neighbors -%} -neighbor {{neighbor_id}} password {{ldp.neighbors[neighbor_id].password}} -{% endfor -%} -address-family ipv4 -label local allocate host-routes -{% if old_ldp.d_transp_ipv4 -%} -no discovery transport-address {{ old_ldp.d_transp_ipv4 }} -{% endif -%} -{% if ldp.d_transp_ipv4 -%} -discovery transport-address {{ ldp.d_transp_ipv4 }} -{% endif -%} -{% for interface in old_ldp.interfaces -%} -no interface {{interface}} -{% endfor -%} -{% for interface in ldp.interfaces -%} -interface {{interface}} -{% endfor -%} -! -! -exit-address-family -! -{% if ldp.d_transp_ipv6 -%} -address-family ipv6 -label local allocate host-routes -{% if old_ldp.d_transp_ipv6 -%} -no discovery transport-address {{ old_ldp.d_transp_ipv6 }} -{% endif -%} -{% if ldp.d_transp_ipv6 -%} -discovery transport-address {{ ldp.d_transp_ipv6 }} -{% endif -%} -{% for interface in old_ldp.interfaces -%} -no interface {{interface}} -{% endfor -%} -{% for interface in ldp.interfaces -%} -interface {{interface}} -{% endfor -%} -! -exit-address-family -{% else -%} -no address-family ipv6 -{% endif -%} -! -{% else -%} -no mpls ldp -{% endif -%} -! -""" - def sysctl(name, value): os.system('sysctl -wq {}={}'.format(name, value)) @@ -159,12 +95,10 @@ def get_config(): } }) - # print(mpls_conf) - # sys.exit(1) return mpls_conf def operate_mpls_on_intfc(interfaces, action): - rp_filter = 0 + rp_filter = 0 if action == 1: rp_filter = 2 for iface in interfaces: @@ -193,7 +127,12 @@ def generate(mpls): if mpls is None: return None - tmpl = jinja2.Template(config_tmpl) + # Prepare Jinja2 template loader from files + tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'mpls') + fs_loader = FileSystemLoader(tmpl_path) + env = Environment(loader=fs_loader) + + tmpl = env.get_template('ldpd.frr.tmpl') config_text = tmpl.render(mpls) with open(config_file, 'w') as f: f.write(config_text) @@ -234,4 +173,4 @@ if __name__ == '__main__': apply(c) except ConfigError as e: print(e) - sys.exit(1) + exit(1) diff --git a/src/conf_mode/protocols_pim.py b/src/conf_mode/protocols_pim.py index f4c365923..7b360d62c 100755 --- a/src/conf_mode/protocols_pim.py +++ b/src/conf_mode/protocols_pim.py @@ -13,58 +13,19 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -# -import sys -import jinja2 -import copy import os -import vyos.validate + from ipaddress import IPv4Address +from jinja2 import FileSystemLoader, Environment +from sys import exit -from vyos import ConfigError from vyos.config import Config +from vyos.defaults import directories as vyos_data_dir +from vyos import ConfigError config_file = r'/tmp/pimd.frr' -# Please be careful if you edit the template. -config_tmpl = """ -! -{% for rp_addr in old_pim.rp -%} -{% for group in old_pim.rp[rp_addr] -%} -no ip pim rp {{ rp_addr }} {{ group }} -{% endfor -%} -{% endfor -%} -{% if old_pim.rp_keep_alive -%} -no ip pim rp keep-alive-timer {{ old_pim.rp_keep_alive }} -{% endif -%} -{% for iface in old_pim.ifaces -%} -interface {{ iface }} -no ip pim -! -{% endfor -%} -{% for iface in pim.ifaces -%} -interface {{ iface }} -ip pim -{% if pim.ifaces[iface].dr_prio -%} -ip pim drpriority {{ pim.ifaces[iface].dr_prio }} -{% endif -%} -{% if pim.ifaces[iface].hello -%} -ip pim hello {{ pim.ifaces[iface].hello }} -{% endif -%} -! -{% endfor -%} -{% for rp_addr in pim.rp -%} -{% for group in pim.rp[rp_addr] -%} -ip pim rp {{ rp_addr }} {{ group }} -{% endfor -%} -{% endfor -%} -{% if pim.rp_keep_alive -%} -ip pim rp keep-alive-timer {{ pim.rp_keep_alive }} -{% endif -%} -! -""" - def get_config(): conf = Config() pim_conf = { @@ -152,7 +113,12 @@ def generate(pim): if pim is None: return None - tmpl = jinja2.Template(config_tmpl) + # Prepare Jinja2 template loader from files + tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'pim') + fs_loader = FileSystemLoader(tmpl_path) + env = Environment(loader=fs_loader) + + tmpl = env.get_template('pimd.frr.tmpl') config_text = tmpl.render(pim) with open(config_file, 'w') as f: f.write(config_text) @@ -164,7 +130,7 @@ def apply(pim): return None if os.path.exists(config_file): - os.system("sudo vtysh -d pimd -f " + config_file) + os.system("vtysh -d pimd -f " + config_file) os.remove(config_file) return None @@ -177,4 +143,4 @@ if __name__ == '__main__': apply(c) except ConfigError as e: print(e) - sys.exit(1) + exit(1) diff --git a/src/conf_mode/salt-minion.py b/src/conf_mode/salt-minion.py index 303ddae48..bc1767454 100755 --- a/src/conf_mode/salt-minion.py +++ b/src/conf_mode/salt-minion.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018 VyOS maintainers and contributors +# Copyright (C) 2018-2020 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -13,102 +13,35 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -# -# -import sys import os -import pwd -import socket -import urllib3 -import jinja2 +from copy import deepcopy +from jinja2 import FileSystemLoader, Environment +from pwd import getpwnam +from socket import gethostname +from sys import exit +from urllib3 import PoolManager from vyos.config import Config +from vyos.defaults import directories as vyos_data_dir from vyos import ConfigError config_file = r'/etc/salt/minion' -# Please be careful if you edit the template. -config_tmpl = """ -### Autogenerated by salt-minion.py ### - -##### Primary configuration settings ##### -########################################## - -# The hash_type is the hash to use when discovering the hash of a file on -# the master server. The default is sha256, but md5, sha1, sha224, sha384 and -# sha512 are also supported. -# -# WARNING: While md5 and sha1 are also supported, do not use them due to the -# high chance of possible collisions and thus security breach. -# -# Prior to changing this value, the master should be stopped and all Salt -# caches should be cleared. -hash_type: {{ hash_type }} - -##### Logging settings ##### -########################################## -# The location of the minion log file -# The minion log can be sent to a regular file, local path name, or network -# location. Remote logging works best when configured to use rsyslogd(8) (e.g.: -# ``file:///dev/log``), with rsyslogd(8) configured for network logging. The URI -# format is: <file|udp|tcp>://<host|socketpath>:<port-if-required>/<log-facility> -#log_file: /var/log/salt/minion -#log_file: file:///dev/log -#log_file: udp://loghost:10514 -# -log_file: {{ log_file }} - -# The level of messages to send to the console. -# One of 'garbage', 'trace', 'debug', info', 'warning', 'error', 'critical'. -# -# The following log levels are considered INSECURE and may log sensitive data: -# ['garbage', 'trace', 'debug'] -# -# Default: 'warning' -log_level: {{ log_level }} - -# Set the location of the salt master server, if the master server cannot be -# resolved, then the minion will fail to start. -master: -{% for host in master -%} -- {{ host }} -{% endfor %} - -# The user to run salt -user: {{ user }} - -# The directory to store the pki information in -pki_dir: /config/salt/pki/minion - -# Explicitly declare the id for this minion to use, if left commented the id -# will be the hostname as returned by the python call: socket.getfqdn() -# Since salt uses detached ids it is possible to run multiple minions on the -# same machine but with different ids, this can be useful for salt compute -# clusters. -id: {{ salt_id }} - - -# The number of minutes between mine updates. -mine_interval: {{ mine_interval }} - -verify_master_pubkey_sign: {{ verify_master_pubkey_sign }} -""" - default_config_data = { 'hash_type': 'sha256', 'log_file': '/var/log/salt/minion', 'log_level': 'warning', 'master' : 'salt', 'user': 'minion', - 'salt_id': socket.gethostname(), + 'salt_id': gethostname(), 'mine_interval': '60', 'verify_master_pubkey_sign': 'false' } def get_config(): - salt = default_config_data + salt = deepcopy(default_config_data) conf = Config() if not conf.exists('service salt-minion'): return None @@ -145,25 +78,31 @@ def get_config(): return salt def generate(salt): - paths = ['/etc/salt/','/var/run/salt','/opt/vyatta/etc/config/salt/'] + paths = ['/etc/salt/','/var/run/salt','/opt/vyatta/etc/config/salt/'] directory = '/opt/vyatta/etc/config/salt/pki/minion' - uid = pwd.getpwnam(salt['user']).pw_uid - http = urllib3.PoolManager() + uid = getpwnam(salt['user']).pw_uid + http = PoolManager() if salt is None: return None + # Prepare Jinja2 template loader from files + tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'salt-minion') + fs_loader = FileSystemLoader(tmpl_path) + env = Environment(loader=fs_loader) + if not os.path.exists(directory): os.makedirs(directory) - tmpl = jinja2.Template(config_tmpl) + tmpl = env.get_template('minion.tmpl') config_text = tmpl.render(salt) with open(config_file, 'w') as f: f.write(config_text) - path = "/etc/salt/" + + path = "/etc/salt/" for path in paths: - for root, dirs, files in os.walk(path): - for usgr in dirs: + for root, dirs, files in os.walk(path): + for usgr in dirs: os.chown(os.path.join(root, usgr), uid, 100) for usgr in files: os.chown(os.path.join(root, usgr), uid, 100) @@ -171,14 +110,14 @@ def generate(salt): if not os.path.exists('/opt/vyatta/etc/config/salt/pki/minion/master_sign.pub'): if not salt['master-key'] is None: r = http.request('GET', salt['master-key'], preload_content=False) - + with open('/opt/vyatta/etc/config/salt/pki/minion/master_sign.pub', 'wb') as out: while True: data = r.read(1024) if not data: break out.write(data) - + r.release_conn() return None @@ -200,4 +139,4 @@ if __name__ == '__main__': apply(c) except ConfigError as e: print(e) - sys.exit(1) + exit(1) diff --git a/src/conf_mode/service-ipoe.py b/src/conf_mode/service-ipoe.py index bd7a898d0..dd9616a62 100755 --- a/src/conf_mode/service-ipoe.py +++ b/src/conf_mode/service-ipoe.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018 VyOS maintainers and contributors +# Copyright (C) 2018-2020 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -13,19 +13,18 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -# -# -import sys import os import re -import time -import socket import subprocess -import jinja2 -import syslog as sl + +from jinja2 import FileSystemLoader, Environment +from socket import socket, AF_INET, SOCK_STREAM +from sys import exit +from time import sleep from vyos.config import Config +from vyos.defaults import directories as vyos_data_dir from vyos import ConfigError ipoe_cnf_dir = r'/etc/accel-ppp/ipoe' @@ -37,157 +36,8 @@ cmd_port = r'2002' chap_secrets = ipoe_cnf_dir + '/chap-secrets' ## accel-pppd -d -c /etc/accel-ppp/pppoe/pppoe.config -p /var/run/accel_pppoe.pid -ipoe_config = ''' -### generated by ipoe.py ### -[modules] -log_syslog -ipoe -shaper -ipv6pool -ipv6_nd -ipv6_dhcp -{% if auth['mech'] == 'radius' %} -radius -{% endif -%} -ippool -{% if auth['mech'] == 'local' %} -chap-secrets -{% endif %} - -[core] -thread-count={{thread_cnt}} - -[log] -syslog=accel-ipoe,daemon -copy=1 -level=5 - -[ipoe] -verbose=1 -{% for intfc in interfaces %} -{% if interfaces[intfc]['vlan_mon'] %} -interface=re:{{intfc}}\.\d+,\ -{% else %} -interface={{intfc}},\ -{% endif %} -shared={{interfaces[intfc]['shared']}},\ -mode={{interfaces[intfc]['mode']}},\ -ifcfg={{interfaces[intfc]['ifcfg']}},\ -range={{interfaces[intfc]['range']}},\ -start={{interfaces[intfc]['sess_start']}},\ -ipv6=1 -{% endfor %} -{% if auth['mech'] == 'noauth' %} -noauth=1 -{% endif %} -{% if auth['mech'] == 'local' %} -username=ifname -password=csid -{% endif %} - -{%- for intfc in interfaces %} -{% if (interfaces[intfc]['shared'] == '0') and (interfaces[intfc]['vlan_mon']) %} -vlan-mon={{intfc}},{{interfaces[intfc]['vlan_mon']|join(',')}} -{% endif %} -{% endfor %} - -{% if (dns['server1']) or (dns['server2']) %} -[dns] -{% if dns['server1'] %} -dns1={{dns['server1']}} -{% endif -%} -{% if dns['server2'] %} -dns2={{dns['server2']}} -{% endif -%} -{% endif -%} - -{% if (dnsv6['server1']) or (dnsv6['server2']) or (dnsv6['server3']) %} -[dnsv6] -dns={{dnsv6['server1']}} -dns={{dnsv6['server2']}} -dns={{dnsv6['server3']}} -{% endif %} - -[ipv6-nd] -verbose=1 - -[ipv6-dhcp] -verbose=1 - -{% if ipv6['prfx'] %} -[ipv6-pool] -{% for prfx in ipv6['prfx'] %} -{{prfx}} -{% endfor %} -{% for pd in ipv6['pd'] %} -delegate={{pd}} -{% endfor %} -{% endif %} - -{% if auth['mech'] == 'local' %} -[chap-secrets] -chap-secrets=/etc/accel-ppp/ipoe/chap-secrets -{% endif %} - -{% if auth['mech'] == 'radius' %} -[radius] -verbose=1 -{% for srv in auth['radius'] %} -server={{srv}},{{auth['radius'][srv]['secret']}},\ -req-limit={{auth['radius'][srv]['req-limit']}},\ -fail-time={{auth['radius'][srv]['fail-time']}} -{% endfor %} -{% if auth['radsettings']['dae-server']['ip-address'] %} -dae-server={{auth['radsettings']['dae-server']['ip-address']}}:\ -{{auth['radsettings']['dae-server']['port']}},\ -{{auth['radsettings']['dae-server']['secret']}} -{% endif -%} -{% if auth['radsettings']['acct-timeout'] %} -acct-timeout={{auth['radsettings']['acct-timeout']}} -{% endif -%} -{% if auth['radsettings']['max-try'] %} -max-try={{auth['radsettings']['max-try']}} -{% endif -%} -{% if auth['radsettings']['timeout'] %} -timeout={{auth['radsettings']['timeout']}} -{% endif -%} -{% if auth['radsettings']['nas-ip-address'] %} -nas-ip-address={{auth['radsettings']['nas-ip-address']}} -{% endif -%} -{% if auth['radsettings']['nas-identifier'] %} -nas-identifier={{auth['radsettings']['nas-identifier']}} -{% endif -%} -{% endif %} - -[cli] -tcp=127.0.0.1:2002 -''' - -# chap secrets -chap_secrets_conf = ''' -# username server password acceptable local IP addresses shaper -{% for aifc in auth['auth_if'] %} -{% for mac in auth['auth_if'][aifc] %} -{% if (auth['auth_if'][aifc][mac]['up']) and (auth['auth_if'][aifc][mac]['down']) %} -{% if auth['auth_if'][aifc][mac]['vlan'] %} -{{aifc}}.{{auth['auth_if'][aifc][mac]['vlan']}}\t*\t{{mac.lower()}}\t*\t{{auth['auth_if'][aifc][mac]['down']}}/{{auth['auth_if'][aifc][mac]['up']}} -{% else %} -{{aifc}}\t*\t{{mac.lower()}}\t*\t{{auth['auth_if'][aifc][mac]['down']}}/{{auth['auth_if'][aifc][mac]['up']}} -{% endif %} -{% else %} -{% if auth['auth_if'][aifc][mac]['vlan'] %} -{{aifc}}.{{auth['auth_if'][aifc][mac]['vlan']}}\t*\t{{mac.lower()}}\t* -{% else %} -{{aifc}}\t*\t{{mac.lower()}}\t* -{% endif %} -{% endif %} -{% endfor %} -{% endfor %} -''' - if not os.path.exists(ipoe_cnf_dir): os.makedirs(ipoe_cnf_dir) - sl.syslog(sl.LOG_NOTICE, ipoe_cnf_dir + " created") def _get_cpu(): @@ -201,13 +51,13 @@ def _get_cpu(): def _chk_con(): cnt = 0 - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s = socket(AF_INET, SOCK_STREAM) while True: try: s.connect(("127.0.0.1", int(cmd_port))) break except ConnectionRefusedError: - time.sleep(0.5) + sleep(0.5) cnt += 1 if cnt == 100: raise("failed to start pppoe server") @@ -224,18 +74,6 @@ def _accel_cmd(cmd=''): except: return 1 -# chap_secrets file if auth mode local - - -def _gen_chap_secrets(c): - - tmpl = jinja2.Template(chap_secrets_conf, trim_blocks=True) - chap_secrets_txt = tmpl.render(c) - old_umask = os.umask(0o077) - open(chap_secrets, 'w').write(chap_secrets_txt) - os.umask(old_umask) - sl.syslog(sl.LOG_NOTICE, chap_secrets + ' written') - ##### Inline functions end #### @@ -388,14 +226,25 @@ def generate(c): if c == None or not c: return None + # Prepare Jinja2 template loader from files + tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'ipoe-server') + fs_loader = FileSystemLoader(tmpl_path) + env = Environment(loader=fs_loader, trim_blocks=True) + c['thread_cnt'] = _get_cpu() if c['auth']['mech'] == 'local': - _gen_chap_secrets(c) - - tmpl = jinja2.Template(ipoe_config, trim_blocks=True) + tmpl = env.get_template('chap-secrets.tmpl') + chap_secrets_txt = tmpl.render(c) + old_umask = os.umask(0o077) + with open(chap_secrets, 'w') as f: + f.write(chap_secrets_txt) + os.umask(old_umask) + + tmpl = env.get_template('ipoe.config.tmpl') config_text = tmpl.render(c) - open(ipoe_cnf, 'w').write(config_text) + with open(ipoe_cnf, 'w') as f: + f.write(config_text) return c @@ -465,7 +314,6 @@ def apply(c): raise ConfigError('accel-pppd failed to start') else: _accel_cmd('restart') - sl.syslog(sl.LOG_NOTICE, "reloading config via daemon restart") if __name__ == '__main__': @@ -476,4 +324,4 @@ if __name__ == '__main__': apply(c) except ConfigError as e: print(e) - sys.exit(1) + exit(1) diff --git a/src/conf_mode/service-pppoe.py b/src/conf_mode/service-pppoe.py index 22250d18b..afcc5ba99 100755 --- a/src/conf_mode/service-pppoe.py +++ b/src/conf_mode/service-pppoe.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018 VyOS maintainers and contributors +# Copyright (C) 2018-2020 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -13,19 +13,18 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -# -# -import sys import os import re import subprocess -import jinja2 -import socket -import time -import syslog as sl + +from jinja2 import FileSystemLoader, Environment +from socket import socket, AF_INET, SOCK_STREAM +from sys import exit +from time import sleep from vyos.config import Config +from vyos.defaults import directories as vyos_data_dir from vyos import ConfigError pidfile = r'/var/run/accel_pppoe.pid' @@ -38,282 +37,26 @@ pppoe_conf = pppoe_cnf_dir + '/pppoe.config' # config path creation if not os.path.exists(pppoe_cnf_dir): os.makedirs(pppoe_cnf_dir) - sl.syslog(sl.LOG_NOTICE, pppoe_cnf_dir + " created") - -pppoe_config = ''' -### generated by accel_pppoe.py ### -[modules] -log_syslog -pppoe -{% if authentication['mode'] == 'radius' %} -radius -{% endif %} -ippool -{% if ppp_options['ipv6'] != 'deny' %} -ipv6pool -ipv6_nd -ipv6_dhcp -{% endif %} -chap-secrets -auth_pap -auth_chap_md5 -auth_mschap_v1 -auth_mschap_v2 -#pppd_compat -shaper -{% if snmp == 'enable' or snmp == 'enable-ma' %} -net-snmp -{% endif %} -{% if limits %} -connlimit -{% endif %} - -[core] -thread-count={{thread_cnt}} - -[log] -syslog=accel-pppoe,daemon -copy=1 -level=5 - -{% if snmp == 'enable-ma' %} -[snmp] -master=1 -{% endif -%} - -[client-ip-range] -disable - -{% if ppp_gw %} -[ip-pool] -gw-ip-address={{ppp_gw}} -{% if client_ip_pool %} -{{client_ip_pool}} -{% endif -%} - -{% if client_ip_subnets %} -{% for sn in client_ip_subnets %} -{{sn}} -{% endfor %} -{% endif %} -{% endif -%} - -{% if client_ipv6_pool %} -[ipv6-pool] -{% for prfx in client_ipv6_pool['prefix']: %} -{{prfx}} -{% endfor %} -{% for prfx in client_ipv6_pool['delegate-prefix']: %} -delegate={{prfx}} -{% endfor %} -{% endif %} - -{% if dns %} -[dns] -{% if dns[0] %} -dns1={{dns[0]}} -{% endif -%} -{% if dns[1] %} -dns2={{dns[1]}} -{% endif -%} -{% endif %} - -{% if dnsv6 %} -[ipv6-dns] -{% for srv in dnsv6: %} -{{srv}} -{% endfor %} -{% endif %} - -{% if wins %} -[wins] -{% if wins[0] %} -wins1={{wins[0]}} -{% endif %} -{% if wins[1] %} -wins2={{wins[1]}} -{% endif -%} -{% endif -%} - -{% if authentication['mode'] == 'local' %} -[chap-secrets] -chap-secrets=/etc/accel-ppp/pppoe/chap-secrets -{% endif -%} - -{% if authentication['mode'] == 'radius' %} -[radius] -{% for rsrv in authentication['radiussrv']: %} -server={{rsrv}},{{authentication['radiussrv'][rsrv]['secret']}},\ -req-limit={{authentication['radiussrv'][rsrv]['req-limit']}},\ -fail-time={{authentication['radiussrv'][rsrv]['fail-time']}} -{% endfor %} -{% if authentication['radiusopt']['timeout'] %} -timeout={{authentication['radiusopt']['timeout']}} -{% endif %} -{% if authentication['radiusopt']['acct-timeout'] %} -acct-timeout={{authentication['radiusopt']['acct-timeout']}} -{% endif %} -{% if authentication['radiusopt']['max-try'] %} -max-try={{authentication['radiusopt']['max-try']}} -{% endif %} -{% if authentication['radiusopt']['nas-id'] %} -nas-identifier={{authentication['radiusopt']['nas-id']}} -{% endif %} -{% if authentication['radiusopt']['nas-ip'] %} -nas-ip-address={{authentication['radiusopt']['nas-ip']}} -{% endif -%} -{% if authentication['radiusopt']['dae-srv'] %} -dae-server={{authentication['radiusopt']['dae-srv']['ip-addr']}}:\ -{{authentication['radiusopt']['dae-srv']['port']}},\ -{{authentication['radiusopt']['dae-srv']['secret']}} -{% endif -%} -gw-ip-address={{ppp_gw}} -verbose=1 - -{% if authentication['radiusopt']['shaper'] %} -[shaper] -verbose=1 -attr={{authentication['radiusopt']['shaper']['attr']}} -{% if authentication['radiusopt']['shaper']['vendor'] %} -vendor={{authentication['radiusopt']['shaper']['vendor']}} -{% endif -%} -{% endif -%} -{% endif %} - -[ppp] -verbose=1 -check-ip=1 -{% if not sesscrtl == 'disable' %} -single-session={{sesscrtl}} -{% endif -%} -{% if ppp_options['ccp'] %} -ccp=1 -{% endif %} -{% if ppp_options['min-mtu'] %} -min-mtu={{ppp_options['min-mtu']}} -{% else %} -min-mtu={{mtu}} -{% endif %} -{% if ppp_options['mru'] %} -mru={{ppp_options['mru']}} -{% endif %} -{% if ppp_options['mppe'] %} -mppe={{ppp_options['mppe']}} -{% else %} -mppe=prefer -{% endif %} -{% if ppp_options['lcp-echo-interval'] %} -lcp-echo-interval={{ppp_options['lcp-echo-interval']}} -{% else %} -lcp-echo-interval=30 -{% endif %} -{% if ppp_options['lcp-echo-timeout'] %} -lcp-echo-timeout={{ppp_options['lcp-echo-timeout']}} -{% endif %} -{% if ppp_options['lcp-echo-failure'] %} -lcp-echo-failure={{ppp_options['lcp-echo-failure']}} -{% else %} -lcp-echo-failure=3 -{% endif %} -{% if ppp_options['ipv4'] %} -ipv4={{ppp_options['ipv4']}} -{% endif %} -{% if client_ipv6_pool %} -ipv6=allow -{% endif %} - -{% if ppp_options['ipv6'] %} -ipv6={{ppp_options['ipv6']}} -{% if ppp_options['ipv6-intf-id'] %} -ipv6-intf-id={{ppp_options['ipv6-intf-id']}} -{% endif %} -{% if ppp_options['ipv6-peer-intf-id'] %} -ipv6-peer-intf-id={{ppp_options['ipv6-peer-intf-id']}} -{% endif %} -{% if ppp_options['ipv6-accept-peer-intf-id'] %} -ipv6-accept-peer-intf-id={{ppp_options['ipv6-accept-peer-intf-id']}} -{% endif %} -{% endif %} -mtu={{mtu}} - -[pppoe] -verbose=1 -{% if concentrator %} -ac-name={{concentrator}} -{% endif %} -{% if interface %} -{% for int in interface %} -interface={{int}} -{% if interface[int]['vlans'] %} -vlan-mon={{int}},{{interface[int]['vlans']|join(',')}} -interface=re:{{int}}\.\d+ -{% endif %} -{% endfor -%} -{% endif -%} - -{% if svc_name %} -service-name={{svc_name|join(',')}} -{% endif -%} - -{% if pado_delay %} -pado-delay={{pado_delay}} -{% endif %} - -{% if limits %} -[connlimit] -limit={{limits['conn-limit']}} -burst={{limits['burst']}} -timeout={{limits['timeout']}} -{% endif %} - -[cli] -tcp=127.0.0.1:2001 -''' - -# pppoe chap secrets -chap_secrets_conf = ''' -# username server password acceptable local IP addresses shaper -{% for user in authentication['local-users'] %} -{% if authentication['local-users'][user]['state'] == 'enabled' %} -{% if (authentication['local-users'][user]['upload']) and (authentication['local-users'][user]['download']) %} -{{user}}\t*\t{{authentication['local-users'][user]['passwd']}}\t{{authentication['local-users'][user]['ip']}}\t\ -{{authentication['local-users'][user]['download']}}/{{authentication['local-users'][user]['upload']}} -{% else %} -{{user}}\t*\t{{authentication['local-users'][user]['passwd']}}\t{{authentication['local-users'][user]['ip']}} -{% endif %} -{% endif %} -{% endfor %} -''' + # # depending on hw and threads, daemon needs a little to start # if it takes longer than 100 * 0.5 secs, exception is being raised # not sure if that's the best way to check it, but it worked so far quite well # - - def _chk_con(): cnt = 0 - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s = socket(AF_INET, SOCK_STREAM) while True: try: s.connect(("127.0.0.1", 2001)) break except ConnectionRefusedError: - time.sleep(0.5) + sleep(0.5) cnt += 1 if cnt == 100: raise("failed to start pppoe server") -def _write_chap_secrets(c): - tmpl = jinja2.Template(chap_secrets_conf, trim_blocks=True) - chap_secrets_txt = tmpl.render(c) - old_umask = os.umask(0o077) - open(chap_secrets, 'w').write(chap_secrets_txt) - os.umask(old_umask) - sl.syslog(sl.LOG_NOTICE, chap_secrets + ' written') - - def _accel_cmd(cmd=''): if not cmd: return None @@ -640,6 +383,11 @@ def generate(c): if c == None: return None + # Prepare Jinja2 template loader from files + tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'pppoe-server') + fs_loader = FileSystemLoader(tmpl_path) + env = Environment(loader=fs_loader, trim_blocks=True) + # accel-cmd reload doesn't work so any change results in a restart of the # daemon try: @@ -653,12 +401,18 @@ def generate(c): else: c['thread_cnt'] = int(os.cpu_count() / 2) - tmpl = jinja2.Template(pppoe_config, trim_blocks=True) + tmpl = env.get_template('pppoe.config.tmpl') config_text = tmpl.render(c) - open(pppoe_conf, 'w').write(config_text) + with open(pppoe_conf, 'w') as f: + f.write(config_text) if c['authentication']['local-users']: - _write_chap_secrets(c) + tmpl = env.get_template('chap-secrets.tmpl') + chap_secrets_txt = tmpl.render(c) + old_umask = os.umask(0o077) + with open(chap_secrets, 'w') as f: + f.write(chap_secrets_txt) + os.umask(old_umask) return c @@ -680,7 +434,6 @@ def apply(c): raise ConfigError('accel-pppd failed to start') else: _accel_cmd('restart') - sl.syslog(sl.LOG_NOTICE, "reloading config via daemon restart") if __name__ == '__main__': @@ -691,4 +444,4 @@ if __name__ == '__main__': apply(c) except ConfigError as e: print(e) - sys.exit(1) + exit(1) diff --git a/src/conf_mode/service-router-advert.py b/src/conf_mode/service-router-advert.py index 1e0d28397..38c5cb2dc 100755 --- a/src/conf_mode/service-router-advert.py +++ b/src/conf_mode/service-router-advert.py @@ -15,55 +15,17 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. import os -import sys -import jinja2 +from jinja2 import FileSystemLoader, Environment from stat import S_IRUSR, S_IWUSR, S_IRGRP +from sys import exit + from vyos.config import Config +from vyos.defaults import directories as vyos_data_dir from vyos import ConfigError config_file = r'/etc/radvd.conf' -config_tmpl = """ -### Autogenerated by service-router-advert.py ### - -{% for i in interfaces -%} -interface {{ i.name }} { - IgnoreIfMissing on; - AdvDefaultPreference {{ i.default_preference }}; - AdvManagedFlag {{ i.managed_flag }}; - MaxRtrAdvInterval {{ i.interval_max }}; -{% if i.interval_min %} - MinRtrAdvInterval {{ i.interval_min }}; -{% endif %} - AdvReachableTime {{ i.reachable_time }}; - AdvIntervalOpt {{ i.send_advert }}; - AdvSendAdvert {{ i.send_advert }}; -{% if i.default_lifetime %} - AdvDefaultLifetime {{ i.default_lifetime }}; -{% endif %} -{% if i.link_mtu %} - AdvLinkMTU {{ i.link_mtu }}; -{% endif %} - AdvOtherConfigFlag {{ i.other_config_flag }}; - AdvRetransTimer {{ i.retrans_timer }}; - AdvCurHopLimit {{ i.hop_limit }}; -{% for p in i.prefixes %} - prefix {{ p.prefix }} { - AdvAutonomous {{ p.autonomous_flag }}; - AdvValidLifetime {{ p.valid_lifetime }}; - AdvOnLink {{ p.on_link }}; - AdvPreferredLifetime {{ p.preferred_lifetime }}; - }; -{% endfor %} -{% if i.name_server %} - RDNSS {{ i.name_server | join(" ") }} { - }; -{% endif %} -}; -{% endfor -%} -""" - default_config_data = { 'interfaces': [] } @@ -175,7 +137,12 @@ def generate(rtradv): if not rtradv['interfaces']: return None - tmpl = jinja2.Template(config_tmpl, trim_blocks=True) + # Prepare Jinja2 template loader from files + tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'router-advert') + fs_loader = FileSystemLoader(tmpl_path) + env = Environment(loader=fs_loader, trim_blocks=True) + + tmpl = env.get_template('radvd.conf.tmpl') config_text = tmpl.render(rtradv) with open(config_file, 'w') as f: f.write(config_text) @@ -189,13 +156,13 @@ def generate(rtradv): def apply(rtradv): if not rtradv['interfaces']: # bail out early - looks like removal from running config - os.system('sudo systemctl stop radvd.service') + os.system('systemctl stop radvd.service') if os.path.exists(config_file): os.unlink(config_file) return None - os.system('sudo systemctl restart radvd.service') + os.system('systemctl restart radvd.service') return None if __name__ == '__main__': @@ -206,4 +173,4 @@ if __name__ == '__main__': apply(c) except ConfigError as e: print(e) - sys.exit(1) + exit(1) diff --git a/src/conf_mode/system-login.py b/src/conf_mode/system-login.py index 959e86e5b..7acb0a9a2 100755 --- a/src/conf_mode/system-login.py +++ b/src/conf_mode/system-login.py @@ -14,36 +14,21 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -import sys import os -import jinja2 +from jinja2 import FileSystemLoader, Environment +from psutil import users from pwd import getpwall, getpwnam from stat import S_IRUSR, S_IWUSR, S_IRWXU, S_IRGRP, S_IXGRP from subprocess import Popen, PIPE, STDOUT -from psutil import users +from sys import exit from vyos.config import Config from vyos.configdict import list_diff +from vyos.defaults import directories as vyos_data_dir from vyos import ConfigError radius_config_file = "/etc/pam_radius_auth.conf" -radius_config_tmpl = """ -# Automatically generated by VyOS -# RADIUS configuration file -{%- if radius_server %} -# server[:port] shared_secret timeout (s) source_ip -{% for s in radius_server %} -{%- if not s.disabled -%} -{{ s.address }}:{{ s.port }} {{ s.key }} {{ s.timeout }} {% if radius_source_address -%}{{ radius_source_address }}{% endif %} -{% endif %} -{%- endfor %} - -priv-lvl 15 -mapped_priv_user radius_priv_user -{% endif %} - -""" default_config_data = { 'deleted': False, @@ -229,7 +214,12 @@ def generate(login): os.system("vyos_libexec_dir=/usr/libexec/vyos /opt/vyatta/sbin/my_set system login user '{}' authentication encrypted-password '{}' >/dev/null".format(user['name'], user['password_encrypted'])) if len(login['radius_server']) > 0: - tmpl = jinja2.Template(radius_config_tmpl) + # Prepare Jinja2 template loader from files + tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'system-login') + fs_loader = FileSystemLoader(tmpl_path) + env = Environment(loader=fs_loader) + + tmpl = env.get_template('pam_radius_auth.conf.tmpl') config_text = tmpl.render(login) with open(radius_config_file, 'w') as f: f.write(config_text) @@ -364,4 +354,4 @@ if __name__ == '__main__': apply(c) except ConfigError as e: print(e) - sys.exit(1) + exit(1) diff --git a/src/conf_mode/system-wifi-regdom.py b/src/conf_mode/system-wifi-regdom.py index 01dc92a20..943c42274 100755 --- a/src/conf_mode/system-wifi-regdom.py +++ b/src/conf_mode/system-wifi-regdom.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2019 VyOS maintainers and contributors +# Copyright (C) 2019-2020 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -15,52 +15,34 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. import os -import jinja2 from copy import deepcopy from sys import exit +from jinja2 import FileSystemLoader, Environment from vyos.config import Config +from vyos.defaults import directories as vyos_data_dir from vyos import ConfigError config_80211_file='/etc/modprobe.d/cfg80211.conf' config_crda_file='/etc/default/crda' -# Please be careful if you edit the template. -config_80211_tmpl = """ -{%- if regdom -%} -options cfg80211 ieee80211_regdom={{ regdom }} -{% endif %} -""" - -# Please be careful if you edit the template. -config_crda_tmpl = """ -{%- if regdom -%} -REGDOMAIN={{ regdom }} -{% endif %} -""" - default_config_data = { 'regdom' : '', 'deleted' : False } - def get_config(): regdom = deepcopy(default_config_data) conf = Config() - - # set new configuration level - conf.set_level('system') + base = ['system', 'wifi-regulatory-domain'] # Check if interface has been removed - if not conf.exists('wifi-regulatory-domain'): + if not conf.exists(base): regdom['deleted'] = True return regdom - - # retrieve configured regulatory domain - if conf.exists('wifi-regulatory-domain'): - regdom['regdom'] = conf.return_value('wifi-regulatory-domain') + else: + regdom['regdom'] = conf.return_value(base) return regdom @@ -85,12 +67,17 @@ def generate(regdom): return None - tmpl = jinja2.Template(config_80211_tmpl) + # Prepare Jinja2 template loader from files + tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'wifi') + fs_loader = FileSystemLoader(tmpl_path) + env = Environment(loader=fs_loader) + + tmpl = env.get_template('cfg80211.conf.tmpl') config_text = tmpl.render(regdom) with open(config_80211_file, 'w') as f: f.write(config_text) - tmpl = jinja2.Template(config_crda_tmpl) + tmpl = env.get_template('crda.tmpl') config_text = tmpl.render(regdom) with open(config_crda_file, 'w') as f: f.write(config_text) diff --git a/src/conf_mode/tftp_server.py b/src/conf_mode/tftp_server.py index ff7cad0c9..fe2da8455 100755 --- a/src/conf_mode/tftp_server.py +++ b/src/conf_mode/tftp_server.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018 VyOS maintainers and contributors +# Copyright (C) 2018-2020 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -13,32 +13,23 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -# -# -import sys import os import stat import pwd -import copy -import glob -import jinja2 -import vyos.validate +from copy import deepcopy +from glob import glob +from jinja2 import FileSystemLoader, Environment +from sys import exit from vyos.config import Config +from vyos.defaults import directories as vyos_data_dir +from vyos.validate import is_ipv4, is_addr_assigned from vyos import ConfigError config_file = r'/etc/default/tftpd' -# Please be careful if you edit the template. -config_tmpl = """ -### Autogenerated by tftp_server.py ### -DAEMON_ARGS="--listen --user tftp --address {% for a in listen-%}{{ a }}{% endfor %}{% if allow_upload %} --create --umask 000{% endif %} --secure {{ directory }}" - - -""" - default_config_data = { 'directory': '', 'allow_upload': False, @@ -47,23 +38,25 @@ default_config_data = { } def get_config(): - tftpd = copy.deepcopy(default_config_data) + tftpd = deepcopy(default_config_data) conf = Config() - if not conf.exists('service tftp-server'): + base = ['service', 'tftp-server'] + if not conf.exists(base): return None else: - conf.set_level('service tftp-server') + conf.set_level(base) - if conf.exists('directory'): - tftpd['directory'] = conf.return_value('directory') + if conf.exists(['directory']): + tftpd['directory'] = conf.return_value(['directory']) - if conf.exists('allow-upload'): + if conf.exists(['allow-upload']): tftpd['allow_upload'] = True - if conf.exists('port'): - tftpd['port'] = conf.return_value('port') + if conf.exists(['port']): + tftpd['port'] = conf.return_value(['port']) - tftpd['listen'] = conf.return_values('listen-address') + if conf.exists(['listen-address']): + tftpd['listen'] = conf.return_values(['listen-address']) return tftpd @@ -80,7 +73,7 @@ def verify(tftpd): raise ConfigError('TFTP server listen address must be configured!') for addr in tftpd['listen']: - if not vyos.validate.is_addr_assigned(addr): + if not is_addr_assigned(addr): print('WARNING: TFTP server listen address {0} not assigned to any interface!'.format(addr)) return None @@ -88,22 +81,27 @@ def verify(tftpd): def generate(tftpd): # cleanup any available configuration file # files will be recreated on demand - for i in glob.glob(config_file + '*'): + for i in glob(config_file + '*'): os.unlink(i) # bail out early - looks like removal from running config if tftpd is None: return None + # Prepare Jinja2 template loader from files + tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'tftp-server') + fs_loader = FileSystemLoader(tmpl_path) + env = Environment(loader=fs_loader) + idx = 0 for listen in tftpd['listen']: - config = copy.deepcopy(tftpd) - if vyos.validate.is_ipv4(listen): + config = deepcopy(tftpd) + if is_ipv4(listen): config['listen'] = [listen + ":" + tftpd['port'] + " -4"] else: config['listen'] = ["[" + listen + "]" + tftpd['port'] + " -6"] - tmpl = jinja2.Template(config_tmpl) + tmpl = env.get_template('default.tmpl') config_text = tmpl.render(config) file = config_file + str(idx) with open(file, 'w') as f: @@ -115,7 +113,7 @@ def generate(tftpd): def apply(tftpd): # stop all services first - then we will decide - os.system('sudo systemctl stop tftpd@{0..20}') + os.system('systemctl stop tftpd@{0..20}') # bail out early - e.g. service deletion if tftpd is None: @@ -140,7 +138,7 @@ def apply(tftpd): idx = 0 for listen in tftpd['listen']: - os.system('sudo systemctl restart tftpd@{0}.service'.format(idx)) + os.system('systemctl restart tftpd@{0}.service'.format(idx)) idx = idx + 1 return None @@ -153,4 +151,4 @@ if __name__ == '__main__': apply(c) except ConfigError as e: print(e) - sys.exit(1) + exit(1) diff --git a/src/conf_mode/vpn-pptp.py b/src/conf_mode/vpn-pptp.py index 355adf715..b1204a505 100755 --- a/src/conf_mode/vpn-pptp.py +++ b/src/conf_mode/vpn-pptp.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018 VyOS maintainers and contributors +# Copyright (C) 2018-2020 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -13,19 +13,18 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -# -# -import sys import os import re import subprocess -import jinja2 -import socket -import time -import syslog as sl + +from jinja2 import FileSystemLoader, Environment +from socket import socket, AF_INET, SOCK_STREAM +from sys import exit +from time import sleep from vyos.config import Config +from vyos.defaults import directories as vyos_data_dir from vyos import ConfigError pidfile = r'/var/run/accel_pptp.pid' @@ -36,117 +35,16 @@ pptp_conf = pptp_cnf_dir + '/pptp.config' # config path creation if not os.path.exists(pptp_cnf_dir): os.makedirs(pptp_cnf_dir) - sl.syslog(sl.LOG_NOTICE, pptp_cnf_dir + " created") - -pptp_config = ''' -### generated by accel_pptp.py ### -[modules] -log_syslog -pptp -ippool -chap-secrets -{% if authentication['auth_proto'] %} -{{ authentication['auth_proto'] }} -{% else %} -auth_mschap_v2 -{% endif %} -{% if authentication['mode'] == 'radius' %} -radius -{% endif -%} - -[core] -thread-count={{thread_cnt}} - -[log] -syslog=accel-pptp,daemon -copy=1 -level=5 - -{% if dns %} -[dns] -{% if dns[0] %} -dns1={{dns[0]}} -{% endif %} -{% if dns[1] %} -dns2={{dns[1]}} -{% endif %} -{% endif %} - -{% if wins %} -[wins] -{% if wins[0] %} -wins1={{wins[0]}} -{% endif %} -{% if wins[1] %} -wins2={{wins[1]}} -{% endif %} -{% endif %} - -[pptp] -ifname=pptp%d -{% if outside_addr %} -bind={{outside_addr}} -{% endif %} -verbose=1 -ppp-max-mtu={{mtu}} -mppe={{authentication['mppe']}} -echo-interval=10 -echo-failure=3 - - -[client-ip-range] -0.0.0.0/0 - -[ip-pool] -tunnel={{client_ip_pool}} -gw-ip-address={{gw_ip}} - -{% if authentication['mode'] == 'local' %} -[chap-secrets] -chap-secrets=/etc/accel-ppp/pptp/chap-secrets -{% endif %} - -[ppp] -verbose=5 -check-ip=1 -single-session=replace - -{% if authentication['mode'] == 'radius' %} -[radius] -{% for rsrv in authentication['radiussrv']: %} -server={{rsrv}},{{authentication['radiussrv'][rsrv]['secret']}},\ -req-limit={{authentication['radiussrv'][rsrv]['req-limit']}},\ -fail-time={{authentication['radiussrv'][rsrv]['fail-time']}} -{% endfor %} -timeout=30 -acct-timeout=30 -max-try=3 -{%endif %} - -[cli] -tcp=127.0.0.1:2003 -''' - -# pptp chap secrets -chap_secrets_conf = ''' -# username server password acceptable local IP addresses -{% for user in authentication['local-users'] %} -{% if authentication['local-users'][user]['state'] == 'enabled' %} -{{user}}\t*\t{{authentication['local-users'][user]['passwd']}}\t{{authentication['local-users'][user]['ip']}} -{% endif %} -{% endfor %} -''' - def _chk_con(): cnt = 0 - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s = socket(AF_INET, SOCK_STREAM) while True: try: s.connect(("127.0.0.1", 2003)) break except ConnectionRefusedError: - time.sleep(0.5) + sleep(0.5) cnt += 1 if cnt == 100: raise("failed to start pptp server") @@ -154,16 +52,6 @@ def _chk_con(): # chap_secrets file if auth mode local - -def _write_chap_secrets(c): - tmpl = jinja2.Template(chap_secrets_conf, trim_blocks=True) - chap_secrets_txt = tmpl.render(c) - old_umask = os.umask(0o077) - open(chap_secrets, 'w').write(chap_secrets_txt) - os.umask(old_umask) - sl.syslog(sl.LOG_NOTICE, chap_secrets + ' written') - - def _accel_cmd(cmd=''): if not cmd: return None @@ -326,6 +214,11 @@ def generate(c): if c == None: return None + # Prepare Jinja2 template loader from files + tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'pptp') + fs_loader = FileSystemLoader(tmpl_path) + env = Environment(loader=fs_loader, trim_blocks=True) + # accel-cmd reload doesn't work so any change results in a restart of the daemon try: if os.cpu_count() == 1: @@ -338,12 +231,18 @@ def generate(c): else: c['thread_cnt'] = int(os.cpu_count()/2) - tmpl = jinja2.Template(pptp_config, trim_blocks=True) + tmpl = env.get_template('pptp.config.tmpl') config_text = tmpl.render(c) - open(pptp_conf, 'w').write(config_text) + with open(pptp_conf, 'w') as f: + f.write(config_text) if c['authentication']['local-users']: - _write_chap_secrets(c) + tmpl = env.get_template('chap-secrets.tmpl') + chap_secrets_txt = tmpl.render(c) + old_umask = os.umask(0o077) + with open(chap_secrets, 'w') as f: + f.write(chap_secrets_txt) + os.umask(old_umask) return c @@ -366,8 +265,6 @@ def apply(c): else: # if gw ip changes, only restart doesn't work _accel_cmd('restart') - sl.syslog(sl.LOG_NOTICE, "reloading config via daemon restart") - if __name__ == '__main__': try: @@ -377,4 +274,4 @@ if __name__ == '__main__': apply(c) except ConfigError as e: print(e) - sys.exit(1) + exit(1) diff --git a/src/conf_mode/vrf.py b/src/conf_mode/vrf.py index 53ee13bec..8cf4b72ae 100755 --- a/src/conf_mode/vrf.py +++ b/src/conf_mode/vrf.py @@ -15,34 +15,22 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. import os -import jinja2 from sys import exit from copy import deepcopy +from jinja2 import FileSystemLoader, Environment from json import loads from subprocess import check_output, CalledProcessError from vyos.config import Config from vyos.configdict import list_diff +from vyos.defaults import directories as vyos_data_dir from vyos.ifconfig import Interface from vyos.util import read_file from vyos import ConfigError config_file = r'/etc/iproute2/rt_tables.d/vyos-vrf.conf' -# Please be careful if you edit the template. -config_tmpl = """ -### Autogenerated by vrf.py ### -# -# Routing table ID to name mapping reference - -# id vrf name comment -{% for vrf in vrf_add -%} -{{ "%-10s" | format(vrf.table) }} {{ "%-16s" | format(vrf.name) }} # {{ vrf.description }} -{% endfor -%} - -""" - default_config_data = { 'bind_to_all': '0', 'deleted': False, @@ -194,7 +182,12 @@ def verify(vrf_config): return None def generate(vrf_config): - tmpl = jinja2.Template(config_tmpl) + # Prepare Jinja2 template loader from files + tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'vrf') + fs_loader = FileSystemLoader(tmpl_path) + env = Environment(loader=fs_loader) + + tmpl = env.get_template('vrf.conf.tmpl') config_text = tmpl.render(vrf_config) with open(config_file, 'w') as f: f.write(config_text) diff --git a/src/conf_mode/vrrp.py b/src/conf_mode/vrrp.py index b206f0d11..8683faca7 100755 --- a/src/conf_mode/vrrp.py +++ b/src/conf_mode/vrrp.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018 VyOS maintainers and contributors +# Copyright (C) 2018-2020 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -13,135 +13,26 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -# import os -import sys import subprocess -import ipaddress -import json -import jinja2 + +from sys import exit +from ipaddress import ip_address, ip_interface, IPv4Interface, IPv6Interface, IPv4Address, IPv6Address +from jinja2 import FileSystemLoader, Environment +from json import dumps +from pathlib import Path import vyos.config import vyos.keepalived +from vyos.defaults import directories as vyos_data_dir from vyos import ConfigError -from pathlib import Path daemon_file = "/etc/default/keepalived" config_file = "/etc/keepalived/keepalived.conf" config_dict_path = "/run/keepalived_config.dict" -config_tmpl = """ -# Autogenerated by VyOS -# Do not edit this file, all your changes will be lost -# on next commit or reboot - -global_defs { - dynamic_interfaces - script_user root - notify_fifo /run/keepalived_notify_fifo - notify_fifo_script /usr/libexec/vyos/system/keepalived-fifo.py -} - -{% for group in groups -%} - -{% if group.health_check_script -%} -vrrp_script healthcheck_{{ group.name }} { - script "{{ group.health_check_script }}" - interval {{ group.health_check_interval }} - fall {{ group.health_check_count }} - rise 1 - -} -{% endif %} - -vrrp_instance {{ group.name }} { - {% if group.description -%} - # {{ group.description }} - {% endif -%} - - state BACKUP - interface {{ group.interface }} - virtual_router_id {{ group.vrid }} - priority {{ group.priority }} - advert_int {{ group.advertise_interval }} - - {% if group.preempt -%} - preempt_delay {{ group.preempt_delay }} - {% else -%} - nopreempt - {% endif -%} - - {% if group.peer_address -%} - unicast_peer { {{ group.peer_address }} } - {% endif -%} - - {% if group.hello_source -%} - {%- if group.peer_address -%} - unicast_src_ip {{ group.hello_source }} - {%- else -%} - mcast_src_ip {{ group.hello_source }} - {%- endif %} - {% endif -%} - - {% if group.use_vmac and group.peer_address -%} - use_vmac {{group.interface}}v{{group.vrid}} - vmac_xmit_base - {% elif group.use_vmac -%} - use_vmac {{group.interface}}v{{group.vrid}} - {% endif -%} - - {% if group.auth_password -%} - authentication { - auth_pass "{{ group.auth_password }}" - auth_type {{ group.auth_type }} - } - {% endif -%} - - virtual_ipaddress { - {% for addr in group.virtual_addresses -%} - {{ addr }} - {% endfor -%} - } - - {% if group.health_check_script -%} - track_script { - healthcheck_{{ group.name }} - } - {% endif -%} -} - -{% endfor -%} - -{% for sync_group in sync_groups -%} -vrrp_sync_group {{ sync_group.name }} { - group { - {% for member in sync_group.members -%} - {{ member }} - {% endfor -%} - } - - {% if sync_group.conntrack_sync -%} - notify_master "/opt/vyatta/sbin/vyatta-vrrp-conntracksync.sh master {{ sync_group.name }}" - notify_backup "/opt/vyatta/sbin/vyatta-vrrp-conntracksync.sh backup {{ sync_group.name }}" - notify_fault "/opt/vyatta/sbin/vyatta-vrrp-conntracksync.sh fault {{ sync_group.name }}" - {% endif -%} -} - -{% endfor -%} - -""" - -daemon_tmpl = """ -# Autogenerated by VyOS -# Options to pass to keepalived - -# DAEMON_ARGS are appended to the keepalived command-line -DAEMON_ARGS="--snmp" -""" - - def get_config(): vrrp_groups = [] sync_groups = [] @@ -209,7 +100,7 @@ def get_config(): vrrp_groups.append(group) - config.set_level("") + config.set_level("") # Get the sync group used for conntrack-sync conntrack_sync_group = None @@ -237,7 +128,7 @@ def get_config(): # create a file with dict with proposed configuration with open("{}.temp".format(config_dict_path), 'w') as dict_file: - dict_file.write(json.dumps({'vrrp_groups': vrrp_groups, 'sync_groups': sync_groups})) + dict_file.write(dumps({'vrrp_groups': vrrp_groups, 'sync_groups': sync_groups})) return (vrrp_groups, sync_groups) @@ -262,31 +153,31 @@ def verify(data): # XXX: filter on map object is destructive, so we force it to list. # Additionally, filter objects always evaluate to True, empty or not, # so we force them to lists as well. - vaddrs = list(map(lambda i: ipaddress.ip_interface(i), group["virtual_addresses"])) - vaddrs4 = list(filter(lambda x: isinstance(x, ipaddress.IPv4Interface), vaddrs)) - vaddrs6 = list(filter(lambda x: isinstance(x, ipaddress.IPv6Interface), vaddrs)) + vaddrs = list(map(lambda i: ip_interface(i), group["virtual_addresses"])) + vaddrs4 = list(filter(lambda x: isinstance(x, IPv4Interface), vaddrs)) + vaddrs6 = list(filter(lambda x: isinstance(x, IPv6Interface), vaddrs)) if vaddrs4 and vaddrs6: raise ConfigError("VRRP group {0} mixes IPv4 and IPv6 virtual addresses, this is not allowed. Create separate groups for IPv4 and IPv6".format(group["name"])) if vaddrs4: if group["hello_source"]: - hsa = ipaddress.ip_address(group["hello_source"]) - if isinstance(hsa, ipaddress.IPv6Address): + hsa = ip_address(group["hello_source"]) + if isinstance(hsa, IPv6Address): raise ConfigError("VRRP group {0} uses IPv4 but its hello-source-address is IPv6".format(group["name"])) if group["peer_address"]: - pa = ipaddress.ip_address(group["peer_address"]) - if isinstance(pa, ipaddress.IPv6Address): + pa = ip_address(group["peer_address"]) + if isinstance(pa, IPv6Address): raise ConfigError("VRRP group {0} uses IPv4 but its peer-address is IPv6".format(group["name"])) if vaddrs6: if group["hello_source"]: - hsa = ipaddress.ip_address(group["hello_source"]) - if isinstance(hsa, ipaddress.IPv4Address): + hsa = ip_address(group["hello_source"]) + if isinstance(hsa, IPv4Address): raise ConfigError("VRRP group {0} uses IPv6 but its hello-source-address is IPv4".format(group["name"])) if group["peer_address"]: - pa = ipaddress.ip_address(group["peer_address"]) - if isinstance(pa, ipaddress.IPv4Address): + pa = ip_address(group["peer_address"]) + if isinstance(pa, IPv4Address): raise ConfigError("VRRP group {0} uses IPv6 but its peer-address is IPv4".format(group["name"])) # Disallow same VRID on multiple interfaces @@ -310,6 +201,11 @@ def verify(data): def generate(data): + # Prepare Jinja2 template loader from files + tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'vrrp') + fs_loader = FileSystemLoader(tmpl_path) + env = Environment(loader=fs_loader) + vrrp_groups, sync_groups = data # Remove disabled groups from the sync group member lists @@ -321,13 +217,15 @@ def generate(data): # Filter out disabled groups vrrp_groups = list(filter(lambda x: x["disable"] is not True, vrrp_groups)) - tmpl = jinja2.Template(config_tmpl) + tmpl = env.get_template('keepalived.conf.tmpl') config_text = tmpl.render({"groups": vrrp_groups, "sync_groups": sync_groups}) with open(config_file, 'w') as f: f.write(config_text) + tmpl = env.get_template('daemon.tmpl') + config_text = tmpl.render() with open(daemon_file, 'w') as f: - f.write(daemon_tmpl) + f.write(config_text) return None @@ -368,4 +266,4 @@ if __name__ == '__main__': apply(c) except ConfigError as e: print("VRRP error: {0}".format(str(e))) - sys.exit(1) + exit(1) |