 So, hello everyone, welcome to our next talk in security dev room. Next speaker is Mojish and he's talking about plain text secrets in configuration files. Let's welcome him. Thank you. Hi everybody. I'm here to present my work on protecting plain text secrets. This work is being driven in the OpenStack project and it's mostly written in Python, what we have so far. Basically in our applications or services we handle input, output and we have many ways to interact with our applications. Basically standard input, standard output networking as well. We also have standard error and then we get other forms of interaction like arguments and for Python we have R parser. In this short code we can see I can import R parser, I create mark parser and then I declare what will I expect as arguments and then I can later use them. I can call and it automatically will generate me a helper for my program and I can call my program passing the arguments in the command line and it will behave accordingly. It will also handle types for example and that's mostly for arguments. Next one we have environment variables. So Python we use OS environment and then you can import OS and then see your environment variables but also we can have some private information there that we want to keep it secret. Another thing configuration files. So usually when we deploy your building service application we have the code and then the code base it usually doesn't change and then we have the configuration which usually changes from deployment to deployment and we want to keep them in a separate place. Usually we use configuration files for database connection for example. In Python we use config parser which consumes any files and you can see you have a session and then like a key value on each session and then you can access them with the code if I import config parser, create my parser and then start parsing the file. I can see the sections, I can get the values of each key and so on. Then we also have signals to interact with. Then we have this bunch of signals I'm not going to go through all of them and finally our application has like an exit code where we can see if it worked or if it didn't and then try to find out what happened. Then in OpenStack it's basically some people call it a cloud operating system. It handles bare metal nodes, virtual machines, containers and then on top of it you can deploy basically anything. It stays in the layer of infrastructure as a service and OpenStack is broke, is separate in lots of projects. This project is Oslo which is OpenStack common libraries and Oslo config is the package we use to handle configuration in OpenStack. It's not, you don't really to be doing OpenStack stuff to use Oslo config, most of the OpenStack libraries, they are, you can use them in any piece of software, you don't really need OpenStack for it. So Oslo config supports configuration files but also arguments, environment variables all in one and this bit more which are the source drivers that I've been working in the past two years. These source drivers it's what allows Oslo config to absorb configuration, to read configuration from different sources. So mainly in this talk we are going to use Oslo config to access secrets behind Vault which is a secret manager. But we have open specifications that you can find links for it in both of the posters I attached to the presentation site and then from the specification you can create your own drivers and then use Oslo config to draw the values from whatever source you want. So Castillon is another library under the Oslo project and it's a secret manager interface. So it can both talk to Barbican which is the OpenStack secret manager but it also talks to HashiCorp Vault and then in the future it can be enabled to talk to other secret managers. And basically your application is talking to Vault and then it has all the secrets stored and then you authenticate to Vault, you provide a key ID and then it finally gives you the key. And then it's all encrypted. Oslo config it has basic types where you can do type verification, string, Boolean, integers, float, list, digs and it also has some special types for validation like URI, host name, IP address, host address and port. And this is a simple, an example of an application using Oslo config. So here I'm importing from Oslo config and you can see my options, for example name, default world, the greeting, default hello, the times, default one and in my code I create my configuration object, I register my options and I call my object and then at this moment it will load all my configuration values. I can register command line options with the register CLI options. I can register a single option with a group and the name and here if you use a plural you'll have more than one option, this is a list and then this is a single option. Okay and when I run it gets you the helper from the information you put on it option. You can also change, it will automatically try to build the name of the arguments but you can also override that. And then when I call my program it's drawing basically the configuration from the default values but now I can pass using the argument and it changes behavior. I can have a configuration file and then tell my program to use the configuration file and then it will load the data from the configuration file and it happens if you put like multiple sources of information you will have the configuration file first and then environment variables and then argument and then different sources. It's in the documentation of the OSO project you can check how the fallbacks happen and the order it acts. So if I call, oh quite fast, if I call my program now you're just using arguments and configuration files and then on this example this is environment variable. The environment variables it's also a source drive that derived from the first implementation we did in 2017. So basically you put environment variable with OS underline as a prefix open stack and then the group name greeting and then underline underline and then you can put your option name. So if I have greeting times as an environment variable like that it will draw the configuration from the environment. So here I have a basic application, flask application I made to run in my welcome machine so it has a host and a port that it will listen to and then I have some database connection. And then I will try to connect to this database and when I run my application it should connect to the database and then tells me if it was successful to connect or not. These values they can be put to default so I can make my configuration file smaller and then just change them if I need. Of course you're not going to put username and password as default on your software so we are going to, I'm calling my application like this, passing the configuration file it's still able to work. So now I can kill my entire configuration file because I had sensitive information there and I put them in the environment variables and I'm passing them here to my call. And then these examples they go from like most insecure to more secure. The next one we have, so this is now using the Castlin driver to talk to the secret manager. So how it works, I have an option called config source in the default group and then it defines this section called secrets. You can have more, as you can have a list type in Oslo config you can have multiple configuration sources, config sources and then it will try to fetch configuration values from them based on the order you declare them. So I could have like secrets one, secrets two, secrets three and then when my application runs it will poke them index that sequence until it gets the value. On the session you have to define the driver so this driver is Castlin and then you can create your own drivers. The config file for Castlin itself and a mapping file. On the config file for Castlin which is here I have key manager, the back end vault on the example that I will be showing and you can also have Barbican on open stack. And then there's a session that should be a session here but I will be passing those values on the environment variable. And then the mapping file it tells me for the group DB username and password they have these IDs. So when I poke the Castlin driver it will use this mapping file for when I ask it like give me the username of the group DB it will then use this ID and then talk to vault and retrieve the secret which is under this ID. So here I call my application again passing my configuration file and I pass my vault token on the environment and then my application is able to talk to vault, get the credentials for the database and then run. I think this is the last slide, yes. Then here is the demo that I presented at EuroPython last year. I have Docker Compose which I will run one Flask app in the first one. The second one is a vault server and the third one is a database. So I will start Docker, should have done that before but it was killing my memory. So we have, and I'm not connected to the Internet, yep, oh it's here. It doesn't work until I connect to my phone and then oh I have Internet and then I can, so I can connect to the database. And then I'm saying here the username and password I used and basically all the values are coming from the configuration file. Then I have the other examples. On this one, on the plain text example I have only my application configuration file and this run script will call my application passing the configuration file. When I run it, it is using with this configuration and I have here another node running in a different port, the same application. So going up another level, this is plain text example. So now I have a secrets static. So I have my application configuration file. I have a casted on file and the mapping for the secret and when I run it, it will crash because vault is not running yet, it's sealed, it's here. So I started my container, my vault container, it is locked so I have to unseal and then now I have my token which I can check it's unsealed. I have my secrets there and then now it will not crash. So when I try to access, it connects to the database but I still have the same secrets there. So if you have multiple nodes, the ideal is that you have different credentials for each node because if you have 100 nodes running with the same secrets, if one gets compromised, all of them are compromised. So I have a third example. So even though the secrets are encrypted and stored in vault, if you have multiple nodes, that's a problem. So on this one, just see, I also have this example code, a simple orchestrator that will talk to vault, get me a temporary token for my node and then generate temporary credentials for my application and then we'll create the configuration files. So when I run this, it talks to vault, creates the credentials for me and then here I can see that I can connect to the database and I have a different username and a different password and if I run this again and again, it will always create different credentials for me. If I go to vault and check my access, lease, database, credentials, so this is that token and as this is an example, it's one minute valid token. You should have more running on production but for this example, I put it just one minute so we could check 13 seconds, it's still working and now probably, okay, the token expired so it's no longer listed here and when I try to connect again to the database, it will fail. So that's it. Thank you. Give it one more pause for the speaker.