Merge branch 'master' into step-sds

This commit is contained in:
Mariano Cano 2019-04-10 13:34:38 -07:00
commit f1cd493ae9
22 changed files with 917 additions and 293 deletions

11
Gopkg.lock generated
View file

@ -277,7 +277,7 @@
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:8b36444f30009b5e124a3ac48b353558024a95c3fccdf3e6bb557a091e67342b" digest = "1:253eec7c89c6fe08ae020877b0b44343e86da68dac99207280e7ddcacd441f1f"
name = "github.com/smallstep/cli" name = "github.com/smallstep/cli"
packages = [ packages = [
"command", "command",
@ -298,7 +298,7 @@
"utils", "utils",
] ]
pruneopts = "UT" pruneopts = "UT"
revision = "3e1e2dcfa54298e0fb86e0be86ab36d79f36473e" revision = "f851b6b63d8d5e78b8a986057034d69fe904c477"
[[projects]] [[projects]]
branch = "master" branch = "master"
@ -460,7 +460,8 @@
version = "v0.9.1" version = "v0.9.1"
[[projects]] [[projects]]
digest = "1:7fbe10f3790dc4e6296c7c844c5a9b35513e5521c29c47e10ba99cd2956a2719" branch = "v2"
digest = "1:9593bab40e981b1f90b7e07faeab0d09b75fe338880d08880f986a9d3283c53f"
name = "gopkg.in/square/go-jose.v2" name = "gopkg.in/square/go-jose.v2"
packages = [ packages = [
".", ".",
@ -469,8 +470,8 @@
"jwt", "jwt",
] ]
pruneopts = "UT" pruneopts = "UT"
revision = "ef984e69dd356202fd4e4910d4d9c24468bdf0b8" revision = "fd0b35a2f1ec103c6bb76cc6d4b8077fa5844fb2"
version = "v2.1.9" source = "github.com/maraino/go-jose"
[[projects]] [[projects]]
digest = "1:342378ac4dcb378a5448dd723f0784ae519383532f5e70ade24132c4c8693202" digest = "1:342378ac4dcb378a5448dd723f0784ae519383532f5e70ade24132c4c8693202"

View file

