summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/conf_mode/service_ipoe-server.py2
-rwxr-xr-xsrc/conf_mode/service_pppoe-server.py5
-rw-r--r--src/migration-scripts/ipoe-server/3-to-430
-rw-r--r--src/migration-scripts/pppoe-server/10-to-1130
-rwxr-xr-xsrc/op_mode/pki.py41
-rw-r--r--src/opt/vyatta/etc/shell/level/users/allowed-op1
-rw-r--r--src/opt/vyatta/etc/shell/level/users/allowed-op.in1
-rwxr-xr-xsrc/services/vyos-http-api-server62
8 files changed, 158 insertions, 14 deletions
diff --git a/src/conf_mode/service_ipoe-server.py b/src/conf_mode/service_ipoe-server.py
index 16c82e591..c7e3ef033 100755
--- a/src/conf_mode/service_ipoe-server.py
+++ b/src/conf_mode/service_ipoe-server.py
@@ -70,6 +70,8 @@ def verify(ipoe):
if 'client_subnet' in iface_config and 'vlan' in iface_config:
raise ConfigError('Option "client-subnet" and "vlan" are mutually exclusive, '
'use "client-ip-pool" instead!')
+ if 'vlan_mon' in iface_config and not 'vlan' in iface_config:
+ raise ConfigError('Option "vlan-mon" requires "vlan" to be set!')
verify_accel_ppp_authentication(ipoe, local_users=False)
verify_accel_ppp_ip_pool(ipoe)
diff --git a/src/conf_mode/service_pppoe-server.py b/src/conf_mode/service_pppoe-server.py
index 566a7b149..ac697c509 100755
--- a/src/conf_mode/service_pppoe-server.py
+++ b/src/conf_mode/service_pppoe-server.py
@@ -121,9 +121,12 @@ def verify(pppoe):
raise ConfigError('At least one listen interface must be defined!')
# Check is interface exists in the system
- for interface in pppoe['interface']:
+ for interface, interface_config in pppoe['interface'].items():
verify_interface_exists(pppoe, interface, warning_only=True)
+ if 'vlan_mon' in interface_config and not 'vlan' in interface_config:
+ raise ConfigError('Option "vlan-mon" requires "vlan" to be set!')
+
return None
diff --git a/src/migration-scripts/ipoe-server/3-to-4 b/src/migration-scripts/ipoe-server/3-to-4
new file mode 100644
index 000000000..3bad9756d
--- /dev/null
+++ b/src/migration-scripts/ipoe-server/3-to-4
@@ -0,0 +1,30 @@
+# Copyright 2024 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/>.
+
+# Add the "vlan-mon" option to the configuration to prevent it
+# from disappearing from the configuration file
+
+from vyos.configtree import ConfigTree
+
+base = ['service', 'ipoe-server']
+
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ return
+
+ for interface in config.list_nodes(base + ['interface']):
+ base_path = base + ['interface', interface]
+ if config.exists(base_path + ['vlan']):
+ config.set(base_path + ['vlan-mon'])
diff --git a/src/migration-scripts/pppoe-server/10-to-11 b/src/migration-scripts/pppoe-server/10-to-11
new file mode 100644
index 000000000..6bc138b5c
--- /dev/null
+++ b/src/migration-scripts/pppoe-server/10-to-11
@@ -0,0 +1,30 @@
+# Copyright 2024 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/>.
+
+# Add the "vlan-mon" option to the configuration to prevent it
+# from disappearing from the configuration file
+
+from vyos.configtree import ConfigTree
+
+base = ['service', 'pppoe-server']
+
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ return
+
+ for interface in config.list_nodes(base + ['interface']):
+ base_path = base + ['interface', interface]
+ if config.exists(base_path + ['vlan']):
+ config.set(base_path + ['vlan-mon'])
diff --git a/src/op_mode/pki.py b/src/op_mode/pki.py
index 84b080023..ab613e5c4 100755
--- a/src/op_mode/pki.py
+++ b/src/op_mode/pki.py
@@ -316,7 +316,13 @@ def generate_certificate_request(private_key=None, key_type=None, return_request
default_values = get_default_values()
subject = {}
- subject['country'] = ask_input('Enter country code:', default=default_values['country'])
+ while True:
+ country = ask_input('Enter country code:', default=default_values['country'])
+ if len(country) != 2:
+ print("Country name must be a 2 character country code")
+ continue
+ subject['country'] = country
+ break
subject['state'] = ask_input('Enter state:', default=default_values['state'])
subject['locality'] = ask_input('Enter locality:', default=default_values['locality'])
subject['organization'] = ask_input('Enter organization name:', default=default_values['organization'])
@@ -693,7 +699,7 @@ def generate_wireguard_psk(interface=None, peer=None, install=False):
print(f'Pre-shared key: {psk}')
# Import functions
-def import_ca_certificate(name, path=None, key_path=None):
+def import_ca_certificate(name, path=None, key_path=None, no_prompt=False, passphrase=None):
if path:
if not os.path.exists(path):
print(f'File not found: {path}')
@@ -717,19 +723,20 @@ def import_ca_certificate(name, path=None, key_path=None):
return
key = None
- passphrase = ask_input('Enter private key passphrase: ') or None
+ if not no_prompt:
+ passphrase = ask_input('Enter private key passphrase: ') or None
with open(key_path) as f:
key_data = f.read()
key = load_private_key(key_data, passphrase=passphrase, wrap_tags=False)
if not key:
- print(f'Invalid private key or passphrase: {path}')
+ print(f'Invalid private key or passphrase: {key_path}')
return
install_certificate(name, private_key=key, is_ca=True)
-def import_certificate(name, path=None, key_path=None):
+def import_certificate(name, path=None, key_path=None, no_prompt=False, passphrase=None):
if path:
if not os.path.exists(path):
print(f'File not found: {path}')
@@ -753,14 +760,15 @@ def import_certificate(name, path=None, key_path=None):
return
key = None
- passphrase = ask_input('Enter private key passphrase: ') or None
+ if not no_prompt:
+ passphrase = ask_input('Enter private key passphrase: ') or None
with open(key_path) as f:
key_data = f.read()
key = load_private_key(key_data, passphrase=passphrase, wrap_tags=False)
if not key:
- print(f'Invalid private key or passphrase: {path}')
+ print(f'Invalid private key or passphrase: {key_path}')
return
install_certificate(name, private_key=key, is_ca=False)
@@ -799,7 +807,7 @@ def import_dh_parameters(name, path):
install_dh_parameters(name, dh)
-def import_keypair(name, path=None, key_path=None):
+def import_keypair(name, path=None, key_path=None, no_prompt=False, passphrase=None):
if path:
if not os.path.exists(path):
print(f'File not found: {path}')
@@ -823,14 +831,15 @@ def import_keypair(name, path=None, key_path=None):
return
key = None
- passphrase = ask_input('Enter private key passphrase: ') or None
+ if not no_prompt:
+ passphrase = ask_input('Enter private key passphrase: ') or None
with open(key_path) as f:
key_data = f.read()
key = load_private_key(key_data, passphrase=passphrase, wrap_tags=False)
if not key:
- print(f'Invalid private key or passphrase: {path}')
+ print(f'Invalid private key or passphrase: {key_path}')
return
install_keypair(name, None, private_key=key, prompt=False)
@@ -1011,6 +1020,9 @@ if __name__ == '__main__':
parser.add_argument('--filename', help='Write certificate into specified filename', action='store')
parser.add_argument('--key-filename', help='Write key into specified filename', action='store')
+ parser.add_argument('--no-prompt', action='store_true', help='Perform action non-interactively')
+ parser.add_argument('--passphrase', help='A passphrase to decrypt the private key')
+
args = parser.parse_args()
try:
@@ -1054,15 +1066,18 @@ if __name__ == '__main__':
generate_wireguard_psk(args.interface, peer=args.peer, install=args.install)
elif args.action == 'import':
if args.ca:
- import_ca_certificate(args.ca, path=args.filename, key_path=args.key_filename)
+ import_ca_certificate(args.ca, path=args.filename, key_path=args.key_filename,
+ no_prompt=args.no_prompt, passphrase=args.passphrase)
elif args.certificate:
- import_certificate(args.certificate, path=args.filename, key_path=args.key_filename)
+ import_certificate(args.certificate, path=args.filename, key_path=args.key_filename,
+ no_prompt=args.no_prompt, passphrase=args.passphrase)
elif args.crl:
import_crl(args.crl, args.filename)
elif args.dh:
import_dh_parameters(args.dh, args.filename)
elif args.keypair:
- import_keypair(args.keypair, path=args.filename, key_path=args.key_filename)
+ import_keypair(args.keypair, path=args.filename, key_path=args.key_filename,
+ no_prompt=args.no_prompt, passphrase=args.passphrase)
elif args.openvpn:
import_openvpn_secret(args.openvpn, args.filename)
elif args.action == 'show':
diff --git a/src/opt/vyatta/etc/shell/level/users/allowed-op b/src/opt/vyatta/etc/shell/level/users/allowed-op
index 74c45af37..381fd26e5 100644
--- a/src/opt/vyatta/etc/shell/level/users/allowed-op
+++ b/src/opt/vyatta/etc/shell/level/users/allowed-op
@@ -6,6 +6,7 @@ clear
connect
delete
disconnect
+execute
exit
force
monitor
diff --git a/src/opt/vyatta/etc/shell/level/users/allowed-op.in b/src/opt/vyatta/etc/shell/level/users/allowed-op.in
index 1976904e4..9752f99a2 100644
--- a/src/opt/vyatta/etc/shell/level/users/allowed-op.in
+++ b/src/opt/vyatta/etc/shell/level/users/allowed-op.in
@@ -2,6 +2,7 @@ clear
connect
delete
disconnect
+execute
exit
force
monitor
diff --git a/src/services/vyos-http-api-server b/src/services/vyos-http-api-server
index 7f5233c6b..97633577d 100755
--- a/src/services/vyos-http-api-server
+++ b/src/services/vyos-http-api-server
@@ -212,6 +212,22 @@ class ImageModel(ApiModel):
}
}
+class ImportPkiModel(ApiModel):
+ op: StrictStr
+ path: List[StrictStr]
+ passphrase: StrictStr = None
+
+ class Config:
+ schema_extra = {
+ "example": {
+ "key": "id_key",
+ "op": "import_pki",
+ "path": ["op", "mode", "path"],
+ "passphrase": "passphrase",
+ }
+ }
+
+
class ContainerImageModel(ApiModel):
op: StrictStr
name: StrictStr = None
@@ -585,6 +601,14 @@ def _configure_op(data: Union[ConfigureModel, ConfigureListModel,
return success(msg)
+def create_path_import_pki_no_prompt(path):
+ correct_paths = ['ca', 'certificate', 'key-pair']
+ if path[1] not in correct_paths:
+ return False
+ path[1] = '--' + path[1].replace('-', '')
+ path[3] = '--key-filename'
+ return path[1:]
+
@app.post('/configure')
def configure_op(data: Union[ConfigureModel,
ConfigureListModel],
@@ -814,6 +838,44 @@ def reset_op(data: ResetModel):
return success(res)
+@app.post('/import-pki')
+def import_pki(data: ImportPkiModel):
+ session = app.state.vyos_session
+
+ op = data.op
+ path = data.path
+
+ lock.acquire()
+
+ try:
+ if op == 'import-pki':
+ # need to get rid or interactive mode for private key
+ if len(path) == 5 and path[3] in ['key-file', 'private-key']:
+ path_no_prompt = create_path_import_pki_no_prompt(path)
+ if not path_no_prompt:
+ return error(400, f"Invalid command: {' '.join(path)}")
+ if data.passphrase:
+ path_no_prompt += ['--passphrase', data.passphrase]
+ res = session.import_pki_no_prompt(path_no_prompt)
+ else:
+ res = session.import_pki(path)
+ if not res[0].isdigit():
+ return error(400, res)
+ # commit changes
+ session.commit()
+ res = res.split('. ')[0]
+ 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.")
+ finally:
+ lock.release()
+
+ return success(res)
+
@app.post('/poweroff')
def poweroff_op(data: PoweroffModel):
session = app.state.vyos_session