Azure blob storage bucket origin (private)
Use Microsoft Azure authenticated requests to protect communication between your Fastly service and Azure.
VCL
Use this solution in your VCL service (click RUN below to test this solution or clone it to make changes):
Compute@Edge
Use this solution in your Compute@Edge service:
- Rust
- JavaScript
Cargo.toml
Rust
[dependencies]anyhow = "1.0.28"base64 = "0.13"chrono = "0.4"fastly = "0.7.0"hex = "0.4"hmac-sha256 = "0.1"log = "0.4"log-fastly = "0.2"
main.rs
Rust
/// AZURE Backend Name/// The backend domain name needs to be set set as STORAGE_ACCOUNT_NAME.blob.core.windows.net/// For more details, check https://developer.fastly.com/learning/integrations/backends/const BACKEND_NAME_AZURE: &str = "noguxun0.blob.core.windows.net";
const MS_API_VERSION: &str = "2017-11-09";
/// Holds Azure Blob storage access informationstruct AzureConfig { account_key: String, account_name: String, blob_container: String,}
impl AzureConfig { /// Load the Azure configuration. /// /// This assumes an Edge Dictionary named "azure_config" is attached to this service, /// with entries for the account name, key, and blob container. fn load_config() -> Self { let dict = Dictionary::open("azure_config"); Self { account_key: dict.get("account_key").expect("account key configured"), account_name: dict.get("account_name").expect("account name configured"), blob_container: dict.get("blob_container").expect("account blob configured"), } }}
#[fastly::main]fn main(mut req: Request) -> Result<Response, Error> { log_fastly::init_simple("my_log", log::LevelFilter::Info); fastly::log::set_panic_endpoint("my_log")?;
let azure_config = AzureConfig::load_config();
// Only generate authorize header for GET and HEAD request // Pass PURGE to backend as it is // Block other kinds of method let resp = match req.get_method() { &Method::GET | &Method::HEAD => { req = authorize_azure_storage_request( req, &azure_config.account_key, &azure_config.account_name, &azure_config.blob_container, )?;
req.send(BACKEND_NAME_AZURE)? } m if m == "PURGE" => { // url of cached object has no query string, and has blob container added to path // so when do the purge, we need to make sure the url matches req.remove_query(); req.set_path(&format!( "/{}{}", azure_config.blob_container, req.get_path() ));
req.send(BACKEND_NAME_AZURE)? } _ => Response::from_status(StatusCode::METHOD_NOT_ALLOWED), };
Ok(resp)}
/// Generate Authorization headers of the Azure requestfn authorize_azure_storage_request( mut req: Request, storage_access_key: &str, storage_account_name: &str, blob_container: &str,) -> Result<Request> { // The date format should be something like // Fri, 01 Jan 2021 00:14:22 GMT let ms_date = chrono::Utc::now().format("%a, %d %b %Y %T GMT").to_string();
// Decode base64 format access key to binary key let access_key_decoded = base64::decode(storage_access_key)?;
// Canonical headers must be sorted and concatenated with newlines. // If there are query params there is a separate spec for that, not supported here yet let canonical_headers = format!("x-ms-date:{}\nx-ms-version:{}\n", ms_date, MS_API_VERSION);
// Canonical Resource is /ACCOUNT/CONTAINER/FILE let canonical_resource = format!( "/{}/{}{}", storage_account_name, blob_container, req.get_path() );
// Construct everything properly before signing // We are adding 4 newlines here, however the spec says we // can override any of these 4 headers with values if we want. // For now we are just blanking them out let string_to_sign = format!("GET\n\n\n\n{}{}", canonical_headers, canonical_resource); // HMAC-sign with SHA256 and Base64-encode the result let signature_binary = HMAC::mac(string_to_sign.as_bytes(), &access_key_decoded); let signature = base64::encode(signature_binary);
let authorization = format!("SharedKeyLite {}:{}", storage_account_name, signature);
// request path for azure storage is /containder/object_id req.set_path(&format!("/{}{}", blob_container, req.get_path())); req.remove_query();
// For the the backend request method as GET req.set_method(Method::GET);
// Add authorization related headers to request req.set_header(header::AUTHORIZATION, &authorization); req.set_header("x-ms-date", &ms_date); req.set_header("x-ms-version", MS_API_VERSION);
log::info!("Path: {}, Authorization: {}", req.get_path(), authorization);
Ok(req)}
This page is part of a series in the Static content topic.