Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
K
k3s
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
k3s
Commits
48c6d1ab
Unverified
Commit
48c6d1ab
authored
Jan 19, 2018
by
Eric Chiang
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
oidc authentication: switch to v2 of coreos/go-oidc
parent
9586cd06
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
891 additions
and
573 deletions
+891
-573
options_test.go
cmd/kube-apiserver/app/options/options_test.go
+1
-0
config.go
pkg/kubeapiserver/authenticator/config.go
+12
-10
authentication.go
pkg/kubeapiserver/options/authentication.go
+6
-0
oidc.go
....io/apiserver/plugin/pkg/authenticator/token/oidc/oidc.go
+180
-134
oidc_test.go
...piserver/plugin/pkg/authenticator/token/oidc/oidc_test.go
+665
-429
gen.sh
...erver/plugin/pkg/authenticator/token/oidc/testdata/gen.sh
+27
-0
No files found.
cmd/kube-apiserver/app/options/options_test.go
View file @
48c6d1ab
...
...
@@ -210,6 +210,7 @@ func TestAddFlags(t *testing.T) {
BootstrapToken
:
&
kubeoptions
.
BootstrapTokenAuthenticationOptions
{},
OIDC
:
&
kubeoptions
.
OIDCAuthenticationOptions
{
UsernameClaim
:
"sub"
,
SigningAlgs
:
[]
string
{
"RS256"
},
},
PasswordFile
:
&
kubeoptions
.
PasswordFileAuthenticationOptions
{},
RequestHeader
:
&
apiserveroptions
.
RequestHeaderAuthenticationOptions
{},
...
...
pkg/kubeapiserver/authenticator/config.go
View file @
48c6d1ab
...
...
@@ -58,6 +58,7 @@ type AuthenticatorConfig struct {
OIDCUsernamePrefix
string
OIDCGroupsClaim
string
OIDCGroupsPrefix
string
OIDCSigningAlgs
[]
string
ServiceAccountKeyFiles
[]
string
ServiceAccountLookup
bool
WebhookTokenAuthnConfigFile
string
...
...
@@ -143,7 +144,7 @@ func (config AuthenticatorConfig) New() (authenticator.Request, *spec.SecurityDe
// simply returns an error, the OpenID Connect plugin may query the provider to
// update the keys, causing performance hits.
if
len
(
config
.
OIDCIssuerURL
)
>
0
&&
len
(
config
.
OIDCClientID
)
>
0
{
oidcAuth
,
err
:=
newAuthenticatorFromOIDCIssuerURL
(
config
.
OIDCIssuerURL
,
config
.
OIDCClientID
,
config
.
OIDCCAFile
,
config
.
OIDCUsernameClaim
,
config
.
OIDCUsernamePrefix
,
config
.
OIDCGroupsClaim
,
config
.
OIDCGroupsPrefix
)
oidcAuth
,
err
:=
newAuthenticatorFromOIDCIssuerURL
(
config
.
OIDCIssuerURL
,
config
.
OIDCClientID
,
config
.
OIDCCAFile
,
config
.
OIDCUsernameClaim
,
config
.
OIDCUsernamePrefix
,
config
.
OIDCGroupsClaim
,
config
.
OIDCGroupsPrefix
,
config
.
OIDCSigningAlgs
)
if
err
!=
nil
{
return
nil
,
nil
,
err
}
...
...
@@ -235,7 +236,7 @@ func newAuthenticatorFromTokenFile(tokenAuthFile string) (authenticator.Token, e
}
// newAuthenticatorFromOIDCIssuerURL returns an authenticator.Token or an error.
func
newAuthenticatorFromOIDCIssuerURL
(
issuerURL
,
clientID
,
caFile
,
usernameClaim
,
usernamePrefix
,
groupsClaim
,
groupsPrefix
string
)
(
authenticator
.
Token
,
error
)
{
func
newAuthenticatorFromOIDCIssuerURL
(
issuerURL
,
clientID
,
caFile
,
usernameClaim
,
usernamePrefix
,
groupsClaim
,
groupsPrefix
string
,
signingAlgs
[]
string
)
(
authenticator
.
Token
,
error
)
{
const
noUsernamePrefix
=
"-"
if
usernamePrefix
==
""
&&
usernameClaim
!=
"email"
{
...
...
@@ -251,14 +252,15 @@ func newAuthenticatorFromOIDCIssuerURL(issuerURL, clientID, caFile, usernameClai
usernamePrefix
=
""
}
tokenAuthenticator
,
err
:=
oidc
.
New
(
oidc
.
OIDCOptions
{
IssuerURL
:
issuerURL
,
ClientID
:
clientID
,
CAFile
:
caFile
,
UsernameClaim
:
usernameClaim
,
UsernamePrefix
:
usernamePrefix
,
GroupsClaim
:
groupsClaim
,
GroupsPrefix
:
groupsPrefix
,
tokenAuthenticator
,
err
:=
oidc
.
New
(
oidc
.
Options
{
IssuerURL
:
issuerURL
,
ClientID
:
clientID
,
CAFile
:
caFile
,
UsernameClaim
:
usernameClaim
,
UsernamePrefix
:
usernamePrefix
,
GroupsClaim
:
groupsClaim
,
GroupsPrefix
:
groupsPrefix
,
SupportedSigningAlgs
:
signingAlgs
,
})
if
err
!=
nil
{
return
nil
,
err
...
...
pkg/kubeapiserver/options/authentication.go
View file @
48c6d1ab
...
...
@@ -61,6 +61,7 @@ type OIDCAuthenticationOptions struct {
UsernamePrefix
string
GroupsClaim
string
GroupsPrefix
string
SigningAlgs
[]
string
}
type
PasswordFileAuthenticationOptions
struct
{
...
...
@@ -208,6 +209,10 @@ func (s *BuiltInAuthenticationOptions) AddFlags(fs *pflag.FlagSet) {
"If provided, all groups will be prefixed with this value to prevent conflicts with "
+
"other authentication strategies."
)
fs
.
StringSliceVar
(
&
s
.
OIDC
.
SigningAlgs
,
"oidc-signing-algs"
,
[]
string
{
"RS256"
},
""
+
"Comma-separated list of allowed JOSE asymmetric signing algorithms. JWTs with a "
+
"'alg' header value not in this list will be rejected. "
+
"Values are defined by RFC 7518 https://tools.ietf.org/html/rfc7518#section-3.1."
)
}
if
s
.
PasswordFile
!=
nil
{
...
...
@@ -272,6 +277,7 @@ func (s *BuiltInAuthenticationOptions) ToAuthenticationConfig() authenticator.Au
ret
.
OIDCIssuerURL
=
s
.
OIDC
.
IssuerURL
ret
.
OIDCUsernameClaim
=
s
.
OIDC
.
UsernameClaim
ret
.
OIDCUsernamePrefix
=
s
.
OIDC
.
UsernamePrefix
ret
.
OIDCSigningAlgs
=
s
.
OIDC
.
SigningAlgs
}
if
s
.
PasswordFile
!=
nil
{
...
...
staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/oidc/oidc.go
View file @
48c6d1ab
...
...
@@ -17,7 +17,7 @@ limitations under the License.
/*
oidc implements the authenticator.Token interface using the OpenID Connect protocol.
config := oidc.O
IDCO
ptions{
config := oidc.Options{
IssuerURL: "https://accounts.google.com",
ClientID: os.Getenv("GOOGLE_CLIENT_ID"),
UsernameClaim: "email",
...
...
@@ -27,25 +27,28 @@ oidc implements the authenticator.Token interface using the OpenID Connect proto
package
oidc
import
(
"context"
"crypto/tls"
"crypto/x509"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
"s
ync
"
"s
trings
"
"sync/atomic"
"time"
"github.com/coreos/go-oidc/jose"
"github.com/coreos/go-oidc/oidc"
oidc
"github.com/coreos/go-oidc"
"github.com/golang/glog"
"k8s.io/apimachinery/pkg/util/net"
"k8s.io/apimachinery/pkg/util/
runtime
"
"k8s.io/apimachinery/pkg/util/
wait
"
"k8s.io/apiserver/pkg/authentication/user"
certutil
"k8s.io/client-go/util/cert"
)
type
O
IDCO
ptions
struct
{
type
Options
struct
{
// IssuerURL is the URL the provider signs ID Tokens as. This will be the "iss"
// field of all tokens produced by the provider and is used for configuration
// discovery.
...
...
@@ -83,30 +86,85 @@ type OIDCOptions struct {
// GroupsPrefix, if specified, causes claims mapping to group names to be prefixed with the
// value. A value "oidc:" would result in groups like "oidc:engineering" and "oidc:marketing".
GroupsPrefix
string
// SupportedSigningAlgs sets the accepted set of JOSE signing algorithms that
// can be used by the provider to sign tokens.
//
// https://tools.ietf.org/html/rfc7518#section-3.1
//
// This value defaults to RS256, the value recommended by the OpenID Connect
// spec:
//
// https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
SupportedSigningAlgs
[]
string
// now is used for testing. It defaults to time.Now.
now
func
()
time
.
Time
}
type
OIDC
Authenticator
struct
{
type
Authenticator
struct
{
issuerURL
string
trustedClientID
string
usernameClaim
string
usernamePrefix
string
groupsClaim
string
groupsPrefix
string
httpClient
*
http
.
Client
// Contains an *oidc.IDTokenVerifier. Do not access directly use the
// idTokenVerifier method.
verifier
atomic
.
Value
// Contains an *oidc.Client. Do not access directly. Use client() method.
oidcClient
atomic
.
Value
cancel
context
.
CancelFunc
}
// Guards the close method and is used to lock during initialization and closing.
mu
sync
.
Mutex
close
func
()
// May be nil
func
(
a
*
Authenticator
)
setVerifier
(
v
*
oidc
.
IDTokenVerifier
)
{
a
.
verifier
.
Store
(
v
)
}
// New creates a token authenticator which validates OpenID Connect ID Tokens.
func
New
(
opts
OIDCOptions
)
(
*
OIDCAuthenticator
,
error
)
{
func
(
a
*
Authenticator
)
idTokenVerifier
()
(
*
oidc
.
IDTokenVerifier
,
bool
)
{
if
v
:=
a
.
verifier
.
Load
();
v
!=
nil
{
return
v
.
(
*
oidc
.
IDTokenVerifier
),
true
}
return
nil
,
false
}
func
(
a
*
Authenticator
)
Close
()
{
a
.
cancel
()
}
func
New
(
opts
Options
)
(
*
Authenticator
,
error
)
{
return
newAuthenticator
(
opts
,
func
(
ctx
context
.
Context
,
a
*
Authenticator
,
config
*
oidc
.
Config
)
{
// Asynchronously attempt to initialize the authenticator. This enables
// self-hosted providers, providers that run on top of Kubernetes itself.
go
wait
.
PollUntil
(
time
.
Second
*
10
,
func
()
(
done
bool
,
err
error
)
{
provider
,
err
:=
oidc
.
NewProvider
(
ctx
,
a
.
issuerURL
)
if
err
!=
nil
{
glog
.
Errorf
(
"oidc authenticator: initializing plugin: %v"
,
err
)
return
false
,
nil
}
verifier
:=
provider
.
Verifier
(
config
)
a
.
setVerifier
(
verifier
)
return
true
,
nil
},
ctx
.
Done
())
})
}
// whitelist of signing algorithms to ensure users don't mistakenly pass something
// goofy.
var
allowedSigningAlgs
=
map
[
string
]
bool
{
oidc
.
RS256
:
true
,
oidc
.
RS384
:
true
,
oidc
.
RS512
:
true
,
oidc
.
ES256
:
true
,
oidc
.
ES384
:
true
,
oidc
.
ES512
:
true
,
oidc
.
PS256
:
true
,
oidc
.
PS384
:
true
,
oidc
.
PS512
:
true
,
}
func
newAuthenticator
(
opts
Options
,
initVerifier
func
(
ctx
context
.
Context
,
a
*
Authenticator
,
config
*
oidc
.
Config
))
(
*
Authenticator
,
error
)
{
url
,
err
:=
url
.
Parse
(
opts
.
IssuerURL
)
if
err
!=
nil
{
return
nil
,
err
...
...
@@ -120,6 +178,18 @@ func New(opts OIDCOptions) (*OIDCAuthenticator, error) {
return
nil
,
errors
.
New
(
"no username claim provided"
)
}
supportedSigningAlgs
:=
opts
.
SupportedSigningAlgs
if
len
(
supportedSigningAlgs
)
==
0
{
// RS256 is the default recommended by OpenID Connect and an 'alg' value
// providers are required to implement.
supportedSigningAlgs
=
[]
string
{
oidc
.
RS256
}
}
for
_
,
alg
:=
range
supportedSigningAlgs
{
if
!
allowedSigningAlgs
[
alg
]
{
return
nil
,
fmt
.
Errorf
(
"oidc: unsupported signing alg: %q"
,
alg
)
}
}
var
roots
*
x509
.
CertPool
if
opts
.
CAFile
!=
""
{
roots
,
err
=
certutil
.
NewPool
(
opts
.
CAFile
)
...
...
@@ -137,137 +207,91 @@ func New(opts OIDCOptions) (*OIDCAuthenticator, error) {
TLSClientConfig
:
&
tls
.
Config
{
RootCAs
:
roots
},
})
authenticator
:=
&
OIDCAuthenticator
{
issuerURL
:
opts
.
IssuerURL
,
trustedClientID
:
opts
.
ClientID
,
usernameClaim
:
opts
.
UsernameClaim
,
usernamePrefix
:
opts
.
UsernamePrefix
,
groupsClaim
:
opts
.
GroupsClaim
,
groupsPrefix
:
opts
.
GroupsPrefix
,
httpClient
:
&
http
.
Client
{
Transport
:
tr
},
}
// Attempt to initialize the authenticator asynchronously.
//
// Ignore errors instead of returning it since the OpenID Connect provider might not be
// available yet, for instance if it's running on the cluster and needs the API server
// to come up first. Errors will be logged within the client() method.
go
func
()
{
defer
runtime
.
HandleCrash
()
authenticator
.
client
()
}()
return
authenticator
,
nil
}
client
:=
&
http
.
Client
{
Transport
:
tr
,
Timeout
:
30
*
time
.
Second
}
// Close stops all goroutines used by the authenticator.
func
(
a
*
OIDCAuthenticator
)
Close
()
{
a
.
mu
.
Lock
()
defer
a
.
mu
.
Unlock
()
ctx
,
cancel
:=
context
.
WithCancel
(
context
.
Background
())
ctx
=
oidc
.
ClientContext
(
ctx
,
client
)
if
a
.
close
!=
nil
{
a
.
close
()
authenticator
:=
&
Authenticator
{
issuerURL
:
opts
.
IssuerURL
,
usernameClaim
:
opts
.
UsernameClaim
,
usernamePrefix
:
opts
.
UsernamePrefix
,
groupsClaim
:
opts
.
GroupsClaim
,
groupsPrefix
:
opts
.
GroupsPrefix
,
cancel
:
cancel
,
}
return
}
func
(
a
*
OIDCAuthenticator
)
client
()
(
*
oidc
.
Client
,
error
)
{
// Fast check to see if client has already been initialized.
if
client
:=
a
.
oidcClient
.
Load
();
client
!=
nil
{
return
client
.
(
*
oidc
.
Client
),
nil
now
:=
opts
.
now
if
now
==
nil
{
now
=
time
.
Now
}
// Acquire lock, then recheck initialization.
a
.
mu
.
Lock
()
defer
a
.
mu
.
Unlock
()
if
client
:=
a
.
oidcClient
.
Load
();
client
!=
nil
{
return
client
.
(
*
oidc
.
Client
),
nil
verifierConfig
:=
&
oidc
.
Config
{
ClientID
:
opts
.
ClientID
,
SupportedSigningAlgs
:
supportedSigningAlgs
,
Now
:
now
,
}
// Try to initialize client.
providerConfig
,
err
:=
oidc
.
FetchProviderConfig
(
a
.
httpClient
,
a
.
issuerURL
)
if
err
!=
nil
{
glog
.
Errorf
(
"oidc authenticator: failed to fetch provider discovery data: %v"
,
err
)
return
nil
,
fmt
.
Errorf
(
"fetch provider config: %v"
,
err
)
}
initVerifier
(
ctx
,
authenticator
,
verifierConfig
)
return
authenticator
,
nil
}
clientConfig
:=
oidc
.
ClientConfig
{
HTTPClient
:
a
.
httpClient
,
Credentials
:
oidc
.
ClientCredentials
{
ID
:
a
.
trustedClientID
},
ProviderConfig
:
providerConfig
,
func
hasCorrectIssuer
(
iss
,
tokenData
string
)
bool
{
parts
:=
strings
.
Split
(
tokenData
,
"."
)
if
len
(
parts
)
!=
3
{
return
false
}
client
,
err
:=
oidc
.
NewClient
(
clientConfig
)
payload
,
err
:=
base64
.
RawURLEncoding
.
DecodeString
(
parts
[
1
])
if
err
!=
nil
{
glog
.
Errorf
(
"oidc authenticator: failed to create client: %v"
,
err
)
return
nil
,
fmt
.
Errorf
(
"create client: %v"
,
err
)
return
false
}
// SyncProviderConfig will start a goroutine to periodically synchronize the provider config.
// The synchronization interval is set by the expiration length of the config, and has a minimum
// and maximum threshold.
stop
:=
client
.
SyncProviderConfig
(
a
.
issuerURL
)
a
.
oidcClient
.
Store
(
client
)
a
.
close
=
func
()
{
// This assumes the stop is an unbuffered channel.
// So instead of closing the channel, we send am empty struct here.
// This guarantees that when this function returns, there is no flying requests,
// because a send to an unbuffered channel happens after the receive from the channel.
stop
<-
struct
{}{}
claims
:=
struct
{
// WARNING: this JWT is not verified. Do not trust these claims.
Issuer
string
`json:"iss"`
}{}
if
err
:=
json
.
Unmarshal
(
payload
,
&
claims
);
err
!=
nil
{
return
false
}
return
client
,
nil
if
claims
.
Issuer
!=
iss
{
return
false
}
return
true
}
// AuthenticateToken decodes and verifies an ID Token using the OIDC client, if the verification succeeds,
// then it will extract the user info from the JWT claims.
func
(
a
*
OIDCAuthenticator
)
AuthenticateToken
(
value
string
)
(
user
.
Info
,
bool
,
error
)
{
jwt
,
err
:=
jose
.
ParseJWT
(
value
)
if
err
!=
nil
{
return
nil
,
false
,
err
func
(
a
*
Authenticator
)
AuthenticateToken
(
token
string
)
(
user
.
Info
,
bool
,
error
)
{
if
!
hasCorrectIssuer
(
a
.
issuerURL
,
token
)
{
return
nil
,
false
,
nil
}
client
,
err
:=
a
.
client
()
if
err
!=
nil
{
return
nil
,
false
,
err
}
if
err
:=
client
.
VerifyJWT
(
jwt
);
err
!=
nil
{
return
nil
,
false
,
err
ctx
:=
context
.
Background
()
verifier
,
ok
:=
a
.
idTokenVerifier
()
if
!
ok
{
return
nil
,
false
,
fmt
.
Errorf
(
"oidc: authenticator not initialized"
)
}
claims
,
err
:=
jwt
.
Claims
()
idToken
,
err
:=
verifier
.
Verify
(
ctx
,
token
)
if
err
!=
nil
{
return
nil
,
false
,
err
return
nil
,
false
,
fmt
.
Errorf
(
"oidc: verify token: %v"
,
err
)
}
return
a
.
parseTokenClaims
(
claims
)
}
// parseTokenClaims maps a set of claims to a user. It performs basic validation such as
// ensuring the email is verified.
func
(
a
*
OIDCAuthenticator
)
parseTokenClaims
(
claims
jose
.
Claims
)
(
user
.
Info
,
bool
,
error
)
{
username
,
ok
,
err
:=
claims
.
StringClaim
(
a
.
usernameClaim
)
if
err
!=
nil
{
return
nil
,
false
,
err
var
c
claims
if
err
:=
idToken
.
Claims
(
&
c
);
err
!=
nil
{
return
nil
,
false
,
fmt
.
Errorf
(
"oidc: parse claims: %v"
,
err
)
}
if
!
ok
{
return
nil
,
false
,
fmt
.
Errorf
(
"cannot find %q in JWT claims"
,
a
.
usernameClaim
)
var
username
string
if
err
:=
c
.
unmarshalClaim
(
a
.
usernameClaim
,
&
username
);
err
!=
nil
{
return
nil
,
false
,
fmt
.
Errorf
(
"oidc: parse username claims %q: %v"
,
a
.
usernameClaim
,
err
)
}
if
a
.
usernameClaim
==
"email"
{
verified
,
ok
:=
claims
[
"email_verified"
]
if
!
ok
{
return
nil
,
false
,
errors
.
New
(
"'email_verified' claim not present"
)
}
emailVerified
,
ok
:=
verified
.
(
bool
)
if
!
ok
{
// OpenID Connect spec defines 'email_verified' as a boolean. For now, be a pain and error if
// it's a different type. If there are enough misbehaving providers we can relax this latter.
//
// See: https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims
return
nil
,
false
,
fmt
.
Errorf
(
"malformed claim 'email_verified', expected boolean got %T"
,
verified
)
// Check the email_verified claim to ensure the email is valid.
// https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims
var
emailVerified
bool
if
err
:=
c
.
unmarshalClaim
(
"email_verified"
,
&
emailVerified
);
err
!=
nil
{
return
nil
,
false
,
fmt
.
Errorf
(
"oidc: parse 'email_verified' claim: %v"
,
err
)
}
if
!
emailVerified
{
return
nil
,
false
,
errors
.
New
(
"
email not verified"
)
return
nil
,
false
,
fmt
.
Errorf
(
"oidc:
email not verified"
)
}
}
...
...
@@ -275,21 +299,18 @@ func (a *OIDCAuthenticator) parseTokenClaims(claims jose.Claims) (user.Info, boo
username
=
a
.
usernamePrefix
+
username
}
// TODO(yifan): Add UID, also populate the issuer to upper layer.
info
:=
&
user
.
DefaultInfo
{
Name
:
username
}
if
a
.
groupsClaim
!=
""
{
groups
,
found
,
err
:=
claims
.
StringsClaim
(
a
.
groupsClaim
)
if
err
!=
nil
{
// Groups type is present but is not an array of strings, try to decode as a string.
group
,
_
,
err
:=
claims
.
StringClaim
(
a
.
groupsClaim
)
if
err
!=
nil
{
// Custom claim is present, but isn't an array of strings or a string.
return
nil
,
false
,
fmt
.
Errorf
(
"custom group claim contains invalid type: %T"
,
claims
[
a
.
groupsClaim
])
if
_
,
ok
:=
c
[
a
.
groupsClaim
];
ok
{
// Some admins want to use string claims like "role" as the group value.
// Allow the group claim to be a single string instead of an array.
//
// See: https://github.com/kubernetes/kubernetes/issues/33290
var
groups
stringOrArray
if
err
:=
c
.
unmarshalClaim
(
a
.
groupsClaim
,
&
groups
);
err
!=
nil
{
return
nil
,
false
,
fmt
.
Errorf
(
"oidc: parse groups claim %q: %v"
,
a
.
groupsClaim
,
err
)
}
info
.
Groups
=
[]
string
{
group
}
}
else
if
found
{
info
.
Groups
=
groups
info
.
Groups
=
[]
string
(
groups
)
}
}
...
...
@@ -298,6 +319,31 @@ func (a *OIDCAuthenticator) parseTokenClaims(claims jose.Claims) (user.Info, boo
info
.
Groups
[
i
]
=
a
.
groupsPrefix
+
group
}
}
return
info
,
true
,
nil
}
type
stringOrArray
[]
string
func
(
s
*
stringOrArray
)
UnmarshalJSON
(
b
[]
byte
)
error
{
var
a
[]
string
if
err
:=
json
.
Unmarshal
(
b
,
&
a
);
err
==
nil
{
*
s
=
a
return
nil
}
var
str
string
if
err
:=
json
.
Unmarshal
(
b
,
&
str
);
err
!=
nil
{
return
err
}
*
s
=
[]
string
{
str
}
return
nil
}
type
claims
map
[
string
]
json
.
RawMessage
func
(
c
claims
)
unmarshalClaim
(
name
string
,
v
interface
{})
error
{
val
,
ok
:=
c
[
name
]
if
!
ok
{
return
fmt
.
Errorf
(
"claim not present"
)
}
return
json
.
Unmarshal
([]
byte
(
val
),
v
)
}
staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/oidc/oidc_test.go
View file @
48c6d1ab
...
...
@@ -17,518 +17,754 @@ limitations under the License.
package
oidc
import
(
"os"
"path"
"context"
"crypto"
"crypto/x509"
"encoding/hex"
"encoding/json"
"encoding/pem"
"fmt"
"io/ioutil"
"reflect"
"sort"
"strings"
"testing"
"time"
"github.com/coreos/go-oidc/jose"
"github.com/coreos/go-oidc/oidc"
oidc
"github.com/coreos/go-oidc"
jose
"gopkg.in/square/go-jose.v2"
"k8s.io/apiserver/pkg/authentication/user"
oidctesting
"k8s.io/apiserver/plugin/pkg/authenticator/token/oidc/testing"
)
func
generateToken
(
t
*
testing
.
T
,
op
*
oidctesting
.
OIDCProvider
,
iss
,
sub
,
aud
string
,
usernameClaim
,
value
,
groupsClaim
string
,
groups
interface
{},
iat
,
exp
time
.
Time
,
emailVerified
bool
)
string
{
claims
:=
oidc
.
NewClaims
(
iss
,
sub
,
aud
,
iat
,
exp
)
claims
.
Add
(
usernameClaim
,
value
)
if
groups
!=
nil
&&
groupsClaim
!=
""
{
claims
.
Add
(
groupsClaim
,
groups
)
}
claims
.
Add
(
"email_verified"
,
emailVerified
)
// utilities for loading JOSE keys.
signer
:=
op
.
PrivKey
.
Signer
()
jwt
,
err
:=
jose
.
NewSignedJWT
(
claims
,
signer
)
if
err
!=
nil
{
t
.
Fatalf
(
"Cannot generate token: %v"
,
err
)
return
""
}
return
jwt
.
Encode
()
func
loadRSAKey
(
t
*
testing
.
T
,
filepath
string
,
alg
jose
.
SignatureAlgorithm
)
*
jose
.
JSONWebKey
{
return
loadKey
(
t
,
filepath
,
alg
,
func
(
b
[]
byte
)
(
interface
{},
error
)
{
key
,
err
:=
x509
.
ParsePKCS1PrivateKey
(
b
)
if
err
!=
nil
{
return
nil
,
err
}
return
key
.
Public
(),
nil
})
}
func
generateTokenWithUnverifiedEmail
(
t
*
testing
.
T
,
op
*
oidctesting
.
OIDCProvider
,
iss
,
sub
,
aud
string
,
email
string
)
string
{
return
generateToken
(
t
,
op
,
iss
,
sub
,
aud
,
"email"
,
email
,
""
,
nil
,
time
.
Now
(),
time
.
Now
()
.
Add
(
time
.
Hour
),
false
)
func
loadRSAPrivKey
(
t
*
testing
.
T
,
filepath
string
,
alg
jose
.
SignatureAlgorithm
)
*
jose
.
JSONWebKey
{
return
loadKey
(
t
,
filepath
,
alg
,
func
(
b
[]
byte
)
(
interface
{},
error
)
{
return
x509
.
ParsePKCS1PrivateKey
(
b
)
})
}
func
generateGoodToken
(
t
*
testing
.
T
,
op
*
oidctesting
.
OIDCProvider
,
iss
,
sub
,
aud
string
,
usernameClaim
,
value
,
groupsClaim
string
,
groups
interface
{})
string
{
return
generateToken
(
t
,
op
,
iss
,
sub
,
aud
,
usernameClaim
,
value
,
groupsClaim
,
groups
,
time
.
Now
(),
time
.
Now
()
.
Add
(
time
.
Hour
),
true
)
func
loadECDSAKey
(
t
*
testing
.
T
,
filepath
string
,
alg
jose
.
SignatureAlgorithm
)
*
jose
.
JSONWebKey
{
return
loadKey
(
t
,
filepath
,
alg
,
func
(
b
[]
byte
)
(
interface
{},
error
)
{
key
,
err
:=
x509
.
ParseECPrivateKey
(
b
)
if
err
!=
nil
{
return
nil
,
err
}
return
key
.
Public
(),
nil
})
}
func
generateMalformedToken
(
t
*
testing
.
T
,
op
*
oidctesting
.
OIDCProvider
,
iss
,
sub
,
aud
string
,
usernameClaim
,
value
,
groupsClaim
string
,
groups
interface
{})
string
{
return
generateToken
(
t
,
op
,
iss
,
sub
,
aud
,
usernameClaim
,
value
,
groupsClaim
,
groups
,
time
.
Now
(),
time
.
Now
()
.
Add
(
time
.
Hour
),
true
)
+
"randombits"
func
loadECDSAPrivKey
(
t
*
testing
.
T
,
filepath
string
,
alg
jose
.
SignatureAlgorithm
)
*
jose
.
JSONWebKey
{
return
loadKey
(
t
,
filepath
,
alg
,
func
(
b
[]
byte
)
(
interface
{},
error
)
{
return
x509
.
ParseECPrivateKey
(
b
)
})
}
func
generateExpiredToken
(
t
*
testing
.
T
,
op
*
oidctesting
.
OIDCProvider
,
iss
,
sub
,
aud
string
,
usernameClaim
,
value
,
groupsClaim
string
,
groups
interface
{})
string
{
return
generateToken
(
t
,
op
,
iss
,
sub
,
aud
,
usernameClaim
,
value
,
groupsClaim
,
groups
,
time
.
Now
()
.
Add
(
-
2
*
time
.
Hour
),
time
.
Now
()
.
Add
(
-
1
*
time
.
Hour
),
true
)
func
loadKey
(
t
*
testing
.
T
,
filepath
string
,
alg
jose
.
SignatureAlgorithm
,
unmarshal
func
([]
byte
)
(
interface
{},
error
))
*
jose
.
JSONWebKey
{
data
,
err
:=
ioutil
.
ReadFile
(
filepath
)
if
err
!=
nil
{
t
.
Fatalf
(
"load file: %v"
,
err
)
}
block
,
_
:=
pem
.
Decode
(
data
)
if
block
==
nil
{
t
.
Fatalf
(
"file contained no PEM encoded data: %s"
,
filepath
)
}
priv
,
err
:=
unmarshal
(
block
.
Bytes
)
if
err
!=
nil
{
t
.
Fatalf
(
"unmarshal key: %v"
,
err
)
}
key
:=
&
jose
.
JSONWebKey
{
Key
:
priv
,
Use
:
"sig"
,
Algorithm
:
string
(
alg
)}
thumbprint
,
err
:=
key
.
Thumbprint
(
crypto
.
SHA256
)
if
err
!=
nil
{
t
.
Fatalf
(
"computing thumbprint: %v"
,
err
)
}
key
.
KeyID
=
hex
.
EncodeToString
(
thumbprint
)
return
key
}
func
TestTLSConfig
(
t
*
testing
.
T
)
{
// Verify the cert/key pair works.
cert1
:=
path
.
Join
(
os
.
TempDir
(),
"oidc-cert-1"
)
key1
:=
path
.
Join
(
os
.
TempDir
(),
"oidc-key-1"
)
cert2
:=
path
.
Join
(
os
.
TempDir
(),
"oidc-cert-2"
)
key2
:=
path
.
Join
(
os
.
TempDir
(),
"oidc-key-2"
)
defer
os
.
Remove
(
cert1
)
defer
os
.
Remove
(
key1
)
defer
os
.
Remove
(
cert2
)
defer
os
.
Remove
(
key2
)
oidctesting
.
GenerateSelfSignedCert
(
t
,
"127.0.0.1"
,
cert1
,
key1
)
oidctesting
.
GenerateSelfSignedCert
(
t
,
"127.0.0.1"
,
cert2
,
key2
)
tests
:=
[]
struct
{
testCase
string
serverCertFile
string
serverKeyFile
string
trustedCertFile
string
// staticKeySet implements oidc.KeySet.
type
staticKeySet
struct
{
keys
[]
*
jose
.
JSONWebKey
}
wantErr
bool
}{
{
testCase
:
"provider using untrusted custom cert"
,
serverCertFile
:
cert1
,
serverKeyFile
:
key1
,
wantErr
:
true
,
},
{
testCase
:
"provider using untrusted cert"
,
serverCertFile
:
cert1
,
serverKeyFile
:
key1
,
trustedCertFile
:
cert2
,
wantErr
:
true
,
},
{
testCase
:
"provider using trusted cert"
,
serverCertFile
:
cert1
,
serverKeyFile
:
key1
,
trustedCertFile
:
cert1
,
wantErr
:
false
,
},
func
(
s
*
staticKeySet
)
VerifySignature
(
ctx
context
.
Context
,
jwt
string
)
(
payload
[]
byte
,
err
error
)
{
jws
,
err
:=
jose
.
ParseSigned
(
jwt
)
if
err
!=
nil
{
return
nil
,
err
}
if
len
(
jws
.
Signatures
)
==
0
{
return
nil
,
fmt
.
Errorf
(
"jwt contained no signatures"
)
}
kid
:=
jws
.
Signatures
[
0
]
.
Header
.
KeyID
for
_
,
tc
:=
range
tests
{
func
()
{
op
:=
oidctesting
.
NewOIDCProvider
(
t
,
""
)
srv
,
err
:=
op
.
ServeTLSWithKeyPair
(
tc
.
serverCertFile
,
tc
.
serverKeyFile
)
if
err
!=
nil
{
t
.
Errorf
(
"%s: %v"
,
tc
.
testCase
,
err
)
return
}
defer
srv
.
Close
()
issuer
:=
srv
.
URL
clientID
:=
"client-foo"
options
:=
OIDCOptions
{
IssuerURL
:
srv
.
URL
,
ClientID
:
clientID
,
CAFile
:
tc
.
trustedCertFile
,
UsernameClaim
:
"email"
,
GroupsClaim
:
"groups"
,
}
authenticator
,
err
:=
New
(
options
)
if
err
!=
nil
{
t
.
Errorf
(
"%s: failed to initialize authenticator: %v"
,
tc
.
testCase
,
err
)
return
}
defer
authenticator
.
Close
()
email
:=
"user-1@example.com"
groups
:=
[]
string
{
"group1"
,
"group2"
}
sort
.
Strings
(
groups
)
token
:=
generateGoodToken
(
t
,
op
,
issuer
,
"user-1"
,
clientID
,
"email"
,
email
,
"groups"
,
groups
)
// Because this authenticator behaves differently for subsequent requests, run these
// tests multiple times (but expect the same result).
for
i
:=
1
;
i
<
4
;
i
++
{
user
,
ok
,
err
:=
authenticator
.
AuthenticateToken
(
token
)
if
err
!=
nil
{
if
!
tc
.
wantErr
{
t
.
Errorf
(
"%s (req #%d): failed to authenticate token: %v"
,
tc
.
testCase
,
i
,
err
)
}
continue
}
if
tc
.
wantErr
{
t
.
Errorf
(
"%s (req #%d): expected error authenticating"
,
tc
.
testCase
,
i
)
continue
}
if
!
ok
{
t
.
Errorf
(
"%s (req #%d): did not get user or error"
,
tc
.
testCase
,
i
)
continue
}
if
gotUsername
:=
user
.
GetName
();
email
!=
gotUsername
{
t
.
Errorf
(
"%s (req #%d): GetName() expected=%q got %q"
,
tc
.
testCase
,
i
,
email
,
gotUsername
)
}
gotGroups
:=
user
.
GetGroups
()
sort
.
Strings
(
gotGroups
)
if
!
reflect
.
DeepEqual
(
gotGroups
,
groups
)
{
t
.
Errorf
(
"%s (req #%d): GetGroups() expected=%q got %q"
,
tc
.
testCase
,
i
,
groups
,
gotGroups
)
}
}
}()
for
_
,
key
:=
range
s
.
keys
{
if
key
.
KeyID
==
kid
{
return
jws
.
Verify
(
key
)
}
}
return
nil
,
fmt
.
Errorf
(
"no keys matches jwk keyid"
)
}
func
TestOIDCAuthentication
(
t
*
testing
.
T
)
{
cert
:=
path
.
Join
(
os
.
TempDir
(),
"oidc-cert"
)
key
:=
path
.
Join
(
os
.
TempDir
(),
"oidc-key"
)
var
(
expired
,
_
=
time
.
Parse
(
time
.
RFC3339Nano
,
"2009-11-10T22:00:00Z"
)
now
,
_
=
time
.
Parse
(
time
.
RFC3339Nano
,
"2009-11-10T23:00:00Z"
)
valid
,
_
=
time
.
Parse
(
time
.
RFC3339Nano
,
"2009-11-11T00:00:00Z"
)
)
defer
os
.
Remove
(
cert
)
defer
os
.
Remove
(
key
)
type
claimsTest
struct
{
name
string
options
Options
now
time
.
Time
signingKey
*
jose
.
JSONWebKey
pubKeys
[]
*
jose
.
JSONWebKey
claims
string
want
*
user
.
DefaultInfo
wantSkip
bool
wantErr
bool
wantInitErr
bool
}
oidctesting
.
GenerateSelfSignedCert
(
t
,
"127.0.0.1"
,
cert
,
key
)
func
(
c
*
claimsTest
)
run
(
t
*
testing
.
T
)
{
a
,
err
:=
newAuthenticator
(
c
.
options
,
func
(
ctx
context
.
Context
,
a
*
Authenticator
,
config
*
oidc
.
Config
)
{
// Set the verifier to use the public key set instead of reading
// from a remote.
a
.
setVerifier
(
oidc
.
NewVerifier
(
c
.
options
.
IssuerURL
,
&
staticKeySet
{
keys
:
c
.
pubKeys
},
config
,
))
})
if
err
!=
nil
{
if
!
c
.
wantInitErr
{
t
.
Fatalf
(
"initialize authenticator: %v"
,
err
)
}
return
}
if
c
.
wantInitErr
{
t
.
Fatalf
(
"wanted initialization error"
)
}
// Ensure all tests pass when the issuer is not at a base URL.
for
_
,
path
:=
range
[]
string
{
""
,
"/path/with/trailing/slash/"
}
{
// Sign and serialize the claims in a JWT.
signer
,
err
:=
jose
.
NewSigner
(
jose
.
SigningKey
{
Algorithm
:
jose
.
SignatureAlgorithm
(
c
.
signingKey
.
Algorithm
),
Key
:
c
.
signingKey
,
},
nil
)
if
err
!=
nil
{
t
.
Fatalf
(
"initialize signer: %v"
,
err
)
}
jws
,
err
:=
signer
.
Sign
([]
byte
(
c
.
claims
))
if
err
!=
nil
{
t
.
Fatalf
(
"sign claims: %v"
,
err
)
}
token
,
err
:=
jws
.
CompactSerialize
()
if
err
!=
nil
{
t
.
Fatalf
(
"serialize token: %v"
,
err
)
}
// Create a TLS server and a client.
op
:=
oidctesting
.
NewOIDCProvider
(
t
,
path
)
srv
,
err
:=
op
.
ServeTLSWithKeyPair
(
cert
,
key
)
if
err
!=
nil
{
t
.
Fatalf
(
"Cannot start server: %v"
,
err
)
}
defer
srv
.
Close
()
tests
:=
[]
struct
{
userClaim
string
groupsClaim
string
token
string
userInfo
user
.
Info
verified
bool
err
string
}{
{
"sub"
,
""
,
generateGoodToken
(
t
,
op
,
srv
.
URL
,
"client-foo"
,
"client-foo"
,
"sub"
,
"user-foo"
,
""
,
nil
),
&
user
.
DefaultInfo
{
Name
:
"user-foo"
},
true
,
""
,
},
{
// Use user defined claim (email here).
"email"
,
""
,
generateGoodToken
(
t
,
op
,
srv
.
URL
,
"client-foo"
,
"client-foo"
,
"email"
,
"foo@example.com"
,
""
,
nil
),
&
user
.
DefaultInfo
{
Name
:
"foo@example.com"
},
true
,
""
,
},
{
// Use user defined claim (email here).
"email"
,
""
,
generateGoodToken
(
t
,
op
,
srv
.
URL
,
"client-foo"
,
"client-foo"
,
"email"
,
"foo@example.com"
,
"groups"
,
[]
string
{
"group1"
,
"group2"
}),
&
user
.
DefaultInfo
{
Name
:
"foo@example.com"
},
true
,
""
,
},
{
// Use user defined claim (email here).
"email"
,
"groups"
,
generateGoodToken
(
t
,
op
,
srv
.
URL
,
"client-foo"
,
"client-foo"
,
"email"
,
"foo@example.com"
,
"groups"
,
[]
string
{
"group1"
,
"group2"
}),
&
user
.
DefaultInfo
{
Name
:
"foo@example.com"
,
Groups
:
[]
string
{
"group1"
,
"group2"
}},
true
,
""
,
},
{
// Group claim is a string rather than an array. Map that string to a single group.
"email"
,
"groups"
,
generateGoodToken
(
t
,
op
,
srv
.
URL
,
"client-foo"
,
"client-foo"
,
"email"
,
"foo@example.com"
,
"groups"
,
"group1"
),
&
user
.
DefaultInfo
{
Name
:
"foo@example.com"
,
Groups
:
[]
string
{
"group1"
}},
true
,
""
,
},
{
// Group claim is not a string or array of strings. Throw out this as invalid.
"email"
,
"groups"
,
generateGoodToken
(
t
,
op
,
srv
.
URL
,
"client-foo"
,
"client-foo"
,
"email"
,
"foo@example.com"
,
"groups"
,
1
),
nil
,
false
,
"custom group claim contains invalid type: float64"
,
},
{
// Email not verified
"email"
,
""
,
generateTokenWithUnverifiedEmail
(
t
,
op
,
srv
.
URL
,
"client-foo"
,
"client-foo"
,
"foo@example.com"
),
nil
,
false
,
"email not verified"
,
},
{
"sub"
,
""
,
generateMalformedToken
(
t
,
op
,
srv
.
URL
,
"client-foo"
,
"client-foo"
,
"sub"
,
"user-foo"
,
""
,
nil
),
nil
,
false
,
"oidc: unable to verify JWT signature: no matching keys"
,
},
{
// Invalid 'aud'.
"sub"
,
""
,
generateGoodToken
(
t
,
op
,
srv
.
URL
,
"client-foo"
,
"client-bar"
,
"sub"
,
"user-foo"
,
""
,
nil
),
nil
,
false
,
"oidc: JWT claims invalid: invalid claims, 'aud' claim and 'client_id' do not match"
,
},
{
// Invalid issuer.
"sub"
,
""
,
generateGoodToken
(
t
,
op
,
"http://foo-bar.com"
,
"client-foo"
,
"client-foo"
,
"sub"
,
"user-foo"
,
""
,
nil
),
nil
,
false
,
"oidc: JWT claims invalid: invalid claim value: 'iss'."
,
},
{
"sub"
,
""
,
generateExpiredToken
(
t
,
op
,
srv
.
URL
,
"client-foo"
,
"client-foo"
,
"sub"
,
"user-foo"
,
""
,
nil
),
nil
,
false
,
"oidc: JWT claims invalid: token is expired"
,
},
got
,
ok
,
err
:=
a
.
AuthenticateToken
(
token
)
if
err
!=
nil
{
if
!
c
.
wantErr
{
t
.
Fatalf
(
"authenticate token: %v"
,
err
)
}
return
}
for
i
,
tt
:=
range
tests
{
client
,
err
:=
New
(
OIDCOptions
{
srv
.
URL
,
"client-foo"
,
cert
,
tt
.
userClaim
,
""
,
tt
.
groupsClaim
,
""
})
if
err
!=
nil
{
t
.
Errorf
(
"Unexpected error: %v"
,
err
)
continue
}
user
,
result
,
err
:=
client
.
AuthenticateToken
(
tt
.
token
)
if
tt
.
err
!=
""
{
if
!
strings
.
HasPrefix
(
err
.
Error
(),
tt
.
err
)
{
t
.
Errorf
(
"#%d: Expecting: %v..., but got: %v"
,
i
,
tt
.
err
,
err
)
}
}
else
{
if
err
!=
nil
{
t
.
Errorf
(
"#%d: Unexpected error: %v"
,
i
,
err
)
}
}
if
!
reflect
.
DeepEqual
(
tt
.
verified
,
result
)
{
t
.
Errorf
(
"#%d: Expecting: %v, but got: %v"
,
i
,
tt
.
verified
,
result
)
}
if
!
reflect
.
DeepEqual
(
tt
.
userInfo
,
user
)
{
t
.
Errorf
(
"#%d: Expecting: %v, but got: %v"
,
i
,
tt
.
userInfo
,
user
)
}
client
.
Close
()
if
c
.
wantErr
{
t
.
Fatalf
(
"expected error authenticating token"
)
}
if
!
ok
{
if
!
c
.
wantSkip
{
// We don't have any cases where we return (nil, false, nil)
t
.
Fatalf
(
"no error but token not authenticated"
)
}
return
}
if
c
.
wantSkip
{
t
.
Fatalf
(
"expected authenticator to skip token"
)
}
gotUser
:=
got
.
(
*
user
.
DefaultInfo
)
if
!
reflect
.
DeepEqual
(
gotUser
,
c
.
want
)
{
t
.
Fatalf
(
"wanted user=%#v, got=%#v"
,
c
.
want
,
gotUser
)
}
}
func
TestParseTokenClaims
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
name
string
// Note this is missing a lot of configuration options because
// parseTokenClaim doesn't handle:
//
// - 'iss' claim matching issuer URL
// - 'exp' claim having not expired
// - 'sub' claim matching a trusted client id
//
// That logic has coverage in other tests.
issuerURL
string
usernameClaim
string
usernamePrefix
string
groupsClaim
string
groupsPrefix
string
claims
jose
.
Claims
wantUser
*
user
.
DefaultInfo
wantErr
bool
}{
func
TestToken
(
t
*
testing
.
T
)
{
tests
:=
[]
claimsTest
{
{
name
:
"email username
"
,
issuerURL
:
"https://foo.com/"
,
usernameClaim
:
"email
"
,
claims
:
jose
.
Claims
{
"email"
:
"jane.doe@example.com
"
,
"email_verified"
:
true
,
name
:
"token
"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com
"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username
"
,
now
:
func
()
time
.
Time
{
return
now
}
,
},
wantUser
:
&
user
.
DefaultInfo
{
Name
:
"jane.doe@example.com"
,
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"username": "jane",
"exp": %d
}`
,
valid
.
Unix
()),
want
:
&
user
.
DefaultInfo
{
Name
:
"jane"
,
},
},
{
name
:
"no email_verified claim"
,
issuerURL
:
"https://foo.com/"
,
usernameClaim
:
"email"
,
claims
:
jose
.
Claims
{
"email"
:
"jane.doe@example.com"
,
name
:
"no-username"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"exp": %d
}`
,
valid
.
Unix
()),
wantErr
:
true
,
},
{
name
:
"email unverified"
,
issuerURL
:
"https://foo.com/"
,
usernameClaim
:
"email"
,
claims
:
jose
.
Claims
{
"email"
:
"jane.doe@example.com"
,
name
:
"email"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"email"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"email": "jane@example.com",
"email_verified": true,
"exp": %d
}`
,
valid
.
Unix
()),
want
:
&
user
.
DefaultInfo
{
Name
:
"jane@example.com"
,
},
},
{
name
:
"email-not-verified"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"email"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"email": "jane@example.com",
"email_verified": false,
"exp": %d
}`
,
valid
.
Unix
()),
wantErr
:
true
,
},
{
// If "email_verified" isn't present, assume false
name
:
"no-email-verified-claim"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"email"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"email": "jane@example.com",
"exp": %d
}`
,
valid
.
Unix
()),
wantErr
:
true
,
},
{
name
:
"non-email user claim"
,
issuerURL
:
"https://foo.com/"
,
usernameClaim
:
"name"
,
claims
:
jose
.
Claims
{
"name"
:
"janedoe"
,
name
:
"groups"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
GroupsClaim
:
"groups"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
wantUser
:
&
user
.
DefaultInfo
{
Name
:
"janedoe"
,
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"username": "jane",
"groups": ["team1", "team2"],
"exp": %d
}`
,
valid
.
Unix
()),
want
:
&
user
.
DefaultInfo
{
Name
:
"jane"
,
Groups
:
[]
string
{
"team1"
,
"team2"
},
},
},
{
name
:
"groups claim"
,
issuerURL
:
"https://foo.com/"
,
usernameClaim
:
"name"
,
groupsClaim
:
"groups"
,
claims
:
jose
.
Claims
{
"name"
:
"janedoe"
,
"groups"
:
[]
string
{
"foo"
,
"bar"
},
},
wantUser
:
&
user
.
DefaultInfo
{
Name
:
"janedoe"
,
Groups
:
[]
string
{
"foo"
,
"bar"
},
// Groups should be able to be a single string, not just a slice.
name
:
"group-string-claim"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
GroupsClaim
:
"groups"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"username": "jane",
"groups": "team1",
"exp": %d
}`
,
valid
.
Unix
()),
want
:
&
user
.
DefaultInfo
{
Name
:
"jane"
,
Groups
:
[]
string
{
"team1"
},
},
},
{
name
:
"groups claim string"
,
issuerURL
:
"https://foo.com/"
,
usernameClaim
:
"name"
,
groupsClaim
:
"groups"
,
claims
:
jose
.
Claims
{
"name"
:
"janedoe"
,
"groups"
:
"foo"
,
},
wantUser
:
&
user
.
DefaultInfo
{
Name
:
"janedoe"
,
Groups
:
[]
string
{
"foo"
},
// if the groups claim isn't provided, this shouldn't error out
name
:
"no-groups-claim"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
GroupsClaim
:
"groups"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"username": "jane",
"exp": %d
}`
,
valid
.
Unix
()),
want
:
&
user
.
DefaultInfo
{
Name
:
"jane"
,
},
},
{
name
:
"username prefix"
,
issuerURL
:
"https://foo.com/"
,
usernameClaim
:
"name"
,
groupsClaim
:
"groups"
,
usernamePrefix
:
"oidc:"
,
claims
:
jose
.
Claims
{
"name"
:
"janedoe"
,
"groups"
:
[]
string
{
"foo"
,
"bar"
},
},
wantUser
:
&
user
.
DefaultInfo
{
Name
:
"oidc:janedoe"
,
Groups
:
[]
string
{
"foo"
,
"bar"
},
name
:
"invalid-groups-claim"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
GroupsClaim
:
"groups"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"username": "jane",
"groups": 42,
"exp": %d
}`
,
valid
.
Unix
()),
wantErr
:
true
,
},
{
name
:
"username prefix with email"
,
issuerURL
:
"https://foo.com/"
,
usernameClaim
:
"email"
,
groupsClaim
:
"groups"
,
usernamePrefix
:
"oidc:"
,
claims
:
jose
.
Claims
{
"email"
:
"jane.doe@example.com"
,
"email_verified"
:
true
,
"groups"
:
[]
string
{
"foo"
,
"bar"
},
name
:
"invalid-signature"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_2.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"username": "jane",
"exp": %d
}`
,
valid
.
Unix
()),
wantErr
:
true
,
},
{
name
:
"expired"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"username": "jane",
"exp": %d
}`
,
expired
.
Unix
()),
wantErr
:
true
,
},
{
name
:
"invalid-aud"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "not-my-client",
"username": "jane",
"exp": %d
}`
,
valid
.
Unix
()),
wantErr
:
true
,
},
{
// ID tokens may contain multiple audiences:
// https://openid.net/specs/openid-connect-core-1_0.html#IDToken
name
:
"multiple-audiences"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": ["not-my-client", "my-client"],
"azp": "not-my-client",
"username": "jane",
"exp": %d
}`
,
valid
.
Unix
()),
want
:
&
user
.
DefaultInfo
{
Name
:
"jane"
,
},
},
{
name
:
"invalid-issuer"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://example.com",
"aud": "my-client",
"username": "jane",
"exp": %d
}`
,
valid
.
Unix
()),
wantSkip
:
true
,
},
{
name
:
"username-prefix"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
UsernamePrefix
:
"oidc:"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"username": "jane",
"exp": %d
}`
,
valid
.
Unix
()),
want
:
&
user
.
DefaultInfo
{
Name
:
"oidc:jane"
,
},
},
{
name
:
"groups-prefix"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
UsernamePrefix
:
"oidc:"
,
GroupsClaim
:
"groups"
,
GroupsPrefix
:
"groups:"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"username": "jane",
"groups": ["team1", "team2"],
"exp": %d
}`
,
valid
.
Unix
()),
want
:
&
user
.
DefaultInfo
{
Name
:
"oidc:jane"
,
Groups
:
[]
string
{
"groups:team1"
,
"groups:team2"
},
},
},
{
name
:
"invalid-signing-alg"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
now
:
func
()
time
.
Time
{
return
now
},
},
// Correct key but invalid signature algorithm "PS256"
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
PS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"username": "jane",
"exp": %d
}`
,
valid
.
Unix
()),
wantErr
:
true
,
},
{
name
:
"ps256"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
SupportedSigningAlgs
:
[]
string
{
"PS256"
},
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
PS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
PS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"username": "jane",
"exp": %d
}`
,
valid
.
Unix
()),
want
:
&
user
.
DefaultInfo
{
Name
:
"jane"
,
},
},
{
name
:
"es512"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
SupportedSigningAlgs
:
[]
string
{
"ES512"
},
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadECDSAPrivKey
(
t
,
"testdata/ecdsa_2.pem"
,
jose
.
ES512
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadECDSAKey
(
t
,
"testdata/ecdsa_1.pem"
,
jose
.
ES512
),
loadECDSAKey
(
t
,
"testdata/ecdsa_2.pem"
,
jose
.
ES512
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"username": "jane",
"exp": %d
}`
,
valid
.
Unix
()),
want
:
&
user
.
DefaultInfo
{
Name
:
"jane"
,
},
},
{
name
:
"not-https"
,
options
:
Options
{
IssuerURL
:
"http://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
now
:
func
()
time
.
Time
{
return
now
},
},
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
wantInitErr
:
true
,
},
{
name
:
"no-username-claim"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
now
:
func
()
time
.
Time
{
return
now
},
},
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
wantInitErr
:
true
,
},
{
name
:
"invalid-sig-alg"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
SupportedSigningAlgs
:
[]
string
{
"HS256"
},
now
:
func
()
time
.
Time
{
return
now
},
},
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
wantUser
:
&
user
.
DefaultInfo
{
Name
:
"oidc:jane.doe@example.com"
,
Groups
:
[]
string
{
"foo"
,
"bar"
},
wantInitErr
:
true
,
},
}
for
_
,
test
:=
range
tests
{
t
.
Run
(
test
.
name
,
test
.
run
)
}
}
func
TestUnmarshalClaimError
(
t
*
testing
.
T
)
{
// Ensure error strings returned by unmarshaling claims don't include the claim.
const
token
=
"96bb299a-02e9-11e8-8673-54ee7553240e"
payload
:=
fmt
.
Sprintf
(
`{
"token": "%s"
}`
,
token
)
var
c
claims
if
err
:=
json
.
Unmarshal
([]
byte
(
payload
),
&
c
);
err
!=
nil
{
t
.
Fatal
(
err
)
}
var
n
int
err
:=
c
.
unmarshalClaim
(
"token"
,
&
n
)
if
err
==
nil
{
t
.
Fatal
(
"expected error"
)
}
if
strings
.
Contains
(
err
.
Error
(),
token
)
{
t
.
Fatalf
(
"unmarshal error included token"
)
}
}
func
TestUnmarshalClaim
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
name
string
claims
string
do
func
(
claims
)
(
interface
{},
error
)
want
interface
{}
wantErr
bool
}{
{
name
:
"string claim"
,
claims
:
`{"aud":"foo"}`
,
do
:
func
(
c
claims
)
(
interface
{},
error
)
{
var
s
string
err
:=
c
.
unmarshalClaim
(
"aud"
,
&
s
)
return
s
,
err
},
want
:
"foo"
,
},
{
name
:
"mismatched types"
,
claims
:
`{"aud":"foo"}`
,
do
:
func
(
c
claims
)
(
interface
{},
error
)
{
var
n
int
err
:=
c
.
unmarshalClaim
(
"aud"
,
&
n
)
return
n
,
err
},
wantErr
:
true
,
},
{
name
:
"groups prefix"
,
issuerURL
:
"https://foo.com/"
,
usernameClaim
:
"name"
,
groupsClaim
:
"groups"
,
groupsPrefix
:
"oidc:"
,
claims
:
jose
.
Claims
{
"name"
:
"janedoe"
,
"groups"
:
[]
string
{
"foo"
,
"bar"
},
},
wantUser
:
&
user
.
DefaultInfo
{
Name
:
"janedoe"
,
Groups
:
[]
string
{
"oidc:foo"
,
"oidc:bar"
},
name
:
"bool claim"
,
claims
:
`{"email":"foo@coreos.com","email_verified":true}`
,
do
:
func
(
c
claims
)
(
interface
{},
error
)
{
var
verified
bool
err
:=
c
.
unmarshalClaim
(
"email_verified"
,
&
verified
)
return
verified
,
err
},
want
:
true
,
},
{
name
:
"username and groups prefix"
,
issuerURL
:
"https://foo.com/"
,
usernameClaim
:
"name"
,
groupsClaim
:
"groups"
,
usernamePrefix
:
"oidc-user:"
,
groupsPrefix
:
"oidc:"
,
claims
:
jose
.
Claims
{
"name"
:
"janedoe"
,
"groups"
:
[]
string
{
"foo"
,
"bar"
},
},
wantUser
:
&
user
.
DefaultInfo
{
Name
:
"oidc-user:janedoe"
,
Groups
:
[]
string
{
"oidc:foo"
,
"oidc:bar"
},
name
:
"strings claim"
,
claims
:
`{"groups":["a","b","c"]}`
,
do
:
func
(
c
claims
)
(
interface
{},
error
)
{
var
groups
[]
string
err
:=
c
.
unmarshalClaim
(
"groups"
,
&
groups
)
return
groups
,
err
},
want
:
[]
string
{
"a"
,
"b"
,
"c"
},
},
}
for
_
,
test
:=
range
tests
{
t
.
Run
(
test
.
name
,
func
(
t
*
testing
.
T
)
{
o
:=
OIDCAuthenticator
{
issuerURL
:
test
.
issuerURL
,
usernameClaim
:
test
.
usernameClaim
,
usernamePrefix
:
test
.
usernamePrefix
,
groupsClaim
:
test
.
groupsClaim
,
groupsPrefix
:
test
.
groupsPrefix
,
var
c
claims
if
err
:=
json
.
Unmarshal
([]
byte
(
test
.
claims
),
&
c
);
err
!=
nil
{
t
.
Fatal
(
err
)
}
u
,
ok
,
err
:=
o
.
parseTokenClaims
(
test
.
claims
)
got
,
err
:=
test
.
do
(
c
)
if
err
!=
nil
{
if
!
test
.
wantErr
{
t
.
Errorf
(
"failed to authenticate user: %v"
,
err
)
if
test
.
wantErr
{
return
}
return
t
.
Fatalf
(
"unexpected error: %v"
,
err
)
}
if
test
.
wantErr
{
t
.
Fatalf
(
"expected
authentication to fail
"
)
t
.
Fatalf
(
"expected
error
"
)
}
if
!
ok
{
// We don't have any cases today when the claims can return
// no error with an unauthenticated signal.
//
// In the future we might.
t
.
Fatalf
(
"user wasn't authenticated"
)
}
got
:=
&
user
.
DefaultInfo
{
Name
:
u
.
GetName
(),
UID
:
u
.
GetUID
(),
Groups
:
u
.
GetGroups
(),
Extra
:
u
.
GetExtra
(),
}
if
!
reflect
.
DeepEqual
(
got
,
test
.
wantUser
)
{
t
.
Errorf
(
"wanted user=%#v, got=%#v"
,
test
.
wantUser
,
got
)
if
!
reflect
.
DeepEqual
(
got
,
test
.
want
)
{
t
.
Errorf
(
"wanted=%#v, got=%#v"
,
test
.
want
,
got
)
}
})
}
...
...
staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/oidc/testdata/gen.sh
0 → 100755
View file @
48c6d1ab
#!/bin/bash -e
# Copyright 2018 The Kubernetes 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.
rm
*
.pem
for
N
in
`
seq
1 3
`
;
do
ssh-keygen
-t
rsa
-b
2048
-f
rsa_
$N
.pem
-N
''
done
for
N
in
`
seq
1 3
`
;
do
ssh-keygen
-t
ecdsa
-b
521
-f
ecdsa_
$N
.pem
-N
''
done
rm
*
.pub
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment