Skip to main content

Request-evaluering med OPA

Request-evaluering med OPA er når èn OPA-regel evaluerer innkommende requests før de når applikasjonen din. Dette kan brukes til å utføre autorisasjonssjekker, berike requesten med headere, eller avvise requesten før den når applikasjonen din. Det første man må gjøre for å bruke request-evaluering med OPA er å skrive OPA-regelen som skal evaluere requesten.

OPA-regel for request-evaluering​

En OPA-regel for request-evaluering vil få inn et input-objekt som inneholder informasjon om requesten, inkludert HTTP-metode, path, query-parametere, headere, med mer. Regelen skal returnere et objekt som forteller om requesten skal slippes gjennom eller ikke. Det gjøres med feltet allowed. I tillegg kan regelen sette headere, statuskode og body som skal brukes når requesten enten skal avvises eller sendes videre til applikasjonen.

Eksempel input
{
"attributes": {
"source": {
"address": {
"socketAddress": {
"address": "172.17.0.1",
"portValue": 61402
}
}
},
"destination": {
"address": {
"socketAddress": {
"address": "172.17.06",
"portValue": 8000
}
}
},
"request": {
"time": "2020-11-20T09:47:47.722473Z",
"http": {
"id": "13519049518330544501",
"method": "POST",
"headers": {
":authority": "192.168.99.206:30164",
":method": "POST",
":path": "/people?lang=en",
"accept": "*/*",
"authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYWRtaW4iLCJzdWIiOiJZbTlpIiwibmJmIjoxNTE0ODUxMTM5LCJleHAiOjE2NDEwODE1Mzl9.WCxNAveAVAdRCmkpIObOTaSd0AJRECY2Ch2Qdic3kU8",
"content-length": "41",
"content-type": "application/json",
"user-agent": "curl/7.54.0",
"x-forwarded-proto": "http",
"x-request-id": "7bca5c86-bf55-432c-b212-8c0f1dc999ec"
},
"host": "192.168.99.206:30164",
"path": "/people?lang=en",
"protocol": "HTTP/1.1",
"body": "{\"firstname\":\"Charlie\", \"lastname\":\"Opa\"}",
"size": 41
}
},
"metadataContext": {}
},
"parsed_body": { "firstname": "Charlie", "lastname": "Opa" },
"parsed_path": ["people"],
"parsed_query": { "lang": ["en"] },
"truncated_body": false,
"version": {
"encoding": "protojson",
"ext_authz": "v3"
}
}
Eksempel output
{
"allowed": true,
"headers": {
"x-subject": "Ym9i"
},
"response_headers_to_add": {
"x-opa-eval": "true"
},
"request_headers_to_remove": [
"x-remove-me"
],
"http_status": 403,
"body": "{\"error\":\"Forbidden\"}",
"query_parameters_to_set": {
"lang": [
"no"
]
},
"query_parameters_to_remove": [
"debug"
]
}
FeltPåkrevdBeskrivelse
allowedJaBoolean som avgjør om requesten skal slippes gjennom eller ikke
headersNeiObjekt med headere. Ved avvist request sendes disse til klienten (downstream). Ved tillatt request legges de til på requesten videre til applikasjonen (upstream)
response_headers_to_addNeiObjekt med headere som skal legges til på responsen til klienten når requesten tillates
request_headers_to_removeNeiListe med header-navn som skal fjernes fra requesten før den sendes videre til applikasjonen
http_statusNeiHTTP-statuskode som sendes til klienten når requesten avvises. Standard er 403
bodyNeiResponse body som sendes til klienten når requesten avvises
query_parameters_to_setNeiObjekt som legger til eller overskriver query-parametere på requesten videre til applikasjonen
query_parameters_to_removeNeiListe med query-parameternavn som skal fjernes fra requesten videre til applikasjonen

Eksempel​

Under følger et eksempel på en regel som krever rollen admin i Bearer-tokenet på alle kall mot /api/admin, og slipper gjennom alle andre kall. Headeren x-subject blir også populert med sub-claimet fra Bearer-tokenet.

