Step v0.9.0: easily curl services secured by mutual TLS and more

By Sebastian Tiedtke & Max Furman March 27, 2019

At Smallstep, we believe that the right thing to do also needs to be the easy thing to do. Otherwise people will just do the easy thing, even though it’s wrong.

Typical Production Environment vs TLS everywhere

We believe that all communication in your infrastructure can and should be mutually authenticated and encrypted. The right way for a developer to connect to a service that enforces mutual authentication (mTLS) is with a client certificate - a certificate that personally identifies the developer. But, in our experience, this is not the easy way. Why? Because obtaining a scoped, personal certificate that is valid within one’s infrastructure is an unreasonable expectation for most developers. The much easier solution is probably some sequence of SSH and curl commands that allows one to bypass all security measures. Let’s fix this!

Our mission, at Smallstep, is to build tools and workflows that make secure communication within your infrastructure simple and seamless. That’s why we’ve created step, the zero trust command-line swiss army knife, and step certificates, a sub-project that adds certificate creation and management capabilities to step. Today’s step release, v0.9.0, makes it easy for developers to obtain personal certificates by taking advantage of existing SSO providers.

Star  step cli
Star  step certificates

Installing step

To follow along you’ll need to install step.


$ brew install step

Or, if you’ve already installed step via homebrew, run:

$ brew upgrade step


Linux users can install step 0.9.0 using dpkg (or build from source).

$ curl -Os && sudo dpkg -i step-cli_0.9.0_amd64.deb


If you use docker you can launch our base image locally:

$ docker run -it --rm smallstep/step-cli:0.9.0

Or in a kubernetes cluster:

$ kubectl run step -it --rm --image smallstep/step-cli:0.9.0 --restart Never

Easily curl services secured by mutual TLS

In the blog post announcing our previous release we discussed why Let’s Encrypt is a great free service to obtain certificates for public websites and APIs. However, we also argue that Let’s Encrypt is less optimal when used to generate certificates for internal infrastructure.

For your internal infrastructure (users, services, hosts, etc.) we believe that you should use an internal Certificate Authority (CA). That’s why we’ve built step certificates to make using a CA and managing certificates for internal services as easy as Let’s Encrypt.

To begin, simply run the init workflow:

$ step ca init --name "Local CA" --provisioner admin \
             --dns localhost --address ":443"

You’ll be prompted for a password to encrypt key material. Let’s call this password your step-key password; you will need it when you start (and restart) your CA.

Next, start your CA using the generated configuration (you’ll be prompted for your sudo password so we can listen on port 443, then your step-key password):

$ sudo step-ca $(step path)/config/ca.json

You can get a certificate from your CA for any name, including localhost. In another terminal run (you’ll be prompted for your step-key password):

$ step ca certificate localhost localhost.crt localhost.key

If you’d rather not run an online CA see the section below highlighting our new offline features.

Armed with certificates your internal services can be easily configured to trust your CA. That means they can securely connect across the internet and enjoy the many benefits of using mutual TLS!

line break

Managing your internal PKI using step to identify services connecting over TLS will enforce strong mutual authentication between them. If every entity in your infrastructure can easily obtain a valid certificate then you can require mutual authentication for every connection in your network.

Q: How do I allow engineers to connect directly to services (for debugging, metrics aggregation, etc.) if every connection requires mutual authentication?

A: Make it easy for engineers to obtain self-identifying (personal) certificates.

If you followed the announcement of step certificates, you probably already know that step certificates comes with a bootstrapping protocol. The CA authorizes provisioners which can be flexibly integrated into config management, container orchestration, and other deploy automation tools to generate one-time-tokens which workloads can redeem for valid certificates. This works great for machines and services but falls short for user identities.

Enter personal certificates using step certificates and step cli!

Personal certificates via OAuth OpenID Connect

step v0.9.0’s newest feature: OpenID Connect authentication support for step certificates.

Unlike an inventory of machines or services, user identities are usually already managed by your existing G-Suite, Okta, Salesforce, or Microsoft Office 365. Almost all of these enterprise services expose OpenID Connect identity providers which are a suite of single-sign-on protocols that allow creation of accounts and login into third party applications using a single account per user identity.

In step v0.9.0 you can now leverage OpenID Connect to authenticate with step certificates to make issuance of personal certificates simple for your whole team.

step OAuth OpenID Connect support

Here’s how it works:

1) Make sure your CA is running.

$ step-ca $(step path)/config/ca.json

2) Add an OpenID Connect provisioner for you G-Suite.

In a different terminal:

$ step ca provisioner add Google --type oidc --ca-config $(step path)/config/ca.json \
  --client-id \
  --client-secret 6Q7lGMua_Oox4nA92QBXYypT \
  --configuration-endpoint \
  --domain --domain

3) Reload the CA.

Return to the terminal from which you originally ran your CA where you’ll be re-prompted for your sudo password so we can listen on port 443, and your step-key password:

$ kill -SIGHUP $(ps aux | grep step-ca | grep -v grep | awk '{print $2}')

