woensdag 21 oktober 2009

Git daemon, SELinux and Fedora 12 Beta.

Recently i decided to redo my Git daemon domain and reinstall a Git daemon server.

This article will explain the issues i had to consider.

SELinux can be used to confined the Git daemon and Git Shell. I have not used SELinux in a optimal way here but i decided to implement a mix of MAC, DAC and Git ACL.

As for SELinux i have installed the following module:

gitd.te:

[code]

policy_module(gitd, 1.0.0)

########################################
#
# Git daemon global private declarations.
#

attribute gitd_type;
attribute gitd_content_type;

type gitd_exec_t;

# FIXME
type gitd_port_t;
corenet_port(gitd_port_t)

########################################
#
# Git daemon system private declarations.
#

##
##


## Allow Git-shell to modify and execute public files
## used for public file transfer services. Directories/Files must
## be labeled public_content_rw_t.
##


##
# gen_tunable(gitd_allow_anon_write, false)

##
##


## Allow Git daemon system to search home directories.
##


##
gen_tunable(gitd_system_enable_homedirs, false)

##
##


## Allow Git daemon system to access cifs file systems.
##


##
gen_tunable(gitd_system_use_cifs, false)

##
##


## Allow Git daemon system to access nfs file systems.
##


##
gen_tunable(gitd_system_use_nfs, false)

type gitd_system_t, gitd_type;
inetd_service_domain(gitd_system_t, gitd_exec_t)
role system_r types gitd_system_t;

type gitd_shared_t, gitd_content_type;
files_type(gitd_shared_t)

# permissive gitd_system_t;

########################################
#
# Git shell private declarations.
#