# METADATA
# entrypoint: true
package request

import rego.v1

default permit := true

permit := false if {
input.parsed_path[0] == "api"
input.parsed_path[1] == "admin"
token.payload.role != "admin"
}

allow := {
"allowed": permit,
"headers": {"x-subject": token.payload.sub},
}

token := {"payload": payload} if {
[_, encoded] := split(input.attributes.request.http.headers.authorization, " ")

# NB: io.jwt.decode validerer IKKE tokenet, men kun dekoder det.
# Ønsker du å validere tokenet kan du bruke io.jwt.decode_verify.
[_, payload, _] := io.jwt.decode(encoded)
}

Request-evaluering med OPA på SKIP​

Request-evaluering med OPA på SKIP konfigureres gjennom Kubernetes-ressursen SecurityConfig. Følgende er et eksempel på en SecurityConfig som konfigurer opp request-evaluering. OPA-regelen som skal ta imot requesten ligger på stien /v1/data/request/allow. Resten av feltene og dens betydning kan du lese mer om i accesserator sin API-doc.

apiVersion: accesserator.kartverket.no/v1alpha
kind: SecurityConfig
metadata:
name: security-config
namespace: tilgangsstyring-main
spec:
applicationRef: min-app
opa:
...
requestPolicy:
enabled: true
failureMode: DENY
endpoint: /v1/data/request/allow
requestBody:
include: true
maxRequestBodyBytes: 8192 # 8 KiB

Lokal kjøring av request-evaluering med OPA​

Du kan utvikle og teste request-evaluering med OPA lokalt ved å bruke docker-imaget ghcr.io/kartverket/skip-tilgangsstyring-local. Hvis SecurityConfig-ressursen fra filen konfigurert i miljøvariabelen SECURITY_CONFIG_PATH har request-evaluering aktivert, vil OPA starte opp med request-evaluering aktivert. Det vil også starte opp en proxy som vil rute innkommende trafikk via OPA for request-evluering, før den sender requesten hvis OPA tillater det. Proxyen konfigureres med to miljøvariabeler:

  • UPSTREAM_URL: URL-en til applikasjonen din.
  • PROXY_PORT: Hvilken port proxyen skal lytte på..
tip

SECURITY_CONFIG_PATH støtter YAML, JSON og Jsonnet. Inneholder filen flere SecurityConfig-ressurser, kan du spesifisere hvilken som skal brukes med SECURITY_CONFIG_NAME. Består Jsonnet-en av flere filer som importerer hverandre fra ulike mapper, kan du sette JSONNET_PATH til en kolonseparert liste med mapper Jsonnet skal søke i under evaluering.

Eksempel​

Følgende docker-compose.yaml kjører opp en proxy på port 8080 som tar imot requests, sender dem til OPA sin gRPC-port på :9191, og sender requesten videre til http://host.docker.internal:3000 (http://localhost:3000på din maskin) hvis OPA tillater det.

important

Kjører applikasjonen din lokalt på maskinen (altså ikke i Docker) kan du bruke host.docker.internal:<PORT> som UPSTREAM_HOST for å nå den. For at Docker containeren skal kunne nå applikasjonen din må applikasjonen din lytte på 0.0.0.0 for å kunne nås fra containeren.

# docker-compose.yml
services:
skip-tilgangsstyring-local:
image: ghcr.io/kartverket/skip-tilgangsstyring-local:latest-dev
ports:
- "3010:3010"
- "8080:8080"
environment:
SECURITY_CONFIG_PATH: /securityconfig.yaml
OPA_HTTP_PORT: 3010
OPA_GRPC_PORT: 9191
PROXY_PORT: 8080
UPSTREAM_URL: http://host.docker.internal:3000
OPA_PATHS: /opa/policies,/opa/data/local
volumes:
- ./opa/policies:/opa/policies:ro
- ./opa/data/local:/opa/data/local:ro
- ./securityconfig.yaml:/securityconfig.yaml:ro