summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/templates/https/nginx.default.j22
-rw-r--r--python/vyos/configsession.py10
-rw-r--r--python/vyos/ifconfig/macsec.py4
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_macsec.py35
-rwxr-xr-xsrc/services/vyos-http-api-server66
5 files changed, 101 insertions, 16 deletions
diff --git a/data/templates/https/nginx.default.j2 b/data/templates/https/nginx.default.j2
index 468640b4b..dde839e9f 100644
--- a/data/templates/https/nginx.default.j2
+++ b/data/templates/https/nginx.default.j2
@@ -36,7 +36,7 @@ server {
ssl_protocols TLSv1.2 TLSv1.3;
# proxy settings for HTTP API, if enabled; 503, if not
- location ~ ^/(retrieve|configure|config-file|image|container-image|generate|show|reset|docs|openapi.json|redoc|graphql) {
+ location ~ ^/(retrieve|configure|config-file|image|container-image|generate|show|reboot|reset|poweroff|docs|openapi.json|redoc|graphql) {
{% if server.api %}
proxy_pass http://unix:/run/api.sock;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
diff --git a/python/vyos/configsession.py b/python/vyos/configsession.py
index 6d4b2af59..9802ebae4 100644
--- a/python/vyos/configsession.py
+++ b/python/vyos/configsession.py
@@ -35,6 +35,8 @@ REMOVE_IMAGE = ['/opt/vyatta/bin/vyatta-boot-image.pl', '--del']
GENERATE = ['/opt/vyatta/bin/vyatta-op-cmd-wrapper', 'generate']
SHOW = ['/opt/vyatta/bin/vyatta-op-cmd-wrapper', 'show']
RESET = ['/opt/vyatta/bin/vyatta-op-cmd-wrapper', 'reset']
+REBOOT = ['/opt/vyatta/bin/vyatta-op-cmd-wrapper', 'reboot']
+POWEROFF = ['/opt/vyatta/bin/vyatta-op-cmd-wrapper', 'poweroff']
OP_CMD_ADD = ['/opt/vyatta/bin/vyatta-op-cmd-wrapper', 'add']
OP_CMD_DELETE = ['/opt/vyatta/bin/vyatta-op-cmd-wrapper', 'delete']
@@ -220,10 +222,18 @@ class ConfigSession(object):
out = self.__run_command(SHOW + path)
return out
+ def reboot(self, path):
+ out = self.__run_command(REBOOT + path)
+ return out
+
def reset(self, path):
out = self.__run_command(RESET + path)
return out
+ def poweroff(self, path):
+ out = self.__run_command(POWEROFF + path)
+ return out
+
def add_container_image(self, name):
out = self.__run_command(OP_CMD_ADD + ['container', 'image'] + [name])
return out
diff --git a/python/vyos/ifconfig/macsec.py b/python/vyos/ifconfig/macsec.py
index 9329c5ee7..bde1d9aec 100644
--- a/python/vyos/ifconfig/macsec.py
+++ b/python/vyos/ifconfig/macsec.py
@@ -45,6 +45,10 @@ class MACsecIf(Interface):
# create tunnel interface
cmd = 'ip link add link {source_interface} {ifname} type {type}'.format(**self.config)
cmd += f' cipher {self.config["security"]["cipher"]}'
+
+ if 'encrypt' in self.config["security"]:
+ cmd += ' encrypt on'
+
self._cmd(cmd)
# Check if using static keys
diff --git a/smoketest/scripts/cli/test_interfaces_macsec.py b/smoketest/scripts/cli/test_interfaces_macsec.py
index ea0f00071..d8d564792 100755
--- a/smoketest/scripts/cli/test_interfaces_macsec.py
+++ b/smoketest/scripts/cli/test_interfaces_macsec.py
@@ -14,7 +14,6 @@
# 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 re
import unittest
@@ -23,9 +22,10 @@ from netifaces import interfaces
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Section
-from vyos.utils.process import cmd
from vyos.utils.file import read_file
from vyos.utils.network import get_interface_config
+from vyos.utils.network import interface_exists
+from vyos.utils.process import cmd
from vyos.utils.process import process_named_running
PROCESS_NAME = 'wpa_supplicant'
@@ -35,10 +35,6 @@ def get_config_value(interface, key):
tmp = re.findall(r'\n?{}=(.*)'.format(key), tmp)
return tmp[0]
-def get_cipher(interface):
- tmp = get_interface_config(interface)
- return tmp['linkinfo']['info_data']['cipher_suite'].lower()
-
class MACsecInterfaceTest(BasicInterfaceTest.TestCase):
@classmethod
def setUpClass(cls):
@@ -117,6 +113,10 @@ class MACsecInterfaceTest(BasicInterfaceTest.TestCase):
tmp = read_file(f'/sys/class/net/{interface}/mtu')
self.assertEqual(tmp, '1460')
+ # Encryption enabled?
+ tmp = get_interface_config(interface)
+ self.assertTrue(tmp['linkinfo']['info_data']['encrypt'])
+
# Check for running process
self.assertTrue(process_named_running(PROCESS_NAME))
@@ -138,10 +138,11 @@ class MACsecInterfaceTest(BasicInterfaceTest.TestCase):
# final commit and verify
self.cli_commit()
- self.assertIn(interface, interfaces())
+ self.assertTrue(interface_exists(interface))
# Verify proper cipher suite (T4537)
- self.assertEqual(cipher, get_cipher(interface))
+ tmp = get_interface_config(interface)
+ self.assertEqual(cipher, tmp['linkinfo']['info_data']['cipher_suite'].lower())
def test_macsec_gcm_aes_256(self):
src_interface = 'eth0'
@@ -161,10 +162,11 @@ class MACsecInterfaceTest(BasicInterfaceTest.TestCase):
# final commit and verify
self.cli_commit()
- self.assertIn(interface, interfaces())
+ self.assertTrue(interface_exists(interface))
# Verify proper cipher suite (T4537)
- self.assertEqual(cipher, get_cipher(interface))
+ tmp = get_interface_config(interface)
+ self.assertEqual(cipher, tmp['linkinfo']['info_data']['cipher_suite'].lower())
def test_macsec_source_interface(self):
# Ensure source-interface can bot be part of any other bond or bridge
@@ -191,7 +193,7 @@ class MACsecInterfaceTest(BasicInterfaceTest.TestCase):
# final commit and verify
self.cli_commit()
- self.assertIn(interface, interfaces())
+ self.assertTrue(interface_exists(interface))
def test_macsec_static_keys(self):
src_interface = 'eth0'
@@ -205,7 +207,7 @@ class MACsecInterfaceTest(BasicInterfaceTest.TestCase):
peer_mac = '00:11:22:33:44:55'
self.cli_set(self._base_path + [interface])
- # Encrypt link
+ # Encrypt link
self.cli_set(self._base_path + [interface, 'security', 'encrypt'])
# check validate() - source interface is mandatory
@@ -261,9 +263,12 @@ class MACsecInterfaceTest(BasicInterfaceTest.TestCase):
# final commit and verify
self.cli_commit()
- self.assertIn(interface, interfaces())
- self.assertEqual(cipher2, get_cipher(interface))
- self.assertTrue(os.path.isdir(f'/sys/class/net/{interface}'))
+
+ self.assertTrue(interface_exists(interface))
+ tmp = get_interface_config(interface)
+ self.assertEqual(cipher2, tmp['linkinfo']['info_data']['cipher_suite'].lower())
+ # Encryption enabled?
+ self.assertTrue(tmp['linkinfo']['info_data']['encrypt'])
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/src/services/vyos-http-api-server b/src/services/vyos-http-api-server
index daee24257..85d7884b6 100755
--- a/src/services/vyos-http-api-server
+++ b/src/services/vyos-http-api-server
@@ -223,6 +223,19 @@ class ShowModel(ApiModel):
}
}
+class RebootModel(ApiModel):
+ op: StrictStr
+ path: List[StrictStr]
+
+ class Config:
+ schema_extra = {
+ "example": {
+ "key": "id_key",
+ "op": "reboot",
+ "path": ["op", "mode", "path"],
+ }
+ }
+
class ResetModel(ApiModel):
op: StrictStr
path: List[StrictStr]
@@ -236,6 +249,19 @@ class ResetModel(ApiModel):
}
}
+class PoweroffModel(ApiModel):
+ op: StrictStr
+ path: List[StrictStr]
+
+ class Config:
+ schema_extra = {
+ "example": {
+ "key": "id_key",
+ "op": "poweroff",
+ "path": ["op", "mode", "path"],
+ }
+ }
+
class Success(BaseModel):
success: bool
@@ -713,6 +739,26 @@ def show_op(data: ShowModel):
return success(res)
+@app.post('/reboot')
+def reboot_op(data: RebootModel):
+ session = app.state.vyos_session
+
+ op = data.op
+ path = data.path
+
+ try:
+ if op == 'reboot':
+ res = session.reboot(path)
+ else:
+ return error(400, f"'{op}' is not a valid operation")
+ except ConfigSessionError as e:
+ return error(400, str(e))
+ except Exception as e:
+ logger.critical(traceback.format_exc())
+ return error(500, "An internal error occured. Check the logs for details.")
+
+ return success(res)
+
@app.post('/reset')
def reset_op(data: ResetModel):
session = app.state.vyos_session
@@ -733,6 +779,26 @@ def reset_op(data: ResetModel):
return success(res)
+@app.post('/poweroff')
+def poweroff_op(data: PoweroffModel):
+ session = app.state.vyos_session
+
+ op = data.op
+ path = data.path
+
+ try:
+ if op == 'poweroff':
+ res = session.poweroff(path)
+ else:
+ return error(400, f"'{op}' is not a valid operation")
+ except ConfigSessionError as e:
+ return error(400, str(e))
+ except Exception as e:
+ logger.critical(traceback.format_exc())
+ return error(500, "An internal error occured. Check the logs for details.")
+
+ return success(res)
+
###
# GraphQL integration