Skip to content

Commit

Permalink
Merge pull request #47 from LeChatP/develop
Browse files Browse the repository at this point in the history
Fixes and tests + Better conflict resolution + Scenarios in documentation
  • Loading branch information
LeChatP authored May 19, 2024
2 parents 89960df + 114e0f6 commit e6d2d55
Show file tree
Hide file tree
Showing 12 changed files with 1,788 additions and 800 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ However you won't find out exact same options as sudo, you can use the `--role`

## Why do you need this tool ?

Traditional Linux system administration relies on a single powerful user, the superuser (root), who holds all system privileges. This model does not adhere to the principle of least privilege, as any program executed with superuser rights gains far more privileges than necessary. For example, `tcpdump`, a tool for sniffing network packets, only needs network capabilities. However, when run as the superuser, tcpdump gains all system privileges, including the ability to reboot the system. This excessive privilege can be exploited by attackers to compromise the entire system if tcpdump has vulnerabilities.
Traditional Linux system administration relies on a single powerful user, the superuser (root), who holds all system privileges. This model does not adhere to the principle of least privilege, as any program executed with superuser rights gains far more privileges than necessary. For example, `tcpdump`, a tool for sniffing network packets, only needs network capabilities. However, when run as the superuser, tcpdump gains all system privileges, including the ability to reboot the system. This excessive privilege can be exploited by attackers to compromise the entire system if tcpdump has vulnerabilities or their developers performs a supply chain attack.

The RootAsRole project offers a role-based approach for managing Linux capabilities. It includes the sr (switch role) tool, which allows users to control the specific privileges assigned to programs. This project eliminates the need for sudo and su commands, which do not provide fine-grained control over privileges.

Expand Down
57 changes: 53 additions & 4 deletions book/src/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,23 +34,23 @@ By using a role-based access control model, this project allows us to better man

## Scenarios

### Scenario 1: Personal usage
### Scenario 1: Installing a new package

You are using your personal computer and you want to install a new package. By default, RootAsRole add one role with 2 tasks : one task for using `chsr` command that grant only the `CAP_LINUX_IMMUTABLE` capability as `root` user (unprivileged), and one task for all commands but without `CAP_LINUX_IMMUTABLE` privilege.
You are using your personal computer and you want to install a new package. By default, RootAsRole add one role with 2 tasks : one task for using `chsr` command that grant only the `CAP_LINUX_IMMUTABLE` capability as `root` user (unprivileged), and one task for all commands but without `CAP_LINUX_IMMUTABLE` privilege. As installing a package may require almost all capabilities, you can use the default role to install a package. Indeed, if you wish to install apache2, you'll need `CAP_NET_BIND_SERVICE`, if you install docker you'll need many privileges, virtualbox needs `CAP_SYS_MODULE`, etc. So, you can use the default role to install a package:

```bash
sr apt install <package>
```

### Scenario 2: System administrator of a company
### Scenario 2: Granting users the right to restart their system

You are the system administrator of a company and you want to delegate the right to restart the server to a user. You can use `chsr` to create a role and grant the right to restart the server to users.

```bash
sr chsr role r_users add # Create a new role
sr chsr role r_users grant -g users # Grant the role to the group users
sr chsr role r_users task t_reboot add # Create a new task
sr chsr role r_users task t_reboot command whitelist add reboot # Add the reboot command to the task
sr chsr role r_users task t_reboot cmd whitelist add reboot # Add the reboot command to the task
sr chsr role r_users task t_reboot cred caps whitelist add CAP_SYS_BOOT # Add the CAP_SYS_BOOT capability to the task
```

Expand All @@ -59,3 +59,52 @@ Then users can restart the server with the following command:
```bash
sr reboot
```

### Scenario 3 : Passing environment variables to a command

You are a developer and you want to pass environment variables to a command. For example with sudo you can use the `-E` option to pass environment variables to a command. With RootAsRole, you'll need to setup a role with a task that allows the command to use environment variables. However, as you keep the default configuration, you'll have two roles that matches ANY commands, and if the first one is more restrictive than the second one, you'll need to specify the role to use. Here is an example:

```bash
sr chsr role env add # Create a new role
sr chsr role env task env add # Create a new task
sr chsr role env task env cmd setpolicy allow-all # Add all command to the task
sr chsr role env task env cred caps setpolicy allow-all # Add all capabilities to the task
sr chsr role env task env o env setpolicy keep-all # Keep the environment variables
```

Then you can use the following command to pass environment variables to a command:

```bash
sr -r env [command]
```

This is because the default role do not keep the environment variables, so if you want to keep environment variables you need to specify the role to use.

