Skip to content
This repository was archived by the owner on Feb 6, 2025. It is now read-only.

Commit

Permalink
Skuba auth login oidc ca (#1249)
Browse files Browse the repository at this point in the history
the user might change the OIDC CA which differs from the kube-apiserver CA.
therefore, introduce a new flag `--oidc-dex-ca` for `skuba auth login` to creates
a secure SSL connection to the OIDC dex server.

Signed-off-by: JenTing Hsiao <jenting.hsiao@suse.com>
  • Loading branch information
JenTing Hsiao authored Jul 15, 2020
1 parent 5165385 commit 1d63166
Show file tree
Hide file tree
Showing 9 changed files with 180 additions and 57 deletions.
27 changes: 21 additions & 6 deletions cmd/skuba/auth/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,28 @@ func NewLoginCmd() *cobra.Command {
cfg.Password = password
}

if cfg.RootCAPath != "" {
fi, err := os.Stat(cfg.RootCAPath)
if cfg.KubeAPIServerCAPath != "" {
fi, err := os.Stat(cfg.KubeAPIServerCAPath)
if os.IsNotExist(err) {
fmt.Printf("Root certificate authority chain file \"%s\" not exist\n", cfg.RootCAPath)
fmt.Printf("The kube-apiserver certificate authority chain file %q not exist\n", cfg.KubeAPIServerCAPath)
os.Exit(1)
}
if fi.IsDir() {
fmt.Printf("Root certificate authority chain file \"%s\" is a folder\n", cfg.RootCAPath)
fmt.Printf("The kube-apiserver certificate authority chain file %q is a folder\n", cfg.KubeAPIServerCAPath)
os.Exit(1)
}
}

// the users might use custom CA for OIDC servers dex & gangway
// in this case, the OIDC dex CA differs to kube-apiserver CA
if cfg.OIDCDexServerCAPath != "" {
fi, err := os.Stat(cfg.OIDCDexServerCAPath)
if os.IsNotExist(err) {
fmt.Printf("The OIDC dex server certificate authority chain file %q not exist\n", cfg.OIDCDexServerCAPath)
os.Exit(1)
}
if fi.IsDir() {
fmt.Printf("The OIDC dex server certificate authority chain file %q is a folder\n", cfg.OIDCDexServerCAPath)
os.Exit(1)
}
}
Expand All @@ -96,11 +110,12 @@ func NewLoginCmd() *cobra.Command {
},
}

cmd.Flags().StringVarP(&cfg.DexServer, "server", "s", "", "OIDC dex server url https://<IP/FQDN>:<Port> (specify port 32000 for standard CaaSP deployments) (required)")
cmd.Flags().StringVarP(&cfg.DexServer, "server", "s", "", "The OIDC dex server url https://<IP/FQDN>:<Port> (specify port 32000 for standard CaaSP deployments) (required)")
cmd.Flags().StringVarP(&cfg.Username, "username", "u", "", "Username")
cmd.Flags().StringVarP(&cfg.Password, "password", "p", "", "Password")
cmd.Flags().StringVarP(&cfg.AuthConnector, "auth-connector", "a", "", "Authentication connector ID")
cmd.Flags().StringVarP(&cfg.RootCAPath, "root-ca", "r", "", "Root certificate authority chain file")
cmd.Flags().StringVarP(&cfg.KubeAPIServerCAPath, "root-ca", "r", "", "The kube-apiserver certificate authority chain file")
cmd.Flags().StringVarP(&cfg.OIDCDexServerCAPath, "oidc-dex-ca", "", "", "The OIDC dex server certificate authority chain file")
cmd.Flags().BoolVarP(&cfg.InsecureSkipVerify, "insecure", "k", false, "Insecure SSL connection")
cmd.Flags().StringVarP(&cfg.ClusterName, "cluster-name", "n", "local", "Kubernetes cluster name")
cmd.Flags().StringVarP(&cfg.KubeConfigPath, "kubeconfig", "c", "kubeconf.txt", "Path to save kubeconfig file")
Expand Down
5 changes: 4 additions & 1 deletion docs/man/skuba-auth-login.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ login - Authenticate to a cluster and authorized with kubeconfig
The authentication connector ID

**--root-ca, -r**
The cluster root certificate authority chain file
The kube-apiserver certificate authority chain file

**--oidc-dex-ca**
The OIDC dex server certificate authority chain file

**--insecure, -k**
Insecure SSL/TLS connection to OIDC dex server and further kube apiserver (true|false)
Expand Down
18 changes: 9 additions & 9 deletions pkg/skuba/actions/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,13 @@ const (

// request represents an OAuth2 auth request flow
type request struct {
IssuerURL string
Username string
Password string
RootCAData []byte
InsecureSkipVerify bool
AuthConnector string
Debug bool
IssuerURL string
Username string
Password string
OIDCDexServerCAData []byte
InsecureSkipVerify bool
AuthConnector string
Debug bool

clientID string
clientSecret string
Expand Down Expand Up @@ -170,8 +170,8 @@ func doAuth(authReq request) (*response, error) {

if authReq.InsecureSkipVerify {
client = httpClientForSkipTLS()
} else if len(authReq.RootCAData) > 0 {
client, err = httpClientForRootCAs(authReq.RootCAData)
} else if len(authReq.OIDCDexServerCAData) > 0 {
client, err = httpClientForRootCAs(authReq.OIDCDexServerCAData)
if err != nil {
return nil, err
}
Expand Down
57 changes: 34 additions & 23 deletions pkg/skuba/actions/auth/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,26 +44,37 @@ const (

// LoginConfig represents the login configuration
type LoginConfig struct {
DexServer string
Username string
Password string
RootCAPath string
InsecureSkipVerify bool
AuthConnector string
ClusterName string
KubeConfigPath string
Debug bool
DexServer string
Username, Password string
KubeAPIServerCAPath, OIDCDexServerCAPath string
InsecureSkipVerify bool
AuthConnector string
ClusterName string
KubeConfigPath string
Debug bool
}

// Login do authentication login process
func Login(cfg LoginConfig) (*clientcmdapi.Config, error) {
var err error
var rootCAData []byte
var kubeAPIServerCAData, dexServerCAData []byte

if !cfg.InsecureSkipVerify && cfg.RootCAPath != "" {
rootCAData, err = ioutil.ReadFile(cfg.RootCAPath)
if !cfg.InsecureSkipVerify && cfg.KubeAPIServerCAPath != "" {
kubeAPIServerCAData, err = ioutil.ReadFile(cfg.KubeAPIServerCAPath)
if err != nil {
return nil, errors.Wrap(err, "read CA failed")
return nil, errors.Wrap(err, "read kube-apiserver CA failed")
}
}

if !cfg.InsecureSkipVerify {
if cfg.OIDCDexServerCAPath != "" {
dexServerCAData, err = ioutil.ReadFile(cfg.OIDCDexServerCAPath)
if err != nil {
return nil, errors.Wrap(err, "read oidc dex CA failed")
}
} else {
// default the oidc dex server CA equals to the kube-apiserver CA
dexServerCAData = kubeAPIServerCAData
}
}

Expand All @@ -73,15 +84,15 @@ func Login(cfg LoginConfig) (*clientcmdapi.Config, error) {
}

authResp, err := doAuth(request{
clientID: clientID,
clientSecret: clientSecret,
IssuerURL: cfg.DexServer,
Username: cfg.Username,
Password: cfg.Password,
RootCAData: rootCAData,
InsecureSkipVerify: cfg.InsecureSkipVerify,
AuthConnector: cfg.AuthConnector,
Debug: cfg.Debug,
clientID: clientID,
clientSecret: clientSecret,
IssuerURL: cfg.DexServer,
Username: cfg.Username,
Password: cfg.Password,
OIDCDexServerCAData: dexServerCAData,
InsecureSkipVerify: cfg.InsecureSkipVerify,
AuthConnector: cfg.AuthConnector,
Debug: cfg.Debug,
})
if err != nil {
return nil, errors.Wrap(err, "auth failed")
Expand All @@ -92,7 +103,7 @@ func Login(cfg LoginConfig) (*clientcmdapi.Config, error) {
kubeConfig.Clusters[cfg.ClusterName] = &clientcmdapi.Cluster{
Server: fmt.Sprintf("%s://%s:%s", defaultScheme, url.Hostname(), defaultAPIServerPort), // Guess kube-apiserver on port 6443
InsecureSkipTLSVerify: cfg.InsecureSkipVerify,
CertificateAuthorityData: rootCAData,
CertificateAuthorityData: kubeAPIServerCAData,
}

// fill out contexts
Expand Down
84 changes: 67 additions & 17 deletions pkg/skuba/actions/auth/login_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func startServer() *httptest.Server {
mux.HandleFunc("/approval", approvalHandler())

srv := httptest.NewUnstartedServer(mux)
cert, _ := tls.LoadX509KeyPair("testdata/localhost.crt", "testdata/localhost.key")
cert, _ := tls.LoadX509KeyPair("testdata/oidc-dex.crt", "testdata/oidc-dex.key")
srv.TLS = &tls.Config{
Certificates: []tls.Certificate{cert},
}
Expand All @@ -61,21 +61,59 @@ func Test_Login(t *testing.T) {
expectedErrorMsg string
}{
{
name: "secure ssl/tls",
name: "secure ssl/tls with same ca of kube-apiserver and oidc dex server",
srvCb: startServer,
cfg: LoginConfig{
Username: mockDefaultUsername,
Password: mockDefaultPassword,
RootCAPath: "testdata/localhost.crt",
ClusterName: "test-cluster-name",
Username: mockDefaultUsername,
Password: mockDefaultPassword,
KubeAPIServerCAPath: "testdata/oidc-dex.crt",
ClusterName: "test-cluster-name",
},
expectedKubeConfCb: func(dexServerURL string, clusterName string) *clientcmdapi.Config {
url, _ := url.Parse(dexServerURL)

kubeConfig := clientcmdapi.NewConfig()
kubeConfig.Clusters[clusterName] = &clientcmdapi.Cluster{
Server: fmt.Sprintf("%s://%s:%s", defaultScheme, url.Hostname(), defaultAPIServerPort),
CertificateAuthorityData: localhostCert,
CertificateAuthorityData: oidcDexCert,
}
kubeConfig.Contexts[clusterName] = &clientcmdapi.Context{
Cluster: clusterName,
AuthInfo: mockDefaultUsername,
}
kubeConfig.CurrentContext = clusterName
kubeConfig.AuthInfos[mockDefaultUsername] = &clientcmdapi.AuthInfo{
AuthProvider: &clientcmdapi.AuthProviderConfig{
Name: authProviderID,
Config: map[string]string{
"idp-issuer-url": dexServerURL,
"client-id": clientID,
"client-secret": clientSecret,
"id-token": mockIDToken,
"refresh-token": mockRefreshToken,
},
},
}
return kubeConfig
},
},
{
name: "secure ssl/tls with different ca of kube-apiserver and oidc dex server",
srvCb: startServer,
cfg: LoginConfig{
Username: mockDefaultUsername,
Password: mockDefaultPassword,
KubeAPIServerCAPath: "testdata/kube-apiserver.crt",
OIDCDexServerCAPath: "testdata/oidc-dex.crt",
ClusterName: "test-cluster-name",
},
expectedKubeConfCb: func(dexServerURL string, clusterName string) *clientcmdapi.Config {
url, _ := url.Parse(dexServerURL)

kubeConfig := clientcmdapi.NewConfig()
kubeConfig.Clusters[clusterName] = &clientcmdapi.Cluster{
Server: fmt.Sprintf("%s://%s:%s", defaultScheme, url.Hostname(), defaultAPIServerPort),
CertificateAuthorityData: kubeAPIServerCert,
}
kubeConfig.Contexts[clusterName] = &clientcmdapi.Context{
Cluster: clusterName,
Expand Down Expand Up @@ -199,26 +237,38 @@ func Test_Login(t *testing.T) {
expectedErrorMsg: "auth failed: invalid input auth connector ID",
},
{
name: "invalid root ca",
name: "invalid kube-apiserver ca",
srvCb: startServer,
cfg: LoginConfig{
Username: mockDefaultUsername,
Password: mockDefaultPassword,
RootCAPath: "testdata/invalid.crt",
ClusterName: "test-cluster-name",
Username: mockDefaultUsername,
Password: mockDefaultPassword,
KubeAPIServerCAPath: "testdata/invalid.crt",
ClusterName: "test-cluster-name",
},
expectedErrorMsg: "auth failed: no valid certificates found in root CA file",
},
{
name: "invalid dex server ca",
srvCb: startServer,
cfg: LoginConfig{
Username: mockDefaultUsername,
Password: mockDefaultPassword,
KubeAPIServerCAPath: "testdata/oidc-dex.crt",
OIDCDexServerCAPath: "testdata/nonexist.crt",
ClusterName: "test-cluster-name",
},
expectedErrorMsg: "read oidc dex CA failed: open testdata/nonexist.crt: no such file or directory",
},
{
name: "cert file not exist",
srvCb: startServer,
cfg: LoginConfig{
Username: mockDefaultUsername,
Password: mockDefaultPassword,
RootCAPath: "testdata/nonexist.crt",
ClusterName: "test-cluster-name",
Username: mockDefaultUsername,
Password: mockDefaultPassword,
KubeAPIServerCAPath: "testdata/nonexist.crt",
ClusterName: "test-cluster-name",
},
expectedErrorMsg: "read CA failed: open testdata/nonexist.crt: no such file or directory",
expectedErrorMsg: "read kube-apiserver CA failed: open testdata/nonexist.crt: no such file or directory",
},
{
name: "auth failed",
Expand Down
21 changes: 21 additions & 0 deletions pkg/skuba/actions/auth/testdata/kube-apiserver.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDZzCCAk+gAwIBAgIIXMHGy4qcMXowDQYJKoZIhvcNAQELBQAwFTETMBEGA1UE
AxMKa3ViZXJuZXRlczAeFw0yMDA3MDcwMjEzNTdaFw0yMTA3MDcwMjEzNTdaMBkx
FzAVBgNVBAMTDmt1YmUtYXBpc2VydmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEAl3sYB7yqGqfRNFfBLz4bY88rBJ0rA3BDwZYtt8MpMCUKyRdMrxaG
UBDBDklo4ezYIJX2Wr/R/30oIK5iPUWlVl9ObXfvNe72McYz2D76sq/zCMIJkU/0
0PxUinNhwXP1NkDcLD5H4oE8EeA5fsUdBOZxNo1sQNJ6Ps+dQJuw/FqrpyfcR1aC
5bzO8uSI/FRYJKBD0yAxaNAwRHNvM5ToB49ktVUBwo+JwqMdUkq+PAPPxCcw62/c
vCT3/a8+Pnn/RFayDmmCE09w3UnvfOQER3Lk0TtLMAL8jBrJ9uc4eN5kTOKdUYQW
hUEXXBMC0qn056xwkzUgPgDcP9uRFFMj6wIDAQABo4G2MIGzMA4GA1UdDwEB/wQE
AwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATCBiwYDVR0RBIGDMIGAgghtYXN0ZXIt
MIIKa3ViZXJuZXRlc4ISa3ViZXJuZXRlcy5kZWZhdWx0ghZrdWJlcm5ldGVzLmRl
ZmF1bHQuc3ZjgiRrdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWyH
BApgAAGHBApUSFKHBApUSAuHBApUSAswDQYJKoZIhvcNAQELBQADggEBAOPdafr0
Pczyepblh45E/i8YBTXNi9IIpeYLXegu6+sfmm6n2zwksip+V82tCU7qLKDInDFc
XN7mXcBeB8xzrctMn4C24nN0K/BqvsUGrP5/e5TTQqS5TWfPvPtyM0DecFvaQfyP
GdVgH/L9h3k7CSIFroDMgCzlWIMhQNG8TZbeO854odyJmYZsUcwqfYBaZVmX7Ony
PRM26UGtu0j0YUu4uK309+eoH1NdIH51IA8MyxZrzirCNXCIYCrtapwC6tm/aZif
jUDIp8tB9FCtstw7ChXRnYMb+f59HezqKrn6kyNg6xTHC2SB6Kq/Opvtc7vxR35u
v2yH9/5R17prxX0=
-----END CERTIFICATE-----
25 changes: 24 additions & 1 deletion pkg/skuba/actions/auth/testing.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,30 @@ const (
)

var (
localhostCert = []byte(`-----BEGIN CERTIFICATE-----
kubeAPIServerCert = []byte(`-----BEGIN CERTIFICATE-----
MIIDZzCCAk+gAwIBAgIIXMHGy4qcMXowDQYJKoZIhvcNAQELBQAwFTETMBEGA1UE
AxMKa3ViZXJuZXRlczAeFw0yMDA3MDcwMjEzNTdaFw0yMTA3MDcwMjEzNTdaMBkx
FzAVBgNVBAMTDmt1YmUtYXBpc2VydmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEAl3sYB7yqGqfRNFfBLz4bY88rBJ0rA3BDwZYtt8MpMCUKyRdMrxaG
UBDBDklo4ezYIJX2Wr/R/30oIK5iPUWlVl9ObXfvNe72McYz2D76sq/zCMIJkU/0
0PxUinNhwXP1NkDcLD5H4oE8EeA5fsUdBOZxNo1sQNJ6Ps+dQJuw/FqrpyfcR1aC
5bzO8uSI/FRYJKBD0yAxaNAwRHNvM5ToB49ktVUBwo+JwqMdUkq+PAPPxCcw62/c
vCT3/a8+Pnn/RFayDmmCE09w3UnvfOQER3Lk0TtLMAL8jBrJ9uc4eN5kTOKdUYQW
hUEXXBMC0qn056xwkzUgPgDcP9uRFFMj6wIDAQABo4G2MIGzMA4GA1UdDwEB/wQE
AwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATCBiwYDVR0RBIGDMIGAgghtYXN0ZXIt
MIIKa3ViZXJuZXRlc4ISa3ViZXJuZXRlcy5kZWZhdWx0ghZrdWJlcm5ldGVzLmRl
ZmF1bHQuc3ZjgiRrdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWyH
BApgAAGHBApUSFKHBApUSAuHBApUSAswDQYJKoZIhvcNAQELBQADggEBAOPdafr0
Pczyepblh45E/i8YBTXNi9IIpeYLXegu6+sfmm6n2zwksip+V82tCU7qLKDInDFc
XN7mXcBeB8xzrctMn4C24nN0K/BqvsUGrP5/e5TTQqS5TWfPvPtyM0DecFvaQfyP
GdVgH/L9h3k7CSIFroDMgCzlWIMhQNG8TZbeO854odyJmYZsUcwqfYBaZVmX7Ony
PRM26UGtu0j0YUu4uK309+eoH1NdIH51IA8MyxZrzirCNXCIYCrtapwC6tm/aZif
jUDIp8tB9FCtstw7ChXRnYMb+f59HezqKrn6kyNg6xTHC2SB6Kq/Opvtc7vxR35u
v2yH9/5R17prxX0=
-----END CERTIFICATE-----
`)

oidcDexCert = []byte(`-----BEGIN CERTIFICATE-----
MIICEzCCAXygAwIBAgIQMIMChMLGrR+QvmQvpwAU6zANBgkqhkiG9w0BAQsFADAS
MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw
MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB
Expand Down

0 comments on commit 1d63166

Please sign in to comment.