Add client certificate authentication support to core Authenticator

This is required to make the websocket tunnel server functional on
etcd-only nodes, and will save some code on the RKE2 side once pulled
through.

Signed-off-by: Brad Davidson <brad.davidson@rancher.com>
(cherry picked from commit af0b496ef3)
pull/5480/head
Brad Davidson 2022-03-28 13:45:39 -07:00 committed by Brad Davidson
parent ba7eb6c121
commit 2b39bf2340
6 changed files with 75 additions and 38 deletions

View File

@ -0,0 +1,56 @@
package authenticator
import (
"strings"
"github.com/rancher/k3s/pkg/authenticator/basicauth"
"github.com/rancher/k3s/pkg/authenticator/passwordfile"
"k8s.io/apiserver/pkg/authentication/authenticator"
"k8s.io/apiserver/pkg/authentication/group"
"k8s.io/apiserver/pkg/authentication/request/union"
"k8s.io/apiserver/pkg/authentication/request/x509"
"k8s.io/apiserver/pkg/server/dynamiccertificates"
)
func FromArgs(args []string) (authenticator.Request, error) {
var authenticators []authenticator.Request
basicFile := getArg("--basic-auth-file", args)
if basicFile != "" {
basicAuthenticator, err := passwordfile.NewCSV(basicFile)
if err != nil {
return nil, err
}
authenticators = append(authenticators, basicauth.New(basicAuthenticator))
}
clientCA := getArg("--client-ca-file", args)
if clientCA != "" {
ca, err := dynamiccertificates.NewDynamicCAContentFromFile("client-ca", clientCA)
if err != nil {
return nil, err
}
authenticators = append(authenticators, x509.NewDynamic(ca.VerifyOptions, x509.CommonNameUserConversion))
}
return Combine(authenticators...), nil
}
func getArg(key string, args []string) string {
for _, arg := range args {
if !strings.HasPrefix(arg, key) {
continue
}
return strings.SplitN(arg, "=", 2)[1]
}
return ""
}
func Combine(auths ...authenticator.Request) authenticator.Request {
var authenticators []authenticator.Request
for _, auth := range auths {
if auth != nil {
authenticators = append(authenticators, auth)
}
}
return group.NewAuthenticatedGroupAdder(union.New(authenticators...))
}

View File

@ -20,17 +20,16 @@ import (
"errors" "errors"
"net/http" "net/http"
localAuthenticator "github.com/rancher/k3s/pkg/authenticator"
"k8s.io/apiserver/pkg/authentication/authenticator" "k8s.io/apiserver/pkg/authentication/authenticator"
) )
// Authenticator authenticates requests using basic auth // Authenticator authenticates requests using basic auth
type Authenticator struct { type Authenticator struct {
auth localAuthenticator.Password auth Password
} }
// New returns a request authenticator that validates credentials using the provided password authenticator // New returns a request authenticator that validates credentials using the provided password authenticator
func New(auth localAuthenticator.Password) *Authenticator { func New(auth Password) *Authenticator {
return &Authenticator{auth} return &Authenticator{auth}
} }

View File

@ -11,7 +11,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package authenticator package basicauth
import ( import (
"context" "context"

View File

@ -1,30 +0,0 @@
package control
import (
"github.com/rancher/k3s/pkg/authenticator/basicauth"
"github.com/rancher/k3s/pkg/authenticator/passwordfile"
"k8s.io/apiserver/pkg/authentication/authenticator"
"k8s.io/apiserver/pkg/authentication/group"
"k8s.io/apiserver/pkg/authentication/request/union"
)
func basicAuthenticator(basicAuthFile string) (authenticator.Request, error) {
if basicAuthFile == "" {
return nil, nil
}
basicAuthenticator, err := passwordfile.NewCSV(basicAuthFile)
if err != nil {
return nil, err
}
return basicauth.New(basicAuthenticator), nil
}
func combineAuthenticators(auths ...authenticator.Request) authenticator.Request {
var authenticators []authenticator.Request
for _, auth := range auths {
if auth != nil {
authenticators = append(authenticators, auth)
}
}
return group.NewAuthenticatedGroupAdder(union.New(authenticators...))
}

View File

@ -12,6 +12,7 @@ import (
"time" "time"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/rancher/k3s/pkg/authenticator"
"github.com/rancher/k3s/pkg/cluster" "github.com/rancher/k3s/pkg/cluster"
"github.com/rancher/k3s/pkg/daemons/config" "github.com/rancher/k3s/pkg/daemons/config"
"github.com/rancher/k3s/pkg/daemons/control/deps" "github.com/rancher/k3s/pkg/daemons/control/deps"
@ -50,11 +51,15 @@ func Server(ctx context.Context, cfg *config.Control) error {
cfg.Runtime.Tunnel = setupTunnel() cfg.Runtime.Tunnel = setupTunnel()
proxyutil.DisableProxyHostnameCheck = true proxyutil.DisableProxyHostnameCheck = true
basicAuth, err := basicAuthenticator(cfg.Runtime.PasswdFile) authArgs := []string{
"--basic-auth-file=" + cfg.Runtime.PasswdFile,
"--client-ca-file=" + cfg.Runtime.ClientCA,
}
auth, err := authenticator.FromArgs(authArgs)
if err != nil { if err != nil {
return err return err
} }
cfg.Runtime.Authenticator = basicAuth cfg.Runtime.Authenticator = auth
if !cfg.DisableAPIServer { if !cfg.DisableAPIServer {
go waitForAPIServerHandlers(ctx, cfg.Runtime) go waitForAPIServerHandlers(ctx, cfg.Runtime)
@ -377,7 +382,7 @@ func waitForAPIServerHandlers(ctx context.Context, runtime *config.ControlRuntim
if err != nil { if err != nil {
logrus.Fatalf("Failed to get request handlers from apiserver: %v", err) logrus.Fatalf("Failed to get request handlers from apiserver: %v", err)
} }
runtime.Authenticator = combineAuthenticators(runtime.Authenticator, auth) runtime.Authenticator = authenticator.Combine(runtime.Authenticator, auth)
runtime.APIServer = handler runtime.APIServer = handler
} }

View File

@ -9,13 +9,20 @@ import (
"github.com/rancher/remotedialer" "github.com/rancher/remotedialer"
"github.com/rancher/wrangler/pkg/kv" "github.com/rancher/wrangler/pkg/kv"
"github.com/sirupsen/logrus"
utilnet "k8s.io/apimachinery/pkg/util/net" utilnet "k8s.io/apimachinery/pkg/util/net"
"k8s.io/apiserver/pkg/endpoints/request" "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/kubernetes/cmd/kube-apiserver/app" "k8s.io/kubernetes/cmd/kube-apiserver/app"
) )
func loggingErrorWriter(rw http.ResponseWriter, req *http.Request, code int, err error) {
logrus.Debugf("remoteDialer error: %d %v", code, err)
rw.WriteHeader(code)
rw.Write([]byte(err.Error()))
}
func setupTunnel() http.Handler { func setupTunnel() http.Handler {
tunnelServer := remotedialer.New(authorizer, remotedialer.DefaultErrorWriter) tunnelServer := remotedialer.New(authorizer, loggingErrorWriter)
setupProxyDialer(tunnelServer) setupProxyDialer(tunnelServer)
return tunnelServer return tunnelServer
} }