diff options
| author | Christian Poessinger <christian@poessinger.com> | 2021-07-23 19:35:20 +0200 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-07-23 19:35:20 +0200 | 
| commit | 7273a6a0a05a000680ee549b76dd40762b73c2d0 (patch) | |
| tree | d359fb3ee4450c37aaf288b54292f119b973e049 | |
| parent | a55585a833a23e64adb666558b3d358523f6cb39 (diff) | |
| parent | 77a9473915b46879bae504dfa3c1c4d0d60fa2e9 (diff) | |
| download | vyos-1x-7273a6a0a05a000680ee549b76dd40762b73c2d0.tar.gz vyos-1x-7273a6a0a05a000680ee549b76dd40762b73c2d0.zip  | |
Merge pull request #939 from sarthurdev/pki_file
pki: T3642: Add ability to write generated certificates/keys to files
| -rw-r--r-- | op-mode-definitions/pki.xml.in | 116 | ||||
| -rwxr-xr-x | src/op_mode/pki.py | 187 | 
2 files changed, 243 insertions, 60 deletions
diff --git a/op-mode-definitions/pki.xml.in b/op-mode-definitions/pki.xml.in index 9c6b56a68..a11814c8a 100644 --- a/op-mode-definitions/pki.xml.in +++ b/op-mode-definitions/pki.xml.in @@ -20,9 +20,18 @@                    </completionHelp>                  </properties>                  <children> +                  <tagNode name="file"> +                    <properties> +                      <help>Write generated CA certificate into the specified filename</help> +                      <completionHelp> +                        <list><filename></list> +                      </completionHelp> +                    </properties> +                    <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --ca "$7" --sign "$5" --file</command> +                  </tagNode>                    <tagNode name="install">                      <properties> -                      <help>Commands for installing generated certificate into running configuration</help> +                      <help>Commands for installing generated CA certificate into running configuration</help>                        <completionHelp>                          <list><certificate name></list>                        </completionHelp> @@ -32,9 +41,18 @@                  </children>                  <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --ca "noname" --sign "$5"</command>                </tagNode> +              <tagNode name="file"> +                <properties> +                  <help>Write generated CA certificate into the specified filename</help> +                  <completionHelp> +                    <list><filename></list> +                  </completionHelp> +                </properties> +                <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --ca "$5" --file</command> +              </tagNode>                <tagNode name="install">                  <properties> -                  <help>Commands for installing generated certificate into running configuration</help> +                  <help>Commands for installing generated CA certificate into running configuration</help>                    <completionHelp>                      <list><CA name></list>                    </completionHelp> @@ -54,6 +72,15 @@                    <help>Generate self-signed certificate</help>                  </properties>                  <children> +                  <tagNode name="file"> +                    <properties> +                      <help>Write generated self-signed certificate into the specified filename</help> +                      <completionHelp> +                        <list><filename></list> +                      </completionHelp> +                    </properties> +                    <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --certificate "$6" --self-sign --file</command> +                  </tagNode>                    <tagNode name="install">                      <properties>                        <help>Commands for installing generated self-signed certificate into running configuration</help> @@ -74,9 +101,18 @@                    </completionHelp>                  </properties>                  <children> +                  <tagNode name="file"> +                    <properties> +                      <help>Write generated signed certificate into the specified filename</help> +                      <completionHelp> +                        <list><filename></list> +                      </completionHelp> +                    </properties> +                    <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --certificate "$7" --sign "$5" --file</command> +                  </tagNode>                    <tagNode name="install">                      <properties> -                      <help>Commands for installing generated certificate into running configuration</help> +                      <help>Commands for installing generated signed certificate into running configuration</help>                        <completionHelp>                          <list><certificate name></list>                        </completionHelp> @@ -86,6 +122,15 @@                  </children>                  <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --certificate "noname" --sign "$5"</command>                </tagNode> +              <tagNode name="file"> +                <properties> +                  <help>Write generated certificate request and key into the specified filename</help> +                  <completionHelp> +                    <list><filename></list> +                  </completionHelp> +                </properties> +                <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --certificate "$5" --file</command> +              </tagNode>                <tagNode name="install">                  <properties>                    <help>Commands for installing generated certificate private key into running configuration</help> @@ -106,6 +151,15 @@                </completionHelp>              </properties>              <children> +              <tagNode name="file"> +                <properties> +                  <help>Write generated CRL into the specified filename</help> +                  <completionHelp> +                    <list><filename></list> +                  </completionHelp> +                </properties> +                <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --crl "$4" --file</command> +              </tagNode>                <leafNode name="install">                  <properties>                    <help>Commands for installing generated CRL into running configuration</help> @@ -120,6 +174,15 @@                <help>Generate DH parameters</help>              </properties>              <children> +              <tagNode name="file"> +                <properties> +                  <help>Write generated DH parameters into the specified filename</help> +                  <completionHelp> +                    <list><filename></list> +                  </completionHelp> +                </properties> +                <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --dh "$5" --file</command> +              </tagNode>                <tagNode name="install">                  <properties>                    <help>Commands for installing generated DH parameters into running configuration</help> @@ -137,6 +200,15 @@                <help>Generate a key pair</help>              </properties>              <children> +              <tagNode name="file"> +                <properties> +                  <help>Write generated key pair into the specified filename</help> +                  <completionHelp> +                    <list><filename></list> +                  </completionHelp> +                </properties> +                <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --keypair "$5" --file</command> +              </tagNode>                <tagNode name="install">                  <properties>                    <help>Commands for installing generated key pair into running configuration</help> @@ -159,6 +231,15 @@                    <help>Generate OpenVPN shared secret key</help>                  </properties>                  <children> +                  <tagNode name="file"> +                    <properties> +                      <help>Write generated OpenVPN shared secret key into the specified filename</help> +                      <completionHelp> +                        <list><filename></list> +                      </completionHelp> +                    </properties> +                    <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --openvpn "$6" --file</command> +                  </tagNode>                    <tagNode name="install">                      <properties>                        <help>Commands for installing generated OpenVPN shared secret key into running configuration</help> @@ -178,6 +259,15 @@                <help>Generate SSH key</help>              </properties>              <children> +              <tagNode name="file"> +                <properties> +                  <help>Write generated SSH keys into the specified filename</help> +                  <completionHelp> +                    <list><filename></list> +                  </completionHelp> +                </properties> +                <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --ssh "$5" --file</command> +              </tagNode>                <tagNode name="install">                  <properties>                    <help>Commands for installing generated SSH key into running configuration</help> @@ -200,6 +290,15 @@                    <help>Generate Wireguard key pair for use with server or peer</help>                  </properties>                  <children> +                  <tagNode name="file"> +                    <properties> +                      <help>Write generated Wireguard keys into the specified filename</help> +                      <completionHelp> +                        <list><filename></list> +                      </completionHelp> +                    </properties> +                    <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --wireguard --key "$6" --file</command> +                  </tagNode>                    <tagNode name="install">                      <properties>                        <help>Commands for installing generated Wireguard key into running configuration</help> @@ -217,9 +316,18 @@                    <help>Generate pre-shared key for use with a Wireguard peer</help>                  </properties>                  <children> +                  <tagNode name="file"> +                    <properties> +                      <help>Write generated Wireguard PSK into the specified filename</help> +                      <completionHelp> +                        <list><filename></list> +                      </completionHelp> +                    </properties> +                    <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --wireguard --psk "$6" --file</command> +                  </tagNode>                    <tagNode name="install">                      <properties> -                      <help>Commands for installing generated Wireguard psk on specified peer into running configuration</help> +                      <help>Commands for installing generated Wireguard PSK on specified peer into running configuration</help>                        <completionHelp>                          <list><peer></list>                        </completionHelp> diff --git a/src/op_mode/pki.py b/src/op_mode/pki.py index b4a68b31c..297270cf1 100755 --- a/src/op_mode/pki.py +++ b/src/op_mode/pki.py @@ -38,6 +38,8 @@ from vyos.util import cmd  CERT_REQ_END = '-----END CERTIFICATE REQUEST-----' +auth_dir = '/config/auth' +  # Helper Functions  def get_default_values(): @@ -230,6 +232,22 @@ def ask_passphrase():          passphrase = ask_input('Enter passphrase:')      return passphrase +def write_file(filename, contents): +    full_path = os.path.join(auth_dir, filename) +    directory = os.path.dirname(full_path) + +    if not os.path.exists(directory): +        print('Failed to write file: directory does not exist') +        return False + +    if os.path.exists(full_path) and not ask_yes_no('Do you want to overwrite the existing file?'): +        return False + +    with open(full_path, 'w') as f: +        f.write(contents) + +    print(f'File written to {full_path}') +  # Generation functions  def generate_private_key(): @@ -266,7 +284,7 @@ def parse_san_string(san_string):              output.append(value)      return output -def generate_certificate_request(private_key=None, key_type=None, return_request=False, name=None, install=False, ask_san=True): +def generate_certificate_request(private_key=None, key_type=None, return_request=False, name=None, install=False, file=False, ask_san=True):      if not private_key:          private_key, key_type = generate_private_key() @@ -291,14 +309,19 @@ def generate_certificate_request(private_key=None, key_type=None, return_request      passphrase = ask_passphrase() -    if not install: +    if not install and not file:          print(encode_certificate(cert_req))          print(encode_private_key(private_key, passphrase=passphrase))          return None -    print("Certificate request:") -    print(encode_certificate(cert_req) + "\n") -    install_certificate(name, private_key=private_key, key_type=key_type, key_passphrase=passphrase, is_ca=False) +    if install: +        print("Certificate request:") +        print(encode_certificate(cert_req) + "\n") +        install_certificate(name, private_key=private_key, key_type=key_type, key_passphrase=passphrase, is_ca=False) + +    if file: +        write_file(f'{name}.csr', encode_certificate(cert_req)) +        write_file(f'{name}.key', encode_private_key(private_key, passphrase=passphrase))  def generate_certificate(cert_req, ca_cert, ca_private_key, is_ca=False, is_sub_ca=False):      valid_days = ask_input('Enter how many days certificate will be valid:', default='365' if not is_ca else '1825', numeric_only=True) @@ -307,20 +330,25 @@ def generate_certificate(cert_req, ca_cert, ca_private_key, is_ca=False, is_sub_          cert_type = ask_input('Enter certificate type: (client, server)', default='server', valid_responses=['client', 'server'])      return create_certificate(cert_req, ca_cert, ca_private_key, valid_days, cert_type, is_ca, is_sub_ca) -def generate_ca_certificate(name, install=False): +def generate_ca_certificate(name, install=False, file=False):      private_key, key_type = generate_private_key()      cert_req = generate_certificate_request(private_key, key_type, return_request=True, ask_san=False)      cert = generate_certificate(cert_req, cert_req, private_key, is_ca=True)      passphrase = ask_passphrase() -    if not install: +    if not install and not file:          print(encode_certificate(cert))          print(encode_private_key(private_key, passphrase=passphrase))          return None -    install_certificate(name, cert, private_key, key_type, key_passphrase=passphrase, is_ca=True) +    if install: +        install_certificate(name, cert, private_key, key_type, key_passphrase=passphrase, is_ca=True) + +    if file: +        write_file(f'{name}.pem', encode_certificate(cert)) +        write_file(f'{name}.key', encode_private_key(private_key, passphrase=passphrase)) -def generate_ca_certificate_sign(name, ca_name, install=False): +def generate_ca_certificate_sign(name, ca_name, install=False, file=False):      ca_dict = get_config_ca_certificate(ca_name)      if not ca_dict: @@ -374,14 +402,19 @@ def generate_ca_certificate_sign(name, ca_name, install=False):      cert = generate_certificate(cert_req, ca_cert, ca_private_key, is_ca=True, is_sub_ca=True)      passphrase = ask_passphrase() -    if not install: +    if not install and not file:          print(encode_certificate(cert))          print(encode_private_key(private_key, passphrase=passphrase))          return None -    install_certificate(name, cert, private_key, key_type, key_passphrase=passphrase, is_ca=True) +    if install: +        install_certificate(name, cert, private_key, key_type, key_passphrase=passphrase, is_ca=True) + +    if file: +        write_file(f'{name}.pem', encode_certificate(cert)) +        write_file(f'{name}.key', encode_private_key(private_key, passphrase=passphrase)) -def generate_certificate_sign(name, ca_name, install=False): +def generate_certificate_sign(name, ca_name, install=False, file=False):      ca_dict = get_config_ca_certificate(ca_name)      if not ca_dict: @@ -435,27 +468,37 @@ def generate_certificate_sign(name, ca_name, install=False):      cert = generate_certificate(cert_req, ca_cert, ca_private_key, is_ca=False)      passphrase = ask_passphrase() -    if not install: +    if not install and not file:          print(encode_certificate(cert))          print(encode_private_key(private_key, passphrase=passphrase))          return None -    install_certificate(name, cert, private_key, key_type, key_passphrase=passphrase, is_ca=False) +    if install: +        install_certificate(name, cert, private_key, key_type, key_passphrase=passphrase, is_ca=False) + +    if file: +        write_file(f'{name}.pem', encode_certificate(cert)) +        write_file(f'{name}.key', encode_private_key(private_key, passphrase=passphrase)) -def generate_certificate_selfsign(name, install=False): +def generate_certificate_selfsign(name, install=False, file=False):      private_key, key_type = generate_private_key()      cert_req = generate_certificate_request(private_key, key_type, return_request=True)      cert = generate_certificate(cert_req, cert_req, private_key, is_ca=False)      passphrase = ask_passphrase() -    if not install: +    if not install and not file:          print(encode_certificate(cert))          print(encode_private_key(private_key, passphrase=passphrase))          return None -    install_certificate(name, cert, private_key=private_key, key_type=key_type, key_passphrase=passphrase, is_ca=False) +    if install: +        install_certificate(name, cert, private_key=private_key, key_type=key_type, key_passphrase=passphrase, is_ca=False) + +    if file: +        write_file(f'{name}.pem', encode_certificate(cert)) +        write_file(f'{name}.key', encode_private_key(private_key, passphrase=passphrase)) -def generate_certificate_revocation_list(ca_name, install=False): +def generate_certificate_revocation_list(ca_name, install=False, file=False):      ca_dict = get_config_ca_certificate(ca_name)      if not ca_dict: @@ -505,26 +548,35 @@ def generate_certificate_revocation_list(ca_name, install=False):          print("Failed to create CRL")          return None -    if not install: +    if not install and not file:          print(encode_certificate(crl))          return None -    install_crl(ca_name, crl) +    if install: +        install_crl(ca_name, crl) + +    if file: +        write_file(f'{name}.crl', encode_certificate(crl)) -def generate_ssh_keypair(name, install=False): +def generate_ssh_keypair(name, install=False, file=False):      private_key, key_type = generate_private_key()      public_key = private_key.public_key()      passphrase = ask_passphrase() -    if not install: +    if not install and not file:          print(encode_public_key(public_key, encoding='OpenSSH', key_format='OpenSSH'))          print("")          print(encode_private_key(private_key, encoding='PEM', key_format='OpenSSH', passphrase=passphrase))          return None -    install_ssh_key(name, public_key, private_key, passphrase) +    if install: +        install_ssh_key(name, public_key, private_key, passphrase) + +    if file: +        write_file(f'{name}.pem', encode_public_key(public_key, encoding='OpenSSH', key_format='OpenSSH')) +        write_file(f'{name}.key', encode_private_key(private_key, encoding='PEM', key_format='OpenSSH', passphrase=passphrase)) -def generate_dh_parameters(name, install=False): +def generate_dh_parameters(name, install=False, file=False):      bits = ask_input('Enter DH parameters key size:', default=2048, numeric_only=True)      print("Generating parameters...") @@ -534,49 +586,62 @@ def generate_dh_parameters(name, install=False):          print("Failed to create DH parameters")          return None -    if not install: +    if not install and not file:          print("DH Parameters:")          print(encode_dh_parameters(dh_params)) -    install_dh_parameters(name, dh_params) +    if install: +        install_dh_parameters(name, dh_params) + +    if file: +        write_file(f'{name}.pem', encode_dh_parameters(dh_params)) -def generate_keypair(name, install=False): +def generate_keypair(name, install=False, file=False):      private_key, key_type = generate_private_key()      public_key = private_key.public_key()      passphrase = ask_passphrase() -    if not install: +    if not install and not file:          print(encode_public_key(public_key))          print("")          print(encode_private_key(private_key, passphrase=passphrase))          return None -    install_keypair(name, key_type, private_key, public_key, passphrase) +    if install: +        install_keypair(name, key_type, private_key, public_key, passphrase) + +    if file: +        write_file(f'{name}.pem', encode_public_key(public_key)) +        write_file(f'{name}.key', encode_private_key(private_key, passphrase=passphrase)) -def generate_openvpn_key(name, install=False): +def generate_openvpn_key(name, install=False, file=False):      result = cmd('openvpn --genkey secret /dev/stdout | grep -o "^[^#]*"')      if not result:          print("Failed to generate OpenVPN key")          return None -    if not install: +    if not install and not file:          print(result)          return None -    key_lines = result.split("\n") -    key_data = "".join(key_lines[1:-1]) # Remove wrapper tags and line endings -    key_version = '1' +    if install: +        key_lines = result.split("\n") +        key_data = "".join(key_lines[1:-1]) # Remove wrapper tags and line endings +        key_version = '1' + +        version_search = re.search(r'BEGIN OpenVPN Static key V(\d+)', result) # Future-proofing (hopefully) +        if version_search: +            key_version = version_search[1] -    version_search = re.search(r'BEGIN OpenVPN Static key V(\d+)', result) # Future-proofing (hopefully) -    if version_search: -        key_version = version_search[1] +        print("Configure mode commands to install OpenVPN key:") +        print("set pki openvpn shared-secret %s key '%s'" % (name, key_data)) +        print("set pki openvpn shared-secret %s version '%s'" % (name, key_version)) -    print("Configure mode commands to install OpenVPN key:") -    print("set pki openvpn shared-secret %s key '%s'" % (name, key_data)) -    print("set pki openvpn shared-secret %s version '%s'" % (name, key_version)) +    if file: +        write_file(f'{name}.key', result) -def generate_wireguard_key(name, install=False): +def generate_wireguard_key(name, install=False, file=False):      private_key = cmd('wg genkey')      public_key = cmd('wg pubkey', input=private_key) @@ -585,17 +650,26 @@ def generate_wireguard_key(name, install=False):          print("Public key: " + public_key)          return None -    install_wireguard_key(name, private_key, public_key) +    if install: +        install_wireguard_key(name, private_key, public_key) -def generate_wireguard_psk(name, install=False): +    if file: +        write_file(f'{name}_public.key', public_key) +        write_file(f'{name}_private.key', private_key) + +def generate_wireguard_psk(name, install=False, file=False):      psk = cmd('wg genpsk') -    if not install: +    if not install and not file:          print("Pre-shared key:")          print(psk)          return None -    install_wireguard_psk(name, psk) +    if install: +        install_wireguard_psk(name, psk) + +    if file: +        write_file(f'{name}.key', psk)  # Show functions @@ -721,6 +795,7 @@ if __name__ == '__main__':      parser.add_argument('--psk', help='Wireguard pre shared key', required=False)      # Global +    parser.add_argument('--file', help='Write generated keys into specified filename', action='store_true')      parser.add_argument('--install', help='Install generated keys into running-config', action='store_true')      args = parser.parse_args() @@ -729,31 +804,31 @@ if __name__ == '__main__':          if args.action == 'generate':              if args.ca:                  if args.sign: -                    generate_ca_certificate_sign(args.ca, args.sign, args.install) +                    generate_ca_certificate_sign(args.ca, args.sign, install=args.install, file=args.file)                  else: -                    generate_ca_certificate(args.ca, args.install) +                    generate_ca_certificate(args.ca, install=args.install, file=args.file)              elif args.certificate:                  if args.sign: -                    generate_certificate_sign(args.certificate, args.sign, args.install) +                    generate_certificate_sign(args.certificate, args.sign, install=args.install, file=args.file)                  elif args.self_sign: -                    generate_certificate_selfsign(args.certificate, args.install) +                    generate_certificate_selfsign(args.certificate, install=args.install, file=args.file)                  else:                      generate_certificate_request(name=args.certificate, install=args.install)              elif args.crl: -                generate_certificate_revocation_list(args.crl, args.install) +                generate_certificate_revocation_list(args.crl, install=args.install, file=args.file)              elif args.ssh: -                generate_ssh_keypair(args.ssh, args.install) +                generate_ssh_keypair(args.ssh, install=args.install, file=args.file)              elif args.dh: -                generate_dh_parameters(args.dh, args.install) +                generate_dh_parameters(args.dh, install=args.install, file=args.file)              elif args.keypair: -                generate_keypair(args.keypair, args.install) +                generate_keypair(args.keypair, install=args.install, file=args.file)              elif args.openvpn: -                generate_openvpn_key(args.openvpn, args.install) +                generate_openvpn_key(args.openvpn, install=args.install, file=args.file)              elif args.wireguard:                  if args.key: -                    generate_wireguard_key(args.key, args.install) +                    generate_wireguard_key(args.key, install=args.install, file=args.file)                  elif args.psk: -                    generate_wireguard_psk(args.psk, args.install) +                    generate_wireguard_psk(args.psk, install=args.install, file=args.file)          elif args.action == 'show':              if args.ca:                  show_certificate_authority(None if args.ca == 'all' else args.ca)  | 
