Merge pull request #1353 from frennkie/code-style

ip2tor: code style
This commit is contained in:
frennkie
2020-07-17 20:54:23 +02:00
committed by GitHub
3 changed files with 607 additions and 481 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,19 +1,20 @@
#!/usr/bin/python3 #!/usr/bin/python3
import sys
import locale
import requests
import json import json
import math import os
import time
import datetime, time
import subprocess import subprocess
import codecs, grpc, os import sys
import time
from datetime import datetime
from pathlib import Path from pathlib import Path
import requests
import toml import toml
from blitzpy import RaspiBlitzConfig from blitzpy import RaspiBlitzConfig
####### SCRIPT INFO ######### #####################
# SCRIPT INFO
#####################
# - this subscription does not require any payments # - this subscription does not require any payments
# - the recurring part is managed by the lets encrypt ACME script # - the recurring part is managed by the lets encrypt ACME script
@@ -29,9 +30,11 @@ if len(sys.argv) <= 1 or sys.argv[1] == "-h" or sys.argv[1] == "help":
print("# blitz.subscriptions.ip2tor.py subscription-cancel <id>") print("# blitz.subscriptions.ip2tor.py subscription-cancel <id>")
sys.exit(1) sys.exit(1)
####### BASIC SETTINGS ######### #####################
# BASIC SETTINGS
#####################
SUBSCRIPTIONS_FILE="/mnt/hdd/app-data/subscriptions/subscriptions.toml" SUBSCRIPTIONS_FILE = "/mnt/hdd/app-data/subscriptions/subscriptions.toml"
cfg = RaspiBlitzConfig() cfg = RaspiBlitzConfig()
cfg.reload() cfg.reload()
@@ -39,9 +42,12 @@ cfg.reload()
# todo: make sure that also ACME script uses TOR if activated # todo: make sure that also ACME script uses TOR if activated
session = requests.session() session = requests.session()
if cfg.run_behind_tor: if cfg.run_behind_tor:
session.proxies = {'http': 'socks5h://127.0.0.1:9050', 'https': 'socks5h://127.0.0.1:9050'} session.proxies = {'http': 'socks5h://127.0.0.1:9050', 'https': 'socks5h://127.0.0.1:9050'}
####### HELPER CLASSES #########
#####################
# HELPER CLASSES
#####################
class BlitzError(Exception): class BlitzError(Exception):
def __init__(self, errorShort, errorLong="", errorException=None): def __init__(self, errorShort, errorLong="", errorException=None):
@@ -49,11 +55,15 @@ class BlitzError(Exception):
self.errorLong = str(errorLong) self.errorLong = str(errorLong)
self.errorException = errorException self.errorException = errorException
####### HELPER FUNCTIONS #########
#####################
# HELPER FUNCTIONS
#####################
def eprint(*args, **kwargs): def eprint(*args, **kwargs):
print(*args, file=sys.stderr, **kwargs) print(*args, file=sys.stderr, **kwargs)
def handleException(e): def handleException(e):
if isinstance(e, BlitzError): if isinstance(e, BlitzError):
eprint(e.errorLong) eprint(e.errorLong)
@@ -64,30 +74,35 @@ def handleException(e):
print("error='{0}'".format(str(e))) print("error='{0}'".format(str(e)))
sys.exit(1) sys.exit(1)
def getsubdomain(fulldomainstring): def getsubdomain(fulldomainstring):
return fulldomainstring.split('.')[0] return fulldomainstring.split('.')[0]
####### API Calls to DNS Servcies #########
############################
# API Calls to DNS Servcies
############################
def duckDNSupdate(domain, token, ip): def duckDNSupdate(domain, token, ip):
print("# duckDNS update IP API call for {0}".format(domain)) print("# duckDNS update IP API call for {0}".format(domain))
# make HTTP request # make HTTP request
url = "https://www.duckdns.org/update?domains={0}&token={1}&ip={2}".format(getsubdomain(domain), token, ip)
try: try:
url="https://www.duckdns.org/update?domains={0}&token={1}&ip={2}".format(getsubdomain(domain), token, ip)
response = session.get(url) response = session.get(url)
if response.status_code != 200:
raise BlitzError("failed HTTP code", str(response.status_code))
except Exception as e: except Exception as e:
raise BlitzError("failed HTTP request",url,e) raise BlitzError("failed HTTP request", url, e)
if response.status_code != 200:
raise BlitzError("failed HTTP code",response.status_code)
return response.content return response.content
####### PROCESS FUNCTIONS #########
#####################
# PROCESS FUNCTIONS
#####################
def subscriptionsNew(ip, dnsservice, id, token, target): def subscriptionsNew(ip, dnsservice, id, token, target):
# id needs to the full domain name # id needs to the full domain name
if id.find(".") == -1: if id.find(".") == -1:
raise BlitzError("not a fully qualified domainname", dnsservice_id) raise BlitzError("not a fully qualified domainname", dnsservice_id)
@@ -100,13 +115,14 @@ def subscriptionsNew(ip, dnsservice, id, token, target):
os.system("/home/admin/config.scripts/bonus.letsencrypt.sh on") os.system("/home/admin/config.scripts/bonus.letsencrypt.sh on")
# dyndns # dyndns
realip=ip realip = ip
if ip == "dyndns": if ip == "dyndns":
updateURL="" updateURL = ""
if dnsservice == "duckdns": if dnsservice == "duckdns":
updateURL=="https://www.duckdns.org/update?domains={0}&token={1}".format(getsubdomain(domain), token, ip) updateURL = "https://www.duckdns.org/update?domains={0}&token={1}".format(getsubdomain(domain), token, ip)
subprocess.run(['/home/admin/config.scriprs/internet.dyndomain.sh', 'on', id, updateURL], stdout=subprocess.PIPE).stdout.decode('utf-8').strip() subprocess.run(['/home/admin/config.scriprs/internet.dyndomain.sh', 'on', id, updateURL],
realip=cfg.public_ip stdout=subprocess.PIPE).stdout.decode('utf-8').strip()
realip = cfg.public_ip
# update DNS with actual IP # update DNS with actual IP
if dnsservice == "duckdns": if dnsservice == "duckdns":
@@ -114,14 +130,16 @@ def subscriptionsNew(ip, dnsservice, id, token, target):
# run the ACME script # run the ACME script
print("# Running letsencrypt ACME script ...") print("# Running letsencrypt ACME script ...")
acmeResult=subprocess.Popen(["/home/admin/config.scripts/bonus.letsencrypt.sh", "issue-cert", dnsservice, id, token, target], stdout=subprocess.PIPE, stderr = subprocess.STDOUT, encoding='utf8') acmeResult = subprocess.Popen(
["/home/admin/config.scripts/bonus.letsencrypt.sh", "issue-cert", dnsservice, id, token, target],
stdout=subprocess.PIPE, stderr=subprocess.STDOUT, encoding='utf8')
out, err = acmeResult.communicate() out, err = acmeResult.communicate()
if out.find("error=") > -1: if out.find("error=") > -1:
time.sleep(6) time.sleep(6)
raise BlitzError("letsancrypt acme failed", out) raise BlitzError("letsancrypt acme failed", out)
# create subscription data for storage # create subscription data for storage
subscription = {} subscription = dict()
subscription['type'] = "letsencrypt-v1" subscription['type'] = "letsencrypt-v1"
subscription['id'] = id subscription['id'] = id
subscription['active'] = True subscription['active'] = True
@@ -131,7 +149,7 @@ def subscriptionsNew(ip, dnsservice, id, token, target):
subscription['ip'] = ip subscription['ip'] = ip
subscription['target'] = target subscription['target'] = target
subscription['description'] = "For {0}".format(target) subscription['description'] = "For {0}".format(target)
subscription['time_created'] = str(datetime.datetime.now().strftime("%Y-%m-%d %H:%M")) subscription['time_created'] = str(datetime.now().strftime("%Y-%m-%d %H:%M"))
subscription['warning'] = "" subscription['warning'] = ""
# load, add and store subscriptions # load, add and store subscriptions
@@ -152,31 +170,35 @@ def subscriptionsNew(ip, dnsservice, id, token, target):
except Exception as e: except Exception as e:
eprint(e) eprint(e)
raise BlitzError("fail on subscription storage",subscription, e) raise BlitzError("fail on subscription storage", str(subscription), e)
print("# OK - LETSENCRYPT DOMAIN IS READY") print("# OK - LETSENCRYPT DOMAIN IS READY")
return subscription return subscription
def subscriptionsCancel(id): def subscriptionsCancel(id):
# ToDo(frennkie) id is not used..
os.system("sudo chown admin:admin {0}".format(SUBSCRIPTIONS_FILE)) os.system("sudo chown admin:admin {0}".format(SUBSCRIPTIONS_FILE))
subs = toml.load(SUBSCRIPTIONS_FILE) subs = toml.load(SUBSCRIPTIONS_FILE)
newList = [] newList = []
removedCert = False removedCert = None
for idx, sub in enumerate(subs['subscriptions_letsencrypt']): for idx, sub in enumerate(subs['subscriptions_letsencrypt']):
if sub['id'] != subscriptionID: if sub['id'] != subscriptionID:
newList.append(sub) newList.append(sub)
else: else:
removedCert=sub removedCert = sub
subs['subscriptions_letsencrypt'] = newList subs['subscriptions_letsencrypt'] = newList
# run the ACME script to remove cert # run the ACME script to remove cert
if removedCert: if removedCert:
acmeResult=subprocess.Popen(["/home/admin/config.scripts/bonus.letsencrypt.sh", "remove-cert", removedCert['id'], removedCert['target']], stdout=subprocess.PIPE, stderr = subprocess.STDOUT, encoding='utf8') acmeResult = subprocess.Popen(
["/home/admin/config.scripts/bonus.letsencrypt.sh", "remove-cert", removedCert['id'],
removedCert['target']], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, encoding='utf8')
out, err = acmeResult.communicate() out, err = acmeResult.communicate()
if out.find("error=") > -1: if out.find("error=") > -1:
time.sleep(6) time.sleep(6)
raise BlitzError("letsancrypt acme failed", out) raise BlitzError("letsencrypt acme failed", out)
# persist change # persist change
with open(SUBSCRIPTIONS_FILE, 'w') as writer: with open(SUBSCRIPTIONS_FILE, 'w') as writer:
@@ -187,8 +209,8 @@ def subscriptionsCancel(id):
# todo: deinstall letsencrypt if this was last subscription # todo: deinstall letsencrypt if this was last subscription
def getSubscription(subscriptionID):
def getSubscription(subscriptionID):
try: try:
if Path(SUBSCRIPTIONS_FILE).is_file(): if Path(SUBSCRIPTIONS_FILE).is_file():
@@ -202,13 +224,12 @@ def getSubscription(subscriptionID):
if sub['id'] == subscriptionID: if sub['id'] == subscriptionID:
return sub return sub
return [] return []
except Exception as e: except Exception as e:
return [] return []
def getDomainByIP(ip): def getDomainByIP(ip):
# does subscriptin file exists # does subscriptin file exists
if Path(SUBSCRIPTIONS_FILE).is_file(): if Path(SUBSCRIPTIONS_FILE).is_file():
os.system("sudo chown admin:admin {0}".format(SUBSCRIPTIONS_FILE)) os.system("sudo chown admin:admin {0}".format(SUBSCRIPTIONS_FILE))
@@ -231,6 +252,8 @@ def getDomainByIP(ip):
def menuMakeSubscription(): def menuMakeSubscription():
# late imports - so that rest of script can run also if dependency is not available
from dialog import Dialog
# todo ... copy parts of IP2TOR dialogs # todo ... copy parts of IP2TOR dialogs
@@ -239,9 +262,9 @@ def menuMakeSubscription():
# ask user for which RaspiBlitz service the bridge should be used # ask user for which RaspiBlitz service the bridge should be used
choices = [] choices = []
choices.append( ("DUCKDNS", "Use duckdns.org") ) choices.append(("DUCKDNS", "Use duckdns.org"))
d = Dialog(dialog="dialog",autowidgetsize=True) d = Dialog(dialog="dialog", autowidgetsize=True)
d.set_background_title("LetsEncrypt Subscription") d.set_background_title("LetsEncrypt Subscription")
code, tag = d.menu( code, tag = d.menu(
"\nChoose a free DNS service to work with:", "\nChoose a free DNS service to work with:",
@@ -252,7 +275,7 @@ def menuMakeSubscription():
sys.exit(0) sys.exit(0)
# get the fixed dnsservice string # get the fixed dnsservice string
dnsservice=tag.lower() dnsservice = tag.lower()
############################ ############################
# PHASE 2: Enter ID & API token for service # PHASE 2: Enter ID & API token for service
@@ -260,18 +283,18 @@ def menuMakeSubscription():
if dnsservice == "duckdns": if dnsservice == "duckdns":
# show basic info on duck dns # show basic info on duck dns
Dialog(dialog="dialog",autowidgetsize=True).msgbox(''' Dialog(dialog="dialog", autowidgetsize=True).msgbox('''
If you havent already go to https://duckdns.org If you havent already go to https://duckdns.org
- consider using the TOR browser - consider using the TOR browser
- create an account or login - create an account or login
- make sure you have a subdomain added - make sure you have a subdomain added
''',title="DuckDNS Account needed") ''', title="DuckDNS Account needed")
# enter the subdomain # enter the subdomain
code, text = d.inputbox( code, text = d.inputbox(
"Enter yor duckDNS subdomain:", "Enter yor duckDNS subdomain:",
height=10, width=40, init="", height=10, width=40, init="",
title="DuckDNS Domain") title="DuckDNS Domain")
subdomain = text.strip() subdomain = text.strip()
subdomain = subdomain.split(' ')[0] subdomain = subdomain.split(' ')[0]
subdomain = getsubdomain(subdomain) subdomain = getsubdomain(subdomain)
@@ -280,16 +303,16 @@ If you havent already go to https://duckdns.org
# check for valid input # check for valid input
if len(subdomain) == 0: if len(subdomain) == 0:
Dialog(dialog="dialog",autowidgetsize=True).msgbox(''' Dialog(dialog="dialog", autowidgetsize=True).msgbox('''
This looks not like a valid subdomain. This looks not like a valid subdomain.
''',title="Unvalid Input") ''', title="Unvalid Input")
sys.exit(0) sys.exit(0)
# enter the token # enter the token
code, text = d.inputbox( code, text = d.inputbox(
"Enter the duckDNS token of your account:", "Enter the duckDNS token of your account:",
height=10, width=50, init="", height=10, width=50, init="",
title="DuckDNS Token") title="DuckDNS Token")
token = text.strip() token = text.strip()
token = token.split(' ')[0] token = token.split(' ')[0]
@@ -297,29 +320,29 @@ This looks not like a valid subdomain.
try: try:
token.index("-") token.index("-")
except Exception as e: except Exception as e:
token="" token = ""
if len(token) < 20: if len(token) < 20:
Dialog(dialog="dialog",autowidgetsize=True).msgbox(''' Dialog(dialog="dialog", autowidgetsize=True).msgbox('''
This looks not like a valid token. This looks not like a valid token.
''',title="Unvalid Input") ''', title="Unvalid Input")
sys.exit(0) sys.exit(0)
else: else:
os.system("clear") os.system("clear")
print("Not supported yet: {0}".format(dnsservice)) print("Not supported yet: {0}".format(dnsservice))
time.sleep(4) time.sleep(4)
sys.exit(0) sys.exit(0)
############################ ############################
# PHASE 3: Choose what kind of IP: dynDNS, IP2TOR, fixedIP # PHASE 3: Choose what kind of IP: dynDNS, IP2TOR, fixedIP
# ask user for which RaspiBlitz service the bridge should be used # ask user for which RaspiBlitz service the bridge should be used
choices = [] choices = list()
choices.append( ("IP2TOR", "HTTPS for a IP2TOR Bridge") ) choices.append(("IP2TOR", "HTTPS for a IP2TOR Bridge"))
choices.append( ("DYNDNS", "HTTPS for {0} DynamicIP DNS".format(dnsservice.upper())) ) choices.append(("DYNDNS", "HTTPS for {0} DynamicIP DNS".format(dnsservice.upper())))
choices.append( ("STATIC", "HTTPS for a static IP") ) choices.append(("STATIC", "HTTPS for a static IP"))
d = Dialog(dialog="dialog",autowidgetsize=True) d = Dialog(dialog="dialog", autowidgetsize=True)
d.set_background_title("LetsEncrypt Subscription") d.set_background_title("LetsEncrypt Subscription")
code, tag = d.menu( code, tag = d.menu(
"\nChoose the kind of IP you want to use:", "\nChoose the kind of IP you want to use:",
@@ -331,33 +354,34 @@ This looks not like a valid token.
sys.exit(0) sys.exit(0)
# default target are the nginx ip ports # default target are the nginx ip ports
target="ip" target = "ip"
ip = ""
if tag == "IP2TOR": if tag == "IP2TOR":
# get all active IP2TOR subscriptions (just in case) # get all active IP2TOR subscriptions (just in case)
ip2torSubs=[] ip2torSubs = []
if Path(SUBSCRIPTIONS_FILE).is_file(): if Path(SUBSCRIPTIONS_FILE).is_file():
os.system("sudo chown admin:admin {0}".format(SUBSCRIPTIONS_FILE)) os.system("sudo chown admin:admin {0}".format(SUBSCRIPTIONS_FILE))
subs = toml.load(SUBSCRIPTIONS_FILE) subs = toml.load(SUBSCRIPTIONS_FILE)
for idx, sub in enumerate(subs['subscriptions_ip2tor']): for idx, sub in enumerate(subs['subscriptions_ip2tor']):
if sub['active']: if sub['active']:
ip2torSubs.append(sub) ip2torSubs.append(sub)
# when user has no IP2TOR subs yet # when user has no IP2TOR subs yet
if len(ip2torSubs) == 0: if len(ip2torSubs) == 0:
Dialog(dialog="dialog",autowidgetsize=True).msgbox(''' Dialog(dialog="dialog", autowidgetsize=True).msgbox('''
You have no active IP2TOR subscriptions. You have no active IP2TOR subscriptions.
Create one first and try again. Create one first and try again.
''',title="No IP2TOR available") ''', title="No IP2TOR available")
sys.exit(0) sys.exit(0)
# let user select a IP2TOR subscription # let user select a IP2TOR subscription
choices = [] choices = []
for idx, sub in enumerate(ip2torSubs): for idx, sub in enumerate(ip2torSubs):
choices.append( ("{0}".format(idx), "IP2TOR {0} {1}:{2}".format(sub['name'], sub['ip'], sub['port'])) ) choices.append(("{0}".format(idx), "IP2TOR {0} {1}:{2}".format(sub['name'], sub['ip'], sub['port'])))
d = Dialog(dialog="dialog",autowidgetsize=True) d = Dialog(dialog="dialog", autowidgetsize=True)
d.set_background_title("LetsEncrypt Subscription") d.set_background_title("LetsEncrypt Subscription")
code, tag = d.menu( code, tag = d.menu(
"\nChoose the IP2TOR subscription:", "\nChoose the IP2TOR subscription:",
@@ -368,22 +392,22 @@ Create one first and try again.
sys.exit(0) sys.exit(0)
# get the slected IP2TOR bridge # get the slected IP2TOR bridge
ip2torSelect=ip2torSubs[int(tag)] ip2torSelect = ip2torSubs[int(tag)]
ip=ip2torSelect["ip"] ip = ip2torSelect["ip"]
target="tor" target = "tor"
elif tag == "DYNDNS": elif tag == "DYNDNS":
# the subscriptioNew method will handle acrivating the dnydns part # the subscriptioNew method will handle acrivating the dnydns part
ip="dyndns" ip = "dyndns"
elif tag == "STATIC": elif tag == "STATIC":
# enter the static IP # enter the static IP
code, text = d.inputbox( code, text = d.inputbox(
"Enter the static public IP of this RaspiBlitz:", "Enter the static public IP of this RaspiBlitz:",
height=10, width=40, init="", height=10, width=40, init="",
title="Static IP") title="Static IP")
ip = text.strip() ip = text.strip()
ip = token.split(' ')[0] ip = token.split(' ')[0]
@@ -391,36 +415,39 @@ Create one first and try again.
try: try:
ip.index(".") ip.index(".")
except Exception as e: except Exception as e:
ip="" ip = ""
if len(ip) == 0: if len(ip) == 0:
Dialog(dialog="dialog",autowidgetsize=True).msgbox(''' Dialog(dialog="dialog", autowidgetsize=True).msgbox('''
This looks not like a valid IP. This looks not like a valid IP.
''',title="Unvalid Input") ''', title="Unvalid Input")
sys.exit(0) sys.exit(0)
# create the letsenscript subscription # create the letsencrypt subscription
try: try:
os.system("clear") os.system("clear")
subscription = subscriptionsNew(ip, dnsservice, domain, token, target) subscription = subscriptionsNew(ip, dnsservice, domain, token, target)
# success dialog # success dialog
Dialog(dialog="dialog",autowidgetsize=True).msgbox(''' Dialog(dialog="dialog", autowidgetsize=True).msgbox('''
OK your LetsEncrypt subscription is now ready. OK your LetsEncrypt subscription is now ready.
Go to SUBSCRIBE > LIST to see details. Go to SUBSCRIBE > LIST to see details.
Use the correct port on {0} Use the correct port on {0}
to reach the service you wanted. to reach the service you wanted.
'''.format(domain),title="OK LetsEncrypt Created") '''.format(domain), title="OK LetsEncrypt Created")
except Exception as e: except Exception as e:
# unkown error happend # unknown error happened
Dialog(dialog="dialog",autowidgetsize=True).msgbox(''' Dialog(dialog="dialog", autowidgetsize=True).msgbox('''
Unkown Error happend - please report to developers: Unknown Error happened - please report to developers:
{0} {0}
'''.format(str(e)),title="Exception on Subscription") '''.format(str(e)), title="Exception on Subscription")
sys.exit(1) sys.exit(1)
####### COMMANDS #########
##################
# COMMANDS
##################
############### ###############
# CREATE SSH DIALOG # CREATE SSH DIALOG
@@ -428,45 +455,44 @@ Unkown Error happend - please report to developers:
############### ###############
if sys.argv[1] == "create-ssh-dialog": if sys.argv[1] == "create-ssh-dialog":
# late imports - so that rest of script can run also if dependency is not available
from dialog import Dialog
menuMakeSubscription() menuMakeSubscription()
sys.exit() sys.exit()
############### ##########################
# SUBSCRIPTIONS NEW # SUBSCRIPTIONS NEW
# call from web interface # call from web interface
############### ##########################
if sys.argv[1] == "subscription-new": if sys.argv[1] == "subscription-new":
# check parameters # check parameters
try: try:
if len(sys.argv) <= 5: raise BlitzError("incorrect parameters","") if len(sys.argv) <= 5:
ip = sys.argv[2] raise BlitzError("incorrect parameters", "")
dnsservice_type = sys.argv[3]
dnsservice_id = sys.argv[4]
dnsservice_token = sys.argv[5]
if len(sys.argv) <= 6:
target = "ip&tor"
else:
target = sys.argv[6]
except Exception as e: except Exception as e:
handleException(e) handleException(e)
ip = sys.argv[2]
dnsservice_type = sys.argv[3]
dnsservice_id = sys.argv[4]
dnsservice_token = sys.argv[5]
if len(sys.argv) <= 6:
target = "ip&tor"
else:
target = sys.argv[6]
# create the subscription # create the subscription
try: try:
subscription = subscriptionsNew(ip, dnsservice_type, dnsservice_id, dnsservice_token, target) subscription = subscriptionsNew(ip, dnsservice_type, dnsservice_id, dnsservice_token, target)
# output json ordered bridge
print(json.dumps(subscription, indent=2))
sys.exit()
except Exception as e: except Exception as e:
handleException(e) handleException(e)
# output json ordered bridge
print(json.dumps(subscription, indent=2))
sys.exit()
####################### #######################
# SUBSCRIPTIONS LIST # SUBSCRIPTIONS LIST
####################### #######################
@@ -483,7 +509,7 @@ if sys.argv[1] == "subscriptions-list":
if "subscriptions_letsencrypt" not in subs: if "subscriptions_letsencrypt" not in subs:
subs['subscriptions_letsencrypt'] = [] subs['subscriptions_letsencrypt'] = []
print(json.dumps(subs['subscriptions_letsencrypt'], indent=2)) print(json.dumps(subs['subscriptions_letsencrypt'], indent=2))
except Exception as e: except Exception as e:
handleException(e) handleException(e)
@@ -496,11 +522,12 @@ if sys.argv[1] == "subscription-detail":
# check parameters # check parameters
try: try:
if len(sys.argv) <= 2: raise BlitzError("incorrect parameters","") if len(sys.argv) <= 2:
subscriptionID = sys.argv[2] raise BlitzError("incorrect parameters", "")
except Exception as e: except Exception as e:
handleException(e) handleException(e)
subscriptionID = sys.argv[2]
try: try:
sub = getSubscription(subscriptionID) sub = getSubscription(subscriptionID)
print(json.dumps(sub, indent=2)) print(json.dumps(sub, indent=2))
@@ -509,7 +536,7 @@ if sys.argv[1] == "subscription-detail":
handleException(e) handleException(e)
sys.exit(0) sys.exit(0)
####################### #######################
# DOMAIN BY IP # DOMAIN BY IP
# to check if an ip has a domain mapping # to check if an ip has a domain mapping
@@ -518,14 +545,16 @@ if sys.argv[1] == "domain-by-ip":
# check parameters # check parameters
try: try:
if len(sys.argv) <= 2: raise BlitzError("incorrect parameters","") if len(sys.argv) <= 2:
ip = sys.argv[2] raise BlitzError("incorrect parameters", "")
except Exception as e: except Exception as e:
handleException(e) handleException(e)
ip = sys.argv[2]
try: try:
domain=getDomainByIP(ip) domain = getDomainByIP(ip)
print("domain='{0}'".format(domain)) print("domain='{0}'".format(domain))
except Exception as e: except Exception as e:
@@ -540,19 +569,18 @@ if sys.argv[1] == "subscription-cancel":
# check parameters # check parameters
try: try:
if len(sys.argv) <= 2: raise BlitzError("incorrect parameters","") if len(sys.argv) <= 2:
subscriptionID = sys.argv[2] raise BlitzError("incorrect parameters", "")
except Exception as e: except Exception as e:
handleException(e) handleException(e)
subscriptionID = sys.argv[2]
try: try:
subscriptionsCancel(subscriptionID) subscriptionsCancel(subscriptionID)
except Exception as e: except Exception as e:
handleException(e) handleException(e)
sys.exit(0) sys.exit(0)
# unkown command # unknown command
print("# unkown command") print("# unknown command")

View File

@@ -4,16 +4,15 @@
# SSH Dialogs to manage Subscriptions on the RaspiBlitz # SSH Dialogs to manage Subscriptions on the RaspiBlitz
######################################################## ########################################################
import sys
import math
import time
import toml
import os import os
import subprocess import subprocess
import sys
import time
from datetime import datetime
from dialog import Dialog import toml
from blitzpy import RaspiBlitzConfig from blitzpy import RaspiBlitzConfig
from dialog import Dialog
# constants for standard services # constants for standard services
LND_REST_API = "LND-REST-API" LND_REST_API = "LND-REST-API"
@@ -26,25 +25,32 @@ cfg = RaspiBlitzConfig()
cfg.reload() cfg.reload()
# basic values # basic values
SUBSCRIPTIONS_FILE="/mnt/hdd/app-data/subscriptions/subscriptions.toml" SUBSCRIPTIONS_FILE = "/mnt/hdd/app-data/subscriptions/subscriptions.toml"
####### HELPER FUNCTIONS #########
#######################
# HELPER FUNCTIONS
#######################
def eprint(*args, **kwargs): def eprint(*args, **kwargs):
print(*args, file=sys.stderr, **kwargs) print(*args, file=sys.stderr, **kwargs)
def parseDateIP2TORSERVER(datestr): def parseDateIP2TORSERVER(datestr):
return datetime.datetime.strptime(datestr,"%Y-%m-%dT%H:%M:%S.%fZ") return datetime.strptime(datestr, "%Y-%m-%dT%H:%M:%S.%fZ")
def secondsLeft(dateObj): def secondsLeft(dateObj):
return round((dateObj - datetime.datetime.utcnow()).total_seconds()) return round((dateObj - datetime.utcnow()).total_seconds())
####### SSH MENU FUNCTIONS #########
#######################
# SSH MENU FUNCTIONS
#######################
def mySubscriptions(): def mySubscriptions():
# check if any subscriptions are available # check if any subscriptions are available
countSubscriptions=0 countSubscriptions = 0
try: try:
os.system("sudo chown admin:admin {0}".format(SUBSCRIPTIONS_FILE)) os.system("sudo chown admin:admin {0}".format(SUBSCRIPTIONS_FILE))
subs = toml.load(SUBSCRIPTIONS_FILE) subs = toml.load(SUBSCRIPTIONS_FILE)
@@ -52,67 +58,69 @@ def mySubscriptions():
countSubscriptions += len(subs['subscriptions_ip2tor']) countSubscriptions += len(subs['subscriptions_ip2tor'])
if 'subscriptions_letsencrypt' in subs: if 'subscriptions_letsencrypt' in subs:
countSubscriptions += len(subs['subscriptions_letsencrypt']) countSubscriptions += len(subs['subscriptions_letsencrypt'])
except Exception as e: pass except Exception as e:
pass
if countSubscriptions == 0: if countSubscriptions == 0:
Dialog(dialog="dialog",autowidgetsize=True).msgbox(''' Dialog(dialog="dialog", autowidgetsize=True).msgbox('''
You have no active or inactive subscriptions. You have no active or inactive subscriptions.
''',title="Info") ''', title="Info")
return return
# load subscriptions and make dialog choices out of it # load subscriptions and make dialog choices out of it
choices = [] choices = []
lookup = {} lookup = {}
lookupIndex=0 lookupIndex = 0
subs = toml.load(SUBSCRIPTIONS_FILE) subs = toml.load(SUBSCRIPTIONS_FILE)
# list ip2tor subscriptions # list ip2tor subscriptions
if 'subscriptions_ip2tor' in subs: if 'subscriptions_ip2tor' in subs:
for sub in subs['subscriptions_ip2tor']: for sub in subs['subscriptions_ip2tor']:
# remember subscription under lookupindex # remember subscription under lookupindex
lookupIndex += 1 lookupIndex += 1
lookup[str(lookupIndex)]=sub lookup[str(lookupIndex)] = sub
# add to dialog choices # add to dialog choices
if sub['active']: if sub['active']:
activeState="active" activeState = "active"
else: else:
activeState="in-active" activeState = "in-active"
name="IP2TOR Bridge for {0}".format(sub['name']) name = "IP2TOR Bridge for {0}".format(sub['name'])
choices.append( ("{0}".format(lookupIndex), "{0} ({1})".format(name.ljust(30), activeState)) ) choices.append(("{0}".format(lookupIndex), "{0} ({1})".format(name.ljust(30), activeState)))
# list letsencrypt subscriptions # list letsencrypt subscriptions
if 'subscriptions_letsencrypt' in subs: if 'subscriptions_letsencrypt' in subs:
for sub in subs['subscriptions_letsencrypt']: for sub in subs['subscriptions_letsencrypt']:
# remember subscription under lookupindex # remember subscription under lookupindex
lookupIndex += 1 lookupIndex += 1
lookup[str(lookupIndex)]=sub lookup[str(lookupIndex)] = sub
# add to dialog choices # add to dialog choices
if sub['active']: if sub['active']:
activeState="active" activeState = "active"
else: else:
activeState="in-active" activeState = "in-active"
name="LETSENCRYPT {0}".format(sub['id']) name = "LETSENCRYPT {0}".format(sub['id'])
choices.append( ("{0}".format(lookupIndex), "{0} ({1})".format(name.ljust(30), activeState)) ) choices.append(("{0}".format(lookupIndex), "{0} ({1})".format(name.ljust(30), activeState)))
# show menu with options # show menu with options
d = Dialog(dialog="dialog",autowidgetsize=True) d = Dialog(dialog="dialog", autowidgetsize=True)
d.set_background_title("RaspiBlitz Subscriptions") d.set_background_title("RaspiBlitz Subscriptions")
code, tag = d.menu( code, tag = d.menu(
"\nYou have the following subscriptions - select for details:", "\nYou have the following subscriptions - select for details:",
choices=choices, cancel_label="Back", width=65, height=15, title="My Subscriptions") choices=choices, cancel_label="Back", width=65, height=15, title="My Subscriptions")
# if user chosses CANCEL # if user chosses CANCEL
if code != d.OK: return if code != d.OK:
return
# get data of selected subscrption # get data of selected subscrption
selectedSub = lookup[str(tag)] selectedSub = lookup[str(tag)]
# show details of selected # show details of selected
d = Dialog(dialog="dialog",autowidgetsize=True) d = Dialog(dialog="dialog", autowidgetsize=True)
d.set_background_title("My Subscriptions") d.set_background_title("My Subscriptions")
if selectedSub['type'] == "letsencrypt-v1": if selectedSub['type'] == "letsencrypt-v1":
if len(selectedSub['warning']) > 0: if len(selectedSub['warning']) > 0:
selectedSub['warning'] = "\n{0}".format(selectedSub['warning']) selectedSub['warning'] = "\n{0}".format(selectedSub['warning'])
text=''' text = '''
This is a LetsEncrypt subscription using the free DNS service This is a LetsEncrypt subscription using the free DNS service
{dnsservice} {dnsservice}
@@ -127,18 +135,18 @@ The state of the subscription is: {active} {warning}
The following additional information is available: The following additional information is available:
{description} {description}
'''.format( dnsservice=selectedSub['dnsservice_type'], '''.format(dnsservice=selectedSub['dnsservice_type'],
domain=selectedSub['id'], domain=selectedSub['id'],
ip=selectedSub['ip'], ip=selectedSub['ip'],
active= "ACTIVE" if selectedSub['active'] else "NOT ACTIVE", active="ACTIVE" if selectedSub['active'] else "NOT ACTIVE",
warning=selectedSub['warning'], warning=selectedSub['warning'],
description=selectedSub['description'] description=selectedSub['description']
) )
elif selectedSub['type'] == "ip2tor-v1": elif selectedSub['type'] == "ip2tor-v1":
if len(selectedSub['warning']) > 0: if len(selectedSub['warning']) > 0:
selectedSub['warning'] = "\n{0}".format(selectedSub['warning']) selectedSub['warning'] = "\n{0}".format(selectedSub['warning'])
text=''' text = '''
This is a IP2TOR subscription bought on {initdate} at This is a IP2TOR subscription bought on {initdate} at
{shop} {shop}
@@ -153,37 +161,43 @@ The state of the subscription is: {active} {warning}
The following additional information is available: The following additional information is available:
{description} {description}
'''.format( initdate=selectedSub['time_created'], '''.format(initdate=selectedSub['time_created'],
shop=selectedSub['shop'], shop=selectedSub['shop'],
publicaddress="{0}:{1}".format(selectedSub['ip'],selectedSub['port']), publicaddress="{0}:{1}".format(selectedSub['ip'], selectedSub['port']),
toraddress=selectedSub['tor'], toraddress=selectedSub['tor'],
renewhours=(round(int(selectedSub['duration'])/3600)), renewhours=(round(int(selectedSub['duration']) / 3600)),
renewsats=(round(int(selectedSub['price_extension'])/1000)), renewsats=(round(int(selectedSub['price_extension']) / 1000)),
totalsats=(round(int(selectedSub['price_extension'])/1000)), totalsats=(round(int(selectedSub['price_extension']) / 1000)),
active= "ACTIVE" if selectedSub['active'] else "NOT ACTIVE", active="ACTIVE" if selectedSub['active'] else "NOT ACTIVE",
warning=selectedSub['warning'], warning=selectedSub['warning'],
description=selectedSub['description'], description=selectedSub['description'],
service=selectedSub['name'] service=selectedSub['name']
) )
else:
text = "no text?! FIXME"
if selectedSub['active']: if selectedSub['active']:
extraLable = "CANCEL SUBSCRIPTION" extraLable = "CANCEL SUBSCRIPTION"
else: else:
extraLable = "DELETE SUBSCRIPTION" extraLable = "DELETE SUBSCRIPTION"
code = d.msgbox(text, title="Subscription Detail", ok_label="Back", extra_button=True, extra_label=extraLable ,width=75, height=30) code = d.msgbox(text, title="Subscription Detail", ok_label="Back", extra_button=True, extra_label=extraLable,
width=75, height=30)
# user wants to delete this subscription # user wants to delete this subscription
# call the responsible sub script for deletion just in case any subscription needs to do some extra api calls when canceling # call the responsible sub script for deletion just in case any subscription needs to do some extra
# api calls when canceling
if code == "extra": if code == "extra":
os.system("clear") os.system("clear")
if selectedSub['type'] == "letsencrypt-v1": if selectedSub['type'] == "letsencrypt-v1":
cmd="python /home/admin/config.scripts/blitz.subscriptions.letsencrypt.py subscription-cancel {0}".format(selectedSub['id']) cmd = "python /home/admin/config.scripts/blitz.subscriptions.letsencrypt.py subscription-cancel {0}".format(
print("# running: {0}".format(cmd)) selectedSub['id'])
print("# running: {0}".format(cmd))
os.system(cmd) os.system(cmd)
time.sleep(2) time.sleep(2)
elif selectedSub['type'] == "ip2tor-v1": elif selectedSub['type'] == "ip2tor-v1":
cmd="python /home/admin/config.scripts/blitz.subscriptions.ip2tor.py subscription-cancel {0}".format(selectedSub['id']) cmd = "python /home/admin/config.scripts/blitz.subscriptions.ip2tor.py subscription-cancel {0}".format(
print("# running: {0}".format(cmd)) selectedSub['id'])
print("# running: {0}".format(cmd))
os.system(cmd) os.system(cmd)
time.sleep(2) time.sleep(2)
else: else:
@@ -193,14 +207,17 @@ The following additional information is available:
# loop until no more subscriptions or user chooses CANCEL on subscription list # loop until no more subscriptions or user chooses CANCEL on subscription list
mySubscriptions() mySubscriptions()
####### SSH MENU #########
choices = [] #######################
choices.append( ("LIST","My Subscriptions") ) # SSH MENU
choices.append( ("NEW1","+ IP2TOR Bridge (paid)") ) #######################
choices.append( ("NEW2","+ LetsEncrypt HTTPS Domain (free)") )
d = Dialog(dialog="dialog",autowidgetsize=True) choices = list()
choices.append(("LIST", "My Subscriptions"))
choices.append(("NEW1", "+ IP2TOR Bridge (paid)"))
choices.append(("NEW2", "+ LetsEncrypt HTTPS Domain (free)"))
d = Dialog(dialog="dialog", autowidgetsize=True)
d.set_background_title("RaspiBlitz Subscriptions") d.set_background_title("RaspiBlitz Subscriptions")
code, tag = d.menu( code, tag = d.menu(
"\nCheck existing subscriptions or create new:", "\nCheck existing subscriptions or create new:",
@@ -210,74 +227,86 @@ code, tag = d.menu(
if code != d.OK: if code != d.OK:
sys.exit(0) sys.exit(0)
####### MANAGE SUBSCRIPTIONS ######### #######################
# MANAGE SUBSCRIPTIONS
#######################
if tag == "LIST": if tag == "LIST":
mySubscriptions() mySubscriptions()
sys.exit(0) sys.exit(0)
####### NEW LETSENCRYPT HTTPS DOMAIN ######### ###############################
# NEW LETSENCRYPT HTTPS DOMAIN
###############################
if tag == "NEW2": if tag == "NEW2":
# run creating a new IP2TOR subscription # run creating a new IP2TOR subscription
os.system("clear") os.system("clear")
cmd="python /home/admin/config.scripts/blitz.subscriptions.letsencrypt.py create-ssh-dialog" cmd = "python /home/admin/config.scripts/blitz.subscriptions.letsencrypt.py create-ssh-dialog"
print("# running: {0}".format(cmd)) print("# running: {0}".format(cmd))
os.system(cmd) os.system(cmd)
sys.exit(0) sys.exit(0)
####### NEW IP2TOR BRIDGE ######### ###############################
# NEW IP2TOR BRIDGE
###############################
if tag == "NEW1": if tag == "NEW1":
# check if Blitz is running behind TOR # check if Blitz is running behind TOR
cfg.reload() cfg.reload()
if not cfg.run_behind_tor.value: if not cfg.run_behind_tor.value:
Dialog(dialog="dialog",autowidgetsize=True).msgbox(''' Dialog(dialog="dialog", autowidgetsize=True).msgbox('''
The IP2TOR service just makes sense if you run The IP2TOR service just makes sense if you run
your RaspiBlitz behind TOR. your RaspiBlitz behind TOR.
''',title="Info") ''', title="Info")
sys.exit(1) sys.exit(1)
os.system("clear") os.system("clear")
print("please wait ..") print("please wait ..")
# check for which standard services already a active bridge exists # check for which standard services already a active bridge exists
lnd_rest_api=False lnd_rest_api = False
lnd_grpc_api=False lnd_grpc_api = False
lnbits=False lnbits = False
btcpay=False btcpay = False
try: try:
if os.path.isfile(SUBSCRIPTIONS_FILE): if os.path.isfile(SUBSCRIPTIONS_FILE):
os.system("sudo chown admin:admin {0}".format(SUBSCRIPTIONS_FILE)) os.system("sudo chown admin:admin {0}".format(SUBSCRIPTIONS_FILE))
subs = toml.load(SUBSCRIPTIONS_FILE) subs = toml.load(SUBSCRIPTIONS_FILE)
for sub in subs['subscriptions_ip2tor']: for sub in subs['subscriptions_ip2tor']:
if not sub['active']: next if not sub['active']:
if sub['active'] and sub['name'] == LND_REST_API: lnd_rest_api=True continue
if sub['active'] and sub['name'] == LND_GRPC_API: lnd_grpc_api=True if sub['active'] and sub['name'] == LND_REST_API:
if sub['active'] and sub['name'] == LNBITS: lnbits=True lnd_rest_api = True
if sub['active'] and sub['name'] == BTCPAY: btcpay=True if sub['active'] and sub['name'] == LND_GRPC_API:
lnd_grpc_api = True
if sub['active'] and sub['name'] == LNBITS:
lnbits = True
if sub['active'] and sub['name'] == BTCPAY:
btcpay = True
except Exception as e: except Exception as e:
print(e) print(e)
# check if BTCPayserver is installed # check if BTCPayserver is installed
btcPayServer=False btcPayServer = False
statusData= subprocess.run(['/home/admin/config.scripts/bonus.btcpayserver.sh', 'status'], stdout=subprocess.PIPE).stdout.decode('utf-8').strip() statusData = subprocess.run(['/home/admin/config.scripts/bonus.btcpayserver.sh', 'status'],
stdout=subprocess.PIPE).stdout.decode('utf-8').strip()
if statusData.find("installed=1") > -1: if statusData.find("installed=1") > -1:
btcPayServer=True btcPayServer = True
# ask user for which RaspiBlitz service the bridge should be used # ask user for which RaspiBlitz service the bridge should be used
choices = [] choices = list()
choices.append( ("REST","LND REST API {0}".format("--> ALREADY BRIDGED" if lnd_rest_api else "")) ) choices.append(("REST", "LND REST API {0}".format("--> ALREADY BRIDGED" if lnd_rest_api else "")))
choices.append( ("GRPC","LND gRPC API {0}".format("--> ALREADY BRIDGED" if lnd_grpc_api else "")) ) choices.append(("GRPC", "LND gRPC API {0}".format("--> ALREADY BRIDGED" if lnd_grpc_api else "")))
if cfg.lnbits: if cfg.lnbits:
choices.append( ("LNBITS","LNbits Webinterface {0}".format("--> ALREADY BRIDGED" if lnd_grpc_api else "")) ) choices.append(("LNBITS", "LNbits Webinterface {0}".format("--> ALREADY BRIDGED" if lnd_grpc_api else "")))
if btcPayServer: if btcPayServer:
choices.append( ("BTCPAY","BTCPay Server Webinterface {0}".format("--> ALREADY BRIDGED" if btcpay else "")) ) choices.append(("BTCPAY", "BTCPay Server Webinterface {0}".format("--> ALREADY BRIDGED" if btcpay else "")))
choices.append( ("SELF","Create a custom IP2TOR Bridge") ) choices.append(("SELF", "Create a custom IP2TOR Bridge"))
d = Dialog(dialog="dialog",autowidgetsize=True) d = Dialog(dialog="dialog", autowidgetsize=True)
d.set_background_title("RaspiBlitz Subscriptions") d.set_background_title("RaspiBlitz Subscriptions")
code, tag = d.menu( code, tag = d.menu(
"\nChoose RaspiBlitz Service to create Bridge for:", "\nChoose RaspiBlitz Service to create Bridge for:",
@@ -287,31 +316,35 @@ your RaspiBlitz behind TOR.
if code != d.OK: if code != d.OK:
sys.exit(0) sys.exit(0)
servicename=None servicename = None
torAddress=None torAddress = None
torPort=None torPort = None
if tag == "REST": if tag == "REST":
# get TOR address for REST # get TOR address for REST
servicename=LND_REST_API servicename = LND_REST_API
torAddress = subprocess.run(['sudo', 'cat', '/mnt/hdd/tor/lndrest8080/hostname'], stdout=subprocess.PIPE).stdout.decode('utf-8').strip() torAddress = subprocess.run(['sudo', 'cat', '/mnt/hdd/tor/lndrest8080/hostname'],
torPort=8080 stdout=subprocess.PIPE).stdout.decode('utf-8').strip()
torPort = 8080
if tag == "GRPC": if tag == "GRPC":
# get TOR address for GRPC # get TOR address for GRPC
servicename=LND_GRPC_API servicename = LND_GRPC_API
torAddress = subprocess.run(['sudo', 'cat', '/mnt/hdd/tor/lndrpc10009/hostname'], stdout=subprocess.PIPE).stdout.decode('utf-8').strip() torAddress = subprocess.run(['sudo', 'cat', '/mnt/hdd/tor/lndrpc10009/hostname'],
torPort=10009 stdout=subprocess.PIPE).stdout.decode('utf-8').strip()
torPort = 10009
if tag == "LNBITS": if tag == "LNBITS":
# get TOR address for LNBits # get TOR address for LNBits
servicename=LNBITS servicename = LNBITS
torAddress = subprocess.run(['sudo', 'cat', '/mnt/hdd/tor/lnbits/hostname'], stdout=subprocess.PIPE).stdout.decode('utf-8').strip() torAddress = subprocess.run(['sudo', 'cat', '/mnt/hdd/tor/lnbits/hostname'],
torPort=443 stdout=subprocess.PIPE).stdout.decode('utf-8').strip()
torPort = 443
if tag == "BTCPAY": if tag == "BTCPAY":
# get TOR address for BTCPAY # get TOR address for BTCPAY
servicename=BTCPAY servicename = BTCPAY
torAddress = subprocess.run(['sudo', 'cat', '/mnt/hdd/tor/btcpay/hostname'], stdout=subprocess.PIPE).stdout.decode('utf-8').strip() torAddress = subprocess.run(['sudo', 'cat', '/mnt/hdd/tor/btcpay/hostname'],
torPort=443 stdout=subprocess.PIPE).stdout.decode('utf-8').strip()
torPort = 443
if tag == "SELF": if tag == "SELF":
servicename="CUSTOM" servicename = "CUSTOM"
try: try:
# get custom TOR address # get custom TOR address
code, text = d.inputbox( code, text = d.inputbox(
@@ -320,9 +353,11 @@ your RaspiBlitz behind TOR.
title="IP2TOR Bridge Target") title="IP2TOR Bridge Target")
text = text.strip() text = text.strip()
os.system("clear") os.system("clear")
if code != d.OK: sys.exit(0) if code != d.OK:
if len(text) == 0: sys.exit(0) sys.exit(0)
if text.find('.onion') < 0 or text.find(' ') > 0 : if len(text) == 0:
sys.exit(0)
if text.find('.onion') < 0 or text.find(' ') > 0:
print("Not a TOR Onion Address") print("Not a TOR Onion Address")
time.sleep(3) time.sleep(3)
sys.exit(0) sys.exit(0)
@@ -334,8 +369,10 @@ your RaspiBlitz behind TOR.
title="IP2TOR Bridge Target") title="IP2TOR Bridge Target")
text = text.strip() text = text.strip()
os.system("clear") os.system("clear")
if code != d.OK: sys.exit(0) if code != d.OK:
if len(text) == 0: sys.exit(0) sys.exit(0)
if len(text) == 0:
sys.exit(0)
torPort = int(text) torPort = int(text)
except Exception as e: except Exception as e:
print(e) print(e)
@@ -344,7 +381,8 @@ your RaspiBlitz behind TOR.
# run creating a new IP2TOR subscription # run creating a new IP2TOR subscription
os.system("clear") os.system("clear")
cmd="python /home/admin/config.scripts/blitz.subscriptions.ip2tor.py create-ssh-dialog {0} {1} {2}".format(servicename,torAddress,torPort) cmd = "python /home/admin/config.scripts/blitz.subscriptions.ip2tor.py create-ssh-dialog {0} {1} {2}".format(
servicename, torAddress, torPort)
print("# running: {0}".format(cmd)) print("# running: {0}".format(cmd))
os.system(cmd) os.system(cmd)
sys.exit(0) sys.exit(0)