gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-deployment] branch master updated: splitops


From: gnunet
Subject: [taler-deployment] branch master updated: splitops
Date: Mon, 17 Apr 2023 23:42:20 +0200

This is an automated email from the git hooks/post-receive script.

dold pushed a commit to branch master
in repository deployment.

The following commit(s) were added to refs/heads/master by this push:
     new ae6d881  splitops
ae6d881 is described below

commit ae6d881a9fd56598005f90963609291d5fa5bdb3
Author: Florian Dold <florian@dold.me>
AuthorDate: Mon Apr 17 23:42:02 2023 +0200

    splitops
---
 splitops/README.md |  40 +++++++++++++++
 splitops/splitops  | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 183 insertions(+)

diff --git a/splitops/README.md b/splitops/README.md
new file mode 100644
index 0000000..a349ce3
--- /dev/null
+++ b/splitops/README.md
@@ -0,0 +1,40 @@
+# splitops
+
+Splitops is a script to allow execution of commands only after the approval of
+multiple users.
+
+It is intended to be used with OpenSSH by specifiying it as the "command" 
option
+for authorized users in `~/.ssh/authorized_keys`.
+
+For example, consider following `authorized_keys` file for the user `root`:
+
+```
+command="/bin/splitops --user=alice" [... key of alice ...]
+command="/bin/splitops --user=bob" [... key of bob ...]
+```
+
+This allows Alice and Bob to jointly run commands:
+
+```
+bob$ ssh root@server propose rm -rf /opt/something
+authenticated as: bob
+requested command: ['rm', '-rf', '/opt/something']
+assigned id: ccafbd
+
+bob$ ssh root@server approve ccafbd
+
+alice$ ssh root@server get 
+{'cmd': ['rm', '-rf', '/opt/something'], 'request_id': 'ccafbd'}
+
+
+alice$ ssh root@server approve ccafbd
+
+bob$ ssh root@server run ccafbd
+==stdout==
+...
+====
+==stderr==
+...
+====
+exit status: 0
+```
diff --git a/splitops/splitops b/splitops/splitops
new file mode 100755
index 0000000..8e5414c
--- /dev/null
+++ b/splitops/splitops
@@ -0,0 +1,143 @@
+#!/usr/bin/env python3
+
+"""
+This script is intended to be used as a SSH command wrapper.
+
+It allows users to propose a command that should be run.
+The command will only be executed after a threshold of
+other users has approved the command.
+"""
+
+import os
+import shlex
+import sys
+import json
+from pathlib import Path
+import uuid
+from dataclasses import dataclass
+import subprocess
+
+# Approval threshold, including the approval
+# of the proposer.
+APPROVAL_THRESHOLD = 2
+
+cmdpath = Path.home() / "cmd.json"
+
+def write_cmd(d):
+    with open(cmdpath, "w") as f:
+        f.write(json.dumps(d))
+
+def read_cmd():
+    try:
+        with open(cmdpath, "r") as f:
+            return json.load(f)
+    except FileNotFoundError:
+        return None
+
+def propose(cmd):
+    request_id = uuid.uuid4().hex.lower()[0:6]
+    for x in cmd:
+        if not x.isascii():
+            print("requested command not ascii")
+            sys.exit(4)
+    print(f"requested command: {cmd}")
+    write_cmd({"cmd": cmd, "request_id": request_id})
+    print(f"assigned id: {request_id}")
+
+def approve(my_user, request_id):
+    print(f"approving command {request_id} as {my_user}")
+    d = read_cmd()
+    if d is None:
+        print("no command proposed")
+        sys.exit(1)
+    if d["request_id"] != request_id:
+        print("request ID does not match")
+        sys.exit(1)
+    approved_by = d.get("approved_by", [])
+    if my_user not in approved_by:
+        approved_by.append(my_user)
+    d["approved_by"] = approved_by
+    write_cmd(d)
+
+def run(request_id):
+    print(f"running command with ID {request_id}")
+    d = read_cmd()
+    if d is None:
+        print("no command proposed")
+        sys.exit(1)
+    if d["request_id"] != request_id:
+        print("request ID does not match")
+        sys.exit(1)
+    approved_by = d.get("approved_by", [])
+    num_approvals = len(approved_by)
+    if num_approvals < APPROVAL_THRESHOLD:
+        print(f"not enough approvals, got {num_approvals} but need 
{APPROVAL_THRESHOLD}")
+        sys.exit(1)
+    if d.get("executed", False):
+        print("command has already been executed once, please request again")
+        sys.exit(1)
+    cmd = d["cmd"]
+    d["executed"] = True
+    # Mark as executed, can only execute once!
+    write_cmd(d)
+    print("running command", cmd)
+    res = subprocess.run(cmd, capture_output=True, encoding="utf-8")
+    print(f"==stdout==\n{res.stdout}====")
+    print(f"==stderr==\n{res.stderr}====")
+    print(f"exit code: {res.returncode}")
+    # FIXME: Write log to disk?
+
+
+def usage():
+    print("Commands:")
+    print(" whoami: Check authentication.")
+    print(" propose CMD...: Propose a new command.")
+    print(" get: Get the currently proposed command.")
+    print(" approve CMDID: Approve a command.")
+    print(" run CMDID: Run a sufficiently approved command.")
+    print(" discard: Discard the currently proposed command.")
+    sys.exit(1)
+
+def die(msg):
+    printf(msg)
+    sys.exit(2)
+
+def main():
+    if len(sys.argv) != 2:
+        die("unexpected usage")
+    user = sys.argv[1]
+    os_user = os.environ["USER"]
+    print(f"authenticated as: {user}")
+    inner_cmd = os.environ.get("SSH_ORIGINAL_COMMAND")
+    if inner_cmd is None:
+        print("no command provided, try help")
+        sys.exit(3)
+    inner_args = shlex.split(inner_cmd)
+    if len(inner_args) < 1:
+        usage()
+    subcommand = inner_args[0]
+    if subcommand == "discard":
+        cmdpath.unlink()
+    elif subcommand == "whoami":
+        print(f"you are {user} on {os_user}")
+    elif subcommand == "propose":
+        propose(inner_args[1:])
+    elif subcommand == "get":
+        print(read_cmd())
+    elif subcommand == "help":
+        usage()
+    elif subcommand == "run":
+        if len(inner_args) != 2:
+            usage()
+        run(inner_args[1])
+    elif subcommand == "approve":
+        if len(inner_args) != 2:
+            usage()
+        approve(user, inner_args[1])
+    else:
+        print(f"unknown subcommand {subcommand}")
+        usage()
+
+if __name__ == '__main__':
+    main()
+

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]