@ -62,4 +62,7 @@ required = [
[[constraint]] [[constraint]]
name = "gopkg.in/square/go-jose.v2" name = "gopkg.in/square/go-jose.v2"
version = "2.1.9" # version = "2.3.0"
# Using special branch with ed25519 fix
source = "github.com/maraino/go-jose"
branch = "v2"

View file

@ -53,10 +53,15 @@ VERSION ?= $(shell [ -d .git ] && git describe --tags --always --dirty="-dev")
# .VERSION contains a slug populated by `git archive`. # .VERSION contains a slug populated by `git archive`.
VERSION := $(or $(VERSION),$(shell ./.version.sh .VERSION)) VERSION := $(or $(VERSION),$(shell ./.version.sh .VERSION))
VERSION := $(shell echo $(VERSION) | sed 's/^v//') VERSION := $(shell echo $(VERSION) | sed 's/^v//')
NOT_RC := $(shell echo $(VERSION) | grep -v -e -rc)
# If TRAVIS_TAG is set then we know this ref has been tagged. # If TRAVIS_TAG is set then we know this ref has been tagged.
ifdef TRAVIS_TAG ifdef TRAVIS_TAG
ifeq ($(NOT_RC),)
PUSHTYPE=release-candidate
else
PUSHTYPE=release PUSHTYPE=release
endif
else else
PUSHTYPE=master PUSHTYPE=master
endif endif
@ -214,24 +219,30 @@ docker-tag:
docker-push-tag: docker-tag docker-push-tag: docker-tag
$(call DOCKER_PUSH,step-ca,$(VERSION)) $(call DOCKER_PUSH,step-ca,$(VERSION))
docker-push-tag-latest:
$(call DOCKER_PUSH,step-ca,latest)
# Rely on DOCKER_USERNAME and DOCKER_PASSWORD being set inside the CI or # Rely on DOCKER_USERNAME and DOCKER_PASSWORD being set inside the CI or
# equivalent environment # equivalent environment
docker-login: docker-login:
$Q docker login -u="$(DOCKER_USERNAME)" -p="$(DOCKER_PASSWORD)" $Q docker login -u="$(DOCKER_USERNAME)" -p="$(DOCKER_PASSWORD)"
.PHONY: docker-login docker-tag docker-push-tag .PHONY: docker-login docker-tag docker-push-tag docker-push-tag-latest
################################################# #################################################
# Targets for pushing the docker images # Targets for pushing the docker images
################################################# #################################################
# For all builds on the master branch, we actually build the container # For all builds we build the docker container
docker-master: docker docker-master: docker
# For all builds on the master branch with an rc tag # For all builds with a release candidate tag
docker-release: docker-master docker-login docker-push-tag docker-release-candidate: docker-master docker-login docker-push-tag
.PHONY: docker-master docker-release # For all builds with a release tag
docker-release: docker-release-candidate docker-push-tag-latest
.PHONY: docker-master docker-release-candidate docker-release
######################################### #########################################
# Debian # Debian
@ -301,7 +312,7 @@ artifacts-darwin-tag: bundle-darwin
artifacts-archive-tag: artifacts-archive-tag:
$Q mkdir -p $(RELEASE) $Q mkdir -p $(RELEASE)
$Q git archive v$(VERSION) | gzip > $(RELEASE)/step-certificates.tar.gz $Q git archive v$(VERSION) | gzip > $(RELEASE)/step-certificates_$(VERSION).tar.gz
artifacts-tag: artifacts-linux-tag artifacts-darwin-tag artifacts-archive-tag artifacts-tag: artifacts-linux-tag artifacts-darwin-tag artifacts-archive-tag
@ -323,4 +334,4 @@ artifacts-release: artifacts-tag
# This command is called by travis directly *after* a successful build # This command is called by travis directly *after* a successful build
artifacts: artifacts-$(PUSHTYPE) docker-$(PUSHTYPE) artifacts: artifacts-$(PUSHTYPE) docker-$(PUSHTYPE)
.PHONY: artifacts-master artifacts-release artifacts .PHONY: artifacts-master artifacts-release-candidate artifacts-release artifacts

View file

@ -47,7 +47,7 @@ func (a *Authority) Authorize(ott string) ([]provisioner.SignOption, error) {
// Do not accept tokens issued before the start of the ca. // Do not accept tokens issued before the start of the ca.
// This check is meant as a stopgap solution to the current lack of a persistence layer. // This check is meant as a stopgap solution to the current lack of a persistence layer.
if a.config.AuthorityConfig != nil && !a.config.AuthorityConfig.DisableIssuedAtCheck { if a.config.AuthorityConfig != nil && !a.config.AuthorityConfig.DisableIssuedAtCheck {
if claims.IssuedAt > 0 && claims.IssuedAt.Time().Before(a.startTime) { if claims.IssuedAt != nil && claims.IssuedAt.Time().Before(a.startTime) {
return nil, &apiError{errors.New("authorize: token issued before the bootstrap of certificate authority"), return nil, &apiError{errors.New("authorize: token issued before the bootstrap of certificate authority"),
http.StatusUnauthorized, errContext} http.StatusUnauthorized, errContext}
} }

View file

@ -174,3 +174,7 @@ $ kubectl get mutatingwebhookconfiguration
NAME CREATED AT NAME CREATED AT
autocert-webhook-config 2019-01-17T22:57:57Z autocert-webhook-config 2019-01-17T22:57:57Z
``` ```
### Move on to usage instructions
Make sure to follow the autocert usage steps at https://github.com/smallstep/certificates/tree/master/autocert#usage

View file

@ -103,7 +103,7 @@ default Active 59m enabled
To get a certificate you need to tell `autocert` your workload's name using the `autocert.step.sm/name` annotation (this name will appear as the X.509 common name and SAN). To get a certificate you need to tell `autocert` your workload's name using the `autocert.step.sm/name` annotation (this name will appear as the X.509 common name and SAN).
Let's deploy a [simple mTLS server](examples/hello-mtls/go/server.go) named `hello-mtls.default.svc.cluster.local`: Let's deploy a [simple mTLS server](examples/hello-mtls/go/server/server.go) named `hello-mtls.default.svc.cluster.local`:
```yaml ```yaml
cat <<EOF | kubectl apply -f - cat <<EOF | kubectl apply -f -
@ -180,7 +180,7 @@ EOF
> Note that **the authority portion of the URL** (the `HELLO_MTLS_URL` env var) **matches the name of the server we're connecting to** (both are `hello-mtls.default.svc.cluster.local`). That's required for standard HTTPS and can sometimes require some DNS trickery. > Note that **the authority portion of the URL** (the `HELLO_MTLS_URL` env var) **matches the name of the server we're connecting to** (both are `hello-mtls.default.svc.cluster.local`). That's required for standard HTTPS and can sometimes require some DNS trickery.
Once deployed we should start seeing the client log responses from the server [saying hello](examples/hello-mtls/go/server.go#L71-L72): Once deployed we should start seeing the client log responses from the server [saying hello](examples/hello-mtls/go/server/server.go#L71-L72):
``` ```
$ export HELLO_MTLS_CLIENT=$(kubectl get pods -l app=hello-mtls-client -o jsonpath={$.items[0].metadata.name}) $ export HELLO_MTLS_CLIENT=$(kubectl get pods -l app=hello-mtls-client -o jsonpath={$.items[0].metadata.name})

View file

@ -1,4 +1,4 @@
FROM smallstep/step-cli:0.8.3 FROM smallstep/step-cli:0.9.0
USER root USER root
ENV CRT="/var/run/autocert.step.sm/site.crt" ENV CRT="/var/run/autocert.step.sm/site.crt"

View file

@ -11,7 +11,7 @@ COPY . ./
RUN go build -o /server . RUN go build -o /server .
# final stage # final stage
FROM smallstep/step-cli:0.8.3 FROM smallstep/step-cli:0.9.0
ENV STEPPATH="/home/step/.step" ENV STEPPATH="/home/step/.step"
ENV PWDPATH="/home/step/password/password" ENV PWDPATH="/home/step/password/password"
ENV CONFIGPATH="/home/step/autocert/config.yaml" ENV CONFIGPATH="/home/step/autocert/config.yaml"

514
autocert/controller/Gopkg.lock generated Normal file
View file

@ -0,0 +1,514 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
digest = "1:9afc639ef88d907f2e87ab68cbc63117b88d0d84238fd6b08224515d00a8136a"
name = "github.com/alecthomas/gometalinter"
packages = ["."]
pruneopts = "UT"
revision = "df395bfa67c5d0630d936c0044cf07ff05086655"
version = "v3.0.0"
[[projects]]
branch = "master"
digest = "1:c198fdc381e898e8fb62b8eb62758195091c313ad18e52a3067366e1dda2fb3c"
name = "github.com/alecthomas/units"
packages = ["."]
pruneopts = "UT"
revision = "2efee857e7cfd4f3d0138cc3cbb1b4966962b93a"
[[projects]]
branch = "master"
digest = "1:454adc7f974228ff789428b6dc098638c57a64aa0718f0bd61e53d3cd39d7a75"
name = "github.com/chzyer/readline"
packages = ["."]
pruneopts = "UT"
revision = "2972be24d48e78746da79ba8e24e8b488c9880de"
[[projects]]
digest = "1:848ef40f818e59905140552cc49ff3dc1a15f955e4b56d1c5c2cc4b54dbadf0c"
name = "github.com/client9/misspell"
packages = [
".",
"cmd/misspell",
]
pruneopts = "UT"
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:41cf598af650689375f647ed7ed87951e3aedb8d5f40400b6e81a84410650626"
name = "github.com/go-chi/chi"
packages = ["."]
pruneopts = "UT"
revision = "d0891661345200ebf8b816cc7de785e9bd570647"
[[projects]]
digest = "1:b7a8552c62868d867795b63eaf4f45d3e92d36db82b428e680b9c95a8c33e5b1"
name = "github.com/gogo/protobuf"
packages = [
"proto",
"sortkeys",
]
pruneopts = "UT"
revision = "342cbe0a04158f6dcb03ca0079991a51a4248c02"
version = "v0.5"
[[projects]]
branch = "travis-1.9"
digest = "1:e8f5d9c09a7209c740e769713376abda388c41b777ba8e9ed52767e21acf379f"
name = "github.com/golang/lint"
packages = [
".",
"golint",
]
pruneopts = "UT"
revision = "883fe33ffc4344bad1ecd881f61afd5ec5d80e0a"
[[projects]]
branch = "master"
digest = "1:3ee90c0d94da31b442dde97c99635aaafec68d0b8a3c12ee2075c6bdabeec6bb"
name = "github.com/google/gofuzz"
packages = ["."]
pruneopts = "UT"
revision = "24818f796faf91cd76ec7bddd72458fbced7a6c1"
[[projects]]
digest = "1:750e747d0aad97b79f4a4e00034bae415c2ea793fd9e61438d966ee9c79579bf"
name = "github.com/google/shlex"
packages = ["."]
pruneopts = "UT"
revision = "6f45313302b9c56850fc17f99e40caebce98c716"
[[projects]]
branch = "master"
digest = "1:824d147914b40e56e9e1eebd602bc6bb9761989d52fd8e4a498428467980eb17"
name = "github.com/gordonklaus/ineffassign"
packages = ["."]
pruneopts = "UT"
revision = "1003c8bd00dc2869cb5ca5282e6ce33834fed514"
[[projects]]
digest = "1:eaefc85d32c03e5f0c2b88ea2f79fce3d993e2c78316d21319575dd4ea9153ca"
name = "github.com/json-iterator/go"
packages = ["."]
pruneopts = "UT"
revision = "ab8a2e0c74be9d3be70b3184d9acc634935ded82"
version = "1.1.4"
[[projects]]
branch = "master"
digest = "1:e51f40f0c19b39c1825eadd07d5c0a98a2ad5942b166d9fc4f54750ce9a04810"
name = "github.com/juju/ansiterm"
packages = [
".",
"tabwriter",
]
pruneopts = "UT"
revision = "720a0952cc2ac777afc295d9861263e2a4cf96a1"
[[projects]]
digest = "1:31e761d97c76151dde79e9d28964a812c46efc5baee4085b86f68f0c654450de"
name = "github.com/konsorten/go-windows-terminal-sequences"
packages = ["."]
pruneopts = "UT"
revision = "f55edac94c9bbba5d6182a4be46d86a2c9b5b50e"
version = "v1.0.2"
[[projects]]
digest = "1:2dc8db55c5b223e6cd50aa7915e698cfcf56d1ddfa89bbbf65e24729e0a0200a"
name = "github.com/lunixbochs/vtclean"
packages = ["."]
pruneopts = "UT"
revision = "88cfb0c2efe8ed7b0ccf0af83db39359829027bb"
version = "v1.0.0"
[[projects]]
digest = "1:2a2a76072bd413b3484a0b5bb2fbb078b0b7dd8950e9276c900e14dce2354679"
name = "github.com/manifoldco/promptui"
packages = [
".",
"list",
"screenbuf",
]
pruneopts = "UT"
revision = "20f2a94120aa14a334121a6de66616a7fa89a5cd"
version = "v0.3.2"
[[projects]]
digest = "1:2fa7b0155cd54479a755c629de26f888a918e13f8857a2c442205d825368e084"
name = "github.com/mattn/go-colorable"
packages = ["."]
pruneopts = "UT"
revision = "3a70a971f94a22f2fa562ffcc7a0eb45f5daf045"
version = "v0.1.1"
[[projects]]
digest = "1:e150b5fafbd7607e2d638e4e5cf43aa4100124e5593385147b0a74e2733d8b0d"
name = "github.com/mattn/go-isatty"
packages = ["."]
pruneopts = "UT"
revision = "c2a7a6ca930a4cd0bc33a3f298eb71960732a3a7"
version = "v0.0.7"
[[projects]]
digest = "1:33422d238f147d247752996a26574ac48dcf472976eda7f5134015f06bf16563"
name = "github.com/modern-go/concurrent"
packages = ["."]
pruneopts = "UT"
revision = "bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94"
version = "1.0.3"
[[projects]]
digest = "1:c56ad36f5722eb07926c979d5e80676ee007a9e39e7808577b9d87ec92b00460"
name = "github.com/modern-go/reflect2"
packages = ["."]
pruneopts = "UT"
revision = "94122c33edd36123c84d5368cfb2b69df93a0ec8"
version = "v1.0.1"
[[projects]]
digest = "1:266d082179f3a29a4bdcf1dcc49d4a304f5c7107e65bd22d1fecacf45f1ac348"
name = "github.com/newrelic/go-agent"
packages = [
".",
"internal",
"internal/cat",
"internal/jsonx",
"internal/logger",
"internal/sysinfo",
"internal/utilization",
]
pruneopts = "UT"
revision = "f5bce3387232559bcbe6a5f8227c4bf508dac1ba"
version = "v1.11.0"
[[projects]]
digest = "1:07140002dbf37da92090f731b46fa47be4820b82fe5c14a035203b0e813d0ec2"
name = "github.com/nicksnyder/go-i18n"
packages = [
"i18n",
"i18n/bundle",
"i18n/language",
"i18n/translation",
]
pruneopts = "UT"
revision = "0dc1626d56435e9d605a29875701721c54bc9bbd"
version = "v1.10.0"
[[projects]]
digest = "1:95741de3af260a92cc5c7f3f3061e85273f5a81b5db20d4bd68da74bd521675e"
name = "github.com/pelletier/go-toml"
packages = ["."]
pruneopts = "UT"
revision = "c01d1270ff3e442a8a57cddc1c92dc1138598194"
version = "v1.2.0"
[[projects]]
digest = "1:cf31692c14422fa27c83a05292eb5cbe0fb2775972e8f1f8446a71549bd8980b"
name = "github.com/pkg/errors"
packages = ["."]
pruneopts = "UT"
revision = "ba968bfe8b2f7e042a574c888954fccecfa385b4"
version = "v0.8.1"
[[projects]]
digest = "1:2e76a73cb51f42d63a2a1a85b3dc5731fd4faf6821b434bd0ef2c099186031d6"
name = "github.com/rs/xid"
packages = ["."]
pruneopts = "UT"
revision = "15d26544def341f036c5f8dca987a4cbe575032c"
version = "v1.2.1"
[[projects]]
branch = "master"
digest = "1:8baa3b16f20963c54e296627ea1dabfd79d1b486f81baf8759e99d73bddf2687"
name = "github.com/samfoo/ansi"
packages = ["."]
pruneopts = "UT"
revision = "b6bd2ded7189ce35bc02233b554eb56a5146af73"
[[projects]]
digest = "1:9421f6e9e28ef86933e824b5caff441366f2b69bb281085b9dca40e1f27a1602"
name = "github.com/shurcooL/sanitized_anchor_name"
packages = ["."]
pruneopts = "UT"
revision = "7bfe4c7ecddb3666a94b053b422cdd8f5aaa3615"
version = "v1.0.0"
[[projects]]
digest = "1:e4c72127d910a96daf869a44f3dd563b86dbe6931a172863a0e99c5ff04b59e4"
name = "github.com/sirupsen/logrus"
packages = ["."]
pruneopts = "UT"
revision = "dae0fa8d5b0c810a8ab733fbd5510c7cae84eca4"
version = "v1.4.0"
[[projects]]
digest = "1:b66ff4abd77f39e94020219427c0c62bc1474d41edb2b445d2058adede69475f"
name = "github.com/smallstep/certificates"
packages = [
"api",
"authority",
"authority/provisioner",
"ca",
"logging",
"monitoring",
"server",
]
pruneopts = "UT"
revision = "1bb25b517115cebfb8ce9e5ecb48fc7bbea055ec"
version = "v0.9.0"
[[projects]]
digest = "1:8b36444f30009b5e124a3ac48b353558024a95c3fccdf3e6bb557a091e67342b"
name = "github.com/smallstep/cli"
packages = [
"command",
"config",
"crypto/keys",
"crypto/pemutil",
"crypto/randutil",
"crypto/tlsutil",
"crypto/x509util",
"errs",
"jose",
"pkg/blackfriday",
"pkg/x509",
"token",
"token/provision",
"ui",
"usage",
"utils",
]
pruneopts = "UT"
revision = "b8972dd3035caefb9f493c4a2c664cc6cf557f93"
version = "v0.9.0"
[[projects]]
branch = "master"
digest = "1:ba52e5a5fb800ce55108b7a5f181bb809aab71c16736051312b0aa969f82ad39"
name = "github.com/tsenart/deadcode"
packages = ["."]
pruneopts = "UT"
revision = "210d2dc333e90c7e3eedf4f2242507a8e83ed4ab"
[[projects]]
branch = "master"
digest = "1:8286f653bf8b8fd155a0c9c3b9ee3dbc2a95b1b51f7a1dc11fe2c66018454a0c"
name = "github.com/urfave/cli"
packages = ["."]
pruneopts = "UT"
revision = "693af58b4d51b8fcc7f9d89576da170765980581"
[[projects]]
branch = "master"
digest = "1:8cee4cbf1682070ab96818200f7f43b78850d68ada71698b551cb6c351420016"
name = "golang.org/x/crypto"
packages = [
"cryptobyte",
"cryptobyte/asn1",
"ed25519",
"ed25519/internal/edwards25519",
"pbkdf2",
"ssh/terminal",
]
pruneopts = "UT"
revision = "a5d413f7728c81fb97d96a2b722368945f651e78"
[[projects]]
branch = "master"
digest = "1:4ebfd72ba817efd42cb4be720c20c200d5a2f3694e8a3bd243b95d558fc5f423"
name = "golang.org/x/net"
packages = [
"html",
"html/atom",
"http/httpguts",
"http2",
"http2/hpack",
"idna",
]
pruneopts = "UT"
revision = "63eda1eb0650888965ead1296efd04d0b2b61128"
[[projects]]
branch = "master"
digest = "1:6b3e6ddcebac95be1d690dbd53b5aa2e520715becb7e521bb526ccf3b4c53c15"
name = "golang.org/x/sys"
packages = [
"unix",
"windows",
]
pruneopts = "UT"
revision = "f49334f85ddcf0f08d7fb6dd7363e9e6d6b777eb"
[[projects]]
digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18"
name = "golang.org/x/text"
packages = [
"collate",
"collate/build",
"internal/colltab",
"internal/gen",
"internal/tag",
"internal/triegen",
"internal/ucd",
"language",
"secure/bidirule",
"transform",
"unicode/bidi",
"unicode/cldr",
"unicode/norm",
"unicode/rangetable",
]
pruneopts = "UT"
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
version = "v0.3.0"
[[projects]]
branch = "master"
digest = "1:a45ec3bb7c73e52430410dff3e0a5534ce518f72a8eb4355bc8502c546b91ecc"
name = "golang.org/x/tools"
packages = [
"go/ast/astutil",
"go/gcexportdata",
"go/internal/gcimporter",
"go/types/typeutil",
]
pruneopts = "UT"
revision = "8f05a32dce9ff80c9bf11baeb89d26b410f39281"
[[projects]]
branch = "v3-unstable"
digest = "1:39efb07a0d773dc09785b237ada4e10b5f28646eb6505d97bc18f8d2ff439362"
name = "gopkg.in/alecthomas/kingpin.v3-unstable"
packages = ["."]
pruneopts = "UT"
revision = "63abe20a23e29e80bbef8089bd3dee3ac25e5306"
[[projects]]
digest = "1:ef72505cf098abdd34efeea032103377bec06abb61d8a06f002d5d296a4b1185"
name = "gopkg.in/inf.v0"
packages = ["."]
pruneopts = "UT"
revision = "3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4"
version = "v0.9.0"
[[projects]]
digest = "1:7fbe10f3790dc4e6296c7c844c5a9b35513e5521c29c47e10ba99cd2956a2719"
name = "gopkg.in/square/go-jose.v2"
packages = [
".",
"cipher",
"json",
"jwt",
]
pruneopts = "UT"
revision = "ef984e69dd356202fd4e4910d4d9c24468bdf0b8"
version = "v2.1.9"
[[projects]]
digest = "1:4d2e5a73dc1500038e504a8d78b986630e3626dc027bc030ba5c75da257cdb96"
name = "gopkg.in/yaml.v2"
packages = ["."]
pruneopts = "UT"
revision = "51d6538a90f86fe93ac480b35f37b2be17fef232"
version = "v2.2.2"
[[projects]]
branch = "master"
digest = "1:e0db54f895460bc12675b12b1c2f8931447051736f370f541214236a84d08023"
name = "k8s.io/api"
packages = [
"admission/v1beta1",
"authentication/v1",
"core/v1",
]
pruneopts = "UT"
revision = "92d2ee7fc726fd16632fbd2dff1466f551f5b8b4"
[[projects]]
branch = "master"
digest = "1:8d51d10f66dbcd39c53f17dc65b9113ca623a72ff99c5e52dfad908b49c07f18"
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 = "4ceb6b6c5db56a2f8f454dd837c07160a3d6e131"
[[projects]]
digest = "1:69367163a23cd68971724f36a6759a01d50968e58936808b7eb5e5c186a3a382"
name = "k8s.io/klog"
packages = ["."]
pruneopts = "UT"
revision = "8e90cee79f823779174776412c13478955131846"
[[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/ghodss/yaml",
"github.com/pkg/errors",
"github.com/sirupsen/logrus",
"github.com/smallstep/certificates/authority/provisioner",
"github.com/smallstep/certificates/ca",
"github.com/smallstep/cli/config",
"github.com/smallstep/cli/crypto/pemutil",
"github.com/smallstep/cli/crypto/randutil",
"github.com/smallstep/cli/jose",
"github.com/smallstep/cli/token",
"github.com/smallstep/cli/token/provision",
"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

View file

@ -0,0 +1,62 @@
# Gopkg.toml example
#
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
#
# [prune]
# non-go = false
# go-tests = true
# unused-packages = true
[[constraint]]
name = "github.com/ghodss/yaml"
version = "1.0.0"
[[constraint]]
name = "github.com/pkg/errors"
version = "0.8.1"
[[constraint]]
name = "github.com/sirupsen/logrus"
version = "1.4.0"
[[constraint]]
name = "github.com/smallstep/certificates"
version = "0.9.0"
[[constraint]]
name = "github.com/smallstep/cli"
version = "0.9.0"
[[constraint]]
branch = "master"
name = "k8s.io/api"
[[constraint]]
branch = "master"
name = "k8s.io/apimachinery"
[[override]]
name = "gopkg.in/square/go-jose.v2"
version = "=2.1.9"
[prune]
go-tests = true
unused-packages = true

View file

@ -51,6 +51,18 @@ type Config struct {
Bootstrapper corev1.Container `yaml:"bootstrapper"` Bootstrapper corev1.Container `yaml:"bootstrapper"`
Renewer corev1.Container `yaml:"renewer"` Renewer corev1.Container `yaml:"renewer"`
CertsVolume corev1.Volume `yaml:"certsVolume"` CertsVolume corev1.Volume `yaml:"certsVolume"`
RestrictCertificatesToNamespace bool `yaml:"restrictCertificatesToNamespace"`
ClusterDomain string `yaml:"clusterDomain"`
}
// GetClusterDomain returns the Kubernetes cluster domain, defaults to
// "cluster.local" if not specified in the configuration.
func (c Config) GetClusterDomain() string {
if c.ClusterDomain != "" {
return c.ClusterDomain
}
return "cluster.local"
} }
// PatchOperation represents a RFC6902 JSONPatch Operation // PatchOperation represents a RFC6902 JSONPatch Operation
@ -216,6 +228,7 @@ func mkBootstrapper(config *Config, commonName string, namespace string, provisi
Name: "COMMON_NAME", Name: "COMMON_NAME",
Value: commonName, Value: commonName,
}) })
b.Env = append(b.Env, corev1.EnvVar{ b.Env = append(b.Env, corev1.EnvVar{
Name: "STEP_TOKEN", Name: "STEP_TOKEN",
ValueFrom: &corev1.EnvVarSource{ ValueFrom: &corev1.EnvVarSource{
@ -357,7 +370,8 @@ func addAnnotations(existing, new map[string]string) (ops []PatchOperation) {
func patch(pod *corev1.Pod, namespace string, config *Config, provisioner Provisioner) ([]byte, 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] annotations := pod.ObjectMeta.GetAnnotations()
commonName := annotations[admissionWebhookAnnotationKey]
renewer := mkRenewer(config) renewer := mkRenewer(config)
bootstrapper, err := mkBootstrapper(config, commonName, namespace, provisioner) bootstrapper, err := mkBootstrapper(config, commonName, namespace, provisioner)
if err != nil { if err != nil {
@ -376,7 +390,10 @@ func patch(pod *corev1.Pod, namespace string, config *Config, provisioner Provis
// shouldMutate checks whether a pod is subject to mutation by this admission controller. A pod // shouldMutate checks whether a pod is subject to mutation by this admission controller. A pod
// is subject to mutation if it's annotated with the `admissionWebhookAnnotationKey` and if it // is subject to mutation if it's annotated with the `admissionWebhookAnnotationKey` and if it
// has not already been processed (indicated by `admissionWebhookStatusKey` set to `injected`). // has not already been processed (indicated by `admissionWebhookStatusKey` set to `injected`).
func shouldMutate(metadata *metav1.ObjectMeta) bool { // If the pod requests a certificate with a subject matching a namespace other than its own
// and restrictToNamespace is true, then shouldMutate will return a validation error
// that should be returned to the client.
func shouldMutate(metadata *metav1.ObjectMeta, namespace string, clusterDomain string, restrictToNamespace bool) (bool, error) {
annotations := metadata.GetAnnotations() annotations := metadata.GetAnnotations()
if annotations == nil { if annotations == nil {
annotations = map[string]string{} annotations = map[string]string{}
@ -385,10 +402,26 @@ func shouldMutate(metadata *metav1.ObjectMeta) bool {
// Only mutate if the object is annotated appropriately (annotation key set) and we haven't // Only mutate if the object is annotated appropriately (annotation key set) and we haven't
// mutated already (status key isn't set). // mutated already (status key isn't set).
if annotations[admissionWebhookAnnotationKey] == "" || annotations[admissionWebhookStatusKey] == "injected" { if annotations[admissionWebhookAnnotationKey] == "" || annotations[admissionWebhookStatusKey] == "injected" {
return false return false, nil
} }
return true if !restrictToNamespace {
return true, nil
}
subject := strings.Trim(annotations[admissionWebhookAnnotationKey], ".")
err := fmt.Errorf("subject \"%s\" matches a namespace other than \"%s\" and is not permitted. This check can be disabled by setting restrictCertificatesToNamespace to false in the autocert-config ConfigMap", subject, namespace)
if strings.HasSuffix(subject, ".svc") && !strings.HasSuffix(subject, fmt.Sprintf(".%s.svc", namespace)) {
return false, err
}
if strings.HasSuffix(subject, fmt.Sprintf(".svc.%s", clusterDomain)) && !strings.HasSuffix(subject, fmt.Sprintf(".%s.svc.%s", namespace, clusterDomain)) {
return false, err
}
return true, nil
} }
// mutate takes an `AdmissionReview`, determines whether it is subject to mutation, and returns // mutate takes an `AdmissionReview`, determines whether it is subject to mutation, and returns
@ -418,7 +451,20 @@ func mutate(review *v1beta1.AdmissionReview, config *Config, provisioner Provisi
"user": request.UserInfo, "user": request.UserInfo,
}) })
if !shouldMutate(&pod.ObjectMeta) { mutationAllowed, validationErr := shouldMutate(&pod.ObjectMeta, request.Namespace, config.GetClusterDomain(), config.RestrictCertificatesToNamespace)
if validationErr != nil {
ctxLog.WithField("error", validationErr).Info("Validation error")
return &v1beta1.AdmissionResponse{
Allowed: false,
UID: request.UID,
Result: &metav1.Status{
Message: validationErr.Error(),
},
}
}
if !mutationAllowed {
ctxLog.WithField("annotations", pod.Annotations).Info("Skipping mutation") ctxLog.WithField("annotations", pod.Annotations).Info("Skipping mutation")
return &v1beta1.AdmissionResponse{ return &v1beta1.AdmissionResponse{
Allowed: true, Allowed: true,

View file

@ -0,0 +1,75 @@
package main
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"testing"
)
func TestGetClusterDomain(t *testing.T) {
c := Config{}
if c.GetClusterDomain() != "cluster.local" {
t.Errorf("cluster domain should default to cluster.local, not: %s", c.GetClusterDomain())
}
c.ClusterDomain = "mydomain.com"
if c.GetClusterDomain() != "mydomain.com" {
t.Errorf("cluster domain should default to cluster.local, not: %s", c.GetClusterDomain())
}
}
func TestShouldMutate(t *testing.T) {
testCases := []struct {
description string
subject string
namespace string
expected bool
}{
{"full cluster domain", "test.default.svc.cluster.local", "default", true},
{"full cluster domain wrong ns", "test.default.svc.cluster.local", "kube-system", false},
{"left dots get stripped", ".test.default.svc.cluster.local", "default", true},
{"left dots get stripped wrong ns", ".test.default.svc.cluster.local", "kube-system", false},
{"right dots get stripped", "test.default.svc.cluster.local.", "default", true},
{"right dots get stripped wrong ns", "test.default.svc.cluster.local.", "kube-system", false},
{"dots get stripped", ".test.default.svc.cluster.local.", "default", true},
{"dots get stripped wrong ns", ".test.default.svc.cluster.local.", "kube-system", false},
{"partial cluster domain", "test.default.svc.cluster", "default", true},
{"partial cluster domain wrong ns is still allowed because not valid hostname", "test.default.svc.cluster", "kube-system", true},
{"service domain", "test.default.svc", "default", true},
{"service domain wrong ns", "test.default.svc", "kube-system", false},
{"two part domain", "test.default", "default", true},
{"two part domain different ns", "test.default", "kube-system", true},
{"one hostname", "test", "default", true},
{"no subject specified", "", "default", false},
{"three part not cluster", "test.default.com", "kube-system", true},
{"four part not cluster", "test.default.svc.com", "kube-system", true},
{"five part not cluster", "test.default.svc.cluster.com", "kube-system", true},
{"six part not cluster", "test.default.svc.cluster.local.com", "kube-system", true},
}
for _, testCase := range testCases {
t.Run(testCase.description, func(t *testing.T) {
mutationAllowed, validationErr := shouldMutate(&metav1.ObjectMeta{
Annotations: map[string]string{
admissionWebhookAnnotationKey: testCase.subject,
},
}, testCase.namespace, "cluster.local", true)
if mutationAllowed != testCase.expected {
t.Errorf("shouldMutate did not return %t for %s", testCase.expected, testCase.description)
}
if testCase.subject != "" && mutationAllowed == false && validationErr == nil {
t.Errorf("shouldMutate should return validation error for invalid hostname")
}
})
}
}
func TestShouldMutateNotRestrictToNamespace(t *testing.T) {
mutationAllowed, _ := shouldMutate(&metav1.ObjectMeta{
Annotations: map[string]string{
admissionWebhookAnnotationKey: "test.default.svc.cluster.local",
},
}, "kube-system", "cluster.local", false)
if mutationAllowed == false {
t.Errorf("shouldMutate should return true even with a wrong namespace if restrictToNamespace is false.")
}
}

View file

@ -27,7 +27,7 @@ kubectl apply -f hello-mtls.client.yaml
## Mutual TLS ## Mutual TLS
Unlike the _server auth TLS_ that's typical with web browsers, where the browser authenticates the server but not vice versa, _mutual TLS_ (mTLS) connections have both remote peers (client and server) authenticate to one another by presenting certificates. mTLS is not a different protocol. It's just a variant of TLS that's not usually turned on by default. This respository demonstrates **how to turn on mTLS** with different tools and languages. It also demonstrates other **TLS best practices** like certificate rotation. Unlike the _server auth TLS_ that's typical with web browsers, where the browser authenticates the server but not vice versa, _mutual TLS_ (mTLS) connections have both remote peers (client and server) authenticate to one another by presenting certificates. mTLS is not a different protocol. It's just a variant of TLS that's not usually turned on by default. This repository demonstrates **how to turn on mTLS** with different tools and languages. It also demonstrates other **TLS best practices** like certificate rotation.
mTLS provides _authenticated encryption_: an _identity dialtone_ and _end-to-end encryption_ for your workloads. It's like a secure line with caller ID. This has [all sorts of benefits](https://smallstep.com/blog/use-tls.html): better security, compliance, and easier auditability for starters. It **makes workloads identity-aware**, improving observability and enabling granular access control. Perhaps most compelling, mTLS lets you securely communicate with workloads running anywhere. Code, containers, devices, people, and anything else can connect securely using mTLS as long as they know one anothers' names and can resolve those names to routable IP addresses. mTLS provides _authenticated encryption_: an _identity dialtone_ and _end-to-end encryption_ for your workloads. It's like a secure line with caller ID. This has [all sorts of benefits](https://smallstep.com/blog/use-tls.html): better security, compliance, and easier auditability for starters. It **makes workloads identity-aware**, improving observability and enabling granular access control. Perhaps most compelling, mTLS lets you securely communicate with workloads running anywhere. Code, containers, devices, people, and anything else can connect securely using mTLS as long as they know one anothers' names and can resolve those names to routable IP addresses.

View file

@ -1,4 +1,4 @@
FROM smallstep/step-cli:0.8.4-rc.1 FROM smallstep/step-cli:0.9.0
ENV CA_NAME="Autocert" ENV CA_NAME="Autocert"
ENV CA_DNS="ca.step.svc.cluster.local,127.0.0.1" ENV CA_DNS="ca.step.svc.cluster.local,127.0.0.1"
@ -6,7 +6,7 @@ ENV CA_ADDRESS=":4443"
ENV CA_DEFAULT_PROVISIONER="admin" ENV CA_DEFAULT_PROVISIONER="admin"
ENV CA_URL="ca.step.svc.cluster.local" ENV CA_URL="ca.step.svc.cluster.local"
ENV KUBE_LATEST_VERSION="v1.13.2" ENV KUBE_LATEST_VERSION="v1.14.0"
USER root USER root
RUN curl -L https://storage.googleapis.com/kubernetes-release/release/${KUBE_LATEST_VERSION}/bin/linux/amd64/kubectl -o /usr/local/bin/kubectl \ RUN curl -L https://storage.googleapis.com/kubernetes-release/release/${KUBE_LATEST_VERSION}/bin/linux/amd64/kubectl -o /usr/local/bin/kubectl \

View file

@ -21,6 +21,8 @@ metadata:
data: data:
config.yaml: | config.yaml: |
logFormat: json # or text logFormat: json # or text
restrictCertificatesToNamespace: true
clusterDomain: cluster.local
caUrl: https://ca.step.svc.cluster.local caUrl: https://ca.step.svc.cluster.local
certLifetime: 24h certLifetime: 24h
renewer: renewer:

View file

@ -1,4 +1,4 @@
FROM smallstep/step-cli:0.8.3 FROM smallstep/step-cli:0.9.0
USER root USER root
ENV CRT="/var/run/autocert.step.sm/site.crt" ENV CRT="/var/run/autocert.step.sm/site.crt"

View file

@ -1,17 +1,13 @@
FROM smallstep/step-cli:0.8.3 FROM smallstep/step-cli:0.9.0
ARG BINPATH="bin/step-ca" ARG BINPATH="bin/step-ca"
ENV PORT=9000 ENV CONFIGPATH="/home/step/config/ca.json"
ENV CONFIGPATH="/home/step/.step/config/ca.json"
ENV PWDPATH="/home/step/secrets/password" ENV PWDPATH="/home/step/secrets/password"
COPY $BINPATH "/usr/local/bin/step-ca" COPY $BINPATH "/usr/local/bin/step-ca"
EXPOSE $PORT VOLUME ["/home/step"]
VOLUME ["/home/step/.step/secrets"]
VOLUME ["/home/step/.step/config"]
VOLUME ["/home/step/secrets"]
STOPSIGNAL SIGTERM STOPSIGNAL SIGTERM
CMD exec /bin/sh -c "/usr/local/bin/step-ca --password-file $PWDPATH $CONFIGPATH" CMD exec /bin/sh -c "/usr/local/bin/step-ca --password-file $PWDPATH $CONFIGPATH"

View file

@ -1,39 +0,0 @@
{
"root": "/home/step/.step/secrets/root_ca.crt",
"crt": "/home/step/.step/secrets/intermediate_ca.crt",
"key": "/home/step/.step/secrets/intermediate_ca_key",
"address": ":9000",
"dnsNames": [
"ca.smallstep.com"
],
"logger": {
"format": "text"
},
"authority": {
"provisioners": [
{
"name": "mariano@smallstep.com",
"type": "jwk",
"key": {
"use": "sig",
"kty": "EC",
"kid": "DmAtZt2EhmZr_iTJJ387fr4Md2NbzMXGdXQNW1UWPXk",
"crv": "P-256",
"alg": "ES256",
"x": "jXoO1j4CXxoTC32pNzkVC8l6k2LfP0k5ndhJZmcdVbk",
"y": "c3JDL4GTFxJWHa8EaHdMh4QgwMh64P2_AGWrD0ADXcI"
},
"encryptedKey": "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJjdHkiOiJqd2sranNvbiIsImVuYyI6IkEyNTZHQ00iLCJwMmMiOjEwMDAwMCwicDJzIjoiOTFVWjdzRGw3RlNXcldfX1I1NUh3USJ9.FcWtrBDNgrkA33G9Ll9sXh1cPF-3jVXeYe1FLmSDc_Q2PmfLOPvJOA.0ZoN32ayaRWnufJb.WrkffMmDLWiq1-2kn-w7-kVBGW12gjNCBHNHB1hyEdED0rWH1YWpKd8FjoOACdJyLhSn4kAS3Lw5AH7fvO27A48zzvoxZU5EgSm5HG9IjkIH-LBJ-v79ShkpmPylchgjkFhxa5epD11OIK4rFmI7s-0BCjmJokLR_DZBhDMw2khGnsr_MEOfAz9UnqXaQ4MIy8eT52xUpx68gpWFlz2YP3EqiYyNEv0PpjMtyP5lO2i8-p8BqvuJdus9H3fO5Dg-1KVto1wuqh4BQ2JKTauv60QAnM_4sdxRHku3F_nV64SCrZfDvnN2ve21raFROtyXaqHZhN6lyoPxDncy8v4.biaOblEe0N-gMpJyFZ-3-A"
}
]
},
"tls": {
"cipherSuites": [
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"
],
"minVersion": 1.2,
"maxVersion": 1.2,
"renegotiation": false
}
}

View file

@ -1,210 +0,0 @@
#!/bin/sh
CA_NAME="ca"
CA_NAMESPACE="step"
DEMO_NAMESPACE="step-demo"
DEMO_ENVIRONMENT="staging"
# The name of an image pull secret (e.g., for private docker hub images)
# Set to "none" for no pull secret.
IMAGE_PULL_SECRET="none"
while getopts "c:d:n:e:h:t:p:i:" opt; do
case "$opt" in
h)
show_help
exit 0
;;
c)
CA_NAMESPACE=$OPTARG
;;
n)
CA_NAME=$OPTARG
;;
e)
DEMO_ENVIRONMENT=$OPTARG
;;
d)
DEMO_NAMESPACE=$OPTARG
;;
t)
INSTALL_TYPE=$OPTARG
;;
p)
PROVISIONER_TYPE=$OPTARG
;;
i)
IMAGE_PULL_SECRET=$OPTARG
;;
esac
done
shift $((OPTIND-1))
# Various container images used throughout the script.
STEP_CA_IMAGE="localhost:5000/smallstep/step-ca:latest"
DIR=`pwd`
PKI="$(dirname "$DIR")/pki"
SECRETS="$PKI/secrets"
##
# Certificate Authority installation (prints yaml to stdout).
##
install_ca()
{
read -p "CA Private Key Password: " -s password
(>&2 echo "")
tmp=$(mktemp -d /tmp/step.XXXXXX)
(
cd "$tmp"
# Bundle up certificates and private key into ConfigMap
mkdir $tmp/certificates
cp $SECRETS/root_ca.crt $tmp/certificates
cp $SECRETS/intermediate_ca.crt $tmp/certificates/
cp $SECRETS/intermediate_ca_key $tmp/certificates/
# ConfigMap for CA configuration
mkdir $tmp/config
cp $DIR/ca.json $tmp/config/ca.json
# Create the namespace
echo "---" > $tmp/step-ca.yml
echo "apiVersion: v1" >> $tmp/step-ca.yml
echo "kind: Namespace" >> $tmp/step-ca.yml
echo "metadata:" >> $tmp/step-ca.yml
echo " name: $CA_NAMESPACE" >> $tmp/step-ca.yml
echo "---" >> $tmp/step-ca.yml
# Create certificates configmap
echo "apiVersion: v1" >> $tmp/step-ca.yml
echo "kind: ConfigMap" >> $tmp/step-ca.yml
kubectl create configmap ca-certificates -n $CA_NAMESPACE --from-file `pwd`/certificates --dry-run -o yaml >> $tmp/step-ca.yml
echo "" >> $tmp/step-ca.yml
echo "---" >> $tmp/step-ca.yml
# Create a CA configuration configmap
echo "" >> $tmp/step-ca.yml
echo "apiVersion: v1" >> $tmp/step-ca.yml
echo "kind: ConfigMap" >> $tmp/step-ca.yml
kubectl create configmap ca-config -n $CA_NAMESPACE --from-file `pwd`/config --dry-run -o yaml >> $tmp/step-ca.yml
echo "" >> $tmp/step-ca.yml
echo "---" >> $tmp/step-ca.yml
# Create a secret with the CA password in it
echo "" >> $tmp/step-ca.yml
echo "apiVersion: v1" >> $tmp/step-ca.yml
echo "kind: Secret" >> $tmp/step-ca.yml
kubectl create secret generic ca-certificate-password -n $CA_NAMESPACE --from-literal=password="$password" --dry-run -o yaml >> $tmp/step-ca.yml
)
echo "" >> $tmp/step-ca.yml
echo "---" >> $tmp/step-ca.yml
echo "" >> $tmp/step-ca.yml
cat <<EOF >> $tmp/step-ca.yml
apiVersion: v1
kind: Service
metadata:
labels:
service: $CA_NAME
name: $CA_NAME
namespace: $CA_NAMESPACE
spec:
type: ClusterIP
ports:
- name: headless
port: 443
targetPort: 9000
selector:
service: $CA_NAME
---
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: $CA_NAME
spec:
minAvailable: 1
selector:
matchLabels:
service: $CA_NAME
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: $CA_NAME
namespace: $CA_NAMESPACE
spec:
replicas: 1
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
creationTimestamp: null
labels:
service: $CA_NAME
spec:
containers:
- name: $CA_NAME
image: $STEP_CA_IMAGE
resources:
requests:
cpu: 100m
memory: 20Mi
readinessProbe:
httpGet:
path: /health
port: 443
scheme: HTTPS
initialDelaySeconds: 3
periodSeconds: 3
livenessProbe:
httpGet:
path: /health
port: 443
scheme: HTTPS
initialDelaySeconds: 5
periodSeconds: 3
volumeMounts:
- name: certificates
mountPath: /home/step/.step/secrets
readOnly: true
- name: config
mountPath: /home/step/.step/config
readOnly: true
- name: secrets
mountPath: /home/step/secrets
readOnly: true
securityContext:
runAsUser: 1000
allowPrivilegeEscalation: false
volumes:
- name: certificates
configMap:
name: ca-certificates
- name: config
configMap:
name: ca-config
- name: secrets
secret:
secretName: ca-certificate-password
EOF
cat $tmp/step-ca.yml
rm -rf "$tmp"
}
install_ca

View file

@ -3,6 +3,9 @@
Demonstrates setting up your own PKI and certificate authority using `step ca` Demonstrates setting up your own PKI and certificate authority using `step ca`
and getting certificates using the `step` command line tool and SDK. and getting certificates using the `step` command line tool and SDK.
Check out [Getting started with docker](docker.md) to run [step certificates](https://github.com/smallstep/certificates)
using docker.
![Animated terminal showing step ca init in practice](https://smallstep.com/images/blog/2018-12-04-unfurl.gif) ![Animated terminal showing step ca init in practice](https://smallstep.com/images/blog/2018-12-04-unfurl.gif)
## Prerequisites ## Prerequisites

View file

@ -88,9 +88,10 @@ e.g. `v1.0.2`
Travis will build and upload the following artifacts: Travis will build and upload the following artifacts:
* **step-ca_1.0.3_amd64.deb**: debian package for installation on linux. * **step-certificates_1.0.3_amd64.deb**: debian package for installation on linux.
* **step-ca_1.0.3_linux_amd64.tar.gz**: tarball containing a statically compiled linux binary. * **step-certificates_1.0.3_linux_amd64.tar.gz**: tarball containing a statically compiled linux binary.
* **step-ca_1.0.3_darwin_amd64.tar.gz**: tarball containing a statically compiled darwin binary. * **step-certificates_1.0.3_darwin_amd64.tar.gz**: tarball containing a statically compiled darwin binary.
* **step-certificates.tar.gz**: tarball containing a git archive of the full repo.
*All Done!* *All Done!*

155
docs/docker.md Normal file
View file

@ -0,0 +1,155 @@
# Getting started with docker
This guide shows how to set up [step certificates](https://github.com/smallstep/certificates) using docker.
For short, we will use **step-ca** to refer to [step certificates](https://github.com/smallstep/certificates).
## Requirements
1. To follow this guide you will need to [install step
cli](https://github.com/smallstep/cli#installation-guide).
2. Get the docker image.
Get the latest version of **step-ca** from the [step-ca docker
hub](https://hub.docker.com/r/smallstep/step-ca):
```sh
$ docker pull smallstep/step-ca
```
3. Create the required volumes.
We need to create a volume in docker where we will store our PKI as well as
the step-ca configuration file.
```sh
$ docker volume create step
```
4. Initialize the PKI.
The simple way to do this is to run an interactive terminal:
```sh
$ docker run -it -v step:/home/step smallstep/step-ca sh
~ $ step ca init
✔ What would you like to name your new PKI? (e.g. Smallstep): Smallstep
✔ What DNS names or IP addresses would you like to add to your new CA? (e.g. ca.smallstep.com[,1.1.1.1,etc.]): localhost
✔ What address will your new CA listen at? (e.g. :443): :9000
✔ What would you like to name the first provisioner for your new CA? (e.g. you@smallstep.com): admin
✔ What do you want your password to be? [leave empty and we'll generate one]: <your password here>
Generating root certificate...
all done!
Generating intermediate certificate...
all done!
✔ Root certificate: /home/step/certs/root_ca.crt
✔ Root private key: /home/step/secrets/root_ca_key
✔ Root fingerprint: f9e45ae9ec5d42d702ce39fd9f3125372ce54d0b29a5ff3016b31d9b887a61a4
✔ Intermediate certificate: /home/step/certs/intermediate_ca.crt
✔ Intermediate private key: /home/step/secrets/intermediate_ca_key
✔ Default configuration: /home/step/config/defaults.json
✔ Certificate Authority configuration: /home/step/config/ca.json
Your PKI is ready to go. To generate certificates for individual services see 'step help ca'.
```
5. Place the PKI root password in a known location.
Our image is expecting the password to be placed in `/home/step/secrets/password`
you can simple go in to the terminal again and write that file:
```sh
$ docker run -it -v step:/home/step smallstep/step-ca sh
~ $ echo <your password here> > /home/step/secrets/password
```
At this time everything is ready to run step-ca!
## Running step certificates
Now that we have configured our environment we are ready to run step-ca.
Expose the server address locally and run the step-ca with:
```sh
$ docker run -d -p 127.0.0.1:9000:9000 -v step:/home/step smallstep/step-ca
```
Let's verify that the service is running with curl:
```sh
$ curl https://localhost:9000/health
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.haxx.se/docs/sslcerts.html
curl performs SSL certificate verification by default, using a "bundle"
of Certificate Authority (CA) public keys (CA certs). If the default
bundle file isn't adequate, you can specify an alternate file
using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
the bundle, the certificate verification probably failed due to a
problem with the certificate (it might be expired, or the name might
not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
the -k (or --insecure) option.
HTTPS-proxy has similar options --proxy-cacert and --proxy-insecure.
```
It's working but curl complains because the certificate is not signed by an
accepted certificate authority.
## Dev environment bootstrap
To initialize the development environment we need to grab the Root fingerprint
from the [Initializing the PKI](#initializing-the-pki) step earlier. In the
case of this example:
`f9e45ae9ec5d42d702ce39fd9f3125372ce54d0b29a5ff3016b31d9b887a61a4`. With the
fingerprint we can bootstrap our dev environment.
```sh
$ step ca bootstrap --ca-url https://localhost:9000 --fingerprint f9e45ae9ec5d42d702ce39fd9f3125372ce54d0b29a5ff3016b31d9b887a61a4 --install
The root certificate has been saved in ~/.step/certs/root_ca.crt.
Your configuration has been saved in ~/.step/config/defaults.json.
Installing the root certificate in the system truststore... done.
```
Now [step cli](https://github.com/smallstep/cli) is configured to use step-ca
and our new root certificate is trusted by our local environment.
```sh
$ curl https://localhost:9000/health
{"status":"ok"}
```
And we are able to run web services configured with TLS (and mTLS):
```sh
~ $ step ca certificate localhost localhost.crt localhost.key
✔ Key ID: aTPGWP0qbuQdflR5VxtNouDIOXyNMH1H9KAZKP-UcHo (admin)
✔ Please enter the password to decrypt the provisioner key:
✔ CA: https://localhost:9000/1.0/sign
✔ Certificate: localhost.crt
✔ Private Key: localhost.key
~ $ step ca root root_ca.crt
The root certificate has been saved in root_ca.crt.
~ $ python <<EOF
import BaseHTTPServer, ssl
class H(BaseHTTPServer.BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200); self.send_header('content-type', 'text/html; charset=utf-8'); self.end_headers()
self.wfile.write(b'\n\xf0\x9f\x91\x8b Hello! Welcome to TLS \xf0\x9f\x94\x92\xe2\x9c\x85\n\n')
httpd = BaseHTTPServer.HTTPServer(('', 8443), H)
httpd.socket = ssl.wrap_socket (httpd.socket, server_side=True, keyfile="localhost.key", certfile="localhost.crt", ca_certs="root_ca.crt")
httpd.serve_forever()
EOF
```
Test from another terminal:
```sh
$ curl https://localhost:8443
👋 Hello! Welcome to TLS 🔒✅
```
Or visit `https://localhost:8443` from your browser.