| Brad Bishop | 1932369 | 2019-04-05 15:28:33 -0400 | [diff] [blame] | 1 | #! /usr/bin/env python3 | 
|  | 2 | # | 
|  | 3 | # Copyright (C) 2018 Garmin Ltd. | 
|  | 4 | # | 
| Brad Bishop | c342db3 | 2019-05-15 21:57:59 -0400 | [diff] [blame] | 5 | # SPDX-License-Identifier: GPL-2.0-only | 
| Brad Bishop | 1932369 | 2019-04-05 15:28:33 -0400 | [diff] [blame] | 6 | # | 
| Brad Bishop | c342db3 | 2019-05-15 21:57:59 -0400 | [diff] [blame] | 7 |  | 
| Brad Bishop | 1932369 | 2019-04-05 15:28:33 -0400 | [diff] [blame] | 8 | import os | 
|  | 9 | import sys | 
|  | 10 | import logging | 
|  | 11 | import argparse | 
|  | 12 | import sqlite3 | 
| Andrew Geissler | 5199d83 | 2021-09-24 16:47:35 -0500 | [diff] [blame] | 13 | import warnings | 
| Patrick Williams | ac13d5f | 2023-11-24 18:59:46 -0600 | [diff] [blame] | 14 |  | 
| Andrew Geissler | 5199d83 | 2021-09-24 16:47:35 -0500 | [diff] [blame] | 15 | warnings.simplefilter("default") | 
| Brad Bishop | 1932369 | 2019-04-05 15:28:33 -0400 | [diff] [blame] | 16 |  | 
| Patrick Williams | ac13d5f | 2023-11-24 18:59:46 -0600 | [diff] [blame] | 17 | sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(__file__)), "lib")) | 
| Brad Bishop | 1932369 | 2019-04-05 15:28:33 -0400 | [diff] [blame] | 18 |  | 
|  | 19 | import hashserv | 
| Patrick Williams | ac13d5f | 2023-11-24 18:59:46 -0600 | [diff] [blame] | 20 | from hashserv.server import DEFAULT_ANON_PERMS | 
| Brad Bishop | 1932369 | 2019-04-05 15:28:33 -0400 | [diff] [blame] | 21 |  | 
|  | 22 | VERSION = "1.0.0" | 
|  | 23 |  | 
| Patrick Williams | ac13d5f | 2023-11-24 18:59:46 -0600 | [diff] [blame] | 24 | DEFAULT_BIND = "unix://./hashserve.sock" | 
| Brad Bishop | a34c030 | 2019-09-23 22:34:48 -0400 | [diff] [blame] | 25 |  | 
| Brad Bishop | 1932369 | 2019-04-05 15:28:33 -0400 | [diff] [blame] | 26 |  | 
|  | 27 | def main(): | 
| Patrick Williams | ac13d5f | 2023-11-24 18:59:46 -0600 | [diff] [blame] | 28 | parser = argparse.ArgumentParser( | 
|  | 29 | description="Hash Equivalence Reference Server. Version=%s" % VERSION, | 
|  | 30 | formatter_class=argparse.RawTextHelpFormatter, | 
|  | 31 | epilog=""" | 
|  | 32 | The 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 | 
| Brad Bishop | a34c030 | 2019-09-23 22:34:48 -0400 | [diff] [blame] | 36 |  | 
| Patrick Williams | ac13d5f | 2023-11-24 18:59:46 -0600 | [diff] [blame] | 37 | To 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 |  | 
|  | 41 | Note that the default Anonymous permissions are designed to not break existing | 
|  | 42 | server instances when upgrading, but are not particularly secure defaults. If | 
|  | 43 | you 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 | 
|  | 45 | give un-authenticated users no access at all. | 
|  | 46 |  | 
|  | 47 | Setting "--anon-perms @all" or "--anon-perms @user-admin" is not allowed, since | 
|  | 48 | this would allow anonymous users to manage all users accounts, which is a bad | 
|  | 49 | idea. | 
|  | 50 |  | 
|  | 51 | If you are using user authentication, you should run your server in websockets | 
|  | 52 | mode with an SSL terminating load balancer in front of it (as this server does | 
|  | 53 | not implement SSL). Otherwise all usernames and passwords will be transmitted | 
|  | 54 | in the clear. When configured this way, clients can connect using a secure | 
|  | 55 | websocket, as in "wss://SERVER:PORT" | 
| Patrick Williams | 169d7bc | 2024-01-05 11:33:25 -0600 | [diff] [blame] | 56 |  | 
|  | 57 | The 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 | 
| Patrick Williams | ac13d5f | 2023-11-24 18:59:46 -0600 | [diff] [blame] | 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 | ) | 
| Brad Bishop | 1932369 | 2019-04-05 15:28:33 -0400 | [diff] [blame] | 128 |  | 
|  | 129 | args = parser.parse_args() | 
|  | 130 |  | 
| Patrick Williams | ac13d5f | 2023-11-24 18:59:46 -0600 | [diff] [blame] | 131 | logger = logging.getLogger("hashserv") | 
| Brad Bishop | 1932369 | 2019-04-05 15:28:33 -0400 | [diff] [blame] | 132 |  | 
|  | 133 | level = getattr(logging, args.log.upper(), None) | 
|  | 134 | if not isinstance(level, int): | 
| Patrick Williams | ac13d5f | 2023-11-24 18:59:46 -0600 | [diff] [blame] | 135 | raise ValueError("Invalid log level: %s" % args.log) | 
| Brad Bishop | 1932369 | 2019-04-05 15:28:33 -0400 | [diff] [blame] | 136 |  | 
|  | 137 | logger.setLevel(level) | 
|  | 138 | console = logging.StreamHandler() | 
|  | 139 | console.setLevel(level) | 
|  | 140 | logger.addHandler(console) | 
|  | 141 |  | 
| Patrick Williams | ac13d5f | 2023-11-24 18:59:46 -0600 | [diff] [blame] | 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 | ) | 
| Brad Bishop | 1932369 | 2019-04-05 15:28:33 -0400 | [diff] [blame] | 159 | server.serve_forever() | 
|  | 160 | return 0 | 
|  | 161 |  | 
| Brad Bishop | a34c030 | 2019-09-23 22:34:48 -0400 | [diff] [blame] | 162 |  | 
| Patrick Williams | ac13d5f | 2023-11-24 18:59:46 -0600 | [diff] [blame] | 163 | if __name__ == "__main__": | 
| Brad Bishop | 1932369 | 2019-04-05 15:28:33 -0400 | [diff] [blame] | 164 | try: | 
|  | 165 | ret = main() | 
|  | 166 | except Exception: | 
|  | 167 | ret = 1 | 
|  | 168 | import traceback | 
| Patrick Williams | ac13d5f | 2023-11-24 18:59:46 -0600 | [diff] [blame] | 169 |  | 
| Brad Bishop | 1932369 | 2019-04-05 15:28:33 -0400 | [diff] [blame] | 170 | traceback.print_exc() | 
|  | 171 | sys.exit(ret) |