# -*- coding: utf-8 -*- # # Copyright (C) 2018 Branko Majic # # This file is part of Gimmecert. # # Gimmecert is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) any # later version. # # Gimmecert is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along with # Gimmecert. If not, see . # from .base import run_command def test_server_command_available_with_help(): # John has finally finished initialising his CA hierarchy. What he # wants to do now is to issue a server certificate. He starts off # by having a look at the list of available commands. stdout, stderr, exit_code = run_command("gimmecert") # Looking at output, John notices the server command. assert exit_code == 0 assert stderr == "" assert "server" in stdout # He goes ahead and has a look at the server command invocation to # check what kind of parameters he might need to provide. stdout, stderr, exit_code = run_command("gimmecert", "server", "-h") # John can see that the command accepts an entity name, and an # optional list of DNS subject alternative names. assert exit_code == 0 assert stderr == "" assert stdout.startswith("usage: gimmecert server") assert " entity_name [dns_name [dns_name ...]]" in stdout def test_server_command_requires_initialised_hierarchy(tmpdir): # John is about to issue a server certificate. He switches to his # project directory. tmpdir.chdir() # John tries to issue a server certificate. stdout, stderr, exit_code = run_command("gimmecert", "server", "myserver") # Unfortunately, John has forgotten to initialise the CA hierarchy # from within this directory, and is instead presented with an # error. assert stdout == "" assert stderr == "CA hierarchy must be initialised prior to issuing server certificates. Run the gimmecert init command first.\n" assert exit_code != 0 def test_server_command_issues_server_certificate(tmpdir): # John is about to issue a server certificate. He switches to his # project directory, and initialises the CA hierarchy there. tmpdir.chdir() run_command("gimmecert", "init") # He then runs command for issuing a server certificate. stdout, stderr, exit_code = run_command('gimmecert', 'server', 'myserver') # John notices that the command has run without an error, and that # it has printed out path to the private key and certificate. assert stderr == "" assert exit_code == 0 assert "Server certificate issued." in stdout assert ".gimmecert/server/myserver.key.pem" in stdout assert ".gimmecert/server/myserver.cert.pem" in stdout # John has a look at the generated private key using the OpenSSL # CLI. stdout, stderr, exit_code = run_command('openssl', 'rsa', '-noout', '-text', '-in', '.gimmecert/server/myserver.key.pem') # No errors are reported, and John is able to see some details # about the generated key. assert exit_code == 0 assert stderr == "" assert "Private-Key: (2048 bit)" in stdout # John then has a look at the generated certificate file. stdout, stderr, exit_code = run_command('openssl', 'x509', '-noout', '-text', '-in', '.gimmecert/server/myserver.cert.pem') # Once again, there are no errors, and he can see some details # about the certificate. assert exit_code == 0 assert stderr == "" assert 'Certificate:' in stdout # He notices that the certificate includes the provided entity # name as DNS subject alternative name. assert "DNS:myserver\n" in stdout # John has a quick look at issuer and subject DN stored in # certificate. issuer_dn, _, _ = run_command('openssl', 'x509', '-noout', '-issuer', '-in', '.gimmecert/server/myserver.cert.pem') subject_dn, _, _ = run_command('openssl', 'x509', '-noout', '-subject', '-in', '.gimmecert/server/myserver.cert.pem') issuer_dn = issuer_dn.replace('issuer=', '', 1).rstrip().replace(' /CN=', 'CN = ', 1) # OpenSSL 1.0 vs 1.1 formatting subject_dn = subject_dn.replace('subject=', '', 1).rstrip().replace(' /CN=', 'CN = ', 1) # OpenSSL 1.0 vs 1.1 formatting # He notices the issuer DN is as expected based on the directory # name, and that server certificate subject DN simply has CN field # with the name he provided earlier. assert issuer_dn == "CN = %s Level 1 CA" % tmpdir.basename assert subject_dn == "CN = myserver" # John takes a look at certificate purpose, since he wants to # ensure it is a proper TLS server certificate. stdout, stderr, exit_code = run_command('openssl', 'x509', '-noout', '-purpose', '-in', '.gimmecert/server/myserver.cert.pem') # He verifies that the provided certificate has correct purpose. assert "SSL server : Yes" in stdout assert "SSL server CA : No" in stdout assert "SSL client : No" in stdout assert "SSL client CA : No" in stdout # Finally, he decides to check if the certificate can be verified # using the CA certificate chain. _, _, error_code = run_command( "openssl", "verify", "-CAfile", ".gimmecert/ca/chain-full.cert.pem", ".gimmecert/server/myserver.cert.pem" ) # He is happy to see that verification succeeds. assert error_code == 0 def test_server_command_issues_server_certificate_with_additional_subject_alternative_names(tmpdir): # John wants to issue a server certificate that will include a # number of additional DNS subject alternative names. He switches # to his project directory, and initialises the CA hierarchy # there. tmpdir.chdir() run_command("gimmecert", "init") # He then runs command for issuing a server certificate, providing # additional DNS subject alternative names.. stdout, stderr, exit_code = run_command('gimmecert', 'server', 'myserver', 'myserver.local', 'myserver.example.com') # The command finishes without any errors being reported. assert stderr == "" assert exit_code == 0 # John then a look at generated certificate file. stdout, stderr, exit_code = run_command('openssl', 'x509', '-noout', '-text', '-in', '.gimmecert/server/myserver.cert.pem') # No errors are reported, and he notices that the provided subject # alternative names have been included in the certificate in # addition to default one based on the server entity name. assert exit_code == 0 assert stderr == "" assert "DNS:myserver," in stdout assert "DNS:myserver.local," in stdout assert "DNS:myserver.example.com\n" in stdout def test_server_command_does_not_overwrite_existing_artifacts(tmpdir): # John has become an avid user of Gimmecert. He uses it in a lot # of projects, including one specific project which he had set-up # a couple of months ago, where he has issued some server # certificate. tmpdir.chdir() run_command("gimmecert", "init") run_command("gimmecert", "server", "myserver") private_key = tmpdir.join(".gimmecert", "server", "myserver.key.pem").read() certificate = tmpdir.join(".gimmecert", "server", "myserver.cert.pem").read() # After a couple of months of inactivity on that particular # project, John is again asked to do something in relation to # it. He recalls that he needed to issue a server certificate for # it, so he goes ahead and tries to do it again. tmpdir.chdir() stdout, stderr, exit_code = run_command("gimmecert", "server", "myserver") # John realizes in last moment, just as he presses ENTER, that he # had issued certificate already. He wonders if he'd need to # redeploy it again now, though. To his (small) relief, he # realizes is it not necessary, since the tool has refused to # overwrite the old key and certificate. Instead he is presented # with an error notifying him that the certificate has already # been issued. assert exit_code != 0 assert stderr == "Refusing to overwrite existing data. Certificate has already been issued for server myserver.\n" assert stdout == "" # John double-checks (just to be on the safe side), and can see # that both the private key and certificate have been left # unchanged. assert tmpdir.join(".gimmecert", "server", "myserver.key.pem").read() == private_key assert tmpdir.join(".gimmecert", "server", "myserver.cert.pem").read() == certificate def test_server_command_update_option(tmpdir): # John is in a bit of a rush to get his project going. Since he # needs a server certificate issued, he goes ahead and quickly # initialises CA and issues a single server certificate, with # intention of accessing the service via URL # https://myservice.example.com/. tmpdir.chdir() run_command("gimmecert", "init") run_command("gimmecert", "server", "myserver1", "mysercive.example.com") # Once he imports the CA certificate into his browser, and tries # to access the service page, he very quickly finds out that he # has misspelled "myservice". Just to be on the safe side, he has # a look at the certificate using the OpenSSL CLI. stdout, stderr, exit_code = run_command('openssl', 'x509', '-noout', '-text', '-in', '.gimmecert/server/myserver1.cert.pem') # And indeed, in addition to his server name, he can see that the # extra DNS subject alternative name he provided is wrong. assert "DNS:myserver1," in stdout assert "DNS:mysercive.example.com\n" in stdout assert "DNS:myservice.example.com" not in stdout # Not sure what to do, he takes a quick look at help for the # server command. stdout, stderr, exit_code = run_command("gimmecert", "server", "-h") # He notices there is an option for updating an DNS subject # alternative names for a server certificate. assert " --update-dns-names" in stdout assert " -u\n" in stdout # Based on help description, this seems to be exactly what he # needs. Private key is still fine, but his certificate needs to # be reissued. He goes ahead and runs the command for updating the # DNS subject alternative names. stdout, stderr, exit_code = run_command("gimmecert", "server", "--update-dns-names", "myserver1", "myservice.example.com") # He notices that no error has been reported by the command, and # that he is informed that the certificate has been renewed with # new DNS names, while the private key has been preserved. assert exit_code == 0 assert "renewed with new DNS subject alternative names" in stdout assert "key has remained unchanged" in stdout # Being paranoid, he decides to double-check the certificate, just # to be on the safe side. He uses the OpenSSL CLI for this # purpose. stdout, stderr, exit_code = run_command('openssl', 'x509', '-noout', '-text', '-in', '.gimmecert/server/myserver1.cert.pem') # He notices that certificate includes the intended naming. He can # finally move ahead with his project. assert "DNS:myserver1," in stdout assert "DNS:myservice.example.com\n" in stdout # John now needs another server certificate. Being a bit lazy, he # goes up in his shell history, changes the server name, and # reruns the command. stdout, stderr, exit_code = run_command("gimmecert", "server", "--update-dns-names", "myserver2", "myservice.example.com") # Just as he presses enter, he realizes that he has run the # command with --update-dns-names option, even though he has not # issued this particular server certificate before. Despite this, # no errors are reported, and he gets the familiar message about # server certificate being issued. Cool! assert stderr == "" assert exit_code == 0 assert "Server certificate issued." in stdout assert ".gimmecert/server/myserver2.key.pem" in stdout assert ".gimmecert/server/myserver2.cert.pem" in stdout