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

Feature/rightsize privileges Execute tunneler with a least-privileged user account #599

Conversation

tomc797
Copy link
Contributor

@tomc797 tomc797 commented Feb 11, 2023

On Linux, a process granted the CAP_NET_ADMIN capability is sufficient to create a tun interface, configure network addresses, and other network-related operations. Testing shows that ziti-edge-tunnel can run as a non-root user if the process is granted CAP_NET_ADMIN.

To run the process as a least-privileged user, this PR performs:

  1. Modifies packaging to add a ziti user and group to the system, adjust directory and file permissions;
  2. Modifies the ziti-edge-tunnel.service to run ziti-edge-tunnel as ziti with CAP_NET_ADMIN; and
  3. Unix sockets are placed under the newly created directory /tmp/.ziti, whereas the directory has limited permissions.

I look forward working with you.

@tomc797 tomc797 requested a review from a team as a code owner February 11, 2023 03:07
@tomc797
Copy link
Contributor Author

tomc797 commented Feb 11, 2023

@qrkourier The CI pipeline failed due to inability to download a windows package. Do you mind re-running it?

@dovholuknf
Copy link
Member

@tomc797 i've resubmitted the job and sent your PR to a couple others for review. we'll be in touch. thx

Copy link
Member

@scareything scareything left a comment

Choose a reason for hiding this comment

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

Hello and thanks for your contribution! We've been thinking about making similar changes for a little while now but haven't gotten around to it yet, so thanks for taking the initiative!

I'm sorry that you've stumbled into this, but polkit / PolicyKit dependencies are kind of a mess when you want to support both redhat and Debian derived distros. The one major issue that I see with your PR in it's current state is that it will not produce packages that work (or work as intended, with reduced privileges) on red hat-based distros and/or future debian-based distros.

The .pkla file that this PR adds to permit the ziti user to set the dns server on a link is only used by the (very old) PolicyKit framework, which as far as I know is only used on debian and derivatives today. PolicyKit was replaced by polkit a while ago. polkit is configured with JavaScript .rules files (polkit can be configured with .pkla files but it requires the polkit-pkla-compat package on redhat, etc., and polkit-pkla on debian based distros that install polkit). All redhat/centos based distros have been using polkit for about a decade now. Debian-unstable is switching to a "transitional" policykit-1 package that actually installs the polkit framework.

So I think for this change to work on redhat and current + future Debian-based distros we need to either:

  1. react to the policy framework that's in play, and install a .rule or .plka file accordingly, OR
  2. always install a .pkla file, and declare a dependency on polkit-pkla-compat when polkit is being used to ensure that the .pkla will actually affect the system.

I'm guessing (2) is easier, I'm not exactly sure how to best implement it though. Maybe @sabedevops and @qrkourier have some ideas. With my shallow knowledge of cpack, etc., I might go about this by:

  • adding a dependency for polkit-pkla-compat on the RPM generator, since all supported rpm-based distros have polkit and need this package to process .pkla files, and
  • use Debian's "alternative package names" syntax to specify both/either policykit-1 or polkit-pkla to cover distros that use the transitional policykit-1 package.

I'm guessing that (1) would need to be handled as a post-install item. Either way though, the polkit/policykit dependencies probably should be correct if this route is taken.

references:

@@ -96,16 +103,26 @@ install(FILES "${INSTALL_OUT_DIR}/${SYSTEMD_EXECSTARTPRE}"
WORLD_READ WORLD_EXECUTE
COMPONENT "${COMPONENT_NAME}")

