Skip to content

Instantly share code, notes, and snippets.

@jefferai
Last active May 20, 2022 13:29
Show Gist options
  • Star 12 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save jefferai/e2bebc3bb97fed521666 to your computer and use it in GitHub Desktop.
Save jefferai/e2bebc3bb97fed521666 to your computer and use it in GitHub Desktop.
Example of Vault PKI (X509) backend issuing certificates to client and server, which then perform TLS mutual auth
package main
import (
"crypto/tls"
"fmt"
"html"
"io/ioutil"
"log"
"net"
"net/http"
"time"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/helper/certutil"
)
const (
pkiIssueToken string = "[insert token]"
vaultAddr string = "http://localhost:8200"
roleName string = "pki/issue/test"
)
func getTLSConfig() (*tls.Config, error) {
client, err := api.NewClient(&api.Config{
Address: vaultAddr,
})
if err != nil {
return nil, err
}
if client == nil {
return nil, fmt.Errorf("Returned client was nil")
}
client.SetToken(pkiIssueToken)
secret, err := client.Logical().Write(roleName, map[string]interface{}{
"common_name": "localhost",
"ip_sans": "127.0.0.1",
"lease": "1h",
})
if err != nil {
return nil, err
}
if secret == nil {
return nil, fmt.Errorf("Returned secret was nil")
}
parsedCertBundle, err := certutil.ParsePKIMap(secret.Data)
if err != nil {
return nil, fmt.Errorf("Error parsing secret: %s", err)
}
tlsConfig, err := parsedCertBundle.GetTLSConfig(certutil.TLSClient | certutil.TLSServer)
if err != nil {
return nil, fmt.Errorf("Could not get TLS config: %s", err)
}
return tlsConfig, nil
}
func runServer() {
tlsConfig, err := getTLSConfig()
if err != nil {
log.Printf("[Server] Encountered error getting tls config: %s", err)
return
}
tlsConfig.ServerName = "localhost"
tlsConfig.ClientAuth = tls.VerifyClientCertIfGiven
ln, err := net.Listen("tcp", ":9182")
if err != nil {
log.Printf("[Server] Error listening: %s", err)
return
}
tlsListener := tls.NewListener(ln.(*net.TCPListener), tlsConfig)
log.Printf("[Server] Starting...")
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
switch len(r.TLS.VerifiedChains) {
case 0:
fmt.Fprintf(w, "Hello! You accesed %q without a client certificate", html.EscapeString(r.URL.Path))
default:
fmt.Fprintf(w, "Hello! You accesed %q WITH a client certificate (good job!)", html.EscapeString(r.URL.Path))
}
})
srv := &http.Server{}
err = srv.Serve(tlsListener)
if err != nil {
log.Printf("[Server] Error serving: %s", err)
}
}
func runClient() {
tlsConfig, err := getTLSConfig()
if err != nil {
log.Printf("[Client] Encountered error getting tls certificate: %s", err)
return
}
log.Printf("[Client] Starting...")
tr := &http.Transport{TLSClientConfig: tlsConfig}
client := &http.Client{Transport: tr}
for {
resp, err := client.Get("https://localhost:9182/")
if err == nil {
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Printf("[Client] Error reading response body: %s", err)
} else {
log.Printf("[Client] Got %s", string(body))
}
resp.Body.Close()
return
}
time.Sleep(500 * time.Millisecond)
}
}
func main() {
go runClient()
runServer()
}
@jboero
Copy link

jboero commented Mar 4, 2021

This is a great sample. Thanks!

@lackhoa
Copy link

lackhoa commented Feb 16, 2022

The certutil package seems to have been moved to github.com/hashicorp/vault/sdk/helper/certutil

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment