-
-
Notifications
You must be signed in to change notification settings - Fork 127
Implement optional admin authorization via qrexec #613
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,7 @@ | ||
| etc/polkit-1/rules.d/00-qubes-allow-all.rules | ||
| etc/qubes/admin-authzd.conf | ||
| etc/sudoers.d/qubes | ||
| usr/share/pam-configs/su.qubes | ||
| usr/share/pam-configs/qubes-admin-authz | ||
| usr/bin/qubes-admin-authzd | ||
| usr/lib/*/security/pam_qubes_admin_authz.so | ||
| lib/systemd/system/qubes-admin-authzd.service | ||
| usr/share/doc/qubes-core-agent-passwordless-root/README.md |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| pam_qubes_admin_authz.so | ||
| qubes-admin-authzd |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| # This will be re-preset on package upgrade. If you really want you can | ||
| # override this with a higher higher-priority preset file but that is probably | ||
| # a bad idea since it will break the pam module. | ||
| enable qubes-admin-authzd.service |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,25 @@ | ||
| BINDIR ?= /usr/bin | ||
| SYSCONFDIR ?= /etc | ||
| SUDOERSDIR = $(SYSCONFDIR)/sudoers.d | ||
| POLKIT1DIR = $(SYSCONFDIR)/polkit-1 | ||
| PAMDIR = $(SYSCONFDIR)/pam.d | ||
| PAMCONFIGSDIR = /usr/share/pam-configs/ | ||
| SYSLIBDIR ?= /lib | ||
|
|
||
| .PHONY: install install-debian install-rh | ||
| ifneq ($(DEB_HOST_MULTIARCH),) | ||
| PAM_MOD_DIR = /usr/lib/$(DEB_HOST_MULTIARCH)/security | ||
| else | ||
| PAM_MOD_DIR = /usr/lib64/security | ||
| endif | ||
|
|
||
| .PHONY: install install-debian install-rh all | ||
|
|
||
| all: qubes-admin-authzd pam_qubes_admin_authz.so | ||
|
|
||
| qubes-admin-authzd: qubes-admin-authzd.c qubes-admin-authz-common.h | ||
| gcc -O2 -Wall -Wextra -Werror -fPIC -pie $< -o $@ | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Include standard CFLAGS var too (both debian and fedora add preferred hardening options here) |
||
|
|
||
| pam_qubes_admin_authz.so: pam_qubes_admin_authz.c qubes-admin-authz-common.h | ||
| gcc -O2 -Wall -Wextra -Werror -fPIC -pie -shared $< -o $@ -lpam | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. and here too |
||
|
|
||
| install: | ||
| install -d -m 0750 $(DESTDIR)$(SUDOERSDIR) | ||
|
|
@@ -14,11 +29,36 @@ install: | |
| sed -E '/^[^#]/s/\<(ROLE|TYPE)=[A-Za-z0-9_]+[[:space:]]+//g' qubes.sudoers | \ | ||
| install -D -m 0440 /dev/stdin $(DESTDIR)$(SUDOERSDIR)/qubes; \ | ||
| fi | ||
| install -d -m 0750 $(DESTDIR)$(POLKIT1DIR)/rules.d | ||
| install -D -m 0644 polkit-1-qubes-allow-all.rules $(DESTDIR)$(POLKIT1DIR)/rules.d/00-qubes-allow-all.rules | ||
| install -D -t $(DESTDIR)$(PAM_MOD_DIR) pam_qubes_admin_authz.so | ||
| install -D -t $(DESTDIR)$(BINDIR) qubes-admin-authzd | ||
| install -D -m 0644 -t $(DESTDIR)$(SYSLIBDIR)/systemd/system qubes-admin-authzd.service | ||
| install -D -m 0644 -t $(DESTDIR)$(SYSCONFDIR)/qubes/ admin-authzd.conf | ||
| install -D -m 0644 -t $(DESTDIR)/usr/share/doc/qubes-core-agent-passwordless-root README.md | ||
|
|
||
| install-rh: | ||
| install -D -m 0644 pam.d_su.qubes $(DESTDIR)$(PAMDIR)/su.qubes | ||
| install -D -m 0644 -t $(DESTDIR)$(SYSLIBDIR)/systemd/system-preset 75-qubes-admin-authz.preset | ||
| install -d $(DESTDIR)/usr/share/authselect/vendor | ||
| for i in \ | ||
| local \ | ||
| nis \ | ||
| sssd \ | ||
| winbind \ | ||
| ; do \ | ||
| install -D -t $(DESTDIR)/usr/share/authselect/vendor/$$i \ | ||
| authselect/$$i/system-auth authselect/$$i/password-auth || exit 1; \ | ||
| for j in \ | ||
| README \ | ||
| REQUIREMENTS \ | ||
| dconf-db \ | ||
| dconf-locks \ | ||
| fingerprint-auth \ | ||
| nsswitch.conf \ | ||
| postlogin \ | ||
| smartcard-auth \ | ||
| ; do \ | ||
| ln -s ../../default/$$i/$$j $(DESTDIR)/usr/share/authselect/vendor/$$i/$$j || exit 1; \ | ||
| done; \ | ||
| done | ||
|
|
||
| install-debian: | ||
| install -D -m 0644 pam-configs_su.qubes $(DESTDIR)$(PAMCONFIGSDIR)/su.qubes | ||
| install -D -m 0644 pam-configs-qubes-admin-authz $(DESTDIR)$(PAMCONFIGSDIR)/qubes-admin-authz | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,82 @@ | ||||||
| # Passwordless in-qubqube admin authorization | ||||||
|
|
||||||
| By default all users in the `qubes` group (usually only the standard `user` | ||||||
| user) are allowed to execute things as root via sudo/su/pkexec. This is allowed | ||||||
| without a password prompt. | ||||||
|
|
||||||
| Separating the user isn't that meaningfull since Qubes' primary | ||||||
| compartimentalization layer are qubes and `user` in a qube has access most | ||||||
| things there anyway (user data, GUI I/O, etc). But some advanced users want | ||||||
| more options therefore this mechanism has been extended. | ||||||
|
|
||||||
| ## Modes | ||||||
|
|
||||||
| There are 3 modes: | ||||||
|
|
||||||
| - `allow`: Always allow admin access. | ||||||
| - `deny`: Always deny admin access. | ||||||
| - `qrexec`: Make a qrexec call and use it's result to allow/deny. The main | ||||||
| usage is to have a trivial qrexec service in dom0 and use the qrexex policy | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| with the ask action to allow only after prompting. | ||||||
|
|
||||||
| "Admin access" here means being able to run things as another user (including | ||||||
| root), via sudo, su and polkit. This is always limited to users in the `qubes` | ||||||
| group. | ||||||
|
|
||||||
|
|
||||||
| ## Config | ||||||
|
|
||||||
| When the `qubes-core-agent-passwordless-root` is installed the | ||||||
| pam_qubes_admin_authz.so is always enabled. This module asks the | ||||||
| `qubes-admin-authzd` daemon which does the actual logic (see below for | ||||||
| technical details). | ||||||
|
|
||||||
| The mode can be set via /usr/local/etc/qubes/admin-authzd.conf for per-qube | ||||||
| setting or more common via /etc/qubes/admin-authzd.conf in a template (setting | ||||||
| in /usr/local has precendce). | ||||||
|
Comment on lines
+34
to
+36
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe make it configurable via qvm-service (too?) so it's more compatible with disposable qubes.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Makes sense. Since services are boolean we need 3. Any preferences regarding naming?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Whonix would like for this to be configurable in Qube Manager, via a tri-select dropdown with a default setting. Is
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indeed a qvm-feature/qvm-prefs might be a better idea. We don't have clear guidelines which one to use when, but since it's mostly opaque config value from dom0 PoV, probably better qvm-feature. And add core-admin part that exposes it in qubesdb - probably with using check_with_template, to allow enabling the feature for all qubes based on the same template at once.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just found my own comment: QubesOS/qubes-issues#9512 (comment) |
||||||
|
|
||||||
| There config file have a very simpley syntax. The first line needs to contain | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| one of the listed mode above without anythings else. Currently the rest of the | ||||||
| file is ignored. Please prefix comments with `#` to allow extension of this | ||||||
| configuration file should the need arise. | ||||||
|
|
||||||
|
|
||||||
| ## qrexec policy | ||||||
|
|
||||||
| After setting the mode to qrexec, you need to configure the qrexec policy in | ||||||
| dom0. For example: | ||||||
|
|
||||||
| ``` | ||||||
| qubes.AuthorizeInVMAdminAccess * * @default ask target=dom0 default_target=dom0 | ||||||
| ``` | ||||||
|
|
||||||
| asks for requests from any VM. | ||||||
|
|
||||||
| Note that in dom0 only a trivial service is run that returns a fixed string | ||||||
| such that the qube knows the result of the policy evaluation. The idea here is | ||||||
| that this way the existing qrexec policy can be reused, including it's ask | ||||||
| prompt. | ||||||
|
|
||||||
|
|
||||||
| ## Limitaions | ||||||
|
|
||||||
| Keep in mind that if you have allowed admin access and the qube was compromised | ||||||
| at that point persistence is trivial. | ||||||
|
|
||||||
|
|
||||||
| ## Technical details | ||||||
|
|
||||||
| We implement a PAM module named `pam_qubes_admin_authz.so` to permit the | ||||||
| access. Since PAM modules can be run in a setuid context (when called by | ||||||
| sudo/su) we want to keep the code simple there. Therefore we use just some | ||||||
| existing PAM helper functions to check for the requsting users group membership | ||||||
| and check the PAM "service" (`sudo`, `su-l`, etc.) and if they match we make a | ||||||
| connection to an abstract unix socket. With `SO_PEERCRED` we check that this | ||||||
| socket has been opened by root. The rest, including config file handling and | ||||||
| invoking qrexec-client-vm is then handled by the `qubes-admin-authzd` at the | ||||||
| other end of the socket (started by qubes-admin-authzd.service). | ||||||
|
|
||||||
| ## Recovery | ||||||
|
|
||||||
| Should you have locked you self out you should still be able to use | ||||||
| `qvm-console-dispvm` and login as root there without a password. | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| allow | ||
|
|
||
| # The first line of this file needs to be 'deny', 'allow' or 'qrexec'. | ||
| # For more details see /usr/share/doc/qubes-core-agent-passwordless-root/README |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| auth required pam_env.so | ||
| auth required pam_faildelay.so delay=2000000 | ||
| auth required pam_faillock.so preauth silent {include if "with-faillock"} | ||
| auth sufficient pam_qubes_admin_authz.so | ||
| auth sufficient pam_u2f.so cue {include if "with-pam-u2f"} | ||
| auth required pam_u2f.so cue {if not "without-pam-u2f-nouserok":nouserok} {include if "with-pam-u2f-2fa"} | ||
| auth sufficient pam_unix.so {if not "without-nullok":nullok} | ||
| auth sufficient pam_systemd_home.so {include if "with-systemd-homed"} | ||
| auth required pam_faillock.so authfail {include if "with-faillock"} | ||
| auth optional pam_gnome_keyring.so only_if=login auto_start {include if "with-pam-gnome-keyring"} | ||
| auth required pam_deny.so | ||
|
|
||
| account required pam_access.so {include if "with-pamaccess"} | ||
| account required pam_faillock.so {include if "with-faillock"} | ||
| account sufficient pam_systemd_home.so {include if "with-systemd-homed"} | ||
| account required pam_unix.so | ||
|
|
||
| password sufficient pam_systemd_home.so {include if "with-systemd-homed"} | ||
| password requisite pam_pwquality.so | ||
| password [default=1 ignore=ignore success=ok] pam_localuser.so {include if "with-pwhistory"} | ||
| password requisite pam_pwhistory.so use_authtok {include if "with-pwhistory"} | ||
| password sufficient pam_unix.so yescrypt shadow {if not "without-nullok":nullok} use_authtok | ||
| password required pam_deny.so | ||
|
|
||
| session optional pam_keyinit.so revoke | ||
| session required pam_limits.so | ||
| session optional pam_ecryptfs.so unwrap {include if "with-ecryptfs"} | ||
| session optional pam_systemd_home.so {include if "with-systemd-homed"} | ||
| -session optional pam_systemd.so | ||
| session optional pam_oddjob_mkhomedir.so {include if "with-mkhomedir"} | ||
| session [success=1 default=ignore] pam_succeed_if.so service in crond quiet use_uid | ||
| session required pam_unix.so | ||
| session optional pam_gnome_keyring.so only_if=login auto_start {include if "with-pam-gnome-keyring"} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| auth required pam_env.so | ||
| auth required pam_faildelay.so delay=2000000 | ||
| auth required pam_faillock.so preauth silent {include if "with-faillock"} | ||
| auth sufficient pam_qubes_admin_authz.so | ||
| auth sufficient pam_fprintd.so {include if "with-fingerprint"} | ||
| auth sufficient pam_u2f.so cue {include if "with-pam-u2f"} | ||
| auth required pam_u2f.so cue {if not "without-pam-u2f-nouserok":nouserok} {include if "with-pam-u2f-2fa"} | ||
| auth sufficient pam_unix.so {if not "without-nullok":nullok} | ||
| auth sufficient pam_systemd_home.so {include if "with-systemd-homed"} | ||
| auth required pam_faillock.so authfail {include if "with-faillock"} | ||
| auth optional pam_gnome_keyring.so only_if=login auto_start {include if "with-pam-gnome-keyring"} | ||
| auth required pam_deny.so | ||
|
|
||
| account required pam_access.so {include if "with-pamaccess"} | ||
| account required pam_faillock.so {include if "with-faillock"} | ||
| account sufficient pam_systemd_home.so {include if "with-systemd-homed"} | ||
| account required pam_unix.so | ||
|
|
||
| password sufficient pam_systemd_home.so {include if "with-systemd-homed"} | ||
| password requisite pam_pwquality.so | ||
| password [default=1 ignore=ignore success=ok] pam_localuser.so {include if "with-pwhistory"} | ||
| password requisite pam_pwhistory.so use_authtok {include if "with-pwhistory"} | ||
| password sufficient pam_unix.so yescrypt shadow {if not "without-nullok":nullok} use_authtok | ||
| password required pam_deny.so | ||
|
|
||
| session optional pam_keyinit.so revoke | ||
| session required pam_limits.so | ||
| session optional pam_ecryptfs.so unwrap {include if "with-ecryptfs"} | ||
| session optional pam_systemd_home.so {include if "with-systemd-homed"} | ||
| -session optional pam_systemd.so | ||
| session optional pam_oddjob_mkhomedir.so {include if "with-mkhomedir"} | ||
| session [success=1 default=ignore] pam_succeed_if.so service in crond quiet use_uid | ||
| session required pam_unix.so | ||
| session optional pam_gnome_keyring.so only_if=login auto_start {include if "with-pam-gnome-keyring"} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| auth required pam_env.so | ||
| auth required pam_faildelay.so delay=2000000 | ||
| auth required pam_faillock.so preauth silent {include if "with-faillock"} | ||
| auth sufficient pam_qubes_admin_authz.so | ||
| auth sufficient pam_u2f.so cue {include if "with-pam-u2f"} | ||
| auth required pam_u2f.so cue {if not "without-pam-u2f-nouserok":nouserok} {include if "with-pam-u2f-2fa"} | ||
| auth sufficient pam_unix.so {if not "without-nullok":nullok} | ||
| auth sufficient pam_systemd_home.so {include if "with-systemd-homed"} | ||
| auth required pam_faillock.so authfail {include if "with-faillock"} | ||
| auth optional pam_gnome_keyring.so only_if=login auto_start {include if "with-pam-gnome-keyring"} | ||
| auth required pam_deny.so | ||
|
|
||
| account required pam_access.so {include if "with-pamaccess"} | ||
| account required pam_faillock.so {include if "with-faillock"} | ||
| account sufficient pam_systemd_home.so {include if "with-systemd-homed"} | ||
| account required pam_unix.so broken_shadow | ||
|
|
||
| password sufficient pam_systemd_home.so {include if "with-systemd-homed"} | ||
| password requisite pam_pwquality.so {if not "with-nispwquality":local_users_only} | ||
| password [default=1 ignore=ignore success=ok] pam_localuser.so {include if "with-pwhistory"} | ||
| password requisite pam_pwhistory.so use_authtok {include if "with-pwhistory"} | ||
| password sufficient pam_unix.so yescrypt shadow {if not "without-nullok":nullok} use_authtok nis | ||
| password required pam_deny.so | ||
|
|
||
| session optional pam_keyinit.so revoke | ||
| session required pam_limits.so | ||
| session optional pam_ecryptfs.so unwrap {include if "with-ecryptfs"} | ||
| session optional pam_systemd_home.so {include if "with-systemd-homed"} | ||
| -session optional pam_systemd.so | ||
| session optional pam_oddjob_mkhomedir.so {include if "with-mkhomedir"} | ||
| session [success=1 default=ignore] pam_succeed_if.so service in crond quiet use_uid | ||
| session required pam_unix.so | ||
| session optional pam_gnome_keyring.so only_if=login auto_start {include if "with-pam-gnome-keyring"} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a concrete scenario where
policy-rc.dblocks startup of something incorrectly? I'm a bit worried about assuming the user's intentions here, since Kicksecure's build system uses a "deny everything" policy-rc.d to prevent everything from starting, because of the fallout it can have on the rest of the build. Bypassing this mechanism seems dangerous at best. If a user does end up blocked, they can always open a DispVM console and log in as root, or useqvm-run -u root ....