plugin/plugin.go (65 lines of code) (raw):
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package plugin implements CloudKMS plugin for GKE as described in go/gke-secrets-encryption-design.
package plugin
import (
"fmt"
"net"
"os"
"strings"
"github.com/golang/glog"
"google.golang.org/grpc"
)
const (
netProtocol = "unix"
)
// Plugin is a CloudKMS plugin for K8S.
type Plugin interface {
Register(s *grpc.Server)
}
type PluginManager struct {
unixSocketFilePath string
// Embedding these only to shorten access to fields.
net.Listener
server *grpc.Server
plugin Plugin
}
// NewManager creates a new plugin manager.
func NewManager(plugin Plugin, unixSocketFilePath string) *PluginManager {
return &PluginManager{
unixSocketFilePath: unixSocketFilePath,
plugin: plugin,
}
}
// ServeKMSRequests starts gRPC server or dies.
func (m *PluginManager) Start() (*grpc.Server, <-chan error) {
errCh := make(chan error, 1)
sendError := func(err error) {
defer close(errCh)
select {
case errCh <- err:
default:
}
}
if err := m.cleanSockFile(); err != nil {
sendError(fmt.Errorf("failed to cleanup socket file: %w", err))
return nil, errCh
}
listener, err := net.Listen(netProtocol, m.unixSocketFilePath)
if err != nil {
sendError(fmt.Errorf("failed to create listener: %w", err))
return nil, errCh
}
m.Listener = listener
glog.Infof("Listening on unix domain socket: %s", m.unixSocketFilePath)
m.server = grpc.NewServer()
m.plugin.Register(m.server)
go func() {
defer m.cleanSockFile()
sendError(m.server.Serve(m.Listener))
}()
return m.server, errCh
}
func (m *PluginManager) cleanSockFile() error {
// @ implies the use of Linux socket namespace - no file on disk and nothing to clean-up.
if strings.HasPrefix(m.unixSocketFilePath, "@") {
return nil
}
err := os.Remove(m.unixSocketFilePath)
if err != nil && !os.IsNotExist(err) {
return fmt.Errorf("failed to delete the socket file, error: %w", err)
}
return nil
}