Merge branch 'master' into hs/scep
This commit is contained in:
commit
c04f556dc2
14 changed files with 259 additions and 163 deletions
2
.github/needs-triage-labeler.yml
vendored
Normal file
2
.github/needs-triage-labeler.yml
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
needs triage:
|
||||
- "**"
|
14
.github/workflows/labeler.yml
vendored
Normal file
14
.github/workflows/labeler.yml
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
name: labeler
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
label:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/labeler@v2
|
||||
with:
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
configuration-path: .github/needs-triage-labeler.yml
|
|
@ -147,6 +147,8 @@ func LoadConfiguration(filename string) (*Config, error) {
|
|||
return nil, errors.Wrapf(err, "error parsing %s", filename)
|
||||
}
|
||||
|
||||
c.init()
|
||||
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -86,6 +86,21 @@ func (o *OIDC) IsAdmin(email string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// IsAdminGroup returns true if the one group in the given list is in the Admins
|
||||
// allowlist, false otherwise.
|
||||
func (o *OIDC) IsAdminGroup(groups []string) bool {
|
||||
for _, g := range groups {
|
||||
// The groups and emails can be in the same array for now, but consider
|
||||
// making a specialized option later.
|
||||
for _, gadmin := range o.Admins {
|
||||
if g == gadmin {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func sanitizeEmail(email string) string {
|
||||
if i := strings.LastIndex(email, "@"); i >= 0 {
|
||||
email = email[:i] + strings.ToLower(email[i:])
|
||||
|
@ -372,7 +387,8 @@ func (o *OIDC) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption
|
|||
}
|
||||
|
||||
// Get the identity using either the default identityFunc or one injected
|
||||
// externally.
|
||||
// externally. Note that the PreferredUsername might be empty.
|
||||
// TBD: Would preferred_username present a safety issue here?
|
||||
iden, err := o.getIdentityFunc(ctx, o, claims.Email)
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(http.StatusInternalServerError, err, "oidc.AuthorizeSSHSign")
|
||||
|
@ -395,6 +411,9 @@ func (o *OIDC) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption
|
|||
// Use the default template unless no-templates are configured and email is
|
||||
// an admin, in that case we will use the parameters in the request.
|
||||
isAdmin := o.IsAdmin(claims.Email)
|
||||
if !isAdmin && len(claims.Groups) > 0 {
|
||||
isAdmin = o.IsAdminGroup(claims.Groups)
|
||||
}
|
||||
defaultTemplate := sshutil.DefaultTemplate
|
||||
if isAdmin && !o.Options.GetSSHOptions().HasTemplate() {
|
||||
defaultTemplate = sshutil.DefaultAdminTemplate
|
||||
|
|
|
@ -506,6 +506,7 @@ func TestOIDC_AuthorizeSSHSign(t *testing.T) {
|
|||
p5.getIdentityFunc = func(ctx context.Context, p Interface, email string) (*Identity, error) {
|
||||
return nil, errors.New("force")
|
||||
}
|
||||
// Additional test needed for empty usernames and duplicate email and usernames
|
||||
|
||||
t1, err := generateSimpleToken("the-issuer", p1.ClientID, &keys.Keys[0])
|
||||
assert.FatalError(t, err)
|
||||
|
@ -514,7 +515,7 @@ func TestOIDC_AuthorizeSSHSign(t *testing.T) {
|
|||
failGetIdentityToken, err := generateSimpleToken("the-issuer", p5.ClientID, &keys.Keys[0])
|
||||
assert.FatalError(t, err)
|
||||
// Admin email not in domains
|
||||
okAdmin, err := generateToken("subject", "the-issuer", p3.ClientID, "root@example.com", []string{}, time.Now(), &keys.Keys[0])
|
||||
okAdmin, err := generateOIDCToken("subject", "the-issuer", p3.ClientID, "root@example.com", "", time.Now(), &keys.Keys[0])
|
||||
assert.FatalError(t, err)
|
||||
// Empty email
|
||||
failEmail, err := generateToken("subject", "the-issuer", p3.ClientID, "", []string{}, time.Now(), &keys.Keys[0])
|
||||
|
@ -576,11 +577,11 @@ func TestOIDC_AuthorizeSSHSign(t *testing.T) {
|
|||
{"ok-options", p1, args{t1, SignSSHOptions{CertType: "user", Principals: []string{"name"}}, pub},
|
||||
&SignSSHOptions{CertType: "user", Principals: []string{"name", "name@smallstep.com"},
|
||||
ValidAfter: NewTimeDuration(tm), ValidBefore: NewTimeDuration(tm.Add(userDuration))}, http.StatusOK, false, false},
|
||||
{"admin-user", p3, args{okAdmin, SignSSHOptions{CertType: "user", KeyID: "root@example.com", Principals: []string{"root", "root@example.com"}}, pub},
|
||||
{"ok-admin-user", p3, args{okAdmin, SignSSHOptions{CertType: "user", KeyID: "root@example.com", Principals: []string{"root", "root@example.com"}}, pub},
|
||||
expectedAdminOptions, http.StatusOK, false, false},
|
||||
{"admin-host", p3, args{okAdmin, SignSSHOptions{CertType: "host", KeyID: "smallstep.com", Principals: []string{"smallstep.com"}}, pub},
|
||||
{"ok-admin-host", p3, args{okAdmin, SignSSHOptions{CertType: "host", KeyID: "smallstep.com", Principals: []string{"smallstep.com"}}, pub},
|
||||
expectedHostOptions, http.StatusOK, false, false},
|
||||
{"admin-options", p3, args{okAdmin, SignSSHOptions{CertType: "user", KeyID: "name", Principals: []string{"name"}}, pub},
|
||||
{"ok-admin-options", p3, args{okAdmin, SignSSHOptions{CertType: "user", KeyID: "name", Principals: []string{"name"}}, pub},
|
||||
&SignSSHOptions{CertType: "user", Principals: []string{"name"},
|
||||
ValidAfter: NewTimeDuration(tm), ValidBefore: NewTimeDuration(tm.Add(userDuration))}, http.StatusOK, false, false},
|
||||
{"fail-rsa1024", p1, args{t1, SignSSHOptions{}, rsa1024.Public()}, expectedUserOptions, http.StatusOK, false, true},
|
||||
|
|
|
@ -345,33 +345,50 @@ type Permissions struct {
|
|||
// GetIdentityFunc is a function that returns an identity.
|
||||
type GetIdentityFunc func(ctx context.Context, p Interface, email string) (*Identity, error)
|
||||
|
||||
// DefaultIdentityFunc return a default identity depending on the provisioner type.
|
||||
// DefaultIdentityFunc return a default identity depending on the provisioner
|
||||
// type. For OIDC email is always present and the usernames might
|
||||
// contain empty strings.
|
||||
func DefaultIdentityFunc(ctx context.Context, p Interface, email string) (*Identity, error) {
|
||||
switch k := p.(type) {
|
||||
case *OIDC:
|
||||
// OIDC principals would be:
|
||||
// 1. Sanitized local.
|
||||
// 2. Raw local (if different).
|
||||
// 3. Email address.
|
||||
// ~~1. Preferred usernames.~~ Note: Under discussion, currently disabled
|
||||
// 2. Sanitized local.
|
||||
// 3. Raw local (if different).
|
||||
// 4. Email address.
|
||||
name := SanitizeSSHUserPrincipal(email)
|
||||
if !sshUserRegex.MatchString(name) {
|
||||
return nil, errors.Errorf("invalid principal '%s' from email '%s'", name, email)
|
||||
}
|
||||
usernames := []string{name}
|
||||
if i := strings.LastIndex(email, "@"); i >= 0 {
|
||||
if local := email[:i]; !strings.EqualFold(local, name) {
|
||||
usernames = append(usernames, local)
|
||||
}
|
||||
usernames = append(usernames, email[:i])
|
||||
}
|
||||
usernames = append(usernames, email)
|
||||
return &Identity{
|
||||
Usernames: usernames,
|
||||
Usernames: SanitizeStringSlices(usernames),
|
||||
}, nil
|
||||
default:
|
||||
return nil, errors.Errorf("provisioner type '%T' not supported by identity function", k)
|
||||
}
|
||||
}
|
||||
|
||||
// SanitizeStringSlices removes duplicated an empty strings.
|
||||
func SanitizeStringSlices(original []string) []string {
|
||||
output := []string{}
|
||||
seen := make(map[string]struct{})
|
||||
for _, entry := range original {
|
||||
if entry == "" {
|
||||
continue
|
||||
}
|
||||
if _, value := seen[entry]; !value {
|
||||
seen[entry] = struct{}{}
|
||||
output = append(output, entry)
|
||||
}
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
// MockProvisioner for testing
|
||||
type MockProvisioner struct {
|
||||
Mret1, Mret2, Mret3 interface{}
|
||||
|
|
|
@ -62,10 +62,11 @@ func TestSanitizeSSHUserPrincipal(t *testing.T) {
|
|||
|
||||
func TestDefaultIdentityFunc(t *testing.T) {
|
||||
type test struct {
|
||||
p Interface
|
||||
email string
|
||||
err error
|
||||
identity *Identity
|
||||
p Interface
|
||||
email string
|
||||
usernames []string
|
||||
err error
|
||||
identity *Identity
|
||||
}
|
||||
tests := map[string]func(*testing.T) test{
|
||||
"fail/unsupported-provisioner": func(t *testing.T) test {
|
||||
|
@ -106,7 +107,7 @@ func TestDefaultIdentityFunc(t *testing.T) {
|
|||
return test{
|
||||
p: &OIDC{},
|
||||
email: "John@smallstep.com",
|
||||
identity: &Identity{Usernames: []string{"john", "John@smallstep.com"}},
|
||||
identity: &Identity{Usernames: []string{"john", "John", "John@smallstep.com"}},
|
||||
}
|
||||
},
|
||||
"ok symbol": func(t *testing.T) test {
|
||||
|
@ -116,6 +117,30 @@ func TestDefaultIdentityFunc(t *testing.T) {
|
|||
identity: &Identity{Usernames: []string{"john_doe", "John+Doe", "John+Doe@smallstep.com"}},
|
||||
}
|
||||
},
|
||||
"ok username": func(t *testing.T) test {
|
||||
return test{
|
||||
p: &OIDC{},
|
||||
email: "john@smallstep.com",
|
||||
usernames: []string{"johnny"},
|
||||
identity: &Identity{Usernames: []string{"john", "john@smallstep.com"}},
|
||||
}
|
||||
},
|
||||
"ok usernames": func(t *testing.T) test {
|
||||
return test{
|
||||
p: &OIDC{},
|
||||
email: "john@smallstep.com",
|
||||
usernames: []string{"johnny", "js", "", "johnny", ""},
|
||||
identity: &Identity{Usernames: []string{"john", "john@smallstep.com"}},
|
||||
}
|
||||
},
|
||||
"ok empty username": func(t *testing.T) test {
|
||||
return test{
|
||||
p: &OIDC{},
|
||||
email: "john@smallstep.com",
|
||||
usernames: []string{""},
|
||||
identity: &Identity{Usernames: []string{"john", "john@smallstep.com"}},
|
||||
}
|
||||
},
|
||||
}
|
||||
for name, get := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
|
|
|
@ -773,6 +773,47 @@ func generateToken(sub, iss, aud string, email string, sans []string, iat time.T
|
|||
return jose.Signed(sig).Claims(claims).CompactSerialize()
|
||||
}
|
||||
|
||||
func generateOIDCToken(sub, iss, aud string, email string, preferredUsername string, iat time.Time, jwk *jose.JSONWebKey, tokOpts ...tokOption) (string, error) {
|
||||
so := new(jose.SignerOptions)
|
||||
so.WithType("JWT")
|
||||
so.WithHeader("kid", jwk.KeyID)
|
||||
|
||||
for _, o := range tokOpts {
|
||||
if err := o(so); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
sig, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.ES256, Key: jwk.Key}, so)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
id, err := randutil.ASCII(64)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
claims := struct {
|
||||
jose.Claims
|
||||
Email string `json:"email"`
|
||||
PreferredUsername string `json:"preferred_username,omitempty"`
|
||||
}{
|
||||
Claims: jose.Claims{
|
||||
ID: id,
|
||||
Subject: sub,
|
||||
Issuer: iss,
|
||||
IssuedAt: jose.NewNumericDate(iat),
|
||||
NotBefore: jose.NewNumericDate(iat),
|
||||
Expiry: jose.NewNumericDate(iat.Add(5 * time.Minute)),
|
||||
Audience: []string{aud},
|
||||
},
|
||||
Email: email,
|
||||
PreferredUsername: preferredUsername,
|
||||
}
|
||||
return jose.Signed(sig).Claims(claims).CompactSerialize()
|
||||
}
|
||||
|
||||
func generateX5CSSHToken(jwk *jose.JSONWebKey, claims *x5cPayload, tokOpts ...tokOption) (string, error) {
|
||||
so := new(jose.SignerOptions)
|
||||
so.WithType("JWT")
|
||||
|
|
|
@ -263,7 +263,7 @@ func (a *Authority) Rekey(oldCert *x509.Certificate, pk crypto.PublicKey) ([]*x5
|
|||
}
|
||||
|
||||
fullchain := append([]*x509.Certificate{resp.Certificate}, resp.CertificateChain...)
|
||||
if err = a.storeCertificate(fullchain); err != nil {
|
||||
if err = a.storeRenewedCertificate(oldCert, fullchain); err != nil {
|
||||
if err != db.ErrNotImplemented {
|
||||
return nil, errs.Wrap(http.StatusInternalServerError, err, "authority.Rekey; error storing certificate in db", opts...)
|
||||
}
|
||||
|
@ -287,6 +287,19 @@ func (a *Authority) storeCertificate(fullchain []*x509.Certificate) error {
|
|||
return a.db.StoreCertificate(fullchain[0])
|
||||
}
|
||||
|
||||
// storeRenewedCertificate allows to use an extension of the db.AuthDB interface
|
||||
// that can log if a certificate has been renewed or rekeyed.
|
||||
//
|
||||
// TODO: at some point we should implement this in the standard implementation.
|
||||
func (a *Authority) storeRenewedCertificate(oldCert *x509.Certificate, fullchain []*x509.Certificate) error {
|
||||
if s, ok := a.db.(interface {
|
||||
StoreRenewedCertificate(*x509.Certificate, ...*x509.Certificate) error
|
||||
}); ok {
|
||||
return s.StoreRenewedCertificate(oldCert, fullchain...)
|
||||
}
|
||||
return a.db.StoreCertificate(fullchain[0])
|
||||
}
|
||||
|
||||
// RevokeOptions are the options for the Revoke API.
|
||||
type RevokeOptions struct {
|
||||
Serial string
|
||||
|
|
30
ca/client.go
30
ca/client.go
|
@ -616,6 +616,36 @@ retry:
|
|||
return &sign, nil
|
||||
}
|
||||
|
||||
// Rekey performs the rekey request to the CA and returns the api.SignResponse
|
||||
// struct.
|
||||
func (c *Client) Rekey(req *api.RekeyRequest, tr http.RoundTripper) (*api.SignResponse, error) {
|
||||
var retried bool
|
||||
body, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error marshaling request")
|
||||
}
|
||||
|
||||
u := c.endpoint.ResolveReference(&url.URL{Path: "/rekey"})
|
||||
client := &http.Client{Transport: tr}
|
||||
retry:
|
||||
resp, err := client.Post(u.String(), "application/json", bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return nil, errs.Wrapf(http.StatusInternalServerError, err, "client.Rekey; client POST %s failed", u)
|
||||
}
|
||||
if resp.StatusCode >= 400 {
|
||||
if !retried && c.retryOnError(resp) {
|
||||
retried = true
|
||||
goto retry
|
||||
}
|
||||
return nil, readError(resp.Body)
|
||||
}
|
||||
var sign api.SignResponse
|
||||
if err := readJSON(resp.Body, &sign); err != nil {
|
||||
return nil, errs.Wrapf(http.StatusInternalServerError, err, "client.Rekey; error reading %s", u)
|
||||
}
|
||||
return &sign, nil
|
||||
}
|
||||
|
||||
// Revoke performs the revoke request to the CA and returns the api.RevokeResponse
|
||||
// struct.
|
||||
func (c *Client) Revoke(req *api.RevokeRequest, tr http.RoundTripper) (*api.RevokeResponse, error) {
|
||||
|
|
|
@ -529,6 +529,75 @@ func TestClient_Renew(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestClient_Rekey(t *testing.T) {
|
||||
ok := &api.SignResponse{
|
||||
ServerPEM: api.Certificate{Certificate: parseCertificate(certPEM)},
|
||||
CaPEM: api.Certificate{Certificate: parseCertificate(rootPEM)},
|
||||
CertChainPEM: []api.Certificate{
|
||||
{Certificate: parseCertificate(certPEM)},
|
||||
{Certificate: parseCertificate(rootPEM)},
|
||||
},
|
||||
}
|
||||
|
||||
request := &api.RekeyRequest{
|
||||
CsrPEM: api.CertificateRequest{CertificateRequest: parseCertificateRequest(csrPEM)},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
request *api.RekeyRequest
|
||||
response interface{}
|
||||
responseCode int
|
||||
wantErr bool
|
||||
err error
|
||||
}{
|
||||
{"ok", request, ok, 200, false, nil},
|
||||
{"unauthorized", request, errs.Unauthorized("force"), 401, true, errors.New(errs.UnauthorizedDefaultMsg)},
|
||||
{"empty request", &api.RekeyRequest{}, errs.BadRequest("force"), 400, true, errors.New(errs.BadRequestDefaultMsg)},
|
||||
{"nil request", nil, errs.BadRequest("force"), 400, true, errors.New(errs.BadRequestDefaultMsg)},
|
||||
}
|
||||
|
||||
srv := httptest.NewServer(nil)
|
||||
defer srv.Close()
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c, err := NewClient(srv.URL, WithTransport(http.DefaultTransport))
|
||||
if err != nil {
|
||||
t.Errorf("NewClient() error = %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
api.JSONStatus(w, tt.response, tt.responseCode)
|
||||
})
|
||||
|
||||
got, err := c.Rekey(tt.request, nil)
|
||||
if (err != nil) != tt.wantErr {
|
||||
fmt.Printf("%+v", err)
|
||||
t.Errorf("Client.Renew() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
|
||||
switch {
|
||||
case err != nil:
|
||||
if got != nil {
|
||||
t.Errorf("Client.Renew() = %v, want nil", got)
|
||||
}
|
||||
|
||||
sc, ok := err.(errs.StatusCoder)
|
||||
assert.Fatal(t, ok, "error does not implement StatusCoder interface")
|
||||
assert.Equals(t, sc.StatusCode(), tt.responseCode)
|
||||
assert.HasPrefix(t, tt.err.Error(), err.Error())
|
||||
default:
|
||||
if !reflect.DeepEqual(got, tt.response) {
|
||||
t.Errorf("Client.Renew() = %v, want %v", got, tt.response)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_Provisioners(t *testing.T) {
|
||||
ok := &api.ProvisionersResponse{
|
||||
Provisioners: provisioner.List{},
|
||||
|
|
|
@ -134,6 +134,12 @@ func WriteDefaultIdentity(certChain []api.Certificate, key crypto.PrivateKey) er
|
|||
return nil
|
||||
}
|
||||
|
||||
// WriteIdentityCertificate writes the identity certificate to disk.
|
||||
func WriteIdentityCertificate(certChain []api.Certificate) error {
|
||||
filename := filepath.Join(identityDir, "identity.crt")
|
||||
return writeCertificate(filename, certChain)
|
||||
}
|
||||
|
||||
// writeCertificate writes the given certificate on disk.
|
||||
func writeCertificate(filename string, certChain []api.Certificate) error {
|
||||
buf := new(bytes.Buffer)
|
||||
|
|
143
distribution.md
143
distribution.md
|
@ -1,143 +0,0 @@
|
|||
# Distribution
|
||||
|
||||
This section describes how to build and deploy publicly available releases of
|
||||
the Step CA.
|
||||
|
||||
## Creating A New Release
|
||||
|
||||
New releases are (almost) entirely built and deployed by Travis-CI. Creating a new
|
||||
release is as simple as pushing a new github tag.
|
||||
|
||||
**Definitions**:
|
||||
|
||||
* **Standard Release**: ready for public use. no `-rc*` suffix on the version.
|
||||
e.g. `v1.0.2`
|
||||
* **Release Candidate**: not ready for public use, still testing. must have a
|
||||
`-rc*` suffix. e.g. `v1.0.2-rc` or `v1.0.2-rc.4`
|
||||
|
||||
---
|
||||
1. **Tag it!**
|
||||
|
||||
1. Find the most recent tag.
|
||||
|
||||
```
|
||||
$> git fetch --tags
|
||||
$> git tag
|
||||
```
|
||||
|
||||
The new tag needs to be the logical successor of the most recent existing tag.
|
||||
See [versioning](#versioning) section for more information on version numbers.
|
||||
|
||||
2. Select the type and value of the next tag.
|
||||
|
||||
Is the new release a *release candidate* or a *standard release*?
|
||||
|
||||
1. **Release Candidate**
|
||||
|
||||
If the most recent tag is a standard release, say `v1.0.2`, then the version
|
||||
of the next release candidate should be `v1.0.3-rc.1`. If the most recent tag
|
||||
is a release candidate, say `v1.0.2-rc.3`, then the version of the next
|
||||
release candidate should be `v1.0.2-rc.4`.
|
||||
|
||||
2. Standard Release
|
||||
|
||||
If the most recent tag is a standard release, say `v1.0.2`, then the version
|
||||
of the next standard release should be `v1.0.3`. If the most recent tag
|
||||
is a release candidate, say `v1.0.2-rc.3`, then the version of the next
|
||||
standard release should be `v1.0.3`.
|
||||
|
||||
|
||||
3. Create a local tag.
|
||||
|
||||
```
|
||||
# standard release
|
||||
$> git tag v1.0.3
|
||||
...or
|
||||
# release candidate
|
||||
$> git tag v1.0.3-rc.1
|
||||
```
|
||||
|
||||
4. Push the new tag to the remote origin.
|
||||
|
||||
```
|
||||
# standard release
|
||||
$> git push origin tag v1.0.3
|
||||
...or
|
||||
# release candidate
|
||||
$> git push origin tag v1.0.3-rc.1
|
||||
```
|
||||
|
||||
2. **Check the build status at**
|
||||
[Travis-CI](https://travis-ci.com/smallstep/certificates/builds/).
|
||||
|
||||
Travis will begin by verifying that there are no compilation or linting errors
|
||||
and then run the unit tests. Assuming all the checks have passed, Travis will
|
||||
build Darwin and Linux artifacts (for easily installing `step`) and upload them
|
||||
as part of the [Github Release](https://github.com/smallstep/certificates/releases).
|
||||
|
||||
Travis will build and upload the following artifacts:
|
||||
|
||||
* **step-ca_1.0.3_amd64.deb**: debian package for installation on linux.
|
||||
* **step-ca_linux_1.0.3_amd64.tar.gz**: tarball containing a statically compiled linux binary.
|
||||
* **step-ca_darwin_1.0.3_amd64.tar.gz**: tarball containing a statically compiled darwin binary.
|
||||
* **step-ca_1.0.3.tar.gz**: tarball containing a git archive of the full repo.
|
||||
|
||||
3. **Update the AUR Arch Linux package**
|
||||
|
||||
> **NOTE**: if you plan to release `cli` next then you can skip this step.
|
||||
|
||||
<pre><code>
|
||||
<b>$ cd archlinux</b>
|
||||
|
||||
# Get up to date...
|
||||
<b>$ git pull origin master</b>
|
||||
<b>$ make</b>
|
||||
|
||||
<b>$ ./update --ca v1.0.3</b>
|
||||
</code></pre>
|
||||
|
||||
4. **Update the Helm packages**
|
||||
|
||||
> **NOTE**: This is an optional step, only necessary if we want to release a
|
||||
> new helm package.
|
||||
|
||||
Once we have the docker images, we can release a new version in our Helm
|
||||
[repository](https://smallstep.github.io/helm-charts/) we need to pull the
|
||||
[helm-charts](https://github.com/smallstep/helm-charts) project, and change the
|
||||
following:
|
||||
|
||||
* On step-certificates/Chart.yaml:
|
||||
* Increase the `version` number (Helm Chart version).
|
||||
* Set the `appVersion` to the step-certificates version.
|
||||
* On step-certificates/values.yaml:
|
||||
* Set the docker tag `image.tag` to the appropriate version.
|
||||
|
||||
Then create the step-certificates package running:
|
||||
|
||||
<pre><code>
|
||||
<b>$ helm package ./step-certificates</b>
|
||||
</code></pre>
|
||||
|
||||
A new file like `step-certificates-<version>.tgz` will be created.
|
||||
Now commit and push your changes (don't commit the tarball) to the master
|
||||
branch of `smallstep/helm-charts`
|
||||
|
||||
Next checkout the `gh-pages` branch. `git add` the new tar-ball and update
|
||||
the index.yaml using the `helm repo index` command:
|
||||
|
||||
<pre><code>
|
||||
<b>$ git checkout gh-pages</b>
|
||||
<b>$ git pull origin gh-pages</b>
|
||||
<b>$ git add "step-certificates-<version>.tgz"</b>
|
||||
<b>$ helm repo index --merge index.yaml --url https://smallstep.github.io/helm-charts/ .</b>
|
||||
<b>$ git commit -a -m "Add package for step-certificates vX.Y.Z"</b>
|
||||
<b>$ git push origin gh-pages</b>
|
||||
</code></pre>
|
||||
|
||||
***All Done!***
|
||||
|
||||
## Versioning
|
||||
|
||||
We use [SemVer](http://semver.org/) for versioning. See the
|
||||
[tags on this repository](https://github.com/smallstep/certificates) for all
|
||||
available versions.
|
|
@ -26,7 +26,7 @@ ExecStart=/usr/bin/step ca renew --force $CERT_LOCATION $KEY_LOCATION
|
|||
|
||||
; Try to reload or restart the systemd service that relies on this cert-renewer
|
||||
; If the relying service doesn't exist, forge ahead.
|
||||
ExecStartPost=-systemctl try-reload-or-restart %i
|
||||
ExecStartPost=/usr/bin/env bash -c "if ! systemctl --quiet is-enabled %i.service ; then exit 0; fi; systemctl try-reload-or-restart %i"
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
|
Loading…
Reference in a new issue