Merge "Remove secret generate cmd and its implementation"
This commit is contained in:
commit
3544c96249
@ -29,7 +29,6 @@ import (
|
||||
"opendev.org/airship/airshipctl/cmd/document"
|
||||
"opendev.org/airship/airshipctl/cmd/phase"
|
||||
"opendev.org/airship/airshipctl/cmd/plan"
|
||||
"opendev.org/airship/airshipctl/cmd/secret"
|
||||
cfg "opendev.org/airship/airshipctl/pkg/config"
|
||||
"opendev.org/airship/airshipctl/pkg/log"
|
||||
)
|
||||
@ -82,7 +81,6 @@ func AddDefaultAirshipCTLCommands(cmd *cobra.Command, factory cfg.Factory) *cobr
|
||||
cmd.AddCommand(completion.NewCompletionCommand())
|
||||
cmd.AddCommand(document.NewDocumentCommand(factory))
|
||||
cmd.AddCommand(config.NewConfigCommand(factory))
|
||||
cmd.AddCommand(secret.NewSecretCommand())
|
||||
cmd.AddCommand(phase.NewPhaseCommand(factory))
|
||||
cmd.AddCommand(plan.NewPlanCommand(factory))
|
||||
cmd.AddCommand(NewVersionCommand())
|
||||
|
@ -1,69 +0,0 @@
|
||||
/*
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package encryptionkey
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/errors"
|
||||
"opendev.org/airship/airshipctl/pkg/secret/generate"
|
||||
)
|
||||
|
||||
const (
|
||||
cmdLong = `
|
||||
Generates a secure encryption key or passphrase.
|
||||
|
||||
If regex arguments are passed the encryption key created would match the regular expression passed.
|
||||
`
|
||||
|
||||
cmdExample = `
|
||||
Generates a secure encryption key or passphrase.
|
||||
# airshipctl secret generate encryptionkey
|
||||
|
||||
Generates a secure encryption key or passphrase matching the regular expression
|
||||
# airshipctl secret generate encryptionkey --regex Xy[a-c][0-9]!a*
|
||||
`
|
||||
)
|
||||
|
||||
// NewGenerateEncryptionKeyCommand creates a new command for generating secret information
|
||||
func NewGenerateEncryptionKeyCommand() *cobra.Command {
|
||||
var regex string
|
||||
var limit int
|
||||
|
||||
encryptionKeyCmd := &cobra.Command{
|
||||
Use: "encryptionkey",
|
||||
Short: "Airshipctl command to generate a secure encryption key or passphrase",
|
||||
Long: cmdLong[1:],
|
||||
Example: cmdExample,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if cmd.Flags().Changed("limit") && !cmd.Flags().Changed("regex") {
|
||||
return fmt.Errorf("required Regex flag with limit option")
|
||||
}
|
||||
if cmd.Flags().Changed("regex") && cmd.Flags().Changed("limit") {
|
||||
return errors.ErrNotImplemented{What: "Regex support not implemented yet!"}
|
||||
}
|
||||
engine := generate.NewEncryptionKeyEngine(nil)
|
||||
encryptionKey := engine.GenerateEncryptionKey()
|
||||
fmt.Fprintln(cmd.OutOrStdout(), encryptionKey)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
encryptionKeyCmd.Flags().StringVar(®ex, "regex", "", "regular expression string")
|
||||
encryptionKeyCmd.Flags().IntVar(&limit, "limit", 5, "limit number of characters for + or * regex")
|
||||
return encryptionKeyCmd
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
/*
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package encryptionkey_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"opendev.org/airship/airshipctl/cmd/secret/generate/encryptionkey"
|
||||
"opendev.org/airship/airshipctl/testutil"
|
||||
)
|
||||
|
||||
func TestGenerateEncryptionKey(t *testing.T) {
|
||||
cmdTests := []*testutil.CmdTest{
|
||||
{
|
||||
Name: "generate-encryptionkey-cmd-with-help",
|
||||
CmdLine: "--help",
|
||||
Cmd: encryptionkey.NewGenerateEncryptionKeyCommand(),
|
||||
},
|
||||
{
|
||||
Name: "generate-encryptionkey-cmd-error",
|
||||
CmdLine: "--limit 10",
|
||||
Error: fmt.Errorf("required Regex flag with limit option"),
|
||||
Cmd: encryptionkey.NewGenerateEncryptionKeyCommand(),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range cmdTests {
|
||||
testutil.RunTest(t, tt)
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
Error: required Regex flag with limit option
|
||||
Usage:
|
||||
encryptionkey [flags]
|
||||
|
||||
Examples:
|
||||
|
||||
Generates a secure encryption key or passphrase.
|
||||
# airshipctl secret generate encryptionkey
|
||||
|
||||
Generates a secure encryption key or passphrase matching the regular expression
|
||||
# airshipctl secret generate encryptionkey --regex Xy[a-c][0-9]!a*
|
||||
|
||||
|
||||
Flags:
|
||||
-h, --help help for encryptionkey
|
||||
--limit int limit number of characters for + or * regex (default 5)
|
||||
--regex string regular expression string
|
||||
|
@ -1,20 +0,0 @@
|
||||
Generates a secure encryption key or passphrase.
|
||||
|
||||
If regex arguments are passed the encryption key created would match the regular expression passed.
|
||||
|
||||
Usage:
|
||||
encryptionkey [flags]
|
||||
|
||||
Examples:
|
||||
|
||||
Generates a secure encryption key or passphrase.
|
||||
# airshipctl secret generate encryptionkey
|
||||
|
||||
Generates a secure encryption key or passphrase matching the regular expression
|
||||
# airshipctl secret generate encryptionkey --regex Xy[a-c][0-9]!a*
|
||||
|
||||
|
||||
Flags:
|
||||
-h, --help help for encryptionkey
|
||||
--limit int limit number of characters for + or * regex (default 5)
|
||||
--regex string regular expression string
|
@ -1,34 +0,0 @@
|
||||
/*
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package generate
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"opendev.org/airship/airshipctl/cmd/secret/generate/encryptionkey"
|
||||
)
|
||||
|
||||
// NewGenerateCommand creates a new command for generating secret information
|
||||
func NewGenerateCommand() *cobra.Command {
|
||||
generateRootCmd := &cobra.Command{
|
||||
Use: "generate",
|
||||
// TODO(howell): Make this more expressive
|
||||
Short: "Airshipctl command to generate secrets",
|
||||
}
|
||||
|
||||
generateRootCmd.AddCommand(encryptionkey.NewGenerateEncryptionKeyCommand())
|
||||
|
||||
return generateRootCmd
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
/*
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package secret
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"opendev.org/airship/airshipctl/cmd/secret/generate"
|
||||
)
|
||||
|
||||
// NewSecretCommand creates a new command for managing airshipctl secrets
|
||||
func NewSecretCommand() *cobra.Command {
|
||||
secretRootCmd := &cobra.Command{
|
||||
Use: "secret",
|
||||
// TODO(howell): Make this more expressive
|
||||
Short: "Airshipctl command to manage secrets",
|
||||
Long: "Commands and sub-commnads defined can be used to manage secrets.",
|
||||
}
|
||||
|
||||
secretRootCmd.AddCommand(generate.NewGenerateCommand())
|
||||
|
||||
return secretRootCmd
|
||||
}
|
@ -14,7 +14,6 @@ Available Commands:
|
||||
help Help about any command
|
||||
phase Airshipctl command to manage phases
|
||||
plan Airshipctl command to manage plans
|
||||
secret Airshipctl command to manage secrets
|
||||
version Airshipctl command to display the current version number
|
||||
|
||||
Flags:
|
||||
|
@ -33,6 +33,5 @@ SEE ALSO
|
||||
* :ref:`airshipctl document <airshipctl_document>` - Airshipctl command to manage site manifest documents
|
||||
* :ref:`airshipctl phase <airshipctl_phase>` - Airshipctl command to manage phases
|
||||
* :ref:`airshipctl plan <airshipctl_plan>` - Airshipctl command to manage plans
|
||||
* :ref:`airshipctl secret <airshipctl_secret>` - Airshipctl command to manage secrets
|
||||
* :ref:`airshipctl version <airshipctl_version>` - Airshipctl command to display the current version number
|
||||
|
||||
|
@ -14,5 +14,4 @@ Commands
|
||||
help/index
|
||||
phase/index
|
||||
plan/index
|
||||
secret/index
|
||||
version/index
|
||||
|
@ -1,112 +0,0 @@
|
||||
/*
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package generate
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultLength = 24
|
||||
|
||||
asciiLowers = "abcdefghijklmnopqrstuvwxyz"
|
||||
asciiUppers = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
asciiNumbers = "0123456789"
|
||||
asciiSymbols = "@#&-+=?"
|
||||
)
|
||||
|
||||
var (
|
||||
// pool is the complete collection of characters that can be used for a
|
||||
// passphrase
|
||||
pool []byte
|
||||
)
|
||||
|
||||
func init() {
|
||||
pool = append(pool, []byte(asciiLowers)...)
|
||||
pool = append(pool, []byte(asciiUppers)...)
|
||||
pool = append(pool, []byte(asciiNumbers)...)
|
||||
pool = append(pool, []byte(asciiSymbols)...)
|
||||
}
|
||||
|
||||
// EncryptionKeyEngine is used to generate secure random passphrases
|
||||
type EncryptionKeyEngine struct {
|
||||
rng *rand.Rand
|
||||
pool []byte
|
||||
}
|
||||
|
||||
// NewEncryptionKeyEngine creates an PassphraseEngine using src. If src is nil,
|
||||
// the returned PassphraseEngine will use the default Source
|
||||
func NewEncryptionKeyEngine(src rand.Source) *EncryptionKeyEngine {
|
||||
if src == nil {
|
||||
src = &Source{}
|
||||
}
|
||||
return &EncryptionKeyEngine{
|
||||
rng: rand.New(src), //nolint:gosec
|
||||
pool: pool,
|
||||
}
|
||||
}
|
||||
|
||||
// GenerateEncryptionKey returns a secure random string of length 24,
|
||||
// containing at least one of each from the following sets:
|
||||
// [0-9]
|
||||
// [a-z]
|
||||
// [A-Z]
|
||||
// [@#&-+=?]
|
||||
func (e *EncryptionKeyEngine) GenerateEncryptionKey() string {
|
||||
return e.GenerateEncryptionKeyN(defaultLength)
|
||||
}
|
||||
|
||||
// GenerateEncryptionKeyN returns a secure random string containing at least
|
||||
// one of each from the following sets. Its length will be max(length, 24)
|
||||
// [0-9]
|
||||
// [a-z]
|
||||
// [A-Z]
|
||||
// [@#&-+=?]
|
||||
func (e *EncryptionKeyEngine) GenerateEncryptionKeyN(length int) string {
|
||||
if length < defaultLength {
|
||||
length = defaultLength
|
||||
}
|
||||
var encryptionkey string
|
||||
for !e.isValidEncryptionKey(encryptionkey) {
|
||||
var sb strings.Builder
|
||||
for i := 0; i < length; i++ {
|
||||
randIndex := e.rng.Intn(len(e.pool))
|
||||
randChar := e.pool[randIndex]
|
||||
sb.WriteString(string(randChar))
|
||||
}
|
||||
encryptionkey = sb.String()
|
||||
}
|
||||
return encryptionkey
|
||||
}
|
||||
|
||||
func (e *EncryptionKeyEngine) isValidEncryptionKey(encryptionkey string) bool {
|
||||
if len(encryptionkey) < defaultLength {
|
||||
return false
|
||||
}
|
||||
|
||||
charSets := []string{
|
||||
asciiLowers,
|
||||
asciiUppers,
|
||||
asciiNumbers,
|
||||
asciiSymbols,
|
||||
}
|
||||
for _, charSet := range charSets {
|
||||
if !strings.ContainsAny(encryptionkey, charSet) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
@ -1,115 +0,0 @@
|
||||
/*
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package generate_test
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/secret/generate"
|
||||
)
|
||||
|
||||
const (
|
||||
asciiLowers = "abcdefghijklmnopqrstuvwxyz"
|
||||
asciiUppers = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
asciiNumbers = "0123456789"
|
||||
asciiSymbols = "@#&-+=?"
|
||||
|
||||
defaultLength = 24
|
||||
)
|
||||
|
||||
func TestDeterministicGenerateValidEncryptionKey(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testSource := rand.NewSource(42)
|
||||
engine := generate.NewEncryptionKeyEngine(testSource)
|
||||
|
||||
// pre-calculated for rand.NewSource(42)
|
||||
expectedEncryptionKey := []string{
|
||||
"erx&97vfqd7LN3HJ?t@oPhds",
|
||||
"##Xeuvf5Njy@hNWSaRoleFkf",
|
||||
"jB=kirg7acIt-=fx1Fb-tZ+7",
|
||||
"eOS#W8yoAljSThPL2oT&aUZu",
|
||||
"vlaQqKr-jXSCJfXYnvGik3b1",
|
||||
"rBKtHZkOmFUM75?c2UWiZjdh",
|
||||
"9g?QV?w6BCWN2EKAc+dZ-Jun",
|
||||
"X@IIyqAg7Mz@Wm8eRE6KMEf3",
|
||||
"7JpQkLd3ufhj4bLB8S=ipjNP",
|
||||
"XC?bDaHTa3mrBYLMG@#B=Q0B",
|
||||
}
|
||||
|
||||
for i, expected := range expectedEncryptionKey {
|
||||
actual := engine.GenerateEncryptionKey()
|
||||
assert.Equal(expected, actual, "Test #%d failed", i)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNondeterministicGenerateValidEncryptionKey(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
// Due to the nondeterminism of random number generators, this
|
||||
// functionality is impossible to fully test. Let's just generate
|
||||
// enough passphrases that we can be confident in the randomness.
|
||||
// Fortunately, Go is pretty fast, so we can do upward of 10,000 of
|
||||
// these without slowing down the test too much
|
||||
charSets := []string{
|
||||
asciiLowers,
|
||||
asciiUppers,
|
||||
asciiNumbers,
|
||||
asciiSymbols,
|
||||
}
|
||||
|
||||
engine := generate.NewEncryptionKeyEngine(nil)
|
||||
for i := 0; i < 10000; i++ {
|
||||
passphrase := engine.GenerateEncryptionKey()
|
||||
assert.Truef(len(passphrase) >= defaultLength,
|
||||
"%s does not meet the length requirement", passphrase)
|
||||
|
||||
for _, charSet := range charSets {
|
||||
assert.Truef(strings.ContainsAny(passphrase, charSet),
|
||||
"%s does not contain any characters from [%s]",
|
||||
passphrase, charSet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateValidEncryptionKeyN(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
testSource := rand.NewSource(42)
|
||||
engine := generate.NewEncryptionKeyEngine(testSource)
|
||||
tests := []struct {
|
||||
inputLegth int
|
||||
expectedLength int
|
||||
}{
|
||||
{
|
||||
inputLegth: 10,
|
||||
expectedLength: defaultLength,
|
||||
},
|
||||
{
|
||||
inputLegth: -5,
|
||||
expectedLength: defaultLength,
|
||||
},
|
||||
{
|
||||
inputLegth: 30,
|
||||
expectedLength: 30,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
passphrase := engine.GenerateEncryptionKeyN(tt.inputLegth)
|
||||
assert.Len(passphrase, tt.expectedLength)
|
||||
}
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
/*
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package generate
|
||||
|
||||
import (
|
||||
crypto "crypto/rand"
|
||||
"encoding/binary"
|
||||
"math/rand"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/log"
|
||||
)
|
||||
|
||||
// Source implements rand.Source
|
||||
type Source struct{}
|
||||
|
||||
var _ rand.Source = &Source{}
|
||||
|
||||
// Uint64 returns a secure random uint64 in the range [0, 1<<64]. It will fail
|
||||
// if an error is returned from the system's secure random number generator
|
||||
func (s *Source) Uint64() uint64 {
|
||||
var value uint64
|
||||
err := binary.Read(crypto.Reader, binary.BigEndian, &value)
|
||||
if err != nil {
|
||||
log.Fatalf("could not generate a random number: %s", err.Error())
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// Int63 returns a secure random int64 in the range [0, 1<<63]
|
||||
func (s *Source) Int63() int64 {
|
||||
return int64(s.Uint64() & ^(uint64(1 << 63)))
|
||||
}
|
||||
|
||||
// Seed does nothing, since Source will use the crypto library
|
||||
func (s *Source) Seed(_ int64) {}
|
Loading…
Reference in New Issue
Block a user