mirror of https://github.com/kubernetes/kops.git
Merge pull request #50 from justinsb/upup_keys
upup: better secrets support
This commit is contained in:
commit
c774777160
|
|
@ -275,6 +275,19 @@ func (c *CreateClusterCmd) Run() error {
|
|||
l.TemplateFunctions["Secrets"] = func() fi.SecretStore {
|
||||
return secretStore
|
||||
}
|
||||
l.TemplateFunctions["GetOrCreateSecret"] = func(id string) (string, error) {
|
||||
secret, err := secretStore.FindSecret(id)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error finding secret %q: %v", id, err)
|
||||
}
|
||||
if secret == nil {
|
||||
secret, err = secretStore.CreateSecret(id)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error creating secret %q: %v", id, err)
|
||||
}
|
||||
}
|
||||
return secret.AsString()
|
||||
}
|
||||
|
||||
if c.SSHPublicKey != "" {
|
||||
authorized, err := ioutil.ReadFile(c.SSHPublicKey)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,67 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/x509/pkix"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func pkixNameToString(name *pkix.Name) string {
|
||||
seq := name.ToRDNSequence()
|
||||
var s bytes.Buffer
|
||||
for _, rdnSet := range seq {
|
||||
for _, rdn := range rdnSet {
|
||||
if s.Len() != 0 {
|
||||
s.WriteString(",")
|
||||
}
|
||||
key := ""
|
||||
t := rdn.Type
|
||||
if len(t) == 4 && t[0] == 2 && t[1] == 5 && t[2] == 4 {
|
||||
switch t[3] {
|
||||
case 3:
|
||||
key = "cn"
|
||||
case 5:
|
||||
key = "serial"
|
||||
case 6:
|
||||
key = "c"
|
||||
case 7:
|
||||
key = "l"
|
||||
case 10:
|
||||
key = "o"
|
||||
case 11:
|
||||
key = "ou"
|
||||
}
|
||||
}
|
||||
if key == "" {
|
||||
key = t.String()
|
||||
}
|
||||
s.WriteString(fmt.Sprintf("%v=%v", key, rdn.Value))
|
||||
}
|
||||
}
|
||||
return s.String()
|
||||
}
|
||||
|
||||
func parsePkixName(s string) (*pkix.Name, error) {
|
||||
name := new(pkix.Name)
|
||||
|
||||
tokens := strings.Split(s, ",")
|
||||
for _, token := range tokens {
|
||||
token = strings.TrimSpace(token)
|
||||
kv := strings.SplitN(token, "=", 2)
|
||||
if len(kv) != 2 {
|
||||
return nil, fmt.Errorf("unrecognized token (expected k=v): %q", token)
|
||||
}
|
||||
k := strings.ToLower(kv[0])
|
||||
v := kv[1]
|
||||
|
||||
switch k {
|
||||
case "cn":
|
||||
name.CommonName = v
|
||||
default:
|
||||
return nil, fmt.Errorf("unrecognized key %q in token %q", k, token)
|
||||
}
|
||||
}
|
||||
|
||||
return name, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// secretsCmd represents the secrets command
|
||||
var secretsCmd = &cobra.Command{
|
||||
Use: "secrets",
|
||||
Short: "Manage secrets & keys",
|
||||
Long: `Manage secrets & keys`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println("syntax: create describe expose get")
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
RootCmd.AddCommand(secretsCmd)
|
||||
|
||||
// Here you will define your flags and configuration settings.
|
||||
|
||||
// Cobra supports Persistent Flags which will work for this command
|
||||
// and all subcommands, e.g.:
|
||||
// secretsCmd.PersistentFlags().String("foo", "", "A help for foo")
|
||||
|
||||
// Cobra supports local flags which will only run when this command
|
||||
// is called directly, e.g.:
|
||||
// secretsCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"crypto/x509"
|
||||
"github.com/golang/glog"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/kube-deploy/upup/pkg/fi"
|
||||
"net"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type CreateSecretsCommand struct {
|
||||
StateDir string
|
||||
|
||||
Id string
|
||||
Type string
|
||||
|
||||
Usage string
|
||||
Subject string
|
||||
AlternateNames []string
|
||||
}
|
||||
|
||||
var createSecretsCommand CreateSecretsCommand
|
||||
|
||||
func init() {
|
||||
cmd := &cobra.Command{
|
||||
Use: "create",
|
||||
Short: "Create secrets",
|
||||
Long: `Create secrets.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := createSecretsCommand.Run()
|
||||
if err != nil {
|
||||
glog.Exitf("%v", err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
secretsCmd.AddCommand(cmd)
|
||||
|
||||
cmd.Flags().StringVarP(&createSecretsCommand.StateDir, "state", "", "", "Directory in which to store state")
|
||||
cmd.Flags().StringVarP(&createSecretsCommand.Type, "type", "", "", "Type of secret to create")
|
||||
cmd.Flags().StringVarP(&createSecretsCommand.Id, "id", "", "", "Id of secret to create")
|
||||
cmd.Flags().StringVarP(&createSecretsCommand.Usage, "usage", "", "", "Usage of secret (for SSL certificate)")
|
||||
cmd.Flags().StringVarP(&createSecretsCommand.Subject, "subject", "", "", "Subject (for SSL certificate)")
|
||||
cmd.Flags().StringSliceVarP(&createSecretsCommand.AlternateNames, "san", "", nil, "Alternate name (for SSL certificate)")
|
||||
}
|
||||
|
||||
func (cmd *CreateSecretsCommand) Run() error {
|
||||
if cmd.StateDir == "" {
|
||||
return fmt.Errorf("state dir is required")
|
||||
}
|
||||
|
||||
if cmd.Id == "" {
|
||||
return fmt.Errorf("id is required")
|
||||
}
|
||||
|
||||
if cmd.Type == "" {
|
||||
return fmt.Errorf("type is required")
|
||||
}
|
||||
|
||||
// TODO: Prompt before replacing?
|
||||
// TODO: Keep history?
|
||||
|
||||
switch cmd.Type {
|
||||
case "secret":
|
||||
{
|
||||
secretStore, err := fi.NewFilesystemSecretStore(path.Join(cmd.StateDir, "secrets"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error building secret store: %v", err)
|
||||
}
|
||||
_, err = secretStore.CreateSecret(cmd.Id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating secrets %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
case "keypair":
|
||||
// TODO: Create a rotate command which keeps the same values?
|
||||
// Or just do it here a "replace" action - existing=fail, replace or rotate
|
||||
// TODO: Create a CreateKeypair class, move to fi (this is duplicated code)
|
||||
{
|
||||
if cmd.Subject == "" {
|
||||
return fmt.Errorf("subject is required")
|
||||
}
|
||||
|
||||
subject, err := parsePkixName(cmd.Subject)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error parsing subject: %v", err)
|
||||
}
|
||||
template := &x509.Certificate{
|
||||
Subject: *subject,
|
||||
BasicConstraintsValid: true,
|
||||
IsCA: false,
|
||||
}
|
||||
|
||||
if len(template.Subject.ToRDNSequence()) == 0 {
|
||||
return fmt.Errorf("Subject name was empty")
|
||||
}
|
||||
|
||||
switch cmd.Usage {
|
||||
case "client":
|
||||
template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}
|
||||
template.KeyUsage = x509.KeyUsageDigitalSignature
|
||||
break
|
||||
|
||||
case "server":
|
||||
template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}
|
||||
template.KeyUsage = x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment
|
||||
break
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unknown usage: %q", cmd.Usage)
|
||||
}
|
||||
|
||||
for _, san := range cmd.AlternateNames {
|
||||
san = strings.TrimSpace(san)
|
||||
if san == "" {
|
||||
continue
|
||||
}
|
||||
if ip := net.ParseIP(san); ip != nil {
|
||||
template.IPAddresses = append(template.IPAddresses, ip)
|
||||
} else {
|
||||
template.DNSNames = append(template.DNSNames, san)
|
||||
}
|
||||
}
|
||||
|
||||
caStore, err := fi.NewFilesystemCAStore(path.Join(cmd.StateDir, "pki"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error building CA store: %v", err)
|
||||
}
|
||||
|
||||
// TODO: Allow resigning of the existing private key?
|
||||
|
||||
key, err := caStore.CreatePrivateKey(cmd.Id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating privatekey %v", err)
|
||||
}
|
||||
_, err = caStore.IssueCert(cmd.Id, key, template)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating certificate %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
default:
|
||||
return fmt.Errorf("secret type not known: %q", cmd.Type)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,187 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"bytes"
|
||||
"crypto/rsa"
|
||||
"github.com/golang/glog"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/kube-deploy/upup/pkg/fi"
|
||||
"os"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
)
|
||||
|
||||
type DescribeSecretsCommand struct {
|
||||
StateDir string
|
||||
}
|
||||
|
||||
var describeSecretsCommand DescribeSecretsCommand
|
||||
|
||||
func init() {
|
||||
cmd := &cobra.Command{
|
||||
Use: "describe",
|
||||
Short: "Describe secrets",
|
||||
Long: `Describe secrets.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := describeSecretsCommand.Run()
|
||||
if err != nil {
|
||||
glog.Exitf("%v", err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
secretsCmd.AddCommand(cmd)
|
||||
|
||||
cmd.Flags().StringVarP(&describeSecretsCommand.StateDir, "state", "", "", "Directory in which to store state")
|
||||
}
|
||||
|
||||
func (c *DescribeSecretsCommand) Run() error {
|
||||
if c.StateDir == "" {
|
||||
return fmt.Errorf("state dir is required")
|
||||
}
|
||||
|
||||
w := new(tabwriter.Writer)
|
||||
var b bytes.Buffer
|
||||
|
||||
// Format in tab-separated columns with a tab stop of 8.
|
||||
w.Init(os.Stdout, 0, 8, 0, '\t', tabwriter.StripEscape)
|
||||
|
||||
{
|
||||
caStore, err := fi.NewFilesystemCAStore(path.Join(c.StateDir, "pki"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error building CA store: %v", err)
|
||||
}
|
||||
ids, err := caStore.List()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error listing CA store items %v", err)
|
||||
}
|
||||
|
||||
for _, id := range ids {
|
||||
cert, err := caStore.FindCert(id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error retrieving cert %q: %v", id, err)
|
||||
}
|
||||
|
||||
key, err := caStore.FindPrivateKey(id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error retrieving private key %q: %v", id, err)
|
||||
}
|
||||
|
||||
if key == nil && cert == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
err = describeKeypair(id, cert, key, &b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b.WriteString("\n")
|
||||
|
||||
_, err = w.Write(b.Bytes())
|
||||
if err != nil {
|
||||
return fmt.Errorf("error writing to output: %v", err)
|
||||
}
|
||||
|
||||
b.Reset()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
secretStore, err := fi.NewFilesystemSecretStore(path.Join(c.StateDir, "secrets"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error building secret store: %v", err)
|
||||
}
|
||||
ids, err := secretStore.ListSecrets()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error listing secrets %v", err)
|
||||
}
|
||||
|
||||
for _, id := range ids {
|
||||
secret, err := secretStore.FindSecret(id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error retrieving secret %q: %v", id, err)
|
||||
}
|
||||
|
||||
if secret == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
err = describeSecret(id, secret, &b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b.WriteString("\n")
|
||||
|
||||
_, err = w.Write(b.Bytes())
|
||||
if err != nil {
|
||||
return fmt.Errorf("error writing to output: %v", err)
|
||||
}
|
||||
|
||||
b.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
return w.Flush()
|
||||
}
|
||||
|
||||
func describeKeypair(id string, c *fi.Certificate, k *fi.PrivateKey, w *bytes.Buffer) error {
|
||||
var alternateNames []string
|
||||
if c != nil {
|
||||
alternateNames = append(alternateNames, c.Certificate.DNSNames...)
|
||||
alternateNames = append(alternateNames, c.Certificate.EmailAddresses...)
|
||||
for _, ip := range c.Certificate.IPAddresses {
|
||||
alternateNames = append(alternateNames, ip.String())
|
||||
}
|
||||
sort.Strings(alternateNames)
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "Id:\t%s\n", id)
|
||||
if c != nil && k != nil {
|
||||
fmt.Fprintf(w, "Type:\t%s\n", "keypair")
|
||||
} else if c != nil && k == nil {
|
||||
fmt.Fprintf(w, "Type:\t%s\n", "certificate")
|
||||
} else if k != nil && c == nil {
|
||||
// Unexpected!
|
||||
fmt.Fprintf(w, "Type:\t%s\n", "privatekey")
|
||||
} else {
|
||||
return fmt.Errorf("expected either certificate or key to be set")
|
||||
}
|
||||
|
||||
if c != nil {
|
||||
fmt.Fprintf(w, "Subject:\t%s\n", pkixNameToString(&c.Certificate.Subject))
|
||||
fmt.Fprintf(w, "Issuer:\t%s\n", pkixNameToString(&c.Certificate.Issuer))
|
||||
fmt.Fprintf(w, "AlternateNames:\t%s\n", strings.Join(alternateNames, ", "))
|
||||
fmt.Fprintf(w, "CA:\t%v\n", c.IsCA)
|
||||
fmt.Fprintf(w, "NotAfter:\t%s\n", c.Certificate.NotAfter)
|
||||
fmt.Fprintf(w, "NotBefore:\t%s\n", c.Certificate.NotBefore)
|
||||
|
||||
// PublicKeyAlgorithm doesn't have a String() function. Also, is this important information?
|
||||
//fmt.Fprintf(w, "PublicKeyAlgorithm:\t%v\n", c.Certificate.PublicKeyAlgorithm)
|
||||
//fmt.Fprintf(w, "SignatureAlgorithm:\t%v\n", c.Certificate.SignatureAlgorithm)
|
||||
}
|
||||
|
||||
if k != nil {
|
||||
if rsaPrivateKey, ok := k.Key.(*rsa.PrivateKey); ok {
|
||||
fmt.Fprintf(w, "PrivateKeyType:\t%v\n", "rsa")
|
||||
fmt.Fprintf(w, "KeyLength:\t%v\n", rsaPrivateKey.N.BitLen())
|
||||
} else {
|
||||
fmt.Fprintf(w, "PrivateKeyType:\tunknown (%T)\n", k.Key)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func describeSecret(id string, s *fi.Secret, w *bytes.Buffer) error {
|
||||
fmt.Fprintf(w, "Id:\t%s\n", id)
|
||||
fmt.Fprintf(w, "Type:\t%s\n", "secret")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/kube-deploy/upup/pkg/fi"
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
type ExposeSecretsCommand struct {
|
||||
StateDir string
|
||||
|
||||
ID string
|
||||
Type string
|
||||
}
|
||||
|
||||
var exposeSecretsCommand ExposeSecretsCommand
|
||||
|
||||
func init() {
|
||||
cmd := &cobra.Command{
|
||||
Use: "expose",
|
||||
Short: "Expose secrets",
|
||||
Long: `Expose secrets.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := exposeSecretsCommand.Run()
|
||||
if err != nil {
|
||||
glog.Exitf("%v", err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
secretsCmd.AddCommand(cmd)
|
||||
|
||||
cmd.Flags().StringVarP(&exposeSecretsCommand.StateDir, "state", "", "", "Directory in which to store state")
|
||||
cmd.Flags().StringVarP(&exposeSecretsCommand.Type, "type", "", "", "Type of secret to create")
|
||||
cmd.Flags().StringVarP(&exposeSecretsCommand.ID, "id", "", "", "Id of secret to create")
|
||||
}
|
||||
|
||||
func (cmd *ExposeSecretsCommand) Run() error {
|
||||
if cmd.StateDir == "" {
|
||||
return fmt.Errorf("state dir is required")
|
||||
}
|
||||
|
||||
id := cmd.ID
|
||||
if id == "" {
|
||||
return fmt.Errorf("id is required")
|
||||
}
|
||||
|
||||
if cmd.Type == "" {
|
||||
return fmt.Errorf("type is required")
|
||||
}
|
||||
|
||||
var value string
|
||||
switch cmd.Type {
|
||||
case "secret":
|
||||
{
|
||||
secretStore, err := fi.NewFilesystemSecretStore(path.Join(cmd.StateDir, "secrets"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error building secret store: %v", err)
|
||||
}
|
||||
secret, err := secretStore.FindSecret(id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error finding secret %q: %v", id, err)
|
||||
}
|
||||
if secret == nil {
|
||||
return fmt.Errorf("secret not found: %q", id)
|
||||
}
|
||||
value = string(secret.Data)
|
||||
}
|
||||
|
||||
case "certificate", "privatekey":
|
||||
{
|
||||
caStore, err := fi.NewFilesystemCAStore(path.Join(cmd.StateDir, "pki"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error building CA store: %v", err)
|
||||
}
|
||||
|
||||
if cmd.Type == "privatekey" {
|
||||
k, err := caStore.FindPrivateKey(id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error finding privatekey: %v", err)
|
||||
}
|
||||
if k == nil {
|
||||
return fmt.Errorf("privatekey not found: %q", id)
|
||||
}
|
||||
value, err = k.AsString()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error encoding privatekey: %v", err)
|
||||
}
|
||||
} else {
|
||||
c, err := caStore.FindCert(id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error finding certificate: %v", err)
|
||||
}
|
||||
if c == nil {
|
||||
return fmt.Errorf("certificate not found: %q", id)
|
||||
}
|
||||
value, err = c.AsString()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error encoding certiifcate: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return fmt.Errorf("secret type not known: %q", cmd.Type)
|
||||
}
|
||||
|
||||
_, err := fmt.Fprint(os.Stdout, value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error writing to output: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"bytes"
|
||||
"github.com/golang/glog"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/kube-deploy/upup/pkg/fi"
|
||||
"os"
|
||||
"path"
|
||||
"text/tabwriter"
|
||||
)
|
||||
|
||||
type GetSecretsCommand struct {
|
||||
StateDir string
|
||||
}
|
||||
|
||||
var getSecretsCommand GetSecretsCommand
|
||||
|
||||
func init() {
|
||||
cmd := &cobra.Command{
|
||||
Use: "get",
|
||||
Short: "Get secrets",
|
||||
Long: `Get secrets.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := getSecretsCommand.Run()
|
||||
if err != nil {
|
||||
glog.Exitf("%v", err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
secretsCmd.AddCommand(cmd)
|
||||
|
||||
cmd.Flags().StringVarP(&getSecretsCommand.StateDir, "state", "", "", "Directory in which to store state")
|
||||
}
|
||||
|
||||
type SecretInfo struct {
|
||||
Id string
|
||||
Type string
|
||||
}
|
||||
|
||||
func (c *GetSecretsCommand) Run() error {
|
||||
if c.StateDir == "" {
|
||||
return fmt.Errorf("state dir is required")
|
||||
}
|
||||
|
||||
var infos []*SecretInfo
|
||||
{
|
||||
caStore, err := fi.NewFilesystemCAStore(path.Join(c.StateDir, "pki"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error building CA store: %v", err)
|
||||
}
|
||||
ids, err := caStore.List()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error listing CA store items %v", err)
|
||||
}
|
||||
|
||||
for _, id := range ids {
|
||||
info := &SecretInfo{
|
||||
Id: id,
|
||||
Type: "keypair",
|
||||
}
|
||||
infos = append(infos, info)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
secretStore, err := fi.NewFilesystemSecretStore(path.Join(c.StateDir, "secrets"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error building secret store: %v", err)
|
||||
}
|
||||
ids, err := secretStore.ListSecrets()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error listing secrets %v", err)
|
||||
}
|
||||
|
||||
for _, id := range ids {
|
||||
info := &SecretInfo{
|
||||
Id: id,
|
||||
Type: "secret",
|
||||
}
|
||||
infos = append(infos, info)
|
||||
}
|
||||
}
|
||||
|
||||
var b bytes.Buffer
|
||||
w := new(tabwriter.Writer)
|
||||
|
||||
// Format in tab-separated columns with a tab stop of 8.
|
||||
w.Init(os.Stdout, 0, 8, 0, '\t', tabwriter.StripEscape)
|
||||
for _, info := range infos {
|
||||
b.WriteByte(tabwriter.Escape)
|
||||
b.WriteString(info.Type)
|
||||
b.WriteByte(tabwriter.Escape)
|
||||
b.WriteByte('\t')
|
||||
b.WriteByte(tabwriter.Escape)
|
||||
b.WriteString(info.Id)
|
||||
b.WriteByte(tabwriter.Escape)
|
||||
b.WriteByte('\n')
|
||||
|
||||
_, err := w.Write(b.Bytes())
|
||||
if err != nil {
|
||||
return fmt.Errorf("error writing to output: %v", err)
|
||||
}
|
||||
b.Reset()
|
||||
}
|
||||
w.Flush()
|
||||
return nil
|
||||
}
|
||||
|
|
@ -12,17 +12,17 @@ APIServer:
|
|||
Key: {{ Base64Encode (CA.PrivateKey "master").AsString }}
|
||||
|
||||
KubeUser: {{ .KubeUser }}
|
||||
KubePassword: {{ (Secrets.Secret "kube").AsString }}
|
||||
KubePassword: {{ GetOrCreateSecret "kube" }}
|
||||
|
||||
Tokens:
|
||||
admin: {{ (Secrets.Secret "admin").AsString }}
|
||||
kubelet: {{ (Secrets.Secret "kubelet").AsString }}
|
||||
kube-proxy: {{ (Secrets.Secret "kube-proxy").AsString }}
|
||||
"system:scheduler": {{ (Secrets.Secret "system:scheduler").AsString }}
|
||||
"system:controller_manager": {{ (Secrets.Secret "system:controller_manager").AsString }}
|
||||
"system:logging": {{ (Secrets.Secret "system:logging").AsString }}
|
||||
"system:monitoring": {{ (Secrets.Secret "system:monitoring").AsString }}
|
||||
"system:dns": {{ (Secrets.Secret "system:dns").AsString }}
|
||||
admin: {{ GetOrCreateSecret "admin" }}
|
||||
kubelet: {{ GetOrCreateSecret "kubelet" }}
|
||||
kube-proxy: {{ GetOrCreateSecret "kube-proxy" }}
|
||||
"system:scheduler": {{ GetOrCreateSecret "system:scheduler" }}
|
||||
"system:controller_manager": {{ GetOrCreateSecret "system:controller_manager" }}
|
||||
"system:logging": {{ GetOrCreateSecret "system:logging" }}
|
||||
"system:monitoring": {{ GetOrCreateSecret "system:monitoring" }}
|
||||
"system:dns": {{ GetOrCreateSecret "system:dns" }}
|
||||
|
||||
Tags:
|
||||
{{ range $tag := Args }}
|
||||
|
|
|
|||
|
|
@ -63,6 +63,8 @@ type CAStore interface {
|
|||
|
||||
IssueCert(id string, privateKey *PrivateKey, template *x509.Certificate) (*Certificate, error)
|
||||
CreatePrivateKey(id string) (*PrivateKey, error)
|
||||
|
||||
List() ([]string, error)
|
||||
}
|
||||
|
||||
func (c *Certificate) AsString() (string, error) {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import (
|
|||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type FilesystemCAStore struct {
|
||||
|
|
@ -106,41 +107,6 @@ func (c *FilesystemCAStore) generateCACertificate() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *FilesystemCAStore) getSubjectKey(subject *pkix.Name) string {
|
||||
seq := subject.ToRDNSequence()
|
||||
var s bytes.Buffer
|
||||
for _, rdnSet := range seq {
|
||||
for _, rdn := range rdnSet {
|
||||
if s.Len() != 0 {
|
||||
s.WriteString(",")
|
||||
}
|
||||
key := ""
|
||||
t := rdn.Type
|
||||
if len(t) == 4 && t[0] == 2 && t[1] == 5 && t[2] == 4 {
|
||||
switch t[3] {
|
||||
case 3:
|
||||
key = "cn"
|
||||
case 5:
|
||||
key = "serial"
|
||||
case 6:
|
||||
key = "c"
|
||||
case 7:
|
||||
key = "l"
|
||||
case 10:
|
||||
key = "o"
|
||||
case 11:
|
||||
key = "ou"
|
||||
}
|
||||
}
|
||||
if key == "" {
|
||||
key = t.String()
|
||||
}
|
||||
s.WriteString(fmt.Sprintf("%v=%v", key, rdn.Value))
|
||||
}
|
||||
}
|
||||
return s.String()
|
||||
}
|
||||
|
||||
func (c *FilesystemCAStore) buildCertificatePath(id string) string {
|
||||
return path.Join(c.basedir, "issued", id+".crt")
|
||||
}
|
||||
|
|
@ -190,6 +156,26 @@ func (c *FilesystemCAStore) FindCert(id string) (*Certificate, error) {
|
|||
return cert, nil
|
||||
}
|
||||
|
||||
func (c *FilesystemCAStore) List() ([]string, error) {
|
||||
var ids []string
|
||||
if c.caCertificate != nil {
|
||||
ids = append(ids, "ca")
|
||||
}
|
||||
|
||||
issuedDir := path.Join(c.basedir, "issued")
|
||||
files, err := ioutil.ReadDir(issuedDir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading directory %q: %v", issuedDir, err)
|
||||
}
|
||||
|
||||
for _, f := range files {
|
||||
name := f.Name()
|
||||
name = strings.TrimSuffix(name, ".crt")
|
||||
ids = append(ids, name)
|
||||
}
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
func (c *FilesystemCAStore) IssueCert(id string, privateKey *PrivateKey, template *x509.Certificate) (*Certificate, error) {
|
||||
p := c.buildCertificatePath(id)
|
||||
|
||||
|
|
|
|||
|
|
@ -38,15 +38,26 @@ func (c *FilesystemSecretStore) FindSecret(id string) (*Secret, error) {
|
|||
return s, nil
|
||||
}
|
||||
|
||||
func (c *FilesystemSecretStore) ListSecrets() ([]string, error) {
|
||||
files, err := ioutil.ReadDir(c.basedir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error listing secrets directory: %v", err)
|
||||
}
|
||||
var ids []string
|
||||
for _, f := range files {
|
||||
id := f.Name()
|
||||
ids = append(ids, id)
|
||||
}
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
func (c *FilesystemSecretStore) Secret(id string) (*Secret, error) {
|
||||
s, err := c.FindSecret(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if s == nil {
|
||||
// For now, we auto-create the secret
|
||||
return c.CreateSecret(id)
|
||||
// return nil, fmt.Errorf("Secret not found: %q", id)
|
||||
return nil, fmt.Errorf("Secret not found: %q", id)
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,8 +8,14 @@ import (
|
|||
)
|
||||
|
||||
type SecretStore interface {
|
||||
// Get a secret. Returns an error if not found
|
||||
Secret(id string) (*Secret, error)
|
||||
// Find a secret, if exists. Returns nil,nil if not found
|
||||
FindSecret(id string) (*Secret, error)
|
||||
// Create or replace a secret
|
||||
CreateSecret(id string) (*Secret, error)
|
||||
// Lists the ids of all known secrets
|
||||
ListSecrets() ([]string, error)
|
||||
}
|
||||
|
||||
type Secret struct {
|
||||
|
|
|
|||
Loading…
Reference in New Issue