summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEshenko Dmitriy <dmitriy.eshenko@vyos.io>2020-08-19 00:37:00 +0300
committerGitHub <noreply@github.com>2020-08-18 23:37:00 +0200
commit865cba395223afaa263ff7581fb53ee509f36266 (patch)
tree833fded2c0993b5557f1e8f708f3565904ab4b63 /src
parent3282250a9a7c7a1df5c32e76fdc0024fd1ce9d9f (diff)
downloadvyos-1x-865cba395223afaa263ff7581fb53ee509f36266.tar.gz
vyos-1x-865cba395223afaa263ff7581fb53ee509f36266.zip
anyconnect: T2036: add anyconnect VPN support
Diffstat (limited to 'src')
-rwxr-xr-xsrc/conf_mode/vpn_anyconnect.py132
-rw-r--r--src/etc/systemd/system/ocserv.service.d/override.conf14
-rwxr-xr-xsrc/op_mode/anyconnect-control.py67
3 files changed, 213 insertions, 0 deletions
diff --git a/src/conf_mode/vpn_anyconnect.py b/src/conf_mode/vpn_anyconnect.py
new file mode 100755
index 000000000..45c06bffa
--- /dev/null
+++ b/src/conf_mode/vpn_anyconnect.py
@@ -0,0 +1,132 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2018-2020 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 os
+from sys import exit
+
+from vyos.config import Config
+from vyos.configdict import dict_merge
+from vyos.xml import defaults
+from vyos.template import render
+from vyos.util import call
+from vyos import ConfigError
+from crypt import crypt, mksalt, METHOD_SHA512
+
+from vyos import airbag
+airbag.enable()
+
+cfg_dir = '/run/ocserv'
+ocserv_conf = cfg_dir + '/ocserv.conf'
+ocserv_passwd = cfg_dir + '/ocpasswd'
+radius_cfg = cfg_dir + '/radiusclient.conf'
+radius_servers = cfg_dir + '/radius_servers'
+
+
+# Generate hash from user cleartext password
+def get_hash(password):
+ return crypt(password, mksalt(METHOD_SHA512))
+
+
+def get_config():
+ conf = Config()
+ base = ['vpn', 'anyconnect']
+ ocserv = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
+ default_values = defaults(base)
+ ocserv = dict_merge(default_values, ocserv)
+ return ocserv
+
+
+def verify(ocserv):
+ if ocserv is None:
+ return None
+
+ # Check authentication
+ if "authentication" in ocserv:
+ if "mode" in ocserv["authentication"]:
+ if "local" in ocserv["authentication"]["mode"]:
+ if not ocserv["authentication"]["local_users"] or not ocserv["authentication"]["local_users"]["username"]:
+ raise ConfigError('Anyconect mode local required at leat one user')
+ else:
+ for user in ocserv["authentication"]["local_users"]["username"]:
+ if not "password" in ocserv["authentication"]["local_users"]["username"][user]:
+ raise ConfigError(f'password required for user {user}')
+ else:
+ raise ConfigError('anyconnect authentication mode required')
+ else:
+ raise ConfigError('anyconnect authentication credentials required')
+
+ # Check ssl
+ if "ssl" in ocserv:
+ req_cert = ['ca_cert_file', 'cert_file', 'key_file']
+ for cert in req_cert:
+ if not cert in ocserv["ssl"]:
+ raise ConfigError('anyconnect ssl {0} required'.format(cert.replace('_', '-')))
+ else:
+ raise ConfigError('anyconnect ssl required')
+
+ # Check network settings
+ if "network_settings" in ocserv:
+ if "push_route" in ocserv["network_settings"]:
+ # Replace default route
+ if "0.0.0.0/0" in ocserv["network_settings"]["push_route"]:
+ ocserv["network_settings"]["push_route"].remove("0.0.0.0/0")
+ ocserv["network_settings"]["push_route"].append("default")
+ else:
+ ocserv["network_settings"]["push_route"] = "default"
+ else:
+ raise ConfigError('anyconnect network settings required')
+
+
+def generate(ocserv):
+ if not ocserv:
+ return None
+
+ if "radius" in ocserv["authentication"]["mode"]:
+ # Render radius client configuration
+ render(radius_cfg, 'ocserv/radius_conf.tmpl', ocserv["authentication"]["radius"], trim_blocks=True)
+ # Render radius servers
+ render(radius_servers, 'ocserv/radius_servers.tmpl', ocserv["authentication"]["radius"], trim_blocks=True)
+ else:
+ if "local_users" in ocserv["authentication"]:
+ for user in ocserv["authentication"]["local_users"]["username"]:
+ ocserv["authentication"]["local_users"]["username"][user]["hash"] = get_hash(ocserv["authentication"]["local_users"]["username"][user]["password"])
+ # Render local users
+ render(ocserv_passwd, 'ocserv/ocserv_passwd.tmpl', ocserv["authentication"]["local_users"], trim_blocks=True)
+
+ # Render config
+ render(ocserv_conf, 'ocserv/ocserv_config.tmpl', ocserv, trim_blocks=True)
+
+
+
+def apply(ocserv):
+ if not ocserv:
+ call('systemctl stop ocserv.service')
+ for file in [ocserv_conf, ocserv_passwd]:
+ if os.path.exists(file):
+ os.unlink(file)
+ else:
+ call('systemctl restart ocserv.service')
+
+
+if __name__ == '__main__':
+ try:
+ c = get_config()
+ verify(c)
+ generate(c)
+ apply(c)
+ except ConfigError as e:
+ print(e)
+ exit(1)
diff --git a/src/etc/systemd/system/ocserv.service.d/override.conf b/src/etc/systemd/system/ocserv.service.d/override.conf
new file mode 100644
index 000000000..89dbb153f
--- /dev/null
+++ b/src/etc/systemd/system/ocserv.service.d/override.conf
@@ -0,0 +1,14 @@
+[Unit]
+RequiresMountsFor=/run
+ConditionPathExists=/run/ocserv/ocserv.conf
+After=
+After=vyos-router.service
+After=dbus.service
+
+[Service]
+WorkingDirectory=/run/ocserv
+PIDFile=
+PIDFile=/run/ocserv/ocserv.pid
+ExecStart=
+ExecStart=/usr/sbin/ocserv --foreground --pid-file /run/ocserv/ocserv.pid --config /run/ocserv/ocserv.conf
+
diff --git a/src/op_mode/anyconnect-control.py b/src/op_mode/anyconnect-control.py
new file mode 100755
index 000000000..6382016b7
--- /dev/null
+++ b/src/op_mode/anyconnect-control.py
@@ -0,0 +1,67 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2020 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 argparse
+import json
+
+from vyos.config import Config
+from vyos.util import popen, run, DEVNULL
+from tabulate import tabulate
+
+occtl = '/usr/bin/occtl'
+occtl_socket = '/run/ocserv/occtl.socket'
+
+def show_sessions():
+ out, code = popen("sudo {0} -j -s {1} show users".format(occtl, occtl_socket),stderr=DEVNULL)
+ if code:
+ sys.exit('Cannot get anyconnect users information')
+ else:
+ headers = ["interface", "username", "ip", "remote IP", "RX", "TX", "state", "uptime"]
+ sessions = json.loads(out)
+ ses_list = []
+ for ses in sessions:
+ ses_list.append([ses["Device"], ses["Username"], ses["IPv4"], ses["Remote IP"], ses["_RX"], ses["_TX"], ses["State"], ses["_Connected at"]])
+ if len(ses_list) > 0:
+ print(tabulate(ses_list, headers))
+ else:
+ print("No active anyconnect sessions")
+
+def is_ocserv_configured():
+ if not Config().exists_effective('vpn anyconnect'):
+ print("vpn anyconnect server is not configured")
+ sys.exit(1)
+
+def main():
+ #parese args
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--action', help='Control action', required=True)
+ parser.add_argument('--selector', help='Selector username|ifname|sid', required=False)
+ parser.add_argument('--target', help='Target must contain username|ifname|sid', required=False)
+ args = parser.parse_args()
+
+
+ # Check is IPoE configured
+ is_ocserv_configured()
+
+ if args.action == "restart":
+ run("systemctl restart ocserv")
+ sys.exit(0)
+ elif args.action == "show_sessions":
+ show_sessions()
+
+if __name__ == '__main__':
+ main()