Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFD: Add Automatic User Provisioning #11077

Merged
merged 1 commit into from
Jun 7, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
252 changes: 252 additions & 0 deletions rfd/0057-automatic-user-provisioning.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
---
authors: Alex McGrath (alex.mcgrath@goteleport.com)
state: draft
---

# RFD 57 - Automatic user and sudoers provisioning

## What

Automatically create non-existing users and optionally add them to
`sudoers` on Teleport nodes. Users will be removed after all sessions
have logged out.

## Why

Currently, when logging into an SSH node, the user must be
pre-created. Adding automatic user and `sudoer` provisioning would
make it so that any Teleport user would be able to login and have the
account created automatically without manual intervention.

## Details

The following are required for this feature:

- Ability to automatically provision a Linux user if it's not present
on the node.
- Ability to automatically provision a Linux group if it's not present
on the node.
- Ability to add the provisioned user to existing Linux groups defined
in the user traits/role.
lxea marked this conversation as resolved.
Show resolved Hide resolved
- Ability to add the provisioned user to sudoers.
- Clean up the provisioned user / sudoers changes upon logout (being
careful not to remove pre-existing users).

### Config/Role Changes

Several new fields will need to be added to to the role `options` and
`allow` sections:

```yaml
kind: role
version: v5
metadata:
name: example
spec:
options:
# Controls whether this role supports auto provisioning of users.
create_host_user: true
allow:
# New field listing Linux groups to assign a provisioned user to.
# Should support user and identity provider traits like other fields (e.g. "logins")
host_groups: [ubuntu, "{{internal.groups}}", "{{external.xxx}}"]
# host_sudoers is a list of entries to be included in a users sudoers file
host_sudoers: ["{{internal.logins}} ALL=(ALL) ALL", ...]
```

An individual `ssh_service` can be configured disable auto user
creation with the below config:

```yaml
ssh_service:
# when disabled, takes precedence over the role setting
disable_create_host_user: true
```

### User creation

In order to create users `useradd` will be executed from teleport
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth mentioning that useradd, compared to adduser, does not provision home directory. I think it should be fine for dynamic users. Any there any other differences between these 2 commands?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

adduser is distro specific so its sometimes not available or has different command sets available.

useradd can create home directories, i think it just doesnt by default, its flag is --create-home

after a user has tried to access a Teleport SSH node.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would agent recycle users that are inactive automatically? Same for sudoers lines

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean by recycle inactive users?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After sessions end the user and their sudoers files are deleted, if they're leftover (due to, for example a running process remaining) they and their sudoers file will be removed also.


#### User Groups

When a user is created they will be added to the specified groups from
the `host_groups` field in the role. In addition the user will be
added to a special `teleport-system` group which can be used to
indicate that the user was created by teleport and that its safe for
it to be deleted. The groups will be created via `groupadd` at startup
if they do not already exist and users will be added to groups via
`usermod -aG <list of groups> <username>`

#### Valid user/group names

