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.

bubble_peer_manager.py 5.9 KiB

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