Acceso a otros recursos de Oracle Cloud Infrastructure desde funciones en ejecución
Descubra cómo acceder a otros recursos de Oracle Cloud Infrastructure desde funciones en ejecución desplegadas en OCI Functions.
Cuando una función desplegada en OCI Functions se está ejecutando, puede acceder a otros recursos de Oracle Cloud Infrastructure. Por ejemplo:
- Es posible que desee que una función obtenga una lista de VCN del servicio Networking.
- Es posible que desee que una función lea datos de un cubo de Object Storage, realice alguna operación en los datos y, a continuación, vuelva a escribir los datos modificados en el cubo de Object Storage.
Para permitir que una función acceda a otro recurso de Oracle Cloud Infrastructure, debe incluir la función en un grupo dinámico y, a continuación, crear una política para otorgar acceso al grupo dinámico a dicho recurso. Para obtener más información sobre los grupos dinámicos, incluidos los permisos necesarios para crearlos, consulte Gestión de grupos dinámicos.
Una vez configurada la política y el grupo dinámico, puede incluir una llamada a un "proveedor de entidad de recurso" en el código de la función. El proveedor de entidad de recurso utiliza un token de sesión de proveedor de recursos (RPST) que permite que la función se autentique con otros servicios de Oracle Cloud Infrastructure. El token solo es válido para los recursos a los que se ha otorgado acceso al grupo dinámico.
Tenga en cuenta también que el token se almacena en caché durante 15 minutos. Por lo tanto, si cambia la política o el grupo dinámico, tendrá que esperar 15 minutos para ver el efecto de los cambios.
Recomendamos utilizar el proveedor de entidad de recurso incluido en el SDK de Oracle Cloud Infrastructure. Sin embargo, es posible que esté escribiendo una función en un lenguaje que el SDK de Oracle Cloud Infrastructure no admita. O puede que simplemente no desee utilizar el SDK de Oracle Cloud Infrastructure. En cualquier caso, puede escribir su propio proveedor de entidad de recurso personalizado para permitir que una función se autentique con otros servicios de Oracle Cloud Infrastructure, utilizando archivos y variables de entorno en el contenedor en el que se ejecuta la función.
Uso de la consola
Para que una función en ejecución acceda a otros recursos de Oracle Cloud Infrastructure:
-
Conéctese a la consola y cree un nuevo grupo dinámico:
- Abra el menú de navegación y seleccione Identidad y seguridad. En Identidad, seleccione Dominios. En Dominio de identidad, seleccione Grupos dinámicos.
- Siga las instrucciones de Para crear un grupo dinámico y asígnele un nombre al grupo dinámico (por ejemplo,
acme-func-dyn-grp
). -
Al especificar una regla para el grupo dinámico, tenga en cuenta los siguientes ejemplos:
-
Si desea que todas las funciones de un compartimento puedan acceder a un recurso, introduzca una regla similar a la siguiente que agregue todas las funciones del compartimento con el OCID del compartimento especificado al grupo dinámico:
ALL {resource.type = 'fnfunc', resource.compartment.id = 'ocid1.compartment.oc1..aaaaaaaa23______smwa'}
-
Si desea que una función específica pueda acceder a un recurso, introduzca una regla similar a la siguiente que agregue la función con el OCID especificado al grupo dinámico:
resource.id = 'ocid1.fnfunc.oc1.iad.aaaaaaaaacq______dnya'
-
Si desea que todas las funciones con una etiqueta específica definida puedan acceder a un recurso, introduzca una regla similar a la siguiente que agregue todas las funciones con la etiqueta definida al grupo dinámico:
ALL {resource.type = 'fnfunc', tag.department.operations.value = '45'}
Tenga en cuenta que no se admiten etiquetas de formato libre. Para obtener más información sobre el etiquetado, consulte Etiquetas de recursos.
-
- Seleccione Create Dynamic Group.
Al haber creado un grupo dinámico que incluye la función, ahora puede crear una política para otorgar al grupo dinámico acceso al recurso necesario de Oracle Cloud Infrastructure.
-
Cree una nueva política:
- Abra el menú de navegación y seleccione Identidad y seguridad. En Identidad, seleccione Políticas.
- Siga las instrucciones de Para crear una política y asigne un nombre a la política (por ejemplo,
acme-func-dyn-grp-policy
). -
Al especificar una sentencia de política, tenga en cuenta los siguientes ejemplos:
-
Si desea que las funciones de
acme-func-dyn-grp
puedan obtener una lista de todas las VCN del arrendamiento, introduzca una regla similar a la siguiente:allow dynamic-group acme-func-dyn-grp to inspect vcns in tenancy
-
Si desea que las funciones de
acme-func-dyn-grp
puedan leer y escribir en un cubo concreto de Object Storage, introduzca una regla similar a la siguiente:allow dynamic-group acme-func-dyn-grp to manage objects in compartment acme-storage-compartment where all {target.bucket.name='acme-functions-bucket'}
-
Si desea que las funciones de
acme-func-dyn-grp
puedan leer y escribir en todos los recursos de un compartimento, introduzca una regla similar a la siguiente:allow dynamic-group acme-func-dyn-grp to manage all-resources in compartment acme-storage-compartment
-
- Seleccione Crear para crear la nueva política.
- Incluya un proveedor de entidad de recurso en el código de la función para permitir que la función se autentique con otros servicios de Oracle Cloud Infrastructure. Consulte lo siguiente:
- Ejemplo: agregar el proveedor de entidad de recurso de Oracle a una función de Python para obtener una lista de VCN del servicio Networking
- Ejemplo: agregar un proveedor de entidad de recurso personalizado a una función
Para obtener una función Java de ejemplo, consulte Función que devuelve la lista de instancias en el compartimento de llamada en el repositorio de ejemplos de OCI Functions en GitHub.
Ejemplo: agregar el proveedor de entidad de recurso de Oracle a una función de Python para obtener una lista de VCN del servicio Networking
Tras agregar una función a un grupo dinámico y crear una política que permite al grupo dinámico mostrar las VCN del arrendamiento, puede incluir código similar al siguiente ejemplo para obtener una lista de VCN del servicio Networking. En este ejemplo se utiliza el proveedor de entidad de recurso de Oracle para extraer credenciales del token RPST.
import io
import json
from fdk import response
import oci
def handler(ctx, data: io.BytesIO=None):
signer = oci.auth.signers.get_resource_principals_signer()
resp = do(signer)
return response.Response(ctx,
response_data=json.dumps(resp),
headers={"Content-Type": "application/json"} )
def do(signer):
# List VCNs --------------------------------------------------------
client = oci.core.VirtualNetworkClient({}, signer=signer)
try:
vcns = client.list_vcns(signer.compartment_id)
vcns = [[v.id, v.display_name] for v in vcns.data]
except Exception as e:
vcns = str(e)
return {"vcns": vcns, }
Ejemplo: agregar un proveedor de entidad de recurso personalizado a una función
Recomendamos utilizar el proveedor de entidad de recurso incluido en el SDK de Oracle Cloud Infrastructure. Sin embargo, es posible que esté escribiendo una función en un lenguaje que el SDK de Oracle Cloud Infrastructure no admita. O puede que simplemente no desee utilizar el SDK de Oracle Cloud Infrastructure. En cualquier caso, puede escribir su propio proveedor de entidad de recurso personalizado para permitir que una función se autentique con otros servicios de Oracle Cloud Infrastructure, utilizando archivos y variables de entorno en el contenedor en el que se ejecuta la función.
El contenedor en el que se ejecuta una función incluye un árbol de directorios que contiene credenciales compatibles con Oracle Cloud Infrastructure, concretamente:
- Un token de sesión de entidad de recurso (RPST) en un archivo denominado rpst. El token RPST tiene el formato de token de JWT e incluye reclamaciones que identifican el arrendamiento de host y el compartimento de la función.
- Una clave privada que se utiliza para realizar solicitudes a los servicios de Oracle Cloud Infrastructure en nombre de la función, en un archivo denominado private.pem.
Las siguientes variables de entorno se definen dentro del contenedor en el que se ejecuta la función:
- OCI_RESOURCE_PRINCIPAL_VERSION, que contiene el valor
2.2
. - OCI_RESOURCE_PRINCIPAL_RPST, que contiene la ruta de acceso absoluta al archivo rpst (incluido el nombre de archivo).
- OCI_RESOURCE_PRINCIPAL_PRIVATE_PEM, que contiene la ruta absoluta al archivo private.pem (incluido el nombre de archivo).
- OCI_RESOURCE_PRINCIPAL_REGION, que contiene el identificador de región en el que se despliega la función (por ejemplo,
us-phoenix-1
).
Para permitir que una función acceda a otro servicio de Oracle Cloud Infrastructure, agregue código a la función para que pueda autenticarse con el otro recurso:
- Agregue código que cargue el token RPST de la ruta en la variable de entorno OCI_RESOURCE_PRINCIPAL_RPST.
-
Agregue código que cargue la clave privada de la ruta en la variable de entorno OCI_RESOURCE_PRINCIPAL_PRIVATE_PEM.
-
Agregue código que utilice el token RPST y la clave privada para crear una firma de solicitud de Oracle Cloud Infrastructure (consulte Firmas de solicitudes).
-
Agregue código que cree la solicitud para el otro recurso de Oracle Cloud Infrastructure.
Si es necesario, puede identificar:
- Los puntos finales de otros servicios de Oracle Cloud Infrastructure en la misma región (local) que la función mediante el identificador de región en la variable de entorno OCI_RESOURCE_PRINCIPAL_REGION.
- El compartimento y arrendamiento de host de la función con las reclamaciones
res_tenant
yres_compartment
en el token RPST.
Por ejemplo, la función Python de ejemplo siguiente incluye un proveedor de entidad de recurso personalizado que extrae credenciales del token RPST. A continuación, envía una solicitud GET a la operación getTenancy de la API de IAM para devolver el OCID del arrendamiento de la función.
#!/usr/bin/env python3
import base64
import email.utils
import hashlib
import httpsig_cffi.sign
import json
import logging
import os.path
import re
import requests.auth
import urllib.parse
LOG = logging.getLogger(__name__)
# The following class is derived from the Python section in https://docs.cloud.oracle.com/iaas/Content/API/Concepts/signingrequests.htm
class SignedRequestAuth(requests.auth.AuthBase):
"""A requests auth instance that can be reused across requests"""
generic_headers = [
"date",
"(request-target)",
"host"
]
body_headers = [
"content-length",
"content-type",
"x-content-sha256",
]
required_headers = {
"get": generic_headers,
"head": generic_headers,
"delete": generic_headers,
"put": generic_headers + body_headers,
"post": generic_headers + body_headers,
}
def __init__(self, key_id, private_key):
# Build a httpsig_cffi.requests_auth.HTTPSignatureAuth for each
# HTTP method's required headers
self.signers = {}
for method, headers in self.required_headers.items():
signer = httpsig_cffi.sign.HeaderSigner(
key_id=key_id, secret=private_key,
algorithm="rsa-sha256", headers=headers[:])
use_host = "host" in headers
self.signers[method] = (signer, use_host)
def inject_missing_headers(self, request, sign_body):
# Inject date, content-type, and host if missing
request.headers.setdefault(
"date", email.utils.formatdate(usegmt=True))
request.headers.setdefault("content-type", "application/json")
request.headers.setdefault(
"host", urllib.parse.urlparse(request.url).netloc)
# Requests with a body need to send content-type,
# content-length, and x-content-sha256
if sign_body:
body = request.body or ""
if "x-content-sha256" not in request.headers:
m = hashlib.sha256(body.encode("utf-8"))
base64digest = base64.b64encode(m.digest())
base64string = base64digest.decode("utf-8")
request.headers["x-content-sha256"] = base64string
request.headers.setdefault("content-length", len(body))
def __call__(self, request):
verb = request.method.lower()
# nothing to sign for options
if verb == "options":
return request
signer, use_host = self.signers.get(verb, (None, None))
if signer is None:
raise ValueError(
"Don't know how to sign request verb {}".format(verb))
# Inject body headers for put/post requests, date for all requests
sign_body = verb in ["put", "post"]
self.inject_missing_headers(request, sign_body=sign_body)
if use_host:
host = urllib.parse.urlparse(request.url).netloc
else:
host = None
signed_headers = signer.sign(
request.headers, host=host,
method=request.method, path=request.path_url)
request.headers.update(signed_headers)
return request
def rp_auther():
if os.environ['OCI_RESOURCE_PRINCIPAL_VERSION'] != "2.2":
raise EnvironmentError('{} must be set to the value "2.2"'.format('OCI_RESOURCE_PRINCIPAL_VERSION'))
rpst = os.environ['OCI_RESOURCE_PRINCIPAL_RPST']
if os.path.isabs(rpst):
with open(rpst) as f:
rpst = f.read()
private_key = os.environ['OCI_RESOURCE_PRINCIPAL_PRIVATE_PEM']
if os.path.isabs(private_key):
with open(private_key) as f:
private_key = f.read()
return get_claims(rpst), SignedRequestAuth('ST${}'.format(rpst), private_key)
def get_claims(rpst):
"""Parse an RPST as a JWT; return a dictionary of claims
The claims that are important are: sub, res_compartment, and res_tenant.
These carry the resource OCID together with its location.
"""
s = rpst.split('.')[1]
s += "=" * ((4 - len(s) % 4) % 4) # Pad to a multiple of 4 characters
return json.loads(base64.b64decode(s).decode('utf-8'))
# Use RP credentials to make a request
region = os.environ['OCI_RESOURCE_PRINCIPAL_REGION']
claims, rp_auth = rp_auther()
response = requests.get("https://identity.{}.oraclecloud.com/20160918/tenancies/{}".format(region, claims['res_tenant']), auth=rp_auth)
print(response.json())