Skip to content

Commit ec686a9

Browse files
committed
Adding interfaces - WIP
1 parent 6e2064a commit ec686a9

File tree

1 file changed

+170
-95
lines changed

1 file changed

+170
-95
lines changed

main.go

+170-95
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,16 @@ func (t *Tui) Init() {
4747
t.pages = tview.NewPages()
4848
}
4949

50+
func (t *Tui) LoadInterfaces() ([]string, error) {
51+
err, out, _ := shellout("ip link show | awk -F: '{ print $2 }' | sed -r 's/^[0-9]+//' | sed '/^$/d' | awk '{$2=$2};1'")
52+
if err != nil {
53+
log.Printf("error: %v\n", err)
54+
}
55+
return strings.Split(out, "\n"), nil
56+
}
57+
5058
func (t *Tui) LoadTableData() ([]string, error) {
51-
err, out, _ := shellout("ufw status | sed '/^$/d' | awk '{$2=$2};1' | tail -n +4 | sed -r 's/^(([0-9]{1,3}\\.){3}[0-9]{1,3})\\s(.*)(\\/[a-z]{3})/\\1\\4 \\3/;s/^(([0-9]*)\\/([a-z]{3}))/\\3 \\2/;s/(\\w)\\s(\\(v6\\))$/\\1\\2/;s/\\(v6\\)//'")
59+
err, out, _ := shellout("ufw status | sed '/^$/d' | awk '{$2=$2};1' | tail -n +4 | sed -r 's/(\\w)\\s(\\(v6\\))/\\1/;s/([A-Z]{2,})\\s([A-Z]{2,3})/\\1-\\2/;s/^(.*)\\s([A-Z]{2,}(-[A-Z]{2,3})?)\\s(.*)(\\/[a-z]{3})/\\1\\5 \\2\\3 \\4/;s/^(.*)\\s([A-Z]{2,}(-[A-Z]{2,3})?)\\s(.*)\\s(on)\\s(.*)#?/\\1_\\5_\\6 - \\2 \\4/;s/^(.*)\\s(([0-9]+)\\/([a-z]{3}))/\\1\\/\\4 \\3/;s/(^[0-9]+)\\/([a-z]{3})/\\2 \\1/;s/(\\w+)\\s(on)\\s(\\w+)/\\1-\\2-\\3 -/;s/^([A-Z][a-z]+\\/[a-z]{3})\\s(([A-Z]+).*)/\\1 - \\2/'")
5260

5361
if err != nil {
5462
log.Printf("error: %v\n", err)
@@ -80,10 +88,12 @@ func (t *Tui) CreateTable(rows []string) {
8088

8189
value := ""
8290
switch {
91+
case len(cols) < len(columns) && c >= len(cols):
92+
value = ""
8393
case c >= 4:
84-
value = strings.Join(cols[c:], " ")
94+
value = strings.ReplaceAll(strings.Join(cols[c:], " "), "#", "")
8595
default:
86-
value = cols[c]
96+
value = strings.ReplaceAll(cols[c], "-", " ")
8797
}
8898

8999
t.table.SetCell(r+1, c+1, tview.NewTableCell(value).SetTextColor(tcell.ColorWhite).SetAlign(tview.AlignCenter).SetExpansion(1))
@@ -126,69 +136,30 @@ func (t *Tui) CreateModal(text string, confirm func(), cancel func(), finally fu
126136
}), true, true)
127137
}
128138

