diff --git a/.gitignore b/.gitignore index cca0cd643..e5f6372dc 100755 --- a/.gitignore +++ b/.gitignore @@ -30,7 +30,6 @@ Thumbs.db /windows/WebUIWrapper/obj /windows/lib /ext/installfiles/windows/ZeroTier One-SetupFiles -/ext/installfiles/windows/Prerequisites /ext/installfiles/windows/*-cache /ZeroTier One.msi *.vcxproj.backup diff --git a/Dockerfile.release b/Dockerfile.release index aa0d422cc..a676226a3 100644 --- a/Dockerfile.release +++ b/Dockerfile.release @@ -11,6 +11,8 @@ RUN curl -sSL -o zerotier-one.deb "${PACKAGE_BASEURL}/zerotier-one_${VERSION}_${ FROM debian:buster +RUN apt-get update -qq && apt-get install openssl libssl1.1 -y + COPY --from=stage zerotier-one.deb . RUN dpkg -i zerotier-one.deb && rm -f zerotier-one.deb @@ -20,5 +22,7 @@ RUN rm -rf /var/lib/zerotier-one COPY entrypoint.sh.release /entrypoint.sh RUN chmod 755 /entrypoint.sh +HEALTHCHECK --interval=1s CMD bash /healthcheck.sh + CMD [] ENTRYPOINT ["/entrypoint.sh"] diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 13756f4ea..dacbc1837 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,11 +1,12 @@ ZeroTier Release Notes ====== -# 2022-03-21 -- Version 1.8.7 +# 2022-03-30 -- Version 1.8.7 - * Fix for dependency installations in Windows MSI package - * Fix for privilege escalation in desktop UI when the user is not a current super-user - * Bug fix in local OIDC / SSO support + * Fix for dependency installations in Windows MSI package. + * Fix for desktop UI setup when run by a non-super-user. + * Bug fix in local OIDC / SSO support for auth0 and other providers. + * Other minor fixes for e.g. old Linux distributions. # 2022-03-04 -- Version 1.8.6 diff --git a/entrypoint.sh.release b/entrypoint.sh.release index adc258194..0194d3658 100644 --- a/entrypoint.sh.release +++ b/entrypoint.sh.release @@ -1,7 +1,7 @@ #!/bin/sh grepzt() { - [ ! -n "$(cat /var/lib/zerotier-one/zerotier-one.pid)" -a -d "/proc/$(cat /var/lib/zerotier-one/zerotier-one.pid)" ] + [ -f /var/lib/zerotier-one/zerotier-one.pid -a -n "$(cat /var/lib/zerotier-one/zerotier-one.pid)" -a -d "/proc/$(cat /var/lib/zerotier-one/zerotier-one.pid)" ] return $? } @@ -40,26 +40,45 @@ killzerotier() { trap killzerotier INT TERM +echo "Configuring networks to join" +mkdir -p /var/lib/zerotier-one/networks.d + +echo "joining networks: $@" +for i in "$@" +do + echo "Configuring join for $i" + touch "/var/lib/zerotier-one/networks.d/${i}.conf" +done + echo "starting zerotier" nohup /usr/sbin/zerotier-one & while ! grepzt do echo "zerotier hasn't started, waiting a second" + + if [ -f nohup.out ] + then + tail -n 10 nohup.out + fi + sleep 1 done -echo "joining networks: $@" +echo "Writing healthcheck for networks: $@" -for i in "$@" +cat >/healthcheck.sh < - + + + + @@ -27,10 +30,10 @@ - + - + @@ -65,14 +68,12 @@ - - + - + - @@ -84,10 +85,7 @@ - - - @@ -102,6 +100,7 @@ + @@ -155,25 +154,19 @@ - - - - - - @@ -192,9 +185,7 @@ - - @@ -242,9 +233,6 @@ - - - @@ -267,9 +255,7 @@ - - @@ -284,13 +270,11 @@ - - @@ -299,9 +283,6 @@ - - - @@ -315,7 +296,6 @@ - @@ -323,6 +303,7 @@ + @@ -353,11 +334,9 @@ - - - + @@ -372,7 +351,7 @@ - + @@ -401,10 +380,8 @@ - - - - + + @@ -416,11 +393,6 @@ - - - - - @@ -438,10 +410,8 @@ - - @@ -460,16 +430,12 @@ - - - - @@ -500,18 +466,7 @@ - - - - - - - - - - - @@ -536,7 +491,7 @@ - + diff --git a/windows/ZeroTierOne/ZeroTierOne.vcxproj b/windows/ZeroTierOne/ZeroTierOne.vcxproj index 6d370555f..e09a5cd05 100644 --- a/windows/ZeroTierOne/ZeroTierOne.vcxproj +++ b/windows/ZeroTierOne/ZeroTierOne.vcxproj @@ -407,7 +407,7 @@ true $(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories) ZT_EXPORT;FD_SETSIZE=1024;STATICLIB;ZT_SALSA20_SSE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;WIN32;NOMINMAX;ZT_SOFTWARE_UPDATE_DEFAULT="apply";ZT_BUILD_PLATFORM=2;ZT_BUILD_ARCHITECTURE=1;%(PreprocessorDefinitions) - MultiThreadedDLL + MultiThreaded StreamingSIMDExtensions2 true AnySuitable @@ -421,6 +421,7 @@ false false true + false false @@ -440,8 +441,8 @@ true $(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories) ZT_EXPORT;FD_SETSIZE=1024;STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="apply";ZT_SALSA20_SSE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;WIN32;NOMINMAX;ZT_BUILD_PLATFORM=2;ZT_BUILD_ARCHITECTURE=2;%(PreprocessorDefinitions) - MultiThreadedDLL - StreamingSIMDExtensions2 + MultiThreaded + NotSet true AnySuitable Speed @@ -456,6 +457,7 @@ false false true + stdc11 false diff --git a/zeroidc/.cargo/config.toml b/zeroidc/.cargo/config.toml index b5447409e..5326b3647 100644 --- a/zeroidc/.cargo/config.toml +++ b/zeroidc/.cargo/config.toml @@ -3,3 +3,9 @@ rustflags=["-C", "link-arg=-mmacosx-version-min=10.13"] [target.aarch64-apple-darwin] rustflags=["-C", "link-arg=-mmacosx-version-min=10.13"] + +[target.x86_64-pc-windows-msvc] +rustflags = ["-C", "target-feature=+crt-static"] + +[target.i686-pc-windows-msvc] +rustflags = ["-C", "target-feature=+crt-static"] diff --git a/zeroidc/Cargo.toml b/zeroidc/Cargo.toml index 9b4449dd2..68fa99488 100644 --- a/zeroidc/Cargo.toml +++ b/zeroidc/Cargo.toml @@ -12,7 +12,7 @@ crate-type = ["staticlib","rlib"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -openidconnect = { version = "2.2", default-features = false, features = ["reqwest", "native-tls"] } +openidconnect = { version = "2.2", default-features = false, features = ["reqwest", "native-tls", "accept-rfc3339-timestamps"] } base64 = "0.13" url = "2.2" reqwest = "0.11" diff --git a/zeroidc/src/lib.rs b/zeroidc/src/lib.rs index 80c13bb74..76bccc9e3 100644 --- a/zeroidc/src/lib.rs +++ b/zeroidc/src/lib.rs @@ -26,6 +26,7 @@ use jwt::{Token}; use openidconnect::core::{CoreClient, CoreProviderMetadata, CoreResponseType}; use openidconnect::reqwest::http_client; use openidconnect::{AccessToken, AccessTokenHash, AuthorizationCode, AuthenticationFlow, ClientId, CsrfToken, IssuerUrl, Nonce, OAuth2TokenResponse, PkceCodeChallenge, PkceCodeVerifier, RedirectUrl, RefreshToken, Scope, TokenResponse}; +use std::error::Error; use std::str::from_utf8; use std::sync::{Arc, Mutex}; use std::thread::{sleep, spawn, JoinHandle}; @@ -140,6 +141,8 @@ impl ZeroIDC { })), }; + println!("issuer: {}, client_id: {}, auth_endopint: {}, local_web_port: {}", + issuer, client_id, auth_ep, local_web_port); let iss = IssuerUrl::new(issuer.to_string())?; let provider_meta = CoreProviderMetadata::discover(&iss, http_client)?; @@ -155,7 +158,8 @@ impl ZeroIDC { ClientId::new(client_id.to_string()), None, ) - .set_redirect_uri(redirect), + .set_redirect_uri(redirect) + .set_auth_type(openidconnect::AuthType::RequestBody), ); Ok(idc) @@ -187,6 +191,7 @@ impl ZeroIDC { println!("refresh token thread tick, now: {}, exp: {}", systemtime_strftime(now, "[year]-[month]-[day] [hour]:[minute]:[second]"), systemtime_strftime(exp, "[year]-[month]-[day] [hour]:[minute]:[second]")); } let refresh_token = (*inner_local.lock().unwrap()).refresh_token.clone(); + if let Some(refresh_token) = refresh_token { let should_kick = (*inner_local.lock().unwrap()).kick; if now >= (exp - Duration::from_secs(30)) || should_kick { @@ -197,6 +202,10 @@ impl ZeroIDC { (*inner_local.lock().unwrap()).kick = false; } + #[cfg(debug_assertions)] { + println!("Refresh Token: {}", refresh_token.secret()); + } + let token_response = (*inner_local.lock().unwrap()).oidc_client.as_ref().map(|c| { let res = c.exchange_refresh_token(&refresh_token) .request(http_client); @@ -207,151 +216,83 @@ impl ZeroIDC { if let Some(res) = token_response { match res { Ok(res) => { - - let n = match nonce.clone() { - Some(n) => n, - None => { - println!("err: no nonce"); - continue; - } - }; - - let id = match res.id_token() { - Some(t) => t, - None => { - println!("err: no id_token"); - continue; - } - }; - - // verify & validate claims - let verified = (*inner_local.lock().unwrap()).oidc_client.as_ref().map(|c| { - let claims = match id.claims(&c.id_token_verifier(), &n) { - Ok(c) => c, - Err(e) => { - println!("claims err: {}", e); - return false; - } - }; - - let signing_algo = match id.signing_alg() { - Ok(s) => s, - Err(e) => { - println!("alg err: {}", e); - return false; - } - }; - - if let Some(expected_hash) = claims.access_token_hash() { - let actual_hash = match AccessTokenHash::from_token(res.access_token(), &signing_algo) { - Ok(h) => h, - Err(e) => { - println!("Error hashing access token: {}", e); - return false; - } + match res.id_token() { + Some(id_token) => { + let n = match nonce.clone() { + Some(n) => n.secret().to_string(), + None => "".to_string(), }; - - if actual_hash != *expected_hash { - println!("token hash error"); - return false; + + let params = [("id_token", id_token.to_string()),("state", "refresh".to_string()),("extra_nonce", n)]; + #[cfg(debug_assertions)] { + println!("New ID token: {}", id_token.to_string()); } - } - return true; - }); - - let v = match verified { - Some(verified) => { - if !verified { - println!("not verified."); - (*inner_local.lock().unwrap()).running = false; - false - } else { - true - } - }, - None => { - println!("no verification performed?"); - (*inner_local.lock().unwrap()).running = false; - false - } - }; - - if v { - match res.id_token() { - Some(id_token) => { - let params = [("id_token", id_token.to_string()),("state", "refresh".to_string())]; - #[cfg(debug_assertions)] { - println!("New ID token: {}", id_token.to_string()); - } - let client = reqwest::blocking::Client::new(); - let r = client.post((*inner_local.lock().unwrap()).auth_endpoint.clone()) - .form(¶ms) - .send(); + let client = reqwest::blocking::Client::new(); + let r = client.post((*inner_local.lock().unwrap()).auth_endpoint.clone()) + .form(¶ms) + .send(); - match r { - Ok(r) => { - if r.status().is_success() { - #[cfg(debug_assertions)] { - println!("hit url: {}", r.url().as_str()); - println!("status: {}", r.status()); - } - - let access_token = res.access_token(); - let at = access_token.secret(); - - let t: Result>, jwt::Error>= Token::parse_unverified(at); - - if let Ok(t) = t { - let claims = t.claims().registered.clone(); - match claims.expiration { - Some(exp) => { - (*inner_local.lock().unwrap()).exp_time = exp; - }, - None => { - panic!("expiration is None. This shouldn't happen") - } - } - } - - (*inner_local.lock().unwrap()).access_token = Some(access_token.clone()); - if let Some(t) = res.refresh_token() { - // println!("New Refresh Token: {}", t.secret()); - (*inner_local.lock().unwrap()).refresh_token = Some(t.clone()); - } - #[cfg(debug_assertions)] { - println!("Central post succeeded"); - } - } else { - println!("Central post failed: {}", r.status().to_string()); + match r { + Ok(r) => { + if r.status().is_success() { + #[cfg(debug_assertions)] { println!("hit url: {}", r.url().as_str()); - println!("Status: {}", r.status()); - if let Ok(body) = r.bytes() { - if let Ok(body) = std::str::from_utf8(&body) { - println!("Body: {}", body); + println!("status: {}", r.status()); + } + + let access_token = res.access_token(); + let idt = &id_token.to_string(); + + let t: Result>, jwt::Error> = + Token::parse_unverified(idt); + + if let Ok(t) = t { + let claims = t.claims().registered.clone(); + match claims.expiration { + Some(exp) => { + (*inner_local.lock().unwrap()).exp_time = exp; + }, + None => { + panic!("expiration is None. This shouldn't happen") } - + } + } + + (*inner_local.lock().unwrap()).access_token = Some(access_token.clone()); + if let Some(t) = res.refresh_token() { + // println!("New Refresh Token: {}", t.secret()); + (*inner_local.lock().unwrap()).refresh_token = Some(t.clone()); + } + #[cfg(debug_assertions)] { + println!("Central post succeeded"); + } + } else { + println!("Central post failed: {}", r.status().to_string()); + println!("hit url: {}", r.url().as_str()); + println!("Status: {}", r.status()); + if let Ok(body) = r.bytes() { + if let Ok(body) = std::str::from_utf8(&body) { + println!("Body: {}", body); } - (*inner_local.lock().unwrap()).exp_time = 0; - (*inner_local.lock().unwrap()).running = false; } - }, - Err(e) => { - println!("Central post failed: {}", e.to_string()); - println!("hit url: {}", e.url().unwrap().as_str()); - println!("Status: {}", e.status().unwrap()); (*inner_local.lock().unwrap()).exp_time = 0; (*inner_local.lock().unwrap()).running = false; } + }, + Err(e) => { + println!("Central post failed: {}", e.to_string()); + println!("hit url: {}", e.url().unwrap().as_str()); + println!("Status: {}", e.status().unwrap()); + (*inner_local.lock().unwrap()).exp_time = 0; + (*inner_local.lock().unwrap()).running = false; } - }, - None => { - println!("no id token?!?"); } + }, + None => { + println!("no id token?!?"); } - } else { - println!("claims not verified"); } }, Err(e) => { @@ -483,6 +424,8 @@ impl ZeroIDC { let res = (*local.lock().unwrap()).as_opt().map(|i| { if let Some(verifier) = i.pkce_verifier.take() { let token_response = i.oidc_client.as_ref().map(|c| { + println!("auth code: {}", code); + let r = c.exchange_code(AuthorizationCode::new(code.to_string())) .set_pkce_verifier(verifier) .request(http_client); @@ -493,6 +436,7 @@ impl ZeroIDC { let n = match i.nonce.clone() { Some(n) => n, None => { + println!("no noce"); return None; } }; @@ -500,6 +444,7 @@ impl ZeroIDC { let id = match res.id_token() { Some(t) => t, None => { + println!("no id token"); return None; } }; @@ -507,6 +452,7 @@ impl ZeroIDC { let claims = match id.claims(&c.id_token_verifier(), &n) { Ok(c) => c, Err(_e) => { + println!("no claims"); return None; } }; @@ -514,6 +460,7 @@ impl ZeroIDC { let signing_algo = match id.signing_alg() { Ok(s) => s, Err(_) => { + println!("no signing algorithm"); return None; } }; @@ -534,11 +481,10 @@ impl ZeroIDC { } Some(res) }, - Err(_e) => { - #[cfg(debug_assertions)] { - println!("token response error: {}", _e.to_string()); - } - + Err(e) => { + println!("token response error: {:?}", e.to_string()); + println!("\t {:?}", e.source()); + return None; }, } @@ -574,15 +520,17 @@ impl ZeroIDC { println!("Status: {}", res.status()); } - let at = tok.access_token().secret(); + let idt = &id_token.to_string(); - let t: Result>, jwt::Error>= Token::parse_unverified(at); + let t: Result>, jwt::Error>= + Token::parse_unverified(idt); if let Ok(t) = t { let claims = t.claims().registered.clone(); match claims.expiration { Some(exp) => { i.exp_time = exp; + println!("Set exp time to: {:?}", i.exp_time); }, None => { panic!("expiration is None. This shouldn't happen")