Beispiel #1
0
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")
Beispiel #2
0
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
Beispiel #3
0
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")
Beispiel #4
0
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")
Beispiel #5
0
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
Beispiel #6
0
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")
    }
Beispiel #7
0
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")
Beispiel #8
0
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
Beispiel #9
0
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)
Beispiel #10
0
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")
Beispiel #11
0
# 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")