rda-python-setuid 1.0.3__tar.gz → 1.0.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {rda_python_setuid-1.0.3 → rda_python_setuid-1.0.4}/MANIFEST.in +1 -0
- rda_python_setuid-1.0.4/PKG-INFO +125 -0
- rda_python_setuid-1.0.4/README.md +109 -0
- {rda_python_setuid-1.0.3 → rda_python_setuid-1.0.4}/pyproject.toml +4 -3
- rda_python_setuid-1.0.4/src/rda_python_setuid/install.py +156 -0
- rda_python_setuid-1.0.4/src/rda_python_setuid/install.usg +132 -0
- {rda_python_setuid-1.0.3 → rda_python_setuid-1.0.4}/src/rda_python_setuid/pgstart.py +14 -10
- rda_python_setuid-1.0.4/src/rda_python_setuid/pywrapper.c +115 -0
- rda_python_setuid-1.0.4/src/rda_python_setuid.egg-info/PKG-INFO +125 -0
- {rda_python_setuid-1.0.3 → rda_python_setuid-1.0.4}/src/rda_python_setuid.egg-info/SOURCES.txt +2 -0
- rda_python_setuid-1.0.4/src/rda_python_setuid.egg-info/entry_points.txt +4 -0
- rda_python_setuid-1.0.3/PKG-INFO +0 -17
- rda_python_setuid-1.0.3/README.md +0 -1
- rda_python_setuid-1.0.3/src/rda_python_setuid/pywrapper.c +0 -79
- rda_python_setuid-1.0.3/src/rda_python_setuid.egg-info/PKG-INFO +0 -17
- rda_python_setuid-1.0.3/src/rda_python_setuid.egg-info/entry_points.txt +0 -3
- {rda_python_setuid-1.0.3 → rda_python_setuid-1.0.4}/LICENSE +0 -0
- {rda_python_setuid-1.0.3 → rda_python_setuid-1.0.4}/setup.cfg +0 -0
- {rda_python_setuid-1.0.3 → rda_python_setuid-1.0.4}/src/rda_python_setuid/__init__.py +0 -0
- {rda_python_setuid-1.0.3 → rda_python_setuid-1.0.4}/src/rda_python_setuid/pywrapper.py +0 -0
- {rda_python_setuid-1.0.3 → rda_python_setuid-1.0.4}/src/rda_python_setuid.egg-info/dependency_links.txt +0 -0
- {rda_python_setuid-1.0.3 → rda_python_setuid-1.0.4}/src/rda_python_setuid.egg-info/requires.txt +0 -0
- {rda_python_setuid-1.0.3 → rda_python_setuid-1.0.4}/src/rda_python_setuid.egg-info/top_level.txt +0 -0
- {rda_python_setuid-1.0.3 → rda_python_setuid-1.0.4}/tests/test_setuid.py +0 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: rda_python_setuid
|
|
3
|
+
Version: 1.0.4
|
|
4
|
+
Summary: RDA Python Package to setuid for program executions as an effective or common user
|
|
5
|
+
Author-email: Zaihua Ji <zji@ucar.edu>
|
|
6
|
+
Project-URL: Homepage, https://github.com/NCAR/rda-python-setuid
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
+
Requires-Python: >=3.7
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Requires-Dist: rda_python_common
|
|
15
|
+
Dynamic: license-file
|
|
16
|
+
|
|
17
|
+
RDA Python package, including a C code wrapper, to execute commandline applications
|
|
18
|
+
via setuid for effective and common user names.
|
|
19
|
+
|
|
20
|
+
## Overview
|
|
21
|
+
|
|
22
|
+
`rda_python_setuid` provides a C binary (`pywrapper`) that acquires a setuid effective
|
|
23
|
+
user, then `execv`s a Python entry point script. This allows Python programs to run
|
|
24
|
+
as a designated common user (e.g. `gdexdata`) without requiring `sudo` access.
|
|
25
|
+
|
|
26
|
+
Two modes are supported:
|
|
27
|
+
|
|
28
|
+
- **Mode 1 (CommonUser program):** a symlink `dsarch -> pywrapper` runs `setuid_dsarch`
|
|
29
|
+
as the common user.
|
|
30
|
+
- **Mode 2 (pgstart specialist):** a copy `pgstart_zji` runs any command as specialist
|
|
31
|
+
`zji` via `pgstart.py`, restricted to authorized users.
|
|
32
|
+
|
|
33
|
+
## Dependency requirement
|
|
34
|
+
|
|
35
|
+
Any Python package whose programs are to be run via the setuid mechanism must declare
|
|
36
|
+
`rda_python_setuid` as a dependency in its `pyproject.toml`:
|
|
37
|
+
|
|
38
|
+
```toml
|
|
39
|
+
[project]
|
|
40
|
+
dependencies = [
|
|
41
|
+
"rda_python_setuid",
|
|
42
|
+
...
|
|
43
|
+
]
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
It must also register each wrapped program's connector entry point with a `setuid_`
|
|
47
|
+
prefix:
|
|
48
|
+
|
|
49
|
+
```toml
|
|
50
|
+
[project.scripts]
|
|
51
|
+
"setuid_dsarch" = "rda_python_dsarch.dsarch:main"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`pip install` then places `setuid_dsarch` in the environment's `bin/` directory
|
|
55
|
+
automatically. `pywrapper-install --link` locks it down to `chmod 700` so users
|
|
56
|
+
cannot bypass the setuid wrapper by running it directly.
|
|
57
|
+
|
|
58
|
+
## Environment setup
|
|
59
|
+
|
|
60
|
+
### Option A — Python venv (DECS machines)
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
python3 -m venv $ENVHOME # e.g. /glade/u/home/gdexdata/gdexmsenv
|
|
64
|
+
source $ENVHOME/bin/activate
|
|
65
|
+
pip install rda_python_setuid rda_python_dsarch ...
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Option B — Conda (DAV/Casper)
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
conda create -n pg-gdex python=3.10
|
|
72
|
+
conda activate pg-gdex
|
|
73
|
+
pip install rda_python_setuid rda_python_dsarch ...
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The conda environment is typically at `/glade/work/gdexdata/conda-envs/pg-gdex`.
|
|
77
|
+
|
|
78
|
+
## Installation
|
|
79
|
+
|
|
80
|
+
After setting up the environment and installing packages, run `pywrapper-install`
|
|
81
|
+
with no arguments to display the full user guide:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
pywrapper-install
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Full setuid setup (requires sudo access to CommonUser)
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# 1. Install the target package (pulls in rda_python_setuid automatically):
|
|
91
|
+
pip install rda_python_dsarch
|
|
92
|
+
|
|
93
|
+
# 2. Compile pywrapper C binary (once per environment):
|
|
94
|
+
pywrapper-install --user gdexdata
|
|
95
|
+
|
|
96
|
+
# 3. Wire up each program as a setuid entry:
|
|
97
|
+
pywrapper-install --link dsarch --user gdexdata
|
|
98
|
+
|
|
99
|
+
# 4. Optionally, allow a specialist to run commands as themselves:
|
|
100
|
+
pywrapper-install --pgstart --user zji
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Simple install (no sudo required, runs as current user)
|
|
104
|
+
|
|
105
|
+
Users who do not need the setuid mechanism can skip steps 2–4 and create a
|
|
106
|
+
direct symlink from `dsarch` to `setuid_dsarch`:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
pip install rda_python_dsarch
|
|
110
|
+
pywrapper-install --link dsarch --simple
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Runtime flow
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
user runs: dsarch [args]
|
|
117
|
+
| (symlink -> pywrapper, setuid bit -> EUID=gdexdata)
|
|
118
|
+
pywrapper.c: execv(bin/setuid_dsarch, args)
|
|
119
|
+
| (chmod 700, only gdexdata can exec directly)
|
|
120
|
+
setuid_dsarch: calls dsarch:main() as gdexdata
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Github
|
|
124
|
+
|
|
125
|
+
<https://github.com/NCAR/rda-python-setuid>
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
RDA Python package, including a C code wrapper, to execute commandline applications
|
|
2
|
+
via setuid for effective and common user names.
|
|
3
|
+
|
|
4
|
+
## Overview
|
|
5
|
+
|
|
6
|
+
`rda_python_setuid` provides a C binary (`pywrapper`) that acquires a setuid effective
|
|
7
|
+
user, then `execv`s a Python entry point script. This allows Python programs to run
|
|
8
|
+
as a designated common user (e.g. `gdexdata`) without requiring `sudo` access.
|
|
9
|
+
|
|
10
|
+
Two modes are supported:
|
|
11
|
+
|
|
12
|
+
- **Mode 1 (CommonUser program):** a symlink `dsarch -> pywrapper` runs `setuid_dsarch`
|
|
13
|
+
as the common user.
|
|
14
|
+
- **Mode 2 (pgstart specialist):** a copy `pgstart_zji` runs any command as specialist
|
|
15
|
+
`zji` via `pgstart.py`, restricted to authorized users.
|
|
16
|
+
|
|
17
|
+
## Dependency requirement
|
|
18
|
+
|
|
19
|
+
Any Python package whose programs are to be run via the setuid mechanism must declare
|
|
20
|
+
`rda_python_setuid` as a dependency in its `pyproject.toml`:
|
|
21
|
+
|
|
22
|
+
```toml
|
|
23
|
+
[project]
|
|
24
|
+
dependencies = [
|
|
25
|
+
"rda_python_setuid",
|
|
26
|
+
...
|
|
27
|
+
]
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
It must also register each wrapped program's connector entry point with a `setuid_`
|
|
31
|
+
prefix:
|
|
32
|
+
|
|
33
|
+
```toml
|
|
34
|
+
[project.scripts]
|
|
35
|
+
"setuid_dsarch" = "rda_python_dsarch.dsarch:main"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`pip install` then places `setuid_dsarch` in the environment's `bin/` directory
|
|
39
|
+
automatically. `pywrapper-install --link` locks it down to `chmod 700` so users
|
|
40
|
+
cannot bypass the setuid wrapper by running it directly.
|
|
41
|
+
|
|
42
|
+
## Environment setup
|
|
43
|
+
|
|
44
|
+
### Option A — Python venv (DECS machines)
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
python3 -m venv $ENVHOME # e.g. /glade/u/home/gdexdata/gdexmsenv
|
|
48
|
+
source $ENVHOME/bin/activate
|
|
49
|
+
pip install rda_python_setuid rda_python_dsarch ...
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Option B — Conda (DAV/Casper)
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
conda create -n pg-gdex python=3.10
|
|
56
|
+
conda activate pg-gdex
|
|
57
|
+
pip install rda_python_setuid rda_python_dsarch ...
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The conda environment is typically at `/glade/work/gdexdata/conda-envs/pg-gdex`.
|
|
61
|
+
|
|
62
|
+
## Installation
|
|
63
|
+
|
|
64
|
+
After setting up the environment and installing packages, run `pywrapper-install`
|
|
65
|
+
with no arguments to display the full user guide:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
pywrapper-install
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Full setuid setup (requires sudo access to CommonUser)
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
# 1. Install the target package (pulls in rda_python_setuid automatically):
|
|
75
|
+
pip install rda_python_dsarch
|
|
76
|
+
|
|
77
|
+
# 2. Compile pywrapper C binary (once per environment):
|
|
78
|
+
pywrapper-install --user gdexdata
|
|
79
|
+
|
|
80
|
+
# 3. Wire up each program as a setuid entry:
|
|
81
|
+
pywrapper-install --link dsarch --user gdexdata
|
|
82
|
+
|
|
83
|
+
# 4. Optionally, allow a specialist to run commands as themselves:
|
|
84
|
+
pywrapper-install --pgstart --user zji
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Simple install (no sudo required, runs as current user)
|
|
88
|
+
|
|
89
|
+
Users who do not need the setuid mechanism can skip steps 2–4 and create a
|
|
90
|
+
direct symlink from `dsarch` to `setuid_dsarch`:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
pip install rda_python_dsarch
|
|
94
|
+
pywrapper-install --link dsarch --simple
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Runtime flow
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
user runs: dsarch [args]
|
|
101
|
+
| (symlink -> pywrapper, setuid bit -> EUID=gdexdata)
|
|
102
|
+
pywrapper.c: execv(bin/setuid_dsarch, args)
|
|
103
|
+
| (chmod 700, only gdexdata can exec directly)
|
|
104
|
+
setuid_dsarch: calls dsarch:main() as gdexdata
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Github
|
|
108
|
+
|
|
109
|
+
<https://github.com/NCAR/rda-python-setuid>
|
|
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "rda_python_setuid"
|
|
9
|
-
version = "1.0.
|
|
9
|
+
version = "1.0.4"
|
|
10
10
|
authors = [
|
|
11
11
|
{ name="Zaihua Ji", email="zji@ucar.edu" },
|
|
12
12
|
]
|
|
@@ -30,11 +30,12 @@ include-package-data = true
|
|
|
30
30
|
where = ["src"]
|
|
31
31
|
|
|
32
32
|
[tool.setuptools.package-data]
|
|
33
|
-
"rda_python_setuid" = ["pywrapper.c"]
|
|
33
|
+
"rda_python_setuid" = ["pywrapper.c", "install.usg"]
|
|
34
34
|
|
|
35
35
|
[project.urls]
|
|
36
36
|
"Homepage" = "https://github.com/NCAR/rda-python-setuid"
|
|
37
37
|
|
|
38
38
|
[project.scripts]
|
|
39
|
-
"
|
|
39
|
+
"setuid_pywrapper" = "rda_python_setuid.pywrapper:main"
|
|
40
40
|
"pgstart.py" = "rda_python_setuid.pgstart:main"
|
|
41
|
+
"pywrapper-install" = "rda_python_setuid.install:main"
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
#
|
|
3
|
+
##################################################################################
|
|
4
|
+
#
|
|
5
|
+
# Title: pywrapper-install
|
|
6
|
+
# Author: Zaihua Ji, zji@ucar.edu
|
|
7
|
+
# Date: 2025-05-11
|
|
8
|
+
# Purpose: Install helper for the pywrapper setuid C binary.
|
|
9
|
+
# Replaces the manual gcc/chmod/ln steps with a single command.
|
|
10
|
+
#
|
|
11
|
+
# Github: https://github.com/NCAR/rda-python-setuid.git
|
|
12
|
+
#
|
|
13
|
+
# Usage:
|
|
14
|
+
# # 1. Compile and install pywrapper (run once per environment):
|
|
15
|
+
# pywrapper-install --user gdexdata [--envhome $ENVHOME/bin/]
|
|
16
|
+
#
|
|
17
|
+
# # 2. Create pgstart_USER entry so USER can run commands as themselves:
|
|
18
|
+
# pywrapper-install --pgstart --user zji [--envhome $ENVHOME/bin/]
|
|
19
|
+
#
|
|
20
|
+
# # 3. Create a symlink so a program runs as CommonUser via pywrapper (setuid):
|
|
21
|
+
# pywrapper-install --link myprog --user gdexdata [--envhome $ENVHOME/bin/]
|
|
22
|
+
#
|
|
23
|
+
# # 4. Simple install: symlink PROGRAM -> setuid_PROGRAM (no setuid, runs as current user):
|
|
24
|
+
# pywrapper-install --link myprog --simple [--envhome $ENVHOME/bin/]
|
|
25
|
+
#
|
|
26
|
+
# Convention for wrapped programs:
|
|
27
|
+
# The target package must register its connector entry point with a setuid_ prefix:
|
|
28
|
+
# [project.scripts]
|
|
29
|
+
# "setuid_dsarch" = "rda_python_dsarch.dsarch:main"
|
|
30
|
+
# pip install places setuid_dsarch in the bin dir automatically.
|
|
31
|
+
# pywrapper-install --link dsarch will chown setuid_dsarch to CommonUser and
|
|
32
|
+
# chmod 700, so users cannot run it directly and must go through the setuid wrapper.
|
|
33
|
+
# pywrapper-install --link dsarch --simple creates dsarch -> setuid_dsarch directly,
|
|
34
|
+
# skipping setuid; the program runs as the current user.
|
|
35
|
+
#
|
|
36
|
+
##################################################################################
|
|
37
|
+
|
|
38
|
+
import argparse
|
|
39
|
+
import os
|
|
40
|
+
import shutil
|
|
41
|
+
import subprocess
|
|
42
|
+
import sys
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def get_bindir():
|
|
46
|
+
"""Return the bin directory of the active Python environment."""
|
|
47
|
+
return os.path.dirname(os.path.abspath(sys.executable))
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def get_c_source():
|
|
51
|
+
"""Return path to pywrapper.c bundled with this package."""
|
|
52
|
+
return os.path.join(os.path.dirname(__file__), 'pywrapper.c')
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def run(cmd):
|
|
56
|
+
print(" $", " ".join(cmd))
|
|
57
|
+
subprocess.run(cmd, check=True)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def show_usage():
|
|
61
|
+
usgfile = os.path.join(os.path.dirname(__file__), 'install.usg')
|
|
62
|
+
os.system("more " + usgfile)
|
|
63
|
+
sys.exit(0)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def main():
|
|
67
|
+
|
|
68
|
+
if len(sys.argv) == 1:
|
|
69
|
+
show_usage()
|
|
70
|
+
|
|
71
|
+
parser = argparse.ArgumentParser(
|
|
72
|
+
description="Compile and install the pywrapper setuid C binary."
|
|
73
|
+
)
|
|
74
|
+
parser.add_argument(
|
|
75
|
+
'--envhome', default=None,
|
|
76
|
+
help="Path to the venv bin/ directory (default: bin/ dir of the current Python executable)"
|
|
77
|
+
)
|
|
78
|
+
parser.add_argument(
|
|
79
|
+
'--user', default=None,
|
|
80
|
+
help="User name to own the setuid binary (default: current login user for --pgstart, gdexdata otherwise)"
|
|
81
|
+
)
|
|
82
|
+
parser.add_argument(
|
|
83
|
+
'--simple', action='store_true',
|
|
84
|
+
help="Simple install: create symlink PROGRAM -> setuid_PROGRAM, skipping setuid (use with --link)"
|
|
85
|
+
)
|
|
86
|
+
group = parser.add_mutually_exclusive_group()
|
|
87
|
+
group.add_argument(
|
|
88
|
+
'--pgstart', action='store_true',
|
|
89
|
+
help="Create pgstart_USER for running commands as USER (Mode 2)"
|
|
90
|
+
)
|
|
91
|
+
group.add_argument(
|
|
92
|
+
'--link', metavar='PROGRAM',
|
|
93
|
+
help="Create symlink PROGRAM -> pywrapper for running a fixed program as CommonUser (Mode 1)"
|
|
94
|
+
)
|
|
95
|
+
args = parser.parse_args()
|
|
96
|
+
|
|
97
|
+
if args.user is None and not args.simple:
|
|
98
|
+
import pwd
|
|
99
|
+
if args.pgstart:
|
|
100
|
+
args.user = pwd.getpwuid(os.getuid()).pw_name
|
|
101
|
+
else:
|
|
102
|
+
args.user = 'gdexdata'
|
|
103
|
+
|
|
104
|
+
bindir = args.envhome or get_bindir()
|
|
105
|
+
pywrapper = os.path.join(bindir, 'pywrapper')
|
|
106
|
+
|
|
107
|
+
if args.link:
|
|
108
|
+
target = os.path.join(bindir, args.link)
|
|
109
|
+
script = os.path.join(bindir, 'setuid_{}'.format(args.link))
|
|
110
|
+
if not os.path.exists(script):
|
|
111
|
+
print("Error: {} not found. Install the package that provides it first.".format(script))
|
|
112
|
+
sys.exit(1)
|
|
113
|
+
if args.simple:
|
|
114
|
+
# Simple install: symlink PROGRAM -> setuid_PROGRAM, no setuid, runs as current user.
|
|
115
|
+
if os.path.lexists(target):
|
|
116
|
+
print("Already exists: {}".format(target))
|
|
117
|
+
else:
|
|
118
|
+
os.symlink(script, target)
|
|
119
|
+
print("Created: {} -> setuid_{}".format(target, args.link))
|
|
120
|
+
else:
|
|
121
|
+
# Mode 1: symlink PROGRAM -> pywrapper, then lock down setuid_PROGRAM
|
|
122
|
+
# so users cannot bypass the setuid wrapper by running it directly.
|
|
123
|
+
if os.path.lexists(target):
|
|
124
|
+
print("Already exists: {}".format(target))
|
|
125
|
+
else:
|
|
126
|
+
os.symlink(pywrapper, target)
|
|
127
|
+
print("Created: {} -> pywrapper".format(target))
|
|
128
|
+
# chown and chmod 700: only CommonUser can execute setuid_PROGRAM directly.
|
|
129
|
+
# pywrapper (running as CommonUser via setuid) can still execv it, but any
|
|
130
|
+
# direct invocation by other users will get "permission denied".
|
|
131
|
+
run(['sudo', '-u', args.user, 'chown', args.user, script])
|
|
132
|
+
run(['sudo', '-u', args.user, 'chmod', '700', script])
|
|
133
|
+
print("Locked: {} (chmod 700, owned by {})".format(script, args.user))
|
|
134
|
+
|
|
135
|
+
elif args.pgstart:
|
|
136
|
+
# Mode 2: copy pywrapper to pgstart_USER with setuid owned by USER
|
|
137
|
+
if not os.path.exists(pywrapper):
|
|
138
|
+
print("Error: {} not found. Run pywrapper-install --user COMMONUSER first.".format(pywrapper))
|
|
139
|
+
sys.exit(1)
|
|
140
|
+
target = os.path.join(bindir, 'pgstart_{}'.format(args.user))
|
|
141
|
+
run(['sudo', '-u', args.user, 'cp', pywrapper, target])
|
|
142
|
+
run(['sudo', '-u', args.user, 'chmod', '4750', target])
|
|
143
|
+
print("Installed: {} (setuid, owned by {})".format(target, args.user))
|
|
144
|
+
|
|
145
|
+
else:
|
|
146
|
+
# Default: compile pywrapper.c and install pywrapper with setuid
|
|
147
|
+
src = get_c_source()
|
|
148
|
+
src_dest = os.path.join(bindir, 'pywrapper.c')
|
|
149
|
+
shutil.copy(src, src_dest)
|
|
150
|
+
print("Copied: {}".format(src_dest))
|
|
151
|
+
run(['sudo', '-u', args.user, 'gcc', '-o', pywrapper, src_dest])
|
|
152
|
+
run(['sudo', '-u', args.user, 'chmod', '4750', pywrapper])
|
|
153
|
+
print("Installed: {} (setuid, owned by {})".format(pywrapper, args.user))
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
if __name__ == '__main__': main()
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
Compile and install the pywrapper setuid C binary, and set up program entries
|
|
2
|
+
that execute Python scripts as a common or effective user via the setuid mechanism.
|
|
3
|
+
Must be run inside the target Python virtual environment.
|
|
4
|
+
|
|
5
|
+
Usage: pywrapper-install --user USER [--envhome BINDIR] [--pgstart | --link PROGRAM [--simple]]
|
|
6
|
+
|
|
7
|
+
- Option --user USER
|
|
8
|
+
The user name to own the setuid binary. For Mode 1 (CommonUser program),
|
|
9
|
+
this is the common user (e.g. gdexdata). For Mode 2 (pgstart), this is
|
|
10
|
+
the specialist user (e.g. zji). Defaults to 'gdexdata' unless --pgstart
|
|
11
|
+
is given, in which case it defaults to the current login user.
|
|
12
|
+
|
|
13
|
+
- Option --envhome BINDIR
|
|
14
|
+
Path to the venv bin/ directory. Defaults to the bin/ directory of the
|
|
15
|
+
currently active Python executable. Only needed when installing into an
|
|
16
|
+
environment other than the one currently active.
|
|
17
|
+
|
|
18
|
+
- Option --pgstart
|
|
19
|
+
Mode 2: copy the compiled pywrapper binary to pgstart_USER (owned by USER,
|
|
20
|
+
chmod 4750). Allows USER to run arbitrary commands as themselves via the
|
|
21
|
+
setuid wrapper. Cannot be combined with --link.
|
|
22
|
+
|
|
23
|
+
- Option --link PROGRAM
|
|
24
|
+
Mode 1: create a symlink PROGRAM -> pywrapper in the bin/ directory, then
|
|
25
|
+
lock down setuid_PROGRAM (chmod 700, owned by USER) so users cannot bypass
|
|
26
|
+
the setuid wrapper by running setuid_PROGRAM directly. Cannot be combined
|
|
27
|
+
with --pgstart.
|
|
28
|
+
|
|
29
|
+
- Option --simple (use with --link)
|
|
30
|
+
Simple install: create a symlink PROGRAM -> setuid_PROGRAM directly,
|
|
31
|
+
skipping the setuid mechanism entirely. The program runs as the current
|
|
32
|
+
user with no privilege change. --user is not required with this option.
|
|
33
|
+
Useful for users who do not need or cannot set up the setuid wrapper.
|
|
34
|
+
|
|
35
|
+
When run without --pgstart or --link, the default action is to compile pywrapper.c
|
|
36
|
+
(bundled with this package) and install it as bin/pywrapper, owned by USER with
|
|
37
|
+
chmod 4750 (setuid). This step must be done once per environment before using
|
|
38
|
+
--pgstart or --link.
|
|
39
|
+
|
|
40
|
+
Convention for wrapped programs:
|
|
41
|
+
Any Python package whose program is to be run via pywrapper must:
|
|
42
|
+
|
|
43
|
+
1. Declare rda_python_setuid as a dependency in pyproject.toml:
|
|
44
|
+
|
|
45
|
+
[project]
|
|
46
|
+
dependencies = [
|
|
47
|
+
"rda_python_setuid",
|
|
48
|
+
...
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
This ensures pywrapper-install is available in the environment after
|
|
52
|
+
pip install.
|
|
53
|
+
|
|
54
|
+
2. Register its connector entry point with a setuid_ prefix in pyproject.toml:
|
|
55
|
+
|
|
56
|
+
[project.scripts]
|
|
57
|
+
"setuid_dsarch" = "rda_python_dsarch.dsarch:main"
|
|
58
|
+
|
|
59
|
+
pip install then places setuid_dsarch in the bin/ directory automatically.
|
|
60
|
+
Running pywrapper-install --link will lock it down (chmod 700) so that only
|
|
61
|
+
pywrapper (running as CommonUser via its setuid bit) can execv it.
|
|
62
|
+
|
|
63
|
+
Environment Setup:
|
|
64
|
+
|
|
65
|
+
Option A - Python venv (recommended for DECS machines):
|
|
66
|
+
|
|
67
|
+
1. Create and activate the virtual environment:
|
|
68
|
+
python3 -m venv $ENVHOME
|
|
69
|
+
source $ENVHOME/bin/activate
|
|
70
|
+
|
|
71
|
+
2. Install the required packages:
|
|
72
|
+
pip install rda_python_setuid rda_python_dsarch ...
|
|
73
|
+
|
|
74
|
+
3. Deactivate when done:
|
|
75
|
+
deactivate
|
|
76
|
+
|
|
77
|
+
$ENVHOME is typically /glade/u/home/gdexdata/gdexmsenv on DECS machines.
|
|
78
|
+
|
|
79
|
+
Option B - Conda (recommended for DAV/Casper):
|
|
80
|
+
|
|
81
|
+
1. Create and activate the conda environment:
|
|
82
|
+
conda create -n pg-gdex python=3.10
|
|
83
|
+
conda activate pg-gdex
|
|
84
|
+
|
|
85
|
+
2. Install the required packages:
|
|
86
|
+
pip install rda_python_setuid rda_python_dsarch ...
|
|
87
|
+
|
|
88
|
+
The conda environment is typically located at:
|
|
89
|
+
/glade/work/gdexdata/conda-envs/pg-gdex
|
|
90
|
+
|
|
91
|
+
Use 'conda activate pg-gdex' to activate it before running pywrapper-install.
|
|
92
|
+
|
|
93
|
+
Setup sequence for a new environment:
|
|
94
|
+
|
|
95
|
+
Option A - Full setuid setup (requires sudo access to CommonUser):
|
|
96
|
+
|
|
97
|
+
1. Install the target package and its dependencies:
|
|
98
|
+
pip install rda_python_dsarch
|
|
99
|
+
|
|
100
|
+
2. Compile pywrapper (once per environment):
|
|
101
|
+
pywrapper-install --user gdexdata
|
|
102
|
+
|
|
103
|
+
3. Wire up each program as a setuid entry:
|
|
104
|
+
pywrapper-install --link dsarch --user gdexdata
|
|
105
|
+
|
|
106
|
+
4. Optionally, allow a specialist to run commands as themselves:
|
|
107
|
+
pywrapper-install --pgstart --user zji
|
|
108
|
+
|
|
109
|
+
Option B - Simple install (no sudo required, runs as current user):
|
|
110
|
+
|
|
111
|
+
1. Install the target package:
|
|
112
|
+
pip install rda_python_dsarch
|
|
113
|
+
|
|
114
|
+
2. Create a direct symlink to the connector script:
|
|
115
|
+
pywrapper-install --link dsarch --simple
|
|
116
|
+
|
|
117
|
+
Examples:
|
|
118
|
+
|
|
119
|
+
1. Compile and install pywrapper for common user gdexdata:
|
|
120
|
+
pywrapper-install --user gdexdata
|
|
121
|
+
|
|
122
|
+
2. Same, but targeting a specific environment:
|
|
123
|
+
pywrapper-install --user gdexdata --envhome /glade/u/home/gdexdata/gdexmsenv/bin/
|
|
124
|
+
|
|
125
|
+
3. Wire up dsarch to run as gdexdata via pywrapper:
|
|
126
|
+
pywrapper-install --link dsarch --user gdexdata
|
|
127
|
+
|
|
128
|
+
4. Allow specialist zji to run commands as themselves via pgstart:
|
|
129
|
+
pywrapper-install --pgstart --user zji
|
|
130
|
+
|
|
131
|
+
5. Simple install of dsarch for a user who does not need setuid:
|
|
132
|
+
pywrapper-install --link dsarch --simple
|
|
@@ -18,6 +18,7 @@ import os
|
|
|
18
18
|
import sys
|
|
19
19
|
import re
|
|
20
20
|
import pwd
|
|
21
|
+
import subprocess
|
|
21
22
|
from rda_python_common import PgLOG
|
|
22
23
|
|
|
23
24
|
#
|
|
@@ -25,15 +26,19 @@ from rda_python_common import PgLOG
|
|
|
25
26
|
#
|
|
26
27
|
def main():
|
|
27
28
|
|
|
28
|
-
permit =
|
|
29
|
+
permit = False
|
|
29
30
|
PgLOG.PGLOG['LOGFILE'] = "pgstart.log"
|
|
30
31
|
aname = PgLOG.get_command()
|
|
31
32
|
bckgrd = ""
|
|
32
33
|
workdir = None
|
|
33
34
|
argv = sys.argv[1:]
|
|
34
35
|
|
|
35
|
-
PgLOG.
|
|
36
|
-
|
|
36
|
+
ruid = PgLOG.PGLOG['RUID']
|
|
37
|
+
euid = PgLOG.PGLOG['EUID']
|
|
38
|
+
ruser = pwd.getpwuid(ruid).pw_name
|
|
39
|
+
euser = pwd.getpwuid(euid).pw_name
|
|
40
|
+
if ruser == euser or ruser == PgLOG.PGLOG['GDEXUSER']: permit = True
|
|
41
|
+
PgLOG.set_suid(euid)
|
|
37
42
|
|
|
38
43
|
while argv:
|
|
39
44
|
ms = re.match(r'^-(\w+)$', argv[0])
|
|
@@ -48,27 +53,26 @@ def main():
|
|
|
48
53
|
display_message(opt)
|
|
49
54
|
|
|
50
55
|
if not (permit and argv):
|
|
51
|
-
ruid = PgLOG.PGLOG['RUID']
|
|
52
|
-
euid = PgLOG.PGLOG['EUID']
|
|
53
|
-
ruser = pwd.getpwuid(ruid).pw_name
|
|
54
|
-
euser = pwd.getpwuid(euid).pw_name
|
|
55
56
|
print("********************************************************************")
|
|
56
57
|
print("* Your Login Name is {}({}) & Effective User Name is {}({}).".format(ruser, ruid, euser, euid))
|
|
57
58
|
print("* Pass a command or options -(plg|env|inc) to run '{}'.".format(aname))
|
|
58
59
|
if not permit:
|
|
59
|
-
print("* You must be '{}' to execute a command as user '{}'.".format(PgLOG.PGLOG['
|
|
60
|
+
print("* You must be '{}' or '{}' to execute a command as user '{}'.".format(euser, PgLOG.PGLOG['GDEXUSER'], euser))
|
|
60
61
|
print("********************************************************************")
|
|
61
62
|
sys.exit(0)
|
|
62
63
|
|
|
63
64
|
cmd = PgLOG.argv_to_string(argv)
|
|
64
|
-
|
|
65
|
+
|
|
65
66
|
msg = "{}-{}{}-{}".format(PgLOG.PGLOG['HOSTNAME'], aname, PgLOG.current_datetime(), PgLOG.PGLOG['CURUID'])
|
|
66
67
|
if workdir:
|
|
67
68
|
msg += "-" + workdir
|
|
68
69
|
os.chdir(workdir)
|
|
69
70
|
|
|
70
71
|
PgLOG.pglog("{}: {}".format(msg, cmd), PgLOG.MSGLOG)
|
|
71
|
-
|
|
72
|
+
if bckgrd:
|
|
73
|
+
subprocess.Popen(argv)
|
|
74
|
+
else:
|
|
75
|
+
subprocess.run(argv)
|
|
72
76
|
sys.exit(0)
|
|
73
77
|
|
|
74
78
|
#
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/***************************************************************************************\
|
|
2
|
+
*
|
|
3
|
+
* Title: pywrapper.c
|
|
4
|
+
* Author: Zaihua Ji, zji@ucar.edu
|
|
5
|
+
* Date: 2025-03-17
|
|
6
|
+
* Purpose: C wrapper to start any executable python code as an effective user
|
|
7
|
+
*
|
|
8
|
+
* Instruction:
|
|
9
|
+
* python -m venv $ENVHOME
|
|
10
|
+
* python -m pip install rda_python_setuid
|
|
11
|
+
*
|
|
12
|
+
* # Compile pywrapper and set setuid bit (owned by CommonUser):
|
|
13
|
+
* pywrapper-install --user CommonUser [--envhome $ENVHOME/bin]
|
|
14
|
+
*
|
|
15
|
+
* # For an existing python program, $ENVHOME/bin/CommonProgram.py, to execute it
|
|
16
|
+
* # as the common user (Mode 1 - symlink):
|
|
17
|
+
* pywrapper-install --link CommonProgram [--envhome $ENVHOME/bin]
|
|
18
|
+
* CommonProgram [options]
|
|
19
|
+
*
|
|
20
|
+
* # For a specialist to run commands as themselves via pgstart (Mode 2):
|
|
21
|
+
* pywrapper-install --pgstart --user EffectUser [--envhome $ENVHOME/bin]
|
|
22
|
+
* pgstart_EffectUser EffectProgram [options]
|
|
23
|
+
*
|
|
24
|
+
* N: python 3 release number, it is 10 for Python 3.10.12
|
|
25
|
+
* CommonUser: a common user login name, such as gdexdata, for GDEXMS configuration
|
|
26
|
+
* EffectUser: any user login name in the same group of the common user
|
|
27
|
+
* $ENVHOME: /glade/u/home/gdexdata/rdamsenv (venv) on DECS machines, and
|
|
28
|
+
* /glade/work/gdexdata/conda-envs/pg-rda (conda) on DAV
|
|
29
|
+
*
|
|
30
|
+
* Convention:
|
|
31
|
+
* Any Python package whose program is to be run via pywrapper must register
|
|
32
|
+
* its connector entry point with a setuid_ prefix in pyproject.toml, e.g.:
|
|
33
|
+
* [project.scripts]
|
|
34
|
+
* "setuid_dsarch" = "rda_python_dsarch.dsarch:main"
|
|
35
|
+
* pip install places setuid_dsarch in $ENVHOME/bin/. pywrapper locates it by
|
|
36
|
+
* prepending setuid_ to the invoked program name. No manual script creation needed.
|
|
37
|
+
* pywrapper-install --link will chown setuid_dsarch to CommonUser and chmod 700,
|
|
38
|
+
* preventing direct execution by other users while still allowing pywrapper
|
|
39
|
+
* (which runs with EUID=CommonUser via the setuid bit) to execv it.
|
|
40
|
+
*
|
|
41
|
+
\***************************************************************************************/
|
|
42
|
+
|
|
43
|
+
#include <sys/types.h>
|
|
44
|
+
#include <sys/stat.h>
|
|
45
|
+
#include <unistd.h>
|
|
46
|
+
#include <stdio.h>
|
|
47
|
+
#include <string.h>
|
|
48
|
+
#include <stdlib.h>
|
|
49
|
+
#include <limits.h>
|
|
50
|
+
#include <libgen.h>
|
|
51
|
+
|
|
52
|
+
int is_executable(const char *filename) {
|
|
53
|
+
struct stat sb;
|
|
54
|
+
|
|
55
|
+
if(stat(filename, &sb) == 0 && sb.st_mode & S_IXUSR) {
|
|
56
|
+
return 1;
|
|
57
|
+
} else {
|
|
58
|
+
return 0;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* main program */
|
|
63
|
+
int main(int argc, char *argv[]) {
|
|
64
|
+
char *name;
|
|
65
|
+
char cname[80], prog[PATH_MAX];
|
|
66
|
+
char exepath[PATH_MAX];
|
|
67
|
+
char *fpath;
|
|
68
|
+
ssize_t exelen;
|
|
69
|
+
char pgstart[] = "pgstart";
|
|
70
|
+
char **apntr = argv;
|
|
71
|
+
|
|
72
|
+
exelen = readlink("/proc/self/exe", exepath, sizeof(exepath) - 1);
|
|
73
|
+
if(exelen == -1) {
|
|
74
|
+
perror("readlink /proc/self/exe");
|
|
75
|
+
exit(1);
|
|
76
|
+
}
|
|
77
|
+
exepath[exelen] = '\0';
|
|
78
|
+
fpath = dirname(exepath);
|
|
79
|
+
|
|
80
|
+
name = strrchr(argv[0], '/');
|
|
81
|
+
strncpy(cname, (name == NULL ? argv[0] : ++name), sizeof(cname) - 1);
|
|
82
|
+
cname[sizeof(cname) - 1] = '\0';
|
|
83
|
+
|
|
84
|
+
if(strstr(cname, pgstart) == cname) {
|
|
85
|
+
if(argc == 1 || argv[1][0] == '-') {
|
|
86
|
+
strncpy(cname, pgstart, sizeof(cname) - 1);
|
|
87
|
+
} else {
|
|
88
|
+
argv += 1;
|
|
89
|
+
name = strrchr(argv[0], '/');
|
|
90
|
+
strncpy(cname, (name == NULL ? argv[0] : ++name), sizeof(cname) - 1);
|
|
91
|
+
cname[sizeof(cname) - 1] = '\0';
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if(snprintf(prog, sizeof(prog), "%s/setuid_%s", fpath, cname) >= (int)sizeof(prog)) {
|
|
95
|
+
fprintf(stderr, "pywrapper: path too long\n");
|
|
96
|
+
exit(1);
|
|
97
|
+
}
|
|
98
|
+
if(is_executable(prog) == 0) {
|
|
99
|
+
/* fall back to cname.py for backward compatibility */
|
|
100
|
+
if(snprintf(prog, sizeof(prog), "%s/%s.py", fpath, cname) >= (int)sizeof(prog)) {
|
|
101
|
+
fprintf(stderr, "pywrapper: path too long\n");
|
|
102
|
+
exit(1);
|
|
103
|
+
}
|
|
104
|
+
if(is_executable(prog) == 0) {
|
|
105
|
+
if(snprintf(prog, sizeof(prog), "%s/pgstart.py", fpath) >= (int)sizeof(prog)) {
|
|
106
|
+
fprintf(stderr, "pywrapper: path too long\n");
|
|
107
|
+
exit(1);
|
|
108
|
+
}
|
|
109
|
+
argv = apntr;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
execv(prog, argv); /* call Python script */
|
|
113
|
+
perror(prog); /* execv only returns on error */
|
|
114
|
+
exit(1);
|
|
115
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: rda_python_setuid
|
|
3
|
+
Version: 1.0.4
|
|
4
|
+
Summary: RDA Python Package to setuid for program executions as an effective or common user
|
|
5
|
+
Author-email: Zaihua Ji <zji@ucar.edu>
|
|
6
|
+
Project-URL: Homepage, https://github.com/NCAR/rda-python-setuid
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
+
Requires-Python: >=3.7
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Requires-Dist: rda_python_common
|
|
15
|
+
Dynamic: license-file
|
|
16
|
+
|
|
17
|
+
RDA Python package, including a C code wrapper, to execute commandline applications
|
|
18
|
+
via setuid for effective and common user names.
|
|
19
|
+
|
|
20
|
+
## Overview
|
|
21
|
+
|
|
22
|
+
`rda_python_setuid` provides a C binary (`pywrapper`) that acquires a setuid effective
|
|
23
|
+
user, then `execv`s a Python entry point script. This allows Python programs to run
|
|
24
|
+
as a designated common user (e.g. `gdexdata`) without requiring `sudo` access.
|
|
25
|
+
|
|
26
|
+
Two modes are supported:
|
|
27
|
+
|
|
28
|
+
- **Mode 1 (CommonUser program):** a symlink `dsarch -> pywrapper` runs `setuid_dsarch`
|
|
29
|
+
as the common user.
|
|
30
|
+
- **Mode 2 (pgstart specialist):** a copy `pgstart_zji` runs any command as specialist
|
|
31
|
+
`zji` via `pgstart.py`, restricted to authorized users.
|
|
32
|
+
|
|
33
|
+
## Dependency requirement
|
|
34
|
+
|
|
35
|
+
Any Python package whose programs are to be run via the setuid mechanism must declare
|
|
36
|
+
`rda_python_setuid` as a dependency in its `pyproject.toml`:
|
|
37
|
+
|
|
38
|
+
```toml
|
|
39
|
+
[project]
|
|
40
|
+
dependencies = [
|
|
41
|
+
"rda_python_setuid",
|
|
42
|
+
...
|
|
43
|
+
]
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
It must also register each wrapped program's connector entry point with a `setuid_`
|
|
47
|
+
prefix:
|
|
48
|
+
|
|
49
|
+
```toml
|
|
50
|
+
[project.scripts]
|
|
51
|
+
"setuid_dsarch" = "rda_python_dsarch.dsarch:main"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`pip install` then places `setuid_dsarch` in the environment's `bin/` directory
|
|
55
|
+
automatically. `pywrapper-install --link` locks it down to `chmod 700` so users
|
|
56
|
+
cannot bypass the setuid wrapper by running it directly.
|
|
57
|
+
|
|
58
|
+
## Environment setup
|
|
59
|
+
|
|
60
|
+
### Option A — Python venv (DECS machines)
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
python3 -m venv $ENVHOME # e.g. /glade/u/home/gdexdata/gdexmsenv
|
|
64
|
+
source $ENVHOME/bin/activate
|
|
65
|
+
pip install rda_python_setuid rda_python_dsarch ...
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Option B — Conda (DAV/Casper)
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
conda create -n pg-gdex python=3.10
|
|
72
|
+
conda activate pg-gdex
|
|
73
|
+
pip install rda_python_setuid rda_python_dsarch ...
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The conda environment is typically at `/glade/work/gdexdata/conda-envs/pg-gdex`.
|
|
77
|
+
|
|
78
|
+
## Installation
|
|
79
|
+
|
|
80
|
+
After setting up the environment and installing packages, run `pywrapper-install`
|
|
81
|
+
with no arguments to display the full user guide:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
pywrapper-install
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Full setuid setup (requires sudo access to CommonUser)
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# 1. Install the target package (pulls in rda_python_setuid automatically):
|
|
91
|
+
pip install rda_python_dsarch
|
|
92
|
+
|
|
93
|
+
# 2. Compile pywrapper C binary (once per environment):
|
|
94
|
+
pywrapper-install --user gdexdata
|
|
95
|
+
|
|
96
|
+
# 3. Wire up each program as a setuid entry:
|
|
97
|
+
pywrapper-install --link dsarch --user gdexdata
|
|
98
|
+
|
|
99
|
+
# 4. Optionally, allow a specialist to run commands as themselves:
|
|
100
|
+
pywrapper-install --pgstart --user zji
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Simple install (no sudo required, runs as current user)
|
|
104
|
+
|
|
105
|
+
Users who do not need the setuid mechanism can skip steps 2–4 and create a
|
|
106
|
+
direct symlink from `dsarch` to `setuid_dsarch`:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
pip install rda_python_dsarch
|
|
110
|
+
pywrapper-install --link dsarch --simple
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Runtime flow
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
user runs: dsarch [args]
|
|
117
|
+
| (symlink -> pywrapper, setuid bit -> EUID=gdexdata)
|
|
118
|
+
pywrapper.c: execv(bin/setuid_dsarch, args)
|
|
119
|
+
| (chmod 700, only gdexdata can exec directly)
|
|
120
|
+
setuid_dsarch: calls dsarch:main() as gdexdata
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Github
|
|
124
|
+
|
|
125
|
+
<https://github.com/NCAR/rda-python-setuid>
|
rda_python_setuid-1.0.3/PKG-INFO
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: rda_python_setuid
|
|
3
|
-
Version: 1.0.3
|
|
4
|
-
Summary: RDA Python Package to setuid for program executions as an effective or common user
|
|
5
|
-
Author-email: Zaihua Ji <zji@ucar.edu>
|
|
6
|
-
Project-URL: Homepage, https://github.com/NCAR/rda-python-setuid
|
|
7
|
-
Classifier: Programming Language :: Python :: 3
|
|
8
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
-
Classifier: Operating System :: OS Independent
|
|
10
|
-
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
-
Requires-Python: >=3.7
|
|
12
|
-
Description-Content-Type: text/markdown
|
|
13
|
-
License-File: LICENSE
|
|
14
|
-
Requires-Dist: rda_python_common
|
|
15
|
-
Dynamic: license-file
|
|
16
|
-
|
|
17
|
-
RDA python package, including a C code wrapper, to execute commandline applications via setuid for effective and common user names.
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
RDA python package, including a C code wrapper, to execute commandline applications via setuid for effective and common user names.
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
/***************************************************************************************\
|
|
2
|
-
*
|
|
3
|
-
* Title: pywrapper.c
|
|
4
|
-
* Author: Zaihua Ji, zji@ucar.edu
|
|
5
|
-
* Date: 2025-03-17
|
|
6
|
-
* Purpose: C wrapper to start any executable python code as an effective user
|
|
7
|
-
*
|
|
8
|
-
* Instruction:
|
|
9
|
-
* python -m venv $ENVHOME
|
|
10
|
-
* python -m pip install rda_python_setuid
|
|
11
|
-
* cd $ENVHOME/bin/
|
|
12
|
-
* cp ../lib/python3.N/site-packages/rda_python_setuid/pywrapper.c ./
|
|
13
|
-
* sudo -u CommonUser gcc -o pywrapper $ENVHOME/bin/pywrapper.c
|
|
14
|
-
* sudo -u CommonUser chmod 4750 pywrapper
|
|
15
|
-
*
|
|
16
|
-
* For an existing python program, $ENVHOME/bin/CommonProgram.py, to execute it
|
|
17
|
-
* as the common user:
|
|
18
|
-
* sudo -u CommonUser ln -s pywrapper CommonProgram
|
|
19
|
-
* CommonProgram [options]
|
|
20
|
-
*
|
|
21
|
-
* For an existing python program, $ENVHOME/bin/EffectProgram.py, to execute it
|
|
22
|
-
* as the effective user:
|
|
23
|
-
* sudo -u EffectUser cp pywrapper pgstart_EffectUser
|
|
24
|
-
* sudo -u EffectUser chmod 4750 pgstart_EffectUser
|
|
25
|
-
* pgstart_EffectUser EffectProgram [options]
|
|
26
|
-
*
|
|
27
|
-
* N: python 3 release number, it is 10 for Python 3.10.12
|
|
28
|
-
* CommonUser: a common user login name, such as rdadata, for RDAMS configuration
|
|
29
|
-
* EffectUser: any user login name in the same group of the common user
|
|
30
|
-
* $ENVHOME: /glade/u/home/rdadata/rdamsenv (venv) on DECS machines, and
|
|
31
|
-
* /glade/work/rdadata/conda-envs/pg-rda (conda) on DAV
|
|
32
|
-
*
|
|
33
|
-
\***************************************************************************************/
|
|
34
|
-
|
|
35
|
-
#include <sys/types.h>
|
|
36
|
-
#include <sys/stat.h>
|
|
37
|
-
#include <unistd.h>
|
|
38
|
-
#include <stdio.h>
|
|
39
|
-
#include <string.h>
|
|
40
|
-
#include <libgen.h>
|
|
41
|
-
|
|
42
|
-
int is_executable(const char *filename) {
|
|
43
|
-
struct stat sb;
|
|
44
|
-
|
|
45
|
-
if(stat(filename, &sb) == 0 && sb.st_mode & S_IXUSR) {
|
|
46
|
-
return 1;
|
|
47
|
-
} else {
|
|
48
|
-
return 0;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/* main program */
|
|
53
|
-
int main(int argc, char *argv[]) {
|
|
54
|
-
char *name;
|
|
55
|
-
char cname[80], prog[255];
|
|
56
|
-
char file[] = __FILE__;
|
|
57
|
-
char *fpath = dirname(file);
|
|
58
|
-
char pgstart[] = "pgstart";
|
|
59
|
-
char **apntr = argv;
|
|
60
|
-
|
|
61
|
-
name = strrchr(argv[0], '/');
|
|
62
|
-
strcpy(cname, (name == NULL ? argv[0] : ++name));
|
|
63
|
-
|
|
64
|
-
if(strstr(cname, pgstart) == cname) {
|
|
65
|
-
if(argc == 1 || argv[1][0] == '-') {
|
|
66
|
-
strcpy(cname, pgstart);
|
|
67
|
-
} else {
|
|
68
|
-
argv += 1;
|
|
69
|
-
name = strrchr(argv[0], '/');
|
|
70
|
-
strcpy(cname, (name == NULL ? argv[0] : ++name));
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
sprintf(prog, "%s/%s.py", fpath, cname);
|
|
74
|
-
if(is_executable(prog) == 0) {
|
|
75
|
-
sprintf(prog, "%s/pgstart.py", fpath);
|
|
76
|
-
argv = apntr;
|
|
77
|
-
}
|
|
78
|
-
execv(prog, argv); /* call Python script */
|
|
79
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: rda_python_setuid
|
|
3
|
-
Version: 1.0.3
|
|
4
|
-
Summary: RDA Python Package to setuid for program executions as an effective or common user
|
|
5
|
-
Author-email: Zaihua Ji <zji@ucar.edu>
|
|
6
|
-
Project-URL: Homepage, https://github.com/NCAR/rda-python-setuid
|
|
7
|
-
Classifier: Programming Language :: Python :: 3
|
|
8
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
-
Classifier: Operating System :: OS Independent
|
|
10
|
-
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
-
Requires-Python: >=3.7
|
|
12
|
-
Description-Content-Type: text/markdown
|
|
13
|
-
License-File: LICENSE
|
|
14
|
-
Requires-Dist: rda_python_common
|
|
15
|
-
Dynamic: license-file
|
|
16
|
-
|
|
17
|
-
RDA python package, including a C code wrapper, to execute commandline applications via setuid for effective and common user names.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{rda_python_setuid-1.0.3 → rda_python_setuid-1.0.4}/src/rda_python_setuid.egg-info/requires.txt
RENAMED
|
File without changes
|
{rda_python_setuid-1.0.3 → rda_python_setuid-1.0.4}/src/rda_python_setuid.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|