How do I prevent anonymous requests to a REST API / NGINX server while allowing authenticated requests to endpoints? - rest

Initial disclosure:
I’m new to nginx and reverse proxy configuration in general.
Background
I have a Swagger-derived, FOSS, https-accessible REST API [written by another party] running on a certain port of an EC2 CentOS 7 instance behind an nginx 1.16.1 reverse proxy (to path https://foo_domain/bar_api/); for my purposes, this API needs to be reachable from a broad variety of services not all of which publish their IP ranges, i.e., the API must be exposed to traffic from any IP.
Access to the API’s data endpoints (e.g., https://foo_domain/bar_api/resource_id) is controlled by a login function located at
https://foo_domain/bar_api/foobar/login
supported by token auth, which is working fine.
Problem
However, the problem is that an anonymous user is able to GET
https://foo_domain/bar_api
without logging in, which results in potentially sensitive data about the API server configuration being returned, such as the API’s true port, server version, some of the available endpoints and parameters, etc. This is not acceptable for the purpose, from a security standpoint.
Question
How do I prevent anonymous GET requests to the /bar_api/ endpoint, while allowing login and authenticated data requests to endpoints beyond /bar_api/ to proceed unhindered? Or, otherwise, how do I prevent any data from being returned upon such requests?

Related

Allow API requests from a specific URL in azure kubernetes

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.

Whitelist web application for API access without API key

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 ... )

How to use JWT Auth0 token for Cloud Run Service to Service communication if the Metaserver Token is overriding the Auth0 Token

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.

Kubernetes API proxy for HTTPS endpoints

Kubernetes surfaces an API proxy, which allows querying the internal services via eg: https://myhost.com/api/v1/proxy/namespaces/kube-system/services/kubernetes-dashboard/
This is all well, and good. However, for security & compliance reasons, all of our services expose an HTTPS endpoint. Attempting to access them by going to https://myhost/api/v1/proxy/namespaces/default/services/myhttpsservice:3000/ results in
Error: 'read tcp 172.20.122.129:48830->100.96.29.113:3000: read: connection reset by peer'
Trying to reach: 'http://100.96.29.113:3000/'
Because the endpoint, 100.96.29.113:3000 is in fact https.
Is there any way to configure the proxy to apply SSL to specific service endpoints?
(Edit: If this is not currently possible, a relevant github issue link for tracking the feature request is also acceptable answer until it will be)
As documented at https://kubernetes.io/docs/tasks/access-application-cluster/access-cluster/#manually-constructing-apiserver-proxy-urls, (and pointed out on slack), you can access services behind HTTPS by prefixing the servicename with "https:" ;
Using the example from above, correctly it would be: https://myhost/api/v1/proxy/namespaces/default/services/https:myhttpsservice:3000/

Restrict access to Kubernetes UI via VPN or other on GKE

GKE currently exposes Kubernetes UI publicly and by default is only protected by basic auth.
Is there a better method for securing access to the UI? It appears to me this should be accessed behind a secure VPN to prevent various types of attacks. If someone could access the Kubernetes UI, they could cause a lot of damage to the cluster.
GKE currently exposes Kubernetes UI publicly and by default is only protected by basic auth.
The UI is running as a Pod in the Kubernetes cluster with a service attached so that it is accessible from inside of the cluster. If you want to access it remotely, you can use the service proxy running in the apiserver, which means that you would authenticate with the apiserver to access the UI.
The apiserver accepts three forms of client authentication: basic auth, bearer token, and client certificate. The basic auth password should have high entropy, and is only transmitted over SSL. It is provided to make access via a browser simpler since OAuth integration does not yet exist (although you should only pass your credentials over the SSL connection if you have verified the server certificate in your web browser so that your credentials aren't stolen by a man in the middle attack).
Is there a better method for securing access to the UI?
There isn't a way to tell GKE to disable the service proxy in the master, but if an attacker had credentials, then they could access your cluster using the API and do as much harm as if they could get to the UI. So I'm not sure why you are particularly concerned with securing the UI via the service proxy vs. securing the apiserver's API endpoint.
It appears to me this should be accessed behind a secure VPN to prevent various types of attacks.
Which types of attacks are you concerned about specifically?