Skip to content
This repository was archived by the owner on Jun 23, 2022. It is now read-only.

cmd/kube-client-agent: add mount sub command to consume cert secrets #30

Closed
wants to merge 23 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
f980f34
OWNERS: init
hexfusion Mar 6, 2019
6c3776b
Merge pull request #2 from openshift/add_owners
hexfusion Mar 6, 2019
a7aa52c
*: add OpenShift Dockerfiles
hexfusion Mar 6, 2019
1b39e01
Merge pull request #1 from openshift/add_docker_4.0
hexfusion Mar 6, 2019
98da95e
certagent: use kubeconfig to create client for k8s api
abhinavdahiya Nov 2, 2018
7c3e736
Merge pull request #4 from openshift/cp
openshift-merge-robot Mar 12, 2019
bbb0614
Dockerfile: add rhel images
hexfusion Mar 17, 2019
ae8f7b5
Merge pull request #5 from openshift/rhel_images
hexfusion Mar 18, 2019
51cc529
*: add support for metrics signer
hexfusion Mar 25, 2019
b2d32c0
pkg/certsigner: add test coverage for metrics signer
hexfusion Mar 25, 2019
9d3e068
Merge pull request #6 from openshift/cp_metrics_signer
openshift-merge-robot Mar 28, 2019
7522873
pkg/certagent: wait forever for CSR request to reply
hexfusion Apr 5, 2019
48eb189
Merge pull request #8 from openshift/inf
openshift-merge-robot Apr 5, 2019
94cc8fc
certsigner: update the expired test_data cert
abhinavdahiya Apr 24, 2019
ccabab5
etcd-signer-server: allow server to serve TLS based on SNI
abhinavdahiya Apr 24, 2019
004fb20
Merge pull request #14 from openshift/cp_upstream_28
openshift-merge-robot Apr 26, 2019
ba12efb
*: add secure and insecure health checks
abhinavdahiya Jul 12, 2019
227189d
Merge pull request #15 from abhinavdahiya/upstream-pr-29
openshift-merge-robot Jul 12, 2019
3760204
Build kubeconfig client with empty ConfigOverrides
csrwng Nov 15, 2018
fe90e5d
pkg/certsigner/signer: Add "client" usage to server profile
wking Dec 6, 2018
13177f1
*: add --max-retry flag to kube-client-agent
hexfusion Apr 3, 2019
2f1af51
Revert "*: add --max-retry flag to kube-client-agent"
hexfusion Apr 5, 2019
aace009
cmd/kube-client-agent: add mount sub command to consume cert secrets
alaypatel07 Sep 24, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions Dockerfile.kube-client-agent.openshift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
FROM registry.svc.ci.openshift.org/openshift/release:golang-1.10 AS builder

WORKDIR /go/src/github.com/coreos/kubecsr

COPY . .

RUN make bin/kube-client-agent

# stage 2
FROM registry.svc.ci.openshift.org/openshift/origin-v4.0:base

ENTRYPOINT ["/usr/bin/kube-client-agent"]

COPY --from=builder /go/src/github.com/coreos/kubecsr/bin/kube-client-agent /usr/bin/

LABEL io.k8s.display-name="kube-client-agent" \
io.k8s.description="This client agent generates a valid CSR according to the configuration flags provided to it." \
maintainer="Sam Batschelet <sbatsche@redhat.com>"
18 changes: 18 additions & 0 deletions Dockerfile.kube-client-agent.rhel
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
FROM registry.svc.ci.openshift.org/openshift/release:golang-1.10 AS builder

WORKDIR /go/src/github.com/coreos/kubecsr

COPY . .

RUN make bin/kube-client-agent

# stage 2
FROM registry.svc.ci.openshift.org/ocp/4.0:base

ENTRYPOINT ["/usr/bin/kube-client-agent"]

COPY --from=builder /go/src/github.com/coreos/kubecsr/bin/kube-client-agent /usr/bin/

LABEL io.k8s.display-name="kube-client-agent" \
io.k8s.description="This client agent generates a valid CSR according to the configuration flags provided to it." \
maintainer="Sam Batschelet <sbatsche@redhat.com>"
18 changes: 18 additions & 0 deletions Dockerfile.kube-etcd-signer-server.openshift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
FROM registry.svc.ci.openshift.org/openshift/release:golang-1.10 AS builder

WORKDIR /go/src/github.com/coreos/kubecsr

COPY . .

RUN make bin/kube-etcd-signer-server

# stage 2
FROM registry.svc.ci.openshift.org/openshift/origin-v4.0:base

ENTRYPOINT ["/usr/bin/kube-etcd-signer-server"]

