travisghansen / external-auth-server

easy auth for reverse proxies
MIT License
330 stars 44 forks source link

How to disable encryption/signing of server-side-tokens #159

Closed kettenbach-it closed 1 year ago

kettenbach-it commented 2 years ago

Hi!

As I have read here: https://github.com/travisghansen/external-auth-server/blob/master/CONFIG_TOKENS.md#server-side-tokens the server-side tokens do not have to be encrypted.

We have been doing it with encryption until now and it is quite cumbersome. Without encryption, it would be sufficient to simply edit a k8s secret or a ConfigMap and reload eas, to change eas' config.

My question is: if I write the JSON unencrypted into a store-token.json using a secret - is it enough to NOT set the *_SIGN_SECRET and *_ENCRYPT_SECRET variables to tell eas that the config is now in plain text?

travisghansen commented 2 years ago

Good to hear from you again! You should leave those variables set regardless (this way it eases the transition and continues to work for existing configurations (or future configurations)).

Also note that server-side does not imply unencrypted but as you mention, you may use unencrypted tokens when using a server-side config.

Essentially to use server-side you should:

As the doc mentions you could use a pointer token but I'm gathering you want the above approach.

The logic behind how it works isn't too terrible to read in the code if that helps. Just search for isServerSideConfigToken here: https://github.com/travisghansen/external-auth-server/blob/master/src/server.js

kettenbach-it commented 2 years ago

I do use server side!
And I want to get rid of the encryption.

travisghansen commented 2 years ago

Ah! Simply don't encrypt it. It will detect on each individual token (so some can be encrypted and others not if you wish).

https://github.com/travisghansen/external-auth-server/blob/master/src/server.js#L259-L265

kettenbach-it commented 2 years ago

Ah, that's easier than I thought! I'll try that!

travisghansen commented 2 years ago

Just make sure you json encode the value as it's json-in-json when using a flat file like that.

EDIT: lies! it's not json, but rather the jwt...ignore me ;)

travisghansen commented 2 years ago

Frankly I've been waiting for someone to create a sort of admin dashboard for eas that automatically hooks into a DB and let's you manage everything via a gui (issue tokens, disable them, etc, etc). I guess it's not popular enough yet ;)

kettenbach-it commented 2 years ago

Hi Travis,

For my taste, a UI with a database would not be very effective. Precisely because we consistently follow the Infrastructure as Code approach and therefore find it good that eas can be completely configured via code in our git. A database & UI would only introduce extra complexity.

But what could make sense would be to configure the whole thing e.g. via yaml instead of json (for a server side config I mean). I find the JSON not very handy. Just too many curly brackets.

I have just tried to give eas the configuration in plain text. So I switched from this:

