在 Shell 脚本中隐藏密码

在 Shell 脚本中隐藏密码

如何在 shell 脚本中隐藏密码?有许多访问数据库的脚本。如果我们打开脚本,其他人也会知道用户名和密码。因此,如果有人知道如何隐藏,请告诉我。

我有一种方法:将密码放在一个文件中,并使该文件隐藏,没有人会访问该文件(更改权限并在访问数据库时在脚本中使用该文件)。

答案1

第一的正如几个人已经说过的,将凭据与脚本分开是至关重要的。 (除了提高安全性之外,这还意味着您可以为具有不同凭据的多个系统重复使用相同的脚本。)

第二,您不仅应该考虑凭证的安全性,还应该考虑这些凭证被泄露时的影响。您不应该只使用一个密码来访问数据库的所有访问权限,而应该使用具有不同访问级别的不同凭据。例如,您可以拥有一个能够在数据库中执行搜索的数据库用户 - 该用户应该具有只读访问权限。另一个用户可能有权插入新记录,但无权删除它们。第三个人可能有权删除记录。

除了限制每个帐户的权限之外,您还应该限制每个帐户的使用范围。例如,您的 Web 服务器使用的帐户不应被允许从 Web 服务器以外的任何其他 IP 地址进行连接。具有数据库完全 root 权限的帐户实际上在可以连接的位置方面应该受到非常严格的限制,并且除了交互之外永远不应该使用。另外,请考虑使用数据库中的存储过程来精确限制每个帐户可以执行的操作。

这些限制需要在系统的数据库服务器端实施,以便即使客户端受到损害,也无法更改这些限制。 (显然,除了数据库配置之外,数据库服务器还需要通过防火墙等进行保护......)

如果数据库帐户仅允许有限的只读访问,并且只能从特定的 IP 地址进行访问,则您可能不需要除此之外的任何其他凭据,具体取决于数据的敏感性和脚本主机的安全性正在被逃离。一个示例可能是网站上的搜索表单,该表单可以由仅允许使用存储过程的用户运行,该存储过程仅提取将在网页上显示的信息。在这种情况下,添加密码实际上并不会带来任何额外的安全性,因为该信息已经是公开的,并且用户无法访问任何其他更敏感的数据。

另外,请确保使用 TLS 建立与数据库的连接,否则任何在网络上侦听的人都可以获取您的凭据。

第三,考虑使用什么样的凭证。密码只是一种形式,并不是最安全的。您可以改为使用某种形式的公钥/私钥对,或 AD/PAM 等。

第四,考虑脚本运行的条件:

如果它是以交互方式运行的,那么当您运行它时,您应该输入密码,或者私钥的密码,或者私钥,或者使用有效的 Kerberos 票证登录 - 换句话说,脚本应该获取其在运行它时直接从您那里获取凭据,而不是从某个文件中读取它们。

如果它是从网络服务器运行,请考虑在启动网络服务器时设置凭据。 SSL 证书就是一个很好的例子 - 它们有一个公共证书和一个私钥,并且私钥有一个密码。您可以将私钥存储在Web服务器上,但启动Apache时仍然需要输入密码。您还可以在某种硬件(例如物理卡或 HSM)上拥有凭据,一旦服务器启动,这些凭据就可以被删除或锁定。 (当然,这种方法的缺点是,如果发生问题,服务器无法自行重新启动。我宁愿这样做,也不愿冒着系统受到损害的风险,但您的里程可能会有所不同......)

如果脚本是从 cron 运行的,这就是困难的部分。您不希望将凭据放在系统上任何其他人可以访问它们的地方 - 但您确实希望将它们放在周围以便您的脚本可以访问它们,对吗?嗯,不太正确。仔细考虑脚本正在做什么。它需要对数据库有什么权限?是否可以对其进行限制,以便错误的人使用这些权限连接也没关系?您是否可以直接在其他人无法访问的数据库服务器上运行脚本,而不是从有其他用户的服务器上运行脚本?如果,出于某种我想不到的原因,你绝对必须让脚本在不安全的服务器上运行,并且它必须能够做一些危险/破坏性的事情......现在是重新思考你的架构的好时机。

第五,如果您重视数据库的安全性,则不应在其他人可以访问的服务器上运行这些脚本。如果有人登录您的系统,那么他们将要有机会获得您的凭据。例如,对于具有 SSL 证书的 Web 服务器,至少理论上有可能有人能够获得 root 权限并访问 httpd 进程的内存区域并提取凭据。最近至少出现了一种利用 SSL 来完成此操作的漏洞,甚至不需要攻击者登录。

另外,请考虑使用 SELinux 或 AppArmor 或您的系统可用的任何内容来限制哪些用户可以执行哪些操作。它们将使您能够禁止用户甚至尝试连接到数据库,即使他们确实设法获得了凭据的访问权限。

如果这一切对你来说太过分了,并且您负担不起或没有时间这样做 - 那么,在我(傲慢和精英主义)看来,您不应该在数据库中存储任何重要或敏感的内容。如果您不存储任何重要或敏感的内容,那么存储凭据的位置也不重要 - 在这种情况下,为什么还要使用密码呢?