install(FILES "${INSTALL_OUT_DIR}/${ZITI_PKLA_FILE}"
Copy link
Member

Choose a reason for hiding this comment

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

In addition to depending on polkit-pkla[-compat] when appropriate, we also need to prevent the cpack generator from including "/var/lib/polkit-1" in the file list, lest our package conflicts with the system's polkit packages. I didn't know this until I tried to install an RPM built from your branch, but cpack creates an entry in the installed file list for each subdirectory in the path to every installed file.

$ sudo dnf install ./build/ziti-edge-tunnel-0.20.20-1.aarch64.rpm 
...
Error: Transaction test error:
  file /var/lib/polkit-1 from install of ziti-edge-tunnel-0.20.20-1.aarch64 conflicts with file from package polkit-pkla-compat-0.1-22.fc37.aarch64

$ rpm -qpl  ./build/ziti-edge-tunnel-0.20.20-1.aarch64.rpm 
/opt/openziti
/opt/openziti/bin
/opt/openziti/bin/ziti-edge-tunnel
/opt/openziti/bin/ziti-edge-tunnel.sh
/opt/openziti/etc
/opt/openziti/etc/identities
/opt/openziti/etc/ziti-edge-tunnel.env
/opt/openziti/share
/opt/openziti/share/ziti-edge-tunnel.service
/usr/lib/.build-id
/usr/lib/.build-id/ac
/usr/lib/.build-id/ac/dc95274f11a9715721121dbce3491715a0f5b4
/var
/var/lib
/var/lib/polkit-1
/var/lib/polkit-1/localauthority
/var/lib/polkit-1/localauthority/10-vendor.d
/var/lib/polkit-1/localauthority/10-vendor.d/ziti-edge-tunnel.pkla
/var/lib/ziti

A default list of excluded directories is in $CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST. It seems the best way to add your own exclusions is to set CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION (to "/var/lib/polkit-1" in this case).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Instead of depending on polkit-pkla[-compat], I create and install ziti-edge-tunnel.rules, which is the correct file for PolicyKit >= 106. I use CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION to exclude /usr/share/polkit-1/rules.d directory from the rpm.

@qrkourier qrkourier linked an issue Feb 16, 2023 that may be closed by this pull request
@tomc797 tomc797 force-pushed the feature/rightsize_privileges branch from 2400e0c to b5de08d Compare February 17, 2023 03:29
@tomc797
Copy link
Contributor Author

tomc797 commented Feb 17, 2023

Regarding #599 (review), I package a ziti-edge-tunnel.pkla and ziti-edge-tunnel.rules, policies that should encode identical authorizations. For fedora and ilk, only ziti-edge-tunnel.rules is packaged and installed. For debian and ubuntu, the ziti-edge-tunnel.pkla is installed if the directory /var/lib/polkit-1/localauthority/10-vendor.d exists; similarly, the ziti-edge-tunnel.rules is installed if /usr/lib/polkit-1/rules.d exists. This is done using ucf in postinst:configure.

@tomc797 tomc797 force-pushed the feature/rightsize_privileges branch from b5de08d to ee15428 Compare February 17, 2023 03:48

# install PolicyKit policies based on policy directory existance
ucf "@CPACK_SHARE_DIR@/@[email protected]" "/usr/share/polkit-1/rules.d/@ZITI_POLKIT_RULES_FILE@" 2>/dev/null || true
ucf "@CPACK_SHARE_DIR@/@[email protected]" "/var/lib/polkit-1/localauthority/10-vendor.d/@ZITI_POLKIT_PKLA_FILE@" 2>/dev/null || true
Copy link
Member

Choose a reason for hiding this comment

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

It looks like the expectation is that one of these ucf commands will succeed, but I don't think there's any guarantee of that. If the pkla or rules file is not installed, the ziti-edge-tunnel service won't start.

Would it be safer to determine the correct ucf command based on whether or not the rules.d directory exists, and let the exit status be seen by the parent shell?

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 decided on an alternate route based around debconf. After reviewing Ubuntu bionic, focal, jammy, and kinetic; and Debian 10, 11, and 12, I observed that systemd package installs rules even though they are not used. A compatible pkla is also installed.

The packaging is revised so that /usr/lib/polkit-1/rules.d/ziti-edge-tunnel.rules is installed. On Debian-based systems, ziti-edge-tunnel.pkla is installed if /var/lib/polkit-1/localauthority/10-vendor.d exists as a post installation step. The status of the pkla installation is tracked as a debconf template. If the pkla was installed, it is removed during the postrm phase.

@@ -26,3 +26,18 @@ fi
if [ -L /usr/bin/@CPACK_PACKAGE_NAME@ ]; then
unlink /usr/bin/@CPACK_PACKAGE_NAME@
fi

ucf --purge "/usr/share/polkit-1/rules.d/@ZITI_POLKIT_RULES_FILE@" || true
ucf --purge "/var/lib/polkit-1/localauthority/10-vendor.d/@ZITI_POLKIT_PKLA_FILE@" || true
Copy link
Member

Choose a reason for hiding this comment

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

in my testing, ucf --purge didn't remove the file. is it supposed to?

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 misread the ucf --purge documentation. Your observations are correct--the file isn't removed.

Package adds user and group `ziti', creates the variable state and
files, and adjust, where necessary, the directory and file
permissions.
Besides specifying the user in the service, a PolicyKit policy
permits the `ziti' to set resolve1 DNS parameters.
Not all unixes honor unix socket permissions. A portable solution
is to place the sockets in a newly create directory associated with
limited access permissions.
Previously, a failed enroll would strand an (empty) identity file.
Unless removed, further attempts to enroll would be blocked as the
identity file could be not exclusively created.
@tomc797 tomc797 force-pushed the feature/rightsize_privileges branch from ee15428 to 9105ed4 Compare February 27, 2023 19:44
@tomc797
Copy link
Contributor Author

tomc797 commented Feb 28, 2023

The revised code is updated to:

  • Install /usr/share/polkit-1/rules.d/ziti-edge-tunnel.rules,
  • On Debian-based systems, optionally install ziti-edge-tunnel.pkla if directory /var/lib/polkit-1/localauthority/10-vendor.d ``` exists,
  • Add user and group ziti to the system,
  • Update file and directory permissions,
  • Run the ziti-edge-tunnel as user ziti with capability CAP_NET_ADMIN,
  • Create directory /tmp/.ziti to house sockets (resolving, in part create a group "ziti" when installing ziti-edge-tunnel #553), and
  • Delete the identity file if enroll fails (resolving ZET creates empty config file if enroll fails #554)

@scareything
Copy link
Member

Thanks again for contributing these changes, @tomc797! You've probably noticed that the PR is approved. We will merge it soon, we just need to coordinate with our desktop UI project and a few users that will need to deal with the socket path change.

@scareything scareything changed the base branch from main to feature/rightsize_privileges March 2, 2023 14:56
@scareything scareything merged commit a53cbab into openziti:feature/rightsize_privileges Mar 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

create a group "ziti" when installing ziti-edge-tunnel
4 participants