Skip to content

Commit ef3e159

Browse files
committed
ufw status numbered version - to prevent from removing the wrong number
1 parent ffdaf0c commit ef3e159

File tree

1 file changed

+84
-50
lines changed

1 file changed

+84
-50
lines changed

main.go

+84-50
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ func (t *Tui) LoadInterfaces() ([]string, error) {
5656
}
5757

5858
func (t *Tui) LoadTableData() ([]string, error) {
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(.*)\\s(on)\\s(.*)#?/\\1_\\5_\\6 - \\2 \\4/;s/^([A-Z][a-z]+\\/[a-z]{3})\\s(([A-Z]+).*)/\\1 - \\2/;s/^([0-9]+)\\s([A-Z]{2,}(-[A-Z]{2,3})?)/- \\1 \\2/;s/^(.*)\\s([0-9]+)\\/([a-z]{3})/\\1\\/\\3 \\2/;s/(\\w+)\\s(on)\\s(\\w+)/\\1-\\2-\\3 -/;s/^([0-9]+)\\/([a-z]{3})/\\2 \\1/;s/^(([0-9]{1,3}\\.){3}[0-9]{1,3})\\s([A-Z]+)/\\1 - \\3/;s/^(\\w+)\\s([A-Z]+)/\\1 - \\2/'")
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(.*)\\s(on)\\s(.*)#?/\\1_\\5_\\6 - \\2 \\4/;s/^([A-Z][a-z]+\\/[a-z]{3})\\s(([A-Z]+).*)/\\1 - \\2/;s/^([0-9]+)\\s([A-Z]{2,}(-[A-Z]{2,3})?)/- \\1 \\2/;s/^(.*)\\s([0-9]+)\\/([a-z]{3})/\\1\\/\\3 \\2/;s/(\\w+)\\s(on)\\s(\\w+)/\\1-\\2-\\3 -/;s/^([0-9]+)\\/([a-z]{3})/\\2 \\1/;s/^(([0-9]{1,3}\\.){3}[0-9]{1,3})\\s([A-Z]+)/\\1 - \\3/;s/^(\\w+)\\s([A-Z]+)/\\1 - \\2/'")
60+
err, out, _ := shellout("ufw status numbered | sed '/^$/d' | awk '{$2=$2};1' | tail -n +4 | sed -r 's/(\\[(\\s)([0-9]+)\\])/\\[\\3\\] /;s/(\\[([0-9]+)\\])/\\[\\2\\] /;s/\\(out\\)//;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(.*)\\s(on)\\s(.*)#?/\\1_\\5_\\6- \\2 \\4/;s/([A-Z][a-z]+\\/[a-z]{3})\\s(([A-Z]+).*)/\\1 - \\2/;s/(\\]\\s+)([0-9]{2,})\\s([A-Z]{2,}(-[A-Z]{2,3})?)/\\1Anywhere \\2 \\3/;s/(\\]\\s+)(([0-9]{1,3}\\.){3}[0-9]{1,3}(\\/[0-9]{1,2})?)\\s([A-Z]{2,}-[A-Z]{2,3})/\\1\\2 - \\5/;s/([A-Z][a-z]+)\\s(([A-Z]+).*)/\\1 - \\2/;s/(\\]\\s+)(.*)\\s([0-9]+)(\\/[a-z]{3})/\\1\\2\\4 \\3/;s/(\\]\\s+)\\/([a-z]{3})\\s/\\1\\2 /;s/^(.*)\\s(on)\\s(.*)\\s([A-Z]{2,}(-[A-Z]{2,3})?)\\s(.*)/\\1_\\2_\\3 - \\4 \\6/'")
6061

6162
if err != nil {
6263
log.Printf("error: %v\n", err)
@@ -73,7 +74,7 @@ func (t *Tui) CreateTable(rows []string) {
7374

7475
for c := 0; c < len(columns); c++ {
7576
t.table.SetCell(0, c, tview.NewTableCell(columns[c]).SetTextColor(tcell.ColorDarkCyan).SetAlign(tview.AlignCenter))
76-
if c >= len(columns)-1 {
77+
if c >= len(columns) {
7778
break
7879
}
7980

@@ -82,21 +83,20 @@ func (t *Tui) CreateTable(rows []string) {
8283
break
8384
}
8485

85-
t.table.SetCell(r+1, 0, tview.NewTableCell(fmt.Sprintf("[%d]", r+1)).SetTextColor(tcell.ColorDarkCyan).SetAlign(tview.AlignCenter).SetExpansion(1))
86-
8786
cols := strings.Fields(row)
88-
87+
alignment := tview.AlignCenter
8988
value := ""
9089
switch {
9190
case len(cols) < len(columns) && c >= len(cols):
9291
value = ""
93-
case c >= 4:
92+
case c >= 5:
9493
value = strings.ReplaceAll(strings.Join(cols[c:], " "), "#", "")
94+
alignment = tview.AlignLeft
9595
default:
9696
value = strings.ReplaceAll(cols[c], "_", " ")
9797
}
9898

99-
t.table.SetCell(r+1, c+1, tview.NewTableCell(value).SetTextColor(tcell.ColorWhite).SetAlign(tview.AlignCenter).SetExpansion(1))
99+
t.table.SetCell(r+1, c, tview.NewTableCell(value).SetTextColor(tcell.ColorWhite).SetAlign(alignment).SetExpansion(1))
100100
}
101101
}
102102

@@ -144,7 +144,7 @@ func (t *Tui) CreateForm() {
144144
AddInputField("Port", "", 20, validatePort, nil).SetFieldTextColor(tcell.ColorWhite).
145145
AddDropDown("Interface", interfaces, len(interfaces), nil).
146146
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).
147+
AddDropDown("Action *", []string{"ALLOW IN", "DENY IN", "REJECT IN", "LIMIT IN", "ALLOW OUT", "DENY OUT", "REJECT OUT", "LIMIT OUT"}, 0, nil).
148148
AddInputField("From", "", 20, nil, nil).
149149
AddInputField("Comment", "", 40, nil, nil).
150150
AddButton("Save", func() { t.CreateRule() }).
@@ -154,14 +154,67 @@ func (t *Tui) CreateForm() {
154154
SetFieldBackgroundColor(tcell.ColorDarkCyan).
155155
SetLabelColor(tcell.ColorWhite)
156156

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)
157+
t.secondHelp.SetText("* Mandatory field\n\nPort, To and From fields respectively match any and Anywhere if left empty").SetTextColor(tcell.ColorDarkCyan).SetBorderPadding(0, 0, 1, 1)
158158
}
159159

160160
func validatePort(text string, ch rune) bool {
161161
_, err := strconv.Atoi(text)
162162
return err == nil
163163
}
164164

165+
func parseIPAddress(input string) string {
166+
r := regexp.MustCompile(`(([0-9]{1,3}\.){3}[0-9]{1,3})(/[0-9]{1,2})?`)
167+
matches := r.FindStringSubmatch(input)
168+
value := ""
169+
if len(matches) > 0 {
170+
value = matches[0]
171+
}
172+
return value
173+
}
174+
175+
func parseProtocol(inputs ...string) string {
176+
r := regexp.MustCompile(`/?(tcp|udp)`)
177+
value := ""
178+
for _, input := range inputs {
179+
matches := r.FindStringSubmatch(input)
180+
if len(matches) > 1 {
181+
return matches[1]
182+
}
183+
}
184+
185+
return value
186+
}
187+
188+
func parsePort(input string) string {
189+
r := regexp.MustCompile(`([0-9]*)(/[a-z]{3})?`)
190+
value := ""
191+
matches := r.FindStringSubmatch(input)
192+
if len(matches) > 0 {
193+
value = matches[1]
194+
}
195+
196+
return value
197+
}
198+
199+
func parseInterfaceIndex(input string, interfaces []string) int {
200+
r := regexp.MustCompile(`.* on (.+)`)
201+
matches := r.FindStringSubmatch(input)
202+
index := len(interfaces)
203+
204+
if len(matches) == 0 {
205+
return index
206+
}
207+
208+
for i, interfaceValue := range interfaces {
209+
if matches[1] == interfaceValue {
210+
fmt.Println(i)
211+
return i
212+
}
213+
}
214+
215+
return index
216+
}
217+
165218
func (t *Tui) EditForm() {
166219
t.table.SetSelectedFunc(func(row int, column int) {
167220
if row == 0 {
@@ -172,28 +225,12 @@ func (t *Tui) EditForm() {
172225
interfaces, _ := t.LoadInterfaces()
173226

174227
to := t.table.GetCell(row, 1).Text
175-
rip := regexp.MustCompile(`(([0-9]{1,3}\.){3}[0-9]{1,3})(/[0-9]{1,2})?`)
176-
rproto := regexp.MustCompile(`/?([a-z]{3})`)
177-
matchIP := rip.FindStringSubmatch(to)
178-
matchProto := rproto.FindStringSubmatch(to)
179-
180-
toValue := ""
181-
proto := ""
182-
if len(matchIP) > 0 {
183-
toValue = matchIP[0]
184-
}
185-
if len(matchProto) > 1 {
186-
proto = matchProto[1]
187-
}
188-
189-
portValue := ""
190-
port := t.table.GetCell(row, 2).Text
191-
rport := regexp.MustCompile(`([0-9]*)(/[a-z]{3})?`)
192-
matchPort := rport.FindStringSubmatch(port)
193-
portValue = matchPort[1]
228+
from := t.table.GetCell(row, 4).Text
194229

195-
interfaceOptionIndex := len(interfaces)
230+
toValue := parseIPAddress(to)
231+
fromValue := parseIPAddress(from)
196232

233+
proto := parseProtocol(to, from)
197234
protocolOptionIndex := 0
198235
switch proto {
199236
case "tcp":
@@ -204,15 +241,19 @@ func (t *Tui) EditForm() {
204241
protocolOptionIndex = 0
205242
}
206243

244+
portValue := parsePort(t.table.GetCell(row, 2).Text)
245+
246+
interfaceOptionIndex := parseInterfaceIndex(to, interfaces)
247+
207248
actionOptionIndex := 0
208249
switch t.table.GetCell(row, 3).Text {
209-
case "ALLOW":
250+
case "ALLOW-IN":
210251
actionOptionIndex = 0
211-
case "DENY":
252+
case "DENY-IN":
212253
actionOptionIndex = 1
213-
case "REJECT":
254+
case "REJECT-IN":
214255
actionOptionIndex = 2
215-
case "LIMIT":
256+
case "LIMIT-IN":
216257
actionOptionIndex = 3
217258
case "ALLOW-OUT":
218259
actionOptionIndex = 4
@@ -224,18 +265,13 @@ func (t *Tui) EditForm() {
224265
actionOptionIndex = 7
225266
}
226267

227-
from := t.table.GetCell(row, 4).Text
228-
fromValue := from
229-
if t.table.GetCell(row, 4).Text == "Anywhere" {
230-
fromValue = ""
231-
}
232268
comment := strings.ReplaceAll(t.table.GetCell(row, 5).Text, "# ", "")
233269

234270
t.form.AddInputField("To", toValue, 20, nil, nil).SetFieldTextColor(tcell.ColorWhite).
235271
AddInputField("Port", portValue, 20, validatePort, nil).SetFieldTextColor(tcell.ColorWhite).
236272
AddDropDown("Interface", interfaces, interfaceOptionIndex, nil).
237273
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).
274+
AddDropDown("Action *", []string{"ALLOW IN", "DENY IN", "REJECT IN", "LIMIT IN", "ALLOW OUT", "DENY OUT", "REJECT OUT", "LIMIT OUT"}, actionOptionIndex, nil).
239275
AddInputField("From", fromValue, 20, nil, nil).
240276
AddInputField("Comment", comment, 40, nil, nil).
241277
AddButton("Save", func() {
@@ -251,7 +287,7 @@ func (t *Tui) EditForm() {
251287
SetFieldBackgroundColor(tcell.ColorDarkCyan).
252288
SetLabelColor(tcell.ColorWhite)
253289

254-
t.secondHelp.SetText("* Mandatory field\n\nTo and From fields match any and Anywhere if left empty").
290+
t.secondHelp.SetText("* Mandatory field\n\nPort, To and From fields respectively match any and Anywhere if left empty").
255291
SetTextColor(tcell.ColorDarkCyan).
256292
SetBorderPadding(0, 0, 1, 1)
257293

@@ -279,15 +315,11 @@ func (t *Tui) CreateRule(position ...int) {
279315
return
280316
}
281317

282-
if proto != "" && (from == "" || to == "") {
318+
if port == "" && proto == "" && ninterface == "" {
283319
return
284320
}
285321

286-
if port != "" && (from == "" || to == "") {
287-
return
288-
}
289-
290-
if (proto == "" || port == "") && from == "" && to == "" && ninterface == "" {
322+
if ninterface != "" && proto != "" {
291323
return
292324
}
293325

@@ -301,9 +333,9 @@ func (t *Tui) CreateRule(position ...int) {
301333
}
302334

303335
cmd := ""
304-
preCmd := fmt.Sprintf("%s ", strings.ToLower(action))
336+
preCmd := fmt.Sprintf("%s from ", strings.ToLower(action))
305337
if ninterface != "" {
306-
preCmd = fmt.Sprintf("%s on %s ", strings.ToLower(action), ninterface)
338+
preCmd = fmt.Sprintf("%s on %s from ", strings.ToLower(action), ninterface)
307339
}
308340

309341
if port != "" && proto == "" {
@@ -318,11 +350,12 @@ func (t *Tui) CreateRule(position ...int) {
318350
if port == "" && proto == "" {
319351
cmd = fmt.Sprintf("%s to %s comment '%s'", fromValue, toValue, comment)
320352
}
321-
if ninterface != "" && (to == "" || from == "") {
322-
cmd = fmt.Sprintf("comment '%s'", comment)
353+
if ninterface != "" && to == "" {
354+
cmd = fmt.Sprintf("%s comment '%s'", fromValue, comment)
323355
}
324356

325357
// Dry-run
358+
//fmt.Println(dryCmd + preCmd + cmd) // debugging
326359
err, _, _ := shellout(dryCmd + preCmd + cmd)
327360
if err == nil {
328361
// Delete first
@@ -337,6 +370,7 @@ func (t *Tui) CreateRule(position ...int) {
337370
}
338371
}
339372
if err != nil {
373+
log.Print(err)
340374
return
341375
}
342376
t.Reset()

0 commit comments

Comments
 (0)