最后,如果您绝对无法避免存储某种凭据,您可以将凭据设置为只读并由 root 拥有,并且 root 可以在脚本请求时临时授予所有权(因为您的脚本应该不是除非绝对必要,否则以 root 身份运行,并且不需要连接到数据库)。但这仍然不是一个好主意。

答案2

首先,如果有任何方法可以更改内容以避免首先将密码存储在脚本内或与脚本一起存储,那么您应该尽一切努力做到这一点。珍妮·D的回答包含很多对此效果的好建议。

否则,您将密码放在具有受限权限的单独文件中的想法就差不多了。例如,您可以从主脚本中获取该文件:

. /usr/local/etc/secret-password-here

您还可以限制主脚本的权限,以便只有授权人员才能执行它,但最好按照您的建议进行操作,并仅将密码本身存储在受限文件中。这样你就可以允许检查代码本身(没有敏感秘密)、版本控制和更轻松地复制脚本等等......

答案3

另一个解决方案,不考虑安全性(我也认为最好将凭据保存在另一个文件或数据库中)是使用 gpg 加密密码并将其插入脚本中。

我使用无密码的 gpg 密钥对,将其保存在 USB 中。(注意:导出此密钥对时不要使用 --armor,而应以二进制格式导出它们)。

首先加密您的密码:

echo -n "pAssw0rd" | gpg --armor --no-default-keyring --keyring /media/usb/key.pub --recipient [email protected] --encrypt

这将在标准输出中打印出 gpg 加密密码。复制整个消息并将其添加到脚本中:

password=$(gpg --batch --quiet --no-default-keyring --secret-keyring /media/usb/key.priv --decrypt <<EOF 
-----BEGIN PGP MESSAGE-----

hQEMA0CjbyauRLJ8AQgAkZT5gK8TrdH6cZEy+Ufl0PObGZJ1YEbshacZb88RlRB9
h2z+s/Bso5HQxNd5tzkwulvhmoGu6K6hpMXM3mbYl07jHF4qr+oWijDkdjHBVcn5
0mkpYO1riUf0HXIYnvCZq/4k/ajGZRm8EdDy2JIWuwiidQ18irp07UUNO+AB9mq8
5VXUjUN3tLTexg4sLZDKFYGRi4fyVrYKGsi0i5AEHKwn5SmTb3f1pa5yXbv68eYE
lCVfy51rBbG87UTycZ3gFQjf1UkNVbp0WV+RPEM9JR7dgR+9I8bKCuKLFLnGaqvc
beA3A6eMpzXQqsAg6GGo3PW6fMHqe1ZCvidi6e4a/dJDAbHq0XWp93qcwygnWeQW
Ozr1hr5mCa+QkUSymxiUrRncRhyqSP0ok5j4rjwSJu9vmHTEUapiyQMQaEIF2e2S
/NIWGg==
=uriR
-----END PGP MESSAGE-----
EOF)

这样只有当系统中安装了U盘时,密码才能被解密。当然,您也可以将密钥导入系统(安全性较低,或根本没有安全性),或者您可以使用密码保护私钥(因此无法自动化)。

答案4

我也遇到了这个问题。简短的答案是将密码与脚本分开存储,然后将脚本从环境中加载到密码中。这就提出了一个问题:如何最好地做到这一点?

理想情况下,您可以使用 HashiCorp Vault 等外部服务将其拉入 shell 环境;然而,Vault 的设置相当复杂,在许多企业环境中,您可能会受到可以安装的内容甚至可以使用的 shell 类型的限制。

为此,我创建了encpass.sh - 在 shell 脚本中使用加密密码的轻量级解决方案它允许您将机密存储在只有您的用户可以访问的隐藏目录中。它应该在任何兼容 POSIX 的 shell 中工作,唯一真正的依赖是在本地机器上安装 OpenSSL 来处理机密加密。您所要做的就是将其包含在脚本中并调用“get_secret”函数。它具有 MIT 许可证,因此您可以在企业环境中使用它。我的公司,普林特有限责任公司,维护脚本并偶尔发布更新。我将复制下面的代码以便于查看。

#!/bin/sh
################################################################################
# Copyright (c) 2020 Plyint, LLC <[email protected]>. All Rights Reserved.
# This file is licensed under the MIT License (MIT). 
# Please see LICENSE.txt for more information.
# 
# DESCRIPTION: 
# This script allows a user to encrypt a password (or any other secret) at 
# runtime and then use it, decrypted, within a script.  This prevents shoulder 
# surfing passwords and avoids storing the password in plain text, which could 
# inadvertently be sent to or discovered by an individual at a later date.
#
# This script generates an AES 256 bit symmetric key for each script (or user-
# defined bucket) that stores secrets.  This key will then be used to encrypt 
# all secrets for that script or bucket.  encpass.sh sets up a directory 
# (.encpass) under the user's home directory where keys and secrets will be 
# stored.
#
# For further details, see README.md or run "./encpass ?" from the command line.
#
################################################################################