### Scenario 4 : Automating reboot every day

You are an administrator that want to automatically reboot the system at 04:05 every day with cron for example. You can disable authentication by setting skip-auth in the options. Here is an example:

```bash
sr chsr role auto add # Create a new role
sr chsr role grant -u cron # Grant the role to the user cron
sr chsr role auto task cron_reboot add # Create a new task
sr chsr role auto task cron_reboot cmd whitelist add reboot # Add the reboot command to the task
sr chsr role auto task cron_reboot cred caps whitelist add CAP_SYS_BOOT # Add the CAP_SYS_BOOT capability to the task
sr chsr role auto task cron_reboot o authentication skip # Skip authentication
```

Then you can configure the cron to reboot the system with the following command:

```bash
sr crontab -u cron -e
```

and add the following line to reboot the system at 04:05 every day

```cron
5 4 * * * sr -r auto -t cron_reboot reboot
```

Note: You should consider to set the `-r auto -t cron_reboot` options to the `sr` command when you automate a task to avoid any security issue or future conflict.

For a more complete example, you can checkout the [Is a Linux system without root user possible ?](knowledge/no-root.md) section.
1 change: 1 addition & 0 deletions book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
- [How does work role hierarchy feature](knowledge/role_hierarchy.md)
- [RootAsRole Command matching](knowledge/command_match.md)
- [What is eBPF ?](knowledge/ebpf.md)
- [Is a Linux system without root user possible ?](knowledge/no-root.md)

# Reference Guide

Expand Down
32 changes: 18 additions & 14 deletions book/src/knowledge/command_match.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,26 @@ As you may know with this RBAC model, it is possible for multiple roles to refer

* Find all the roles that match the user id assignment or the group id, and the command input
* Within the matching roles, select the one that is the most precise and least privileged :
1. user assignment is more precise than the combination of group assignment
1. the combination of group assignment is more precise than single group assignment
1. exact command is more precise than command with regex argument
1. command with regex argument is more precise than a wildcarded command path
1. wildcarded command path is more precise than wildcarded command path and regex args
1. wildcarded command path and regex args is more precise than complete wildcard
1. A role granting no capability is less privileged than one granting at least one capability
1. A role granting no insecure capability is less privileged than one at least one insecure capability
1. A role granting insecure capability is less privileged than one granting all capabilities.
1. A role without setuid is less privileged than one has setuid.
1. if no root is disabled, a role without 'root' setuid is less privileged than a role with 'root' setuid
1. A role without setgid is less privileged than one has setgid.
1. A role with a single setgid is less privileged than one that set multiple gid.
1. if no root is disabled, A role with multiple setgid is less privileged than one that set root gid
1. if no root is disabled, A role with root setgid is less privileged than one that set multiple gid, particularly using root group
1. A role that enables root privileges is less privileged than one which disables root privileges (see "no-root" feature)
1. A role that disables the Bounding set feature in RootAsRole is less privileged than one that enables it
1. A task granting no capability is less privileged than one granting at least one capability
1. A task granting no insecure capability is less privileged than one at least one insecure capability
1. A task granting insecure capability is less privileged than one granting all capabilities.
1. A task without setuid is less privileged than one has setuid.
1. if no root is disabled, a task without 'root' setuid is less privileged than a task with 'root' setuid
1. A task without setgid is less privileged than one has setgid.
1. A task with a single setgid is less privileged than one that set multiple gid.
1. if no root is disabled, A task with multiple setgid is less privileged than one that set root gid
1. if no root is disabled, A task with root setgid is less privileged than one that set multiple gid, particularly using root group
1. A task that requires authentication is less privileged than one that doesn't
1. A task that keeps safe PATH values is less privileged than one that doesn't
1. A task that keeps unsafe PATH values is less privileged than one that keep it safe
1. A task that keeps environment variables is less privileged than one that doesn't
1. A task that enables root privileges is less privileged than one which disables root privileges (see "no-root" feature)
1. A task that disables the Bounding set feature in RootAsRole is less privileged than one that enables it
1. user assignment is more precise than the combination of group assignment
1. the combination of group assignment is more precise than single group assignment

After these step, if two roles are conflicting, these roles are considered equal (only the environment variables are different), so configurator is being warned that roles could be in conflict and these could not be reached without specifing precisely the role to choose (with `--role` option). In such cases, we highly recommend to review the design of the configured access control.
After these step, if two roles are conflicting, these roles are considered equal. In this case if execution settings are totally equal, no matter which role is chosen, it execute the asked command. If execution settings are different, there is a conflict, so configurator is being warned that roles could be in conflict and these could not be reached without specifing precisely the role to choose (with `--role` or/and `--task` option). In such cases, we highly recommend to review the design of the configured access control.
41 changes: 41 additions & 0 deletions book/src/knowledge/no-root.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Is a Linux system without root user possible ?

