Snowpark Container Services : considérations supplémentaires pour les services¶
Exécution SQL¶
Votre conteneur d’application peut se connecter à Snowflake et exécuter SQL. Cette rubrique décrit comment le code du conteneur obtient les informations requises pour se connecter à Snowflake, y compris les identifiants de connexion d’authentification, la base de données et le contexte du schéma du service, ainsi que l’entrepôt utilisé pour exécuter les SQL Instructions.
Options de configuration des identifiants¶
Snowflake recommande que les conteneurs d’application utilisent des identifiants de connexion fournis par Snowflake pour s’authentifier auprès de Snowflake lors de l’exécution de SQL. Bien qu’il soit possible d’utiliser d’autres identifiants de connexion en se connectant via une intégration d’accès externe (EAI), la connexion via une EAI traite le service comme s’il fonctionnait en dehors de Snowflake et se connecte à Snowflake via Internet.
You have three options to connect to Snowflake from a service container:
Utiliser les informations d’identification de l’utilisateur du service fournies par Snowflake : Snowflake fournit chaque service avec des identifiants de connexion, qui sont appelés identifiants de connexion de service. Un service utilise ces identifiants de connexion pour se connecter à Snowflake en tant qu’utilisateur du service.
Utiliser les identifiants de connexion de l’appelant fournis par Snowflake (droits de l’appelant) : lorsque vous configurez votre service avec les droits de l’appelant, Snowflake fournit également des identifiants de connexion pour que le service puisse se connecter à Snowflake en tant qu’utilisateur appelant.
Use other credentials: In this case, you use an external access integration (EAI) that allows your service to connect to Snowflake’s internet endpoint by using valid authentication credentials. This option requires an administrator to create the EAI, and then grant the USAGE privilege on the integration to the service owner role.
Note
Si vous utilisez des intégrations d’accès externes pour accéder à Snowflake, vous pourriez envoyer des informations potentiellement sensibles sur Internet.
Pour des exemples de code qui utilisent divers pilotes Snowflake pour la connexion à Snowflake, voir Exemples de connexion Snowflake.
Utilisation des identifiants de connexion de l’utilisateur du service fournis par Snowflake¶
Lorsque vous utilisez des identifiants de connexion de service fournis par Snowflake, tenez compte des effets suivants :
Every object in Snowflake has an owner role, which is the role that is used to create the object. A service’s owner role determines the capabilities that the service is allowed when it interacts with Snowflake. These capabilities include executing SQL, accessing stages, and performing service-to-service networking.
When you create a service, Snowflake also creates a service user that is specific to that service. That service user has access to only two roles: the service owner role and the “PUBLIC” role. The default role for the service user is the service owner role.
When you start a service, including job services, Snowflake performs several actions. In each of your application containers, Snowflake enables the container code to use drivers for connecting to Snowflake and executing SQL, which is similar to any other code on your computer connecting to Snowflake. The following list shows the actions that Snowflake performs when you start a service:
Provides credentials (an OAuth token) in the container in a file that is named
/snowflake/session/token. The container code uses these credentials to authenticate as the service user. This OAuth token can’t be used outside Snowpark Container ServicesSets the following environment variables for you to configure a Snowflake client in your service code:
SNOWFLAKE_ACCOUNT: This variable is set to the account locator for the Snowflake account that the service is currently running under.
SNOWFLAKE_HOST: This variable provides the hostname that is used to connect to Snowflake.
Lorsque vous créez une connexion à Snowflake en tant qu’utilisateur de service, le code du conteneur doit utiliser SNOWFLAKE_HOST, SNOWFLAKE_ACCOUNT et le jeton OAuth. Le jeton OAuth ne peut pas être utilisé sans utiliser également SNOWFLAKE_HOST.
Exemple
Dans Tutoriel 2 (voir main.py), le code lit les variables d’environnement comme indiqué dans l’exemple suivant :
SNOWFLAKE_ACCOUNT = os.getenv('SNOWFLAKE_ACCOUNT')
SNOWFLAKE_HOST = os.getenv('SNOWFLAKE_HOST')
The code passes these variables to a connection creation code for the Snowflake client of choice. The container uses these credentials to create a new session, with the service’s owner role as the session’s primary role, to run queries. The following example shows the minimum code that you need to create a Snowflake connection in Python:
def get_login_token():
with open('/snowflake/session/token', 'r') as f:
return f.read()
conn = snowflake.connector.connect(
host = os.getenv('SNOWFLAKE_HOST'),
account = os.getenv('SNOWFLAKE_ACCOUNT'),
token = get_login_token(),
authenticator = 'oauth'
)
Be aware of the following details about this OAuth token:
Snowflake refreshes the content of the
/snowflake/session/tokenfile every few minutes. Every token is valid for up to one hour. After a container connects to Snowflake successfully, the expiration time doesn’t apply to the connection, as is the case with any sessions that users create directly.This OAuth token is valid only within the specific Snowflake service. You can’t copy the OAuth token and use it outside the service.
If you use the OAuth token to connect, it creates a new session. The OAuth token is not associated with any existing SQL session.
Note
A significant difference between executing stored procedures and executing a service is that stored procedures run in the same session as the SQL that runs the procedures. But every time a container establishes a new connection, you create a new session.
Pour voir les requêtes émises par un utilisateur de service spécifique, vous pouvez utiliser le rôle ACCOUNTADMIN pour voir l”historique des requêtes. Le nom d’utilisateur de l’utilisateur du service apparaît sous les formes suivantes :
Pour un service créé avant la version du serveur 8.35, le nom d’utilisateur du service est au format
SF$SERVICE$unique-id.Pour un service créé après la version du serveur 8.35, le nom d’utilisateur du service est le même que le nom du service.
Note
A service’s owner role is the role that created the service. You can define one or more service roles to manage access to the endpoints that the service exposes. For more information, see Gestion des privilèges liés aux services.
À propos de l’utilisation des informations d’identification de l’appelant fournies par Snowflake (droits de l’appelant)¶
In certain application scenarios, you might need to execute queries by using the context of the end user rather than the service user as explained in the preceding section. The caller’s rights feature is used in this context.
For example, suppose that you create a service that exposes a public endpoint for a web application that displays a dashboard that uses data stored in Snowflake. You grant other users in your Snowflake account access to the dashboard by granting them the service role. When a user signs in, the dashboard displays only the data that user is authorized to access.
However, because containers by default execute queries by using the service user and the service’s owner role, the dashboard shows the data that the service’s owner role has access to, regardless of which end user connected to the endpoint. As a result, the dashboard isn’t limited to the data the end user is authorized to access, allowing the signed-in user to see data they shouldn’t have access to.
To limit the dashboard to show only data that is accessible to the signed in user, the application containers must execute SQL by using privileges granted to the end user. You can enable this by using caller’s rights in the application.
Note
The caller’s rights feature is supported only when accessing a service using network ingress. The feature isn’t available when using a service function to access the service.
La fonction des droits de l’appelant n’est actuellement pas prise en charge dans une application Snowflake Native App (applications avec les conteneurs).
Configure caller’s rights for your service¶
Configuring caller’s rights for your application is a two-step procedure.
In the service specification, set the
executeAsCallertotrue, in as shown in the following specification fragment:spec: containers: ... capabilities: securityContext: executeAsCaller: true
This setting tells Snowflake that the application intends to use caller’s rights and causes Snowflake to insert the
Sf-Context-Current-User-Tokenheader in every incoming request before sending the request to the application container. This user token facilitates query execution as the calling user. If not specified,executeAsCallerdefaults tofalse.Specifying the
executeAsCalleroption doesn’t affect the service’s ability to execute queries as the service user and service’s owner role. WithexecuteAsCallerenabled, the service has the option to connect to Snowflake both as a calling user and as a service user.To establish a Snowflake connection on behalf of the calling user, update your application code to create a login token that includes both the OAuth token that Snowflake provided to the service and the user token from the
Sf-Context-Current-User-Tokenheader.Le jeton de connexion doit respecter ce format :
<service-oauth-token>.<Sf-Context-Current-User-Token>.This update is demonstrated in the following Python code fragment:
# Environment variables below will be automatically populated by Snowflake. SNOWFLAKE_ACCOUNT = os.getenv("SNOWFLAKE_ACCOUNT") SNOWFLAKE_HOST = os.getenv("SNOWFLAKE_HOST") def get_login_token(): with open("/snowflake/session/token", "r") as f: return f.read() def get_connection_params(ingress_user_token = None): # start a Snowflake session as ingress user # (if user token header provided) if ingress_user_token: logger.info("Creating a session on behalf of the current user.") token = get_login_token() + "." + ingress_user_token else: logger.info("Creating a session as the service user.") token = get_login_token() return { "account": SNOWFLAKE_ACCOUNT, "host": SNOWFLAKE_HOST, "authenticator": "oauth", "token": token } def run_query(request, query): ingress_user_token = request.headers.get('Sf-Context-Current-User-Token') # ingress_user_token is None if header not present connection_params = get_connection_params(ingress_user_token) with Session.builder.configs(connection_params).create() as session: # use the session to execute a query.
Dans l’exemple ci-dessus :
La fonction
get_login_tokenlit le fichier dans lequel Snowflake a copié le jeton OAuth pour que le conteneur l’utilise.The
get_connection_paramsfunction constructs a token by concatenating the OAuth token and the user token from theSf-Context-Current-User-Tokenheader. The function includes this token in a dictionary of parameters that the application uses to connect to Snowflake.
Note
When a service uses caller’s rights, it can connect to Snowflake as multiple users. You are responsible for managing access to resources that arn’t managed by Snowflake.
For example, in Streamlit apps, the st.connection object automatically caches the connection by using st.cache_resource in the global state, making it accessible across Streamlit sessions that are started by different users. When you use caller’s rights, consider using st.session_state to store connections on a per-session basis to avoid sharing connections between users.
For an example with step-by-step instructions, see Create a service with caller’s rights enabled.
Accès à un service dont les droits de l’appelant sont configurés¶
Configuring caller’s rights means that your service is establishing a Snowflake connection on behalf of the caller. How you log in to the service’s ingress endpoints, either programmatically or by using a browser, remains the same. After log in, the following behaviors and optionsapply:
Accessing a public endpoint using a browser: After you log into an endpoint, the service establishes a connection to** Snowflake on behalf of the calling user using the default role of the user. If there is no default role configured for the user, the PUBLIC role is used.
Accessing a public endpoint programmatically: When logging into an endpoint programmatically using JWT token, you can optionally set the
scopeparameter to specify the role to activate
Currently, after a service establishes a caller’s right connection to Snowflake on behalf of the caller, switching roles is not supported. If your application needs to use different roles to access different objects, you must change the user’s default secondary roles property.
To set up the user to have all secondary roles active by default, use the ALTER USER command to set the DEFAULT_SECONDARY_ROLES property of the user to (“ALL”), as shown in the following example:
ALTER USER my_user SET DEFAULT_SECONDARY_ROLES = ( 'ALL' );
Gestion de l’accès des appelants à un service¶
When a service creates a caller’s rights session, the session operates as the calling user, not as the service user. When an operation is performed by using this session, Snowflake applies a sequence oftwo permissions checks:
The first permissions check is performed as if the user created the session directly. This check is part of thenormal permission checks that Snowflake performs for the user.
The second permissions check verifies that the service is allowed to perform the operation on behalf of a user. Snowflake verifies this by ensuring that the service’s owner role was granted the necessary caller grants.
Dans une session de droits de l’appelant, le contrôle normal des autorisations et le contrôle des droits de l’appelant du rôle de propriétaire du service <label-spcs_additional_considerations_configuring_callers_rights> doivent tous deux autoriser l’opération ; on parle alors de droits restreints de l’appelant. Par défaut, le service n’a pas l’autorisation de faire quoi que ce soit au nom d’un utilisateur. Vous devez explicitement accorder des droits d’appelant au service afin qu’il puisse fonctionner avec les privilèges de l’appelant.
For example, suppose a user U1 uses a role R1 that has the SELECT privilege on the table T1. When U1 logs into the public endpoint of your
service (example_service), which is configured to use the caller’s rights, the service then establishes a connection with Snowflake on
behalf of U1.
Pour permettre au service d’interroger la table de requête T1 pour le compte de U1, vous devez accorder au rôle de propriétaire du service les privilèges suivants :
Privilèges pour résoudre le nom de la table, en accordant une autorisation à l’appelant qui permet au service de s’exécuter avec le privilège USAGE sur la base de données et le schéma de cette table.
Privilèges permettant d’utiliser un entrepôt pour exécuter des requêtes en accordant une autorisation à l’appelant qui permet au service de s’exécuter avec le privilège USAGE sur un entrepôt.
Privilèges pour interroger la table en accordant une autorisation à l’appelant qui permet au service de s’exécuter avec le privilège SELECT sur la table
T1.
L’exemple suivant montre comment accorder ces privilèges au rôle de propriétaire du service :
-- Permissions to resolve the table's name.
GRANT CALLER USAGE ON DATABASE <db_name> TO ROLE <service_owner_role>;
GRANT CALLER USAGE ON SCHEMA <schema_name> TO ROLE <service_owner_role>;
-- Permissions to use a warehouse
GRANT CALLER USAGE ON WAREHOUSE <warehouse_name> TO ROLE <service_owner_role>;
-- Permissions to query the table.
GRANT CALLER SELECT ON TABLE T1 TO ROLE <service_owner_role>;
Tout rôle de votre compte disposant du privilège global MANAGE CALLER GRANT peut accorder des autorisations aux appelants. Pour plus d’informations sur les autorisations accordées à l’appelant, voir GRANT CALLER et Droits restreints de l’appelant.
Exemple¶
For an example of a service that uses the caller’s rights feature when executing SQL queries on behalf of the users is provided. For more information, see Create a service with caller’s rights enabled.
Connect to Snowflake by using other credentials¶
Vous pouvez utiliser d’autres formes d’authentification pour la connexion à Snowflake, pas seulement le jeton OAuth fourni par Snowflake. Pour ce faire, vous créez une intégration d’accès externe (EAI) qui permet à votre conteneur de se connecter à Snowflake comme si le conteneur fonctionnait en dehors de Snowflake et se connectait via Internet. Lorsque vous vous connectez de cette manière, vous n’avez pas besoin de configurer l’hôte qui est utilisé par le client.
Note
Comme ces connexions passent par une EAI, l’authentification Snowflake applique également les politiques réseau. Si votre entreprise nécessite des politiques réseau, la connexion avec d’autres identifiants de connexion n’est pas prise en charge.
Par exemple, la connexion suivante spécifie le nom d’utilisateur et le mot de passe pour l’authentification :
conn = snowflake.connector.connect(
account = '<acct-name>',
user = '<user-name>',
password = '<password>'
)
To use a default hostname, you need external access integration with a network rule that allows access from your service to the
Snowflake internet hostname for your account. For example, if your account name is MYACCOUNT in the organization MYORG, the hostname is
myorg-myaccount.snowflakecomputing.com. For more information, see Configuration de la sortie réseau. Privatelink hostnames are not supported
Créez une règle de réseau qui correspond au nom d’hôte d’API Snowflake de votre compte :
CREATE OR REPLACE NETWORK RULE snowflake_egress_access MODE = EGRESS TYPE = HOST_PORT VALUE_LIST = ('myorg-myaccount.snowflakecomputing.com');
Créez une intégration à l’aide de la règle de réseau précédente :
CREATE EXTERNAL ACCESS INTEGRATION snowflake_egress_access_integration ALLOWED_NETWORK_RULES = (snowflake_egress_access) ENABLED = TRUE;
Configuration of the database and schema context for executing SQL¶
En plus de fournir des identifiants de connexion, Snowflake fournit également la base de données et le contexte de schéma dans lesquels le service est créé. Le code du conteneur peut utiliser ces informations pour exécuter SQL dans le même contexte de base de données et de schéma que le service.
Cette section présente deux concepts :
La logique utilisée par Snowflake pour déterminer la base de données et le schéma dans lesquels créer votre service.
La méthode par laquelle Snowflake transmet cette information à vos conteneurs, permettant ainsi au code du conteneur d’exécuter SQL dans le même contexte de base de données et de schéma.
Snowflake utilise le nom du service pour déterminer la base de données et le schéma dans lesquels créer un service :
Exemple 1 : dans les commandes CREATE SERVICE et EXECUTE JOB SERVICE suivantes, le nom du service ne spécifie pas explicitement une base de données et un nom de schéma. Snowflake crée le service et le service de tâche dans la base de données et le schéma actuels.
-- Create a service. CREATE SERVICE test_service IN COMPUTE POOL ... -- Execute a job service. EXECUTE JOB SERVICE IN COMPUTE POOL tutorial_compute_pool NAME = example_job_service ...
Exemple 2 : dans les commandes CREATE SERVICE et EXECUTE JOB SERVICE suivantes, le nom du service comprend une base de données et un nom de schéma. Snowflake crée le service et le service de tâche dans la base de données (
test_db) et le schéma (test_schema) spécifiés, quel que soit le schéma actuel.-- Create a service. CREATE SERVICE test_db.test_schema.test_service IN COMPUTE POOL ... -- Execute a job service. EXECUTE JOB SERVICE IN COMPUTE POOL tutorial_compute_pool NAME = test_db.test_schema.example_job_service ...
Lorsque Snowflake démarre un service, Snowflake fournit les informations relatives à la base de données et au schéma aux conteneurs en cours d’exécution en utilisant les variables d’environnement suivantes :
SNOWFLAKE_DATABASE
SNOWFLAKE_SCHEMA
Le code de votre conteneur peut utiliser des variables d’environnement dans le code de connexion pour déterminer la base de données et le schéma à utiliser, comme le montre cet exemple :
conn = snowflake.connector.connect(
host = os.getenv('SNOWFLAKE_HOST'),
account = os.getenv('SNOWFLAKE_ACCOUNT'),
token = get_login_token(),
authenticator = 'oauth',
database = os.getenv('SNOWFLAKE_DATABASE'),
schema = os.getenv('SNOWFLAKE_SCHEMA')
)
Exemple
Dans le tutoriel 2, vous créez un service de tâche Snowflake qui se connecte à Snowflake et exécute des instructions SQL. Les étapes suivantes résument la manière dont le code du tutoriel utilise les variables d’environnement :
Dans la configuration commune (voir la section Configuration commune), vous créez des ressources, notamment une base de données et un schéma. Vous définissez également la base de données et le schéma actuels pour la session :
USE DATABASE tutorial_db; ... USE SCHEMA data_schema;
Après avoir créé un service de tâche (exécution de EXECUTE JOB SERVICE), Snowflake démarre le conteneur et définit les variables d’environnement suivantes dans le conteneur en fonction de la base de données et du schéma actuels de la session :
SNOWFLAKE_DATABASE est défini sur « TUTORIAL_DB »
SNOWFLAKE_SCHEMA est défini sur « DATA_SCHEMA »
Le code de la tâche (voir
main.pydans le tutoriel 2) lit ces variables d’environnement :SNOWFLAKE_DATABASE = os.getenv('SNOWFLAKE_DATABASE') SNOWFLAKE_SCHEMA = os.getenv('SNOWFLAKE_SCHEMA')
Le code de la tâche définit la base de données et le schéma comme le contexte dans lequel exécuter les instructions SQL (fonction
run_job()dansmain.py) :{ "account": SNOWFLAKE_ACCOUNT, "host": SNOWFLAKE_HOST, "authenticator": "oauth", "token": get_login_token(), "warehouse": SNOWFLAKE_WAREHOUSE, "database": SNOWFLAKE_DATABASE, "schema": SNOWFLAKE_SCHEMA } ...
Note
SNOWFLAKE_ACCOUNT, SNOWFLAKE_HOST, SNOWFLAKE_DATABASE, SNOWFLAKE_SCHEMA sont des variables d’environnement que Snowflake génère pour le conteneur d’application, mais SNOWFLAKE_WAREHOUSE ne l’est pas (le code de l’application du tutoriel 2 a créé cette variable parce que Snowflake ne transmet pas de nom d’entrepôt à un conteneur).
Spécification de l’entrepôt pour votre conteneur¶
Si votre service se connecte à Snowflake pour exécuter une requête dans un entrepôt Snowflake, vous disposez des options suivantes pour spécifier un entrepôt :
Spécifiez un entrepôt dans votre code d’application. Spécifiez un entrepôt dans le cadre de la configuration de la connexion lorsque vous démarrez une session Snowflake pour exécuter des requêtes dans votre code. Pour un exemple, voir le tutoriel 2.
Specify a default warehouse when creating a service. Specify the optional QUERY_WAREHOUSE parameter in the CREATE SERVICE or EXECUTE JOB SERVICE command to provide a default warehouse. If your application code doesn’t provide a warehouse as part of connection configuration, Snowflake uses the default warehouse. Use the ALTER SERVICE command to change the default warehouse.
Note
L’entrepôt spécifié à l’aide du paramètre QUERY_WAREHOUSE est le paramètre par défaut uniquement pour l’utilisateur de service. Lorsque le service se connecte à Snowflake au nom d’un autre utilisateur — dans le contexte de scénario des droits de l’appelant, Snowflake utilise l’entrepôt par défaut de l’utilisateur.
If you specify a warehouse by using both methods, the warehouse that is specified in the application code is used.
Access service user query history¶
You can find queries executed by your service as the service user by filtering the Vue QUERY_HISTORY or QUERY_HISTORY function where user_type is SNOWFLAKE_SERVICE.
Exemple 1 : récupérer les requêtes exécutées par un service.
SELECT *
FROM snowflake.account_usage.query_history
WHERE user_type = 'SNOWFLAKE_SERVICE'
AND user_name = '<service_name>'
AND user_database_name = '<service_db_name>'
AND user_schema_name = '<service_schema_name>'
order by start_time;
Dans la clause WHERE :
user_name = '<service_name>': Vous spécifiez le nom du service comme nom d’utilisateur car un service exécute des requêtes en tant qu’utilisateur du service <label-snowpark_containers_connect_to_snowflake_from_container>, et que le nom de l’utilisateur du service est le même que le nom du service.user_type = 'SNOWFLAKE_SERVICE'etuser_name = '<service_name>': Cela limite le résultat de la requête à récupérer uniquement les requêtes exécutées par un service.user_database_nameetuser_schema_name: Pour un utilisateur de service, il s’agit de la base de données et du schéma du service.
Vous pouvez obtenir les mêmes résultats en appelant la fonction QUERY_HISTORY.
SELECT *
FROM TABLE(<service_db_name>.information_schema.query_history())
WHERE user_database_name = '<service_db_name>'
AND user_schema_name = '<service_schema_name>'
AND user_type = 'SNOWFLAKE_SERVICE'
AND user_name = '<service_name>'
order by start_time;
Dans la clause WHERE :
user_type = 'SNOWFLAKE_SERVICE'etuser_name = '<service_name>'limitent le résultat de la requête pour récupérer uniquement les requêtes exécutées par un service.Les noms
user_database_nameetuser_schema_name(pour un utilisateur de service) sont la base de données et le schéma du service.
Exemple 2 : récupérer les requêtes exécutées par les services et les informations correspondantes sur les services.
SELECT query_history.*, services.*
FROM snowflake.account_usage.query_history
JOIN snowflake.account_usage.services
ON query_history.user_name = services.service_name
AND query_history.user_schema_id = services.service_schema_id
AND query_history.user_type = 'SNOWFLAKE_SERVICE'
La requête rejoint les vues QUERY_HISTORY et SERVICES pour récupérer des informations sur les requêtes et les services qui ont exécuté les requêtes. Remarques :
Pour les requêtes exécutées par les services, le
query_history.user_nameest le nom de l’utilisateur du service, qui est le même que le nom du service.La requête joint les vues à l’aide des IDs de schémas (pas le nom des schémas) pour vous assurer que vous faites référence au même schéma, car si vous supprimez et recréez un schéma, l’ID du schéma change, mais le nom reste le même.
Vous pouvez ajouter des filtres facultatifs à la requête. Par exemple :
Filtre
query_historypour récupérer uniquement les services qui ont exécuté des requêtes spécifiques.Filtre
servicespour récupérer uniquement les requêtes exécutées par des services spécifiques.
Exemple 3 : pour chaque service, récupérer les informations sur l’utilisateur du service.
SELECT services.*, users.*
FROM snowflake.account_usage.users
JOIN snowflake.account_usage.services
ON users.name = services.service_name
AND users.schema_id = services.service_schema_id
AND users.type = 'SNOWFLAKE_SERVICE'
La requête joint les vues SERVICES et USERS dans le schéma ACCOUNT_USAGE permettant de récupérer les services et les informations sur les utilisateurs des services. Remarques :
Lorsqu’un service exécute des requêtes, il exécute les requêtes en tant qu’utilisateur du service et le nom de l’utilisateur du service est le même que le nom du service. Par conséquent, vous spécifiez la condition de jointure :
users.name = services.service_name.Les noms de service ne sont uniques qu’au sein d’un schéma. Par conséquent, la requête spécifie la condition de jointure (
users.schema_id = services.service_schema_id) pour garantir que chaque utilisateur de service est associé au service spécifique auquel il appartient (et non à un autre service du même nom exécuté dans des schémas différents).
Configuration de l’entrée du réseau¶
Pour permettre à quiconque d’interagir avec votre service depuis Internet, vous déclarez les ports réseau sur lesquels votre service écoute en tant que points de terminaison dans le fichier de spécification du service. Ces points de terminaison contrôlent l’entrée.
Par défaut, les points de terminaison de service sont privés. Seules les fonctions de service et les communications de service à service peuvent adresser des requêtes aux points de terminaison privés. Vous pouvez déclarer un point de terminaison comme étant public afin d’autoriser les requêtes vers un point de terminaison depuis Internet. La création d’un point de terminaison public est une opération privilégiée et le rôle de propriétaire du service doit disposer du privilège BIND SERVICE ENDPOINT sur le compte.
endpoints:
- name: <endpoint name>
port: <port number>
protocol : < TCP / HTTP >
public: true
corsSettings: # optional CORS configuration
Access-Control-Allow-Origin: # required list of allowed origins
- <origin> # for example, "http://example.com"
- <origin>
...
Access-Control-Allow-Methods: # optional list of HTTP methods
- <method>
- <method>
...
Access-Control-Allow-Headers: # optional list of HTTP headers
- <header-name>
- <header-name>
...
Access-Control-Expose-Headers: # optional list of HTTP headers
- <header-name>
- <header-name>
...
Pour un exemple, voir le tutoriel 1.
Délai d’expiration de la connexion d’entrée¶
Les points de terminaison ont un délai d’expiration de 90 secondes. S’il n’y a aucune activité sur une connexion à un point de terminaison d’entrée pendant 90 secondes, Snowflake met fin à la connexion. Si votre application a besoin d’une connexion plus longue, utilisez l’interrogation ou WebSockets.
Déconnexion de l’authentification du navigateur web d’entrée¶
Si vous créez une application Web qui fonctionne en tant que service, vous avez la possibilité de permettre aux utilisateurs de se déconnecter de votre application en les dirigeant vers /sfc-endpoint/logout.
Après s’être déconnecté, l’utilisateur devra s’authentifier à nouveau auprès de Snowflake pour accéder au point de terminaison public du service.
Entrée et sécurité des applications web¶
Vous pouvez créer un service Snowpark Container Services pour l’hébergement web en utilisant la prise en charge de point de terminaison public (entrée réseau). Pour plus de sécurité, Snowflake utilise un service proxy pour contrôler les requêtes entrantes des clients vers votre service et les réponses sortantes de votre service vers les clients. Cette section explique ce que fait le proxy et comment il affecte un service déployé dans Snowpark Container Services.
Note
Lorsque vous testez un service localement, vous n’utilisez pas le proxy Snowflake et il y aura donc des différences entre votre expérience d’exécution d’un service en local et celle d’un service déployé dans Snowpark Container Services. Passez en revue cette section et mettez à jour votre configuration locale pour améliorer les tests.
Par exemple :
Le proxy ne transmet pas une requête HTTP entrante si la requête utilise une méthode HTTP interdite.
Le proxy envoie une réponse 403 au client si l’en-tête Content-Type de la réponse indique qu’elle contient un exécutable.
En outre, le proxy peut également injecter de nouveaux en-têtes et modifier les en-têtes existants dans la demande et la réponse, en tenant compte de votre conteneur et de la sécurité des données.
Par exemple, à la réception d’une requête, votre service peut envoyer du contenu HTML, JavaScript, CSS et d’autres contenus d’une page web au navigateur du client dans la réponse. La page web dans le navigateur fait partie de votre service et agit en tant qu’interface utilisateur. Pour des raisons de sécurité, si votre service est soumis à des restrictions (par exemple, l’interdiction d’établir des connexions réseau avec d’autres sites), vous voudrez peut-être que la page web de votre service soit soumise aux mêmes restrictions.
Par défaut, les services disposent d’autorisations limitées pour accéder à Internet. Le navigateur doit également empêcher l’application cliente d’accéder à Internet et de partager potentiellement des données dans la plupart des cas. Si vous avez mis en place une intégration d’accès externe (EAI) pour permettre à votre service d’accéder à example.com (voir Configuration de la sortie réseau), la page web de votre service devrait également être en mesure d’accéder à example.com via votre navigateur.
Le proxy Snowflake applique les mêmes restrictions de réseau au service et à la page web en ajoutant un en-tête Content-Security-Policy (CSP) dans la réponse. Par défaut, le proxy ajoute une CSP de base dans la réponse pour se protéger contre les menaces de sécurité courantes. La sécurité du navigateur est le meilleur effort pour équilibrer la fonctionnalité et la sécurité. Il s’agit d’une responsabilité partagée qui garantit que cette base de référence est appropriée à votre cas d’utilisation. En outre, si votre service est configuré pour utiliser une EAI, le proxy applique les mêmes règles de réseau du EAI à la CSP pour la page web. Cette CSP permet à la page web du navigateur d’accéder aux mêmes sites que ceux auxquels le service peut accéder.
Snowflake fournit l’assistance CORS que vous configurez dans la spécification du service.
Le proxy Snowflake renvoie les paramètres CORS définis dans la spécification du service. Notez que le proxy supprime les en-têtes CORS renvoyés par le service.
Les en-têtes CORS suivants sont définis par défaut :
L’en-tête
Access-Control-Expose-Headerssignale toujours les noms d’en-têtes suivants, en plus des en-têtes configurés dans la spécification de service pour le point de terminaison.X-Frame-OptionsCross-Origin-Opener-PolicyCross-Origin-Resource-PolicyX-Content-Type-OptionsCross-Origin-Embedder-PolicyContent-Security-Policy-Report-OnlyContent-Security-Policy
Access-Control-Max-Ageest défini sur deux heures.Access-Control-Allow-Credentialsest défini sur true.
En outre, Snowflake définit l’en-tête Vary avec la valeur Origin pour indiquer au navigateur qu’en fonction de la valeur de Origin, la valeur de Access-Control-Allow-Origin peut être différente.
L’en-tête Authorization est nécessaire pour effectuer la requête CORS. Vous pouvez spécifier un jeton d’accès programmatique (PAT) dans cet en-tête (Authorization: "Snowflake Token=\"${patToken}\""). Pour plus d’informations sur la génération d’un jeton d’accès programmatique, voir Utilisation de jetons d’accès programmatique pour l’authentification.
Les sections suivantes expliquent comment le proxy Snowflake gère les requêtes entrantes pour votre service et modifie les réponses sortantes de votre service aux clients.
Requêtes reçues par le service¶
Lorsqu’une requête arrive, le proxy effectue les opérations suivantes avant de transmettre la requête au service :
Requêtes entrantes avec des méthodes HTTP interdites : Si une requête HTTP entrante utilise l’une des méthodes HTTP interdites suivantes, le proxy ne transmet pas la requête à votre service :
TRACECONNECT
Nettoyage des en-têtes des requêtes entrantes : Le proxy Snowflake supprime les en-têtes des requêtes suivants s’ils sont présents :
X-SF-SPCS-AuthorizationAuthorization: Uniquement supprimé s’il contient un jeton Snowflake Services ; sinon, il est transmis à votre service.
Réponses envoyées aux clients¶
Le proxy Snowflake applique ces modifications à la réponse envoyée par votre service avant de la transmettre au client.
Nettoyage des en-têtes : le proxy Snowflake supprime ces en-têtes de réponse, s’ils sont présents :
X-XSS-ProtectionServerX-Powered-ByPublic-Key-Pins
Manipulation des en-têtesCORS : Voir Considérations sur les entrées et CORS.
En-tête de réponse Content-Type : Si la réponse de votre service inclut l’en-tête Content-Type avec l’une des valeurs de type MIME suivantes (qui indiquent un exécutable), le proxy Snowflake ne transmet pas cette réponse au client. Au lieu de cela, le proxy envoie une réponse
403 Forbidden.application/x-msdownload: exécutable Microsoft.application/exe: exécutable générique.application/x-exe: autre exécutable générique.application/dos-exe: exécutable DOS.application/x-winexe: exécutable Windows.application/msdos-windows: exécutable Windows MS-DOS.application/x-msdos-program: exécutable MS-DOS.application/x-sh: script shell Unix.application/x-bsh: script shell Bourne.application/x-csh: script shell C.application/x-tcsh: script shell Tcsh.application/batch: fichier batch Windows.
En-tête de réponse X-Frame-Options : Pour prévenir les attaques par détournement de clics, le proxy Snowflake définit cet en-tête de réponse sur
DENY, empêchant d’autres pages web d’utiliser un iframe vers la page web de votre service.En-tête de réponse Cross-Origin-Opener-Policy (COOP) : Snowflake définit l’en-tête de réponse COOP sur
same-originpour empêcher les fenêtres cross-origin d’accéder à votre onglet de service.En-tête de réponse Cross-Origin-Resource-Policy (CORP) : Snowflake définit l’en-tête CORP sur
same-originpour empêcher les sites externes de charger les ressources exposées par le point de terminaison d’entrée (par exemple, dans un iframe).En-tête de réponse X-Content-Type-Options : Snowflake proxy définit ce header sur
nosniffpour s’assurer que les clients ne changent pas le type MIME indiqué dans la réponse par votre service.En-tête de réponse Cross-Origin-Embedder-Policy (COEP) : le proxy Snowflake définit l’en-tête de réponse COEP sur
credentialless, ce qui signifie que lors du chargement d’un objet cross-origin tel qu’une image ou un script, si l’objet distant ne prend pas en charge le protocole Cross-Origin Resource Sharing (CORS), Snowflake n’envoie pas les identifiants de connexion lors du chargement de l’objet.En-tête de réponse Content-Security-Policy-Report-Only : le proxy Snowflake remplace cet en-tête de réponse par une nouvelle valeur indiquant au client d’envoyer les rapports CSP à Snowflake.
En-tête de réponse Content-Security-Policy (CSP) : par défaut, le proxy Snowflake ajoute la CSP de base suivante pour se protéger contre les attaques web courantes.
default-src 'self' 'unsafe-inline' 'unsafe-eval' blob: data:; object-src 'none'; connect-src 'self'; frame-ancestors 'self';
Il existe deux considérations relatives à la politique de sécurité du contenu :
En plus de la politique de sécurité du contenu de base que le proxy ajoute, le service lui-même peut explicitement ajouter une CSP dans la réponse. Un service peut choisir de renforcer la sécurité en ajoutant une CSP plus stricte. Par exemple, un service peut ajouter la CSP suivante pour n’autoriser que les scripts provenant de
self.script-src 'self'
La réponse envoyée au client contiendra deux en-têtes CSP. Lorsqu’ils reçoivent la réponse, les navigateurs clients appliquent alors la politique de sécurité du contenu la plus stricte qui inclut les restrictions supplémentaires spécifiées par chaque politique.
Si vous configurez une intégration d’accès externe (EAI) pour permettre à votre service d’accéder à un site externe (Configuration de la sortie réseau), le proxy Snowflake crée une CSP qui permet à votre page web d’accéder à ce site. Par exemple, supposons qu’une règle de réseau associée à un EAI permette à votre service d’accéder à la sortie
example.com. Ensuite, le proxy Snowflake ajoute cet en-tête de réponse CSP :default-src 'self' 'unsafe-inline' 'unsafe-eval' http://example.com https://example.com blob: data:; object-src 'none'; connect-src 'self' http://example.com https://example.com wss://example.com; frame-ancestors 'self';
Les navigateurs respectent la politique d’accès au contenu reçue dans la réponse. Dans cet exemple, les navigateurs autorisent l’application à accéder à
example.commais pas à d’autres sites.
Considérations sur les entrées et CORS¶
Par défaut, les navigateurs empêchent les applications web hébergées sur un serveur d’envoyer des requêtes à un autre serveur dont le nom d’hôte est différent. Par exemple, si vous hébergez une application Web en dehors de Snowpark Container Services qui doit interagir avec un service backend déployé au sein de Snowpark Container Services, cette restriction s’applique.
CORS (Cross-Origin Resource Sharing) permet à un service Snowpark Container Services d’indiquer aux navigateurs d’autoriser les requêtes provenant d’applications Web hébergées en dehors de son environnement. Vous pouvez configurer chaque point de terminaison public pour spécifier la manière dont il répond aux requêtes de contrôle en amont et aux requêtes standard CORS.
Le proxy Snowflake remplace toujours les en-têtes de réponse suivants :
Access-Control-Allow-OriginAccess-Control-Allow-MethodsAccess-Control-Allow-HeadersAccess-Control-Expose-HeadersAccess-Control-Max-AgeAccess-Control-Allow-Credentials
Le proxy Snowflake n’inclut aucun de ces en-têtes CORS dans la réponse lorsque l’une des conditions suivantes s’applique :
CORS n’est pas configuré pour le point de terminaison du service. C’est-à-dire qu’il n’y a pas de
corsSettingsdans la spécification du serviceCORS est configuré pour le point de terminaison du service, mais l’en-tête
Originde la requête ne correspond pas au champAccess-Control-Allow-Originspécifié dans la spécification du service
Dans la spécification du service, vous pouvez configurer les paramètres de CORS pour chaque point de terminaison public. Lorsque l’en-tête origin de la requête correspond au champ Access-Control-Allow-Origin spécifié pour le point de terminaison dans la spécification, le proxy inclut dans la réponse les en-têtes CORS définis dans la spécification, avec les ajustements suivants :
Access-Control-Allow-Origin: Renvoie l’en-têteOriginde la requête.Access-Control-Expose-Headers: Fusionne la liste des en-têtes autorisés que vous avez configurés avec ces en-têtes toujours exposés :X-Frame-Options,Cross-Origin-Opener-Policy,Cross-Origin-Resource-Policy,X-Content-Type-Options,Cross-Origin-Embedder-Policy,Content-Security-Policy-Report-Only,Content-Security-Policy.Access-Control-Max-Ageest défini sur deux heures.Access-Control-Allow-Credentialsest défini sur true.
Considérations sur les entrées et SSO¶
Lorsque vous accédez au point de terminaison public depuis Internet, vous pouvez constater que l’authentification par nom d’utilisateur/mot de passe fonctionne, mais que SSO aboutit à une page blanche ou à l’erreur : « L’intégration du clientOAuth avec l’ID de client donné est introuvable. »
Cela se produit lorsque vous utilisez l’ancien style d’authentification fédérée (SSO) avec Snowflake au lieu de la nouvelle version d’intégration de sécurité comme expliqué dans Configuration de Snowflake pour l’utilisation de l’authentification fédérée. Procédez comme suit pour vérifier :
Exécutez la requête suivante :
SHOW PARAMETERS LIKE 'SAML_IDENTITY_PROVIDER' IN ACCOUNT;
Si ce paramètre est défini, c’est que vous avez utilisé l’ancienne méthode d’authentification fédérée.
Si le paramètre précédent a été défini, exécutez les requêtes suivantes pour voir si vous avez une intégration de sécurité SAML :
SHOW INTEGRATIONS ->> SELECT * FROM $1 WHERE "type" = 'SAML2';
Si vous n’avez pas d’intégration du type SAML2, vous utilisez l’ancienne méthode d’authentification fédérée.
Dans ce cas, la solution consiste à passer de l’ancienne authentification fédérée à la nouvelle authentification fédérée de type intégration. Pour plus d’informations, voir Migration d’une intégration de sécurité SAML2.
Configuration de la sortie réseau¶
Le code de votre application peut nécessiter un accès à Internet. Par défaut, les conteneurs d’application n’ont pas la permission d’accéder à Internet. Vous devez activer l’accès à Internet en utilisant les intégrations d’accès externe (EAIs).
En règle générale, vous souhaitez qu’un administrateur de compte crée des EAIs pour gérer l’accès externe autorisé à partir des services (y compris des services de tâche). Les administrateurs de comptes peuvent alors accorder l’utilisation d’EAI à des rôles spécifiques que les développeurs utilisent pour exécuter des services.
L’exemple suivant décrit les étapes de la création d’une EAI qui autorise le trafic de sortie vers des destinations spécifiques spécifiées à l’aide de règles de réseau. Vous vous référez ensuite à l’EAI lorsque vous créez un service pour autoriser des requêtes vers des destinations Internet spécifiques.
Exemple
Supposons que vous souhaitiez que le code de votre application envoie des requêtes aux destinations suivantes :
Requêtes HTTPS vers translation.googleapis.com
Requêtes HTTP et HTTPS vers google.com
Suivez ces étapes pour permettre à votre service d’accéder à ces domaines sur Internet :
Créez une intégration d’accès externe (EAI). Pour ce faire, il faut disposer des autorisations nécessaires. Par exemple, vous pouvez utiliser le rôle ACCOUNTADMIN pour créer une EAI. Il s’agit d’un processus en deux étapes :
Utilisez la commande CREATE NETWORK RULE pour créer une ou plusieurs règles de réseau de sortie répertoriant les destinations externes auxquelles vous souhaitez autoriser l’accès. Vous pouvez réaliser cet exemple avec une seule règle de réseau, mais pour l’illustration, nous créons deux règles de réseau :
Créez une règle de réseau nommée
translate_network_rule:CREATE OR REPLACE NETWORK RULE translate_network_rule MODE = EGRESS TYPE = HOST_PORT VALUE_LIST = ('translation.googleapis.com');
Cette règle autorise les connexions TCP vers la destination
translation.googleapis.com. Le domaine dans la propriété VALUE_LIST ne spécifie pas le numéro de port optionnel, de sorte que le port par défaut 443 (HTTPS) est supposé. Cela permet à votre application de se connecter à n’importe quelle URL qui commence parhttps://translation.googleapis.com/.Créez une règle de réseau nommée
google_network_rule:CREATE OR REPLACE NETWORK RULE google_network_rule MODE = EGRESS TYPE = HOST_PORT VALUE_LIST = ('google.com:80', 'google.com:443');
Cela permet à votre application de se connecter à n’importe quelle URL qui commence par
http://google.com/ouhttps://google.com/.
Note
Pour le paramètre
VALUE_LISTvous devez fournir un nom d’hôte complet. Les caractères génériques (par exemple,*.googleapis.com) ne sont pas pris en charge.Snowpark Container Services ne prend en charge que les règles de réseau qui autorisent les ports 22, 80, 443 et 1024+. Si une règle de réseau référencée autorise l’accès à d’autres ports, la création du service échouera. Contactez votre représentant de compte si vous avez besoin d’utiliser des ports supplémentaires.
Note
Pour permettre à votre service d’envoyer des requêtes HTTP ou HTTPS à n’importe quelle destination sur Internet, vous spécifiez « 0.0.0.0 » comme domaine dans la propriété VALUE_LIST. La règle de réseau suivante permet d’envoyer des requêtes « HTTP » et « HTTPS » n’importe où sur Internet. Seuls les ports 80 ou 443 sont pris en charge avec « 0.0.0.0 ».
CREATE NETWORK RULE allow_all_rule TYPE = 'HOST_PORT' MODE= 'EGRESS' VALUE_LIST = ('0.0.0.0:443','0.0.0.0:80');
Créez une intégration d’accès externe (EAI) qui spécifie que les deux règles de réseau de sortie précédentes sont autorisées :
CREATE EXTERNAL ACCESS INTEGRATION google_apis_access_integration ALLOWED_NETWORK_RULES = (translate_network_rule, google_network_rule) ENABLED = true;
L’administrateur du compte peut désormais accorder l’utilisation de l’intégration aux développeurs pour leur permettre d’exécuter un service qui peut accéder à des destinations spécifiques sur Internet.
GRANT USAGE ON INTEGRATION google_apis_access_integration TO ROLE test_role;
Créez le service en fournissant l’EAI comme indiqué dans les exemples suivants. Le rôle de propriétaire qui crée le service a besoin du privilège USAGE sur l’EAI et du privilège READ sur les secrets référencés. Notez que vous ne pouvez pas utiliser le rôle ACCOUNTADMIN pour créer un service.
Créez un service :
USE ROLE test_role; CREATE SERVICE eai_service IN COMPUTE POOL MYPOOL EXTERNAL_ACCESS_INTEGRATIONS = (GOOGLE_APIS_ACCESS_INTEGRATION) FROM SPECIFICATION $$ spec: containers: - name: main image: /db/data_schema/tutorial_repository/my_echo_service_image:tutorial env: TEST_FILE_STAGE: source_stage/test_file args: - read_secret.py endpoints: - name: read port: 8080 $$;
Cet exemple de requête CREATE SERVICE utilise une spécification de service en ligne et spécifie la propriété facultative EXTERNAL_ACCESS_INTEGRATIONS pour inclure l’EAI. L’EAI spécifie les règles de réseau qui autorisent le trafic de sortie du service vers les destinations spécifiques.
Exécuter un service de tâche :
EXECUTE JOB SERVICE IN COMPUTE POOL tt_cp NAME = example_job_service EXTERNAL_ACCESS_INTEGRATIONS = (GOOGLE_APIS_ACCESS_INTEGRATION) FROM SPECIFICATION $$ spec: container: - name: curl image: /tutorial_db/data_schema/tutorial_repo/alpine-curl:latest command: - "curl" - "http://google.com/" $$;
Cet exemple de commande EXECUTE JOBSERVICE spécifie la spécification en ligne et la propriété facultative EXTERNAL_ACCESS_INTEGRATIONS pour inclure l’EAI. Cela autorise le trafic de sortie de la tâche vers les destinations spécifiées dans les règles de réseau que l’EAI autorise.
Sortie réseau par connectivité privée¶
Au lieu de router la sortie réseau via l’internet public, vous pouvez choisir de diriger le trafic de sortie de votre service vers un point de terminaison de connectivité privée.
Vous devez d’abord créer le point de terminaison de connectivité privée dans votre compte Snowflake. Configurez ensuite une règle de réseau pour autoriser le trafic sortant à utiliser la connectivité privée. Le processus de paramétrage d’une intégration d’accès externe (EAI) reste le même que celui décrit dans la section précédente.
Note
La communication privée exige que Snowflake et le compte Cloud du client utilisent le même fournisseur de Cloud et la même région.
Par exemple, si vous souhaitez activer l’accès internet sortant de votre service à un compartiment Amazon S3 via une connectivité privée, vous procédez comme suit :
Activez la connectivité du lien privé pour le service de point de terminaison autonome (Amazon S3). Pour obtenir des instructions étape par étape, consultez AWS Private Link pour Amazon S3.
Appelez la fonction du système SYSTEM$PROVISION_PRIVATELINK_ENDPOINT pour provisionner un point de terminaison de connectivité privée dans votre VNet Snowflake. Cela permet à Snowflake de se connecter au service externe (dans cet exemple, Amazon S3) en utilisant une connectivité privée.
USE ROLE ACCOUNTADMIN; SELECT SYSTEM$PROVISION_PRIVATELINK_ENDPOINT( 'com.amazonaws.us-west-2.s3', '*.s3.us-west-2.amazonaws.com' );
Dans le compte du fournisseur Cloud, approuvez le point de terminaison. Dans cet exemple, pour Amazon AWS, voir Accepter ou rejeter les requêtes de connexion dans la documentation AWS. Par ailleurs, pour approuver le point de terminaison dans Azure, consultez la documentation Azure.
Utilisez la commande CREATE NETWORK RULE pour créer une règle de réseau de sortie spécifiant les destinations externes auxquelles vous souhaitez autoriser l’accès.
CREATE OR REPLACE NETWORK RULE private_link_network_rule MODE = EGRESS TYPE = PRIVATE_HOST_PORT VALUE_LIST = ('<bucket-name>.s3.us-west-2.amazonaws.com');
La valeur du paramètre TYPE est définie sur PRIVATE_HOST_PORT. Elle indique que la règle de réseau autorise le trafic réseau sortant à utiliser la connectivité privée.
Le reste des étapes pour créer un EAI et l’utiliser pour créer un service sont les mêmes que celles expliquées dans la section précédente (voir Configuration de la sortie réseau).
Pour plus d’informations sur l’utilisation des points de terminaison de connectivité privée, reportez-vous à ce qui suit :
Configuration des communications réseau entre les conteneurs¶
Il y a deux considérations à prendre en compte :
Communications entre les conteneurs d’une instance de service : si une instance de service utilise plusieurs conteneurs, ces conteneurs peuvent communiquer entre eux via localhost (il n’est pas nécessaire de définir des points de terminaison dans la spécification du service).
Communication entre les conteneurs appartenant à différents services ou plusieurs instances de services : les conteneurs appartenant à différents services (ou à différentes instances d’un même service) peuvent communiquer en utilisant des points de terminaison définis dans des fichiers de spécification. Pour plus d’informations, voir Communications de service à service.
Transmission d’identifiants de connexion à un conteneur à l’aide de secrets Snowflake¶
Il existe de nombreuses raisons pour lesquelles vous pourriez vouloir transmettre des identifiants de connexion gérés par Snowflake à votre conteneur. Par exemple, votre service peut communiquer avec des points de terminaison externes (en dehors de Snowflake), auquel cas vous devrez fournir des identifiants de connexion dans votre conteneur pour que votre code d’application puisse les utiliser.
Pour fournir des identifiants de connexion, stockez-les d’abord dans des objets de secrets Snowflake. Ensuite, dans la spécification du service, utilisez containers.secrets pour spécifier quels objets de secrets utiliser et où les placer à l’intérieur du conteneur. Vous pouvez soit transmettre ces identifiants de connexion à des variables d’environnement dans les conteneurs, soit les rendre disponibles dans des fichiers locaux dans les conteneurs.
Spécification des secrets Snowflake¶
Spécifiez un secret Snowflake par nom ou référence (la référence n’est applicable que dans le cas de Native Application) :
Transmettez le secret Snowflake par nom : vous pouvez transmettre un nom de secret comme valeur de champ
snowflakeSecret.... secrets: - snowflakeSecret: objectName: '<secret-name>' <other info about where in the container to copy the secret> ...
Notez que vous pouvez éventuellement spécifier
<secret-name>directement comme la valeursnowflakeSecret.Transmettez le secret Snowflake par référence : lorsque vous utilisez les services de conteneurs Snowpark pour créer une Native App (une application avec des conteneurs), le producteur et les consommateurs de l’application utilisent des comptes Snowflake différents. Dans certains contextes, une Native App Snowflake installée doit accéder à des objets de secrets existants dans le compte consommateur et qui existent en dehors de l’objet APPLICATION. Dans ce cas, les développeurs peuvent utiliser la syntaxe de spécification « secrets par référence » pour gérer les identifiants de connexion comme indiqué :
containers: - name: main image: <url> secrets: - snowflakeSecret: objectReference: '<reference-name>' <other info about where in the container to copy the secret>
Notez que la spécification utilise
objectReferenceau lieu deobjectNamepour fournir un nom de référence secret.
Spécification du placement des secrets à l’intérieur du conteneur¶
Vous pouvez demander à Snowflake de placer les secrets dans les conteneurs en tant que variables d’environnement ou de les écrire dans des fichiers de conteneur locaux.
Transmettre des secrets en tant que variables d’environnement¶
Pour transmettre des secrets Snowflake aux conteneurs en tant que variables d’environnement, incluez envVarName dans le champ containers.secrets.
containers:
- name: main
image: <url>
secrets:
- snowflakeSecret: <secret-name>
secretKeyRef: username | password | secret_string | 'access_token'
envVarName: '<env-variable-name>'
La valeur secretKeyRef dépend du type de secret Snowflake. Les valeurs possibles sont les suivantes :
usernameoupasswordsi le secret Snowflake est de typepassword.secret_stringsi le secret Snowflake est de typegeneric_string.
Notez que Snowflake ne met pas à jour les secrets transmis en tant que variables d’environnement après la création d’un service.
Exemple 1 : transmission de secrets de type password comme variables d’environnement¶
Dans cet exemple, vous créez l’objet secret Snowflake suivant de type password :
CREATE SECRET testdb.testschema.my_secret_object
TYPE = password
USERNAME = 'snowman'
PASSWORD = '1234abc';
Pour fournir cet objet secret Snowflake aux variables d’environnement (par exemple, LOGIN_USER et LOGIN_PASSWORD) dans votre conteneur, ajoutez le champ containers.secrets suivant dans le fichier de spécification :
containers:
- name: main
image: <url>
secrets:
- snowflakeSecret: testdb.testschema.my_secret_object
secretKeyRef: username
envVarName: LOGIN_USER
- snowflakeSecret: testdb.testschema.my_secret_object
secretKeyRef: password
envVarName: LOGIN_PASSWORD
Dans cet exemple, la valeur snowflakeSecret est un nom d’objet complet, car les secrets peuvent être stockés dans un schéma différent de celui du service en cours de création.
Dans cet exemple, le champ containers.secrets est une liste de deux objets snowflakeSecret :
Le premier objet fait correspondre
usernamede l’objet secret de Snowflake à la variable d’environnementLOGIN_USERde votre conteneur.Le second objet fait correspondre
passwordde l’objet secret de Snowflake à la variable d’environnementLOGIN_PASSWORDde votre conteneur.
Exemple 2 : passer des secrets de type generic_string comme variables d’environnement¶
Dans cet exemple, vous créez l’objet secret Snowflake suivant de type generic_string :
CREATE SECRET testdb.testschema.my_secret
TYPE=generic_string
SECRET_STRING='
some_magic: config
';
Pour fournir cet objet secret Snowflake aux variables d’environnement (par exemple, GENERIC_SECRET) dans votre conteneur, vous ajoutez le champ containers.secrets suivant dans le fichier de spécification :
containers:
- name: main
image: <url>
secrets:
- snowflakeSecret: testdb.testschema.my_secret
secretKeyRef: secret_string
envVarName: GENERIC_SECRET
Écrire des secrets dans des fichiers de conteneurs locaux¶
Pour rendre les secrets Snowflake disponibles pour votre conteneur d’application dans des fichiers de conteneur locaux, incluez un champ containers.secrets : pour rendre les secrets Snowflake disponibles pour votre conteneur d’application dans les fichiers de conteneur locaux, incluez directoryPath dans containers.secrets :
containers:
- name: <name>
image: <url>
...
secrets:
- snowflakeSecret: <snowflake-secret-name>
directoryPath: '<local directory path in the container>'
Snowflake renseigne les fichiers nécessaires pour le secret dans ce directoryPath spécifié ; la spécification de secretKeyRef n’est pas nécessaire. Selon le type de secret, Snowflake crée les fichiers suivants dans le conteneur sous le chemin de répertoire que vous avez fourni :
usernameetpasswordsi le secret Snowflake est de typepassword.secret_stringsi le secret Snowflake est de typegeneric_string.access_tokensi le secret Snowflake est de typeoauth2.
Note
Après la création d’un service, si l’objet secret de Snowflake est mis à jour, Snowflake mettra à jour les fichiers secrets correspondants dans les conteneurs en cours d’exécution.
Exemple 1 : transmission de secrets de type password dans des fichiers de conteneurs locaux¶
Dans cet exemple, vous créez l’objet secret Snowflake suivant de type password :
CREATE SECRET testdb.testschema.my_secret_object
TYPE = password
USERNAME = 'snowman'
PASSWORD = '1234abc';
Pour que ces identifiants de connexion soient disponibles dans les fichiers conteneurs locaux, ajoutez le champ containers.secrets suivant dans le fichier de spécification :
containers:
- name: main
image: <url>
secrets:
- snowflakeSecret: testdb.testschema.my_secret_object
directoryPath: '/usr/local/creds'
Lorsque vous démarrez votre service, Snowflake crée deux fichiers à l’intérieur du conteneur : /usr/local/creds/username et /usr/local/creds/password. Votre code d’application peut alors lire ces fichiers.
Exemple 2 : transmission de secrets de type generic_string dans des fichiers de conteneurs locaux¶
Dans cet exemple, vous créez l’objet secret Snowflake suivant de type generic_string :
CREATE SECRET testdb.testschema.my_secret
TYPE=generic_string
SECRET_STRING='
some_magic: config
';
Pour fournir cet objet de secret Snowflake dans les fichiers de conteneurs locaux, vous ajoutez le champ containers.secrets suivant dans le fichier de spécifications :
containers:
- name: main
image: <url>
secrets:
- snowflakeSecret: testdb.testschema.my_secret
directoryPath: '/usr/local/creds'
Lorsque vous démarrez votre service, Snowflake crée ce fichier à l’intérieur des conteneurs : /usr/local/creds/secret_string.
Exemple 3 : transmission de secrets de type oauth2 dans des fichiers de conteneurs locaux¶
Dans cet exemple, vous créez l’objet secret Snowflake suivant de type oauth2 :
CREATE SECRET testdb.testschema.oauth_secret
TYPE = OAUTH2
OAUTH_REFRESH_TOKEN = '34n;vods4nQsdg09wee4qnfvadH'
OAUTH_REFRESH_TOKEN_EXPIRY_TIME = '2023-12-31 20:00:00'
API_AUTHENTICATION = my_integration;
Pour que ces identifiants de connexion soient disponibles dans les fichiers conteneurs locaux, ajoutez le champ containers.secrets suivant dans le fichier de spécification :
containers:
- name: main
image: <url>
secrets:
- snowflakeSecret: testdb.testschema.oauth_secret
directoryPath: '/usr/local/creds'
Snowflake récupère le jeton d’accès de l’objet secret OAuth et crée /usr/local/creds/access_token dans les conteneurs.
Lorsqu’un service utilise des secrets de type oauth2, le service est censé utiliser ce secret pour accéder à une destination Internet. Un secret oauth doit être autorisé par l’intégration d’accès externe (EAI) ; sinon CREATE SERVICE ou EXECUTE JOB SERVICE échoueront. Cette exigence d’EAI supplémentaire ne s’applique qu’aux secrets de type oauth2 et non aux autres types de secrets.
En résumé, les étapes typiques de la création d’un tel service sont les suivantes :
Créez un secret de type oauth2 (voir plus haut).
Créez un EAI pour permettre l’utilisation du secret par un service. Par exemple :
CREATE OR REPLACE EXTERNAL ACCESS INTEGRATION example_eai ALLOWED_NETWORK_RULES = (<name>) ALLOWED_AUTHENTICATION_SECRETS = (testdb.testschema.oauth_secret) ENABLED = true;
Créez un service qui inclut un champ
containers.secretsdans la spécification. Il spécifie également la propriété facultative EXTERNAL_ACCESS_INTEGRATIONS pour inclure un EAI afin de permettre l’utilisation du secret oauth2.Exemple de commande CREATE SERVICE (avec spécification en ligne) :
CREATE SERVICE eai_service IN COMPUTE POOL MYPOOL EXTERNAL_ACCESS_INTEGRATIONS = (example_eai) FROM SPECIFICATION $$ spec: containers: - name: main image: <url> secrets: - snowflakeSecret: testdb.testschema.oauth_secret directoryPath: '/usr/local/creds' endpoints: - name: api port: 8080 $$;
Pour plus d’informations sur la sortie, voir Configuration de la sortie réseau.
Lignes directrices et limitations¶
Limitations générales : si vous rencontrez des problèmes avec ces limitations, contactez votre représentant de compte.
Vous pouvez créer jusqu’à 200 services dans votre compte Snowflake.
Chaque service peut avoir jusqu’à 100 points de terminaison (voir spec.endpoints).
Chaque service peut avoir jusqu’à 20 conteneurs (voir spec.containers).
Chaque service peut avoir jusqu’à 50 secrets (voir containers.secrets).
Chaque service peut avoir jusqu’à 20 volumes (voir spec.volumes).
Les limitations suivantes s’appliquent lorsque vous activez l’accès Internet (voir Configuration de la sortie réseau) en utilisant des intégrations d’accès externes (EAIs).
Chaque service peut prendre en charge jusqu’à 10 EAIs (voir CREATE SERVICE et ALTER SERVICE).
Chaque EAI peut avoir jusqu’à 100 noms d’hôtes.
Lorsque vous accédez au point de terminaison public depuis Internet, vous pouvez constater que l’authentification par nom d’utilisateur/mot de passe fonctionne, mais que SSO aboutit à une page blanche ou à l’erreur : « L’intégration du clientOAuth avec l’ID du client donné est introuvable ». Pour obtenir des informations sur la manière de résoudre ce problème, consultez Considérations sur les entrées et SSO.
Exigences relatives à la plateforme d’images : Actuellement, Snowpark Container Services requiert des images de plateforme linux/amd64.
Les conteneurs de services ne sont pas privilégiés : les conteneurs de services ne s’exécutent pas avec des privilèges et ne peuvent donc pas modifier la configuration du matériel sur l’hôte et ne peuvent modifier que des configurations OS limitées. Les conteneurs de services ne peuvent effectuer que les configurations du système d’exploitation qu’un utilisateur normal (c’est-à-dire un utilisateur qui n’a pas besoin d’être rooté) peut effectuer.
Renommage de la base de données et du schéma :
Ne renommez pas les bases de données et les schémas pour lesquels vous avez déjà créé un service. Renommer revient à déplacer un service vers une autre base de données et un autre schéma, ce qui n’est pas pris en charge. Par exemple :
Les informations sur les bases de données et les schémas fournies par Snowflake aux conteneurs de service en cours d’exécution continueront à se référer aux anciens noms.
Les nouveaux journaux que les services intègrent dans la table d’événements continueront à faire référence aux anciens noms de base de données et de schéma.
La fonction de service continuera à référencer le service dans l’ancienne base de données et l’ancien schéma, et lorsque vous appellerez la fonction de service, elle échouera.
Une spécification de service peut faire référence à des objets tels que des zones de préparation de Snowflake et les référentiels d’images. Si vous renommez les noms des bases de données ou des schémas dans lesquels résident ces objets, vous devez mettre à jour manuellement les noms des bases de données et des schémas des objets référencés dans la spécification de service.
Ownership transferof the parent schema or database:
You can transfer the ownership of the parent schema or database to a different role. But the ownership of services inside the schema or database isn’t transferred to the new role because services run as service’s owner roles and that doesn’t change. As a result, the services could lose permissions on objects inside the schema, such as image repositories and Snowflake stages in the same schema.
If you must transfer the ownership of the parent schema or database, consider re-creating the services.
Suppression et annulation de la suppression d’une base de données et d’un schéma :
Lorsque vous supprimez la base de données ou le schéma parent, les services sont supprimés de manière asynchrone. Cela signifie qu’un service peut continuer à fonctionner pendant un certain temps avant que des processus internes ne le suppriment.
Si vous tentez d’annuler la suppression d’une base de données ou d’un schéma précédemment supprimé, il n’y a aucune garantie que les services seront restaurés.
Ownership transfer of services: Ownership transfer or future ownership transfer for services, including job services, isn’t supported.
Transfert de propriété des fonctions de service :
La propriété d’une fonction de service peut être transférée à un rôle différent. Si le nouveau rôle de propriétaire n’a pas le privilège USAGE sur le service, les invocations de fonctions échoueront. Vous devez accorder le privilège USAGE au nouveau rôle de propriétaire de la fonction.
Réplication : en ce qui concerne la réplication dans Snowflake, notez ce qui suit :
Les objets Snowpark Container Services, comme les services, les pools de calcul et les référentiels, ne peuvent pas être répliqués.
Si vous créez un référentiel au sein d’une base de données, la base de données entière ne peut pas être répliquée. Si la base de données contient d’autres ressources, comme des services, ou des pools de calcul, le processus de réplication de base de données aboutira, mais ces objets individuels au sein de la base de données ne seront pas répliqués.
Job services timeout: Snowpark Container Services job services runs synchronously by default. If a statement times out, the job service is canceled. The default statement timeout is two days. Customers can change the timeout by setting the parameter STATEMENT_TIMEOUT_IN_SECONDS using ALTER SESSION.
ALTER SESSION SET statement_timeout_in_seconds=<time>
Il doit être défini avant l’exécution de la commande EXECUTE JOB SERVICE. Vous pouvez exécuter des services associés aux tâches de manière asynchrone, en spécifiant
ASYNC=true, pour éviter que ceux-ci ne soient interrompus par un délai d’exécution d’instructions.File staging commands support in Google Cloud: To use the PUT, GET, LIST, or REMOVE command with Snowflake client libraries on Google Cloud, update your clients to at least the following versions.
Client
Version
Pilote Go Snowflake
1.14.1
Connecteur Snowflake pour Python
3.16.0
Pilote .NET
4.6.0
Pilote Node.js
2.1.3
Pilote JDBC
3.25.1
Pilote ODBC
3.10.0