COPY --from=builder /go/src/github.com/coreos/kubecsr/bin/kube-etcd-signer-server /usr/bin/

LABEL io.k8s.display-name="kube-etcd-signer-server" \
io.k8s.description="The certificate signer approves a given valid CSR provided by the client agent and returns a signed certificate." \
maintainer="Sam Batschelet <sbatsche@redhat.com>"
18 changes: 18 additions & 0 deletions Dockerfile.kube-etcd-signer-server.rhel
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
FROM registry.svc.ci.openshift.org/openshift/release:golang-1.10 AS builder

WORKDIR /go/src/github.com/coreos/kubecsr

COPY . .

RUN make bin/kube-etcd-signer-server

# stage 2
FROM registry.svc.ci.openshift.org/ocp/4.0:base

ENTRYPOINT ["/usr/bin/kube-etcd-signer-server"]

COPY --from=builder /go/src/github.com/coreos/kubecsr/bin/kube-etcd-signer-server /usr/bin/

LABEL io.k8s.display-name="kube-etcd-signer-server" \
io.k8s.description="The certificate signer approves a given valid CSR provided by the client agent and returns a signed certificate." \
maintainer="Sam Batschelet <sbatsche@redhat.com>"
8 changes: 8 additions & 0 deletions OWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
approvers:
- abhinavdahiya
- crawford
- csrwng
- ericavonb
- hexfusion
- smarterclayton
- wking
119 changes: 119 additions & 0 deletions cmd/kube-client-agent/mount.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package main

import (
"fmt"
"io/ioutil"
"path"
"strings"
"time"

"github.com/golang/glog"
"github.com/spf13/cobra"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
)

var (
mountSecretCmd = &cobra.Command{
Use: "mount --FLAGS",
Short: "Mount a secret with certs",
Long: "This command mouts the secret with valid certs signed by etcd-cert-signer-controller",
PreRunE: validateMountSecretOpts,
RunE: runCmdMountSecret,
}

mountSecretOpts struct {
commonName string
assetsDir string
}
)

func validateMountSecretOpts(cmd *cobra.Command, args []string) error {
if mountSecretOpts.commonName == "" {
return fmt.Errorf("missing required flag: --commonname")
}
if mountSecretOpts.assetsDir == "" {
return fmt.Errorf("missing required flag: --assetsdir")
}
return nil

}

func runCmdMountSecret(cmd *cobra.Command, args []string) error {
return mountSecret()
}

// mount will secret will look for secret in the form of
// <profile>-<podFQDN>, where profile can be peer, server
// and metric and mount the certs as commonname.crt/commonname.key
// this will run as init container in etcd pod managed by CEO.
func mountSecret() error {
var err error
inClusterConfig, err := rest.InClusterConfig()
if err != nil {
return fmt.Errorf("error creating in cluster client config: %v", err)
}

client, err := kubernetes.NewForConfig(inClusterConfig)
if err != nil {
return fmt.Errorf("error creating client: %v", err)
}

duration := 10 * time.Second
var s *v1.Secret
// wait forever for success and retry every duration interval
err = wait.PollInfinite(duration, func() (bool, error) {
fmt.Println(requestOpts.commonName)
s, err = client.CoreV1().Secrets("openshift-etcd").Get(getSecretName(mountSecretOpts.commonName), metav1.GetOptions{})
if err != nil {
glog.Errorf("error in getting secret %s/%s: %v", "openshift-etcd", getSecretName(mountSecretOpts.commonName), err)
return false, err
}
err = ensureCertKeys(s.Data)
if err != nil {
return false, err
}

return true, nil

})

if err != nil {
return err
}

// write out signed certificate to disk
certFile := path.Join(mountSecretOpts.assetsDir, mountSecretOpts.commonName+".crt")
if err := ioutil.WriteFile(certFile, s.Data["tls.crt"], 0644); err != nil {
return fmt.Errorf("unable to write to %s: %v", certFile, err)
}
keyFile := path.Join(mountSecretOpts.assetsDir, mountSecretOpts.commonName+".key")
if err := ioutil.WriteFile(keyFile, s.Data["tls.key"], 0644); err != nil {
return fmt.Errorf("unable to write to %s: %v", keyFile, err)
}
return nil
}

func getSecretName(commonName string) string {
prefix := ""
if strings.Contains(commonName, "peer") {
prefix = "peer"
}
if strings.Contains(commonName, "server") {
prefix = "server"
}
if strings.Contains(commonName, "metric") {
prefix = "metric"
}
return prefix + "-" + strings.Split(commonName, ":")[2]
}

