This repository has been archived by the owner on Apr 20, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fixed xplat builds with windows' specific code changes.
Signed-off-by: Mike Lloyd <mike.lloyd@gsa.gov>
- Loading branch information
1 parent
b2d26b1
commit be9f275
Showing
2 changed files
with
250 additions
and
46 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
} |