Skip to content

Commit

Permalink
NDS-540 / NDS-483: LMA basic auth (#126)
Browse files Browse the repository at this point in the history
* Create basic-auth using admin password for kube-system namespace, if
present

* Fixed problem with admin account creation order

* Allow ability to create basic auth for user other than namespace

* NDS-483: Update ingress rule when password changes to trigger refresh in ingress controller
  • Loading branch information
craig-willis authored and bodom0015 committed Sep 16, 2016
1 parent 066d9fa commit a75e318
Show file tree
Hide file tree
Showing 2 changed files with 161 additions and 56 deletions.
88 changes: 73 additions & 15 deletions apiserver/cmd/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ import (
"github.com/golang/glog"
)

var adminUser = "admin"
var systemNamespace = "kube-system"

type Server struct {
etcd *etcd.EtcdHelper
kube *kube.KubeHelper
Expand Down Expand Up @@ -243,7 +246,7 @@ func (s *Server) start(cfg Config, adminPasswd string) {
Timeout: timeout,
MaxRefresh: time.Hour * 24,
Authenticator: func(userId string, password string) bool {
if userId == "admin" && password == adminPasswd {
if userId == adminUser && password == adminPasswd {
return true
} else {
return s.etcd.CheckPassword(userId, password) && s.etcd.CheckAccess(userId)
Expand All @@ -260,8 +263,8 @@ func (s *Server) start(cfg Config, adminPasswd string) {
},
PayloadFunc: func(userId string) map[string]interface{} {
payload := make(map[string]interface{})
if userId == "admin" {
payload["admin"] = true
if userId == adminUser {
payload[adminUser] = true
}
payload["server"] = s.hostname
payload["user"] = userId
Expand Down Expand Up @@ -488,7 +491,7 @@ func (s *Server) GetAllAccounts(w rest.ResponseWriter, r *rest.Request) {

func (s *Server) getUser(r *rest.Request) string {
payload := r.Env["JWT_PAYLOAD"].(map[string]interface{})
if payload["admin"] == true {
if payload[adminUser] == true {
return ""
} else {
return payload["user"].(string)
Expand All @@ -497,7 +500,7 @@ func (s *Server) getUser(r *rest.Request) string {

func (s *Server) IsAdmin(r *rest.Request) bool {
payload := r.Env["JWT_PAYLOAD"].(map[string]interface{})
if payload["admin"] == true {
if payload[adminUser] == true {
return true
} else {
return false
Expand Down Expand Up @@ -589,7 +592,57 @@ func (s *Server) createBasicAuthSecret(uid string) error {
return err
}

_, err = s.kube.CreateBasicAuthSecret(account.Namespace, account.Password)
_, err = s.kube.CreateBasicAuthSecret(account.Namespace, account.Namespace, account.Password)
if err != nil {
glog.Error(err)
return err
}

err = s.updateIngress(uid)
if err != nil {
glog.Error(err)
return err
}
return nil
}

func (s *Server) updateIngress(uid string) error {

ingresses, err := s.kube.GetIngresses(uid)
if err != nil {
glog.Error(err)
return err
}
if ingresses != nil {
for _, ingress := range ingresses {
glog.V(4).Infof("Touching ingress %s\n", ingress.Name)
_, err = s.kube.CreateUpdateIngress(uid, &ingress, true)
if err != nil {
glog.Error(err)
return err
}
}
}

return nil
}

func (s *Server) createLMABasicAuthSecret() error {
if s.kube.NamespaceExists(systemNamespace) {
account, err := s.etcd.GetAccount(adminUser)
if err != nil {
glog.Error(err)
return err
}

_, err = s.kube.CreateBasicAuthSecret(systemNamespace, adminUser, account.Password)
if err != nil {
glog.Error(err)
return err
}
}

err := s.updateIngress(systemNamespace)
if err != nil {
glog.Error(err)
return err
Expand Down Expand Up @@ -2274,7 +2327,7 @@ func (s *Server) loadSpecs(path string) error {

func (s *Server) HandlePodEvent(eventType watch.EventType, event *k8api.Event, pod *k8api.Pod) {

if pod.Namespace != "default" && pod.Namespace != "kube-system" {
if pod.Namespace != "default" && pod.Namespace != systemNamespace {
glog.V(4).Infof("HandlePodEvent %s", eventType)

//name := pod.Name
Expand Down Expand Up @@ -2369,7 +2422,7 @@ func (s *Server) HandlePodEvent(eventType watch.EventType, event *k8api.Event, p
func (s *Server) HandleReplicationControllerEvent(eventType watch.EventType, event *k8api.Event,
rc *k8api.ReplicationController) {

if rc.Namespace != "default" && rc.Namespace != "kube-system" {
if rc.Namespace != "default" && rc.Namespace != systemNamespace {
glog.V(4).Infof("HandleReplicationControllerEvent %s", eventType)

userId := rc.Namespace
Expand Down Expand Up @@ -2522,10 +2575,10 @@ func (s *Server) createAdminUser(password string) error {

glog.V(4).Infof("Creating admin user")

if !s.accountExists("admin") {
if !s.accountExists(adminUser) {
account := &api.Account{
Name: "admin",
Namespace: "admin",
Name: adminUser,
Namespace: adminUser,
Description: "NDS Labs administrator",
Password: password,
ResourceLimits: api.AccountResourceLimits{
Expand All @@ -2535,30 +2588,35 @@ func (s *Server) createAdminUser(password string) error {
MemoryDefault: s.memDefault,
},
}
err := s.setupAccount(account)
err := s.etcd.PutAccount(adminUser, account, true)
if err != nil {
glog.Error(err)
return err
}
err = s.etcd.PutAccount("admin", account, true)
err = s.setupAccount(account)
if err != nil {
glog.Error(err)
return err
}

} else {
account, err := s.etcd.GetAccount("admin")
account, err := s.etcd.GetAccount(adminUser)
if err != nil {
glog.Error(err)
return err
}
account.Password = password
err = s.etcd.PutAccount("admin", account, true)
err = s.etcd.PutAccount(adminUser, account, true)
if err != nil {
glog.Error(err)
return err
}
}
err := s.createLMABasicAuthSecret()
if err != nil {
glog.Error(err)
return err
}

return nil
}
Expand Down
129 changes: 88 additions & 41 deletions apiserver/pkg/kube/kube.go
Original file line number Diff line number Diff line change
Expand Up @@ -1148,72 +1148,85 @@ func (k *KubeHelper) Exec(pid string, pod string, container string, kube *KubeHe
func (k *KubeHelper) CreateIngress(pid string, host string, service string, port int, tlsSecretName string, basicAuth bool) (*extensions.Ingress, error) {

name := service + "-ingress"
update := true

// Delete existing ingress
ingress, err := k.GetIngress(pid, name)
if ingress != nil {
k.DeleteIngress(pid, name)
if err == nil {
return nil, err
}
if ingress == nil {
update = false

// https://github.com/kubernetes/contrib/tree/master/ingress/controllers/nginx/examples/auth
annotations := map[string]string{}
if basicAuth {
annotations["ingress.kubernetes.io/auth-type"] = "basic"
annotations["ingress.kubernetes.io/auth-secret"] = "basic-auth"
annotations["ingress.kubernetes.io/auth-realm"] = "NDS Labs"
}
// https://github.com/kubernetes/contrib/tree/master/ingress/controllers/nginx/examples/auth
annotations := map[string]string{}
if basicAuth {
annotations["ingress.kubernetes.io/auth-type"] = "basic"
annotations["ingress.kubernetes.io/auth-secret"] = "basic-auth"
annotations["ingress.kubernetes.io/auth-realm"] = "NDS Labs"
}

ingress = &extensions.Ingress{
ObjectMeta: api.ObjectMeta{
Name: name,
Namespace: pid,
Annotations: annotations,
},
Spec: extensions.IngressSpec{
TLS: []extensions.IngressTLS{
extensions.IngressTLS{
Hosts: []string{host},
SecretName: tlsSecretName,
},
ingress = &extensions.Ingress{
ObjectMeta: api.ObjectMeta{
Name: name,
Namespace: pid,
Annotations: annotations,
},
Rules: []extensions.IngressRule{
extensions.IngressRule{
Host: host,
IngressRuleValue: extensions.IngressRuleValue{
HTTP: &extensions.HTTPIngressRuleValue{
Paths: []extensions.HTTPIngressPath{
extensions.HTTPIngressPath{
Path: "/",
Backend: extensions.IngressBackend{
ServiceName: service,
ServicePort: intstr.FromInt(port),
Spec: extensions.IngressSpec{
TLS: []extensions.IngressTLS{
extensions.IngressTLS{
Hosts: []string{host},
SecretName: tlsSecretName,
},
},
Rules: []extensions.IngressRule{
extensions.IngressRule{
Host: host,
IngressRuleValue: extensions.IngressRuleValue{
HTTP: &extensions.HTTPIngressRuleValue{
Paths: []extensions.HTTPIngressPath{
extensions.HTTPIngressPath{
Path: "/",
Backend: extensions.IngressBackend{
ServiceName: service,
ServicePort: intstr.FromInt(port),
},
},
},
},
},
},
},
},
},
}
}
return k.CreateUpdateIngress(pid, ingress, update)
}

func (k *KubeHelper) CreateUpdateIngress(pid string, ingress *extensions.Ingress, update bool) (*extensions.Ingress, error) {

ingress.ObjectMeta.Annotations["ndslabs.org/updated"] = time.Now().String()
data, err := json.Marshal(ingress)
if err != nil {
return nil, err
}

url := k.kubeBase + extBase + "/namespaces/" + pid + "/ingresses/"
url := k.kubeBase + extBase + "/namespaces/" + pid + "/ingresses"
method := "POST"
if update {
method = "PUT"
url += "/" + ingress.Name
}
glog.V(4).Infoln(url)
request, _ := http.NewRequest("POST", url, bytes.NewBuffer(data))
request, _ := http.NewRequest(method, url, bytes.NewBuffer(data))
request.Header.Set("Content-Type", "application/json")
request.Header.Set("Authorization", k.getAuthHeader())
httpresp, httperr := k.client.Do(request)
if httperr != nil {
glog.Error(httperr)
return nil, httperr
} else {
if httpresp.StatusCode == http.StatusCreated {
glog.V(2).Infof("Added ingress %s-%s\n", pid, service)
if httpresp.StatusCode == http.StatusCreated || httpresp.StatusCode == http.StatusOK {
glog.V(2).Infof("Added/updated ingress %s\n", ingress.Name)
data, err := ioutil.ReadAll(httpresp.Body)
if err != nil {
return nil, err
Expand All @@ -1224,7 +1237,7 @@ func (k *KubeHelper) CreateIngress(pid string, host string, service string, port
} else if httpresp.StatusCode == http.StatusConflict {
return nil, fmt.Errorf("Ingress exists for namespace %s: %s\n", pid, httpresp.Status)
} else {
return nil, fmt.Errorf("Error adding ingress for namespace %s: %s\n", pid, httpresp.Status)
return nil, fmt.Errorf("Error adding/updating ingress for namespace %s: %s\n", pid, httpresp.Status)
}
}
return nil, nil
Expand Down Expand Up @@ -1258,6 +1271,40 @@ func (k *KubeHelper) GetIngress(pid string, ingressName string) (*extensions.Ing
return nil, nil
}

func (k *KubeHelper) GetIngresses(pid string) ([]extensions.Ingress, error) {

url := k.kubeBase + extBase + "/namespaces/" + pid + "/ingresses"
glog.V(4).Infoln(url)
request, _ := http.NewRequest("GET", url, nil)
request.Header.Set("Content-Type", "application/json")
request.Header.Set("Authorization", k.getAuthHeader())
httpresp, httperr := k.client.Do(request)
if httperr != nil {
glog.Error(httperr)
return nil, httperr
} else {
if httpresp.StatusCode == http.StatusOK {
data, err := ioutil.ReadAll(httpresp.Body)
if err != nil {
return nil, err
}

ingressList := extensions.IngressList{}
json.Unmarshal(data, &ingressList)

var ingresses = []extensions.Ingress{}
for _, ingress := range ingressList.Items {
ingresses = append(ingresses, ingress)
}
return ingresses, nil

} else {
return nil, fmt.Errorf("Error getting ingresses for account %s: %s\n", pid, httpresp.Status)
}
}
return nil, nil
}

//http://kubernetes.io/docs/api-reference/extensions/v1beta1/operations/
func (k *KubeHelper) DeleteIngress(pid string, name string) (*extensions.Ingress, error) {

Expand Down Expand Up @@ -1288,7 +1335,7 @@ func (k *KubeHelper) DeleteIngress(pid string, name string) (*extensions.Ingress
return nil, nil
}

func (k *KubeHelper) CreateBasicAuthSecret(pid string, hashedPassword string) (*api.Secret, error) {
func (k *KubeHelper) CreateBasicAuthSecret(pid string, username string, hashedPassword string) (*api.Secret, error) {
secret, _ := k.GetSecret(pid, "basic-auth")
if secret != nil {
k.DeleteSecret(pid, "basic-auth")
Expand All @@ -1300,7 +1347,7 @@ func (k *KubeHelper) CreateBasicAuthSecret(pid string, hashedPassword string) (*
Namespace: pid,
},
Data: map[string][]byte{
"auth": []byte(fmt.Sprintf("%s:%s", pid, string(hashedPassword))),
"auth": []byte(fmt.Sprintf("%s:%s", username, string(hashedPassword))),
},
}
return k.CreateSecret(pid, secret)
Expand Down

0 comments on commit a75e318

Please sign in to comment.