{"id":23021184,"url":"https://github.com/edneville/please","last_synced_at":"2026-02-25T12:35:22.162Z","repository":{"id":54440749,"uuid":"289453935","full_name":"edneville/please","owner":"edneville","description":"please, a sudo clone","archived":false,"fork":false,"pushed_at":"2024-09-06T19:19:30.000Z","size":765,"stargazers_count":72,"open_issues_count":1,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-05T00:33:55.839Z","etag":null,"topics":["acl","clone","edit","elevation","privilege","regex","rust","sudo"],"latest_commit_sha":null,"homepage":"https://gitlab.com/edneville/please","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/edneville.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-08-22T08:55:09.000Z","updated_at":"2025-02-25T13:06:41.000Z","dependencies_parsed_at":"2024-03-01T21:23:42.094Z","dependency_job_id":"aee1dc0e-d250-4849-b910-2a18565a6e64","html_url":"https://github.com/edneville/please","commit_stats":null,"previous_names":[],"tags_count":22,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edneville%2Fplease","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edneville%2Fplease/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edneville%2Fplease/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edneville%2Fplease/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/edneville","download_url":"https://codeload.github.com/edneville/please/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243902279,"owners_count":20366259,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["acl","clone","edit","elevation","privilege","regex","rust","sudo"],"created_at":"2024-12-15T12:16:50.435Z","updated_at":"2026-02-25T12:35:22.121Z","avatar_url":"https://github.com/edneville.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Please, a sudo alternative\n\nDelegate accurate least privilege access with ease. Express easily with a regex and expose only what is needed and nothing more. Or validate file edits with `pleaseedit`.\n\nAdmin your box without giving users full root shells, most admins have experience of regex in one form or another, so lets configure access that way.\n\nI saw regex but don't like regex. No problem, you can still use please and pleaseedit without regex by using `exact_` counterparts, or treat each field/property as plain text, and escape control characters `?(){}[]+` etc. Most of the regex match statements have `exact` counterparts.\n\nPlease is written with memory safe rust. Traditional C memory unsafety is avoided, logic problems may exist but this codebase is relatively small.\n\n# How do I install it\n\nIt might already be in the repo that you're using:\n\n[![Packaging status](https://repology.org/badge/vertical-allrepos/pleaser.svg)](https://repology.org/project/pleaser/versions)\n\nIf not, it is a simple install:\n\n```\ngit clone https://gitlab.com/edneville/please.git\ncd please\ncargo test \u0026\u0026 cargo build --release \\\n\u0026\u0026 install -o 0 -g 0 -m4755 target/release/please target/release/pleaseedit /usr/local/bin\n```\n\nArch:\n\n```\npacman -Syu git fakeroot devtools binutils gcc rust \ngit clone https://aur@aur.archlinux.org/pleaser.git\ncd pleaser \u0026\u0026 makepkg -isr\n```\n\nDebian/Ubuntu:\n\n```\napt install pleaser\n```\n\nFedora (35):\n\n```\ndnf install pleaser\n```\n\nNetBSD:\n\n```\npkgin install pleaser\n```\n\nSUSE Tumbleweed:\n\n```\nzypper install pleaser\n```\n\nRHEL 7 (EPEL):\n```\nyum install cargo git pam-devel\ngit clone 'https://gitlab.com/edneville/please.git'\ncd please/\ncargo test \u0026\u0026 cargo build --release \u0026\u0026 install -oroot -groot -D -m4755 target/release/please target/release/pleaseedit /usr/local/bin\n```\n\nOptionally, set `sudo` as an alias of `please`:\n\n```\nalias sudo=\"please\"\nalias sudoedit=\"pleaseedit\"\n```\n\nOr, if you like, symlink in local:\n\n```\ncd /usr/local/bin \u0026\u0026 ln -s /usr/local/bin/please sudo \u0026\u0026 ln -s /usr/local/bin/pleaseedit sudoedit\n```\n\n# How do I set it up\n\nYou may need to configure PAM if you didn't use a distro package in order for `require_pass` to authenticate. Debian-based needs something similar to this in `/etc/pam.d/please` and `/etc/pam.d/pleaseedit`:\n\n```\n#%PAM-1.0\n\n# Set up user limits from /etc/security/limits.conf.\nsession    required   pam_limits.so\n\n@include common-auth\n@include common-account\n@include common-session-noninteractive\n```\n\nRed Hat based needs something similar to this in the same files:\n\n```\n#%PAM-1.0\nauth       include      system-auth\naccount    include      system-auth\npassword   include      system-auth\nsession    optional     pam_keyinit.so revoke\nsession    required     pam_limits.so\nsession    include      system-auth\n```\n\nNext, configure your `/etc/please.ini`, replace user names with appropriate values. The `ini` is divided into section options, matches and actions.\n\n## Section options\n\n| Part                        | Effect       |\n|-----------------------------|--------------|\n| [section-name]              | Section name, shown in list mode |\n| include=file                | Include file as another ini source, other options will be skipped in this section. |\n| includedir=dir              | Include dir of `.ini` files as other sources, in ascii sort order other options will be skipped in this section. Files not matching `.ini` will be ignored to allow for editor tmp files. |\n\n`include` and `includedir` will override mandatory arguments.\n\n## Matches\n\nOne of the simplest, that does not require password authentication can be defined as follows, assuming the user is `jim`:\n\nThe options are as follows:\n\n| Part                        | Effect       |\n|-----------------------------|--------------|\n| name=regex                  | Mandatory, apply configuration to this entity. |\n| target=regex                | May become these users. |\n| rule=regex                  | This is the command regex for the section, default is ^$ |\n| notbefore=YYYYmmdd          | The date, or YYYYmmddHHMMSS when this rule becomes effective. |\n| notafter=YYYYmmdd           | The date, or YYYYmmddHHMMSS when this rule expires. |\n| datematch=[Day dd Mon HH:MM:SS UTC YYYY] | regex to match against a date string |\n| type=[edit/run/list]        | Set the entry type, run = execution, edit = pleaseedit, list = show user rights |\n| group=[true/false]          | True to signify that name= refers to a group rather than a user. |\n| hostname=regex              | Hosts where this applies, defaults to 'localhost'. |\n| target_group=regex          | When set a group must be provided that matches |\n| dir=regex                   | Permit switching to regex defined directory prior to execution. |\n| permit_env=regex            | When combined with `-a`, permit matching environments keys |\n| search_path=string          | Change search_path to `:` separated directory list |\n\nExact counterparts, which must match exactly. When both regex and exact rules are present, the exact rule match will have precedence.\n\n| Part                        | Effect       |\n|-----------------------------|--------------|\n| exact_name=string           | Match this exact name |\n| exact_hostname=string       | Match this exact hostname |\n| exact_target=string         | Match this exact target user |\n| exact_target_group=string   | Match this exact target group |\n| exact_rule=string           | Match this exact rule |\n| exact_dir=string            | Match this exact directory |\n\n## Actions\n\n| Part                        | Effect       |\n|-----------------------------|--------------|\n| permit=[true/false]         | Defaults to true |\n| require_pass=[true/false]   | Defaults to true |\n| last=[true/false]           | When true, stop processing when matched, defaults to false |\n| reason=[true/false/regex]   | When set, require a reason provided by `-r`, defaults to false |\n| timeout=[number]            | How long to wait for password input, in whole seconds |\n| syslog=[true/false]         | Log this activity to syslog, default = true |\n| token_timeout=[number]      | How long the authentication token is valid for, in whole seconds |\n| env_assign.key=value        | Force environment **key** to be assigned **value** |\n| exitcmd=[program]           | (pleaseedit) Continue with file replacement if `program` exits 0 |\n| editmode=[octal mode/keep]  | (pleaseedit) Set destination file mode to `octal mode`, or keep the mode of an existing file. If the file is not present, or mode is not declared, then mode falls back to 0600. If there is a file present, then the mode is read and used just prior to file rename |\n\nUsing a greedy `.*` for the regex field will be as good as saying the rule should match any command. In previous releases there was no anchor (`^` and `$`) however, it seems more sensible to follow `find`'s approach and insist that there are anchors around the regex. This avoids `/bin/bash` matching `/home/user/bin/bash`.\n\nIf a `include` directive is met, no other entries in the section will be processed. The same goes for `includedir`.\n\nThe ordering of rules matters. The last match will win. Set `permit=false` if you wish to exclude something, but this should be very rare as the permit should be against a regex rather than using a positive and then a negative match. A rule of best practice is to avoid a fail open and then try and exclude most of the universe.\n\nFor example, using the two entries below:\n\n```\n[jim_root_du]\nname = jim\ntarget = root\npermit = true\nrule = ^(/usr)?/bin/du (/home/[a-z0-9-]+\\s?)+\nrequire_pass=false\n```\n\n```\n[jim_postgres]\nname = jim\ntarget = postgres\npermit = true\nrule = /bin/bash\nrequire_pass = false\n```\n\nWould permit running `du`, as `/usr/bin/du` or `/bin/du` as `root`:\n\n```\n$ please du /home/*\n```\n\nAnd would also permit running a bash shell as `postgres`:\n\n```\n$ please -t postgres /bin/bash\npostgres$\n```\n\n# Date ranges\n\nFor large environments it is not unusual for a third party to require access during a short time frame for debugging. To accommodate this there are the `notbefore` and `notafter` time brackets. These can be either `YYYYMMDD` or `YYYYMMDDHHMMSS`.\n\nThe whole day is considered when using the shorter date form of `YYYYMMDD`.\n\nMany enterprises may wish to permit access to a user for a limited time only, even if that individual is in the role permanently.\n\n# Date matches\n\nAnother date type is the `datematch` item, this constrains sections to a regex match against the date string `Day dd Mon HH:MM:SS UTC Year`.\n\nYou can permit some a group of users to perform some house keeping on a Monday:\n\n```\n[l2_housekeeping]\nname = l2users\ngroup = true\ntarget = root\npermit = true\nrule = /usr/local/housekeeping/tidy_(logs|images|mail)\ndatematch = ^Mon.*\n```\n\n# Default sections\n\nWhen a matching section name begins with `default` the actions will remain set until overwritten by another matching section. It is important to note that **permit=true** will be set implicitly on matches, therefore, unless there is good reason, set **permit=false** in default sections and **permit=true** in subsequent matching sections. See **please.ini** for further details.\n\n# pleaseedit\n\n`pleaseedit` enables editing of files as another user. Enable editing rather than execution with `type=edit`. The first argument will be passed to `EDITOR`.\n\nBy default file permission bits will mirror existing file permissions.\n\nThis is performed as follows:\n\n1. user runs edit as `pleaseedit -u root /etc/fstab`\n2. `/etc/fstab` is copied to `/tmp/pleaseedit.$USER.r8cYph9h._etc_fstab`\n3. user's `EDITOR` is executed against `/tmp/pleaseedit.$USER.r8cYph9h._etc_fstab`\n4. if `EDITOR` exits 0, and `exitcmd` exits 0, then `/tmp/pleaseedit.$USER.r8cYph9h._etc_fstab` is copied to `/etc/fstab.llD3wRQB.pleaseedit.copy.$USER`\n5.  `/etc/fstab.llD3wRQB.pleaseedit.copy.$USER` is set as (target) root owned and `renamed` to `/etc/fstab`\n\n# exitcmd\n\nexitcmd can be used prior to the tmp edit file move to the source location. This can be used to test configuration files are valid prior to renaming in place.\n\nFor something similar to apache, consider copying the config tree to a tmp directory before running the test to accommodate includes.\n\n# Other examples\n\nMembers of the `audio` group may remove temporary users that an application may not have cleaned up in the form of `username_tmp.\u003c10 random alphanumerics\u003e` using `userdel`:\n\n```\n[user_remove_tmp_user]\nname = audio\ngroup = true\npermit = true\nrequire_pass = false\nrule = /usr/sbin/userdel -f -r %{USER}_tmp\\.[a-zA-Z0-9]{10}\n```\n\nHow about, for the purpose of housekeeping, some users may be permitted to destroy zfs snapshots that look roughly like they're date stamped:\n\n```\n[user_remove_snapshots]\nname = data\ngroup = true\npermit = true\nrequire_pass = false\nrule = /usr/sbin/zfs destroy storage/photos@\\d{8}T\\d{6}\n```\n\nTo list what you may or may not do:\n\n```\n$ please -l\nYou may run the following:\n  file: /etc/please.ini\n    ed_root_list:root: ^.*$\nYou may edit the following:\n  file: /etc/please.ini\n    ed_edit_ini:root: ^/etc/please.ini$\n```\n\nThe above output shows that I may run anything and may edit the `please.ini` configuration. \n\nOr, perhaps any user who's name starts `admin` may execute `useradd` and `userdel`:\n\n```\n[admin_users]\nname = admin_\\S+\npermit = true\nrequire_pass = false\nrule = /usr/sbin/user(add -m|del) \\S+\n```\n\n# Files\n\n/etc/please.ini\n\n# Big installs\n\nFor big installs, consider the following:\n\n## Consolidate\n\nWhere you can use groups when all member least privilege matches the set. It is best here to consider that people often perform the same role, so try and organise the rules that way, so use either a group or list accounts in a single `name` regex match.\n\n## Central configuration considerations\n\nTo avoid single points of failure in a service, `ini` configuration should be generated in a single location and pushed to installs. `ini` files parse very quickly whilst accessing LDAP is not only slower but also error prone.\n\nIt could be possible to use caching, but a form of positive (correct match) and negative (incorrect match) would be required. 10,000 computers with hundreds of active users performing lookups against an LDAP server could be problematic.\n\nFor these reasons I prefer rsync distribution as the protocol is highly efficient and reduces network transfer overall.\n\nLDAP may at a later date be reconsidered.\n\n# Contributions\n\nShould you find anything that you feel is missing, regardless of initial design, please feel free to raise an issue with or without a pull request.\n\nLocating bugs and logging issues are very appreciated, and I thank you in advance.\n\nI welcome pull requests with open arms.\n\n# Locations\n\nThe source code for this project is currently hosted on [gitlab](https://gitlab.com/edneville/please) and mirrored to [github](https://github.com/edneville/please). There is a [crate on crates.io](https://crates.io/crates/pleaser). It also has a [homepage](https://www.usenix.org.uk/content/please.html) where other project information is kept.\n\n# Why pleaser in some circles?\n\nThis project is named \"please\". In some places that project name was used by others for other things. Some packages will be named pleaser, some will be named please. The only important thing is if you wish someone to make you a sandwich, just say \"please\" first.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fedneville%2Fplease","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fedneville%2Fplease","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fedneville%2Fplease/lists"}