管理密钥并配置 Streamlit 应用程序

Streamlit 应用程序通常需要访问敏感信息,例如 API 密钥、密码和其他凭据。在 Streamlit 应用程序中管理密钥的方式取决于您使用的运行时环境。Streamlit in Snowflake 提供安全的内置机制,用于访问仓库和容器运行时中的密钥。对于 Streamlit 配置,每个运行时也都有不同的限制。

在 Streamlit 库中,应用程序使用 .streamlit/ 目录来存储配置和密钥:

  • .streamlit/config.toml:选择使用 时默认使用的角色和仓库。自定义应用程序设置,例如主题、布局和服务器行为。

  • .streamlit/secrets.toml:选择使用 时默认使用的角色和仓库。存储敏感信息,例如 API 密钥和凭据(在本地开发中)。

Streamlit in Snowflake 支持这些文件,但根据您的运行时环境存在一些限制。下表总结了仓库和容器运行时中对这些文件的支持:

功能

仓库运行时

容器运行时

config.toml 支持

有限的配置选项子集

更广泛的配置选项子集

secrets.toml 支持

不支持

支持,但仅建议用于非密钥环境变量

对于 secrets.toml,Streamlit in Snowflake 提供了一个更安全的内置密钥管理系统,建议用于管理敏感信息。以下部分介绍如何在应用程序中使用 Snowflake 密钥。

管理与 Snowflake 的连接

