Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[release-1.32] Fix local password validation when bind-address is set #11610

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pkg/nodepassword/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func GetNodeAuthValidator(ctx context.Context, control *config.Control) NodeAuth
// get client address, to see if deferred node password validation should be allowed when the apiserver
// is not available. Deferred password validation is only allowed for requests from the local client.
client, _, _ := net.SplitHostPort(req.RemoteAddr)
isLocal := client == "127.0.0.1" || client == "::1"
isLocal := client == "127.0.0.1" || client == "::1" || client == control.BindAddress

if secretClient == nil || nodeClient == nil {
if runtime.Core != nil {
Expand Down
264 changes: 254 additions & 10 deletions pkg/server/handlers/handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"crypto/tls"
"crypto/x509"
"io"
"net"
"net/http"
"net/http/httptest"
"os"
Expand Down Expand Up @@ -189,7 +190,7 @@ func Test_UnitHandlers(t *testing.T) {
req.Header.Add("k3s-Node-Name", control.ServerNodeName)
req.Header.Add("k3s-Node-Password", "password")
req.SetBasicAuth("node", control.AgentToken)
withLocalClient(req)
withClientAddress(req, control.BindAddressOrLoopback(false, false))
},
match: func(_ *config.Control) types.GomegaMatcher {
return And(
Expand All @@ -208,7 +209,7 @@ func Test_UnitHandlers(t *testing.T) {
Organization: []string{user.NodesGroup},
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
})
withLocalClient(req)
withClientAddress(req, control.BindAddressOrLoopback(false, false))
},
match: func(_ *config.Control) types.GomegaMatcher {
return And(
Expand Down Expand Up @@ -305,7 +306,7 @@ func Test_UnitHandlers(t *testing.T) {
prepare: func(control *config.Control, req *http.Request) {
req.Header.Add("k3s-Node-Name", control.ServerNodeName)
req.Header.Add("k3s-Node-Password", "invalid-password")
withLocalClient(req)
withClientAddress(req, control.BindAddressOrLoopback(false, false))
withCertificateRequest(req)
req.SetBasicAuth("node", control.AgentToken)
},
Expand All @@ -318,7 +319,250 @@ func Test_UnitHandlers(t *testing.T) {
prepare: func(control *config.Control, req *http.Request) {
req.Header.Add("k3s-Node-Name", control.ServerNodeName)
req.Header.Add("k3s-Node-Password", "invalid-password")
withLocalClient(req)
withClientAddress(req, control.BindAddressOrLoopback(false, false))
withCertificateRequest(req)
withNewClientCert(req, control.Runtime.ClientCA, control.Runtime.ClientCAKey, control.Runtime.ClientKubeletKey, certutil.Config{
CommonName: "system:node:" + control.ServerNodeName,
Organization: []string{user.NodesGroup},
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
})
},
match: func(_ *config.Control) types.GomegaMatcher {
return HaveHTTPStatus(http.StatusForbidden)
},
},
),
},
},
},
{
//*** tests with runtime core not ready and bind address set ***
name: "no runtime core with bind-address",
controlFunc: func(t *testing.T) (*config.Control, context.CancelFunc) {
control, cancel := getCorelessControl(t)
control.BindAddress = "192.0.2.100"
return control, cancel
},
paths: []pathTest{
//** paths accessible with node cert or agent token, and specific headers **
{
method: http.MethodGet,
path: "/v1-k3s/serving-kubelet.crt",
subs: append(genericFailures,
sub{
name: "valid basic but missing headers",
prepare: func(control *config.Control, req *http.Request) {
req.SetBasicAuth("node", control.AgentToken)
},
match: func(_ *config.Control) types.GomegaMatcher {
return HaveHTTPStatus(http.StatusBadRequest)
},
},
sub{
name: "valid cert but missing headers",
prepare: func(control *config.Control, req *http.Request) {
withNewClientCert(req, control.Runtime.ClientCA, control.Runtime.ClientCAKey, control.Runtime.ClientKubeletKey, certutil.Config{
CommonName: "system:node:" + control.ServerNodeName,
Organization: []string{user.NodesGroup},
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
})
},
match: func(_ *config.Control) types.GomegaMatcher {
return HaveHTTPStatus(http.StatusBadRequest)
},
},
sub{
name: "valid cert but wrong node name",
prepare: func(control *config.Control, req *http.Request) {
req.Header.Add("k3s-Node-Name", control.ServerNodeName)
req.Header.Add("k3s-Node-Password", "password")
withNewClientCert(req, control.Runtime.ClientCA, control.Runtime.ClientCAKey, control.Runtime.ClientKubeletKey, certutil.Config{
CommonName: "system:node:k3s-agent-1",
Organization: []string{user.NodesGroup},
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
})
},
match: func(_ *config.Control) types.GomegaMatcher {
return HaveHTTPStatus(http.StatusBadRequest)
},
},
sub{
name: "valid cert but nonexistent node",
prepare: func(control *config.Control, req *http.Request) {
req.Header.Add("k3s-Node-Name", "nonexistent")
req.Header.Add("k3s-Node-Password", "password")
withNewClientCert(req, control.Runtime.ClientCA, control.Runtime.ClientCAKey, control.Runtime.ClientKubeletKey, certutil.Config{
CommonName: "system:node:nonexistent",
Organization: []string{user.NodesGroup},
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
})
},
match: func(_ *config.Control) types.GomegaMatcher {
return HaveHTTPStatus(http.StatusServiceUnavailable)
},
},
sub{
name: "valid basic legacy key",
prepare: func(control *config.Control, req *http.Request) {
req.Header.Add("k3s-Node-Name", control.ServerNodeName)
req.Header.Add("k3s-Node-Password", "password")
req.SetBasicAuth("node", control.AgentToken)
},
match: func(_ *config.Control) types.GomegaMatcher {
return HaveHTTPStatus(http.StatusServiceUnavailable)
},
},
sub{
name: "valid cert legacy key",
prepare: func(control *config.Control, req *http.Request) {
req.Header.Add("k3s-Node-Name", control.ServerNodeName)
req.Header.Add("k3s-Node-Password", "password")
withNewClientCert(req, control.Runtime.ClientCA, control.Runtime.ClientCAKey, control.Runtime.ClientKubeletKey, certutil.Config{
CommonName: "system:node:" + control.ServerNodeName,
Organization: []string{user.NodesGroup},
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
})
},
match: func(_ *config.Control) types.GomegaMatcher {
return HaveHTTPStatus(http.StatusServiceUnavailable)
},
},
sub{
name: "valid basic legacy key deferred local password",
prepare: func(control *config.Control, req *http.Request) {
req.Header.Add("k3s-Node-Name", control.ServerNodeName)
req.Header.Add("k3s-Node-Password", "password")
req.SetBasicAuth("node", control.AgentToken)
withClientAddress(req, control.BindAddressOrLoopback(false, false))
},
match: func(_ *config.Control) types.GomegaMatcher {
return And(
HaveHTTPStatus(http.StatusOK),
HaveHTTPBody(ContainSubstring("PRIVATE KEY")),
)
},
},
sub{
name: "valid cert legacy key deferred local password",
prepare: func(control *config.Control, req *http.Request) {
req.Header.Add("k3s-Node-Name", control.ServerNodeName)
req.Header.Add("k3s-Node-Password", "password")
withNewClientCert(req, control.Runtime.ClientCA, control.Runtime.ClientCAKey, control.Runtime.ClientKubeletKey, certutil.Config{
CommonName: "system:node:" + control.ServerNodeName,
Organization: []string{user.NodesGroup},
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
})
withClientAddress(req, control.BindAddressOrLoopback(false, false))
},
match: func(_ *config.Control) types.GomegaMatcher {
return And(
HaveHTTPStatus(http.StatusOK),
HaveHTTPBody(ContainSubstring("PRIVATE KEY")),
)
},
},
sub{
name: "valid basic different node",
prepare: func(control *config.Control, req *http.Request) {
req.Header.Add("k3s-Node-Name", "k3s-agent-1")
req.Header.Add("k3s-Node-Password", "password")
req.SetBasicAuth("node", control.AgentToken)
},
match: func(_ *config.Control) types.GomegaMatcher {
return HaveHTTPStatus(http.StatusServiceUnavailable)
},
},
sub{
name: "valid basic bad node password",
prepare: func(control *config.Control, req *http.Request) {
req.Header.Add("k3s-Node-Name", "k3s-agent-1")
req.Header.Add("k3s-Node-Password", "invalid-password")
req.SetBasicAuth("node", control.AgentToken)
},
match: func(_ *config.Control) types.GomegaMatcher {
return HaveHTTPStatus(http.StatusServiceUnavailable)
},
},
),
}, {
method: http.MethodPost,
path: "/v1-k3s/serving-kubelet.crt",
subs: append(genericFailures,
sub{
name: "valid basic client key but bad password",
prepare: func(control *config.Control, req *http.Request) {
req.Header.Add("k3s-Node-Name", control.ServerNodeName)
req.Header.Add("k3s-Node-Password", "password")
withCertificateRequest(req)
req.SetBasicAuth("node", control.AgentToken)
},
match: func(_ *config.Control) types.GomegaMatcher {
return HaveHTTPStatus(http.StatusServiceUnavailable)
},
},
sub{
name: "valid cert client key",
prepare: func(control *config.Control, req *http.Request) {
req.Header.Add("k3s-Node-Name", control.ServerNodeName)
req.Header.Add("k3s-Node-Password", "password")
withCertificateRequest(req)
withNewClientCert(req, control.Runtime.ClientCA, control.Runtime.ClientCAKey, control.Runtime.ClientKubeletKey, certutil.Config{
CommonName: "system:node:" + control.ServerNodeName,
Organization: []string{user.NodesGroup},
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
})
},
match: func(_ *config.Control) types.GomegaMatcher {
return HaveHTTPStatus(http.StatusServiceUnavailable)
},
},
sub{
name: "valid basic client key but bad password",
prepare: func(control *config.Control, req *http.Request) {
req.Header.Add("k3s-Node-Name", control.ServerNodeName)
req.Header.Add("k3s-Node-Password", "invalid-password")
withCertificateRequest(req)
req.SetBasicAuth("node", control.AgentToken)
},
match: func(_ *config.Control) types.GomegaMatcher {
return HaveHTTPStatus(http.StatusServiceUnavailable)
},
},
sub{
name: "valid cert client key but bad password",
prepare: func(control *config.Control, req *http.Request) {
req.Header.Add("k3s-Node-Name", control.ServerNodeName)
req.Header.Add("k3s-Node-Password", "invalid-password")
withCertificateRequest(req)
withNewClientCert(req, control.Runtime.ClientCA, control.Runtime.ClientCAKey, control.Runtime.ClientKubeletKey, certutil.Config{
CommonName: "system:node:" + control.ServerNodeName,
Organization: []string{user.NodesGroup},
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
})
},
match: func(_ *config.Control) types.GomegaMatcher {
return HaveHTTPStatus(http.StatusServiceUnavailable)
},
},
sub{
name: "valid basic client key but bad deferred local password",
prepare: func(control *config.Control, req *http.Request) {
req.Header.Add("k3s-Node-Name", control.ServerNodeName)
req.Header.Add("k3s-Node-Password", "invalid-password")
withClientAddress(req, control.BindAddressOrLoopback(false, false))
withCertificateRequest(req)
req.SetBasicAuth("node", control.AgentToken)
},
match: func(_ *config.Control) types.GomegaMatcher {
return HaveHTTPStatus(http.StatusForbidden)
},
},
sub{
name: "valid cert client key but bad deferred local password",
prepare: func(control *config.Control, req *http.Request) {
req.Header.Add("k3s-Node-Name", control.ServerNodeName)
req.Header.Add("k3s-Node-Password", "invalid-password")
withClientAddress(req, control.BindAddressOrLoopback(false, false))
withCertificateRequest(req)
withNewClientCert(req, control.Runtime.ClientCA, control.Runtime.ClientCAKey, control.Runtime.ClientKubeletKey, certutil.Config{
CommonName: "system:node:" + control.ServerNodeName,
Expand Down Expand Up @@ -428,7 +672,7 @@ func Test_UnitHandlers(t *testing.T) {
req.Header.Add("k3s-Node-Name", control.ServerNodeName)
req.Header.Add("k3s-Node-Password", "password")
req.SetBasicAuth("node", control.AgentToken)
withLocalClient(req)
withClientAddress(req, control.BindAddressOrLoopback(false, false))
},
match: func(_ *config.Control) types.GomegaMatcher {
return And(
Expand All @@ -447,7 +691,7 @@ func Test_UnitHandlers(t *testing.T) {
Organization: []string{user.NodesGroup},
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
})
withLocalClient(req)
withClientAddress(req, control.BindAddressOrLoopback(false, false))
},
match: func(_ *config.Control) types.GomegaMatcher {
return And(
Expand Down Expand Up @@ -544,7 +788,7 @@ func Test_UnitHandlers(t *testing.T) {
prepare: func(control *config.Control, req *http.Request) {
req.Header.Add("k3s-Node-Name", control.ServerNodeName)
req.Header.Add("k3s-Node-Password", "invalid-password")
withLocalClient(req)
withClientAddress(req, control.BindAddressOrLoopback(false, false))
withCertificateRequest(req)
req.SetBasicAuth("node", control.AgentToken)
},
Expand All @@ -560,7 +804,7 @@ func Test_UnitHandlers(t *testing.T) {
prepare: func(control *config.Control, req *http.Request) {
req.Header.Add("k3s-Node-Name", control.ServerNodeName)
req.Header.Add("k3s-Node-Password", "invalid-password")
withLocalClient(req)
withClientAddress(req, control.BindAddressOrLoopback(false, false))
withCertificateRequest(req)
withNewClientCert(req, control.Runtime.ClientCA, control.Runtime.ClientCAKey, control.Runtime.ClientKubeletKey, certutil.Config{
CommonName: "system:node:" + control.ServerNodeName,
Expand Down Expand Up @@ -1493,6 +1737,6 @@ func withCertificateRequest(req *http.Request) {
req.Body = io.NopCloser(bytes.NewReader(csr))
}

func withLocalClient(req *http.Request) {
req.RemoteAddr = "127.0.0.1:0"
func withClientAddress(req *http.Request, address string) {
req.RemoteAddr = net.JoinHostPort(address, "1234")
}
Loading