mirror of https://github.com/k3s-io/k3s
193 lines
4.1 KiB
Go
193 lines
4.1 KiB
Go
// Copyright 2018 The etcd Authors
|
|
//
|
|
// 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
|
|
//
|
|
// http://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 auth
|
|
|
|
import (
|
|
"crypto/ecdsa"
|
|
"crypto/rsa"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"time"
|
|
|
|
jwt "github.com/dgrijalva/jwt-go"
|
|
)
|
|
|
|
const (
|
|
optSignMethod = "sign-method"
|
|
optPublicKey = "pub-key"
|
|
optPrivateKey = "priv-key"
|
|
optTTL = "ttl"
|
|
)
|
|
|
|
var knownOptions = map[string]bool{
|
|
optSignMethod: true,
|
|
optPublicKey: true,
|
|
optPrivateKey: true,
|
|
optTTL: true,
|
|
}
|
|
|
|
var (
|
|
// DefaultTTL will be used when a 'ttl' is not specified
|
|
DefaultTTL = 5 * time.Minute
|
|
)
|
|
|
|
type jwtOptions struct {
|
|
SignMethod jwt.SigningMethod
|
|
PublicKey []byte
|
|
PrivateKey []byte
|
|
TTL time.Duration
|
|
}
|
|
|
|
// ParseWithDefaults will load options from the specified map or set defaults where appropriate
|
|
func (opts *jwtOptions) ParseWithDefaults(optMap map[string]string) error {
|
|
if opts.TTL == 0 && optMap[optTTL] == "" {
|
|
opts.TTL = DefaultTTL
|
|
}
|
|
|
|
return opts.Parse(optMap)
|
|
}
|
|
|
|
// Parse will load options from the specified map
|
|
func (opts *jwtOptions) Parse(optMap map[string]string) error {
|
|
var err error
|
|
if ttl := optMap[optTTL]; ttl != "" {
|
|
opts.TTL, err = time.ParseDuration(ttl)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if file := optMap[optPublicKey]; file != "" {
|
|
opts.PublicKey, err = ioutil.ReadFile(file)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if file := optMap[optPrivateKey]; file != "" {
|
|
opts.PrivateKey, err = ioutil.ReadFile(file)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// signing method is a required field
|
|
method := optMap[optSignMethod]
|
|
opts.SignMethod = jwt.GetSigningMethod(method)
|
|
if opts.SignMethod == nil {
|
|
return ErrInvalidAuthMethod
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Key will parse and return the appropriately typed key for the selected signature method
|
|
func (opts *jwtOptions) Key() (interface{}, error) {
|
|
switch opts.SignMethod.(type) {
|
|
case *jwt.SigningMethodRSA, *jwt.SigningMethodRSAPSS:
|
|
return opts.rsaKey()
|
|
case *jwt.SigningMethodECDSA:
|
|
return opts.ecKey()
|
|
case *jwt.SigningMethodHMAC:
|
|
return opts.hmacKey()
|
|
default:
|
|
return nil, fmt.Errorf("unsupported signing method: %T", opts.SignMethod)
|
|
}
|
|
}
|
|
|
|
func (opts *jwtOptions) hmacKey() (interface{}, error) {
|
|
if len(opts.PrivateKey) == 0 {
|
|
return nil, ErrMissingKey
|
|
}
|
|
return opts.PrivateKey, nil
|
|
}
|
|
|
|
func (opts *jwtOptions) rsaKey() (interface{}, error) {
|
|
var (
|
|
priv *rsa.PrivateKey
|
|
pub *rsa.PublicKey
|
|
err error
|
|
)
|
|
|
|
if len(opts.PrivateKey) > 0 {
|
|
priv, err = jwt.ParseRSAPrivateKeyFromPEM(opts.PrivateKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
if len(opts.PublicKey) > 0 {
|
|
pub, err = jwt.ParseRSAPublicKeyFromPEM(opts.PublicKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
if priv == nil {
|
|
if pub == nil {
|
|
// Neither key given
|
|
return nil, ErrMissingKey
|
|
}
|
|
// Public key only, can verify tokens
|
|
return pub, nil
|
|
}
|
|
|
|
// both keys provided, make sure they match
|
|
if pub != nil && pub.E != priv.E && pub.N.Cmp(priv.N) != 0 {
|
|
return nil, ErrKeyMismatch
|
|
}
|
|
|
|
return priv, nil
|
|
}
|
|
|
|
func (opts *jwtOptions) ecKey() (interface{}, error) {
|
|
var (
|
|
priv *ecdsa.PrivateKey
|
|
pub *ecdsa.PublicKey
|
|
err error
|
|
)
|
|
|
|
if len(opts.PrivateKey) > 0 {
|
|
priv, err = jwt.ParseECPrivateKeyFromPEM(opts.PrivateKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
if len(opts.PublicKey) > 0 {
|
|
pub, err = jwt.ParseECPublicKeyFromPEM(opts.PublicKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
if priv == nil {
|
|
if pub == nil {
|
|
// Neither key given
|
|
return nil, ErrMissingKey
|
|
}
|
|
// Public key only, can verify tokens
|
|
return pub, nil
|
|
}
|
|
|
|
// both keys provided, make sure they match
|
|
if pub != nil && pub.Curve != priv.Curve &&
|
|
pub.X.Cmp(priv.X) != 0 && pub.Y.Cmp(priv.Y) != 0 {
|
|
return nil, ErrKeyMismatch
|
|
}
|
|
|
|
return priv, nil
|
|
}
|