Skip to content

Instantly share code, notes, and snippets.

@liggitt
Created September 22, 2015 19:15
Show Gist options
  • Save liggitt/f3eb8ddabb3805d6efbe to your computer and use it in GitHub Desktop.
Save liggitt/f3eb8ddabb3805d6efbe to your computer and use it in GitHub Desktop.
package main
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"os"
"k8s.io/kubernetes/pkg/auth/authenticator"
"k8s.io/kubernetes/pkg/auth/user"
x509request "k8s.io/kubernetes/plugin/pkg/auth/authenticator/request/x509"
)
func main() {
// Usage and args
fmt.Println("Usage: go run cert_auth.go [/path/to/client/ca.crt]\n\n")
clientCAFile := ""
if len(os.Args) > 1 {
clientCAFile = os.Args[1]
fmt.Printf("Using client CA bundle %s\n", clientCAFile)
} else {
fmt.Printf("Using system roots to verify client certs\n")
}
// Authn/Authz setup
authn, err := newAuthenticatorFromClientCAFile(clientCAFile)
if err != nil {
fmt.Println(err)
return
}
authz, err := newAuthorizerFromUserList("system:admin")
if err != nil {
fmt.Println(err)
return
}
// Stub handler
handler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
// Check authn
user, ok, err := authn.AuthenticateRequest(req)
if err != nil {
fmt.Printf("Got error from request authenticator: %s\n", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if !ok {
fmt.Printf("Request authenticator did not get user info from request\n")
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
fmt.Printf("Request authenticator got user info from request: %#v\n", user)
// Check authz
allowed, err := authz.AuthorizeRequest(req, user)
if err != nil {
fmt.Printf("Got error from request authorizer: %s\n", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if !allowed {
fmt.Printf("Request authorizer forbade the request\n")
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
// Handle request
w.Write([]byte("ok\n"))
})
// Start test server
server := httptest.NewUnstartedServer(handler)
server.TLS = &tls.Config{
ClientAuth: tls.RequestClientCert,
}
server.StartTLS()
defer server.Close()
fmt.Printf(`Try:
curl -k --cert path/to/client.crt --key path/to/client.key %s
`, server.URL)
select {}
}
// newAuthenticatorFromClientCAFile returns an authenticator.Request or an error
func newAuthenticatorFromClientCAFile(clientCAFile string) (authenticator.Request, error) {
opts := x509request.DefaultVerifyOptions()
// If at custom CA bundle is provided, load it (otherwise just use system roots)
if len(clientCAFile) > 0 {
if caData, err := ioutil.ReadFile(clientCAFile); err != nil {
return nil, err
} else if len(caData) > 0 {
roots := x509.NewCertPool()
if !roots.AppendCertsFromPEM(caData) {
return nil, fmt.Errorf("no valid certs found in %s", clientCAFile)
}
opts.Roots = roots
}
}
return x509request.New(opts, x509request.CommonNameUserConversion), nil
}
type Authorizer interface {
AuthorizeRequest(req *http.Request, user user.Info) (bool, error)
}
func newAuthorizerFromUserList(allowedUsers ...string) (Authorizer, error) {
u := map[string]bool{}
for _, allowedUser := range allowedUsers {
u[allowedUser] = true
}
return &userAuthorizer{u}, nil
}
type userAuthorizer struct {
allowedUsers map[string]bool
}
func (a *userAuthorizer) AuthorizeRequest(req *http.Request, user user.Info) (bool, error) {
return a.allowedUsers[user.GetName()], nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment