Merge branch 'master' into hs/acme-revocation

pull/625/head
Herman Slatman 3 years ago
commit 2d50c96d99
No known key found for this signature in database
GPG Key ID: F4D8A44EA0A75A4F

@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-20.04
strategy:
matrix:
go: [ '1.15', '1.16', '1.17' ]
go: [ '1.16', '1.17' ]
steps:
-
name: Checkout
@ -33,7 +33,7 @@ jobs:
uses: golangci/golangci-lint-action@v2
with:
# Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
version: 'v1.41.0'
version: 'v1.43.0'
# Optional: working directory, useful for monorepos
# working-directory: somedir
@ -58,6 +58,7 @@ jobs:
run: V=1 make ci
-
name: Codecov
if: matrix.go == '1.17'
uses: codecov/codecov-action@v1.2.1
with:
file: ./coverage.out # optional

@ -73,9 +73,3 @@ issues:
- error strings should not be capitalized or end with punctuation or a newline
- Wrapf call needs 1 arg but has 2 args
- cs.NegotiatedProtocolIsMutual is deprecated
# golangci.com configuration
# https://github.com/golangci/golangci/wiki/Configuration
service:
golangci-lint-version: 1.19.x # use the fixed version to not introduce new linters unexpectedly
prepare:
- echo "here I can run custom commands, but no preparation needed for this repo"

@ -4,15 +4,23 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## [Unreleased - 0.17.7] - DATE
## [Unreleased - 0.18.1] - DATE
### Added
- Support for generate extractable keys and certificates on a pkcs#11 module.
### Changed
### Deprecated
### Removed
### Fixed
### Security
## [0.18.0] - 2021-11-17
### Added
- Support for multiple certificate authority contexts.
- Support for generating extractable keys and certificates on a pkcs#11 module.
### Changed
- Support two latest versions of golang (1.16, 1.17)
### Deprecated
- go 1.15 support
## [0.17.6] - 2021-10-20
### Notes
- 0.17.5 failed in CI/CD