The set of valid names that are valid on Linux varies between distros
and are generally more restrictive than the allowed usernames in
Teleport. This will require that names containing invalid characters
have those characters removed/replaced. Information on the valid
characters between Linux distros is available [here](https://systemd.io/USER_NAMES/).
The common core of valid characters is `^[a-z][a-z0-9-]{0,30}$`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make this configurable and start with this. This doesn't have @, _ which are pretty common. Also GCP will create users with their personalized emails.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm dont know how to make this configurable or what the ux should be like for such a thing, I intended to replace any characters that dont fit with -.

I was operating under the impression that the users unix username would need to match the teleport user however I think it would make sense that it be any user available in the logins trait, in which case it could be up to the user to provide an external trait -- something like {{external.linix_username}}, which conforms to the requirements of whatever Linux distro is being used and have Teleport try to blindly create users and just log an error when it fails


#### Adding and removing users from sudoers

Each user with entries in `host_sudoers` will have a file created in
`/etc/sudoers.d`, with one entry per line.

If a user is in multiple rules that specify `host_sudoers` they will
be all be concatenated together.

##### sudoers file syntax validation

If a system has `visudo` present, validation could be performed by
executing `visudo -c -f path/to/sudoersfile`, where if it fails to
validate, the user fails to have the shell start and the error is
reported.

##### sudoers security considerations

In order to stop users from being able to edit the sudoers file a
command allow list must be used, as or equivalent to below:

```
${USERNAME} ALL = (${USER TO RUN AS}) NOPASSWD: /bin/cmd1 args, /bin/cmd2 args
```

Should a user be given `root` access to all commands, they will be
able to modify any file, including sudoers files.


### User and group deletion

After all of a users sessions are logged out the created user and any
lxea marked this conversation as resolved.
Show resolved Hide resolved
`sudoers` files that were created for that user will be deleted if
that user is also a member of the `teleport-system` group.
Comment on lines +120 to +122
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do we deal with leftover files? Are we ok with the UID being reused by some other user (real or ephemeral) in the future?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As it stands I expected UIDs to be reused, but perhaps it could make sense to have user deletion set the shell to /bin/nologin or something, this way UIDs wont clash


Users can not be deleted while they have running processes so each
r0mant marked this conversation as resolved.
Show resolved Hide resolved
time a session ends, an attempt to delete the user can happen, if it
succeeds the sudoers file can also be removed.

If it does not succeed a cleanup process will run every 5 minutes, that
will attempt to delete users if they no longer have running processes.
This clean up process will also ensure that users with running
sessions during a restart will be cleaned up appropriately.

Groups will not be cleaned up and will be created once and be reused
this is to avoid files created with specified groups will remain
accessible between sessions to users in those groups.

### Multiple matching roles

Automatic user provisioning will require that all roles matching a
node via `labels` have `create_host_user=true`

## UX Examples
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Props for UX examples, makes it much easier to understand


### Teleport admin wants each user to have a dedicated host user defined by their Okta attributes
```yaml
kind: role
version: v5
metadata:
name: auto-user-groups
spec:
options:
# allow auto provisioning of users.
create_host_user: true
allow:
# username from external okta attribute
logins: [ "{{external.username}}" ]
```

### Teleport admin wants to define which Linux groups each auto-created user will be added to

```yaml
kind: role
version: v5
metadata:
name: auto-user-groups
spec:
options:
# allow auto provisioning of users.
create_host_user: true
allow:
# List of each group the user will be added to
host_groups: [ubuntu, docker, ...]
# username from external okta attribute
logins: [ "{{external.username}}" ]
```

### Teleport admin wants to make each auto-created user a sudoer

```yaml
kind: role
version: v5
metadata:
name: users-as-sudoers
spec:
options:
# allow auto provisioning of users.
create_host_user: true
allow:
# add users to the wheel group
host_groups: [wheel]
# make it so users in the wheel group will be able to execute sudoers commands without a password
host_sudoers: ["%wheel ALL=(ALL) NOPASSWD: ALL"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this field an array of entries for the sudoers file? Just want to make sure

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, each entry will be a new line

```

### Teleport admin wants to define particular commands user will be able to run as root
```yaml
kind: role
version: v5
metadata:
name: specify-commands-as-sudoers
spec:
options:
# allow auto provisioning of users.
create_host_user: true
allow:
# make it so this specific user can execute `systemctl restart nginx.service `
host_sudoers: ["{{internal.logins}} ALL = (root) NOPASSWD: /usr/bin/systemctl restart nginx.service"]
```

### Teleport admin wants to prohibit some nodes from auto-creating users

Include the below config for the Teleport node that should not allow automatic user creation:

```yaml
ssh_service:
enabled: "yes"
# stops a specific node from auto-creating users
disable_create_host_user: true
```

Nodes where `diable_create_host_user` is `false` will still be able to
have users be automatically created.

### Teleport user has multiple roles but not all of them enable `create_host_user`

In the situtation where a user has roles as below, the user would not
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems overly cautious - this means that all roles assigned to a user must have this on. What is the rationale for that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@r0mant suggested this, I assumed it was to avoid the possibility that a user accidentally receives too much access

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the idea was to have all roles that match the node you're SSH'ing into require "create_host_user: true", right?

In the example below, both roles match nodes with label env: example so most strict setting takes advantage and automatic user creation is basically disabled when you're SSH'ing into such a node.

If, however, your second role did not match env: example nodes, then the first role's setting would kick in when you're SSH'ing into env: example nodes so auto creation is enabled.

@lxea @klizhentas Does that behavior sound ok?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, but it could be confusing to some customers, please make it very clear in the docs

be able to make use of automatically provisioning users as both roles
do not enable `create_host_user`.

```yaml
kind: role
version: v5
metadata:
name: allow-access-and-auto-create
spec:
options:
# allow auto provisioning of users.
create_host_user: true
node_labels:
- 'env': 'example'
```

```yaml
kind: role
version: v5
metadata:
name: specify-commands-as-sudoers
spec:
options:
node_labels:
- 'env': 'example'
```