encpass_checks() {
    if [ -n "$ENCPASS_CHECKS" ]; then
        return
    fi

    if [ ! -x "$(command -v openssl)" ]; then
        echo "Error: OpenSSL is not installed or not accessible in the current path." \
            "Please install it and try again." >&2
        exit 1
    fi

    if [ -z "$ENCPASS_HOME_DIR" ]; then
        ENCPASS_HOME_DIR=$(encpass_get_abs_filename ~)/.encpass
    fi

    if [ ! -d "$ENCPASS_HOME_DIR" ]; then
        mkdir -m 700 "$ENCPASS_HOME_DIR"
        mkdir -m 700 "$ENCPASS_HOME_DIR/keys"
        mkdir -m 700 "$ENCPASS_HOME_DIR/secrets"
    fi

    if [ "$(basename "$0")" != "encpass.sh" ]; then
        encpass_include_init "$1" "$2"
    fi

    ENCPASS_CHECKS=1
}

# Initializations performed when the script is included by another script
encpass_include_init() {
    if [ -n "$1" ] && [ -n "$2" ]; then
        ENCPASS_BUCKET=$1
        ENCPASS_SECRET_NAME=$2
    elif [ -n "$1" ]; then
        ENCPASS_BUCKET=$(basename "$0")
        ENCPASS_SECRET_NAME=$1
    else
        ENCPASS_BUCKET=$(basename "$0")
        ENCPASS_SECRET_NAME="password"
    fi
}

encpass_generate_private_key() {
    ENCPASS_KEY_DIR="$ENCPASS_HOME_DIR/keys/$ENCPASS_BUCKET"

    if [ ! -d "$ENCPASS_KEY_DIR" ]; then
        mkdir -m 700 "$ENCPASS_KEY_DIR"
    fi

    if [ ! -f "$ENCPASS_KEY_DIR/private.key" ]; then
        (umask 0377 && printf "%s" "$(openssl rand -hex 32)" >"$ENCPASS_KEY_DIR/private.key")
    fi
}

encpass_get_private_key_abs_name() {
    ENCPASS_PRIVATE_KEY_ABS_NAME="$ENCPASS_HOME_DIR/keys/$ENCPASS_BUCKET/private.key"

    if [ "$1" != "nogenerate" ]; then 
        if [ ! -f "$ENCPASS_PRIVATE_KEY_ABS_NAME" ]; then
            encpass_generate_private_key
        fi
    fi
}

encpass_get_secret_abs_name() {
    ENCPASS_SECRET_ABS_NAME="$ENCPASS_HOME_DIR/secrets/$ENCPASS_BUCKET/$ENCPASS_SECRET_NAME.enc"

    if [ "$3" != "nocreate" ]; then 
        if [ ! -f "$ENCPASS_SECRET_ABS_NAME" ]; then
            set_secret "$1" "$2"
        fi
    fi
}

get_secret() {
    encpass_checks "$1" "$2"
    encpass_get_private_key_abs_name
    encpass_get_secret_abs_name "$1" "$2"
    encpass_decrypt_secret
}

set_secret() {
    encpass_checks "$1" "$2"

    if [ "$3" != "reuse" ] || { [ -z "$ENCPASS_SECRET_INPUT" ] && [ -z "$ENCPASS_CSECRET_INPUT" ]; }; then
        echo "Enter $ENCPASS_SECRET_NAME:" >&2
        stty -echo
        read -r ENCPASS_SECRET_INPUT
        stty echo
        echo "Confirm $ENCPASS_SECRET_NAME:" >&2
        stty -echo
        read -r ENCPASS_CSECRET_INPUT
        stty echo
    fi

    if [ "$ENCPASS_SECRET_INPUT" = "$ENCPASS_CSECRET_INPUT" ]; then
        encpass_get_private_key_abs_name
        ENCPASS_SECRET_DIR="$ENCPASS_HOME_DIR/secrets/$ENCPASS_BUCKET"

        if [ ! -d "$ENCPASS_SECRET_DIR" ]; then
            mkdir -m 700 "$ENCPASS_SECRET_DIR"
        fi

        printf "%s" "$(openssl rand -hex 16)" >"$ENCPASS_SECRET_DIR/$ENCPASS_SECRET_NAME.enc"

        ENCPASS_OPENSSL_IV="$(cat "$ENCPASS_SECRET_DIR/$ENCPASS_SECRET_NAME.enc")"

        echo "$ENCPASS_SECRET_INPUT" | openssl enc -aes-256-cbc -e -a -iv \
            "$ENCPASS_OPENSSL_IV" -K \
            "$(cat "$ENCPASS_HOME_DIR/keys/$ENCPASS_BUCKET/private.key")" 1>> \
                    "$ENCPASS_SECRET_DIR/$ENCPASS_SECRET_NAME.enc"
    else
        echo "Error: secrets do not match.  Please try again." >&2
        exit 1
    fi
}

