How to use Keycloak Policy Enforcer with Spring boot application - spring-boot

Keycloak policy enforcer not working with a sample Sprint boot application.
I am using Keycloak version 6.0.1 and trying to integrate a sample Sprint boot application (Sprint boot version 2.1.3). My objective to setup policies and permissions in Keycloak and use Keycloak policy enforcer in my sample Spring boot application so that all authorization decisions are automatically enforced using appropriate permission defined in Keycloak and no code is required in Sample application.
My Sample spring boot application just prints a list of users from a in memory List:
public class JPAUserResource {
#Autowired
private UserRepository userRepo;
#GetMapping(path = "/jpausers")
public List<JPAUser> retrieveAllUsers() {
return userRepo.findAll();
}
}
My application.properties file has following content:
server.port=38080
spring.jpa.show-sql=true
spring.h2.console.enabled=true
logging.level.org.springframework.security=DEBUG
logging.level.org.keycloak.adapters.authorization=DEBUG
#Keycloak Configuration
keycloak.auth-server-url=http://192.168.154.190:18180/auth
keycloak.realm=master
keycloak.resource=login-app
keycloak.principal-attribute=preferred_username
keycloak.credentials.secret=195925d6-b258-407d-a65d-f1fd12d7a876
keycloak.policy-enforcer-config.enforcement-mode=enforcing
keycloak.realm-key=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjyYRe6LxBxO9hVtr4ScsMCBp3aPE9qbJLptPIMQCZR6JhVhOxA1kxhRmVYHXR5pdwiQWU8MriRhAY1JGniG6GNS1+BL+JaUiaGxov4rpD2SIMdrs8YjjSoD3Z8wvsMAopzWG48i9T/ppNaqKTkDZHbHAXOYJn+lymQ4EqpQrJ1Uh+SUA8XcLvWUQ12ty9BieujudWhnAgQ4zxyJY3I8sZwjaRIxndzSlyPJo45lWzXkpqcl92eU/Max7LRM4WKqsUvu86DgqlXbJcz8T+GUeF30ONQDSLX9rwNIT4ZiCVMT7x6YfKXZW6jxC0UiXxZuT23xk8A9iCP4rC9xo1NfGTwIDAQAB
keycloak.policy-enforcer-config.paths[0].path=/jpausers
keycloak.policy-enforcer-config.paths[0].methods[0].method=GET
My Keycloak authorization settings are as below:
{
"allowRemoteResourceManagement": true,
"policyEnforcementMode": "ENFORCING",
"resources": [
{
"name": "Default Resource",
"type": "urn:login-app:resources:default",
"ownerManagedAccess": false,
"attributes": {},
"_id": "501febc8-f3e1-411f-aecf-376b4786c24e",
"uris": [
"/*"
]
},
{
"name": "jpausers",
"ownerManagedAccess": false,
"displayName": "jpausers",
"attributes": {},
"_id": "a8f691db-39ef-4b2c-80fb-37224e270f1e",
"uris": [
"/jpausers"
],
"scopes": [
{
"name": "GET"
},
{
"name": "POST"
}
]
}
],
"policies": [
{
"id": "94518189-3794-451c-9996-eec22543d802",
"name": "Default Policy",
"description": "A policy that grants access only for users within this realm",
"type": "js",
"logic": "POSITIVE",
"decisionStrategy": "AFFIRMATIVE",
"config": {
"code": "// by default, grants any permission associated with this policy\n$evaluation.grant();\n"
}
},
{
"id": "0242cf72-365d-49ae-8d5b-4ced24736f24",
"name": "test_jpa",
"type": "role",
"logic": "POSITIVE",
"decisionStrategy": "UNANIMOUS",
"config": {
"roles": "[{\"id\":\"jpa\",\"required\":false}]"
}
},
{
"id": "5c34e2b4-a56a-45f9-a1cc-94788bcb41b0",
"name": "test_perm1",
"type": "resource",
"logic": "POSITIVE",
"decisionStrategy": "UNANIMOUS",
"config": {
"resources": "[\"jpausers\"]",
"applyPolicies": "[\"test_jpa\"]"
}
}
],
"scopes": [
{
"id": "4ee351e6-7095-453a-a4f4-badbc9ec1ba0",
"name": "GET",
"displayName": "GET"
},
{
"id": "9119aab2-75a0-49d1-a076-8d9210c3e457",
"name": "POST",
"displayName": "POST"
}
]
}
When I send a request to my Rest API '/jpausers', it fails with following messages on console:
*19:17:52.044 [http-nio-38080-exec-1] INFO o.k.a.authorization.PolicyEnforcer - Paths provided in configuration.
19:17:52.045 [http-nio-38080-exec-1] DEBUG o.k.a.authorization.PolicyEnforcer - Trying to find resource with uri [/jpausers] for path [/jpausers].
19:17:52.151 [http-nio-38080-exec-1] DEBUG o.k.a.authorization.PolicyEnforcer - Initialization complete. Path configurations:
19:17:52.151 [http-nio-38080-exec-1] DEBUG o.k.a.authorization.PolicyEnforcer - PathConfig{name='null', type='null', path='/jpausers', scopes=[], id='a8f691db-39ef-4b2c-80fb-37224e270f1e', enforcerMode='ENFORCING'}
19:17:52.154 [http-nio-38080-exec-1] DEBUG o.k.a.authorization.PolicyEnforcer - Policy enforcement is enabled. Enforcing policy decisions for path [http://192.168.109.97:38080/jpausers].
19:17:52.156 [http-nio-38080-exec-1] DEBUG o.k.a.a.KeycloakAdapterPolicyEnforcer - Sending challenge
19:17:52.157 [http-nio-38080-exec-1] DEBUG o.k.a.authorization.PolicyEnforcer - Policy enforcement result for path [http://192.168.109.97:38080/jpausers] is : DENIED
19:17:52.157 [http-nio-38080-exec-1] DEBUG o.k.a.authorization.PolicyEnforcer - Returning authorization context with permissions:*
UMA Authorization is disabled. I had first retrieved access token using Openid Connect token API with Password credentials grant type and then I am trying to access my Rest API '/jpausers' with access token.
Can someone help with the issue here ? How do I fix this ? Do I have to enable UMA to make policy enforcer work ?

With quick look, I can see your mapping is not complete in application.properties, you have not mapped your HTTP method to scope you have configured in keycloak. Some thing like this
keycloak.policy-enforcer-config.paths[0].path=/jpausers
keycloak.policy-enforcer-config.paths[0].methods[0].method=GET
keycloak.policy-enforcer-config.paths[0].methods[0].scopes[0]=GET

I think you are missing keycloak.securityConstraints[0].securityCollections[0].name= jpausers

I had the same issue and I was able to resolve it with similar settings in my application properties yaml file as shown below:
keycloak:
security-constraints:
- auth-roles:
- "*"
security-collections:
- name:
patterns:
- /*

It working for me on Keycloak 19.0.1
keycloak.securityConstraints[0].authRoles[0]=*
keycloak.securityConstraints[0].securityCollections[0].patterns[0]=/
keycloak.securityConstraints[0].securityCollections[0].name= test
keycloak.policy-enforcer-config.on-deny-redirect-to=/403

Related

apache/apisix working mocking plugin example

I've installed apisix and apisix-dashboard with helm on my k8s cluster.
I used all defaults except APIKEY for admin and viewer acc., and custom username/password for dashboard. So I'm currently running the 2.15 version.
My installation steps
helm repo add apisix https://charts.apiseven.com
helm repo update
# installing apisix/apisix
helm install --set-string admin.credentials.admin="new_api_key"
--set-string admin.credentials.viewer="new_api_key" apisix apisix/apisix --create-namespace --namespace my-apisix
# installing apisix/apisix-dashboard, where values.yaml contains username/password
helm install -f values.yaml apisix-dashboard apisix/apisix-dashboard --create-namespace --namespace my-apisix
I'm unable to configure the mocking plugin, I've been following the docs.
In the provided example I'm unable to call the API on route with ID 1, so I've created a custom route and after that used the VIEW json, where I've changed the configuration accordingly to the sample provided.
All calls on this routes are returning 502 errors, in the logs i can see the route is routing traffic to a non existing server. All of that leads me to believe that the mocking plugin is disabled.
Example of my route:
{
"uri": "/mock-test.html",
"name": "mock-sample-read",
"methods": [
"GET"
],
"plugins": {
"mocking": {
"content_type": "application/json",
"delay": 1,
"disable": false,
"response_schema": {
"$schema": "http://json-schema.org/draft-04/schema#",
"properties": {
"a": {
"type": "integer"
},
"b": {
"type": "integer"
}
},
"required": [
"a",
"b"
],
"type": "object"
},
"response_status": 200,
"with_mock_header": true
}
},
"upstream": {
"nodes": [
{
"host": "127.0.0.1",
"port": 1980,
"weight": 1
}
],
"timeout": {
"connect": 6,
"send": 6,
"read": 6
},
"type": "roundrobin",
"scheme": "https",
"pass_host": "node",
"keepalive_pool": {
"idle_timeout": 60,
"requests": 1000,
"size": 320
}
},
"status": 1
}
Can anyone provide me with an actual working example or point out what I'm missing? Any suggestions are welcomed.
EDIT:
Looking at the logs of the apache/apisix:2.15.0-alpine it looks like this mocking plugin is disabled. Looking at the docs The mocking Plugin is used for mocking an API. When executed, it returns random mock data in the format specified and the request is not forwarded to the Upstream.
Error logs where I've changed the domain and IP addr. suggest that the traffic is being redirected to the upstream:
10.10.10.24 - - [23/Sep/2022:11:33:16 +0000] my.domain.com "GET /mock-test.html HTTP/1.1" 502 154 0.001 "-" "PostmanRuntime/7.29.2" 127.0.0.1:1980 502 0.001 "http://my.domain.com"
Globally plugins are enabled, I've tested using the Keycloak plugin.
EDIT 2: Could this be a bug in version 2.15 of apisix? There is currently no open issue on the github repo.
yes, mocking plugin is not enabled.
you can just add it here.
https://github.com/apache/apisix-helm-chart/blob/7ddeca5395a2de96acd06bada30f3ab3580a6252/charts/apisix/values.yaml#L219-L269
You can also submit a PR directly to fix it

web app works locally and on app engine, but not on cloud run

So I've run into this issue with a web app I've made:
it gets a file path as input
if the file exists on a bucket, it uses a python client api to create a compute engine instance
it passes the file path to the instance in the startup script
When I ran it locally, I created a python virtual environment and then ran the app. When I make the input on the web browser, the virtual machine is created by the api call. I assumed it used my personal account. I changed to the service account in the command line with this command 'gcloud config set account', it ran fine once more.
When I simply go to the source code directory deploy it as is, the application can create the virtual machine instances as well.
When I use Google cloud build and deploy to cloud run, it doesn't create the vm instance.
the web app itself is not throwing any errors, but when I check compute engine's logs, there is an error in the logs:
`{
"protoPayload": {
"#type": "type.googleapis.com/google.cloud.audit.AuditLog",
"status": {
"code": 3,
"message": "INVALID_PARAMETER"
},
"authenticationInfo": {
"principalEmail": "####"
},
"requestMetadata": {
"callerIp": "#####",
"callerSuppliedUserAgent": "(gzip),gzip(gfe)"
},
"serviceName": "compute.googleapis.com",
"methodName": "v1.compute.instances.insert",
"resourceName": "projects/someproject/zones/somezone/instances/nameofinstance",
"request": {
"#type": "type.googleapis.com/compute.instances.insert"
}
},
"insertId": "######",
"resource": {
"type": "gce_instance",
"labels": {
"instance_id": "#####",
"project_id": "someproject",
"zone": "somezone"
}
},
"timestamp": "2021-06-16T12:18:21.253551Z",
"severity": "ERROR",
"logName": "projects/someproject/logs/cloudaudit.googleapis.com%2Factivity",
"operation": {
"id": "operation-#####",
"producer": "compute.googleapis.com",
"last": true
},
"receiveTimestamp": "2021-06-16T12:18:21.253551Z"
}`
In theory, it is the same exact code that worked from my laptop and on app engine. I'm baffled why it only does this for cloud run.
App engines default service account was stripped of all its roles and given a custom role tailored to the web apps function.
The cloud run is using a different service account, but was given that exact same custom role.
Here is the method I use to call the api.
def create_instance(path):
compute = googleapiclient.discovery.build('compute', 'v1')
vmname = "piinnuclei" + date.today().strftime("%Y%m%d%H%M%S")
startup_script = "#! /bin/bash\napt update\npip3 install pg8000\nexport BUCKET_PATH=my-bucket/{}\ngsutil -m cp -r gs://$BUCKET_PATH /home/connor\ncd /home/connor\n./cloud_sql_proxy -dir=cloudsql -instances=sql-connection-name=unix:sql-connection-name &\npython3 run_analysis_upload.py\nexport ZONE=$(curl -X GET http://metadata.google.internal/computeMetadata/v1/instance/zone -H 'Metadata-Flavor: Google')\nexport NAME=$(curl -X GET http://metadata.google.internal/computeMetadata/v1/instance/name -H 'Metadata-Flavor: Google')\ngcloud --quiet compute instances delete $NAME --zone=$ZONE".format(path)
config = {
"kind": "compute#instance",
"name": vmname,
"zone": "projects/my-project/zones/northamerica-northeast1-a",
"machineType": "projects/my-project/zones/northamerica-northeast1-a/machineTypes/e2-standard-4",
"displayDevice": {
"enableDisplay": False
},
"metadata": {
"kind": "compute#metadata",
"items": [
{
"key": "startup-script",
"value": startup_script
}
]
},
"tags": {
"items": []
},
"disks": [
{
"kind": "compute#attachedDisk",
"type": "PERSISTENT",
"boot": True,
"mode": "READ_WRITE",
"autoDelete": True,
"deviceName": vmname,
"initializeParams": {
"sourceImage": "projects/my-project/global/images/my-image",
"diskType": "projects/my-project/zones/northamerica-northeast1-a/diskTypes/pd-balanced",
"diskSizeGb": "100"
},
"diskEncryptionKey": {}
}
],
"canIpForward": False,
"networkInterfaces": [
{
"kind": "compute#networkInterface",
"subnetwork": "projects/my-project/regions/northamerica-northeast1/subnetworks/default",
"accessConfigs": [
{
"kind": "compute#accessConfig",
"name": "External NAT",
"type": "ONE_TO_ONE_NAT",
"networkTier": "PREMIUM"
}
],
"aliasIpRanges": []
}
],
"description": "",
"labels": {},
"scheduling": {
"preemptible": False,
"onHostMaintenance": "MIGRATE",
"automaticRestart": True,
"nodeAffinities": []
},
"deletionProtection": False,
"reservationAffinity": {
"consumeReservationType": "ANY_RESERVATION"
},
"serviceAccounts": [
{
"email": "batch-service-accountg#my-project.iam.gserviceaccount.com",
"scopes": [
"https://www.googleapis.com/auth/cloud-platform"
]
}
],
"shieldedInstanceConfig": {
"enableSecureBoot": False,
"enableVtpm": True,
"enableIntegrityMonitoring": True
},
"confidentialInstanceConfig": {
"enableConfidentialCompute": False
}
}
return compute.instances().insert(
project="my-project",
zone="northamerica-northeast1",
body=config).execute()
The issue was with the zone. For some reason, when it was ran on cloud run, the code below was the culprit.
return compute.instances().insert(
project="my-project",
zone="northamerica-northeast1",
body=config).execute()
"northamerica-northeast1" should have been "northamerica-northeast1-a"
EDIT:
I made a new virtual machine image and quickly ran into the same problem, it would work locally and break down in the cloud run environment. After letting it sit for some time, it began to work again. This is leading me to the conclusion that there is also some sort of delay before it can be called by cloud run.

Chat bot Hands-Off published on azure - gives connection error emulator :Cannot post activity. Unauthorized

I have been trying to host bot which works on local to the Azure Hosting.
I'm trying to connect hosted bot with local emulator gives connection error (emulator :Cannot post activity. Unauthorized).
My .bot file:
{
"name": "production",
"description": "",
"services": [
{
"type": "endpoint",
"appId": "********************",
"appPassword": "*************",
"endpoint": "intermediatorbotsample2019.azurewebsites.net/api/messages",
"name": "AzureAccountLive",
"id": "178"
}
],
"padlock": "",
"version": "2.0",
"path": "D:\\Architecture\IntermediatorBot\\production.bot",
"overrides": null
}
I took a look at your bot file in your comment. The problem is that you have "name": "AzureAccountLive" in your services section. This name MUST be "production". The outer level "name" has to match the name of the bot (in this case, it's probably intermediatorbotsample2019). It's the Name:Production, Type:Endpoint combination that ABS looks for. If you update your botfile to match what I have below, your bot should work as expected.
{
"name": "YOURBOTNAMEHERE",
"description": "",
"services": [
{
"type": "endpoint",
"appId": "********************",
"appPassword": "*************",
"endpoint": "http://intermediatorbotsample2019.azurewebsites.net/api/messages",
"name": "production",
"id": "178"
}
],
"padlock": "",
"version": "2.0",
"path": "D:\\Architecture\IntermediatorBot\\production.bot",
"overrides": null
}
Re genreted AppId and secret from https://dev.botframework.com/.
Previously was using AzureBotProject replaced it with AzureBotChannelProject instead.

Failure to connect to proxy "Certificate signed by unknown authority"

I'm attempting to connect to a CloudSQL instance via a cloudsql-proxy container on my Kubernetes deployment. I have the cloudsql credentials mounted and the value of GOOGLE_APPLICATION_CREDENTIALS set.
However, I'm still receiving the following error in my logs:
2018/10/08 20:07:28 Failed to connect to database: Post https://www.googleapis.com/sql/v1beta4/projects/[projectID]/instances/[appName]/createEphemeral?alt=json&prettyPrint=false: oauth2: cannot fetch token: Post https://oauth2.googleapis.com/token: x509: certificate signed by unknown authority
My connection string looks like this:
[dbUser]:[dbPassword]#cloudsql([instanceName])/[dbName]]?charset=utf8&parseTime=True&loc=Local
And the proxy dialer is shadow-imported as:
_ github.com/GoogleCloudPlatform/cloudsql-proxy/proxy/dialers/mysql
Anyone have an idea what might be missing?
EDIT:
Deployment Spec looks something like this (JSON formatted):
{
"replicas": 1,
"selector": {
...
},
"template": {
...
"spec": {
"containers": [
{
"image": "[app-docker-imager]",
"name": "...",
"env": [
...
{
"name": "MYSQL_PASSWORD",
...
},
{
"name": "MYSQL_USER",
...
},
{
"name": "GOOGLE_APPLICATION_CREDENTIALS",
"value": "..."
}
],
"ports": [
{
"containerPort": 8080,
"protocol": "TCP"
}
],
"volumeMounts": [
{
"mountPath": "/secrets/cloudsql",
"name": "[secrets-mount-name]",
"readOnly": true
}
]
},
{
"command": [
"/cloud_sql_proxy",
"-instances=...",
"-credential_file=..."
],
"image": "gcr.io/cloudsql-docker/gce-proxy:1.11",
"name": "...",
"ports": [
{
"containerPort": 3306,
"protocol": "TCP"
}
],
"volumeMounts": [
{
"mountPath": "/secrets/cloudsql",
"name": "[secrets-mount-name]",
"readOnly": true
}
]
}
],
"volumes": [
{
"name": "[secrets-mount-name]",
"secret": {
"defaultMode": 420,
"secretName": "[secrets-mount-name]"
}
}
]
}
}
}
The error message indicates that your client is not able to trust the certificate of https://www.googleapis.com. There are two possible causes for this:
Your client does not know what root certificates to trust. The official cloudsql-proxy docker image includes root certificates, so if you are using that image, this is not your problem. If you are not using that image, you should (or at least install ca certificates in your image).
Your outbound traffic is being intercepted by a proxy server that is using a different, untrusted, certificate. This might be malicious (in which case you need to investigate who is intercepting your traffic). More benignly, you might be in a organization using an outbound proxy to inspect traffic according to policy. If this is the case, you should build a new docker image that includes the CA certificate used by your organization's outbound proxy.

Restart server on node failure with Consul

Newbie to Microservices here.
I have been looking into develop a microservice with spring actuator while having Consul for service discovery and fail recovery.
I have configured a cluster as explained in Consul documentation.
Now what I'm trying to do is configure a Consul Watch to trigger when any of my service is down and execute a shell script to restart my service. Following is my configuration file.
{
"bind_addr": "127.0.0.1",
"datacenter": "dc1",
"encrypt": "EXz7LsrhpQ4idwqffiFoQ==",
"data_dir": "/data",
"log_level": "INFO",
"enable_syslog": true,
"enable_debug": true,
"enable_script_checks": true,
"ui":true,
"node_name": "SpringConsulClient",
"server": false,
"service": { "name": "Apache", "tags": ["HTTP"], "port": 8080,
"check": {"script": "curl localhost >/dev/null 2>&1", "interval": "10s"}},
"rejoin_after_leave": true,
"watches": [
{
"type": "service",
"handler": "/Consul-Script.sh"
}
]
}
Any help/tip would be greatly appreciate.
Regards,
Chrishan
Take a closer look at the description of the service watch type in the official documentation. It has an example, how you can specify it:
{
"type": "service",
"service": "redis",
"args": ["/usr/bin/my-service-handler.sh", "-redis"]
}
Note that it has no property handler and but takes a path to the script as an argument. And one more:
It requires the "service" parameter
It seems, in you case you need to specify it as follows:
"watches": [
{
"type": "service",
"service": "Apache",
"args": ["/fully/qualified/path/to/Consul-Script.sh"]
}
]

Resources