diff --git a/cmd/dkl-local-server/ws-clusters.go b/cmd/dkl-local-server/ws-clusters.go
index a79793e..857c2f5 100644
--- a/cmd/dkl-local-server/ws-clusters.go
+++ b/cmd/dkl-local-server/ws-clusters.go
@@ -2,6 +2,8 @@ package main
import (
"bytes"
+ "crypto/x509"
+ "encoding/pem"
"errors"
"fmt"
"log"
@@ -355,3 +357,39 @@ func parseCertDuration(d string, now time.Time) (t time.Time, err error) {
return
}
+
+type KubeCSRParseReq struct {
+ CSR string `json:"csr"`
+}
+
+type KubeCSRParseResp struct {
+ User string `json:"user,omitempty"`
+ Group string `json:"group,omitempty"`
+}
+
+func wsParseKubeCSR(req *restful.Request, resp *restful.Response) {
+ var r KubeCSRParseReq
+ if err := req.ReadEntity(&r); err != nil {
+ wsError(resp, err)
+ return
+ }
+
+ block, _ := pem.Decode([]byte(r.CSR))
+ if block == nil || block.Type != "CERTIFICATE REQUEST" {
+ wsError(resp, fmt.Errorf("invalid CSR PEM"))
+ return
+ }
+
+ cr, err := x509.ParseCertificateRequest(block.Bytes)
+ if err != nil {
+ wsError(resp, fmt.Errorf("parse CSR: %w", err))
+ return
+ }
+
+ out := KubeCSRParseResp{User: cr.Subject.CommonName}
+ if len(cr.Subject.Organization) > 0 {
+ out.Group = cr.Subject.Organization[0]
+ }
+
+ resp.WriteAsJson(out)
+}
diff --git a/cmd/dkl-local-server/ws.go b/cmd/dkl-local-server/ws.go
index 5be5f71..b54b6e9 100644
--- a/cmd/dkl-local-server/ws.go
+++ b/cmd/dkl-local-server/ws.go
@@ -42,7 +42,11 @@ func registerWS(rest *restful.Container) {
Param(ws.PathParameter("asset", "the requested asset")).
Doc("Fetch an asset via a download token")).
Route(ws.GET("/download-set").To(wsDownloadSet)).
- Route(ws.GET("/download-set/{kind}/{name}/{asset}").To(wsDownloadSetAsset))
+ Route(ws.GET("/download-set/{kind}/{name}/{asset}").To(wsDownloadSetAsset)).
+ Route(ws.POST("/parse-kube-csr").To(wsParseKubeCSR).
+ Reads(KubeCSRParseReq{}).
+ Writes(KubeCSRParseResp{}).
+ Doc("Parse a Kubernetes CSR PEM and return the subject fields"))
rest.Add(ws)
}
diff --git a/html/ui/ClusterAccess-8b4165435ba4fcac.js b/html/ui/ClusterAccess-1090c2ac32649bb9.js
similarity index 91%
rename from html/ui/ClusterAccess-8b4165435ba4fcac.js
rename to html/ui/ClusterAccess-1090c2ac32649bb9.js
index f9c8421..b54a74e 100644
--- a/html/ui/ClusterAccess-8b4165435ba4fcac.js
+++ b/html/ui/ClusterAccess-1090c2ac32649bb9.js
@@ -21,6 +21,22 @@ const ClusterAccess = {
msg: null,
};
},
+ watch: {
+ 'kubeSignReq.CSR'(v) {
+ if (v && v.includes('BEGIN CERTIFICATE REQUEST')) {
+ fetch(`/public/parse-kube-csr`, {
+ method: 'POST',
+ body: JSON.stringify({ csr: v }),
+ headers: { 'Content-Type': 'application/json' },
+ }).then(r => {
+ if (r.ok) r.json().then(d => {
+ if (d.user) this.kubeSignReq.User = d.user;
+ if (d.group) this.kubeSignReq.Group = d.group;
+ });
+ }).catch(() => {});
+ }
+ },
+ },
computed: {
availableAssets() {
return [
diff --git a/html/ui/index.html b/html/ui/index.html
index 73e4d87..676f890 100644
--- a/html/ui/index.html
+++ b/html/ui/index.html
@@ -13,7 +13,7 @@
-
+
diff --git a/ui/js/ClusterAccess.js b/ui/js/ClusterAccess.js
index f9c8421..b54a74e 100644
--- a/ui/js/ClusterAccess.js
+++ b/ui/js/ClusterAccess.js
@@ -21,6 +21,22 @@ const ClusterAccess = {
msg: null,
};
},
+ watch: {
+ 'kubeSignReq.CSR'(v) {
+ if (v && v.includes('BEGIN CERTIFICATE REQUEST')) {
+ fetch(`/public/parse-kube-csr`, {
+ method: 'POST',
+ body: JSON.stringify({ csr: v }),
+ headers: { 'Content-Type': 'application/json' },
+ }).then(r => {
+ if (r.ok) r.json().then(d => {
+ if (d.user) this.kubeSignReq.User = d.user;
+ if (d.group) this.kubeSignReq.Group = d.group;
+ });
+ }).catch(() => {});
+ }
+ },
+ },
computed: {
availableAssets() {
return [