Compare commits
40 Commits
3c45c7487b
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| abbb0842fc | |||
| eac8adbcfa | |||
| d7ad25c4d6 | |||
| 241f70cb22 | |||
| 6822c65a7a | |||
| fb7ec2ac9e | |||
| 76c812794c | |||
| 3b9c295a35 | |||
| 3a068149a7 | |||
| ea7905d7c7 | |||
| d5a715fcfc | |||
| 815278e918 | |||
| 58c31e73bf | |||
| 1a66e46615 | |||
| 93fb89446d | |||
| c8ac9ddebf | |||
| ffdfc2697b | |||
| b698521720 | |||
| 8813a4d5ed | |||
| b8f6c2dccf | |||
| 199346b48f | |||
| 8d30246ea0 | |||
| 050986ff87 | |||
| aa9274aca3 | |||
| ae7542100b | |||
| c65c347ca5 | |||
| 8be0eb7d90 | |||
| f336039cd8 | |||
| 901477ef5a | |||
| 103df98c0f | |||
| 32a438dee2 | |||
| 790f779572 | |||
| 60f3fcfa60 | |||
| 1b22711071 | |||
| 5483f22f6f | |||
| 0862cad994 | |||
| 2393a281c3 | |||
| ddc8fe8241 | |||
| 241ee9f645 | |||
| f23d36ecd8 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,4 +1,5 @@
|
|||||||
storage
|
storage
|
||||||
test
|
**/.terraform
|
||||||
|
**/.terraform.lock.hcl
|
||||||
Caddyfile
|
Caddyfile
|
||||||
*.env
|
*.env
|
||||||
|
|||||||
5
.vscode/settings.json
vendored
Normal file
5
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"files.associations": {
|
||||||
|
"start-azurite": "shellscript"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,16 +2,31 @@
|
|||||||
# It uses /etc/hosts entries to route requests to the emulator
|
# It uses /etc/hosts entries to route requests to the emulator
|
||||||
# Replace "__ACCOUNT_NAME__" with a desired storage account name if needed
|
# Replace "__ACCOUNT_NAME__" with a desired storage account name if needed
|
||||||
__ACCOUNT_NAME__.blob.core.windows.net {
|
__ACCOUNT_NAME__.blob.core.windows.net {
|
||||||
tls __AZURITE_DIR__/__ACCOUNT_NAME___cert.pem __AZURITE_DIR__/__ACCOUNT_NAME___key.pem
|
tls __AZURITE_STORAGE__/__ACCOUNT_NAME___cert.pem __AZURITE_STORAGE__/__ACCOUNT_NAME___key.pem
|
||||||
reverse_proxy localhost:10000
|
reverse_proxy https://__ACCOUNT_NAME__.blob.core.windows.net:10000 {
|
||||||
|
transport http {
|
||||||
|
tls
|
||||||
|
tls_trust_pool file __AZURITE_STORAGE__/ca_cert.pem
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
__ACCOUNT_NAME__.queue.core.windows.net {
|
__ACCOUNT_NAME__.queue.core.windows.net {
|
||||||
tls __AZURITE_DIR__/__ACCOUNT_NAME___cert.pem __AZURITE_DIR__/__ACCOUNT_NAME___key.pem
|
tls __AZURITE_STORAGE__/__ACCOUNT_NAME___cert.pem __AZURITE_STORAGE__/__ACCOUNT_NAME___key.pem
|
||||||
reverse_proxy localhost:10001
|
reverse_proxy https://__ACCOUNT_NAME__.queue.core.windows.net:10001 {
|
||||||
|
transport http {
|
||||||
|
tls
|
||||||
|
tls_trust_pool file __AZURITE_STORAGE__/ca_cert.pem
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
__ACCOUNT_NAME__.table.core.windows.net {
|
__ACCOUNT_NAME__.table.core.windows.net {
|
||||||
tls __AZURITE_DIR__/__ACCOUNT_NAME___cert.pem __AZURITE_DIR__/__ACCOUNT_NAME___key.pem
|
tls __AZURITE_STORAGE__/__ACCOUNT_NAME___cert.pem __AZURITE_STORAGE__/__ACCOUNT_NAME___key.pem
|
||||||
reverse_proxy localhost:10002
|
reverse_proxy https://__ACCOUNT_NAME__.table.core.windows.net:10002 {
|
||||||
|
transport http {
|
||||||
|
tls
|
||||||
|
tls_trust_pool file __AZURITE_STORAGE__/ca_cert.pem
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,13 +26,9 @@ RUN npm pkg set scripts.prepare="echo no-prepare"
|
|||||||
RUN npm ci --omit=dev --unsafe-perm
|
RUN npm ci --omit=dev --unsafe-perm
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
COPY ./cert-functions.sh ./
|
||||||
COPY ./entrypoint.sh .
|
COPY ./entrypoint.sh .
|
||||||
COPY ./Caddyfile.example .
|
COPY ./Caddyfile.example .
|
||||||
RUN chmod +x entrypoint.sh
|
RUN chmod +x entrypoint.sh
|
||||||
|
|
||||||
EXPOSE 443
|
|
||||||
EXPOSE 10000
|
|
||||||
EXPOSE 10001
|
|
||||||
EXPOSE 10002
|
|
||||||
|
|
||||||
ENTRYPOINT [ "/app/entrypoint.sh" ]
|
ENTRYPOINT [ "/app/entrypoint.sh" ]
|
||||||
|
|||||||
222
README.md
222
README.md
@@ -1,18 +1,65 @@
|
|||||||
# Azure Storage Emulator
|
# Azure Storage Emulator
|
||||||
|
|
||||||
This is a simple guide that will help you set up and run the Azure Storage Emulator on your local machine. The Azure Storage Emulator allows you to develop and test your applications that use Azure Storage services without needing an actual Azure subscription nor Internet connection.
|
This is a simple guide that will help you set up and run the Azure Storage Emulator on your local machine. The Azure Storage Emulator allows you to develop and test your applications that use Azure Storage services without needing an Azure subscription or an Internet connection.
|
||||||
|
|
||||||
## Prerequisites
|
Review the [Documentation](#reference) for more details on how to use the emulator and its features. This document covers a scenario where you want to run the emulator as close to the real Azure Storage service as possible, which means using triple HTTPS endpoints and OAuth simulation.
|
||||||
|
|
||||||
- Any system that supports fairly recent versions of Node.js (LTS vesions are recommended).
|
## Installation
|
||||||
- A Bash shell (a Linux or macOS system or Windows with WSL2 installed).
|
|
||||||
- Docker Community Edition or Docker Desktop (for running the emulator in a containerized environment).
|
|
||||||
- Apple `container` command (for running the containerized version of the emulator on macOS).
|
|
||||||
- (Optional) Caddy HTTP server (for running the emulator with triple HTTPS endpoints, as it would be in Azure).
|
|
||||||
|
|
||||||
## Native Installation
|
You can install and use the emulator in a few different ways, depending on your preferences and environment. The recommended way is to use a container runtime or Kubernetes, but you can also install it natively using Node.js.
|
||||||
|
|
||||||
To install the Azure Storage Emulator natively on your machine, follow these steps:
|
### Using a container runtime
|
||||||
|
|
||||||
|
To run the Azure Storage Emulator in a container, follow these steps:
|
||||||
|
|
||||||
|
1. Ensure that a container runtime is installed. This repository supports both Docker and Apple `container` command.
|
||||||
|
|
||||||
|
2. Build the emulator image using the provided Dockerfile:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./build.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Run the emulator container:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./start-azurite
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also use the included example `compose.yaml` file for running it using `docker compose` (or any other compose compatible CLI).
|
||||||
|
|
||||||
|
### Using Kubernetes
|
||||||
|
|
||||||
|
Use the example manifest `azurite-deployment.yaml`.
|
||||||
|
|
||||||
|
1. Create the required Secret from your local env file:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl create secret generic azurite-accounts \
|
||||||
|
--from-env-file="$HOME/.azurite/accounts.env"
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Deploy the manifest:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl apply -f azurite-deployment.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Get the Kubernetes Service external IP:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl get svc azurite -o jsonpath='{.status.loadBalancer.ingress[0].ip}'
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Add `/etc/hosts` entries on your workstation and point all Azurite account FQDNs to that Service IP (not `127.0.0.1`):
|
||||||
|
|
||||||
|
```
|
||||||
|
<service_ip> <accountname>.blob.core.windows.net <accountname>.queue.core.windows.net <accountname>.table.core.windows.net
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using Node.js
|
||||||
|
|
||||||
|
To install the Azure Storage Emulator natively on your machine, ensure you have Node.js (with npm) and Caddy HTTP Server installed, and follow these steps:
|
||||||
|
|
||||||
1. Clone the repository:
|
1. Clone the repository:
|
||||||
|
|
||||||
@@ -47,40 +94,167 @@ To install the Azure Storage Emulator natively on your machine, follow these ste
|
|||||||
rm -rf azurite
|
rm -rf azurite
|
||||||
```
|
```
|
||||||
|
|
||||||
## Running the Emulator
|
6. Create an `accounts.env` file in the same directory as the `run-server.sh` script with the following content:
|
||||||
|
|
||||||
There are two ways to run the Azure Storage Emulator: as a development server exposing the API on localhost using high ports (10000-10002) or as a fake Azure Storage service. The first one is easy, just run the `azurite` command in your terminal and that's it. The second one is a bit more complex, but it allows you to use the emulator as if it were the real Azure Storage service, and connect to it applications that do not support custom endpoints (Terraform azurerm state storage backend for example).
|
```bash
|
||||||
|
AZURITE_ACCOUNTS=accountname:accountkey
|
||||||
|
```
|
||||||
|
|
||||||
Run the `make-certs.sh` script to generate a self-signed CA certificate and a server certificate signed by that CA. The server certificate will be used by the emulator to serve HTTPS traffic, and the CA certificate can be imported into your system's trusted root certificates store to avoid security warnings when connecting to the emulator.
|
Replace `accountname` with the desired account name. Use OpenSSL to generate an account key.
|
||||||
|
|
||||||
The `make-certs.sh` script will create a storage directory for the emulator and place the generated PEM files there. The script will create a certificate for the `devstoreaccount1.blob.core.windows.net`. You can change that by adding you own storage account name as a single positional parameter. You will also need to map that name to `127.0.0.1` in your hosts file.
|
```bash
|
||||||
|
openssl rand -base64 32
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also generate a deterministic account key using any string as a seed:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
echo -n "your-seed-string" | base64
|
||||||
|
```
|
||||||
|
|
||||||
|
7. Add the following line to your `/etc/hosts` file to map the custom domain names to localhost:
|
||||||
|
|
||||||
|
```
|
||||||
|
127.0.0.1 <accountname>.blob.core.windows.net <accountname>.queue.core.windows.net <accountname>.table.core.windows.net
|
||||||
|
```
|
||||||
|
|
||||||
|
8. Run the server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./run-server.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
You can add the `--oauth` or `-o` flag to simulate OAuth authentication.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./run-server.sh --oauth
|
||||||
|
```
|
||||||
|
|
||||||
|
## Accessing the blob storage
|
||||||
|
|
||||||
|
### RClone
|
||||||
|
|
||||||
|
RClone is a command-line program to manage files on cloud storage. You can use it to interact with the Azure Storage Emulator the same way you would with the real Azure Storage service. Edit the rclone.conf file and add the following configuration:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[azurite]
|
||||||
|
type = azureblob
|
||||||
|
account = accountname
|
||||||
|
key = accountkey
|
||||||
|
```
|
||||||
|
|
||||||
|
or, if you want to use simulated OAuth authentication:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[azurite]
|
||||||
|
type = azureblob
|
||||||
|
account = accountname
|
||||||
|
env_auth = true
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, you can use rclone commands to interact with the emulator. For example, to list the containers in the blob service:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./make-certs.sh myaccount
|
rclone ls azurite:
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
> **Note**: On modern Linux distributions and MacOS systems the `rclone.conf` file is typically located at `~/.config/rclone/rclone.conf`.
|
||||||
127.0.0.1 devstoreaccount1.blob.core.windows.net devstoreaccount1.queue.core.windows.net devstoreaccount1.table.core.windows.net
|
|
||||||
|
### Terraform
|
||||||
|
|
||||||
|
Use the following Terraform Azure RM backend configuration to use the Azure Storage Emulator as the backend for storing Terraform state:
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
terraform {
|
||||||
|
backend "azurerm" {
|
||||||
|
storage_account_name = "accountname"
|
||||||
|
container_name = "tfstate"
|
||||||
|
key = "terraform.tfstate"
|
||||||
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Next, create an empty `accounts.env` file in the same directory. This file will be used by the emulator to retrieve the storage accounts names and access keys. You can use a single key or a pair of keys for each account. The format of the file is as follows:
|
and initialize the module:
|
||||||
|
|
||||||
```shell
|
```bash
|
||||||
AZURITE_ACCOUNTS="account1:key1,key2;account2:key1,key2;..."
|
terraform init -backend-config=access_key=__base64_encoded_account_key__
|
||||||
```
|
```
|
||||||
|
|
||||||
Use `add-account.sh` script to add at least one account to the `accounts.env` file. The script will generate random key for each added account. You can use `list-accounts.sh` script to list all accounts and their keys. You can also specify a password that will be used as the value for the key. The key is always Base64 encoded, and you password will also be Base64 encoded before being stored in the `accounts.env` file. The benefit of using a password is that the account key will be deterministic, so you can easily recreate the same account with the same key if needed.
|
> **Note**: Be aware that AI Agents may generate or suggest using the `endpoint` parameter, which will not work. You have to create *fake* account FQDNs in your `/etc/hosts` file as described in the installation steps.
|
||||||
|
|
||||||
Finally, run the emulator using the `run-emulator.sh` script.
|
|
||||||
|
|
||||||
You can specify `--oauth` to enable simulation of Entra ID Authentication. It requires the endpoints to be directly accessed via HTTPS.
|
You can use OAuth simulation with Terraform by adding the `use_azuread_auth` parameter to the backend configuration:
|
||||||
|
|
||||||
The `run-emulator.sh` will detect if Caddy HTTP server is installed on your system, and if it is, it will use it to serve the emulator on triple HTTPS endpoints (Blob, Queue and Table services) as it would be in Azure.
|
```hcl
|
||||||
|
terraform {
|
||||||
|
backend "azurerm" {
|
||||||
|
storage_account_name = "accountname"
|
||||||
|
container_name = "tfstate"
|
||||||
|
key = "terraform.tfstate"
|
||||||
|
use_azuread_auth = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Use `--no-caddy` to disable Caddy even if it is installed. It is required to test Entra ID Authentication simulation.
|
## Command Reference
|
||||||
|
|
||||||
|
### `run-server.sh`
|
||||||
|
|
||||||
|
The script is the entry point for starting the Azure Storage Emulator natively. It discovers the account name and key from the `accounts.env` file, generates the necessary SSL certificates, configures Caddy for HTTPS endpoints, and starts the Azurite server with the appropriate settings.
|
||||||
|
|
||||||
|
It accepts the following optional flag: `--oauth` or `-o`. It enables OAuth simulation for the emulator. When this flag is set, the emulator simulates OAuth authentication flows, allowing you to test scenarios that involve Entra ID authentication. It does not implement a real authentication flow; instead, it accepts any valid token.
|
||||||
|
|
||||||
|
The script assumes that both Azurite and Caddy are installed and available in the system's PATH. It also assumes that the `accounts.env` file is properly configured with at least one account name and key, and that the `/etc/hosts` file contains the necessary entries for the custom domain names, e.g. `accountname.blob.core.windows.net`, `accountname.queue.core.windows.net`, and `accountname.table.core.windows.net`.
|
||||||
|
|
||||||
|
The storage location is determined by the `AZURITE_DIR` environment variable, which defaults to the relative `storage` subdirectory of the current directory if not set. Ensure that the specified directory is writable by the user running the script, as Azurite will need to create and manage files for the emulated storage accounts.
|
||||||
|
|
||||||
|
The script will generate self-signed SSL certificates for the specified account name and store them in the storage directory. Caddy will be configured to use these certificates for the HTTPS endpoints. The emulator will be accessible at the following endpoints:
|
||||||
|
|
||||||
|
- Blob service: `https://accountname.blob.core.windows.net`
|
||||||
|
- Queue service: `https://accountname.queue.core.windows.net`
|
||||||
|
- Table service: `https://accountname.table.core.windows.net`
|
||||||
|
|
||||||
|
You need to add `storage/ca_cert.pem` as a trusted root certificate in your system to avoid SSL errors when connecting to the emulator.
|
||||||
|
|
||||||
|
For Linux-based systems, you can use the following commands to add the certificate to the trusted store:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo cp storage/ca_cert.pem /usr/local/share/ca-certificates/azurite_ca_cert.crt
|
||||||
|
sudo update-ca-certificates
|
||||||
|
```
|
||||||
|
|
||||||
|
For macOS, you can use the Keychain Access application to import the certificate and mark it as trusted. Windows users can use the Certificate Manager to import the certificate into the "Trusted Root Certification Authorities" store.
|
||||||
|
|
||||||
|
### `build.sh`
|
||||||
|
|
||||||
|
The script builds a container image for the Azure Storage Emulator using the provided Dockerfile. The image includes the Azurite server and Caddy HTTP server, configured to run the emulator with triple HTTPS endpoints and optional OAuth simulation. It does not require Azurite or Caddy to be installed on the host machine, as they are included in the container image.
|
||||||
|
|
||||||
|
Accepted flags:
|
||||||
|
|
||||||
|
- `--arch`: Specifies the target architecture for the container image. Supported values are `amd64` and `arm64`. If not provided, the script will build for the architecture of the host machine. It can be specified twice to build for both architectures.
|
||||||
|
- `--version`: Specifies the version tag for the built container image. The version value must correspond to a valid Azurite GitHub tag.
|
||||||
|
- `--latest`: Uses the latest released version of Azurite from GitHub as the base for the container image. This flag cannot be used together with `--version`.
|
||||||
|
- `--registry`: Specifies the container registry to which the built image will be pushed. If not provided, the image will only be built locally and not tagged with registry prefix.
|
||||||
|
|
||||||
|
### `start-azurite`
|
||||||
|
|
||||||
|
The script runs the Azure Storage Emulator using a supported container runtime (Docker or Apple `container` command). It accepts the same flag as `run-server.sh` to enable OAuth simulation (`--oauth` or `-o`). It also assumes `AZURITE_DIR` is either set in the environment or empty (not set), in which case it will default to the `./storage` subdirectory of the current directory. The script mounts the specified storage directory into the container, allowing you to persist data and access the generated SSL certificates on the host machine.
|
||||||
|
|
||||||
|
The default image name is `azurite:latest`, but it can be overridden by setting the `AZURITE_IMAGE` environment variable before running the script. For example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
AZURITE_IMAGE=myregistry/azurite:latest ./start-azurite
|
||||||
|
```
|
||||||
|
|
||||||
|
Both `AZURITE_DIR` and `AZURITE_IMAGE` should be set in the shell profile if you are running the emulator as a local Azure Storage replacement for development purposes.
|
||||||
|
|
||||||
|
> **Remember**: Make backups of the storage directory when the container is not running.
|
||||||
|
|
||||||
|
You have to use the same procedure as for `run-server.sh` to install the generated CA certificate as a trusted root certificate in your system to avoid SSL errors when connecting to the emulator.
|
||||||
|
|
||||||
## Reference
|
## Reference
|
||||||
|
|
||||||
- [Azure Storage Emulator GitHub Repository](https://github.com/azure/azurite)
|
- [Azure Storage Emulator GitHub Repository](https://github.com/azure/azurite)
|
||||||
- [Azure Storage Emulator Documentation](https://learn.microsoft.com/en-us/azure/storage/common/storage-use-azurite)
|
- [Azure Storage Emulator Documentation](https://learn.microsoft.com/en-us/azure/storage/common/storage-use-azurite)
|
||||||
- [Caddy Server](https://caddyserver.com)
|
- [Caddy Server](https://caddyserver.com)
|
||||||
|
- [RClone](https://rclone.org)
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
if [[ -z "$1" ]]; then
|
|
||||||
echo "Usage: $0 <account-name> [ password ]"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -z "$2" ]]; then
|
|
||||||
PASSWORD=$(openssl rand -base64 32)
|
|
||||||
else
|
|
||||||
PASSWORD=$(echo -n "$2" | base64)
|
|
||||||
fi
|
|
||||||
|
|
||||||
AZURITE_DIR="storage"
|
|
||||||
mkdir -p "$AZURITE_DIR"
|
|
||||||
AZURITE_ACCOUNTS_FILE="$AZURITE_DIR/accounts.env"
|
|
||||||
|
|
||||||
if [[ -f "$AZURITE_ACCOUNTS_FILE" ]]; then
|
|
||||||
. "$AZURITE_ACCOUNTS_FILE"
|
|
||||||
STORAGE_ACCOUNTS=($(echo "$AZURITE_ACCOUNTS" | tr ';' ' '))
|
|
||||||
else
|
|
||||||
# No accounts file, start with an empty array
|
|
||||||
STORAGE_ACCOUNTS=()
|
|
||||||
fi
|
|
||||||
|
|
||||||
STORAGE_ACCOUNTS+=("$1:$PASSWORD")
|
|
||||||
printf 'AZURITE_ACCOUNTS="%s"\n' $(IFS=';'; echo "${STORAGE_ACCOUNTS[*]}") > "$AZURITE_ACCOUNTS_FILE"
|
|
||||||
69
azurite-deployment.yaml
Normal file
69
azurite-deployment.yaml
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: azurite-storage
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: azurite
|
||||||
|
app.kubernetes.io/component: storage-emulator
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 100Mi
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: azurite
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: azurite
|
||||||
|
app.kubernetes.io/component: storage-emulator
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/name: azurite
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: azurite
|
||||||
|
app.kubernetes.io/component: storage-emulator
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: azurite
|
||||||
|
image: azurite:latest
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
args:
|
||||||
|
- --oauth
|
||||||
|
ports:
|
||||||
|
- name: https
|
||||||
|
containerPort: 443
|
||||||
|
protocol: TCP
|
||||||
|
envFrom:
|
||||||
|
- secretRef:
|
||||||
|
name: azurite-accounts
|
||||||
|
volumeMounts:
|
||||||
|
- name: storage
|
||||||
|
mountPath: /storage
|
||||||
|
volumes:
|
||||||
|
- name: storage
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: azurite-storage
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: azurite
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: azurite
|
||||||
|
app.kubernetes.io/component: storage-emulator
|
||||||
|
spec:
|
||||||
|
type: LoadBalancer
|
||||||
|
selector:
|
||||||
|
app.kubernetes.io/name: azurite
|
||||||
|
ports:
|
||||||
|
- name: https
|
||||||
|
port: 443
|
||||||
|
targetPort: 443
|
||||||
|
protocol: TCP
|
||||||
55
build.sh
Normal file → Executable file
55
build.sh
Normal file → Executable file
@@ -1,8 +1,57 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
if command -v dockerd &> /dev/null; then
|
|
||||||
docker build -t azurite:latest .
|
ARCH=()
|
||||||
|
VERSION_ARG=()
|
||||||
|
VERSION=""
|
||||||
|
REGISTRY=""
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--arch|-a)
|
||||||
|
ARCH+=("--arch" "$2")
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--version|-v)
|
||||||
|
VERSION="$2"
|
||||||
|
VERSION_ARG+=("--build-arg" "VERSION=$VERSION")
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--latest|-l)
|
||||||
|
VERSION="latest"
|
||||||
|
LATEST_TAG=$(git ls-remote --tags --refs --sort='v:refname' https://github.com/Azure/Azurite | tail -n1 | awk -F/ '{ print $3 }')
|
||||||
|
VERSION_ARG+=("--build-arg" "VERSION=$LATEST_TAG")
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--registry|-r)
|
||||||
|
REGISTRY="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unknown argument: $1" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -z "$REGISTRY" ]]; then
|
||||||
|
IMAGE="azurite"
|
||||||
|
else
|
||||||
|
IMAGE="$REGISTRY/azurite"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$VERSION" ]]; then
|
||||||
|
TAG_ARGS=("--tag" "$IMAGE:latest")
|
||||||
|
elif [[ "$VERSION" == "latest" ]]; then
|
||||||
|
TAG_ARGS=("--tag" "$IMAGE:${LATEST_TAG#v}" "--tag" "$IMAGE:latest")
|
||||||
|
else
|
||||||
|
TAG_ARGS=("--tag" "$IMAGE:${VERSION#v}")
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Effective command line arguments:" ${ARCH[@]} ${VERSION_ARG[@]} ${TAG_ARGS[@]}
|
||||||
|
if command -v docker &> /dev/null; then
|
||||||
|
docker build "${ARCH[@]}" "${VERSION_ARG[@]}" "${TAG_ARGS[@]}" .
|
||||||
elif command -v container &> /dev/null; then
|
elif command -v container &> /dev/null; then
|
||||||
container build -t azurite:latest .
|
container build "${ARCH[@]}" "${VERSION_ARG[@]}" "${TAG_ARGS[@]}" .
|
||||||
else
|
else
|
||||||
echo "Neither supported container runtime found." >&2
|
echo "Neither supported container runtime found." >&2
|
||||||
exit 1
|
exit 1
|
||||||
|
|||||||
176
cert-functions.sh
Normal file
176
cert-functions.sh
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
function make_ca() {
|
||||||
|
# Use the provided directory argument or default to AZURITE_DIR if not provided
|
||||||
|
local CERT_DIR="$1"
|
||||||
|
local CA_NAME="$2"
|
||||||
|
|
||||||
|
if [[ -z "$CERT_DIR" || -z "$CA_NAME" || ! -d "$CERT_DIR" ]]; then
|
||||||
|
echo "ERROR: Certificate directory $CERT_DIR does not exist."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Generate CA certificate and key if they don't exist
|
||||||
|
if [[ ! -f "$CERT_DIR/ca_cert.pem" || ! -f "$CERT_DIR/ca_key.pem" ]]; then
|
||||||
|
echo "Generating CA certificate '$CA_NAME' and key..."
|
||||||
|
if ! openssl req \
|
||||||
|
-x509 \
|
||||||
|
-newkey rsa:4096 \
|
||||||
|
-keyout "$CERT_DIR/ca_key.pem" \
|
||||||
|
-out "$CERT_DIR/ca_cert.pem" \
|
||||||
|
-days 3650 \
|
||||||
|
-nodes \
|
||||||
|
-subj "/CN=${CA_NAME}" \
|
||||||
|
-text \
|
||||||
|
-addext "basicConstraints=critical,CA:TRUE,pathlen:0"; then
|
||||||
|
echo "ERROR: Failed to generate CA certificate and key." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function _is_ip() {
|
||||||
|
if [[ "$1" =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}$ ]]; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function _is_dns() {
|
||||||
|
if [[ "$1" =~ ^[a-z0-9-]+(\.[a-z0-9-]+)*$ ]]; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function make_server_cert() {
|
||||||
|
local CERT_DIR="$1"
|
||||||
|
local CERT_SUBJECT_NAME="$2"
|
||||||
|
shift 2
|
||||||
|
|
||||||
|
if [[ -z "$CERT_DIR" || ! -d "$CERT_DIR" ]]; then
|
||||||
|
echo "ERROR: Certificate directory $CERT_DIR does not exist."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$CERT_SUBJECT_NAME" ]]; then
|
||||||
|
echo "ERROR: Subject name is required." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _is_dns "$CERT_SUBJECT_NAME"; then
|
||||||
|
echo "ERROR: Invalid subject name '$CERT_SUBJECT_NAME'. Must be a valid DNS name." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -f "$CERT_DIR/ca_cert.pem" || ! -f "$CERT_DIR/ca_key.pem" ]]; then
|
||||||
|
echo "ERROR: CA certificate and key not found in $CERT_DIR. Please call make_ca first." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Calculate the "account" name from the subject name, the hostname part before the first dot
|
||||||
|
local CERT_NAME="${CERT_SUBJECT_NAME%%.*}"
|
||||||
|
|
||||||
|
# Start with the subjectAltName extension containing the main DNS name
|
||||||
|
local SANS=("DNS:${CERT_SUBJECT_NAME}")
|
||||||
|
|
||||||
|
# Combine the remaining arguments into a single string for the subjectAltName extension
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
if _is_ip "$1"; then
|
||||||
|
SANS+=("IP:$1")
|
||||||
|
elif _is_dns "$1"; then
|
||||||
|
SANS+=("DNS:$1")
|
||||||
|
else
|
||||||
|
echo "ERROR: Invalid SAN entry '$1'" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
# Join the SAN entries with commas for the OpenSSL command
|
||||||
|
local SANS_EXT="subjectAltName=$(IFS=,; echo "${SANS[*]}")"
|
||||||
|
|
||||||
|
echo "Generating server certificate for '$CERT_SUBJECT_NAME' with SANs:"
|
||||||
|
for san in "${SANS[@]}"; do
|
||||||
|
echo " - $san"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Generate server certificate and key if they don't exist
|
||||||
|
if [[ ! -f "$CERT_DIR/${CERT_NAME}_cert.pem" || ! -f "$CERT_DIR/${CERT_NAME}_key.pem" ]]; then
|
||||||
|
echo "Generating server certificate and key..."
|
||||||
|
if ! openssl req \
|
||||||
|
-newkey rsa:4096 \
|
||||||
|
-keyout "$CERT_DIR/${CERT_NAME}_key.pem" \
|
||||||
|
-nodes \
|
||||||
|
-subj "/CN=${CERT_SUBJECT_NAME}" \
|
||||||
|
-addext "basicConstraints=critical,CA:FALSE" \
|
||||||
|
-addext "keyUsage=digitalSignature,keyEncipherment" \
|
||||||
|
-addext "extendedKeyUsage=serverAuth,clientAuth" \
|
||||||
|
-addext "$SANS_EXT" \
|
||||||
|
| openssl x509 \
|
||||||
|
-req \
|
||||||
|
-CA "$CERT_DIR/ca_cert.pem" \
|
||||||
|
-CAkey "$CERT_DIR/ca_key.pem" \
|
||||||
|
-set_serial "0x$(openssl rand -hex 16)" \
|
||||||
|
-copy_extensions copyall \
|
||||||
|
-days 365 \
|
||||||
|
-text \
|
||||||
|
-out "$CERT_DIR/${CERT_NAME}_cert.pem"; then
|
||||||
|
echo "ERROR: Failed to generate server certificate and key." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function make_pfx() {
|
||||||
|
local CERT_DIR="$1"
|
||||||
|
local CERT_NAME="$2"
|
||||||
|
local PFX_PASSWORD="${3:-}"
|
||||||
|
|
||||||
|
if [[ -z "$CERT_DIR" || ! -d "$CERT_DIR" ]]; then
|
||||||
|
echo "ERROR: Certificate directory $CERT_DIR does not exist."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$CERT_NAME" ]]; then
|
||||||
|
echo "ERROR: Certificate name is required." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -f "$CERT_DIR/${CERT_NAME}_cert.pem" || ! -f "$CERT_DIR/${CERT_NAME}_key.pem" ]]; then
|
||||||
|
echo "ERROR: Server certificate and key not found in $CERT_DIR. Please call make_server_cert first." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -f "$CERT_DIR/ca_cert.pem" || ! -f "$CERT_DIR/ca_key.pem" ]]; then
|
||||||
|
echo "ERROR: CA certificate and key not found in $CERT_DIR. Please call make_ca first." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$PFX_PASSWORD" ]]; then
|
||||||
|
PFX_PASSWORD="changeit"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -f "$CERT_DIR/${CERT_NAME}.pfx" ]]; then
|
||||||
|
echo -n "Generating PKCS#12 (PFX) file..."
|
||||||
|
if ! openssl pkcs12 \
|
||||||
|
-export -out "$CERT_DIR/${CERT_NAME}.pfx" \
|
||||||
|
-inkey "$CERT_DIR/${CERT_NAME}_key.pem" \
|
||||||
|
-in "$CERT_DIR/${CERT_NAME}_cert.pem" \
|
||||||
|
-certfile "$CERT_DIR/ca_cert.pem" \
|
||||||
|
-password pass:"$PFX_PASSWORD"; then
|
||||||
|
echo "ERROR: Failed to generate PKCS#12 (PFX) file." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
echo "done."
|
||||||
|
else
|
||||||
|
echo "PKCS#12 (PFX) file already exists, aborting generation."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
13
compose.yaml
Normal file
13
compose.yaml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
services:
|
||||||
|
azurite:
|
||||||
|
image: azurite:latest
|
||||||
|
container_name: azurite
|
||||||
|
command:
|
||||||
|
- --oauth
|
||||||
|
ports:
|
||||||
|
- "443:443"
|
||||||
|
env_file:
|
||||||
|
- $HOME/.azurite/accounts.env
|
||||||
|
volumes:
|
||||||
|
- $HOME/.azurite/storage:/storage
|
||||||
|
restart: unless-stopped
|
||||||
158
entrypoint.sh
158
entrypoint.sh
@@ -2,77 +2,39 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
function make_ca() {
|
# Include certificate generation functions.
|
||||||
# Use the provided directory argument or default to AZURITE_DIR if not provided
|
. /app/cert-functions.sh
|
||||||
local CERT_DIR="${1:-$AZURITE_DIR}"
|
|
||||||
|
|
||||||
if [[ ! -d "$CERT_DIR" ]]; then
|
function make_account_cert() {
|
||||||
echo "ERROR: Certificate directory $CERT_DIR does not exist."
|
local CERT_DIR="$1"
|
||||||
return 1
|
local ACCOUNT_NAME="${2:-devstoreaccount1}"
|
||||||
fi
|
# -addext "subjectAltName=DNS:${ACCOUNT_NAME}.blob.core.windows.net,DNS:${ACCOUNT_NAME}.queue.core.windows.net,DNS:${ACCOUNT_NAME}.table.core.windows.net,DNS:${ACCOUNT_NAME}.blob.localhost,DNS:${ACCOUNT_NAME}.queue.localhost,DNS:${ACCOUNT_NAME}.table.localhost,DNS:localhost,IP:127.0.0.1"
|
||||||
|
|
||||||
# Generate CA certificate and key if they don't exist
|
make_server_cert "$CERT_DIR" "${ACCOUNT_NAME}.blob.core.windows.net" \
|
||||||
if [[ ! -f "$CERT_DIR/ca_cert.pem" || ! -f "$CERT_DIR/ca_key.pem" ]]; then
|
"${ACCOUNT_NAME}.queue.core.windows.net" \
|
||||||
echo "Generating CA certificate and key..."
|
"${ACCOUNT_NAME}.table.core.windows.net" \
|
||||||
if ! openssl req \
|
"${ACCOUNT_NAME}.blob.localhost" \
|
||||||
-x509 \
|
"${ACCOUNT_NAME}.queue.localhost" \
|
||||||
-newkey rsa:4096 \
|
"${ACCOUNT_NAME}.table.localhost" \
|
||||||
-keyout "$CERT_DIR/ca_key.pem" \
|
"localhost" \
|
||||||
-out "$CERT_DIR/ca_cert.pem" \
|
"127.0.0.1"
|
||||||
-days 3650 \
|
|
||||||
-nodes \
|
|
||||||
-subj "/CN=Azurite CA" \
|
|
||||||
-text \
|
|
||||||
-addext "basicConstraints=critical,CA:TRUE,pathlen:0"; then
|
|
||||||
echo "Error: Failed to generate CA certificate and key." >&2
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function make_server_cert() {
|
return $?
|
||||||
local ACCOUNT_NAME="${1:-devstoreaccount1}"
|
|
||||||
local CERT_DIR="${2:-$AZURITE_DIR}"
|
|
||||||
|
|
||||||
if [[ ! -d "$CERT_DIR" ]]; then
|
|
||||||
echo "ERROR: Certificate directory $CERT_DIR does not exist."
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Generate server certificate and key if they don't exist
|
|
||||||
if [[ ! -f "$CERT_DIR/${ACCOUNT_NAME}_cert.pem" || ! -f "$CERT_DIR/${ACCOUNT_NAME}_key.pem" ]]; then
|
|
||||||
echo "Generating server certificate and key..."
|
|
||||||
if ! openssl req \
|
|
||||||
-newkey rsa:4096 \
|
|
||||||
-keyout "$CERT_DIR/${ACCOUNT_NAME}_key.pem" \
|
|
||||||
-nodes \
|
|
||||||
-subj "/CN=${ACCOUNT_NAME}.blob.core.windows.net" \
|
|
||||||
-addext "basicConstraints=critical,CA:FALSE" \
|
|
||||||
-addext "keyUsage=digitalSignature,keyEncipherment" \
|
|
||||||
-addext "extendedKeyUsage=serverAuth,clientAuth" \
|
|
||||||
-addext "subjectAltName=DNS:${ACCOUNT_NAME}.blob.core.windows.net,DNS:${ACCOUNT_NAME}.queue.core.windows.net,DNS:${ACCOUNT_NAME}.table.core.windows.net,DNS:localhost,IP:127.0.0.1" \
|
|
||||||
| openssl x509 \
|
|
||||||
-req \
|
|
||||||
-CA "$CERT_DIR/ca_cert.pem" \
|
|
||||||
-CAkey "$CERT_DIR/ca_key.pem" \
|
|
||||||
-set_serial "0x$(openssl rand -hex 16)" \
|
|
||||||
-copy_extensions copyall \
|
|
||||||
-days 365 \
|
|
||||||
-text \
|
|
||||||
-out "$CERT_DIR/${ACCOUNT_NAME}_cert.pem"; then
|
|
||||||
echo "Error: Failed to generate server certificate and key." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Setup default storage location, account name and accounts file path.
|
# Setup default storage location, account name and accounts file path.
|
||||||
AZURITE_DIR="/storage"
|
AZURITE_STORAGE="${AZURITE_STORAGE:-/storage}"
|
||||||
|
|
||||||
# Check, if the AZURITE_ACCOUNTS variable is set
|
# Check, if the AZURITE_ACCOUNTS variable is set
|
||||||
if [[ -z "$AZURITE_ACCOUNTS" ]]; then
|
if [[ -z "$AZURITE_ACCOUNTS" ]]; then
|
||||||
# Generate a default account
|
if [[ -f "$AZURITE_STORAGE/accounts.env" ]]; then
|
||||||
export AZURITE_ACCOUNTS="devstoreaccount1:$(openssl rand -base64 32)"
|
set -a
|
||||||
|
source "$AZURITE_STORAGE/accounts.env"
|
||||||
|
set +a
|
||||||
|
else
|
||||||
|
# Generate a default account
|
||||||
|
export AZURITE_ACCOUNTS="devstoreaccount1:$(openssl rand -base64 32)"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Look up the account name from the AZURITE_ACCOUNTS variable, which is in the format "accountName:accountKey1:accountKey2;accountName2:accountKey1:accountKey2"
|
# Look up the account name from the AZURITE_ACCOUNTS variable, which is in the format "accountName:accountKey1:accountKey2;accountName2:accountKey1:accountKey2"
|
||||||
@@ -80,35 +42,27 @@ ACCOUNT_NAME=$(echo "$AZURITE_ACCOUNTS" | cut -f 1 -d ';' | cut -f 1 -d ':')
|
|||||||
|
|
||||||
# Ensure /etc/hosts contains an entry the Azure endpoint names,
|
# Ensure /etc/hosts contains an entry the Azure endpoint names,
|
||||||
# so Caddy can route requests to the correct service based on the hostname.
|
# so Caddy can route requests to the correct service based on the hostname.
|
||||||
if ! grep -qE "${ACCOUNT_NAME}\.blob\.core\.windows\.net" /etc/hosts ||
|
if grep -q "${ACCOUNT_NAME}" /etc/hosts; then
|
||||||
! grep -qE "${ACCOUNT_NAME}\.queue\.core\.windows\.net" /etc/hosts ||
|
cp -a /etc/hosts /etc/hosts.bak && sed -E "/${ACCOUNT_NAME}/d" /etc/hosts > /etc/hosts
|
||||||
! grep -qE "${ACCOUNT_NAME}\.table\.core\.windows\.net" /etc/hosts; then
|
|
||||||
echo -e "\n127.0.0.1\t${ACCOUNT_NAME}.blob.core.windows.net ${ACCOUNT_NAME}.queue.core.windows.net ${ACCOUNT_NAME}.table.core.windows.net" >> /etc/hosts
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
CADDY=true
|
cat <<EOF >> /etc/hosts
|
||||||
AZURITE_SSL=""
|
|
||||||
CERT_ARGS=()
|
127.0.0.1 ${ACCOUNT_NAME}.blob.core.windows.net ${ACCOUNT_NAME}.queue.core.windows.net ${ACCOUNT_NAME}.table.core.windows.net ${ACCOUNT_NAME}.blob.localhost ${ACCOUNT_NAME}.queue.localhost ${ACCOUNT_NAME}.table.localhost
|
||||||
BLOB_ARGS=()
|
EOF
|
||||||
|
|
||||||
OAUTH_ARGS=()
|
OAUTH_ARGS=()
|
||||||
|
NO_CADDY=
|
||||||
|
|
||||||
while [[ $# -gt 0 ]]; do
|
while [[ $# -gt 0 ]]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
--oauth)
|
--oauth)
|
||||||
OAUTH_ARGS=("--oauth" "basic")
|
OAUTH_ARGS=("--oauth" "basic")
|
||||||
# Ensure Caddy is disabled when using OAuth, as Azurite does not support OAuth behind a reverse proxy.
|
# Ensure Caddy is disabled when using OAuth, as Azurite does not support OAuth behind a reverse proxy.
|
||||||
CADDY=""
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
--ssl)
|
|
||||||
AZURITE_SSL=true
|
|
||||||
# Ensure Caddy is disabled when using Azurite's built-in SSL support.
|
|
||||||
CADDY=""
|
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
--no-caddy)
|
--no-caddy)
|
||||||
# Disable Caddy on request.
|
NO_CADDY=true
|
||||||
CADDY=""
|
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
@@ -119,37 +73,31 @@ while [[ $# -gt 0 ]]; do
|
|||||||
done
|
done
|
||||||
|
|
||||||
# Ensure certificates are generated before starting Azurite or Caddy.
|
# Ensure certificates are generated before starting Azurite or Caddy.
|
||||||
if [[ -n "$AZURITE_SSL" || -n "$CADDY" ]]; then
|
if ! make_ca "$AZURITE_STORAGE" "Azurite CA $(date +%Y.%m)"; then
|
||||||
if ! make_ca "$AZURITE_DIR"; then
|
echo "Error: Failed to create CA certificate and key." >&2
|
||||||
echo "Error: Failed to create CA certificate and key." >&2
|
exit 1
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! make_server_cert "$ACCOUNT_NAME" "$AZURITE_DIR"; then
|
|
||||||
echo "Error: Failed to create server certificate and key." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -n "$CADDY" ]]; then
|
if ! make_account_cert "$AZURITE_STORAGE" "$ACCOUNT_NAME"; then
|
||||||
# If using Caddy, it will reverse proxy blob, queue, and table endpoints to different ports on localhost,
|
echo "Error: Failed to create server certificate and key." >&2
|
||||||
# allowing simultaneous access to all services on a single HTTPS port.
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$NO_CADDY" ]]; then
|
||||||
# Generate a Caddyfile configuration based on the account name and storage directory.
|
# Generate a Caddyfile configuration based on the account name and storage directory.
|
||||||
sed -E "s/__ACCOUNT_NAME__/${ACCOUNT_NAME}/g; s|__AZURITE_DIR__|${AZURITE_DIR}|g" /app/Caddyfile.example > "$AZURITE_DIR/Caddyfile"
|
sed -E "s/__ACCOUNT_NAME__/${ACCOUNT_NAME}/g; s|__AZURITE_STORAGE__|${AZURITE_STORAGE}|g" /app/Caddyfile.example > "$AZURITE_STORAGE/Caddyfile"
|
||||||
echo "Starting Caddy server..."
|
# Start Caddy in the background to handle HTTPS requests and route them to Azurite.
|
||||||
caddy start --config "$AZURITE_DIR/Caddyfile" # Use start not run, start does not block the shell process.
|
caddy start --config "$AZURITE_STORAGE/Caddyfile" # Use start not run, start does not block the shell process.
|
||||||
|
HOST_ARGS=("--blobHost" "127.0.0.1" "--queueHost" "127.0.0.1" "--tableHost" "127.0.0.1")
|
||||||
else
|
else
|
||||||
# If not using Caddy, configure Azurite to listen on all interfaces and use the generated self-signed certificate.
|
HOST_ARGS=("--blobHost" "0.0.0.0" "--queueHost" "0.0.0.0" "--tableHost" "0.0.0.0")
|
||||||
# This mode does not require Caddy, but it will not allow simultaneous access to the table and queue services on the same HTTPS port.
|
|
||||||
if [[ -n "$AZURITE_SSL" ]]; then
|
|
||||||
BLOB_ARGS=("--blobHost" "0.0.0.0" "--blobPort" "443")
|
|
||||||
CERT_ARGS=("--key" "$AZURITE_DIR/${ACCOUNT_NAME}_key.pem" "--cert" "$AZURITE_DIR/${ACCOUNT_NAME}_cert.pem")
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
PORT_ARGS=("--blobPort" "10000" "--queuePort" "10001" "--tablePort" "10002")
|
||||||
|
|
||||||
|
# Start Azurite with the appropriate arguments based on the configuration.
|
||||||
exec node /app/azurite/src/azurite.js \
|
exec node /app/azurite/src/azurite.js \
|
||||||
--disableTelemetry \
|
--disableTelemetry \
|
||||||
--location "$AZURITE_DIR" \
|
--location "$AZURITE_STORAGE" \
|
||||||
"${CERT_ARGS[@]}" \
|
--key "$AZURITE_STORAGE/${ACCOUNT_NAME}_key.pem" --cert "$AZURITE_STORAGE/${ACCOUNT_NAME}_cert.pem" \
|
||||||
"${BLOB_ARGS[@]}" \
|
"${HOST_ARGS[@]}" "${PORT_ARGS[@]}" "${OAUTH_ARGS[@]}"
|
||||||
"${OAUTH_ARGS[@]}"
|
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
AZURITE_DIR="storage"
|
|
||||||
|
|
||||||
if [[ ! -d "$AZURITE_DIR" ]]; then
|
|
||||||
echo "No accounts found"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
AZURITE_ACCOUNTS_FILE="$AZURITE_DIR/accounts.env"
|
|
||||||
. $AZURITE_ACCOUNTS_FILE
|
|
||||||
|
|
||||||
while IFS=';' read -ra ACCOUNTS; do
|
|
||||||
for ACCOUNT in "${ACCOUNTS[@]}"; do
|
|
||||||
IFS=':' read -ra KV <<< "$ACCOUNT"
|
|
||||||
echo "Account: ${KV[0]}"
|
|
||||||
echo "Password: $(echo -n "${KV[1]}")"
|
|
||||||
echo
|
|
||||||
done
|
|
||||||
done <<< "$AZURITE_ACCOUNTS"
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
CERT_DIR="./storage"
|
|
||||||
|
|
||||||
mkdir -p "$CERT_DIR"
|
|
||||||
|
|
||||||
ACCOUNT_NAME="${1:-devstoreaccount1}"
|
|
||||||
|
|
||||||
# Generate CA certificate and key if they don't exist
|
|
||||||
if [[ ! -f "$CERT_DIR/ca_cert.pem" || ! -f "$CERT_DIR/ca_key.pem" ]]; then
|
|
||||||
echo "Generating CA certificate and key..."
|
|
||||||
if ! openssl req \
|
|
||||||
-x509 \
|
|
||||||
-newkey rsa:4096 \
|
|
||||||
-keyout "$CERT_DIR/ca_key.pem" \
|
|
||||||
-out "$CERT_DIR/ca_cert.pem" \
|
|
||||||
-days 3650 \
|
|
||||||
-nodes \
|
|
||||||
-subj "/CN=Azurite CA" \
|
|
||||||
-text \
|
|
||||||
-addext "basicConstraints=critical,CA:TRUE,pathlen:0"; then
|
|
||||||
echo "Error: Failed to generate CA certificate and key." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Generate server certificate and key if they don't exist
|
|
||||||
if [[ ! -f "$CERT_DIR/server_cert.pem" || ! -f "$CERT_DIR/server_key.pem" ]]; then
|
|
||||||
echo "Generating server certificate and key..."
|
|
||||||
if ! openssl req \
|
|
||||||
-newkey rsa:4096 \
|
|
||||||
-keyout "$CERT_DIR/server_key.pem" \
|
|
||||||
-nodes \
|
|
||||||
-subj "/CN=${ACCOUNT_NAME}.blob.core.windows.net" \
|
|
||||||
-addext "basicConstraints=critical,CA:FALSE" \
|
|
||||||
-addext "keyUsage=digitalSignature,keyEncipherment" \
|
|
||||||
-addext "extendedKeyUsage=serverAuth,clientAuth" \
|
|
||||||
-addext "subjectAltName=DNS:${ACCOUNT_NAME}.blob.core.windows.net,DNS:${ACCOUNT_NAME}.queue.core.windows.net,DNS:${ACCOUNT_NAME}.table.core.windows.net,DNS:localhost,IP:127.0.0.1" \
|
|
||||||
| openssl x509 \
|
|
||||||
-req \
|
|
||||||
-CA "$CERT_DIR/ca_cert.pem" \
|
|
||||||
-CAkey "$CERT_DIR/ca_key.pem" \
|
|
||||||
-set_serial "0x$(openssl rand -hex 16)" \
|
|
||||||
-copy_extensions copyall \
|
|
||||||
-days 365 \
|
|
||||||
-text \
|
|
||||||
-out "$CERT_DIR/server_cert.pem"; then
|
|
||||||
echo "Error: Failed to generate server certificate and key." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
105
run-server.sh
105
run-server.sh
@@ -1,67 +1,86 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
# Include certificate generation functions.
|
||||||
|
. "$SCRIPT_DIR/cert-functions.sh"
|
||||||
|
|
||||||
AZURITE_DIR="$SCRIPT_DIR/storage"
|
AZURITE_DIR="$SCRIPT_DIR/storage"
|
||||||
|
|
||||||
if [[ ! -d "$AZURITE_DIR" ]]; then
|
if [[ ! -d "$AZURITE_DIR" ]]; then
|
||||||
echo "No accounts found"
|
echo "No accounts found"
|
||||||
exit 0
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$AZURITE_ACCOUNTS" && -f "$SCRIPT_DIR/accounts.env" ]]; then
|
||||||
|
set -a
|
||||||
|
. "$SCRIPT_DIR/accounts.env"
|
||||||
|
set +a
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Look up the account name from the AZURITE_ACCOUNTS variable, which is in the format "accountName:accountKey1:accountKey2;accountName2:accountKey1:accountKey2"
|
||||||
|
ACCOUNT_NAME=$(echo "$AZURITE_ACCOUNTS" | cut -f 1 -d ';' | cut -f 1 -d ':')
|
||||||
|
|
||||||
|
# Ensure /etc/hosts contains an entry the Azure endpoint names,
|
||||||
|
# so Caddy can route requests to the correct service based on the hostname.
|
||||||
|
if ! grep -qE "${ACCOUNT_NAME}\.blob\.core\.windows\.net" /etc/hosts ||
|
||||||
|
! grep -qE "${ACCOUNT_NAME}\.queue\.core\.windows\.net" /etc/hosts ||
|
||||||
|
! grep -qE "${ACCOUNT_NAME}\.table\.core\.windows\.net" /etc/hosts; then
|
||||||
|
echo "ERROR: /etc/hosts does not contain entries for the Azure endpoints. Please ensure the following line is present in /etc/hosts:"
|
||||||
|
echo -e "\n127.0.0.1\t${ACCOUNT_NAME}.blob.core.windows.net ${ACCOUNT_NAME}.queue.core.windows.net ${ACCOUNT_NAME}.table.core.windows.net"
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! command -v azurite &> /dev/null; then
|
if ! command -v azurite &> /dev/null; then
|
||||||
echo "Azurite is not installed. Please install it with 'npm install -g azurite'"
|
echo "ERROR: Azurite is not installed. Please install it with 'npm install -g azurite'"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if command -v caddy &> /dev/null; then
|
if ! command -v caddy &> /dev/null; then
|
||||||
CADDY=true
|
echo "ERROR: Caddy is not installed. Please install it from https://caddyserver.com/docs/install or with 'brew install caddy' on macOS."
|
||||||
else
|
exit 1
|
||||||
CADDY=""
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
OAUTH_ARGS=()
|
OAUTH_ARGS=()
|
||||||
|
|
||||||
while [[ $# -gt 0 ]]; do
|
while [[ $# -gt 0 ]]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
--oauth)
|
--oauth|-o)
|
||||||
OAUTH_ARGS=("--oauth" "basic")
|
OAUTH_ARGS=("--oauth" "basic")
|
||||||
shift
|
# Ensure Caddy is disabled when using OAuth, as Azurite does not support OAuth behind a reverse proxy.
|
||||||
;;
|
shift
|
||||||
--no-caddy)
|
;;
|
||||||
CADDY=""
|
*)
|
||||||
shift
|
echo "Unknown argument: $1"
|
||||||
;;
|
exit 1
|
||||||
*)
|
;;
|
||||||
echo "Unknown argument: $1"
|
esac
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
done
|
||||||
|
|
||||||
AZURITE_ACCOUNTS_FILE="$AZURITE_DIR/accounts.env"
|
# Ensure certificates are generated before starting Azurite or Caddy.
|
||||||
set -a
|
if ! make_ca "$AZURITE_DIR"; then
|
||||||
. $AZURITE_ACCOUNTS_FILE
|
echo "Error: Failed to create CA certificate and key." >&2
|
||||||
set +a
|
exit 1
|
||||||
|
|
||||||
if [[ -n "$CADDY" ]]; then
|
|
||||||
# If using Caddy, it will reverse proxy blob, queue, and table endpoints to different ports on localhost,
|
|
||||||
# allowing simultaneous access to all services on a single HTTPS port.
|
|
||||||
echo "Starting Caddy server..."
|
|
||||||
caddy start --config Caddyfile # Use start not run, start does not block the shell process.
|
|
||||||
trap "echo 'Stopping Caddy server...'; caddy stop" EXIT INT TERM HUP KILL STOP
|
|
||||||
CERT_ARGS=()
|
|
||||||
BLOB_ARGS=()
|
|
||||||
OAUTH_ARGS=() # Ensure OAuth is disabled when using Caddy, as Azurite does not support OAuth in reverse proxy mode.
|
|
||||||
else
|
|
||||||
# If not using Caddy, configure Azurite to listen on all interfaces and use the generated self-signed certificate.
|
|
||||||
# This mode does not require Caddy, but it will not allow simultaneous access to the table and queue services on the same HTTPS port.
|
|
||||||
BLOB_ARGS=("--blobHost" "0.0.0.0" "--blobPort" "443")
|
|
||||||
CERT_ARGS=("--key" "$AZURITE_DIR/server_key.pem" "--cert" "$AZURITE_DIR/server_cert.pem")
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if ! make_server_cert "$ACCOUNT_NAME" "$AZURITE_DIR"; then
|
||||||
|
echo "Error: Failed to create server certificate and key." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
CERT_ARGS=()
|
||||||
|
|
||||||
|
# Generate a Caddyfile configuration based on the account name and storage directory.
|
||||||
|
sed -E "s/__ACCOUNT_NAME__/${ACCOUNT_NAME}/g; s|__AZURITE_DIR__|${AZURITE_DIR}|g" "$SCRIPT_DIR/Caddyfile.example" > "$AZURITE_DIR/Caddyfile"
|
||||||
|
|
||||||
|
echo "Starting Caddy server..."
|
||||||
|
caddy start --config "$AZURITE_DIR/Caddyfile" # Use start not run, start does not block the shell process.
|
||||||
|
trap "echo 'Stopping Caddy server...'; caddy stop" EXIT INT TERM HUP KILL STOP
|
||||||
|
|
||||||
|
# Start Azurite
|
||||||
|
echo "Starting Azurite..."
|
||||||
azurite \
|
azurite \
|
||||||
--disableTelemetry \
|
--disableTelemetry \
|
||||||
--location "$AZURITE_DIR" \
|
--location "$AZURITE_DIR" \
|
||||||
"${CERT_ARGS[@]}" \
|
--blobHost 127.0.0.1 --queueHost 127.0.0.1 --tableHost 127.0.0.1 \
|
||||||
"${BLOB_ARGS[@]}" \
|
--key "$AZURITE_DIR/${ACCOUNT_NAME}_key.pem" --cert "$AZURITE_DIR/${ACCOUNT_NAME}_cert.pem" \
|
||||||
"${OAUTH_ARGS[@]}"
|
"${OAUTH_ARGS[@]}"
|
||||||
|
|||||||
9
run.sh
9
run.sh
@@ -1,9 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
if command -v dockerd &> /dev/null; then
|
|
||||||
docker run --rm -it --env-file accounts.env -p 443:443 -v ./storage:/storage azurite:latest
|
|
||||||
elif command -v container &> /dev/null; then
|
|
||||||
container run --rm -it --env-file accounts.env -p 443:443 --mount type=bind,source=./storage,target=/storage azurite:latest
|
|
||||||
else
|
|
||||||
echo "Neither supported container runtime found." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
37
start-azurite
Executable file
37
start-azurite
Executable file
@@ -0,0 +1,37 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
AZURITE_DIR="${AZURITE_DIR:-$(pwd)}"
|
||||||
|
CONTAINER_ARGS=()
|
||||||
|
AZURITE_IMAGE="${AZURITE_IMAGE:-azurite:latest}"
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
-o|--oauth)
|
||||||
|
# OAuth support
|
||||||
|
CONTAINER_ARGS+=("--oauth")
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-d|--azurite-dir)
|
||||||
|
if [[ -n "$2" && -d "$2" ]]; then
|
||||||
|
AZURITE_DIR="$2"
|
||||||
|
shift 2
|
||||||
|
else
|
||||||
|
echo "Error: Selected directory does not exist." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unknown argument: $1" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if command -v dockerd &> /dev/null; then
|
||||||
|
docker run --rm -d --name azurite --env-file "$AZURITE_DIR/accounts.env" -p 443:443 -v "$AZURITE_DIR/storage":/storage "$AZURITE_IMAGE" "${CONTAINER_ARGS[@]}"
|
||||||
|
elif command -v container &> /dev/null; then
|
||||||
|
container run -c 2 -m 512M --rm -d --name azurite --env-file "$AZURITE_DIR/accounts.env" -p 443:443 --mount type=bind,source="$AZURITE_DIR/storage",target=/storage "$AZURITE_IMAGE" "${CONTAINER_ARGS[@]}"
|
||||||
|
else
|
||||||
|
echo "Neither supported container runtime found." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
3
test/backend.config
Normal file
3
test/backend.config
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
storage_account_name = "terraform"
|
||||||
|
container_name = "tfstate"
|
||||||
|
key = "test.tfstate"
|
||||||
38
test/init.sh
Executable file
38
test/init.sh
Executable file
@@ -0,0 +1,38 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
AZURITE_DIR="$HOME/.azurite"
|
||||||
|
if [[ ! -d "$AZURITE_DIR" ]]; then
|
||||||
|
echo "Error: Azurite data directory not found at $AZURITE_DIR" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -f "$AZURITE_DIR/accounts.env" ]]; then
|
||||||
|
echo "Error: accounts.env file not found at $AZURITE_DIR/accounts.env" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
ACCESS_KEY=""
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--key|-k)
|
||||||
|
if [[ ! -f "$AZURITE_DIR/accounts.env" ]]; then
|
||||||
|
echo "Error: accounts.env file not found at $AZURITE_DIR/accounts.env" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# Load the accounts.env file to get the ACCESS_KEY variable.
|
||||||
|
source "$AZURITE_DIR/accounts.env"
|
||||||
|
ACCESS_KEY=$(echo "$AZURITE_ACCOUNTS" | cut -f 1 -d ';' | cut -f 2 -d ':')
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unknown argument: $1"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -z "$ACCESS_KEY" ]]; then
|
||||||
|
terraform init -backend-config="backend.config" -backend-config="use_azuread_auth=true" -reconfigure
|
||||||
|
else
|
||||||
|
terraform init -backend-config="backend.config" -backend-config="access_key=$ACCESS_KEY" -reconfigure
|
||||||
|
fi
|
||||||
35
test/main.tf
Normal file
35
test/main.tf
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
terraform {
|
||||||
|
required_providers {
|
||||||
|
azurerm = {
|
||||||
|
source = "hashicorp/azurerm"
|
||||||
|
}
|
||||||
|
|
||||||
|
random = {
|
||||||
|
source = "hashicorp/random"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
backend "azurerm" {}
|
||||||
|
}
|
||||||
|
|
||||||
|
provider "azurerm" {
|
||||||
|
features {}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "random_password" "user1" {
|
||||||
|
length = 12
|
||||||
|
min_lower = 1
|
||||||
|
min_upper = 1
|
||||||
|
min_numeric = 1
|
||||||
|
min_special = 1
|
||||||
|
special = true
|
||||||
|
override_special = "!@#$"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "terraform_data" "password" {
|
||||||
|
input = random_password.user1.result
|
||||||
|
}
|
||||||
|
|
||||||
|
output "user1" {
|
||||||
|
value = terraform_data.password.output
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user