diff --git a/README.md b/README.md index a788596..e2d9fcd 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ Created out/Alice.csr certstrap requires either `-common-name` or `-domain` flag to be set in order to generate a certificate signing request. The CN for the certificate will be found from these fields. -If your server has mutiple ip addresses or domains, use comma seperated ip/domain/uri list. eg: `./certstrap request-cert -ip $ip1,$ip2 -domain $domain1,$domain2 -uri $uri1,$uri2` +If your server has mutiple ip addresses, domains or organizational units, use comma seperated ip/domain/uri/ou list. eg: `./certstrap request-cert -ip $ip1,$ip2 -domain $domain1,$domain2 -uri $uri1,$uri2 -ou $org1,$org2` If you do not wish to generate a new keypair, you can use a pre-existing private PEM key with the `-key` flag diff --git a/cmd/init.go b/cmd/init.go index 3c94af1..f407307 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -59,7 +59,7 @@ func NewInitCommand() cli.Command { }, cli.StringFlag{ Name: "organizational-unit, ou", - Usage: "Sets the Organizational Unit (OU) field of the certificate", + Usage: "Sets the Organizational Unit (OU) values of the certificate", }, cli.StringFlag{ Name: "country, c", diff --git a/cmd/request_cert.go b/cmd/request_cert.go index f6e8b93..d0cc586 100644 --- a/cmd/request_cert.go +++ b/cmd/request_cert.go @@ -63,7 +63,7 @@ func NewCertRequestCommand() cli.Command { }, cli.StringFlag{ Name: "organizational-unit, ou", - Usage: "Sets the Organizational Unit (OU) field of the certificate", + Usage: "Sets the Organizational Unit (OU) values of the certificate", }, cli.StringFlag{ Name: "province, st", @@ -182,7 +182,13 @@ func newCertAction(c *cli.Context) { } } - csr, err := pkix.CreateCertificateSigningRequest(key, c.String("organizational-unit"), ips, domains, uris, c.String("organization"), c.String("country"), c.String("province"), c.String("locality"), name) + // split multiple organizational-unit (if any) + organizationalUnits := strings.Split(c.String("organizational-unit"), ",") + if c.String("organizational-unit") == "" { + organizationalUnits = nil + } + + csr, err := pkix.CreateCertificateSigningRequest(key, organizationalUnits, ips, domains, uris, c.String("organization"), c.String("country"), c.String("province"), c.String("locality"), name) if err != nil { fmt.Fprintln(os.Stderr, "Create certificate request error:", err) os.Exit(1) diff --git a/pkix/csr.go b/pkix/csr.go index 861ef7f..6634fa9 100644 --- a/pkix/csr.go +++ b/pkix/csr.go @@ -78,11 +78,11 @@ func ParseAndValidateURIs(uriList string) (res []*url.URL, err error) { } // CreateCertificateSigningRequest sets up a request to create a csr file with the given parameters -func CreateCertificateSigningRequest(key *Key, organizationalUnit string, ipList []net.IP, domainList []string, uriList []*url.URL, organization string, country string, province string, locality string, commonName string) (*CertificateSigningRequest, error) { +func CreateCertificateSigningRequest(key *Key, organizationalUnits []string, ipList []net.IP, domainList []string, uriList []*url.URL, organization string, country string, province string, locality string, commonName string) (*CertificateSigningRequest, error) { csrPkixName := pkix.Name{CommonName: commonName} - if len(organizationalUnit) > 0 { - csrPkixName.OrganizationalUnit = []string{organizationalUnit} + if len(organizationalUnits) > 0 { + csrPkixName.OrganizationalUnit = organizationalUnits } if len(organization) > 0 { csrPkixName.Organization = []string{organization} diff --git a/tests/organization_unit_test.go b/tests/organization_unit_test.go new file mode 100644 index 0000000..51ddb28 --- /dev/null +++ b/tests/organization_unit_test.go @@ -0,0 +1,56 @@ +//go:build integration +// +build integration + +/*- + * Copyright 2015 Square Inc. + * Copyright 2014 CoreOS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests + +import ( + "os" + "strings" + "testing" +) + +func TestOrganizationalUnit(t *testing.T) { + os.RemoveAll(depotDir) + defer os.RemoveAll(depotDir) + + // test with single organizational-unit + stdout, stderr, err := run(binPath, "request-cert", "--passphrase", passphrase, "--common-name", "CA", "--ou", "ORG-1") + if stderr != "" || err != nil { + t.Fatalf("Received unexpected error: %v, %v", stderr, err) + } + if strings.Count(stdout, "Created") != 2 { + t.Fatalf("Received incorrect create: %v", stdout) + } + + os.RemoveAll(depotDir) + defer os.RemoveAll(depotDir) + + // test with multiple organizational-units + stdout, stderr, err = run(binPath, "request-cert", "--passphrase", passphrase, "--common-name", "CA", "--ou", "ORG-1,ORG-2") + if stderr != "" || err != nil { + t.Fatalf("Received unexpected error: %v, %v", stderr, err) + } + if strings.Count(stdout, "Created") != 2 { + t.Fatalf("Received incorrect create: %v", stdout) + } + + os.RemoveAll(depotDir) + defer os.RemoveAll(depotDir) +}