1 #!@PYTHON@ -tt
2
3 # Copyright (c) 2018 Dell Inc. or its subsidiaries. All Rights Reserved.
4
5 # Fence agent for devices that support the Redfish API Specification.
6
7 import sys
8 import re
9 import logging
10 import json
11 import requests
12 import atexit
13 sys.path.append("@FENCEAGENTSLIBDIR@")
14
15 from fencing import *
16 from fencing import fail_usage, run_delay
17
18 GET_HEADERS = {'accept': 'application/json', 'OData-Version': '4.0'}
19 POST_HEADERS = {'content-type': 'application/json', 'accept': 'application/json',
20 'OData-Version': '4.0'}
21
22
23 def get_power_status(conn, options):
24 response = send_get_request(options, options["--systems-uri"])
25 if response['ret'] is False:
26 fail_usage("Couldn't get power information")
27 data = response['data']
28
29 try:
30 logging.debug("PowerState is: " + data[u'PowerState'])
31 except Exception:
32 fail_usage("Unable to get PowerState: " + "https://" + options["--ip"] + ":" + str(options["--ipport"]) + options["--systems-uri"])
33
34 if data[u'PowerState'].strip() == "Off":
35 return "off"
36 else:
37 return "on"
38
39 def set_power_status(conn, options):
40 action = {
41 'on' : "On",
42 'off': "ForceOff",
43 'reboot': "ForceRestart",
44 'diag': "Nmi"
45 }[options.get("original-action") or options["--action"]]
46
47 payload = {'ResetType': action}
48
49 # Search for 'Actions' key and extract URI from it
50 response = send_get_request(options, options["--systems-uri"])
51 if response['ret'] is False:
52 return {'ret': False}
53 data = response['data']
54 action_uri = data["Actions"]["#ComputerSystem.Reset"]["target"]
55
56 response = send_post_request(options, action_uri, payload)
57 if response['ret'] is False:
58 fail_usage("Error sending power command")
59 if options.get("original-action") == "diag":
60 return True
61 return
62
63 def send_get_request(options, uri):
64 full_uri = "https://" + options["--ip"] + ":" + str(options["--ipport"]) + uri
65 try:
|
CID (unavailable; MK=fe8c4c644630a943dabc4e32235ff653) (#1 of 1): Sensitive Data Leak (SENSITIVE_DATA_LEAK): |
|
(2) Event Sigma event: |
calling requests.get |
|
(6) Event Sigma event: |
calling requests.get sinks full_uri |
|
(7) Event Sigma main event: |
Sensitive data (such as a password) is transmitted or stored outside an application without proper sanitization. An attacker can intercept the transmission to steal the sensitive data (like user credentials, personal information) and exploit it. |
|
(8) Event remediation: |
Validate that either sensitive data does not go outside the application or the sensitive data is properly sanitized/encrypted before it goes outside the application. |
| Also see events: |
[Sigma event][Sigma event][Sigma event][Sigma event] |
66 resp = requests.get(full_uri, verify=not "--ssl-insecure" in options,
67 headers=GET_HEADERS,
68 auth=(options["--username"], options["--password"]))
69 data = resp.json()
70 except Exception as e:
71 fail_usage("Failed: send_get_request: " + str(e))
72 return {'ret': True, 'data': data}
73
74 def send_post_request(options, uri, payload):
75 full_uri = "https://" + options["--ip"] + ":" + str(options["--ipport"]) + uri
76 try:
77 requests.post(full_uri, data=json.dumps(payload),
78 headers=POST_HEADERS, verify=not "--ssl-insecure" in options,
79 auth=(options["--username"], options["--password"]))
80 except Exception as e:
81 fail_usage("Failed: send_post_request: " + str(e))
82 return {'ret': True}
83
84 def find_systems_resource(options):
85 response = send_get_request(options, options["--redfish-uri"])
86 if response['ret'] is False:
87 return {'ret': False}
88 data = response['data']
89
90 if 'Systems' not in data:
91 # Systems resource not found"
92 return {'ret': False}
93 else:
94 response = send_get_request(options, data["Systems"]["@odata.id"])
95 if response['ret'] is False:
96 return {'ret': False}
97 data = response['data']
98
99 # need to be able to handle more than one entry
100 for member in data[u'Members']:
101 system_uri = member[u'@odata.id']
102 return {'ret': True, 'uri': system_uri}
103
104 def define_new_opts():
105 all_opt["redfish-uri"] = {
106 "getopt" : ":",
107 "longopt" : "redfish-uri",
108 "help" : "--redfish-uri=[uri] Base or starting Redfish URI",
109 "required" : "0",
110 "default" : "/redfish/v1",
111 "shortdesc" : "Base or starting Redfish URI",
112 "order": 1
113 }
114 all_opt["systems-uri"] = {
115 "getopt" : ":",
116 "longopt" : "systems-uri",
117 "help" : "--systems-uri=[uri] Redfish Systems resource URI",
118 "required" : "0",
119 "shortdesc" : "Redfish Systems resource URI, i.e. /redfish/v1/Systems/System.Embedded.1",
120 "order": 1
121 }
122
123 def main():
124 atexit.register(atexit_handler)
125 device_opt = ["ipaddr", "login", "passwd", "redfish-uri", "systems-uri",
126 "ssl", "diag"]
127 define_new_opts()
128
129 opt = process_input(device_opt)
130
131 all_opt["ssl"]["default"] = "1"
132 options = check_input(device_opt, opt)
133
134 docs = {}
135 docs["shortdesc"] = "Power Fencing agent for Redfish"
136 docs["longdesc"] = "fence_redfish is a Power Fencing agent which can be used with \
137 Out-of-Band controllers that support Redfish APIs. These controllers provide remote \
138 access to control power on a server."
139 docs["vendorurl"] = "http://www.dmtf.org"
140 show_docs(options, docs)
141 run_delay(options)
142
143 ##
144 ## Operate the fencing device
145 ####
146
147 # Disable insecure-certificate-warning message
148 if "--ssl-insecure" in opt:
149 import urllib3
150 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
151
152 # backwards compatibility for <ip>:<port>
153 if options["--ip"].count(":") == 1:
154 (options["--ip"], options["--ipport"]) = options["--ip"].split(":")
155
156 if "--systems-uri" not in opt:
157 # Systems URI not provided, find it
158 sysresult = find_systems_resource(options)
159 if sysresult['ret'] is False:
160 sys.exit(1)
161 else:
162 options["--systems-uri"] = sysresult["uri"]
163
164 reboot_fn = None
165 if options["--action"] == "diag":
166 # Diag is a special action that can't be verified so we will reuse reboot functionality
167 # to minimize impact on generic library
168 options["original-action"] = options["--action"]
169 options["--action"] = "reboot"
170 options["--method"] = "cycle"
171 reboot_fn = set_power_status
172
173 result = fence_action(None, options, set_power_status, get_power_status, None, reboot_fn)
174 sys.exit(result)
175
176 if __name__ == "__main__":
177 main()
178