@ -5,7 +5,7 @@ import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"io"
"net/http/httptest"
"net/url"
"testing"
@ -263,7 +263,7 @@ func TestHandler_GetOrdersByAccountID(t *testing.T) {
assert.Equals(t, res.StatusCode, tc.statusCode)
body, err := ioutil.ReadAll(res.Body)
body, err := io.ReadAll(res.Body)
res.Body.Close()
assert.FatalError(t, err)
@ -468,7 +468,7 @@ func TestHandler_NewAccount(t *testing.T) {
assert.Equals(t, res.StatusCode, tc.statusCode)
body, err := ioutil.ReadAll(res.Body)
body, err := io.ReadAll(res.Body)
res.Body.Close()
assert.FatalError(t, err)
@ -668,7 +668,7 @@ func TestHandler_GetOrUpdateAccount(t *testing.T) {
assert.Equals(t, res.StatusCode, tc.statusCode)
body, err := ioutil.ReadAll(res.Body)
body, err := io.ReadAll(res.Body)
res.Body.Close()
assert.FatalError(t, err)

@ -17,7 +17,7 @@ import (
)
func link(url, typ string) string {
return fmt.Sprintf("<%s>;rel=\"%s\"", url, typ)
return fmt.Sprintf("<%s>;rel=%q", url, typ)
}
// Clock that returns time in UTC rounded to seconds.

@ -7,7 +7,7 @@ import (
"encoding/json"
"encoding/pem"
"fmt"
"io/ioutil"
"io"
"net/http"
"net/http/httptest"
"net/url"
@ -89,7 +89,7 @@ func TestHandler_GetDirectory(t *testing.T) {
assert.Equals(t, res.StatusCode, tc.statusCode)
body, err := ioutil.ReadAll(res.Body)
body, err := io.ReadAll(res.Body)
res.Body.Close()
assert.FatalError(t, err)
@ -261,7 +261,7 @@ func TestHandler_GetAuthorization(t *testing.T) {
assert.Equals(t, res.StatusCode, tc.statusCode)
body, err := ioutil.ReadAll(res.Body)
body, err := io.ReadAll(res.Body)
res.Body.Close()
assert.FatalError(t, err)
@ -404,7 +404,7 @@ func TestHandler_GetCertificate(t *testing.T) {
assert.Equals(t, res.StatusCode, tc.statusCode)
body, err := ioutil.ReadAll(res.Body)
body, err := io.ReadAll(res.Body)
res.Body.Close()
assert.FatalError(t, err)
@ -660,7 +660,7 @@ func TestHandler_GetChallenge(t *testing.T) {
assert.Equals(t, res.StatusCode, tc.statusCode)
body, err := ioutil.ReadAll(res.Body)
body, err := io.ReadAll(res.Body)
res.Body.Close()
assert.FatalError(t, err)

@ -4,7 +4,7 @@ import (
"context"
"crypto/rsa"
"errors"
"io/ioutil"
"io"
"net/http"
"net/url"
"strings"
@ -118,7 +118,7 @@ func (h *Handler) verifyContentType(next nextHTTP) nextHTTP {
// parseJWS is a middleware that parses a request body into a JSONWebSignature struct.
func (h *Handler) parseJWS(next nextHTTP) nextHTTP {
return func(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
body, err := io.ReadAll(r.Body)
if err != nil {
api.WriteError(w, acme.WrapErrorISE(err, "failed to read request body"))
return
@ -413,7 +413,7 @@ func (h *Handler) verifyAndExtractJWSPayload(next nextHTTP) nextHTTP {
}
ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{
value: payload,
isPostAsGet: string(payload) == "",
isPostAsGet: len(payload) == 0,
isEmptyJSON: string(payload) == "{}",
})
next(w, r.WithContext(ctx))

@ -8,7 +8,6 @@ import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/http/httptest"
"net/url"
@ -148,7 +147,7 @@ func TestHandler_addNonce(t *testing.T) {
assert.Equals(t, res.StatusCode, tc.statusCode)
body, err := ioutil.ReadAll(res.Body)
body, err := io.ReadAll(res.Body)
res.Body.Close()
assert.FatalError(t, err)
@ -205,7 +204,7 @@ func TestHandler_addDirLink(t *testing.T) {
assert.Equals(t, res.StatusCode, tc.statusCode)
body, err := ioutil.ReadAll(res.Body)
body, err := io.ReadAll(res.Body)
res.Body.Close()
assert.FatalError(t, err)
@ -332,7 +331,7 @@ func TestHandler_verifyContentType(t *testing.T) {
assert.Equals(t, res.StatusCode, tc.statusCode)
body, err := ioutil.ReadAll(res.Body)
body, err := io.ReadAll(res.Body)
res.Body.Close()
assert.FatalError(t, err)
@ -400,7 +399,7 @@ func TestHandler_isPostAsGet(t *testing.T) {
assert.Equals(t, res.StatusCode, tc.statusCode)
body, err := ioutil.ReadAll(res.Body)
body, err := io.ReadAll(res.Body)
res.Body.Close()
assert.FatalError(t, err)
@ -490,7 +489,7 @@ func TestHandler_parseJWS(t *testing.T) {
assert.Equals(t, res.StatusCode, tc.statusCode)
body, err := ioutil.ReadAll(res.Body)
body, err := io.ReadAll(res.Body)
res.Body.Close()
assert.FatalError(t, err)
@ -689,7 +688,7 @@ func TestHandler_verifyAndExtractJWSPayload(t *testing.T) {
assert.Equals(t, res.StatusCode, tc.statusCode)
body, err := ioutil.ReadAll(res.Body)
body, err := io.ReadAll(res.Body)
res.Body.Close()
assert.FatalError(t, err)
@ -891,7 +890,7 @@ func TestHandler_lookupJWK(t *testing.T) {
assert.Equals(t, res.StatusCode, tc.statusCode)
body, err := ioutil.ReadAll(res.Body)
body, err := io.ReadAll(res.Body)
res.Body.Close()
assert.FatalError(t, err)
@ -1087,7 +1086,7 @@ func TestHandler_extractJWK(t *testing.T) {
assert.Equals(t, res.StatusCode, tc.statusCode)
body, err := ioutil.ReadAll(res.Body)
body, err := io.ReadAll(res.Body)
res.Body.Close()
assert.FatalError(t, err)
@ -1454,7 +1453,7 @@ func TestHandler_validateJWS(t *testing.T) {
assert.Equals(t, res.StatusCode, tc.statusCode)
body, err := ioutil.ReadAll(res.Body)
body, err := io.ReadAll(res.Body)
res.Body.Close()
assert.FatalError(t, err)

@ -7,7 +7,7 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
"io"
"net/http/httptest"
"net/url"
"reflect"
@ -430,7 +430,7 @@ func TestHandler_GetOrder(t *testing.T) {
assert.Equals(t, res.StatusCode, tc.statusCode)
body, err := ioutil.ReadAll(res.Body)
body, err := io.ReadAll(res.Body)
res.Body.Close()
assert.FatalError(t, err)
@ -1343,7 +1343,7 @@ func TestHandler_NewOrder(t *testing.T) {
assert.Equals(t, res.StatusCode, tc.statusCode)
body, err := ioutil.ReadAll(res.Body)
body, err := io.ReadAll(res.Body)
res.Body.Close()
assert.FatalError(t, err)
@ -1633,7 +1633,7 @@ func TestHandler_FinalizeOrder(t *testing.T) {
assert.Equals(t, res.StatusCode, tc.statusCode)
body, err := ioutil.ReadAll(res.Body)
body, err := io.ReadAll(res.Body)
res.Body.Close()
assert.FatalError(t, err)

@ -12,7 +12,7 @@ import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"io"
"net"
"net/http"
"net/url"
@ -92,7 +92,7 @@ func http01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWeb
"error doing http GET for url %s with status code %d", u, resp.StatusCode))
}
body, err := ioutil.ReadAll(resp.Body)
body, err := io.ReadAll(resp.Body)
if err != nil {
return WrapErrorISE(err, "error reading "+
"response body for url %s", u)

@ -15,7 +15,6 @@ import (
"encoding/hex"
"fmt"
"io"
"io/ioutil"
"math/big"
"net"
"net/http"
@ -707,7 +706,7 @@ func TestHTTP01Validate(t *testing.T) {
vo: &ValidateChallengeOptions{
HTTPGet: func(url string) (*http.Response, error) {
return &http.Response{
Body: ioutil.NopCloser(bytes.NewBufferString("foo")),
Body: io.NopCloser(bytes.NewBufferString("foo")),
}, nil
},
},
@ -733,7 +732,7 @@ func TestHTTP01Validate(t *testing.T) {
vo: &ValidateChallengeOptions{
HTTPGet: func(url string) (*http.Response, error) {
return &http.Response{
Body: ioutil.NopCloser(bytes.NewBufferString("foo")),
Body: io.NopCloser(bytes.NewBufferString("foo")),
}, nil
},
},
@ -775,7 +774,7 @@ func TestHTTP01Validate(t *testing.T) {
vo: &ValidateChallengeOptions{
HTTPGet: func(url string) (*http.Response, error) {
return &http.Response{
Body: ioutil.NopCloser(bytes.NewBufferString("foo")),
Body: io.NopCloser(bytes.NewBufferString("foo")),
}, nil
},
},
@ -818,7 +817,7 @@ func TestHTTP01Validate(t *testing.T) {
vo: &ValidateChallengeOptions{
HTTPGet: func(url string) (*http.Response, error) {
return &http.Response{
Body: ioutil.NopCloser(bytes.NewBufferString(expKeyAuth)),
Body: io.NopCloser(bytes.NewBufferString(expKeyAuth)),
}, nil
},
},
@ -860,7 +859,7 @@ func TestHTTP01Validate(t *testing.T) {
vo: &ValidateChallengeOptions{
HTTPGet: func(url string) (*http.Response, error) {
return &http.Response{
Body: ioutil.NopCloser(bytes.NewBufferString(expKeyAuth)),
Body: io.NopCloser(bytes.NewBufferString(expKeyAuth)),
}, nil
},
},

@ -16,7 +16,7 @@ import (
"encoding/json"
"encoding/pem"
"fmt"
"io/ioutil"
"io"
"math/big"
"net/http"
"net/http/httptest"
@ -788,7 +788,7 @@ func Test_caHandler_Health(t *testing.T) {
t.Errorf("caHandler.Health StatusCode = %d, wants 200", res.StatusCode)
}
body, err := ioutil.ReadAll(res.Body)
body, err := io.ReadAll(res.Body)
res.Body.Close()
if err != nil {
t.Errorf("caHandler.Health unexpected error = %v", err)
@ -829,7 +829,7 @@ func Test_caHandler_Root(t *testing.T) {
t.Errorf("caHandler.Root StatusCode = %d, wants %d", res.StatusCode, tt.statusCode)
}
body, err := ioutil.ReadAll(res.Body)
body, err := io.ReadAll(res.Body)
res.Body.Close()
if err != nil {
t.Errorf("caHandler.Root unexpected error = %v", err)
@ -902,7 +902,7 @@ func Test_caHandler_Sign(t *testing.T) {
t.Errorf("caHandler.Root StatusCode = %d, wants %d", res.StatusCode, tt.statusCode)
}
body, err := ioutil.ReadAll(res.Body)
body, err := io.ReadAll(res.Body)
res.Body.Close()
if err != nil {
t.Errorf("caHandler.Root unexpected error = %v", err)
@ -954,7 +954,7 @@ func Test_caHandler_Renew(t *testing.T) {
t.Errorf("caHandler.Renew StatusCode = %d, wants %d", res.StatusCode, tt.statusCode)
}
body, err := ioutil.ReadAll(res.Body)
body, err := io.ReadAll(res.Body)
res.Body.Close()
if err != nil {
t.Errorf("caHandler.Renew unexpected error = %v", err)
@ -1015,7 +1015,7 @@ func Test_caHandler_Rekey(t *testing.T) {
t.Errorf("caHandler.Rekey StatusCode = %d, wants %d", res.StatusCode, tt.statusCode)
}
body, err := ioutil.ReadAll(res.Body)
body, err := io.ReadAll(res.Body)
res.Body.Close()
if err != nil {
t.Errorf("caHandler.Rekey unexpected error = %v", err)
@ -1038,12 +1038,12 @@ func Test_caHandler_Provisioners(t *testing.T) {
r *http.Request
}
req, err := http.NewRequest("GET", "http://example.com/provisioners?cursor=foo&limit=20", nil)
req, err := http.NewRequest("GET", "http://example.com/provisioners?cursor=foo&limit=20", http.NoBody)
if err != nil {
t.Fatal(err)
}
reqLimitFail, err := http.NewRequest("GET", "http://example.com/provisioners?limit=abc", nil)
reqLimitFail, err := http.NewRequest("GET", "http://example.com/provisioners?limit=abc", http.NoBody)
if err != nil {
t.Fatal(err)
}
@ -1105,7 +1105,7 @@ func Test_caHandler_Provisioners(t *testing.T) {
if res.StatusCode != tt.statusCode {
t.Errorf("caHandler.Provisioners StatusCode = %d, wants %d", res.StatusCode, tt.statusCode)
}
body, err := ioutil.ReadAll(res.Body)
body, err := io.ReadAll(res.Body)
res.Body.Close()
if err != nil {
t.Errorf("caHandler.Provisioners unexpected error = %v", err)
@ -1175,7 +1175,7 @@ func Test_caHandler_ProvisionerKey(t *testing.T) {
if res.StatusCode != tt.statusCode {
t.Errorf("caHandler.Provisioners StatusCode = %d, wants %d", res.StatusCode, tt.statusCode)
}
body, err := ioutil.ReadAll(res.Body)
body, err := io.ReadAll(res.Body)
res.Body.Close()
if err != nil {
t.Errorf("caHandler.Provisioners unexpected error = %v", err)
@ -1225,7 +1225,7 @@ func Test_caHandler_Roots(t *testing.T) {
t.Errorf("caHandler.Roots StatusCode = %d, wants %d", res.StatusCode, tt.statusCode)
}
body, err := ioutil.ReadAll(res.Body)
body, err := io.ReadAll(res.Body)
res.Body.Close()
if err != nil {
t.Errorf("caHandler.Roots unexpected error = %v", err)
@ -1271,7 +1271,7 @@ func Test_caHandler_Federation(t *testing.T) {
t.Errorf("caHandler.Federation StatusCode = %d, wants %d", res.StatusCode, tt.statusCode)
}
body, err := ioutil.ReadAll(res.Body)
body, err := io.ReadAll(res.Body)
res.Body.Close()
if err != nil {
t.Errorf("caHandler.Federation unexpected error = %v", err)

@ -6,7 +6,7 @@ import (
"crypto/tls"
"crypto/x509"
"encoding/json"
"io/ioutil"
"io"
"net/http"
"net/http/httptest"
"strings"
@ -233,7 +233,7 @@ func Test_caHandler_Revoke(t *testing.T) {
assert.Equals(t, tc.statusCode, res.StatusCode)
body, err := ioutil.ReadAll(res.Body)
body, err := io.ReadAll(res.Body)
res.Body.Close()
assert.FatalError(t, err)

@ -10,7 +10,7 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
"io"
"net/http"
"net/http/httptest"
"reflect"
@ -299,14 +299,14 @@ func Test_caHandler_SSHSign(t *testing.T) {
body []byte
statusCode int
}{
{"ok-user", userReq, nil, user, nil, nil, nil, nil, nil, []byte(fmt.Sprintf(`{"crt":"%s"}`, userB64)), http.StatusCreated},
{"ok-host", hostReq, nil, host, nil, nil, nil, nil, nil, []byte(fmt.Sprintf(`{"crt":"%s"}`, hostB64)), http.StatusCreated},
{"ok-user-add", userAddReq, nil, user, nil, user, nil, nil, nil, []byte(fmt.Sprintf(`{"crt":"%s","addUserCrt":"%s"}`, userB64, userB64)), http.StatusCreated},
{"ok-user-identity", userIdentityReq, nil, user, nil, user, nil, identityCerts, nil, []byte(fmt.Sprintf(`{"crt":"%s","identityCrt":[%s]}`, userB64, identityCertsPEM)), http.StatusCreated},
{"ok-user", userReq, nil, user, nil, nil, nil, nil, nil, []byte(fmt.Sprintf(`{"crt":%q}`, userB64)), http.StatusCreated},
{"ok-host", hostReq, nil, host, nil, nil, nil, nil, nil, []byte(fmt.Sprintf(`{"crt":%q}`, hostB64)), http.StatusCreated},
{"ok-user-add", userAddReq, nil, user, nil, user, nil, nil, nil, []byte(fmt.Sprintf(`{"crt":%q,"addUserCrt":%q}`, userB64, userB64)), http.StatusCreated},
{"ok-user-identity", userIdentityReq, nil, user, nil, user, nil, identityCerts, nil, []byte(fmt.Sprintf(`{"crt":%q,"identityCrt":[%s]}`, userB64, identityCertsPEM)), http.StatusCreated},
{"fail-body", []byte("bad-json"), nil, nil, nil, nil, nil, nil, nil, nil, http.StatusBadRequest},
{"fail-validate", []byte("{}"), nil, nil, nil, nil, nil, nil, nil, nil, http.StatusBadRequest},
{"fail-publicKey", []byte(`{"publicKey":"Zm9v","ott":"ott"}`), nil, nil, nil, nil, nil, nil, nil, nil, http.StatusBadRequest},
{"fail-publicKey", []byte(fmt.Sprintf(`{"publicKey":"%s","ott":"ott","addUserPublicKey":"Zm9v"}`, base64.StdEncoding.EncodeToString(user.Key.Marshal()))), nil, nil, nil, nil, nil, nil, nil, nil, http.StatusBadRequest},
{"fail-publicKey", []byte(fmt.Sprintf(`{"publicKey":%q,"ott":"ott","addUserPublicKey":"Zm9v"}`, base64.StdEncoding.EncodeToString(user.Key.Marshal()))), nil, nil, nil, nil, nil, nil, nil, nil, http.StatusBadRequest},
{"fail-authorize", userReq, fmt.Errorf("an-error"), nil, nil, nil, nil, nil, nil, nil, http.StatusUnauthorized},
{"fail-signSSH", userReq, nil, nil, fmt.Errorf("an-error"), nil, nil, nil, nil, nil, http.StatusForbidden},
{"fail-SignSSHAddUser", userAddReq, nil, user, nil, nil, fmt.Errorf("an-error"), nil, nil, nil, http.StatusForbidden},
@ -338,7 +338,7 @@ func Test_caHandler_SSHSign(t *testing.T) {
t.Errorf("caHandler.SignSSH StatusCode = %d, wants %d", res.StatusCode, tt.statusCode)
}
body, err := ioutil.ReadAll(res.Body)
body, err := io.ReadAll(res.Body)
res.Body.Close()
if err != nil {
t.Errorf("caHandler.SignSSH unexpected error = %v", err)
@ -368,10 +368,10 @@ func Test_caHandler_SSHRoots(t *testing.T) {
body []byte
statusCode int
}{
{"ok", &authority.SSHKeys{HostKeys: []ssh.PublicKey{host}, UserKeys: []ssh.PublicKey{user}}, nil, []byte(fmt.Sprintf(`{"userKey":["%s"],"hostKey":["%s"]}`, userB64, hostB64)), http.StatusOK},
{"many", &authority.SSHKeys{HostKeys: []ssh.PublicKey{host, host}, UserKeys: []ssh.PublicKey{user, user}}, nil, []byte(fmt.Sprintf(`{"userKey":["%s","%s"],"hostKey":["%s","%s"]}`, userB64, userB64, hostB64, hostB64)), http.StatusOK},
{"user", &authority.SSHKeys{UserKeys: []ssh.PublicKey{user}}, nil, []byte(fmt.Sprintf(`{"userKey":["%s"]}`, userB64)), http.StatusOK},
{"host", &authority.SSHKeys{HostKeys: []ssh.PublicKey{host}}, nil, []byte(fmt.Sprintf(`{"hostKey":["%s"]}`, hostB64)), http.StatusOK},
{"ok", &authority.SSHKeys{HostKeys: []ssh.PublicKey{host}, UserKeys: []ssh.PublicKey{user}}, nil, []byte(fmt.Sprintf(`{"userKey":[%q],"hostKey":[%q]}`, userB64, hostB64)), http.StatusOK},
{"many", &authority.SSHKeys{HostKeys: []ssh.PublicKey{host, host}, UserKeys: []ssh.PublicKey{user, user}}, nil, []byte(fmt.Sprintf(`{"userKey":[%q,%q],"hostKey":[%q,%q]}`, userB64, userB64, hostB64, hostB64)), http.StatusOK},
{"user", &authority.SSHKeys{UserKeys: []ssh.PublicKey{user}}, nil, []byte(fmt.Sprintf(`{"userKey":[%q]}`, userB64)), http.StatusOK},
{"host", &authority.SSHKeys{HostKeys: []ssh.PublicKey{host}}, nil, []byte(fmt.Sprintf(`{"hostKey":[%q]}`, hostB64)), http.StatusOK},
{"empty", &authority.SSHKeys{}, nil, nil, http.StatusNotFound},
{"error", nil, fmt.Errorf("an error"), nil, http.StatusInternalServerError},
}
@ -392,7 +392,7 @@ func Test_caHandler_SSHRoots(t *testing.T) {
t.Errorf("caHandler.SSHRoots StatusCode = %d, wants %d", res.StatusCode, tt.statusCode)
}
body, err := ioutil.ReadAll(res.Body)
body, err := io.ReadAll(res.Body)
res.Body.Close()
if err != nil {
t.Errorf("caHandler.SSHRoots unexpected error = %v", err)
@ -422,10 +422,10 @@ func Test_caHandler_SSHFederation(t *testing.T) {
body []byte
statusCode int
}{
{"ok", &authority.SSHKeys{HostKeys: []ssh.PublicKey{host}, UserKeys: []ssh.PublicKey{user}}, nil, []byte(fmt.Sprintf(`{"userKey":["%s"],"hostKey":["%s"]}`, userB64, hostB64)), http.StatusOK},
{"many", &authority.SSHKeys{HostKeys: []ssh.PublicKey{host, host}, UserKeys: []ssh.PublicKey{user, user}}, nil, []byte(fmt.Sprintf(`{"userKey":["%s","%s"],"hostKey":["%s","%s"]}`, userB64, userB64, hostB64, hostB64)), http.StatusOK},
{"user", &authority.SSHKeys{UserKeys: []ssh.PublicKey{user}}, nil, []byte(fmt.Sprintf(`{"userKey":["%s"]}`, userB64)), http.StatusOK},
{"host", &authority.SSHKeys{HostKeys: []ssh.PublicKey{host}}, nil, []byte(fmt.Sprintf(`{"hostKey":["%s"]}`, hostB64)), http.StatusOK},
{"ok", &authority.SSHKeys{HostKeys: []ssh.PublicKey{host}, UserKeys: []ssh.PublicKey{user}}, nil, []byte(fmt.Sprintf(`{"userKey":[%q],"hostKey":[%q]}`, userB64, hostB64)), http.StatusOK},
{"many", &authority.SSHKeys{HostKeys: []ssh.PublicKey{host, host}, UserKeys: []ssh.PublicKey{user, user}}, nil, []byte(fmt.Sprintf(`{"userKey":[%q,%q],"hostKey":[%q,%q]}`, userB64, userB64, hostB64, hostB64)), http.StatusOK},
{"user", &authority.SSHKeys{UserKeys: []ssh.PublicKey{user}}, nil, []byte(fmt.Sprintf(`{"userKey":[%q]}`, userB64)), http.StatusOK},
{"host", &authority.SSHKeys{HostKeys: []ssh.PublicKey{host}}, nil, []byte(fmt.Sprintf(`{"hostKey":[%q]}`, hostB64)), http.StatusOK},
{"empty", &authority.SSHKeys{}, nil, nil, http.StatusNotFound},
{"error", nil, fmt.Errorf("an error"), nil, http.StatusInternalServerError},
}
@ -446,7 +446,7 @@ func Test_caHandler_SSHFederation(t *testing.T) {
t.Errorf("caHandler.SSHFederation StatusCode = %d, wants %d", res.StatusCode, tt.statusCode)
}
body, err := ioutil.ReadAll(res.Body)
body, err := io.ReadAll(res.Body)
res.Body.Close()
if err != nil {
t.Errorf("caHandler.SSHFederation unexpected error = %v", err)
@ -506,7 +506,7 @@ func Test_caHandler_SSHConfig(t *testing.T) {
t.Errorf("caHandler.SSHConfig StatusCode = %d, wants %d", res.StatusCode, tt.statusCode)
}
body, err := ioutil.ReadAll(res.Body)
body, err := io.ReadAll(res.Body)
res.Body.Close()
if err != nil {
t.Errorf("caHandler.SSHConfig unexpected error = %v", err)
@ -553,7 +553,7 @@ func Test_caHandler_SSHCheckHost(t *testing.T) {
t.Errorf("caHandler.SSHCheckHost StatusCode = %d, wants %d", res.StatusCode, tt.statusCode)
}
body, err := ioutil.ReadAll(res.Body)
body, err := io.ReadAll(res.Body)
res.Body.Close()
if err != nil {
t.Errorf("caHandler.SSHCheckHost unexpected error = %v", err)
@ -604,7 +604,7 @@ func Test_caHandler_SSHGetHosts(t *testing.T) {
t.Errorf("caHandler.SSHGetHosts StatusCode = %d, wants %d", res.StatusCode, tt.statusCode)
}
body, err := ioutil.ReadAll(res.Body)
body, err := io.ReadAll(res.Body)
res.Body.Close()
if err != nil {
t.Errorf("caHandler.SSHGetHosts unexpected error = %v", err)
@ -659,7 +659,7 @@ func Test_caHandler_SSHBastion(t *testing.T) {
t.Errorf("caHandler.SSHBastion StatusCode = %d, wants %d", res.StatusCode, tt.statusCode)
}
body, err := ioutil.ReadAll(res.Body)
body, err := io.ReadAll(res.Body)
res.Body.Close()
if err != nil {
t.Errorf("caHandler.SSHBastion unexpected error = %v", err)

@ -3,7 +3,6 @@ package api
import (
"encoding/json"
"io"
"io/ioutil"
"log"
"net/http"
@ -102,7 +101,7 @@ func ReadJSON(r io.Reader, v interface{}) error {
// ReadProtoJSON reads JSON from the request body and stores it in the value
// pointed by v.
func ReadProtoJSON(r io.Reader, m proto.Message) error {
data, err := ioutil.ReadAll(r)
data, err := io.ReadAll(r)
if err != nil {
return errs.Wrap(http.StatusBadRequest, err, "error reading request body")
}

@ -7,8 +7,8 @@ import (
"crypto/x509"
"encoding/hex"
"fmt"
"io/ioutil"
"net"
"os"
"reflect"
"testing"
"time"
@ -195,7 +195,7 @@ func TestAuthority_GetDatabase(t *testing.T) {
}
func TestNewEmbedded(t *testing.T) {
caPEM, err := ioutil.ReadFile("testdata/certs/root_ca.crt")
caPEM, err := os.ReadFile("testdata/certs/root_ca.crt")
assert.FatalError(t, err)
crt, err := pemutil.ReadCertificate("testdata/certs/intermediate_ca.crt")
@ -268,7 +268,7 @@ func TestNewEmbedded(t *testing.T) {
}
func TestNewEmbedded_Sign(t *testing.T) {
caPEM, err := ioutil.ReadFile("testdata/certs/root_ca.crt")
caPEM, err := os.ReadFile("testdata/certs/root_ca.crt")
assert.FatalError(t, err)
crt, err := pemutil.ReadCertificate("testdata/certs/intermediate_ca.crt")
@ -294,7 +294,7 @@ func TestNewEmbedded_Sign(t *testing.T) {
}
func TestNewEmbedded_GetTLSCertificate(t *testing.T) {
caPEM, err := ioutil.ReadFile("testdata/certs/root_ca.crt")
caPEM, err := os.ReadFile("testdata/certs/root_ca.crt")
assert.FatalError(t, err)
crt, err := pemutil.ReadCertificate("testdata/certs/intermediate_ca.crt")

@ -2,14 +2,14 @@ package authority
import (
"encoding/json"
"io/ioutil"
"net/url"
"os"
"path/filepath"
"strings"
"github.com/pkg/errors"
"github.com/smallstep/certificates/authority/provisioner"
"go.step.sm/cli-utils/config"
"go.step.sm/cli-utils/step"
"go.step.sm/linkedca"
"google.golang.org/protobuf/types/known/structpb"
)
@ -245,7 +245,7 @@ func mustReadFileOrURI(fn string, m map[string][]byte) string {
return ""
}
stepPath := filepath.ToSlash(config.StepPath())
stepPath := filepath.ToSlash(step.Path())
if !strings.HasSuffix(stepPath, "/") {
stepPath += "/"
}
@ -257,7 +257,7 @@ func mustReadFileOrURI(fn string, m map[string][]byte) string {
panic(err)
}
if ok {
b, err := ioutil.ReadFile(config.StepAbs(fn))
b, err := os.ReadFile(step.Abs(fn))
if err != nil {
panic(errors.Wrapf(err, "error reading %s", fn))
}

@ -9,9 +9,10 @@ import (
"encoding/json"
"encoding/pem"
"fmt"
"io/ioutil"
"io"
"net"
"net/http"
"os"
"strings"
"time"
@ -165,7 +166,7 @@ func newAWSConfig(certPath string) (*awsConfig, error) {
if certPath == "" {
certBytes = []byte(awsCertificate)
} else {
if b, err := ioutil.ReadFile(certPath); err == nil {
if b, err := os.ReadFile(certPath); err == nil {
certBytes = b
} else {
return nil, errors.Wrapf(err, "error reading %s", certPath)
@ -569,7 +570,7 @@ func (p *AWS) readURLv2(url string) (*http.Response, error) {
client := http.Client{}
// first get the token
req, err := http.NewRequest(http.MethodPut, p.config.tokenURL, nil)
req, err := http.NewRequest(http.MethodPut, p.config.tokenURL, http.NoBody)
if err != nil {
return nil, err
}
@ -582,7 +583,7 @@ func (p *AWS) readURLv2(url string) (*http.Response, error) {
if resp.StatusCode >= 400 {
return nil, fmt.Errorf("Request for API token returned non-successful status code %d", resp.StatusCode)
}
token, err := ioutil.ReadAll(resp.Body)
token, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
@ -602,7 +603,7 @@ func (p *AWS) readURLv2(url string) (*http.Response, error) {
func (p *AWS) readResponseBody(resp *http.Response) ([]byte, error) {
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
b, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}

@ -6,7 +6,7 @@ import (
"crypto/x509"
"encoding/hex"
"encoding/json"
"io/ioutil"
"io"
"net/http"
"regexp"
"strings"
@ -173,7 +173,7 @@ func (p *Azure) GetIdentityToken(subject, caURL string) (string, error) {
}
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
b, err := io.ReadAll(resp.Body)
if err != nil {
return "", errors.Wrap(err, "error reading identity token response")
}

@ -107,7 +107,7 @@ func TestAzure_GetIdentityToken(t *testing.T) {
w.Write([]byte(t1))
default:
w.Header().Add("Content-Type", "application/json")
w.Write([]byte(fmt.Sprintf(`{"access_token":"%s"}`, t1)))
fmt.Fprintf(w, `{"access_token":"%s"}`, t1)
}
}))
defer srv.Close()

@ -7,7 +7,7 @@ import (
"crypto/x509"
"encoding/hex"
"fmt"
"io/ioutil"
"io"
"net/http"
"net/url"
"strings"
@ -183,7 +183,7 @@ func (p *GCP) GetIdentityToken(subject, caURL string) (string, error) {
return "", errors.Wrap(err, "error doing identity request, are you in a GCP VM?")
}
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
b, err := io.ReadAll(resp.Body)
if err != nil {
return "", errors.Wrap(err, "error on identity request")
}

@ -8,12 +8,15 @@ import (
"crypto/x509/pkix"
"encoding/asn1"
"encoding/json"
"fmt"
"net"
"net/http"
"net/url"
"reflect"
"time"
"github.com/pkg/errors"
"github.com/smallstep/certificates/errs"
"go.step.sm/crypto/x509util"
)
@ -369,8 +372,19 @@ func newValidityValidator(min, max time.Duration) *validityValidator {
return &validityValidator{min: min, max: max}
}
// TODO(mariano): refactor errs package to allow sending real errors to the
// user.
func badRequest(format string, args ...interface{}) error {
msg := fmt.Sprintf(format, args...)
return &errs.Error{
Status: http.StatusBadRequest,
Msg: msg,
Err: errors.New(msg),
}
}
// Valid validates the certificate validity settings (notBefore/notAfter) and
// and total duration.
// total duration.
func (v *validityValidator) Valid(cert *x509.Certificate, o SignOptions) error {
var (
na = cert.NotAfter.Truncate(time.Second)
@ -381,22 +395,20 @@ func (v *validityValidator) Valid(cert *x509.Certificate, o SignOptions) error {
d := na.Sub(nb)
if na.Before(now) {
return errors.Errorf("notAfter cannot be in the past; na=%v", na)
return badRequest("notAfter cannot be in the past; na=%v", na)
}
if na.Before(nb) {
return errors.Errorf("notAfter cannot be before notBefore; na=%v, nb=%v", na, nb)
return badRequest("notAfter cannot be before notBefore; na=%v, nb=%v", na, nb)
}
if d < v.min {
return errors.Errorf("requested duration of %v is less than the authorized minimum certificate duration of %v",
d, v.min)
return badRequest("requested duration of %v is less than the authorized minimum certificate duration of %v", d, v.min)
}
// NOTE: this check is not "technically correct". We're allowing the max
// duration of a cert to be "max + backdate" and not all certificates will
// be backdated (e.g. if a user passes the NotBefore value then we do not
// apply a backdate). This is good enough.
if d > v.max+o.Backdate {
return errors.Errorf("requested duration of %v is more than the authorized maximum certificate duration of %v",
d, v.max+o.Backdate)
return badRequest("requested duration of %v is more than the authorized maximum certificate duration of %v", d, v.max+o.Backdate)
}
return nil
}

@ -335,11 +335,11 @@ type sshCertValidityValidator struct {
func (v *sshCertValidityValidator) Valid(cert *ssh.Certificate, opts SignSSHOptions) error {
switch {
case cert.ValidAfter == 0:
return errors.New("ssh certificate validAfter cannot be 0")
return badRequest("ssh certificate validAfter cannot be 0")
case cert.ValidBefore < uint64(now().Unix()):
return errors.New("ssh certificate validBefore cannot be in the past")
return badRequest("ssh certificate validBefore cannot be in the past")
case cert.ValidBefore < cert.ValidAfter:
return errors.New("ssh certificate validBefore cannot be before validAfter")
return badRequest("ssh certificate validBefore cannot be before validAfter")
}
var min, max time.Duration
@ -351,9 +351,9 @@ func (v *sshCertValidityValidator) Valid(cert *ssh.Certificate, opts SignSSHOpti
min = v.MinHostSSHCertDuration()
max = v.MaxHostSSHCertDuration()
case 0:
return errors.New("ssh certificate type has not been set")
return badRequest("ssh certificate type has not been set")
default:
return errors.Errorf("unknown ssh certificate type %d", cert.CertType)
return badRequest("unknown ssh certificate type %d", cert.CertType)
}
// To not take into account the backdate, time.Now() will be used to
@ -362,11 +362,9 @@ func (v *sshCertValidityValidator) Valid(cert *ssh.Certificate, opts SignSSHOpti
switch {
case dur < min:
return errors.Errorf("requested duration of %s is less than minimum "+
"accepted duration for selected provisioner of %s", dur, min)
return badRequest("requested duration of %s is less than minimum accepted duration for selected provisioner of %s", dur, min)
case dur > max+opts.Backdate:
return errors.Errorf("requested duration of %s is greater than maximum "+
"accepted duration for selected provisioner of %s", dur, max+opts.Backdate)
return badRequest("requested duration of %s is greater than maximum accepted duration for selected provisioner of %s", dur, max+opts.Backdate)
default:
return nil
}

@ -10,9 +10,9 @@ import (
"encoding/json"
"encoding/pem"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"os"
"strings"
"time"
@ -188,7 +188,7 @@ func generateJWK() (*JWK, error) {
}
func generateK8sSA(inputPubKey interface{}) (*K8sSA, error) {
fooPubB, err := ioutil.ReadFile("./testdata/certs/foo.pub")
fooPubB, err := os.ReadFile("./testdata/certs/foo.pub")
if err != nil {
return nil, err
}
@ -196,7 +196,7 @@ func generateK8sSA(inputPubKey interface{}) (*K8sSA, error) {
if err != nil {
return nil, err
}
barPubB, err := ioutil.ReadFile("./testdata/certs/bar.pub")
barPubB, err := os.ReadFile("./testdata/certs/bar.pub")
if err != nil {
return nil, err
}
@ -234,7 +234,7 @@ func generateSSHPOP() (*SSHPOP, error) {
return nil, err
}
userB, err := ioutil.ReadFile("./testdata/certs/ssh_user_ca_key.pub")
userB, err := os.ReadFile("./testdata/certs/ssh_user_ca_key.pub")
if err != nil {
return nil, err
}
@ -242,7 +242,7 @@ func generateSSHPOP() (*SSHPOP, error) {
if err != nil {
return nil, err
}
hostB, err := ioutil.ReadFile("./testdata/certs/ssh_host_ca_key.pub")
hostB, err := os.ReadFile("./testdata/certs/ssh_host_ca_key.pub")
if err != nil {
return nil, err
}

@ -6,14 +6,14 @@ import (
"encoding/json"
"encoding/pem"
"fmt"
"io/ioutil"
"os"
"github.com/pkg/errors"
"github.com/smallstep/certificates/authority/admin"
"github.com/smallstep/certificates/authority/config"
"github.com/smallstep/certificates/authority/provisioner"
"github.com/smallstep/certificates/errs"
step "go.step.sm/cli-utils/config"
"go.step.sm/cli-utils/step"
"go.step.sm/cli-utils/ui"
"go.step.sm/crypto/jose"
"go.step.sm/linkedca"
@ -238,6 +238,8 @@ func (a *Authority) RemoveProvisioner(ctx context.Context, id string) error {
return nil
}
// CreateFirstProvisioner creates and stores the first provisioner when using
// admin database provisioner storage.
func CreateFirstProvisioner(ctx context.Context, db admin.DB, password string) (*linkedca.Provisioner, error) {
if password == "" {
pass, err := ui.PromptPasswordGenerate("Please enter the password to encrypt your first provisioner, leave empty and we'll generate one")
@ -287,6 +289,7 @@ func CreateFirstProvisioner(ctx context.Context, db admin.DB, password string) (
return p, nil
}
// ValidateClaims validates the Claims type.
func ValidateClaims(c *linkedca.Claims) error {
if c == nil {
return nil
@ -313,6 +316,7 @@ func ValidateClaims(c *linkedca.Claims) error {
return nil
}
// ValidateDurations validates the Durations type.
func ValidateDurations(d *linkedca.Durations) error {
var (
err error
@ -523,8 +527,8 @@ func provisionerOptionsToLinkedca(p *provisioner.Options) (*linkedca.Template, *
if p.X509.Template != "" {
x509Template.Template = []byte(p.SSH.Template)
} else if p.X509.TemplateFile != "" {
filename := step.StepAbs(p.X509.TemplateFile)
if x509Template.Template, err = ioutil.ReadFile(filename); err != nil {
filename := step.Abs(p.X509.TemplateFile)
if x509Template.Template, err = os.ReadFile(filename); err != nil {
return nil, nil, errors.Wrap(err, "error reading x509 template")
}
}
@ -539,8 +543,8 @@ func provisionerOptionsToLinkedca(p *provisioner.Options) (*linkedca.Template, *
if p.SSH.Template != "" {
sshTemplate.Template = []byte(p.SSH.Template)
} else if p.SSH.TemplateFile != "" {
filename := step.StepAbs(p.SSH.TemplateFile)
if sshTemplate.Template, err = ioutil.ReadFile(filename); err != nil {
filename := step.Abs(p.SSH.TemplateFile)
if sshTemplate.Template, err = os.ReadFile(filename); err != nil {
return nil, nil, errors.Wrap(err, "error reading ssh template")
}
}

@ -101,6 +101,15 @@ func (a *Authority) GetSSHConfig(ctx context.Context, typ string, data map[strin
if err != nil {
return nil, err
}
// Backwards compatibility for version of the cli older than v0.18.0.
// Before v0.18.0 we were not passing any value for SSHTemplateVersionKey
// from the cli.
if o.Name == "step_includes.tpl" && data[templates.SSHTemplateVersionKey] == "" {
o.Type = templates.File
o.Path = strings.TrimPrefix(o.Path, "${STEPPATH}/")
}
output = append(output, o)
}
return output, nil

@ -501,6 +501,32 @@ func TestAuthority_GetSSHConfig(t *testing.T) {
{Name: "sshd_config.tpl", Type: templates.File, Comment: "#", Path: "/etc/ssh/sshd_config", Content: []byte("Match all\n\tTrustedUserCAKeys /etc/ssh/ca.pub\n\tHostCertificate /etc/ssh/ssh_host_ecdsa_key-cert.pub\n\tHostKey /etc/ssh/ssh_host_ecdsa_key")},
}
tmplConfigUserIncludes := &templates.Templates{
SSH: &templates.SSHTemplates{
User: []templates.Template{
{Name: "step_includes.tpl", Type: templates.PrependLine, TemplatePath: "./testdata/templates/step_includes.tpl", Path: "${STEPPATH}/ssh/includes", Comment: "#"},
},
},
Data: map[string]interface{}{
"Step": &templates.Step{
SSH: templates.StepSSH{
UserKey: user,
HostKey: host,
},
},
},
}
userOutputEmptyData := []templates.Output{
{Name: "step_includes.tpl", Type: templates.File, Comment: "#", Path: "ssh/includes", Content: []byte("Include \"<no value>/ssh/config\"\n")},
}
userOutputWithoutTemplateVersion := []templates.Output{
{Name: "step_includes.tpl", Type: templates.File, Comment: "#", Path: "ssh/includes", Content: []byte("Include \"/home/user/.step/ssh/config\"\n")},
}
userOutputWithTemplateVersion := []templates.Output{
{Name: "step_includes.tpl", Type: templates.PrependLine, Comment: "#", Path: "${STEPPATH}/ssh/includes", Content: []byte("Include \"/home/user/.step/ssh/config\"\n")},
}
tmplConfigErr := &templates.Templates{
SSH: &templates.SSHTemplates{
User: []templates.Template{
@ -542,6 +568,9 @@ func TestAuthority_GetSSHConfig(t *testing.T) {
{"host", fields{tmplConfig, nil, hostSigner}, args{"host", nil}, hostOutput, false},
{"userWithData", fields{tmplConfigWithUserData, userSigner, hostSigner}, args{"user", map[string]string{"StepPath": "/home/user/.step"}}, userOutputWithUserData, false},
{"hostWithData", fields{tmplConfigWithUserData, userSigner, hostSigner}, args{"host", map[string]string{"Certificate": "ssh_host_ecdsa_key-cert.pub", "Key": "ssh_host_ecdsa_key"}}, hostOutputWithUserData, false},
{"userIncludesEmptyData", fields{tmplConfigUserIncludes, userSigner, hostSigner}, args{"user", nil}, userOutputEmptyData, false},
{"userIncludesWithoutTemplateVersion", fields{tmplConfigUserIncludes, userSigner, hostSigner}, args{"user", map[string]string{"StepPath": "/home/user/.step"}}, userOutputWithoutTemplateVersion, false},
{"userIncludesWithTemplateVersion", fields{tmplConfigUserIncludes, userSigner, hostSigner}, args{"user", map[string]string{"StepPath": "/home/user/.step", "StepSSHTemplateVersion": "v2"}}, userOutputWithTemplateVersion, false},
{"disabled", fields{tmplConfig, nil, nil}, args{"host", nil}, nil, true},
{"badType", fields{tmplConfig, userSigner, hostSigner}, args{"bad", nil}, nil, true},
{"userError", fields{tmplConfigErr, userSigner, hostSigner}, args{"user", nil}, nil, true},

@ -0,0 +1 @@
{{- if or .User.GOOS "none" | eq "windows" }}Include "{{ .User.StepPath | replace "\\" "/" | trimPrefix "C:" }}/ssh/config"{{- else }}Include "{{.User.StepPath}}/ssh/config"{{- end }}

@ -310,7 +310,7 @@ func TestAuthority_Sign(t *testing.T) {
extraOpts: extraOpts,
signOpts: _signOpts,
err: errors.New("authority.Sign: requested duration of 25h0m0s is more than the authorized maximum certificate duration of 24h1m0s"),
code: http.StatusUnauthorized,
code: http.StatusBadRequest,
}
},
"fail validate sans when adding common name not in claims": func(t *testing.T) *signTest {
@ -538,15 +538,15 @@ ZYtQ9Ot36qc=
if tc.csr.Subject.CommonName == "" {
assert.Equals(t, leaf.Subject, pkix.Name{})
} else {
assert.Equals(t, fmt.Sprintf("%v", leaf.Subject),
fmt.Sprintf("%v", &pkix.Name{
assert.Equals(t, leaf.Subject.String(),
pkix.Name{
Country: []string{tmplt.Country},
Organization: []string{tmplt.Organization},
Locality: []string{tmplt.Locality},
StreetAddress: []string{tmplt.StreetAddress},
Province: []string{tmplt.Province},
CommonName: "smallstep test",
}))
}.String())
assert.Equals(t, leaf.DNSNames, []string{"test.smallstep.com"})
}
assert.Equals(t, leaf.Issuer, intermediate.Subject)
@ -718,15 +718,15 @@ func TestAuthority_Renew(t *testing.T) {
assert.True(t, leaf.NotAfter.Before(expiry.Add(time.Minute)))
tmplt := a.config.AuthorityConfig.Template
assert.Equals(t, fmt.Sprintf("%v", leaf.Subject),
fmt.Sprintf("%v", &pkix.Name{
assert.Equals(t, leaf.Subject.String(),
pkix.Name{
Country: []string{tmplt.Country},
Organization: []string{tmplt.Organization},
Locality: []string{tmplt.Locality},
StreetAddress: []string{tmplt.StreetAddress},
Province: []string{tmplt.Province},
CommonName: tmplt.CommonName,
}))
}.String())
assert.Equals(t, leaf.Issuer, intermediate.Subject)
assert.Equals(t, leaf.SignatureAlgorithm, x509.ECDSAWithSHA256)
@ -925,15 +925,15 @@ func TestAuthority_Rekey(t *testing.T) {
assert.True(t, leaf.NotAfter.Before(expiry.Add(time.Minute)))
tmplt := a.config.AuthorityConfig.Template
assert.Equals(t, fmt.Sprintf("%v", leaf.Subject),
fmt.Sprintf("%v", &pkix.Name{
assert.Equals(t, leaf.Subject.String(),
pkix.Name{
Country: []string{tmplt.Country},
Organization: []string{tmplt.Organization},
Locality: []string{tmplt.Locality},
StreetAddress: []string{tmplt.StreetAddress},
Province: []string{tmplt.Province},
CommonName: tmplt.CommonName,
}))
}.String())
assert.Equals(t, leaf.Issuer, intermediate.Subject)
assert.Equals(t, leaf.SignatureAlgorithm, x509.ECDSAWithSHA256)

@ -7,7 +7,6 @@ import (
"encoding/pem"
"fmt"
"io"
"io/ioutil"
"net/http"
"strings"
@ -292,7 +291,7 @@ func (c *ACMEClient) GetCertificate(url string) (*x509.Certificate, []*x509.Cert
return nil, nil, readACMEError(resp.Body)
}
defer resp.Body.Close()
bodyBytes, err := ioutil.ReadAll(resp.Body)
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
return nil, nil, errors.Wrap(err, "error reading GET certificate response")
}
@ -338,7 +337,7 @@ func (c *ACMEClient) GetAccountOrders() ([]string, error) {
func readACMEError(r io.ReadCloser) error {
defer r.Close()
b, err := ioutil.ReadAll(r)
b, err := io.ReadAll(r)
if err != nil {
return errors.Wrap(err, "error reading from body")
}

@ -5,7 +5,7 @@ import (
"encoding/base64"
"encoding/json"
"encoding/pem"
"io/ioutil"
"io"
"net/http"
"net/http/httptest"
"testing"
@ -317,7 +317,7 @@ func TestACMEClient_post(t *testing.T) {
}
// validate jws request protected headers and body
body, err := ioutil.ReadAll(req.Body)
body, err := io.ReadAll(req.Body)
assert.FatalError(t, err)
jws, err := jose.ParseJWS(string(body))
assert.FatalError(t, err)
@ -455,7 +455,7 @@ func TestACMEClient_NewOrder(t *testing.T) {
}
// validate jws request protected headers and body
body, err := ioutil.ReadAll(req.Body)
body, err := io.ReadAll(req.Body)
assert.FatalError(t, err)
jws, err := jose.ParseJWS(string(body))
assert.FatalError(t, err)
@ -575,7 +575,7 @@ func TestACMEClient_GetOrder(t *testing.T) {
}
// validate jws request protected headers and body
body, err := ioutil.ReadAll(req.Body)
body, err := io.ReadAll(req.Body)
assert.FatalError(t, err)
jws, err := jose.ParseJWS(string(body))
assert.FatalError(t, err)
@ -695,7 +695,7 @@ func TestACMEClient_GetAuthz(t *testing.T) {
}
// validate jws request protected headers and body
body, err := ioutil.ReadAll(req.Body)
body, err := io.ReadAll(req.Body)
assert.FatalError(t, err)
jws, err := jose.ParseJWS(string(body))
assert.FatalError(t, err)
@ -815,7 +815,7 @@ func TestACMEClient_GetChallenge(t *testing.T) {
}
// validate jws request protected headers and body
body, err := ioutil.ReadAll(req.Body)
body, err := io.ReadAll(req.Body)
assert.FatalError(t, err)
jws, err := jose.ParseJWS(string(body))
assert.FatalError(t, err)
@ -936,7 +936,7 @@ func TestACMEClient_ValidateChallenge(t *testing.T) {
}
// validate jws request protected headers and body
body, err := ioutil.ReadAll(req.Body)
body, err := io.ReadAll(req.Body)
assert.FatalError(t, err)
jws, err := jose.ParseJWS(string(body))
assert.FatalError(t, err)
@ -1061,7 +1061,7 @@ func TestACMEClient_FinalizeOrder(t *testing.T) {
}
// validate jws request protected headers and body
body, err := ioutil.ReadAll(req.Body)
body, err := io.ReadAll(req.Body)
assert.FatalError(t, err)
jws, err := jose.ParseJWS(string(body))
assert.FatalError(t, err)
@ -1188,7 +1188,7 @@ func TestACMEClient_GetAccountOrders(t *testing.T) {
}
// validate jws request protected headers and body
body, err := ioutil.ReadAll(req.Body)
body, err := io.ReadAll(req.Body)
assert.FatalError(t, err)
jws, err := jose.ParseJWS(string(body))
assert.FatalError(t, err)
@ -1317,7 +1317,7 @@ func TestACMEClient_GetCertificate(t *testing.T) {
}
// validate jws request protected headers and body
body, err := ioutil.ReadAll(req.Body)
body, err := io.ReadAll(req.Body)
assert.FatalError(t, err)
jws, err := jose.ParseJWS(string(body))
assert.FatalError(t, err)

@ -197,7 +197,7 @@ func (c *AdminClient) GetAdminsPaginate(opts ...AdminOption) (*adminAPI.GetAdmin
if err != nil {
return nil, errors.Wrapf(err, "error generating admin token")
}
req, err := http.NewRequest("GET", u.String(), nil)
req, err := http.NewRequest("GET", u.String(), http.NoBody)
if err != nil {
return nil, errors.Wrapf(err, "create GET %s request failed", u)
}
@ -284,7 +284,7 @@ func (c *AdminClient) RemoveAdmin(id string) error {
if err != nil {
return errors.Wrapf(err, "error generating admin token")
}
req, err := http.NewRequest("DELETE", u.String(), nil)
req, err := http.NewRequest("DELETE", u.String(), http.NoBody)
if err != nil {
return errors.Wrapf(err, "create DELETE %s request failed", u)
}
@ -363,7 +363,7 @@ func (c *AdminClient) GetProvisioner(opts ...ProvisionerOption) (*linkedca.Provi
if err != nil {
return nil, errors.Wrapf(err, "error generating admin token")
}
req, err := http.NewRequest("GET", u.String(), nil)
req, err := http.NewRequest("GET", u.String(), http.NoBody)
if err != nil {
return nil, errors.Wrapf(err, "create PUT %s request failed", u)
}
@ -402,7 +402,7 @@ func (c *AdminClient) GetProvisionersPaginate(opts ...ProvisionerOption) (*admin
if err != nil {
return nil, errors.Wrapf(err, "error generating admin token")
}
req, err := http.NewRequest("GET", u.String(), nil)
req, err := http.NewRequest("GET", u.String(), http.NoBody)
if err != nil {
return nil, errors.Wrapf(err, "create PUT %s request failed", u)
}
@ -472,7 +472,7 @@ func (c *AdminClient) RemoveProvisioner(opts ...ProvisionerOption) error {
if err != nil {
return errors.Wrapf(err, "error generating admin token")
}
req, err := http.NewRequest("DELETE", u.String(), nil)
req, err := http.NewRequest("DELETE", u.String(), http.NoBody)
if err != nil {
return errors.Wrapf(err, "create DELETE %s request failed", u)
}

@ -3,7 +3,7 @@ package ca
import (
"context"
"crypto/tls"
"io/ioutil"
"io"
"net"
"net/http"
"net/http/httptest"
@ -382,7 +382,7 @@ func TestBootstrapClientServerRotation(t *testing.T) {
return errors.Wrapf(err, "client.Get(%s) failed", srvURL)
}
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
b, err := io.ReadAll(resp.Body)
if err != nil {
return errors.Wrap(err, "client.Get() error reading response")
}
@ -499,7 +499,7 @@ func TestBootstrapClientServerFederation(t *testing.T) {
return errors.Wrapf(err, "client.Get(%s) failed", srvURL)
}
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
b, err := io.ReadAll(resp.Body)
if err != nil {
return errors.Wrap(err, "client.Get() error reading response")
}
@ -589,9 +589,9 @@ func TestBootstrapListener(t *testing.T) {
return
}
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
b, err := io.ReadAll(resp.Body)
if err != nil {
t.Errorf("ioutil.ReadAll() error = %v", err)
t.Errorf("io.ReadAll() error = %v", err)
return
}
if string(b) != "ok" {

@ -294,15 +294,15 @@ ZEp7knvU2psWRw==
assert.Equals(t, leaf.NotBefore, now.Truncate(time.Second))
assert.Equals(t, leaf.NotAfter, leafExpiry.Truncate(time.Second))
assert.Equals(t, fmt.Sprintf("%v", leaf.Subject),
fmt.Sprintf("%v", &pkix.Name{
assert.Equals(t, leaf.Subject.String(),
pkix.Name{
Country: []string{asn1dn.Country},
Organization: []string{asn1dn.Organization},
Locality: []string{asn1dn.Locality},
StreetAddress: []string{asn1dn.StreetAddress},
Province: []string{asn1dn.Province},
CommonName: asn1dn.CommonName,
}))
}.String())
assert.Equals(t, leaf.Issuer, intermediate.Subject)
assert.Equals(t, leaf.SignatureAlgorithm, x509.ECDSAWithSHA256)
@ -641,10 +641,10 @@ func TestCARenew(t *testing.T) {
assert.Equals(t, leaf.NotBefore, now.Truncate(time.Second))
assert.Equals(t, leaf.NotAfter, leafExpiry.Truncate(time.Second))
assert.Equals(t, fmt.Sprintf("%v", leaf.Subject),
fmt.Sprintf("%v", &pkix.Name{
assert.Equals(t, leaf.Subject.String(),
pkix.Name{
CommonName: asn1dn.CommonName,
}))
}.String())
assert.Equals(t, leaf.Issuer, intermediate.Subject)
assert.Equals(t, leaf.SignatureAlgorithm, x509.ECDSAWithSHA256)

@ -15,7 +15,6 @@ import (
"encoding/json"
"encoding/pem"
"io"
"io/ioutil"
"net/http"
"net/url"
"os"
@ -29,7 +28,7 @@ import (
"github.com/smallstep/certificates/authority/provisioner"
"github.com/smallstep/certificates/ca/identity"
"github.com/smallstep/certificates/errs"
"go.step.sm/cli-utils/config"
"go.step.sm/cli-utils/step"
"go.step.sm/crypto/jose"
"go.step.sm/crypto/keyutil"
"go.step.sm/crypto/pemutil"
@ -75,7 +74,7 @@ func (c *uaClient) SetTransport(tr http.RoundTripper) {
}
func (c *uaClient) Get(u string) (*http.Response, error) {
req, err := http.NewRequest("GET", u, nil)
req, err := http.NewRequest("GET", u, http.NoBody)
if err != nil {
return nil, errors.Wrapf(err, "new request GET %s failed", u)
}
@ -226,7 +225,7 @@ func (o *clientOptions) getTransport(endpoint string) (tr http.RoundTripper, err
return tr, nil
}
// WithTransport adds a custom transport to the Client. It will fail if a
// WithTransport adds a custom transport to the Client. It will fail if a
// previous option to create the transport has been configured.
func WithTransport(tr http.RoundTripper) ClientOption {
return func(o *clientOptions) error {
@ -238,6 +237,17 @@ func WithTransport(tr http.RoundTripper) ClientOption {
}
}
// WithInsecure adds a insecure transport that bypasses TLS verification.
func WithInsecure() ClientOption {
return func(o *clientOptions) error {
o.transport = &http.Transport{
Proxy: http.ProxyFromEnvironment,
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
return nil
}
}
// WithRootFile will create the transport using the given root certificate. It
// will fail if a previous option to create the transport has been configured.
func WithRootFile(filename string) ClientOption {
@ -350,7 +360,7 @@ func WithRetryFunc(fn RetryFunc) ClientOption {
}
func getTransportFromFile(filename string) (http.RoundTripper, error) {
data, err := ioutil.ReadFile(filename)
data, err := os.ReadFile(filename)
if err != nil {
return nil, errors.Wrapf(err, "error reading %s", filename)
}
@ -1295,7 +1305,7 @@ func createCertificateRequest(commonName string, sans []string, key crypto.Priva
// 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")
return filepath.Join(step.Path(), "certs", "root_ca.crt")
}
func readJSON(r io.ReadCloser, v interface{}) error {
@ -1305,7 +1315,7 @@ func readJSON(r io.ReadCloser, v interface{}) error {
func readProtoJSON(r io.ReadCloser, m proto.Message) error {
defer r.Close()
data, err := ioutil.ReadAll(r)
data, err := io.ReadAll(r)
if err != nil {
return err
}

@ -5,9 +5,9 @@ import (
"crypto/x509"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"os"
"github.com/pkg/errors"
)
@ -27,21 +27,22 @@ func (c *Client) ResolveReference(ref *url.URL) *url.URL {
// $STEPPATH/config/defaults.json and the identity defined in
// $STEPPATH/config/identity.json
func LoadClient() (*Client, error) {
b, err := ioutil.ReadFile(DefaultsFile)
defaultsFile := DefaultsFile()
b, err := os.ReadFile(defaultsFile)
if err != nil {
return nil, errors.Wrapf(err, "error reading %s", DefaultsFile)
return nil, errors.Wrapf(err, "error reading %s", defaultsFile)
}
var defaults defaultsConfig
if err := json.Unmarshal(b, &defaults); err != nil {
return nil, errors.Wrapf(err, "error unmarshaling %s", DefaultsFile)
return nil, errors.Wrapf(err, "error unmarshaling %s", defaultsFile)
}
if err := defaults.Validate(); err != nil {
return nil, errors.Wrapf(err, "error validating %s", DefaultsFile)
return nil, errors.Wrapf(err, "error validating %s", defaultsFile)
}
caURL, err := url.Parse(defaults.CaURL)
if err != nil {
return nil, errors.Wrapf(err, "error validating %s", DefaultsFile)
return nil, errors.Wrapf(err, "error validating %s", defaultsFile)
}
if caURL.Scheme == "" {
caURL.Scheme = "https"
@ -52,7 +53,7 @@ func LoadClient() (*Client, error) {
return nil, err
}
if err := identity.Validate(); err != nil {
return nil, errors.Wrapf(err, "error validating %s", IdentityFile)
return nil, errors.Wrapf(err, "error validating %s", IdentityFile())
}
if kind := identity.Kind(); kind != MutualTLS {
return nil, errors.Errorf("unsupported identity %s: only mTLS is currently supported", kind)
@ -65,7 +66,7 @@ func LoadClient() (*Client, error) {
}
// RootCAs
b, err = ioutil.ReadFile(defaults.Root)
b, err = os.ReadFile(defaults.Root)
if err != nil {
return nil, errors.Wrapf(err, "error loading %s", defaults.Root)
}

@ -3,14 +3,20 @@ package identity
import (
"crypto/tls"
"crypto/x509"
"io/ioutil"
"net/http"
"net/http/httptest"
"net/url"
"os"
"reflect"
"testing"
)
func returnInput(val string) func() string {
return func() string {
return val
}
}
func TestClient(t *testing.T) {
oldIdentityFile := IdentityFile
oldDefaultsFile := DefaultsFile
@ -19,8 +25,8 @@ func TestClient(t *testing.T) {
DefaultsFile = oldDefaultsFile
}()
IdentityFile = "testdata/config/identity.json"
DefaultsFile = "testdata/config/defaults.json"
IdentityFile = returnInput("testdata/config/identity.json")
DefaultsFile = returnInput("testdata/config/defaults.json")
client, err := LoadClient()
if err != nil {
@ -40,7 +46,7 @@ func TestClient(t *testing.T) {
if err != nil {
t.Fatal(err)
}
b, err := ioutil.ReadFile("testdata/certs/root_ca.crt")
b, err := os.ReadFile("testdata/certs/root_ca.crt")
if err != nil {
t.Fatal(err)
}
@ -114,7 +120,7 @@ func TestLoadClient(t *testing.T) {
if err != nil {
t.Fatal(err)
}
b, err := ioutil.ReadFile("testdata/certs/root_ca.crt")
b, err := os.ReadFile("testdata/certs/root_ca.crt")
if err != nil {
t.Fatal(err)
}
@ -140,36 +146,36 @@ func TestLoadClient(t *testing.T) {
wantErr bool
}{
{"ok", func() {
IdentityFile = "testdata/config/identity.json"
DefaultsFile = "testdata/config/defaults.json"
IdentityFile = returnInput("testdata/config/identity.json")
DefaultsFile = returnInput("testdata/config/defaults.json")
}, expected, false},
{"fail identity", func() {
IdentityFile = "testdata/config/missing.json"
DefaultsFile = "testdata/config/defaults.json"
IdentityFile = returnInput("testdata/config/missing.json")
DefaultsFile = returnInput("testdata/config/defaults.json")
}, nil, true},
{"fail identity", func() {
IdentityFile = "testdata/config/fail.json"
DefaultsFile = "testdata/config/defaults.json"
IdentityFile = returnInput("testdata/config/fail.json")
DefaultsFile = returnInput("testdata/config/defaults.json")
}, nil, true},
{"fail defaults", func() {
IdentityFile = "testdata/config/identity.json"
DefaultsFile = "testdata/config/missing.json"
IdentityFile = returnInput("testdata/config/identity.json")
DefaultsFile = returnInput("testdata/config/missing.json")
}, nil, true},
{"fail defaults", func() {
IdentityFile = "testdata/config/identity.json"
DefaultsFile = "testdata/config/fail.json"
IdentityFile = returnInput("testdata/config/identity.json")
DefaultsFile = returnInput("testdata/config/fail.json")
}, nil, true},
{"fail ca", func() {
IdentityFile = "testdata/config/identity.json"
DefaultsFile = "testdata/config/badca.json"
IdentityFile = returnInput("testdata/config/identity.json")
DefaultsFile = returnInput("testdata/config/badca.json")
}, nil, true},
{"fail root", func() {
IdentityFile = "testdata/config/identity.json"
DefaultsFile = "testdata/config/badroot.json"
IdentityFile = returnInput("testdata/config/identity.json")
DefaultsFile = returnInput("testdata/config/badroot.json")
}, nil, true},
{"fail type", func() {
IdentityFile = "testdata/config/badIdentity.json"
DefaultsFile = "testdata/config/defaults.json"
IdentityFile = returnInput("testdata/config/badIdentity.json")
DefaultsFile = returnInput("testdata/config/defaults.json")
}, nil, true},
}
for _, tt := range tests {

@ -7,7 +7,6 @@ import (
"crypto/x509"
"encoding/json"
"encoding/pem"
"io/ioutil"
"net/http"
"os"
"path/filepath"
@ -16,7 +15,7 @@ import (
"github.com/pkg/errors"
"github.com/smallstep/certificates/api"
"go.step.sm/cli-utils/config"
"go.step.sm/cli-utils/step"
"go.step.sm/crypto/pemutil"
)
@ -39,11 +38,18 @@ const TunnelTLS Type = "tTLS"
// DefaultLeeway is the duration for matching not before claims.
const DefaultLeeway = 1 * time.Minute
// IdentityFile contains the location of the identity file.
var IdentityFile = filepath.Join(config.StepPath(), "config", "identity.json")
var (
identityDir = step.IdentityPath
configDir = step.ConfigPath
// IdentityFile contains a pointer to a function that outputs the location of
// the identity file.
IdentityFile = step.IdentityFile
// DefaultsFile contains the location of the defaults file.
var DefaultsFile = filepath.Join(config.StepPath(), "config", "defaults.json")
// DefaultsFile contains a prointer a function that outputs the location of the
// defaults configuration file.
DefaultsFile = step.DefaultsFile
)
// Identity represents the identity file that can be used to authenticate with
// the CA.
@ -61,7 +67,7 @@ type Identity struct {
// LoadIdentity loads an identity present in the given filename.
func LoadIdentity(filename string) (*Identity, error) {
b, err := ioutil.ReadFile(filename)
b, err := os.ReadFile(filename)
if err != nil {
return nil, errors.Wrapf(err, "error reading %s", filename)
}
@ -74,23 +80,17 @@ func LoadIdentity(filename string) (*Identity, error) {
// LoadDefaultIdentity loads the default identity.
func LoadDefaultIdentity() (*Identity, error) {
return LoadIdentity(IdentityFile)
return LoadIdentity(IdentityFile())
}
// configDir and identityDir are used in WriteDefaultIdentity for testing
// purposes.
var (
configDir = filepath.Join(config.StepPath(), "config")
identityDir = filepath.Join(config.StepPath(), "identity")
)
// WriteDefaultIdentity writes the given certificates and key and the
// identity.json pointing to the new files.
func WriteDefaultIdentity(certChain []api.Certificate, key crypto.PrivateKey) error {
if err := os.MkdirAll(configDir, 0700); err != nil {
if err := os.MkdirAll(configDir(), 0700); err != nil {
return errors.Wrap(err, "error creating config directory")
}
identityDir := identityDir()
if err := os.MkdirAll(identityDir, 0700); err != nil {
return errors.Wrap(err, "error creating identity directory")
}
@ -112,7 +112,7 @@ func WriteDefaultIdentity(certChain []api.Certificate, key crypto.PrivateKey) er
if err := pem.Encode(buf, block); err != nil {
return errors.Wrap(err, "error encoding identity key")
}
if err := ioutil.WriteFile(keyFilename, buf.Bytes(), 0600); err != nil {
if err := os.WriteFile(keyFilename, buf.Bytes(), 0600); err != nil {
return errors.Wrap(err, "error writing identity certificate")
}
@ -127,7 +127,7 @@ func WriteDefaultIdentity(certChain []api.Certificate, key crypto.PrivateKey) er
}); err != nil {
return errors.Wrap(err, "error writing identity json")
}
if err := ioutil.WriteFile(IdentityFile, buf.Bytes(), 0600); err != nil {
if err := os.WriteFile(IdentityFile(), buf.Bytes(), 0600); err != nil {
return errors.Wrap(err, "error writing identity certificate")
}
@ -136,7 +136,7 @@ func WriteDefaultIdentity(certChain []api.Certificate, key crypto.PrivateKey) er
// WriteIdentityCertificate writes the identity certificate to disk.
func WriteIdentityCertificate(certChain []api.Certificate) error {
filename := filepath.Join(identityDir, "identity.crt")
filename := filepath.Join(identityDir(), "identity.crt")
return writeCertificate(filename, certChain)
}
@ -153,7 +153,7 @@ func writeCertificate(filename string, certChain []api.Certificate) error {
}
}
if err := ioutil.WriteFile(filename, buf.Bytes(), 0600); err != nil {
if err := os.WriteFile(filename, buf.Bytes(), 0600); err != nil {
return errors.Wrap(err, "error writing certificate")
}
@ -263,7 +263,7 @@ func (i *Identity) GetCertPool() (*x509.CertPool, error) {
if i.Root == "" {
return nil, nil
}
b, err := ioutil.ReadFile(i.Root)
b, err := os.ReadFile(i.Root)
if err != nil {
return nil, errors.Wrap(err, "error reading identity root")
}
@ -319,8 +319,8 @@ func (i *Identity) Renew(client Renewer) error {
return errors.Wrap(err, "error encoding identity certificate")
}
}
certFilename := filepath.Join(identityDir, "identity.crt")
if err := ioutil.WriteFile(certFilename, buf.Bytes(), 0600); err != nil {
certFilename := filepath.Join(identityDir(), "identity.crt")
if err := os.WriteFile(certFilename, buf.Bytes(), 0600); err != nil {
return errors.Wrap(err, "error writing identity certificate")
}

@ -33,9 +33,9 @@ func TestLoadDefaultIdentity(t *testing.T) {
want *Identity
wantErr bool
}{
{"ok", func() { IdentityFile = "testdata/config/identity.json" }, expected, false},
{"fail read", func() { IdentityFile = "testdata/config/missing.json" }, nil, true},
{"fail unmarshal", func() { IdentityFile = "testdata/config/fail.json" }, nil, true},
{"ok", func() { IdentityFile = returnInput("testdata/config/identity.json") }, expected, false},
{"fail read", func() { IdentityFile = returnInput("testdata/config/missing.json") }, nil, true},
{"fail unmarshal", func() { IdentityFile = returnInput("testdata/config/fail.json") }, nil, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@ -217,9 +217,9 @@ func TestWriteDefaultIdentity(t *testing.T) {
certChain = append(certChain, api.Certificate{Certificate: c})
}
configDir = filepath.Join(tmpDir, "config")
identityDir = filepath.Join(tmpDir, "identity")
IdentityFile = filepath.Join(tmpDir, "config", "identity.json")
configDir = returnInput(filepath.Join(tmpDir, "config"))
identityDir = returnInput(filepath.Join(tmpDir, "identity"))
IdentityFile = returnInput(filepath.Join(tmpDir, "config", "identity.json"))
type args struct {
certChain []api.Certificate
@ -233,27 +233,27 @@ func TestWriteDefaultIdentity(t *testing.T) {
}{
{"ok", func() {}, args{certChain, key}, false},
{"fail mkdir config", func() {
configDir = filepath.Join(tmpDir, "identity", "identity.crt")
identityDir = filepath.Join(tmpDir, "identity")
configDir = returnInput(filepath.Join(tmpDir, "identity", "identity.crt"))
identityDir = returnInput(filepath.Join(tmpDir, "identity"))
}, args{certChain, key}, true},
{"fail mkdir identity", func() {
configDir = filepath.Join(tmpDir, "config")
identityDir = filepath.Join(tmpDir, "identity", "identity.crt")
configDir = returnInput(filepath.Join(tmpDir, "config"))
identityDir = returnInput(filepath.Join(tmpDir, "identity", "identity.crt"))
}, args{certChain, key}, true},
{"fail certificate", func() {
configDir = filepath.Join(tmpDir, "config")
identityDir = filepath.Join(tmpDir, "bad-dir")
os.MkdirAll(identityDir, 0600)
configDir = returnInput(filepath.Join(tmpDir, "config"))
identityDir = returnInput(filepath.Join(tmpDir, "bad-dir"))
os.MkdirAll(identityDir(), 0600)
}, args{certChain, key}, true},
{"fail key", func() {
configDir = filepath.Join(tmpDir, "config")
identityDir = filepath.Join(tmpDir, "identity")
configDir = returnInput(filepath.Join(tmpDir, "config"))
identityDir = returnInput(filepath.Join(tmpDir, "identity"))
}, args{certChain, "badKey"}, true},
{"fail write identity", func() {
configDir = filepath.Join(tmpDir, "bad-dir")
identityDir = filepath.Join(tmpDir, "identity")
IdentityFile = filepath.Join(configDir, "identity.json")
os.MkdirAll(configDir, 0600)
configDir = returnInput(filepath.Join(tmpDir, "bad-dir"))
identityDir = returnInput(filepath.Join(tmpDir, "identity"))
IdentityFile = returnInput(filepath.Join(configDir(), "identity.json"))
os.MkdirAll(configDir(), 0600)
}, args{certChain, key}, true},
}
@ -377,7 +377,7 @@ func TestIdentity_Renew(t *testing.T) {
}
oldIdentityDir := identityDir
identityDir = "testdata/identity"
identityDir = returnInput("testdata/identity")
defer func() {
identityDir = oldIdentityDir
os.RemoveAll(tmpDir)
@ -432,8 +432,8 @@ func TestIdentity_Renew(t *testing.T) {
{"fail renew", func() {}, fields{"mTLS", "testdata/identity/identity.crt", "testdata/identity/identity_key"}, args{fail}, true},
{"fail certificate", func() {}, fields{"mTLS", "testdata/certs/server.crt", "testdata/identity/identity_key"}, args{ok}, true},
{"fail write identity", func() {
identityDir = filepath.Join(tmpDir, "bad-dir")
os.MkdirAll(identityDir, 0600)
identityDir = returnInput(filepath.Join(tmpDir, "bad-dir"))
os.MkdirAll(identityDir(), 0600)
}, fields{"mTLS", "testdata/identity/identity.crt", "testdata/identity/identity_key"}, args{ok}, true},
}
for _, tt := range tests {

@ -1,8 +1,8 @@
package ca
import (
"io/ioutil"
"net/url"
"os"
"reflect"
"testing"
"time"
@ -45,7 +45,7 @@ func TestNewProvisioner(t *testing.T) {
defer ca.Close()
want := getTestProvisioner(t, ca.URL)
caBundle, err := ioutil.ReadFile("testdata/secrets/root_ca.crt")
caBundle, err := os.ReadFile("testdata/secrets/root_ca.crt")
if err != nil {
t.Fatal(err)
}

@ -18,7 +18,7 @@ var minCertDuration = time.Minute
// TLSRenewer automatically renews a tls certificate using a RenewFunc.
type TLSRenewer struct {
sync.RWMutex
renewMutex sync.RWMutex
RenewCertificate RenewFunc
cert *tls.Certificate
timer *time.Timer
@ -81,9 +81,9 @@ func NewTLSRenewer(cert *tls.Certificate, fn RenewFunc, opts ...tlsRenewerOption
func (r *TLSRenewer) Run() {
cert := r.getCertificate()
next := r.nextRenewDuration(cert.Leaf.NotAfter)
r.Lock()
r.renewMutex.Lock()
r.timer = time.AfterFunc(next, r.renewCertificate)
r.Unlock()
r.renewMutex.Unlock()
}
// RunContext starts the certificate renewer for the given certificate.
@ -133,25 +133,25 @@ func (r *TLSRenewer) GetClientCertificate(*tls.CertificateRequestInfo) (*tls.Cer
// if the timer does not fire e.g. when the CA is run from a laptop that
// enters sleep mode.
func (r *TLSRenewer) getCertificate() *tls.Certificate {
r.RLock()
r.renewMutex.RLock()
cert := r.cert
r.RUnlock()
r.renewMutex.RUnlock()
return cert
}
// getCertificateForCA returns the certificate using a read-only lock. It will
// automatically renew the certificate if it has expired.
func (r *TLSRenewer) getCertificateForCA() *tls.Certificate {
r.RLock()
r.renewMutex.RLock()
// Force certificate renewal if the timer didn't run.
// This is an special case that can happen after a computer sleep.
if time.Now().After(r.certNotAfter) {
r.RUnlock()
r.renewMutex.RUnlock()
r.renewCertificate()
r.RLock()
r.renewMutex.RLock()
}
cert := r.cert
r.RUnlock()
r.renewMutex.RUnlock()
return cert
}
@ -159,10 +159,10 @@ func (r *TLSRenewer) getCertificateForCA() *tls.Certificate {
// updates certNotAfter with 1m of delta; this will force the renewal of the
// certificate if it is about to expire.
func (r *TLSRenewer) setCertificate(cert *tls.Certificate) {
r.Lock()
r.renewMutex.Lock()
r.cert = cert
r.certNotAfter = cert.Leaf.NotAfter.Add(-1 * time.Minute)
r.Unlock()
r.renewMutex.Unlock()
}
func (r *TLSRenewer) renewCertificate() {
@ -175,9 +175,9 @@ func (r *TLSRenewer) renewCertificate() {
r.setCertificate(cert)
next = r.nextRenewDuration(cert.Leaf.NotAfter)
}
r.Lock()
r.renewMutex.Lock()
r.timer.Reset(next)
r.Unlock()
r.renewMutex.Unlock()
}
func (r *TLSRenewer) nextRenewDuration(notAfter time.Time) time.Duration {

@ -4,8 +4,8 @@ import (
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"net/http"
"os"
"reflect"
"sort"
"testing"
@ -202,7 +202,7 @@ func TestAddRootsToRootCAs(t *testing.T) {
t.Fatal(err)
}
root, err := ioutil.ReadFile("testdata/secrets/root_ca.crt")
root, err := os.ReadFile("testdata/secrets/root_ca.crt")
if err != nil {
t.Fatal(err)
}
@ -256,7 +256,7 @@ func TestAddRootsToClientCAs(t *testing.T) {
t.Fatal(err)
}
root, err := ioutil.ReadFile("testdata/secrets/root_ca.crt")
root, err := os.ReadFile("testdata/secrets/root_ca.crt")
if err != nil {
t.Fatal(err)
}
@ -310,12 +310,12 @@ func TestAddFederationToRootCAs(t *testing.T) {
t.Fatal(err)
}
root, err := ioutil.ReadFile("testdata/secrets/root_ca.crt")
root, err := os.ReadFile("testdata/secrets/root_ca.crt")
if err != nil {
t.Fatal(err)
}
federated, err := ioutil.ReadFile("testdata/secrets/federated_ca.crt")
federated, err := os.ReadFile("testdata/secrets/federated_ca.crt")
if err != nil {
t.Fatal(err)
}
@ -374,12 +374,12 @@ func TestAddFederationToClientCAs(t *testing.T) {
t.Fatal(err)
}
root, err := ioutil.ReadFile("testdata/secrets/root_ca.crt")
root, err := os.ReadFile("testdata/secrets/root_ca.crt")
if err != nil {
t.Fatal(err)
}
federated, err := ioutil.ReadFile("testdata/secrets/federated_ca.crt")
federated, err := os.ReadFile("testdata/secrets/federated_ca.crt")
if err != nil {
t.Fatal(err)
}
@ -438,7 +438,7 @@ func TestAddRootsToCAs(t *testing.T) {
t.Fatal(err)
}
root, err := ioutil.ReadFile("testdata/secrets/root_ca.crt")
root, err := os.ReadFile("testdata/secrets/root_ca.crt")
if err != nil {
t.Fatal(err)
}
@ -492,12 +492,12 @@ func TestAddFederationToCAs(t *testing.T) {
t.Fatal(err)
}
root, err := ioutil.ReadFile("testdata/secrets/root_ca.crt")
root, err := os.ReadFile("testdata/secrets/root_ca.crt")
if err != nil {
t.Fatal(err)
}
federated, err := ioutil.ReadFile("testdata/secrets/federated_ca.crt")
federated, err := os.ReadFile("testdata/secrets/federated_ca.crt")
if err != nil {
t.Fatal(err)
}

@ -8,7 +8,7 @@ import (
"crypto/tls"
"crypto/x509"
"encoding/hex"
"io/ioutil"
"io"
"log"
"net/http"
"net/http/httptest"
@ -221,7 +221,7 @@ func TestClient_GetServerTLSConfig_http(t *testing.T) {
return
}
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
b, err := io.ReadAll(resp.Body)
if err != nil {
t.Fatalf("ioutil.RealAdd() error = %v", err)
}
@ -335,7 +335,7 @@ func TestClient_GetServerTLSConfig_renew(t *testing.T) {
}
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
b, err := io.ReadAll(resp.Body)
if err != nil {
t.Errorf("ioutil.RealAdd() error = %v", err)
return
@ -374,9 +374,9 @@ func TestClient_GetServerTLSConfig_renew(t *testing.T) {
}
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
b, err := io.ReadAll(resp.Body)
if err != nil {
t.Errorf("ioutil.RealAdd() error = %v", err)
t.Errorf("io.ReadAll() error = %v", err)
return
}
if !bytes.Equal(b, []byte("ok")) {

@ -91,7 +91,7 @@ func mustSerializeCrt(filename string, certs ...*x509.Certificate) {
panic(err)
}
}
if err := ioutil.WriteFile(filename, buf.Bytes(), 0600); err != nil {
if err := os.WriteFile(filename, buf.Bytes(), 0600); err != nil {
panic(err)
}
}
@ -105,7 +105,7 @@ func mustSerializeKey(filename string, key crypto.Signer) {
Type: "PRIVATE KEY",
Bytes: b,
})
if err := ioutil.WriteFile(filename, b, 0600); err != nil {
if err := os.WriteFile(filename, b, 0600); err != nil {
panic(err)
}
}

@ -21,7 +21,7 @@ import (
"github.com/urfave/cli"
"go.step.sm/cli-utils/command"
"go.step.sm/cli-utils/command/version"
"go.step.sm/cli-utils/config"
"go.step.sm/cli-utils/step"
"go.step.sm/cli-utils/ui"
"go.step.sm/cli-utils/usage"
@ -49,7 +49,7 @@ var (
)
func init() {
config.Set("Smallstep CA", Version, BuildTime)
step.Set("Smallstep CA", Version, BuildTime)
authority.GlobalVersion.Version = Version
rand.Seed(time.Now().UnixNano())
}
@ -115,7 +115,7 @@ func main() {
app := cli.NewApp()
app.Name = "step-ca"
app.HelpName = "step-ca"
app.Version = config.Version()
app.Version = step.Version()
app.Usage = "an online certificate authority for secure automated certificate management"
app.UsageText = `**step-ca** <config> [**--password-file**=<file>]
[**--ssh-host-password-file**=<file>] [**--ssh-user-password-file**=<file>]

@ -32,7 +32,7 @@ import (
// Config is a mapping of the cli flags.
type Config struct {
KMS string
RootOnly bool
GenerateRoot bool
RootObject string
RootKeyObject string
RootSubject string
@ -58,22 +58,29 @@ func (c *Config) Validate() error {
switch {
case c.KMS == "":
return errors.New("flag `--kms` is required")
case c.CrtPath == "":
return errors.New("flag `--crt-cert-path` is required")
case c.RootFile != "" && c.KeyFile == "":
return errors.New("flag `--root` requires flag `--key`")
return errors.New("flag `--root-cert-file` requires flag `--root-key-file`")
case c.KeyFile != "" && c.RootFile == "":
return errors.New("flag `--key` requires flag `--root`")
case c.RootOnly && c.RootFile != "":
return errors.New("flag `--root-only` is incompatible with flag `--root`")
return errors.New("flag `--root-key-file` requires flag `--root-cert-file`")
case c.RootFile == "" && c.RootObject == "":
return errors.New("one of flag `--root` or `--root-cert` is required")
case c.RootFile == "" && c.RootKeyObject == "":
return errors.New("one of flag `--root` or `--root-key` is required")
return errors.New("one of flag `--root-cert-file` or `--root-cert-obj` is required")
case c.KeyFile == "" && c.RootKeyObject == "":
return errors.New("one of flag `--root-key-file` or `--root-key-obj` is required")
case c.CrtKeyPath == "" && c.CrtKeyObject == "":
return errors.New("one of flag `--crt-key-path` or `--crt-key-obj` is required")
case c.RootFile == "" && c.GenerateRoot && c.RootKeyObject == "":
return errors.New("flag `--root-gen` requires flag `--root-key-obj`")
case c.RootFile == "" && c.GenerateRoot && c.RootPath == "":
return errors.New("flag `--root-gen` requires `--root-cert-path`")
default:
if c.RootFile != "" {
c.GenerateRoot = false
c.RootObject = ""
c.RootKeyObject = ""
}
if c.RootOnly {
if c.CrtKeyPath != "" {
c.CrtObject = ""
c.CrtKeyObject = ""
}
@ -101,21 +108,27 @@ func main() {
var c Config
flag.StringVar(&c.KMS, "kms", kmsuri, "PKCS #11 URI with the module-path and token to connect to the module.")
flag.StringVar(&c.Pin, "pin", "", "PKCS #11 PIN")
flag.StringVar(&c.RootObject, "root-cert", "pkcs11:id=7330;object=root-cert", "PKCS #11 URI with object id and label to store the root certificate.")
flag.StringVar(&c.RootPath, "root-cert-path", "root_ca.crt", "Location to write the root certificate.")
flag.StringVar(&c.RootKeyObject, "root-key", "pkcs11:id=7330;object=root-key", "PKCS #11 URI with object id and label to store the root key.")
// Option 1: Generate new root
flag.BoolVar(&c.GenerateRoot, "root-gen", true, "Enable the generation of a root key.")
flag.StringVar(&c.RootSubject, "root-name", "PKCS #11 Smallstep Root", "Subject and Issuer of the root certificate.")
flag.StringVar(&c.CrtObject, "crt-cert", "pkcs11:id=7331;object=intermediate-cert", "PKCS #11 URI with object id and label to store the intermediate certificate.")
flag.StringVar(&c.CrtPath, "crt-cert-path", "intermediate_ca.crt", "Location to write the intermediate certificate.")
flag.StringVar(&c.CrtKeyObject, "crt-key", "pkcs11:id=7331;object=intermediate-key", "PKCS #11 URI with object id and label to store the intermediate certificate.")
flag.StringVar(&c.RootObject, "root-cert-obj", "pkcs11:id=7330;object=root-cert", "PKCS #11 URI with object id and label to store the root certificate.")
flag.StringVar(&c.RootKeyObject, "root-key-obj", "pkcs11:id=7330;object=root-key", "PKCS #11 URI with object id and label to store the root key.")
// Option 2: Read root from disk and sign intermediate
flag.StringVar(&c.RootFile, "root-cert-file", "", "Path to the root certificate to use.")
flag.StringVar(&c.KeyFile, "root-key-file", "", "Path to the root key to use.")
// Option 3: Generate certificate signing request
flag.StringVar(&c.CrtSubject, "crt-name", "PKCS #11 Smallstep Intermediate", "Subject of the intermediate certificate.")
flag.StringVar(&c.CrtKeyPath, "crt-key-path", "intermediate_ca_key", "Location to write the intermediate private key.")
flag.StringVar(&c.CrtObject, "crt-cert-obj", "pkcs11:id=7331;object=intermediate-cert", "PKCS #11 URI with object id and label to store the intermediate certificate.")
flag.StringVar(&c.CrtKeyObject, "crt-key-obj", "pkcs11:id=7331;object=intermediate-key", "PKCS #11 URI with object id and label to store the intermediate certificate.")
// SSH certificates
flag.BoolVar(&c.EnableSSH, "ssh", false, "Enable the creation of ssh keys.")
flag.StringVar(&c.SSHHostKeyObject, "ssh-host-key", "pkcs11:id=7332;object=ssh-host-key", "PKCS #11 URI with object id and label to store the key used to sign SSH host certificates.")
flag.StringVar(&c.SSHUserKeyObject, "ssh-user-key", "pkcs11:id=7333;object=ssh-user-key", "PKCS #11 URI with object id and label to store the key used to sign SSH user certificates.")
flag.BoolVar(&c.RootOnly, "root-only", false, "Store only only the root certificate and sign and intermediate.")
flag.StringVar(&c.RootFile, "root", "", "Path to the root certificate to use.")
flag.StringVar(&c.KeyFile, "key", "", "Path to the root key to use.")
flag.BoolVar(&c.EnableSSH, "ssh", false, "Enable the creation of ssh keys.")
// Output files
flag.StringVar(&c.RootPath, "root-cert-path", "root_ca.crt", "Location to write the root certificate.")
flag.StringVar(&c.CrtPath, "crt-cert-path", "intermediate_ca.crt", "Location to write the intermediate certificate.")
flag.StringVar(&c.CrtKeyPath, "crt-key-path", "", "Location to write the intermediate private key.")
// Others
flag.BoolVar(&c.NoCerts, "no-certs", false, "Do not store certificates in the module.")
flag.BoolVar(&c.Force, "force", false, "Force the delete of previous keys.")
flag.BoolVar(&c.Extractable, "extractable", false, "Allow export of private keys under wrap.")
@ -276,22 +289,8 @@ func createPKI(k kms.KeyManager, c Config) error {
// Root Certificate
var signer crypto.Signer
var root *x509.Certificate
if c.RootFile != "" && c.KeyFile != "" {
root, err = pemutil.ReadCertificate(c.RootFile)
if err != nil {
return err
}
key, err := pemutil.Read(c.KeyFile)
if err != nil {
return err
}
var ok bool
if signer, ok = key.(crypto.Signer); !ok {
return errors.Errorf("key type '%T' does not implement a signer", key)
}
} else {
switch {
case c.GenerateRoot:
resp, err := k.CreateKey(&apiv1.CreateKeyRequest{
Name: c.RootKeyObject,
SignatureAlgorithm: apiv1.ECDSAWithSHA256,
@ -331,7 +330,7 @@ func createPKI(k kms.KeyManager, c Config) error {
return errors.Wrap(err, "error parsing root certificate")
}
if cm, ok := k.(kms.CertificateManager); ok && !c.NoCerts {
if cm, ok := k.(kms.CertificateManager); ok && c.RootObject != "" && !c.NoCerts {
if err := cm.StoreCertificate(&apiv1.StoreCertificateRequest{
Name: c.RootObject,
Certificate: root,
@ -339,6 +338,8 @@ func createPKI(k kms.KeyManager, c Config) error {
}); err != nil {
return err
}
} else {
c.RootObject = ""
}
if err := fileutil.WriteFile(c.RootPath, pem.EncodeToMemory(&pem.Block{
@ -350,12 +351,31 @@ func createPKI(k kms.KeyManager, c Config) error {
ui.PrintSelected("Root Key", resp.Name)
ui.PrintSelected("Root Certificate", c.RootPath)
if c.RootObject != "" {
ui.PrintSelected("Root Certificate Object", c.RootObject)
}
case c.RootFile != "" && c.KeyFile != "": // Read Root From File
root, err = pemutil.ReadCertificate(c.RootFile)
if err != nil {
return err
}
key, err := pemutil.Read(c.KeyFile)
if err != nil {
return err
}
var ok bool
if signer, ok = key.(crypto.Signer); !ok {
return errors.Errorf("key type '%T' does not implement a signer", key)
}
}
// Intermediate Certificate
var keyName string
var publicKey crypto.PublicKey
if c.RootOnly {
var intSigner crypto.Signer
if c.CrtKeyPath != "" {
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return errors.Wrap(err, "error creating intermediate key")
@ -373,6 +393,7 @@ func createPKI(k kms.KeyManager, c Config) error {
}
publicKey = priv.Public()
intSigner = priv
} else {
resp, err := k.CreateKey(&apiv1.CreateKeyRequest{
Name: c.CrtKeyObject,
@ -384,56 +405,89 @@ func createPKI(k kms.KeyManager, c Config) error {
}
publicKey = resp.PublicKey
keyName = resp.Name
}
template := &x509.Certificate{
IsCA: true,
NotBefore: now,
NotAfter: now.Add(time.Hour * 24 * 365 * 10),
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
BasicConstraintsValid: true,
MaxPathLen: 0,
MaxPathLenZero: true,
Issuer: root.Subject,
Subject: pkix.Name{CommonName: c.CrtSubject},
SerialNumber: mustSerialNumber(),
SubjectKeyId: mustSubjectKeyID(publicKey),
}
b, err := x509.CreateCertificate(rand.Reader, template, root, publicKey, signer)
if err != nil {
return err
intSigner, err = k.CreateSigner(&resp.CreateSignerRequest)
if err != nil {
return err
}
}
intermediate, err := x509.ParseCertificate(b)
if err != nil {
return errors.Wrap(err, "error parsing intermediate certificate")
}
if root != nil {
template := &x509.Certificate{
IsCA: true,
NotBefore: now,
NotAfter: now.Add(time.Hour * 24 * 365 * 10),
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
BasicConstraintsValid: true,
MaxPathLen: 0,
MaxPathLenZero: true,
Issuer: root.Subject,
Subject: pkix.Name{CommonName: c.CrtSubject},
SerialNumber: mustSerialNumber(),
SubjectKeyId: mustSubjectKeyID(publicKey),
}
if cm, ok := k.(kms.CertificateManager); ok && !c.NoCerts {
if err := cm.StoreCertificate(&apiv1.StoreCertificateRequest{
Name: c.CrtObject,
Certificate: intermediate,
Extractable: c.Extractable,
}); err != nil {
b, err := x509.CreateCertificate(rand.Reader, template, root, publicKey, signer)
if err != nil {
return err
}
}
if err := fileutil.WriteFile(c.CrtPath, pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: b,
}), 0600); err != nil {
return err
intermediate, err := x509.ParseCertificate(b)
if err != nil {
return errors.Wrap(err, "error parsing intermediate certificate")
}
if cm, ok := k.(kms.CertificateManager); ok && c.CrtObject != "" && !c.NoCerts {
if err := cm.StoreCertificate(&apiv1.StoreCertificateRequest{
Name: c.CrtObject,
Certificate: intermediate,
Extractable: c.Extractable,
}); err != nil {
return err
}
} else {
c.CrtObject = ""
}
if err := fileutil.WriteFile(c.CrtPath, pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: b,
}), 0600); err != nil {
return err
}
} else {
// No root available, generate CSR for external root.
csrTemplate := x509.CertificateRequest{
Subject: pkix.Name{CommonName: c.CrtSubject},
SignatureAlgorithm: x509.ECDSAWithSHA256,
}
// step: generate the csr request
csrCertificate, err := x509.CreateCertificateRequest(rand.Reader, &csrTemplate, intSigner)
if err != nil {
return err
}
if err := fileutil.WriteFile(c.CrtPath, pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE REQUEST",
Bytes: csrCertificate,
}), 0600); err != nil {
return err
}
}
if c.RootOnly {
if c.CrtKeyPath != "" {
ui.PrintSelected("Intermediate Key", c.CrtKeyPath)
} else {
ui.PrintSelected("Intermediate Key", keyName)
}
ui.PrintSelected("Intermediate Certificate", c.CrtPath)
if root != nil {
ui.PrintSelected("Intermediate Certificate", c.CrtPath)
if c.CrtObject != "" {
ui.PrintSelected("Intermediate Certificate Object", c.CrtObject)
}
} else {
ui.PrintSelected("Intermediate Certificate Request", c.CrtPath)
}
if c.SSHHostKeyObject != "" {
resp, err := k.CreateKey(&apiv1.CreateKeyRequest{

@ -4,7 +4,6 @@ import (
"bytes"
"context"
"fmt"
"io/ioutil"
"net"
"net/http"
"os"
@ -98,7 +97,7 @@ To get a linked authority token:
var password []byte
if passFile != "" {
if password, err = ioutil.ReadFile(passFile); err != nil {
if password, err = os.ReadFile(passFile); err != nil {
fatal(errors.Wrapf(err, "error reading %s", passFile))
}
password = bytes.TrimRightFunc(password, unicode.IsSpace)
@ -106,7 +105,7 @@ To get a linked authority token:
var sshHostPassword []byte
if sshHostPassFile != "" {
if sshHostPassword, err = ioutil.ReadFile(sshHostPassFile); err != nil {
if sshHostPassword, err = os.ReadFile(sshHostPassFile); err != nil {
fatal(errors.Wrapf(err, "error reading %s", sshHostPassFile))
}
sshHostPassword = bytes.TrimRightFunc(sshHostPassword, unicode.IsSpace)
@ -114,7 +113,7 @@ To get a linked authority token:
var sshUserPassword []byte
if sshUserPassFile != "" {
if sshUserPassword, err = ioutil.ReadFile(sshUserPassFile); err != nil {
if sshUserPassword, err = os.ReadFile(sshUserPassFile); err != nil {
fatal(errors.Wrapf(err, "error reading %s", sshUserPassFile))
}
sshUserPassword = bytes.TrimRightFunc(sshUserPassword, unicode.IsSpace)
@ -122,7 +121,7 @@ To get a linked authority token:
var issuerPassword []byte
if issuerPassFile != "" {
if issuerPassword, err = ioutil.ReadFile(issuerPassFile); err != nil {
if issuerPassword, err = os.ReadFile(issuerPassFile); err != nil {
fatal(errors.Wrapf(err, "error reading %s", issuerPassFile))
}
issuerPassword = bytes.TrimRightFunc(issuerPassword, unicode.IsSpace)

@ -4,7 +4,7 @@ import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"unicode"
"github.com/pkg/errors"
@ -72,14 +72,14 @@ func exportAction(ctx *cli.Context) error {
}
if passwordFile != "" {
b, err := ioutil.ReadFile(passwordFile)
b, err := os.ReadFile(passwordFile)
if err != nil {
return errors.Wrapf(err, "error reading %s", passwordFile)
}
cfg.Password = string(bytes.TrimRightFunc(b, unicode.IsSpace))
}
if issuerPasswordFile != "" {
b, err := ioutil.ReadFile(issuerPasswordFile)
b, err := os.ReadFile(issuerPasswordFile)
if err != nil {
return errors.Wrapf(err, "error reading %s", issuerPasswordFile)
}

@ -3,7 +3,7 @@ package main
import (
"context"
"fmt"
"io/ioutil"
"io"
"os"
"time"
@ -32,7 +32,7 @@ func main() {
if err != nil {
panic(err)
}
b, err := ioutil.ReadAll(resp.Body)
b, err := io.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
panic(err)

@ -3,7 +3,7 @@ package main
import (
"context"
"fmt"
"io/ioutil"
"io"
"os"
"time"
@ -32,7 +32,7 @@ func main() {
if err != nil {
panic(err)
}
b, err := ioutil.ReadAll(resp.Body)
b, err := io.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
panic(err)

@ -1,6 +1,6 @@
module github.com/smallstep/certificates
go 1.15
go 1.16
require (
cloud.google.com/go v0.83.0
@ -29,10 +29,10 @@ 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.8
github.com/smallstep/nosql v0.3.9
github.com/urfave/cli v1.22.4
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352
go.step.sm/cli-utils v0.6.2
go.step.sm/cli-utils v0.7.0
go.step.sm/crypto v0.13.0
go.step.sm/linkedca v0.7.0
golang.org/x/crypto v0.0.0-20210915214749-c084706c2272
@ -44,7 +44,8 @@ require (
gopkg.in/square/go-jose.v2 v2.6.0
)
// replace github.com/smallstep/nosql => ../nosql
// replace go.step.sm/crypto => ../crypto
// replace go.step.sm/cli-utils => ../cli-utils
// replace go.step.sm/linkedca => ../linkedca
//replace github.com/smallstep/nosql => ../nosql
//replace go.step.sm/crypto => ../crypto
//replace go.step.sm/cli-utils => ../cli-utils

@ -367,10 +367,8 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
@ -496,8 +494,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.8 h1:1/EWUbbEdz9ai0g9Fd09VekVjtxp+5+gIHpV2PdwW3o=
github.com/smallstep/nosql v0.3.8/go.mod h1:X2qkYpNcW3yjLUvhEHfgGfClpKbFPapewvx7zo4TOFs=
github.com/smallstep/nosql v0.3.9 h1:YPy5PR3PXClqmpFaVv0wfXDXDc7NXGBE1auyU2c87dc=
github.com/smallstep/nosql v0.3.9/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=
@ -561,8 +559,8 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.step.sm/cli-utils v0.6.2 h1:ofa3G/EqE3dTDXmzoXHDZr18qJZoFsKSzbzuF+mxuZU=
go.step.sm/cli-utils v0.6.2/go.mod h1:0tZ8F2QwLgD6KbKj4nrQZhMakTasEAnOcW3Ekc5pnrA=
go.step.sm/cli-utils v0.7.0 h1:2GvY5Muid1yzp7YQbfCCS+gK3q7zlHjjLL5Z0DXz8ds=
go.step.sm/cli-utils v0.7.0/go.mod h1:Ur6bqA/yl636kCUJbp30J7Unv5JJ226eW2KqXPDwF/E=
go.step.sm/crypto v0.9.0/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0=
go.step.sm/crypto v0.13.0 h1:mQuP9Uu2FNmqCJNO0OTbvolnYXzONy4wdUBtUVcP1s8=
go.step.sm/crypto v0.13.0/go.mod h1:5YzQ85BujYBu6NH18jw7nFjwuRnDch35nLzH0ES5sKg=
@ -956,7 +954,6 @@ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=

@ -4,7 +4,7 @@ import (
"context"
"crypto"
"fmt"
"io/ioutil"
"os"
"reflect"
"testing"
@ -165,7 +165,7 @@ func TestCloudKMS_Close(t *testing.T) {
func TestCloudKMS_CreateSigner(t *testing.T) {
keyName := "projects/p/locations/l/keyRings/k/cryptoKeys/c/cryptoKeyVersions/1"
pemBytes, err := ioutil.ReadFile("testdata/pub.pem")
pemBytes, err := os.ReadFile("testdata/pub.pem")
if err != nil {
t.Fatal(err)
}
@ -223,7 +223,7 @@ func TestCloudKMS_CreateKey(t *testing.T) {
testError := fmt.Errorf("an error")
alreadyExists := status.Error(codes.AlreadyExists, "already exists")
pemBytes, err := ioutil.ReadFile("testdata/pub.pem")
pemBytes, err := os.ReadFile("testdata/pub.pem")
if err != nil {
t.Fatal(err)
}
@ -389,7 +389,7 @@ func TestCloudKMS_GetPublicKey(t *testing.T) {
keyName := "projects/p/locations/l/keyRings/k/cryptoKeys/c/cryptoKeyVersions/1"
testError := fmt.Errorf("an error")
pemBytes, err := ioutil.ReadFile("testdata/pub.pem")
pemBytes, err := os.ReadFile("testdata/pub.pem")
if err != nil {
t.Fatal(err)
}

@ -7,7 +7,7 @@ import (
"crypto/x509"
"fmt"
"io"
"io/ioutil"
"os"
"reflect"
"testing"
@ -17,7 +17,7 @@ import (
)
func Test_newSigner(t *testing.T) {
pemBytes, err := ioutil.ReadFile("testdata/pub.pem")
pemBytes, err := os.ReadFile("testdata/pub.pem")
if err != nil {
t.Fatal(err)
}
@ -70,7 +70,7 @@ func Test_newSigner(t *testing.T) {
}
func Test_signer_Public(t *testing.T) {
pemBytes, err := ioutil.ReadFile("testdata/pub.pem")
pemBytes, err := os.ReadFile("testdata/pub.pem")
if err != nil {
t.Fatal(err)
}
@ -159,7 +159,7 @@ func Test_signer_Sign(t *testing.T) {
}
func TestSigner_SignatureAlgorithm(t *testing.T) {
pemBytes, err := ioutil.ReadFile("testdata/pub.pem")
pemBytes, err := os.ReadFile("testdata/pub.pem")
if err != nil {
t.Fatal(err)
}

@ -11,7 +11,7 @@ import (
"crypto/x509"
"encoding/pem"
"fmt"
"io/ioutil"
"os"
"reflect"
"testing"
@ -78,7 +78,7 @@ func TestSoftKMS_CreateSigner(t *testing.T) {
}
// Read and decode file using standard packages
b, err := ioutil.ReadFile("testdata/priv.pem")
b, err := os.ReadFile("testdata/priv.pem")
if err != nil {
t.Fatal(err)
}
@ -234,7 +234,7 @@ func TestSoftKMS_CreateKey(t *testing.T) {
}
func TestSoftKMS_GetPublicKey(t *testing.T) {
b, err := ioutil.ReadFile("testdata/pub.pem")
b, err := os.ReadFile("testdata/pub.pem")
if err != nil {
t.Fatal(err)
}
@ -332,7 +332,7 @@ func TestSoftKMS_CreateDecrypter(t *testing.T) {
if err != nil {
t.Fatal(err)
}
b, err := ioutil.ReadFile("testdata/rsa.priv.pem")
b, err := os.ReadFile("testdata/rsa.priv.pem")
if err != nil {
t.Fatal(err)
}

@ -9,7 +9,6 @@ import (
"crypto/rand"
"crypto/x509"
"encoding/pem"
"io/ioutil"
"net"
"os"
"os/exec"
@ -202,7 +201,7 @@ func TestNew(t *testing.T) {
})
// Load ssh test fixtures
b, err := ioutil.ReadFile("testdata/ssh")
b, err := os.ReadFile("testdata/ssh")
if err != nil {
t.Fatal(err)
}
@ -290,7 +289,7 @@ func TestSSHAgentKMS_CreateSigner(t *testing.T) {
}
// Read and decode file using standard packages
b, err := ioutil.ReadFile("testdata/priv.pem")
b, err := os.ReadFile("testdata/priv.pem")
if err != nil {
t.Fatal(err)
}
@ -315,7 +314,7 @@ func TestSSHAgentKMS_CreateSigner(t *testing.T) {
})
// Load ssh test fixtures
sshPubKeyStr, err := ioutil.ReadFile("testdata/ssh.pub")
sshPubKeyStr, err := os.ReadFile("testdata/ssh.pub")
if err != nil {
t.Fatal(err)
}
@ -323,7 +322,7 @@ func TestSSHAgentKMS_CreateSigner(t *testing.T) {
if err != nil {
t.Fatal(err)
}
b, err = ioutil.ReadFile("testdata/ssh")
b, err = os.ReadFile("testdata/ssh")
if err != nil {
t.Fatal(err)
}
@ -499,7 +498,7 @@ func TestSSHAgentKMS_CreateKey(t *testing.T) {
*/
func TestSSHAgentKMS_GetPublicKey(t *testing.T) {
b, err := ioutil.ReadFile("testdata/pub.pem")
b, err := os.ReadFile("testdata/pub.pem")
if err != nil {
t.Fatal(err)
}
@ -510,7 +509,7 @@ func TestSSHAgentKMS_GetPublicKey(t *testing.T) {
}
// Load ssh test fixtures
b, err = ioutil.ReadFile("testdata/ssh.pub")
b, err = os.ReadFile("testdata/ssh.pub")
if err != nil {
t.Fatal(err)
}
@ -518,7 +517,7 @@ func TestSSHAgentKMS_GetPublicKey(t *testing.T) {
if err != nil {
t.Fatal(err)
}
b, err = ioutil.ReadFile("testdata/ssh")
b, err = os.ReadFile("testdata/ssh")
if err != nil {
t.Fatal(err)
}

@ -3,8 +3,8 @@ package uri
import (
"bytes"
"encoding/hex"
"io/ioutil"
"net/url"
"os"
"strings"
"unicode"
@ -140,7 +140,7 @@ func readFile(path string) ([]byte, error) {
if err == nil && (u.Scheme == "" || u.Scheme == "file") && u.Path != "" {
path = u.Path
}
b, err := ioutil.ReadFile(path)
b, err := os.ReadFile(path)
if err != nil {
return nil, errors.Wrapf(err, "error reading %s", path)
}

@ -29,9 +29,9 @@ import (
"github.com/smallstep/certificates/kms"
kmsapi "github.com/smallstep/certificates/kms/apiv1"
"github.com/smallstep/nosql"
"go.step.sm/cli-utils/config"
"go.step.sm/cli-utils/errs"
"go.step.sm/cli-utils/fileutil"
"go.step.sm/cli-utils/step"
"go.step.sm/cli-utils/ui"
"go.step.sm/crypto/jose"
"go.step.sm/crypto/pemutil"
@ -87,44 +87,50 @@ const (
)
// GetDBPath returns the path where the file-system persistence is stored
// based on the STEPPATH environment variable.
// based on the $(step path).
func GetDBPath() string {
return filepath.Join(config.StepPath(), dbPath)
return filepath.Join(step.Path(), dbPath)
}
// GetConfigPath returns the directory where the configuration files are stored
// based on the STEPPATH environment variable.
// based on the $(step path).
func GetConfigPath() string {
return filepath.Join(config.StepPath(), configPath)
return filepath.Join(step.Path(), configPath)
}
// GetProfileConfigPath returns the directory where the profile configuration
// files are stored based on the $(step path).
func GetProfileConfigPath() string {
return filepath.Join(step.ProfilePath(), configPath)
}
// GetPublicPath returns the directory where the public keys are stored based on
// the STEPPATH environment variable.
// the $(step path).
func GetPublicPath() string {
return filepath.Join(config.StepPath(), publicPath)
return filepath.Join(step.Path(), publicPath)
}
// GetSecretsPath returns the directory where the private keys are stored based
// on the STEPPATH environment variable.
// on the $(step path).
func GetSecretsPath() string {
return filepath.Join(config.StepPath(), privatePath)
return filepath.Join(step.Path(), privatePath)
}
// GetRootCAPath returns the path where the root CA is stored based on the
// STEPPATH environment variable.
// $(step path).
func GetRootCAPath() string {
return filepath.Join(config.StepPath(), publicPath, "root_ca.crt")
return filepath.Join(step.Path(), publicPath, "root_ca.crt")
}
// GetOTTKeyPath returns the path where the one-time token key is stored based
// on the STEPPATH environment variable.
// on the $(step path).
func GetOTTKeyPath() string {
return filepath.Join(config.StepPath(), privatePath, "ott_key")
return filepath.Join(step.Path(), privatePath, "ott_key")
}
// GetTemplatesPath returns the path where the templates are stored.
func GetTemplatesPath() string {
return filepath.Join(config.StepPath(), templatesPath)
return filepath.Join(step.Path(), templatesPath)
}
// GetProvisioners returns the map of provisioners on the given CA.
@ -286,20 +292,22 @@ func WithKeyURIs(rootKey, intermediateKey, hostKey, userKey string) Option {
// PKI represents the Public Key Infrastructure used by a certificate authority.
type PKI struct {
linkedca.Configuration
Defaults linkedca.Defaults
casOptions apiv1.Options
caService apiv1.CertificateAuthorityService
caCreator apiv1.CertificateAuthorityCreator
keyManager kmsapi.KeyManager
config string
defaults string
ottPublicKey *jose.JSONWebKey
ottPrivateKey *jose.JSONWebEncryption
options *options
Defaults linkedca.Defaults
casOptions apiv1.Options
caService apiv1.CertificateAuthorityService
caCreator apiv1.CertificateAuthorityCreator
keyManager kmsapi.KeyManager
config string
defaults string
profileDefaults string
ottPublicKey *jose.JSONWebKey
ottPrivateKey *jose.JSONWebEncryption
options *options
}
// New creates a new PKI configuration.
func New(o apiv1.Options, opts ...Option) (*PKI, error) {
currentCtx := step.Contexts().GetCurrent()
caService, err := cas.New(context.Background(), o)
if err != nil {
return nil, err
@ -358,6 +366,9 @@ func New(o apiv1.Options, opts ...Option) (*PKI, error) {
cfg = GetConfigPath()
// Create directories
dirs := []string{public, private, cfg, GetTemplatesPath()}
if currentCtx != nil {
dirs = append(dirs, GetProfileConfigPath())
}
for _, name := range dirs {
if _, err := os.Stat(name); os.IsNotExist(err) {
if err = os.MkdirAll(name, 0700); err != nil {
@ -415,6 +426,10 @@ func New(o apiv1.Options, opts ...Option) (*PKI, error) {
if p.defaults, err = getPath(cfg, "defaults.json"); err != nil {
return nil, err
}
if currentCtx != nil {
p.profileDefaults = currentCtx.ProfileDefaultsFile()
}
if p.config, err = getPath(cfg, "ca.json"); err != nil {
return nil, err
}
@ -944,6 +959,18 @@ func (p *PKI) Save(opt ...ConfigOption) error {
if err = fileutil.WriteFile(p.defaults, b, 0644); err != nil {
return errs.FileError(err, p.defaults)
}
// If we're using contexts then write a blank object to the default profile
// configuration location.
if p.profileDefaults != "" {
if _, err := os.Stat(p.profileDefaults); os.IsNotExist(err) {
// Write with 0600 to be consistent with directories structure.
if err = fileutil.WriteFile(p.profileDefaults, []byte("{}"), 0600); err != nil {
return errs.FileError(err, p.profileDefaults)
}
} else if err != nil {
return errs.FileError(err, p.profileDefaults)
}
}
// Generate and write templates
if err := generateTemplates(cfg.Templates); err != nil {
@ -958,6 +985,9 @@ func (p *PKI) Save(opt ...ConfigOption) error {
}
ui.PrintSelected("Default configuration", p.defaults)
if p.profileDefaults != "" {
ui.PrintSelected("Default profile configuration", p.profileDefaults)
}
ui.PrintSelected("Certificate Authority configuration", p.config)
if p.options.deploymentType != LinkedDeployment {
ui.Println()

@ -6,9 +6,9 @@ import (
"github.com/pkg/errors"
"github.com/smallstep/certificates/templates"
"go.step.sm/cli-utils/config"
"go.step.sm/cli-utils/errs"
"go.step.sm/cli-utils/fileutil"
"go.step.sm/cli-utils/step"
)
// getTemplates returns all the templates enabled
@ -44,7 +44,7 @@ func generateTemplates(t *templates.Templates) error {
if !ok {
return errors.Errorf("template %s does not exists", t.Name)
}
if err := fileutil.WriteFile(config.StepAbs(t.TemplatePath), []byte(data), 0644); err != nil {
if err := fileutil.WriteFile(step.Abs(t.TemplatePath), []byte(data), 0644); err != nil {
return err
}
}
@ -53,7 +53,7 @@ func generateTemplates(t *templates.Templates) error {
if !ok {
return errors.Errorf("template %s does not exists", t.Name)
}
if err := fileutil.WriteFile(config.StepAbs(t.TemplatePath), []byte(data), 0644); err != nil {
if err := fileutil.WriteFile(step.Abs(t.TemplatePath), []byte(data), 0644); err != nil {
return err
}
}

@ -5,7 +5,6 @@ import (
"crypto/x509"
"encoding/base64"
"io"
"io/ioutil"
"net/http"
"net/url"
"strings"
@ -167,7 +166,7 @@ func decodeSCEPRequest(r *http.Request) (SCEPRequest, error) {
return SCEPRequest{}, errors.Errorf("unsupported operation: %s", operation)
}
case http.MethodPost:
body, err := ioutil.ReadAll(io.LimitReader(r.Body, maxPayloadSize))
body, err := io.ReadAll(io.LimitReader(r.Body, maxPayloadSize))
if err != nil {
return SCEPRequest{}, err
}

@ -2,7 +2,6 @@ package templates
import (
"bytes"
"io/ioutil"
"os"
"path/filepath"
"strings"
@ -10,8 +9,8 @@ import (
"github.com/Masterminds/sprig/v3"
"github.com/pkg/errors"
"go.step.sm/cli-utils/config"
"go.step.sm/cli-utils/fileutil"
"go.step.sm/cli-utils/step"
)
// TemplateType defines how a template will be written in disk.
@ -20,6 +19,9 @@ type TemplateType string
const (
// Snippet will mark a template as a part of a file.
Snippet TemplateType = "snippet"
// PrependLine is a template for prepending a single line to a file. If the
// line already exists in the file it will be removed first.
PrependLine TemplateType = "prepend-line"
// File will mark a templates as a full file.
File TemplateType = "file"
// Directory will mark a template as a directory.
@ -99,7 +101,7 @@ func (t *SSHTemplates) Validate() (err error) {
return
}
// Template represents on template file.
// Template represents a template file.
type Template struct {
*template.Template
Name string `json:"name"`
@ -118,8 +120,8 @@ func (t *Template) Validate() error {
return nil
case t.Name == "":
return errors.New("template name cannot be empty")
case t.Type != Snippet && t.Type != File && t.Type != Directory:
return errors.Errorf("invalid template type %s, it must be %s, %s, or %s", t.Type, Snippet, File, Directory)
case t.Type != Snippet && t.Type != File && t.Type != Directory && t.Type != PrependLine:
return errors.Errorf("invalid template type %s, it must be %s, %s, %s, or %s", t.Type, Snippet, PrependLine, File, Directory)
case t.TemplatePath == "" && t.Type != Directory && len(t.Content) == 0:
return errors.New("template template cannot be empty")
case t.TemplatePath != "" && t.Type == Directory:
@ -132,7 +134,7 @@ func (t *Template) Validate() error {
if t.TemplatePath != "" {
// Check for file
st, err := os.Stat(config.StepAbs(t.TemplatePath))
st, err := os.Stat(step.Abs(t.TemplatePath))
if err != nil {
return errors.Wrapf(err, "error reading %s", t.TemplatePath)
}
@ -166,8 +168,8 @@ func (t *Template) Load() error {
if t.Template == nil && t.Type != Directory {
switch {
case t.TemplatePath != "":
filename := config.StepAbs(t.TemplatePath)
b, err := ioutil.ReadFile(filename)
filename := step.Abs(t.TemplatePath)
b, err := os.ReadFile(filename)
if err != nil {
return errors.Wrapf(err, "error reading %s", filename)
}
@ -247,7 +249,10 @@ type Output struct {
// Write writes the Output to the filesystem as a directory, file or snippet.
func (o *Output) Write() error {
path := config.StepAbs(o.Path)
// Replace ${STEPPATH} with the base step path.
o.Path = strings.ReplaceAll(o.Path, "${STEPPATH}", step.BasePath())
path := step.Abs(o.Path)
if o.Type == Directory {
return mkdir(path, 0700)
}
@ -257,11 +262,17 @@ func (o *Output) Write() error {
return err
}
if o.Type == File {
switch o.Type {
case File:
return fileutil.WriteFile(path, o.Content, 0600)
case Snippet:
return fileutil.WriteSnippet(path, o.Content, 0600)
case PrependLine:
return fileutil.PrependLine(path, o.Content, 0600)
default:
// Default to using a Snippet type if the type is not known.
return fileutil.WriteSnippet(path, o.Content, 0600)
}
return fileutil.WriteSnippet(path, o.Content, 0600)
}
func mkdir(path string, perm os.FileMode) error {

@ -4,6 +4,10 @@ import (
"golang.org/x/crypto/ssh"
)
// SSHTemplateVersionKey is a key that can be submitted by a client to select
// the template version that will be returned by the server.
var SSHTemplateVersionKey = "StepSSHTemplateVersion"
// Step represents the default variables available in the CA.
type Step struct {
SSH StepSSH
@ -22,16 +26,23 @@ type StepSSH struct {
var DefaultSSHTemplates = SSHTemplates{
User: []Template{
{
Name: "include.tpl",
Name: "config.tpl",
Type: Snippet,
TemplatePath: "templates/ssh/include.tpl",
TemplatePath: "templates/ssh/config.tpl",
Path: "~/.ssh/config",
Comment: "#",
},
{
Name: "config.tpl",
Name: "step_includes.tpl",
Type: PrependLine,
TemplatePath: "templates/ssh/step_includes.tpl",
Path: "${STEPPATH}/ssh/includes",
Comment: "#",
},
{
Name: "step_config.tpl",
Type: File,
TemplatePath: "templates/ssh/config.tpl",
TemplatePath: "templates/ssh/step_config.tpl",
Path: "ssh/config",
Comment: "#",
},
@ -64,30 +75,43 @@ var DefaultSSHTemplates = SSHTemplates{
// DefaultSSHTemplateData contains the data of the default templates used on ssh.
var DefaultSSHTemplateData = map[string]string{
// include.tpl adds the step ssh config file.
// config.tpl adds the step ssh config file.
//
// Note: on windows `Include C:\...` is treated as a relative path.
"include.tpl": `Host *
"config.tpl": `Host *
{{- if or .User.GOOS "none" | eq "windows" }}
Include "{{ .User.StepPath | replace "\\" "/" | trimPrefix "C:" }}/ssh/config"
{{- if .User.StepBasePath }}
Include "{{ .User.StepBasePath | replace "\\" "/" | trimPrefix "C:" }}/ssh/includes"
{{- else }}
Include "{{.User.StepPath}}/ssh/config"
Include "{{ .User.StepPath | replace "\\" "/" | trimPrefix "C:" }}/ssh/includes"
{{- end }}
{{- else }}
{{- if .User.StepBasePath }}
Include "{{.User.StepBasePath}}/ssh/includes"
{{- else }}
Include "{{.User.StepPath}}/ssh/includes"
{{- end }}
{{- end }}`,
// config.tpl is the step ssh config file, it includes the Match rule and
// step_includes.tpl adds the step ssh config file.
//
// Note: on windows `Include C:\...` is treated as a relative path.
"step_includes.tpl": `{{- if or .User.GOOS "none" | eq "windows" }}Include "{{ .User.StepPath | replace "\\" "/" | trimPrefix "C:" }}/ssh/config"{{- else }}Include "{{.User.StepPath}}/ssh/config"{{- end }}`,
// step_config.tpl is the step ssh config file, it includes the Match rule and
// references the step known_hosts file.
//
// Note: on windows ProxyCommand requires the full path
"config.tpl": `Match exec "step ssh check-host %h"
"step_config.tpl": `Match exec "step ssh check-host{{- if .User.Context }} --context {{ .User.Context }}{{- end }} %h"
{{- if .User.User }}
User {{.User.User}}
{{- end }}
{{- if or .User.GOOS "none" | eq "windows" }}
UserKnownHostsFile "{{.User.StepPath}}\ssh\known_hosts"
ProxyCommand C:\Windows\System32\cmd.exe /c step ssh proxycommand %r %h %p
ProxyCommand C:\Windows\System32\cmd.exe /c step ssh proxycommand{{- if .User.Context }} --context {{ .User.Context }}{{- end }} %r %h %p
{{- else }}
UserKnownHostsFile "{{.User.StepPath}}/ssh/known_hosts"
ProxyCommand step ssh proxycommand %r %h %p
ProxyCommand step ssh proxycommand{{- if .User.Context }} --context {{ .User.Context }}{{- end }} %r %h %p
{{- end }}
`,

Loading…
Cancel
Save