129-
func (t *Tui) CreateRule(position ...int) {
130-
to := t.form.GetFormItem(0).(*tview.InputField).GetText()
131-
port := t.form.GetFormItem(1).(*tview.InputField).GetText()
132-
_, proto := t.form.GetFormItem(2).(*tview.DropDown).GetCurrentOption()
133-
_, action := t.form.GetFormItem(3).(*tview.DropDown).GetCurrentOption()
134-
from := t.form.GetFormItem(4).(*tview.InputField).GetText()
135-
comment := t.form.GetFormItem(5).(*tview.InputField).GetText()
136-
137-
baseCmd := "ufw "
138-
if len(position) > 0 && position[0] < t.table.GetRowCount()-1 {
139-
baseCmd = fmt.Sprintf("ufw insert %d ", position[0])
140-
}
141-
142-
if port == "" {
143-
return
144-
}
145-
146-
cmd := ""
147-
if from == "" && to == "" {
148-
cmd = fmt.Sprintf("%s proto %s to any port %s comment '%s'", strings.ToLower(action), proto, port, comment)
149-
}
150-
151-
if from == "" && to != "" {
152-
cmd = fmt.Sprintf("%s proto %s to %s port %s comment '%s'", strings.ToLower(action), proto, to, port, comment)
153-
}
154-
155-
if to == "" && from != "" {
156-
cmd = fmt.Sprintf("%s from %s proto %s to any port %s comment '%s'", strings.ToLower(action), from, proto, port, comment)
157-
}
139+
func (t *Tui) CreateForm() {
140+
t.help.SetText("Use <Tab> and <Enter> keys to navigate through the form").SetBorderPadding(1, 0, 1, 1)
141+
interfaces, _ := t.LoadInterfaces()
158142

159-
if to != "" && from != "" {
160-
cmd = fmt.Sprintf("%s from %s proto %s to %s port %s comment '%s'", strings.ToLower(action), from, proto, to, port, comment)
161-
}
143+
t.form.AddInputField("To", "", 20, nil, nil).SetFieldTextColor(tcell.ColorWhite).
144+
AddInputField("Port", "", 20, validatePort, nil).SetFieldTextColor(tcell.ColorWhite).
145+
AddDropDown("Interface", interfaces, len(interfaces), nil).
146+
AddDropDown("Protocol", []string{"", "tcp", "udp"}, 0, nil).
147+
AddDropDown("Action *", []string{"ALLOW", "DENY", "REJECT", "LIMIT", "ALLOW OUT", "DENY OUT", "REJECT OUT", "LIMIT OUT"}, 0, nil).
148+
AddInputField("From", "", 20, nil, nil).
149+
AddInputField("Comment", "", 40, nil, nil).
150+
AddButton("Save", func() { t.CreateRule() }).
151+
AddButton("Cancel", t.Reset).
152+
SetButtonTextColor(tcell.ColorWhite).
153+
SetButtonBackgroundColor(tcell.ColorDarkCyan).
154+
SetFieldBackgroundColor(tcell.ColorDarkCyan).
155+
SetLabelColor(tcell.ColorWhite)
162156

163-
err, _, _ := shellout(baseCmd + cmd)
164-
if err != nil {
165-
log.Print(err)
166-
}
167-
t.Reset()
168-
t.ReloadTable()
157+
t.secondHelp.SetText("* Mandatory field\n\nTo and From fields match any and Anywhere if left empty").SetTextColor(tcell.ColorDarkCyan).SetBorderPadding(0, 0, 1, 1)
169158
}
170159

171-
func (t *Tui) RemoveRule() {
172-
t.table.SetSelectedFunc(func(row int, column int) {
173-
t.table.SetSelectable(false, false)
174-
if row == 0 {
175-
t.app.SetFocus(t.table)
176-
return
177-
}
178-
t.CreateModal("Are you sure you want to remove this rule?",
179-
func() {
180-
shellout(fmt.Sprintf("ufw --force delete %d", row))
181-
},
182-
func() {
183-
t.pages.HidePage("modal")
184-
t.app.SetFocus(t.table)
185-
},
186-
func() {
187-
t.pages.HidePage("modal")
188-
t.app.SetFocus(t.table)
189-
},
190-
)
191-
})
160+
func validatePort(text string, ch rune) bool {
161+
_, err := strconv.Atoi(text)
162+
return err == nil
192163
}
193164

194165
func (t *Tui) EditForm() {
@@ -198,6 +169,7 @@ func (t *Tui) EditForm() {
198169
return
199170
}
200171
t.help.SetText("Use <Tab> and <Enter> keys to navigate through the form").SetBorderPadding(1, 0, 1, 1)
172+
interfaces, _ := t.LoadInterfaces()
201173

202174
to := t.table.GetCell(row, 1).Text
203175
rip := regexp.MustCompile(`(([0-9]{1,3}\.){3}[0-9]{1,3})(/[0-9]{1,2})?`)
@@ -220,8 +192,15 @@ func (t *Tui) EditForm() {
220192
matchPort := rport.FindStringSubmatch(port)
221193
portValue = matchPort[1]
222194

223-
protocolOptionIndex := 1
224-
if proto == "tcp" {
195+
interfaceOptionIndex := len(interfaces)
196+
197+
protocolOptionIndex := 0
198+
switch proto {
199+
case "tcp":
200+
protocolOptionIndex = 1
201+
case "udp":
202+
protocolOptionIndex = 2
203+
default:
225204
protocolOptionIndex = 0
226205
}
227206

@@ -235,6 +214,14 @@ func (t *Tui) EditForm() {
235214
actionOptionIndex = 2
236215
case "LIMIT":
237216
actionOptionIndex = 3
217+
case "ALLOW-OUT":
218+
actionOptionIndex = 4
219+
case "DENY-OUT":
220+
actionOptionIndex = 5
221+
case "REJECT-OUT":
222+
actionOptionIndex = 6
223+
case "LIMIT-OUT":
224+
actionOptionIndex = 7
238225
}
239226

240227
from := t.table.GetCell(row, 4).Text
@@ -245,20 +232,25 @@ func (t *Tui) EditForm() {
245232
comment := strings.ReplaceAll(t.table.GetCell(row, 5).Text, "# ", "")
246233

247234
t.form.AddInputField("To", toValue, 20, nil, nil).SetFieldTextColor(tcell.ColorWhite).
248-
AddInputField("Port *", portValue, 20, validatePort, nil).SetFieldTextColor(tcell.ColorWhite).
249-
AddDropDown("Protocol *", []string{"tcp", "udp"}, protocolOptionIndex, nil).
250-
AddDropDown("Action *", []string{"ALLOW", "DENY", "REJECT", "LIMIT"}, actionOptionIndex, nil).
235+
AddInputField("Port", portValue, 20, validatePort, nil).SetFieldTextColor(tcell.ColorWhite).
236+
AddDropDown("Interface", interfaces, interfaceOptionIndex, nil).
237+
AddDropDown("Protocol", []string{"", "tcp", "udp"}, protocolOptionIndex, nil).
238+
AddDropDown("Action *", []string{"ALLOW", "DENY", "REJECT", "LIMIT", "ALLOW OUT", "DENY OUT", "REJECT OUT", "LIMIT OUT"}, actionOptionIndex, nil).
251239
AddInputField("From", fromValue, 20, nil, nil).
252240
AddInputField("Comment", comment, 40, nil, nil).
253-
AddButton("Save", func() { t.CreateRule(row); t.table.SetSelectable(false, false) }).
254-
AddButton("Cancel", func() { t.CreateRule(row); t.Reset(); t.table.SetSelectable(false, false) }).
241+
AddButton("Save", func() {
242+
t.CreateRule(row)
243+
t.table.SetSelectable(false, false)
244+
}).
245+
AddButton("Cancel", func() {
246+
t.Reset()
247+
t.table.SetSelectable(false, false)
248+
}).
255249
SetButtonTextColor(tcell.ColorWhite).
256250
SetButtonBackgroundColor(tcell.ColorDarkCyan).
257251
SetFieldBackgroundColor(tcell.ColorDarkCyan).
258252
SetLabelColor(tcell.ColorWhite)
259253

260-
shellout(fmt.Sprintf("ufw --force delete %d", row))
261-
262254
t.secondHelp.SetText("* Mandatory field\n\nTo and From fields match any and Anywhere if left empty").
263255
SetTextColor(tcell.ColorDarkCyan).
264256
SetBorderPadding(0, 0, 1, 1)
@@ -267,6 +259,113 @@ func (t *Tui) EditForm() {
267259
})
268260
}
269261

262+
func (t *Tui) CreateRule(position ...int) {
263+
to := t.form.GetFormItem(0).(*tview.InputField).GetText()
264+
port := t.form.GetFormItem(1).(*tview.InputField).GetText()
265+
_, ninterface := t.form.GetFormItem(2).(*tview.DropDown).GetCurrentOption()
266+
_, proto := t.form.GetFormItem(3).(*tview.DropDown).GetCurrentOption()
267+
_, action := t.form.GetFormItem(4).(*tview.DropDown).GetCurrentOption()
268+
from := t.form.GetFormItem(5).(*tview.InputField).GetText()
269+
comment := t.form.GetFormItem(6).(*tview.InputField).GetText()
270+
271+
dryCmd := "ufw --dry-run "
272+
baseCmd := "ufw "
273+
if len(position) > 0 && position[0] < t.table.GetRowCount()-1 {
274+
dryCmd = fmt.Sprintf("ufw --dry-run insert %d ", position[0])
275+
baseCmd = fmt.Sprintf("ufw insert %d ", position[0])
276+
}
277+
278+
if port != "" && ninterface != "" {
279+
return
280+
}
281+
282+
if proto != "" && (from == "" || to == "") {
283+
return
284+
}
285+
286+
if port != "" && (from == "" || to == "") {
287+
return
288+
}
289+
290+
if (proto == "" || port == "") && from == "" && to == "" && ninterface == "" {
291+
return
292+
}
293+
294+
toValue := to
295+
if to == "" {
296+
toValue = "any"
297+
}
298+
fromValue := from
299+
if from == "" {
300+
fromValue = "any"
301+
}
302+
303+
cmd := ""
304+
preCmd := fmt.Sprintf("%s ", strings.ToLower(action))
305+
if ninterface != "" {
306+
preCmd = fmt.Sprintf("%s on %s ", strings.ToLower(action), ninterface)
307+
}
308+
309+
if port != "" && proto == "" {
310+
cmd = fmt.Sprintf("%s to %s port %s comment '%s'", fromValue, toValue, port, comment)
311+
}
312+
if port == "" && proto != "" {
313+
cmd = fmt.Sprintf("%s proto %s to %s comment '%s'", fromValue, proto, toValue, comment)
314+
}
315+
if port != "" && proto != "" {
316+
cmd = fmt.Sprintf("%s proto %s to %s port %s comment '%s'", fromValue, proto, toValue, port, comment)
317+
}
318+
if port == "" && proto == "" {
319+
cmd = fmt.Sprintf("%s to %s comment '%s'", fromValue, toValue, comment)
320+
}
321+
if ninterface != "" && (to == "" || from == "") {
322+
cmd = fmt.Sprintf("comment '%s'", comment)
323+
}
324+
325+
// Dry-run
326+
err, _, _ := shellout(dryCmd + preCmd + cmd)
327+
if err == nil {
328+
// Delete first
329+
if len(position) > 0 {
330+
shellout(fmt.Sprintf("ufw --force delete %d", position[0]))
331+
}
332+
333+
// Then create
334+
err, _, _ = shellout(baseCmd + preCmd + cmd)
335+
if err != nil {
336+
log.Print(err)
337+
}
338+
}
339+
if err != nil {
340+
return
341+
}
342+
t.Reset()
343+
t.ReloadTable()
344+
}
345+
346+
func (t *Tui) RemoveRule() {
347+
t.table.SetSelectedFunc(func(row int, column int) {
348+
t.table.SetSelectable(false, false)
349+
if row == 0 {
350+
t.app.SetFocus(t.table)
351+
return
352+
}
353+
t.CreateModal("Are you sure you want to remove this rule?",
354+
func() {
355+
shellout(fmt.Sprintf("ufw --force delete %d", row))
356+
},
357+
func() {
358+
t.pages.HidePage("modal")
359+
t.app.SetFocus(t.table)
360+
},
361+
func() {
362+
t.pages.HidePage("modal")
363+
t.app.SetFocus(t.table)
364+
},
365+
)
366+
})
367+
}
368+
270369
func (t *Tui) CreateMenu() {
271370
menuList := tview.NewList()
272371
menuList.
@@ -319,30 +418,6 @@ func (t *Tui) CreateMenu() {
319418
t.menu.SetBorder(true).SetTitle(" Menu ")
320419
}
321420

322-
func (t *Tui) CreateForm() {
323-
t.help.SetText("Use <Tab> and <Enter> keys to navigate through the form").SetBorderPadding(1, 0, 1, 1)
324-
325-
t.form.AddInputField("To", "", 20, nil, nil).SetFieldTextColor(tcell.ColorWhite).
326-
AddInputField("Port *", "", 20, validatePort, nil).SetFieldTextColor(tcell.ColorWhite).
327-
AddDropDown("Protocol *", []string{"tcp", "udp"}, 0, nil).
328-
AddDropDown("Action *", []string{"ALLOW", "DENY", "REJECT", "LIMIT"}, 0, nil).
329-
AddInputField("From", "", 20, nil, nil).
330-
AddInputField("Comment", "", 40, nil, nil).
331-
AddButton("Save", func() { t.CreateRule() }).
332-
AddButton("Cancel", t.Reset).
333-
SetButtonTextColor(tcell.ColorWhite).
334-
SetButtonBackgroundColor(tcell.ColorDarkCyan).
335-
SetFieldBackgroundColor(tcell.ColorDarkCyan).
336-
SetLabelColor(tcell.ColorWhite)
337-
338-
t.secondHelp.SetText("* Mandatory field\n\nTo and From fields match any and Anywhere if left empty").SetTextColor(tcell.ColorDarkCyan).SetBorderPadding(0, 0, 1, 1)
339-
}
340-
341-
func validatePort(text string, ch rune) bool {
342-
_, err := strconv.Atoi(text)
343-
return err == nil
344-
}
345-
346421
func (t *Tui) Reset() {
347422
t.pages.HidePage("form")
348423
t.form.Clear(true)

0 commit comments

Comments
 (0)