Authenticate a Google Kubernetes cluster certificate over https? - javascript

I'm a little lost trying to get acccess to a Kubernetes cluster hosted on Google Kubernetes Engine.
I would like to use a cluster certificate to authenticate to the provided kubernetes endpoint, so that I can run API requests to the kubernetes api, like creating deployments for example.
I'm trying to create deployments from an external API (from a NodeJS app, hosted on google app engine), where I will automatically create deployments on various different kubernetes clusters, not necessarily in the same google project.
I used to use basic auth to authenticate to the kubernetes api, this was trivial, all I needed was the username and password for a cluster, and then to base64 encode the two and put it in an Authentication header. I did this using axios and had no problems at all.
Now I'd like to switch over to using client certificates and I think I lack some understanding.
I guess I need to get the provided endpoint ip of the cluster, download the cluster certificate provided by google... that looks something like this:
...possibly base64 encode it and save it as a .crt, .cert, or ??.pem file and point axios to the file using a httpagent? (I tried saving the raw data as a .crt and .cert file, setting it as a httpagent and this unsurprisingly didn't work).
Do I need some kind of client/server key pair for the certificate, or maybe an API key?
I also read something about setting a Bearer token as an Authorization header, I guess this needs to be paired with the certificate, but I'm unsure where I can find/generate this token?
If there's anyone who can help with this obscure issue I'd be very grateful,
Thanks in advance!
P.S. I've been trying to decipher the K8s docs and I think I'm close, but I'm still not sure I'm looking at the right docs: https://kubernetes.io/docs/reference/access-authn-authz/certificate-signing-requests/

I've resolved the issue and I thought I'd post an answer in case someone is in my situation and needs a reference.
Alberto's answer is a great one, and I think this is the most secure mindset to have, its definitely preferable to keep everything internal to the google environment. But in my case, even if just for testing, I really needed to interface with the kubernetes API over https from another domain.
It turns out that it was simpler than I anticipated, instead of using the CA certificate, what I needed to do was to fetch a service account token from google cloud shell and place this in the Authorization header (very similar to basic auth). (In addition I should check the validity of the kubernetes endpoint by using the service account's client certificate, but I did not so this.)
Once in the google cloud shell and authenticated to the relevant cluster run the following:
kubectl get serviceaccount
kubectl get serviceaccount default -o yaml
kubectl get secret-default-token-some_id_string -o yaml
kubectl create clusterrolebinding default-serviceaccount-admin --clusterrole=cluster-admin --serviceaccount=default:default
Point 1 displays whether there is in fact a default service account present, this should be the case with a cluster created on Google Kubernetes Engine.
Point 2 prints out which service account is the default and should provide a service account name, something like "secret-default-token-abcd"
Point 3 prints out the token as well as its corresponding certificate. The token is printed as a base64 encoded string and needs to be decoded before it can be used in a http request.
Point 4 assigns the default service account a role (part of the Kubernetes RBAC thing) so that it has the correct permissions to create and delete deployments in the cluster.
Then in postman or wherever you're making the Kubernetes api call, set an Authorization header as follows:
Authorization: Bearer <base64 decoded token received in point 3>
Now you can make calls directly to the kubernetes api, for example, to create or delete deployments.
As mentioned above the certificate also provided as a base64 encoded string in Point 3, can be used to verify the kubernetes endpoint, but I haven't tested this (definitely more secure way of doing things).
In addition I was making the http call in a nodejs script and I had to set an environment variable process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; to override the certificate check... definitely not a safe way to go and not recommended.

I'm not sure if using manual authentication is the best for this case, as one of the good features of Kubernetes Engine is that it will provide authentication configuration for kubectl for you automatically, via the ‘gcloud container clusters get-credentials’ command.
There are also some resources (like connecting to another cluster) that are controlled via GCP IAM permissions instead of inside Kubernetes.
I would advise you to take a deep look at this page:
https://cloud.google.com/kubernetes-engine/docs/how-to/api-server-authentication
This should also handle the certificate for you.
In general I would advise you to check the GKE documentation first before the kubernetes one, as GKE is not only just Kubernetes but it also makes managing some things way easier.

Related

How do I use APIs with access tokens in apps created with create-react-app

How do I hide an API's secret access tokens in a production build of an app created with create-react-app?
I've visited this question but it does not have an acceptable answer to my question. I do not want to use process.env.REACT_APP_SECRET_VALUE in my app as this variable would be exposed in the client-side javascript.
Given that the production build of a create-react-app app is composed of static files only, the only solution I can think of involves only using APIs that use a combination of public client IDs and some form of backend client whitelisting, IP or otherwise.
Am I missing something here?
Like you already mentioned, you can request an api backend that only returns your secret if it's whitelisted IP.
But still can you be a litle more specific what are you trying to achieve, just guessing but maybe you need to look at direction of some authentication first so after that the user on the client side would be able to do something (see secret).
You're on the right track. If you're unwilling/unable to expose keys for services you're using, you may need to create an endpoint on a server somewhere that you call and have that endpoint proxy the relevant API request. Amazon's API gateway could probably handle this for you.
https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-api-usage-plans.html

Storing AWS credentials in the frontend

I am trying to get hold of my image objects from S3, from my JavaScript frontend application.
Acording to the documentation, these are the steps required:
import * as AWS from "aws-sdk";
AWS.config.update({accesKeyId, secretAccesKey, region});
let s3 = new AWS.S3();
And then, you can get the objects like so:
function listObjects(bucketName, folderName) {
return new Promise((resolve) => {
s3.listObjects({Bucket: bucketName, Prefix: folderName}).promise()
.then((data) => {
resolve(data.Contents);
})
});
}
All seems to work correctly, but what worries me is that I also need to keep the accessKeyId and the secretAccessKey in my frontend application, in order to access the bucket.
How does one secure the bucket, or access the objects without providing these confidential data?
You're right to worry. Any one will be able to take the credentials out of your app. There's a few approaches to this:
if the objects aren't actually sensitive, then there's nothing lost if the credential can only take the actions you wish to allow everyone. For that matter you should be able to get rid of the need for credentials all together if you set the permissions on your bucket properly .. I think that includes list permissions if necessary.
if the objects are sensitive, then you already have some sort of authentication system for your users. IF you're using Oauth accounts to auth ( google, amazon, facebook ,etc ) then you can use AWS Cognito to generate short lived AWS credentials that are associated to that user, which would allow you to differentiate permissions between users ... it's pretty slick and a great fit if already using oauth. IF you're not using oauth, consider whether you should be. It's far more secure than having to hande your own auth creds layer for your users. https://aws.amazon.com/cognito/
if you don't want to or can't user cognito, you can still assume an AWS role from the backend and generate temporary credentials that automatically expire in anywhere from 15 minutes to 1 hour or more and then pass those credentials to the front end. I'd call it "poor man's cognito" but I think it's probably actually more expensive to run the infra to provide the service than cognito costs.
Or, as #Tomasz Swinder suggests, you can simply proxy the requests through your application, resolving the asset the user requests to an s3 resource and pulling it in your backend and then serving to your user. This is an inferior solution in most cases because your servers are farther away from the end user than s3's endpoints are likely to be. And, you have to run infrastructure to proxy. But, that having been said, it has it's place.
Finally, pre-signed s3 urls may be a good fit for your application. Typically a backend would sign the s3 urls directly before providing them to the user. The signature is enough to authorize the operation ( which can be PUT or GET) but doesn't itself contain the private key used to sign - in other words, presigned urls provide an authorized URL but not the credentials used to authorize them, so they're a great way to provide ad hoc authorization to s3.
Overall it's really awesome to have a backend-free application, and for that you're going to need a 3rd party auth and something like cognito. but once you start using it, you can then use all sorts of aws services to provide what would otherwise be done by a backend. Just be careful with permissions because aws is all pay as you go and there's usually no capability to limit the calls to a service to make sure a cruel internet user strives to drive up your AWS bill by making tons of calls with the temporary creds you've provided them. One notable exception to that is API Gateway, which does allow per user rate limits and therefore is a great fit for a cognito-authorized serverless backend.
Also bear in mind that LISTing s3 objects is both much slower, and much more expensive ( still cheap per op, but 10x ) than GETing s3 objects, so it's usually best to avoid calling lIST whenever possible. I'm just throwing that out there, I suspect you're just doing that to test out the s3 connection.
Could you request that through your server? or is it a static site?
If this is a static site then, You can create IAM User for s3 that can only read the content that you are going to use and show in the fronted anyway.
One method we use is to store the credentials in a .env file and use dotenv (https://github.com/motdotla/dotenv) to read in the variables. These can then be accessed through process.env. For example, the .env file would contain:
AWSKEY=1234567abcdefg
AWSSECRET=hijklmn7654321
REGION=eu-west
Then in your code you would then call require('dotenv').load() to read the environment variables. You then access them as:
AWS.config.update({process.env.AWSKEY, process.env.AWSSECRET, process.env.REGION});
Make sure that the .env file is not committed into your repo. If you want you could have a env.example and instructions on how to create a .env when creating either a dev or production install.
As for securing the bucket, you can do so by restricting the read/write access to an IAM user that owns the AWS key/secret pair.

Direct Line API Microsoft Bot Framework - Get token without exposing secret publicly in Javascript

I'm creating custom chat window on a wordpress site (hosted outside Azure) for my bot using Direct line connector and Javascript, and to start conversation I need to specify Direct line SECRET or a TOKEN for my bot app.
To get a token i have to make a REST call to https://directline.botframework.com/v3/directline/tokens/generate and add to header "Authorization : Bearer SECRET".
I don't want to expose that SECRET publicly in my javascript file and I don't want to pass it as a URL parameter.
What are my other options?
How to get token without exposing my bot Direct line SECRET to client?
There is no front end solution, unfortunately. After your web page is served, all of its contents including all script files are available to be scrutinized by anybody who requested the page. The only way to hide your key is to use your server as a middle man, and store the secret there. Sorry for the crude drawing:
If your entire project is a static page, then this means substantial work is needed to set up a server. Thankfully, it's not as difficult to get a server up and running today as it was 10 years ago. If you're already familiar with JavaScript, then you'll be able to learn node.js quickly. Then, you can implement your server with a framework such as express which will do a majority of the heavy lifting for you.

Securing API with Node

I'm trying to build my first API to be consumed by a mobile application built with Ionic.
Before starting I'm looking into the architecture and I can not understand exactly how to make secure my API routes.
Let's say I have an endpoint like http://myapi/v1/get-items and my application doesn't need an user to be authenticated to view those items in the mobile app.
How should I protect that route from external queries, using Postman for example?
I wish that route to be not accessible unless is not requested by the application.
Looking on Google I can find many solution using basic authentication but all of those require an user to log in... What if my app doesn't have users to log in?
I'm a bit confused but I think there is a solution and I don't know it yet...
I hope you can help me to understand it.
EDIT:
My Question is totally different from the following: How to implement a secure REST API with node.js
I'm looking for solution that DO NOT require a User Authentication.
If you don't want to use User Auth through something like Passport then you can institute a whitelist in your Node API instead. express-ipfilter is an express middleware module that allows you to filter requests based on the request IP.
Requiring a login would be the cleanest and safest way to make sure your api remains private. However, if you want to keep external users out of your services without requiring a login, you will need to "sign" your requests. By that I mean doing something like encrypting a current timestamp on the client using a key known to both the server and the client app, adding that encrypted string as a header, receiving that header in your server, decrypting it and checking that it's not too old of a timestamp before you return a response.
It's not really safe (if someone can see the code they can see the encryption key) but it's an obstacle and it down't require logging in. See this for an example on encryption/decryption

How to Secure ASP.NET Web API with Cross Domain AJAX Calls?

I want to create an API at www.MyDomain.com that is accessible from public websites www.Customer1.com and www.Customer2.com. These public websites display each customers inventory and do not have any login features. They will use AJAX calls to read data from my API.
How can I secure the API so that it can be accessed via AJAX from different domains but no one can access the API to be able to scrape all of my customers data and all of their inventory?
I have tried thinking of different solutions on my own but they would all either require people to login to the public websites (which isn't an option) or it would require some secret "key" to be displayed publicly in the browser source code which could then be easily stolen.
Any ideas would be greatly appreciated.
Thanks!
P.S. Are their any obstacles that I am going to run into using Javascript & CORS that I need to look into now?
Anything that is accessible without authentication from a browser is by definition insecure, so you can't stop that. Your best bet is to have to have a relationship with the owner of customer1.com and customer2.com - the server apps for those two websites would make an HTTP call to you and authenticate with your service. Going this way also avoids the CORS issues you're talking about.
If you've already designed the client functionality, you can still probably do it without much change to the javascript - have it point to customer1.com for its AJAX call instead of your API, and customer1.com would accept this request and just act as a proxy to your API. Aside from the authentication, the rest of the request and response could just be pass-throughs to your API.
You can use Microsoft.AspNet.WebApi.Cors.
It's just need add ONE line at webapi config to use CORS in ASP.NET WEB API:
config.EnableCors("*","*","*");
View this for detail.
The simplest way to provide a minimum security here is to provide some kind of token system. Each app has its own token, or combination of tokens which it must pass to the server to be verified. How you generate this tokens is up to you and other than being linked to app's access, doesn't have to mean anything.
Provide a way for each API implementer to open an account with you. This way you will know who is accessing what and in some cases you can block/stop service.
For instance, a token can just be an MD5 hash:
7f138a09169b250e9dcb378140907378
In the database, this hash is linked to their account. On each request, they send this token with what they want. It is verified first to be valid, then the request is fore filled. If the token is invalid, then you can decide how to deal with it. Either don't return anything or return an "access denied" (or anything you want).
One thing to avoid is having a single token for everyone, though this can be a starting point. The reason for this is if some unauthorized app gets a hold of this token and exploits it, you have to change the token for everyone, not just the app that somehow leaked the token. You also can't control if someone has access to something or not.
Since you listed ASP.NET, I can also point you to WCF, which is fairly complex but has all the tools that you need to setup a comprehensive web service to service both you and your clients.
I hope this gives you a starting point!
EDIT:
There are security concerns here in the case that someone leaks their token key somehow. Make sure that you setup a way in which the app/your service do not expose the the token in anyway. Also have a flexible way of blocking a token, both by your clients in you, if it so happens that a token is exploited.

Categories