Declarative GraphQL in a Cloud-Native Gateway

Bookmark

We’ve all heard the buzz around pushing application security into the hands of developers, but if you’re like most companies, it has been hard to actually make this a reality. You aren’t alone – putting the culture, processes, and tooling in place to make this happen is tough – especially for sophisticated applications like those backed by GraphQL.


In this hands-on technical session, StackHawk Lead Engineer Topher Lamey will walk through how to protect your GraphQL APIs from vulnerabilities using automated security testing. Get ready to roll-up your sleeves for automated AppSec testing.



Transcription


Hello everyone. So about me, I am a technical support specialist in Solow and I have recently joined a month or so. I have a background in devops and Linux administration. And yeah, this is a whole new thing for me, kubernetes and all these cloud native things, but I'm learning as I go. So when you open the link, you will be shown this sort of page and you can just click on this. So once you click the start track option, it will take around two minutes to create the environment. And the meanwhile, you can watch the video that has an overview of graphql. So what exactly is graphql? So graphql is like a schema query language. So if you've heard of SQL, the famous SQL database, MySQL, PostgreSQL. So it's something similar, but this time it is for REST APIs. So then you have a bunch of REST APIs. You know, most of the times you have to query one and then you get a lot of information from it and you have to filter information from it. So this can get a bit cumbersome considering, you know, you want, for example, just a name of a user, but due to REST being REST, you get a bunch of details like their phone numbers and the other details like address, etc. And then you have to filter it. So this causes like, you know, unnecessary calls. And if you have, say, you have a shopping cart or something like that, or some shopping query where you have to, like, you know, give multiple REST api calls just to get a little bit of data, then, you know, in that terms, REST becomes a bit cumbersome and you have to have multiple calls to just get some information from the backend. Whereas in graphql, what happens is you have a server on the backend and this server is like a sort of a Uber to you, to your Uber chat, we would say, to your application. And you just tell the server in a schema definition language, like what you want exactly and how you want it. For example, if you want, if you have a pet store application and you know, you have a pet and you have a store. So if you want just a pet names, all the pet names in the store, you could just tell graphql, like, give me that and graphql will give you only that. So you get what you ask basically, and not all the noise that is unnecessary and not required. Okay, so a little bit about Instruct. This is a self-contained workshop. Everything that you require is inside this workshop. You do not have to switch over to your local machine or anything as such. You can just run everything in this lab inside this web browser. You will have these UI buttons that you can click on. Sometimes in case you're not loading or anything as such, there's a refresh button here. On the right hand side, you will see this bar which can be resized in case the text is too small. So you can go through the details that the workshop provides. We give you a basic overview of what graphql is and how it works and all. So it's very useful in terms of microservices where you have multiple rest APIs. So we have a product called BlueEdge that is sort of an entry point to your application. It's like an api gateway, say, and everything passes through that. So we have integrated graphql within this product. So it has its own, so there is no separate graphql server that is running or as such. When you have, when you usually deploy graphql, you have to deploy it with a server. Mostly there is apollo graphql and there are various servers that are available that you can deploy and sort of use, but we have it integrated within our product. Also, this is an enterprise feature. So the open source version does not have this. In case you would like to try it out, you can always reach out to us or me and we will help you with a trial license, which you can use to test this and do your testing on that. So yeah, let's begin. So first you will begin by installing BlueEdge. You can just copy the commands on the right and just hit shift and insert. And it will copy the commands for you onto the screen. You do not need to sort of copy paste using right click. You can just directly do it with the keyboard. Some features that we provide is automatic schema generation. So those who know, those who don't know, or those who know, graphql is like a schema language, right? So you have to define a schema for your APIs and you give that schema to the graphql server and you tell it that this is how my graphql schema is when the users query, there is a special type of type query in graphql that the users can sort of get query and there is mutations. So these two are very special types in graphql. So you usually query the type query and inside that you define all your data structures and your types basically and functions. So a little bit on mutations. Mutations is something like, you know, your CRUD where you can update, delete and create queries, but using graphql. And the query type is like an entry point to your graphql server basically or your graphql backend. The entry type is where everything is defined. You define your types, the data that you know, your client can access basically. And you can also have functions as such. You can have various input types also. So we have installed glue-edge now in the cluster. And if you see, if you run this command, basically, you will be able to view all the rollouts happening. So glue-edge is installed in a namespace called glue-system. So if you do kubectl get pods on it, you'll be able to see all the pods that are in this namespace. And you can see they are all running right now. So we will now deploy the demo application. So all the files required for this workshop is within this instruct module. So you do not need to copy paste from anywhere. In case anyone has a query, you can always post it in the chat. I'm not sure if anyone is using the Q&A section. But I'm keeping an eye on the chat. So in case any query comes up, I can answer it. So we have deployed the application, the demo application, and we can sort of check it in case everyone are following. We have now deployed the demo application. We will just test the rest api now. So these are rest api basically. And we will just create a so in blue, for anything to, you know, enter to the system, we create something known as a virtual service. If anyone has deployed Apache applications, you must have heard of virtual host. So this is something similar to that. So we will create this virtual service. You can also view the contents of it. So you can see we have set a prefix. So whenever a query is hit, if the URL is prefixed with blogs, then we will route it to the upstream. So upstream in this case is a backend server or a backend service basically, that is the blue system. So you can just click on this. And you can see that, you know, we got the address from the JSON spec of this service, and we have just hit a call request to it. So you can see the data that is written, its ID, user username, title content, title content. So you can see, there are a lot of things that are written. And in case I wanted just the ID, I'd have to probably create a another route that says gimme slash ID, or gimme slash user, like get user, get ID, something like that. And this is like, you know, access information. So let's see how graphql sort of solves this issue. So we will now expose a Glue Edge UI. So we have an Edge UI that comes with Glue. And you can view everything in this UI too. It's like a read only UI sort of. So in case it shows you a blank page, you can just hit the refresh and you will be able to see this UI. So you so you can see if you go here, you can see there are different virtual services. For example, the one that we created right now, you can see the various upstreams that we have. So we have deployed upstreams. So Glue automatically creates upstreams from services that you deploy. So you do not need to automatically or manually create any service by hand, Glue will automatically take care of that. We also have, we also have a Glue CTL. We do have a Glue CTL CLI, it doesn't seem to be installed at the moment. So we have automatic schema generation basically for gRPC and the rest services. All you have to do is label the service and Glue automatically picks it up and generates a schema out of it. So as you can see, there is no graphql api, CRD objects created right now. So what we will do is we will now label the service blogs. So now we have labeled a service blogs. And in a few seconds, we will see that this has been generated. So you can also check what schema is generated. In case this is not the thing that you wanted, you can always edit the schema. So you can see a lot of things have been generated. So let's take it step by step. So we have something called an executable schema in graphql, that is where your schema is generated. We have something called a resolver functions. So in this case, we are using a rest resolver. So this is a rest api. So we are using a rest resolver. In case it's a gRPC service, then you will have a gRPC resolver. So we sort of give this name, like a can it is like a alias basically, it just tells you what what type of you know, what type of sort of rest api is this like it's a mutation. So if you remember mutations are basically CRUD, you can create, read, update and delete with those. So this is for posting blogs. So you can also you know, from the generated schema see that we have these arguments. So these are sent to the rest api from graphql. So we do not need to pass the result we automatically send provided you give this input. So this is a post type and we also have a get get query where you can get all the blogs basically. And the upstream that is here is the backend. This is a backend rest resolver. And if you notice here, we have something called a schema definition. So this schema definition is the graphql schema definition, the link that I shared before. This is the schema that that you know, you give graphql and it will sort of let you query it. So if you notice here, there's an input. So this is an input. So this the user can pass these as inputs. The content is a string type. The ID is an int type and title and title is a string type. And if you notice the user is another input. So if you notice below, it's called user input and it's a username. So this is a custom type, custom data type. And we also have a mutation where you can post blogs using this as a blog input. And if you notice here, there's this thing called a resolve name. And it points to this. It points to this alias. And this alias is located here. So this is how in case you're wondering how graphql knows which REST api to reach out to in case it requires any data. This is how it does. And this is also these are two special types. One is mutation, one is query. Query is your entry point. Whenever you type a query, you you have to type this and you get the data and this resolves to this get function. And then we have a type blog and a type user which has been defined within it. So when you query get blogs, this is what you can fetch from that. And all this is also in case what I have explained is a bit confusing. This is also explained on the right hand side within this instructor. And if you go to your glue edge UI, you can sort of play around and see the so so we have introspected this graphql spec for you and you can see all the you know, generated inputs and resolvers here. You can also view the raw config and you can also there's also a graph like a graphql explorer that we will now expose in the next few steps. So let's create a virtual service now to sort of like we will generate a virtual service with a graphql endpoint so it can query that and you can get your answers from it. So we have created a virtual service now, we look at yes, you can see we have created this default virtual service to query the graphql endpoint, you have to use this command here that will give you a URL, you have to copy this URL, it will be unique for everyone. So you copy this and you go to the you go to this graphql explorer. So if you if you want to see where it is, you can go to the main UI, click on api. Under the graphql section, you should see this default blog, click on explore, click on show URL bar and just replace it with this that you that you the URL that you got here when you ran this echo command. And now if you run this, it will say nothing, it will just give you an error. So currently nothing. So let's try a sample query. On the right hand side, you will have a graphql query. You paste this, you will get this query, my query. And if you run it, you will get the answers. So if you notice, we have got what we have asked. For example, we ran a function called get blocks. And we wanted the content, we just wanted the ID and inside the user, we just wanted the username. Right? So we got the same response that we know. So you get what you ask in graphql and how you want it is the same way you ask. So it's a very implicit sort of declaration. There is no, you know, guesswork as to how your data will come. Will it come when the user come above the ID or will the ID come at the top? However, you ask the data, that is the same way graphql returns it to you. So no more, you know, if you have used Python, there are dicts that were first unordered. So it used to become a huge problem like this, where you do not have the order of the dict. But now it is much better, I guess, there are ordered dictionaries in Python. So it's something similar to that. Also, one of the very frequent challenges, you know, in REST is aggregation of data. When you have like, you know, when you have to pull data. Okay, so in this, so as I was saying before, there is a very particular use case for graphql and comes to aggregation. So aggregation is when you have multiple REST APIs, but you want your query to be returned, you know, in a single response, basically. So graphql is very useful when you have mobile apps or anything as such, because you will have just one endpoint, like one query that you have to hit. And you know, the backend that is your powerful servers that are running in data centers, they will do the heavy lifting for you of resolving and you know, getting all the data. So you don't have to rely on the user's network or their mobile phone and how you know, how modern it is or how old it is. You just have to hit the REST api and get the data. So aggregation is this thing where, you know, for example, if I have three REST APIs, and I have to get data that is related to all three, but I don't want to hit them all at all three REST APIs. So that is where I can just aggregate them in one query, and I can get back the answer. So let's begin with this third section. As I said before, we, you know, we have automatic schema generation. But you know, there are always edge cases or times that you know, you want to have your own schema. So even that is supported. So if you notice there is this configuration tab here, you open this tab, you can go to this file that is called GQL1-GQL.yami. And all this is already created here for you. So we have two resolvers here, both are REST resolvers. One will fetch the blogs for you. All the all the blogs will be fetched and one will fetch the command, sorry, the comments on these blog posts. And you notice there is a blog ID that is given here with parent.id. So if you notice here, we have this thing called comments. And now as you know, one blog can have multiple comments, right? So we have a one to many relationship defined here. And this this thing around this comment, this is called as a list. If you notice in this schema definition, there's a type blog. Okay. And you will notice here that the comments in this list format, and beside it, we have a resolved it to get comments, which is defined here above. So you highlight it, you get to see that this is getting resolved from this api, this REST api. Now, what we have here is a one to many relationship, like one blog can have many comments, right? So if you had a REST api, you would have to, you know, probably query the blogs first. Then within that, you would have to get all the IDs from there. And then you would have to fire that to the comments to get all the answers, right? So in graphql, you just define the schema. You said that, you know, I want the ID, the user, the content, title and comments. And comment is of comment type. So within comments, you have these fields, ID, user, comments. So you can request graphql to like, if you only want the comment ID, for example, or you want just the comment user who has commented this, who want to get the number of comments or something as such, right? And you also have a user type that has a user name. So all that I'm explaining is also on the right hand side, just in case, you know, it's a bit hard to understand or anything, we can always refer to the side to the right hand side. So once you have gone through this, in case anything is confusing, you can always post it in the comment box. I'm not sure if anyone is using the Q&A section. All right. So, so and this is the query. So this is a type query that this is your entry point basically. So we will apply this now. Right. So you should get this thing that says graphql api created. Now if we do get graphql api, you should see a new blogs-graphql has been defined. And if you notice, this is what we have sent basically to the graphql. So we have defined our own graphql api. Now that we have defined our graphql api, let's define the virtual service for it. So as I mentioned earlier, virtual services like an entry point, or if you want to think in terms of Apache, it's like a virtual host, tell it what URL, what prefix, etc. And you can sort of give an entry point to your upstream. Upstream in the sense of your back end services, you see that this virtual service has been configured. If you want, you can also check what is there in this virtual service. You can see we have defined this graphql api reference, which is pointing to the blogs-graphql that we defined earlier. So this is how we sort of map the graphql api to the virtual service. And you notice we have just one singular endpoint for graphql. So now that we have this, we can switch back to the BlueEdge UI, go to the api section. And in case you don't have just the URL, you have this command that is given just below that. And you can get this URL once again. You can just click on the blog-graphql-graph and then go to the explore section. And if you run this, you still get the blogs, but let's just change this query. So we do not support, you know, SOAP automatic graphql generation, but you can convert your SOAP to REST and then, you know, generate the schema using REST. So we have a way to convert SOAP to REST api basically. So now we have a much larger query. So before running it, let's just go through it and see what exactly is happening, because it shouldn't be like, you know, you run something and you just see the output. So let's just go through it. So you notice this get blogs is what we have in our type query. If you go up here, you notice that this get blogs is what we have in our query and it is of the type blog. So in blog, we have ID, we have user, we have content, we have title and we have comments, which is further a comment type. So you see it's sort of aggregating, you know, different APIs right now. So we can request whatever we want. So if I run this and so here, whenever we give something, we just tell it like I want comments and in comments, I want ID. We've given all the comments and everything in the comment will come here, the ID and the user. All right. And in the content, we have the ID, the user and the comment. So now from the user, we only have user name, right? Let's try to do something else. So we remove this ID. So within the comments, we have we have removed the ID from there. So you notice we don't have the ID now, right? So similarly, you have a nested structure, basically. So as you notice that we've just told graphql, like, just give me this data. And that's all that I want. So if I remove this comments blog, it's an entire comments blog. Or for example, I remove the user blog. I run it, I won't have a user. Twice. So you see, we don't have the data, but we have the remaining data that we require. Right. So you get basically what you ask. You do not get any excess data. You do not get any less data. You get the amount that is required and the amount that you requested. So everyone with me on this, like, is anything about this confusing or something as such exciting, anything you can just, I mean, tell me about it. So I mean, I know, like, you're sort of following around. All right. So let's start going once. Going twice. Okay, so let's, let's probably move on. So you all have seen that you get a JSON response from this. Now we will basically fill up multiple graphql schema. So the ones that you have right now, multiple resources. Now we will, we will sort of combine multiple graphql schemas. It's called schema stitching. So you stitch multiple graphql schemas into one schema and you call the smallest schema as sub-schemas. So we'll just see that in the next section. So we will create a super graph right now, which will be a combination of multiple graphs. So let's see how this is done in the demo. So we will be deploying a service called users and a pre-existing graphql api. So we have a resolver called get user details, which we will integrate with our blog and comment services. So if you see, we have this graphql api defined here. We have a remote upstream basically. And we have defined the schema here. In this, we have a type user and we have a type query. So this is the entry point. We have get user details and it takes a username, which is in a string format. So you notice we have now given a filter sort of, so we give this get user details, a username and it will give us the details of that user. What kind of details it will give us is this user type. So if you notice up here, this type is defined with ID, username, last name and first name. And if you notice here, there is a, there's an exclamation mark after the string. So this means that it is required. So you will have to provide this username as an input to this function basically. Without that, you will get an error. So let's apply this. So you will notice we have something called a remote executor. So first, let's just see what it is. Okay. Apart from namespace. We have a service name called users. So basically, this is the service that is the api. And we haven't, if you notice, we haven't given any sort of rest resolver or anything as such. So let's reference this upstream that is mentioned here. So we just do get a cc. It's using the app equals to users. So you can see this is just a normal, just like a normal api. And we haven't given any URL or anything as such. It's just a normal kubernetes service. So now we will create this, we create a graphql api for this. So we have created a graphql api. So if you click here, you'll see this next section on schema stitching. So this is a remote query. And this is schema stitching. So if we go here, in the new APIs, we see remote graphql users, we have get user details. So we haven't created a virtual service yet. So you notice here we have, we can see all these definitions that are created in the graphql api. So let's move on. And we will now stitch these schemas together. So we can then get a, you know, unified output basically. So let's take a look at this graphql api schema that is called a stitch schema. So now we are stitching schema. We are not creating any schema definition here. We are stitching existing schemas into one. And what we are doing here, we are telling a sort of way to merge basically. And it's called type merge in graphql. And what we can do is we can sort of merge it on the field that is called username and the query. So we've given it username as a field. And we have given the arguments. So we will send a username argument. And it will stitch both this remote as well as blogs into one super super graphic. So you have only one graphql endpoint, but you can sort of query both using the username. So let's let's apply this stitch schema also. So we have applied the stitch schema. Now we will update the virtual service. So now we have updated our virtual service to expose the endpoint basically. You'll notice here we have given a graphql api reference to the stitch schema that is the combination of all schemas that are generated and that we have defined previously. So now let's go back to the glue system, the glue edge UI basically. And let's go to the api. So you notice we have a stitch TQL here. And you'll notice we have sub graphs. So these are the graphql APIs by themselves. These are both graphql APIs. And we have just combined them into this one stitched api, stitch graphql api. And you can also see all the you know, these are merged basically and you can see how it is. So the first is asking, can this schema share common types? Could you? Are you saying that if there were two type logs, like in both the schemas, are you saying that? So if that's the case, then they are merged together. So it's like a merge, the types also merge. Yeah. So in this case, what will happen is both the types will be merged and it will be merged into one type with all the fields. So you have to specify the merge config. And using that, it's it's usually merged. So in case you have foreign key relationships, then you can sort of, you know, specify the merge config. And then you will merge it accordingly. So yeah, let's move on. So we have a query now, and you'll notice that we have something called as get blocks. And we have also another function called get user detail. But we only have get blocks. And we'll see how this is sort of, you know, combining all together. So let's run our query. So we ran a query get block, we asked for the comments in the comment, we asked for the actual comment, the ID. And we also wanted to user. So we have the user first, then last. Now, if you see your log has a user type, right? And user is defined here. So you notice that these both are merged together, right based on the user. So we have a single super graph in this case. So let's run our query. Is everyone with me till here? So this username is used to fetch the details that you can see here. Returns a get get user details, returns a username. Okay, then let's move on. So like any other api, you have to also secure, you know, your graphql APIs. So let's see how we can do that. So with glue edge, we have something known as external authentication or extended authentication, where you can, you know, integrate OIDC, OPA, LDAP, basic authentication, and api keys. Okay, so we are going to create an api key. And we will secure the api using this key basic. So let's create a key. So this is the actual value of the key. It's stored in base 64. You open this, you will see this key that is here. But once you so once you open it, you will not be able to access it. So it's already base 64 encoded. So now we have to create something called as an auth config. This is a mechanism that glue uses to sort of add like sort of a guard between the back end and your applications. This is how you create an auth config. So we are going to use api key or Tindex. Sorry about that. So you can see this command here that says kubectl apply authentication file. And the secrets are mapped with this label called team infrastructure. All right. So we have created the auth config. So and now let's update our virtual service to sort of, you know, attach this auth config like the virtual service will attach it. So when the request comes in, it will check that and then it will tell me api key if it is valid or not. And then we will get the answer. Let's go to the blue edge UI once again, click on the APIs, go to the stitched GQL api. Click on explore again. You should see this URL if it is not there, you can run the command that is given in the chat and you will just run this command. And you will see this error saying unexpected end of data. We will just check once again, this URL is like. So let's add this to the request headers. If you notice there's a small bar down here that says query variables and request header. Let's just add this and then you're taking and you notice we got the query that we are looking for from here, all the details that we wanted. So this is how you can secure your api with an api key. So you can give the user an api key basically and they will be able to query. You also have things like rate limiting and we have a lot of other features, rate limiting, data loss prevention, data loss prevention, and data loss prevention. So you can use this feature to sort of mask the data. So if you notice here, you can see that we have a lot of data loss prevention. So you can see that we have a lot of data loss prevention. So you can see that we have a lot of data loss prevention. So you can see that we have a lot of data loss prevention. So you can see that we have a lot of data loss prevention. So you can see that we have a lot of data loss prevention. So you can see that we have a lot of data loss prevention. So you can see that we have a lot of data loss prevention. So you can see that we have a lot of data loss prevention. So you can see that we have a lot of data loss prevention. So you can see that we have a lot of data loss prevention. So you can see that we have a lot of data loss prevention. So you can see that we have a lot of data loss prevention. And we have given a regex. The regex is the username. So anywhere there is a username, anywhere this regex matches, it will be replaced with this character, the Asterix character. So and we will only mask 60% of that. So if it's got 10 characters, only 60% of that will be masked. The remaining won't be. So let's try it here. We have configured the virtual service. Now let's run this query again. And now you notice that in this username section here, we have username and we have masked 60% of the data. So this is how you can mask sensitive data, basically, in your applications. So the next feature that we have is called. So is everyone following with me? Is everyone reached here? Any questions, any queries, anything as such? So we almost reached the end of the session. So if any queries or anything as such are there. So if there are no queries, we can just move on to the last part of the section. So resiliency is a very important feature. You do not want your services going down. And if and as. And there is no such thing as like, you know, always 99.99 uptime, right? There is there are failures. There will be some or the other issue. It's how you how you like, you know, sort of recover from that, basically. So services do go down. Network is never. How do we say network is never reliable. You will always see some of the other service going down or some network hop, you know, getting missed or something like that. And so that is why we have, you know, when even in kubernetes, you run replicas. So if one replica goes down, you have other two replicas. So let's take a look on how we do that. So we will we will it's already created for us. We have a user's service, sort of user backup service. We will check the deployment is running. And we can see that it is running. Now, we will sort of modify this upstream to create a to simulate a failure basically. So this will update the default. So it will cause the causes deployment to fail basically. So let's apply this. So we have created an upstream which will fail. So we'll confirm this one last time and everything is working as expected. Now, let's simulate a failure. We will be scaling down the deployment to zero. And we will check if the pods are running. You see that it is terminating here. It will take some time. So the pods are terminating. And that will simulate like a failure because there is nothing running right. So we have we have no users, no user pod running right now. So there is no user service. And let's see what happens. So you notice that there was nothing, nothing that happened. So this is why we did not fail because we gave this failover part here. We gave a failover section here, which said if in case there is a failover, you change the backend to the users hyphen backup. Right. So in case the health check fails and there is no pod running, it will automatically switch to the default backup service that we have in the default namespace. So this is just a service URL basically. So this is how we have sort of, you know, resiliency built in where you just specify the backup and you can go through. So this is it. This is the end of it. Thank you for staying around. Have a nice day. Bye.
48 min
01 Dec, 2022