Merge branch 'master' into linkedca

This commit is contained in:
Mariano Cano 2021-08-25 15:56:50 -07:00 committed by GitHub
commit 42fde8ba28
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 490 additions and 47 deletions

9
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View file

@ -0,0 +1,9 @@
blank_issues_enabled: true
contact_links:
- name: Ask on Discord
url: https://discord.gg/7xgjhVAg6g
about: You can ask for help here!
- name: Want to contribute to step certificates?
url: https://github.com/smallstep/certificates/blob/master/docs/CONTRIBUTING.md
about: Be sure to read contributing guidelines!

View file

@ -1,5 +1,5 @@
---
name: Certificates Enhancement
name: Enhancement
about: Suggest an enhancement to step certificates
title: ''
labels: enhancement, needs triage

View file

@ -1,14 +1,12 @@
name: labeler
name: Pull Request Labeler
on:
pull_request:
branches:
- master
pull_request_target
jobs:
label:
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v3
- uses: actions/labeler@v3.0.2
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
configuration-path: .github/labeler.yml

View file

@ -18,7 +18,14 @@ You can use it to:
Whatever your use case, `step-ca` is easy to use and hard to misuse, thanks to [safe, sane defaults](https://smallstep.com/docs/step-ca/certificate-authority-server-production#sane-cryptographic-defaults).
**Questions? Find us in [Discussions](https://github.com/smallstep/certificates/discussions).**
---
**Don't want to run your own CA?**
To get up and running quickly, or as an alternative to running your own `step-ca` server, consider creating a [free hosted smallstep Certificate Manager authority](https://info.smallstep.com/certificate-manager-early-access-mvp/).
---
**Questions? Find us in [Discussions](https://github.com/smallstep/certificates/discussions) or [Join our Discord](https://bit.ly/step-discord).**
[Website](https://smallstep.com/certificates) |
[Documentation](https://smallstep.com/docs) |
@ -27,7 +34,6 @@ Whatever your use case, `step-ca` is easy to use and hard to misuse, thanks to [
[Contributor's Guide](./docs/CONTRIBUTING.md)
[![GitHub release](https://img.shields.io/github/release/smallstep/certificates.svg)](https://github.com/smallstep/certificates/releases/latest)
[![CA Image](https://images.microbadger.com/badges/image/smallstep/step-ca.svg)](https://microbadger.com/images/smallstep/step-ca)
[![Go Report Card](https://goreportcard.com/badge/github.com/smallstep/certificates)](https://goreportcard.com/report/github.com/smallstep/certificates)
[![Build Status](https://travis-ci.com/smallstep/certificates.svg?branch=master)](https://travis-ci.com/smallstep/certificates)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
@ -58,10 +64,10 @@ You can issue certificates in exchange for:
- ID tokens from Okta, GSuite, Azure AD, Auth0.
- ID tokens from an OAuth OIDC service that you host, like [Keycloak](https://www.keycloak.org/) or [Dex](https://github.com/dexidp/dex)
- [Cloud instance identity documents](https://smallstep.com/blog/embarrassingly-easy-certificates-on-aws-azure-gcp/), for VMs on AWS, GCP, and Azure
- [Single-use, short-lived JWK tokens]() issued by your CD tool — Puppet, Chef, Ansible, Terraform, etc.
- [Single-use, short-lived JWK tokens](https://smallstep.com/docs/step-ca/provisioners#jwk) issued by your CD tool — Puppet, Chef, Ansible, Terraform, etc.
- A trusted X.509 certificate (X5C provisioner)
- Expiring SSH host certificates needing rotation (the SSHPOP provisioner)
- Learn more in our [provisioner documentation](https://smallstep.com/docs/step-ca/configuration#jwk)
- Learn more in our [provisioner documentation](https://smallstep.com/docs/step-ca/provisioners)
### 🏔 Your own private ACME server

View file

@ -10,11 +10,13 @@ import (
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net"
"net/http"
"net/url"
"reflect"
"strings"
"time"
@ -114,6 +116,17 @@ func http01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWeb
return nil
}
func tlsAlert(err error) uint8 {
var opErr *net.OpError
if errors.As(err, &opErr) {
v := reflect.ValueOf(opErr.Err)
if v.Kind() == reflect.Uint8 {
return uint8(v.Uint())
}
}
return 0
}
func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebKey, vo *ValidateChallengeOptions) error {
config := &tls.Config{
NextProtos: []string{"acme-tls/1"},
@ -129,6 +142,14 @@ func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSON
conn, err := vo.TLSDial("tcp", hostPort, config)
if err != nil {
// With Go 1.17+ tls.Dial fails if there's no overlap between configured
// client and server protocols. When this happens the connection is
// closed with the error no_application_protocol(120) as required by
// RFC7301. See https://golang.org/doc/go1.17#ALPN
if tlsAlert(err) == 120 {
return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType,
"cannot negotiate ALPN acme-tls/1 protocol for tls-alpn-01 challenge"))
}
return storeError(ctx, db, ch, false, WrapError(ErrorConnectionType, err,
"error doing TLS dial for %s", hostPort))
}

View file

@ -1395,7 +1395,7 @@ func TestTLSALPN01Validate(t *testing.T) {
assert.Equals(t, updch.Type, ch.Type)
assert.Equals(t, updch.Value, ch.Value)
err := NewError(ErrorConnectionType, "error doing TLS dial for %v:443: tls: DialWithDialer timed out", ch.Value)
err := NewError(ErrorConnectionType, "error doing TLS dial for %v:443:", ch.Value)
assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error())
assert.Equals(t, updch.Error.Type, err.Type)

View file

@ -124,10 +124,8 @@ func (db *DB) updateAddOrderIDs(ctx context.Context, accID string, addOids ...st
ordersByAccountMux.Lock()
defer ordersByAccountMux.Unlock()
var oldOids []string
b, err := db.db.Get(ordersByAccountIDTable, []byte(accID))
var (
oldOids []string
)
if err != nil {
if !nosql.IsErrNotFound(err) {
return nil, errors.Wrapf(err, "error loading orderIDs for account %s", accID)

View file

@ -12,6 +12,7 @@ import (
"github.com/smallstep/certificates/acme"
"github.com/smallstep/certificates/db"
"github.com/smallstep/nosql"
"github.com/smallstep/nosql/database"
nosqldb "github.com/smallstep/nosql/database"
)
@ -710,6 +711,34 @@ func TestDB_updateAddOrderIDs(t *testing.T) {
err: errors.Errorf("error saving orderIDs index for account %s", accID),
}
},
"ok/no-old": func(t *testing.T) test {
return test{
db: &db.MockNoSQLDB{
MGet: func(bucket, key []byte) ([]byte, error) {
switch string(bucket) {
case string(ordersByAccountIDTable):
return nil, database.ErrNotFound
default:
assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
return nil, errors.New("force")
}
},
MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) {
switch string(bucket) {
case string(ordersByAccountIDTable):
assert.Equals(t, key, []byte(accID))
assert.Equals(t, old, nil)
assert.Equals(t, nu, nil)
return nil, true, nil
default:
assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
return nil, false, errors.New("force")
}
},
},
res: []string{},
}
},
"ok/all-old-not-pending": func(t *testing.T) test {
oldOids := []string{"foo", "bar"}
bOldOids, err := json.Marshal(oldOids)

View file

@ -400,7 +400,7 @@ func logOtt(w http.ResponseWriter, token string) {
func LogCertificate(w http.ResponseWriter, cert *x509.Certificate) {
if rl, ok := w.(logging.ResponseLogger); ok {
m := map[string]interface{}{
"serial": cert.SerialNumber,
"serial": cert.SerialNumber.String(),
"subject": cert.Subject.CommonName,
"issuer": cert.Issuer.CommonName,
"valid-from": cert.NotBefore.Format(time.RFC3339),
@ -418,7 +418,7 @@ func LogCertificate(w http.ResponseWriter, cert *x509.Certificate) {
if len(val.CredentialID) > 0 {
m["provisioner"] = fmt.Sprintf("%s (%s)", val.Name, val.CredentialID)
} else {
m["provisioner"] = val.Name
m["provisioner"] = string(val.Name)
}
break
}

View file

@ -174,6 +174,9 @@ func (a *Authority) AuthorizeAdminToken(r *http.Request, token string) (*linkedc
}
// UseToken stores the token to protect against reuse.
//
// This method currently ignores any error coming from the GetTokenID, but it
// should specifically ignore the error provisioner.ErrAllowTokenReuse.
func (a *Authority) UseToken(token string, prov provisioner.Interface) error {
if reuseKey, err := prov.GetTokenID(token); err == nil {
if reuseKey == "" {

View file

@ -131,9 +131,10 @@ func (p *Azure) GetTokenID(token string) (string, error) {
return "", errors.Wrap(err, "error verifying claims")
}
// If TOFU is disabled create return the token kid
// If TOFU is disabled then allow token re-use. Azure caches the token for
// 24h and without allowing the re-use we cannot use it twice.
if p.DisableTrustOnFirstUse {
return claims.ID, nil
return "", ErrAllowTokenReuse
}
sum := sha256.Sum256([]byte(claims.XMSMirID))

View file

@ -72,7 +72,7 @@ func TestAzure_GetTokenID(t *testing.T) {
wantErr bool
}{
{"ok", p1, args{t1}, w1, false},
{"ok no TOFU", p2, args{t2}, "the-jti", false},
{"ok no TOFU", p2, args{t2}, "", true},
{"fail token", p1, args{"bad-token"}, "", true},
{"fail claims", p1, args{"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.ey.fooo"}, "", true},
}

View file

@ -4,6 +4,7 @@ import (
"context"
"crypto/x509"
"encoding/json"
stderrors "errors"
"net/url"
"regexp"
"strings"
@ -32,6 +33,17 @@ type Interface interface {
AuthorizeSSHRekey(ctx context.Context, token string) (*ssh.Certificate, []SignOption, error)
}
// ErrAllowTokenReuse is an error that is returned by provisioners that allows
// the reuse of tokens.
//
// This is, for example, returned by the Azure provisioner when
// DisableTrustOnFirstUse is set to true. Azure caches tokens for up to 24hr and
// has no mechanism for getting a different token - this can be an issue when
// rebooting a VM. In contrast, AWS and GCP have facilities for requesting a new
// token. Therefore, for the Azure provisioner we are enabling token reuse, with
// the understanding that we are not following security best practices
var ErrAllowTokenReuse = stderrors.New("allow token reuse")
// Audiences stores all supported audiences by request type.
type Audiences struct {
Sign []string

View file

@ -383,7 +383,7 @@ func (a *Authority) Revoke(ctx context.Context, revokeOpts *RevokeOptions) error
}
rci.ProvisionerID = p.GetID()
rci.TokenID, err = p.GetTokenID(revokeOpts.OTT)
if err != nil {
if err != nil && !errors.Is(err, provisioner.ErrAllowTokenReuse) {
return errs.Wrap(http.StatusInternalServerError, err,
"authority.Revoke; could not get ID for token")
}

View file

@ -24,4 +24,7 @@ VOLUME ["/home/step"]
STOPSIGNAL SIGTERM
HEALTHCHECK CMD step ca health 2>/dev/null | grep "^ok" >/dev/null
COPY docker/entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/bin/bash", "/entrypoint.sh"]
CMD exec /usr/local/bin/step-ca --password-file $PWDPATH $CONFIGPATH

60
docker/entrypoint.sh Normal file
View file

@ -0,0 +1,60 @@
#!/bin/bash
set -eo pipefail
# Paraphrased from:
# https://github.com/influxdata/influxdata-docker/blob/0d341f18067c4652dfa8df7dcb24d69bf707363d/influxdb/2.0/entrypoint.sh
# (a repo with no LICENSE.md)
export STEPPATH=$(step path)
# List of env vars required for step ca init
declare -ra REQUIRED_INIT_VARS=(DOCKER_STEPCA_INIT_NAME DOCKER_STEPCA_INIT_DNS_NAMES)
# Ensure all env vars required to run step ca init are set.
function init_if_possible () {
local missing_vars=0
for var in "${REQUIRED_INIT_VARS[@]}"; do
if [ -z "${!var}" ]; then
missing_vars=1
fi
done
if [ ${missing_vars} = 1 ]; then
>&2 echo "there is no ca.json config file; please run step ca init, or provide config parameters via DOCKER_STEPCA_INIT_ vars"
else
step_ca_init "${@}"
fi
}
function generate_password () {
set +o pipefail
< /dev/urandom tr -dc A-Za-z0-9 | head -c40
echo
set -o pipefail
}
# Initialize a CA if not already initialized
function step_ca_init () {
local -a setup_args=(
--name "${DOCKER_STEPCA_INIT_NAME}"
--dns "${DOCKER_STEPCA_INIT_DNS_NAMES}"
--provisioner "${DOCKER_STEPCA_INIT_PROVISIONER_NAME:-admin}"
--password-file "${STEPPATH}/password"
--address ":9000"
)
if [ -n "${DOCKER_STEPCA_INIT_PASSWORD}" ]; then
echo "${DOCKER_STEPCA_INIT_PASSWORD}" > "${STEPPATH}/password"
else
generate_password > "${STEPPATH}/password"
fi
if [ -n "${DOCKER_STEPCA_INIT_SSH}" ]; then
setup_args=("${setup_args[@]}" --ssh)
fi
step ca init "${setup_args[@]}"
mv $STEPPATH/password $PWDPATH
}
if [ ! -f "${STEPPATH}/config/ca.json" ]; then
init_if_possible
fi
exec "${@}"

View file

@ -7,12 +7,20 @@ to manage issues, etc.
## Table of Contents
* [Building From Source](#building-from-source)
* [Asking Support Questions](#asking-support-questions)
* [Reporting Issues](#reporting-issues)
* [Submitting Patches](#submitting-patches)
* [Code Contribution Guidelines](#code-contribution-guidelines)
* [Git Commit Message Guidelines](#git-commit-message-guidelines)
- [Contributing to `step certificates`](#contributing-to-step-certificates)
- [Table of Contents](#table-of-contents)
- [Building From Source](#building-from-source)
- [Build a standard `step-ca`](#build-a-standard-step-ca)
- [Build `step-ca` using CGO](#build-step-ca-using-cgo)
- [The CGO build enables PKCS #11 and YubiKey PIV support](#the-cgo-build-enables-pkcs-11-and-yubikey-piv-support)
- [1. Install PCSC support](#1-install-pcsc-support)
- [2. Build `step-ca`](#2-build-step-ca)
- [Asking Support Questions](#asking-support-questions)
- [Reporting Issues](#reporting-issues)
- [Code Contribution](#code-contribution)
- [Submitting Patches](#submitting-patches)
- [Code Contribution Guidelines](#code-contribution-guidelines)
- [Git Commit Message Guidelines](#git-commit-message-guidelines)
## Building From Source
@ -73,7 +81,7 @@ When the build is complete, you will find binaries in `bin/`.
## Asking Support Questions
Feel free to post a question on our [GitHub Discussions](https://github.com/smallstep/certificates/discussions) page, or find us on [Gitter](https://gitter.im/smallstep/community).
Feel free to post a question on our [GitHub Discussions](https://github.com/smallstep/certificates/discussions) page, or find us on [Discord](https://bit.ly/step-discord).
## Reporting Issues

View file

@ -1,7 +1,7 @@
# Provisioners
> Note: The canonical documentation for `step-ca` provisioners now lives at
> https://smallstep.com/docs/step-ca/configuration#provisioners. Documentation
> https://smallstep.com/docs/step-ca/provisioners. Documentation
> found on this page may be out of date.
Provisioners are people or code that are registered with the CA and authorized

View file

@ -202,7 +202,8 @@ through an example.
[Use TLS Everywhere](https://smallstep.com/blog/use-tls.html) and let us know
what you think of our tools. Get in touch over
[Twitter](twitter.com/smallsteplabs) or through our
[GitHub Discussions](https://github.com/smallstep/certificates/discussions) to chat with us in real time.
[GitHub Discussions](https://github.com/smallstep/certificates/discussions) to find answers to frequently asked questions.
[Discord](https://bit.ly/step-discord) to chat with us in real time.
## Further Reading

3
go.mod
View file

@ -7,6 +7,7 @@ require (
github.com/Masterminds/sprig/v3 v3.1.0
github.com/ThalesIgnite/crypto11 v1.2.4
github.com/aws/aws-sdk-go v1.30.29
github.com/dgraph-io/ristretto v0.0.4-0.20200906165740-41ebdbffecfd // indirect
github.com/go-chi/chi v4.0.2+incompatible
github.com/go-kit/kit v0.10.0 // indirect
github.com/go-piv/piv-go v1.7.0
@ -22,7 +23,7 @@ require (
github.com/rs/xid v1.2.1
github.com/sirupsen/logrus v1.4.2
github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262
github.com/smallstep/nosql v0.3.6
github.com/smallstep/nosql v0.3.8
github.com/stretchr/testify v1.7.0 // indirect
github.com/urfave/cli v1.22.4
go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1

13
go.sum
View file

@ -136,9 +136,10 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8=
github.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE=
github.com/dgraph-io/badger/v2 v2.0.1-rc1.0.20201003150343-5d1bab4fc658 h1:/WBjuutuivOA02gpDtrvrWKw01ugkyt3QnimB7enbtI=
github.com/dgraph-io/badger/v2 v2.0.1-rc1.0.20201003150343-5d1bab4fc658/go.mod h1:2uGEvGm+JSDLd5UAaKIFSbXDcYyeH0fWJP4N2HMMYMI=
github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o=
github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk=
github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
github.com/dgraph-io/ristretto v0.0.4-0.20200906165740-41ebdbffecfd h1:KoJOtZf+6wpQaDTuOWGuo61GxcPBIfhwRxRTaTWGCTc=
github.com/dgraph-io/ristretto v0.0.4-0.20200906165740-41ebdbffecfd/go.mod h1:YylP9MpCYGVZQrly/j/diqcdUetCRRePeBB0c2VGXsA=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
@ -226,8 +227,6 @@ github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
@ -328,6 +327,8 @@ github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.12.3 h1:G5AfA94pHPysR56qqrkO2pxEexdDzrpFJ6yt/VqWxVU=
github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@ -462,8 +463,8 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd
github.com/smallstep/assert v0.0.0-20180720014142-de77670473b5/go.mod h1:TC9A4+RjIOS+HyTH7wG17/gSqVv95uDw2J64dQZx7RE=
github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 h1:unQFBIznI+VYD1/1fApl1A+9VcBk+9dcqGfnePY87LY=
github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262/go.mod h1:MyOHs9Po2fbM1LHej6sBUT8ozbxmMOFG+E+rx/GSGuc=
github.com/smallstep/nosql v0.3.6 h1:cq6a3NwjFJxkVlWU1T4qGskcfEXr0fO1WqQrraDO1Po=
github.com/smallstep/nosql v0.3.6/go.mod h1:h1zC/Z54uNHc8euquLED4qJNCrMHd3nytA141ZZh4qQ=
github.com/smallstep/nosql v0.3.8 h1:1/EWUbbEdz9ai0g9Fd09VekVjtxp+5+gIHpV2PdwW3o=
github.com/smallstep/nosql v0.3.8/go.mod h1:X2qkYpNcW3yjLUvhEHfgGfClpKbFPapewvx7zo4TOFs=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=

View file

@ -46,8 +46,8 @@ var signatureAlgorithmMapping = map[apiv1.SignatureAlgorithm]interface{}{
4096: kmspb.CryptoKeyVersion_RSA_SIGN_PKCS1_4096_SHA256,
},
apiv1.SHA512WithRSA: map[int]kmspb.CryptoKeyVersion_CryptoKeyVersionAlgorithm{
0: kmspb.CryptoKeyVersion_RSA_SIGN_PKCS1_4096_SHA256,
4096: kmspb.CryptoKeyVersion_RSA_SIGN_PKCS1_4096_SHA256,
0: kmspb.CryptoKeyVersion_RSA_SIGN_PKCS1_4096_SHA512,
4096: kmspb.CryptoKeyVersion_RSA_SIGN_PKCS1_4096_SHA512,
},
apiv1.SHA256WithRSAPSS: map[int]kmspb.CryptoKeyVersion_CryptoKeyVersionAlgorithm{
0: kmspb.CryptoKeyVersion_RSA_SIGN_PSS_3072_SHA256,

View file

@ -59,7 +59,9 @@ func Parse(rawuri string) (*URI, error) {
if u.Scheme == "" {
return nil, errors.Errorf("error parsing %s: scheme is missing", rawuri)
}
v, err := url.ParseQuery(u.Opaque)
// Starting with Go 1.17 url.ParseQuery returns an error using semicolon as
// separator.
v, err := url.ParseQuery(strings.ReplaceAll(u.Opaque, ";", "&"))
if err != nil {
return nil, errors.Wrapf(err, "error parsing %s", rawuri)
}

View file

@ -274,3 +274,28 @@ func TestURI_Pin(t *testing.T) {
})
}
}
func TestURI_String(t *testing.T) {
mustParse := func(s string) *URI {
u, err := Parse(s)
if err != nil {
t.Fatal(err)
}
return u
}
tests := []struct {
name string
uri *URI
want string
}{
{"ok new", New("yubikey", url.Values{"slot-id": []string{"9a"}, "foo": []string{"bar"}}), "yubikey:foo=bar;slot-id=9a"},
{"ok parse", mustParse("yubikey:slot-id=9a;foo=bar?bar=zar"), "yubikey:slot-id=9a;foo=bar?bar=zar"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.uri.String(); got != tt.want {
t.Errorf("URI.String() = %v, want %v", got, tt.want)
}
})
}
}

4
scripts/README.md Normal file
View file

@ -0,0 +1,4 @@
# Scripts folder
Please note that `install-step-ra.sh` is referenced on the `files.smallstep.com` S3 website bucket as a redirect to `raw.githubusercontent.com`. If you move it, please update the S3 redirect.

265
scripts/install-step-ra.sh Normal file
View file

@ -0,0 +1,265 @@
#!/bin/bash
set -e
# TODO:
# - Parse params using argbash (argbash.io). Here's a template that I have tested but have not implemented yet:
#
# ARG_OPTIONAL_SINGLE([ca-url], , [the URL of the upstream (issuing) step-ca server])
# ARG_OPTIONAL_SINGLE([fingerprint], , [the SHA256 fingerprint of the upstream peer step-ca server])
# ARG_OPTIONAL_SINGLE([provisioner-name], , [the name of a JWK provisioner on the upstream CA that this RA will use])
# ARG_OPTIONAL_SINGLE([provisioner-password-file], , [the name a file containing the upstream JWK provisioner password])
# ARG_OPTIONAL_REPEATED([dns-name], , [DNS name of this RA that will appear on its TLS certificate; you may pass this flag multiple times])
# ARG_OPTIONAL_SINGLE([listen-address], , [the address (and port #) this RA will listen on, eg. :443 or 127.0.0.1:4443])
# ARG_HELP([This script will install and configure a Registration Authority that connects to an upstream CA running step-ca.])
# ARGBASH_GO
echo "This script will install and start a step-ca server running in Registration Authority (RA) mode."
echo ""
echo "You will need an upstream CA (URL and fingerprint)"
echo "Don't have a CA? Sign up for a hosted CA at smallstep.com — or run your own."
echo ""
# Fail if this script is not run as root.
if ! [ $(id -u) = 0 ]; then
echo "This script must be run as root"
exit 1
fi
# Architecture detection
arch=$(uname -m)
case $arch in
x86_64) arch="amd64" ;;
x86) arch="386" ;;
i686) arch="386" ;;
i386) arch="386" ;;
aarch64) arch="arm64" ;;
armv5*) arch="armv5" ;;
armv6*) arch="armv6" ;;
armv7*) arch="armv7" ;;
esac
if [ "$arch" = "armv5" ]; then
echo "This script doesn't work on armv5 machines"
exit 1
fi
if ! hash jq &> /dev/null; then
echo "This script requires the jq commmand; please install it."
exit 1
fi
if ! hash curl &> /dev/null; then
echo "This script requires the curl commmand; please install it."
exit 1
fi
if ! hash tar &> /dev/null; then
echo "This script requires the tar commmand; please install it."
exit 1
fi
while [ $# -gt 0 ]; do
case "$1" in
--ca-url)
CA_URL="$2"
shift
shift
;;
--fingerprint)
CA_FINGERPRINT="$2"
shift
shift
;;
--provisioner-name)
CA_PROVISIONER_NAME="$2"
shift
shift
;;
--provisioner-password-file)
CA_PROVISIONER_JWK_PASSWORD_FILE="$2"
shift
shift
;;
--dns-names)
RA_DNS_NAMES="$2"
shift
shift
;;
--listen-address)
RA_ADDRESS="$2"
shift
shift
;;
*)
shift
;;
esac
done
# Install step
if ! hash step &> /dev/null; then
echo "Installing 'step' in /usr/bin..."
STEP_VERSION=$(curl -s https://api.github.com/repos/smallstep/cli/releases/latest | jq -r '.tag_name')
curl -sLO https://github.com/smallstep/cli/releases/download/$STEP_VERSION/step_linux_${STEP_VERSION:1}_$arch.tar.gz
tar xvzf step_linux_${STEP_VERSION:1}_$arch.tar.gz
install -m 0755 -t /usr/bin step_${STEP_VERSION:1}/bin/step
rm step_linux_${STEP_VERSION:1}_$arch.tar.gz
rm -rf step_${STEP_VERSION:1}
fi
# Prompt for required parameters
if [ -z "$CA_URL" ]; then
CA_URL=""
while [[ $CA_URL = "" ]]; do
read -p "Issuing CA URL: " CA_URL < /dev/tty
done
fi
if [ -z "$CA_FINGERPRINT" ]; then
CA_FINGERPRINT=""
while [[ $CA_FINGERPRINT = "" ]]; do
read -p "Issuing CA Fingerprint: " CA_FINGERPRINT < /dev/tty
done
fi
echo "Bootstrapping with the CA..."
export STEPPATH=$(mktemp -d)
export STEP_CONSOLE=true
step ca bootstrap --ca-url $CA_URL --fingerprint $CA_FINGERPRINT
if [ -z "$CA_PROVISIONER_NAME" ]; then
declare -a provisioners
readarray -t provisioners < <(step ca provisioner list | jq -r '.[] | select(.type == "JWK") | .name')
provisioners+=("Create provisioner")
printf '%s\n' "${provisioners[@]}"
printf "%b" "\nSelect a JWK provisioner:\n" >&2
select provisioner in "${provisioners[@]}"; do
if [ "$provisioner" == "Create provisioner" ]; then
echo "Creating a JWK provisioner on the upstream CA..."
echo ""
read -p "Label your provisioner (e.g. example-ra): " CA_PROVISIONER_NAME < /dev/tty
step beta ca provisioner add $CA_PROVISIONER_NAME --type JWK --create
break
elif [ -n "$provisioner" ]; then
echo "Using existing provisioner $provisioner."
CA_PROVISIONER_NAME=$provisioner
break
else
echo "Invalid selection!"
fi
done
fi
if [ -z "$RA_DNS_NAMES" ]; then
RA_DNS_NAMES=""
while [[ $RA_DNS_NAMES = "" ]]; do
echo "What DNS names or IP addresses will your RA use?"
read -p "(e.g. acme.example.com[,1.1.1.1,etc.]): " RA_DNS_NAMES < /dev/tty
done
fi
if [ -z "$RA_ADDRESS" ]; then
RA_ADDRESS=""
while [[ $RA_ADDRESS = "" ]] ; do
echo "What address should your RA listen on?"
read -p "(e.g. :443 or 10.2.1.201:4430): " RA_ADDRESS < /dev/tty
done
fi
if [ -z "$CA_PROVISIONER_JWK_PASSWORD_FILE" ]; then
read -s -p "Enter the CA Provisioner Password: " CA_PROVISIONER_JWK_PASSWORD < /dev/tty
printf "%b" "\n"
fi
echo "Installing 'step-ca' in /usr/bin..."
CA_VERSION=$(curl -s https://api.github.com/repos/smallstep/certificates/releases/latest | jq -r '.tag_name')
curl -sLO https://github.com/smallstep/certificates/releases/download/$CA_VERSION/step-ca_linux_${CA_VERSION:1}_$arch.tar.gz
tar -xf step-ca_linux_${CA_VERSION:1}_$arch.tar.gz
install -m 0755 -t /usr/bin step-ca_${CA_VERSION:1}/bin/step-ca
setcap CAP_NET_BIND_SERVICE=+eip $(which step-ca)
rm step-ca_linux_${CA_VERSION:1}_$arch.tar.gz
rm -rf step-ca_${CA_VERSION:1}
echo "Creating 'step' user..."
export STEPPATH=/etc/step-ca
useradd --system --home $(step path) --shell /bin/false step
echo "Creating RA configuration..."
mkdir -p $(step path)/db
mkdir -p $(step path)/config
cat <<EOF > $(step path)/config/ca.json
{
"address": "$RA_ADDRESS",
"dnsNames": ["$RA_DNS_NAMES"],
"db": {
"type": "badgerV2",
"dataSource": "/etc/step-ca/db"
},
"logger": {"format": "text"},
"authority": {
"type": "stepcas",
"certificateAuthority": "$CA_URL",
"certificateAuthorityFingerprint": "$CA_FINGERPRINT",
"certificateIssuer": {
"type" : "jwk",
"provisioner": "$CA_PROVISIONER_NAME"
},
"provisioners": [{
"type": "ACME",
"name": "acme"
}]
},
"tls": {
"cipherSuites": [
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"
],
"minVersion": 1.2,
"maxVersion": 1.3,
"renegotiation": false
}
}
EOF
if ! [ -z "$CA_PROVISIONER_JWK_PASSWORD" ]; then
echo "Saving provisoiner password to $(step path)/password.txt..."
echo $CA_PROVISIONER_JWK_PASSWORD > $(step path)/password.txt
else
echo "Copying provisioner password file to $(step path)/password.txt..."
cp $CA_PROVISIONER_JWK_PASSWORD_FILE $(step path)/password.txt
fi
chmod 440 $(step path)/password.txt
# Add a service to systemd for the RA.
echo "Creating systemd service step-ca.service..."
curl -sL https://raw.githubusercontent.com/smallstep/certificates/master/systemd/step-ca.service \
-o /etc/systemd/system/step-ca.service
echo "Creating RA mode override /etc/systemd/system/step-ca.service.d/local.conf..."
mkdir /etc/systemd/system/step-ca.service.d
cat <<EOF > /etc/systemd/system/step-ca.service.d/local.conf
[Service]
; The empty ExecStart= clears the inherited ExecStart= value
ExecStart=
ExecStart=/usr/bin/step-ca config/ca.json --issuer-password-file password.txt
EOF
echo "Starting step-ca.service..."
systemctl daemon-reload
chown -R step:step $(step path)
systemctl enable --now step-ca
echo "Adding STEPPATH export to /root/.bash_profile..."
echo "export STEPPATH=$STEPPATH" >> /root/.bash_profile
echo "Finished. Check the journal with journalctl -fu step-ca.service"

View file

@ -12,14 +12,10 @@ Environment=STEPPATH=/etc/step-ca \
CERT_LOCATION=/etc/step/certs/%i.crt \
KEY_LOCATION=/etc/step/certs/%i.key
; ExecStartPre checks if the certificate is ready for renewal,
; ExecCondition checks if the certificate is ready for renewal,
; based on the exit status of the command.
; (In systemd 243 and above, you can use ExecCondition= here.)
ExecStartPre=/usr/bin/env bash -c \
'step certificate inspect $CERT_LOCATION --format json --roots "$STEPPATH/certs/root_ca.crt" | \
jq -e "(((.validity.start | fromdate) + \
((.validity.end | fromdate) - (.validity.start | fromdate)) * 0.66) \
- now) <= 0" > /dev/null'
; (In systemd 242 or below, you can use ExecStartPre= here.)
ExecCondition=/usr/bin/step certificate needs-renewal $CERT_LOCATION
; ExecStart renews the certificate, if ExecStartPre was successful.
ExecStart=/usr/bin/step ca renew --force $CERT_LOCATION $KEY_LOCATION