-
Notifications
You must be signed in to change notification settings - Fork 112
/
firewallservice.go
166 lines (137 loc) · 5.85 KB
/
firewallservice.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
package firewallservice
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"strconv"
"time"
"github.com/chef/automate/components/automate-cli/pkg/verifyserver/constants"
"github.com/chef/automate/components/automate-cli/pkg/verifyserver/models"
"github.com/chef/automate/components/automate-cli/pkg/verifyserver/response"
"github.com/chef/automate/lib/logger"
)
type IFirewallService interface {
GetFirewallDetails(models.FirewallRequest) models.FirewallResponse
}
type FirewallService struct {
log logger.Logger
timeout time.Duration
port string
}
func NewFirewallService(log logger.Logger, timeout time.Duration, port string) *FirewallService {
return &FirewallService{
log: log,
timeout: timeout,
port: port,
}
}
func (fw *FirewallService) GetFirewallDetails(reqBody models.FirewallRequest) models.FirewallResponse {
resp := models.FirewallResponse{}
reachableCheck := fw.checkReachability(reqBody)
resp.Checks = append(resp.Checks, reachableCheck)
resp.Passed = true
for _, check := range resp.Checks {
if !check.Passed {
resp.Passed = false
break
}
}
return resp
}
func (fw *FirewallService) checkReachability(req models.FirewallRequest) models.Checks {
client := &http.Client{
Timeout: fw.timeout * time.Second,
}
respBody, err := fw.triggerRequest(req, client)
if err != nil || !respBody.Passed {
// this means while triggering the request we encountered some error
fw.log.Error("Error: ", err)
// this means we successfully made the call. but the port-reachable api returns that the port is not reachable
// hence from firewall api we are returning the same
fw.log.Error("port-reachable api result: ", respBody.Passed)
errorMsg := GetFirewallErrorMsg(req.DestinationServiceProtocol, req.DestinationNodeIP, req.DestinationServicePort, req.SourceNodeIP)
resolutionMsg := GetFirewallResolutionMsg(req.DestinationNodeIP, req.DestinationServicePort, req.SourceNodeIP)
return createFirewallCheck(false, "", errorMsg, resolutionMsg)
}
fw.log.Debug("Response From port-reachable API: ", respBody)
return createFirewallCheck(true, GetFirewallSuccessMsg(req.DestinationServiceProtocol, req.DestinationNodeIP, req.DestinationServicePort, req.SourceNodeIP), "", "")
}
func (fw *FirewallService) triggerRequest(req models.FirewallRequest, client *http.Client) (models.Checks, error) {
fw.log.Debug("triggerRequest Called...")
url := fmt.Sprintf("http://%s:%s%s", req.SourceNodeIP, fw.port, constants.PORT_REACHABLE_API_PATH)
fw.log.Debug("URL: ", url)
destinationPort, err := strconv.Atoi(req.DestinationServicePort)
if err != nil {
return models.Checks{}, errors.New("Failed to convert string ip into int: " + err.Error())
}
httpReqBody := models.PortReachableRequest{
DestinationNodeIp: req.DestinationNodeIP,
DestinationNodePort: destinationPort,
DestinationNodeServiceProtocol: req.DestinationServiceProtocol,
RootCA: req.RootCert,
}
httpReqBodyJSON, err := json.Marshal(httpReqBody)
if err != nil {
return models.Checks{}, errors.New("Failed to Marshal the request for Port reachability API: " + err.Error())
}
httpReq, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(httpReqBodyJSON))
if err != nil {
return models.Checks{}, errors.New("Error creating request for Port reachability API: " + err.Error())
}
httpReq.Header.Set(constants.CONTENT_TYPE, constants.TYPE_JSON)
resp, err := client.Do(httpReq)
if err != nil {
return models.Checks{}, errors.New("Error sending request to Port reachability API: " + err.Error())
}
return fw.getResultStructFromRespBody(resp.Body)
}
func (fw *FirewallService) getResultStructFromRespBody(respBody io.Reader) (models.Checks, error) {
fw.log.Debug("getResultStructFromRespBody called...")
body, err := io.ReadAll(respBody)
if err != nil {
return models.Checks{}, errors.New("Cannot able to read data from response body: " + err.Error())
}
// Converting API Response Body into Generic Response Struct.
apiRespStruct := response.ResponseBody{}
err = json.Unmarshal(body, &apiRespStruct)
if err != nil {
return models.Checks{}, errors.New("Failed to Unmarshal the response from Port reachability API: " + err.Error())
}
// If API(/port-reachable) is itself failing.
if apiRespStruct.Error != nil {
return models.Checks{}, apiRespStruct.Error
}
// Converting interface into JSON encoding. apiResp.Result is a interface and for accessing the values we are converting that into json.
resultByte, err := json.Marshal(apiRespStruct.Result)
if err != nil {
return models.Checks{}, errors.New("Failed to Marshal the response from Port reachability API: " + err.Error())
}
resultField := models.Checks{}
// converting JSON into struct.
err = json.Unmarshal(resultByte, &resultField)
if err != nil {
return models.Checks{}, errors.New("Failed to Unmarshal the result struct from Port reachability API response: " + err.Error())
}
return resultField, nil
}
func GetFirewallSuccessMsg(service, destinationIP, destinationPort, sourceIP string) string {
return fmt.Sprintf("The %s service running on %s:%s is reachable from %s", service, destinationIP, destinationPort, sourceIP)
}
func GetFirewallErrorMsg(service, destinationIP, destinationPort, sourceIP string) string {
return fmt.Sprintf("The %s service running on %s:%s is not reachable from %s", service, destinationIP, destinationPort, sourceIP)
}
func GetFirewallResolutionMsg(destinationIP, destinationPort, sourceIP string) string {
return fmt.Sprintf("Check your firewall settings and provide access to port %s on %s from %s", destinationPort, destinationIP, sourceIP)
}
func createFirewallCheck(passed bool, successMsg, errorMsg, resolutionMsg string) models.Checks {
return models.Checks{
Title: constants.FIREWALL_TITLE,
Passed: passed,
SuccessMsg: successMsg,
ErrorMsg: errorMsg,
ResolutionMsg: resolutionMsg,
}
}