The main Bubble source repository. Contains the Bubble API server, the web UI, documentation and utilities. https://getbubblenow.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

181 lines
5.8 KiB

  1. #!/usr/bin/python3
  2. import json
  3. import logging
  4. import os
  5. import sys
  6. import time
  7. import subprocess
  8. logger = logging.getLogger(__name__)
  9. logger.setLevel(logging.INFO)
  10. EMPTY_PEERS = {'peers': [], 'ports': []}
  11. class PeerPort(object):
  12. def __init__(self, port):
  13. if ':' in port:
  14. self.proto = port[0:port.index(':')]
  15. self.port = port[port.index(':') + 1:]
  16. else:
  17. self.proto = 'tcp'
  18. self.port = port
  19. def __str__(self):
  20. return self.proto + ':' + self.port
  21. def find_peers(port):
  22. out = subprocess.run(['iptables', '-vnL', 'INPUT'],
  23. stdout=subprocess.PIPE,
  24. stderr=subprocess.PIPE)
  25. peers = []
  26. for line in out.stdout.decode('utf-8').split('\n'):
  27. line = line.strip()
  28. if len(line) == 0 or line.startswith('Chain ') or line.startswith('pkts '):
  29. continue
  30. for parts in line.split(' '):
  31. packets = parts[0]
  32. bytes = parts[1]
  33. target = parts[2]
  34. proto = parts[3]
  35. if proto != port.proto:
  36. continue
  37. opt = parts[4]
  38. iface_in = parts[5]
  39. iface_out = parts[6]
  40. source = parts[7]
  41. if source == '0.0.0.0/0':
  42. continue
  43. dest = parts[8]
  44. if parts[9] != port.proto:
  45. continue
  46. if parts[10].startswith('dpt:'):
  47. dest_port = int(parts[10][len('dpt:'):])
  48. if dest_port == port.port:
  49. peers.append(source)
  50. return peers
  51. def add_peers(peers, port):
  52. out = subprocess.run(['iptables', '-vnL', 'INPUT'],
  53. stdout=subprocess.PIPE,
  54. stderr=subprocess.PIPE)
  55. lines = out.stdout.decode('utf-8').split('\n')
  56. insert_at = len(lines) - 2
  57. if insert_at < 2:
  58. raise ValueError('add_peers: insert_at was < 2: '+str(insert_at))
  59. for peer in peers:
  60. logger.info("add_peers: alllowing peer: " + peer + " on port " + port)
  61. out = subprocess.run(['iptables', '-I', 'INPUT', str(insert_at),
  62. '-p', port.proto, '-s', peer + '/32',
  63. '--dport', port.port, '-j', 'ACCEPT'])
  64. logger.info("add_peers: allowed peer: " + peer + " on port " + port)
  65. def remove_peers(peers, port):
  66. for peer in peers:
  67. remove_peer(peer, port)
  68. def remove_peer(peer, port):
  69. out = subprocess.run(['iptables', '-vnL', 'INPUT'],
  70. stdout=subprocess.PIPE,
  71. stderr=subprocess.PIPE)
  72. index = 0
  73. for line in out.stdout.decode('utf-8').split('\n'):
  74. line = line.strip()
  75. if len(line) == 0 or line.startswith('Chain ') or line.startswith('pkts '):
  76. continue
  77. index = index + 1
  78. for parts in line.split(' '):
  79. packets = parts[0]
  80. bytes = parts[1]
  81. target = parts[2]
  82. proto = parts[3]
  83. if proto != port.proto:
  84. continue
  85. opt = parts[4]
  86. iface_in = parts[5]
  87. iface_out = parts[6]
  88. source = parts[7]
  89. if not source.startswith(peer+'/32'):
  90. continue
  91. dest = parts[8]
  92. if parts[9] != port.proto:
  93. continue
  94. if parts[10].startswith('dpt:'):
  95. dest_port = int(parts[10][len('dpt:'):])
  96. if dest_port == port.port:
  97. logger.info("remove_peer: removing peer: " + peer + " on port " + port)
  98. out = subprocess.run(['iptables', '-D', 'INPUT', str(index)],
  99. stdout=subprocess.PIPE,
  100. stderr=subprocess.PIPE)
  101. return True
  102. return False
  103. class BubblePeers(object):
  104. def __init__(self, peer_path, self_path):
  105. self.peer_path = peer_path
  106. if os.path.exists(peer_path):
  107. self.last_modified = os.path.getmtime(self.peer_path)
  108. else:
  109. self.last_modified = 0
  110. self.last_update = None
  111. self.peers = []
  112. self.ports = []
  113. self.self_path = self_path
  114. self.self_node = {}
  115. def load_peers(self):
  116. if os.path.exists(self.peer_path):
  117. with open(self.peer_path) as f:
  118. val = json.load(f)
  119. else:
  120. val = EMPTY_PEERS
  121. self.peers = val['peers']
  122. self.ports = []
  123. for port in val['ports']:
  124. self.ports.append(PeerPort(port))
  125. def load_self(self):
  126. if os.path.exists(self.self_path):
  127. with open(self.self_path) as f:
  128. self.self_node = json.load(f)
  129. def monitor(self):
  130. self.load_peers()
  131. self.load_self()
  132. if os.path.exists(self.peer_path):
  133. self.last_modified = os.path.getmtime(self.peer_path)
  134. if self.last_update is None or self.last_update < self.last_modified:
  135. self.load_peers()
  136. for port in self.ports:
  137. peers_on_port = find_peers(port)
  138. peers_to_remove = []
  139. peers_to_add = []
  140. for peer in peers_on_port:
  141. if peer not in self.peers:
  142. peers_to_remove.append(peer)
  143. for peer in self.peers:
  144. if peer not in peers_on_port:
  145. peers_to_add.append(peer)
  146. remove_peers(peers_to_remove, port)
  147. add_peers(peers_to_add, port)
  148. if __name__ == "__main__":
  149. peers = BubblePeers(sys.argv[1], sys.argv[2])
  150. interval = int(sys.argv[3])
  151. try:
  152. while True:
  153. peers.monitor()
  154. time.sleep(interval)
  155. except Exception as e:
  156. logger.error("Unexpected error: " + repr(e))