// 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 }
// 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 }
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 }
// 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 }
// 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 }
// 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) } }
// 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) }