diff options
Diffstat (limited to 'smoketest/scripts/cli/test_service_https.py')
-rwxr-xr-x | smoketest/scripts/cli/test_service_https.py | 311 |
1 files changed, 265 insertions, 46 deletions
diff --git a/smoketest/scripts/cli/test_service_https.py b/smoketest/scripts/cli/test_service_https.py index 1ae5c104c..94eade2d7 100755 --- a/smoketest/scripts/cli/test_service_https.py +++ b/smoketest/scripts/cli/test_service_https.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2019-2023 VyOS maintainers and contributors +# Copyright (C) 2019-2024 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,14 +15,20 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. import unittest +import json from requests import request from urllib3.exceptions import InsecureRequestWarning +from time import sleep from base_vyostest_shim import VyOSUnitTestSHIM from base_vyostest_shim import ignore_warning from vyos.utils.file import read_file -from vyos.utils.process import run +from vyos.utils.file import write_file +from vyos.utils.process import call +from vyos.utils.process import process_named_running + +from vyos.configsession import ConfigSessionError base_path = ['service', 'https'] pki_base = ['pki'] @@ -48,78 +54,114 @@ MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgPLpD0Ohhoq0g4nhx u8/3jHMM7sDwL3aWzW/zp54/LhCWUoLMjDdDEEigK4fal4ZF9aA9F0Ww """ +dh_1024 = """ +MIGHAoGBAM3nvMkHGi/xmRs8cYg4pcl5sAanxel9EM+1XobVhUViXw8JvlmSEVOj +n2aXUifc4SEs3WDzVPRC8O8qQWjvErpTq/HOgt3aqBCabMgvflmt706XP0KiqnpW +EyvNiI27J3wBUzEXLIS110MxPAX5Tcug974PecFcOxn1RWrbWcx/AgEC +""" + +dh_2048 = """ +MIIBCAKCAQEA1mld/V7WnxxRinkOlhx/BoZkRELtIUQFYxyARBqYk4C5G3YnZNNu +zjaGyPnfIKHu8SIUH85OecM+5/co9nYlcUJuph2tbR6qNgPw7LOKIhf27u7WhvJk +iVsJhwZiWmvvMV4jTParNEI2svoooMyhHXzeweYsg6YtgLVmwiwKj3XP3gRH2i3B +Mq8CDS7X6xaKvjfeMPZBFqOM5nb6HhsbaAUyiZxrfipLvXxtnbzd/eJUQVfVdxM3 +pn0i+QrO2tuNAzX7GoPc9pefrbb5xJmGS50G0uqsR59+7LhYmyZSBASA0lxTEW9t +kv/0LPvaYTY57WL7hBeqqHy/WPZHPzDI3wIBAg== +""" +# to test load config via HTTP URL +nginx_tmp_site = '/etc/nginx/sites-enabled/smoketest' +nginx_conf_smoketest = """ +server { + listen 8000; + server_name localhost; + + root /tmp; + + index index.html; + + location / { + try_files $uri $uri/ =404; + autoindex on; + } +} +""" + +PROCESS_NAME = 'nginx' + class TestHTTPSService(VyOSUnitTestSHIM.TestCase): - def setUp(self): + @classmethod + def setUpClass(cls): + super(TestHTTPSService, cls).setUpClass() + # ensure we can also run this test on a live system - so lets clean # out the current configuration :) - self.cli_delete(base_path) - self.cli_delete(pki_base) + cls.cli_delete(cls, base_path) + cls.cli_delete(cls, pki_base) + + @classmethod + def tearDownClass(cls): + super(TestHTTPSService, cls).tearDownClass() + call(f'sudo rm -f {nginx_tmp_site}') def tearDown(self): self.cli_delete(base_path) self.cli_delete(pki_base) self.cli_commit() - def test_default(self): - self.cli_set(base_path) - self.cli_commit() - - ret = run('sudo /usr/sbin/nginx -t') - self.assertEqual(ret, 0) - - def test_server_block(self): - vhost_id = 'example' - address = '0.0.0.0' - port = '8443' - name = 'example.org' - - test_path = base_path + ['virtual-host', vhost_id] - - self.cli_set(test_path + ['listen-address', address]) - self.cli_set(test_path + ['listen-port', port]) - self.cli_set(test_path + ['server-name', name]) - - self.cli_commit() - - ret = run('sudo /usr/sbin/nginx -t') - self.assertEqual(ret, 0) - - nginx_config = read_file('/etc/nginx/sites-enabled/default') - self.assertIn(f'listen {address}:{port} ssl;', nginx_config) - self.assertIn(f'ssl_protocols TLSv1.2 TLSv1.3;', nginx_config) + # Check for stopped process + self.assertFalse(process_named_running(PROCESS_NAME)) def test_certificate(self): - self.cli_set(pki_base + ['certificate', 'test_https', 'certificate', cert_data.replace('\n','')]) - self.cli_set(pki_base + ['certificate', 'test_https', 'private', 'key', key_data.replace('\n','')]) - - self.cli_set(base_path + ['certificates', 'certificate', 'test_https']) + cert_name = 'test_https' + dh_name = 'dh-test' + + self.cli_set(base_path + ['certificates', 'certificate', cert_name]) + # verify() - certificates do not exist (yet) + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_set(pki_base + ['certificate', cert_name, 'certificate', cert_data.replace('\n','')]) + self.cli_set(pki_base + ['certificate', cert_name, 'private', 'key', key_data.replace('\n','')]) + + self.cli_set(base_path + ['certificates', 'dh-params', dh_name]) + # verify() - dh-params do not exist (yet) + with self.assertRaises(ConfigSessionError): + self.cli_commit() + + self.cli_set(pki_base + ['dh', dh_name, 'parameters', dh_1024.replace('\n','')]) + # verify() - dh-param minimum length is 2048 bit + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_set(pki_base + ['dh', dh_name, 'parameters', dh_2048.replace('\n','')]) self.cli_commit() + self.assertTrue(process_named_running(PROCESS_NAME)) + self.debug = False + + def test_api_missing_keys(self): + self.cli_set(base_path + ['api']) + self.assertRaises(ConfigSessionError, self.cli_commit) - ret = run('sudo /usr/sbin/nginx -t') - self.assertEqual(ret, 0) + def test_api_incomplete_key(self): + self.cli_set(base_path + ['api', 'keys', 'id', 'key-01']) + self.assertRaises(ConfigSessionError, self.cli_commit) @ignore_warning(InsecureRequestWarning) def test_api_auth(self): vhost_id = 'example' address = '127.0.0.1' - port = '443' + port = '443' # default value name = 'localhost' - self.cli_set(base_path + ['api', 'socket']) key = 'MySuperSecretVyOS' self.cli_set(base_path + ['api', 'keys', 'id', 'key-01', 'key', key]) - test_path = base_path + ['virtual-host', vhost_id] - self.cli_set(test_path + ['listen-address', address]) - self.cli_set(test_path + ['listen-port', port]) - self.cli_set(test_path + ['server-name', name]) + self.cli_set(base_path + ['listen-address', address]) self.cli_commit() nginx_config = read_file('/etc/nginx/sites-enabled/default') self.assertIn(f'listen {address}:{port} ssl;', nginx_config) - self.assertIn(f'ssl_protocols TLSv1.2 TLSv1.3;', nginx_config) + self.assertIn(f'ssl_protocols TLSv1.2 TLSv1.3;', nginx_config) # default url = f'https://{address}/retrieve' payload = {'data': '{"op": "showConfig", "path": []}', 'key': f'{key}'} @@ -138,6 +180,13 @@ class TestHTTPSService(VyOSUnitTestSHIM.TestCase): # Must get HTTP code 401 on missing key (Unauthorized) self.assertEqual(r.status_code, 401) + # Check path config + payload = {'data': '{"op": "showConfig", "path": ["system", "login"]}', 'key': f'{key}'} + r = request('POST', url, verify=False, headers=headers, data=payload) + response = r.json() + vyos_user_exists = 'vyos' in response.get('data', {}).get('user', {}) + self.assertTrue(vyos_user_exists, "The 'vyos' user does not exist in the response.") + # GraphQL auth test: a missing key will return status code 400, as # 'key' is a non-nullable field in the schema; an incorrect key is # caught by the resolver, and returns success 'False', so one must @@ -240,5 +289,175 @@ class TestHTTPSService(VyOSUnitTestSHIM.TestCase): success = r.json()['data']['ShowVersion']['success'] self.assertTrue(success) + @ignore_warning(InsecureRequestWarning) + def test_api_add_delete(self): + address = '127.0.0.1' + key = 'VyOS-key' + url = f'https://{address}/retrieve' + payload = {'data': '{"op": "showConfig", "path": []}', 'key': f'{key}'} + headers = {} + + self.cli_set(base_path) + self.cli_commit() + + r = request('POST', url, verify=False, headers=headers, data=payload) + # api not configured; expect 503 + self.assertEqual(r.status_code, 503) + + self.cli_set(base_path + ['api', 'keys', 'id', 'key-01', 'key', key]) + self.cli_commit() + sleep(2) + + r = request('POST', url, verify=False, headers=headers, data=payload) + # api configured; expect 200 + self.assertEqual(r.status_code, 200) + + self.cli_delete(base_path + ['api']) + self.cli_commit() + + r = request('POST', url, verify=False, headers=headers, data=payload) + # api deleted; expect 503 + self.assertEqual(r.status_code, 503) + + @ignore_warning(InsecureRequestWarning) + def test_api_show(self): + address = '127.0.0.1' + key = 'VyOS-key' + url = f'https://{address}/show' + headers = {} + + self.cli_set(base_path + ['api', 'keys', 'id', 'key-01', 'key', key]) + self.cli_commit() + + payload = { + 'data': '{"op": "show", "path": ["system", "image"]}', + 'key': f'{key}', + } + r = request('POST', url, verify=False, headers=headers, data=payload) + self.assertEqual(r.status_code, 200) + + @ignore_warning(InsecureRequestWarning) + def test_api_generate(self): + address = '127.0.0.1' + key = 'VyOS-key' + url = f'https://{address}/generate' + headers = {} + + self.cli_set(base_path + ['api', 'keys', 'id', 'key-01', 'key', key]) + self.cli_commit() + + payload = { + 'data': '{"op": "generate", "path": ["macsec", "mka", "cak", "gcm-aes-256"]}', + 'key': f'{key}', + } + r = request('POST', url, verify=False, headers=headers, data=payload) + self.assertEqual(r.status_code, 200) + + @ignore_warning(InsecureRequestWarning) + def test_api_configure(self): + address = '127.0.0.1' + key = 'VyOS-key' + url = f'https://{address}/configure' + headers = {} + conf_interface = 'dum0' + conf_address = '192.0.2.44/32' + + self.cli_set(base_path + ['api', 'keys', 'id', 'key-01', 'key', key]) + self.cli_commit() + + payload_path = [ + "interfaces", + "dummy", + f"{conf_interface}", + "address", + f"{conf_address}", + ] + + payload = {'data': json.dumps({"op": "set", "path": payload_path}), 'key': key} + + r = request('POST', url, verify=False, headers=headers, data=payload) + self.assertEqual(r.status_code, 200) + + @ignore_warning(InsecureRequestWarning) + def test_api_config_file(self): + address = '127.0.0.1' + key = 'VyOS-key' + url = f'https://{address}/config-file' + headers = {} + + self.cli_set(base_path + ['api', 'keys', 'id', 'key-01', 'key', key]) + self.cli_commit() + + payload = { + 'data': '{"op": "save"}', + 'key': f'{key}', + } + r = request('POST', url, verify=False, headers=headers, data=payload) + self.assertEqual(r.status_code, 200) + + @ignore_warning(InsecureRequestWarning) + def test_api_reset(self): + address = '127.0.0.1' + key = 'VyOS-key' + url = f'https://{address}/reset' + headers = {} + + self.cli_set(base_path + ['api', 'keys', 'id', 'key-01', 'key', key]) + self.cli_commit() + + payload = { + 'data': '{"op": "reset", "path": ["ip", "arp", "table"]}', + 'key': f'{key}', + } + r = request('POST', url, verify=False, headers=headers, data=payload) + self.assertEqual(r.status_code, 200) + + @ignore_warning(InsecureRequestWarning) + def test_api_config_file_load_http(self): + # Test load config from HTTP URL + address = '127.0.0.1' + key = 'VyOS-key' + url = f'https://{address}/config-file' + url_config = f'https://{address}/configure' + headers = {} + tmp_file = 'tmp-config.boot' + + self.cli_set(base_path + ['api', 'keys', 'id', 'key-01', 'key', key]) + self.cli_commit() + + # load config via HTTP requires nginx config + call(f'sudo touch {nginx_tmp_site}') + call(f'sudo chmod 666 {nginx_tmp_site}') + write_file(nginx_tmp_site, nginx_conf_smoketest) + call('sudo systemctl reload nginx') + + # save config + payload = { + 'data': '{"op": "save", "file": "/tmp/tmp-config.boot"}', + 'key': f'{key}', + } + r = request('POST', url, verify=False, headers=headers, data=payload) + self.assertEqual(r.status_code, 200) + + # change config + payload = { + 'data': '{"op": "set", "path": ["interfaces", "dummy", "dum1", "address", "192.0.2.31/32"]}', + 'key': f'{key}', + } + r = request('POST', url_config, verify=False, headers=headers, data=payload) + self.assertEqual(r.status_code, 200) + + # load config from URL + payload = { + 'data': '{"op": "load", "file": "http://localhost:8000/tmp-config.boot"}', + 'key': f'{key}', + } + r = request('POST', url, verify=False, headers=headers, data=payload) + self.assertEqual(r.status_code, 200) + + # cleanup tmp nginx conf + call(f'sudo rm -f {nginx_tmp_site}') + call('sudo systemctl reload nginx') + if __name__ == '__main__': - unittest.main(verbosity=2) + unittest.main(verbosity=5) |