]> git.ipfire.org Git - people/ms/network.git/blob - src/functions/functions.ipsec
b7e09a4c67119624a2365f22a5267dc2b9c4869c
[people/ms/network.git] / src / functions / functions.ipsec
1 #!/bin/bash
2 ###############################################################################
3 # #
4 # IPFire.org - A linux based firewall #
5 # Copyright (C) 2017 IPFire Network Development Team #
6 # #
7 # This program is free software: you can redistribute it and/or modify #
8 # it under the terms of the GNU General Public License as published by #
9 # the Free Software Foundation, either version 3 of the License, or #
10 # (at your option) any later version. #
11 # #
12 # This program is distributed in the hope that it will be useful, #
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
15 # GNU General Public License for more details. #
16 # #
17 # You should have received a copy of the GNU General Public License #
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. #
19 # #
20 ###############################################################################
21
22 IPSEC_CONNECTION_CONFIG_SETTINGS="\
23 AUTH_MODE \
24 DPD_ACTION \
25 DPD_DELAY \
26 DPD_TIMEOUT \
27 INACTIVITY_TIMEOUT \
28 LOCAL_ADDRESS \
29 LOCAL_ID \
30 LOCAL_PREFIX \
31 MODE \
32 PEER \
33 POOLS \
34 PSK \
35 REMOTE_ID \
36 REMOTE_PREFIX \
37 SECURITY_POLICY \
38 START_ACTION \
39 TYPE \
40 ENABLED \
41 ZONE"
42
43 # Default values
44 IPSEC_DEFAULT_AUTH_MODE="PSK"
45 IPSEC_DEFAULT_DPD_ACTION="restart"
46 IPSEC_DEFAULT_DPD_DELAY="30"
47 IPSEC_DEFAULT_DPD_TIMEOUT="120"
48 IPSEC_DEFAULT_ENABLED="true"
49 IPSEC_DEFAULT_INACTIVITY_TIMEOUT="0"
50 IPSEC_DEFAULT_MODE="tunnel"
51 IPSEC_DEFAULT_SECURITY_POLICY="system"
52 IPSEC_DEFAULT_START_ACTION="on-demand"
53 IPSEC_DEFAULT_TYPE="net-to-net"
54
55 IPSEC_VALID_MODES="gre-transport tunnel vti"
56 IPSEC_VALID_AUTH_MODES="PSK"
57
58 cli_ipsec() {
59 local action=${1}
60 shift 1
61
62 case "${action}" in
63 connection)
64 cli_ipsec_connection "$@"
65 ;;
66 pool)
67 cli_ipsec_pool "$@"
68 ;;
69 *)
70 error "Unrecognized argument: ${action}"
71 exit ${EXIT_ERROR}
72 ;;
73 esac
74 }
75
76 cli_ipsec_connection() {
77 if ipsec_connection_exists ${1}; then
78 local connection=${1}
79 local key=${2}
80 key=${key//-/_}
81 shift 2
82
83 case "${key}" in
84 authentication|down|disable|dpd|enable|inactivity_timeout|local|mode|peer|pool|remote|security_policy|start_action|up|zone)
85 ipsec_connection_${key} ${connection} "$@"
86 ;;
87 color)
88 color_cli "ipsec-connection" "${connection}" "$@"
89 ;;
90 description)
91 description_cli "ipsec-connection" ${connection} $@
92 ;;
93 show)
94 cli_ipsec_connection_show "${connection}"
95 exit $?
96 ;;
97 *)
98 error "Unrecognized argument: ${key}"
99 exit ${EXIT_ERROR}
100 ;;
101 esac
102 else
103 local action=${1}
104 shift
105
106 case "${action}" in
107 new)
108 ipsec_connection_new "$@"
109 ;;
110 destroy)
111 cli_ipsec_connection_destroy "$@"
112 ;;
113 ""|*)
114 if [ -n "${action}" ]; then
115 error "Unrecognized argument: '${action}'"
116 fi
117 exit ${EXIT_ERROR}
118 ;;
119 esac
120 fi
121 }
122
123 cli_ipsec_connection_destroy() {
124 local connection="${1}"
125
126 if ! ipsec_connection_destroy "${connection}"; then
127 return ${EXIT_ERROR}
128 fi
129
130 # Inform strongswan about the changes
131 ipsec_strongswan_load
132
133 # Configure strongswan autostart
134 ipsec_strongswan_autostart
135 }
136
137 ipsec_connection_get_color() {
138 # This function return the color of a zone
139 assert [ $# -eq 1 ]
140
141 local name=${1}
142 color_read "ipsec-connection" ${name}
143 }
144
145 ipsec_connection_get_description_title() {
146 assert [ $# -eq 1 ]
147
148 local name=${1}
149 description_title_read $(description_format_filename "ipsec-connection" "${name}")
150 }
151
152 cli_ipsec_connection_show() {
153 local connection="${1}"
154
155 # Read the config settings
156 local ${IPSEC_CONNECTION_CONFIG_SETTINGS}
157 if ! ipsec_connection_read_config "${connection}"; then
158 error "Could not read the connection configuration"
159 return ${EXIT_ERROR}
160 fi
161
162 cli_headline 0 "IPsec VPN Connection: ${connection}"
163 cli_space
164
165 cli_print_fmt1 1 "Color" "$(cli_color_bar $(ipsec_connection_get_color ${connection}))"
166 cli_print_fmt1 1 "Description" "$(ipsec_connection_get_description_title ${connection})"
167 cli_space
168
169 # Peer
170 if isset PEER; then
171 cli_print_fmt1 1 "Peer" "${PEER}"
172 fi
173
174 # Security Policy
175 cli_print_fmt1 1 "Security Policy" "${SECURITY_POLICY-${IPSEC_DEFAULT_SECURITY_POLICY}}"
176 cli_space
177
178 cli_headline 2 "Authentication"
179 case "${AUTH_MODE^^}" in
180 PSK)
181 cli_print_fmt1 2 "Mode" "Pre-Shared-Key"
182
183 if isset PSK; then
184 cli_print_fmt1 2 "Pre-Shared-Key" "****"
185 else
186 cli_print_fmt1 2 "Pre-Shared-Key" "- is not set -"
187 fi
188 ;;
189 X509)
190 : # TODO
191 ;;
192 esac
193 cli_space
194
195 local i
196 for i in LOCAL REMOTE; do
197 case "${i}" in
198 LOCAL)
199 cli_headline 2 "Local"
200 ;;
201 REMOTE)
202 cli_headline 2 "Remote"
203 ;;
204 esac
205
206 local id_var="${i}_ID"
207 if [ -n "${!id_var}" ]; then
208 cli_print_fmt1 2 "ID" "${!id_var}"
209 fi
210
211 local prefix_var="${i}_PREFIX"
212 if isset ${prefix_var}; then
213 cli_headline 3 "Prefix(es)"
214
215 local prefix
216 for prefix in ${!prefix_var}; do
217 cli_print_fmt1 3 "${prefix}"
218 done
219 fi
220
221 cli_space
222 done
223
224 cli_headline 2 "Misc."
225
226 case "${MODE}" in
227 gre-transport)
228 cli_print_fmt1 2 "Transport Mode" "GRE Transport"
229 ;;
230 tunnel)
231 cli_print_fmt1 2 "Transport Mode" "Tunnel"
232 ;;
233 vti)
234 cli_print_fmt1 2 "Transport Mode" "Virtual Tunnel Interface"
235 ;;
236 *)
237 cli_print_fmt1 2 "Transport Mode" "- Unknown -"
238 ;;
239 esac
240
241 # Inactivity timeout
242 if isset INACTIVITY_TIMEOUT && [ ${INACTIVITY_TIMEOUT} -gt 0 ]; then
243 cli_print_fmt1 2 "Inactivity Timeout" "$(format_time ${INACTIVITY_TIMEOUT})"
244 fi
245 cli_space
246
247 return ${EXIT_OK}
248 }
249
250 ipsec_connection_disable() {
251 local connection=${1}
252
253 if ! ipsec_connection_write_config_key "${connection}" "ENABLED" "false"; then
254 log ERROR "Could not write configuration settings"
255 return ${EXIT_ERROR}
256 fi
257
258 # Configure strongswan autostart
259 ipsec_strongswan_autostart
260 }
261
262 ipsec_connection_enable() {
263 local connection=${1}
264
265 if ! ipsec_connection_write_config_key "${connection}" "ENABLED" "true"; then
266 log ERROR "Could not write configuration settings"
267 return ${EXIT_ERROR}
268 fi
269
270 # Configure strongswan autostart
271 ipsec_strongswan_autostart
272 }
273
274 # This function writes all values to a via ${connection} specificated VPN IPsec configuration file
275 ipsec_connection_write_config() {
276 assert [ $# -ge 1 ]
277
278 local connection="${1}"
279
280 if ! ipsec_connection_exists "${connection}"; then
281 log ERROR "No such VPN IPsec connection: ${connection}"
282 return ${EXIT_ERROR}
283 fi
284
285 local path="${NETWORK_IPSEC_CONNS_DIR}/${connection}/settings"
286
287 if ! settings_write "${path}" ${IPSEC_CONNECTION_CONFIG_SETTINGS}; then
288 log ERROR "Could not write configuration settings for VPN IPsec connection ${connection}"
289 return ${EXIT_ERROR}
290 fi
291
292 ipsec_reload ${connection}
293 }
294
295 # This funtion writes the value for one key to a via ${connection} specificated VPN IPsec connection configuration file
296 ipsec_connection_write_config_key() {
297 assert [ $# -ge 3 ]
298
299 local connection=${1}
300 local key=${2}
301 shift 2
302
303 local value="$@"
304
305 if ! ipsec_connection_exists "${connection}"; then
306 log ERROR "No such VPN ipsec connection: ${connection}"
307 return ${EXIT_ERROR}
308 fi
309
310 log DEBUG "Set '${key}' to new value '${value}' in VPN ipsec connection '${connection}'"
311
312 local ${IPSEC_CONNECTION_CONFIG_SETTINGS}
313
314 # Read the config settings
315 if ! ipsec_connection_read_config "${connection}"; then
316 return ${EXIT_ERROR}
317 fi
318
319 # Set the key to a new value
320 assign "${key}" "${value}"
321
322 if ! ipsec_connection_write_config "${connection}"; then
323 return ${EXIT_ERROR}
324 fi
325
326 return ${EXIT_TRUE}
327 }
328
329 # Reads one or more keys out of a settings file or all if no key is provided.
330 ipsec_connection_read_config() {
331 assert [ $# -ge 1 ]
332
333 local connection="${1}"
334 shift 1
335
336 if ! ipsec_connection_exists "${connection}"; then
337 log ERROR "No such VPN IPsec connection : ${connection}"
338 return ${EXIT_ERROR}
339 fi
340
341
342 local args
343 if [ $# -eq 0 ] && [ -n "${IPSEC_CONNECTION_CONFIG_SETTINGS}" ]; then
344 list_append args ${IPSEC_CONNECTION_CONFIG_SETTINGS}
345 else
346 list_append args "$@"
347 fi
348
349 local path="${NETWORK_IPSEC_CONNS_DIR}/${connection}/settings"
350
351 if ! settings_read "${path}" ${args}; then
352 log ERROR "Could not read settings for VPN IPsec connection ${connection}"
353 return ${EXIT_ERROR}
354 fi
355 }
356
357 # This function checks if a vpn ipsec connection exists
358 # Returns True when yes and false when not
359 ipsec_connection_exists() {
360 assert [ $# -eq 1 ]
361
362 local connection=${1}
363
364 local path="${NETWORK_IPSEC_CONNS_DIR}/${connection}"
365
366 [ -d "${path}" ] && return ${EXIT_TRUE} || return ${EXIT_FALSE}
367 }
368
369 # Determines if strongswan should be automatically started
370 # when the system boots up.
371 ipsec_strongswan_autostart() {
372 local autostart_needed="false"
373
374 local connection
375 for connection in $(ipsec_list_connections); do
376 local ENABLED
377
378 if ! ipsec_connection_read_config "${connection}" "ENABLED"; then
379 log WARNING "Could not read configuation"
380 continue
381 fi
382
383 if enabled ENABLED; then
384 autostart_needed="true"
385 break
386 fi
387 done
388
389 # Start strongswan when we need it and when it is not yet enabled
390 if ${autostart_needed}; then
391 if ! service_is_enabled "strongswan"; then
392 service_enable "strongswan"
393 fi
394
395 if ! service_is_active "strongswan"; then
396 service_start "strongswan"
397 fi
398
399 # Disable strongswan when we do not need it but it is enabled
400 elif ! ${autostart_needed}; then
401 if service_is_enabled "strongswan"; then
402 service_disable "strongswan"
403 fi
404
405 if service_is_active "strongswan"; then
406 service_stop "strongswan"
407 fi
408 fi
409 }
410
411 ipsec_strongswan_load() {
412 # Do nothing if strongswan is not running
413 if ! service_is_active "strongswan"; then
414 return ${EXIT_OK}
415 fi
416
417 if ! cmd swanctl --load-all; then
418 log ERROR "Could not reload strongswan config"
419 return ${EXIT_ERROR}
420 fi
421 }
422
423 # Reloads the connection after config changes
424 ipsec_reload() {
425 local connection=${1}
426
427 local ENABLED
428
429 if ! ipsec_connection_read_config "${connection}" "ENABLED"; then
430 log ERROR "Could not read configuration for IPsec connection ${connection}"
431 return ${EXIT_ERROR}
432 fi
433
434 if enabled ENABLED; then
435 if ! ipsec_connection_to_strongswan ${connection}; then
436 log ERROR "Could not generate strongswan config for ${connnection}"
437 return ${EXIT_ERROR}
438 fi
439 else
440 log DEBUG "Deleting strongswan config ${NETWORK_IPSEC_SWANCTL_CONNECTIONS_DIR}/${connection}.conf"
441 unlink "${NETWORK_IPSEC_SWANCTL_CONNECTIONS_DIR}/${connection}.conf"
442 fi
443
444 ipsec_strongswan_load
445 }
446
447 # Handle the cli after authentification
448 ipsec_connection_authentication() {
449 if [ ! $# -gt 1 ]; then
450 log ERROR "Not enough arguments"
451 return ${EXIT_ERROR}
452 fi
453
454 local connection=${1}
455 local cmd=${2}
456 shift 2
457
458 case ${cmd} in
459 mode)
460 ipsec_connection_authentication_mode "${connection}" "$@"
461 ;;
462 pre-shared-key)
463 ipsec_connection_authentication_psk "${connection}" "$@"
464 ;;
465 *)
466 log ERROR "Unrecognized argument: ${cmd}"
467 return ${EXIT_ERROR}
468 ;;
469 esac
470 }
471
472 # Set the authentification mode
473 ipsec_connection_authentication_mode() {
474 if [ ! $# -eq 2 ]; then
475 log ERROR "Not enough arguments"
476 return ${EXIT_ERROR}
477 fi
478 local connection=${1}
479 local mode=${2}
480
481 if ! isoneof mode ${IPSEC_VALID_AUTH_MODES}; then
482 log ERROR "Auth mode '${mode}' is invalid"
483 return ${EXIT_ERROR}
484 fi
485
486 if ! ipsec_connection_write_config_key "${connection}" "AUTH_MODE" ${mode^^}; then
487 log ERROR "Could not write configuration settings"
488 return ${EXIT_ERROR}
489 fi
490 }
491
492 # Set the psk
493 ipsec_connection_authentication_psk() {
494 if [ ! $# -eq 2 ]; then
495 log ERROR "Not enough arguments"
496 return ${EXIT_ERROR}
497 fi
498
499 local connection=${1}
500 local psk=${2}
501
502 local length=${#psk}
503
504 if [ ${length} -lt 4 ]; then
505 error "The PSK must be longer than four characters"
506 return ${EXIT_ERROR}
507 fi
508
509 if [ ${length} -gt 128 ]; then
510 error "The PSK cannot be longer than 128 characters"
511 return ${EXIT_ERROR}
512 fi
513
514 if ! ipsec_connection_write_config_key "${connection}" "PSK" "${psk}"; then
515 log ERROR "Could not write configuration settings"
516 return ${EXIT_ERROR}
517 fi
518
519 return ${EXIT_OK}
520 }
521
522 ipsec_connection_up() {
523 local connection="${1}"
524
525 if ! ipsec_connection_exists "${connection}"; then
526 error "No such VPN IPsec connection: ${connection}"
527 return ${EXIT_ERROR}
528 fi
529
530 if ! [ -f "${NETWORK_IPSEC_SWANCTL_CONNECTIONS_DIR}/${connection}.conf" ]; then
531 log DEBUG "Could not find a swanctl config, generating swanctl config"
532 ipsec_connection_to_strongswan "${connection}"
533 ipsec_strongswan_load
534 fi
535
536 cmd swanctl --initiate --child "${connection}"
537 }
538
539 ipsec_connection_down() {
540 local connection="${1}"
541
542 if ! ipsec_connection_exists "${connection}"; then
543 error "No such VPN IPsec connection: ${connection}"
544 return ${EXIT_ERROR}
545 fi
546
547 cmd swanctl --terminate --ike "${connection}"
548 }
549
550 # Handle the cli after authentification
551 ipsec_connection_dpd() {
552 if [ ! $# -gt 1 ]; then
553 log ERROR "Not enough arguments"
554 return ${EXIT_ERROR}
555 fi
556
557 local connection=${1}
558 local cmd=${2}
559 shift 2
560
561 case ${cmd} in
562 action)
563 ipsec_connection_dpd_action "${connection}" "$@"
564 ;;
565 delay)
566 ipsec_connection_dpd_delay "${connection}" "$@"
567 ;;
568 timeout)
569 ipsec_connection_dpd_timeout "${connection}" "$@"
570 ;;
571 *)
572 log ERROR "Unrecognized argument: ${cmd}"
573 return ${EXIT_ERROR}
574 ;;
575 esac
576 }
577
578 # Set the default dpd action
579 ipsec_connection_dpd_action() {
580 if [ ! $# -eq 2 ]; then
581 log ERROR "Not enough arguments"
582 return ${EXIT_ERROR}
583 fi
584 local connection=${1}
585 local action=${2}
586
587 if ! isoneof action "restart" "clear"; then
588 log ERROR "dpd action '${action}' is invalid"
589 return ${EXIT_ERROR}
590 fi
591
592 if ! ipsec_connection_write_config_key "${connection}" "DPD_ACTION" ${action}; then
593 log ERROR "Could not write configuration settings"
594 return ${EXIT_ERROR}
595 fi
596 }
597
598 # Set the dpd delay
599 ipsec_connection_dpd_delay() {
600 if [ ! $# -ge 2 ]; then
601 log ERROR "Not enough arguments"
602 return ${EXIT_ERROR}
603 fi
604
605 local connection=${1}
606 shift 1
607 local value=$@
608
609 if ! isinteger value; then
610 value=$(parse_time "$@")
611 if [ ! $? -eq 0 ]; then
612 log ERROR "Parsing the passed time was not sucessful please check the passed values."
613 return ${EXIT_ERROR}
614 fi
615 fi
616
617 if [ ${value} -lt 0 ]; then
618 log ERROR "The passed time value must be in the sum greater or equal zero seconds."
619 return ${EXIT_ERROR}
620 fi
621
622 if ! ipsec_connection_write_config_key "${connection}" "DPD_DELAY" ${value}; then
623 log ERROR "Could not write configuration settings"
624 return ${EXIT_ERROR}
625 fi
626
627 return ${EXIT_OK}
628 }
629
630 # Set the dpd timeout
631 ipsec_connection_dpd_timeout() {
632 if [ ! $# -ge 2 ]; then
633 log ERROR "Not enough arguments"
634 return ${EXIT_ERROR}
635 fi
636
637 local connection=${1}
638 shift 1
639 local value=$@
640
641 if ! isinteger value; then
642 value=$(parse_time "$@")
643 if [ ! $? -eq 0 ]; then
644 log ERROR "Parsing the passed time was not sucessful please check the passed values."
645 return ${EXIT_ERROR}
646 fi
647 fi
648
649 if [ ${value} -le 0 ]; then
650 log ERROR "The passed time value must be in the sum greater or equal zero seconds."
651 return ${EXIT_ERROR}
652 fi
653
654 if ! ipsec_connection_write_config_key "${connection}" "DPD_TIMEOUT" ${value}; then
655 log ERROR "Could not write configuration settings"
656 return ${EXIT_ERROR}
657 fi
658
659 return ${EXIT_OK}
660 }
661
662 # Handle the cli after local
663 ipsec_connection_local() {
664 if [ ! $# -ge 2 ]; then
665 log ERROR "Not enough arguments"
666 return ${EXIT_ERROR}
667 fi
668
669 local connection=${1}
670 local cmd=${2}
671 shift 2
672
673 case ${cmd} in
674 address)
675 ipsec_connection_local_address "${connection}" "$@"
676 ;;
677 id)
678 ipsec_connection_id "${connection}" "LOCAL" "$@"
679 ;;
680 prefix)
681 ipsec_connection_prefix "${connection}" "LOCAL" "$@"
682 ;;
683 *)
684 log ERROR "Unrecognized argument: ${cmd}"
685 return ${EXIT_ERROR}
686 ;;
687 esac
688
689 return ${EXIT_OK}
690 }
691
692 # Set the connection mode
693 ipsec_connection_mode() {
694 if [ ! $# -eq 2 ]; then
695 log ERROR "Not enough arguments"
696 return ${EXIT_ERROR}
697 fi
698 local connection=${1}
699 local mode=${2}
700
701 if ! isoneof mode ${IPSEC_VALID_MODES}; then
702 log ERROR "Mode '${mode}' is invalid"
703 return ${EXIT_ERROR}
704 fi
705
706 if ! ipsec_connection_write_config_key "${connection}" "MODE" ${mode}; then
707 log ERROR "Could not write configuration settings"
708 return ${EXIT_ERROR}
709 fi
710
711 return ${EXIT_OK}
712 }
713
714 ipsec_connection_zone() {
715 local connection="${1}"
716 local zone="${2}"
717 shift 2
718
719 # Check if we got an argument
720 if ! isset zone; then
721 error "Zone is not set"
722 return ${EXIT_ERROR}
723 fi
724
725 local ZONE
726 case "${zone}" in
727 -)
728 if ! ipsec_connection_read_config "${connection}" "ZONE"; then
729 log ERROR "Could not read configuration for IPsec connection ${connection}"
730 return ${EXIT_ERROR}
731 fi
732
733 # Removes zone setting
734 zone=""
735
736 if isset ZONE; then
737 log INFO "Removing zone ${ZONE} from IPsec connection '${connection}'"
738 fi
739 ;;
740
741 *)
742 # Check if the zone exists
743 if ! zone_exists "${zone}"; then
744 error "Zone ${zone} does not exist"
745 return ${EXIT_ERROR}
746 fi
747
748 # Zone must be of type tunnel
749 local hook="$(zone_get_hook "${zone}")"
750
751 case "${hook}" in
752 ip-tunnel)
753 # We support ip-tunnels
754 ;;
755
756 *)
757 error "Zones of type ${hook} are not supported"
758 return ${EXIT_ERROR}
759 ;;
760 esac
761
762 # Check if this zone is alreadz attached to another IPsec connection
763 # XXX
764
765 log INFO "Adding zone ${zone} to IPsec connection '${connection}'"
766 ;;
767 esac
768
769 # Save settings
770 if ! ipsec_connection_write_config_key "${connection}" "ZONE" "${zone}"; then
771 error "Could not write configuration settings"
772 return ${EXIT_ERROR}
773 fi
774
775 return ${EXIT_OK}
776 }
777
778 # Set the local address
779 ipsec_connection_local_address() {
780 if [ ! $# -eq 2 ]; then
781 log ERROR "Not enough arguments"
782 return ${EXIT_ERROR}
783 fi
784 local connection=${1}
785 local local_address=${2}
786
787 if ! ipsec_connection_check_peer ${local_address}; then
788 log ERROR "Local address '${local_address}' is invalid"
789 return ${EXIT_ERROR}
790 fi
791
792 if ! ipsec_connection_write_config_key "${connection}" "LOCAL_ADDRESS" ${local_address}; then
793 log ERROR "Could not write configuration settings"
794 return ${EXIT_ERROR}
795 fi
796
797 return ${EXIT_OK}
798 }
799
800 # Set the peer to connect to
801 ipsec_connection_peer() {
802 if [ ! $# -eq 2 ]; then
803 log ERROR "Not enough arguments"
804 return ${EXIT_ERROR}
805 fi
806 local connection=${1}
807 local peer=${2}
808
809 if ! ipsec_connection_check_peer ${peer}; then
810 log ERROR "Peer '${peer}' is invalid"
811 return ${EXIT_ERROR}
812 fi
813
814 if ! ipsec_connection_write_config_key "${connection}" "PEER" ${peer}; then
815 log ERROR "Could not write configuration settings"
816 return ${EXIT_ERROR}
817 fi
818
819 return ${EXIT_OK}
820 }
821
822 #Set the local or remote id
823 ipsec_connection_id() {
824 if [ ! $# -eq 3 ]; then
825 log ERROR "Not enough arguments"
826 return ${EXIT_ERROR}
827 fi
828 local connection=${1}
829 local type=${2}
830 local id=${3}
831
832 if ! ipsec_connection_check_id ${id}; then
833 log ERROR "Id '${id}' is invalid"
834 return ${EXIT_ERROR}
835 fi
836
837 if ! ipsec_connection_write_config_key "${connection}" "${type}_ID" ${id}; then
838 log ERROR "Could not write configuration settings"
839 return ${EXIT_ERROR}
840 fi
841
842 return ${EXIT_OK}
843 }
844
845 # Set the local or remote prefix
846 ipsec_connection_prefix() {
847 if [ ! $# -ge 3 ]; then
848 log ERROR "Not enough arguments"
849 return ${EXIT_ERROR}
850 fi
851 local connection=${1}
852 local type=${2}
853 shift 2
854
855 local _prefix="${type}_PREFIX"
856 local "${_prefix}"
857 if ! ipsec_connection_read_config "${connection}" "${_prefix}"; then
858 return ${EXIT_ERROR}
859 fi
860
861 # Remove duplicated entries to proceed the list safely
862 assign "${_prefix}" "$(list_unique ${!_prefix} )"
863
864 local prefixes_added
865 local prefixes_removed
866 local prefixes_set
867
868 while [ $# -gt 0 ]; do
869 local arg="${1}"
870
871 case "${arg}" in
872 +*)
873 list_append prefixes_added "${arg:1}"
874 ;;
875 -*)
876 list_append prefixes_removed "${arg:1}"
877 ;;
878 [A-Fa-f0-9]*)
879 list_append prefixes_set "${arg}"
880 ;;
881 *)
882 error "Invalid argument: ${arg}"
883 return ${EXIT_ERROR}
884 ;;
885 esac
886 shift
887 done
888
889 # Check if the user is trying a mixed operation
890 if ! list_is_empty prefixes_set && (! list_is_empty prefixes_added || ! list_is_empty prefixes_removed); then
891 error "You cannot reset the prefix list and add or remove prefixes at the same time"
892 return ${EXIT_ERROR}
893 fi
894
895 # Set new prefix list
896 if ! list_is_empty prefixes_set; then
897 # Check if all prefixes are valid
898 local prefix
899 for prefix in ${prefixes_set}; do
900 if ! ip_net_is_valid ${prefix}; then
901 error "Unsupported prefix: ${prefix}"
902 return ${EXIT_ERROR}
903 fi
904 done
905
906 assign "${_prefix}" "${prefixes_set}"
907
908 # Perform incremental updates
909 else
910 local prefix
911
912 # Perform all removals
913 for prefix in ${prefixes_removed}; do
914 if ! list_remove "${_prefix}" ${prefix}; then
915 warning "${prefix} was not on the list and could not be removed"
916 fi
917 done
918
919
920 for prefix in ${prefixes_added}; do
921 if ip_net_is_valid ${prefix}; then
922 if ! list_append_unique "${_prefix}" ${prefix}; then
923 warning "${prefix} is already on the prefix list"
924 fi
925 else
926 warning "${prefix} is not a valid IP network and could not be added"
927 fi
928 done
929 fi
930
931 # Check if the list contain at least one valid prefix
932 if list_is_empty ${_prefix}; then
933 error "Cannot save an empty prefix list"
934 return ${EXIT_ERROR}
935 fi
936
937 # Save everything
938 if ! ipsec_connection_write_config_key "${connection}" "${_prefix}" ${!_prefix}; then
939 log ERROR "Could not write configuration settings"
940 fi
941
942 return ${EXIT_OK}
943 }
944
945 # Set the pools to use
946 ipsec_connection_pool() {
947 if [ ! $# -ge 2 ]; then
948 log ERROR "Not enough arguments"
949 return ${EXIT_ERROR}
950 fi
951 local connection=${1}
952 shift
953
954 local POOLS
955 if ! ipsec_connection_read_config "${connection}" "POOLS"; then
956 return ${EXIT_ERROR}
957 fi
958
959 # Remove duplicated entries to proceed the list safely
960 assign "POOLS" "$(list_unique ${POOLS})"
961
962 local pools_added
963 local pools_removed
964 local pools_set
965
966 while [ $# -gt 0 ]; do
967 local arg="${1}"
968
969 case "${arg}" in
970 +*)
971 list_append pools_added "${arg:1}"
972 ;;
973 -*)
974 list_append pools_removed "${arg:1}"
975 ;;
976 [A-Za-z0-9]*)
977 list_append pools_set "${arg}"
978 ;;
979 *)
980 error "Invalid argument: ${arg}"
981 return ${EXIT_ERROR}
982 ;;
983 esac
984 shift
985 done
986
987 # Check if the user is trying a mixed operation
988 if ! list_is_empty pools_set && (! list_is_empty pools_added || ! list_is_empty pools_removed); then
989 error "You cannot reset the pools list and add or remove pools at the same time"
990 return ${EXIT_ERROR}
991 fi
992
993 # Set new pools list
994 if ! list_is_empty pools_set; then
995 # Check if all pools are valid
996 local pool
997 for pool in ${pools_set}; do
998 if ! ipsec_pool_exists ${pool} || ! ipsec_pool_check_config ${pool}; then
999 error "Pool ${pool} is not valid"
1000 return ${EXIT_ERROR}
1001 fi
1002 done
1003
1004 assign "POOLS" "${pools_set}"
1005
1006 # Perform incremental updates
1007 else
1008 local pool
1009
1010 # Perform all removals
1011 for pool in ${pools_removed}; do
1012 if ! list_remove "POOLS" ${pool}; then
1013 warning "${pool} was not on the list and could not be removed"
1014 fi
1015 done
1016
1017
1018 for pool in ${pools_added}; do
1019 if ipsec_pool_exists ${pool} && ipsec_pool_check_config ${pool}; then
1020 if ! list_append_unique "POOLS" ${pool}; then
1021 warning "${pool} is already on the prefix list"
1022 fi
1023 else
1024 warning "${pool} is not a valid pool"
1025 fi
1026 done
1027 fi
1028
1029 # Check if the list contain at least one valid pool
1030 if list_is_empty POOLS; then
1031 error "Cannot save an empty pool list"
1032 return ${EXIT_ERROR}
1033 fi
1034
1035 # Save everything
1036 if ! ipsec_connection_write_config_key "${connection}" "POOLS" ${POOLS}; then
1037 log ERROR "Could not write configuration settings"
1038 fi
1039
1040 return ${EXIT_OK}
1041 }
1042
1043 # Handle the cli after remote
1044 ipsec_connection_remote() {
1045 if [ ! $# -ge 2 ]; then
1046 log ERROR "Not enough arguments"
1047 return ${EXIT_ERROR}
1048 fi
1049
1050 local connection=${1}
1051 local cmd=${2}
1052 shift 2
1053
1054 case ${cmd} in
1055 id)
1056 ipsec_connection_id "${connection}" "REMOTE" "$@"
1057 ;;
1058
1059 prefix)
1060 ipsec_connection_prefix "${connection}" "REMOTE" "$@"
1061 ;;
1062 *)
1063 log ERROR "Unrecognized argument: ${cmd}"
1064 return ${EXIT_ERROR}
1065 ;;
1066 esac
1067
1068 return ${EXIT_OK}
1069 }
1070
1071 # Set the inactivity timeout
1072 ipsec_connection_inactivity_timeout() {
1073 if [ ! $# -ge 2 ]; then
1074 log ERROR "Not enough arguments"
1075 return ${EXIT_ERROR}
1076 fi
1077
1078 local connection=${1}
1079 shift 1
1080 local value=$@
1081
1082 if ! isinteger value; then
1083 value=$(parse_time "$@")
1084 if [ ! $? -eq 0 ]; then
1085 log ERROR "Parsing the passed time was not sucessful please check the passed values."
1086 return ${EXIT_ERROR}
1087 fi
1088 fi
1089
1090 if [ ${value} -le 0 ]; then
1091 log ERROR "The passed time value must be in the sum greater zero seconds."
1092 return ${EXIT_ERROR}
1093 fi
1094
1095 if ! ipsec_connection_write_config_key "${connection}" "INACTIVITY_TIMEOUT" ${value}; then
1096 log ERROR "Could not write configuration settings"
1097 return ${EXIT_ERROR}
1098 fi
1099
1100 return ${EXIT_OK}
1101 }
1102
1103 # Set the default start action
1104 ipsec_connection_start_action() {
1105 if [ ! $# -eq 2 ]; then
1106 log ERROR "Not enough arguments"
1107 return ${EXIT_ERROR}
1108 fi
1109 local connection=${1}
1110 local action=${2}
1111
1112 if ! isoneof action "on-demand" "always-on"; then
1113 log ERROR "Start action '${action}' is invalid"
1114 return ${EXIT_ERROR}
1115 fi
1116
1117 if ! ipsec_connection_write_config_key "${connection}" "START_ACTION" ${action}; then
1118 log ERROR "Could not write configuration settings"
1119 return ${EXIT_ERROR}
1120 fi
1121 }
1122
1123 # Set the security policy to use
1124 ipsec_connection_security_policy() {
1125 if [ ! $# -eq 2 ]; then
1126 log ERROR "Not enough arguments"
1127 return ${EXIT_ERROR}
1128 fi
1129 local connection=${1}
1130 local security_policy=${2}
1131
1132 if ! vpn_security_policy_exists ${security_policy}; then
1133 log ERROR "No such vpn security policy '${security_policy}'"
1134 return ${EXIT_ERROR}
1135 fi
1136
1137 if ! ipsec_connection_write_config_key "${connection}" "SECURITY_POLICY" ${security_policy}; then
1138 log ERROR "Could not write configuration settings"
1139 return ${EXIT_ERROR}
1140 fi
1141 }
1142
1143 # Check if a id is valid
1144 ipsec_connection_check_id() {
1145 assert [ $# -eq 1 ]
1146 local id=${1}
1147
1148 if [[ ${id} =~ ^@[[:alnum:]]+$ ]] || ip_is_valid ${id}; then
1149 return ${EXIT_TRUE}
1150 else
1151 return ${EXIT_FALSE}
1152 fi
1153 }
1154
1155 # Checks if a peer is valid
1156 ipsec_connection_check_peer() {
1157 assert [ $# -eq 1 ]
1158 local peer=${1}
1159
1160 # IP addresses are accepted
1161 if ip_is_valid ${peer}; then
1162 return ${EXIT_TRUE}
1163 fi
1164
1165 # FQDNs are okay, too
1166 if fqdn_is_valid "${peer}"; then
1167 return ${EXIT_TRUE}
1168 fi
1169
1170 # We cannot use anything else
1171 return ${EXIT_FALSE}
1172 }
1173
1174 # This function checks if a VPN IPsec connection name is valid
1175 # Allowed are only A-Za-z0-9
1176 ipsec_connection_check_name() {
1177 assert [ $# -eq 1 ]
1178
1179 local connection=${1}
1180
1181 [[ "${connection}" =~ [^[:alnum:]$] ]]
1182 }
1183
1184 # Function that creates one VPN IPsec connection
1185 ipsec_connection_new() {
1186 if [ $# -gt 2 ]; then
1187 error "Too many arguments"
1188 return ${EXIT_ERROR}
1189 fi
1190
1191 local connection="${1}"
1192 local type="${2}"
1193
1194 if ! isset connection; then
1195 error "Please provide a connection name"
1196 return ${EXIT_ERROR}
1197 fi
1198
1199 # Check for duplicates
1200 if ipsec_connection_exists "${connection}"; then
1201 error "The VPN IPsec connection ${connection} already exists"
1202 return ${EXIT_ERROR}
1203 fi
1204
1205 # Check if the name of the connection is valid
1206 if ipsec_connection_check_name "${connection}"; then
1207 error "'${connection}' contains illegal characters"
1208 return ${EXIT_ERROR}
1209 fi
1210
1211 # Set TYPE to default if not set by the user
1212 if ! isset type; then
1213 type="${IPSEC_DEFAULT_TYPE}"
1214 fi
1215
1216 if ! isoneof "type" "net-to-net" "host-to-net"; then
1217 error "Type is invalid"
1218 return ${EXIT_ERROR}
1219 fi
1220
1221 log DEBUG "Creating VPN IPsec connection ${connection}"
1222
1223 if ! mkdir -p "${NETWORK_IPSEC_CONNS_DIR}/${connection}"; then
1224 log ERROR "Could not create config directory for ${connection}"
1225 return ${EXIT_ERROR}
1226 fi
1227
1228 local ${IPSEC_CONNECTION_CONFIG_SETTINGS}
1229
1230 AUTH_MODE=${IPSEC_DEFAULT_AUTH_MODE}
1231 DPD_ACTION=${IPSEC_DEFAULT_DPD_ACTION}
1232 DPD_DELAY=${IPSEC_DEFAULT_DPD_DELAY}
1233 DPD_TIMEOUT=${IPSEC_DEFAULT_DPD_TIMEOUT}
1234 ENABLED=${IPSEC_DEFAULT_ENABLED}
1235 MODE=${IPSEC_DEFAULT_MODE}
1236 START_ACTION=${IPSEC_DEFAULT_START_ACTION}
1237 TYPE="${type}"
1238
1239 INACTIVITY_TIMEOUT=${IPSEC_DEFAULT_INACTIVITY_TIMEOUT}
1240 SECURITY_POLICY=${IPSEC_DEFAULT_SECURITY_POLICY}
1241
1242 if ! ipsec_connection_write_config "${connection}"; then
1243 log ERROR "Could not write new config file"
1244 return ${EXIT_ERROR}
1245 fi
1246
1247 # Configure strongswan autostart
1248 ipsec_strongswan_autostart
1249 }
1250
1251 # Function that deletes based on the passed parameters one ore more vpn security policies
1252 ipsec_connection_destroy() {
1253 local connection
1254 for connection in "$@"; do
1255 if ! ipsec_connection_exists "${connection}"; then
1256 log ERROR "The VPN IPsec connection ${connection} does not exist."
1257 continue
1258 fi
1259
1260 log DEBUG "Deleting VPN IPsec connection ${connection}"
1261
1262 # Delete strongswan configuration file
1263 file_delete "${NETWORK_IPSEC_SWANCTL_CONNECTIONS_DIR}/${connection}.conf"
1264
1265 if ! rm -rf "${NETWORK_IPSEC_CONNS_DIR}/${connection}"; then
1266 log ERROR "Deleting the VPN IPsec connection ${connection} was not sucessful"
1267 return ${EXIT_ERROR}
1268 fi
1269
1270 done
1271 }
1272
1273 # List all ipsec connections
1274 ipsec_list_connections() {
1275 list_directory "${NETWORK_IPSEC_CONNS_DIR}"
1276 }
1277
1278 ipsec_connection_to_strongswan() {
1279 local connection="${1}"
1280 log DEBUG "Generating IPsec configuration for ${connection}"
1281
1282 # Read the config settings
1283 local ${IPSEC_CONNECTION_CONFIG_SETTINGS}
1284 if ! ipsec_connection_read_config "${connection}"; then
1285 error "Could not read the connection ${connection}"
1286 return ${EXIT_ERROR}
1287 fi
1288
1289 local path="${NETWORK_IPSEC_SWANCTL_CONNECTIONS_DIR}/${connection}.conf"
1290
1291 (
1292 # Write the connection section
1293 _ipsec_connection_to_strongswan_connection "${connection}"
1294
1295 # Write the secrets section
1296 _ipsec_connection_to_strongswan_secrets "${connection}"
1297
1298 ) > ${path}
1299 }
1300
1301 _ipsec_connection_to_strongswan_connection() {
1302 local connection="${1}"
1303
1304 # Read the security policy
1305 local ${VPN_SECURITY_POLICIES_CONFIG_SETTINGS}
1306 if ! vpn_security_policies_read_config "${SECURITY_POLICY}"; then
1307 return ${EXIT_ERROR}
1308 fi
1309
1310 # Is DPD enabled?
1311 local dpd="false"
1312 if isset DPD_DELAY && isinteger DPD_DELAY && [ ${DPD_DELAY} -gt 0 ]; then
1313 dpd="true"
1314 fi
1315
1316 # Write configuration header
1317 config_header "strongSwan configuration for ${connection}"
1318
1319 print_indent 0 "connections {"
1320 print_indent 1 "${connection} {"
1321
1322 # IKE Version
1323 print_indent 2 "# IKE Version"
1324 case "${KEY_EXCHANGE^^}" in
1325 IKEV1)
1326 print_indent 2 "version = 1"
1327 ;;
1328
1329 # Fall back to IKEv2 for any random values
1330 IKEV2|*)
1331 print_indent 2 "version = 2"
1332 ;;
1333 esac
1334 print # empty line
1335
1336 # Always only keep one connection open at a time
1337 print_indent 2 "# Unique IDs"
1338 print_indent 2 "unique = replace"
1339 print
1340
1341 # Local Address
1342 print_indent 2 "# Local Address"
1343 if isset LOCAL_ADDRESS; then
1344 print_indent 2 "local_addrs = ${LOCAL_ADDRESS}"
1345 else
1346 print_indent 2 "local_addrs = %any"
1347 fi
1348 print
1349
1350 # Remote Address
1351 print_indent 2 "# Remote Address"
1352 if isset PEER; then
1353 print_indent 2 "remote_addrs = ${PEER}"
1354 else
1355 print_indent 2 "remote_addrs = %any"
1356 fi
1357 print
1358
1359 # IKE Proposals
1360 print_indent 2 "# IKE Proposals"
1361 print_indent 2 "proposals = $(vpn_security_policies_make_ike_proposal ${SECURITY_POLICY})"
1362 print
1363
1364 # DPD Settings
1365 if enabled dpd; then
1366 print_indent 2 "# Dead Peer Detection"
1367 print_indent 2 "dpd_delay = ${DPD_DELAY}"
1368
1369 if isset DPD_TIMEOUT; then
1370 print_indent 2 "dpd_timeout = ${DPD_TIMEOUT}"
1371 fi
1372
1373 print
1374 fi
1375
1376 # Fragmentation
1377 print_indent 2 "# Fragmentation"
1378 print_indent 2 "fragmentation = yes"
1379 print
1380
1381
1382 # Host-to-Net specific settings
1383 case "${TYPE}" in
1384 host-to-net)
1385 # Pools
1386 if isset POOLS; then
1387 print_indent 2 "# Pools"
1388 print_indent 2 "pools = $(list_join POOLS ", ")"
1389 print
1390 fi
1391 ;;
1392 esac
1393
1394 # Local
1395 print_indent 2 "local {"
1396
1397 # Local ID
1398 if isset LOCAL_ID; then
1399 print_indent 3 "id = ${LOCAL_ID}"
1400 fi
1401
1402 # Authentication
1403 case "${AUTH_MODE}" in
1404 PSK)
1405 print_indent 3 "auth = psk"
1406 ;;
1407 esac
1408
1409 print_indent 2 "}"
1410 print
1411
1412 # Remote
1413 print_indent 2 "remote {"
1414
1415 # Remote ID
1416 if isset REMOTE_ID; then
1417 print_indent 3 "id = ${REMOTE_ID}"
1418 fi
1419
1420 # Authentication
1421 case "${AUTH_MODE}" in
1422 PSK)
1423 print_indent 3 "auth = psk"
1424 ;;
1425 esac
1426
1427 print_indent 2 "}"
1428 print
1429
1430 # Children
1431
1432 print_indent 2 "children {"
1433 print_indent 3 "${connection} {"
1434
1435 print_indent 4 "# ESP Proposals"
1436 print_indent 4 "esp_proposals = $(vpn_security_policies_make_esp_proposal ${SECURITY_POLICY})"
1437 print
1438
1439 # Traffic Selectors
1440
1441 case "${MODE}" in
1442 gre-*)
1443 print_indent 4 "local_ts = dynamic[gre]"
1444 print_indent 4 "remote_ts = dynamic[gre]"
1445 ;;
1446 *)
1447 # Local Prefixes
1448 if isset LOCAL_PREFIX; then
1449 print_indent 4 "local_ts = $(list_join LOCAL_PREFIX ,)"
1450 else
1451 print_indent 4 "local_ts = dynamic"
1452 fi
1453
1454 # Remote Prefixes
1455 if isset REMOTE_PREFIX; then
1456 print_indent 4 "remote_ts = $(list_join REMOTE_PREFIX ,)"
1457 else
1458 print_indent 4 "remote_ts = dynamic"
1459 fi
1460 ;;
1461 esac
1462 print
1463
1464 # Netfilter Marks
1465 case "${MODE}" in
1466 vti)
1467 print_indent 4 "# Netfilter Marks"
1468 print_indent 4 "mark_in = %unique"
1469 print_indent 4 "mark_out = %unique"
1470 print
1471 ;;
1472 esac
1473
1474 # Dead Peer Detection
1475 if enabled dpd; then
1476 print_indent 4 "# Dead Peer Detection"
1477 print_indent 4 "dpd_action = ${DPD_ACTION}"
1478 print
1479 fi
1480
1481 # Rekeying
1482 if isset LIFETIME; then
1483 print_indent 4 "# Rekey Time"
1484 print_indent 4 "rekey_time = ${LIFETIME}"
1485 print
1486 fi
1487
1488 # Updown Script
1489 print_indent 4 "updown = ${NETWORK_HELPERS_DIR}/ipsec-updown"
1490 print
1491
1492 # Mode
1493 print_indent 4 "# Mode"
1494 case "${MODE}" in
1495 gre-transport)
1496 print_indent 4 "mode = transport"
1497 ;;
1498 tunnel|vti|*)
1499 print_indent 4 "mode = tunnel"
1500 ;;
1501 esac
1502 print
1503
1504 # Compression
1505 print_indent 4 "# Compression"
1506 if enabled COMPRESSION; then
1507 print_indent 4 "ipcomp = yes"
1508 else
1509 print_indent 4 "ipcomp = no"
1510 fi
1511 print
1512
1513 # Inactivity Timeout
1514 if isset INACTIVITY_TIMEOUT; then
1515 print_indent 4 "# Inactivity Timeout"
1516 print_indent 4 "inactivity = ${INACTIVITY_TIMEOUT}"
1517 print
1518 fi
1519
1520 # Net-to-Net specific settings
1521 case "${TYPE}" in
1522 net-to-net)
1523 # Start Action
1524 print_indent 4 "# Start Action"
1525 case "${START_ACTION}" in
1526 on-demand)
1527 print_indent 4 "start_action = trap"
1528 print_indent 4 "close_action = trap"
1529 ;;
1530 wait)
1531 print_indent 4 "start_action = none"
1532 print_indent 4 "close_action = none"
1533 ;;
1534 always-on|*)
1535 print_indent 4 "start_action = start"
1536 print_indent 4 "close_action = start"
1537 ;;
1538 esac
1539 print
1540 ;;
1541 esac
1542
1543 print_indent 3 "}"
1544 print_indent 2 "}"
1545 print
1546
1547 print_indent 1 "}"
1548 print_indent 0 "}"
1549 print
1550 }
1551
1552 _ipsec_connection_to_strongswan_secrets() {
1553 local connection="${1}"
1554
1555 print_indent 0 "secrets {"
1556
1557 case "${AUTH_MODE}" in
1558 PSK)
1559 print_indent 1 "ike {"
1560
1561 # Secret
1562 print_indent 2 "secret = ${PSK}"
1563
1564 # ID
1565 if isset REMOTE_ID; then
1566 print_indent 2 "id = ${REMOTE_ID}"
1567 fi
1568
1569 print_indent 1 "}"
1570 ;;
1571 esac
1572
1573 print_indent 0 "}"
1574 }