GithubHelp home page GithubHelp logo

bserdar / took Goto Github PK

View Code? Open in Web Editor NEW
6.0 3.0 5.0 15.3 MB

OIDC Token manager CLI

License: Apache License 2.0

Go 95.77% Makefile 1.48% Makefile 2.58% Shell 0.18%
oidc-client oidc token-based-authentication jwt-authentication

took's Introduction

Command line token manager for OpenID Connect. Supports authorization flow and direct access (resource owner password credentials grant).

What does it do?

The main purpose of took is to maintain access tokens for API invocations and refresh them as needed. Once things are set up, you can run:

   took token myapi myuser

and it should either print out the token for myuser to call myapi, or take you through authentication and then print the token. If the token is expired and if there is a refresh token, it should get you a new token without any further interaction. Once you have a valid token, you can do:

   curl -H "Authorization: Bearer `took token myapi myuser`" http://myapi

or

   curl -H `took token -e myapi myuser`" http://myapi

Setup

Run

  took setup

If this is the first time took is run, this will ask you if you want to keep your configuration and tokens encrypted on disk. Once you decide whether you want to do that or not and enter your password if you do, the setup command will take you through the setup of an OIDC authorization server based on a known server profile.

  • Enter the name of the server profile corresponding to the server you want to authenticate with
  • Assign a name to this authentication configuration
  • You need to enter the following options to create a new authentication configuration:
    • client id
    • client secret (not required for public clients)
    • callback url (not required for password grants)
    • whether the client will use password grants or not

After entering all the information, you can run:

  took token confName userName

This will take you through authentication, and will print out your token.

Server profile information is stored in /etc/took.yaml. You can add more server profiles by editing that file.

Setup command is only useful to add a new authentication configuration based on a server profile. To add an authentication configuration, use the "took add" command.

Add new authentication server using the "add" command

If your authentication server is not listed as one of the known server profiles (defined in /etc/took.yaml), then you have to use this method.

  took add oidc <options>

Required options for oidc:

  • -n Name of the configuration. This is the 'myapi' parameter in the above examples
  • -c Client ID
  • -u Server URL, including domain, excluding protocol specific paths

The following sets up a configuration called 'prod' using OIDC authorization flow:

  took add oidc -n prod -c 12345 -s abcdef -u https://myserver/realms/myrealm -b http://callback

Then, when you run

  took token prod myuser

It will ask you to visit a URL. That URL will authenticate the user, and redirect to the callback URL, 'http://callback'. Copy this URL, and paste it to the command line, and it should print out a new token.

Direct Access Grants Flow

Took supports direct access grants. In this flow, took asks username and password, and sends them to the authentication server.

  took add oidc -n prod-direct -c 12345 -s abcdef -u https://myserver/realms/myrealm -f pwd

To use this, the authentication server must be configured to support direct access grants flow for this client.

Refresh Token Flow

Took supports using only refresh token grants. In this flow, took asks for a refresh token to send to the authentication server. This is commonly used in conjunction with offline refresh tokens.

  took add oidc -n prod-refresh -c 12345 -u https://myserver/realms/myrealm -f refresh

To use this, you must already have obtained a refresh token via some other means (usually from a web portal).

Multiple users

Took can maintain tokens for multiple users. If username is omitted, the last username will be used:

  took token myapi user1
  <token for user1>

  took token myapi
  <token for user1>

  took token myapi user2
  <token for user2>

  took token myapi
  <token for user2>

(In)security

Took can be run in one of three different security modes:

With Encrypted Configuration and Tokens

Took stores authentication server credentials, access tokens and refresh tokens in ~/.took.yaml. This file is created with owner read/write mode, so you might think this is secure enough. However, if you are not comfortable storing plaintext credentials on disk, you have the option to encrypt them with a password. When you run took for the first time, it'll take you through the steps to encrypt the configuration file. If however you did not want to encrypt then, and you want to encrypt now, run:

  took encrypt

This will ask you a password to encrypt the configuration file.

Once the configuration file is encrypted, you have to decrypt it to use it.

  took decrypt -t 10m

This will ask the encryption password, and if the password is correct, start the decryption server with an idle timeout of 10 minutes. The server will stop after 10 minutes of inactivity. If you do not specify -t flag, the default is 10 minutes. You may specify a 0 timeout, which will start a server that will never terminate until the current terminal session logs out.

Warning: Took does not store your password. If you forget it, there is no way to recover it.

With Plaintext Configuration and Tokens

