-
Notifications
You must be signed in to change notification settings - Fork 4
/
main.go
208 lines (197 loc) · 7.44 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
package main
import (
"bufio"
"fmt"
"github.com/SUSE/HANA-Firewall/generator"
"github.com/SUSE/HANA-Firewall/model"
"github.com/SUSE/HANA-Firewall/txtparser"
"io/ioutil"
"log"
"os"
"path"
"path/filepath"
"regexp"
"strings"
)
// printHelpAndExit prints usage help and then exits the program.
func printHelpAndExit(exitStatus int) {
fmt.Println(`hana-firewall: helps to generate HANA network service definitions for firewalld.
Usage:
# hana-firewall generate-firewalld-services
Generate firewalld service XML files according to HANA service definitions.
Previously generated XML files will be overwritten.
# hana-firewall dry-run
Display the service name and port numbers that will be generated in firewalld service XML files.
# hana-firewall define-new-hana-service
Interactively create a new HANA network service definition.
# hana-firewall help
Display this help message.`)
os.Exit(exitStatus)
}
// cliArg returns the i-th command line parameter, or an empty string if the parameter is not specified.
func cliArg(i int) string {
if len(os.Args) >= i+1 {
return os.Args[i]
}
return ""
}
// errorExit prints out a message to standard error and then exits the program with status 1.
func errorExit(template string, stuff ...interface{}) {
fmt.Fprintf(os.Stderr, template+"\n", stuff...)
os.Exit(1)
}
func main() {
if arg1 := cliArg(1); arg1 == "" || strings.Contains(arg1, "help") {
printHelpAndExit(0)
}
// All other actions require root privilege
if os.Geteuid() != 0 {
errorExit("Please run hana-firewall with root privilege.")
return
}
switch cliArg(1) {
case "generate-firewalld-services":
GenerateFirewalldServices()
case "dry-run":
DryRun()
case "define-new-hana-service":
CreateNewService()
}
}
// readConfig reads HANA firewall configuration from /etc and return. If an error occurs, the program will exit.
func readConfig() (globalParams model.HANAGlobalParameters, services []model.HANAServiceDefinition) {
// Read HANA firewall global config
globalConf, err := txtparser.ParseSysconfigFile("/etc/sysconfig/hana-firewall", true)
if err != nil {
errorExit("Failed to create/open /etc/sysconfig/hana-firewall - %v", err)
return
}
globalParams = model.HANAGlobalParameters{}
globalParams.ReadFrom(globalConf)
// Read HANA service definitions - all of them
services = make([]model.HANAServiceDefinition, 0, 10)
walkRoot := "/etc/hana-firewall"
err = filepath.Walk(walkRoot, func(path string, info os.FileInfo, err error) error {
if path == walkRoot {
// Move on from the directory
return nil
}
service := model.HANAServiceDefinition{}
serviceConf, err := txtparser.ParseSysconfigFile(path, true)
if err != nil {
log.Printf("GenerateFirewalldServices: skip definition file \"%s\" due to error - %v", path, err)
// Move on
return nil
}
service.ReadFrom(serviceConf)
// Skip services with empty definition
if len(service.TCP) > 0 || len(service.UDP) > 0 {
service.FileBaseName = filepath.Base(path)
services = append(services, service)
}
return nil
})
if err != nil {
errorExit("Failed to read /etc/hana-firewall directory - %v", err)
return
}
return
}
// GenerateFirewalldServices generates latest HANA service definition XML files for firewalld.
func GenerateFirewalldServices() {
globalParams, services := readConfig()
// Generate firewalld service definitions
fw := generator.Firewalld{
HANAGlobal: globalParams,
HANAServices: services,
}
firewalldServices, err := fw.GenerateConfig()
if err != nil {
errorExit("Failed to generate firewall config - %v", err)
return
}
if len(firewalldServices) == 0 {
errorExit("HANA instance number or service definitions are missing. Please check /etc/hana-firewall directory and /etc/sysconfig/hana-firewall file.")
return
}
fmt.Printf("Generating %d services in /etc/firewalld/services:\n", len(firewalldServices))
for _, svc := range firewalldServices {
fmt.Println(svc.String())
fmt.Println("----------------------------------------------------------")
}
// Write firewalld service definition XML
if err := fw.WriteConfig("/etc/firewalld/services", firewalldServices); err != nil {
errorExit("Failed to write XML files into /etc/firewalld/services - %v", err)
return
}
fmt.Println(`All done!
Please restart firewalld service (systemctl restart firewalld.service) to make new HANA services visible.
Remember: transient firewall configuration are lost when restarting firewalld.service.`)
}
func DryRun() {
globalParams, services := readConfig()
// Generate firewalld service definitions
fw := generator.Firewalld{
HANAGlobal: globalParams,
HANAServices: services,
}
firewalldServices, err := fw.GenerateConfig()
if err != nil {
errorExit("Failed to generate firewall config - %v", err)
return
}
for _, svc := range firewalldServices {
fmt.Println(svc.String())
fmt.Println("----------------------------------------------------------")
}
fmt.Println(`If you run "hana-firewall generate-firewalld-services", the services above will be made available in firewalld.`)
}
func CreateNewService() {
stdin := bufio.NewReader(os.Stdin)
fmt.Println("--------------------------------------------------------------")
fmt.Println("How would you like to name the new service? (e.g. \"database application support\"")
name, _ := stdin.ReadString('\n')
name = strings.TrimSpace(name)
if strings.ContainsRune(name, '/') || strings.ContainsRune(name, '.') {
errorExit("Sorry, the name may not contain slash or full-stop character.")
return
}
if name == "" {
errorExit("Sorry, you have to give the new service a name.")
return
}
fmt.Println("--------------------------------------------------------------")
fmt.Println("Which TCP ports are used by the service? Use space to separate multiple ports. If there are none, simply press enter.")
fmt.Println("For a special case, placeholder \"__INST_NUM__\" will be substituted by HANA instance numbers. and \"__INST_NUM+1__\" will be substituted by HANA instance number plus one.")
fmt.Println("Examples: 3__INST_NUM__01 4__INST_NUM+1__02")
tcpPortsStr, _ := stdin.ReadString('\n')
fmt.Println("--------------------------------------------------------------")
fmt.Println("Which UDP ports are used by the service? Use space to separate multiple ports. If there are none, simply press enter.")
fmt.Println("The special placeholders may also be used in these UDP ports.")
udpPortsStr, _ := stdin.ReadString('\n')
consecutiveSpaces := regexp.MustCompile("[[:space:]]+")
tcpPorts := consecutiveSpaces.Split(strings.TrimSpace(tcpPortsStr), -1)
udpPorts := consecutiveSpaces.Split(strings.TrimSpace(udpPortsStr), -1)
if len(tcpPorts) == 0 && len(udpPorts) == 0 {
errorExit("Sorry, the service must have at least one TCP or UDP port defined.")
return
}
service := model.HANAServiceDefinition{
FileBaseName: name,
TCP: tcpPorts,
UDP: udpPorts,
}
filePath := path.Join("/etc/hana-firewall/", name)
serviceConf, err := txtparser.ParseSysconfigFile(filePath, true)
if err != nil {
errorExit("Failed to create service definition file at \"%s\": %v", filePath, err)
return
}
service.WriteInto(serviceConf)
if err := ioutil.WriteFile(filePath, []byte(serviceConf.ToText()), 0600); err != nil {
errorExit("Failed to create service definition file at \"%s\": %v", filePath, err)
return
}
fmt.Println("--------------------------------------------------------------")
fmt.Println("All done! Remember to run \"hana-firewall generate-firewalld-services\" to make use of the new service.")
}