PASSWORD can be provided as SHA256 optionally (Issue #100)
This commit is contained in:
parent
8e3aabb895
commit
2707ba5dcc
2 changed files with 34 additions and 21 deletions
|
@ -55,7 +55,7 @@ If you plan on using the restart button, you have to set your API password. Call
|
|||
#### USERNAME (string)
|
||||
If you want to enable [HTTP basic authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) you can set the desired username here. The `:` character is not allowed.
|
||||
#### PASSWORD (string)
|
||||
Set the password that should be used for authentication. Only if `USERNAME` __and__ `PASSWORD` are set authentication will be enabled.
|
||||
Set the password that should be used for authentication. Only if `USERNAME` __and__ `PASSWORD` are set authentication will be enabled. You may provide the password as a SHA256-hash with the prefix `{sha256}`. For example `PASSWORD = "test"` is functionally equal to `PASSWORD = "{sha256}9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"`. The hash will be converted to lower case automatically. Using the hash provides extra security by not exposing the actual password in plaintext in your configuration.
|
||||
#### CREDENTIALS (string)
|
||||
The credentials in the form of `"username:password"` are now deprecated and should be removed from you configuration. Replace it by specifying `USERNAME` and `PASSWORD`. It will still work though to ensure backwards compatibility.
|
||||
#### ALLOWED_NETWORKS (list)
|
||||
|
|
|
@ -18,6 +18,7 @@ import shlex
|
|||
import subprocess
|
||||
import logging
|
||||
import fnmatch
|
||||
import hashlib
|
||||
from string import Template
|
||||
from http.server import BaseHTTPRequestHandler
|
||||
import urllib.request
|
||||
|
@ -3443,6 +3444,8 @@ def load_settings(settingsfile):
|
|||
if CREDENTIALS and (USERNAME is None or PASSWORD is None):
|
||||
USERNAME = CREDENTIALS.split(":")[0]
|
||||
PASSWORD = ":".join(CREDENTIALS.split(":")[1:])
|
||||
if PASSWORD and PASSWORD.startswith("{sha256}"):
|
||||
PASSWORD = PASSWORD.lower()
|
||||
|
||||
def is_safe_path(basedir, path, follow_symlinks=True):
|
||||
if basedir is None:
|
||||
|
@ -4498,18 +4501,24 @@ class AuthHandler(RequestHandler):
|
|||
if not verify_hostname(self.headers.get('Host', '')):
|
||||
self.do_BLOCK(403, "Forbidden")
|
||||
return
|
||||
authorization = self.headers.get('Authorization', None)
|
||||
token = base64.b64encode(bytes("%s:%s" % (USERNAME, PASSWORD), "utf-8"))
|
||||
if authorization is None:
|
||||
header = self.headers.get('Authorization', None)
|
||||
if header is None:
|
||||
self.do_AUTHHEAD()
|
||||
self.wfile.write(bytes('no auth header received', 'utf-8'))
|
||||
pass
|
||||
elif authorization == 'Basic %s' % token.decode('utf-8'):
|
||||
if BANLIMIT:
|
||||
FAIL2BAN_IPS.pop(self.client_address[0], None)
|
||||
super().do_GET()
|
||||
pass
|
||||
else:
|
||||
authorization = header.split()
|
||||
if len(authorization) == 2 and authorization[0] == "Basic":
|
||||
plain = base64.b64decode(authorization[1]).decode("utf-8")
|
||||
parts = plain.split(':')
|
||||
username = parts[0]
|
||||
password = ":".join(parts[1:])
|
||||
if PASSWORD.startswith("{sha256}"):
|
||||
password = "{sha256}%s" % hashlib.sha256(password.encode("utf-8")).hexdigest()
|
||||
if username == USERNAME and password == PASSWORD:
|
||||
if BANLIMIT:
|
||||
FAIL2BAN_IPS.pop(self.client_address[0], None)
|
||||
super().do_GET()
|
||||
return
|
||||
if BANLIMIT:
|
||||
bancounter = FAIL2BAN_IPS.get(self.client_address[0], 1)
|
||||
if bancounter >= BANLIMIT:
|
||||
|
@ -4520,24 +4529,29 @@ class AuthHandler(RequestHandler):
|
|||
FAIL2BAN_IPS[self.client_address[0]] = bancounter + 1
|
||||
self.do_AUTHHEAD()
|
||||
self.wfile.write(bytes('Authentication required', 'utf-8'))
|
||||
pass
|
||||
|
||||
def do_POST(self):
|
||||
if not verify_hostname(self.headers.get('Host', '')):
|
||||
self.do_BLOCK(403, "Forbidden")
|
||||
return
|
||||
authorization = self.headers.get('Authorization', None)
|
||||
token = base64.b64encode(bytes("%s:%s" % (USERNAME, PASSWORD), "utf-8"))
|
||||
if authorization is None:
|
||||
header = self.headers.get('Authorization', None)
|
||||
if header is None:
|
||||
self.do_AUTHHEAD()
|
||||
self.wfile.write(bytes('no auth header received', 'utf-8'))
|
||||
pass
|
||||
elif authorization == 'Basic %s' % token.decode('utf-8'):
|
||||
if BANLIMIT:
|
||||
FAIL2BAN_IPS.pop(self.client_address[0], None)
|
||||
super().do_POST()
|
||||
pass
|
||||
else:
|
||||
authorization = header.split()
|
||||
if len(authorization) == 2 and authorization[0] == "Basic":
|
||||
plain = base64.b64decode(authorization[1]).decode("utf-8")
|
||||
parts = plain.split(':')
|
||||
username = parts[0]
|
||||
password = ":".join(parts[1:])
|
||||
if PASSWORD.startswith("{sha256}"):
|
||||
password = "{sha256}%s" % hashlib.sha256(password.encode("utf-8")).hexdigest()
|
||||
if username == USERNAME and password == PASSWORD:
|
||||
if BANLIMIT:
|
||||
FAIL2BAN_IPS.pop(self.client_address[0], None)
|
||||
super().do_POST()
|
||||
return
|
||||
if BANLIMIT:
|
||||
bancounter = FAIL2BAN_IPS.get(self.client_address[0], 1)
|
||||
if bancounter >= BANLIMIT:
|
||||
|
@ -4548,7 +4562,6 @@ class AuthHandler(RequestHandler):
|
|||
FAIL2BAN_IPS[self.client_address[0]] = bancounter + 1
|
||||
self.do_AUTHHEAD()
|
||||
self.wfile.write(bytes('Authentication required', 'utf-8'))
|
||||
pass
|
||||
|
||||
class SimpleServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
|
||||
daemon_threads = True
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue