diff options
author | Mike Crute <mike@crute.us> | 2021-11-16 21:50:31 -0800 |
---|---|---|
committer | Mike Crute <mike@crute.us> | 2021-11-17 07:56:17 -0800 |
commit | 73ab23b5f74174ec6b6a988103306eeec389d5a8 (patch) | |
tree | ed56d604a22d36ca9bf658064442d29f0e0a18b0 | |
parent | cc58a3da7d647de8520e33dc4356672d2ed1a366 (diff) | |
download | cloud-identity-broker-73ab23b5f74174ec6b6a988103306eeec389d5a8.tar.bz2 cloud-identity-broker-73ab23b5f74174ec6b6a988103306eeec389d5a8.tar.xz cloud-identity-broker-73ab23b5f74174ec6b6a988103306eeec389d5a8.zip |
Add some docs
-rw-r--r-- | README.md | 205 | ||||
-rw-r--r-- | README_BROKER.md | 228 |
2 files changed, 431 insertions, 2 deletions
@@ -1,6 +1,207 @@ | |||
1 | # Cloud Identity Broker | 1 | # Cloud Identity Broker |
2 | 2 | ||
3 | // TODO: https://docs.gitlab.com/ee/api/jobs.html#get-job-tokens-job | 3 | This is a cloud identity broker. Identity brokers exist to bridge |
4 | // TODO: Admin UI | 4 | authentication from one system into authentication to another system. In the |
5 | case of this broker it bridges GitHub and GitLab authentication into temporary | ||
6 | credentials for cloud providers. The broker has an interactive API that can be | ||
7 | used in a web browser as well as a JSON/REST API that can be consumed | ||
8 | programmatically. | ||
5 | 9 | ||
10 | The reason this broker exists is to provide one secured location for long-lived | ||
11 | cloud provider credentials that can be easily consumed from development | ||
12 | environments and build systems while limiting the scope of access and time frame | ||
13 | of access those systems have to cloud resources. This helps to increase the | ||
14 | security of those accounts and reduce the risk of abuse. All access through the | ||
15 | broker is chained to an authenticated and authorized user account and all | ||
16 | access through those user accounts are logged for auditing. All credentials | ||
17 | returned by the broker have a strict time limit after which they are useless. | ||
6 | 18 | ||
19 | This broker started life as a piece of internal infrastructure specific to AWS | ||
20 | that has been re-written and open sourced with the goal to support multiple | ||
21 | clouds. | ||
22 | |||
23 | For documentation the REST API look at the `README_BROKER.md` file. | ||
24 | |||
25 | ## Contributing | ||
26 | |||
27 | Contributions are welcomed. Please send contributions to mike-at-crute-dot-us | ||
28 | either through email in `git format-patch` format or send a link to a git repo | ||
29 | and branch name that the author can pull and merge. | ||
30 | |||
31 | If you're reading this on one of the author's git forge accounts (GitHub, | ||
32 | GitLab, sr.ht, etc...) feel free to open a pull/merge request with your | ||
33 | changes. | ||
34 | |||
35 | The author reserves the right to request changes to contributions before | ||
36 | merging them or to not merge them at all but will endeavour to be reasonable in | ||
37 | those requests. | ||
38 | |||
39 | ## Building | ||
40 | |||
41 | This requires Go 1.17 or newer to build. Provided that a simple `make` should | ||
42 | create a `cloud-identity-broker` binary that's ready to use. | ||
43 | |||
44 | The built binary embeds all of the templates in production mode and will | ||
45 | require nothing more than the binary itself to run, if running in `--debug` | ||
46 | mode then the `templates` folder must be available. | ||
47 | |||
48 | ## Running | ||
49 | |||
50 | It's complicated, so probably don't? | ||
51 | |||
52 | The application requires some bits of infrastructure to be in place for it to | ||
53 | run but then can be exposed directly to the internet or put behind a reverse | ||
54 | proxy. You will need: | ||
55 | |||
56 | - [Mongodb](https://www.mongodb.com/) | ||
57 | - [Vault](https://www.hashicorp.com/products/vault) | ||
58 | - [GitHub Oauth Application](https://docs.github.com/en/developers/apps/building-oauth-apps/creating-an-oauth-app) | ||
59 | - [SSL certificates](https://letsencrypt.org/). | ||
60 | |||
61 | Once the requisite infrastructure (see below) is configured, run the binary | ||
62 | like so: | ||
63 | |||
64 | ``` | ||
65 | VAULT_ROLE_ID="..." \ | ||
66 | VAULT_SECRET_ID="..." \ | ||
67 | VAULT_ADDR="https://your-vault-addr:8200" \ | ||
68 | ./cloud-identity-broker \ | ||
69 | --mongodb-uri="mongodb://your-mongodb-host:27017/your-db-name?authSource=admin" \ | ||
70 | --mongodb-vault-path="database/static-creds/your-cred-name" \ | ||
71 | --github-oauth-vault-path="service/service-name/github-oauth" \ | ||
72 | web | ||
73 | ``` | ||
74 | |||
75 | ### SSL Certificates | ||
76 | |||
77 | SSL certificates of a cipher type compatible with Go's SSL library are required | ||
78 | to run the binary, even in development mode. Obtain those certificates however | ||
79 | you would normal do so. | ||
80 | |||
81 | Both a key and certificate are required to be stored in the directory | ||
82 | identified by `--tls-cache-dir` (which defaults to `./ssl/`). Both files should | ||
83 | be of PEM format with the certificate named `cert.pem` and the key named | ||
84 | `key.pem`. | ||
85 | |||
86 | ### Vault Setup | ||
87 | |||
88 | Configure Vault per the manufacturers instructions. Create an AppRole for the | ||
89 | identity broker and provide the credentials to the binary using the standard | ||
90 | Vault environment variables. | ||
91 | |||
92 | The binary will need access to a set of GitHub Oauth client credentials for a | ||
93 | GitHub Oauth application that is used to authenticate users to the broker. | ||
94 | Setup an application on GitHub and store the client ID and client secret in a | ||
95 | JSON document in Vault the vault KV backend with the following format: | ||
96 | |||
97 | ``` | ||
98 | { | ||
99 | "client-id": "your client id", | ||
100 | "client-secret": "your client secret" | ||
101 | } | ||
102 | ``` | ||
103 | |||
104 | Provide the path to this material with the `--github-oauth-vault-path` command | ||
105 | line argument. You should omit the `kv/data/` prefix to the path. | ||
106 | |||
107 | For each AWS cloud account to which the broker provides access a JSON document | ||
108 | must be stored in the Vault KV backend. The format of this document is as | ||
109 | follows. The path to this document is provided (with the `kv/data/` prefix | ||
110 | omitted) in the `VaultMaterial` field of the `Account` record for the account | ||
111 | in Mongodb. | ||
112 | |||
113 | ``` | ||
114 | { | ||
115 | "AccessKeyId": "...", | ||
116 | "SecretAccessKey": "...", | ||
117 | "Roles": { | ||
118 | "account-short-name": { | ||
119 | "ARN": "arn:aws:iam::...:role/...", | ||
120 | "ExternalId": "..." | ||
121 | } | ||
122 | } | ||
123 | } | ||
124 | ``` | ||
125 | |||
126 | The fields are: | ||
127 | |||
128 | - `AccessKeyId` which is the access key ID of an IAM user that can assume roles | ||
129 | in the target account. | ||
130 | - `SecretAccessKey` is the secret key for the IAM user identified by the access | ||
131 | key ID. | ||
132 | - `Roles` is a mapping of account `ShortName` to an AWS ARN and ExternalId used | ||
133 | for assuming roles. The `ARN` is the role to be assumed and the `ExternalId` | ||
134 | is the External ID configured in the role trust policy. | ||
135 | |||
136 | Note that this application does **not** use the AWS Vault backend to assume | ||
137 | roles due to limitations AWS places on roles assumed using assumed role | ||
138 | credentials and not user credentials. | ||
139 | |||
140 | Finally the broker will need access to a Mongodb credential. Currently this | ||
141 | requires a static credential but in the future a dynamic credential will be | ||
142 | acceptable. Create this credential and pass it in the `--mongodb-vault-path` | ||
143 | argument. It should have the form `database/static-creds/your-cred-name`. | ||
144 | |||
145 | ### Mongodb Setup | ||
146 | |||
147 | The application needs a Mongodb database with at least a `users` collection | ||
148 | created. The rest are optional. | ||
149 | |||
150 | To create the `users` collection insert a record to the collection that | ||
151 | contains your GitHub username as the `_id` field and has `IsAdmin` set to true. | ||
152 | For example: | ||
153 | |||
154 | ``` | ||
155 | db.users.insert({ "_id": "mcrute", "IsAdmin": true }) | ||
156 | ``` | ||
157 | |||
158 | This is adequate to allow login via GitHub for the named user. All other fields | ||
159 | will be populated automatically by the app. | ||
160 | |||
161 | To create allowed AWS accounts for use in the broker add them to the `accounts` | ||
162 | collection. Those records have the following form: | ||
163 | |||
164 | ``` | ||
165 | { | ||
166 | "_id": "acocunt-short-name", | ||
167 | "AccountNumber": 12345, | ||
168 | "AccountType": "aws", | ||
169 | "ConsoleSessionDuration": 21600000000000, | ||
170 | "DefaultRegion": "us-west-2", | ||
171 | "Name": "Some Description", | ||
172 | "Users": [ | ||
173 | "username1", | ||
174 | "username2" | ||
175 | ], | ||
176 | "VaultMaterial": "some/path/as/above" | ||
177 | } | ||
178 | ``` | ||
179 | |||
180 | - `_id` is the short name of the account. This must also map into a Vault | ||
181 | material per above. | ||
182 | - `AccountNumber` is the AWS account number for the account being brokered. | ||
183 | - `AccountType` is the string `aws` for now | ||
184 | - `ConsoleSessionDuration` is the number of nanoseconds a console session can | ||
185 | exist before timing out. (Nanoseconds are silly, yes; it's a serialized | ||
186 | time.Time) | ||
187 | - `DefaultRegion` is the region considered default, this is just used as an | ||
188 | indicator in the JSON API. | ||
189 | - `Name` a longer form, descriptive name of the account. | ||
190 | - `Users` a case-sensitive list of GitHub user names that have access to this | ||
191 | account | ||
192 | - `VaultMaterial` the path to the Vault material that contains the account | ||
193 | credentials document, as above. | ||
194 | |||
195 | ## To Do | ||
196 | |||
197 | - Allow GitLab CI jobs to auth using [job tokens](https://docs.gitlab.com/ee/api/jobs.html#get-job-tokens-job) | ||
198 | - Implement an Admin UI, all admin ops are directly on the DB at the moment | ||
199 | - Support dynamic mongodb credentials | ||
200 | - Support for other clouds | ||
201 | - GCP | ||
202 | - OCI | ||
203 | - Azure | ||
204 | |||
205 | ## Contributors | ||
206 | |||
207 | - [Mike Crute](https://mike.crute.us) | ||
diff --git a/README_BROKER.md b/README_BROKER.md new file mode 100644 index 0000000..03db1af --- /dev/null +++ b/README_BROKER.md | |||
@@ -0,0 +1,228 @@ | |||
1 | # AWS Identity Broker | ||
2 | |||
3 | The identity broker is used to obtain short-lived and per-region credentials | ||
4 | for an account. Opt-in regions require the use of a long-lived credential (e.g. | ||
5 | IAM user), enabling global STS tokens, or an STS token sourced in that region. | ||
6 | The identity broker holds long-term credentials and uses them to acquire | ||
7 | short-term credentials in a given region. The broker also provides a list of | ||
8 | opt-in regions and should be used to enumerate regions. | ||
9 | |||
10 | For human-interactive users the identity broker performs OAUTH against GitHub | ||
11 | to chain the user's GitHub identity to the tokens they are given from the | ||
12 | broker. The broker also provides the user an API key to use when interacting | ||
13 | with the broker programmatically. | ||
14 | |||
15 | # The API | ||
16 | |||
17 | The identity broker API is a REST-ful service with an entry-point of | ||
18 | `/api/account`. All further navigation through the API follows links within the | ||
19 | hypertext. | ||
20 | |||
21 | **Note:** Outside of the account entry-point, URI formats should be considered | ||
22 | opaque implementation details of the broker API and should never be templated. | ||
23 | Beyond the account entry-point, nothing in this specification is normative with | ||
24 | respect to URI paths and locations. | ||
25 | |||
26 | ## Media Formats | ||
27 | |||
28 | There are two supported media types in the API `application/vnd.broker.v1+json` | ||
29 | and `application/vnd.broker.v2+json`. A client that requests the default media | ||
30 | type or `application/json` will recieve the V1 media type. | ||
31 | |||
32 | To request a specific media type use the `Accept` header: | ||
33 | |||
34 | ``` | ||
35 | Accept: application/vnd.broker.v2+json | ||
36 | ``` | ||
37 | |||
38 | ## Authentication | ||
39 | |||
40 | All requests to the API must be authenticated with a broker-specific key. That | ||
41 | key is provided to the broker in the `Authorization` header with a sub-type of | ||
42 | `Bearer`. When the broker determines that the key is either expired or invalid | ||
43 | it must redirect the user to `/logout` to indicate that the user is logged out | ||
44 | and must log-in again. | ||
45 | |||
46 | API keys are bearer tokens and thus must only be exchanged over HTTPS. | ||
47 | |||
48 | Example of authorization header: | ||
49 | |||
50 | ``` | ||
51 | Authorization: Bearer ...an auth token... | ||
52 | ``` | ||
53 | |||
54 | Legacy versions of the API supported an `X-API-Key` header, which is now | ||
55 | deprecated but remains supported for backwards compatability. | ||
56 | |||
57 | ## Status Codes | ||
58 | |||
59 | `200 OK`: indicates that the request to the broker was successful. | ||
60 | |||
61 | `302 Found`: indicates that the broker is providing a redirect. Users should | ||
62 | check the redirect, if it is to the location `/logout` the user should consider | ||
63 | themselves logged out and proceed to login. This condition should not be | ||
64 | followed. Otherwise the user should follow all redirects. | ||
65 | |||
66 | `400 Bad Request`: indicates that some part of the request is invalid as | ||
67 | submitted. The hypertext MAY provide a description of this error and how to | ||
68 | remedy it. | ||
69 | |||
70 | `401 Unauthorized`: indicates that the authentication or authorzation for the | ||
71 | user has failed. This is not retryable, a user should obtain a new | ||
72 | authorization token before attempting another request. | ||
73 | |||
74 | `429 Rate Limit Exceeded`: indicates that the broker has rate-limited the user. | ||
75 | A user should discontinue requests to the broker immediately and wait for at | ||
76 | least 30 seconds before continuing their requests. The rate limit parameters | ||
77 | are specific to the broker and not controlled by this spec. | ||
78 | |||
79 | `500 Server Error`: indicates a server error condition that is not under the | ||
80 | user's control. | ||
81 | |||
82 | ## Account End-point | ||
83 | |||
84 | ### V1 Media Type | ||
85 | |||
86 | The account end-point acts as a index of the rest of the API. It presents a | ||
87 | list of accounts to which the user has access as well as links to navigate | ||
88 | further into the API. The format of this document is: | ||
89 | |||
90 | `short_name` (string): a url-safe name for the account, used as the primary | ||
91 | account identifier within the broker. | ||
92 | |||
93 | `account_number` (integer): the AWS account number | ||
94 | |||
95 | `name` (string): a user-friendly name for the account | ||
96 | |||
97 | `vendor` (string): the name of the cloud vendor (currently only `aws`) | ||
98 | |||
99 | `console_redirect_url` (uri): a URI that, when followed, leads to a resource | ||
100 | that redirects the user to an authenticated console session. | ||
101 | |||
102 | `get_console_url` (uri): a URI that, when followed, leads to a console URL | ||
103 | resource. | ||
104 | |||
105 | `credentials_url` (uri): a URI that, when followed, leads to a region list | ||
106 | resource. | ||
107 | |||
108 | `global_credential_url` (uri): a URI that, when followed, leads to a credential | ||
109 | resource which provides a credential usable by all non-opt-in regions. The | ||
110 | contents of this resource are a STS global credential which is not usable in | ||
111 | opt-in regions. | ||
112 | |||
113 | ``` | ||
114 | [ | ||
115 | { | ||
116 | "short_name": "primary-account", | ||
117 | "vendor": "aws", | ||
118 | "account_number": 123456789012, | ||
119 | "name": "Primary AWS Account", | ||
120 | "console_redirect_url": "https://broker/api/account/primary-account/console?redirect=1", | ||
121 | "get_console_url": "https://broker/api/account/primary-account/console", | ||
122 | "credentials_url": "https://broker/api/account/primary-account/credentials", | ||
123 | "global_credential_url": "https://broker/api/account/primary-account/credentials/global" | ||
124 | } | ||
125 | ] | ||
126 | ``` | ||
127 | |||
128 | ### V2 Media Type | ||
129 | |||
130 | The fiends in the V2 media type have identical meaning to those in the V1 media | ||
131 | type but the top level container format is different. Instead of containing a | ||
132 | list of accounts with a `vendor` field V2 responses are a map of vendors to a | ||
133 | list of accounts. | ||
134 | |||
135 | ``` | ||
136 | { | ||
137 | "aws": [ | ||
138 | { | ||
139 | "short_name": "primary-account", | ||
140 | "account_number": 123456789012, | ||
141 | "name": "Primary AWS Account", | ||
142 | "console_redirect_url": "https://broker/api/account/primary-account/console?redirect=1", | ||
143 | "get_console_url": "https://broker/api/account/primary-account/console", | ||
144 | "credentials_url": "https://broker/api/account/primary-account/credentials", | ||
145 | "global_credential_url": "https://broker/api/account/primary-account/credentials/global" | ||
146 | } | ||
147 | ] | ||
148 | } | ||
149 | ``` | ||
150 | |||
151 | ## Console URL Resource | ||
152 | |||
153 | **Note:** This resource is not used by the build scripts. | ||
154 | |||
155 | The console URL resource provides a URL to the AWS console. This resource is | ||
156 | designed for interactive use. | ||
157 | |||
158 | When provided the query parameter `redirect` with a value of `1` this resource | ||
159 | will not generate a body and will instead redirect to the URL that would have | ||
160 | been returned for `console_url`. | ||
161 | |||
162 | `console_url` (uri): a link to the AWS console with authentication credentials | ||
163 | embedded. | ||
164 | |||
165 | ``` | ||
166 | { | ||
167 | "console_url": "https://signin.aws.amazon.com/federation?..." | ||
168 | } | ||
169 | ``` | ||
170 | |||
171 | ## Credential Resource | ||
172 | |||
173 | The credential resource provides a set of credentials that can be used to | ||
174 | configure AWS tools to access an account. | ||
175 | |||
176 | `access_key` (string): the AWS access key ID | ||
177 | |||
178 | `secret_key` (string): the AWS secret access key | ||
179 | |||
180 | `session_token` (string): the AWS session token | ||
181 | |||
182 | `expiration` (iso-formatted date): the date and time when the credential will | ||
183 | expire | ||
184 | |||
185 | ``` | ||
186 | { | ||
187 | "access_key": "ASIA123ABC456DEF567G", | ||
188 | "secret_key": "r7KcIuGdPwoUG2YOLISX2XDrVts55IFGTGaY5Tqa", | ||
189 | "session_token": "7C7FyvzyneaS/eRCVDcjHOSTTIHQyvhGqW...", | ||
190 | "expiration": "2020-01-01T00:00:00Z" | ||
191 | } | ||
192 | ``` | ||
193 | |||
194 | The API will set a valid `Expires` header that matches the `expiration` field | ||
195 | of this resource to facilitate use of HTTP caches. | ||
196 | |||
197 | ## Region List Resource | ||
198 | |||
199 | The region list resource provides a list of regions associated with the account | ||
200 | both opted-in and not. For opted-in regions the resource includes a link to a | ||
201 | credential resource for that region. | ||
202 | |||
203 | `name` (string): AWS name for the region | ||
204 | |||
205 | `enabled` (boolean): indicates if the region is enabled and opted-in for this | ||
206 | account. | ||
207 | |||
208 | `credentials_url` (uri): a URI that, when followed, leads to a credential | ||
209 | resource containing a credential for access to that region. The credential | ||
210 | provided will be usable against the region-local STS endpoint for the specified | ||
211 | region. This also applies for classic regions, which typically use a global | ||
212 | endpoint and credential. The returned credential is scoped to the acquiring | ||
213 | region and may not be usable against the global endpoints or a different | ||
214 | regional endpoint. | ||
215 | |||
216 | ``` | ||
217 | [ | ||
218 | { | ||
219 | "name": "af-south-1", | ||
220 | "enabled": false | ||
221 | }, | ||
222 | { | ||
223 | "name": "us-west-2", | ||
224 | "enabled": true, | ||
225 | "credentials_url": "https://broker/api/account/primary-account/credentials/us-west-2" | ||
226 | } | ||
227 | ] | ||
228 | ``` | ||