author | Alberto Bertogli
<albertito@blitiri.com.ar> 2020-08-21 01:46:14 UTC |
committer | Alberto Bertogli
<albertito@blitiri.com.ar> 2020-08-21 15:53:37 UTC |
parent | 2ec7193cf143884db2f7a7017424284d329afbf6 |
.travis.yml | +2 | -2 |
Makefile | +12 | -4 |
README.md | +2 | -3 |
doc/quick_start.md | +4 | -2 |
kxgencert/kxgencert.go | +104 | -0 |
scripts/create-kxd-config | +10 | -12 |
scripts/kxc-add-key | +4 | -12 |
tests/openssl.cnf | +0 | -276 |
tests/run_tests | +13 | -159 |
diff --git a/.travis.yml b/.travis.yml index 69da01f..fc16647 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,13 +7,13 @@ go_import_path: blitiri.com.ar/go/kxd go: - 1.11 # Debian stable. - - tip + - stable + - master addons: apt: packages: - python3 - - openssl # Nothing to do for install, the tests will build the binaries anyway. install: true diff --git a/Makefile b/Makefile index 882f635..649b94d 100644 --- a/Makefile +++ b/Makefile @@ -2,11 +2,14 @@ GO = go OUTDIR = ./out -default: kxd kxc +default: kxd kxc kxgencert kxd: $(GO) build -o $(OUTDIR)/kxd ./kxd +kxgencert: + $(GO) build -o $(OUTDIR)/kxgencert ./kxgencert + # For the client, because it can be run in a very limited environment without # glibc (like initramfs), we build it using the native go networking so it can # work even when glibc's resolvers are missing. @@ -19,7 +22,7 @@ fmt: vet: $(GO) vet ./... -test: kxd kxc +test: kxd kxc kxgencert tests/run_tests -b tests: test @@ -33,7 +36,8 @@ SYSTEMDDIR=$(shell pkg-config systemd --variable=systemdsystemunitdir) # Install utility, we assume it's GNU/BSD compatible. INSTALL=install -install-all: install-kxd install-init.d install-kxc install-initramfs +install-all: install-kxd install-kxc install-kxgencert \ + install-init.d install-initramfs install-kxd: kxd $(INSTALL) -d $(PREFIX)/bin @@ -41,6 +45,10 @@ install-kxd: kxd $(INSTALL) -m 0755 scripts/create-kxd-config $(PREFIX)/bin/ $(INSTALL) -m 0755 scripts/kxd-add-client-key $(PREFIX)/bin/ +install-kxgencert: kxgencert + $(INSTALL) -d $(PREFIX)/bin + $(INSTALL) -m 0755 out/kxgencert $(PREFIX)/bin/ + install-init.d: install-kxd $(INSTALL) -m 0755 scripts/init.d/kxd $(ETCDIR)/init.d/kxd $(INSTALL) -m 0644 scripts/default/kxd $(ETCDIR)/default/kxd @@ -67,7 +75,7 @@ install-initramfs: install-kxc $(PREFIX)/share/initramfs-tools/scripts/init-premount/ -.PHONY: kxd kxc +.PHONY: kxd kxc kxgencert .PHONY: install-all install-kxd install-init.d install-kxc install-initramfs .PHONY: test tests diff --git a/README.md b/README.md index 70ce4a1..3f61e13 100644 --- a/README.md +++ b/README.md @@ -75,10 +75,9 @@ There are no runtime dependencies for the kxd and kxc binaries. Building requires Go 1.11. The configuration helper scripts (`create-kxd-config`, `kxc-add-key`, etc.) -depend on: `bash`, `openssl` (the binary), and core utilities (`mkdir`, `dd`, -etc.). +depend on: `bash` and core utilities (`mkdir`, `dd`, etc.). -Testing needs Python 3, and openssl (the binary). +Testing needs Python 3. ## Bugs and contact diff --git a/doc/quick_start.md b/doc/quick_start.md index 353094e..a64337c 100644 --- a/doc/quick_start.md +++ b/doc/quick_start.md @@ -19,10 +19,12 @@ be similar but may differ on some of the details (specially on the Install [kxd] on the server, via your distribution packages (e.g. `apt install kxd`), or directly from source. -Then, run `create-kxd-config`, which will create the configuration +Then, run `create-kxd-config <hostname>`, which will create the configuration directories, and generate a [self-signed] key/cert pair for the server (valid for 10 years). -Everything is in `/etc/kxd/`. + +For the hostname, use the name or IP that the clients will use to reach the +server. You can use more than one, separated with commas. ## Client setup diff --git a/kxgencert/kxgencert.go b/kxgencert/kxgencert.go new file mode 100644 index 0000000..e1f40a7 --- /dev/null +++ b/kxgencert/kxgencert.go @@ -0,0 +1,104 @@ +// Utility to generate self-signed certificates. +// It generates a self-signed x509 certificate and key pair. +package main + +import ( + crand "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "flag" + "fmt" + "math/big" + "net" + "os" + "strings" + "time" +) + +var ( + host = flag.String("host", "*", + "Hostnames/IPs to generate the certificate for (comma separated)") + validFor = flag.Duration("validfor", 24*time.Hour*365*10, + "How long will the certificate be valid for (default: 10y)") + orgName = flag.String("organization", "", + "Organization to use in the certificate, useful for debugging") + + certPath = flag.String("cert", "cert.pem", + "Where to write the generated certificate") + keyPath = flag.String("key", "key.pem", + "Where to write the generated key") +) + +func fatalf(f string, a ...interface{}) { + fmt.Printf(f, a...) + os.Exit(1) +} + +func main() { + flag.Parse() + + // Build the certificate template. + serial, err := crand.Int(crand.Reader, big.NewInt(1<<62)) + if err != nil { + fatalf("Error generating serial number: %v\n", err) + } + tmpl := x509.Certificate{ + SerialNumber: serial, + Subject: pkix.Name{Organization: []string{*orgName}}, + + NotBefore: time.Now(), + NotAfter: time.Now().Add(*validFor), + + KeyUsage: x509.KeyUsageKeyEncipherment | + x509.KeyUsageDigitalSignature | + x509.KeyUsageCertSign, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + + BasicConstraintsValid: true, + } + + hosts := strings.Split(*host, ",") + for _, h := range hosts { + if ip := net.ParseIP(h); ip != nil { + tmpl.IPAddresses = append(tmpl.IPAddresses, ip) + } else { + tmpl.DNSNames = append(tmpl.DNSNames, h) + } + } + + // Generate a private key (RSA 2048). + privK, err := rsa.GenerateKey(crand.Reader, 2048) + if err != nil { + fatalf("Error generating key: %v\n", err) + } + + // Write the certificate. + { + derBytes, err := x509.CreateCertificate( + crand.Reader, &tmpl, &tmpl, &privK.PublicKey, privK) + if err != nil { + fatalf("Failed to create certificate: %v\n", err) + } + + fullchain, err := os.Create(*certPath) + if err != nil { + fatalf("Failed to open %q: %v\n", *certPath, err) + } + pem.Encode(fullchain, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) + fullchain.Close() + } + + // Write the private key. + { + privkey, err := os.Create(*keyPath) + if err != nil { + fatalf("failed to open %q: %v\n", *keyPath, err) + } + block := &pem.Block{Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(privK)} + pem.Encode(privkey, block) + privkey.Close() + } +} diff --git a/scripts/create-kxd-config b/scripts/create-kxd-config index 3f1c6f0..6861eef 100755 --- a/scripts/create-kxd-config +++ b/scripts/create-kxd-config @@ -7,6 +7,11 @@ # # It should be run under the same user as kxd itself. +if [ "$1" == "" ]; then + echo "Usage: $0 <hostname>" + exit 1 +fi + set -e # Create the base configuration directory. @@ -18,19 +23,12 @@ mkdir -p /etc/kxd/data # Create a private key for the server. if ! [ -e /etc/kxd/key.pem ]; then - echo "Generating private key (/etc/kxd/key.pem)" - openssl genrsa -out /etc/kxd/key.pem 2048 + kxgencert \ + -host "${1?}" \ + -organization "kxd@$HOSTNAME" \ + -key /etc/kxd/key.pem \ + -cert /etc/kxd/cert.pem chmod 400 /etc/kxd/key.pem else echo "Private key already exists (/etc/kxd/key.pem)" fi - -# And a self-signed certificate. -if ! [ -e /etc/kxd/cert.pem ]; then - echo "Generating certificate (/etc/kxd/cert.pem)" - openssl req -new -x509 -batch -days 3650 \ - -subj "/commonName=*/organizationalUnitName=kxd@$HOSTNAME/" \ - -key /etc/kxd/key.pem -out /etc/kxd/cert.pem -else - echo "Certificate already exists (/etc/kxd/cert.pem)" -fi diff --git a/scripts/kxc-add-key b/scripts/kxc-add-key index 0e62e43..bc84ecd 100755 --- a/scripts/kxc-add-key +++ b/scripts/kxc-add-key @@ -29,23 +29,15 @@ mkdir -p /etc/kxc/ # Create a private key for the client. if ! [ -e /etc/kxc/key.pem ]; then - echo "Generating private key (/etc/kxc/key.pem)" - openssl genrsa -out /etc/kxc/key.pem 2048 + kxgencert \ + -organization "kxc@$HOSTNAME" \ + -key /etc/kxc/key.pem \ + -cert /etc/kxc/cert.pem chmod 400 /etc/kxc/key.pem else echo "Private key already exists (/etc/kxc/key.pem)" fi -# And a self-signed certificate. -if ! [ -e /etc/kxc/cert.pem ]; then - echo "Generating certificate (/etc/kxc/cert.pem)" - openssl req -new -x509 -batch -days 3650 \ - -subj "/commonName=*/organizationalUnitName=kxc@$HOSTNAME/" \ - -key /etc/kxc/key.pem -out /etc/kxc/cert.pem -else - echo "Certificate already exists (/etc/kxc/cert.pem)" -fi - echo "Setting URL to kxd://$SERVER/$HOSTNAME/$KEYNAME" echo "kxd://$SERVER/$HOSTNAME/$KEYNAME" > "/etc/kxc/${KEYNAME}.url" diff --git a/tests/openssl.cnf b/tests/openssl.cnf deleted file mode 100644 index 9c8b4f3..0000000 --- a/tests/openssl.cnf +++ /dev/null @@ -1,276 +0,0 @@ -# OpenSSL configuration for kxd tests. -# -# This file is used in some of the kxd tests, to avoid depending on the local -# configuration which can vary between different systems. -# -# It's only used for CA operations, so it focuses on that. -# It is based on Debian's default configuration. -############################################################################# - -# This definition stops the following lines choking if HOME isn't -# defined. -HOME = . -RANDFILE = $ENV::HOME/.rnd - -[ ca ] -default_ca = CA_default - -[ CA_default ] - -dir = ./kxd-ca # Where everything is kept -certs = $dir/certs # Where the issued certs are kept -crl_dir = $dir/crl # Where the issued crl are kept -database = $dir/index.txt # database index file. -#unique_subject = no # Set to 'no' to allow creation of - # several ctificates with same subject. -new_certs_dir = $dir/newcerts # default place for new certs. - -certificate = $dir/cacert.pem # The CA certificate -serial = $dir/serial # The current serial number -crlnumber = $dir/crlnumber # the current crl number - # must be commented out to leave a V1 CRL -crl = $dir/crl.pem # The current CRL -private_key = $dir/private/cakey.pem # The private key -RANDFILE = $dir/private/.rand # private random number file - -x509_extensions = usr_cert # The extentions to add to the cert - -# Comment out the following two lines for the "traditional" -# (and highly broken) format. -name_opt = ca_default # Subject Name options -cert_opt = ca_default # Certificate field options - -default_days = 365 # how long to certify for -default_crl_days= 30 # how long before next CRL -default_md = default # use public key default MD -preserve = no # keep passed DN ordering - -policy = policy_anything - -[ policy_anything ] -countryName = optional -stateOrProvinceName = optional -localityName = optional -organizationName = optional -organizationalUnitName = optional -commonName = supplied -emailAddress = optional - -#################################################################### -[ req ] -default_bits = 2048 -default_keyfile = privkey.pem -distinguished_name = req_distinguished_name -attributes = req_attributes -x509_extensions = v3_ca # The extentions to add to the self signed cert - -# Passwords for private keys if not present they will be prompted for -# input_password = secret -# output_password = secret - -# This sets a mask for permitted string types. There are several options. -# default: PrintableString, T61String, BMPString. -# pkix : PrintableString, BMPString (PKIX recommendation before 2004) -# utf8only: only UTF8Strings (PKIX recommendation after 2004). -# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings). -# MASK:XXXX a literal mask value. -# WARNING: ancient versions of Netscape crash on BMPStrings or UTF8Strings. -string_mask = utf8only - -# req_extensions = v3_req # The extensions to add to a certificate request - -[ req_distinguished_name ] -countryName = Country Name (2 letter code) -countryName_default = AU -countryName_min = 2 -countryName_max = 2 - -stateOrProvinceName = State or Province Name (full name) -stateOrProvinceName_default = Some-State - -localityName = Locality Name (eg, city) - -0.organizationName = Organization Name (eg, company) -0.organizationName_default = Internet Widgits Pty Ltd - -# we can do this but it is not needed normally :-) -#1.organizationName = Second Organization Name (eg, company) -#1.organizationName_default = World Wide Web Pty Ltd - -organizationalUnitName = Organizational Unit Name (eg, section) -#organizationalUnitName_default = - -commonName = Common Name (e.g. server FQDN or YOUR name) -commonName_max = 64 - -emailAddress = Email Address -emailAddress_max = 64 - -# SET-ex3 = SET extension number 3 - -[ req_attributes ] -challengePassword = A challenge password -challengePassword_min = 4 -challengePassword_max = 20 - -unstructuredName = An optional company name - -[ usr_cert ] - -# These extensions are added when 'ca' signs a request. - -# This goes against PKIX guidelines but some CAs do it and some software -# requires this to avoid interpreting an end user certificate as a CA. - -basicConstraints=CA:FALSE - -# Here are some examples of the usage of nsCertType. If it is omitted -# the certificate can be used for anything *except* object signing. - -# This is OK for an SSL server. -# nsCertType = server - -# For an object signing certificate this would be used. -# nsCertType = objsign - -# For normal client use this is typical -# nsCertType = client, email - -# and for everything including object signing: -# nsCertType = client, email, objsign - -# This is typical in keyUsage for a client certificate. -# keyUsage = nonRepudiation, digitalSignature, keyEncipherment - -# This will be displayed in Netscape's comment listbox. -nsComment = "OpenSSL Generated Certificate" - -# PKIX recommendations harmless if included in all certificates. -subjectKeyIdentifier=hash -authorityKeyIdentifier=keyid,issuer - -# This stuff is for subjectAltName and issuerAltname. -# Import the email address. -# subjectAltName=email:copy -# An alternative to produce certificates that aren't -# deprecated according to PKIX. -# subjectAltName=email:move - -# Copy subject details -# issuerAltName=issuer:copy - -#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem -#nsBaseUrl -#nsRevocationUrl -#nsRenewalUrl -#nsCaPolicyUrl -#nsSslServerName - -# This is required for TSA certificates. -# extendedKeyUsage = critical,timeStamping - -[ v3_req ] - -# Extensions to add to a certificate request - -basicConstraints = CA:FALSE -keyUsage = nonRepudiation, digitalSignature, keyEncipherment - -[ v3_ca ] - - -# Extensions for a typical CA - - -# PKIX recommendation. - -subjectKeyIdentifier=hash - -authorityKeyIdentifier=keyid:always,issuer - -# This is what PKIX recommends but some broken software chokes on critical -# extensions. -#basicConstraints = critical,CA:true -# So we do this instead. -basicConstraints = CA:true - -# Key usage: this is typical for a CA certificate. However since it will -# prevent it being used as an test self-signed certificate it is best -# left out by default. -# keyUsage = cRLSign, keyCertSign - -# Some might want this also -# nsCertType = sslCA, emailCA - -# Include email address in subject alt name: another PKIX recommendation -# subjectAltName=email:copy -# Copy issuer details -# issuerAltName=issuer:copy - -# DER hex encoding of an extension: beware experts only! -# obj=DER:02:03 -# Where 'obj' is a standard or added object -# You can even override a supported extension: -# basicConstraints= critical, DER:30:03:01:01:FF - -[ crl_ext ] - -# CRL extensions. -# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL. - -# issuerAltName=issuer:copy -authorityKeyIdentifier=keyid:always - -[ proxy_cert_ext ] -# These extensions should be added when creating a proxy certificate - -# This goes against PKIX guidelines but some CAs do it and some software -# requires this to avoid interpreting an end user certificate as a CA. - -basicConstraints=CA:FALSE - -# Here are some examples of the usage of nsCertType. If it is omitted -# the certificate can be used for anything *except* object signing. - -# This is OK for an SSL server. -# nsCertType = server - -# For an object signing certificate this would be used. -# nsCertType = objsign - -# For normal client use this is typical -# nsCertType = client, email - -# and for everything including object signing: -# nsCertType = client, email, objsign - -# This is typical in keyUsage for a client certificate. -# keyUsage = nonRepudiation, digitalSignature, keyEncipherment - -# This will be displayed in Netscape's comment listbox. -nsComment = "OpenSSL Generated Certificate" - -# PKIX recommendations harmless if included in all certificates. -subjectKeyIdentifier=hash -authorityKeyIdentifier=keyid,issuer - -# This stuff is for subjectAltName and issuerAltname. -# Import the email address. -# subjectAltName=email:copy -# An alternative to produce certificates that aren't -# deprecated according to PKIX. -# subjectAltName=email:move - -# Copy subject details -# issuerAltName=issuer:copy - -#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem -#nsBaseUrl -#nsRevocationUrl -#nsRenewalUrl -#nsCaPolicyUrl -#nsSslServerName - -# This really needs to be in place for it to be a proxy certificate. -proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo - diff --git a/tests/run_tests b/tests/run_tests index 9499bfe..62079fb 100755 --- a/tests/run_tests +++ b/tests/run_tests @@ -42,10 +42,6 @@ tracemalloc.start() BINS = os.path.abspath( os.path.dirname(os.path.realpath(__file__)) + "/../out") -# Path to the test OpenSSL configuration. -OPENSSL_CONF = os.path.abspath( - os.path.dirname(os.path.realpath(__file__)) + "/openssl.cnf") - TEMPDIR = "/does/not/exist" # User the script is running as. Just informational, for troubleshooting @@ -58,6 +54,8 @@ def setUpModule(): # pylint: disable=invalid-name raise RuntimeError("kxd not found at " + BINS + "/kxd") if not os.path.isfile(BINS + "/kxc"): raise RuntimeError("kxc not found at " + BINS + "/kxc") + if not os.path.isfile(BINS + "/kxgencert"): + raise RuntimeError("kxgencert not found at " + BINS + "/kxgencert") global TEMPDIR # pylint: disable=global-statement TEMPDIR = tempfile.mkdtemp(prefix="kxdtest-") @@ -86,29 +84,15 @@ class Config: self.path = tempfile.mkdtemp(prefix="config-%s-" % name, dir=TEMPDIR) self.name = name - def gen_certs(self, self_sign=True): + def gen_cert(self): try: - cmd = ["openssl", "genrsa", - "-out", "%s/key.pem" % self.path, "2048"] + cmd = [BINS + "/kxgencert", + "-organization=kxd-tests-%s" % self.name, + "-key=" + self.key_path(), + "-cert=" + self.cert_path()] subprocess.check_output(cmd, stderr=subprocess.STDOUT) except subprocess.CalledProcessError as err: - print("openssl call failed, output: %r" % err.output) - raise - - ouname = "kxd-tests-%s" % self.name - req_args = ["openssl", "req", "-new", "-batch", - "-subj", ("/commonName=*" + - "/organizationalUnitName=%s" % ouname), - "-key", "%s/key.pem" % self.path] - if self_sign: - req_args.extend(["-x509", "-out", "%s/cert.pem" % self.path]) - else: - req_args.extend(["-out", "%s/cert.csr" % self.path]) - - try: - subprocess.check_output(req_args, stderr=subprocess.STDOUT) - except subprocess.CalledProcessError as err: - print("openssl call failed, output: %r" % err.output) + print("kxgencert call failed, output: %r" % err.output) raise def cert_path(self): @@ -117,61 +101,15 @@ class Config: def key_path(self): return self.path + "/key.pem" - def csr_path(self): - return self.path + "/cert.csr" - def cert(self): return read_all(self.path + "/cert.pem") -class CA: - def __init__(self): - self.path = tempfile.mkdtemp(prefix="config-ca-", dir=TEMPDIR) - os.makedirs(self.path + "/kxd-ca/newcerts/") - - try: - # We need to run the CA commands from within the path. - with pushd(self.path): - open("kxd-ca/index.txt", "w").close() - with open("kxd-ca/serial", "w") as serial: - serial.write("1000\n") - subprocess.check_output( - ["openssl", "req", "-new", "-x509", "-batch", - "-config", OPENSSL_CONF, - "-subj", ("/commonName=*" + - "/organizationalUnitName=kxd-tests-ca"), - "-extensions", "v3_ca", "-nodes", - "-keyout", "cakey.pem", - "-out", "cacert.pem"], - stderr=subprocess.STDOUT) - except subprocess.CalledProcessError as err: - print("openssl call failed, output: %r" % err.output) - raise - - def sign(self, csr): - try: - with pushd(self.path): - subprocess.check_output( - ["openssl", "ca", "-batch", "-config", OPENSSL_CONF, - "-keyfile", "cakey.pem", "-cert", "cacert.pem", - "-in", csr, "-out", "%s.pem" % os.path.splitext(csr)[0]], - stderr=subprocess.STDOUT) - except subprocess.CalledProcessError as err: - print("openssl call failed, output: %r" % err.output) - raise - - def cert_path(self): - return self.path + "/cacert.pem" - - def cert(self): - return read_all(self.path + "/cacert.pem") - - class ServerConfig(Config): - def __init__(self, self_sign=True, name="server"): + def __init__(self, name="server"): Config.__init__(self, name) self.keys = {} - self.gen_certs(self_sign) + self.gen_cert() def new_key(self, name, allowed_clients=None, allowed_hosts=None): self.keys[name] = os.urandom(1024) @@ -193,9 +131,9 @@ class ServerConfig(Config): class ClientConfig(Config): - def __init__(self, self_sign=True, name="client"): + def __init__(self, name="client"): Config.__init__(self, name) - self.gen_certs(self_sign) + self.gen_cert() def call(self, server_cert, url): args = [BINS + "/kxc", @@ -469,90 +407,6 @@ class BrokenServerConfig(TestCase): self.assertClientFails("kxd://localhost/k1", "404 Not Found") -class Delegation(TestCase): - """Tests for CA delegations.""" - def setUp(self): - # For these tests, we don't have a common setup, as each will create - # server and clients in a slightly different way. - self.daemon = None - - def prepare(self, server_self_sign=True, client_self_sign=True, - ca_sign_server=None, ca_sign_client=None): - self.server = ServerConfig(self_sign=server_self_sign) - self.client = ClientConfig(self_sign=client_self_sign) - - self.ca = CA() - if ca_sign_server is None: - ca_sign_server = not server_self_sign - if ca_sign_client is None: - ca_sign_client = not client_self_sign - - if ca_sign_server: - self.ca.sign(self.server.csr_path()) - if ca_sign_client: - self.ca.sign(self.client.csr_path()) - - self.launch_server(self.server) - - def test_server_delegated(self): - self.prepare(server_self_sign=False) - - self.server.new_key("k1", - allowed_clients=[self.client.cert()], - allowed_hosts=["localhost"]) - - # Successful request. - key = self.client.call(self.ca.cert_path(), "kxd://localhost/k1") - self.assertEqual(key, self.server.keys["k1"]) - - # The server is signed by the CA, but the CA is unknown to the client. - # But the client knows the server directly, so it's allowed. - key = self.client.call(self.server.cert_path(), "kxd://localhost/k1") - self.assertEqual(key, self.server.keys["k1"]) - - # Same as above, but give the wrong CA. - ca2 = CA() - self.assertClientFails("kxd://localhost/k1", - "certificate signed by unknown authority", - cert_path=ca2.cert_path()) - - def test_client_delegated(self): - self.prepare(client_self_sign=False) - - # Successful request. - self.server.new_key("k1", - allowed_clients=[self.ca.cert()], - allowed_hosts=["localhost"]) - key = self.client.call(self.server.cert_path(), "kxd://localhost/k1") - self.assertEqual(key, self.server.keys["k1"]) - - # The CA signing the client is unknown to the server. - ca2 = CA() - self.server.new_key("k2", - allowed_clients=[ca2.cert()], - allowed_hosts=["localhost"]) - self.assertClientFails("kxd://localhost/k2", - "403 Forbidden.*No allowed certificate found", - cert_path=self.server.cert_path()) - - # The client is signed by the CA, but the CA is unknown to the server. - # But the server it knows the client directly, so it's allowed. - self.server.new_key("k3", - allowed_clients=[self.client.cert()], - allowed_hosts=["localhost"]) - key = self.client.call(self.server.cert_path(), "kxd://localhost/k3") - self.assertEqual(key, self.server.keys["k3"]) - - def test_both_delegated(self): - self.prepare(server_self_sign=False, client_self_sign=False) - self.server.new_key("k1", - allowed_clients=[self.ca.cert()], - allowed_hosts=["localhost"]) - - key = self.client.call(self.ca.cert_path(), "kxd://localhost/k1") - self.assertEqual(key, self.server.keys["k1"]) - - class Hook(TestCase): """Test cases for hook support.""" @@ -582,7 +436,7 @@ class Hook(TestCase): self.assertEqual(key, self.server.keys["k1"]) hook_out = read_all(self.server.path + "/data/hook-output") - self.assertIn("CLIENT_CERT_SUBJECT=OU=kxd-tests-client", hook_out) + self.assertIn("CLIENT_CERT_SUBJECT=O=kxd-tests-client", hook_out) # Failure caused by the hook exiting with error. self.write_hook(exit_code=1)