We're developing a web application (SPA) consisting of the following parts:
NextJS container
Django backend for user management
Data API (FastAPI) protected with API keys, for which we also provide 3rd party access
The NextJS container uses an API key to access the data API. We don't want to expose the API key to the client (browser), so the browser sends the API requests to the NextJS container, which then relays it to the data API, see here. This seems secure, but is more complicated and slower than sending requests from the browser to the data API directly.
I'm wondering if it's possible to whitelist the web application in the data API, so that the client (browser) can call the data API directly without API key, but 3rd parties can't. FastAPI provides a TrustedHostMiddleware, but it's insecure because it's possible to spoof the host header. It has been suggested to whitelist IPs instead, but we don't have a dedicated IP for our web application. I looked into using the referer header, but it's not available in the FastAPI request object for some reason (I suspect some config problem in our hosting). Also, the referer header could be spoofed as well.
Is there even a safe way to whitelist our web application for data API access, or do we need to relay the request via NextJS container and use an API key?
Is there even a safe way to whitelist our web application for data API
access,
No, you need in all case an Authentication mechanism, something before the backend that check if the client is an authorize client.
The simplest pattern is using the NextJS container as the proxy. Proxy that have an api-key to call the backend ( what you are currently doing ).
There is many way to implement a secured proxy for a backend, but this authentication logic should not be inside the backend but in a separate service ( like envoy , nginx ... )
Related
I am using azure kubernetes for backend deployment. I have 2 URLs one is API URL(api.project.com) and other one is BFF URL(bff.project.com).
From Web application, instead of calling API URL(api.project.com) they use BFF URL(bff.project.com) which internally calls the API URL(api.project.com) and sends the response.
I now want to restrict direct usage of API URL(api.project.com) even from any REST API Clients(like postman, insomnia, ...) it should only work when triggered from BFF URL(bff.project.com).
We have used nginx-ingress for subdomain creation and both the URLs(BFF and API) are in same cluster.
Is there any firewall or inbuilt azure services to resolve the above mentioned problem ?
Thanks in Advance :)
You want to keep your api private, only accessible from another K8S service, so don't expose it using your ingress controller and it simply won't be accessible outside K8S to any client.
This means that you lose the api.project.com address (although you can get that back if you really want to, it seems unnecessary). The BFF would then access the API via the URL: http://<service-name>.<namespace>.svc.cluster.local:<service-port>, which in your case might be:
http://api.api_ns.svc.cluster.local
Assuming you haven't used TLS (http rather than https), the service is called api, it's running on port 80 (which it should be) and the namespace is called api_ns.
Should you need to provide temporary access to the API for developers to use, say, postman, then they can use port-forwarding to provide that in a dev environment without allowing external access all the time.
However, this won't restrict access to BFF alone. Any service running in K8S could access the API. If you need/want to restrict things further, then you have a lot of options.
I am using angular-oauth2-oidc to connect to Keycloak server which is behind nginx.
I am using code flow for retrieving the token.
Even when using HTTP also, I see a GET request being sent for /protocol/openid-connect/certs
which is failing since the frontend APIs are having the correct hostname, but the backend APIs (jwks_uri) is having localhost.
Also, I am unable to set the KC_HOSTNAME_STRICT_BACKCHANNEL=true since the backend microservices will not be able to connect to Keycloak.
Is there a workaround for this.
Prerequisites
I have two Cloud Run services a frontend and a backend. The frontend is written in Vue.js/Nuxt.js and is using a Node backend therefore. The backend is written in Kotlin with Spring Boot.
Problem
To have an authenticated internal communication between the frontend and the backend I need to use a token thttps://cloud.google.com/run/docs/authenticating/service-to-service#javahat is fetched from the google metaserver. This is documented here: https://cloud.google.com/run/docs/authenticating/service-to-service#java
I did set it all up and it works.
For my second layer of security I integrated the Auth0 authentication provider both in my frontend and my backend. In my frontend a user can log in. The frontend is calling the backend API. Since only authorized users should be able to call the backend I integrated Spring Security to secure the backend API endpoints.
Now the backend verifies if the token of the caller's request are valid before allowing it to pass on to the API logic.
However this theory does not work. And that simply is because I delegate the API calls through the Node backend proxy. The proxy logic however is already applying a token to the request to the backend; it is the google metaserver token. So let me illustrate that:
Client (Browser) -> API Request with Auth0 Token -> Frontend Backend Proxy -> Overriding Auth0 Token with Google Metaserver Token -> Calling Backend API
Since the backend is receiving the metaserver token instead of the Auth0 Token it can never successfully authorize the API call.
Question
Due the fact that I was not able to find any articles about this problem I wonder if it's simply because I am doing it basically wrong.
What do I need to do to have a valid Cloud Run Service to Service communication (guaranteed by the metaserver token) but at the same time have a secured backend API with Auth0 authorization?
I see two workarounds to make this happen:
Authorize the API call in the Node backend proxy logic
Make the backend service public available thus the metaserver token is unnecessary
I don't like any of the above - especially the latter one. I would really like to have it working with my current setup but I have no idea how. There is no such thing like multiple authorization token, right?
Ok I figured out a third way to have a de-facto internal service to service communication.
To omit the meta-server token authentication but still restrict access from the internet I did the following for my backend cloud run service:
This makes the service available from the internet however the ingress is preventing any outsider from accessing the service. The service is available without IAM but only for internal traffic.
So my frontend is calling the backend API now via the Node backend proxy. Even though the frontend node-backend and the backend service are both somewhat "in the cloud" they do not share the same "internal network". In fact the frontend node-backend requests would be redirected via egress to the internet and call the backend service just like any other internet-user would do.
To make it work "like it is coming from internal" you have to do something similar like VPN but it's called VPC (Virtual Private Cloud). And luckily that is very simple. Just create a VPC Connector in GCP.
BUT be aware to create a so called Serverless VPC Access (Connector). Explained here: https://cloud.google.com/vpc/docs/serverless-vpc-access
After the Serverless VPC Access has been created you can select it in your Cloud Run Service "Connection" settings. For the backend service it can be simply selected. For the frontend service however it is important to select the second option:
At least that is important in my case since I am calling the backend service by it's assigned service URL instead of a private IP.
After all that is done my JWT token from the frontend is successfully delivered to the backend API without being overwritten by a MetaServer token.
I'm building a microservice based REST API and a native SPA Web Frontend for an application.
The API should be protected using OAuth2.0 to allow for other clients in the future. It should use the Authorization Code Flow ideally with Proof Key for Code Exchange (PKCE)
As I understand it I need to run my own OAuth Auth Server that's managing the API Clients and generating access tokens, etc.
Also I need my own Authentication/IAM service with it's own fronted for user login and client authorization granting. This service is the place the users login credentials are ultimately checked against a backend. That last part should be flexible and the backend might be an LDAP server in some private cloud deployment.
These components (Auth Server and IAM servicve) are outside of the OAuth scope but appear, correct me if I'm wrong, to be required if I'm running my own API for my own users.
However creating these services myself appears to be more work than I appreciate besides the obvious security risks involved.
I read about auth0 and okta but I'm not sure if they are suited for my use case with the application potentially deployed in private cloud.
I also thought about running Hydra (OAuth Server) and Kratos (IAM) by ory but I'm not sure if this is adding too many dependencys to my project.
Isn't there an easy way to secure an API with OAuth that deals with the Auth Server and the IAM that's good for small projects?!
I have two Spring Boot web applications. Both applications have different databases and different sets of users. Also, both applications use Spring Security for authentication and authorisation which works properly.
At any given point I will have one instance of the first application running and multiple instances of the 2nd web application running.
I want to expose REST APIs from 1st web application (one instance running) and be able to use that REST APIs from 2nd web application (multiple instances running).
How do I make sure that REST APIs can be accessed securely with proper authentication and by instances of the 2nd applications only.
If you could change your security, I would recommend you to use OAUTH2. Basically it generates a token that is used in your APP2 instances to make the API calls.
You can see more here.
https://spring.io/guides/tutorials/spring-boot-oauth2/
http://websystique.com/spring-security/secure-spring-rest-api-using-oauth2/
But if you can't change your APP's security, you can continue using your current schema. In the APP1 you can create an user for the API calls, this user only has access to the API services. In your APP2 you need to store the credentials to access the APP1. Finally you do login into APP1 and invoke the API using HTTP client, you can use Spring RestTemplate or Apache HttpComponents Client.
SSL based authentication could be an option, if you seriously thinking about the security aspects.
Assume that you REST api exposed by App 1 is over HTTPs, then you can configure the App 1 to ask the client to give their SSL/TLS certificate when they try to access this REST API (exposed by App 1).
This will help us identify that the client is indeed a client from app 2.
Two More Cents:
In case if your App 1 REST API calls needs load balancing, NGINX should be your chose. The SSL client certificate based authentication can be offloaded to NGINX and Your Spring boot app no more worry about the SSL related configurations.
The solution we went with was to secure both using an OAuth2 client_credentials workflow. That is the OAuth2 flow where clients request a token on behalf of themselves, not a calling User.
Check out Spring Cloud Security
1) Secure your services using #EnableResourceServer
#SpringBootApplication
#EnableResourceServer
public class Application ...
2) Make calls from one service to another using an OAuth2RestTemplate
Check out Resource Server Token Relay in http://cloud.spring.io/spring-cloud-security/spring-cloud-security.html which will specify how to configure an Oauth2RestTemplate to forward on security context details (token) from one service to another.
3) Service A and Service B should be able to communicate using these techniques if they are configured using the same Oauth2 Client and Secret. This will be configured in the applications' application.properties file, hopefully injected by the environment. Oauth2 Scopes can be used as role identifiers. You could therefore say that only a Client with Scopes (api-read, api-write) should have access to Endpoint A in Service A. This is configurable using Spring Security's Authorization configuration as well as #EnableGlobalMethodSecurity