Beispiel #1
0
// GenerateConfig returns a *tls.Config for either a client if true or server if client
// is false, the given key pair ${name}-[cert,key].pem files and accepting the caCertNames
// if given.
func GenerateConfig(client bool, keyPairName string, caCertNames []string) (config *tls.Config, err error) {
	config = &tls.Config{
		InsecureSkipVerify: _insecure,
		MinVersion:         tls.VersionTLS12,
	}

	label := "server"
	if client {
		label = "client"
	}

	var keyPair tls.Certificate
	var cFile, kFile string
	if keyPairName != "" {
		keyPair, cFile, kFile, err = LoadPackagedKeypair(keyPairName)
		if err != nil {
			return
		}

		vlog.VLogf("Loaded packaged %s keypair from %s and %s", label, cFile, kFile)
		config.Certificates = []tls.Certificate{keyPair}
	}

	if len(caCertNames) > 0 {
		caPool := x509.NewCertPool()
		if client {
			config.RootCAs = caPool
		} else {
			config.ClientCAs = caPool
			if _insecure {
				config.ClientAuth = tls.RequestClientCert
			} else {
				config.ClientAuth = tls.RequireAndVerifyClientCert
			}
		}

		for _, name := range caCertNames {
			n := name
			if strings.Index(name, "/") < 0 {
				n = name + "-cert"
			}
			var file string
			if file, err = LocatePackagedPEMFile(n); err != nil {
				return nil, fmt.Errorf("Failed to find cert named %q: %s", name, err)
			}
			var b []byte
			if b, err = ioutil.ReadFile(file); err != nil {
				return nil, fmt.Errorf("Can't read cert from %s: %s", file, err)
			}

			vlog.VLogf("Allowing %s CA cert from %s", label, file)
			if ok := caPool.AppendCertsFromPEM(b); !ok {
				return nil, fmt.Errorf("No cert could be found in %s", file)
			}
		}
	}
	return
}
Beispiel #2
0
// LoadKeyPairFromDisk returns a KeyPair from disk files based on the given
// name.
func LoadKeyPairFromDisk(name string) (*KeyPair, error) {
	// cert
	certFile := name + "-cert.pem"
	b, err := ioutil.ReadFile(certFile)
	if err != nil {
		return nil, err
	}

	block, _ := pem.Decode(b)
	if block == nil {
		return nil, fmt.Errorf("No PEM data found in %q", certFile)
	}

	cert, err := x509.ParseCertificate(block.Bytes)
	if err != nil {
		return nil, err
	}

	vlog.VLogf("Loaded cert from %s", certFile)

	// private key
	keyFile := name + "-key.pem"
	b, err = ioutil.ReadFile(keyFile)
	if err != nil {
		return nil, err
	}

	block, _ = pem.Decode(b)
	if block == nil {
		return nil, fmt.Errorf("No PEM data found in %q", keyFile)
	}

	key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		return nil, err
	}

	vlog.VLogf("Loaded key from %s", keyFile)

	return &KeyPair{cert, key}, nil
}
Beispiel #3
0
func NewGmetric() (*gmetric.Gmetric, error) {
	b, err := ioutil.ReadFile(GmondConfig)
	if err != nil {
		return nil, err
	}
	stanzas := gmondChannelRe.FindAllStringSubmatch(string(b), -1)
	if len(stanzas) == 0 {
		return nil, fmt.Errorf("No udp_send_channel stanzas found in %s", GmondConfig)
	}

	servers := make([]gmetric.Server, 0)
	for _, stanza := range stanzas {
		var host, port string
		for _, match := range gmondHostPortRe.FindAllStringSubmatch(stanza[1], 2) {
			if match[1] == "host" {
				host = match[2]
			} else if match[1] == "port" {
				port = match[2]
			}
		}
		if host == "" || port == "" {
			return nil, fmt.Errorf("Missing host or port from %s stanza %q", GmondConfig, stanza[0])
		}
		portNum, err := strconv.Atoi(port)
		if err != nil {
			return nil, err
		}
		ips, err := net.LookupIP(host)
		if err != nil {
			return nil, err
		}
		for _, ip := range ips {
			vlog.VLogf("Reporting to Ganglia server at %s:%d", ip, portNum)
			servers = append(servers, gmetric.Server{ip, portNum})
		}
	}

	// see http://sourceforge.net/apps/trac/ganglia/wiki/gmetric_spoofing
	hostname, _ := os.Hostname()
	spoofName := fmt.Sprintf("%s:%s", hostname, hostname)

	gm := gmetric.Gmetric{Spoof: spoofName}
	for _, server := range servers {
		gm.AddServer(server)
	}
	return &gm, nil
}
Beispiel #4
0
// NowRunning returns true if there is a running process whose
// binary has the same name as this one.
func NowRunning() bool {
	binary, err := Path()
	if err != nil {
		log.Fatalf("Couldn't find own process: %s", err)
		return false
	}
	proc, _, err := FindDuplicateProcess(binary)
	if err != nil {
		vlog.VLogf("Couldn't look for running processes: %s", err)
		return false
	}
	if proc == nil {
		return false
	}
	if proc.Signal(syscall.Signal(0x0)) == nil {
		return true
	}
	return false
}
Beispiel #5
0
// New creates a new Lifecycle. This should be called after validating
// parameters but before starting work or allocating external resources. A
// startup message is displayed and shutdown handlers for SIGINT and SIGTERM
// are registered.
//
// If New is passed 'true' for singleProcess, it will wait for existing duplicate
// processes to exit before returning.
func New(singleProcess bool) *Lifecycle {
	l := Lifecycle{
		interrupt: make(chan os.Signal, 1),
		fatalQuit: make(chan struct{}, 1),
	}

	// make sigint trigger a clean shutdown
	signal.Notify(l.interrupt, os.Interrupt)
	signal.Notify(l.interrupt, syscall.SIGTERM)
	signal.Notify(l.interrupt, syscall.SIGHUP)

	if singleProcess && executable.NowRunning() {
		vlog.VLogf("Waiting for existing %s processes to exit...", os.Args[0])
		for executable.NowRunning() {
			select {
			case <-l.interrupt:
				log.Fatalf("Aborting")
			case <-time.After(100 * time.Millisecond):
			}
		}
	}

	return &l
}
Beispiel #6
0
// RunWhenKilled blocks until a shutdown signal is received, then executes
// finalizer and only returns either after it has finished or another
// shutdown signal is received. If timeout is non-zero, RunWhenKilled will
// force shutdown if the finalizer cannot complete within the timeout duration.
//
// RunWhenKilled should only be called once with a master function to run
// on program shutdown.
//
// RunWhenKilled runs the finalizer before any deferred AddKillFunc functions.
// This is so that the finalizer can begin the shutdown process that any
// other AddKillFunc functions can rely on.
func (l *Lifecycle) RunWhenKilled(finalizer func(), timeout time.Duration) {
	vlog.VLogf("%s started", os.Args[0])
	select {
	case sig := <-l.interrupt:
		vlog.VLogf("Caught signal %q, shutting down", sig)
	case <-l.fatalQuit:
		vlog.VLogf("Caught fatal quit, shutting down")
	}

	// wait for either confirmation that we finished or another interrupt
	shutdown := make(chan struct{}, 1)
	go func() {
		if finalizer != nil {
			finalizer()
		}
		for i := len(l.killFuncs) - 1; i >= 0; i-- {
			l.killFuncs[i]()
		}
		close(shutdown)
	}()
	var t <-chan time.Time
	if timeout > 0 {
		t = time.After(timeout)
	}
	select {
	case <-shutdown:
		vlog.VLogf("Shutdown complete, goodbye")
		os.Exit(0)
	case <-t:
		vlog.VLogf("Shutdown timeout exceeded (%v)", timeout)
		os.Exit(1)
	case <-l.interrupt:
		vlog.VLogf("Second interrupt, exiting")
		os.Exit(1)
	}
}
Beispiel #7
0
// GenerateKeyPair generates or reloads an RSA keypair with key usages
// determined by `purpose`. The disk files that are generated or reused are
// named `name`-key.pem and `name`-cert.pem for the private and public halves,
// respectively. `commonName` and `hosts` are the corresponding fields in the
// certificate.
//
// cc.Serial is incremented for each key that is freshly generated.
func (cc *CertCreator) GenerateKeyPair(purpose Purpose, parent *KeyPair, name string, commonName string, hosts ...string) (*KeyPair, error) {
	if pair, err := LoadKeyPairFromDisk(name); err == nil {
		return pair, nil
	}

	keyFile, certFile := name+"-key.pem", name+"-cert.pem"
	if _, err := os.Stat(certFile); !os.IsNotExist(err) {
		return nil, fmt.Errorf("Cert file %q already exists", certFile)
	}

	var extUsages []x509.ExtKeyUsage
	if purpose == CA {
		extUsages = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}
	} else if purpose == SERVER {
		extUsages = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}
	} else {
		extUsages = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}
	}

	serial := new(big.Int).SetInt64(cc.Serial)
	cc.Serial++
	template := x509.Certificate{
		Subject: pkix.Name{
			Country:      []string{cc.Country},
			Province:     []string{cc.State},
			Locality:     []string{cc.City},
			Organization: []string{cc.Organization},
			CommonName:   commonName,
			SerialNumber: serial.String(),
		},

		NotBefore:    cc.NotBefore,
		NotAfter:     cc.NotAfter,
		SerialNumber: serial,

		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		ExtKeyUsage:           extUsages,
		BasicConstraintsValid: true, // enforces IsCA and KeyUsage
	}
	if purpose == CA {
		template.IsCA = true
		template.KeyUsage = x509.KeyUsageCertSign
	} else {
		template.MaxPathLen = 1
	}

	// generate IP and DNS SANs
	for _, h := range hosts {
		if ip := net.ParseIP(h); ip != nil {
			template.IPAddresses = append(template.IPAddresses, ip)
		} else {
			template.DNSNames = append(template.DNSNames, h)
		}
	}

	var privKey *rsa.PrivateKey
	var reusedKey bool
	if keyBytes, err := ioutil.ReadFile(keyFile); err == nil {
		// reuse existing key
		keyDERBlock, _ := pem.Decode(keyBytes)
		if keyDERBlock == nil {
			return nil, errors.New("failed to parse key PEM data")
		}
		if keyDERBlock.Type != "RSA PRIVATE KEY" {
			return nil, fmt.Errorf("key is not a RSA PRIVATE KEY: %s", keyDERBlock.Type)
		}
		privKey, err = x509.ParsePKCS1PrivateKey(keyDERBlock.Bytes)
		if err != nil {
			return nil, err
		}
		reusedKey = true
		vlog.VLogf("Reusing key %q\n", keyFile)
	} else if os.IsNotExist(err) {
		// generate a new key
		privKey, err = rsa.GenerateKey(rand.Reader, cc.KeySize)
		if err != nil {
			return nil, fmt.Errorf("Failed to generate private key: %s", err)
		}
	} else {
		return nil, err
	}

	origParent := parent
	if parent == nil {
		// CA signs itself
		parent = &KeyPair{&template, privKey}
	}

	// sign the key
	derBytes, err := x509.CreateCertificate(rand.Reader, &template, parent.Cert, &privKey.PublicKey, parent.PrivKey)
	if err != nil {
		return nil, err
	}

	// check that the cert can be parsed
	cert, err := x509.ParseCertificate(derBytes)
	if err != nil {
		return nil, fmt.Errorf("Cert doesn't verify against its CA: %s", err)
	}

	// check that the cert verifies against its own CA
	roots := x509.NewCertPool()
	if origParent == nil {
		roots.AddCert(cert)
	} else {
		roots.AddCert(parent.Cert)
	}
	_, err = cert.Verify(x509.VerifyOptions{Roots: roots, KeyUsages: extUsages})
	if err != nil {
		return nil, fmt.Errorf("Couldn't verify %q against its own CA: %s", name, err)
	}

	// write key and cert to disk
	if !reusedKey {
		keyOut, err := os.OpenFile(keyFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
		if err != nil {
			return nil, fmt.Errorf("Failed to open %q for writing: %s", keyFile, err)
		}
		pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(privKey)})
		keyOut.Close()
		vlog.VLogf("Wrote key %q\n", keyFile)
	}

	certOut, err := os.Create(certFile)
	if err != nil {
		return nil, fmt.Errorf("Failed to open %q for writing: %s", certFile, err)
	}
	pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
	certOut.Close()
	vlog.VLogf("Wrote cert %q\n", certFile)

	return LoadKeyPairFromDisk(name)
}