Skip to content

Commit 05377ba

Browse files
adamamylclaude
andcommitted
fix(perms): stop clone_or_update_repo clobbering home dir with docker group
chgrp/chmod in clone_or_update_repo applied to parent_dir unconditionally, so cloning into /home/adam/pseudohome ran chgrp -R docker /home/adam and chmod -R g+w — nuking .ssh permissions (SSH then rejects 770 .ssh dir). Add group param to clone_or_update_repo and clone_or_update_private_repo_with_key_check; guard chgrp/chmod block behind `if group`. Callers pass group from constants: - HWGA repos and docker-related personal repos: group=docker - pseudohome and dracula: no group (user:user only) Also tighten module_pseudohome to non-recursive chown on home dir and re-enforce set_ssh_perms after clone. Restructure PERSONAL_GITHUB_REPOS from Dict[str,str] to Dict[str,Dict] consistent with HWGA_REPOS. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F2ixsEtGQH2uBTwv7iXhAv
1 parent 5997d95 commit 05377ba

5 files changed

Lines changed: 53 additions & 30 deletions

File tree

lib/constants.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
HWGA_REPOS: Dict[str, Dict[str, Any]] = {
4747
"herewegoagain": {
4848
"user": "no2id-docker",
49+
"group": "docker",
4950
"url": "git@github.com:no2id/herewegoagain.git",
5051
"dest": f"{ROOT_SRC_CHECKOUT}/herewegoagain",
5152
"installer": "install",
@@ -54,6 +55,7 @@
5455
},
5556
"fake-le": {
5657
"user": "adam",
58+
"group": "docker",
5759
"url": "git@github.com:adamamyl/fake-le.git",
5860
"dest": f"{ROOT_SRC_CHECKOUT}/fake-le",
5961
"installer": "fake-le-for-no2id-docker-installer",
@@ -128,10 +130,19 @@
128130

129131
# Personal GitHub Repos (public, cloned for the 'adam' user).
130132
# dest is resolved at runtime relative to the target user's home dir.
131-
PERSONAL_GITHUB_REPOS: Dict[str, str] = {
132-
"traefik-proxy": "https://github.com/adamamyl/traefik-proxy.git",
133-
"dracula": "https://github.com/adamamyl/dracula.git",
134-
"docker-dns-reso": "https://github.com/adamamyl/docker-dns-reso.git",
133+
# group: set group ownership on the checkout; omit for user-only repos.
134+
PERSONAL_GITHUB_REPOS: Dict[str, Dict[str, str]] = {
135+
"traefik-proxy": {
136+
"url": "https://github.com/adamamyl/traefik-proxy.git",
137+
"group": "docker",
138+
},
139+
"dracula": {
140+
"url": "https://github.com/adamamyl/dracula.git",
141+
},
142+
"docker-dns-reso": {
143+
"url": "https://github.com/adamamyl/docker-dns-reso.git",
144+
"group": "docker",
145+
},
135146
}
136147

137148
# Firewall module:

lib/installer_utils/git_tools.py

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@
77
from .repo_utils import _display_key_and_url_for_repo
88
import time # For retry sleep
99

10-
def clone_or_update_repo(exec_obj: Executor,
11-
repo_url: str,
12-
dest_dir: str,
13-
ssh_key_path: Optional[str] = None,
10+
def clone_or_update_repo(exec_obj: Executor,
11+
repo_url: str,
12+
dest_dir: str,
13+
ssh_key_path: Optional[str] = None,
1414
extra_git_flags: Optional[str] = "",
15-
user: Optional[str] = None) -> None:
15+
user: Optional[str] = None,
16+
group: Optional[str] = None) -> None:
1617
"""
1718
Clones or updates a Git repository, handling SSH deploy keys if specified.
1819
@@ -34,10 +35,12 @@ def clone_or_update_repo(exec_obj: Executor,
3435
# 1. Ensure parent dir exists and has correct group/permissions
3536
exec_obj.run(f"mkdir -p {parent_dir}", force_sudo=True)
3637

37-
# Use 'docker' group and ensure recursive chmod
38-
exec_obj.run(f"chgrp -R docker {parent_dir} || true", force_sudo=True)
39-
exec_obj.run(f"chmod -R g+w {parent_dir}", force_sudo=True)
40-
exec_obj.run(f"chmod -R -s {parent_dir} || true", force_sudo=True)
38+
# Set group ownership and ensure group-writeable; only when a group is explicitly requested.
39+
# Never apply to home directories — callers that need a specific group (e.g. docker) pass it.
40+
if group:
41+
exec_obj.run(f"chgrp -R {group} {parent_dir} || true", force_sudo=True)
42+
exec_obj.run(f"chmod -R g+w {parent_dir}", force_sudo=True)
43+
exec_obj.run(f"chmod -R -s {parent_dir} || true", force_sudo=True)
4144

4245
# 2. Prepare environment prefix for SSH key usage
4346
env_prefix = ""
@@ -86,13 +89,14 @@ def clone_or_update_repo(exec_obj: Executor,
8689
exec_obj.run(final_cmd, user=user)
8790
log.success(f"Repository cloned: {dest_dir}")
8891

89-
def clone_or_update_private_repo_with_key_check(exec_obj: Executor,
90-
repo_url: str,
91-
dest_dir: str,
92-
ssh_key_path: str,
92+
def clone_or_update_private_repo_with_key_check(exec_obj: Executor,
93+
repo_url: str,
94+
dest_dir: str,
95+
ssh_key_path: str,
9396
repo_name: str,
9497
extra_git_flags: Optional[str] = "",
95-
user: str = "root") -> None:
98+
user: str = "root",
99+
group: Optional[str] = None) -> None:
96100
"""
97101
Attempts to clone a private repo. If it fails due to SSH permission,
98102
it prompts the user to add the deploy key and retries the clone once.
@@ -108,12 +112,13 @@ def clone_or_update_private_repo_with_key_check(exec_obj: Executor,
108112
f"(Attempt {attempt + 1}/{MAX_CLONE_ATTEMPTS})..."
109113
)
110114
clone_or_update_repo(
111-
exec_obj,
112-
repo_url,
113-
dest_dir,
115+
exec_obj,
116+
repo_url,
117+
dest_dir,
114118
ssh_key_path=ssh_key_path,
115119
extra_git_flags=extra_git_flags,
116-
user=user
120+
user=user,
121+
group=group,
117122
)
118123
clone_succeeded = True
119124
break # Exit loop on success

lib/installer_utils/module_no2id.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,14 @@ def setup_no2id(exec_obj: Executor) -> None:
5353
ssh_key_path = os.path.join(ssh_dir, repo_name)
5454

5555
clone_or_update_private_repo_with_key_check(
56-
exec_obj,
57-
repo_url,
58-
dest_dir,
56+
exec_obj,
57+
repo_url,
58+
dest_dir,
5959
ssh_key_path=ssh_key_path,
6060
repo_name=repo_name,
6161
extra_git_flags=extra_flags,
62-
user=user # Execute as target user
62+
user=user,
63+
group=config.get('group'),
6364
)
6465

6566
# --- NEW STEP: Configure local Git SSH key for subsequent pulls/fetches ---

lib/installer_utils/module_personal_repos.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111

1212

1313
def _setup_repo(exec_obj: Executor, key: str) -> None:
14-
url = PERSONAL_GITHUB_REPOS[key]
14+
config = PERSONAL_GITHUB_REPOS[key]
15+
url = config["url"]
16+
group = config.get("group")
1517
user_home = Path(f"~{PERSONAL_REPOS_USER}").expanduser()
1618
projects_dir = user_home / "projects"
1719
dest = str(projects_dir / key)
@@ -22,8 +24,10 @@ def _setup_repo(exec_obj: Executor, key: str) -> None:
2224
)
2325

2426
log.info(f"Cloning/updating {key}...")
25-
clone_or_update_repo(exec_obj, url, dest, user=PERSONAL_REPOS_USER)
27+
clone_or_update_repo(exec_obj, url, dest, user=PERSONAL_REPOS_USER, group=group)
2628
exec_obj.run(f"chown -R {PERSONAL_REPOS_USER}:{PERSONAL_REPOS_USER} {dest}", force_sudo=True)
29+
if group:
30+
exec_obj.run(f"chgrp -R {group} {dest}", force_sudo=True)
2731
log.success(f"{key} ready at {dest}.")
2832

2933

lib/installer_utils/module_pseudohome.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,11 @@ def setup_pseudohome(exec_obj: Executor) -> None:
7171
user=user, # Execute as 'adam'
7272
)
7373

74-
# 6. Fix permissions
75-
exec_obj.run(f"chown -R {user}:{user} {os.path.dirname(dest_dir)}", force_sudo=True)
74+
# 6. Fix permissions: home dir ownership (non-recursive — must not clobber .ssh),
75+
# then repo contents, then re-enforce .ssh in case anything above touched it.
76+
exec_obj.run(f"chown {user}:{user} {os.path.dirname(dest_dir)}", force_sudo=True)
7677
set_homedir_perms_recursively(exec_obj, user, dest_dir)
78+
set_ssh_perms(exec_obj, user, ssh_dir)
7779

7880
# 7. Run installer script (as the user)
7981
installer_path = os.path.join(dest_dir, PSEUDOHOME_INSTALLER)

0 commit comments

Comments
 (0)