get.pme.sh/pnats@v0.0.0-20240304004023-26bb5a137ed0/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 }