When took asks you whether you want to encrypt the configuration or not, answer "N", and it will not ask you for a decryption password again. Authentication service credentials, access tokens, and refresh tokens will be stored as plaintext in ~/.took.yaml. If this makes you uncomfortable, you can run

  took encrypt

to encrypt your configuration file.

Insecure mode

Took requires you use https:// URLs for your servers, and it validates the server certificates. If you do not want to validate certificates, or if you want to call http:// server URLs, you have to run took as took-insecure. You can copy the took executable with that name, or create a symlink.

  ln -s took took-insecure

When run as took-insecure, you can use the -k flag to disable certificate validation. Also, took will not complain if you make calls to http:// servers.

Hack: Bypassing the server login page

It might be possible to describe the authentication form used by your server, so took can emulate what the browser does to authenticate a user. When you go to the login page with the browser, inspect the HTML page, and identify the forms and input fields. For instance, my server has the following form:

<form id="kc-form-login" class="form-horizontal" onsubmit="login.disabled = true; return true;" 
action="https://sso.someserver/auth/realms/myrealm/login-actions/authenticate?code=QWI1Bmwm0&amp;execution=bca7381b-65b-4196-936c-7f8941f121&amp;client_id=security-admin-console&amp;tab_id=uRub-YYUVuk" method="post">
  <div class="form-group">
    <div class="col-xs-12 col-sm-12 col-md-4 col-lg-3">
       <label for="username" class="control-label">Username or email</label>
    </div>
    <div class="col-xs-12 col-sm-12 col-md-8 col-lg-9">
      <input tabindex="1" id="username" class="form-control" name="username" value="" type="text" autofocus autocomplete="off" />
     </div>
  </div>
  <div class="form-group">
    <div class="col-xs-12 col-sm-12 col-md-4 col-lg-3">
       <label for="password" class="control-label">Password</label>
    </div>
    <div class="col-xs-12 col-sm-12 col-md-8 col-lg-9">
      <input tabindex="2" id="password" class="form-control" name="password" type="password" autocomplete="off" />
   </div>

This HTML page has a form with id="kc-form-login", containing two input fields: username and password. You can define this structure with the -F flag:

took add oidc -n myapi -s 123 -b http://callback -c abc -u https://myserver \
 -F '{"id":"kc-form-login","usernameField":"username","passwordField":"password","fields":[{"input":"username","prompt":"User name"},\
    {"input":"password","prompt":"Password","password":true}]}'

When a new token is requested, took will ask for the username and password fields, submit the HTML form, and get the tokens.

Code Organization

Took is designed as a generic front-end for multiple authentication protocols. Protocol implementations should be under proto/, and included in main.go. When included, protocol implementation registers its own command line handlers. Currently there is only OIDC.

These are the directories:

  • cmd/: Files under this package contain all the entry points for commands. There should be no dependency from this package to protocol implementations under proto/.
  • proto/: This package contains a protocol registry, and utility functions common to all protocols. In particular, HTTP utilities in this package should be used for all HTTP calls, because they look at the secure flag and turn off certificate validation
  • proto/oidc: This is the OIDC implementation. When included, this implementation registers command line commands, and registers itself to the registry.
    • cfg.go: Contains the ServerProfile struct, and the code to merge default configs to user configs
    • cmd.go: Contains command line commands. The setup wizard is also here.
    • htmlform.go: Contains the parsing code that reads a login web page,parses login fields, and asks those fields in the command line.
    • protocol.go: Contains the implementation of 'token' command
    • refresh.go: Token refresh logic
    • serverinfo.go: Contains the code to get auth server information (part of oidc spec)
    • validate.go: Contains token validation code
  • crypta/: This package deals with encrypting/decrypting the tokens file.
    • crypta.go: Contains the encryption/decryption implementation.
    • proto.go: Defines a simple rpc protocol
    • rpc/: This package contains an rpc server and rpc client When took detects that the token file is encrypted, it asks for the user password, and starts another instance of took with an RPC server listening to a domain socket under $HOME. This RPC server contains all the ecnryption/decryption functions unlocked using the user's password.

took's People

Contributors

bserdar avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

took's Issues

URL for authentication not working when using an added server configuration

