Skip to content

Commit

Permalink
ui: fixed exporting/importing rules
Browse files Browse the repository at this point in the history
When exporting rules, use rfc3339 format for the Created field.
We were exporting as timestamp, which caused issues when importing them.

Related:
 58aa979
 issue #1140
  • Loading branch information
gustavo-iniguez-goya committed Jun 18, 2024
1 parent 68de351 commit 552aed5
Showing 1 changed file with 64 additions and 34 deletions.
98 changes: 64 additions & 34 deletions ui/opensnitch/rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@

import os
import json
from slugify import slugify
from datetime import datetime
from google.protobuf.json_format import MessageToJson, Parse

DefaultRulesPath = "/etc/opensnitchd/rules"

# date format displayed on the GUI (created column)
DBDateFieldFormat = "%Y-%m-%d %H:%M:%S"

class Rule():
def __init__(self):
pass
Expand Down Expand Up @@ -43,7 +47,7 @@ def new_from_records(records):
created = int(datetime.now().timestamp())
if records.value(RuleFields.Created) != "":
created = int(datetime.strptime(
records.value(RuleFields.Created), "%Y-%m-%d %H:%M:%S"
records.value(RuleFields.Created), DBDateFieldFormat
).timestamp())
rule.created = created

Expand Down Expand Up @@ -100,17 +104,20 @@ def add_rules(self, addr, rules):
for _,r in enumerate(rules):
# Operator list is always saved as json string to the db.
rjson = json.loads(MessageToJson(r))
if r.operator.type == Config.RULE_TYPE_LIST and rjson.get('operator') != None and rjson.get('operator').get('list') != None:
if r.operator.type == Config.RULE_TYPE_LIST and \
rjson.get('operator') != None and \
rjson.get('operator').get('list') != None:

r.operator.data = json.dumps(rjson.get('operator').get('list'))

self.add(datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
self.add(datetime.now().strftime(DBDateFieldFormat),
addr,
r.name, r.description, str(r.enabled),
str(r.precedence), str(r.nolog), r.action, r.duration,
r.operator.type,
str(r.operator.sensitive),
r.operator.operand, r.operator.data,
str(datetime.fromtimestamp(r.created).strftime("%Y-%m-%d %H:%M:%S")))
str(datetime.fromtimestamp(r.created).strftime(DBDateFieldFormat)))

return True
except Exception as e:
Expand Down Expand Up @@ -167,6 +174,12 @@ def update_time(self, time, name, addr):
action_on_conflict="OR REPLACE"
)

def _timestamp_to_rfc3339(self, time):
"""converts timestamp to rfc3339 format"""
return "{0}Z".format(
datetime.fromtimestamp(time).isoformat(timespec='microseconds')
)

def rule_to_json(self, node, rule_name):
try:
records = self._db.get_rule(rule_name, node)
Expand All @@ -178,16 +191,33 @@ def rule_to_json(self, node, rule_name):
# exclude this field when exporting to json
tempRule = MessageToJson(rule)
jRule = json.loads(tempRule)
jRule['created'] = "{0}Z".format(
datetime.fromtimestamp(rule.created).isoformat(timespec='microseconds')
)
jRule['created'] = self._timestamp_to_rfc3339(rule.created)
return json.dumps(jRule, indent=" ")
except Exception as e:
print("rule_to_json() exception:", e)
return None

def _export_rule_common(self, node, records, outdir):
try:
rule = Rule.new_from_records(records)
rulename = rule.name
if ".json" not in rulename:
rulename = rulename + ".json"
with open(outdir + "/" + rulename, 'w') as jsfile:
actual_json_text = MessageToJson(rule)
jRule = json.loads(actual_json_text)
jRule['created'] = self._timestamp_to_rfc3339(rule.created)
actual_json_text = json.dumps(jRule, indent=" ")
jsfile.write( actual_json_text )

return True
except Exception as e:
print(self.LOG_TAG, "export_rules(", node, outdir, ") exception:", e)

return False

def export_rule(self, node, rule_name, outdir):
"""Gets the the rule from the DB and writes it out to a directory.
"""Gets the rule from the DB and writes it out to a directory.
A new directory per node will be created.
"""
try:
Expand All @@ -196,51 +226,38 @@ def export_rule(self, node, rule_name, outdir):
print("export_rule() get_error 2:", records)
return False

rule = Rule.new_from_records(records)
rulesdir = outdir + "/" + node
rulesdir = outdir + "/" + slugify(node)
try:
os.makedirs(rulesdir, 0o700)
except Exception as e:
print("exception creating dirs:", e)
rulename = rule.name
if ".json" not in rulename:
rulename = rulename + ".json"
with open(rulesdir + "/" + rulename, 'w') as jsfile:
actual_json_text = MessageToJson(rule)
jsfile.write( actual_json_text )

return True
return self._export_rule_common(node, records, rulesdir)

except Exception as e:
print(self.LOG_TAG, "export_rules(", node, outdir, ") exception:", e)
print(self.LOG_TAG, "export_rules(", node, rulesdir, ") exception:", e)

return False

def export_rules(self, node, outdir):
"""Gets the the rules from the DB and writes them out to a directory.
"""Gets the rules from the DB and writes them out to a directory.
A new directory per node will be created.
"""
records = self._db.get_rules(node)
if records == None:
return False

rulesdir = outdir + "/" + slugify(node)
try:
os.makedirs(rulesdir, 0o700)
except Exception as e:
print("exception creating dirs:", e)
try:
while records.next() != False:
rule = Rule.new_from_records(records)

rulesdir = outdir + "/" + node
try:
os.makedirs(rulesdir, 0o700)
except:
pass
rulename = rule.name
if ".json" not in rulename:
rulename = rulename + ".json"
with open(rulesdir + "/" + rulename, 'w') as jsfile:
actual_json_text = MessageToJson(rule)
jsfile.write( actual_json_text )
self._export_rule_common(node, records, rulesdir)

except Exception as e:
print(self.LOG_TAG, "export_rules(", node, outdir, ") exception:", e)
print(self.LOG_TAG, "export_rules(", node, rulesdir, ") exception:", e)
return False

return True
Expand All @@ -254,7 +271,20 @@ def import_rules(self, rulesdir):
for rulename in os.listdir(rulesdir):
with open(rulesdir + "/" + rulename, 'r') as f:
jsrule = f.read()
pb_rule = Parse(text=jsrule, message=ui_pb2.Rule(), ignore_unknown_fields=True)
# up until v1.6.5/v1.7.0, 'created' field was exported as timestamp.
# since > v1.6.5 it's exported in rfc3339 format, so if we fail to
# parse the rule, we'll try to convert the 'created' value from
# timestamp to rfc3339.
try:
pb_rule = Parse(text=jsrule, message=ui_pb2.Rule(), ignore_unknown_fields=True)
except:
jRule = json.loads(jsrule)
created = int(datetime.strptime(
jRule['created'], "%Y-%m-%dT%H:%M:%S.%fZ"
).timestamp())
jRule['created'] = created
jsrule = json.dumps(jRule)
pb_rule = Parse(text=jsrule, message=ui_pb2.Rule(), ignore_unknown_fields=True)
rules.append(pb_rule)

return rules
Expand Down

0 comments on commit 552aed5

Please sign in to comment.