Create GnuPG and OpenSSH keys; usage notes.
authorOlof-Joachim Frahm <olof@macrolet.net>
Sat, 10 Jan 2015 20:53:01 +0000 (20:53 +0000)
committerOlof-Joachim Frahm <olof@macrolet.net>
Sat, 10 Jan 2015 20:54:15 +0000 (20:54 +0000)
README.md
crypto-install.py

index b44b229..eef6c8a 100644 (file)
--- a/README.md
+++ b/README.md
@@ -2,5 +2,22 @@ crypto-install.py
 
 # USAGE
 
-Run the script to install a baseline setup.  Existing files are detected
-and not touched in the process, so running it is always safe to do.
+Run the script to install a baseline setup for both GnuPG and OpenSSH.
+
+Existing files are detected and not touched in the process, so running
+it is always safe to do.
+
+# INSTALLATION
+
+Until I set up a better routine:
+
+    git clone https://github.com/Ferada/crypto-install.git
+    # or
+    git clone git@github.com:Ferada/crypto-install.git
+
+    cd crypto-install
+    make
+    cp build/crypto-install.py ~/bin # or wherever
+
+Simply copy the built file into your path and possibly ensure execution
+permissions.
index 0aff602..9715441 100755 (executable)
@@ -2,17 +2,35 @@
 # -*- mode: python; coding: utf-8-unix; -*-
 
 
-import argparse, ConfigParser, os, sys, textwrap
+import argparse, errno, os, readline, subprocess, sys, tempfile, textwrap
 
 
-def print_version ():
-    print ("crypto-install.py GIT-TAG (GIT-COMMIT/GIT-BRANCH)")
+if sys.version_info[0] == 2:
+    def input_string (prompt=""):
+        return raw_input (prompt)
+elif sys.version_info[0] > 2:
+    def input_string (prompt=""):
+        return input (prompt)
+else:
+    raise Exception ("Unsupported Python version {}".format (sys.version_info))
 
 
-def input_string (prompt=""):
-    if sys.version_info[0] == 2:
-        return raw_input(prompt)
-    return input(prompt)
+def dedented (text):
+    return textwrap.dedent (text).strip ()
+
+
+def filled (text):
+    return textwrap.fill (dedented (text), width = 72)
+
+
+def read_input_string (prompt="", default=""):
+    if default != "":
+        readline.set_startup_hook (lambda: readline.insert_text (default))
+
+    try:
+        return input_string(prompt)
+    finally:
+        readline.set_startup_hook()
 
 
 def parse_arguments ():
@@ -20,71 +38,135 @@ def parse_arguments ():
     parser.add_argument (
         "-v", "--version",
         dest = "version",
-        action = "store_true",
+        action = "version",
+        version = "crypto-install.py version GIT-TAG (GIT-COMMIT/GIT-BRANCH)",
         help = "Display version.")