encpass_get_abs_filename() {
    # $1 : relative filename
    filename="$1"
    parentdir="$(dirname "${filename}")"

    if [ -d "${filename}" ]; then
        cd "${filename}" && pwd
    elif [ -d "${parentdir}" ]; then
        echo "$(cd "${parentdir}" && pwd)/$(basename "${filename}")"
    fi
}

encpass_decrypt_secret() {
    if [ -f "$ENCPASS_PRIVATE_KEY_ABS_NAME" ]; then
        ENCPASS_DECRYPT_RESULT="$(dd if="$ENCPASS_SECRET_ABS_NAME" ibs=1 skip=32 2> /dev/null | openssl enc -aes-256-cbc \
            -d -a -iv "$(head -c 32 "$ENCPASS_SECRET_ABS_NAME")" -K "$(cat "$ENCPASS_PRIVATE_KEY_ABS_NAME")" 2> /dev/null)"
        if [ ! -z "$ENCPASS_DECRYPT_RESULT" ]; then
            echo "$ENCPASS_DECRYPT_RESULT"
        else
            # If a failed unlock command occurred and the user tries to show the secret
            # Present either locked or decrypt command
            if [ -f "$ENCPASS_HOME_DIR/keys/$ENCPASS_BUCKET/private.lock" ]; then 
            echo "**Locked**"
            else
                # The locked file wasn't present as expected.  Let's display a failure
            echo "Error: Failed to decrypt"
            fi
        fi
    elif [ -f "$ENCPASS_HOME_DIR/keys/$ENCPASS_BUCKET/private.lock" ]; then
        echo "**Locked**"
    else
        echo "Error: Unable to decrypt. The key file \"$ENCPASS_PRIVATE_KEY_ABS_NAME\" is not present."
    fi
}


##########################################################
# COMMAND LINE MANAGEMENT SUPPORT
# -------------------------------
# If you don't need to manage the secrets for the scripts
# with encpass.sh you can delete all code below this point
# in order to significantly reduce the size of encpass.sh.
# This is useful if you want to bundle encpass.sh with
# your existing scripts and just need the retrieval
# functions.
##########################################################

encpass_show_secret() {
    encpass_checks
    ENCPASS_BUCKET=$1

    encpass_get_private_key_abs_name "nogenerate"

    if [ ! -z "$2" ]; then
        ENCPASS_SECRET_NAME=$2
        encpass_get_secret_abs_name "$1" "$2" "nocreate"
        if [ -z "$ENCPASS_SECRET_ABS_NAME" ]; then
            echo "No secret named $2 found for bucket $1."
            exit 1
        fi

        encpass_decrypt_secret
    else
        ENCPASS_FILE_LIST=$(ls -1 "$ENCPASS_HOME_DIR"/secrets/"$1")
        for ENCPASS_F in $ENCPASS_FILE_LIST; do
            ENCPASS_SECRET_NAME=$(basename "$ENCPASS_F" .enc)

            encpass_get_secret_abs_name "$1" "$ENCPASS_SECRET_NAME" "nocreate"
            if [ -z "$ENCPASS_SECRET_ABS_NAME" ]; then
                echo "No secret named $ENCPASS_SECRET_NAME found for bucket $1."
                exit 1
            fi

            echo "$ENCPASS_SECRET_NAME = $(encpass_decrypt_secret)"
        done
    fi
}

encpass_getche() {
        old=$(stty -g)
        stty raw min 1 time 0
        printf '%s' "$(dd bs=1 count=1 2>/dev/null)"
        stty "$old"
}

encpass_remove() {
    if [ ! -n "$ENCPASS_FORCE_REMOVE" ]; then
        if [ ! -z "$ENCPASS_SECRET" ]; then
            printf "Are you sure you want to remove the secret \"%s\" from bucket \"%s\"? [y/N]" "$ENCPASS_SECRET" "$ENCPASS_BUCKET"
        else
            printf "Are you sure you want to remove the bucket \"%s?\" [y/N]" "$ENCPASS_BUCKET"
        fi

        ENCPASS_CONFIRM="$(encpass_getche)"
        printf "\n"
        if [ "$ENCPASS_CONFIRM" != "Y" ] && [ "$ENCPASS_CONFIRM" != "y" ]; then
            exit 0
        fi
    fi

    if [ ! -z "$ENCPASS_SECRET" ]; then
        rm -f "$1"
        printf "Secret \"%s\" removed from bucket \"%s\".\n" "$ENCPASS_SECRET" "$ENCPASS_BUCKET"
    else
        rm -Rf "$ENCPASS_HOME_DIR/keys/$ENCPASS_BUCKET"
        rm -Rf "$ENCPASS_HOME_DIR/secrets/$ENCPASS_BUCKET"
        printf "Bucket \"%s\" removed.\n" "$ENCPASS_BUCKET"
    fi
}

