From d49cd3383b2710daeca3e6ee8b03de9e6f91cdd6 Mon Sep 17 00:00:00 2001 From: Graham Christensen Date: Mon, 6 Nov 2023 21:47:01 -0500 Subject: [PATCH] Make the nix flake check tests pass --- book/src/admin-guide/deployment/nixos.md | 4 ++-- integration-tests/basic/default.nix | 4 ++-- nixos/atticd.nix | 6 +++--- server/src/config-template.toml | 2 +- server/src/config.rs | 2 +- token/Cargo.toml | 2 +- token/src/lib.rs | 19 +++++++++++++++---- 7 files changed, 25 insertions(+), 14 deletions(-) diff --git a/book/src/admin-guide/deployment/nixos.md b/book/src/admin-guide/deployment/nixos.md index 45ede64..e5aef81 100644 --- a/book/src/admin-guide/deployment/nixos.md +++ b/book/src/admin-guide/deployment/nixos.md @@ -14,13 +14,13 @@ Attic provides [a NixOS module](https://github.com/zhaofengli/attic/blob/main/ni The RS256 JWT secret can be generated with the `openssl` utility: ```bash -openssl genrsa -traditional -out private_key.pem 4096 +nix run nixpkgs#openssl -- genrsa -traditional -out - 4096 | base64 -w0 ``` Create a file on the server containing the following contents: ``` -ATTIC_SERVER_TOKEN_RS256_SECRET="output from openssl" +ATTIC_SERVER_TOKEN_RS256_SECRET="output from above" ``` Ensure the file is only accessible by root. diff --git a/integration-tests/basic/default.nix b/integration-tests/basic/default.nix index 2165f72..53982d2 100644 --- a/integration-tests/basic/default.nix +++ b/integration-tests/basic/default.nix @@ -5,7 +5,7 @@ let serverConfigFile = config.nodes.server.services.atticd.configFile; cmd = { - atticadm = "atticd-atticadm"; + atticadm = ". /etc/atticd.env && export ATTIC_SERVER_TOKEN_RS256_SECRET && atticd-atticadm"; atticd = ". /etc/atticd.env && export ATTIC_SERVER_TOKEN_RS256_SECRET && atticd -f ${serverConfigFile}"; }; @@ -125,7 +125,7 @@ in { # For testing only - Don't actually do this environment.etc."atticd.env".text = '' - ATTIC_SERVER_TOKEN_RS256_SECRET="$(openssl genrsa -traditional -out - 512)" + ATTIC_SERVER_TOKEN_RS256_SECRET='LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcEFJQkFBS0NBUUVBekhqUzFGKzlRaFFUdlJZYjZ0UGhxS09FME5VYkIraTJMOTByWVBNQVVoYVBUMmlKCmVUNk9vWFlmZWszZlZ1dXIrYks1VWFVRjhUbEx2Y1FHa1Arckd0WDRiQUpGTWJBcTF3Y25FQ3R6ZGVERHJnSlIKMGUvNWJhdXQwSS9YS0ticG9oYjNvWVhtUmR5eG9WVGE3akY1bk11ajBsd25kUTcwYTF1ZGkzMGNpYkdTWHZMagpVeGltL3ByYjUrV3ZPdjN4UnhlbDZHYmptUW1RMVBHeHVLcmx3b1ZKRnlWTjl3QmExajBDelJDcURnTFRwQWw0CjhLVWlDY2V1VUZQcmdZaW9vSVhyVExlWmxVbFVVV3FHSDBJbGFKeVUyQ05iNWJtZWM1TnZ4RDlaakFoYytucmgKRS80VzkxajdQMFVyQnp4am9NUTRlKzBPZDhmQnBvSDAwbm4xUXdJREFRQUJBb0lCQUE2RmxEK21Ed3gyM1pJRAoxSGJBbHBuQ0IwaEhvbFJVK0Q5OC96d3k5ZlplaU00VWVCTUcyTjFweE1HTWIweStqeWU4UkVJaXJNSGRsbDRECllvNEF3bmUwODZCRUp3TG81cG4vOVl2RjhqelFla1ZNLzkrZm9nRGlmUVUvZWdIMm5NZzR4bHlQNUhOWXdicmEKQ25SNVNoQlRQQzdRQWJOa0hRTFU3bUwrUHowZUlXaG9KWVRoUUpkU0g3RDB0K1QwZzVVNDdPam5qbXJaTWwxaApHOE1IUHhKMk5WU1l2N0dobnpjblZvcVVxYzlxeldXRDZXZERtV1BPNGJ1K2p0b2E2U2o4cjJtb0RRZ1A5YXNhCm93RUFJbHBmbVkxYUx2dENwWG4rejRTTWJKcHRXMlVvaktGa2dkYm9jZmtXYWdtSGZRa2xmS0dBQ0hibU9ZV24KeDRCbTU3a0NnWUVBN1dXaXJDZnBRR01hR3A2WWxMQlVUc1VJSXJOclF4UmtuRlc3dFVYd0NqWFZ5SDlTR3FqNgphTkNhYzZpaks3QVNBYXlxY1JQRjFPY2gyNmxpVmRKUHNuRGxwUjhEVXB2TzRVOVRzSTJyZ1lZYzNrSWkzVGFKClgzV0Vic1Z6Nk45WXFPSXlnVnZiTEhLS0F4Uyt4b1Z2SjkzQmdWRHN5SkxRdmhrM3VubXk3M2tDZ1lFQTNINnYKeUhOKzllOVAyOS9zMVY1eWZxSjdvdVdKV0lBTHFDYm9zOTRRSVdPSG5HRUtSSGkydWIzR0d6U2tRSzN1eTUrdQo4M0txaFJOejRVMkdOK1pLaFE0NHhNVmV4TUVvZzJVU3lTaVZ0cFdqWXBwT2Q1NnVaMzRWaFU2TWRNZS9zT0JnCnNoei84MUxUSis2cHdFZE9wV2tPVlRaMXJISlZXQmdtVk5qWjc1c0NnWUVBNVd5YjBaU2dyMEVYTVRLa2NzNFcKTENudXV0cDZodEZtaWsrd29IZCtpOStMUThFSU1BdXVOUzJrbHJJYlAxVmhrWXkxQzZMNFJkRTV2M2ZyT05XUApmL3ZyYzdDTkhZREdacWlyVUswWldvdXB5b0pQLzBsOWFXdkJHT3hxSUZ2NDZ2M3ZvV1NNWkdBdFVOenpvaGZDClhOeks3WmF2dndka0JOT0tNQVQ5RU1FQ2dZRUF3NEhaWDRWNUo1d2dWVGVDQ2RjSzhsb2tBbFpBcUNZeEw5SUEKTjZ4STVUSVpSb0dNMXhXcC81dlRrci9rZkMwOU5YUExiclZYbVZPY1JrTzFKTStmZDhjYWN1OEdqck11dHdMaAoyMWVQR0N3cWlQMkZZZTlqZVFTRkZJU0hhZXpMZll3V2NSZmhvdURudGRxYXpaRHNuU0kvd1RMZXVCOVFxU0lRCnF0NzByczBDZ1lCQ2lzV0VKdXpQUUlJNzVTVkU4UnJFZGtUeUdhOEVBOHltcStMdDVLRDhPYk80Q2JHYVFlWXkKWFpjSHVyOFg2cW1lWHZVU3MwMHBMMUdnTlJ3WCtSUjNMVDhXTm9vc0NqVDlEUW9GOFZveEtseDROVTRoUGlrTQpBc0w1RS9wYnVLeXkvSU5LTnQyT3ZPZmJYVitlTXZQdGs5c1dORjNyRTBYcU15TW9maG9NaVE9PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=' ''; services.atticd = { diff --git a/nixos/atticd.nix b/nixos/atticd.nix index a33b415..b8b678a 100644 --- a/nixos/atticd.nix +++ b/nixos/atticd.nix @@ -16,7 +16,7 @@ let } '' cat $configFile - export ATTIC_SERVER_TOKEN_RS256_SECRET="$(${pkgs.openssl}/bin/openssl genrsa -traditional -out - 512)" + export ATTIC_SERVER_TOKEN_RS256_SECRET="$(${pkgs.openssl}/bin/openssl genrsa -traditional -out - 1024 | ${pkgs.coreutils}/bin/base64 -w0)" export ATTIC_SERVER_DATABASE_URL="sqlite://:memory:" ${cfg.package}/bin/atticd --mode check-config -f $configFile cat <$configFile >$out @@ -79,7 +79,7 @@ in variables: - ATTIC_SERVER_TOKEN_RS256_SECRET: The PEM-encoded version of the - RS256 JWT secret. Generate it with `openssl genrsa -traditional -out private_key.pem 4096`. + RS256 JWT secret. Generate it with `openssl genrsa -traditional -out - 4096 | base64 -w0`. ''; type = types.nullOr types.path; default = null; @@ -135,7 +135,7 @@ in message = '' is not set. - Run `openssl genrsa -traditional -out private_key.pem 4096` and create a file with the following contents: + Run `openssl genrsa -traditional -out private_key.pem 4096 | base64 -w0` and create a file with the following contents: ATTIC_SERVER_TOKEN_RS256_SECRET="output from command" diff --git a/server/src/config-template.toml b/server/src/config-template.toml index ba67689..58e398f 100644 --- a/server/src/config-template.toml +++ b/server/src/config-template.toml @@ -36,7 +36,7 @@ allowed-hosts = [] # JWT signing token # -# Set this to the PEM encoding of some random data. +# Set this to the base64-encoded, then PEM encoding of an RSA key. # You can also set it via the `ATTIC_SERVER_TOKEN_RS256_SECRET` environment # variable. token-rs256-secret = "%token_rs256_secret%" diff --git a/server/src/config.rs b/server/src/config.rs index c3b605a..e99fd63 100644 --- a/server/src/config.rs +++ b/server/src/config.rs @@ -26,7 +26,7 @@ const XDG_PREFIX: &str = "attic"; /// This is useful for deploying to certain application platforms like Fly.io const ENV_CONFIG_BASE64: &str = "ATTIC_SERVER_CONFIG_BASE64"; -/// Environment variable storing the PEM-encoded RS256 JWT secret. +/// Environment variable storing the base64 encoded, then PEM-encoded RS256 JWT secret. const ENV_TOKEN_RS256_SECRET: &str = "ATTIC_SERVER_TOKEN_RS256_SECRET"; /// Environment variable storing the database connection string. diff --git a/token/Cargo.toml b/token/Cargo.toml index d49ac98..f549dd2 100644 --- a/token/Cargo.toml +++ b/token/Cargo.toml @@ -11,7 +11,7 @@ attic = { path = "../attic", default-features = false } base64 = "0.21.2" chrono = "0.4.24" displaydoc = "0.2.4" -jsonwebtoken = "9.1.0" +jsonwebtoken = { version = "9.1.0", features = ["use_pem"] } lazy_static = "1.4.0" regex = "1.8.3" serde = "1.0.163" diff --git a/token/src/lib.rs b/token/src/lib.rs index 3cfd46f..1e0fd41 100644 --- a/token/src/lib.rs +++ b/token/src/lib.rs @@ -93,6 +93,7 @@ pub use jsonwebtoken::{DecodingKey, EncodingKey}; use rsa::pkcs1::{DecodeRsaPrivateKey, EncodeRsaPublicKey}; use serde::{Deserialize, Serialize}; use serde_with::{serde_as, BoolFromInt}; +use base64::{engine::general_purpose::STANDARD as BASE64_STANDARD, Engine}; use attic::cache::{CacheName, CacheNamePattern}; @@ -272,8 +273,14 @@ pub enum Error { /// JWT error: {0} TokenError(jsonwebtoken::errors::Error), + /// Base64 decode error: {0} + Base64Error(base64::DecodeError), + /// RSA Key error: {0} RsaKeyError(rsa::pkcs1::Error), + + /// Failure decoding the base64 layer of the base64 encoded PEM + Utf8Error(std::str::Utf8Error) } impl Token { @@ -285,7 +292,7 @@ impl Token { validation.validate_nbf = true; // validation.set_issuer(&[ctx.config.flakehub_jwt_bound_issuer.clone()]); // validation.set_audience(&[ctx.config.jwt_bound_audience.clone()]); - validation.set_required_spec_claims(&["exp", "nbf", "aud", "iss", "sub"]); + //validation.set_required_spec_claims(&["exp", "nbf", "aud", "iss", "sub"]); jsonwebtoken::decode::>(token, key, &validation) .map_err(Error::TokenError) @@ -314,7 +321,8 @@ impl Token { /// Encodes the token. pub fn encode(&self, key: &jsonwebtoken::EncodingKey) -> Result { - let header = jsonwebtoken::Header::default(); + let mut header = jsonwebtoken::Header::default(); + header.alg = Algorithm::RS256; jsonwebtoken::encode(&header, &self.0, key).map_err(Error::TokenError) } @@ -420,14 +428,17 @@ impl CachePermission { impl StdError for Error {} -pub fn decode_token_rs256_secret(secret: &str) -> Result<(EncodingKey, DecodingKey)> { +pub fn decode_token_rs256_secret(s: &str) -> Result<(EncodingKey, DecodingKey)> { + let decoded = BASE64_STANDARD.decode(s).map_err(Error::Base64Error)?; + let secret = std::str::from_utf8(&decoded).map_err(Error::Utf8Error)?; + let private_key = rsa::RsaPrivateKey::from_pkcs1_pem(secret).map_err(Error::RsaKeyError)?; let public_key = private_key.to_public_key(); let public_pkcs1_pem = public_key .to_pkcs1_pem(rsa::pkcs1::LineEnding::LF) .map_err(Error::RsaKeyError)?; - let encoding_key = EncodingKey::from_rsa_pem(secret.as_bytes()).map_err(Error::TokenError)?; + let encoding_key = EncodingKey::from_rsa_pem(&secret.as_bytes()).map_err(Error::TokenError)?; let decoding_key = DecodingKey::from_rsa_pem(public_pkcs1_pem.as_bytes()).map_err(Error::TokenError)?;