369 lines
11 KiB
Go
369 lines
11 KiB
Go
// Copyright 2014 ISRG. All rights reserved
|
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
|
|
"github.com/codegangsta/cli"
|
|
_ "github.com/mattn/go-sqlite3"
|
|
"github.com/streadway/amqp"
|
|
|
|
"github.com/letsencrypt/boulder/ca"
|
|
"github.com/letsencrypt/boulder/ra"
|
|
"github.com/letsencrypt/boulder/rpc"
|
|
"github.com/letsencrypt/boulder/sa"
|
|
"github.com/letsencrypt/boulder/va"
|
|
"github.com/letsencrypt/boulder/wfe"
|
|
)
|
|
|
|
// Exit and print error message if we encountered a problem
|
|
func failOnError(err error, msg string) {
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "%s: %s\n", msg, err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
// This is the same as amqpConnect in boulder, but with even
|
|
// more aggressive error dropping
|
|
func amqpChannel(url string) (ch *amqp.Channel) {
|
|
conn, err := amqp.Dial(url)
|
|
failOnError(err, "Unable to connect to AMQP server")
|
|
|
|
ch, err = conn.Channel()
|
|
failOnError(err, "Unable to establish channel to AMQP server")
|
|
return
|
|
}
|
|
|
|
// Start the server and wait around
|
|
func runForever(server *rpc.AmqpRpcServer) {
|
|
forever := make(chan bool)
|
|
server.Start()
|
|
fmt.Fprintf(os.Stderr, "Server running...\n")
|
|
<-forever
|
|
}
|
|
|
|
func main() {
|
|
app := cli.NewApp()
|
|
app.Name = "boulder-start"
|
|
app.Usage = "Command-line utility to start Boulder's servers in stand-alone mode"
|
|
app.Version = "0.0.0"
|
|
|
|
app.Flags = []cli.Flag{
|
|
cli.StringFlag{
|
|
Name: "amqp",
|
|
Value: "amqp://guest:guest@localhost:5672",
|
|
EnvVar: "AMQP_SERVER",
|
|
Usage: "AMQP Broker URI",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "cfssl",
|
|
Value: "localhost:8888",
|
|
EnvVar: "CFSSL_SERVER",
|
|
Usage: "CFSSL Server URI",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "cfsslAuthKey",
|
|
EnvVar: "CFSSL_AUTH_KEY",
|
|
Usage: "CFSSL authentication key",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "cfsslProfile",
|
|
EnvVar: "CFSSL_PROFILE",
|
|
Usage: "CFSSL signing profile",
|
|
},
|
|
}
|
|
|
|
// One command per element of the system
|
|
// * WebFrontEnd
|
|
// * RegistrationAuthority
|
|
// * ValidationAuthority
|
|
// * CertificateAuthority
|
|
// * StorageAuthority
|
|
//
|
|
// Once started, we just run until killed
|
|
//
|
|
// AMQP queue names are hard-coded for now
|
|
app.Commands = []cli.Command{
|
|
{
|
|
Name: "monolithic",
|
|
Usage: "Start the CA in monolithic mode, without using AMQP",
|
|
Action: func(c *cli.Context) {
|
|
|
|
// Grab parameters
|
|
cfsslServer := c.GlobalString("cfssl")
|
|
authKey := c.GlobalString("cfsslAuthKey")
|
|
profile := c.GlobalString("cfsslProfile")
|
|
|
|
// Create the components
|
|
wfe := wfe.NewWebFrontEndImpl()
|
|
sa, err := sa.NewSQLStorageAuthority("sqlite3", ":memory:")
|
|
failOnError(err, "Unable to create SA")
|
|
err = sa.InitTables()
|
|
failOnError(err, "Unable to initialize SA")
|
|
ra := ra.NewRegistrationAuthorityImpl()
|
|
va := va.NewValidationAuthorityImpl()
|
|
ca, err := ca.NewCertificateAuthorityImpl(cfsslServer, authKey, profile)
|
|
failOnError(err, "Unable to create CA")
|
|
|
|
// Wire them up
|
|
wfe.RA = &ra
|
|
wfe.SA = sa
|
|
ra.CA = ca
|
|
ra.SA = sa
|
|
ra.VA = &va
|
|
va.RA = &ra
|
|
ca.SA = sa
|
|
|
|
// Go!
|
|
authority := "0.0.0.0:4000"
|
|
urlBase := "http://" + authority
|
|
newRegPath := "/acme/new-reg"
|
|
regPath := "/acme/reg/"
|
|
newAuthzPath := "/acme/new-authz"
|
|
authzPath := "/acme/authz/"
|
|
newCertPath := "/acme/new-cert"
|
|
certPath := "/acme/cert/"
|
|
wfe.NewReg = urlBase + newRegPath
|
|
wfe.RegBase = urlBase + regPath
|
|
wfe.NewAuthz = urlBase + newAuthzPath
|
|
wfe.AuthzBase = urlBase + authzPath
|
|
wfe.NewCert = urlBase + newCertPath
|
|
wfe.CertBase = urlBase + certPath
|
|
http.HandleFunc(newRegPath, wfe.NewRegistration)
|
|
http.HandleFunc(newAuthzPath, wfe.NewAuthorization)
|
|
http.HandleFunc(newCertPath, wfe.NewCertificate)
|
|
http.HandleFunc(regPath, wfe.Registration)
|
|
http.HandleFunc(authzPath, wfe.Authorization)
|
|
http.HandleFunc(certPath, wfe.Certificate)
|
|
|
|
// Add a simple ToS
|
|
termsPath := "/terms"
|
|
http.HandleFunc(termsPath, func(w http.ResponseWriter, r *http.Request) {
|
|
fmt.Fprintf(w, "You agree to do the right thing")
|
|
})
|
|
wfe.SubscriberAgreementURL = urlBase + termsPath
|
|
|
|
// We need to tell the RA how to make challenge URIs
|
|
// XXX: Better way to do this? Part of improved configuration
|
|
ra.AuthzBase = wfe.AuthzBase
|
|
|
|
fmt.Fprintf(os.Stderr, "Server running...\n")
|
|
err = http.ListenAndServe(authority, nil)
|
|
failOnError(err, "Error starting HTTP server")
|
|
},
|
|
},
|
|
{
|
|
Name: "monolithic-amqp",
|
|
Usage: "Start the CA in monolithic mode, using AMQP",
|
|
Action: func(c *cli.Context) {
|
|
// Grab parameters
|
|
cfsslServer := c.GlobalString("cfssl")
|
|
authKey := c.GlobalString("cfsslAuthKey")
|
|
profile := c.GlobalString("cfsslProfile")
|
|
|
|
// Create an AMQP channel
|
|
ch := amqpChannel(c.GlobalString("amqp"))
|
|
|
|
// Create AMQP-RPC clients for CA, VA, RA, SA
|
|
cac, err := rpc.NewCertificateAuthorityClient("CA.client", "CA.server", ch)
|
|
failOnError(err, "Failed to create CA client")
|
|
vac, err := rpc.NewValidationAuthorityClient("VA.client", "VA.server", ch)
|
|
failOnError(err, "Failed to create VA client")
|
|
rac, err := rpc.NewRegistrationAuthorityClient("RA.client", "RA.server", ch)
|
|
failOnError(err, "Failed to create RA client")
|
|
sac, err := rpc.NewStorageAuthorityClient("SA.client", "SA.server", ch)
|
|
failOnError(err, "Failed to create SA client")
|
|
|
|
// ... and corresponding servers
|
|
// (We need this order so that we can give the servers
|
|
// references to the clients)
|
|
cai, err := ca.NewCertificateAuthorityImpl(cfsslServer, authKey, profile)
|
|
failOnError(err, "Failed to create CA impl")
|
|
vai := va.NewValidationAuthorityImpl()
|
|
rai := ra.NewRegistrationAuthorityImpl()
|
|
sai, err := sa.NewSQLStorageAuthority("sqlite3", ":memory:")
|
|
failOnError(err, "Failed to create SA impl")
|
|
|
|
// Wire them up...
|
|
vai.RA = &rac
|
|
rai.VA = &vac
|
|
rai.CA = cac
|
|
rai.SA = sac
|
|
|
|
// ... and wrap them in RPC servers
|
|
cas, err := rpc.NewCertificateAuthorityServer("CA.server", ch, cai)
|
|
failOnError(err, "Failed to create CA server")
|
|
vas, err := rpc.NewValidationAuthorityServer("VA.server", ch, &vai)
|
|
failOnError(err, "Failed to create VA server")
|
|
ras, err := rpc.NewRegistrationAuthorityServer("RA.server", ch, &rai)
|
|
failOnError(err, "Failed to create RA server")
|
|
sas := rpc.NewStorageAuthorityServer("SA.server", ch, sai)
|
|
|
|
// Start the servers
|
|
cas.Start()
|
|
vas.Start()
|
|
ras.Start()
|
|
sas.Start()
|
|
|
|
// Wire up the front end (wrappers are already wired)
|
|
wfe := wfe.NewWebFrontEndImpl()
|
|
wfe.RA = &rac
|
|
wfe.SA = &sac
|
|
|
|
// Go!
|
|
authority := "0.0.0.0:4000"
|
|
urlBase := "http://" + authority
|
|
newRegPath := "/acme/new-reg"
|
|
regPath := "/acme/reg/"
|
|
newAuthzPath := "/acme/new-authz"
|
|
authzPath := "/acme/authz/"
|
|
newCertPath := "/acme/new-cert"
|
|
certPath := "/acme/cert/"
|
|
wfe.NewReg = urlBase + newRegPath
|
|
wfe.RegBase = urlBase + regPath
|
|
wfe.NewAuthz = urlBase + newAuthzPath
|
|
wfe.AuthzBase = urlBase + authzPath
|
|
wfe.NewCert = urlBase + newCertPath
|
|
wfe.CertBase = urlBase + certPath
|
|
http.HandleFunc(newRegPath, wfe.NewRegistration)
|
|
http.HandleFunc(newAuthzPath, wfe.NewAuthorization)
|
|
http.HandleFunc(newCertPath, wfe.NewCertificate)
|
|
http.HandleFunc(regPath, wfe.Registration)
|
|
http.HandleFunc(authzPath, wfe.Authorization)
|
|
http.HandleFunc(certPath, wfe.Certificate)
|
|
|
|
fmt.Fprintf(os.Stderr, "Server running...\n")
|
|
err = http.ListenAndServe(authority, nil)
|
|
failOnError(err, "Error starting HTTP server")
|
|
},
|
|
},
|
|
{
|
|
Name: "wfe",
|
|
Usage: "Start the WebFrontEnd",
|
|
Action: func(c *cli.Context) {
|
|
// Create necessary clients
|
|
ch := amqpChannel(c.GlobalString("amqp"))
|
|
|
|
rac, err := rpc.NewRegistrationAuthorityClient("RA.client", "RA.server", ch)
|
|
failOnError(err, "Unable to create RA client")
|
|
|
|
sac, err := rpc.NewStorageAuthorityClient("SA.client", "SA.server", ch)
|
|
failOnError(err, "Unable to create SA client")
|
|
|
|
// Create the front-end and wire in its resources
|
|
wfe := wfe.NewWebFrontEndImpl()
|
|
wfe.RA = &rac
|
|
wfe.SA = &sac
|
|
|
|
// Connect the front end to HTTP
|
|
authority := "0.0.0.0:4000"
|
|
urlBase := "http://" + authority
|
|
newRegPath := "/acme/new-reg"
|
|
regPath := "/acme/reg/"
|
|
newAuthzPath := "/acme/new-authz"
|
|
authzPath := "/acme/authz/"
|
|
newCertPath := "/acme/new-cert"
|
|
certPath := "/acme/cert/"
|
|
wfe.NewReg = urlBase + newRegPath
|
|
wfe.RegBase = urlBase + regPath
|
|
wfe.NewAuthz = urlBase + newAuthzPath
|
|
wfe.AuthzBase = urlBase + authzPath
|
|
wfe.NewCert = urlBase + newCertPath
|
|
wfe.CertBase = urlBase + certPath
|
|
http.HandleFunc(newRegPath, wfe.NewRegistration)
|
|
http.HandleFunc(newAuthzPath, wfe.NewAuthorization)
|
|
http.HandleFunc(newCertPath, wfe.NewCertificate)
|
|
http.HandleFunc(regPath, wfe.Registration)
|
|
http.HandleFunc(authzPath, wfe.Authorization)
|
|
http.HandleFunc(certPath, wfe.Certificate)
|
|
|
|
fmt.Fprintf(os.Stderr, "Server running...\n")
|
|
http.ListenAndServe(authority, nil)
|
|
},
|
|
},
|
|
{
|
|
Name: "ca",
|
|
Usage: "Start the CertificateAuthority",
|
|
Action: func(c *cli.Context) {
|
|
// Grab parameters
|
|
cfsslServer := c.GlobalString("cfssl")
|
|
authKey := c.GlobalString("cfsslAuthKey")
|
|
profile := c.GlobalString("cfsslProfile")
|
|
|
|
ch := amqpChannel(c.GlobalString("amqp"))
|
|
|
|
cai, err := ca.NewCertificateAuthorityImpl(cfsslServer, authKey, profile)
|
|
failOnError(err, "Failed to create CA impl")
|
|
cas, err := rpc.NewCertificateAuthorityServer("CA.server", ch, cai)
|
|
failOnError(err, "Unable to create CA server")
|
|
runForever(cas)
|
|
},
|
|
},
|
|
{
|
|
Name: "sa",
|
|
Usage: "Start the StorageAuthority",
|
|
Action: func(c *cli.Context) {
|
|
ch := amqpChannel(c.GlobalString("amqp"))
|
|
|
|
sai, err := sa.NewSQLStorageAuthority("sqlite3", ":memory:")
|
|
failOnError(err, "Failed to create SA impl")
|
|
sas := rpc.NewStorageAuthorityServer("SA.server", ch, sai)
|
|
runForever(sas)
|
|
},
|
|
},
|
|
{
|
|
Name: "va",
|
|
Usage: "Start the ValidationAuthority",
|
|
Action: func(c *cli.Context) {
|
|
ch := amqpChannel(c.GlobalString("amqp"))
|
|
|
|
rac, err := rpc.NewRegistrationAuthorityClient("RA.client", "RA.server", ch)
|
|
failOnError(err, "Unable to create RA client")
|
|
|
|
vai := va.NewValidationAuthorityImpl()
|
|
vai.RA = &rac
|
|
|
|
vas, err := rpc.NewValidationAuthorityServer("VA.server", ch, &vai)
|
|
failOnError(err, "Unable to create VA server")
|
|
runForever(vas)
|
|
},
|
|
},
|
|
{
|
|
Name: "ra",
|
|
Usage: "Start the RegistrationAuthority",
|
|
Action: func(c *cli.Context) {
|
|
// TODO
|
|
ch := amqpChannel(c.GlobalString("amqp"))
|
|
|
|
vac, err := rpc.NewValidationAuthorityClient("VA.client", "VA.server", ch)
|
|
failOnError(err, "Unable to create VA client")
|
|
|
|
cac, err := rpc.NewCertificateAuthorityClient("CA.client", "CA.server", ch)
|
|
failOnError(err, "Unable to create CA client")
|
|
|
|
sac, err := rpc.NewStorageAuthorityClient("SA.client", "SA.server", ch)
|
|
failOnError(err, "Unable to create SA client")
|
|
|
|
rai := ra.NewRegistrationAuthorityImpl()
|
|
rai.VA = &vac
|
|
rai.CA = &cac
|
|
rai.SA = &sac
|
|
|
|
ras, err := rpc.NewRegistrationAuthorityServer("RA.server", ch, &rai)
|
|
failOnError(err, "Unable to create RA server")
|
|
runForever(ras)
|
|
},
|
|
},
|
|
}
|
|
|
|
err := app.Run(os.Args)
|
|
failOnError(err, "Failed to run application")
|
|
}
|