func main() { var Usage = func() { fmt.Fprintf(os.Stderr, "Usage: %s [options] url mountpoint\n", os.Args[0]) flag.PrintDefaults() } flag.Parse() if flag.NArg() != 2 { Usage() os.Exit(1) } serverURL, mountpoint := flag.Args()[0], flag.Args()[1] logConfig := klog.Config{*debug, mountpoint} logger = klog.New("kwfs_main", logConfig) defer logger.Close() if *certFile == "" { logger.Debugf("Certificate file not specified, assuming certificate also in %s", *keyFile) certFile = keyFile } lockMemory() clientTimeout := time.Duration(*timeoutSeconds) * time.Second freshThreshold := 200 * time.Millisecond backendDeadline := 500 * time.Millisecond maxWait := clientTimeout + backendDeadline timeouts := keywhizfs.Timeouts{freshThreshold, backendDeadline, maxWait} client := keywhizfs.NewClient(*certFile, *keyFile, *caFile, serverURL, clientTimeout, logConfig, *ping) ownership := keywhizfs.NewOwnership(*user, *group) kwfs, root, err := keywhizfs.NewKeywhizFs(&client, ownership, timeouts, logConfig) if err != nil { log.Fatalf("KeywhizFs init fail: %v\n", err) } mountOptions := &fuse.MountOptions{ AllowOther: true, Name: kwfs.String(), Options: []string{"default_permissions"}, } // Empty Options struct avoids setting a global uid/gid override. conn := nodefs.NewFileSystemConnector(root, &nodefs.Options{}) server, err := fuse.NewServer(conn.RawFS(), mountpoint, mountOptions) if err != nil { log.Fatalf("Mount fail: %v\n", err) } server.Serve() }
// NewKeywhizFs readies a KeywhizFs struct and its parent filesystem objects. func NewKeywhizFs(client *Client, ownership Ownership, timeouts Timeouts, logConfig log.Config) (kwfs *KeywhizFs, root nodefs.Node, err error) { logger := log.New("kwfs", logConfig) cache := NewCache(client, timeouts, logConfig) defaultfs := pathfs.NewDefaultFileSystem() // Returns ENOSYS by default readonlyfs := pathfs.NewReadonlyFileSystem(defaultfs) // R/W calls return EPERM kwfs = &KeywhizFs{readonlyfs, logger, client, cache, time.Now(), ownership} nfs := pathfs.NewPathNodeFs(kwfs, nil) nfs.SetDebug(logConfig.Debug) return kwfs, nfs.Root(), nil }
// Locks memory, preventing memory from being written to disk as swap func lockMemory(debug bool) { logConfig := klog.Config{ Debug: debug, Mountpoint: "", } logger := klog.New("kwfs_main", logConfig) err := unix.Mlockall(unix.MCL_FUTURE | unix.MCL_CURRENT) switch err { case nil: case unix.ENOSYS: logger.Warnf("mlockall() not implemented on this system") case unix.ENOMEM: logger.Warnf("mlockall() failed with ENOMEM") default: log.Fatalf("Could not perform mlockall and prevent swapping memory: %v", err) } }
// NewClient produces a read-to-use client struct given PEM-encoded certificate file, key file, and // ca file with the list of trusted certificate authorities. func NewClient(certFile, keyFile, caFile, serverURL string, timeout time.Duration, logConfig klog.Config, ping bool) (client Client) { logger := klog.New("kwfs_client", logConfig) params := httpClientParams{certFile, keyFile, caFile, timeout} reqc := make(chan http.Client) // Getter from channel. getClient := func() *http.Client { client := <-reqc return &(client) } initial, err := params.buildClient() if err != nil { panic(err) } // Asynchronously updates client and owns current reference. go func() { var current = *initial for { select { case t := <-time.Tick(clientRefresh): // Periodically update client. logger.Infof("Updating http client at %v", t) if c, err := params.buildClient(); err != nil { logger.Errorf("Error refreshing http client: %v", err) } else { current = *c } case reqc <- current: // Service request for current client. } } }() client = Client{logger, getClient, serverURL} if ping { if _, ok := client.SecretList(); !ok { log.Fatalf("Failed startup /secrets ping to %v", client.url) } } return client }
// NewCache initializes a Cache. func NewCache(backend SecretBackend, timeouts Timeouts, logConfig log.Config) *Cache { logger := log.New("kwfs_cache", logConfig) return &Cache{logger, NewSecretMap(), backend, timeouts} }