Added sesame feature

This commit is contained in:
Daniel Perna 2018-01-25 00:25:46 +01:00
parent 4ffe967b25
commit 76d227b274
4 changed files with 39 additions and 17 deletions

View file

@ -64,7 +64,9 @@ Set this variable to `True` to enable Git integration. This feature requires [Gi
To push local commits to a remote repository, you have to add the remote manually: `git remote add origin ssh://somehost:/user/repo.git` To push local commits to a remote repository, you have to add the remote manually: `git remote add origin ssh://somehost:/user/repo.git`
Verify, that the user that is running the configurator is allowed to push without any interaction (by using SSH PubKey authentication for example). Verify, that the user that is running the configurator is allowed to push without any interaction (by using SSH PubKey authentication for example).
#### DIRSFIRST (bool) #### DIRSFIRST (bool)
if set to `true`, directories will be displayed at the top If set to `true`, directories will be displayed at the top.
#### SESAME (string)
If set to _somesecretkeynobodycanguess_, you can browse to `https://your.configurator:3218/somesecretkeynobodycanguess` from any IP, and it will be removed from the `BANNED_IPS` list (in case it has been banned before) and added to the `ALLOWED_NETWORKS` list. Once the request has been processed you will automatically be redirected to the configurator. Think of this as dynamically allowing access from untrusted IPs by providing a secret key (_open sesame!_). Keep in mind, that once the IP has been added, you will either have to restart the configurator or manually remove the IP through the _Newwork status_ to revoke access.
__Note regarding `ALLOWED_NETWORKS`, `BANNED_IPS` and `BANLIMIT`__: __Note regarding `ALLOWED_NETWORKS`, `BANNED_IPS` and `BANLIMIT`__:
The way this is implemented works in the following order: The way this is implemented works in the following order:

View file

@ -1,8 +1,10 @@
Version 0.2.5 (2018-) Version 0.2.5 (2018-01-25)
- Added warning-logs for access failure @danielperna84 - Added warning-logs for access failure @danielperna84
- Added transparency to whitespace characters @danielperna84 - Added transparency to whitespace characters @danielperna84
- Using external repository for Docker @Munsio - Using external repository for Docker @Munsio
- Modify BANNED_IPS and ALLOWED_NETWORKS at runtime @danielperna84 - Modify BANNED_IPS and ALLOWED_NETWORKS at runtime @danielperna84
- Use relative paths in webserver @danielperna84
- Added "Sesame" feature @danielperna84
Version 0.2.4 (2018-01-02) Version 0.2.4 (2018-01-02)
- Added YAML linting @AtoxIO - Added YAML linting @AtoxIO

View file

@ -27,10 +27,11 @@ from urllib.parse import urlparse, parse_qs, unquote
### Some options for you to change ### Some options for you to change
LISTENIP = "0.0.0.0" LISTENIP = "0.0.0.0"
LISTENPORT = 3218 LISTENPORT = 3218
# Set BASEPATH to something like "/home/hass/.homeassistant/" if you're not running the # Set BASEPATH to something like "/home/hass/.homeassistant/" if you're not
# configurator from that path # running the configurator from that path
BASEPATH = None BASEPATH = None
# Set the paths to a certificate and the key if you're using SSL, e.g "/etc/ssl/certs/mycert.pem" # Set the paths to a certificate and the key if you're using SSL,
# e.g "/etc/ssl/certs/mycert.pem"
SSL_CERTIFICATE = None SSL_CERTIFICATE = None
SSL_KEY = None SSL_KEY = None
# Set the destination where the HASS API is reachable # Set the destination where the HASS API is reachable
@ -38,24 +39,27 @@ HASS_API = "http://127.0.0.1:8123/api/"
# If a password is required to access the API, set it in the form of "password" # If a password is required to access the API, set it in the form of "password"
# if you have HA ignoring SSL locally this is not needed if on same machine. # if you have HA ignoring SSL locally this is not needed if on same machine.
HASS_API_PASSWORD = None HASS_API_PASSWORD = None
# To enable authentication, set the credentials in the form of "username:password" # Enable authentication, set the credentials in the form of "username:password"
CREDENTIALS = None CREDENTIALS = None
# Limit access to the configurator by adding allowed IP addresses / networks to the list, # Limit access to the configurator by adding allowed IP addresses / networks to
# e.g ALLOWED_NETWORKS = ["192.168.0.0/24", "172.16.47.23"] # the list, e.g ALLOWED_NETWORKS = ["192.168.0.0/24", "172.16.47.23"]
ALLOWED_NETWORKS = [] ALLOWED_NETWORKS = []
# List of statically banned IP addresses, e.g. ["1.1.1.1", "2.2.2.2"] # List of statically banned IP addresses, e.g. ["1.1.1.1", "2.2.2.2"]
BANNED_IPS = [] BANNED_IPS = []
# Ban IPs after n failed login attempts. Restart service to reset banning. The default # Ban IPs after n failed login attempts. Restart service to reset banning.
# of `0` disables this feature. # The default of `0` disables this feature.
BANLIMIT = 0 BANLIMIT = 0
# Enable git integration. GitPython (https://gitpython.readthedocs.io/en/stable/) has # Enable git integration.
# to be installed. # GitPython (https://gitpython.readthedocs.io/en/stable/) has to be installed.
GIT = False GIT = False
# Files to ignore in the UI. A good example list that cleans up the UI is # Files to ignore in the UI. A good example list that cleans up the UI is
# [".*", "*.log", "deps", "icloud", "*.conf", "*.json", "certs", "__pycache__"] # [".*", "*.log", "deps", "icloud", "*.conf", "*.json", "certs", "__pycache__"]
IGNORE_PATTERN = [] IGNORE_PATTERN = []
# if DIRSFIRST is set to `true`, directories will be displayed at the top # if DIRSFIRST is set to `true`, directories will be displayed at the top
DIRSFIRST = False DIRSFIRST = False
# Sesame token. Browse to the configurator URL + /secrettoken to unban your
# client IP and add it to the list of allowed IPs.
SESAME = None
### End of options ### End of options
LOGLEVEL = logging.INFO LOGLEVEL = logging.INFO
@ -63,7 +67,8 @@ LOG = logging.getLogger(__name__)
LOG.setLevel(LOGLEVEL) LOG.setLevel(LOGLEVEL)
SO = logging.StreamHandler(sys.stdout) SO = logging.StreamHandler(sys.stdout)
SO.setLevel(LOGLEVEL) SO.setLevel(LOGLEVEL)
SO.setFormatter(logging.Formatter('%(levelname)s:%(asctime)s:%(name)s:%(message)s')) SO.setFormatter(
logging.Formatter('%(levelname)s:%(asctime)s:%(name)s:%(message)s'))
LOG.addHandler(SO) LOG.addHandler(SO)
RELEASEURL = "https://api.github.com/repos/danielperna84/hass-configurator/releases/latest" RELEASEURL = "https://api.github.com/repos/danielperna84/hass-configurator/releases/latest"
VERSION = "0.2.4" VERSION = "0.2.4"
@ -3092,8 +3097,8 @@ def signal_handler(sig, frame):
def load_settings(settingsfile): def load_settings(settingsfile):
global LISTENIP, LISTENPORT, BASEPATH, SSL_CERTIFICATE, SSL_KEY, HASS_API, \ global LISTENIP, LISTENPORT, BASEPATH, SSL_CERTIFICATE, SSL_KEY, HASS_API, \
HASS_API_PASSWORD, CREDENTIALS, ALLOWED_NETWORKS, BANNED_IPS, BANLIMIT, DEV, \ HASS_API_PASSWORD, CREDENTIALS, ALLOWED_NETWORKS, BANNED_IPS, BANLIMIT, \
IGNORE_PATTERN, DIRSFIRST DEV, IGNORE_PATTERN, DIRSFIRST, SESAME
try: try:
if os.path.isfile(settingsfile): if os.path.isfile(settingsfile):
with open(settingsfile) as fptr: with open(settingsfile) as fptr:
@ -3112,6 +3117,7 @@ def load_settings(settingsfile):
DEV = settings.get("DEV", DEV) DEV = settings.get("DEV", DEV)
IGNORE_PATTERN = settings.get("IGNORE_PATTERN", IGNORE_PATTERN) IGNORE_PATTERN = settings.get("IGNORE_PATTERN", IGNORE_PATTERN)
DIRSFIRST = settings.get("DIRSFIRST", DIRSFIRST) DIRSFIRST = settings.get("DIRSFIRST", DIRSFIRST)
SESAME = settings.get("SESAME", SESAME)
except Exception as err: except Exception as err:
LOG.warning(err) LOG.warning(err)
LOG.warning("Not loading static settings") LOG.warning("Not loading static settings")
@ -3219,10 +3225,21 @@ class RequestHandler(BaseHTTPRequestHandler):
self.wfile.write(bytes("Policy not fulfilled", "utf8")) self.wfile.write(bytes("Policy not fulfilled", "utf8"))
def do_GET(self): def do_GET(self):
req = urlparse(self.path)
if SESAME:
if req.path.endswith("/%s" % SESAME):
if self.client_address[0] not in ALLOWED_NETWORKS:
ALLOWED_NETWORKS.append(self.client_address[0])
if self.client_address[0] in BANNED_IPS:
BANNED_IPS.remove(self.client_address[0])
url = req.path[:req.path.rfind(SESAME)]
self.send_response(302)
self.send_header('Location', url)
self.end_headers()
return
if not check_access(self.client_address[0]): if not check_access(self.client_address[0]):
self.do_BLOCK() self.do_BLOCK()
return return
req = urlparse(self.path)
query = parse_qs(req.query) query = parse_qs(req.query)
self.send_response(200) self.send_response(200)
if req.path.endswith('/api/file'): if req.path.endswith('/api/file'):

View file

@ -11,5 +11,6 @@
"BANNED_IPS": [], "BANNED_IPS": [],
"BANLIMIT": 0, "BANLIMIT": 0,
"IGNORE_PATTERN": [], "IGNORE_PATTERN": [],
"DIRSFIRST": false "DIRSFIRST": false,
"SESAME": null
} }