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
87f9c4ab
Commit
87f9c4ab
authored
Dec 24, 2022
by
Brad Davidson
Committed by
Brad Davidson
Feb 07, 2023
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Ensure that node exists when using node auth
Signed-off-by:
Brad Davidson
<
brad.davidson@rancher.com
>
parent
992e6499
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
77 additions
and
33 deletions
+77
-33
router.go
pkg/server/router.go
+77
-33
No files found.
pkg/server/router.go
View file @
87f9c4ab
...
...
@@ -32,13 +32,19 @@ import (
"k8s.io/apimachinery/pkg/util/json"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
"k8s.io/apiserver/pkg/endpoints/request"
bootstrapapi
"k8s.io/cluster-bootstrap/token/api"
"k8s.io/kubernetes/pkg/auth/nodeidentifier"
)
const
(
staticURL
=
"/static/"
)
var
(
identifier
=
nodeidentifier
.
NewDefaultNodeIdentifier
()
)
func
router
(
ctx
context
.
Context
,
config
*
Config
,
cfg
*
cmds
.
Server
)
http
.
Handler
{
serverConfig
:=
&
config
.
ControlConfig
nodeAuth
:=
passwordBootstrap
(
ctx
,
config
)
...
...
@@ -143,18 +149,27 @@ func cacerts(serverCA string) http.Handler {
})
}
func
getNodeInfo
(
req
*
http
.
Request
)
(
string
,
string
,
error
)
{
func
getNodeInfo
(
req
*
http
.
Request
)
(
*
nodeInfo
,
error
)
{
user
,
ok
:=
request
.
UserFrom
(
req
.
Context
())
if
!
ok
{
return
nil
,
errors
.
New
(
"auth user not set"
)
}
nodeName
:=
req
.
Header
.
Get
(
version
.
Program
+
"-Node-Name"
)
if
nodeName
==
""
{
return
""
,
""
,
errors
.
New
(
"node name not set"
)
return
nil
,
errors
.
New
(
"node name not set"
)
}
nodePassword
:=
req
.
Header
.
Get
(
version
.
Program
+
"-Node-Password"
)
if
nodePassword
==
""
{
return
""
,
""
,
errors
.
New
(
"node password not set"
)
return
nil
,
errors
.
New
(
"node password not set"
)
}
return
strings
.
ToLower
(
nodeName
),
nodePassword
,
nil
return
&
nodeInfo
{
Name
:
strings
.
ToLower
(
nodeName
),
Password
:
nodePassword
,
User
:
user
,
},
nil
}
func
getCACertAndKeys
(
caCertFile
,
caKeyFile
,
signingKeyFile
string
)
([]
*
x509
.
Certificate
,
crypto
.
Signer
,
crypto
.
Signer
,
error
)
{
...
...
@@ -398,43 +413,63 @@ func sendError(err error, resp http.ResponseWriter, status ...int) {
// nodePassBootstrapper returns a node name, or http error code and error
type
nodePassBootstrapper
func
(
req
*
http
.
Request
)
(
string
,
int
,
error
)
// nodeInfo contains information on the requesting node, derived from auth creds
// and request headers.
type
nodeInfo
struct
{
Name
string
Password
string
User
user
.
Info
}
func
passwordBootstrap
(
ctx
context
.
Context
,
config
*
Config
)
nodePassBootstrapper
{
runtime
:=
config
.
ControlConfig
.
Runtime
deferredNodes
:=
map
[
string
]
bool
{}
var
secretClient
coreclient
.
SecretClient
var
nodeClient
coreclient
.
NodeClient
var
mu
sync
.
Mutex
return
nodePassBootstrapper
(
func
(
req
*
http
.
Request
)
(
string
,
int
,
error
)
{
node
Name
,
nodePassword
,
err
:=
getNodeInfo
(
req
)
node
,
err
:=
getNodeInfo
(
req
)
if
err
!=
nil
{
return
""
,
http
.
StatusBadRequest
,
err
}
if
secretClient
==
nil
{
nodeName
,
isNodeAuth
:=
identifier
.
NodeIdentity
(
node
.
User
)
if
isNodeAuth
&&
nodeName
!=
node
.
Name
{
return
""
,
http
.
StatusBadRequest
,
errors
.
New
(
"header node name does not match auth node name"
)
}
if
secretClient
==
nil
||
nodeClient
==
nil
{
if
runtime
.
Core
!=
nil
{
// initialize the client if we can
secretClient
=
runtime
.
Core
.
Core
()
.
V1
()
.
Secret
()
}
else
if
nodeName
==
os
.
Getenv
(
"NODE_NAME"
)
{
// or verify the password locally and ensure a secret later
return
verifyLocalPassword
(
ctx
,
config
,
&
mu
,
deferredNodes
,
nodeName
,
nodePassword
)
}
else
if
config
.
ControlConfig
.
DisableAPIServer
{
// or defer node password verification until an apiserver joins the cluster
return
verifyRemotePassword
(
ctx
,
config
,
&
mu
,
deferredNodes
,
nodeName
,
nodePassword
)
nodeClient
=
runtime
.
Core
.
Core
()
.
V1
()
.
Node
()
}
else
if
node
.
Name
==
os
.
Getenv
(
"NODE_NAME"
)
{
// If we're verifying our own password, verify it locally and ensure a secret later.
return
verifyLocalPassword
(
ctx
,
config
,
&
mu
,
deferredNodes
,
node
)
}
else
if
config
.
ControlConfig
.
DisableAPIServer
&&
!
isNodeAuth
{
// If we're running on an etcd-only node, and the request didn't use Node Identity auth,
// defer node password verification until an apiserver joins the cluster.
return
verifyRemotePassword
(
ctx
,
config
,
&
mu
,
deferredNodes
,
node
)
}
else
{
//
or reject the request until the core is ready
//
Otherwise, reject the request until the core is ready.
return
""
,
http
.
StatusServiceUnavailable
,
errors
.
New
(
"runtime core not ready"
)
}
}
if
err
:=
nodepassword
.
Ensure
(
secretClient
,
nodeName
,
nodePassword
);
err
!=
nil
{
if
err
:=
verifyNode
(
ctx
,
nodeClient
,
node
);
err
!=
nil
{
return
""
,
http
.
StatusUnauthorized
,
err
}
if
err
:=
nodepassword
.
Ensure
(
secretClient
,
node
.
Name
,
node
.
Password
);
err
!=
nil
{
return
""
,
http
.
StatusForbidden
,
err
}
return
nodeName
,
http
.
StatusOK
,
nil
return
node
.
Name
,
http
.
StatusOK
,
nil
})
}
func
verifyLocalPassword
(
ctx
context
.
Context
,
config
*
Config
,
mu
*
sync
.
Mutex
,
deferredNodes
map
[
string
]
bool
,
node
Name
,
nodePassword
string
)
(
string
,
int
,
error
)
{
func
verifyLocalPassword
(
ctx
context
.
Context
,
config
*
Config
,
mu
*
sync
.
Mutex
,
deferredNodes
map
[
string
]
bool
,
node
*
nodeInfo
)
(
string
,
int
,
error
)
{
// use same password file location that the agent creates
nodePasswordRoot
:=
"/"
if
config
.
ControlConfig
.
Rootless
{
...
...
@@ -449,36 +484,45 @@ func verifyLocalPassword(ctx context.Context, config *Config, mu *sync.Mutex, de
}
password
:=
strings
.
TrimSpace
(
string
(
passBytes
))
if
password
!=
nodePassword
{
return
""
,
http
.
StatusForbidden
,
errors
.
Wrapf
(
err
,
"unable to verify local password for node '%s'"
,
nodeName
)
if
password
!=
node
.
Password
{
return
""
,
http
.
StatusForbidden
,
errors
.
Wrapf
(
err
,
"unable to verify local password for node '%s'"
,
node
.
Name
)
}
mu
.
Lock
()
defer
mu
.
Unlock
()
if
_
,
ok
:=
deferredNodes
[
nodeName
];
!
ok
{
deferredNodes
[
nodeName
]
=
true
go
ensureSecret
(
ctx
,
config
,
node
Name
,
nodePassword
)
logrus
.
Debugf
(
"Password verified locally for node '%s'"
,
nodeName
)
if
_
,
ok
:=
deferredNodes
[
node
.
Name
];
!
ok
{
deferredNodes
[
node
.
Name
]
=
true
go
ensureSecret
(
ctx
,
config
,
node
)
logrus
.
Debugf
(
"Password verified locally for node '%s'"
,
node
.
Name
)
}
return
nodeName
,
http
.
StatusOK
,
nil
return
node
.
Name
,
http
.
StatusOK
,
nil
}
func
verifyRemotePassword
(
ctx
context
.
Context
,
config
*
Config
,
mu
*
sync
.
Mutex
,
deferredNodes
map
[
string
]
bool
,
node
Name
,
nodePassword
string
)
(
string
,
int
,
error
)
{
func
verifyRemotePassword
(
ctx
context
.
Context
,
config
*
Config
,
mu
*
sync
.
Mutex
,
deferredNodes
map
[
string
]
bool
,
node
*
nodeInfo
)
(
string
,
int
,
error
)
{
mu
.
Lock
()
defer
mu
.
Unlock
()
if
_
,
ok
:=
deferredNodes
[
nodeName
];
!
ok
{
deferredNodes
[
nodeName
]
=
true
go
ensureSecret
(
ctx
,
config
,
node
Name
,
nodePassword
)
logrus
.
Debugf
(
"Password verification deferred for node '%s'"
,
nodeName
)
if
_
,
ok
:=
deferredNodes
[
node
.
Name
];
!
ok
{
deferredNodes
[
node
.
Name
]
=
true
go
ensureSecret
(
ctx
,
config
,
node
)
logrus
.
Debugf
(
"Password verification deferred for node '%s'"
,
node
.
Name
)
}
return
nodeName
,
http
.
StatusOK
,
nil
return
node
.
Name
,
http
.
StatusOK
,
nil
}
func
verifyNode
(
ctx
context
.
Context
,
nodeClient
coreclient
.
NodeClient
,
node
*
nodeInfo
)
error
{
if
nodeName
,
isNodeAuth
:=
identifier
.
NodeIdentity
(
node
.
User
);
isNodeAuth
{
if
_
,
err
:=
nodeClient
.
Get
(
nodeName
,
metav1
.
GetOptions
{});
err
!=
nil
{
return
errors
.
Wrap
(
err
,
"unable to verify node identity"
)
}
}
return
nil
}
func
ensureSecret
(
ctx
context
.
Context
,
config
*
Config
,
node
Name
,
nodePassword
string
)
{
func
ensureSecret
(
ctx
context
.
Context
,
config
*
Config
,
node
*
nodeInfo
)
{
runtime
:=
config
.
ControlConfig
.
Runtime
for
{
select
{
...
...
@@ -486,10 +530,10 @@ func ensureSecret(ctx context.Context, config *Config, nodeName, nodePassword st
return
case
<-
time
.
After
(
1
*
time
.
Second
)
:
if
runtime
.
Core
!=
nil
{
logrus
.
Debugf
(
"Runtime core has become available, ensuring password secret for node '%s'"
,
nodeName
)
logrus
.
Debugf
(
"Runtime core has become available, ensuring password secret for node '%s'"
,
node
.
Name
)
secretClient
:=
runtime
.
Core
.
Core
()
.
V1
()
.
Secret
()
if
err
:=
nodepassword
.
Ensure
(
secretClient
,
node
Name
,
node
Password
);
err
!=
nil
{
logrus
.
Warnf
(
"Error ensuring node password secret for pre-validated node '%s': %v"
,
nodeName
,
err
)
if
err
:=
nodepassword
.
Ensure
(
secretClient
,
node
.
Name
,
node
.
Password
);
err
!=
nil
{
logrus
.
Warnf
(
"Error ensuring node password secret for pre-validated node '%s': %v"
,
node
.
Name
,
err
)
}
return
}
...
...
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