encpass_save_err() {
    if read -r x; then
        { printf "%s\n" "$x"; cat; } > "$1"
    elif [ "$x" != "" ]; then
        printf "%s" "$x" > "$1"
    fi
}

encpass_help() {
less << EOF
NAME:
    encpass.sh - Use encrypted passwords in shell scripts

DESCRIPTION: 
    A lightweight solution for using encrypted passwords in shell scripts 
    using OpenSSL. It allows a user to encrypt a password (or any other secret)
    at runtime and then use it, decrypted, within a script. This prevents
    shoulder surfing passwords and avoids storing the password in plain text, 
    within a script, which could inadvertently be sent to or discovered by an 
    individual at a later date.

    This script generates an AES 256 bit symmetric key for each script 
    (or user-defined bucket) that stores secrets. This key will then be used 
    to encrypt all secrets for that script or bucket.

    Subsequent calls to retrieve a secret will not prompt for a secret to be 
    entered as the file with the encrypted value already exists.

    Note: By default, encpass.sh sets up a directory (.encpass) under the 
    user's home directory where keys and secrets will be stored.  This directory
    can be overridden by setting the environment variable ENCPASS_HOME_DIR to a
    directory of your choice.

    ~/.encpass (or the directory specified by ENCPASS_HOME_DIR) will contain 
    the following subdirectories:
      - keys (Holds the private key for each script/bucket)
      - secrets (Holds the secrets stored for each script/bucket)

USAGE:
    To use the encpass.sh script in an existing shell script, source the script 
    and then call the get_secret function.

    Example:

        #!/bin/sh
        . encpass.sh
        password=\$(get_secret)

    When no arguments are passed to the get_secret function,
    then the bucket name is set to the name of the script and
    the secret name is set to "password".

    There are 2 other ways to call get_secret:

      Specify the secret name:
      Ex: \$(get_secret user)
        - bucket name = <script name>
        - secret name = "user"

      Specify both the secret name and bucket name:
      Ex: \$(get_secret personal user)
        - bucket name = "personal"
        - secret name = "user"

    encpass.sh also provides a command line interface to manage the secrets.
    To invoke a command, pass it as an argument to encpass.sh from the shell.

        $ encpass.sh [COMMAND]

    See the COMMANDS section below for a list of available commands.  Wildcard
    handling is implemented for secret and bucket names.  This enables
    performing operations like adding/removing a secret to/from multiple buckets
        at once.

COMMANDS:
    add [-f] <bucket> <secret>
        Add a secret to the specified bucket.  The bucket will be created
        if it does not already exist. If a secret with the same name already
        exists for the specified bucket, then the user will be prompted to
        confirm overwriting the value.  If the -f option is passed, then the
        add operation will perform a forceful overwrite of the value. (i.e. no
        prompt)

    list|ls [<bucket>]
        Display the names of the secrets held in the bucket.  If no bucket
        is specified, then the names of all existing buckets will be
        displayed.

    lock
        Locks all keys used by encpass.sh using a password.  The user
        will be prompted to enter a password and confirm it.  A user
        should take care to securely store the password.  If the password
        is lost then keys can not be unlocked.  When keys are locked,
        secrets can not be retrieved. (e.g. the output of the values
        in the "show" command will be encrypted/garbage)

    remove|rm [-f] <bucket> [<secret>]
        Remove a secret from the specified bucket.  If only a bucket is
        specified then the entire bucket (i.e. all secrets and keys) will
        be removed.  By default the user is asked to confirm the removal of
        the secret or the bucket.  If the -f option is passed then a 
        forceful removal will be performed.  (i.e. no prompt)

    show [<bucket>] [<secret>]
        Show the unencrypted value of the secret from the specified bucket.
        If no secret is specified then all secrets for the bucket are displayed.

    update <bucket> <secret>
        Updates a secret in the specified bucket.  This command is similar
        to using an "add -f" command, but it has a safety check to only 
        proceed if the specified secret exists.  If the secret, does not
        already exist, then an error will be reported. There is no forceable
        update implemented.  Use "add -f" for any required forceable update
        scenarios.

    unlock
        Unlocks all the keys for encpass.sh.  The user will be prompted to 
        enter the password and confirm it.

    dir
        Prints out the current value of the ENCPASS_HOME_DIR environment variable.

    help|--help|usage|--usage|?
        Display this help message.
EOF
}

