github.com/nats-io/nats-server/v2@v2.11.0-preview.2/server/jwt.go (about)

     1  // Copyright 2018-2022 The NATS Authors
     2  // Licensed under the Apache License, Version 2.0 (the "License");
     3  // you may not use this file except in compliance with the License.
     4  // You may obtain a copy of the License at
     5  //
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package server
    15  
    16  import (
    17  	"errors"
    18  	"fmt"
    19  	"net"
    20  	"os"
    21  	"strings"
    22  	"time"
    23  
    24  	"github.com/nats-io/jwt/v2"
    25  	"github.com/nats-io/nkeys"
    26  )
    27  
    28  // All JWTs once encoded start with this
    29  const jwtPrefix = "eyJ"
    30  
    31  // ReadOperatorJWT will read a jwt file for an operator claim. This can be a decorated file.
    32  func ReadOperatorJWT(jwtfile string) (*jwt.OperatorClaims, error) {
    33  	_, claim, err := readOperatorJWT(jwtfile)
    34  	return claim, err
    35  }
    36  
    37  func readOperatorJWT(jwtfile string) (string, *jwt.OperatorClaims, error) {
    38  	contents, err := os.ReadFile(jwtfile)
    39  	if err != nil {
    40  		// Check to see if the JWT has been inlined.
    41  		if !strings.HasPrefix(jwtfile, jwtPrefix) {
    42  			return "", nil, err
    43  		}
    44  		// We may have an inline jwt here.
    45  		contents = []byte(jwtfile)
    46  	}
    47  	defer wipeSlice(contents)
    48  
    49  	theJWT, err := jwt.ParseDecoratedJWT(contents)
    50  	if err != nil {
    51  		return "", nil, err
    52  	}
    53  	opc, err := jwt.DecodeOperatorClaims(theJWT)
    54  	if err != nil {
    55  		return "", nil, err
    56  	}
    57  	return theJWT, opc, nil
    58  }
    59  
    60  // Just wipe slice with 'x', for clearing contents of nkey seed file.
    61  func wipeSlice(buf []byte) {
    62  	for i := range buf {
    63  		buf[i] = 'x'
    64  	}
    65  }
    66  
    67  // validateTrustedOperators will check that we do not have conflicts with
    68  // assigned trusted keys and trusted operators. If operators are defined we
    69  // will expand the trusted keys in options.
    70  func validateTrustedOperators(o *Options) error {
    71  	if len(o.TrustedOperators) == 0 {
    72  		return nil
    73  	}
    74  	if o.AccountResolver == nil {
    75  		return fmt.Errorf("operators require an account resolver to be configured")
    76  	}
    77  	if len(o.Accounts) > 0 {
    78  		return fmt.Errorf("operators do not allow Accounts to be configured directly")
    79  	}
    80  	if len(o.Users) > 0 || len(o.Nkeys) > 0 {
    81  		return fmt.Errorf("operators do not allow users to be configured directly")
    82  	}
    83  	if len(o.TrustedOperators) > 0 && len(o.TrustedKeys) > 0 {
    84  		return fmt.Errorf("conflicting options for 'TrustedKeys' and 'TrustedOperators'")
    85  	}
    86  	if o.SystemAccount != _EMPTY_ {
    87  		foundSys := false
    88  		foundNonEmpty := false
    89  		for _, op := range o.TrustedOperators {
    90  			if op.SystemAccount != _EMPTY_ {
    91  				foundNonEmpty = true
    92  			}
    93  			if op.SystemAccount == o.SystemAccount {
    94  				foundSys = true
    95  				break
    96  			}
    97  		}
    98  		if foundNonEmpty && !foundSys {
    99  			return fmt.Errorf("system_account in config and operator JWT must be identical")
   100  		}
   101  	} else if o.TrustedOperators[0].SystemAccount == _EMPTY_ {
   102  		// In case the system account is neither defined in config nor in the first operator.
   103  		// If it would be needed due to the nats account resolver, raise an error.
   104  		switch o.AccountResolver.(type) {
   105  		case *DirAccResolver, *CacheDirAccResolver:
   106  			return fmt.Errorf("using nats based account resolver - the system account needs to be specified in configuration or the operator jwt")
   107  		}
   108  	}
   109  
   110  	srvMajor, srvMinor, srvUpdate, _ := versionComponents(VERSION)
   111  	for _, opc := range o.TrustedOperators {
   112  		if major, minor, update, err := jwt.ParseServerVersion(opc.AssertServerVersion); err != nil {
   113  			return fmt.Errorf("operator %s expects version %s got error instead: %s",
   114  				opc.Subject, opc.AssertServerVersion, err)
   115  		} else if major > srvMajor {
   116  			return fmt.Errorf("operator %s expected major version %d > server major version %d",
   117  				opc.Subject, major, srvMajor)
   118  		} else if srvMajor > major {
   119  		} else if minor > srvMinor {
   120  			return fmt.Errorf("operator %s expected minor version %d > server minor version %d",
   121  				opc.Subject, minor, srvMinor)
   122  		} else if srvMinor > minor {
   123  		} else if update > srvUpdate {
   124  			return fmt.Errorf("operator %s expected update version %d > server update version %d",
   125  				opc.Subject, update, srvUpdate)
   126  		}
   127  	}
   128  	// If we have operators, fill in the trusted keys.
   129  	// FIXME(dlc) - We had TrustedKeys before TrustedOperators. The jwt.OperatorClaims
   130  	// has a DidSign(). Use that longer term. For now we can expand in place.
   131  	for _, opc := range o.TrustedOperators {
   132  		if o.TrustedKeys == nil {
   133  			o.TrustedKeys = make([]string, 0, 4)
   134  		}
   135  		if !opc.StrictSigningKeyUsage {
   136  			o.TrustedKeys = append(o.TrustedKeys, opc.Subject)
   137  		}
   138  		o.TrustedKeys = append(o.TrustedKeys, opc.SigningKeys...)
   139  	}
   140  	for _, key := range o.TrustedKeys {
   141  		if !nkeys.IsValidPublicOperatorKey(key) {
   142  			return fmt.Errorf("trusted Keys %q are required to be a valid public operator nkey", key)
   143  		}
   144  	}
   145  	if len(o.resolverPinnedAccounts) > 0 {
   146  		for key := range o.resolverPinnedAccounts {
   147  			if !nkeys.IsValidPublicAccountKey(key) {
   148  				return fmt.Errorf("pinned account key %q is not a valid public account nkey", key)
   149  			}
   150  		}
   151  		// ensure the system account (belonging to the operator can always connect)
   152  		if o.SystemAccount != _EMPTY_ {
   153  			o.resolverPinnedAccounts[o.SystemAccount] = struct{}{}
   154  		}
   155  	}
   156  
   157  	// If we have an auth callout defined make sure we are not in operator mode.
   158  	if o.AuthCallout != nil {
   159  		return errors.New("operators do not allow authorization callouts to be configured directly")
   160  	}
   161  
   162  	return nil
   163  }
   164  
   165  func validateSrc(claims *jwt.UserClaims, host string) bool {
   166  	if claims == nil {
   167  		return false
   168  	} else if len(claims.Src) == 0 {
   169  		return true
   170  	} else if host == "" {
   171  		return false
   172  	}
   173  	ip := net.ParseIP(host)
   174  	if ip == nil {
   175  		return false
   176  	}
   177  	for _, cidr := range claims.Src {
   178  		if _, net, err := net.ParseCIDR(cidr); err != nil {
   179  			return false // should not happen as this jwt is invalid
   180  		} else if net.Contains(ip) {
   181  			return true
   182  		}
   183  	}
   184  	return false
   185  }
   186  
   187  func validateTimes(claims *jwt.UserClaims) (bool, time.Duration) {
   188  	if claims == nil {
   189  		return false, time.Duration(0)
   190  	} else if len(claims.Times) == 0 {
   191  		return true, time.Duration(0)
   192  	}
   193  	now := time.Now()
   194  	loc := time.Local
   195  	if claims.Locale != "" {
   196  		var err error
   197  		if loc, err = time.LoadLocation(claims.Locale); err != nil {
   198  			return false, time.Duration(0) // parsing not expected to fail at this point
   199  		}
   200  		now = now.In(loc)
   201  	}
   202  	for _, timeRange := range claims.Times {
   203  		y, m, d := now.Date()
   204  		m = m - 1
   205  		d = d - 1
   206  		start, err := time.ParseInLocation("15:04:05", timeRange.Start, loc)
   207  		if err != nil {
   208  			return false, time.Duration(0) // parsing not expected to fail at this point
   209  		}
   210  		end, err := time.ParseInLocation("15:04:05", timeRange.End, loc)
   211  		if err != nil {
   212  			return false, time.Duration(0) // parsing not expected to fail at this point
   213  		}
   214  		if start.After(end) {
   215  			start = start.AddDate(y, int(m), d)
   216  			d++ // the intent is to be the next day
   217  		} else {
   218  			start = start.AddDate(y, int(m), d)
   219  		}
   220  		if start.Before(now) {
   221  			end = end.AddDate(y, int(m), d)
   222  			if end.After(now) {
   223  				return true, end.Sub(now)
   224  			}
   225  		}
   226  	}
   227  	return false, time.Duration(0)
   228  }