Skip to content
This repository has been archived by the owner on Apr 20, 2021. It is now read-only.

Commit

Permalink
fixed xplat builds with windows' specific code changes.
Browse files Browse the repository at this point in the history
Signed-off-by: Mike Lloyd <mike.lloyd@gsa.gov>
  • Loading branch information
siennathesane committed Nov 8, 2019
1 parent b2d26b1 commit be9f275
Show file tree
Hide file tree
Showing 2 changed files with 250 additions and 46 deletions.
53 changes: 7 additions & 46 deletions gravel.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,8 @@ import (
"fmt"
"net"
"net/http"
"runtime"
"syscall"
"testing"
"time"
"unsafe"

"github.com/18f/gravel/ca"
"github.com/18f/gravel/db"
Expand Down Expand Up @@ -150,53 +147,17 @@ func New(opts *GravelOpts) (*Gravel, error) {
return &Gravel{}, err
}

// we have to catch if this is on windows due to https://github.com/golang/go/issues/16736
var certPool *x509.CertPool
switch runtime.GOOS {
case "windows":
certPool = x509.NewCertPool()
default:
certPool, err = x509.SystemCertPool()
}
// generate our http client transport using the root certificates from the gravel certificate authority.
certPool, err := x509.SystemCertPool()
if err != nil {
return &Gravel{}, err
}

var certs []*x509.Certificate
var cert *syscall.CertContext
if runtime.GOOS == "windows" {
storeHandle, err := syscall.CertOpenSystemStore(0, syscall.StringToUTF16Ptr("Root"))
if err != nil {
fmt.Println(syscall.GetLastError())
}
for {
cert, err = syscall.CertEnumCertificatesInStore(storeHandle, cert)
if err != nil {
if errno, ok := err.(syscall.Errno); ok {
// CRYPT_E_NOT_FOUND
if errno == 0x80092004 {
break
}
}
fmt.Println(syscall.GetLastError())
}
if cert == nil {
break
}
// Copy the buf, since ParseCertificate does not create its own copy.
buf := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:]
buf2 := make([]byte, cert.Length)
copy(buf2, buf)
if c, err := x509.ParseCertificate(buf2); err == nil {
certs = append(certs, c)
}
}
if ok := certPool.AppendCertsFromPEM(rootPublicKey); !ok {
g.t.Log("no gravel certs appended, only using system certificates")
}

if len(certs) > 0 {
for cert := range certs {
certPool.AddCert(certs[cert])
}
g.clientTlsConfig = &tls.Config{
InsecureSkipVerify: true,
RootCAs: certPool,
}

if ok := certPool.AppendCertsFromPEM(rootPublicKey); !ok {
Expand Down
243 changes: 243 additions & 0 deletions gravel_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
package gravel

import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/pem"
"fmt"
"net"
"net/http"
"syscall"
"testing"
"time"
"unsafe"

"github.com/18f/gravel/ca"
"github.com/18f/gravel/db"
"github.com/18f/gravel/dns"
"github.com/18f/gravel/va"
"github.com/18f/gravel/wfe"
"github.com/18f/gravel/wrappers"
"github.com/sirupsen/logrus"
)

// Integration environment for Let's Encrypt.
type Gravel struct {
// The internal DNS server used for integration and record serving.
DnsServer *dns.IntegrationServer

// Internal certificate database.
Database db.GravelStore

// Verification Authority implementation.
VerificationAuthority *va.VerificationAuthority

// Certificate Authority for a given Gravel instance.
CertificateAuthority *ca.CertificateAuthority

// Web Front End for a given Gravel instance.
WebFrontEnd *wfe.WebFrontEnd

// Internal certificate manager for clients, etc.
CertificateManager ca.GravelCertificateChain

// HTTP integration web server for Let's Encrypt.
CertificateServer http.Server

// Logger
Logger *logrus.Logger

TestRecords map[string]string
Client *http.Client

// testing reference.
t *wrappers.TestWrapper

// internal TLS configuration for the HTTP server.
serverTlsConfig *tls.Config

// internal TLS configuration for the HTTP client.
clientTlsConfig *tls.Config

Opts *GravelOpts
}

// Options used for configuring the integration environment.
type GravelOpts struct {
// Options used for the internal datastore.
DatabaseOpts *db.DatabaseOpts

// Options used for the integration DNS server
DnsOpts *dns.IntegrationServerOpts

// Options used for the Verification Authority.
VAOpts *va.VerificationAuthorityOpts

// Options used for configuring the Certificate Authority.
CAOpts *ca.CertificateAuthorityOpts

// Options used for configuring the Web Front End.
WfeOpts *wfe.WebFrontEndOpts

// Integrate the DNS instance with a testing suite.
EnableTestIntegration *testing.T

// Set to true if you want records automatically generated by Gravel to be added to the DNS server for automatic
// verification.
AutoUpdateAuthZRecords bool

// Address on which Gravel should listen.
ListenAddress string

// Logger to use.
Logger *logrus.Logger
}

// Generate a new set of default options for an integration environment.
func NewDefaultGravelOpts() *GravelOpts {
gopts := &GravelOpts{}
gopts.DnsOpts = dns.NewDefaultIntegrationServerOpts()
gopts.DatabaseOpts = db.NewDefaultDatabaseOpts()
gopts.VAOpts = va.NewDefaultVerificationAuthorityOpts()
gopts.CAOpts = ca.NewDefaultCertificateAuthorityOpts()
gopts.WfeOpts = wfe.NewDefaultWebFrontEndOpts()

gopts.Logger = logrus.New()
gopts.ListenAddress = "localhost:5000"

return gopts
}

// Generate a new Gravel integration harness.
func New(opts *GravelOpts) (*Gravel, error) {
g := &Gravel{
Opts: opts,
Logger: opts.Logger,
}

// map the test suite to the harness.
if opts.EnableTestIntegration != nil {
g.t = &wrappers.TestWrapper{T: opts.EnableTestIntegration}
}

// boot the integration dns server.
g.DnsServer = dns.NewIntegrationServer(opts.DnsOpts)

// build gravel.
g.Database = db.NewMemoryStore(opts.DatabaseOpts)
g.VerificationAuthority = va.New(opts.VAOpts)
g.CertificateAuthority = ca.New(g.Database, opts.CAOpts)
g.WebFrontEnd = wfe.New(g.Database, g.VerificationAuthority, g.CertificateAuthority, opts.WfeOpts)

// setup the tls configuration for the custom http transport we're building.
g.serverTlsConfig = &tls.Config{}
g.serverTlsConfig.Certificates = make([]tls.Certificate, 1)

// get the public key, private key, and encode the private key so we can create the transport.
rootPublicKey := g.CertificateAuthority.GetRootCert(0).PEM()
privateKeyBlock := &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(g.CertificateAuthority.GetRootKey(0)),
}

rootPrivateKey := pem.EncodeToMemory(privateKeyBlock)

var err error
g.serverTlsConfig.Certificates[0], err = tls.X509KeyPair(rootPublicKey, rootPrivateKey)
if err != nil {
return &Gravel{}, err
}

// we have to catch if this is on windows due to https://github.com/golang/go/issues/16736
certPool := x509.NewCertPool()

var certs []*x509.Certificate
var cert *syscall.CertContext
storeHandle, err := syscall.CertOpenSystemStore(0, syscall.StringToUTF16Ptr("Root"))
if err != nil {
fmt.Println(syscall.GetLastError())
}
for {
cert, err = syscall.CertEnumCertificatesInStore(storeHandle, cert)
if err != nil {
if errno, ok := err.(syscall.Errno); ok {
// CRYPT_E_NOT_FOUND
if errno == 0x80092004 {
break
}
}
fmt.Println(syscall.GetLastError())
}
if cert == nil {
break
}
// Copy the buf, since ParseCertificate does not create its own copy.
buf := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:]
buf2 := make([]byte, cert.Length)
copy(buf2, buf)
if c, err := x509.ParseCertificate(buf2); err == nil {
certs = append(certs, c)
}
}

if len(certs) > 0 {
for cert := range certs {
certPool.AddCert(certs[cert])
}
}

if ok := certPool.AppendCertsFromPEM(rootPublicKey); !ok {
g.t.Log("no gravel certs appended, only using system certificates")
}
g.clientTlsConfig = &tls.Config{
InsecureSkipVerify: true,
RootCAs: certPool,
}

// build our client transport.
g.Client = &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
Resolver: &net.Resolver{
PreferGo: true,
Dial: func(ctx context.Context, network, address string) (conn net.Conn, e error) {
d := net.Dialer{}
return d.DialContext(ctx, "udp", fmt.Sprintf("localhost:%d", g.Opts.DnsOpts.DnsPort))
},
},
}).DialContext,
TLSHandshakeTimeout: 15 * time.Second,
ResponseHeaderTimeout: 15 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
TLSClientConfig: g.clientTlsConfig,
},
}

return g, nil
}

// Start the integration harness web server.
func (g *Gravel) StartWebServer() {
g.CertificateServer = http.Server{
Addr: g.Opts.ListenAddress,
TLSConfig: g.serverTlsConfig,
Handler: g.WebFrontEnd.Handler(),
}

err := g.CertificateServer.ListenAndServeTLS("", "")
if err != nil {
if err.Error() != "http: Server closed" {
// todo (mxplusb): implement better error handling.
panic(err)
}
}
}

func (g *Gravel) StartDnsServer() {
// boot the integration DNS server instance.
go g.DnsServer.Start()
}

0 comments on commit be9f275

Please sign in to comment.