要管理与 Snowflake 的连接,可以使用 st.connection("snowflake") (https://docs.streamlit.io/develop/api-reference/connections/st.connections.snowflakeconnection)。这允许您从本地开发环境和部署的应用程序连接到 Snowflake。

import streamlit as st

conn = st.connection("snowflake")
session = conn.session()

session.sql("SELECT 1").collect()

在仓库运行时中,您还可以使用 Snowpark 的 get_active_session() 函数来获取活动会话。

import streamlit as st
from snowflake.snowpark.context import get_active_session

# ONLY IN WAREHOUSE RUNTIMES
session = get_active_session()
session.sql("SELECT 1").collect()

重要

get_active_session() 不是线程安全的,不能在容器运行时中使用。

容器运行时中的密钥

您可以使用 st.secrets (https://docs.streamlit.io/develop/api-reference/connections/st.secrets) 在容器运行时 Streamlit in Snowflake 应用程序中访问 Snowflake 密钥。这使您可以安全地存储和检索敏感信息,例如 API 密钥、凭据和其他配置值。就像 Streamlit 在本地开发中为 .streamlit/secrets.toml 所做的那样,Streamlit in Snowflake 也会将密钥填充到环境变量中。

备注

容器运行时无权访问 _snowflake 模块。如果要迁移使用 _snowflake 密钥函数的旧版仓库运行时应用程序,请按照本节所述,将这些调用替换为 st.secrets (https://docs.streamlit.io/develop/api-reference/connections/st.secrets)。

访问容器运行时中的密钥

  1. 将以下 Python 文件暂存到 @my_stage/app_folder/streamlit_app.py。有关暂存文件的信息,请参阅 使用 Snowsight 暂存文件

    import streamlit as st
    
    secret_value = st.secrets["my_secret_name"]
    
  2. 在 Snowflake 账户中创建密钥:

    CREATE OR REPLACE SECRET my_secret
      TYPE = GENERIC_STRING
      SECRET_STRING = 'my_secret_value';
    

    有关更多信息,请参阅 CREATE SECRET

  3. 创建外部访问集成 (EAI),并为其分配密钥:

    CREATE OR REPLACE EXTERNAL ACCESS INTEGRATION my_eai
      ALLOWED_AUTHENTICATION_SECRETS = (my_secret)
      ENABLED = TRUE;
    
  4. 使用 SECRETS 参数创建 Streamlit 应用程序以引用密钥:

    CREATE STREAMLIT my_container_app
      FROM '@my_stage/app_folder'
      MAIN_FILE = 'streamlit_app.py'
      RUNTIME_NAME = 'SYSTEM$ST_CONTAINER_RUNTIME_PY3_11'
      COMPUTE_POOL = my_compute_pool
      QUERY_WAREHOUSE = my_warehouse
      EXTERNAL_ACCESS_INTEGRATIONS = (my_eai)
      SECRETS = ('my_secret_name' = my_secret);
    
    ALTER STREAMLIT my_container_app ADD LIVE VERSION FROM LAST;
    

    备注

    您必须将 EAI 和密钥同时分配给 Streamlit 对象。您不能单独将密钥分配给 Streamlit 对象。

    因为 generic-string 密钥 my_secret 与字符串 "my_secret_name" 在 SECRETS 参数中关联,您可以在 Streamlit 应用代码中使用 st.secrets["my_secret_name"] 访问该密钥。

支持的密钥类型和环境变量

容器运行时支持通用字符串和基本身份验证密钥。除了将密钥映射到 st.secrets 之外,Streamlit in Snowflake 还将密钥映射到环境变量。环境变量名称区分大小写。对于基本身份验证密钥,将创建两个环境变量:一个用于用户名(_USERNAME 后缀),一个用于密码(_PASSWORD 后缀)。

密钥类型

st.secrets 访问

环境变量访问

通用字符串

st.secrets["my_secret_name"]

os.environ["my_secret_name"]

基本身份验证(用户名)

st.secrets["my_secret_name"]["username"]

os.environ["my_secret_name_USERNAME"]

基本身份验证(密码)

st.secrets["my_secret_name"]["password"]

os.environ["my_secret_name_PASSWORD"]

备注

目前不支持云提供商、对称密钥和 OAuth 密钥类型。

通用字符串密钥

通用字符串密钥作为顶级密钥存储在 st.secrets

ALTER STREAMLIT my_container_app
  SET SECRETS = ('my_generic_secret_name' = my_generic_secret);

您可以使用字典或属性表示法访问密钥:

import streamlit as st

api_key = st.secrets["my_generic_secret_name"]
api_key = st.secrets.my_generic_secret_name

基本身份验证密钥

基本身份验证密钥以类字典对象的形式存储,具有 "username"``"password"``属性:

ALTER STREAMLIT my_container_app
  SET SECRETS = ('my_basic_auth_secret_name' = my_basic_auth_secret);

您可以使用字典或属性表示法访问密钥:

import streamlit as st

username = st.secrets["my_basic_auth_secret_name"]["username"]
password = st.secrets["my_basic_auth_secret_name"]["password"]

username = st.secrets.my_basic_auth_secret_name.username
password = st.secrets.my_basic_auth_secret_name.password

经过身份验证的包存储库的密钥

密钥会自动公开为环境变量。特别是,这允许使用私有包存储库进行身份验证,例如 JFrogArtifactory。

对于大多数经过身份验证的包存储库,请使用基本身份验证密钥。密钥会自动转换为具有 _USERNAME_PASSWORD 后缀的环境变量。如果需要不同的命名约定,请使用通用字符串密钥,并手动设置每个环境变量的名称。有关 uv 使用的环境变量的更多信息,请参阅 uv 文档中的 包索引 (https://docs.astral.sh/uv/concepts/indexes/#providing-credentials-directly)。

示例:对私有 JFrog Artifactory 存储库进行身份验证

  1. 将应用程序的源文件暂存到 @my_stage/app_folder。应用程序的源文件必须包含 pyproject.toml 文件,用于在 [[tool.uv.index]] 表中配置私有软件包索引:

    [[tool.uv.index]]
    name = "my_jfrog_repo"
    url = "https://my-org.jfrog.io/artifactory/api/pypi/pypi-local/simple"
    

    有关在 pyproject.toml 文件中声明应用程序依赖项的更多信息,请参阅 管理 Streamlit 应用程序的依赖项

  2. 使用 JFrog 凭据创建基本身份验证密钥:

    CREATE OR REPLACE SECRET jfrog_creds
      TYPE = PASSWORD
      USERNAME = 'my_username'
      PASSWORD = 'my_api_token';
    
  3. 为您的私有仓库创建外部访问集成:

    CREATE OR REPLACE NETWORK RULE jfrog_network_rule
      TYPE = HOST_PORT
      MODE = EGRESS
      VALUE_LIST = ('my-org.jfrog.io');
    
    CREATE OR REPLACE EXTERNAL ACCESS INTEGRATION jfrog_eai
      ALLOWED_NETWORK_RULES = (jfrog_network_rule)
      ALLOWED_AUTHENTICATION_SECRETS = (jfrog_creds)
      ENABLED = TRUE;
    

    备注

    为避免 DNS 错误,您可能需要在网络规则值列表中包含存储库的云提供商。例如,如果您的存储库位于 AWS,您可能需要在网络规则中使用以下值列表:

    VALUE_LIST = ('my-org.jfrog.io', '<jfrog-server-name>.s3.amazonaws.com');
    
  4. 将 EAI 和密钥附加到 Streamlit 应用程序:

    CREATE STREAMLIT my_app
      FROM '@my_stage/app_folder'
      MAIN_FILE = 'streamlit_app.py'
      RUNTIME_NAME = 'SYSTEM$ST_CONTAINER_RUNTIME_PY3_11'
      COMPUTE_POOL = my_compute_pool
      QUERY_WAREHOUSE = my_warehouse
      EXTERNAL_ACCESS_INTEGRATIONS = (jfrog_eai)
      SECRETS = ('UV_INDEX_MY_JFROG_REPO' = jfrog_creds);
    
    ALTER STREAMLIT my_app ADD LIVE VERSION FROM LAST;
    

    因为基本身份验证密钥 jfrog_creds 与 SECRETS 参数中的字符串 "UV_INDEX_MY_JFROG_REPO" 关联,运行时会根据 uv 的要求自动注入 UV_INDEX_MY_JFROG_REPO_USERNAMEUV_INDEX_MY_JFROG_REPO_PASSWORD 环境变量。

本地 .streamlit/secrets.toml 文件的优先级

您可以在应用程序源文件目录中将 Snowflake 管理的密钥与本地 .streamlit/secrets.toml 文件相结合。当两者都存在时,Streamlit 库会将它们合并。本地定义的 .streamlit/secrets.toml 文件优先于 Snowflake 管理的密钥。

因为 .streamlit/secrets.toml 以纯文本形式存储在暂存文件中,因此在其中存储实际密钥不是安全最佳实践。使用 Snowflake 的内置密钥管理来管理敏感凭据。使用本地定义的 .streamlit/secrets.toml 文件来存储非敏感配置值或特定于环境的设置。

移除或更改 Streamlit 应用程序的密钥

  • 要移除 Streamlit in Snowflake 应用程序中的所有密钥,请使用 ALTER STREAMLIT 的 UNSET SECRETS 子句:

    ALTER STREAMLIT my_database.my_schema.my_app
      UNSET SECRETS;
    

    这将移除 Streamlit in Snowflake 应用程序的所有密钥关联。底层密钥对象保留在您的 Snowflake 账户中,可以稍后重新分配。同时移除任意 EAI 关联,并且取消设置 EXTERNAL_ACCESS_INTEGRATIONS 属性。

  • 要更新或修改附加的密钥,请使用 ALTER STREAMLIT 与 SET SECRETS:

    ALTER STREAMLIT my_database.my_schema.my_app
      SET SECRETS = ('new_secret' = my_new_secret);
    

示例:使用经过身份验证的外部 API 创建容器运行时 Streamlit 应用程序

此示例演示如何创建使用密钥 API 键调用外部 API 的 Streamlit in Snowflake 应用程序。

  1. 将以下 Python 文件暂存到 @my_stage/weather_app/streamlit_app.py

    import streamlit as st
    import requests
    
    api_key = st.secrets["weather_api_name"]
    
    response = requests.get(
        "https://api.weather.com/v1/current",
        headers={"Authorization": f"Bearer {api_key}"}
    )
    
    st.write(response.json())
    

    因为 requestsstreamlit 的依赖项,它包含在运行时基础镜像中。因此,即使您没有包含依赖项文件或配置包索引,运行时也会自动安装它。

  2. 创建密钥、网络规则和 EAI:

    CREATE OR REPLACE SECRET weather_api_key
      TYPE = GENERIC_STRING
      SECRET_STRING = 'secret_value';
    
    CREATE OR REPLACE NETWORK RULE weather_api_rule
      TYPE = HOST_PORT
      MODE = EGRESS
      VALUE_LIST = ('api.weather.com');
    
    CREATE OR REPLACE EXTERNAL ACCESS INTEGRATION weather_eai
      ALLOWED_NETWORK_RULES = (weather_api_rule)
      ALLOWED_AUTHENTICATION_SECRETS = (weather_api_key)
      ENABLED = TRUE;
    
  3. 创建 Streamlit 对象:

    CREATE STREAMLIT weather_app
      FROM '@my_stage/weather_app'
      MAIN_FILE = 'streamlit_app.py'
      RUNTIME_NAME = 'SYSTEM$ST_CONTAINER_RUNTIME_PY3_11'
      COMPUTE_POOL = my_compute_pool
      QUERY_WAREHOUSE = my_warehouse
      EXTERNAL_ACCESS_INTEGRATIONS = (weather_eai)
      SECRETS = ('weather_api_name' = weather_api_key);
    
    ALTER STREAMLIT weather_app ADD LIVE VERSION FROM LAST;
    

在容器运行时中调用 Cortex Agent

要在容器运行时应用中调用 Cortex Agent,请从底层 Snowpark Container Services 容器读取会话令牌,然后使用 requests 库。这是推荐的 _snowflake.send_snow_api_request() 替代方案。

import requests
import json
import os

SNOWFLAKE_HOST = os.getenv("SNOWFLAKE_HOST")
SNOWFLAKE_ACCOUNT = os.getenv("SNOWFLAKE_ACCOUNT")
ANALYST_ENDPOINT = "/api/v2/cortex/analyst/message"
URL = "https://" + SNOWFLAKE_HOST + ANALYST_ENDPOINT

def get_token() -> str:
    """Read the oauth token embedded into SPCS container"""
    return open("/snowflake/session/token", "r").read()

def send_request(semantic_model_file, prompt):
    """Sends the prompt using the semantic model file """
    headers = {
        "Content-Type": "application/json",
        "accept": "application/json",
        "Authorization": f"Bearer {get_token()}",
        "X-Snowflake-Authorization-Token-Type": "OAUTH"
    }
    request_body = {
        "messages": [
            {
                "role": "user",
                "content": [{"type": "text", "text": prompt}],
            }
        ],
        "semantic_model_file": semantic_model_file,
    }
    return requests.post(URL, headers=headers, data=json.dumps(request_body))

仓库运行时中的密钥

在仓库运行时中,您可以使用 _snowflake 模块直接在 Streamlit 应用程序代码中访问密钥。仓库运行时继承了存储过程中对 _snowflake 模块的访问权限,这允许您检索在 Streamlit 对象中引用的密钥。

要在仓库运行时中使用密钥,请执行以下操作:

  1. 在 Snowflake 中创建密钥对象。有关更多信息,请参阅 CREATE SECRET

    CREATE OR REPLACE SECRET my_secret
      TYPE = GENERIC_STRING
      SECRET_STRING = 'my_secret_value';
    
  2. 创建外部访问集成并为其分配密钥。

    CREATE OR REPLACE EXTERNAL ACCESS INTEGRATION my_eai
      ALLOWED_AUTHENTICATION_SECRETS = (my_secret)
      ENABLED = TRUE;
    
  3. 使用 SECRETS 参数在 Streamlit 对象中引用该密钥:

    ALTER STREAMLIT my_warehouse_app
      SET EXTERNAL_ACCESS_INTEGRATIONS = (my_eai)
      SECRETS = ('my_secret_key' = my_secret);
    

    您必须将外部访问集成和密钥同时分配给 Streamlit 对象。您不能单独将密钥分配给 Streamlit 对象。

  4. 在 Streamlit 应用程序代码中,导入 _snowflake 模块并检索密钥:

    import streamlit as st
    import _snowflake
    
    # Retrieve an API key from a generic string secret
    my_secret = _snowflake.get_generic_secret_string('my_secret_key')
    

有关使用 _snowflake 模块访问密钥的更多信息,请参阅 用于密钥访问的 Python API

Streamlit 配置

Streamlit 应用程序可以包含一个配置文件 (.streamlit/config.toml)。此文件允许您自定义应用程序的各个方面,例如主题、布局和行为。配置文件以 TOML 格式编写。有关可用配置选项的更多信息,请参阅 config.toml (https://docs.streamlit.io/develop/api-reference/configuration/config.toml) 上的 Streamlit 文档。

对配置选项的支持因运行时环境而异。与仓库运行时相比,容器运行时通常为配置选项提供更广泛的支持,特别是在静态服务方面。下表显示了仓库和容器运行时支持哪些配置部分:

配置部分

仓库运行时

容器运行时

[global]

不支持

有限支持 (disableWidgetStateDuplicationWarning)

[logger]

不支持

不支持

[client]

不支持

有限支持(showErrorDetailsshowSidebarNavigation

[runner]

不支持

支持

[server]

不支持

不支持

[browser]

不支持

不支持

[mapbox]

不支持

支持(已弃用,请改用环境变量)

[theme]

支持

支持

[theme.sidebar]

支持

支持

[secrets]

不支持

支持(但仅建议用于非密钥环境变量)

[snowflake.sleep]

支持

不适用

有关使用 [snowflake.sleep] 部分在仓库运行时配置睡眠计时器的信息,请参阅 为 Streamlit 应用程序自定义睡眠计时器

以下目录结构显示了带有配置文件的 Streamlit 应用程序示例:

source_directory/
├── .streamlit/
│   └── config.toml
├── pyproject.toml
├── streamlit_app.py
└── uv.lock