Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[added] operator edit option to require signing keys in operator jwt #352

Merged
merged 2 commits into from
Jan 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion cmd/addaccount.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,9 @@ func (p *AddAccountParams) validSigners(ctx ActionCtx) ([]string, error) {
return nil, err
}
var signers []string
signers = append(signers, oc.Subject)
if !oc.StrictSigningKeyUsage {
signers = append(signers, oc.Subject)
}
signers = append(signers, oc.SigningKeys...)
if ctx.StoreCtx().Store.IsManaged() && p.akp != nil {
pk, err := p.akp.PublicKey()
Expand Down
36 changes: 31 additions & 5 deletions cmd/addoperator.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ func createAddOperatorCmd() *cobra.Command {
}
cmd.Flags().StringVarP(&params.name, "name", "n", "", "operator name")
cmd.Flags().StringVarP(&params.jwtPath, "url", "u", "", "import from a jwt server url, file, or well known operator")
cmd.Flags().BoolVarP(&params.sysAcc, "sys", "s", false, "generate system account with the operator")
cmd.Flags().BoolVarP(&params.genSk, "generate-signing-key", "", false, "generate a signing key with the operator")
cmd.Flags().BoolVarP(&params.sysAcc, "sys", "s", false, "generate system account with the operator (if specified will be signed with signing key)")
cmd.Flags().BoolVarP(&params.force, "force", "", false, "on import, overwrite existing when already present")
params.TimeParams.BindFlags(cmd)

Expand All @@ -69,6 +70,7 @@ type AddOperatorParams struct {
generate bool
sysAcc bool
force bool
genSk bool
keyPath string
}

Expand Down Expand Up @@ -122,6 +124,9 @@ func (p *AddOperatorParams) PreInteractive(ctx ActionCtx) error {
if p.sysAcc, err = cli.Confirm("Generate system account?", true); err != nil {
return err
}
if p.genSk, err = cli.Confirm("Generate signing key?", true); err != nil {
return err
}
}

return nil
Expand Down Expand Up @@ -252,6 +257,10 @@ func (p *AddOperatorParams) Validate(ctx ActionCtx) error {
if p.keyPath, err = ctx.StoreCtx().KeyStore.Store(p.signerKP); err != nil {
return err
}
} else {
if p.genSk {
return fmt.Errorf("signing key can not be added when importing the operator")
}
}

if p.keyPath != "" {
Expand All @@ -272,7 +281,7 @@ func (p *AddOperatorParams) Validate(ctx ActionCtx) error {
return nil
}

func (p *AddOperatorParams) Run(_ ActionCtx) (store.Status, error) {
func (p *AddOperatorParams) Run(ctx ActionCtx) (store.Status, error) {
r := store.NewDetailedReport(false)
operator := &store.NamedKey{Name: p.name, KP: p.signerKP}
s, err := GetConfig().LoadStore(p.name)
Expand All @@ -288,6 +297,7 @@ func (p *AddOperatorParams) Run(_ ActionCtx) (store.Status, error) {

var sAcc *keys
var sUsr *keys
var skPub string

if p.token == "" {
ctx, err := s.GetContext()
Expand All @@ -300,7 +310,6 @@ func (p *AddOperatorParams) Run(_ ActionCtx) (store.Status, error) {
return nil, err
}
}

oc, err := ctx.Store.ReadOperatorClaim()
if err != nil {
return nil, err
Expand All @@ -317,9 +326,23 @@ func (p *AddOperatorParams) Run(_ ActionCtx) (store.Status, error) {
return nil, err
}
}

sysAccSigner := p.signerKP
if p.genSk {
sysAccSigner, err = nkeys.CreateOperator()
if err != nil {
return nil, err
}
if _, err := ctx.KeyStore.Store(sysAccSigner); err != nil {
return nil, err
}
skPub, err = sysAccSigner.PublicKey()
if err != nil {
return nil, err
}
oc.SigningKeys.Add(skPub)
}
if p.sysAcc {
if sAcc, sUsr, err = createSystemAccount(ctx, p.signerKP); err != nil {
if sAcc, sUsr, err = createSystemAccount(ctx, sysAccSigner); err != nil {
return nil, err
}
oc.SystemAccount = sAcc.PubKey
Expand Down Expand Up @@ -375,6 +398,9 @@ func (p *AddOperatorParams) Run(_ ActionCtx) (store.Status, error) {
}
r.AddOK("%s operator %q", verb, p.name)
if sAcc != nil && sUsr != nil {
if skPub != "" {
r.AddOK("created operator signing key: %s", skPub)
}
r.AddOK("created system_account: name:SYS id:%s", sAcc.PubKey)
r.AddOK("created system account user: name:sys id:%s", sUsr.PubKey)
r.AddOK("system account user creds file stored in %#q", AbbrevHomePaths(sUsr.CredsPath))
Expand Down
25 changes: 20 additions & 5 deletions cmd/addoperator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func Test_AddOperator(t *testing.T) {
t.Fatal(err)
}

_, _, err = ExecuteCmd(createAddOperatorCmd(), "--name", "O", "--sys")
_, _, err = ExecuteCmd(createAddOperatorCmd(), "--name", "O", "--sys", "--generate-signing-key")
require.NoError(t, err)

require.FileExists(t, filepath.Join(ts.Dir, "store", "O", ".nsc"))
Expand Down Expand Up @@ -97,7 +97,7 @@ func TestAddOperatorInteractive(t *testing.T) {
ts := NewEmptyStore(t)
defer ts.Done(t)

_, _, err := ExecuteInteractiveCmd(createAddOperatorCmd(), []interface{}{false, "O", "2019-12-01", "2029-12-01", true, true})
_, _, err := ExecuteInteractiveCmd(createAddOperatorCmd(), []interface{}{false, "O", "2019-12-01", "2029-12-01", true, true, true})
require.NoError(t, err)
d, err := Read(filepath.Join(ts.Dir, "store", "O", "O.jwt"))
require.NoError(t, err)
Expand All @@ -108,15 +108,30 @@ func TestAddOperatorInteractive(t *testing.T) {
require.Equal(t, 2019, start.Year())
require.Equal(t, time.Month(12), start.Month())
require.Equal(t, 1, start.Day())
require.Len(t, oc.SigningKeys, 1)

expiry := time.Unix(oc.Expires, 0).UTC()
require.Equal(t, 2029, expiry.Year())
require.Equal(t, time.Month(12), expiry.Month())
require.Equal(t, 1, expiry.Day())
require.NotEmpty(t, oc.SystemAccount)

require.FileExists(t, filepath.Join(ts.Dir, "store", "O", "accounts", "SYS", "SYS.jwt"))
require.FileExists(t, filepath.Join(ts.Dir, "store", "O", "accounts", "SYS", "users", "sys.jwt"))
sys := filepath.Join(ts.Dir, "store", "O", "accounts", "SYS", "SYS.jwt")
require.FileExists(t, sys)
sysJWT, err := Read(sys)
require.NoError(t, err)
sysClaim, err := jwt.DecodeAccountClaims(string(sysJWT))
require.NoError(t, err)
require.Equal(t, sysClaim.Issuer, oc.SigningKeys[0])

usr := filepath.Join(ts.Dir, "store", "O", "accounts", "SYS", "users", "sys.jwt")
require.FileExists(t, usr)
usrJWT, err := Read(usr)
require.NoError(t, err)
usrClaim, err := jwt.DecodeUserClaims(string(usrJWT))
require.NoError(t, err)
_, ok := sysClaim.SigningKeys[usrClaim.Issuer]
require.True(t, ok)
}

func TestImportOperatorInteractive(t *testing.T) {
Expand Down Expand Up @@ -196,7 +211,7 @@ func Test_AddOperatorWithKeyInteractive(t *testing.T) {
cmd := createAddOperatorCmd()
HoistRootFlags(cmd)

args := []interface{}{false, "T", "0", "0", false, false, string(seed)}
args := []interface{}{false, "T", "0", "0", false, false, false, string(seed)}
_, _, err := ExecuteInteractiveCmd(cmd, args)
require.NoError(t, err)

Expand Down
6 changes: 5 additions & 1 deletion cmd/adduser.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,12 +179,16 @@ func (p *AddUserParams) Load(_ ActionCtx) error {
}

func validUserSigners(ctx ActionCtx, accName string) ([]string, error) {
opc, err := ctx.StoreCtx().Store.ReadOperatorClaim()
if err != nil {
return nil, err
}
ac, err := ctx.StoreCtx().Store.ReadAccountClaim(accName)
if err != nil {
return nil, err
}
var signers []string
if ctx.StoreCtx().KeyStore.HasPrivateKey(ac.Subject) {
if !opc.StrictSigningKeyUsage && ctx.StoreCtx().KeyStore.HasPrivateKey(ac.Subject) {
signers = append(signers, ac.Subject)
}
for signingKey := range ac.SigningKeys {
Expand Down
4 changes: 3 additions & 1 deletion cmd/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,9 @@ func GetOperatorSigners(ctx ActionCtx) ([]string, error) {
return nil, err
}
var signers []string
signers = append(signers, oc.Subject)
if !oc.StrictSigningKeyUsage {
signers = append(signers, oc.Subject)
}
signers = append(signers, oc.SigningKeys...)
return signers, nil
}
Expand Down
1 change: 1 addition & 0 deletions cmd/describer.go
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,7 @@ func (o *OperatorDescriber) Describe() string {
}
table.AddRow("System Account", o.SystemAccount+decoration)
}
table.AddRow("Require Signing Keys", o.StrictSigningKeyUsage)

if len(o.SigningKeys) > 0 {
table.AddSeparator()
Expand Down
31 changes: 30 additions & 1 deletion cmd/editoperator.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ func createEditOperatorCmd() *cobra.Command {
cmd.Flags().StringVarP(&params.sysAcc, "system-account", "", "", "set system account by account by public key or name")
cmd.Flags().StringSliceVarP(&params.serviceURLs, "service-url", "n", nil, "add an operator service url for nsc where clients can access the NATS service (only nats/tls urls supported)")
cmd.Flags().StringSliceVarP(&params.rmServiceURLs, "rm-service-url", "", nil, "remove an operator service url for nsc where clients can access the NATS service (only nats/tls urls supported)")
cmd.Flags().BoolVarP(&params.reqSk, "require-signing-keys", "", false, "require accounts/user to be signed with a signing key")
params.TimeParams.BindFlags(cmd)

return cmd
Expand All @@ -65,12 +66,13 @@ type EditOperatorParams struct {
rmServiceURLs []string
signingKeys SigningKeysParams
rmSigningKeys []string
reqSk bool
}

func (p *EditOperatorParams) SetDefaults(ctx ActionCtx) error {
p.SignerParams.SetDefaults(nkeys.PrefixByteOperator, false, ctx)

if !InteractiveFlag && ctx.NothingToDo("sk", "rm-sk", "start", "expiry", "tag", "rm-tag", "account-jwt-server-url", "service-url", "rm-service-url", "system-account") {
if !InteractiveFlag && ctx.NothingToDo("sk", "rm-sk", "start", "expiry", "tag", "rm-tag", "account-jwt-server-url", "service-url", "rm-service-url", "system-account", "require-signing-keys") {
ctx.CurrentCmd().SilenceUsage = false
return fmt.Errorf("specify an edit option")
}
Expand Down Expand Up @@ -201,6 +203,31 @@ func (p *EditOperatorParams) Validate(ctx ActionCtx) error {
}
}
}
if p.reqSk {
accounts, err := ctx.StoreCtx().Store.ListSubContainers(store.Accounts)
if err != nil {
return err
}
for _, accName := range accounts {
ac, err := ctx.StoreCtx().Store.ReadAccountClaim(accName)
if err != nil {
return err
}
if ac.Issuer == p.claim.Subject {
return fmt.Errorf("account %q needs to be issued with a signing key first", accName)
}
usrs, _ := ctx.StoreCtx().Store.ListEntries(store.Accounts, accName, store.Users)
for _, usrName := range usrs {
uc, err := ctx.StoreCtx().Store.ReadUserClaim(accName, usrName)
if err != nil {
return err
}
if uc.Issuer == ac.Subject {
return fmt.Errorf("user %q in account %q needs to be issued with a signing key first", usrName, accName)
}
}
}
}
if err = p.signingKeys.Valid(); err != nil {
return err
}
Expand Down Expand Up @@ -230,6 +257,8 @@ func (p *EditOperatorParams) Run(ctx ActionCtx) (store.Status, error) {
r.AddOK("removed signing key %q", k)
}

p.claim.StrictSigningKeyUsage = p.reqSk
r.AddOK("strict signing key usage set to: %t", p.reqSk)
flags := ctx.CurrentCmd().Flags()
p.claim.AccountServerURL = p.asu
if flags.Changed("account-jwt-server-url") {
Expand Down
Loading