4) Get a personal certificate using your G-Suite account.

Be sure to specify your G-Suite email address (the command’s first argument) and select the Google provisioner from the dropdown:

$ step ca certificate personal.crt personal.key
Use the arrow keys to navigate: ↓ ↑ → ←
What provisioner key do you want to use?
    pDpbCsI8Thvci7EqyJJY7AoUIadufTeZQnAcBCwGuHE (admin)
  ▸ (Google)

Unlike regular JWK-based provisioners no password prompt will come up in the terminal. Instead expect the default browser to open the G-Suite login screen. After successfully logging-in the CA will issue a certificate for the respective user.

$ step certificate inspect --short personal.crt
X.509v3 TLS Certificate (ECDSA P-256) [Serial: 1445...0545]
  Subject:     113900523962383213156
  Issuer:      Local CA Intermediate CA
  Provisioner: Google [ID:]
  Valid from:  2019-03-20T18:17:55Z
          to:  2019-03-21T18:17:55Z

Now you can mutually authenticate with your services using TLS. Bring up a web server:

$ step ca root root_ca.crt
$ step ca localhost localhost.crt localhost.key # if you didn't run this earlier
$ 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()
        san = self.connection.getpeercert().get('subjectAltName')[0][1]
        self.wfile.write(b'\n\xf0\x9f\x91\x8b Hello '+san+'! Welcome to mTLS \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",
                                cert_reqs=ssl.CERT_REQUIRED, ca_certs="root_ca.crt")

Now we’re ready to ping the HTTPS endpoint using curl. All we need to do is provide a root certificate for client-side server authentication plus your personal certificate and private key to pass the server-side client authentication.

$ curl --cacert root_ca.crt \
  --cert personal.crt --key personal.key \

Ping HTTPS endpoint with personal certificate

Success! Using OpenID Connect to make personal certificates for your teams self-serve is an easy and powerful way to unlock strong identity for everybody everywhere. For more details including how to setup the OAuth backend please review step certificatesdocumentation.

What else is new?

CA offline mode

It’s not always practical or desirable to run an Online CA. step now provides the means to create certificates without step certificates CA running. step offline mode will still leverage the CA’s configuration, which must be available locally, to issue certificates.

$ step ca init --name "Local CA" --provisioner admin \
             --dns localhost --address ":443"
$ step ca certificate --offline foo.crt foo.key

step can also generate bootstrap tokens in offline mode and exchange them with a running CA for a certificate. This workflow is useful when generating tokens on a host without direct access to the running CA.

$ TOKEN=$(step ca token --offline
$ step ca certificate --token=$TOKEN foo.crt foo.key

NOTE: You cannot exchange a token for a certificate that will be generated offline - the token must be exchanged with an online CA.

Shell auto-complete for bash and zsh

step now comes with auto-completion for bash and zsh using the standard _<TAB>_ key as you type out commands.

$ step certificate ins<TAB>
 -- values --
inspect  -- print certificate or CSR details in human readable format
install  -- install a root certificate in the system truststore

Shorter format for step certificate inspect

The new short format of step certificate inspect with the --short flag will provide a quick peek at the most important information of a certificate without getting lost in the weeds. Handy for debugging and troubleshooting.

$ step certificate inspect --short
X.509v3 TLS Certificate (ECDSA P-256) [Serial: 3263...4954]
  Issuer:      Let's Encrypt Authority X3
  Valid from:  2019-02-08T13:07:44Z
          to:  2019-05-09T13:07:44Z

Better audit logging for CA

We’ve added better audit logging for api requests to step certificates. The logs now record the PEM encoded body of the certificate, the provisioner token, and a number of the most commonly searched attributes from the certificate for later audits.

INFO[467190] certificate="[CERT-PEM]" duration=3.164401ms duration-ns=3164401 fi
elds.time="2019-03-18T16:28:05-07:00" issuer="Example Inc. Intermediate CA" meth
od=POST name=ca ott=[OTT] path=/sign protocol=HTTP/2.0 provisioner="gif@example.
com (EWCA8ltRBtL17eEA-Hun3AkB7K1LDaQr-6Gouw7EphU)" public-key="ECDSA P-256" refe
rer= remote-address= request-id=bi82j1crtr3746jf72pg serial=67532934347
487000046383869679560614883 size=1618 status=201 subject=foo user-agent=Go-http-
client/2.0 user-id= valid-from="2019-03-18T23:28:05Z" valid-to="2019-03-19T23:28

Let us know what you think

We strive to make step the best open source solution for managing your internal PKI and enabling you to run TLS everywhere. In future releases, we will ship tighter integrations with common cloud and container environments, more advanced certificate lifecycle management, and better visibility into certificate operations. As always, we’d love to hear from you.

Star  step cli
Star  step certificates

Stay tuned by following us on twitter and GitHub!

Leverage Smallstep's technology to unlock the many benefits of using TLS everywhere to connect across clouds and easily access services and applications in the cloud-native age. Subscribe for infrequent updates and announcements.

Subscribe to our mailing list or drop us a line