core-extra/gui/addons/ipsecservice.tcl

329 lines
11 KiB
Tcl

#
# This is a separate "addons" file because it is closely tied to Python
# service definition for the IPsec service.
#
#
# Helper dialog for configuring the IPsec service
#
proc popupServiceConfig_IPsec { parent w node service btn } {
global plugin_img_add plugin_img_del plugin_img_edit
set f $w.note.ipsec
ttk::frame $f
set h "This IPsec service helper will assist with building an ipsec.sh file"
set h "$h (located on the Files tab).\nThe IPsec service builds ESP"
set h "$h tunnels between the specified peers using the racoon IKEv2"
set h "$h\nkeying daemon. You need to provide keys and the addresses of"
set h "$h peers, along with the\nsubnets to tunnel."
ttk::label $f.help -text $h
pack $f.help -side top -anchor w -padx 4 -pady 4
$w.note add $f -text "IPsec" -underline 0
global g_ipsec_key_dir g_ipsec_key_name
set g_ipsec_key_dir "/etc/core/keys"
set g_ipsec_key_name "ipsec1"
ttk::labelframe $f.keys -text "Keys"
ttk::frame $f.keys.dir
ttk::label $f.keys.dir.lab -text "Key directory:"
ttk::entry $f.keys.dir.ent -width 40 -textvariable g_ipsec_key_dir
ttk::button $f.keys.dir.btn -width 5 -text "..." -command {
set f .popupServicesCustomize.note.ipsec
set d [$f.keys.dir.ent get]
set d [tk_chooseDirectory -initialdir $d -title "Key directory"]
if { $d != "" } {
$f.keys.dir.ent delete 0 end
$f.keys.dir.ent insert 0 $d
}
}
pack $f.keys.dir.lab $f.keys.dir.ent $f.keys.dir.btn \
-side left -padx 4 -pady 4
pack $f.keys.dir -side top -anchor w
ttk::frame $f.keys.name
ttk::label $f.keys.name.lab -text "Key base name:"
ttk::entry $f.keys.name.ent -width 10 -textvariable g_ipsec_key_name
pack $f.keys.name.lab $f.keys.name.ent -side left -padx 4 -pady 4
pack $f.keys.name -side top -anchor w
set h "The (name).pem x509 certificate and (name).key RSA private key need"
set h "$h to exist in the\nspecified directory. These can be generated"
set h "$h using the openssl tool. Also, a ca-cert.pem\nfile should exist"
set h "$h in the key directory for the CA that issued the certs."
ttk::label $f.keys.help -text $h
pack $f.keys.help -side top -anchor w -padx 4 -pady 4
pack $f.keys -side top -pady 4 -pady 4 -expand true -fill x
ttk::labelframe $f.t -text "IPsec Tunnel Endpoints"
set h "(1) Define tunnel endpoints (select peer node using the button"
set h "$h, then select address from the list)"
ttk::label $f.t.lab1 -text $h
pack $f.t.lab1 -side top -anchor w -padx 4 -pady 4
ttk::frame $f.t.ep
ttk::label $f.t.ep.lab1 -text "Local:"
ttk::combobox $f.t.ep.combo1 -width 12
pack $f.t.ep.lab1 $f.t.ep.combo1 -side left -padx 4 -pady 4
populateComboWithIPs $f.t.ep.combo1 $node
global g_twoNodeSelect g_twoNodeSelectCallback
set g_twoNodeSelect ""
set g_twoNodeSelectCallback selectTwoNodeIPsecCallback
set h "Choose a node by clicking it on the canvas"
set h "$h or\nby selecting it from the list below."
ttk::label $f.t.ep.lab2 -text "Peer node:"
ttk::checkbutton $f.t.ep.node -text " (none) " -variable g_twoNodeSelect \
-onvalue "$f.t.ep.node" -style Toolbutton \
-command "popupSelectNodes {$h} {} selectNodesIPsecCallback"
ttk::label $f.t.ep.lab3 -text "Peer:"
ttk::combobox $f.t.ep.combo2 -width 12
ttk::button $f.t.ep.add -text "Add Endpoint" -image $plugin_img_add \
-compound left -command "ipsecTreeHelper $f ep"
pack $f.t.ep.lab2 $f.t.ep.node $f.t.ep.lab3 $f.t.ep.combo2 \
$f.t.ep.add -side left -padx 4 -pady 4
pack $f.t.ep -side top -anchor w
set h "(2) Select endpoints below and add the subnets to be encrypted"
ttk::label $f.t.lab2 -text $h
pack $f.t.lab2 -side top -anchor w -padx 4 -pady 4
ttk::frame $f.t.sub
ttk::label $f.t.sub.lab1 -text "Local subnet:"
ttk::combobox $f.t.sub.combo1 -width 12
ttk::label $f.t.sub.lab2 -text "Remote subnet:"
ttk::combobox $f.t.sub.combo2 -width 12
ttk::button $f.t.sub.add -text "Add Subnet" -image $plugin_img_add \
-compound left -command "ipsecTreeHelper $f sub"
pack $f.t.sub.lab1 $f.t.sub.combo1 $f.t.sub.lab2 $f.t.sub.combo2 \
$f.t.sub.add -side left -padx 5 -pady 4
pack $f.t.sub -side top -anchor w
global node_list
set net_list [ipv4SubnetList $node_list]
$f.t.sub.combo1 configure -values $net_list
$f.t.sub.combo2 configure -values $net_list
ttk::treeview $f.t.tree -height 5 -selectmode browse -show tree
pack $f.t.tree -side top -padx 4 -pady 4 -fill both
pack $f.t -side top -expand true -fill both
ttk::frame $f.bottom
ttk::button $f.bottom.del -image $plugin_img_del \
-command "ipsecTreeHelper $f del"
ttk::button $f.bottom.gen -text "Generate ipsec.sh" \
-image $plugin_img_edit -compound left -command "generateIPsecScript $w"
pack $f.bottom.del $f.bottom.gen -side left -padx 4 -pady 4
pack $f.bottom -side top
}
#
# Callback invoked when receiving configuration values
# from a Configuration Message; this service helper depends on the ipsec.sh
# file, not the other configuration values.
#
#proc popupServiceConfig_IPsec_vals { node values services w } {
#}
#
# Callback invoked when receiving service file data from a File Message
proc popupServiceConfig_IPsec_file { node name data w } {
if { $name == "ipsec.sh" } {
readIPsecScript $w
}
}
# helper to insert all of a node's IP addresses into a combo
proc populateComboWithIPs { combo node } {
set ip_list [ipv4List $node 0]
$combo configure -values $ip_list
$combo delete 0 end
$combo insert 0 [lindex $ip_list 0]
}
# called from editor.tcl:button1 when user clicks on a node
# search for IP address and populate
proc selectTwoNodeIPsecCallback {} {
set w .popupServicesCustomize
set f $w.note.ipsec
if { ![winfo exists $w] } { return }; # user has closed window
catch {destroy .nodeselect}
set node [string trim [$f.t.ep.node cget -text]]
if { [set node] == "(none)" } { set node "" }
# populate peer interface combo with list of IPs
populateComboWithIPs $f.t.ep.combo2 $node
}
# called from popupSelectNodes dialog when a node selection has been made
proc selectNodesIPsecCallback { nodes } {
global g_twoNodeSelect
set w .popupServicesCustomize
set f $w.note.ipsec
set g_twoNodeSelect ""
set node [lindex $nodes 0]
if { $node == "" } {
$f.t.ep.node configure -text "(none)"
return
}
$f.t.ep.node configure -text " $node "
# populate peer interface combo with list of IPs
populateComboWithIPs $f.t.ep.combo2 $node
}
# helper to manipulate tree; cmd is "del", "ep" or "sub"
proc ipsecTreeHelper { f cmd } {
if { $cmd == "del" } {
set sel [$f.t.tree selection]
$f.t.tree delete $sel
return
}
# add endpoint (ep) or subnet (sub)
set l [string trim [$f.t.$cmd.combo1 get]]
set p [string trim [$f.t.$cmd.combo2 get]]
if { $l == "" || $p == "" } {
if { $cmd == "ep" } {
set h "tunnel interface addresses"
} else {
set h "subnet addresses"
}
tk_messageBox -type ok -icon warning -message \
"You need to select local and peer $h."
return
}
if { $cmd == "ep" } {
set item [$f.t.tree insert {} end -text "$l <--> $p" -open true]
$f.t.tree selection set $item
} elseif { $cmd == "sub" } {
set parent [$f.t.tree selection]
if { $parent == "" } {
tk_messageBox -type ok -icon warning -message \
"You need to first select endpoints, then configure their subnets."
return
}
if { [$f.t.tree parent $parent] != {} } {
set parent [$f.t.tree parent $parent]
}
$f.t.tree insert $parent end -text "$l <===> $p"
}
}
# update an ipsec.sh file that was generated by the IPsec service
proc generateIPsecScript { w } {
#puts "generateIPsecScript $w..."
set cfg [$w.note.files.txt get 0.0 end-1c]
set newcfg ""
#
# Gather data for a new config
#
set f $w.note.ipsec
set keydir [$f.keys.dir.ent get]
set keyname [$f.keys.name.ent get]
set tunnelhosts ""
set subnet_list ""
set ti 0
set th_items [$f.t.tree children {}]
foreach th $th_items {
set ep [$f.t.tree item $th -text]
set i [string first " " $ep]
# replace " <--> " with "AND"
set ep [string replace $ep $i $i+5 "AND"]
# build a list e.g.:
# tunnelhosts="172.16.0.1AND172.16.0.2 172.16.0.1AND172.16.2.1"
lappend tunnelhosts $ep
set subnets ""
foreach subnet_item [$f.t.tree children $th] {
set sn [$f.t.tree item $subnet_item -text]
set i [string first " " $sn]
# replace " <===> " with "AND"
set sn [string replace $sn $i $i+6 "AND"]
lappend subnets $sn
}
incr ti
set subnetstxt [join $subnets " "]
# build a list e.g.:
# T2="172.16.4.0/24AND172.16.5.0/24 172.16.4.0/24AND172.16.6.0/24"
set subnets "T$ti=\"$subnetstxt\""
lappend subnet_list $subnets
}
#
# Perform replacements in existing ipsec.sh file.
#
set have_subnets 0
foreach line [split $cfg "\n"] {
if { [string range $line 0 6] == "keydir=" } {
set line "keydir=$keydir"
} elseif { [string range $line 0 8] == "certname=" } {
set line "certname=$keyname"
} elseif { [string range $line 0 11] == "tunnelhosts=" } {
set tunnelhosts [join $tunnelhosts " "]
set line "tunnelhosts=\"$tunnelhosts\""
} elseif { [string range $line 0 0] == "T" && \
[string is digit [string range $line 1 1]] } {
if { $have_subnets } {
continue ;# skip this line
} else {
set line [join $subnet_list "\n"]
set have_subnets 1
}
}
lappend newcfg $line
}
$w.note.files.txt delete 0.0 end
$w.note.files.txt insert 0.0 [join $newcfg "\n"]
$w.note select $w.note.files
$w.btn.apply configure -state normal
}
proc readIPsecScript { w } {
set cfg [$w.note.files.txt get 0.0 end-1c]
set f $w.note.ipsec
$f.keys.dir.ent delete 0 end
$f.keys.name.ent delete 0 end
$f.t.tree delete [$f.t.tree children {}]
set ti 1
foreach line [split $cfg "\n"] {
if { [string range $line 0 6] == "keydir=" } {
$f.keys.dir.ent insert 0 [string range $line 7 end]
} elseif { [string range $line 0 8] == "certname=" } {
$f.keys.name.ent insert 0 [string range $line 9 end]
} elseif { [string range $line 0 11] == "tunnelhosts=" } {
set tunnelhosts [string range $line 13 end-1]
set ti 0
foreach ep [split $tunnelhosts " "] {
incr ti
set i [string first "AND" $ep]
set ep [string replace $ep $i $i+2 " <--> "]
$f.t.tree insert {} end -id "T$ti" -text "$ep" -open true
}
} elseif { [string range $line 0 0] == "T" && \
[string is digit [string range $line 1 1]] } {
set i [string first "=" $line]
set ti [string range $line 0 $i-1]
set subnets [split [string range $line $i+2 end-1] " "]
foreach sn $subnets {
set i [string first "AND" $sn]
set sn [string replace $sn $i $i+2 " <===> "]
if { [catch {$f.t.tree insert $ti end -text "$sn"} e] } {
puts "IPsec service ignoring line '$ti='"
}
}
}
}
}