@bserdar Not sure if this is an error on my part, but I'll explain.
When I add a token using the following command:
Not sure if I should put the client secret and id here, but I have them if needed.
took add oidc -n testing-prod-2 -s -c -u https://sso.redhat.com/auth/realms/3scale/ -b https://api.access.redhat.com/management
[Note: I've also eliminated the -b and produced the same results]
And then I run took token for my account (credentials also proved upon request), I get here:
"Go to this URL to authenticate "
When I try to visit that url, I get an invalid parameter error:
https://sso.redhat.com/auth/realms/3scale/protocol/openid-connect/auth?access_type=online&client_id=f119df48&redirect_uri=https%3A%2F%2Fapi.access.redhat.com%2Fmanagement&response_type=code&scope=openid&state=eqo_xX3VBkWhAvI1uy9r3INkK8F7aNMf_XepVujrY-4%3D

strip leading/trailing spaces during took setup

When running took setup if a extra space is submitted at end of value then the yaml can be populated with a value that is not expected for later token operations,
from the yaml:

    type: bearer
    username: rhn-support-shughes

'hive ':
type: oidc-auth
cfg:
insecure: false
profile: rh

Notice the app "hive" has an extra space created.

When trying to create a token we have the following:

[shughes@workerbee ~]$ took token hive
Cannot find hive

If I escape the string on command line then it works but think we need to validate incoming values against leading and trailing spaces. If I remove the space in yaml then it also works.

Cannot get token for user with certain special characters

When trying to produce tokens for the following username with certain special characters, I get an error
Ex.
]$ took token dev SLAC_Users_With_#!&@'[]{.}`+-_?_3
-bash: !: event not found
The user is an authenticated user in the app

FWIW the tool worked for the following user with special characters:
took token dev #B@rry&!@!en

always get error 'Invalid parameter: redirect_uri' when using oidc and the '-F' option does not work

