Remote mode and dangerous flag, other tweaks
* Add remote mode for harvesting a remote machine via a local workstation (no need to install enroll remotely) Optionally use `--no-sudo` if you don't want the remote user to have passwordless sudo when conducting the harvest, albeit you'll end up with less useful data (same as if running `enroll harvest` on a machine without sudo) * Add `--dangerous` flag to capture even sensitive data (use at your own risk!) * Do a better job at capturing other config files in `/etc/<package>/` even if that package doesn't normally ship or manage those files.
This commit is contained in:
parent
026416d158
commit
6a36a9d2d5
13 changed files with 1083 additions and 155 deletions
116
enroll/cli.py
116
enroll/cli.py
|
|
@ -1,9 +1,14 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from .cache import new_harvest_cache_dir
|
||||
from .harvest import harvest
|
||||
from .manifest import manifest
|
||||
from .remote import remote_harvest
|
||||
|
||||
|
||||
def _add_common_manifest_args(p: argparse.ArgumentParser) -> None:
|
||||
|
|
@ -32,46 +37,129 @@ def _jt_mode(args: argparse.Namespace) -> str:
|
|||
return "auto"
|
||||
|
||||
|
||||
def _add_remote_args(p: argparse.ArgumentParser) -> None:
|
||||
p.add_argument(
|
||||
"--remote-host",
|
||||
help="SSH host to run harvesting on (if set, harvest runs remotely and is pulled locally).",
|
||||
)
|
||||
p.add_argument(
|
||||
"--remote-port",
|
||||
type=int,
|
||||
default=22,
|
||||
help="SSH port for --remote-host (default: 22).",
|
||||
)
|
||||
p.add_argument(
|
||||
"--remote-user",
|
||||
default=os.environ.get("USER") or None,
|
||||
help="SSH username for --remote-host (default: local $USER).",
|
||||
)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
ap = argparse.ArgumentParser(prog="enroll")
|
||||
sub = ap.add_subparsers(dest="cmd", required=True)
|
||||
|
||||
h = sub.add_parser("harvest", help="Harvest service/package/config state")
|
||||
h.add_argument("--out", required=True, help="Harvest output directory")
|
||||
h.add_argument("--out", help="Harvest output directory")
|
||||
h.add_argument(
|
||||
"--dangerous",
|
||||
action="store_true",
|
||||
help="Collect files more aggressively (may include secrets). Disables secret-avoidance checks.",
|
||||
)
|
||||
h.add_argument(
|
||||
"--no-sudo",
|
||||
action="store_true",
|
||||
help="Don't use sudo on the remote host (when using --remote options). This may result in a limited harvest due to permission restrictions.",
|
||||
)
|
||||
_add_remote_args(h)
|
||||
|
||||
r = sub.add_parser("manifest", help="Render Ansible roles from a harvest")
|
||||
r.add_argument(
|
||||
m = sub.add_parser("manifest", help="Render Ansible roles from a harvest")
|
||||
m.add_argument(
|
||||
"--harvest",
|
||||
required=True,
|
||||
help="Path to the directory created by the harvest command",
|
||||
)
|
||||
r.add_argument(
|
||||
m.add_argument(
|
||||
"--out",
|
||||
required=True,
|
||||
help="Output directory for generated roles/playbook Ansible manifest",
|
||||
)
|
||||
_add_common_manifest_args(r)
|
||||
_add_common_manifest_args(m)
|
||||
|
||||
e = sub.add_parser(
|
||||
s = sub.add_parser(
|
||||
"single-shot", help="Harvest state, then manifest Ansible code, in one shot"
|
||||
)
|
||||
e.add_argument(
|
||||
"--harvest", required=True, help="Path to the directory to place the harvest in"
|
||||
s.add_argument("--harvest", help="Path to the directory to place the harvest in")
|
||||
s.add_argument(
|
||||
"--dangerous",
|
||||
action="store_true",
|
||||
help="Collect files more aggressively (may include secrets). Disables secret-avoidance checks.",
|
||||
)
|
||||
e.add_argument(
|
||||
s.add_argument(
|
||||
"--no-sudo",
|
||||
action="store_true",
|
||||
help="Don't use sudo on the remote host (when using --remote options). This may result in a limited harvest due to permission restrictions.",
|
||||
)
|
||||
s.add_argument(
|
||||
"--out",
|
||||
required=True,
|
||||
help="Output directory for generated roles/playbook Ansible manifest",
|
||||
)
|
||||
_add_common_manifest_args(e)
|
||||
_add_common_manifest_args(s)
|
||||
_add_remote_args(s)
|
||||
|
||||
args = ap.parse_args()
|
||||
|
||||
remote_host: Optional[str] = getattr(args, "remote_host", None)
|
||||
|
||||
if args.cmd == "harvest":
|
||||
path = harvest(args.out)
|
||||
print(path)
|
||||
if remote_host:
|
||||
out_dir = (
|
||||
Path(args.out)
|
||||
if args.out
|
||||
else new_harvest_cache_dir(hint=remote_host).dir
|
||||
)
|
||||
state = remote_harvest(
|
||||
local_out_dir=out_dir,
|
||||
remote_host=remote_host,
|
||||
remote_port=int(args.remote_port),
|
||||
remote_user=args.remote_user,
|
||||
dangerous=bool(args.dangerous),
|
||||
no_sudo=bool(args.no_sudo),
|
||||
)
|
||||
print(str(state))
|
||||
else:
|
||||
if not args.out:
|
||||
raise SystemExit("error: --out is required unless --remote-host is set")
|
||||
path = harvest(args.out, dangerous=bool(args.dangerous))
|
||||
print(path)
|
||||
elif args.cmd == "manifest":
|
||||
manifest(args.harvest, args.out, fqdn=args.fqdn, jinjaturtle=_jt_mode(args))
|
||||
elif args.cmd == "single-shot":
|
||||
harvest(args.harvest)
|
||||
manifest(args.harvest, args.out, fqdn=args.fqdn, jinjaturtle=_jt_mode(args))
|
||||
if remote_host:
|
||||
harvest_dir = (
|
||||
Path(args.harvest)
|
||||
if args.harvest
|
||||
else new_harvest_cache_dir(hint=remote_host).dir
|
||||
)
|
||||
remote_harvest(
|
||||
local_out_dir=harvest_dir,
|
||||
remote_host=remote_host,
|
||||
remote_port=int(args.remote_port),
|
||||
remote_user=args.remote_user,
|
||||
dangerous=bool(args.dangerous),
|
||||
no_sudo=bool(args.no_sudo),
|
||||
)
|
||||
manifest(
|
||||
str(harvest_dir), args.out, fqdn=args.fqdn, jinjaturtle=_jt_mode(args)
|
||||
)
|
||||
# For usability (when --harvest wasn't provided), print the harvest path.
|
||||
if not args.harvest:
|
||||
print(str(harvest_dir / "state.json"))
|
||||
else:
|
||||
if not args.harvest:
|
||||
raise SystemExit(
|
||||
"error: --harvest is required unless --remote-host is set"
|
||||
)
|
||||
harvest(args.harvest, dangerous=bool(args.dangerous))
|
||||
manifest(args.harvest, args.out, fqdn=args.fqdn, jinjaturtle=_jt_mode(args))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue