docs: restructure docs (#14421)

Closes #13434 
Supersedes #14182

---------

Co-authored-by: Ethan <39577870+ethanndickson@users.noreply.github.com>
Co-authored-by: Ethan Dickson <ethan@coder.com>
Co-authored-by: Ben Potter <ben@coder.com>
Co-authored-by: Stephen Kirby <58410745+stirby@users.noreply.github.com>
Co-authored-by: Stephen Kirby <me@skirby.dev>
Co-authored-by: EdwardAngert <17991901+EdwardAngert@users.noreply.github.com>
Co-authored-by: Edward Angert <EdwardAngert@users.noreply.github.com>
This commit is contained in:
Muhammad Atif Ali
2024-10-05 10:52:04 -05:00
committed by GitHub
co-authored by Ethan Ethan Dickson Ben Potter Stephen Kirby Stephen Kirby EdwardAngert Edward Angert
parent 288df75686
commit 419eba5fb6
298 changed files with 5009 additions and 3889 deletions
@@ -0,0 +1,87 @@
# API Tokens of deleted users not invalidated
---
## Summary
Coder identified an issue in
[https://github.com/coder/coder](https://github.com/coder/coder) where API
tokens belonging to a deleted user were not invalidated. A deleted user in
possession of a valid and non-expired API token is still able to use the above
token with their full suite of capabilities.
## Impact: HIGH
If exploited, an attacker could perform any action that the deleted user was
authorized to perform.
## Exploitability: HIGH
The CLI writes the API key to `~/.coderv2/session` by default, so any deleted
user who previously logged in via the Coder CLI has the potential to exploit
this. Note that there is a time window for exploitation; API tokens have a
maximum lifetime after which they are no longer valid.
The issue only affects users who were active (not suspended) at the time they
were deleted. Users who were first suspended and later deleted cannot exploit
this issue.
## Affected Versions
All versions of Coder between v0.8.15 and v0.22.2 (inclusive) are affected.
All customers are advised to upgrade to
[v0.23.0](https://github.com/coder/coder/releases/tag/v0.23.0) as soon as
possible.
## Details
Coder incorrectly failed to invalidate API keys belonging to a user when they
were deleted. When authenticating a user via their API key, Coder incorrectly
failed to check whether the API key corresponds to a deleted user.
## Indications of Compromise
> 💡 Automated remediation steps in the upgrade purge all affected API keys.
> Either perform the following query before upgrade or run it on a backup of
> your database from before the upgrade.
Execute the following SQL query:
```sql
SELECT
users.email,
users.updated_at,
api_keys.id,
api_keys.last_used
FROM
users
LEFT JOIN
api_keys
ON
api_keys.user_id = users.id
WHERE
users.deleted
AND
api_keys.last_used > users.updated_at
;
```
If the output is similar to the below, then you are not affected:
```sql
-----
(0 rows)
```
Otherwise, the following information will be reported:
- User email
- Time the user was last modified (i.e. deleted)
- User API key ID
- Time the affected API key was last used
> 💡 If your license includes the
> [Audit Logs](https://coder.com/docs/admin/audit-logs#filtering-logs) feature,
> you can then query all actions performed by the above users by using the
> filter `email:$USER_EMAIL`.
+126
View File
@@ -0,0 +1,126 @@
# Audit Logs
Audit Logs allows **Auditors** to monitor user operations in their deployment.
## Tracked Events
We track the following resources:
<!-- Code generated by 'make docs/admin/security/audit-logs.md'. DO NOT EDIT -->
| <b>Resource<b> | |
| -------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| APIKey<br><i>login, logout, register, create, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>created_at</td><td>true</td></tr><tr><td>expires_at</td><td>true</td></tr><tr><td>hashed_secret</td><td>false</td></tr><tr><td>id</td><td>false</td></tr><tr><td>ip_address</td><td>false</td></tr><tr><td>last_used</td><td>true</td></tr><tr><td>lifetime_seconds</td><td>false</td></tr><tr><td>login_type</td><td>false</td></tr><tr><td>scope</td><td>false</td></tr><tr><td>token_name</td><td>false</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>user_id</td><td>true</td></tr></tbody></table> |
| AuditOAuthConvertState<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>created_at</td><td>true</td></tr><tr><td>expires_at</td><td>true</td></tr><tr><td>from_login_type</td><td>true</td></tr><tr><td>to_login_type</td><td>true</td></tr><tr><td>user_id</td><td>true</td></tr></tbody></table> |
| Group<br><i>create, write, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>avatar_url</td><td>true</td></tr><tr><td>display_name</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>members</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>quota_allowance</td><td>true</td></tr><tr><td>source</td><td>false</td></tr></tbody></table> |
| AuditableOrganizationMember<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>created_at</td><td>true</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>roles</td><td>true</td></tr><tr><td>updated_at</td><td>true</td></tr><tr><td>user_id</td><td>true</td></tr><tr><td>username</td><td>true</td></tr></tbody></table> |
| CustomRole<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>created_at</td><td>false</td></tr><tr><td>display_name</td><td>true</td></tr><tr><td>id</td><td>false</td></tr><tr><td>name</td><td>true</td></tr><tr><td>org_permissions</td><td>true</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>site_permissions</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>user_permissions</td><td>true</td></tr></tbody></table> |
| GitSSHKey<br><i>create</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>created_at</td><td>false</td></tr><tr><td>private_key</td><td>true</td></tr><tr><td>public_key</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>user_id</td><td>true</td></tr></tbody></table> |
| HealthSettings<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>dismissed_healthchecks</td><td>true</td></tr><tr><td>id</td><td>false</td></tr></tbody></table> |
| License<br><i>create, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>exp</td><td>true</td></tr><tr><td>id</td><td>false</td></tr><tr><td>jwt</td><td>false</td></tr><tr><td>uploaded_at</td><td>true</td></tr><tr><td>uuid</td><td>true</td></tr></tbody></table> |
| NotificationTemplate<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>actions</td><td>true</td></tr><tr><td>body_template</td><td>true</td></tr><tr><td>group</td><td>true</td></tr><tr><td>id</td><td>false</td></tr><tr><td>kind</td><td>true</td></tr><tr><td>method</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>title_template</td><td>true</td></tr></tbody></table> |
| NotificationsSettings<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>id</td><td>false</td></tr><tr><td>notifier_paused</td><td>true</td></tr></tbody></table> |
| OAuth2ProviderApp<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>callback_url</td><td>true</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>icon</td><td>true</td></tr><tr><td>id</td><td>false</td></tr><tr><td>name</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr></tbody></table> |
| OAuth2ProviderAppSecret<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>app_id</td><td>false</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>display_secret</td><td>false</td></tr><tr><td>hashed_secret</td><td>false</td></tr><tr><td>id</td><td>false</td></tr><tr><td>last_used_at</td><td>false</td></tr><tr><td>secret_prefix</td><td>false</td></tr></tbody></table> |
| Organization<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>created_at</td><td>false</td></tr><tr><td>description</td><td>true</td></tr><tr><td>display_name</td><td>true</td></tr><tr><td>icon</td><td>true</td></tr><tr><td>id</td><td>false</td></tr><tr><td>is_default</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>updated_at</td><td>true</td></tr></tbody></table> |
| Template<br><i>write, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>active_version_id</td><td>true</td></tr><tr><td>activity_bump</td><td>true</td></tr><tr><td>allow_user_autostart</td><td>true</td></tr><tr><td>allow_user_autostop</td><td>true</td></tr><tr><td>allow_user_cancel_workspace_jobs</td><td>true</td></tr><tr><td>autostart_block_days_of_week</td><td>true</td></tr><tr><td>autostop_requirement_days_of_week</td><td>true</td></tr><tr><td>autostop_requirement_weeks</td><td>true</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>created_by</td><td>true</td></tr><tr><td>created_by_avatar_url</td><td>false</td></tr><tr><td>created_by_username</td><td>false</td></tr><tr><td>default_ttl</td><td>true</td></tr><tr><td>deleted</td><td>false</td></tr><tr><td>deprecated</td><td>true</td></tr><tr><td>description</td><td>true</td></tr><tr><td>display_name</td><td>true</td></tr><tr><td>failure_ttl</td><td>true</td></tr><tr><td>group_acl</td><td>true</td></tr><tr><td>icon</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>max_port_sharing_level</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>organization_display_name</td><td>false</td></tr><tr><td>organization_icon</td><td>false</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>organization_name</td><td>false</td></tr><tr><td>provisioner</td><td>true</td></tr><tr><td>require_active_version</td><td>true</td></tr><tr><td>time_til_dormant</td><td>true</td></tr><tr><td>time_til_dormant_autodelete</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>user_acl</td><td>true</td></tr></tbody></table> |
| TemplateVersion<br><i>create, write</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>archived</td><td>true</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>created_by</td><td>true</td></tr><tr><td>created_by_avatar_url</td><td>false</td></tr><tr><td>created_by_username</td><td>false</td></tr><tr><td>external_auth_providers</td><td>false</td></tr><tr><td>id</td><td>true</td></tr><tr><td>job_id</td><td>false</td></tr><tr><td>message</td><td>false</td></tr><tr><td>name</td><td>true</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>readme</td><td>true</td></tr><tr><td>template_id</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr></tbody></table> |
| User<br><i>create, write, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>avatar_url</td><td>false</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>deleted</td><td>true</td></tr><tr><td>email</td><td>true</td></tr><tr><td>github_com_user_id</td><td>false</td></tr><tr><td>hashed_one_time_passcode</td><td>true</td></tr><tr><td>hashed_password</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>last_seen_at</td><td>false</td></tr><tr><td>login_type</td><td>true</td></tr><tr><td>must_reset_password</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>one_time_passcode_expires_at</td><td>true</td></tr><tr><td>quiet_hours_schedule</td><td>true</td></tr><tr><td>rbac_roles</td><td>true</td></tr><tr><td>status</td><td>true</td></tr><tr><td>theme_preference</td><td>false</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>username</td><td>true</td></tr></tbody></table> |
| Workspace<br><i>create, write, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>automatic_updates</td><td>true</td></tr><tr><td>autostart_schedule</td><td>true</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>deleted</td><td>false</td></tr><tr><td>deleting_at</td><td>true</td></tr><tr><td>dormant_at</td><td>true</td></tr><tr><td>favorite</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>last_used_at</td><td>false</td></tr><tr><td>name</td><td>true</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>owner_id</td><td>true</td></tr><tr><td>template_id</td><td>true</td></tr><tr><td>ttl</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr></tbody></table> |
| WorkspaceBuild<br><i>start, stop</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>build_number</td><td>false</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>daily_cost</td><td>false</td></tr><tr><td>deadline</td><td>false</td></tr><tr><td>id</td><td>false</td></tr><tr><td>initiator_by_avatar_url</td><td>false</td></tr><tr><td>initiator_by_username</td><td>false</td></tr><tr><td>initiator_id</td><td>false</td></tr><tr><td>job_id</td><td>false</td></tr><tr><td>max_deadline</td><td>false</td></tr><tr><td>provisioner_state</td><td>false</td></tr><tr><td>reason</td><td>false</td></tr><tr><td>template_version_id</td><td>true</td></tr><tr><td>transition</td><td>false</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>workspace_id</td><td>false</td></tr></tbody></table> |
| WorkspaceProxy<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>created_at</td><td>true</td></tr><tr><td>deleted</td><td>false</td></tr><tr><td>derp_enabled</td><td>true</td></tr><tr><td>derp_only</td><td>true</td></tr><tr><td>display_name</td><td>true</td></tr><tr><td>icon</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>region_id</td><td>true</td></tr><tr><td>token_hashed_secret</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>url</td><td>true</td></tr><tr><td>version</td><td>true</td></tr><tr><td>wildcard_hostname</td><td>true</td></tr></tbody></table> |
<!-- End generated by 'make docs/admin/security/audit-logs.md'. -->
## Filtering logs
In the Coder UI you can filter your audit logs using the pre-defined filter or
by using the Coder's filter query like the examples below:
- `resource_type:workspace action:delete` to find deleted workspaces
- `resource_type:template action:create` to find created templates
The supported filters are:
- `resource_type` - The type of the resource. It can be a workspace, template,
user, etc. You can
[find here](https://pkg.go.dev/github.com/coder/coder/v2/codersdk#ResourceType)
all the resource types that are supported.
- `resource_id` - The ID of the resource.
- `resource_target` - The name of the resource. Can be used instead of
`resource_id`.
- `action`- The action applied to a resource. You can
[find here](https://pkg.go.dev/github.com/coder/coder/v2/codersdk#AuditAction)
all the actions that are supported.
- `username` - The username of the user who triggered the action. You can also
use `me` as a convenient alias for the logged-in user.
- `email` - The email of the user who triggered the action.
- `date_from` - The inclusive start date with format `YYYY-MM-DD`.
- `date_to` - The inclusive end date with format `YYYY-MM-DD`.
- `build_reason` - To be used with `resource_type:workspace_build`, the
[initiator](https://pkg.go.dev/github.com/coder/coder/v2/codersdk#BuildReason)
behind the build start or stop.
## Capturing/Exporting Audit Logs
In addition to the user interface, there are multiple ways to consume or query
audit trails.
## REST API
Audit logs can be accessed through our REST API. You can find detailed
information about this in our
[endpoint documentation](../../reference/api/audit.md#get-audit-logs).
## Service Logs
Audit trails are also dispatched as service logs and can be captured and
categorized using any log management tool such as [Splunk](https://splunk.com).
Example of a [JSON formatted](../../reference/cli/server.md#--log-json) audit
log entry:
```json
{
"ts": "2023-06-13T03:45:37.294730279Z",
"level": "INFO",
"msg": "audit_log",
"caller": "/home/runner/work/coder/coder/enterprise/audit/backends/slog.go:36",
"func": "github.com/coder/coder/enterprise/audit/backends.slogBackend.Export",
"logger_names": ["coderd"],
"fields": {
"ID": "033a9ffa-b54d-4c10-8ec3-2aaf9e6d741a",
"Time": "2023-06-13T03:45:37.288506Z",
"UserID": "6c405053-27e3-484a-9ad7-bcb64e7bfde6",
"OrganizationID": "00000000-0000-0000-0000-000000000000",
"Ip": "{IPNet:{IP:\u003cnil\u003e Mask:\u003cnil\u003e} Valid:false}",
"UserAgent": "{String: Valid:false}",
"ResourceType": "workspace_build",
"ResourceID": "ca5647e0-ef50-4202-a246-717e04447380",
"ResourceTarget": "",
"Action": "start",
"Diff": {},
"StatusCode": 200,
"AdditionalFields": {
"workspace_name": "linux-container",
"build_number": "9",
"build_reason": "initiator",
"workspace_owner": ""
},
"RequestID": "bb791ac3-f6ee-4da8-8ec2-f54e87013e93",
"ResourceIcon": ""
}
}
```
Example of a [human readable](../../reference/cli/server.md#--log-human) audit
log entry:
```console
2023-06-13 03:43:29.233 [info] coderd: audit_log ID=95f7c392-da3e-480c-a579-8909f145fbe2 Time="2023-06-13T03:43:29.230422Z" UserID=6c405053-27e3-484a-9ad7-bcb64e7bfde6 OrganizationID=00000000-0000-0000-0000-000000000000 Ip=<nil> UserAgent=<nil> ResourceType=workspace_build ResourceID=988ae133-5b73-41e3-a55e-e1e9d3ef0b66 ResourceTarget="" Action=start Diff="{}" StatusCode=200 AdditionalFields="{\"workspace_name\":\"linux-container\",\"build_number\":\"7\",\"build_reason\":\"initiator\",\"workspace_owner\":\"\"}" RequestID=9682b1b5-7b9f-4bf2-9a39-9463f8e41cd6 ResourceIcon=""
```
## Enabling this feature
This feature is only available with an enterprise license.
[Learn more](../licensing/index.md)
+186
View File
@@ -0,0 +1,186 @@
# Database Encryption
By default, Coder stores external user tokens in plaintext in the database.
Database Encryption allows Coder administrators to encrypt these tokens at-rest,
preventing attackers with database access from using them to impersonate users.
## How it works
Coder allows administrators to specify
[external token encryption keys](../../reference/cli/server.md#external-token-encryption-keys).
If configured, Coder will use these keys to encrypt external user tokens before
storing them in the database. The encryption algorithm used is AES-256-GCM with
a 32-byte key length.
Coder will use the first key provided for both encryption and decryption. If
additional keys are provided, Coder will use it for decryption only. This allows
administrators to rotate encryption keys without invalidating existing tokens.
The following database fields are currently encrypted:
- `user_links.oauth_access_token`
- `user_links.oauth_refresh_token`
- `external_auth_links.oauth_access_token`
- `external_auth_links.oauth_refresh_token`
Additional database fields may be encrypted in the future.
> Implementation notes: each encrypted database column `$C` has a corresponding
> `$C_key_id` column. This column is used to determine which encryption key was
> used to encrypt the data. This allows Coder to rotate encryption keys without
> invalidating existing tokens, and provides referential integrity for encrypted
> data.
>
> The `$C_key_id` column stores the first 7 bytes of the SHA-256 hash of the
> encryption key used to encrypt the data.
>
> Encryption keys in use are stored in `dbcrypt_keys`. This table stores a
> record of all encryption keys that have been used to encrypt data. Active keys
> have a null `revoked_key_id` column, and revoked keys have a non-null
> `revoked_key_id` column. You cannot revoke a key until you have rotated all
> values using that key to a new key.
## Enabling encryption
> NOTE: Enabling encryption does not encrypt all existing data. To encrypt
> existing data, see [rotating keys](#rotating-keys) below.
- Ensure you have a valid backup of your database. **Do not skip this step.** If
you are using the built-in PostgreSQL database, you can run
[`coder server postgres-builtin-url`](../../reference/cli/server_postgres-builtin-url.md)
to get the connection URL.
- Generate a 32-byte random key and base64-encode it. For example:
```shell
dd if=/dev/urandom bs=32 count=1 | base64
```
- Store this key in a secure location (for example, a Kubernetes secret):
```shell
kubectl create secret generic coder-external-token-encryption-keys --from-literal=keys=<key>
```
- In your Coder configuration set `CODER_EXTERNAL_TOKEN_ENCRYPTION_KEYS` to a
comma-separated list of base64-encoded keys. For example, in your Helm
`values.yaml`:
```yaml
coder:
env:
[...]
- name: CODER_EXTERNAL_TOKEN_ENCRYPTION_KEYS
valueFrom:
secretKeyRef:
name: coder-external-token-encryption-keys
key: keys
```
- Restart the Coder server. The server will now encrypt all new data with the
provided key.
## Rotating keys
We recommend only having one active encryption key at a time normally. However,
if you need to rotate keys, you can perform the following procedure:
- Ensure you have a valid backup of your database. **Do not skip this step.**
- Generate a new encryption key following the same procedure as above.
- Add the above key to the list of
[external token encryption keys](../../reference/cli/server.md#--external-token-encryption-keys).
**The new key must appear first in the list**. For example, in the Kubernetes
secret created above:
```yaml
apiVersion: v1
kind: Secret
type: Opaque
metadata:
name: coder-external-token-encryption-keys
namespace: coder-namespace
data:
keys: <new-key>,<old-key1>,<old-key2>,...
```
- After updating the configuration, restart the Coder server. The server will
now encrypt all new data with the new key, but will be able to decrypt tokens
encrypted with the old key(s).
- To re-encrypt all encrypted database fields with the new key, run
[`coder server dbcrypt rotate`](../../reference/cli/server_dbcrypt_rotate.md).
This command will re-encrypt all tokens with the specified new encryption key.
We recommend performing this action during a maintenance window.
> Note: this command requires direct access to the database. If you are using
> the built-in PostgreSQL database, you can run
> [`coder server postgres-builtin-url`](../../reference/cli/server_postgres-builtin-url.md)
> to get the connection URL.
- Once the above command completes successfully, remove the old encryption key
from Coder's configuration and restart Coder once more. You can now safely
delete the old key from your secret store.
## Disabling encryption
To disable encryption, perform the following actions:
- Ensure you have a valid backup of your database. **Do not skip this step.**
- Stop all active coderd instances. This will prevent new encrypted data from
being written, which may cause the next step to fail.
- Run
[`coder server dbcrypt decrypt`](../../reference/cli/server_dbcrypt_decrypt.md).
This command will decrypt all encrypted user tokens and revoke all active
encryption keys.
> Note: for `decrypt` command, the equivalent environment variable for
> `--keys` is `CODER_EXTERNAL_TOKEN_ENCRYPTION_DECRYPT_KEYS` and not
> `CODER_EXTERNAL_TOKEN_ENCRYPTION_KEYS`. This is explicitly named differently
> to help prevent accidentally decrypting data.
- Remove all
[external token encryption keys](../../reference/cli/server.md#--external-token-encryption-keys)
from Coder's configuration.
- Start coderd. You can now safely delete the encryption keys from your secret
store.
## Deleting Encrypted Data
> NOTE: This is a destructive operation.
To delete all encrypted data from your database, perform the following actions:
- Ensure you have a valid backup of your database. **Do not skip this step.**
- Stop all active coderd instances. This will prevent new encrypted data from
being written.
- Run
[`coder server dbcrypt delete`](../../reference/cli/server_dbcrypt_delete.md).
This command will delete all encrypted user tokens and revoke all active
encryption keys.
- Remove all
[external token encryption keys](../../reference/cli/server.md#--external-token-encryption-keys)
from Coder's configuration.
- Start coderd. You can now safely delete the encryption keys from your secret
store.
## Troubleshooting
- If Coder detects that the data stored in the database was not encrypted with
any known keys, it will refuse to start. If you are seeing this behavior,
ensure that the encryption keys provided are correct.
- If Coder detects that the data stored in the database was encrypted with a key
that is no longer active, it will refuse to start. If you are seeing this
behavior, ensure that the encryption keys provided are correct and that you
have not revoked any keys that are still in use.
- Decryption may fail if newly encrypted data is written while decryption is in
progress. If this happens, ensure that all active coder instances are stopped,
and retry.
+20
View File
@@ -0,0 +1,20 @@
# Security Advisories
> If you discover a vulnerability in Coder, please do not hesitate to report it
> to us by following the instructions
> [here](https://github.com/coder/coder/blob/main/SECURITY.md).
From time to time, Coder employees or other community members may discover
vulnerabilities in the product.
If a vulnerability requires an immediate upgrade to mitigate a potential
security risk, we will add it to the below table.
Click on the description links to view more details about each specific
vulnerability.
---
| Description | Severity | Fix | Vulnerable Versions |
| --------------------------------------------------------------------------------------------------------------------------------------- | -------- | -------------------------------------------------------------- | ------------------- |
| [API tokens of deleted users not invalidated](https://github.com/coder/coder/blob/main/docs/security/0001_user_apikeys_invalidation.md) | HIGH | [v0.23.0](https://github.com/coder/coder/releases/tag/v0.23.0) | v0.8.25 - v0.22.2 |
+113
View File
@@ -0,0 +1,113 @@
# Secrets
<blockquote class="info">
This article explains how to use secrets in a workspace. To authenticate the
workspace provisioner, see <a href="/admin/auth">this</a>.
</blockquote>
Coder is open-minded about how you get your secrets into your workspaces.
## Wait a minute...
Your first stab at secrets with Coder should be your local method. You can do
everything you can locally and more with your Coder workspace, so whatever
workflow and tools you already use to manage secrets may be brought over.
Often, this workflow is simply:
1. Give your users their secrets in advance
1. Your users write them to a persistent file after they've built their
workspace
[Template parameters](../templates/extending-templates/parameters.md) are a
dangerous way to accept secrets. We show parameters in cleartext around the
product. Assume anyone with view access to a workspace can also see its
parameters.
## SSH Keys
Coder generates SSH key pairs for each user. This can be used as an
authentication mechanism for git providers or other tools. Within workspaces,
git will attempt to use this key within workspaces via the `$GIT_SSH_COMMAND`
environment variable.
Users can view their public key in their account settings:
![SSH keys in account settings](../../images/ssh-keys.png)
> Note: SSH keys are never stored in Coder workspaces, and are fetched only when
> SSH is invoked. The keys are held in-memory and never written to disk.
## Dynamic Secrets
Dynamic secrets are attached to the workspace lifecycle and automatically
injected into the workspace. With a little bit of up front template work, they
make life simpler for both the end user and the security team.
This method is limited to
[services with Terraform providers](https://registry.terraform.io/browse/providers),
which excludes obscure API providers.
Dynamic secrets can be implemented in your template code like so:
```tf
resource "twilio_iam_api_key" "api_key" {
account_sid = "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
friendly_name = "Test API Key"
}
resource "coder_agent" "main" {
# ...
env = {
# Let users access the secret via $TWILIO_API_SECRET
TWILIO_API_SECRET = "${twilio_iam_api_key.api_key.secret}"
}
}
```
A catch-all variation of this approach is dynamically provisioning a cloud
service account (e.g
[GCP](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/google_service_account_key#private_key))
for each workspace and then making the relevant secrets available via the
cloud's secret management system.
## Displaying Secrets
While you can inject secrets into the workspace via environment variables, you
can also show them in the Workspace UI with
[`coder_metadata`](https://registry.terraform.io/providers/coder/coder/latest/docs/resources/metadata).
![Secrets UI](../../images/admin/secret-metadata.PNG)
Can be produced with
```tf
resource "twilio_iam_api_key" "api_key" {
account_sid = "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
friendly_name = "Test API Key"
}
resource "coder_metadata" "twilio_key" {
resource_id = twilio_iam_api_key.api_key.id
item {
key = "Username"
value = "Administrator"
}
item {
key = "Password"
value = twilio_iam_api_key.api_key.secret
sensitive = true
}
}
```
## Secrets Management
For more advanced secrets management, you can use a secrets management tool to
store and retrieve secrets in your workspace. For example, you can use
[HashiCorp Vault](https://www.vaultproject.io/) to inject secrets into your
workspace.
Refer to our [HashiCorp Vault Integration](../integrations/vault.md) guide for
more information on how to integrate HashiCorp Vault with Coder.