gen_require(`
attribute unpriv_userdomain, userdomain;
class context contains;
')

attribute gits_file_type;
attribute gits_usertype;

type gits_t, userdomain, gits_usertype, unpriv_userdomain;
domain_type(gits_t)

role gits_r types gits_t;
allow system_r gits_r;

corecmd_shell_entry_type(gits_t)
corecmd_bin_entry_type(gits_t)

domain_interactive_fd(gits_t)
domain_user_exemption_target(gits_t)

# permissive gits_t;

########################################
#
# Git daemon session session private declarations.
#

##
##


## Allow Git daemon session to bind
## tcp sockets to all unreserved ports.
##


##
gen_tunable(gitd_session_bind_all_unreserved_ports, false)

type gitd_session_t, gitd_type;
application_domain(gitd_session_t, gitd_exec_t)
ubac_constrained(gitd_session_t)

type gitd_personal_t, gitd_content_type;
userdom_user_home_content(gitd_personal_t)

# permissive gitd_session_t;

########################################
#
# Git daemon global private policy.
#

allow gitd_type self:fifo_file rw_fifo_file_perms;
allow gitd_type self:netlink_route_socket { create_socket_perms nlmsg_read };
allow gitd_type self:tcp_socket create_socket_perms;
allow gitd_type self:udp_socket create_socket_perms;
allow gitd_type self:unix_dgram_socket create_socket_perms;

# FIXME
allow gitd_type gitd_port_t:tcp_socket name_bind;

corenet_all_recvfrom_netlabel(gitd_type)
corenet_all_recvfrom_unlabeled(gitd_type)

corenet_tcp_sendrecv_all_if(gitd_type)
corenet_tcp_sendrecv_all_nodes(gitd_type)
corenet_tcp_sendrecv_all_ports(gitd_type)

corenet_tcp_bind_all_nodes(gitd_type)

corecmd_exec_bin(gitd_type)

files_read_etc_files(gitd_type)
files_read_usr_files(gitd_type)

fs_search_auto_mountpoints(gitd_type)

kernel_read_system_state(gitd_type)

logging_send_syslog_msg(gitd_type)

miscfiles_read_localization(gitd_type)

sysnet_read_config(gitd_type)

optional_policy(`
nis_use_ypbind(gitd_type)
')

optional_policy(`
nscd_read_pid(gitd_type)
')

########################################
#
# Git daemon system repository private policy.
#

list_dirs_pattern(gitd_system_t, gitd_content_type, gitd_content_type)
read_files_pattern(gitd_system_t, gitd_content_type, gitd_content_type)
files_search_var(gitd_system_t)

# This will not work since git-shell needs to execute gitd content thus public content files.
# There is currently no clean way to execute public content files.
# miscfiles_read_public_files(gitd_system_t)

tunable_policy(`gitd_system_enable_homedirs', `
userdom_search_user_home_dirs(gitd_system_t)
')

tunable_policy(`gitd_system_enable_homedirs && use_nfs_home_dirs', `
fs_list_nfs(gitd_system_t)
fs_read_nfs_files(gitd_system_t)
')

tunable_policy(`gitd_system_enable_homedirs && use_samba_home_dirs', `
fs_list_cifs(gitd_system_t)
fs_read_cifs_files(gitd_system_t)
')

tunable_policy(`gitd_system_use_cifs', `
fs_list_cifs(gitd_system_t)
fs_read_cifs_files(gitd_system_t)
')

tunable_policy(`gitd_system_use_nfs', `
fs_list_nfs(gitd_system_t)
fs_read_nfs_files(gitd_system_t)
')

########################################
#
# Git shell private policy.
#

allow gits_t self:context contains;
allow gits_t self:fifo_file rw_fifo_file_perms;

corecmd_exec_bin(gits_t)

kernel_read_system_state(gits_t)

files_read_etc_files(gits_t)

files_search_home(gits_t)

gitd_execute_shared_files(gits_t)
gitd_manage_shared_content(gits_t)

miscfiles_read_localization(gits_t)
# miscfiles_read_public_files(gits_t)

ssh_rw_stream_sockets(gits_t)

# FIXME
# This will not work since git-shell needs to execute gitd content thus public content files.
# There is currently no clean way to execute public content files.
# tunable_policy(`gitd_allow_anon_write', `
# miscfiles_exec_public_files(gits_t)
# miscfiles_manage_public_files(gits_t)
# ')

tunable_policy(`gitd_system_enable_homedirs && use_nfs_home_dirs', `
fs_exec_nfs_files(gits_t)
fs_manage_nfs_dirs(gits_t)
fs_manage_nfs_files(gits_t)
')

tunable_policy(`gitd_system_enable_homedirs && use_samba_home_dirs', `
fs_exec_cifs_files(gits_t)
fs_manage_cifs_dirs(gits_t)
fs_manage_cifs_files(gits_t)
')

tunable_policy(`gitd_system_use_cifs', `
fs_exec_cifs_files(gits_t)
fs_manage_cifs_dirs(gits_t)
fs_manage_cifs_files(gits_t)
')

tunable_policy(`gitd_system_use_nfs', `
fs_exec_nfs_files(gits_t)
fs_manage_nfs_dirs(gits_t)
fs_manage_nfs_files(gits_t)
')

optional_policy(`
nscd_read_pid(gits_t)
')

########################################
#
# Git daemon session repository private policy.
#

list_dirs_pattern(gitd_session_t, gitd_personal_t, gitd_personal_t)
read_files_pattern(gitd_session_t, gitd_personal_t, gitd_personal_t)
userdom_search_user_home_dirs(gitd_session_t)

userdom_use_user_terminals(gitd_session_t)

tunable_policy(`gitd_session_bind_all_unreserved_ports', `
corenet_tcp_bind_all_unreserved_ports(gitd_session_t)
')

tunable_policy(`use_nfs_home_dirs', `
fs_list_nfs(gitd_session_t)
fs_read_nfs_files(gitd_session_t)
')

tunable_policy(`use_samba_home_dirs', `
fs_list_cifs(gitd_session_t)
fs_read_cifs_files(gitd_session_t)
')
[/code]

gitd.if
[code]
## Git daemon is a really simple server for Git repositories.
##
##


## A really simple TCP git daemon that normally listens on
## port DEFAULT_GIT_PORT aka 9418. It waits for a
## connection asking for a service, and will serve that
## service if it is enabled.
##


##


## It verifies that the directory has the magic file
## git-daemon-export-ok, and it will refuse to export any
## git directory that has not explicitly been marked for
## export this way (unless the --export-all parameter is
## specified). If you pass some directory paths as
## git-daemon arguments, you can further restrict the
## offers to a whitelist comprising of those.
##


##


## By default, only upload-pack service is enabled, which
## serves git-fetch-pack and git-ls-remote clients, which
## are invoked from git-fetch, git-pull, and git-clone.
##


##


## This is ideally suited for read-only updates, i.e.,
## pulling from git repositories.
##


##


## An upload-archive also exists to serve git-archive.
##


##

#######################################
##
## Role access for Git daemon session.
##

##
##
## Role allowed access.
##

##
##
##
## User domain for the role.
##

##
#
interface(`gitd_session_role', `
gen_require(`
type gitd_session_t, gitd_exec_t, gitd_personal_t;
')

########################################
#
# Git daemon session shared declarations.
#

##
##


## Allow transitions to the Git daemon
## session domain.
##


##
gen_tunable(gitd_session_transition, false)

role $1 types gitd_session_t;

########################################
#
# Git daemon session shared policy.
#

tunable_policy(`gitd_session_transition', `
domtrans_pattern($2, gitd_exec_t, gitd_session_t)
', `
can_exec($2, gitd_exec_t)
')

allow $2 gitd_session_t:process { ptrace signal_perms };
ps_process_pattern($2, gitd_session_t)

exec_files_pattern($2, gitd_personal_t, gitd_personal_t)
manage_dirs_pattern($2, gitd_personal_t, gitd_personal_t)
manage_files_pattern($2, gitd_personal_t, gitd_personal_t)

relabel_dirs_pattern($2, gitd_personal_t, gitd_personal_t)
relabel_files_pattern($2, gitd_personal_t, gitd_personal_t)
')

########################################
##
## Allow the specified domain to execute
## Git daemon shared files.
##

##
##
## Domain allowed access.
##

##
##
#
interface(`gitd_execute_shared_files', `
gen_require(`
type gitd_shared_t;
')

exec_files_pattern($1, gitd_shared_t, gitd_shared_t)
files_search_var($1)
')

########################################
##
## Allow the specified domain to manage
## Git daemon shared content.
##

##
##
## Domain allowed access.
##

##
##
#
interface(`gitd_manage_shared_content', `
gen_require(`
type gitd_shared_t;
')

manage_dirs_pattern($1, gitd_shared_t, gitd_shared_t)
manage_files_pattern($1, gitd_shared_t, gitd_shared_t)
files_search_var($1)
')

########################################
##
## Allow the specified domain to manage
## Git daemon personal content.
##

##
##
## Domain allowed access.
##

##
##
#
interface(`gitd_manage_personal_content', `
gen_require(`
type gitd_personal_t;
')

manage_dirs_pattern($1, gitd_personal_t, gitd_personal_t)
manage_files_pattern($1, gitd_personal_t, gitd_personal_t)
files_search_home($1)
')

########################################
##
## Allow the specified domain to read
## Git daemon personal content.
##

##
##
## Domain allowed access.
##

##
##
#
interface(`gitd_read_personal_content', `
gen_require(`
type gitd_personal_t;
')

list_dirs_pattern($1, gitd_personal_t, gitd_personal_t)
read_files_pattern($1, gitd_personal_t, gitd_personal_t)
files_search_home($1)
')

########################################
##
## Allow the specified domain to read
## Git daemon shared content.
##

##
##
## Domain allowed access.
##

##
##
#
interface(`gitd_read_shared_content', `
gen_require(`
type gitd_shared_t;
')

list_dirs_pattern($1, gitd_shared_t, gitd_shared_t)
read_files_pattern($1, gitd_shared_t, gitd_shared_t)
files_search_var($1)
')

########################################
##
## Allow the specified domain to relabel
## Git daemon shared content.
##

##
##
## Domain allowed access.
##

##
##
#
interface(`gitd_relabel_shared_content', `
gen_require(`
type gitd_shared_t;
')

relabel_dirs_pattern($1, gitd_shared_t, gitd_shared_t)
relabel_files_pattern($1, gitd_shared_t, gitd_shared_t)
files_search_var($1)
')

########################################
##
## Allow the specified domain to relabel
## Git daemon personal content.
##

##
##
## Domain allowed access.
##

##
##
#
interface(`gitd_relabel_personal_content', `
gen_require(`
type gitd_personal_t;
')

relabel_dirs_pattern($1, gitd_personal_t, gitd_personal_t)
relabel_files_pattern($1, gitd_personal_t, gitd_personal_t)
files_search_home($1)
')

########################################
##
## All of the rules required to administrate an
## Git daemon system environment
##

##
##
## Prefix of the domain. Example, user would be
## the prefix for the user_t domain.
##

##
##
##
## Domain allowed access.
##

##
##
##
## The role to be allowed to manage the Git daemon domain.
##

##
##
#
interface(`gitd_system_admin', `
gen_require(`
type gitd_system_t, gitd_exec_t;
')

allow $1 gitd_system_t:process { getattr ptrace signal_perms };

kernel_search_proc($1)
allow $1 git_system_t:dir list_dir_perms;
read_files_pattern($1, gitd_system_t, gitd_system_t)
read_lnk_files_pattern($1, gitd_system_t, gitd_system_t)

manage_files_pattern($1, gitd_exec_t, gitd_exec_t)

# This will not work since git-shell needs to execute gitd content thus public content files.
# There is currently no clean way to execute public content files.
# miscfiles_manage_public_files($1)

gitd_manage_shared_content($1)
gitd_relabel_shared_content($1)

seutil_domtrans_setfiles($1)
')
[/code]

gitd.fc
[code]HOME_DIR/public_git(/.*)? gen_context(system_u:object_r:gitd_personal_t, s0)
HOME_DIR/\.gitconfig -- gen_context(system_u:object_r:gitd_personal_t, s0)

/srv/git(/.*)? gen_context(system_u:object_r:gitd_shared_t, s0)

/usr/libexec/git-core/git-daemon -- gen_context(system_u:object_r:gitd_exec_t, s0)

# Conflict with Fedora cgit fc spec.
# /var/lib/git(/.*)? gen_context(system_u:object_r:gitd_shared_t, s0)
[/code]

I manually labelled tcp:9418 gitd_port_t
[code]
semanage port -a -t gitd_port_t -p tcp 9418
[/code]

I also manually added a SELinux user mapping:

semanage user -a -L s0 -r s0 -R "gits_r" -P gits_u

echo "system_r:sshd_t:s0 gits_r:gits_t:s0" > /etc/selinux/targeted/contexts/users/gits_u

I edited /etc/xinetd.d/git:
[code]
# default: off
# description: The git dæmon allows git repositories to be exported using \
# the git:// protocol.

service git
{
disable = no
socket_type = stream
type = UNLISTED
port = 9418
wait = no
user = nobody
# server = /usr/bin/git
# server_args = daemon --base-path=/srv/git --export-all
--user-path=public_git --syslog --inetd --verbose
server = /usr/libexec/git-core/git-daemon
server_args = --base-path=/srv/git --export-all
--user-path=public_git --syslog --inetd --verbose
log_on_failure += USERID
# xinetd doesn't do this by default. bug #195265
flags = IPv6
}
[/code]

Basically the selinux policy has 3 parts. one part is for the gitd system inetd server. the second part is for running gitd as a unprivileged user, and the third part is policy for the system wide git-shell environment.
So we secure the system wide gitd process, gitd process that users run and the git-shell process that is used to push pull to shared repositories.

When you compile git-daemon, configured it like above and install the selinux module, restore the contexts of the paths in gitd.fc. then start xinetd: youll notice that inetd_t is not allowed to bind to gitd_port_t.
You can simply use audit2allow with the -M option to allow inetd_t to bind to gitd_port on behalf of gitd_system_t

I use DAC to give usergroups access to the particular repositories and i use git ACL to restrict access to branches, tags etc.

I used this great web site do implement this:

http://www.kernel.org/pub/software/scm/git/docs/everyday.html

I wrote two simple scripts:

1. to add new users
2. to add new repostiries

create_repository:

crepo.sh:
[code]
#!/bin/bash

# crepo.sh

groupadd $1 || exit 1;
usermod -a -G $1 badabing || exit 1;

mkdir /srv/git/$1.git || exit 1;
chmod -R +t /srv/git/$1.git || exit 1;
cd /srv/git/$1.git && git --bare init || exit 1;

cp /home/dgrift/create_repository/update /srv/git/$1.git/hooks/ || exit 1;
cp /home/dgrift/create_repository/allowed-users /srv/git/$1.git/info/ || exit 1;

chown -R nobody:$1 /srv/git/$1.git || exit 1;

chmod -R g+s /srv/git/$1.git/branches || exit 1;
chmod -R g+s /srv/git/$1.git/hooks || exit 1;
chmod -R g+s /srv/git/$1.git/info || exit 1;
chmod -R g+s /srv/git/$1.git/objects || exit 1;
chmod -R g+s /srv/git/$1.git/refs || exit 1;
chmod -R g+w /srv/git/$1.git || exit 1;

exit 0;

#EOF
[/code]

This script installs the git ACL files update and allowed-users:

update:
[code]
http://www.kernel.org/pub/software/scm/git/docs/howto/update-hook-example.txt
[/code]

allowed-users:
[code]
refs/heads/master badabing
+refs/heads/pu badabing
refs/heads/bw/.* badabing
refs/heads/tmp/.* .*
refs/tags/v[0-9].* badabing
[/code]

I also created a script to do part of what is required to add new users (i havent tested this script yet:

[code]
#!/bin/bash

# agits.sh

useradd -Z gits_u -s /usr/bin/git-shell $1 || exit 1;
usermod -a -G sshusers,$2 $1 || exit 1;

echo "Do not forget to set a password!"
echo "Manually add entries for $1 in /etc/security/namespace.conf!"
echo "You may need to edit /srv/git/$2.git/info/allowed-users!"

# TODO: usrquota

exit 0;
[/code]

By the way xinetd and selinux dont play nice so you may need to edit the xinetd init script:

[code]
https://bugzilla.redhat.com/show_bug.cgi?id=529681
[/code]

So that is basically it.
The SELinux policy for the git system service should work by default. Additionally there are some booleans to to toggle for example to allow the git system service to also host personal repositories in ~/public_git, allow git to use any unreserved port for mass git repostory hosting. enable disable transition to git_session_t which is the user daemon.
For this to work you must also enable git policy for users by creating a module:

for example if you want unconfined users to transition to git domain if they run git daemon <...>

myunconfined.te:
[code]
policy_module(myunconfined, 0.0.1)
optional_policy(`
gen_require(`
type unconfined_t;
')

git_session_role(unconfined_r, unconfined_t)
[/code]

build and install that:

make -f /usr/share/selinux/devel/Makefile
sudo semodule -i myunconfined.pp

That should work and make unconfined_t users transition to git_session_t when they run git daemon <...>

For me this setup works, but i have not thoroughly tested the git_session_t domain yet.

Would be nice to get some feedback so that i can improve this.


The git selinux policy is malformed above use the command below to pull the current gitd policy from my git repository

git clone git://
82.197.205.60/selinux-modules.git

dinsdag 6 oktober 2009

My view on domains, domain types, roles, domain transitions and more.

A domain is an environment in which a process operates. That environment is defined by the access vectors where a particular process is the sources.

Sometimes people refer to types of processes as domains. This is technically incorrect. Types of processes are domain types.

A domain is a general term for process environments. Processes can have different natures or properties. For example a user process environment can be called a domain and a process of a program can also be called a domain.

In SELinux world we like to "label" everything. So instead of calling a user process environment a domain we call it a user domain. The type of a user process is called a user domain type although technically its a domain type since user processes are processes.

Besides user domains, there are more different domains. These domains are defined by who transitioned to them. For example, init daemons are started by init scripts. The init script process sandbox is called init script domain and process environments that init script domains transition to are called init daemon domains.

Examples of init daemon domains are the environment of the httpd_t init daemon domain type, or postgresql. The main property is that these processes are started by init scripts.

Process environments of programs that user process environments transition to are called application domains, and the type of such a process is called a application domain type.

It depends on what transitions to what, that defines what domain it is. A cgi webapp that operates in its own environment is called a apache daemon domain if the httpd_t init daemon domain transitions to the process environment of a cgi webapp.

There are many more such domains and they are defined by what domain transitioned to them.

Domain transitions occur upon entrypoints. An entrypoint is a defined path to usually a executable file. Types of executable files are called executable file types. They are important in entrypoints to domains.

For example:

apache init script has a executable file type called for example httpd_initrc_exec_t.
When the init_t domain type executes the file with executable file type httpd_initrc_exec_t,
the process of that executable file type will get the initrc_t domain type and the process will operate in the init script domain (init script process environment).

The entrypoint is:
init_t -> httpd_initrc_exec_t

This entrypoint leads to the initrc_t process environment. (init script domain)

Now this starts all over again when the process that has domain type initrc_t runs the apache executable.

Assuming in this example that /usr/sbin/httpd is apaches executable file and that it has a executable file type of httpd_exec_t.

The entrypoint is:
initrc_t -> httpd_exec_t

The init script domain type runs the apache executable file type. There is a rule that says:
when initrc_t executes httpd_exec_t, then transition to the httpd_t process environment type.

initrc_t -> httpd_exec_t -> httpd_t

initrc_t is a (init script) domain type. policy where initrc_t is the source type is called a init script domain.

httpd_exec_t is a executable file type.
initrc_t -> httpd_exec_t is the entrypoint to the httpd_t domain (type)
httpd_t -> is a (init daemon) domain type. policy where httpd_t is the source type is called a init daemon domain.

There are api available that make transitioning to these types of domains easier.

For example you can simply call the init_daemon_domain() interface if your process is started by a init script. proper calling of this single interface will take care of some of the declarations that are required. it will also set up a domain transition pattern.

type myinitdaemondomaintype_t;
type myinitdaemondomaintypeexecutablefiletype_t;
init_daemon_domain(myinitdaemondomaintype_t, myinitdaemondomaintypeexecutablefiletype_t)

This will instruct SELinux to let processes that run in the initrc_t init script domain that execute files with executable file type myinitdaemondomaintypeexecutablefiletype_t, domain transition to the myinitdaemondomaintype_t process enviroment and give that process the init daemon domain type.

initrc_t -> myinitdaemondomaintypeexecutablefiletype_t -> myinitdaemondomaintype_t

This ofcourse requires that file files in question are labelled accordingly.

application domains are handled a bit differently. This is mainly because of RBAC.

Role based access control is a mechanism that allows a single user to operate in various environments (user domains)

This means that you must also define which role is allowed to use the target domain type.

user_t: user domain type
userapp_exec_t: (application) executable file type of the application to transition to.
userapp_t: application domain type

entrypoint:
user_t -> userapp_exec_t

Target of the entry point:
userapp_tRole of the user that this domain transition pattern is defined for:
user_r

type user_t;
type userapp_exec_t;
application_domain(user_t, userapp_exec_t)
role user_r types userapp_t;

domain_transition_pattern(user_t, userapp_exec_t, userapp_t)

Again same idea but its just a bit more complicated than the init daemon domain because users
(and applications started by users) can have different roles.

Applications started by init (system) can only have one role(system_r), So that makes the init daemon domains less complicated.

Explained:
When a user process that operates in the user_t user process environment runs a file with executable file type myapp_exec_t, then the process of that executable file type will run in the myapp_t application process environment (application domain).

Since users can have different roles we also define that particular role to have access to the application domain type.

We also manually define a domain transition pattern (user_t -> myapp_exec_t -> myapp_t)

Keep in mind that domains transitioned to by users also have to deal with roles, unlike domain transitioned to by system processes. processes where the role field in the context is system_r.

Your policy source has many example of the different domains. If you want to write policy for a init daemon than look up an example of a init daemon domain in the source policy, and see if that can get you started. Idem ditto for application domains, apache daemon domains, xinet service domains, dbus service domains and etcetera.

User domains are different. They arent started by other processes. instead a real person logs into the system and by running a tty or pts a new domain is initiated. These transtions are defined both in the user domain policy and selinux mappings, many of which can be defined with the semanage command.

for example:
(real human) -> tty_device_t -> user_t

I wont go into much detail here but i want to touch on the different user domains to consider. Users can have different roles. Roles are mappings to domains. and as explained domain are environments in which processes operate, defined by the policy in which a domain type is a source.

There are two different classes of user domain to consider. user domains that need a login environment, and user domains that do not need a login enviroment. i refer to them as primary and secondary user domains.

An example of a primary login user domain is staff_t. A user can login to a system in the staff_t user domain.

An example of a secundary user domain is webadm_t.

A primary user domain can be allowed to domain transition via roles (RBAC) to this secondary user domain. Thus may not be required to login and have a home directory and etcetera. Secondary user domains are often used for environments that have super user privileges. Like for example the webadm_t environment allows a user process to manage the webserver environment.

Have a look at the different defined user domain in the source policy. look at both staff domain and webadm domain and keep in mind that the first is a primary domain and the latter a secondary domain. By mapping the webadm_r and staff_r roles to a selinux user and mapping this selinux user to a linux login you can make selinux allow users that operate in the staff_t user domain to use Role based access control to domain transition to the secondary webadm_t user domain via sudo or su in conjunction with newrole.