1135 lines
37 KiB
Org Mode
1135 lines
37 KiB
Org Mode
# -*- mode: org -*-
|
|
|
|
|
|
Archived entries from file /Users/yaesposi/.deft/Cisco.org.gpg
|
|
|
|
|
|
* [#A] OAuth2 Provider
|
|
:PROPERTIES:
|
|
:ARCHIVE_TIME: 2019-01-09 Wed 10:52
|
|
:ARCHIVE_FILE: ~/.deft/Cisco.org.gpg
|
|
:ARCHIVE_OLPATH: Epics
|
|
:ARCHIVE_CATEGORY: Cisco.org
|
|
:END:
|
|
:SCHEDULE: <2018-01-11 Thu>
|
|
:END:
|
|
** Workflow
|
|
|
|
Authorize, Approve, Refuse, Token
|
|
|
|
Threatgrid portal would like access to the (enrich API, response API, ...
|
|
Private CTIA Public CTIA) -> scopes
|
|
|
|
users ses yes, he approves, POST approve, get back auth code, POST that to token
|
|
|
|
*** =/authorize=
|
|
|
|
Ask the user to authorize an App to access sub services API:
|
|
|
|
Grants: https://alexbilbie.com/guide-to-oauth-2-grants/
|
|
|
|
We'll never support grant type password or client_credentials.
|
|
We'll support: =authorization_code= and =refresh_token=.
|
|
|
|
**** Scopes
|
|
|
|
Format:
|
|
- =<high-level-api>(:<access-type>)?=
|
|
- =<high-level-api>(/subset)*(:<access-type>)?=
|
|
|
|
|
|
Examples:
|
|
|
|
- openid
|
|
- email
|
|
- collect
|
|
- collect:read
|
|
- iroh-int/observe (read)
|
|
- enrich:write
|
|
- global-intel
|
|
|
|
Meaningful:
|
|
|
|
- deliberations
|
|
- enrichment
|
|
- private-intel (read/write)
|
|
- global-intel (read/write)
|
|
- references (read)
|
|
- response (write)
|
|
- deliberate, describe, observe
|
|
|
|
.
|
|
|
|
*** =/approve=
|
|
|
|
The User can approve access to an App.
|
|
The App get back a Auth Code.
|
|
|
|
*** =/refuse=
|
|
|
|
The User can refuse or revoke access to an App.
|
|
|
|
*** =/token=
|
|
|
|
The App can ask to refresh tokens.
|
|
Tokens returned must be "short" lifetime JWT.
|
|
** OAuth2 in IROH-Auth Spec RFC second pass
|
|
|
|
*** Vocabulary
|
|
|
|
- Roles
|
|
+ resource owner: has the ability to grant access to a protected resource
|
|
+ resource server: host of the protected resources
|
|
+ client: app making requests to resources
|
|
+ authorization server: server issuing access token to the client
|
|
- Authorization Grant: creds representing resource owner authorization
|
|
- Authorization Code: client redirect resource owner to auth server which in
|
|
turn redirect the resource owner back to the client with the Auth code
|
|
- Access Token: creds used to access protected resource
|
|
- Refresh Token: creds to obtain access tokens
|
|
- Redirects (generally 302, but up to server)
|
|
|
|
*** Client Registration
|
|
|
|
- not really part of the spec. It is up to us to find a way to add a list of
|
|
authorized clients, accepted redirection URIs, etc...
|
|
|
|
- Client Type
|
|
+ confidential: private server
|
|
+ public: executing itself on some device used by the resource owner
|
|
- Client Identifier
|
|
+ provided by the authorization server to the client
|
|
+ It is not secret
|
|
+ Unique
|
|
- Client password
|
|
can be used via Basic Auth, or in req params: ~client_id=...&client_secret=...~
|
|
Auth server MUST protect agains brute force attacks
|
|
#+BEGIN_SRC HTTP
|
|
POST /token HTTP/1.1
|
|
Host: server.example.com
|
|
Content-Type: application/x-www.form-urlencoded
|
|
|
|
grant_type=refresh_token&refresh=...&client_id=...&client_secret=...
|
|
#+END_SRC
|
|
|
|
OAuth2 doesn't support unregistered clients
|
|
|
|
*** Protocol Endpoints
|
|
|
|
Two auth server endpoints:
|
|
|
|
- Authorization endpoint: used by the client to obtain authorization from the
|
|
resource owner via redirection
|
|
- Token endpoint: used by the client to exchange an authorization grant for an
|
|
access token, typically with client auth.
|
|
|
|
Client endpoint:
|
|
|
|
- Redirection endpoint: used by the auth server to return responses containing
|
|
auth creds to the client via the resource owner user-agent
|
|
|
|
**** Authorization Endpoint
|
|
|
|
- MUST ignore unrecognized parameters
|
|
- MUST not accept repeated params
|
|
|
|
Client must inform the client of the =response_type=:
|
|
- must be set to ~code~ for requesting auth code
|
|
- must be set to ~token~ for implicit grant
|
|
|
|
**** Redirection Endpoint
|
|
|
|
After completing its interaction with the resource owner, the auth server
|
|
directs the resource owner's user-agent to the client.
|
|
|
|
The redirection URI MUST NOT include a fragment component.
|
|
|
|
Registration requirement: the auth server MUST require the public and
|
|
confidential clients to register their redirection endpoint.
|
|
|
|
For our usage: the end point must use TLS (https) and also there should be a
|
|
single URI per client.
|
|
|
|
If an auth fails due to missing, invalid or mismatching redirection URI.
|
|
The Auth Server should inform the resource owner without redirection.
|
|
|
|
**** Scope
|
|
|
|
An unordered list of space-delimited, case-sensitive strings.
|
|
|
|
The scopes are defined by the auth-server.
|
|
|
|
If requested scopes by the client are different by the ones granted by the auth
|
|
server. The auth server must return the granted scopes as a response parameter.
|
|
|
|
*** Obtaining Authorization
|
|
|
|
4 different Grants:
|
|
|
|
- authorization code
|
|
- implicit (no)
|
|
- resource owner creds (no)
|
|
- client creds (no user, direct client to IROH)
|
|
|
|
- an extension for more grant types.
|
|
|
|
**** Code Grant
|
|
|
|
Used to obtain both access token & refresh tokens.
|
|
Optimized for confidential clients.
|
|
|
|
1. client construct the request URI with the following params:
|
|
+ ~response_type~ REQUIRED. Must be ~code~.
|
|
+ ~client_id~
|
|
+ ~redirect_uri~ (OPTIONAL)
|
|
+ ~scope~ (OPTIONAL)
|
|
+ ~state~ (RECOMMENDED)
|
|
Example:
|
|
#+BEGIN_SRC
|
|
GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz
|
|
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
|
|
Host: server.example.co
|
|
#+END_SRC
|
|
2. Authorization Response. If auth is granted, then the auth server issues an
|
|
authorization code and delivers it to the client by adding the following
|
|
parameters to the query component of the redirection URI using the
|
|
"application/x-www-form-urlencoded" format.
|
|
+ ~code~ REQUIRED. auth code generated. Must expire shortly (10 min
|
|
recommended) and shouldn't be used more than once. If the code is used more
|
|
than once, should revoke all tokens previously issued based on that
|
|
authorization code.
|
|
+ ~state~ REQUIRED. Same value as the client provided.
|
|
3. Client makes a request to the token endpoint. Should contains the following params:
|
|
+ ~grant_type~ REQUIRED. must equal to ~authorization_code~.
|
|
+ ~code~ REQUIRED. The auth code received.
|
|
+ ~redirect_uri~ REQUIRED. Same as in the authorization request.
|
|
+ ~client_id~ REQUIRED. If the client is not authenticating with the auth server.
|
|
Example:
|
|
#+BEGIN_SRC
|
|
POST /token HTTP/1.1
|
|
Host: server.example.com
|
|
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
|
|
Content-Type: application/x-www-form-urlencoded
|
|
|
|
grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
|
|
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
|
|
#+END_SRC
|
|
4. Access Token Response.
|
|
If the access token is valid and authorized, the auth server issues an access
|
|
token and optional refresh token.
|
|
Example:
|
|
#+BEGIN_SRC
|
|
HTTP/1.1 200 OK
|
|
Content-Type: application/json;charset=UTF-8
|
|
Cache-Control: no-store
|
|
Pragma: no-cache
|
|
|
|
{
|
|
"access_token":"2YotnFZFEjr1zCsicMWpAA",
|
|
"token_type":"example",
|
|
"expires_in":3600,
|
|
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
|
|
"example_parameter":"example_value"
|
|
}
|
|
#+END_SRC
|
|
** OAuth2 Provider Epic
|
|
|
|
https://github.com/threatgrid/iroh/issues/1349
|
|
|
|
*** Functional Spec
|
|
|
|
We want 3rd party to be able to use our APIs (IROH + AMP Global Intel) by making
|
|
an OAuth2 Provider.
|
|
|
|
*** Tasks
|
|
|
|
See next sections for technical details.
|
|
|
|
=[#A]=, =[#B]=, =[#C]= are priority order to be able to test the OAuth system
|
|
before finishing all the tasks:
|
|
|
|
- =[#A]= Be able to provide keys to access our API
|
|
- =[#B]= Be able to migrate Visibility to OAuth2
|
|
- =[#C]= Support revocations, restrictions, etc...
|
|
|
|
Task without any set priority are lower priority.
|
|
|
|
*[[client-db][Client DB]]*:
|
|
|
|
- [ ] Write SQL store
|
|
- [ ] =[#C]= Write a service to manage clients
|
|
- [ ] =[#C]= Write routes restricted to IROH-Admins to manage OAuth2 clients
|
|
- [ ] =[#C]= POST =/clients= (the server sets the ~:id~ and returns the ~:password~
|
|
in clear text, in the DB the password should be encrypted).
|
|
- [ ] =[#C]= GET =/clients= (the ~:password~ field should be obfuscated)
|
|
- [ ] =[#C]= GET =/clients/:id= (the ~:password~ field should be obfuscated)
|
|
- [ ] =[#C]= PUT =/clients/:id= to revoke a client access (the ~:password~ field should be obfuscated)
|
|
- [ ] =[#C]= DELETE =/clients/:id= to revoke a client access (the ~:password~ field should be obfuscated)
|
|
|
|
*[[token-db][Authorization DB]]*:
|
|
|
|
- [ ] =[#B]= Write a service to manage "apps" for a resource owner
|
|
- [ ] =[#B]= Write routes to manage "apps" for a resource owner.
|
|
The =refresh-token= field should always be hidden or obfuscated.
|
|
- [ ] =[#B]= GET =/oauth/apps= returns the list of authorized apps
|
|
- [ ] =[#B]= GET =/oauth/apps/:app-id= return the authorized app informations
|
|
- [ ] =[#B]= DELETE =/oauth/apps/:app-id= remove the authorized app, further refresh
|
|
token queries should fail.
|
|
|
|
*[[user-db][User DB]]*:
|
|
|
|
- [ ] =[#C]= Write a Service to manage Users with the following methods:
|
|
- [ ] =[#C]= Create an user and generate its id. To be used for new login of still unkown user.
|
|
- [ ] =[#C]= search an user by internal id or 3rd party User id (typically AMP user id)
|
|
- [ ] =[#C]= revoke an user
|
|
- [ ] =[#C]= change scopes of an user
|
|
- [ ] =[#C]= Write routes restricted to IROH-Admins to manage Users
|
|
- [ ] =[#C]= GET =/users= returns a list of users
|
|
- [ ] =[#C]= GET =/users/:user-id= returns the specific user infos
|
|
- [ ] =[#C]= PUT =/users/:user-id= update an user, either revoke access or change scope
|
|
|
|
*[[routes][OAUTH2 Routes]]*:
|
|
|
|
- [ ] =/authorize= endpoint (should be behing the classic JWT protection)
|
|
- [ ] =[#A]= Support for authorization code grant
|
|
- [ ] =[#B]= Support for client creds grant
|
|
- [ ] Support implicit grant (close the current workflow with /login)
|
|
- [ ] =[#A]= =/token= endpoint
|
|
|
|
*[[middleware][Protected Resource Middleware]]*:
|
|
|
|
- [ ] =[#A]= Update the JWT middleware to only accept access-tokens
|
|
- [ ] =[#B]= Add a :scopes to compojure-api routes to filter access to JWT granting those scopes
|
|
- [ ] =[#B]= Update all routes to add a :scopes (cf. [[scopes][scopes]])
|
|
|
|
*[[frontend][Frontend]]*:
|
|
|
|
- [ ] =[#C]= Update the frontend to use the new OAuth2 routes.
|
|
- [ ] Create a dahsboard to help an user to revoke apps
|
|
- [ ] Create an IROH-Admin only dahsboard to manage clients (create, revoke, etc...)
|
|
- [ ] Create an IROH-Admin only dahsboard to manage users (revoke, etc...)
|
|
|
|
*[[doc][Documentation]]*:
|
|
|
|
- [ ] Documentation for making trusted daemons (authorization code grant)
|
|
- [ ] Documentation for frontend dev (implicit grant)
|
|
- [ ] Documentation for scripts (client creds grant)
|
|
|
|
*** Technical Spec
|
|
|
|
Write an OAuth2 provider.
|
|
Specificity of our OAuth2 provider.
|
|
|
|
- codes, access-tokens & refresh-tokens should be JWT.
|
|
Because JWT expires, and we can control how often we check their status
|
|
(revoked for example) server side.
|
|
|
|
Supported Grants (by priority order):
|
|
|
|
- ~authorization code~ grant
|
|
- ~implicit~ grant (for Visibility/iroh-ui) (similar to today =/login=)
|
|
- ~client creds~ grant
|
|
|
|
|
|
**** Client DB
|
|
|
|
<<client-db>>
|
|
|
|
We need to be able to add new OAuth2 "clients".
|
|
And we must be able to revoke a client.
|
|
So we need to add another store for clients.
|
|
Actually we shouldn't store those informations in ES.
|
|
We need to write an SQL backed CRUD store.
|
|
More specifically we'll use postgresql.
|
|
There should be another web-service exposing routes only for IROH admins.
|
|
|
|
*Question*: How should we keep client password?
|
|
|
|
*Proposal*: Present password in cleartext to the "creator" only once but save
|
|
the password using bcrypt.
|
|
|
|
A client MUST have a ~client-type~, a ~client-id~ and a ~client-password~.
|
|
|
|
Proposed internal representation:
|
|
|
|
#+BEGIN_SRC clojure
|
|
(s/defschema CRUDMetas
|
|
{:created-at s/DateTime
|
|
(s/optional-key :updated-at) s/DateTime
|
|
(s/optional-key :activated-at) s/DateTime
|
|
(s/optional-key :approved-at) s/DateTime
|
|
(s/optional-key :enabled-at) s/DateTime
|
|
(s/optional-key :blocked-at) s/DateTime})
|
|
(def Scope s/Str) ;; MUST NOT contain any space see the Scope section
|
|
(def URI s/Str) ;; shouldn't contains any fragment '#...', space or '..'
|
|
;; HIGHLY RECOMMENDED should be HTTPS
|
|
(defschema Client
|
|
(st/merge {:client-type (s/enum :confidential :public)
|
|
:grants #{(s/enum :auth-code :implicit :client-creds)}
|
|
:redirects #{URI}
|
|
:id s/Str
|
|
:password s/Str
|
|
:name s/Str
|
|
:scopes #{Scope}
|
|
:approved? s/Bool
|
|
:enabled? s/Bool}
|
|
CRUDMetas)
|
|
#+END_SRC
|
|
|
|
For example:
|
|
|
|
#+BEGIN_SRC clojure
|
|
(def threatgrid-client
|
|
{:client-type :confidential
|
|
:grants :auth-code
|
|
:redirects ["https://threatgrid.cisco.com/"]
|
|
:id "5fb04b9a-faad-11e7-8c3f-9a214cf093ae"
|
|
:password "70D3C005435E0F1C75A622C6C5AE92DB395A9E3140DE6980AC733AC9316AC0AE"
|
|
:name "ThreatGrid"
|
|
:scopes ["response" "enrich" "iroh-collect" "ui-settings" "ctia"]
|
|
:approved? true
|
|
:enabled? true
|
|
:created-at "2018-01-20T10:04:33"
|
|
:updated-at "2018-01-20T10:04:33"
|
|
:enabled-at "2018-01-20T10:04:33"
|
|
:activated-at "2018-01-20T10:04:33"
|
|
:approved-at "2018-01-20T10:04:33"})
|
|
#+END_SRC
|
|
|
|
**** User DB
|
|
|
|
<<user-db>>
|
|
|
|
We need to have an OAuth2 oriented Internal User DB.
|
|
For example to be able to change the scopes or simplly to revoke access to some
|
|
users.
|
|
|
|
Even if in the future we'll need a more complex User DB, for now we only need to
|
|
keep track of relation between an user-id for some 3rd party and an internal
|
|
user-id.
|
|
|
|
#+BEGIN_SRC clojure
|
|
(s/def IdPMapping
|
|
(st/merge
|
|
{:idp (s/enum :amp :threatgrid)
|
|
:user-id s/Str
|
|
:org-id s/Str}
|
|
{s/Any s/Any}))
|
|
(s/def UserClaims
|
|
{"cisco.com/iroh/user/id" s/Str
|
|
"cisco.com/iroh/user/name" s/Str
|
|
"cisco.com/iroh/org/id" s/Str
|
|
"cisco.com/iroh/org/name" s/Str
|
|
"cisco.com/iroh/roles" [s/Str]
|
|
;; see IROH-Auth JWT fields (remove all time-related ones)
|
|
})
|
|
(s/def User
|
|
(st/merge
|
|
{:enabled? s/Bool
|
|
:external-infos #{IdPMapping}}
|
|
PartialJWT
|
|
CRUDMetas))
|
|
#+END_SRC
|
|
|
|
**** Authorization DB / App DB
|
|
|
|
<<token-db>>
|
|
|
|
A refresh token are credentials representing the access granted by an user to
|
|
some client.
|
|
|
|
☞ Refresh tokens are typically long-lasting credentials used to request additional
|
|
access tokens, the refresh token is bound to the client to which it was issued.
|
|
(See https://tools.ietf.org/html/rfc6749#section-6)
|
|
|
|
So we should provide a mean for an user to revoke those accesses.
|
|
Remark the db should be populated via the =/authorize= endpoint.
|
|
|
|
There shouldn't be any way for an user to create or update an app, only delete.
|
|
|
|
Example:
|
|
|
|
#+BEGIN_SRC clojure
|
|
(s/defschema OAuthApp
|
|
{:id s/Str
|
|
:refresh-token s/Str
|
|
:client-id s/Str
|
|
(s/optional-key :client-name) s/Str
|
|
:authorized-date s/DateTime
|
|
:scopes #{Scope}})
|
|
#+END_SRC
|
|
|
|
**** OAUTH2 Routes
|
|
|
|
<<routes>>
|
|
|
|
We need to support the Code Grant so here is details about the workflow.
|
|
Note a lot of important details are in the RFC about how to handle those routes
|
|
securely.
|
|
Also, examples were modified so that the tokens are JWT.
|
|
|
|
***** Authorization Code Grant Worflow
|
|
|
|
The full details can be seen in the RFC: https://tools.ietf.org/html/rfc6749#section-4.1
|
|
|
|
Here some details were changed to adapt to our specifities (like using JWT).
|
|
|
|
The workflow is used to obtain both *access token* & *refresh tokens*.
|
|
Optimized for confidential clients.
|
|
|
|
1. client construct the request URI and redirect the
|
|
resource owner's user-agent to =/authorize= with the following params:
|
|
+ ~response_type~ REQUIRED. Must be ~code~.
|
|
+ ~client_id~
|
|
+ ~redirect_uri~ (OPTIONAL)
|
|
+ ~scope~ (OPTIONAL)
|
|
+ ~state~ (RECOMMENDED)
|
|
Example:
|
|
#+BEGIN_SRC
|
|
GET /authorize?response_type=code
|
|
&client_id=5fb04b9a-faad-11e7-8c3f-9a214cf093ae
|
|
&state=xyz
|
|
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
|
|
Host: server.example.co
|
|
#+END_SRC
|
|
2. Authorization Response. If auth is granted, then the auth server issues an
|
|
authorization code and delivers it to the client by adding the following
|
|
parameters to the query component of the redirection URI using the
|
|
"application/x-www-form-urlencoded" format.
|
|
+ ~code~ REQUIRED. auth code generated. Must expire shortly (10 min
|
|
recommended) and shouldn't be used more than once. If the code is used more
|
|
than once, should revoke all tokens previously issued based on that
|
|
authorization code.
|
|
+ ~state~ REQUIRED. Same value as the client provided.
|
|
3. Client makes a request to the token endpoint. Should contains the following params:
|
|
+ ~grant_type~ REQUIRED. must equal to ~authorization_code~.
|
|
+ ~code~ REQUIRED. The auth code received.
|
|
+ ~redirect_uri~ REQUIRED. Same as in the authorization request.
|
|
+ ~client_id~ REQUIRED. If the client is not authenticating with the auth server.
|
|
Example:
|
|
#+BEGIN_SRC
|
|
POST /token HTTP/1.1
|
|
Host: server.example.com
|
|
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
|
|
Content-Type: application/x-www-form-urlencoded
|
|
|
|
grant_type=authorization_code
|
|
&code=eyJh...SplxlOBeZQQYbYS6WxSbIA
|
|
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
|
|
#+END_SRC
|
|
4. Access Token Response.
|
|
If the access token is valid and authorized, the auth server issues an access
|
|
token and optional refresh token.
|
|
Example:
|
|
#+BEGIN_SRC
|
|
HTTP/1.1 200 OK
|
|
Content-Type: application/json;charset=UTF-8
|
|
Cache-Control: no-store
|
|
Pragma: no-cache
|
|
|
|
{
|
|
"access_token":"eyJh...2YotnFZFEjr1zCsicMWpAA",
|
|
"token_type":"example",
|
|
"expires_in":3600,
|
|
"refresh_token":"eyJh..tGzv3JOkF0XG5Qx2TlKWIA",
|
|
"example_parameter":"example_value"
|
|
}
|
|
#+END_SRC
|
|
|
|
☞ *Refres Token*: for this PR, refreshing a token shouldn't create another
|
|
=refresh-token=. That way our client won't need to update their credentials. But
|
|
that is an option to keep in mind.
|
|
|
|
***** IROH Specificities
|
|
|
|
In IROH we don't have internal user login/password.
|
|
But we have a =/login= route that redirect with a =jwt= containing credentials.
|
|
|
|
So if we reach =/authorize= with such a valid =jwt= then we could continue by
|
|
using the user creds, if on the other hand we don't have such =jwt= the
|
|
=/authorize= should make a "double" redirection.
|
|
One to =AMP= (or =TG= in the future) login page and then the user-agent should
|
|
be redirected with the same URI but this time, a JWT should be provided in the
|
|
Headers if possible.
|
|
And then, if the =authorize= is validated, it will redirect to the client.
|
|
|
|
***** Tokens/Code format
|
|
|
|
One easy way to handle the lifetime of tokens and code is to use JWT.
|
|
|
|
During the code grant, the call to =/authorize= should redirect the user to some
|
|
page where the user interact and grant the access to the client. The output of
|
|
this call, the user is redirected via its user-agent with a short-living "code".
|
|
|
|
#+BEGIN_SRC clojure
|
|
(s/defschema IROHJWT ...) ;; see iroh-auth current JWT format
|
|
(s/defschema OAuthAuthorizeCode
|
|
(st/merge IROHJWT
|
|
{:kind (s/enum :grant)
|
|
:grant-type (s/enum :authorization-code)
|
|
:client-id s/Str}))
|
|
#+END_SRC
|
|
|
|
Such JWT MUST be REFUSED if used directly to access a protected resource.
|
|
|
|
Such =OAuthAuthorizedCode= should only be used to access the =/token= endpoint.
|
|
The =/token= endpoint will in turn returns two JWT.
|
|
The /access-token/ and the /refresh-token/.
|
|
|
|
- /access-tokens/ are the only token which grant access to protected resources.
|
|
- /refresh-tokens/ can be used to get /access-tokens/ without resource-owner interaction.
|
|
|
|
Proposed schemas:
|
|
|
|
#+BEGIN_SRC clojure
|
|
(s/defschema OAuthAccessToken
|
|
(st/merge IROHJWT
|
|
{:kind (s/enum :access-token)
|
|
:user-id s/Str
|
|
:client-id s/Str
|
|
:scopes #{Scope}))
|
|
|
|
(s/defschema OAuthRefreshToken
|
|
(st/merge IROHJWT
|
|
{:kind (s/enum :refresh-token)
|
|
:app-id s/Str
|
|
:user-id s/Str
|
|
:client-id s/Str
|
|
:scopes #{Scope}}))
|
|
#+END_SRC
|
|
|
|
So when asking for a refresh token we could check the statuses of the user,
|
|
the client and the refresh-token.
|
|
|
|
**** Protected Resource Middleware
|
|
|
|
<<middleware>>
|
|
|
|
The current JWT middleware should be modified to only accept =:access-token= JWT
|
|
for protected resources routes.
|
|
|
|
There should more specific JWT system to handle the =/authorize= and =/token= routes.
|
|
|
|
**** Frontend
|
|
|
|
<<frontend>>
|
|
|
|
***** Become OAuth2 compliant
|
|
|
|
Visibility/IROH-UI should also become a "client".
|
|
And thus some minor changes should be done.
|
|
Instead of going to =/login= Visibility should access =/authorize= with the
|
|
following parameters:
|
|
|
|
- ~response_type~ value MUST be ~token~
|
|
- ~client_id~ the client id for visibility
|
|
- ~redirect_uri~ (as it is today)
|
|
- ~scope~ (the list of scopes Visibility need to access)
|
|
- ~state~ (the inner state of the app, the parameter as another name today)
|
|
|
|
The response format will also be slightly changed.
|
|
The fragment of the URL will contain:
|
|
|
|
- ~access_token~ (previously was ~jwt~)
|
|
- ~token_type~ should be ~bearer~
|
|
- ~scope~ OPTIONAL if identical to the scope requested by the client; REQUIRED otherwise
|
|
- ~state~ the state provided in the request
|
|
|
|
It is up to the server to make the same workflow than with the previous =/login=
|
|
|
|
***** App Dahsboard
|
|
|
|
Make an app dashboard to help users manage their authorized apps.
|
|
It should be inspired by the features of the API.
|
|
Only ability to revoke/delete some app.
|
|
|
|
***** IROH-Admin only dahsboard to manage clients
|
|
|
|
Have a dashboard for IROH-Admin only to manage clients.
|
|
Typically block accesses to a client but also change the client properties.
|
|
|
|
***** Create an IROH-Admin only dahsboard to manage users (revoke, etc...)
|
|
|
|
Have a dashboard for IROH-Admin only to manage users and be able to block some.
|
|
|
|
**** Scopes
|
|
|
|
<<scopes>>
|
|
|
|
While we can easily update scopes in the future.
|
|
We should start with a sane convention.
|
|
|
|
A scope should match the following regex: ~\w+[\w\d_-]*(/\w+[\w\d_-]*)*(:\w+[\w\d_]*)?~
|
|
|
|
A nicer way to look at it is that it should have the following format:
|
|
|
|
- =<high-level-api>=
|
|
- =<high-level-api>(:<access-type>)?=
|
|
- =<high-level-api>(/subset)*(:<access-type>)?=
|
|
|
|
Mainly identifier with potential =:= followed by another identifier.
|
|
The main format is: =<api>= or =<api>:<access-restriction>=.
|
|
|
|
For example: ~enrich~, ~enrich:read~, ~enrich/observe~ or ~enrich/observe:read~.
|
|
|
|
In order to filter access, if a JWT contains the following scopes:
|
|
|
|
#+BEGIN_SRC clojure
|
|
["ui-settings","enrich:read","collect/inspect"]
|
|
#+END_SRC
|
|
|
|
The mean the JWT can access all the routes with the following scopes:
|
|
|
|
- ~ui-settings:read~
|
|
- ~ui-settings:write~
|
|
- ~enrich/observe:read~
|
|
- ~enrich/enrich:read~
|
|
- ~enrich/deliberate:read~
|
|
- ~enrich/settings:read~
|
|
- ~collect/inspect:read~
|
|
|
|
But shouldn't access the following scopes:
|
|
|
|
- ~auth/client-mgmt:write~
|
|
- ~enrich/enrich:write~
|
|
- ~enrich/settings:write~
|
|
- ~collect/config~
|
|
|
|
Here is the exhaustive list of our exposed web services and the proposed scope prefix:
|
|
|
|
| route | scope |
|
|
|-------------------+-------------|
|
|
| /iroh-ui-settings | ui-settings |
|
|
| /iroh-auth | auth |
|
|
| /iroh-auth-mgmt | auth/mgmt |
|
|
| /fake-iroh-auth | auth/fake |
|
|
| /iroh-enrich | enrich |
|
|
| /iroh-response | response |
|
|
| /iroh-inspect | inpect |
|
|
|
|
When the route change some inner state (like writing to some store/db, then we
|
|
should use the ~write~ access-restriction. Otherwise the access restriction
|
|
should be ~read~.
|
|
|
|
Depending on the routes we should also add subsets. Typically:
|
|
|
|
- ~auth/mgmt/jwt-revoke~
|
|
- ~auth/mgmt/saml-org-whitelist~
|
|
|
|
☞ *Scope Accesses*: the scopes granted to an access-token MUST always be a
|
|
subset of the intersection of the scopes of the client and the scopes of the
|
|
user.
|
|
|
|
**** Documentation
|
|
|
|
<<doc>>
|
|
|
|
We should write a clear documentation to help 3rd party developer use our API
|
|
via OAuth2.
|
|
** OAuth2 Epics (3rd pass)
|
|
|
|
*** SPA compatible OAuth2
|
|
|
|
Provide a way for any website to use OAuth2 and make call to IROH API. Typically
|
|
make it easy to create a generic casebook lib or even perhaps a web browser
|
|
plugin that could work on any website.
|
|
|
|
**** Issues
|
|
|
|
- [ ] Support Auth-code without client password
|
|
- [ ] Support CORS that use informations from the access token
|
|
- [ ] Documentation:
|
|
- [ ] Write a document/blog post for developers
|
|
- [ ] Make a demo website if possible using a 3rd party lib like hello.js
|
|
- [ ] Provide a lib (for example a module for hello.js)
|
|
|
|
*** User's made OAuth2 clients
|
|
|
|
Users should be able to create OAuth2 clients with some limitations regarding
|
|
response and passwordless client.
|
|
|
|
IROH Admin users should create OAuth2 clients without scope limitation and
|
|
should be able to create public clients (destined for SPA).
|
|
|
|
**** Issues
|
|
|
|
- [ ] Decide if we authorize any user to create a public OAuth2 client that are
|
|
needed for SPA clients (see https://tools.ietf.org/html/rfc6749#section-2.1)
|
|
- [ ] User can access a webpage to manage their OAuth app grants #iroh-ui
|
|
- [ ] Provide the ability for user to create OAuth2 clients
|
|
- [ ] Forbid any client to require any `response/*:write` scope
|
|
- [ ] Provide urls to make all response using HTLM pages generated by Visibility
|
|
|
|
*** Internal User Representation
|
|
|
|
Currently all the data about an user is provided via the generation of IROH Auth JWT.
|
|
|
|
This kind of limit our ability to manage user access.
|
|
For example, while we can currently revoke access to any user given its id it would be
|
|
difficult to limit an user scope.
|
|
|
|
Also without an internal user representation it is not possible to merge
|
|
different IdP for the same user.
|
|
|
|
In the future it might also be useful to have Visibility only user preferrences.
|
|
|
|
**** Issues
|
|
|
|
- [ ] Write a documentation about how we should manage internal representation.
|
|
Also how to deal with default org, preferred IdP, etc...
|
|
- [ ] Write IROH admin-only, routes and service to
|
|
- create an user and generate its id
|
|
- search an user by either internal id or other 3rd party user id (typically
|
|
AMP or TG user id)
|
|
- revoke access to an user
|
|
- change authorized scopes of an user
|
|
|
|
*** OAuth2 Client Credentials Grant
|
|
|
|
Support OAuth2 Client Credentials grant which is really useful for scripts.
|
|
Mainly the workflow should be:
|
|
|
|
1. The user create a new confidential client with only client credential grant.
|
|
2. The user is returned with a client-id / client-password
|
|
3. The script can use those client creds to retrieve access only for that user.
|
|
|
|
|
|
- [ ] =[#C]= Write a Service to manage Users with the following methods:
|
|
- [ ] =[#C]= Create an user and generate its id. To be used for new login of still unkown user.
|
|
- [ ] =[#C]= search an user by internal id or 3rd party User id (typically AMP user id)
|
|
- [ ] =[#C]= revoke an user
|
|
- [ ] =[#C]= change scopes of an user
|
|
- [ ] =[#C]= Write routes restricted to IROH-Admins to manage Users
|
|
- [ ] =[#C]= GET =/users= returns a list of users
|
|
- [ ] =[#C]= GET =/users/:user-id= returns the specific user infos
|
|
- [ ] =[#C]= PUT =/users/:user-id= update an user, either revoke access or change scope
|
|
|
|
**** Issues
|
|
|
|
- [ ] Support Client Credentials Grant (for confidential client only, see
|
|
https://tools.ietf.org/html/rfc6749#section-4.4)
|
|
|
|
***** Client DB
|
|
|
|
- [ ] =[#C]= Write a service to manage clients
|
|
- [ ] =[#C]= Write routes to manage OAuth2 clients
|
|
- [ ] =[#C]= POST =/clients= (the server sets the ~:id~ and returns the ~:password~
|
|
in clear text, in the DB the password should be encrypted).
|
|
- [ ] =[#C]= GET =/clients= (the ~:password~ field should be obfuscated)
|
|
- [ ] =[#C]= GET =/clients/:id= (the ~:password~ field should be obfuscated)
|
|
- [ ] =[#C]= PUT =/clients/:id= to revoke a client access (the ~:password~ field should be obfuscated)
|
|
- [ ] =[#C]= DELETE =/clients/:id= to revoke a client access (the ~:password~ field should be obfuscated)
|
|
|
|
***** User DB
|
|
|
|
- [ ] =[#C]= Write a Service to manage Users with the following methods:
|
|
- [ ] =[#C]= Create an user and generate its id. To be used for new login of still unkown user.
|
|
- [ ] =[#C]= search an user by internal id or 3rd party User id (typically AMP user id)
|
|
- [ ] =[#C]= revoke an user
|
|
- [ ] =[#C]= change scopes of an user
|
|
- [ ] =[#C]= Write routes restricted to IROH-Admins to manage Users
|
|
- [ ] =[#C]= GET =/users= returns a list of users
|
|
- [ ] =[#C]= GET =/users/:user-id= returns the specific user infos
|
|
- [ ] =[#C]= PUT =/users/:user-id= update an user, either revoke access or change scope
|
|
|
|
***** Frontend
|
|
|
|
- [ ] Create an admin UI for any user to manage OAuth clients
|
|
|
|
*** IROH Admin Dashboard
|
|
|
|
It would be very effective to have a dashboard instead of just using the
|
|
swagger-ui. The dashboard would make it easy to centralize the access of all
|
|
informations as well as administration tasks.
|
|
|
|
Typically:
|
|
|
|
- be able to see some IROH logs filtered by some keywords, and be able
|
|
to revoke an user or an OAuth client. Also be able to change the scopes of some
|
|
clients.
|
|
- Provide the ability to force all user to be prompted to grant access again to
|
|
some client, etc...
|
|
|
|
*** OAuth2 Enhancements
|
|
|
|
Many OAuth2 feature should be enabled in order to provide the best security.
|
|
|
|
- [ ] Automatically revoke refresh token access if the code was used more than once.
|
|
|
|
Archived entries from file /Users/yaesposi/.deft/Cisco.org.gpg
|
|
|
|
|
|
* [#C] Scopes Dictionary
|
|
:PROPERTIES:
|
|
:ARCHIVE_TIME: 2019-01-09 Wed 10:53
|
|
:ARCHIVE_FILE: ~/.deft/Cisco.org.gpg
|
|
:ARCHIVE_OLPATH: Epics
|
|
:ARCHIVE_CATEGORY: Cisco.org
|
|
:END:
|
|
|
|
Regarding access right of API consumer this is enforced via scopes.
|
|
|
|
There are low-level scopes. Mostly one for each route. To help represent
|
|
coherent groups the scopes are organized as a tree whose leaves are tagged
|
|
either by read or write.
|
|
|
|
For example if an user has the scopes ~["foo/bar:read" "baz"]~
|
|
He'll could access all routes with those scopes:
|
|
|
|
- ~foo/bar:read~
|
|
- ~foo/bar/baz:read~
|
|
- ~foo/bar/baz/quux:read~
|
|
- ~baz:write~
|
|
- ~baz/x/y:write~
|
|
|
|
But he won't be granted the access to:
|
|
|
|
- ~bad:read~
|
|
- ~foo/bar:write~
|
|
- ~foo/bar/baz:write~
|
|
|
|
The problem with this organisation is that it's somehow too technical. This is
|
|
why we need to introduce high-level abstracts scopes to give meaningful names to
|
|
precise subsets of route access.
|
|
|
|
To that end we should provide a dictionnary (with schema
|
|
~{s/Str {:scopes #{s/Str}} :name s/Str :description s/Str}~):
|
|
|
|
#+BEGIN_SRC clojure
|
|
{"importer" {:scopes #{"collect" "ctia:read"}
|
|
:name "Importer"
|
|
:description "This client would like to be able to import events into
|
|
your private intel store,
|
|
and needs some limited read permissions as well"}
|
|
...
|
|
}
|
|
#+END_SRC
|
|
|
|
Technically it would simply mean that if an user has the scope "importer" we'll look into that
|
|
dictionnary to provide him the technical scopes.
|
|
Furthermore we'll be able to look into this dictionary to provide a meaninful name and description
|
|
to the user when he's asked to grant some access to some 3rd party app (OAuth client).
|
|
|
|
Archived entries from file /Users/yaesposi/.deft/Cisco.org.gpg
|
|
|
|
|
|
* DONE Document for Raghavaiah
|
|
CLOSED: [2018-11-28 Wed 18:01]
|
|
:PROPERTIES:
|
|
:ARCHIVE_TIME: 2019-01-09 Wed 10:54
|
|
:ARCHIVE_FILE: ~/.deft/Cisco.org.gpg
|
|
:ARCHIVE_CATEGORY: Cisco.org
|
|
:ARCHIVE_TODO: DONE
|
|
:END:
|
|
|
|
** Frank's proposal; Auth config untangling
|
|
|
|
IROH Auth and SSE staging config changes to correctly link environments and improve naming
|
|
|
|
*** INT
|
|
**** idb-amp-staging / selector: amp
|
|
|
|
This is actually pointing to QA1 on the AMP side. We should name it as such.
|
|
|
|
- CTR
|
|
- Change identifier to `idb-amp-qa1`
|
|
- Change name to `AMP QA1`
|
|
- Change selector to `amp-QA1`
|
|
|
|
- SSE IDB
|
|
- Change selector to `amp-qa1`
|
|
|
|
**** idb-tg-staging / selector: tg
|
|
|
|
This is TG INT, just need a rename
|
|
|
|
- CTR
|
|
- Change identifier to `idb-tg-int`
|
|
|
|
- SSE IDB
|
|
- CHange selector to `tg-int`
|
|
|
|
*** TEST
|
|
**** idb-amp-staging / selector: amp
|
|
|
|
This needs to go to AMPs SDC environment instead. We'll need a new auth provider setup on the SSE side for AMP SDC
|
|
|
|
- CTR
|
|
- Change identifier to `idb-amp-sdc`
|
|
- Change name to `AMP SDC`
|
|
- Change selector to `amp-sdc`
|
|
|
|
- SSE IDB
|
|
- New auth provider with URL: https://auth-ext.qa1.immunet.com/
|
|
- Selector `amp-sdc`
|
|
|
|
**** idb-tg-staging / selector: tg
|
|
|
|
This will need to point at the TG TEST environment, not INT. It'll also need a new auth provider be created in the IDB
|
|
|
|
- CTR
|
|
- Change identifier to `idb-tg-test`
|
|
- Change name to `Threat Grid TEST`
|
|
- Change selector to `tg-test`
|
|
|
|
- SSE IDB
|
|
- New auth provider with URL: https://test.threatgrid.com/ (Frank to create new oauth2 client if needed)
|
|
- Selector`tg-test`
|
|
|
|
** Proposal
|
|
|
|
To help people manage all the IDB clients and connection we would like to follow a stricter naming convention.
|
|
|
|
There are many different named entities:
|
|
|
|
- IROH IdP id
|
|
- IDB selector
|
|
- IROH-UI button label
|
|
|
|
The IROH IdP id is used in the =redirect_uri= that should be configured in the
|
|
IDB client.
|
|
|
|
Also there are two different IDB environment. IDB prod and IDB stage.
|
|
|
|
Each client should also point to some IdP environment, typically, AMP NAM Prod,
|
|
or AMP QA1, or AMP SDC.
|
|
|
|
The namin convention should make it clear what IDB instance is used as well as
|
|
with IdP it targets, in particular we should make each region explicit.
|
|
|
|
Note all point prefixed by =IDB= are the information needed by the SSE/IDB team.
|
|
|
|
*** INT
|
|
**** AMP
|
|
|
|
Note: I think it should point to AMP prod as before to prevent the use of the VPN
|
|
to login to INT
|
|
|
|
- IROH IdP id: idb-stage-amp-nam
|
|
- IDB env: Stage
|
|
- IDB selector: idb-stage-amp-nam
|
|
- IDB redirect uris:
|
|
- https://visibility.int.iroh.site/iroh/iroh-auth/login/idb-stage-amp-nam/answer
|
|
- https://localhost:5443/callback
|
|
- IDB IdP Target: https://auth.amp.cisco.com/auth/session/new
|
|
- IROH-UI button label: Cisco Security
|
|
|
|
**** TG
|
|
|
|
- IROH IdP id: idb-stage-tg-int
|
|
- IDB env: Stage
|
|
- IDB selector: idb-stage-tg-int
|
|
- IDB redirect uris:
|
|
- https://visibility.int.iroh.site/iroh/iroh-auth/login/idb-stage-tg-int/answer
|
|
- https://localhost:5443/callback
|
|
- IDB IdP Target: https://int.threatgrid.com/oauth2/authorize
|
|
- IROH-UI button label: Cisco Security
|
|
|
|
*** PROD NAM
|
|
**** AMP
|
|
|
|
- IROH IdP id: idb-amp-nam
|
|
- IDB env: Prod
|
|
- IDB selector: idb-amp-nam
|
|
- IDB redirect uris:
|
|
- https://visibility.amp.cisco.com/iroh/iroh-auth/login/idb-amp-nam/answer
|
|
- IDB IdP Target: https://auth.amp.cisco.com/auth/session/new
|
|
- IROH-UI button label: Cisco Security
|
|
|
|
**** TG
|
|
|
|
- IROH IdP id: idb-tg-nam
|
|
- IDB env: Prod
|
|
- IDB selector: idb-tg-nam
|
|
- IDB redirect uris:
|
|
- https://visibility.amp.cisco.com/iroh/iroh-auth/login/idb-tg-nam/answer
|
|
- https://visibility.apjc.amp.cisco.com/iroh/iroh-auth/login/idb-tg-nam/answer
|
|
- https://visibility.test.iroh.site/iroh/iroh-auth/login/idb-tg-nam/answer
|
|
- IDB IdP Target: https://panacea.threatgrid.com
|
|
- IROH-UI button label: Threat Grid
|
|
|
|
*** PROD EU
|
|
**** AMP
|
|
|
|
- IROH IdP id: idb-amp-eu
|
|
- IDB env: Prod
|
|
- IDB selector: idb-amp-eu
|
|
- IDB redirect uris:
|
|
- https://visibility.eu.amp.cisco.com/iroh/iroh-auth/login/idb-amp-eu/answer
|
|
- https://visibility.test.iroh.site/iroh/iroh-auth/login/idb-amp-eu/answer
|
|
- IDB IdP Target: https://auth.eu.amp.cisco.com/auth/session/new
|
|
- IROH-UI button label: Cisco Security
|
|
|
|
**** TG
|
|
|
|
- IROH IdP id: idb-tg-eu
|
|
- IDB env: Prod
|
|
- IDB selector: idb-tg-eu
|
|
- IDB redirect uris:
|
|
- https://visibility.eu.amp.cisco.com/iroh/iroh-auth/login/idb-tg-eu/answer
|
|
- https://visibility.test.iroh.site/iroh/iroh-auth/login/idb-tg-eu/answer
|
|
- IDB IdP Target: https://panacea.threatgrid.eu
|
|
- IROH-UI button label: Threat Grid
|
|
|
|
*** PROD APJC
|
|
**** AMP
|
|
|
|
- IROH IdP id: idb-amp-apjc
|
|
- IDB env: Prod
|
|
- IDB selector: idb-amp-apjc
|
|
- IDB redirect uris:
|
|
- https://visibility.apjc.amp.cisco.com/iroh/iroh-auth/login/idb-amp-eu/answer
|
|
- https://visibility.test.iroh.site/iroh/iroh-auth/login/idb-amp-apjc/answer
|
|
- IDB IdP Target: https://auth.apjc.amp.cisco.com/auth/session/new
|
|
- IROH-UI button label: Cisco Security
|
|
|
|
**** TG
|
|
|
|
This one is the same as the TG for prod. TG doesn't have an APJC endpoint for login.
|
|
|
|
- IROH IdP id: idb-tg-nam
|
|
- IDB env: Prod
|
|
- IDB selector: idb-tg-nam
|
|
- IDB redirect uris:
|
|
- https://visibility.amp.cisco.com/iroh/iroh-auth/login/idb-tg-nam/answer
|
|
- https://visibility.apjc.amp.cisco.com/iroh/iroh-auth/login/idb-tg-nam/answer
|
|
- https://visibility.test.iroh.site/iroh/iroh-auth/login/idb-tg-nam/answer
|
|
- IDB IdP Target: https://panacea.threatgrid.com
|
|
- IROH-UI button label: Threat Grid
|
|
|
|
*** TEST
|
|
|
|
For test, we should want to be able to login through many different envs to
|
|
check none is broken or a future change won't break our login.
|
|
|
|
So the "classical integration with test envs".
|
|
|
|
**** TEST IdPs
|
|
|
|
***** AMP SDC
|
|
|
|
- IROH IdP id: idb-stage-amp-sdc
|
|
- IDB env: Stage
|
|
- IDB selector: idb-stage-amp-sdc
|
|
- IDB redirect uris:
|
|
- https://visibility.test.iroh.site/iroh/iroh-auth/login/idb-stage-amp-sdc/answer
|
|
- https://localhost:5443/callback
|
|
- IDB IdP Target: https://auth-ext.qa1.immunet.com/
|
|
- IROH-UI button label: AMP SDC
|
|
|
|
***** TG Test
|
|
|
|
- IROH IdP id: idb-stage-tg-test
|
|
- IDB env: Stage
|
|
- IDB selector: idb-stage-tg-test
|
|
- IDB redirect uris:
|
|
- https://visibility.int.iroh.site/iroh/iroh-auth/login/idb-stage-tg-test/answer
|
|
- https://localhost:5443/callback
|
|
- IDB IdP Target: https://test.threatgrid.com/oauth2/authorize
|
|
- IROH-UI button label: Cisco Security
|
|
|
|
**** Prod Logins
|
|
|
|
Instead of duplicate the infos, we should copy all buttons from the 3 prod regions.
|
|
Note it will only create 5 buttons instead of 6 because there is no TG APJC login page.
|
|
|