1#! /usr/bin/env python3 2# 3# Copyright (C) 2018 Garmin Ltd. 4# 5# SPDX-License-Identifier: GPL-2.0-only 6# 7 8import os 9import sys 10import logging 11import argparse 12import sqlite3 13import warnings 14 15warnings.simplefilter("default") 16 17sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(__file__)), "lib")) 18 19import hashserv 20from hashserv.server import DEFAULT_ANON_PERMS 21 22VERSION = "1.0.0" 23 24DEFAULT_BIND = "unix://./hashserve.sock" 25 26 27def main(): 28 parser = argparse.ArgumentParser( 29 description="Hash Equivalence Reference Server. Version=%s" % VERSION, 30 formatter_class=argparse.RawTextHelpFormatter, 31 epilog=""" 32The bind address may take one of the following formats: 33 unix://PATH - Bind to unix domain socket at PATH 34 ws://ADDRESS:PORT - Bind to websocket on ADDRESS:PORT 35 ADDRESS:PORT - Bind to raw TCP socket on ADDRESS:PORT 36 37To bind to all addresses, leave the ADDRESS empty, e.g. "--bind :8686" or 38"--bind ws://:8686". To bind to a specific IPv6 address, enclose the address in 39"[]", e.g. "--bind [::1]:8686" or "--bind ws://[::1]:8686" 40 41Note that the default Anonymous permissions are designed to not break existing 42server instances when upgrading, but are not particularly secure defaults. If 43you want to use authentication, it is recommended that you use "--anon-perms 44@read" to only give anonymous users read access, or "--anon-perms @none" to 45give un-authenticated users no access at all. 46 47Setting "--anon-perms @all" or "--anon-perms @user-admin" is not allowed, since 48this would allow anonymous users to manage all users accounts, which is a bad 49idea. 50 51If you are using user authentication, you should run your server in websockets 52mode with an SSL terminating load balancer in front of it (as this server does 53not implement SSL). Otherwise all usernames and passwords will be transmitted 54in the clear. When configured this way, clients can connect using a secure 55websocket, as in "wss://SERVER:PORT" 56 57The following permissions are supported by the server: 58 59 @none - No permissions 60 @read - The ability to read equivalent hashes from the server 61 @report - The ability to report equivalent hashes to the server 62 @db-admin - Manage the hash database(s). This includes cleaning the 63 database, removing hashes, etc. 64 @user-admin - The ability to manage user accounts. This includes, creating 65 users, deleting users, resetting login tokens, and assigning 66 permissions. 67 @all - All possible permissions, including any that may be added 68 in the future 69 """, 70 ) 71 72 parser.add_argument( 73 "-b", 74 "--bind", 75 default=os.environ.get("HASHSERVER_BIND", DEFAULT_BIND), 76 help='Bind address (default $HASHSERVER_BIND, "%(default)s")', 77 ) 78 parser.add_argument( 79 "-d", 80 "--database", 81 default=os.environ.get("HASHSERVER_DB", "./hashserv.db"), 82 help='Database file (default $HASHSERVER_DB, "%(default)s")', 83 ) 84 parser.add_argument( 85 "-l", 86 "--log", 87 default=os.environ.get("HASHSERVER_LOG_LEVEL", "WARNING"), 88 help='Set logging level (default $HASHSERVER_LOG_LEVEL, "%(default)s")', 89 ) 90 parser.add_argument( 91 "-u", 92 "--upstream", 93 default=os.environ.get("HASHSERVER_UPSTREAM", None), 94 help="Upstream hashserv to pull hashes from ($HASHSERVER_UPSTREAM)", 95 ) 96 parser.add_argument( 97 "-r", 98 "--read-only", 99 action="store_true", 100 help="Disallow write operations from clients ($HASHSERVER_READ_ONLY)", 101 ) 102 parser.add_argument( 103 "--db-username", 104 default=os.environ.get("HASHSERVER_DB_USERNAME", None), 105 help="Database username ($HASHSERVER_DB_USERNAME)", 106 ) 107 parser.add_argument( 108 "--db-password", 109 default=os.environ.get("HASHSERVER_DB_PASSWORD", None), 110 help="Database password ($HASHSERVER_DB_PASSWORD)", 111 ) 112 parser.add_argument( 113 "--anon-perms", 114 metavar="PERM[,PERM[,...]]", 115 default=os.environ.get("HASHSERVER_ANON_PERMS", ",".join(DEFAULT_ANON_PERMS)), 116 help='Permissions to give anonymous users (default $HASHSERVER_ANON_PERMS, "%(default)s")', 117 ) 118 parser.add_argument( 119 "--admin-user", 120 default=os.environ.get("HASHSERVER_ADMIN_USER", None), 121 help="Create default admin user with name ADMIN_USER ($HASHSERVER_ADMIN_USER)", 122 ) 123 parser.add_argument( 124 "--admin-password", 125 default=os.environ.get("HASHSERVER_ADMIN_PASSWORD", None), 126 help="Create default admin user with password ADMIN_PASSWORD ($HASHSERVER_ADMIN_PASSWORD)", 127 ) 128 129 args = parser.parse_args() 130 131 logger = logging.getLogger("hashserv") 132 133 level = getattr(logging, args.log.upper(), None) 134 if not isinstance(level, int): 135 raise ValueError("Invalid log level: %s (Try ERROR/WARNING/INFO/DEBUG)" % args.log) 136 137 logger.setLevel(level) 138 console = logging.StreamHandler() 139 console.setLevel(level) 140 logger.addHandler(console) 141 142 read_only = (os.environ.get("HASHSERVER_READ_ONLY", "0") == "1") or args.read_only 143 if "," in args.anon_perms: 144 anon_perms = args.anon_perms.split(",") 145 else: 146 anon_perms = args.anon_perms.split() 147 148 server = hashserv.create_server( 149 args.bind, 150 args.database, 151 upstream=args.upstream, 152 read_only=read_only, 153 db_username=args.db_username, 154 db_password=args.db_password, 155 anon_perms=anon_perms, 156 admin_username=args.admin_user, 157 admin_password=args.admin_password, 158 ) 159 server.serve_forever() 160 return 0 161 162 163if __name__ == "__main__": 164 try: 165 ret = main() 166 except Exception: 167 ret = 1 168 import traceback 169 170 traceback.print_exc() 171 sys.exit(ret) 172