From 564c75c511c2cfd23404a500340a53441c694ffd Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 23 Oct 2019 05:45:28 +0200 Subject: ddclient: T1030: use new default configuration file path --- src/conf_mode/dynamic_dns.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'src/conf_mode') diff --git a/src/conf_mode/dynamic_dns.py b/src/conf_mode/dynamic_dns.py index ff3c1f825..d4e890ebb 100755 --- a/src/conf_mode/dynamic_dns.py +++ b/src/conf_mode/dynamic_dns.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018 VyOS maintainers and contributors +# Copyright (C) 2018-2019 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,8 +13,6 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# import os import sys @@ -23,7 +21,7 @@ import jinja2 from vyos.config import Config from vyos import ConfigError -config_file = r'/etc/ddclient.conf' +config_file = r'/etc/ddclient/ddclient.conf' cache_file = r'/var/cache/ddclient/ddclient.cache' config_tmpl = """ -- cgit v1.2.3 From 760ac992c827734032cc41a77ba21bcc4bde50e1 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 23 Oct 2019 05:46:34 +0200 Subject: ddclient: T1030: auto create runtime directories --- src/conf_mode/dynamic_dns.py | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) (limited to 'src/conf_mode') diff --git a/src/conf_mode/dynamic_dns.py b/src/conf_mode/dynamic_dns.py index d4e890ebb..3dc6857f9 100755 --- a/src/conf_mode/dynamic_dns.py +++ b/src/conf_mode/dynamic_dns.py @@ -23,14 +23,15 @@ from vyos import ConfigError config_file = r'/etc/ddclient/ddclient.conf' cache_file = r'/var/cache/ddclient/ddclient.cache' +pid_file = r'/var/run/ddclient/ddclient.pid' config_tmpl = """ ### Autogenerated by dynamic_dns.py ### daemon=1m syslog=yes ssl=yes -pid=/var/run/ddclient/ddclient.pid -cache=/var/cache/ddclient/ddclient.cache +pid={{ pid_file }} +cache={{ cache_file }} {% for interface in interfaces -%} @@ -89,6 +90,8 @@ default_service_protocol = { default_config_data = { 'interfaces': [], + 'cache_file': cache_file, + 'pid_file': pid_file } def get_config(): @@ -235,8 +238,15 @@ def generate(dyndns): if dyndns is None: return None - tmpl = jinja2.Template(config_tmpl) + dirname = os.path.dirname(dyndns['pid_file']) + if not os.path.exists(dirname): + os.mkdir(dirname) + + dirname = os.path.dirname(config_file) + if not os.path.exists(dirname): + os.mkdir(dirname) + tmpl = jinja2.Template(config_tmpl) config_text = tmpl.render(dyndns) with open(config_file, 'w') as f: f.write(config_text) @@ -244,11 +254,16 @@ def generate(dyndns): return None def apply(dyndns): - if os.path.exists(cache_file): - os.unlink(cache_file) + if os.path.exists(dyndns['cache_file']): + os.unlink(dyndns['cache_file']) + + if os.path.exists('/etc/ddclient.conf'): + os.unlink('/etc/ddclient.conf') if dyndns is None: os.system('/etc/init.d/ddclient stop') + if os.path.exists(dyndns['pid_file']): + os.unlink(dyndns['pid_file']) else: os.system('/etc/init.d/ddclient restart') -- cgit v1.2.3 From 967067970494c1800f028e5a44ff2fc9e39eabb9 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 23 Oct 2019 05:46:54 +0200 Subject: ddclient: T1030: adjust to latest syntax --- src/conf_mode/dynamic_dns.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'src/conf_mode') diff --git a/src/conf_mode/dynamic_dns.py b/src/conf_mode/dynamic_dns.py index 3dc6857f9..c4a95f5f1 100755 --- a/src/conf_mode/dynamic_dns.py +++ b/src/conf_mode/dynamic_dns.py @@ -47,11 +47,11 @@ use=if, if={{ interface.interface }} {% for rfc in interface.rfc2136 -%} {% for record in rfc.record %} # RFC2136 dynamic DNS configuration for {{ record }}.{{ rfc.zone }} -server={{ rfc.server }} -protocol=nsupdate -password={{ rfc.keyfile }} -ttl={{ rfc.ttl }} -zone={{ rfc.zone }} +server={{ rfc.server }}, +protocol=nsupdate, +password={{ rfc.keyfile }}, +ttl={{ rfc.ttl }}, +zone={{ rfc.zone }}, {{ record }} {% endfor -%} {% endfor -%} @@ -59,12 +59,12 @@ zone={{ rfc.zone }} {% for srv in interface.service %} {% for host in srv.host %} # DynDNS provider configuration for {{ host }} -protocol={{ srv.protocol }} -max-interval=28d -login={{ srv.login }} -password='{{ srv.password }}' +protocol={{ srv.protocol }}, +max-interval=28d, +login={{ srv.login }}, +password='{{ srv.password }}', {% if srv.server -%} -server={{ srv.server }} +server={{ srv.server }}, {% endif -%} {{ host }} {% endfor %} -- cgit v1.2.3 From 3f8884587ab1291c13f633b079058879241ac3c1 Mon Sep 17 00:00:00 2001 From: Daniil Baturin Date: Wed, 23 Oct 2019 15:41:58 +0200 Subject: [HTTP API] Add endpoints for config file and image management. --- python/vyos/configsession.py | 18 +++++++++ src/conf_mode/https.py | 2 +- src/services/vyos-http-api-server | 77 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 95 insertions(+), 2 deletions(-) (limited to 'src/conf_mode') diff --git a/python/vyos/configsession.py b/python/vyos/configsession.py index 09fae78a1..ed6288939 100644 --- a/python/vyos/configsession.py +++ b/python/vyos/configsession.py @@ -25,6 +25,9 @@ COMMIT = '/opt/vyatta/sbin/my_commit' DISCARD = '/opt/vyatta/sbin/my_discard' SHOW_CONFIG = ['/bin/cli-shell-api', 'showConfig'] LOAD_CONFIG = ['/bin/cli-shell-api', 'loadFile'] +SAVE_CONFIG = ['/opt/vyatta/sbin/vyatta-save-config.pl'] +INSTALL_IMAGE = ['/opt/vyatta/sbin/install-image'] +REMOVE_IMAGE = ['/opt/vyatta/bin/vyatta-boot-image.pl', '--del'] # Default "commit via" string APP = "vyos-http-api" @@ -36,6 +39,8 @@ APP = "vyos-http-api" def inject_vyos_env(env): env['VYATTA_CFG_GROUP_NAME'] = 'vyattacfg' env['VYATTA_USER_LEVEL_DIR'] = '/opt/vyatta/etc/shell/level/admin' + env['VYATTA_PROCESS_CLIENT'] = 'gui2_rest' + env['VYOS_HEADLESS_CLIENT'] = 'vyos_http_api' env['vyatta_bindir']= '/opt/vyatta/bin' env['vyatta_cfg_templates'] = '/opt/vyatta/share/vyatta-cfg/templates' env['vyatta_configdir'] = '/opt/vyatta/config' @@ -160,3 +165,16 @@ class ConfigSession(object): def load_config(self, file_path): out = self.__run_command(LOAD_CONFIG + [file_path]) return out + + def save_config(self, file_path): + out = self.__run_command(SAVE_CONFIG + [file_path]) + return out + + def install_image(self, url): + out = self.__run_command(INSTALL_IMAGE + [url]) + return out + + def remove_image(self, name): + out = self.__run_command(REMOVE_IMAGE + [name]) + return out + diff --git a/src/conf_mode/https.py b/src/conf_mode/https.py index f948063e9..233c815bc 100755 --- a/src/conf_mode/https.py +++ b/src/conf_mode/https.py @@ -67,7 +67,7 @@ server { {% endif %} # proxy settings for HTTP API, if enabled; 503, if not - location ~ /(retrieve|configure) { + location ~ /(retrieve|configure|config-file|image) { {% if api %} proxy_pass http://localhost:{{ api.port }}; proxy_buffering off; diff --git a/src/services/vyos-http-api-server b/src/services/vyos-http-api-server index 63e67e855..04c44c2be 100755 --- a/src/services/vyos-http-api-server +++ b/src/services/vyos-http-api-server @@ -34,7 +34,6 @@ from vyos.config import VyOSError DEFAULT_CONFIG_FILE = '/etc/vyos/http-api.conf' - CFG_GROUP = 'vyattacfg' app = bottle.default_app() @@ -223,6 +222,82 @@ def get_value(): return success(res) +@app.route('/config-file', method='POST') +@auth_required +def config_file_op(): + config = app.config['vyos_config'] + session = app.config['vyos_session'] + + command = bottle.request.forms.get("data") + command = json.loads(command) + + try: + op = command['op'] + except KeyError: + return error(400, "Missing required field \"op\"") + + try: + if op == 'save': + try: + path = command['file'] + except KeyError: + path = '/config/config.boot' + res = session.save_config(path) + elif op == 'load': + try: + path = command['file'] + except KeyError: + return error(400, "Missing required field \"file\"") + res = session.load_config(path) + res = session.commit() + else: + return error(400, "\"{0}\" is not a valid operation".format(op)) + except VyOSError as e: + return error(400, str(e)) + except Exception as e: + print(traceback.format_exc(), file=sys.stderr) + return error(500, "An internal error occured. Check the logs for details.") + + return success(res) + +@app.route('/image', method='POST') +@auth_required +def config_file_op(): + config = app.config['vyos_config'] + session = app.config['vyos_session'] + + command = bottle.request.forms.get("data") + command = json.loads(command) + + try: + op = command['op'] + except KeyError: + return error(400, "Missing required field \"op\"") + + try: + if op == 'add': + try: + url = command['url'] + except KeyError: + return error(400, "Missing required field \"url\"") + res = session.install_image(url) + elif op == 'delete': + try: + name = command['name'] + except KeyError: + return error(400, "Missing required field \"name\"") + res = session.remove_image(name) + else: + return error(400, "\"{0}\" is not a valid operation".format(op)) + except VyOSError as e: + return error(400, str(e)) + except Exception as e: + print(traceback.format_exc(), file=sys.stderr) + return error(500, "An internal error occured. Check the logs for details.") + + return success(res) + + if __name__ == '__main__': # systemd's user and group options don't work, do it by hand here, # else no one else will be able to commit -- cgit v1.2.3 From 3aadfc1fb0e3af03a98fb908c8709e4099296821 Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Wed, 23 Oct 2019 14:13:21 -0500 Subject: [service https] T1443: organize internal data by server block --- src/conf_mode/https.py | 107 +++++++++++++++++++++---------------------------- 1 file changed, 46 insertions(+), 61 deletions(-) (limited to 'src/conf_mode') diff --git a/src/conf_mode/https.py b/src/conf_mode/https.py index 233c815bc..d7fcb74de 100755 --- a/src/conf_mode/https.py +++ b/src/conf_mode/https.py @@ -30,34 +30,34 @@ config_file = '/etc/nginx/sites-available/default' # Please be careful if you edit the template. config_tmpl = """ -### Autogenerated by http-api.py ### +### Autogenerated by https.py ### # Default server configuration # server { listen 80 default_server; listen [::]:80 default_server; server_name _; - return 302 https://$server_name$request_uri; + return 301 https://$server_name$request_uri; } -{% for addr, names in listen_addresses.items() %} +{% for server in server_block_list %} server { # SSL configuration # -{% if addr == '*' %} - listen 443 ssl default_server; - listen [::]:443 ssl default_server; +{% if server.address == '*' %} + listen 443 ssl; + listen [::]:443 ssl; {% else %} - listen {{ addr }}:443 ssl; + listen {{ server.address }}:443 ssl; {% endif %} -{% for name in names %} +{% for name in server.name %} server_name {{ name }}; {% endfor %} -{% if vyos_cert %} - include {{ vyos_cert.conf }}; +{% if server.vyos_cert %} + include {{ server.vyos_cert.conf }}; {% else %} # # Self signed certs generated by the ssl-cert package @@ -68,45 +68,8 @@ server { # proxy settings for HTTP API, if enabled; 503, if not location ~ /(retrieve|configure|config-file|image) { -{% if api %} - proxy_pass http://localhost:{{ 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"}'; - } - -} -{% else %} -server { - # SSL configuration - # - listen 443 ssl default_server; - listen [::]:443 ssl default_server; - - server_name _; - -{% if vyos_cert %} - include {{ 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) { -{% if api %} - proxy_pass http://localhost:{{ api.port }}; +{% if server.api %} + proxy_pass http://localhost:{{ server.api.port }}; proxy_buffering off; {% else %} return 503; @@ -125,8 +88,16 @@ server { {% endfor %} """ +default_server_block = { + 'address' : '*', + 'name' : ['_'], + # api : + # vyos_cert : + # le_cert : +} + def get_config(): - https = vyos.defaults.https_data + server_block_list = [] conf = Config() if not conf.exists('service https'): return None @@ -134,25 +105,36 @@ def get_config(): conf.set_level('service https') if conf.exists('listen-address'): - addrs = {} for addr in conf.list_nodes('listen-address'): - addrs[addr] = ['_'] + server_block = {'address' : addr} + server_block['name'] = ['_'] if conf.exists('listen-address {0} server-name'.format(addr)): names = conf.return_values('listen-address {0} server-name'.format(addr)) - addrs[addr] = names[:] - https['listen_addresses'] = addrs + server_block['name'] = names[:] + server_block_list.append(server_block) + if not server_block_list: + server_block_list.append(default_server_block) + + vyos_cert_data = {} if conf.exists('certificates'): if conf.exists('certificates system-generated-certificate'): - https['vyos_cert'] = vyos.defaults.vyos_cert_data + vyos_cert_data = vyos.defaults.vyos_cert_data + if vyos_cert_data: + for block in server_block_list: + block['vyos_cert'] = vyos_cert_data + api_data = {} if conf.exists('api'): - https['api'] = vyos.defaults.api_data - - if conf.exists('api port'): - port = conf.return_value('api port') - https['api']['port'] = port - + api_data = vyos.defaults.api_data + if conf.exists('api port'): + port = conf.return_value('api port') + api_data['port'] = port + if api_data: + for block in server_block_list: + block['api'] = api_data + + https = {'server_block_list' : server_block_list} return https def verify(https): @@ -162,6 +144,9 @@ 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] + tmpl = jinja2.Template(config_tmpl, trim_blocks=True) config_text = tmpl.render(https) with open(config_file, 'w') as f: -- cgit v1.2.3 From bbb3a9ecdf2cf76b6336414928030798007dc1c7 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Wed, 23 Oct 2019 06:30:12 -0600 Subject: ddclient: T1030: add cloudflare zone config entry --- src/conf_mode/dynamic_dns.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/conf_mode') diff --git a/src/conf_mode/dynamic_dns.py b/src/conf_mode/dynamic_dns.py index c4a95f5f1..027a7f7e3 100755 --- a/src/conf_mode/dynamic_dns.py +++ b/src/conf_mode/dynamic_dns.py @@ -66,6 +66,10 @@ password='{{ srv.password }}', {% if srv.server -%} server={{ srv.server }}, {% endif -%} +{% if 'cloudflare' in srv.protocol -%} +{% set zone = host.split('.',1) -%} +zone={{ zone[1] }}, +{% endif -%} {{ host }} {% endfor %} {% endfor %} -- cgit v1.2.3 From 3400b1dd79702553ebbd40516bf454f3fe47885b Mon Sep 17 00:00:00 2001 From: Daniil Baturin Date: Thu, 24 Oct 2019 11:33:06 +0200 Subject: T1762: adjust the set_level() calls to use the new list representation. --- python/vyos/configdict.py | 6 +++--- src/conf_mode/interfaces-ethernet.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'src/conf_mode') diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py index 983906923..e8e52b33d 100644 --- a/python/vyos/configdict.py +++ b/python/vyos/configdict.py @@ -106,7 +106,7 @@ def vlan_to_dict(conf): Function call's itself recursively if a vif-s/vif-c pair is detected. """ vlan = { - 'id': conf.get_level().split()[-1], # get the '100' in 'interfaces bonding bond0 vif-s 100' + 'id': conf.get_level()[-1], # get the '100' in 'interfaces bonding bond0 vif-s 100' 'address': [], 'address_remove': [], 'description': '', @@ -192,7 +192,7 @@ def vlan_to_dict(conf): # ethertype is mandatory on vif-s nodes and only exists here! # check if this is a vif-s node at all: - if conf.get_level().split()[-2] == 'vif-s': + if conf.get_level()[-2] == 'vif-s': vlan['vif_c'] = [] vlan['vif_c_remove'] = [] @@ -215,7 +215,7 @@ def vlan_to_dict(conf): # add new key (vif-c) to dictionary for vif in conf.list_nodes('vif-c'): # set config level to vif interface - conf.set_level(cfg_level + ' vif-c ' + vif) + conf.set_level(cfg_level + ['vif-c', vif]) vlan['vif_c'].append(vlan_to_dict(conf)) return vlan diff --git a/src/conf_mode/interfaces-ethernet.py b/src/conf_mode/interfaces-ethernet.py index cd40aff3e..a9ed6bfb6 100755 --- a/src/conf_mode/interfaces-ethernet.py +++ b/src/conf_mode/interfaces-ethernet.py @@ -130,7 +130,7 @@ def get_config(): print("Interface not specified") # check if ethernet interface has been removed - cfg_base = 'interfaces ethernet ' + eth['intf'] + cfg_base = ['interfaces', 'ethernet', eth['intf']] if not conf.exists(cfg_base): eth['deleted'] = True # we can not bail out early as ethernet interface can not be removed @@ -249,7 +249,7 @@ def get_config(): if conf.exists('vif-s'): for vif_s in conf.list_nodes('vif-s'): # set config level to vif-s interface - conf.set_level(cfg_base + ' vif-s ' + vif_s) + conf.set_level(cfg_base + ['vif-s', vif_s]) eth['vif_s'].append(vlan_to_dict(conf)) # re-set configuration level to parse new nodes @@ -263,7 +263,7 @@ def get_config(): if conf.exists('vif'): for vif in conf.list_nodes('vif'): # set config level to vif interface - conf.set_level(cfg_base + ' vif ' + vif) + conf.set_level(cfg_base + ['vif', vif]) eth['vif'].append(vlan_to_dict(conf)) return eth -- cgit v1.2.3