diff --git a/interface-definitions/https.xml b/interface-definitions/https.xml index 13d5c43ea..7a87133f3 100644 --- a/interface-definitions/https.xml +++ b/interface-definitions/https.xml @@ -1,111 +1,123 @@ <?xml version="1.0"?> <!-- HTTPS configuration --> <interfaceDefinition> <node name="service"> <children> <node name="https" owner="${vyos_conf_scripts_dir}/https.py"> <properties> <help>HTTPS configuration</help> <priority>1001</priority> </properties> <children> - <leafNode name="listen-address"> + <tagNode name="listen-addresses"> <properties> <help>Addresses to listen for HTTPS requests</help> <valueHelp> <format>ipv4</format> <description>HTTPS IPv4 address</description> </valueHelp> <valueHelp> <format>ipv6</format> <description>HTTPS IPv6 address</description> </valueHelp> - <multi/> + <valueHelp> + <format>'*'</format> + <description>any</description> + </valueHelp> <constraint> <validator name="ipv4-address"/> <validator name="ipv6-address"/> + <regex>^\\*$</regex> </constraint> </properties> - </leafNode> + <children> + <leafNode name="server-names"> + <properties> + <help>Server names: exact, wildcard, regex, or '_' (any)</help> + <multi/> + </properties> + </leafNode> + </children> + </tagNode> <node name="certificates"> <properties> <help>TLS certificates</help> </properties> <children> <node name="system-generated-certificate" owner="${vyos_conf_scripts_dir}/vyos_cert.py"> <properties> <help>Use an automatically generated self-signed certificate</help> <valueless/> </properties> <children> <leafNode name="lifetime"> <properties> <help>Lifetime in days; default is 365</help> <valueHelp> <format>1-65535</format> <description>Number of days</description> </valueHelp> </properties> </leafNode> </children> </node> </children> </node> <node name="api" owner="${vyos_conf_scripts_dir}/http-api.py"> <properties> <help>VyOS HTTP API configuration</help> <priority>1002</priority> </properties> <children> <leafNode name="port"> <properties> <help>Port for HTTP API service</help> <valueHelp> <format>1-65535</format> <description>Numeric IP port</description> </valueHelp> <constraint> <validator name="numeric" argument="--range 1-65535"/> </constraint> </properties> </leafNode> <node name="keys"> <properties> <help>HTTP API keys</help> </properties> <children> <tagNode name="id"> <properties> <help>HTTP API id</help> </properties> <children> <leafNode name="key"> <properties> <help>HTTP API plaintext key</help> </properties> </leafNode> </children> </tagNode> </children> </node> <leafNode name="strict"> <properties> <help>Enforce strict path checking</help> <valueless/> </properties> </leafNode> <leafNode name="debug"> <properties> <help>Debug</help> <valueless/> <hidden/> </properties> </leafNode> </children> </node> </children> </node> </children> </node> </interfaceDefinition> diff --git a/python/vyos/defaults.py b/python/vyos/defaults.py index 3e4c02562..85d27d60d 100644 --- a/python/vyos/defaults.py +++ b/python/vyos/defaults.py @@ -1,48 +1,48 @@ # Copyright 2018 VyOS maintainers and contributors <maintainers@vyos.io> # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see <http://www.gnu.org/licenses/>. directories = { "data": "/usr/share/vyos/", "conf_mode": "/usr/libexec/vyos/conf_mode", "config": "/opt/vyatta/etc/config", "current": "/opt/vyatta/etc/config-migrate/current", "migrate": "/opt/vyatta/etc/config-migrate/migrate", } cfg_group = 'vyattacfg' cfg_vintage = 'vyatta' commit_lock = '/opt/vyatta/config/.lock' https_data = { - 'listen_address' : [ '127.0.0.1' ] + 'listen_addresses' : { '*': ['_'] } } api_data = { 'listen_address' : '127.0.0.1', 'port' : '8080', 'strict' : 'false', 'debug' : 'false', 'api_keys' : [ {"id": "testapp", "key": "qwerty"} ] } vyos_cert_data = { "conf": "/etc/nginx/snippets/vyos-cert.conf", "crt": "/etc/ssl/certs/vyos-selfsigned.crt", "key": "/etc/ssl/private/vyos-selfsign", "lifetime": "365", } diff --git a/src/conf_mode/https.py b/src/conf_mode/https.py index 289eacf69..d5aa1f5b3 100755 --- a/src/conf_mode/https.py +++ b/src/conf_mode/https.py @@ -1,137 +1,186 @@ #!/usr/bin/env python3 # # Copyright (C) 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 # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # 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 vyos.defaults from vyos.config import Config from vyos import ConfigError config_file = '/etc/nginx/sites-available/default' # Please be careful if you edit the template. config_tmpl = """ ### Autogenerated by http-api.py ### # Default server configuration # server { listen 80 default_server; listen [::]:80 default_server; server_name _; return 302 https://$server_name$request_uri; } +{% for addr, names in listen_addresses.items() %} server { # SSL configuration # +{% if addr == '*' %} listen 443 ssl default_server; listen [::]:443 ssl default_server; +{% else %} + listen {{ addr }}:443 ssl; +{% endif %} + +{% for name in names %} + server_name {{ name }}; +{% endfor %} {% 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 %} -{% for l_addr in listen_address %} - server_name {{ l_addr }}; -{% endfor %} + # proxy settings for HTTP API, if enabled; 503, if not + location ~ /(retrieve|configure) { +{% 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 }}; 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 %} """ def get_config(): https = vyos.defaults.https_data conf = Config() if not conf.exists('service https'): return None else: conf.set_level('service https') - if conf.exists('listen-address'): - addrs = conf.return_values('listen-address') - https['listen_address'] = addrs[:] + if conf.exists('listen-addresses'): + addrs = {} + for addr in conf.list_nodes('listen-addresses'): + addrs[addr] = ['_'] + if conf.exists('listen-addresses {0} server-names'.format(addr)): + names = conf.return_values('listen-addresses {0} server-names'.format(addr)) + addrs[addr] = names[:] + https['listen_addresses'] = addrs if conf.exists('certificates'): if conf.exists('certificates system-generated-certificate'): https['vyos_cert'] = vyos.defaults.vyos_cert_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 return https def verify(https): return None def generate(https): if https is None: return None tmpl = jinja2.Template(config_tmpl, trim_blocks=True) config_text = tmpl.render(https) with open(config_file, 'w') as f: f.write(config_text) return None def apply(https): if https is not None: os.system('sudo systemctl restart nginx.service') else: os.system('sudo systemctl stop nginx.service') if __name__ == '__main__': try: c = get_config() verify(c) generate(c) apply(c) except ConfigError as e: print(e) sys.exit(1)