summaryrefslogtreecommitdiff
path: root/src/conf_mode/ipsec-settings.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/conf_mode/ipsec-settings.py')
-rwxr-xr-xsrc/conf_mode/ipsec-settings.py227
1 files changed, 227 insertions, 0 deletions
diff --git a/src/conf_mode/ipsec-settings.py b/src/conf_mode/ipsec-settings.py
new file mode 100755
index 000000000..015d1a480
--- /dev/null
+++ b/src/conf_mode/ipsec-settings.py
@@ -0,0 +1,227 @@
+#!/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 re
+import os
+
+from time import sleep
+from sys import exit
+
+from vyos.config import Config
+from vyos import ConfigError
+from vyos.util import call
+from vyos.template import render
+
+from vyos import airbag
+airbag.enable()
+
+ra_conn_name = "remote-access"
+charon_conf_file = "/etc/strongswan.d/charon.conf"
+ipsec_secrets_file = "/etc/ipsec.secrets"
+ipsec_ra_conn_dir = "/etc/ipsec.d/tunnels/"
+ipsec_ra_conn_file = ipsec_ra_conn_dir + ra_conn_name
+ipsec_conf_file = "/etc/ipsec.conf"
+ca_cert_path = "/etc/ipsec.d/cacerts"
+server_cert_path = "/etc/ipsec.d/certs"
+server_key_path = "/etc/ipsec.d/private"
+delim_ipsec_l2tp_begin = "### VyOS L2TP VPN Begin ###"
+delim_ipsec_l2tp_end = "### VyOS L2TP VPN End ###"
+charon_pidfile = "/var/run/charon.pid"
+
+def get_config():
+ config = Config()
+ data = {"install_routes": "yes"}
+
+ if config.exists("vpn ipsec options disable-route-autoinstall"):
+ data["install_routes"] = "no"
+
+ if config.exists("vpn ipsec ipsec-interfaces interface"):
+ data["ipsec_interfaces"] = config.return_values("vpn ipsec ipsec-interfaces interface")
+
+ # Init config variables
+ data["delim_ipsec_l2tp_begin"] = delim_ipsec_l2tp_begin
+ data["delim_ipsec_l2tp_end"] = delim_ipsec_l2tp_end
+ data["ipsec_ra_conn_file"] = ipsec_ra_conn_file
+ data["ra_conn_name"] = ra_conn_name
+ # Get l2tp ipsec settings
+ data["ipsec_l2tp"] = False
+ conf_ipsec_command = "vpn l2tp remote-access ipsec-settings " #last space is useful
+ if config.exists(conf_ipsec_command):
+ data["ipsec_l2tp"] = True
+
+ # Authentication params
+ if config.exists(conf_ipsec_command + "authentication mode"):
+ data["ipsec_l2tp_auth_mode"] = config.return_value(conf_ipsec_command + "authentication mode")
+ if config.exists(conf_ipsec_command + "authentication pre-shared-secret"):
+ data["ipsec_l2tp_secret"] = config.return_value(conf_ipsec_command + "authentication pre-shared-secret")
+
+ # mode x509
+ if config.exists(conf_ipsec_command + "authentication x509 ca-cert-file"):
+ data["ipsec_l2tp_x509_ca_cert_file"] = config.return_value(conf_ipsec_command + "authentication x509 ca-cert-file")
+ if config.exists(conf_ipsec_command + "authentication x509 crl-file"):
+ data["ipsec_l2tp_x509_crl_file"] = config.return_value(conf_ipsec_command + "authentication x509 crl-file")
+ if config.exists(conf_ipsec_command + "authentication x509 server-cert-file"):
+ data["ipsec_l2tp_x509_server_cert_file"] = config.return_value(conf_ipsec_command + "authentication x509 server-cert-file")
+ data["server_cert_file_copied"] = server_cert_path+"/"+re.search('\w+(?:\.\w+)*$', config.return_value(conf_ipsec_command + "authentication x509 server-cert-file")).group(0)
+ if config.exists(conf_ipsec_command + "authentication x509 server-key-file"):
+ data["ipsec_l2tp_x509_server_key_file"] = config.return_value(conf_ipsec_command + "authentication x509 server-key-file")
+ data["server_key_file_copied"] = server_key_path+"/"+re.search('\w+(?:\.\w+)*$', config.return_value(conf_ipsec_command + "authentication x509 server-key-file")).group(0)
+ if config.exists(conf_ipsec_command + "authentication x509 server-key-password"):
+ data["ipsec_l2tp_x509_server_key_password"] = config.return_value(conf_ipsec_command + "authentication x509 server-key-password")
+
+ # Common l2tp ipsec params
+ if config.exists(conf_ipsec_command + "ike-lifetime"):
+ data["ipsec_l2tp_ike_lifetime"] = config.return_value(conf_ipsec_command + "ike-lifetime")
+ else:
+ data["ipsec_l2tp_ike_lifetime"] = "3600"
+
+ if config.exists(conf_ipsec_command + "lifetime"):
+ data["ipsec_l2tp_lifetime"] = config.return_value(conf_ipsec_command + "lifetime")
+ else:
+ data["ipsec_l2tp_lifetime"] = "3600"
+
+ if config.exists("vpn l2tp remote-access outside-address"):
+ data['outside_addr'] = config.return_value('vpn l2tp remote-access outside-address')
+
+ return data
+
+def write_ipsec_secrets(c):
+ if c.get("ipsec_l2tp_auth_mode") == "pre-shared-secret":
+ secret_txt = "{0}\n{1} %any : PSK \"{2}\"\n{3}\n".format(delim_ipsec_l2tp_begin, c['outside_addr'], c['ipsec_l2tp_secret'], delim_ipsec_l2tp_end)
+ elif c.get("ipsec_l2tp_auth_mode") == "x509":
+ secret_txt = "{0}\n: RSA {1}\n{2}\n".format(delim_ipsec_l2tp_begin, c['server_key_file_copied'], delim_ipsec_l2tp_end)
+
+ old_umask = os.umask(0o077)
+ with open(ipsec_secrets_file, 'a+') as f:
+ f.write(secret_txt)
+ os.umask(old_umask)
+
+def write_ipsec_conf(c):
+ ipsec_confg_txt = "{0}\ninclude {1}\n{2}\n".format(delim_ipsec_l2tp_begin, ipsec_ra_conn_file, delim_ipsec_l2tp_end)
+
+ old_umask = os.umask(0o077)
+ with open(ipsec_conf_file, 'a+') as f:
+ f.write(ipsec_confg_txt)
+ os.umask(old_umask)
+
+### Remove config from file by delimiter
+def remove_confs(delim_begin, delim_end, conf_file):
+ call("sed -i '/"+delim_begin+"/,/"+delim_end+"/d' "+conf_file)
+
+
+### Checking certificate storage and notice if certificate not in /config directory
+def check_cert_file_store(cert_name, file_path, dts_path):
+ if not re.search('^\/config\/.+', file_path):
+ print("Warning: \"" + file_path + "\" lies outside of /config/auth directory. It will not get preserved during image upgrade.")
+ #Checking file existence
+ if not os.path.isfile(file_path):
+ raise ConfigError("L2TP VPN configuration error: Invalid "+cert_name+" \""+file_path+"\"")
+ else:
+ ### Cpy file to /etc/ipsec.d/certs/ /etc/ipsec.d/cacerts/
+ # todo make check
+ ret = call('cp -f '+file_path+' '+dts_path)
+ if ret:
+ raise ConfigError("L2TP VPN configuration error: Cannot copy "+file_path)
+
+def verify(data):
+ # l2tp ipsec check
+ if data["ipsec_l2tp"]:
+ # Checking dependecies for "authentication mode pre-shared-secret"
+ if data.get("ipsec_l2tp_auth_mode") == "pre-shared-secret":
+ if not data.get("ipsec_l2tp_secret"):
+ raise ConfigError("pre-shared-secret required")
+ if not data.get("outside_addr"):
+ raise ConfigError("outside-address not defined")
+
+ # Checking dependecies for "authentication mode x509"
+ if data.get("ipsec_l2tp_auth_mode") == "x509":
+ if not data.get("ipsec_l2tp_x509_server_key_file"):
+ raise ConfigError("L2TP VPN configuration error: \"server-key-file\" not defined.")
+ else:
+ check_cert_file_store("server-key-file", data['ipsec_l2tp_x509_server_key_file'], server_key_path)
+
+ if not data.get("ipsec_l2tp_x509_server_cert_file"):
+ raise ConfigError("L2TP VPN configuration error: \"server-cert-file\" not defined.")
+ else:
+ check_cert_file_store("server-cert-file", data['ipsec_l2tp_x509_server_cert_file'], server_cert_path)
+
+ if not data.get("ipsec_l2tp_x509_ca_cert_file"):
+ raise ConfigError("L2TP VPN configuration error: \"ca-cert-file\" must be defined for X.509")
+ else:
+ check_cert_file_store("ca-cert-file", data['ipsec_l2tp_x509_ca_cert_file'], ca_cert_path)
+
+ if not data.get('ipsec_interfaces'):
+ raise ConfigError("L2TP VPN configuration error: \"vpn ipsec ipsec-interfaces\" must be specified.")
+
+def generate(data):
+ render(charon_conf_file, 'ipsec/charon.tmpl', data, trim_blocks=True)
+
+ if data["ipsec_l2tp"]:
+ remove_confs(delim_ipsec_l2tp_begin, delim_ipsec_l2tp_end, ipsec_secrets_file)
+ # old_umask = os.umask(0o077)
+ # render(ipsec_secrets_file, 'ipsec/ipsec.secrets.tmpl', data, trim_blocks=True)
+ # os.umask(old_umask)
+ ## Use this method while IPSec CLI handler won't be overwritten to python
+ write_ipsec_secrets(data)
+
+ old_umask = os.umask(0o077)
+
+ # Create tunnels directory if does not exist
+ if not os.path.exists(ipsec_ra_conn_dir):
+ os.makedirs(ipsec_ra_conn_dir)
+
+ render(ipsec_ra_conn_file, 'ipsec/remote-access.tmpl', data, trim_blocks=True)
+ os.umask(old_umask)
+
+ remove_confs(delim_ipsec_l2tp_begin, delim_ipsec_l2tp_end, ipsec_conf_file)
+ # old_umask = os.umask(0o077)
+ # render(ipsec_conf_file, 'ipsec/ipsec.conf.tmpl', data, trim_blocks=True)
+ # os.umask(old_umask)
+ ## Use this method while IPSec CLI handler won't be overwritten to python
+ write_ipsec_conf(data)
+
+ else:
+ if os.path.exists(ipsec_ra_conn_file):
+ remove_confs(delim_ipsec_l2tp_begin, delim_ipsec_l2tp_end, ipsec_ra_conn_file)
+ remove_confs(delim_ipsec_l2tp_begin, delim_ipsec_l2tp_end, ipsec_secrets_file)
+ remove_confs(delim_ipsec_l2tp_begin, delim_ipsec_l2tp_end, ipsec_conf_file)
+
+def restart_ipsec():
+ call('ipsec restart >&/dev/null')
+ # counter for apply swanctl config
+ counter = 10
+ while counter <= 10:
+ if os.path.exists(charon_pidfile):
+ call('swanctl -q >&/dev/null')
+ break
+ counter -=1
+ sleep(1)
+ if counter == 0:
+ raise ConfigError('VPN configuration error: IPSec is not running.')
+
+def apply(data):
+ # Restart IPSec daemon
+ restart_ipsec()
+
+if __name__ == '__main__':
+ try:
+ c = get_config()
+ verify(c)
+ generate(c)
+ apply(c)
+ except ConfigError as e:
+ print(e)
+ exit(1)