Page Menu
Home
VyOS Platform
Search
Configure Global Search
Log In
Files
F119062991
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
17 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/interface-definitions/https.xml b/interface-definitions/https.xml
index 828de449c..13d5c43ea 100644
--- a/interface-definitions/https.xml
+++ b/interface-definitions/https.xml
@@ -1,87 +1,111 @@
<?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">
<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/>
<constraint>
<validator name="ipv4-address"/>
<validator name="ipv6-address"/>
</constraint>
</properties>
</leafNode>
+ <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 524b80424..3e4c02562 100644
--- a/python/vyos/defaults.py
+++ b/python/vyos/defaults.py
@@ -1,29 +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' ]
+}
+
+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/http-api.py b/src/conf_mode/http-api.py
index c1d596ea3..1f91ac582 100755
--- a/src/conf_mode/http-api.py
+++ b/src/conf_mode/http-api.py
@@ -1,105 +1,98 @@
#!/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 subprocess
import json
import vyos.defaults
from vyos.config import Config
from vyos import ConfigError
config_file = '/etc/vyos/http-api.conf'
-default_config_data = {
- 'listen_address' : '127.0.0.1',
- 'port' : '8080',
- 'strict' : 'false',
- 'debug' : 'false',
- 'api_keys' : [ {"id": "testapp", "key": "qwerty"} ]
-}
-
vyos_conf_scripts_dir=vyos.defaults.directories['conf_mode']
# XXX: this model will need to be extended for tag nodes
dependencies = [
'https.py',
]
def get_config():
- http_api = default_config_data
+ http_api = vyos.defaults.api_data
+
conf = Config()
if not conf.exists('service https api'):
return None
else:
conf.set_level('service https api')
if conf.exists('strict'):
http_api['strict'] = 'true'
if conf.exists('debug'):
http_api['debug'] = 'true'
if conf.exists('port'):
port = conf.return_value('port')
http_api['port'] = port
if conf.exists('keys'):
for name in conf.list_nodes('keys id'):
if conf.exists('keys id {0} key'.format(name)):
key = conf.return_value('keys id {0} key'.format(name))
new_key = { 'id': name, 'key': key }
http_api['api_keys'].append(new_key)
return http_api
def verify(http_api):
return None
def generate(http_api):
if http_api is None:
return None
with open(config_file, 'w') as f:
json.dump(http_api, f, indent=2)
return None
def apply(http_api):
if http_api is not None:
os.system('sudo systemctl restart vyos-http-api.service')
else:
os.system('sudo systemctl stop vyos-http-api.service')
for dep in dependencies:
cmd = '{0}/{1}'.format(vyos_conf_scripts_dir, dep)
try:
subprocess.check_call(cmd, shell=True)
except subprocess.CalledProcessError as err:
raise ConfigError("{}.".format(err))
if __name__ == '__main__':
try:
c = get_config()
verify(c)
generate(c)
apply(c)
except ConfigError as e:
print(e)
sys.exit(1)
diff --git a/src/conf_mode/https.py b/src/conf_mode/https.py
index e1e81eef1..289eacf69 100755
--- a/src/conf_mode/https.py
+++ b/src/conf_mode/https.py
@@ -1,135 +1,137 @@
#!/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;
}
server {
# SSL configuration
#
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
+
+{% 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"}';
}
}
"""
-default_config_data = {
- 'listen_address' : [ '127.0.0.1' ]
-}
-
-default_api_config_data = {
- 'port' : '8080',
-}
-
def get_config():
- https = default_config_data
+ 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('certificates'):
+ if conf.exists('certificates system-generated-certificate'):
+ https['vyos_cert'] = vyos.defaults.vyos_cert_data
+
if conf.exists('api'):
- https['api'] = default_api_config_data
+ 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)
diff --git a/src/conf_mode/vyos_cert.py b/src/conf_mode/vyos_cert.py
new file mode 100755
index 000000000..4a44573ca
--- /dev/null
+++ b/src/conf_mode/vyos_cert.py
@@ -0,0 +1,143 @@
+#!/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 subprocess
+import tempfile
+import pathlib
+import ssl
+
+import vyos.defaults
+from vyos.config import Config
+from vyos import ConfigError
+
+vyos_conf_scripts_dir = vyos.defaults.directories['conf_mode']
+
+# XXX: this model will need to be extended for tag nodes
+dependencies = [
+ 'https.py',
+]
+
+def status_self_signed(cert_data):
+# check existence and expiration date
+ path = pathlib.Path(cert_data['conf'])
+ if not path.is_file():
+ return False
+ path = pathlib.Path(cert_data['crt'])
+ if not path.is_file():
+ return False
+ path = pathlib.Path(cert_data['key'])
+ if not path.is_file():
+ return False
+
+ # check if certificate is 1/2 past lifetime, with openssl -checkend
+ end_days = int(cert_data['lifetime'])
+ end_seconds = int(0.5*60*60*24*end_days)
+ checkend_cmd = ('openssl x509 -checkend {end} -noout -in {crt}'
+ ''.format(end=end_seconds, **cert_data))
+ try:
+ subprocess.check_call(checkend_cmd, shell=True)
+ return True
+ except subprocess.CalledProcessError as err:
+ if err.returncode == 1:
+ return False
+ else:
+ print("Called process error: {}.".format(err))
+
+def generate_self_signed(cert_data):
+ san_config = None
+
+ if ssl.OPENSSL_VERSION_INFO < (1, 1, 1, 0, 0):
+ san_config = tempfile.NamedTemporaryFile()
+ with open(san_config.name, 'w') as fd:
+ fd.write('[req]\n')
+ fd.write('distinguished_name=req\n')
+ fd.write('[san]\n')
+ fd.write('subjectAltName=DNS:vyos\n')
+
+ openssl_req_cmd = ('openssl req -x509 -nodes -days {lifetime} '
+ '-newkey rsa:4096 -keyout {key} -out {crt} '
+ '-subj "/O=Sentrium/OU=VyOS/CN=vyos" '
+ '-extensions san -config {san_conf}'
+ ''.format(san_conf=san_config.name,
+ **cert_data))
+
+ else:
+ openssl_req_cmd = ('openssl req -x509 -nodes -days {lifetime} '
+ '-newkey rsa:4096 -keyout {key} -out {crt} '
+ '-subj "/O=Sentrium/OU=VyOS/CN=vyos" '
+ '-addext "subjectAltName=DNS:vyos"'
+ ''.format(**cert_data))
+
+ try:
+ subprocess.check_call(openssl_req_cmd, shell=True)
+ except subprocess.CalledProcessError as err:
+ print("Called process error: {}.".format(err))
+
+ os.chmod('{key}'.format(**cert_data), 0o400)
+
+ with open('{conf}'.format(**cert_data), 'w') as f:
+ f.write('ssl_certificate {crt};\n'.format(**cert_data))
+ f.write('ssl_certificate_key {key};\n'.format(**cert_data))
+
+ if san_config:
+ san_config.close()
+
+def get_config():
+ vyos_cert = vyos.defaults.vyos_cert_data
+
+ conf = Config()
+ if not conf.exists('service https certificates system-generated-certificate'):
+ return None
+ else:
+ conf.set_level('service https certificates system-generated-certificate')
+
+ if conf.exists('lifetime'):
+ lifetime = conf.return_value('lifetime')
+ vyos_cert['lifetime'] = lifetime
+
+ return vyos_cert
+
+def verify(vyos_cert):
+ return None
+
+def generate(vyos_cert):
+ if vyos_cert is None:
+ return None
+
+ if not status_self_signed(vyos_cert):
+ generate_self_signed(vyos_cert)
+
+def apply(vyos_cert):
+ for dep in dependencies:
+ cmd = '{0}/{1}'.format(vyos_conf_scripts_dir, dep)
+ try:
+ subprocess.check_call(cmd, shell=True)
+ except subprocess.CalledProcessError as err:
+ raise ConfigError("{}.".format(err))
+
+if __name__ == '__main__':
+ try:
+ c = get_config()
+ verify(c)
+ generate(c)
+ apply(c)
+ except ConfigError as e:
+ print(e)
+ sys.exit(1)
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Fri, Nov 6, 7:45 AM (1 d, 21 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
4653225
Default Alt Text
(17 KB)
Attached To
Mode
rVYOSONEX vyos-1x
Attached
Detach File
Event Timeline
Log In to Comment