-    parser.add_argument (
+    gnupg_group = parser.add_argument_group ("GnuPG",
+        "Options related to the GnuPG setup.")
+    gnupg_group.add_argument (
         "--no-gpg",
         dest = "gnupg",
         action = "store_false",
         help = "Disable GnuPG setup.")
-    parser.add_argument (
+    gnupg_group.add_argument (
+        "--gpg-home",
+        dest = "gnupg_home",
+        default = "~/.gnupg",
+        metavar = "PATH",
+        help = "Default directory for GnuPG files.")
+    openssh_group = parser.add_argument_group ("OpenSSH",
+        "Options related to the OpenSSH setup.")
+    openssh_group.add_argument (
         "--no-ssh",
         dest = "openssh",
         action = "store_false",
         help = "Disable OpenSSH setup.")
-    parser.add_argument (
-        "--ssh-config",
-        dest = "openssh_config",
-        default = "~/.ssh/config",
-        help = "Set path for OpenSSH configuration file.")
+    openssh_group.add_argument (
+        "--ssh-home",
+        dest = "openssh_home",
+        default = "~/.ssh",
+        metavar = "PATH",
+        help = "Default directory for OpenSSH files.")
     return parser.parse_args ()
 
 
-def gnupg_setup ():
-    if False:
-        print("Default GnuPG key already exists.")
+def gnupg_setup (arguments):
+    gnupg_home = os.path.expanduser (arguments.gnupg_home)
+    gnupg_secring = os.path.join (gnupg_home, "secring.gpg")
+
+    if os.path.exists (gnupg_secring):
+        print ("GnuPG secret keyring already exists at {!r}."
+               .format (gnupg_secring))
         return
 
-    print (textwrap.fill (textwrap.dedent("""\
+    print (filled ("""
     No default GnuPG key available.  Please enter your information to
-    create a new key."""), width = 80))
+    create a new key."""))
+
+    default_name = os.getenv ("FULLNAME")
+    name = read_input_string ("What is your name? ", default_name)
+
+    default_email = os.getenv ("EMAIL")
+    email = read_input_string ("What is your email address? ", default_email)
+
+    comment = read_input_string ("What is your comment phrase, if any (e.g. 'key for 2014')? ")
+
+    if not os.path.exists (gnupg_home):
+        ensure_directories (gnupg_home, 0o700)
 
-    name = input_string("What is your name?  (Max Mustermann) ")
-    email = input_string("What is your email address?  (max@example.de) ")
-    motto = input_string("What is your motto phrase, if any?  (Schlüssel für 2014) ")
+    with tempfile.NamedTemporaryFile () as tmp:
+        batch_key = dedented ("""
+        %ask-passphrase
+        Key-Type: DSA
+        Key-Length: 2048
+        Subkey-Type: ELG-E
+        Subkey-Length: 2048
+        Name-Real: {}
+        Name-Email: {}
+        Expire-Date: 0
+        """).format (name, email)
+
+        if comment != "":
+            batch_key += "\nName-Comment: {}\n".format (comment)
+
+        tmp.write (batch_key)
+        tmp.flush ()
+
+        batch_env = dict(os.environ)
+        del batch_env["DISPLAY"]
+
+        gnupg_process = subprocess.Popen (["gpg2", "--homedir", gnupg_home, "--batch", "--gen-key", tmp.name],
+                                        env = batch_env)
+        gnupg_process.wait ()
+
+        if gnupg_process.returncode != 0:
+            raise Exception ("Couldn't create GnuPG key.")
+
+
+def ensure_directories (path, mode = 0o777):
+    try:
+        os.makedirs (path, mode)
+    except OSError as exception:
+        if exception.errno != errno.EEXIST:
+            raise
 
 
 def openssh_setup (arguments):
-    if not os.path.exists(arguments.openssh_config):
-        with open(arguments.openssh_config, "w") as ssh_config:
-            ssh_config.write(textwrap.dedent("""\
+    openssh_home = os.path.expanduser (arguments.openssh_home)
+    openssh_config = os.path.join (openssh_home, "config")
+
+    if not os.path.exists (openssh_config):
+        ensure_directories (openssh_home, 0o700)
+
+        with open (openssh_config, "w") as config:
+            config.write (dedented ("""
             ForwardAgent yes
             ForwardX11 yes
             """))
 
-    if os.path.exists (os.path.expanduser ("~/.ssh/id_rsa")) \
-       or os.path.exists (os.path.expanduser ("~/.ssh/id_dsa")):
-        print("OpenSSH key already exists.")
+    openssh_key = os.path.join (openssh_home, "id_rsa")
+
+    if os.path.exists (openssh_key):
+        print("OpenSSH key already exists at {!r}.".format (openssh_key))
         return
 
-    print (textwrap.fill (textwrap.dedent("""\
-    No OpenSSH key available.  Generating new key."""), width = 80))
+    print (filled ("No OpenSSH key available.  Generating new key."))
 
-    os.system ("ssh-keygen")
+    openssh_process = subprocess.Popen (["ssh-keygen", "-f", openssh_key])
+    openssh_process.wait ()
 
+    if openssh_process.returncode != 0:
+        raise Exception ("Couldn't create OpenSSH key.")
 
-def main ():
-    args = parse_arguments ()
 
-    if args.version:
-        print_version ()
-        sys.exit ()
+def main ():
+    arguments = parse_arguments ()
 
-    if args.gnupg:
-        gnupg_setup ()
+    if arguments.gnupg:
+        gnupg_setup (arguments)
 
-    if args.openssh:
-        openssh_setup (args)
+    if arguments.openssh:
+        openssh_setup (arguments)
 
 
 if __name__ == "__main__":