Skip to content

Commit

Permalink
Ufw comment (#14)
Browse files Browse the repository at this point in the history
* Support ufw deny outgoing traffic UFW_TO label

* Renaming UFW_TO label, improving variables naming and validations, adding comments and removing remaining sudo in ufw commands

* Renaming UFW_FROM label to UFW_ALLOW_FROM

* Appending prefix ufw-docker-automated: for log readability and removing remaingin sudo

* Update Readme UFW_ALLOW_TO example

* Add Warning when host is not found

* Filter empty ipnet entry in UFW_ALLOW_TO and UFW_ALLOW_FROM label

* Revert label renaming, port validation and hostname validation

* Simplify conditionnal with ufw_from

* Implement port validation for UFW_TO label

* Implement host validation for UFW_TO label

* Rename labels with UFW_ALLOW prefix

* Remove re package in requirements.txt

* Add ufw comment for deleting rules and use die instead of kill event

* Fix die event doesn't find container, now using event data to clean ufw rules instead
  • Loading branch information
mlollo authored Apr 30, 2021
1 parent e96c65b commit b01fef3
Showing 1 changed file with 21 additions and 11 deletions.
32 changes: 21 additions & 11 deletions src/ufw-docker-automated.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,13 @@ def manage_ufw():
event_type = event.get('status')

# container network is attached on start or stop event
if event_type == 'start' or event_type == 'kill':
if event.get('Type') == 'container' and event_type == 'start':
container = None
try:
container = client.containers.get(event['id'])
except docker.errors.NotFound as e:
attributes = event.get('Actor', {}).get('Attributes', {})
print(f"ufw-docker-automated: Warning container '{attributes.get('name')}' not found '{event['id']}'")
continue
container_network = container.attrs['HostConfig']['NetworkMode']
container_ip = None
Expand Down Expand Up @@ -133,7 +135,6 @@ def manage_ufw():
ufw_allow_to = None
pass

if event_type == 'start' and ufw_managed == 'True':
for key, value in container_port_dict:
if value and ufw_allow_from:
container_port_num = list(key.split("/"))[0]
Expand All @@ -143,15 +144,17 @@ def manage_ufw():
print(f"ufw-docker-automated: Adding UFW rule: allow from {source} to container {container.name} on port {container_port_num}/{container_port_protocol}")
subprocess.run([f"ufw route allow proto {container_port_protocol} \
from {source} \
to {container_ip} port {container_port_num}"],
to {container_ip} port {container_port_num} \
comment '{container.name}:{container.id}'"],
stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True,
shell=True)
if ufw_deny_outgoing == 'True':
# Allow the container to reply back to the client (if outgoing requests are denied by default)
print(f"ufw-docker-automated: Adding UFW rule: allow reply from container {container.name} on port {container_port_num}/{container_port_protocol} to {source}")
subprocess.run([f"ufw route allow proto {container_port_protocol} \
from {container_ip} port {container_port_num} \
to {source}"],
to {source} \
comment '{container.name}:{container.id}'"],
stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True,
shell=True)

Expand All @@ -164,25 +167,32 @@ def manage_ufw():
destination_protocol = f"proto {destination.get('protocol')}" if destination.get('protocol') else ""
subprocess.run([f"ufw route allow {destination_protocol} \
from {container_ip} \
to {destination.get('ipnet')} {destination_port}"],
to {destination.get('ipnet')} {destination_port} \
comment '{container.name}:{container.id}'"],
stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True,
shell=True)
# Deny any other outgoing requests
print(f"ufw-docker-automated: Adding UFW rule: deny outgoing from container {container.name} to any")
subprocess.run([f"ufw route deny from {container_ip} to any"],
subprocess.run([f"ufw route deny from {container_ip} to any comment '{container.name}:{container.id}'"],
stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True,
shell=True)

if event_type == 'kill' and ufw_managed == 'True':
if event.get('Type') == 'container' and event_type == 'die':
attributes = event.get('Actor', {}).get('Attributes', {})
container_name = attributes.get('name')
container_id = event['id']
ufw_managed = attributes.get('UFW_MANAGED').capitalize() if 'UFW_MANAGED' in attributes else None

if ufw_managed == 'True':
ufw_length = subprocess.run(
[f"ufw status numbered | grep {container_ip} | wc -l"],
[f"ufw status numbered | grep '# {container_name}:{container_id}' | wc -l"],
stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True,
shell=True)

for i in range(int(ufw_length.stdout.strip().split("\n")[0])):
for _ in range(int(ufw_length.stdout.strip().split("\n")[0])):
awk = "'{print $2}'"
ufw_status = subprocess.run(
[f"ufw status numbered | grep {container_ip} | awk -F \"[][]\" {awk} "],
[f"ufw status numbered | grep '# {container_name}:{container_id}' | awk -F \"[][]\" {awk} "],
stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True,
shell=True)

Expand All @@ -192,7 +202,7 @@ def manage_ufw():
stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True,
shell=True)
ufw_delete_result = ufw_delete.stdout.split("\n")[1].strip()
print(f"ufw-docker-automated: Cleaning UFW rule: container {container.name} deleted rule '{ufw_delete_result}'")
print(f"ufw-docker-automated: Cleaning UFW rule: container {container_name} deleted rule '{ufw_delete_result}'")


if __name__ == '__main__':
Expand Down

0 comments on commit b01fef3

Please sign in to comment.