aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Clerc <jclerc@google.com>2017-02-15 14:29:43 +0100
committerJeremy Clerc <jclerc@google.com>2017-02-15 14:29:43 +0100
commit51e2e81502cdab076a3c0e4958fbb58884418fc8 (patch)
tree76d94f65b7b3814d0c5e335ec7e6952adf259647
parent1d7c7f7b897ad203f8d873d6338edd7ec735f936 (diff)
downloadeasypki-51e2e81502cdab076a3c0e4958fbb58884418fc8.tar.xz
Add client authentication example.
-rw-r--r--example/client-auth/README.md59
-rw-r--r--example/client-auth/build-pki.go82
-rw-r--r--example/client-auth/get.go85
-rw-r--r--example/client-auth/nginx.conf29
-rw-r--r--example/client-auth/pki.yaml33
-rw-r--r--example/env.sh2
6 files changed, 289 insertions, 1 deletions
diff --git a/example/client-auth/README.md b/example/client-auth/README.md
new file mode 100644
index 0000000..b359ace
--- /dev/null
+++ b/example/client-auth/README.md
@@ -0,0 +1,59 @@
+# Client authentication
+
+In this example, we generate a PKI based on a yaml definition, then we
+provision a nginx server that will only allow connections from clients
+having a trusted certificate.
+
+Build the PKI from the yaml definition:
+
+```
+go run build-pki.go -config_path pki.yaml -db_path pki.boltdb
+```
+
+Fetch the certificates needed for nginx:
+
+```
+go run get.go -db_path pki.boltdb -ca_name "Admins Intermediate CA" -bundle_name "localhost"
+go run get.go -db_path pki.boltdb -bundle_name "Admins Intermediate CA"
+```
+
+Create the nginx config structure:
+
+```
+mkdir conf.d
+cp nginx.conf conf.d/
+mv localhost+chain.crt localhost.key conf.d/
+mv Admins\ Intermediate\ CA+chain.crt conf.d/trusted+chain.crt
+```
+
+To import the client certs in a browser we need a pkcs12 file, unfortunately
+golang.org/x/crypto/pkcs12 only provides decoding, so we use openssl.
+
+Fetch the client certificate and create a pkcs12 formatted file:
+
+```
+go run get.go -db_path pki.boltdb -ca_name "Admins Intermediate CA" -bundle_name bob@acme.com -full_chain=false
+cat bob@acme.com.{key,crt} | openssl pkcs12 -export -out bob@acme.com+pkcs12.crt
+```
+
+Import bob@acme.com+pkcs12.crt in your favorite browser.
+
+Fetch the root CA to import in the browser:
+
+```
+go run get.go -db_path pki.boltdb -bundle_name "CA"
+```
+
+Import CA+chain.crt in your favorite browser.
+
+Run nginx:
+
+```
+docker run --rm -v $PWD/conf.d:/etc/nginx/conf.d -p 8080:443 nginx
+```
+
+Open you browser at https://localhost:8080, and you should see "Welcome to
+nginx!".
+
+Try to remove your client certificate from your browser and you get 400 bad
+request.
diff --git a/example/client-auth/build-pki.go b/example/client-auth/build-pki.go
new file mode 100644
index 0000000..63dfefc
--- /dev/null
+++ b/example/client-auth/build-pki.go
@@ -0,0 +1,82 @@
+package main
+
+import (
+ "crypto/x509/pkix"
+ "flag"
+ "io/ioutil"
+ "log"
+
+ "crypto/x509"
+ "time"
+
+ "github.com/boltdb/bolt"
+ "github.com/go-yaml/yaml"
+ "github.com/google/easypki/pkg/certificate"
+ "github.com/google/easypki/pkg/easypki"
+ "github.com/google/easypki/pkg/store"
+)
+
+type configCerts struct {
+ Name string `yaml:"name"`
+ CommonName string `yaml:"commonName"`
+ DNSNames []string `yaml:"dnsNames"`
+ EmailAddresses []string `yaml:"emailAddresses"`
+ IsCA bool `yaml:"isCA"`
+ IsClient bool `yaml:"isClient"`
+ Signer string `yaml:"signer"`
+ Expire time.Duration `yaml:"expire"`
+}
+
+type config struct {
+ Subject pkix.Name `yaml:"subject"`
+ Certs []configCerts `yaml:"certs"`
+}
+
+func main() {
+ var (
+ configPath = flag.String("config_path", "chain.yaml", "Configuration path to generate PKI.")
+ dbPath = flag.String("db_path", "", "Bolt database path.")
+ )
+ flag.Parse()
+ b, err := ioutil.ReadFile(*configPath)
+ if err != nil {
+ log.Fatalf("Failed reading configuration file %v: %v", *configPath, err)
+ }
+ conf := &config{}
+ if err := yaml.Unmarshal(b, conf); err != nil {
+ log.Fatalf("Failed umarshaling yaml config (%v) %v: %v", *configPath, string(b), err)
+ }
+ db, err := bolt.Open(*dbPath, 0600, nil)
+ if err != nil {
+ log.Fatalf("Failed opening bolt database %v: %v", *dbPath, err)
+ }
+ defer db.Close()
+ pki := &easypki.EasyPKI{Store: &store.Bolt{DB: db}}
+ for _, cert := range conf.Certs {
+ req := &easypki.Request{
+ Name: cert.Name,
+ Template: &x509.Certificate{
+ Subject: conf.Subject,
+ NotAfter: time.Now().Add(cert.Expire),
+ IsCA: cert.IsCA,
+ DNSNames: cert.DNSNames,
+ EmailAddresses: cert.EmailAddresses,
+ },
+ IsClientCertificate: cert.IsClient,
+ }
+ if cert.IsCA {
+ req.Template.MaxPathLen = -1
+ }
+ req.Template.Subject.CommonName = cert.CommonName
+ var signer *certificate.Bundle
+ if cert.Signer != "" {
+ signer, err = pki.GetCA(cert.Signer)
+ if err != nil {
+ log.Fatalf("Cannot sign %v because cannot get CA %v: %v", cert.Name, cert.Signer, err)
+ }
+ }
+ if err := pki.Sign(signer, req); err != nil {
+ log.Fatalf("Cannot create bundle for %v: %v", cert.Name, err)
+ }
+ }
+}
diff --git a/example/client-auth/get.go b/example/client-auth/get.go
new file mode 100644
index 0000000..a252216
--- /dev/null
+++ b/example/client-auth/get.go
@@ -0,0 +1,85 @@
+package main
+
+import (
+ "encoding/pem"
+ "flag"
+ "log"
+
+ "os"
+
+ "crypto/x509"
+
+ "github.com/boltdb/bolt"
+ "github.com/google/easypki/pkg/certificate"
+ "github.com/google/easypki/pkg/easypki"
+ "github.com/google/easypki/pkg/store"
+)
+
+func main() {
+ var (
+ caName = flag.String("ca_name", "", "Name of the CA which signed the bundle.")
+ bundleName = flag.String("bundle_name", "", "Name of the bundle to retrieve.")
+ fullChain = flag.Bool("full_chain", true, "Include chain of trust in certificate output.")
+ dbPath = flag.String("db_path", "", "Bolt database path.")
+ )
+ flag.Parse()
+ if *bundleName == "" {
+ log.Fatal("bundle_name cannot be empty")
+ }
+ db, err := bolt.Open(*dbPath, 0600, nil)
+ if err != nil {
+ log.Fatalf("Failed opening bolt database %v: %v", *dbPath, err)
+ }
+ defer db.Close()
+ pki := &easypki.EasyPKI{Store: &store.Bolt{DB: db}}
+
+ var bundle *certificate.Bundle
+ if *caName == "" {
+ *caName = *bundleName
+ }
+ bundle, err = pki.GetBundle(*caName, *bundleName)
+ if err != nil {
+ log.Fatalf("Failed getting bundle %v within CA %v: %v", *bundleName, *caName, err)
+ }
+ leaf := bundle
+ chain := []*certificate.Bundle{bundle}
+ if *fullChain {
+ for {
+ if leaf.Cert.Issuer.CommonName == leaf.Cert.Subject.CommonName {
+ break
+ }
+ ca, err := pki.GetCA(leaf.Cert.Issuer.CommonName)
+ if err != nil {
+ log.Fatalf("Failed getting signing CA %v: %v", leaf.Cert.Issuer.CommonName, err)
+ }
+ chain = append(chain, ca)
+ leaf = ca
+ }
+ }
+ key, err := os.Create(*bundleName + ".key")
+ if err != nil {
+ log.Fatalf("Failed creating key output file: %v", err)
+ }
+ if err := pem.Encode(key, &pem.Block{
+ Bytes: x509.MarshalPKCS1PrivateKey(bundle.Key),
+ Type: "RSA PRIVATE KEY",
+ }); err != nil {
+ log.Fatalf("Failed ecoding private key: %v", err)
+ }
+ crtName := *bundleName + ".crt"
+ if *fullChain {
+ crtName = *bundleName + "+chain.crt"
+ }
+ cert, err := os.Create(crtName)
+ if err != nil {
+ log.Fatalf("Failed creating chain output file: %v", err)
+ }
+ for _, c := range chain {
+ if err := pem.Encode(cert, &pem.Block{
+ Bytes: c.Cert.Raw,
+ Type: "CERTIFICATE",
+ }); err != nil {
+ log.Fatalf("Failed ecoding %v certificate: %v", c.Name, err)
+ }
+ }
+}
diff --git a/example/client-auth/nginx.conf b/example/client-auth/nginx.conf
new file mode 100644
index 0000000..8a27f9b
--- /dev/null
+++ b/example/client-auth/nginx.conf
@@ -0,0 +1,29 @@
+server {
+ listen 443 ssl;
+ server_name localhost;
+ keepalive_timeout 70;
+
+ ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
+ ssl_ciphers AES128-SHA:AES256-SHA:RC4-SHA:DES-CBC3-SHA:RC4-MD5;
+ ssl_certificate /etc/nginx/conf.d/localhost+chain.crt;
+ ssl_certificate_key /etc/nginx/conf.d/localhost.key;
+ ssl_session_cache shared:SSL:10m;
+ ssl_session_timeout 10m;
+
+
+ ssl_client_certificate /etc/nginx/conf.d/trusted+chain.crt;
+ ssl_verify_depth 2;
+
+ ssl_verify_client on;
+
+ location / {
+ root /usr/share/nginx/html;
+ index index.html index.htm;
+ }
+ # redirect server error pages to the static page /50x.html
+ #
+ error_page 500 502 503 504 /50x.html;
+ location = /50x.html {
+ root /usr/share/nginx/html;
+ }
+}
diff --git a/example/client-auth/pki.yaml b/example/client-auth/pki.yaml
new file mode 100644
index 0000000..e115c04
--- /dev/null
+++ b/example/client-auth/pki.yaml
@@ -0,0 +1,33 @@
+subject:
+ organization:
+ - "Acme Inc."
+ organizationalUnit:
+ - "IT"
+ locality:
+ - "Agloe"
+ country:
+ - "US"
+ province:
+ - "New York"
+certs:
+ - name: "CA"
+ commonName: "CA"
+ isCA: true
+ expire: "720h"
+ - name: "Admins Intermediate CA"
+ commonName: "Admins Intermediate CA"
+ signer: "CA"
+ isCA: true
+ expire: "720h"
+ - name: "localhost"
+ commonName: "localhost"
+ dnsNames:
+ - "localhost"
+ signer: "Admins Intermediate CA"
+ expire: "720h"
+ - name: "bob@acme.com"
+ commonName: "bob@acme.com"
+ emailAddresses:
+ - "bob@acme.com"
+ signer: "Admins Intermediate CA"
+ expire: "720h" \ No newline at end of file
diff --git a/example/env.sh b/example/env.sh
index ac28064..144fa76 100644
--- a/example/env.sh
+++ b/example/env.sh
@@ -1,5 +1,5 @@
export PKI_ROOT="/tmp/pki"
-export PKI_ORGANIZATION="Umbrella Corp"
+export PKI_ORGANIZATION="Acme Inc."
export PKI_ORGANIZATIONAL_UNIT="IT"
export PKI_COUNTRY="US"
export PKI_LOCALITY="Agloe"