To make it short, not really. But you can design your system to never have to use the root user. This is what RootAsRole aims, and the exact purpose of Linux Capabilities. Let's consider you want a system without root user and you want to setup a webserver. Firstly, let's create the apache2 user and group:

```bash
sr adduser apache2
```

We consider that we still use the default configuration of RootAsRole. Then, let's add a task to install apache2 with the apache2 user:

```bash
sr chsr r r_root t install_apache2 add
sr chsr r r_root t install_apache2 cmd whitelist add apt install apache2
sr chsr r r_root t install_apache2 cmd whitelist add "apt upgrade( -y)? apache2"
sr chsr r r_root t install_apache2 cred set --caps CAP_CHOWN,CAP_DAC_OVERRIDE,CAP_NET_BIND_SERVICE,CAP_SETUID --setuid apache2 --setgid apache2
```

Then, let's add a task to start apache2 with the apache2 user:

```bash
sr chsr r r_root t start_apache2 add
sr chsr r r_root t start_apache2 cmd whitelist add "systemctl ((re)?start|stop) apache2"
sr chsr r r_root t start_apache2 cmd whitelist add "service apache2 ((re)?start|stop)"
sr chsr r r_root t install_apache2 cred set --caps CAP_NET_BIND_SERVICE,CAP_SETUID --setuid apache2 --setgid apache2
```

So now you can install and start apache2 with the apache2 user:

```bash
sr apt install apache2
```

This should install apache2 configuration files owned by apache2 user and group. Then you can start apache2 with the apache2 user:

```bash
sr systemctl start apache2
```

This should start apache2 with the apache2 user. You can also stop it with the apache2 user:


4 changes: 4 additions & 0 deletions configure.sh
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ write() {
cp resources/rootasrole.json /etc/security || exit
echo "Define root role for the user $INSTALL_USER"
sed -i "s/ROOTADMINISTRATOR/$INSTALL_USER/g" /etc/security/rootasrole.json
if [[ ${DOCKER} -eq 1 ]]; then
sed -i "s/\"immutable\": true/\"immutable\": false/g" /etc/security/rootasrole.json
sed -i "s;\"CAP_LINUX_IMMUTABLE\";;g" /etc/security/rootasrole.json
fi
}

if [[ $INSTALL_USER == "0" ]]; then
Expand Down
35 changes: 20 additions & 15 deletions src/chsr/cli.pest
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ role_type_arg = @{ "actors" | "tasks" | all }


user_or_groups = { (user | group)+ }
user = ${ ("--user" ~ assignment? | "-u" ~ WHITESPACE) ~ actor_name }
group = ${ ("--group" ~ assignment? | "-g" ~ WHITESPACE) ~ name_combination }
name_combination = _{ actor_name ~ (("&" | ",") ~ name_combination) | actor_name }
user = ${ ("--user" ~ assignment | "-u" ~ WHITESPACE+) ~ actor_name }
group = ${ ("--group" ~ assignment | "-g" ~ WHITESPACE+) ~ name_combination }
name_combination = { actor_name ~ (("&" | ",") ~ name_combination) | actor_name }
actor_name = @{ (CASED_LETTER | "_") ~ ((CASED_LETTER | ASCII_DIGIT | "-" | "_"){,30} ~ "$" | (CASED_LETTER | ASCII_DIGIT | "-" | "_"){,31}) | ASCII_DIGIT+ }

// ========================
Expand Down Expand Up @@ -95,12 +95,12 @@ cred_set_args = _{
(cred_c | cred_u | cred_g)+
| help
}
cred_c = { ("--caps" ~ assignment? | "-c" ~ WHITESPACE) ~ capabilities? }
cred_u = { ("--setuid" ~ assignment? | "-u" ~ WHITESPACE) ~ actor_name }
cred_g = { ("--setgid" ~ assignment? | "-g" ~ WHITESPACE) ~ name_combination }
cred_c = ${ ("--caps" ~ assignment | "-c" ~ WHITESPACE+) ~ capabilities? }
cred_u = ${ ("--setuid" ~ assignment | "-u" ~ WHITESPACE+) ~ actor_name }
cred_g = ${ ("--setgid" ~ assignment | "-g" ~ WHITESPACE+) ~ name_combination }

capabilities = _{ capability ~ ("," | WHITESPACE) ~ capabilities | capability}
capability = { "CAP_"? ~ (LETTER | "_")+ }
capabilities = _{ capability ~ (","?) ~ capabilities | capability }
capability = @{ ^"CAP_"? ~ (LETTER | "_")+ }


