diff --git a/Gopkg.lock b/Gopkg.lock index b039b30c..5964aef5 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -35,6 +35,14 @@ revision = "b90dc15cfd220ecf8bbc9043ecb928cef381f011" version = "v0.3.4" +[[projects]] + digest = "1:2cd7915ab26ede7d95b8749e6b1f933f1c6d5398030684e6505940a10f31cfda" + name = "github.com/ghodss/yaml" + packages = ["."] + pruneopts = "UT" + revision = "0ca9ea5df5451ffdf184b4428c902747c2c11cd7" + version = "v1.0.0" + [[projects]] branch = "master" digest = "1:81fda4d18a16651bf92245ce5d6178cdd99f918db30ae9794732655f0686e895" @@ -43,6 +51,17 @@ pruneopts = "UT" revision = "0ebf7795c516423a110473652e9ba3a59a504863" +[[projects]] + digest = "1:b402bb9a24d108a9405a6f34675091b036c8b056aac843bf6ef2389a65c5cf48" + name = "github.com/gogo/protobuf" + packages = [ + "proto", + "sortkeys", + ] + pruneopts = "UT" + revision = "4cbf7e384e768b4e01799441fdf2a706a5635ae7" + version = "v1.2.0" + [[projects]] branch = "travis-1.9" digest = "1:e8f5d9c09a7209c740e769713376abda388c41b777ba8e9ed52767e21acf379f" @@ -54,6 +73,28 @@ pruneopts = "UT" revision = "883fe33ffc4344bad1ecd881f61afd5ec5d80e0a" +[[projects]] + digest = "1:0f1010da61da43ef23bae5c43a619abda82e04f409ec642e6bdeee0d7736530d" + name = "github.com/google/certificate-transparency-go" + packages = [ + ".", + "asn1", + "tls", + "x509", + "x509/pkix", + ] + pruneopts = "UT" + revision = "3629d6846518309d22c16fee15d1007262a459d2" + version = "v1.0.21" + +[[projects]] + branch = "master" + digest = "1:3ee90c0d94da31b442dde97c99635aaafec68d0b8a3c12ee2075c6bdabeec6bb" + name = "github.com/google/gofuzz" + packages = ["."] + pruneopts = "UT" + revision = "24818f796faf91cd76ec7bddd72458fbced7a6c1" + [[projects]] branch = "master" digest = "1:750e747d0aad97b79f4a4e00034bae415c2ea793fd9e61438d966ee9c79579bf" @@ -70,6 +111,14 @@ pruneopts = "UT" revision = "1003c8bd00dc2869cb5ca5282e6ce33834fed514" +[[projects]] + digest = "1:3e551bbb3a7c0ab2a2bf4660e7fcad16db089fdcfbb44b0199e62838038623ea" + name = "github.com/json-iterator/go" + packages = ["."] + pruneopts = "UT" + revision = "1624edc4454b8682399def8740d46db5e4362ba4" + version = "v1.1.5" + [[projects]] branch = "master" digest = "1:e51f40f0c19b39c1825eadd07d5c0a98a2ad5942b166d9fc4f54750ce9a04810" @@ -125,6 +174,22 @@ revision = "6ca4dbf54d38eea1a992b3c722a76a5d1c4cb25c" version = "v0.0.4" +[[projects]] + digest = "1:33422d238f147d247752996a26574ac48dcf472976eda7f5134015f06bf16563" + name = "github.com/modern-go/concurrent" + packages = ["."] + pruneopts = "UT" + revision = "bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94" + version = "1.0.3" + +[[projects]] + digest = "1:e32bdbdb7c377a07a9a46378290059822efdce5c8d96fe71940d87cb4f918855" + name = "github.com/modern-go/reflect2" + packages = ["."] + pruneopts = "UT" + revision = "4b7aa43c6742a2c18fdef89dd197aaae7dac7ccd" + version = "1.0.1" + [[projects]] digest = "1:266d082179f3a29a4bdcf1dcc49d4a304f5c7107e65bd22d1fecacf45f1ac348" name = "github.com/newrelic/go-agent" @@ -212,7 +277,7 @@ [[projects]] branch = "master" - digest = "1:4c9e30abfe7c119eb4d40287f6c23f854f3ad71c69206d8dc6402e1fef14ac88" + digest = "1:17d4424defbc718315d61e296841867ff76b3e03a941b41fdddbae11a7d47746" name = "github.com/smallstep/cli" packages = [ "command", @@ -226,6 +291,8 @@ "jose", "pkg/blackfriday", "pkg/x509", + "token", + "token/provision", "ui", "usage", "utils", @@ -333,6 +400,14 @@ pruneopts = "UT" revision = "63abe20a23e29e80bbef8089bd3dee3ac25e5306" +[[projects]] + digest = "1:2d1fbdc6777e5408cabeb02bf336305e724b925ff4546ded0fa8715a7267922a" + name = "gopkg.in/inf.v0" + packages = ["."] + pruneopts = "UT" + revision = "d2d2541c53f18d2a059457998ce2876cc8e67cbf" + version = "v0.9.1" + [[projects]] digest = "1:7fbe10f3790dc4e6296c7c844c5a9b35513e5521c29c47e10ba99cd2956a2719" name = "gopkg.in/square/go-jose.v2" @@ -354,20 +429,90 @@ revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183" version = "v2.2.1" +[[projects]] + branch = "master" + digest = "1:767b6c0b2c1d9487ee50cb8df1d0fdebf06ac0b19b723f6489d388e7b47c962d" + name = "k8s.io/api" + packages = [ + "admission/v1beta1", + "authentication/v1", + "core/v1", + ] + pruneopts = "UT" + revision = "de494049e22a9ccf748c5bbda7492f42f344d0cd" + +[[projects]] + branch = "master" + digest = "1:5eb353533eaebdfec2392210ab218a389965ba5d4dc02b4aef87b9549e5d0f84" + name = "k8s.io/apimachinery" + packages = [ + "pkg/api/resource", + "pkg/apis/meta/v1", + "pkg/apis/meta/v1/unstructured", + "pkg/conversion", + "pkg/conversion/queryparams", + "pkg/fields", + "pkg/labels", + "pkg/runtime", + "pkg/runtime/schema", + "pkg/runtime/serializer", + "pkg/runtime/serializer/json", + "pkg/runtime/serializer/protobuf", + "pkg/runtime/serializer/recognizer", + "pkg/runtime/serializer/versioning", + "pkg/selection", + "pkg/types", + "pkg/util/errors", + "pkg/util/framer", + "pkg/util/intstr", + "pkg/util/json", + "pkg/util/naming", + "pkg/util/net", + "pkg/util/runtime", + "pkg/util/sets", + "pkg/util/validation", + "pkg/util/validation/field", + "pkg/util/yaml", + "pkg/watch", + "third_party/forked/golang/reflect", + ] + pruneopts = "UT" + revision = "4b3b852955ebe47857fcf134b531b23dd8f3e793" + +[[projects]] + digest = "1:72fd56341405f53c745377e0ebc4abeff87f1a048e0eea6568a20212650f5a82" + name = "k8s.io/klog" + packages = ["."] + pruneopts = "UT" + revision = "71442cd4037d612096940ceb0f3fec3f7fff66e0" + version = "v0.2.0" + +[[projects]] + digest = "1:7719608fe0b52a4ece56c2dde37bedd95b938677d1ab0f84b8a7852e4c59f849" + name = "sigs.k8s.io/yaml" + packages = ["."] + pruneopts = "UT" + revision = "fd68e9863619f6ec2fdd8625fe1f02e7c877e480" + version = "v1.1.0" + [solve-meta] analyzer-name = "dep" analyzer-version = 1 input-imports = [ "github.com/alecthomas/gometalinter", "github.com/client9/misspell/cmd/misspell", + "github.com/ghodss/yaml", "github.com/go-chi/chi", "github.com/golang/lint/golint", + "github.com/google/certificate-transparency-go", + "github.com/google/certificate-transparency-go/x509", "github.com/gordonklaus/ineffassign", "github.com/newrelic/go-agent", "github.com/pkg/errors", "github.com/rs/xid", "github.com/sirupsen/logrus", "github.com/smallstep/assert", + "github.com/smallstep/cli/config", "github.com/smallstep/cli/crypto/keys", "github.com/smallstep/cli/crypto/pemutil", "github.com/smallstep/cli/crypto/randutil", @@ -376,12 +521,19 @@ "github.com/smallstep/cli/errs", "github.com/smallstep/cli/jose", "github.com/smallstep/cli/pkg/x509", + "github.com/smallstep/cli/token", + "github.com/smallstep/cli/token/provision", "github.com/smallstep/cli/usage", "github.com/tsenart/deadcode", "github.com/urfave/cli", "golang.org/x/net/http2", "gopkg.in/square/go-jose.v2", "gopkg.in/square/go-jose.v2/jwt", + "k8s.io/api/admission/v1beta1", + "k8s.io/api/core/v1", + "k8s.io/apimachinery/pkg/apis/meta/v1", + "k8s.io/apimachinery/pkg/runtime", + "k8s.io/apimachinery/pkg/runtime/serializer", ] solver-name = "gps-cdcl" solver-version = 1 diff --git a/autocert/controller/client.go b/autocert/controller/client.go index 6a443168..852f1053 100644 --- a/autocert/controller/client.go +++ b/autocert/controller/client.go @@ -113,12 +113,3 @@ func NewInClusterK8sClient() (Client, error) { httpClient: httpClient, }, nil } - -// NewInsecureK8sClient creates an insecure k8s client which is suitable -// to connect kubernetes api behind proxy -func NewInsecureK8sClient(apiURL string) Client { - return &k8sClient{ - host: apiURL, - httpClient: http.DefaultClient, - } -} \ No newline at end of file diff --git a/autocert/controller/main.go b/autocert/controller/main.go index cb849aa1..68cf3bfd 100644 --- a/autocert/controller/main.go +++ b/autocert/controller/main.go @@ -3,8 +3,8 @@ package main import ( "context" "crypto/sha256" - "encoding/json" "encoding/hex" + "encoding/json" "fmt" "io/ioutil" "net/http" @@ -18,8 +18,8 @@ import ( "github.com/smallstep/certificates/ca" "github.com/smallstep/cli/crypto/pemutil" "k8s.io/api/admission/v1beta1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/serializer" ) @@ -30,8 +30,8 @@ var ( deserializer = codecs.UniversalDeserializer() // GetRootCAPath() is broken; points to secrets not certs. So // we'll hard code instead for now. - //rootCAPath = pki.GetRootCAPath() - rootCAPath = "/home/step/.step/certs/root_ca.crt" + //rootCAPath = pki.GetRootCAPath() + rootCAPath = "/home/step/.step/certs/root_ca.crt" ) const ( @@ -45,23 +45,23 @@ const ( // Config options for the autocert admission controller. type Config struct { - LogFormat string `yaml:"logFormat"` - CaUrl string `yaml:"caUrl"` - CertLifetime string `yaml:"certLifetime"` - Bootstrapper corev1.Container `yaml:"bootstrapper"` - Renewer corev1.Container `yaml:"renewer"` - CertsVolume corev1.Volume `yaml:"certsVolume"` + LogFormat string `yaml:"logFormat"` + CaURL string `yaml:"caUrl"` + CertLifetime string `yaml:"certLifetime"` + Bootstrapper corev1.Container `yaml:"bootstrapper"` + Renewer corev1.Container `yaml:"renewer"` + CertsVolume corev1.Volume `yaml:"certsVolume"` } -// RFC6902 JSONPatch Operation +// PatchOperation represents a RFC6902 JSONPatch Operation type PatchOperation struct { - Op string `json:"op"` - Path string `json:"path"` + Op string `json:"op"` + Path string `json:"path"` Value interface{} `json:"value,omitempty"` } // RFC6901 JSONPath Escaping -- https://tools.ietf.org/html/rfc6901 -func escapeJsonPath(path string) string { +func escapeJSONPath(path string) string { // Replace`~` with `~0` then `/` with `~1`. Note that the order // matters otherwise we'll turn a `/` into a `~/`. path = strings.Replace(path, "~", "~0", -1) @@ -79,7 +79,7 @@ func loadConfig(file string) (*Config, error) { if err := yaml.Unmarshal(data, &cfg); err != nil { return nil, err } - + return &cfg, nil } @@ -88,19 +88,19 @@ func loadConfig(file string) (*Config, error) { // A goroutine is scheduled to cleanup the secret after the token expires. The secret // is also labelled for easy identification and manual cleanup. func createTokenSecret(prefix, namespace, token string) (string, error) { - secret := corev1.Secret { - TypeMeta: metav1.TypeMeta { + secret := corev1.Secret{ + TypeMeta: metav1.TypeMeta{ Kind: "Secret", APIVersion: "v1", }, - ObjectMeta: metav1.ObjectMeta { + ObjectMeta: metav1.ObjectMeta{ GenerateName: prefix, Namespace: namespace, - Labels: map[string]string { + Labels: map[string]string{ tokenSecretLabel: "true", }, }, - StringData: map[string]string { + StringData: map[string]string{ tokenSecretKey: token, }, Type: corev1.SecretTypeOpaque, @@ -162,7 +162,7 @@ func createTokenSecret(prefix, namespace, token string) (string, error) { time.Sleep(tokenLifetime) req, err := client.DeleteRequest(fmt.Sprintf("api/v1/namespaces/%s/secrets/%s", namespace, created.Name)) ctxLog := log.WithFields(log.Fields{ - "name": created.Name, + "name": created.Name, "namespace": namespace, }) if err != nil { @@ -176,7 +176,7 @@ func createTokenSecret(prefix, namespace, token string) (string, error) { } if resp.StatusCode < 200 || resp.StatusCode >= 300 { ctxLog.WithFields(log.Fields{ - "status": resp.Status, + "status": resp.Status, "statusCode": resp.StatusCode, }).Error("Error deleting expired boostrap token secret") return @@ -206,109 +206,109 @@ func mkBootstrapper(config *Config, commonName string, namespace string, provisi sum := sha256.Sum256(crt.Raw) fingerprint := strings.ToLower(hex.EncodeToString(sum[:])) - secretName, err := createTokenSecret(commonName + "-", namespace, token) + secretName, err := createTokenSecret(commonName+"-", namespace, token) if err != nil { return b, errors.Wrap(err, "create token secret") } log.Infof("Secret name is: %s", secretName) - b.Env = append(b.Env, corev1.EnvVar { - Name: "COMMON_NAME", + b.Env = append(b.Env, corev1.EnvVar{ + Name: "COMMON_NAME", Value: commonName, }) - b.Env = append(b.Env, corev1.EnvVar { + b.Env = append(b.Env, corev1.EnvVar{ Name: "STEP_TOKEN", - ValueFrom: &corev1.EnvVarSource { - SecretKeyRef: &corev1.SecretKeySelector { - LocalObjectReference: corev1.LocalObjectReference { + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ Name: secretName, }, Key: tokenSecretKey, }, }, }) - b.Env = append(b.Env, corev1.EnvVar { - Name: "STEP_CA_URL", - Value: config.CaUrl, + b.Env = append(b.Env, corev1.EnvVar{ + Name: "STEP_CA_URL", + Value: config.CaURL, }) - b.Env = append(b.Env, corev1.EnvVar { - Name: "STEP_FINGERPRINT", + b.Env = append(b.Env, corev1.EnvVar{ + Name: "STEP_FINGERPRINT", Value: fingerprint, }) - b.Env = append(b.Env, corev1.EnvVar { - Name: "STEP_NOT_AFTER", + b.Env = append(b.Env, corev1.EnvVar{ + Name: "STEP_NOT_AFTER", Value: config.CertLifetime, }) return b, nil } // mkRenewer generates a new renewer based on the template provided in Config. -func mkRenewer(config *Config) (corev1.Container) { +func mkRenewer(config *Config) corev1.Container { r := config.Renewer - r.Env = append(r.Env, corev1.EnvVar { - Name: "STEP_CA_URL", - Value: config.CaUrl, + r.Env = append(r.Env, corev1.EnvVar{ + Name: "STEP_CA_URL", + Value: config.CaURL, }) return r } func addContainers(existing, new []corev1.Container, path string) (ops []PatchOperation) { - if len(existing) == 0 { - return []PatchOperation { - PatchOperation { - Op: "add", - Path: path, - Value: new, - }, - } - } else { - for _, add := range new { - ops = append(ops, PatchOperation { - Op: "add", - Path: path + "/-", - Value: add, - }) - } - return ops - } -} - -func addVolumes(existing, new []corev1.Volume, path string) (ops []PatchOperation) { if len(existing) == 0 { return []PatchOperation{ - PatchOperation { + { Op: "add", Path: path, Value: new, }, } - } else { - for _, add := range new { - ops = append(ops, PatchOperation { - Op: "add", - Path: path + "/-", - Value: add, - }) - } - return ops } + + for _, add := range new { + ops = append(ops, PatchOperation{ + Op: "add", + Path: path + "/-", + Value: add, + }) + } + return ops +} + +func addVolumes(existing, new []corev1.Volume, path string) (ops []PatchOperation) { + if len(existing) == 0 { + return []PatchOperation{ + { + Op: "add", + Path: path, + Value: new, + }, + } + } + + for _, add := range new { + ops = append(ops, PatchOperation{ + Op: "add", + Path: path + "/-", + Value: add, + }) + } + return ops } func addCertsVolumeMount(volumeName string, containers []corev1.Container) (ops []PatchOperation) { - volumeMount := corev1.VolumeMount { - Name: volumeName, + volumeMount := corev1.VolumeMount{ + Name: volumeName, MountPath: volumeMountPath, - ReadOnly: true, + ReadOnly: true, } for i, container := range containers { if len(container.VolumeMounts) == 0 { - ops = append(ops, PatchOperation { + ops = append(ops, PatchOperation{ Op: "add", Path: fmt.Sprintf("/spec/containers/%v/volumeMounts", i), Value: []corev1.VolumeMount{volumeMount}, }) } else { - ops = append(ops, PatchOperation { + ops = append(ops, PatchOperation{ Op: "add", Path: fmt.Sprintf("/spec/containers/%v/volumeMounts/-", i), Value: volumeMount, @@ -321,7 +321,7 @@ func addCertsVolumeMount(volumeName string, containers []corev1.Container) (ops func addAnnotations(existing, new map[string]string) (ops []PatchOperation) { if len(existing) == 0 { return []PatchOperation{ - PatchOperation { + { Op: "add", Path: "/metadata/annotations", Value: new, @@ -330,15 +330,15 @@ func addAnnotations(existing, new map[string]string) (ops []PatchOperation) { } for k, v := range new { if existing[k] == "" { - ops = append(ops, PatchOperation { + ops = append(ops, PatchOperation{ Op: "add", - Path: "/metadata/annotations/" + escapeJsonPath(k), + Path: "/metadata/annotations/" + escapeJSONPath(k), Value: v, }) } else { - ops = append(ops, PatchOperation { - Op: "replace", - Path: "/metadata/annotations/" + escapeJsonPath(k), + ops = append(ops, PatchOperation{ + Op: "replace", + Path: "/metadata/annotations/" + escapeJSONPath(k), Value: v, }) } @@ -355,7 +355,7 @@ func addAnnotations(existing, new map[string]string) (ops []PatchOperation) { // - Annotate the pod to indicate that it's been processed by this controller // The result is a list of serialized JSONPatch objects (or an error). func patch(pod *corev1.Pod, namespace string, config *Config, provisioner Provisioner) ([]byte, error) { - var ops[] PatchOperation + var ops []PatchOperation commonName := pod.ObjectMeta.GetAnnotations()[admissionWebhookAnnotationKey] renewer := mkRenewer(config) @@ -386,9 +386,9 @@ func shouldMutate(metadata *metav1.ObjectMeta) bool { // mutated already (status key isn't set). if annotations[admissionWebhookAnnotationKey] == "" || annotations[admissionWebhookStatusKey] == "injected" { return false - } else { - return true } + + return true } // mutate takes an `AdmissionReview`, determines whether it is subject to mutation, and returns @@ -400,27 +400,27 @@ func mutate(review *v1beta1.AdmissionReview, config *Config, provisioner Provisi var pod corev1.Pod if err := json.Unmarshal(request.Object.Raw, &pod); err != nil { ctxLog.WithField("error", err).Error("Error unmarshalling pod") - return &v1beta1.AdmissionResponse { + return &v1beta1.AdmissionResponse{ Allowed: false, UID: request.UID, - Result: &metav1.Status { + Result: &metav1.Status{ Message: err.Error(), }, } } ctxLog = ctxLog.WithFields(log.Fields{ - "kind": request.Kind, - "operation": request.Operation, - "name": pod.Name, + "kind": request.Kind, + "operation": request.Operation, + "name": pod.Name, "generateName": pod.GenerateName, - "namespace": request.Namespace, - "user": request.UserInfo, + "namespace": request.Namespace, + "user": request.UserInfo, }) if !shouldMutate(&pod.ObjectMeta) { ctxLog.WithField("annotations", pod.Annotations).Info("Skipping mutation") - return &v1beta1.AdmissionResponse { + return &v1beta1.AdmissionResponse{ Allowed: true, UID: request.UID, } @@ -429,20 +429,20 @@ func mutate(review *v1beta1.AdmissionReview, config *Config, provisioner Provisi patchBytes, err := patch(&pod, request.Namespace, config, provisioner) if err != nil { ctxLog.WithField("error", err).Error("Error generating patch") - return &v1beta1.AdmissionResponse { + return &v1beta1.AdmissionResponse{ Allowed: false, UID: request.UID, - Result: &metav1.Status { + Result: &metav1.Status{ Message: err.Error(), }, } } ctxLog.WithField("patch", string(patchBytes)).Info("Generated patch") - return &v1beta1.AdmissionResponse { - Allowed: true, - Patch: patchBytes, - UID: request.UID, + return &v1beta1.AdmissionResponse{ + Allowed: true, + Patch: patchBytes, + UID: request.UID, PatchType: func() *v1beta1.PatchType { pt := v1beta1.PatchTypeJSONPatch return &pt @@ -477,17 +477,17 @@ func main() { provisionerKid := os.Getenv("PROVISIONER_KID") log.WithFields(log.Fields{ "provisionerName": provisionerName, - "provisionerKid": provisionerKid, + "provisionerKid": provisionerKid, }).Info("Loaded provisioner configuration") - provisioner, err := NewProvisioner(provisionerName, provisionerKid, config.CaUrl, rootCAPath, provisionerPasswordFile) + provisioner, err := NewProvisioner(provisionerName, provisionerKid, config.CaURL, rootCAPath, provisionerPasswordFile) if err != nil { log.Errorf("Error loading provisioner: %v", err) os.Exit(1) } log.WithFields(log.Fields{ "name": provisioner.Name(), - "kid": provisioner.Kid(), + "kid": provisioner.Kid(), }).Info("Loaded provisioner") namespace := os.Getenv("NAMESPACE") @@ -519,10 +519,10 @@ func main() { } /* - var name string - if r.TLS != nil && len(r.TLS.PeerCertificates) > 0 { - name = r.TLS.PeerCertificates[0].Subject.CommonName - } + var name string + if r.TLS != nil && len(r.TLS.PeerCertificates) > 0 { + name = r.TLS.PeerCertificates[0].Subject.CommonName + } */ if r.URL.Path != "/mutate" { @@ -554,12 +554,12 @@ func main() { review := v1beta1.AdmissionReview{} if _, _, err := deserializer.Decode(body, nil, &review); err != nil { log.WithFields(log.Fields{ - "body": body, + "body": body, "error": err, }).Error("Can't decode body") - response = &v1beta1.AdmissionResponse { + response = &v1beta1.AdmissionResponse{ Allowed: false, - Result: &metav1.Status { + Result: &metav1.Status{ Message: err.Error(), }, } @@ -567,23 +567,23 @@ func main() { response = mutate(&review, config, provisioner) } - resp, err := json.Marshal(v1beta1.AdmissionReview { + resp, err := json.Marshal(v1beta1.AdmissionReview{ Response: response, }) if err != nil { log.WithFields(log.Fields{ - "uid": review.Request.UID, + "uid": review.Request.UID, "error": err, }).Info("Marshal error") http.Error(w, fmt.Sprintf("Marshal Error: %v", err), http.StatusInternalServerError) } else { log.WithFields(log.Fields{ - "uid": review.Request.UID, + "uid": review.Request.UID, "response": string(resp), }).Info("Returning review") if _, err := w.Write(resp); err != nil { log.WithFields(log.Fields{ - "uid": review.Request.UID, + "uid": review.Request.UID, "error": err, }).Info("Write error") } diff --git a/autocert/controller/provisioner.go b/autocert/controller/provisioner.go index eb739fe1..453b87e8 100644 --- a/autocert/controller/provisioner.go +++ b/autocert/controller/provisioner.go @@ -3,10 +3,13 @@ package main import ( "encoding/json" "fmt" + "path/filepath" "time" "github.com/pkg/errors" - "github.com/smallstep/cli/crypto/pki" + "github.com/smallstep/certificates/authority" + "github.com/smallstep/certificates/ca" + "github.com/smallstep/cli/config" "github.com/smallstep/cli/crypto/randutil" "github.com/smallstep/cli/jose" "github.com/smallstep/cli/token" @@ -17,6 +20,8 @@ const ( tokenLifetime = 5 * time.Minute ) +// Provisioner is an authorized entity that can sign tokens necessary for +// signature requests. type Provisioner interface { Name() string Kid() string @@ -26,7 +31,7 @@ type Provisioner interface { type provisioner struct { name string kid string - caUrl string + caURL string caRoot string jwk *jose.JSONWebKey tokenLifetime time.Duration @@ -52,13 +57,13 @@ func (p *provisioner) Token(subject string) (string, error) { notBefore := time.Now() notAfter := notBefore.Add(tokenLifetime) - signUrl := fmt.Sprintf("%v/1.0/sign", p.caUrl) + signURL := fmt.Sprintf("%v/1.0/sign", p.caURL) tokOptions := []token.Options{ token.WithJWTID(jwtID), token.WithKid(p.kid), token.WithIssuer(p.name), - token.WithAudience(signUrl), + token.WithAudience(signURL), token.WithValidity(notBefore, notAfter), token.WithRootCA(p.caRoot), } @@ -86,20 +91,20 @@ func decryptProvisionerJWK(encryptedKey, passFile string) (*jose.JSONWebKey, err // loadProvisionerJWKByKid retrieves a provisioner key from the CA by key ID and // decrypts it using the specified password file. -func loadProvisionerJWKByKid(kid, caUrl, caRoot, passFile string) (*jose.JSONWebKey, error) { - encrypted, err := pki.GetProvisionerKey(caUrl, caRoot, kid) +func loadProvisionerJWKByKid(kid, caURL, caRoot, passFile string) (*jose.JSONWebKey, error) { + encrypted, err := getProvisionerKey(caURL, caRoot, kid) if err != nil { return nil, err } - + return decryptProvisionerJWK(encrypted, passFile) } // loadProvisionerJWKByName retrieves the list of provisioners and encrypted key then // returns the key of the first provisioner with a matching name that can be successfully // decrypted with the specified password file. -func loadProvisionerJWKByName(name, caUrl, caRoot, passFile string) (key *jose.JSONWebKey, err error) { - provisioners, err := pki.GetProvisioners(caUrl, caRoot) +func loadProvisionerJWKByName(name, caURL, caRoot, passFile string) (key *jose.JSONWebKey, err error) { + provisioners, err := getProvisioners(caURL, caRoot) if err != nil { err = errors.Wrap(err, "error getting the provisioners") return @@ -113,20 +118,20 @@ func loadProvisionerJWKByName(name, caUrl, caRoot, passFile string) (key *jose.J } } } - return nil, errors.New(fmt.Sprintf("provisioner '%s' not found (or your password is wrong)", name)) + return nil, errors.Errorf("provisioner '%s' not found (or your password is wrong)", name) } // NewProvisioner loads and decrypts key material from the CA for the named // provisioner. The key identified by `kid` will be used if specified. If `kid` // is the empty string we'll use the first key for the named provisioner that // decrypts using `passFile`. -func NewProvisioner(name, kid, caUrl, caRoot, passFile string) (Provisioner, error) { +func NewProvisioner(name, kid, caURL, caRoot, passFile string) (Provisioner, error) { var jwk *jose.JSONWebKey var err error if kid != "" { - jwk, err = loadProvisionerJWKByKid(kid, caUrl, caRoot, passFile) + jwk, err = loadProvisionerJWKByKid(kid, caURL, caRoot, passFile) } else { - jwk, err = loadProvisionerJWKByName(name, caUrl, caRoot, passFile) + jwk, err = loadProvisionerJWKByName(name, caURL, caRoot, passFile) } if err != nil { return nil, err @@ -135,9 +140,56 @@ func NewProvisioner(name, kid, caUrl, caRoot, passFile string) (Provisioner, err return &provisioner{ name: name, kid: jwk.KeyID, - caUrl: caUrl, + caURL: caURL, caRoot: caRoot, jwk: jwk, tokenLifetime: tokenLifetime, }, nil -} \ No newline at end of file +} + +// getRootCAPath returns the path where the root CA is stored based on the +// STEPPATH environment variable. +func getRootCAPath() string { + return filepath.Join(config.StepPath(), "certs", "root_ca.crt") +} + +// getProvisioners returns the map of provisioners on the given CA. +func getProvisioners(caURL, rootFile string) ([]*authority.Provisioner, error) { + if len(rootFile) == 0 { + rootFile = getRootCAPath() + } + client, err := ca.NewClient(caURL, ca.WithRootFile(rootFile)) + if err != nil { + return nil, err + } + cursor := "" + provisioners := []*authority.Provisioner{} + for { + resp, err := client.Provisioners(ca.WithProvisionerCursor(cursor), ca.WithProvisionerLimit(100)) + if err != nil { + return nil, err + } + provisioners = append(provisioners, resp.Provisioners...) + if resp.NextCursor == "" { + return provisioners, nil + } + cursor = resp.NextCursor + } +} + +// getProvisionerKey returns the encrypted provisioner key with the for the +// given kid. +func getProvisionerKey(caURL, rootFile, kid string) (string, error) { + if len(rootFile) == 0 { + rootFile = getRootCAPath() + } + client, err := ca.NewClient(caURL, ca.WithRootFile(rootFile)) + if err != nil { + return "", err + } + resp, err := client.ProvisionerKey(kid) + if err != nil { + return "", err + } + return resp.Key, nil +} diff --git a/autocert/examples/hello-mtls/go/Dockerfile.client b/autocert/examples/hello-mtls/go/client/Dockerfile.client similarity index 100% rename from autocert/examples/hello-mtls/go/Dockerfile.client rename to autocert/examples/hello-mtls/go/client/Dockerfile.client diff --git a/autocert/examples/hello-mtls/go/client.go b/autocert/examples/hello-mtls/go/client/client.go similarity index 100% rename from autocert/examples/hello-mtls/go/client.go rename to autocert/examples/hello-mtls/go/client/client.go diff --git a/autocert/examples/hello-mtls/go/hello-mtls.client.yaml b/autocert/examples/hello-mtls/go/client/hello-mtls.client.yaml similarity index 100% rename from autocert/examples/hello-mtls/go/hello-mtls.client.yaml rename to autocert/examples/hello-mtls/go/client/hello-mtls.client.yaml diff --git a/autocert/examples/hello-mtls/go/Dockerfile.server b/autocert/examples/hello-mtls/go/server/Dockerfile.server similarity index 100% rename from autocert/examples/hello-mtls/go/Dockerfile.server rename to autocert/examples/hello-mtls/go/server/Dockerfile.server diff --git a/autocert/examples/hello-mtls/go/hello-mtls.server.yaml b/autocert/examples/hello-mtls/go/server/hello-mtls.server.yaml similarity index 100% rename from autocert/examples/hello-mtls/go/hello-mtls.server.yaml rename to autocert/examples/hello-mtls/go/server/hello-mtls.server.yaml diff --git a/autocert/examples/hello-mtls/go/server.go b/autocert/examples/hello-mtls/go/server/server.go similarity index 96% rename from autocert/examples/hello-mtls/go/server.go rename to autocert/examples/hello-mtls/go/server/server.go index 6c10a3e3..9449b5f1 100644 --- a/autocert/examples/hello-mtls/go/server.go +++ b/autocert/examples/hello-mtls/go/server/server.go @@ -44,7 +44,7 @@ func (r *rotator) loadCertificate(certFile, keyFile string) error { } r.certificate = &c - + return nil } @@ -87,8 +87,8 @@ func main() { ClientCAs: roots, MinVersion: tls.VersionTLS12, CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256}, - PreferServerCipherSuites: true, - CipherSuites: []uint16{ + PreferServerCipherSuites: true, + CipherSuites: []uint16{ tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, }, @@ -119,7 +119,7 @@ func main() { if err != nil { log.Println("Error loading certificate and key", err) } - case <- done: + case <-done: return } } @@ -127,7 +127,7 @@ func main() { defer close(done) log.Println("Listening no :443") - + // Start serving HTTPS err = srv.ListenAndServeTLS("", "") if err != nil {