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>
This commit is contained in:
Brad Davidson 2022-03-28 13:45:39 -07:00 committed by Brad Davidson
parent e7437d4ad8
commit af0b496ef3
6 changed files with 75 additions and 38 deletions

View File

@ -0,0 +1,56 @@
package authenticator
import (
"strings"
"github.com/k3s-io/k3s/pkg/authenticator/basicauth"
"github.com/k3s-io/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"
"net/http"
localAuthenticator "github.com/k3s-io/k3s/pkg/authenticator"
"k8s.io/apiserver/pkg/authentication/authenticator"
)
// Authenticator authenticates requests using basic auth
type Authenticator struct {
auth localAuthenticator.Password
auth Password
}
// 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}
}

View File

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

View File

@ -1,30 +0,0 @@
package control
import (
"github.com/k3s-io/k3s/pkg/authenticator/basicauth"
"github.com/k3s-io/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

@ -11,6 +11,7 @@ import (
"strings"
"time"
"github.com/k3s-io/k3s/pkg/authenticator"
"github.com/k3s-io/k3s/pkg/cluster"
"github.com/k3s-io/k3s/pkg/daemons/config"
"github.com/k3s-io/k3s/pkg/daemons/control/deps"
@ -56,11 +57,15 @@ func Server(ctx context.Context, cfg *config.Control) error {
cfg.Runtime.Tunnel = setupTunnel()
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 {
return err
}
cfg.Runtime.Authenticator = basicAuth
cfg.Runtime.Authenticator = auth
if !cfg.DisableAPIServer {
go waitForAPIServerHandlers(ctx, cfg.Runtime)
@ -399,7 +404,7 @@ func waitForAPIServerHandlers(ctx context.Context, runtime *config.ControlRuntim
if err != nil {
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
}

View File

@ -9,13 +9,20 @@ import (
"github.com/rancher/remotedialer"
"github.com/rancher/wrangler/pkg/kv"
"github.com/sirupsen/logrus"
utilnet "k8s.io/apimachinery/pkg/util/net"
"k8s.io/apiserver/pkg/endpoints/request"
"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 {
tunnelServer := remotedialer.New(authorizer, remotedialer.DefaultErrorWriter)
tunnelServer := remotedialer.New(authorizer, loggingErrorWriter)
setupProxyDialer(tunnelServer)
return tunnelServer
}