Skip to content
This repository has been archived by the owner on Mar 16, 2024. It is now read-only.

Commit

Permalink
Parameterize basic secret generation
Browse files Browse the repository at this point in the history
Enable users to specify character set and length for username
and password fields of generated basic auth secrets.

Signed-off-by: Nick Hale <[email protected]>
  • Loading branch information
njhale committed Oct 13, 2023
1 parent 11b735b commit aa650df
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 46 deletions.
6 changes: 3 additions & 3 deletions integration/secrets/testdata/generated/Acornfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ jobs: {
cmd: ["sh", "-c", "echo -n $PASS > /run/secrets/output"]
}
cronpass: {
pass
schedule: "* * * * * "
}
pass
schedule: "* * * * * "
}
}

secrets: {
Expand Down
10 changes: 10 additions & 0 deletions pkg/appdefinition/app.acorn
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,16 @@
SecretBasicAuth: {
SecretBase
type: string == "basic"
params: {
// The character set used in the generated string
passwordCharacters: string || default "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%^&*_-=+"
// The length of the token to be generated
passwordLength: (int >= 0 && int <= 256) || default 16
// The character set used in the generated string
usernameCharacters: string || default "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%^&*_-=+"
// The length of the token to be generated
usernameLength: (int >= 0 && int <= 256) || default 8
}
data?: {
username?: string
password?: string
Expand Down
6 changes: 6 additions & 0 deletions pkg/appdefinition/appdefinition_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1454,6 +1454,12 @@ secrets: {
}, appSpec.Secrets["explicit"])
assert.Equal(t, v1.Secret{
Type: "basic",
Params: v1.NewGenericMap(map[string]any{
"passwordCharacters": "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%^&*_-=+",
"passwordLength": int64(16),
"usernameCharacters": "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%^&*_-=+",
"usernameLength": int64(8),
}),
Data: map[string]string{
"username": "bardata",
"password": "barpass",
Expand Down
8 changes: 5 additions & 3 deletions pkg/controller/secrets/secret_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/acorn-io/runtime/pkg/labels"
"github.com/acorn-io/runtime/pkg/scheme"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
Expand Down Expand Up @@ -86,7 +87,8 @@ func TestBasic_Gen(t *testing.T) {
},
AppSpec: v1.AppSpec{
Secrets: map[string]v1.Secret{
"pass": {Type: "basic",
"pass": {
Type: "basic",
Data: map[string]string{
// cue will populate empty string if not sent
"username": "",
Expand All @@ -108,8 +110,8 @@ func TestBasic_Gen(t *testing.T) {
t.Fatal(err)
}

assert.Len(t, resp.Client.Created, 2)
assert.Len(t, resp.Collected, 2)
require.Len(t, resp.Client.Created, 2)
require.Len(t, resp.Collected, 2)

secret := resp.Client.Created[0].(*corev1.Secret)
assert.Equal(t, "pass", secret.Labels[labels.AcornSecretName])
Expand Down
36 changes: 36 additions & 0 deletions pkg/secrets/generate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package secrets

import (
"crypto/rand"
"math/big"
)

const (
defaultLength = 54
defaultCharacterSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%^&*_-=+"
)

// GenerateRandomSecret generates a random secret with the specified length and character set.
// If the length is less than 1, a default value of 54 will be used.
// If the character set is empty, a default value of "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%^&*_-=+"
// will be used.
func GenerateRandomSecret(length int, characterSet string) (string, error) {
if length < 1 {
length = defaultLength
}
if characterSet == "" {
characterSet = defaultCharacterSet
}

// Generate a random secret by randomly selecting characters from the given character set.
secret := make([]byte, length)
for i := 0; i < length; i++ {
index, err := rand.Int(rand.Reader, big.NewInt(int64(len(characterSet))))
if err != nil {
return "", err
}
secret[i] = characterSet[index.Int64()]
}

return string(secret), nil
}
32 changes: 0 additions & 32 deletions pkg/secrets/generateSecret.go

This file was deleted.

37 changes: 31 additions & 6 deletions pkg/secrets/secret.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,15 +249,40 @@ func generateBasic(req router.Request, appInstance *v1.AppInstance, secretName s
Type: v1.SecretTypeBasic,
}

for i, key := range []string{corev1.BasicAuthUsernameKey, corev1.BasicAuthPasswordKey} {
if len(secret.Data[key]) == 0 {
v, err := GenerateRandomSecret(54)
v = v[:(i+1)*8]
if err != nil {
for _, keys := range []struct {
dataKey, lengthKey, charactersKey string
}{
{
dataKey: corev1.BasicAuthUsernameKey,
lengthKey: "usernameLength",
charactersKey: "usernameCharacters",
},
{
dataKey: corev1.BasicAuthPasswordKey,
lengthKey: "passwordLength",
charactersKey: "passwordCharacters",
},
} {
if len(secret.Data[keys.dataKey]) > 0 {
// Explicitly set by user, don't generate
continue
}

var length int64
if lengthParam, ok := secretRef.Params.GetData()[keys.lengthKey]; ok {
var err error
if length, err = convert.ToNumber(lengthParam); err != nil {
return nil, err
}
secret.Data[key] = []byte(v)
}
characters := convert.ToString(secretRef.Params.GetData()[keys.charactersKey])

v, err := GenerateRandomSecret(int(length), characters)
if err != nil {
return nil, err
}

secret.Data[keys.dataKey] = []byte(v)
}

return updateOrCreate(req, existing, secret)
Expand Down
4 changes: 2 additions & 2 deletions pkg/server/registry/apigroups/acorn/secrets/strategy.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ func (d *defaultSecretGenerateStrategy) Create(ctx context.Context, object types
// If the secret is of type 'basic' and data is empty,
// default username and password values are set.
if secret.Type == "basic" && secret.Data == nil {
username, err := sec.GenerateRandomSecret(8)
username, err := sec.GenerateRandomSecret(8, "")
if err != nil {
return nil, err
}

password, err := sec.GenerateRandomSecret(16)
password, err := sec.GenerateRandomSecret(16, "")
if err != nil {
return nil, err
}
Expand Down

0 comments on commit aa650df

Please sign in to comment.