github.com/nats-io/nsc@v0.0.0-20221206222106-35db9400b257/cmd/natsresolverconfigbuilder.go (about)

     1  /*
     2   * Copyright 2018-2020 The NATS Authors
     3   * Licensed under the Apache License, Version 2.0 (the "License");
     4   * you may not use this file except in compliance with the License.
     5   * You may obtain a copy of the License at
     6   *
     7   * http://www.apache.org/licenses/LICENSE-2.0
     8   *
     9   * Unless required by applicable law or agreed to in writing, software
    10   * distributed under the License is distributed on an "AS IS" BASIS,
    11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12   * See the License for the specific language governing permissions and
    13   * limitations under the License.
    14   */
    15  
    16  package cmd
    17  
    18  import (
    19  	"errors"
    20  	"fmt"
    21  
    22  	"github.com/nats-io/jwt/v2"
    23  )
    24  
    25  type NatsResolverConfigBuilder struct {
    26  	operator       string
    27  	operatorName   string
    28  	sysAccountSubj string
    29  	sysAccount     string
    30  	sysAccountName string
    31  	cache          bool
    32  }
    33  
    34  func NewNatsResolverConfigBuilder(cache bool) *NatsResolverConfigBuilder {
    35  	cb := NatsResolverConfigBuilder{cache: cache}
    36  	return &cb
    37  }
    38  
    39  func (cb *NatsResolverConfigBuilder) Add(rawClaim []byte) error {
    40  	token := string(rawClaim)
    41  	gc, err := jwt.DecodeGeneric(token)
    42  	if err != nil {
    43  		return err
    44  	}
    45  	switch gc.ClaimType() {
    46  	case jwt.OperatorClaim:
    47  		if claim, err := jwt.DecodeOperatorClaims(token); err != nil {
    48  			return err
    49  		} else {
    50  			cb.operator = token
    51  			cb.operatorName = claim.Name
    52  		}
    53  	case jwt.AccountClaim:
    54  		if claim, err := jwt.DecodeAccountClaims(token); err != nil {
    55  			return err
    56  		} else if claim.Subject == cb.sysAccountSubj {
    57  			cb.sysAccount = token
    58  			cb.sysAccountName = claim.Name
    59  		}
    60  	}
    61  	return nil
    62  }
    63  
    64  func (cb *NatsResolverConfigBuilder) SetOutputDir(fp string) error {
    65  	return errors.New("nats-resolver configurations don't support directory output")
    66  }
    67  
    68  func (cb *NatsResolverConfigBuilder) SetSystemAccount(id string) error {
    69  	cb.sysAccountSubj = id
    70  	return nil
    71  }
    72  
    73  const tmplPreLoad = `
    74  # Preload the nats based resolver with the system account jwt.
    75  # This is not necessary but avoids a bootstrapping system account. 
    76  # This only applies to the system account. Therefore other account jwt are not included here.
    77  # To populate the resolver:
    78  # 1) make sure that your operator has the account server URL pointing at your nats servers.
    79  #    The url must start with: "nats://" 
    80  #    nsc edit operator --account-jwt-server-url nats://localhost:4222
    81  # 2) push your accounts using: nsc push --all
    82  #    The argument to push -u is optional if your account server url is set as described.
    83  # 3) to prune accounts use: nsc push --prune 
    84  #    In order to enable prune you must set above allow_delete to true
    85  # Later changes to the system account take precedence over the system account jwt listed here.
    86  resolver_preload: {
    87  	%s: %s,
    88  }
    89  `
    90  
    91  const tmplFull = `# Operator named %s
    92  operator: %s
    93  # System Account named %s
    94  system_account: %s
    95  
    96  # configuration of the nats based resolver
    97  resolver {
    98      type: full
    99      # Directory in which the account jwt will be stored
   100      dir: './jwt'
   101      # In order to support jwt deletion, set to true
   102      # If the resolver type is full delete will rename the jwt.
   103      # This is to allow manual restoration in case of inadvertent deletion.
   104      # To restore a jwt, remove the added suffix .delete and restart or send a reload signal.
   105      # To free up storage you must manually delete files with the suffix .delete.
   106      allow_delete: false
   107      # Interval at which a nats-server with a nats based account resolver will compare
   108      # it's state with one random nats based account resolver in the cluster and if needed, 
   109      # exchange jwt and converge on the same set of jwt.
   110      interval: "2m"
   111      # Timeout for lookup requests in case an account does not exist locally.
   112      timeout: "1.9s"
   113  }
   114  
   115  %s
   116  `
   117  
   118  const tmplCache = `# Operator named %s
   119  operator: %s
   120  # System Account named %s
   121  system_account: %s
   122  
   123  # configuration of the nats based cache resolver
   124  resolver {
   125      type: cache
   126      # Directory in which the account jwt will be stored
   127      dir: './jwt'
   128      # ttl after which the file will be removed from the cache. Set to a large value in order to disable.
   129      ttl: "1h"
   130      # Timeout for lookup requests in case an account does not exist locally.
   131      timeout: "1.9s"
   132  }
   133  
   134  %s
   135  `
   136  
   137  func (cb *NatsResolverConfigBuilder) Generate() ([]byte, error) {
   138  	if cb.operator == "" {
   139  		return nil, errors.New("operator is not set")
   140  	}
   141  	if cb.sysAccountSubj == "" || cb.sysAccount == "" {
   142  		return nil, errors.New("system account is not set")
   143  	}
   144  	tmpl := tmplFull
   145  	if cb.cache {
   146  		tmpl = tmplCache
   147  	}
   148  	return []byte(fmt.Sprintf(tmpl, cb.operatorName, cb.operator, cb.sysAccountName, cb.sysAccountSubj,
   149  		fmt.Sprintf(tmplPreLoad, cb.sysAccountSubj, cb.sysAccount))), nil
   150  }