Skip to content

Commit 0789693

Browse files
committed
Add credential-plugin authentication support
1 parent d3e0f4e commit 0789693

File tree

4 files changed

+88
-2
lines changed

4 files changed

+88
-2
lines changed

src/config/apis.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ pub struct AuthInfo {
9292

9393
#[serde(rename = "auth-provider")]
9494
pub auth_provider: Option<AuthProviderConfig>,
95+
96+
pub exec: Option<ExecConfig>,
9597
}
9698

9799
/// AuthProviderConfig stores auth for specified cloud provider.
@@ -101,6 +103,16 @@ pub struct AuthProviderConfig {
101103
pub config: HashMap<String, String>,
102104
}
103105

106+
/// ExecConfig stores credential-plugin configuration.
107+
#[derive(Clone, Debug, Serialize, Deserialize)]
108+
pub struct ExecConfig {
109+
#[serde(rename = "apiVersion")]
110+
pub api_version: Option<String>,
111+
pub args: Option<Vec<String>>,
112+
pub command: String,
113+
pub env: Option<Vec<HashMap<String, String>>>,
114+
}
115+
104116
/// NamedContext associates name with context.
105117
#[derive(Clone, Debug, Serialize, Deserialize)]
106118
pub struct NamedContext {
@@ -145,7 +157,9 @@ impl AuthInfo {
145157
self.token = Some(provider.config["access-token"].clone());
146158
if utils::is_expired(&provider.config["expiry"]) {
147159
let client = oauth2::CredentialsClient::new()?;
148-
let token = client.request_token(&vec!["https://www.googleapis.com/auth/cloud-platform".to_string()])?;
160+
let token = client.request_token(&vec![
161+
"https://www.googleapis.com/auth/cloud-platform".to_string(),
162+
])?;
149163
self.token = Some(token.access_token);
150164
}
151165
}

src/config/exec.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
use std::process::Command;
2+
3+
use failure::Error;
4+
use serde_json;
5+
6+
use config::apis;
7+
8+
/// ExecCredentials is used by exec-based plugins to communicate credentials to
9+
/// HTTP transports.
10+
#[derive(Clone, Debug, Serialize, Deserialize)]
11+
pub struct ExecCredential {
12+
pub kind: Option<String>,
13+
#[serde(rename = "apiVersion")]
14+
pub api_version: Option<String>,
15+
pub spec: Option<ExecCredentialSpec>,
16+
pub status: Option<ExecCredentialStatus>,
17+
}
18+
19+
/// ExecCredenitalSpec holds request and runtime specific information provided
20+
/// by transport.
21+
#[derive(Clone, Debug, Serialize, Deserialize)]
22+
pub struct ExecCredentialSpec {}
23+
24+
/// ExecCredentialStatus holds credentials for the transport to use.
25+
#[derive(Clone, Debug, Serialize, Deserialize)]
26+
pub struct ExecCredentialStatus {
27+
#[serde(rename = "expirationTimestamp")]
28+
pub expiration_timestamp: Option<chrono::DateTime<chrono::Utc>>,
29+
pub token: Option<String>,
30+
#[serde(rename = "clientCertificateData")]
31+
pub client_certificate_data: Option<String>,
32+
#[serde(rename = "clientKeyData")]
33+
pub client_key_data: Option<String>,
34+
}
35+
36+
pub fn auth_exec(auth: &apis::ExecConfig) -> Result<ExecCredential, Error> {
37+
let mut cmd = Command::new(&auth.command);
38+
if let Some(args) = &auth.args {
39+
cmd.args(args);
40+
}
41+
if let Some(env) = &auth.env {
42+
let envs = env
43+
.iter()
44+
.flat_map(|env| match (env.get("name"), env.get("value")) {
45+
(Some(name), Some(value)) => Some((name, value)),
46+
_ => None,
47+
});
48+
cmd.envs(envs);
49+
}
50+
let out = cmd.output()?;
51+
if !out.status.success() {
52+
return Err(format_err!("command `{:?}` failed: {:?}", cmd, out));
53+
}
54+
serde_json::from_slice(&out.stdout).map_err(Error::from)
55+
}

src/config/mod.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
mod apis;
2+
mod exec;
23
mod incluster_config;
34
mod kube_config;
45
mod utils;
@@ -39,6 +40,21 @@ pub fn load_kube_config() -> Result<Configuration, Error> {
3940
.ok_or(format_err!("Unable to load kubeconfig"))?;
4041

4142
let loader = KubeConfigLoader::load(kubeconfig)?;
43+
let token = match &loader.user.token {
44+
Some(token) => Some(token.clone()),
45+
None => {
46+
if let Some(exec) = &loader.user.exec {
47+
let creds = exec::auth_exec(exec)?;
48+
let status = creds
49+
.status
50+
.ok_or(format_err!("exec-plugin response did not contain a status"))?;
51+
status.token
52+
} else {
53+
None
54+
}
55+
}
56+
};
57+
4258
let mut client_builder = Client::builder();
4359

4460
if let Some(ca) = loader.ca() {
@@ -61,7 +77,7 @@ pub fn load_kube_config() -> Result<Configuration, Error> {
6177
let mut headers = header::HeaderMap::new();
6278

6379
match (
64-
utils::data_or_file(&loader.user.token, &loader.user.token_file),
80+
utils::data_or_file(&token, &loader.user.token_file),
6581
(loader.user.username, loader.user.password),
6682
) {
6783
(Ok(token), _) => {

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ extern crate http;
1010
extern crate openssl;
1111
extern crate reqwest;
1212
extern crate serde;
13+
extern crate serde_json;
1314
extern crate serde_yaml;
1415
extern crate time;
1516
extern crate url;

0 commit comments

Comments
 (0)