data:
  store-token.json: |
    {
    "verivinum-prod":"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",
    "verivinum-staging":"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",
    "
.....

to this:

data:
  store-token.json: |
     {
      "verivinum-prod": {
       "aud": "Verivinum Production",
       "eas": {
        "plugins": [
         {
          "type": "oidc",
          "issuer": {
           "discover_url": "https://accounts.google.com/.well-known/openid-configuration"
          },
          "client": {
.....
         }
        ]
       }
      },
      "verivinum-staging": {
       "aud": "Vicosmo Staging",
       "eas": {
        "plugins": [
         {
          "type": "oidc",
          "issuer": {
           "discover_url": "https://accounts.google.com/.well-known/openid-configuration"
          },
          "client": {
....
etc....

and get the following error:

eas-nxjjt external-auth-server 2022-10-15T09:10:58.626253294+02:00 {"level":"error","service":"external-auth-
server","message":"The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like 
Object. Received an instance of Object","stack":"Error: The first argument must be of type string or an instance of Buffer, 
ArrayBuffer, or Array or an Array-like Object. Received an instance of Object\n    at Object.decrypt 
(/home/eas/app/src/utils.js:82:11)\n    at verifyHandler (/home/eas/app/src/server.js:246:48)\n    at 
processTicksAndRejections (internal/process/task_queues.js:97:5)"}

By the way: removing EAS_CONFIG_TOKEN_SIGN_SECRET results in this so I left it set:

eas-ng4z4 external-auth-server 2022-10-15T09:07:52.028397021+02:00 missing EAS_CONFIG_TOKEN_SIGN_SECRET env variable

I created this store-token.jsonthis way:

let tokens_unencrypted = {
    'verivinum-prod': config_token_cleartext_verivinum_prod,
    'verivinum-staging': config_token_cleartext_verivinum_staging,
    'verivinum-dev': config_token_cleartext_verivinum_dev,
    'gitlab-vicosmo': config_token_cleartext_gitlab_vicosmo
}

console.log(JSON.stringify(tokens_unencrypted, null, " "))

What's wrong with this?

travisghansen commented 2 years ago

I can support yaml easily enough. The yaml parser support json so the code would be exactly the same.

The values should be the unencrypted jwt (and therefore signed, the signature is still checked) vs the input to create the jwt.

travisghansen commented 2 years ago

Regarding configuration I have also considered exposing an endpoint in the service that will sign/encrypt a config token using POSTed data (but it would need to be optional and have some form of auth on it).

kettenbach-it commented 2 years ago

The values should be the unencrypted jwt (and therefore signed, the signature is still checked) vs the input to create the jwt.

Does this mean to do this:

let tokens = {
    'verivinum-prod': jwt.sign(config_token_cleartext_verivinum_prod,  config_token_sign_secret),
    'verivinum-staging': jwt.sign(config_token_cleartext_verivinum_staging, config_token_sign_secret),
    'verivinum-dev': jwt.sign(config_token_cleartext_verivinum_dev, config_token_sign_secret),
    'gitlab-vicosmo': jwt.sign(config_token_cleartext_gitlab_vicosmo config_token_sign_secret),
}

If yes, please allow me to ask why all this security (signing, encrypting etc.) is needed for a server side config? This tokens could be kept in a k8s secret just as plain json (or even better yaml). This isn't a problem. There's no need for signing or even encrypting. This way I could just edit the k8s secret, update it via kubectl and I'm done and it i wouldn't need to run those javascript tools. Configuring eas would be a lot easier and more cloud native this way.

And: in the docs you write, that the server side tokens introduced state. I don't think they do. The tokens are just the configuration (https://12factor.net/config) and can be kept in a ConfigMap or Secret. The actual state are e.g. the sessions. Those shouldn't be kept in a container and therefore we're using Redis for this.

travisghansen commented 2 years ago

Fair points. server-side tokens were not in the original design (in my head and a piece of paper) and as such I don’t necessarily disagree with your assessment for the need of such strict security measures. Indeed for development I have a plugin that isn’t checked into the project which reads config as you have suggested (so I don’t have keep re-signing/encrypting everything for each config change).

Initially for server-side tokens I generally envisioned db usage so the signed/encrypted makes more sense vs injected as config as you have suggested (12 factor and all that). ie: the security of deploying the tokens jointly with eas is stronger and fundamentally different than eas reading from a truly external store…hence the comment about state and the desire to ensure encryption and signing.

Having said all of that, I’m not opposed to allowing tokens to be used in the fashion you propose, but I would likely prefer an env var control that ability so as to maintain more secure defaults.

Another consideration is that eas can run outside of k8s. I have considered adding a plugin store type that is k8s secret(s) which would make the app ‘k8s native’ and likely be a great approach for what you need. Such a plugin could deliberately remove the need for signing/encryption but even then I would want it a configurable option. Mostly in an effort to ensure safe configurations for organizations that need strict ‘separation of duties’.

All good ideas and points! I really like the idea of a native store plugin using k8s secrets!

kettenbach-it commented 2 years ago

Concerning db usage: it depends who is updating the config an how frequently! If your target user is a windows-type sysadmin that has to create tokens rarely (and therefore needs an "assistant" to do that), then a db (with UI) would make sense. If your target user is a cloud-native devOps guy who updates tokens using IaC (Infrastructure as Code) and maybe pretty often as well (like you do in dev), then a db would be contract productive. I'm the second guy. And: a db always add's an extra layer of complexity. E.g. who creates the users that can access this db? How is the DB managed? If you have a db, you should have a UI (especially for user type 1). How to you protect the access to the UI? You can't use eas, because of the chicken-and-egg problem. What if you take sql and have to update the schema - then you need to add migrations to your node application and an ORM as well...... If I wanted a solution with UI, I could - afaik - e.g. use kong or apache-api-six. I think the advantage is that eas can be easily integrated in an IaC pattern, just like traefik.

And if you you'd like to strengthen that (and maybe put in into the docs "this is made for IaC!"), then you should go for a better support of k8s. BUT you won't need a plugin that reads k8s secrets!! A k8s secret is brought into the container using env vars or config files by a mapping process at the start of the container. The only thing eas needs to support is reading the cleartext (no signature, no encryption) either from a file or env var(s). The separation of duties (separation of concerns) IS already taken care of by k8s - if other guys are responsible to create the tokens then running the container, this can easily be configured in k8s. Please don't take care of that in the eas. There are even a couple 3rd party solutions for handling secrets in k8s, that would exactly take care of that aspect in much more elaborate way, then eas could ever do.

kettenbach-it commented 2 years ago

Hi Travis,

another customer of us, who we consult in cloud native aspects, has been trying to use eas during the last weeks, because we had suggested to him to use eas for a specific use case (Keycloak + traefik + eas + hisCloudApp on k8s). Although the colleague there is an experienced DevOps-expert, the team there did not manage to implement eas. I have advised the team over the last few days and looked at where the problems were. I don't want to name the reasons per se here in public. If you want me to, please let me know. On the other hand, I can also send you an email.

travisghansen commented 2 years ago

If the reasons are security related please email! If they are usability related etc the feedback is entirely welcome here!

kettenbach-it commented 2 years ago

I called the customer todays and asked the senior platform engineer for a honest feedback. Here's what he said (raw and uncut):

  1. Even for a senior platform engineer, it is/was hard to get into eas
  2. There is no big picture what conceptually is required to run eas
  3. It would help to have a bigger picture off the flow that eas needs to be put into
  4. The customer would like to have some examples how to use it together with Keycloak and Traefik2
  5. There are so many options and it is very easy to get lost into all of these options
  6. The customer likes some recommendations for typical use of eas (like "protecting an API" etc.)
  7. The customer thinks it's a matter of documentation
  8. The customers says the documentation does not answer the questions the are raised when you want to run it
  9. EAS has so many features (which is good!) but it's hard to overlook them and it's hard for non-experts to use eas
  10. The customers would suggest writing a "beginners manual"
travisghansen commented 2 years ago

Agreed with all of them, all valid points :(

As you mentioned it's a very advanced tool with lots of options which definitely can be daunting (especially oidc/oauth). Obviously I can't do all of the above right now so do they have some specific questions still looming or something I can help with at the moment?

I'll likely have some time next week to dig into issues we've been discussing and hopefully make some progress on each.

kettenbach-it commented 2 years ago

Addition from myself:

What would be perfect is:

apiVersion: v1
kind: ConfigMap
metadata:
  name: eas-configuration
  namespace: eas
data:
  store-token.json: |
     token1:
       aud: "My first token"
       eas:
          plugins:
              type: oidc
              issuer:
                discover_url: "https://....."
              client:
                  client_id: ......
                  client_secret: .....

There should be examples for the plugins to run in different scenarios.

The customer should not need to:

But still, putting the config in a git repo following the IaC idea is a very good thing compared to Kong, Traefik Enterprise oder APISix (all of them having OIDC already on board).

kettenbach-it commented 2 years ago

I'm willing to help to make this project more successful, because I think it deserves it.

What I could do is to work on the content of the docs as well as their structure

What I can't do: implement things in JS/TS

travisghansen commented 2 years ago

The problem with generally allowing unsigned and unencypted configs is that the service will accept any non-server-side tokens as well...so then you would be allowing any arbitrary configs potentially doing malicious activity or even just exposing secrets you don't want exposed organizationally. We could add an option to only allow server-side tokens which would make that much more secure and feasible.

Supporting yaml configs generally is a desirable and generally should be quite easy.

Operators shouldn't need to know js, npm, etc, etc. Config tokens can be generated with:

cat config-token.json | docker run --rm -i -e EAS_CONFIG_TOKEN_SIGN_SECRET=foo -e EAS_CONFIG_TOKEN_ENCRYPT_SECRET=bar travisghansen/external-auth-server generate-config-token

In fact I never actually intended the project to directly be used for generating tokens anyway. I'm using standard jwt sign and standard encryption features that can be implemented in any language. I don't really think it's necessary to build tooling though given the availability of the simple docker command above.

I'm open to all forms of contribution! The feedback is appreciated and I'm happy to work through the individual points and come up with solutions that are helpful and secure.

kettenbach-it commented 2 years ago

The problem with generally allowing unsigned and unencypted configs is that the service will accept any non-server-side tokens as well...so then you would be allowing any arbitrary configs potentially doing malicious activity or even just exposing secrets you don't want exposed organizationally. We could add an option to only allow server-side tokens which would make that much more secure and feasible.

I can follow the reasoning, even though I am not the type who wants to prevent others from doing stupid things. So allowing the plaintext configuration only when reading it via the file adapter, for example, would therefore make sense.

Operators shouldn't need to know js, npm, etc, etc. Config tokens can be generated with:

Why can't I just write the configuration as yaml in a ConfigMap and be done with it? That's what all the other tools do.

In fact I never actually intended the project to directly be used for generating tokens anyway. I'm using standard jwt sign and standard encryption features that can be implemented in any language. I don't really think it's necessary to build tooling though given the availability of the simple docker command above.

I'd like to need no tooling at all!

Question: What use case is/was eas intended for? I don't really understand passing tokens via http(s). I can think of use cases in which these tokens are generated automatically. But I can't think of anything that makes sense in practice..... I used eas at the time because thomsedden's forwardAuth tool is very limited. I don't remember exactly what was missing. But I remember that it didn't work for my use case. It's definitely the tool you'll find first - probably because of its name - when you Google "forwardauth" in combination with a few other keywords. At least back then, about 3 years ago, it was like this. UI tools like APISix etc. didn't exist back then or they were (and are) too expensive like Traefik Enterprise. Besides, I like IaC. In the end, eas is a super tool for a flexible implementation of a forwardAuth service together with traefik. It does IaC and can do everything it is supposed to. I configure it as often as I configure traefik - once every few years! Therefore, it would make sense to configure it (technically) as simply as possible in the k8s environment. I mean, looking through the many parameters is hard enough. Why then sign and encrypt if it's not necessary in a k8s environment!?

And another question: is it used significantly outside k8s and together with traefik (and OIDC)? I consider this to be the primary use case that will hit every k8s operator using traefik in the community variant sooner or later....

travisghansen commented 2 years ago

A lot of your questions are asking about removing the need for signing/encryption which I've already said I support, it just needs to be done in a sensible fashion keeping security in mind is all.

The use-case for eas was to create a stateless service that could handle any number of configurations without needing to deploy a new instance for every configuration (ie: not the pattern needed when using oauth2-proxy and similar). I wanted a simply way to secure any service I wanted without needing to redeploy the/a forward auth server over and over.

While achieving those goals it needs to:

Again, I agree with the goal of handling the config as IaC (it does so already as your doing, simply signing/encrypting (or not) the values does not change that goal...it does require more work/effort obviously). To alleviate some of the configuration complexity I think there are scenarios where removing the need to sign/encrypt are acceptable, it just needs to be done sensibly. To that end I intend to address this ticket when I have some time to dedicate to this project. I want to add this request and support the remaining oidc flows to remove the need for eas to idp communication.

kettenbach-it commented 2 years ago

The use-case for eas was to create a stateless service that could handle any number of configurations without needing to deploy a new instance for every configuration (ie: not the pattern needed when using oauth2-proxy and similar). I wanted a simply way to secure any service I wanted without needing to redeploy the/a forward auth server over and over.

I think in a broader sense "stateless" means that the container does not write any files - at least not those that must not be lost or used by others. (Factor VI) For persistence there are backing services (Factor IV). That a container has a config and is reloaded when it changes is perfectly okay (Factor III). The container does not get a state only because it has an exchangeable config.

You have solved the problem of not having to create a completely new instance of the software just to bind another service very well, since several services per token and even several tokens per store are supported. I think here lies (among other things) a great strength of eas and I don't want to question this part at all.

The fact that you don't even have to adapt the config-file(s) for different configurations, but can pass the same via a http parameter, is something that has rather confused the customer and me, because we have never seen anything like this before. It seems that you have interpreted the term stateless extraordinarily strictly here. A somewhat relaxed approach to this would not be a mistake in my opinion. The system would still be stateless and the other things like performance and separation of concerns (can be done in k8s as well as already said) won't be affected either. Of course, I agree with you that safety must not suffer as a result.

This is getting rather philosophical now.

If I can support you, let me know!

travisghansen commented 2 years ago

Yeah, I like the input and will do my best to accommodate! I'm generally aware of the complexity and relatively steep curve trying to understand how the project works, it's definitely unique and takes some time to 'digest' the design/approach.

The relaxed approach to stateless is server-side tokens which as you have pointed out can effectively be considered stateless when using k8s tooling to handle the state (ie: config as state). I think we can retain the security goals while eliminating the need for signing/encrypting the tokens.

kettenbach-it commented 2 years ago

That's great! We have a plan!

travisghansen commented 1 year ago

OK, sorry for the long wait! Life had a bit of a storm there for a few months but I finally have a chance to revisit this. I'm working in the next branch currently and have added several things:

  1. unencrypted and unsigned server-side config tokens are now allowed, simply set the EAS_ALLOW_PLAIN_SERVER_SIDE_TOKENS env var to 1
  2. as pre-requisite for client-side/user-agent token handling I've implemented pkce support for oauth / oidc plugins
  3. with pkce support in place it is now possible to support scenarios where eas can use oidc without the need for eas to have access to op (https://github.com/travisghansen/external-auth-server/issues/158)
  4. support yaml parsing everywhere that should be remotely relevant...if you find you cannot use yaml in some place where json is being used please let me know

https://github.com/travisghansen/external-auth-server/pull/165/files

kettenbach-it commented 1 year ago

Hi Travis! Happy new year!

I will try using unencrypted and unsigned server-side config tokens given as yaml as soon as you'll upload a docker image! It will greatly simplify my config!

travisghansen commented 1 year ago

You can try it now in the next images. I'm working on some more features for the client-side token retrieval flow as well and should have those wrapped up shortly.

kettenbach-it commented 1 year ago

Question: right now I have a store-token.json signed+encrypted in a k8s configMap mounted into the container(s) referenced in the content of EAS_CONFIG_TOKEN_STORES.

Am I right, that to switch to yaml and plain text, I have to:

Correct?

travisghansen commented 1 year ago

That env var isn't signed/encrypted/etc anyway (at least not as it relates to how it's handled internally). The app is currently expecting a valid json string. The new code allows it to be a valid yaml or json string.

let config_token_stores = process.env["EAS_CONFIG_TOKEN_STORES"];

if (config_token_stores) {
  config_token_stores = YAML.parse(config_token_stores);
} else {
  config_token_stores = {};
}
travisghansen commented 1 year ago

As far as the data inside the individual store you can:

  1. store each value/token as signed jwt + encrypted
  2. store each value/token as signed jwt
  3. store each value/token as valid json string
  4. store each value/token as valid yaml string

The latter 2 require setting EAS_ALLOW_PLAIN_SERVER_SIDE_TOKENS=1 or they will be rejected/fail.

kettenbach-it commented 1 year ago

Test succeeded!

I put my config as unencrypted/unsigned yaml in a k8s-configMap and eas read it and work correctly.

Now it will be much easier to test some new things like #158

travisghansen commented 1 year ago

Awesome!