def start() -> None: """ Ran by the webserver before it boots """ try: # Chains are given HMAC keys when created. If found, we write them to storage. key_id = secrets.get_dc_secret("hmac-id") _log.info( "HMAC keys were given to this chain on-boot. Writing them to storage." ) storage.put_object_as_json( f"KEYS/{key_id}", { "id": key_id, "key": secrets.get_dc_secret("hmac-key"), "root": True, "registration_time": 0 }) except exceptions.NotFound: _log.info( "No HMAC keys were given to this chain on-boot. Skipping credential storage write." ) _log.info("Checking if redisearch indexes need to be regenerated") redisearch.generate_indexes_if_necessary() _log.info("Finish pre-boot successful")
def load_address(network: str) -> Tuple[str, bit.wallet.BaseKey]: if network == "BTC_MAINNET": private_key = bit.Key.from_bytes(base64.b64decode(secrets.get_dc_secret("btc-mainnet-private-key"))) if network == "BTC_TESTNET3": private_key = bit.PrivateKeyTestnet.from_bytes(base64.b64decode(secrets.get_dc_secret("btc-testnet3-private-key"))) interchain_address = private_key.address return interchain_address, private_key
def start() -> None: """ Ran by the webserver before it boots """ _log.info("Checking if api key migrations need to be performed") api_key_dao.perform_api_key_migration_v1_if_necessary() try: # Chains are given HMAC keys when created. If found and root key doesn't already exist, we write them to storage. key_id = secrets.get_dc_secret("hmac-id") except exceptions.NotFound: _log.info( "No HMAC keys were given to this chain on-boot. Skipping credential storage write." ) else: try: api_key_dao.get_api_key(key_id, interchain=False) except exceptions.NotFound: _log.info( "HMAC keys were given to this chain on-boot. Writing them to storage." ) api_key = api_key_model.new_root_key( key_id, secrets.get_dc_secret("hmac-key")) api_key_dao.save_api_key(api_key) else: _log.info( "HMAC key secret already exists. Skipping credential storage write." ) if redisearch.ENABLED: _log.info("Checking if redisearch indexes need to be regenerated") redisearch.generate_indexes_if_necessary() _log.info("Finish pre-boot successful")
def start() -> None: """ Ran by the webserver before it boots """ try: # New chains are often given HMAC keys when created. If found, we write them to storage. key_id = secrets.get_dc_secret("hmac-id") json_key = json.dumps( { "id": key_id, "key": secrets.get_dc_secret("hmac-key"), "root": True, "registration_time": 0 }, separators=(",", ":")) _log.info( "HMAC keys were given to this chain on-boot. Writing them to storage." ) storage.put(f"KEYS/{key_id}", json_key.encode("utf-8")) except exceptions.NotFound: _log.info( "No HMAC keys were given to this chain on-boot. Skipping cretential storage write." ) _log.info("Checking if redisearch indexes need to be regenerated") try: redisearch.generate_indexes_if_necessary() except Exception: if not error_allowed: raise _log.info("Finish build successful")
def load_address(network: str) -> keys.PrivateKey: if network == "ETH_MAINNET": private_key = keys.PrivateKey( base64.b64decode(secrets.get_dc_secret("eth-mainnet-private-key"))) if network == "ETH_ROPSTEN": private_key = keys.PrivateKey( base64.b64decode(secrets.get_dc_secret("eth-ropsten-private-key"))) if network == "ETC_MAINNET": private_key = keys.PrivateKey( base64.b64decode(secrets.get_dc_secret("etc-mainnet-private-key"))) if network == "ETC_MORDEN": private_key = keys.PrivateKey( base64.b64decode(secrets.get_dc_secret("etc-morden-private-key"))) interchain_address = private_key.public_key.to_checksum_address() return interchain_address, private_key
def get_login(as_token: bool = False) -> "DockerLogin": """ returns auth from container registry service which will be stored in environment variable """ return { "username": REGISTRY_USERNAME, "password": secrets.get_dc_secret("registry-password") }
def start() -> None: """ Ran by the webserver before it boots """ try: # New chains are often given HMAC keys when created. If found, we write them to storage. key_id = secrets.get_dc_secret("hmac-id") json_key = json.dumps( { "id": key_id, "key": secrets.get_dc_secret("hmac-key"), "root": True, "registration_time": 0 }, separators=(",", ":")) _log.info( "HMAC keys were given to this chain on-boot. Writing them to storage." ) storage.put(f"KEYS/{key_id}", json_key.encode("utf-8")) except exceptions.NotFound: _log.info( "No HMAC keys were given to this chain on-boot. Skipping cretential storage write." ) try: btc.BTCClient("BTC_MAINNET").register_address() btc.BTCClient("BTC_TESTNET3").register_address() except Exception: _log.exception( "!WARNING! Failed to register bitcoin address(es) with remote bitcoin RPC nodes" ) _log.info("Rehydrating the redis transaction list cache") try: transaction_type_dao.rehydrate_transaction_types() except Exception: if not error_allowed: raise _log.info("Finish build successful")
def get_my_keys() -> "DCKeys": """ Memo-ized retrieval for the keys owned by this chain """ global _my_keys if _my_keys is None: _my_keys = DCKeys(pull_keys=False).initialize( level=int(LEVEL), scheme=PROOF_SCHEME, private_key_string=secrets.get_dc_secret("private-key"), hash_type=HASH, encryption=ENCRYPTION) return _my_keys
def delete_api_key_v1(key_id: str) -> None: """Delete api key by key ID ONLY if it is not the last key on the chain Args: key_id: ID of api key to delete """ # Don't allow removal of reserved keys if key_id.startswith("SC_") or key_id.startswith("INTERCHAIN"): raise exceptions.ActionForbidden("Cannot delete reserved API keys") # Don't allow removal of root keys if secrets.get_dc_secret("hmac-id") == key_id: raise exceptions.ActionForbidden("Cannot remove root API key") # Delete the actual key after previous checks pass api_key_dao.delete_api_key(key_id=key_id, interchain=False)
def delete_api_key_v1(key_id: str) -> None: """Delete api key by key ID ONLY if it is not the last key on the chain Args: key_id: ID of api key to delete """ # Don't allow removal of root keys root_key_id = secrets.get_dc_secret("hmac-id") if root_key_id == key_id: raise exceptions.ActionForbidden("Cannot remove root API key") # Don't allow removal of reserved keys if key_id.startswith("SC_") or key_id.startswith( "INTERCHAIN") or key_id.startswith("WEB_"): raise exceptions.ActionForbidden("cannot delete reserved API keys") # Delete the actual key after previous checks pass if not authorization.remove_auth_key(auth_key_id=key_id, interchain=False): raise RuntimeError("Unkown error deleting key from storage")
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the Apache License for the specific # language governing permissions and limitations under the Apache License. import os from typing import Any, TYPE_CHECKING import docker from dragonchain.lib.interfaces import secrets if TYPE_CHECKING: from dragonchain.lib.types import DockerLogin REGISTRY_USERNAME = os.environ["REGISTRY_USERNAME"] REGISTRY_PASSWORD = secrets.get_dc_secret("registry-password") def get_login(as_token: bool = False) -> "DockerLogin": """ returns auth from container registry service which will be stored in environment variable """ return {"username": REGISTRY_USERNAME, "password": REGISTRY_PASSWORD} def delete_image(repository: Any, image_digest: str) -> None: """ Remove image from Docker registry """ docker.remove_image(image_digest)
def verify_request_authorization( # noqa: C901 authorization: str, http_verb: str, full_path: str, dcid: str, timestamp: str, content_type: str, content: bytes, interchain: bool, root_only: bool, ) -> None: """Verify an http request to the webserver Args: authorization: Authorization header of the request http_verb: HTTP Verb of the request (i.e. GET, POST, etc) full_path: full path of the request after the FQDN (including any query parameters) (i.e. /chains/transaction) dcid: dragonchain header of the request timestamp: timestamp header of the request content-type: content-type header of the request (if it exists) content: byte object of the body of the request (if it exists) interchain: boolean whether to use interchain keys to check or not root_only: boolean whether or not root is required Raises: exceptions.UnauthorizedException (with message) when the authorization is not valid exceptions.APIRateLimitException (with message) when rate limit is currently exceeded for the provided api key id """ if dcid != keys.get_public_id(): raise exceptions.UnauthorizedException("Incorrect Dragonchain ID") try: # Note, noqa for typing on re.searches are because we explicitly catch the exceptions and handle below version = re.search("^DC(.*)-HMAC", authorization).group(1) # noqa: T484 if version == "1": hash_type = re.search("HMAC-(.*) ", authorization).group(1) # noqa: T484 try: supported_hash = get_supported_hmac_hash(hash_type) except ValueError: raise exceptions.UnauthorizedException( "Unsupported HMAC Hash Type") # Make sure clock drift isn't too far to prevent replays now = get_now_datetime() request_time = None # Tolerate given timestamps both with/without decimals of a second if "." in timestamp: request_time = datetime.datetime.strptime( timestamp, "%Y-%m-%dT%H:%M:%S.%fZ") else: request_time = datetime.datetime.strptime( timestamp, "%Y-%m-%dT%H:%M:%SZ") delta = datetime.timedelta(seconds=TIMEOUT_SEC) # Allow all requests within +/- TIMEOUT_SEC seconds of the chain's curent time if now + delta < request_time or now - delta > request_time: raise exceptions.UnauthorizedException( "Timestamp of request too skewed") hmac_index = authorization.rfind(":") if hmac_index == -1: raise exceptions.UnauthorizedException( "Malformed Authorization Header") hmac = base64.b64decode(authorization[hmac_index + 1:]) message_string = get_hmac_message_string(http_verb, full_path, dcid, timestamp, content_type, content, supported_hash) try: auth_key_id = re.search(" (.*):", authorization).group(1) # noqa: T484 if "/" in auth_key_id: _log.info( f"Authorization failure from potentially malicious key id {auth_key_id}" ) raise exceptions.UnauthorizedException( "Invalid HMAC Authentication") if root_only and auth_key_id != dc_secrets.get_dc_secret( "hmac-id"): raise exceptions.ActionForbidden( "this action can only be performed with root auth key") auth_key = get_auth_key(auth_key_id, interchain) if not auth_key: _log.info( f"Authorization failure from key that does not exist {auth_key_id}" ) raise exceptions.UnauthorizedException( "Invalid HMAC Authentication") # Check if this key should be rate limited (does not apply to interchain keys) if not interchain and should_rate_limit(auth_key_id): raise exceptions.APIRateLimitException( f"API Rate Limit Exceeded. {RATE_LIMIT} requests allowed per minute." ) if crypto.compare_hmac(supported_hash, hmac, auth_key, message_string): # Check if this signature has already been used for replay protection if signature_is_replay( f"{auth_key_id}:{base64.b64encode(hmac).decode('ascii')}" ): raise exceptions.UnauthorizedException( "Previous matching request found (no replays allowed)" ) # Signature is valid; Return nothing on success return else: # HMAC doesn't match raise exceptions.UnauthorizedException( "Invalid HMAC Authentication") except exceptions.DragonchainException: raise except Exception: raise exceptions.UnauthorizedException("Invalid HMAC Format") else: raise exceptions.UnauthorizedException( "Unsupported DC Authorization Version") except exceptions.DragonchainException: raise except Exception: raise exceptions.UnauthorizedException( "Malformed Authorization Header")