// chsr r r1 t t1 cred caps setpolicy (deny-all|allow-all)
Expand All @@ -126,12 +126,13 @@ caps_listing = { (whitelist | blacklist) ~ ((add | del | set) ~ capabilities
// chsr o root (privileged|user|inherit)
// chsr o bounding (strict|ignore|inherit)
// chsr o wildcard-denied (set|add|del) *
// chsr o skip-auth (true|false)

// chsr o timeout set --type tty --duration 5:00 --max_usage 1
// chsr o t unset --type --duration --max_usage

options_operations = { ("options" | "o") ~ opt_args }
opt_args = _{ opt_show | opt_path | opt_env | opt_root | opt_bounding | opt_wildcard | opt_timeout }
opt_args = _{ opt_show | opt_path | opt_env | opt_root | opt_bounding | opt_wildcard | opt_timeout | opt_skip_auth }

opt_show = _{ list ~ opt_show_arg? }
opt_show_arg = { "all" | "cmd" | "cred" | "path" | "env" | "root" | "bounding" | "wildcard-denied" | "timeout" }
Expand All @@ -150,9 +151,9 @@ opt_env_setpolicy = { setpolicy ~ env_policy }
env_policy = { "delete-all" | "keep-all" | "inherit" }
opt_env_listing = { (whitelist | blacklist | checklist) ~ (((add | del | set) ~ env_list) | purge) }
opt_env_set = _{ set ~ env_list }
env_list = _{ env_key ~ (("," | WHITESPACE) ~ env_list) | env_key }
env_list = { env_key ~ ((","?) ~ env_list) | env_key }

env_key = { (CASED_LETTER | "_") ~ (CASED_LETTER | ASCII_DIGIT | "-" | "_")+ }
env_key = @{ (CASED_LETTER | "_") ~ (CASED_LETTER | ASCII_DIGIT | "-" | "_")+ }

opt_root = { "root" ~ (opt_root_args | help) }
opt_root_args = { "privileged" | "user" | "inherit" }
Expand All @@ -162,6 +163,10 @@ opt_bounding_args = { "strict" | "ignore" | "inherit" }

opt_wildcard = { "wildcard-denied" ~ (opt_wildcard_args | help) }
opt_wildcard_args = _{ (add | del | set) ~ wildcard_value }

opt_skip_auth = { ( "authentication" | "auth") ~ (opt_skip_auth_args | help) }
opt_skip_auth_args = { "skip" | "perform" | "inherit" }

wildcard_value = { name }


Expand All @@ -174,19 +179,19 @@ opt_timeout_args = _{
| opt_timeout_d_arg ~ (opt_timeout_t_arg ~ opt_timeout_m_arg? | opt_timeout_m_arg ~ opt_timeout_t_arg?)?
| opt_timeout_m_arg ~ (opt_timeout_t_arg ~ opt_timeout_d_arg? | opt_timeout_d_arg ~ opt_timeout_t_arg?)?
}
opt_timeout_t_arg = ${ ("--type" ~ assignment? | "-t" ~ WHITESPACE*) ~ opt_timeout_type? }
opt_timeout_t_arg = ${ ("--type" ~ assignment | "-t" ~ WHITESPACE+) ~ opt_timeout_type? }
opt_timeout_type = { "tty" | "ppid" | "uid" }
opt_timeout_d_arg = { ("--duration" ~ assignment? | "-d" ~ WHITESPACE*) ~ time? }
opt_timeout_d_arg = { ("--duration" ~ assignment | "-d" ) ~ time? }
time = { (hours~colon)? ~ minutes ~ colon ~ seconds | (minutes~colon)? ~ seconds }
colon = _{ ":"}
hours = _{ ASCII_DIGIT+ }
minutes = _{ ASCII_DIGIT+ }
seconds = _{ ASCII_DIGIT+ }
opt_timeout_m_arg = { ("--max-usage" ~ assignment? | "-m" ~ WHITESPACE*) ~ opt_timeout_max_usage? }
opt_timeout_m_arg = { ("--max-usage" ~ assignment | "-m" ) ~ opt_timeout_max_usage? }
opt_timeout_max_usage = { ASCII_DIGIT+ }


assignment = _{ "=" | WHITESPACE }
assignment = _{ "=" | WHITESPACE*}
help = { "-h" | "--help" }

NOT_ESCAPE_QUOTE = @{ !"\\" ~ ("\""|"'") }
Expand Down
Loading

0 comments on commit e6d2d55

Please sign in to comment.