tried using took to authenticate with our oidc server and added oidc as in the readme (btw: you forgot to explain option 's' there, I guessed it's the secret).

when I then request a token with the command:
took token ats-dev [email protected]

where '[email protected]' is my username on the oidc it display a url where I should authenticate. When I open this url I always get an error from the oidc server saying 'invalid parameter: redirect_uri' no matter if I use the -b parameter or not

I also tried bypassing the login server because the form looks exactly like yours but took doesn't ask for username & password when using above command, it again wants me to visit the form and the oidc again says "invalid parameter: redirect_uri"

Any way to overcome this?

Took Insecure Mode Is Insecure

This one is a bit obvious. Insecure mode disables certificate chain and host name verification which is dangerous / a bad habit.

I'm guessing this is used for development or testing, but it seems like a poor practice and could lead to insecure setups. Tokens should never be sent to untrusted TLS endpoints. Instead, if a self-signed cert needs to be used, that should be installed in the operating systems cert store.

Could this be put into a development only build, i.e. require a specific flag to build this into the binary? Ideally this would not be a default. If not, could we print an annoying warning every time a took request is sent to an insecure endpoint, else prompt the user again? ("Are you absolutely sure you want to transmit tokens/credentials to an untrusted endpoint? An attacker could man-in-the-middle this connection and steal tokens/usernames/passwords" -- something like that)

Support for self signed certificates

Currently when the oidc server you're authenticating against is using a self signed certificate, you'll get the error: x509: certificate signed by unknown authority

Steps to reproduce

took add oidc-direct-access -n <name> -c <clientID> -s <clientSecret> -u <oidcServer>
# ...
took token <name>
# Get <oidcServer>: x509: certificate signed by unknown authority

Took Support for HTTP

Took supports HTTP endpoints / OIDC flows. Thus, if someone configures took with an endpoint or callback url using http, there is a possibility of secrets, tokens, and username/passwords being transmitted in plain-text.

The spec seems to suggest (I am unclear if it requires) TLS. See https://tools.ietf.org/html/rfc6749 10.5:

   The transmission of authorization codes SHOULD be made over a secure
   channel, and the client SHOULD require the use of TLS with its
   redirection URI if the URI identifies a network resource.  Since
   authorization codes are transmitted via user-agent redirections, they
   could potentially be disclosed through user-agent history and HTTP
   referrer headers.

Detecting and blocking non-HTTPS URLs seems like a great way to prevent users from shooting themselves in the foot.

Wireshark of plaintext password grant request:

HTML Form URL Encoded: application/x-www-form-urlencoded
    Form item: "client_id" = "<censored>"
    Form item: "client_secret" = "<censored>"
    Form item: "grant_type" = "password"
    Form item: "password" = "<censored>"
    Form item: "scope" = "openid"
    Form item: "username" = "<censored>"

Token Refresh Failure

Attempting to test this with a okta.com test setup. Have this setup such that the first token grab works correctly. The second invocation, when it tries to do a refresh, I get the following:

took token okla <user>

DEBU[0000] There is an access token, validating         
DEBU[0000] Validation error: <nil>
FATA[0005] Cannot get server info for https://<instance>.oktapreview.com/: invalid character '<' looking for beginning of value

Looks like it was pulling down https://<instance>.oktapreview.com/, instead of the correct URL that returns the JWKS, which would be https://<instance>.oktapreview.com/oauth2/default/v1/keys

So a few potential issues:

  1. I'm doing something completely wrong, entirely possible.
  2. took needs to query for the JWKS url via openid connect discovery.
  3. took needs to support a config option to point to the correct JWKS location.

I tried bypassing the issue by hardcoding the correct URL, and get a new error:

took -v token okla <user>
DEBU[0000] There is an access token, validating         
DEBU[0000] Validation error: <nil>                      
About to get: https://<instance>.oktapreview.com/oauth2/default/v1/keys
FATA[0000] Cannot get server info for https://<instance>.oktapreview.com/: asn1: syntax error: sequence truncated

Here's my slightly modified .took.yaml:

remotes:
  okla:
    type: oidc-auth
    cfg:
      additionalscopes: []
      authapi: oauth2/default/v1/authorize
      callbackurl: https://www.example.com
      clientid: <client id>
      clientsecret: <client secret>
      form: null
      insecure: false
      passwordgrant: false
      profile: ""
      tokenapi: oauth2/default/v1/token
      url: https://<instance>.oktapreview.com/
    data:
      last: <user>
      tokens:
      - username: <user>
        accesstoken: <access token>
        refreshtoken: ""
        type: Bearer

Took -p false doesn not set the passwordgrant: false in .took.yml

took-insecure add oidc -c <client id> -k -n <name> -s <secret> -b <callback url> -u <server_url> -p false

Here is the created profile -

  <name>:
    type: oidc-auth
    cfg:
      url: <url>
      tokenapi: ""
      authapi: ""
      form: null
      insecure: true
      profile: ""
      clientid: <client id>
      clientsecret: <secret>
      passwordgrant: true
      callbackurl: <callback url>

PKCE Support

PKCE support may prove beneficial. I'm unclear of the likelihood of an attacker registering or taking over a URI on Linux/Windows/Mac, but it seems possible.

Here's more detail:
https://openid.net/2015/05/26/enhancing-oauth-security-for-mobile-applications-with-pkse/

Here's an example:

Firefox does support opening custom, system register URIs. (man uri). As an example, we can open man:ls in Firefox and it will prompt and ask if we want to open that URI in Help.

So took theoretically could support custom URIs on the system. I.e. if I set my callbackurl to man:ls (just an example to send the token to a system app), the took flow will send me to the authorization server, verify, then redirect me back to help. If I allow firefox to open the system application, I get this:

Document Not Found
The URI ‘man:?code=<code>&state=ivhvKDSmaIRXjo222sZU-_H3utO0zJPBi8IjFwyt7tc%3D’ does not point to a valid page.

which is pretty neat. Thus, the attack that PKCE defends against is if an attacker somehow (let's say via a malicious app in a repository) registers for the same URI. I'd need to check, but I believe it could potentially overwrite others set and a malicious app could steal the code.

Of course, if you have a malicious app on the system, it could most likely just read .took.yaml and you certainly have other, bigger issues. This may be a good step for defense in depth though.

Plaintext Token Storage in .took.yaml

The .took.yaml configuration file stores secret and sensitive tokens for required functionality. The last access token is written to disk and stored. I am unsure why -- is this in order to support refresh tokens?

This issue could be a bit controversial as you can argue that as long as the user directory is locked down correctly (good job in 989ea1d, was going to mention that one), that should be enough and if anyone else gets access, some other vulnerability had to be exploited / the user may have made a mistake in another way. You could argue that the user should encrypt their own hard-drive, etc. Examples of this would be SSH private keys (although they do support encryption, it is not required).

Still, this design dramatically increases the attack surface and potential for secure token leakage. One simple example involves other CLI tools that store tokens to the hard disk. There are plenty of examples of users checking in these tokens (i.e. AWS tokens) to github. Can we do better?

Here's a few ideas:

  1. Store tokens in environment variables. Tokens/functionality would only be valid for the current session. You log out, you'll need to go through auth again to get a new token. This seems like a pretty good improvement.
  2. Encrypt the tokens and client secret. This could get complicated -- I don't know if golang has some form of cross-platform keystore. I suppose we could scrypt/AES the data. Would make the usability a little worse, but potentially help with security. Would require the user chooses a good password and we salt/hash use a good derivation function, etc.
  3. Use OS specific key store. Very OS specific, probably hard to implement in a good, cross-platform way. Unclear as I'm not a golang expert.

Any other thoughts?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.