Auf andere Oracle Cloud Infrastructure-Ressourcen aus aktiven Funktionen zugreifen
Erfahren Sie, wie Sie aus aktiven Funktionen, die in OCI Functions bereitgestellt sind, auf andere Oracle Cloud Infrastructure-Ressourcen zugreifen.
Wenn eine Funktion, die Sie in OCI Functions bereitgestellt haben, ausgeführt wird, kann sie auf andere Oracle Cloud Infrastructure-Ressourcen zugreifen. Beispiel:
- Möglicherweise soll eine Funktion eine Liste der VCNs aus dem Networking-Service abrufen.
- Möglicherweise soll eine Funktion Daten aus einem Object Storage-Bucket lesen, einen Vorgang mit den Daten ausführen und die geänderten Daten in den Object Storage-Bucket zurückschreiben.
Um einer Funktion den Zugriff auf eine andere Oracle Cloud Infrastructure-Ressource zu ermöglichen, müssen Sie die Funktion in eine dynamische Gruppe aufnehmen und dann eine Policy erstellen, um der dynamischen Gruppe Zugriff auf diese Ressource zu erteilen. Weitere Informationen zu dynamischen Gruppen, einschließlich der Berechtigungen, die für das Erstellen der Gruppen erforderlich sind, finden Sie unter Dynamische Gruppen verwalten.
Nachdem Sie die Policy und die dynamische Gruppe eingerichtet haben, können Sie einen Aufruf an einen "Resource Principal-Provider" in den Funktionscode aufnehmen. Der Resource Principal-Provider verwendet ein Resource Provider Session-Token (RPST), mit dem sich die Funktion gegenüber anderen Oracle Cloud Infrastructure-Services authentifizieren kann. Das Token ist nur für die Ressourcen gültig, auf die die dynamische Gruppe Zugriff hat.
Beachten Sie auch, dass das Token 15 Minuten gecacht wird. Wenn Sie also die Policy oder die dynamische Gruppe ändern, müssen Sie 15 Minuten warten, um das Ergebnis Ihrer Änderungen zu sehen.
Wir empfehlen, den im Oracle Cloud Infrastructure-SDK enthaltenen Resource Principal-Provider zu verwenden. Möglicherweise schreiben Sie jedoch eine Funktion in einer Sprache, die nicht vom Oracle Cloud Infrastructure-SDK unterstützt wird. Möglicherweise möchten Sie das Oracle Cloud Infrastructure-SDK gar nicht verwenden. In beiden Fällen können Sie Ihren eigenen benutzerdefinierten Resource Principal-Provider schreiben, um einer Funktion die Authentifizierung gegenüber anderen Oracle Cloud Infrastructure-Services anhand von Dateien und Umgebungsvariablen in dem Container zu ermöglichen, in dem die Funktion ausgeführt wird.
Konsole verwenden
So ermöglichen Sie einer aktiven Funktion, auf andere Oracle Cloud Infrastructure-Ressourcen zuzugreifen:
-
Melden Sie sich in der Konsole an, und erstellen Sie eine neue dynamische Gruppe:
- Öffnen Sie das Navigationsmenü , und wählen Sie Identität und Sicherheit aus. Wählen Sie unter Identität die Option Domains aus. Wählen Sie unter Identitätsdomain die Option Dynamische Gruppen aus.
- Befolgen Sie die Anweisungen unter So erstellen Sie eine dynamische Gruppe, und geben Sie der dynamischen Gruppe einen Namen (z.B.
acme-func-dyn-grp
). -
Beachten Sie bei der Angabe einer Regel für die dynamische Gruppe die folgenden Beispiele:
-
Wenn alle Funktionen in einem Compartment auf eine Ressource zugreifen können sollen, geben Sie eine Regel wie im folgenden Beispiel ein, die alle Funktionen im Compartment mit der angegebenen Compartment-OCID zur dynamischen Gruppe hinzufügt:
ALL {resource.type = 'fnfunc', resource.compartment.id = 'ocid1.compartment.oc1..aaaaaaaa23______smwa'}
-
Wenn eine bestimmte Funktion auf eine Ressource zugreifen können soll, geben Sie eine Regel wie im folgenden Beispiel ein, die die Funktion mit der angegebenen OCID zur dynamischen Gruppe hinzufügt:
resource.id = 'ocid1.fnfunc.oc1.iad.aaaaaaaaacq______dnya'
-
Wenn alle Funktionen mit einem bestimmten definierten Tag auf eine Ressource zugreifen können sollen, geben Sie eine Regel wie im folgenden Beispiel ein, die alle Funktionen mit dem definierten Tag zur dynamischen Gruppe hinzufügt:
ALL {resource.type = 'fnfunc', tag.department.operations.value = '45'}
Beachten Sie, dass Freiformtags nicht unterstützt werden. Weitere Informationen zum Tagging finden Sie unter Ressourcentags.
-
- Wählen Sie Dynamische Gruppe erstellen aus.
Nachdem Sie eine dynamische Gruppe erstellt haben, die die Funktion enthält, können Sie jetzt eine Policy erstellen, um der dynamischen Gruppe Zugriff auf die erforderliche Oracle Cloud Infrastructure-Ressource zu erteilen.
-
Erstellen Sie eine neue Policy:
- Öffnen Sie das Navigationsmenü , und wählen Sie Identität und Sicherheit aus. Wählen Sie unter Identität die Option Policys aus.
- Befolgen Sie die Anweisungen unter So erstellen Sie eine Policy, und geben Sie der Policy einen Namen (z.B.
acme-func-dyn-grp-policy
). -
Beachten Sie bei der Angabe einer Policy-Anweisung die folgenden Beispiele:
-
Wenn Funktionen in der Gruppe
acme-func-dyn-grp
eine Liste aller VCNs im Mandanten abrufen können sollen, geben Sie eine Regel wie im folgenden Beispiel ein:allow dynamic-group acme-func-dyn-grp to inspect vcns in tenancy
-
Wenn Funktionen in der Gruppe
acme-func-dyn-grp
in einem bestimmten Object Storage-Bucket lesen und schreiben können sollen, geben Sie eine Regel wie im folgenden Beispiel ein:allow dynamic-group acme-func-dyn-grp to manage objects in compartment acme-storage-compartment where all {target.bucket.name='acme-functions-bucket'}
-
Wenn Funktionen in der Gruppe
acme-func-dyn-grp
alle Ressourcen in einem Compartment lesen und schreiben können sollen, geben Sie eine Regel wie im folgenden Beispiel ein:allow dynamic-group acme-func-dyn-grp to manage all-resources in compartment acme-storage-compartment
-
- Wählen Sie Erstellen aus, um die neue Policy zu erstellen.
- Nehmen Sie einen Resource Principal-Provider in den Funktionscode auf, um der Funktion die Authentifizierung bei anderen Oracle Cloud Infrastructure-Services zu ermöglichen. Siehe:
- Beispiel: Resource Principal-Provider von Oracle zu einer Python-Funktion hinzufügen, um eine Liste der VCNs aus dem Networking-Service abzurufen
- Beispiel: Benutzerdefinierten Resource Principal-Provider zu einer Funktion hinzufügen
Eine Java-Beispielfunktion finden Sie unter Funktion, die die Liste der Instanzen im aufrufenden Compartment zurückgibt im OCI Functions-Beispiel-Repository auf GitHub.
Beispiel: Resource Principal-Provider von Oracle zu einer Python-Funktion hinzufügen, um eine Liste der VCNs aus dem Networking-Service abzurufen
Wenn Sie eine Funktion zu einer dynamischen Gruppe hinzugefügt und eine Policy erstellt haben, mit der die dynamische Gruppe die VCNs im Mandanten auflisten kann, könnten Sie einen Code wie im folgenden Beispiel aufnehmen, um eine Liste der VCNs aus dem Networking-Service abzurufen. In diesem Beispiel wird der Resource Principal-Provider von Oracle verwendet, um Zugangsdaten aus dem RPST-Token zu extrahieren.
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, }
Beispiel: Benutzerdefinierten Resource Principal-Provider zu einer Funktion hinzufügen
Wir empfehlen, den im Oracle Cloud Infrastructure-SDK enthaltenen Resource Principal-Provider zu verwenden. Möglicherweise schreiben Sie jedoch eine Funktion in einer Sprache, die nicht vom Oracle Cloud Infrastructure-SDK unterstützt wird. Möglicherweise möchten Sie das Oracle Cloud Infrastructure-SDK gar nicht verwenden. In beiden Fällen können Sie Ihren eigenen benutzerdefinierten Resource Principal-Provider schreiben, um einer Funktion die Authentifizierung gegenüber anderen Oracle Cloud Infrastructure-Services anhand von Dateien und Umgebungsvariablen in dem Container zu ermöglichen, in dem die Funktion ausgeführt wird.
Der Container, in dem eine Funktion ausgeführt wird, enthält einen Verzeichnisbaum, der mit Oracle Cloud Infrastructure kompatible Zugangsdaten enthält, insbesondere:
- ein Resource Principal Session Token (RPST) in einer Datei mit dem Namen rpst. Das RPST-Token wird als JWT-Token formatiert und enthält Claims, die den Hostmandanten und das Compartment der Funktion identifizieren.
- einen Private Key, der beim Erstellen von Anforderungen an Oracle Cloud Infrastructure-Services für die Funktion in einer Datei mit dem Namen private.pem verwendet wird.
Die folgenden Umgebungsvariablen werden innerhalb des Containers, in dem die Funktion ausgeführt wird, festgelegt:
- OCI_RESOURCE_PRINCIPAL_VERSION mit dem Wert
2.2
- OCI_RESOURCE_PRINCIPAL_RPST mit dem absoluten Pfad zur Datei rpst (einschließlich Dateiname)
- OCI_RESOURCE_PRINCIPAL_PRIVATE_PEM mit dem absoluten Pfad zur Datei private.pem (einschließlich Dateiname)
- OCI_RESOURCE_PRINCIPAL_REGION mit der Regions-ID, in der die Funktion bereitgestellt wird (Beispiel:
us-phoenix-1
)
Um einer Funktion den Zugriff auf einen anderen Oracle Cloud Infrastructure-Service zu ermöglichen, fügen Sie der Funktion Code hinzu, damit sich diese gegenüber der anderen Ressource authentifizieren kann:
- Fügen Sie Code hinzu, der das RPST-Token aus dem Pfad in der Umgebungsvariablen OCI_RESOURCE_PRINCIPAL_RPST lädt.
-
Fügen Sie Code hinzu, der den Private Key aus dem Pfad in der Umgebungsvariablen OCI_RESOURCE_PRINCIPAL_PRIVATE_PEM lädt.
-
Fügen Sie Code hinzu, der das RPST-Token und den Private Key verwendet, um eine Oracle Cloud Infrastructure-Anforderungssignatur zu erstellen (siehe Anforderungssignaturen).
-
Fügen Sie Code hinzu, der die Anforderung für die andere Oracle Cloud Infrastructure-Ressource erstellt.
Gegebenenfalls können Sie Folgendes angeben:
- Die Endpunkte anderer Oracle Cloud Infrastructure-Services in derselben (lokalen) Region wie die Funktion, und zwar mit der Regions-ID in der Umgebungsvariablen OCI_RESOURCE_PRINCIPAL_REGION.
- Den Hostmandanten und das Compartment der Funktion mit den Claims
res_tenant
undres_compartment
im RPST-Token.
Beispiel: Die unten als Beispiel genannte Python-Funktion enthält einen benutzerdefinierten Resource Principal-Provider, der Zugangsdaten aus dem RPST-Token extrahiert. Anschließend sendet sie eine GET-Anforderung an den getTenancy-Vorgang der IAM-API, um die OCID des Mandanten der Funktion zurückzugeben.
#!/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())