# Subcommands for cli support
case "$1" in
    add )
        shift
        while getopts ":f" ENCPASS_OPTS; do
            case "$ENCPASS_OPTS" in
                f ) ENCPASS_FORCE_ADD=1;;
            esac
        done

        encpass_checks

        if [ -n "$ENCPASS_FORCE_ADD" ]; then
            shift $((OPTIND-1))
        fi

        if [ ! -z "$1" ] && [ ! -z "$2" ]; then
            # Allow globbing
            # shellcheck disable=SC2027,SC2086
            ENCPASS_ADD_LIST="$(ls -1d "$ENCPASS_HOME_DIR/secrets/"$1"" 2>/dev/null)"
            if [ -z "$ENCPASS_ADD_LIST" ]; then
                ENCPASS_ADD_LIST="$1"
            fi

            for ENCPASS_ADD_F in $ENCPASS_ADD_LIST; do
                ENCPASS_ADD_DIR="$(basename "$ENCPASS_ADD_F")"
                ENCPASS_BUCKET="$ENCPASS_ADD_DIR"
                if [ ! -n "$ENCPASS_FORCE_ADD" ] && [ -f "$ENCPASS_ADD_F/$2.enc" ]; then
                    echo "Warning: A secret with the name \"$2\" already exists for bucket $ENCPASS_BUCKET."
                    echo "Would you like to overwrite the value? [y/N]"

                    ENCPASS_CONFIRM="$(encpass_getche)"
                    if [ "$ENCPASS_CONFIRM" != "Y" ] && [ "$ENCPASS_CONFIRM" != "y" ]; then
                        continue
                    fi
                fi

                ENCPASS_SECRET_NAME="$2"
                echo "Adding secret \"$ENCPASS_SECRET_NAME\" to bucket \"$ENCPASS_BUCKET\"..."
                set_secret "$ENCPASS_BUCKET" "$ENCPASS_SECRET_NAME" "reuse"
            done
        else
            echo "Error: A bucket name and secret name must be provided when adding a secret."
            exit 1
        fi
        ;;
    update )
        shift

        encpass_checks
        if [ ! -z "$1" ] && [ ! -z "$2" ]; then

            ENCPASS_SECRET_NAME="$2"
            # Allow globbing
            # shellcheck disable=SC2027,SC2086
            ENCPASS_UPDATE_LIST="$(ls -1d "$ENCPASS_HOME_DIR/secrets/"$1"" 2>/dev/null)"

            for ENCPASS_UPDATE_F in $ENCPASS_UPDATE_LIST; do
                # Allow globbing
                # shellcheck disable=SC2027,SC2086
                if [ -f "$ENCPASS_UPDATE_F/"$2".enc" ]; then
                        ENCPASS_UPDATE_DIR="$(basename "$ENCPASS_UPDATE_F")"
                        ENCPASS_BUCKET="$ENCPASS_UPDATE_DIR"
                        echo "Updating secret \"$ENCPASS_SECRET_NAME\" to bucket \"$ENCPASS_BUCKET\"..."
                        set_secret "$ENCPASS_BUCKET" "$ENCPASS_SECRET_NAME" "reuse"
                else
                    echo "Error: A secret with the name \"$2\" does not exist for bucket $1."
                    exit 1
                fi
            done
        else
            echo "Error: A bucket name and secret name must be provided when updating a secret."
            exit 1
        fi
        ;;
    rm|remove )
        shift
        encpass_checks

        while getopts ":f" ENCPASS_OPTS; do
            case "$ENCPASS_OPTS" in
                f ) ENCPASS_FORCE_REMOVE=1;;
            esac
        done

        if [ -n "$ENCPASS_FORCE_REMOVE" ]; then
            shift $((OPTIND-1))
        fi

        if [ -z "$1" ]; then 
            echo "Error: A bucket must be specified for removal."
        fi

        # Allow globbing
        # shellcheck disable=SC2027,SC2086
        ENCPASS_REMOVE_BKT_LIST="$(ls -1d "$ENCPASS_HOME_DIR/secrets/"$1"" 2>/dev/null)"
        if [ ! -z "$ENCPASS_REMOVE_BKT_LIST" ]; then
            for ENCPASS_REMOVE_B in $ENCPASS_REMOVE_BKT_LIST; do

                ENCPASS_BUCKET="$(basename "$ENCPASS_REMOVE_B")"
                if [ ! -z "$2" ]; then
                    # Removing secrets for a specified bucket
                    # Allow globbing
                    # shellcheck disable=SC2027,SC2086
                    ENCPASS_REMOVE_LIST="$(ls -1p "$ENCPASS_REMOVE_B/"$2".enc" 2>/dev/null)"

                    if [ -z "$ENCPASS_REMOVE_LIST" ]; then
                        echo "Error: No secrets found for $2 in bucket $ENCPASS_BUCKET."
                        exit 1
                    fi

                    for ENCPASS_REMOVE_F in $ENCPASS_REMOVE_LIST; do
                        ENCPASS_SECRET="$2"
                        encpass_remove "$ENCPASS_REMOVE_F"
                    done
                else
                    # Removing a specified bucket
                    encpass_remove
                fi

            done
        else
            echo "Error: The bucket named $1 does not exist."
            exit 1
        fi
        ;;
    show )
        shift
        encpass_checks
        if [ -z "$1" ]; then
            ENCPASS_SHOW_DIR="*"
        else
            ENCPASS_SHOW_DIR=$1
        fi

        if [ ! -z "$2" ]; then
            # Allow globbing
            # shellcheck disable=SC2027,SC2086
            if [ -f "$(encpass_get_abs_filename "$ENCPASS_HOME_DIR/secrets/$ENCPASS_SHOW_DIR/"$2".enc")" ]; then
                encpass_show_secret "$ENCPASS_SHOW_DIR" "$2"
            fi
        else
            # Allow globbing
            # shellcheck disable=SC2027,SC2086
            ENCPASS_SHOW_LIST="$(ls -1d "$ENCPASS_HOME_DIR/secrets/"$ENCPASS_SHOW_DIR"" 2>/dev/null)"

            if [ -z "$ENCPASS_SHOW_LIST" ]; then
                if [ "$ENCPASS_SHOW_DIR" = "*" ]; then
                    echo "Error: No buckets exist."
                else
                    echo "Error: Bucket $1 does not exist."
                fi
                exit 1
            fi

            for ENCPASS_SHOW_F in $ENCPASS_SHOW_LIST; do
                ENCPASS_SHOW_DIR="$(basename "$ENCPASS_SHOW_F")"
                echo "$ENCPASS_SHOW_DIR:"
                encpass_show_secret "$ENCPASS_SHOW_DIR"
                echo " "
            done
        fi
        ;;
    ls|list )
        shift
        encpass_checks
        if [ ! -z "$1" ]; then
            # Allow globbing
            # shellcheck disable=SC2027,SC2086
            ENCPASS_FILE_LIST="$(ls -1p "$ENCPASS_HOME_DIR/secrets/"$1"" 2>/dev/null)"

            if [ -z "$ENCPASS_FILE_LIST" ]; then
                # Allow globbing
                # shellcheck disable=SC2027,SC2086
                ENCPASS_DIR_EXISTS="$(ls -d "$ENCPASS_HOME_DIR/secrets/"$1"" 2>/dev/null)"
                if [ ! -z "$ENCPASS_DIR_EXISTS" ]; then
                    echo "Bucket $1 is empty."
                else
                    echo "Error: Bucket $1 does not exist."
                fi
                exit 1
            fi

            ENCPASS_NL=""
            for ENCPASS_F in $ENCPASS_FILE_LIST; do
                if [ -d "${ENCPASS_F%:}" ]; then
                    printf "$ENCPASS_NL%s\n" "$(basename "$ENCPASS_F")"
                    ENCPASS_NL="\n"
                else
                    printf "%s\n" "$(basename "$ENCPASS_F" .enc)"
                fi
            done
        else
            # Allow globbing
            # shellcheck disable=SC2027,SC2086
            ENCPASS_BUCKET_LIST="$(ls -1p "$ENCPASS_HOME_DIR/secrets/"$1"" 2>/dev/null)"
            for ENCPASS_C in $ENCPASS_BUCKET_LIST; do
                if [ -d "${ENCPASS_C%:}" ]; then
                    printf "\n%s" "\n$(basename "$ENCPASS_C")"
                else
                    basename "$ENCPASS_C" .enc
                fi
            done
        fi
        ;;
    lock )
        shift
        encpass_checks

        echo "************************!!!WARNING!!!*************************" >&2
        echo "* You are about to lock your keys with a password.           *" >&2
        echo "* You will not be able to use your secrets again until you   *" >&2
        echo "* unlock the keys with the same password. It is important    *" >&2
        echo "* that you securely store the password, so you can recall it *" >&2
        echo "* in the future.  If you forget your password you will no    *" >&2
        echo "* longer be able to access your secrets.                     *" >&2
        echo "************************!!!WARNING!!!*************************" >&2

        printf "\n%s\n" "About to lock keys held in directory $ENCPASS_HOME_DIR/keys/"

        printf "\nEnter Password to lock keys:" >&2
        stty -echo
        read -r ENCPASS_KEY_PASS
        printf "\nConfirm Password:" >&2
        read -r ENCPASS_CKEY_PASS
        printf "\n"
        stty echo

        if [ -z "$ENCPASS_KEY_PASS" ]; then
            echo "Error: You must supply a password value."
            exit 1
        fi

        if [ "$ENCPASS_KEY_PASS" = "$ENCPASS_CKEY_PASS" ]; then
            ENCPASS_NUM_KEYS_LOCKED=0
            ENCPASS_KEYS_LIST="$(ls -1d "$ENCPASS_HOME_DIR/keys/"*"/" 2>/dev/null)"
            for ENCPASS_KEY_F in $ENCPASS_KEYS_LIST; do

                if [ -d "${ENCPASS_KEY_F%:}" ]; then
                    ENCPASS_KEY_NAME="$(basename "$ENCPASS_KEY_F")"
                    ENCPASS_KEY_VALUE=""
                    if [ -f "$ENCPASS_KEY_F/private.key" ]; then
                        ENCPASS_KEY_VALUE="$(cat "$ENCPASS_KEY_F/private.key")"
                        if [ ! -f "$ENCPASS_KEY_F/private.lock" ]; then
                        echo "Locking key $ENCPASS_KEY_NAME..."
                        else
                          echo "Error: The key $ENCPASS_KEY_NAME appears to have been previously locked."
                            echo "       The current key file may hold a bad value. Exiting to avoid encrypting"
                            echo "       a bad value and overwriting the lock file."
                            exit 1
                        fi
                    else
                        echo "Error: Private key file ${ENCPASS_KEY_F}private.key missing for bucket $ENCPASS_KEY_NAME."
                        exit 1
                    fi
                    if [ ! -z "$ENCPASS_KEY_VALUE" ]; then
                        openssl enc -aes-256-cbc -pbkdf2 -iter 10000 -salt -in "$ENCPASS_KEY_F/private.key" -out "$ENCPASS_KEY_F/private.lock" -k "$ENCPASS_KEY_PASS"
                        if [ -f "$ENCPASS_KEY_F/private.key" ] && [ -f "$ENCPASS_KEY_F/private.lock" ]; then
                            # Both the key and lock file exist.  We can remove the key file now
                            rm -f "$ENCPASS_KEY_F/private.key"
                            echo "Locked key $ENCPASS_KEY_NAME."
                            ENCPASS_NUM_KEYS_LOCKED=$(( ENCPASS_NUM_KEYS_LOCKED + 1 ))
                        else
                            echo "Error: The key fle and/or lock file were not found as expected for key $ENCPASS_KEY_NAME."
                        fi
                    else
                        echo "Error: No key value found for the $ENCPASS_KEY_NAME key."
                        exit 1
                    fi
                fi
            done
            echo "Locked $ENCPASS_NUM_KEYS_LOCKED keys."
        else
            echo "Error: Passwords do not match."
        fi
        ;;
    unlock )
        shift
        encpass_checks

        printf "%s\n" "About to unlock keys held in the $ENCPASS_HOME_DIR/keys/ directory."

        printf "\nEnter Password to unlock keys: " >&2
        stty -echo
        read -r ENCPASS_KEY_PASS
        printf "\n"
        stty echo

        if [ ! -z "$ENCPASS_KEY_PASS" ]; then
            ENCPASS_NUM_KEYS_UNLOCKED=0
            ENCPASS_KEYS_LIST="$(ls -1d "$ENCPASS_HOME_DIR/keys/"*"/" 2>/dev/null)"
            for ENCPASS_KEY_F in $ENCPASS_KEYS_LIST; do

                if [ -d "${ENCPASS_KEY_F%:}" ]; then
                    ENCPASS_KEY_NAME="$(basename "$ENCPASS_KEY_F")"
                    echo "Unlocking key $ENCPASS_KEY_NAME..."
                    if [ -f "$ENCPASS_KEY_F/private.key" ] && [ ! -f "$ENCPASS_KEY_F/private.lock" ]; then
                        echo "Error: Key $ENCPASS_KEY_NAME appears to be unlocked already."
                        exit 1
                    fi

                    if [ -f "$ENCPASS_KEY_F/private.lock" ]; then
                        # Remove the failed file in case previous decryption attempts were unsuccessful
                        rm -f "$ENCPASS_KEY_F/failed" 2>/dev/null

                        # Decrypt key. Log any failure to the "failed" file.
                        openssl enc -aes-256-cbc -d -pbkdf2 -iter 10000 -salt \
                            -in "$ENCPASS_KEY_F/private.lock" -out "$ENCPASS_KEY_F/private.key" \
                            -k "$ENCPASS_KEY_PASS" 2>&1 | encpass_save_err "$ENCPASS_KEY_F/failed"

                        if [ ! -f "$ENCPASS_KEY_F/failed" ]; then
                            # No failure has occurred.
                          if [ -f "$ENCPASS_KEY_F/private.key" ] && [ -f "$ENCPASS_KEY_F/private.lock" ]; then
                              # Both the key and lock file exist.  We can remove the lock file now.
                              rm -f "$ENCPASS_KEY_F/private.lock"
                              echo "Unlocked key $ENCPASS_KEY_NAME."
                              ENCPASS_NUM_KEYS_UNLOCKED=$(( ENCPASS_NUM_KEYS_UNLOCKED + 1 ))
                          else
                              echo "Error: The key file and/or lock file were not found as expected for key $ENCPASS_KEY_NAME."
                          fi
                        else
                          printf "Error: Failed to unlock key %s.\n" "$ENCPASS_KEY_NAME"
                            printf "       Please view %sfailed for details.\n" "$ENCPASS_KEY_F"
                        fi
                    else
                        echo "Error: No lock file found for the $ENCPASS_KEY_NAME key."
                    fi
                fi
            done
            echo "Unlocked $ENCPASS_NUM_KEYS_UNLOCKED keys."
        else
            echo "No password entered."
        fi
        ;;
    dir )
        shift
        encpass_checks
        echo "ENCPASS_HOME_DIR=$ENCPASS_HOME_DIR"
        ;;
    help|--help|usage|--usage|\? )
        encpass_checks
        encpass_help
        ;;
    * )
        if [ ! -z "$1" ]; then
            echo "Command not recognized. See \"encpass.sh help\" for a list commands."
            exit 1
        fi
        ;;
esac

相关内容