func ensureCertKeys(data map[string][]byte) error {
if len(data["tls.crt"]) == 0 || len(data["tls.key"]) == 0 {
return fmt.Errorf("invalid secret data")
}
return nil
}
44 changes: 44 additions & 0 deletions cmd/kube-client-agent/mount_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package main

import "testing"

func Test_getSecretName(t *testing.T) {
type args struct {
commonName string
}
tests := []struct {
name string
args args
want string
}{
// TODO: Add test cases.
{
name: "server test case",
args: args{
commonName: "system:etcd-server:etcd-0.foo.bar",
},
want: "server-etcd-0.foo.bar",
},
{
name: "peer test case",
args: args{
commonName: "system:etcd-peer:etcd-0.foo.bar",
},
want: "peer-etcd-0.foo.bar",
},
{
name: "metric test case",
args: args{
commonName: "system:etcd-metric:etcd-0.foo.bar",
},
want: "metric-etcd-0.foo.bar",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := getSecretName(tt.args.commonName); got != tt.want {
t.Errorf("getSecretName() = %v, want %v", got, tt.want)
}
})
}
}
37 changes: 20 additions & 17 deletions cmd/kube-client-agent/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,21 @@ var (
dnsNames string
ipAddresses string
assetsDir string
addr string
caCrt string
kubeconfig string
}
)

func init() {
rootCmd.AddCommand(requestCmd)
rootCmd.AddCommand(requestCmd, mountSecretCmd)
requestCmd.PersistentFlags().StringVar(&requestOpts.commonName, "commonname", "", "Common name for the certificate being requested")
requestCmd.PersistentFlags().StringVar(&requestOpts.orgName, "orgname", "", "CA private key file for signer")
requestCmd.PersistentFlags().StringVar(&requestOpts.dnsNames, "dnsnames", "", "Comma separated DNS names of the node to be provided for the X509 certificate")
requestCmd.PersistentFlags().StringVar(&requestOpts.ipAddresses, "ipaddrs", "", "Comma separated IP addresses of the node to be provided for the X509 certificate")
requestCmd.PersistentFlags().StringVar(&requestOpts.assetsDir, "assetsdir", "", "Directory location for the agent where it stores signed certs")
requestCmd.PersistentFlags().StringVar(&requestOpts.addr, "address", "0.0.0.0:6443", "Address on which the signer listens for requests")
requestCmd.PersistentFlags().StringVar(&requestOpts.caCrt, "cacrt", "", "CA certificate for the client agent to establish trust with the signer")
requestCmd.PersistentFlags().StringVar(&requestOpts.kubeconfig, "kubeconfig", "", "Path to the kubeconfig file to connect to apiserver. If \"\", InClusterConfig is used which uses the service account kubernetes gives to pods.")

mountSecretCmd.PersistentFlags().StringVar(&mountSecretOpts.commonName, "commonname", "", "Common name for the certificate being requested")
mountSecretCmd.PersistentFlags().StringVar(&mountSecretOpts.assetsDir, "assetsdir", "", "Directory location for the agent where it stores signed certs")
}

func validateRequestOpts(cmd *cobra.Command, args []string) error {
Expand All @@ -54,8 +55,8 @@ func validateRequestOpts(cmd *cobra.Command, args []string) error {
if requestOpts.assetsDir == "" {
return errors.New("missing required flag: --assetsdir")
}
if requestOpts.caCrt == "" {
return errors.New("missing required flag: --cacrt")
if requestOpts.kubeconfig == "" {
return errors.New("missing required flag: --kubeconfig")
}
return nil

Expand All @@ -77,17 +78,19 @@ func runCmdRequest(cmd *cobra.Command, args []string) error {
}
}

c := agent.CSRConfig{
CommonName: requestOpts.commonName,
OrgName: requestOpts.orgName,
DNSNames: strings.Split(requestOpts.dnsNames, ","),
IPAddresses: ips,
AssetsDir: requestOpts.assetsDir,
SignerAddress: requestOpts.addr,
config := agent.CSRConfig{
CommonName: requestOpts.commonName,
OrgName: requestOpts.orgName,
DNSNames: strings.Split(requestOpts.dnsNames, ","),
IPAddresses: ips,
AssetsDir: requestOpts.assetsDir,
}

if err := agent.StartAgent(c, requestOpts.caCrt); err != nil {
return fmt.Errorf("error starting agent: %s", err)
a, err := agent.NewAgent(config, requestOpts.kubeconfig)
if err != nil {
return fmt.Errorf("error creating agent: %s", err)
}
if err := a.RequestCertificate(); err != nil {
return fmt.Errorf("error requesting certificate: %s", err)
}
return nil
}
Loading