Skip to content

Commit

Permalink
Merge pull request #123 from davidB/bug/gke_rusttls
Browse files Browse the repository at this point in the history
provide rustls implementation to sign request to gke
  • Loading branch information
clux committed Feb 9, 2020
2 parents 36ae985 + 33f3288 commit 7e7e2aa
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 45 deletions.
19 changes: 6 additions & 13 deletions src/config/apis.rs
Expand Up @@ -155,19 +155,12 @@ impl AuthInfo {
Some(provider) => {
if let Some(access_token) = provider.config.get("access-token") {
self.token = Some(access_token.clone());
#[cfg(feature = "native-tls")]
{ // TODO: allow rusttls with this auth provider bs
if utils::is_expired(&provider.config["expiry"]) {
let client = oauth2::CredentialsClient::new()?;
let token = client.request_token(&[
"https://www.googleapis.com/auth/cloud-platform".to_string(),
]).await?;
self.token = Some(token.access_token);
}
}
#[cfg(feature = "rustls-tls")]
{
error!("kube-rs does not support auth_provider setup with rustls atm")
if utils::is_expired(&provider.config["expiry"]) {
let client = oauth2::CredentialsClient::new()?;
let token = client.request_token(&[
"https://www.googleapis.com/auth/cloud-platform".to_string(),
]).await?;
self.token = Some(token.access_token);
}
}
if let Some(id_token) = provider.config.get("id-token") {
Expand Down
136 changes: 104 additions & 32 deletions src/oauth2/mod.rs
Expand Up @@ -5,13 +5,6 @@ use std::path::PathBuf;
use chrono::Utc;
use crate::{Result, Error};

#[cfg(feature = "native-tls")]
use openssl::{
pkey::{PKey, Private},
sign::Signer,
rsa::Padding,
hash::MessageDigest,
};
use reqwest::Client;
use reqwest::header::CONTENT_TYPE;
//use time::Duration;
Expand Down Expand Up @@ -122,53 +115,132 @@ impl CredentialsClient {
})
}

#[cfg(feature = "native-tls")]
pub async fn request_token(&self, scopes: &[String]) -> Result<Token> {
let private_key = PKey::private_key_from_pem(&self.credentials.private_key.as_bytes())
.map_err(|e| Error::SslError(format!("{}", e)))?;
let encoded = &self.jws_encode(
let encoded = jws_encode(
&Claim::new(&self.credentials, scopes),
&Header{
alg: "RS256".to_string(),
typ: "JWT".to_string(),
},
private_key)?;
&self.credentials.private_key)?;

let body = Serializer::new(String::new())
.extend_pairs(vec![
("grant_type".to_string(), DEFAULT_GRANT_TYPE.to_string()),
("assertion".to_string(), encoded.to_string()),
]).finish();
let token_response: TokenResponse = self.client
let token_response: TokenResponse = self.client
.post(&self.credentials.token_uri)
.body(body)
.header(CONTENT_TYPE, "application/x-www-form-urlencoded")
.send()
.await
.map_err(|e| Error::KubeConfig(format!("Unable to request token: {}", e)))?
.json::<TokenResponse>()
.map_err(|e| Error::KubeConfig(format!("Unable to request token: {}", e)))
.and_then(|response| {
if response.status() != reqwest::StatusCode::OK {
Err(Error::KubeConfig(format!("Fail to retrieve new credential {:#?}", response)))
} else {
Ok(response)
}
})?.json::<TokenResponse>()
.await
.map_err(|e| Error::KubeConfig(format!("Unable to parse request token: {}", e)))?;
Ok(token_response.into_token())
}
}

fn jws_encode(claim: &Claim, header: &Header, private_key: &str) -> Result<String> {
let encoded_header = base64_encode(serde_json::to_string(&header).unwrap().as_bytes());
let encoded_claims = base64_encode(serde_json::to_string(&claim).unwrap().as_bytes());
let signature_base = format!("{}.{}", encoded_header, encoded_claims);
let signature = sign(&signature_base, private_key)?;
let encoded_signature = base64_encode(&signature);
Ok(format!("{}.{}", signature_base, encoded_signature))
}

#[cfg(feature = "native-tls")]
fn sign(signature_base: &str, private_key: &str) -> Result<Vec<u8>> {
use openssl::{
pkey::PKey,
sign::Signer,
rsa::Padding,
hash::MessageDigest,
};
let key = PKey::private_key_from_pem(private_key.as_bytes())
.map_err(|e| Error::SslError(format!("{}", e)))?;
let mut signer = Signer::new(MessageDigest::sha256(), &key)
.map_err(|e| Error::SslError(format!("{}", e)))?;
signer.set_rsa_padding(Padding::PKCS1)
.map_err(|e| Error::SslError(format!("{}", e)))?;
signer.update(signature_base.as_bytes())
.map_err(|e| Error::SslError(format!("{}", e)))?;
signer.sign_to_vec()
.map_err(|e| Error::SslError(format!("{}", e)))
}

#[cfg(feature = "rustls-tls")]
fn sign(signature_base: &str, private_key: &str) -> Result<Vec<u8>> {
use rustls::internal::pemfile;
use rustls::sign::RSASigningKey;
use rustls::sign::SigningKey;

let keys = pemfile::pkcs8_private_keys(&mut private_key.as_bytes().clone())
.map_err(|_| Error::SslError("fail to parse private key".into()))?;
let key = keys.get(0)
.ok_or_else(|| Error::SslError("no usable private key found to sign with RS256".into()))?;
let signing_key = RSASigningKey::new(key)
.map_err(|_| Error::SslError("fail to make RSA signing key".into()))?;
let signer = signing_key.choose_scheme(&[rustls::SignatureScheme::RSA_PKCS1_SHA256])
.ok_or_else(|| Error::SslError("scheme RSA_PKCS1_SHA256 not found into private key".into()))?;
signer.sign(signature_base.as_bytes()).map_err(|e| Error::SslError(format!("{}", e)))
}

fn base64_encode(bytes: &[u8]) -> String {
base64::encode_config(bytes, base64::URL_SAFE)
}

#[cfg(feature = "native-tls")]
fn jws_encode(&self, claim: &Claim, header: &Header, key: PKey<Private>) -> Result<String> {
let encoded_header = self.base64_encode(serde_json::to_string(&header).unwrap().as_bytes());
let encoded_claims = self.base64_encode(serde_json::to_string(&claim).unwrap().as_bytes());
let signature_base = format!("{}.{}", encoded_header, encoded_claims);
let mut signer = Signer::new(MessageDigest::sha256(), &key)
.map_err(|e| Error::SslError(format!("{}", e)))?;
signer.set_rsa_padding(Padding::PKCS1)
.map_err(|e| Error::SslError(format!("{}", e)))?;
signer.update(signature_base.as_bytes())
.map_err(|e| Error::SslError(format!("{}", e)))?;
let signature = signer.sign_to_vec()
.map_err(|e| Error::SslError(format!("{}", e)))?;
Ok(format!("{}.{}", signature_base, self.base64_encode(&signature)))
}

fn base64_encode(&self, bytes: &[u8]) -> String {
base64::encode_config(bytes, base64::URL_SAFE)
#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_true() {
// generated with
// ```
// openssl genpkey -out rsakey.pem -algorithm RSA -pkeyopt rsa_keygen_bits:2048
// ```
let private_key = r#"-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDjT1UyWwk/v2UG
BhTEB+8NIL4RW3+u7TSOVP0Qpxf22bhJH9+RqwZPlwzbhYQT1TNXT4fFnARaGWaG
EtPV/rlV6o9PcLCMj3y2sOiKBy0qS6/3nYHKlFNPGnesYLTIbk54Orp4OYnSqQ/G
zBZbS3IRDsTaOb4D+KaxdPm/I8qN1TEPIDEkDRYtRprbmTQaz3rl0ooKuDCHiWoW
I7rG6zGkcGwBZAkwh0XFeklJSwZbC0JK88wolHKWJba6KCO8A2LpskacPB/KP5mQ
bnTzIS5xiNMKf9qhLm/HgDzgCL9E8StnZygUmRFKYh4MTzrpfGaoT5Vm+tijlrDi
CDE33tuZAgMBAAECggEBANuVDnsngCbJsECCbVr1QxNOdu1zk0ObN3LrXM/Sao72
wVQ6axFfwifuhegl8XHrOb51QHY/geC7utN3qpWFjOoXPbuC47nU/qfI+8oippm+
Jc2wpOnaISRAMC0f+mPIUxtHuExdYOtUj7399vbYSed6eeVJdGqHsBerJXtkis44
uuzlQ6ISMPd3YhxN6S4QbPyw6aaoJG0qYpdHSL/n9r49hA3sKbAQVSOTzM1PMRje
6kB6BPrfmyVavHUXRZG1lU7gD41F8nG0wXOvsFu1XXPeEjw2/uBRanA8rWtAPv02
vBXcBMHpv7ySWCVOXMLWfZmo4GJIjfhtjTasUTSxVAECgYEA+Tvei02NBGWdjOzu
xoLvF71oT22Ic862StvmDJYSV/9bs1r8pxGCPs0tkHr/DQoBcmvAComrcEBltkaZ
yyKxDdSLrsy1nkL4hPMwJF0gNZAAaj4rMNfadKGOmlhQBcFCBph8kijcGsyYn1Vs
2cGCIZCALofDm4t8oIUpB8+UsYECgYEA6XsYh50+JkpuTknTbciOQJijl0a0oK6X
SO9mwoWEtg0+mrR3gL0kjghUObN5p0lhLLyqCBDVeFdaDcWbZXdF/KuSyI48Bube
c0EYkCFk1W/39yVb6LqQP6xoPrA/nLB4AuZYSqkZwx+bHH7OBgHaXRh2m2HawU5B
pQsM2PVwhhkCgYAonJfTzSw4VjKI/yadVEKPdL6liqycakeMBS8ESAPvMN4JaL8Y
niLCBv7wtwoOXt4DfglJ7krwPJ4WSITQ8/Mz1Ll6H0NM6Y7DYzkqA76226MlrMGu
8M1ZCeZJwjAv7+DJYFmUG3JaL5KDDBFznjONMpWgf2DhXKZPJcOc0TdigQKBgGHL
4NN1JsItLRT30WrLteISzXsg76naV54CQR27hYIn/BAbBW9USop/rJ/asFtE3kI5
6FKmknPsyti368ZNdnBGgZ4mDbiqXYUTQDGm+zB3zPqlmGDcPG2fTq7rbkm4lRxJ
1bO4LwVPKM5/wtY7UnbqN0wQaevMVqzF+ySpce+JAoGBAOLkdZrv7+JPfusIlj/H
CuNhikh1WMHm6Al0SYBURhUb52hHGEAnpaIxwQrN4PPD4Iboyp0kLsjMtKxlvnBm
WpsqFXdkj9GLZt1s1Q/5SW5Twb7gxdR7cXrXOcATivN1/GDdhIHS1NEb3MM7EBXc
9RSM375nLWCP0LDosgKSaq+u
-----END PRIVATE KEY-----
"#;
let msg = "foo bar";
let expected = "h0H_U6SO_i1F7JwzzTBHL1DNTw-YD6jdMul9Uwo_5xB_TmztP9c7T8e5CVY1o5_vMfQ3SZJXZ9liwd7FK8a7NjNumWIWq0_KZvDxMK6D2SSkA8WAz4KsdUU1CNVxM51UYQgYnHpaJvtNmowgzCnahNQQso4hKsYCe7nNKlTiCP1yPzM4MWJYh2cekH1SGqSaOtgvQZz4GrOPG-hhcyMZMk_u-sZ0F3PUFj0-kfbhZPNVpvv4-wI_XA84q85Wech4nsgLbxO9397-whsmGVNlqqo2PwwxASn7dEqtrrvD7mkabf32OqmgJ-xXT_n4m67kvgzC7ausezX7E0zcnBj3RQ==".to_string();
assert_eq!(base64_encode(&sign(&msg, private_key).unwrap()), expected);
}
}

0 comments on commit 7e7e2aa

Please sign in to comment.