diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 7b6fc82..4e9dfe9 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -37,8 +37,7 @@ jobs: run: | cargo build --release - name: tar - run: | - tar --directory=target/release -cf archive.tar.gz nitrogen.exe + run: tar --directory=target/release -cf archive.tar.gz nitrogen.exe - name: upload run: | $id = gh api -H "Accept: application/vnd.github+json" /repos/capeprivacy/nitrogen/releases/tags/${{ github.ref_name }} --jq .id diff --git a/examples/vault/Dockerfile b/examples/vault/Dockerfile new file mode 100644 index 0000000..867ea1f --- /dev/null +++ b/examples/vault/Dockerfile @@ -0,0 +1,14 @@ +FROM vault:latest + + +RUN apk --no-cache add socat=1.7.4.1-r1 + +COPY config.hcl config.hcl + +COPY run.sh ./ +RUN ["chmod", "+x", "./run.sh"] + +COPY app.sh ./ +RUN ["chmod", "+x", "./app.sh"] + +CMD ["/bin/sh", "/run.sh"] diff --git a/examples/vault/Dockerfile.tls b/examples/vault/Dockerfile.tls new file mode 100644 index 0000000..fb4e5c0 --- /dev/null +++ b/examples/vault/Dockerfile.tls @@ -0,0 +1,15 @@ +FROM vault:latest + + +RUN apk --no-cache add socat=1.7.4.1-r1 + +COPY config-tls.hcl config-tls.hcl +COPY vault.key vault.pem ./ + +COPY run.sh ./ +RUN ["chmod", "+x", "./run.sh"] + +COPY app-tls.sh ./app.sh +RUN ["chmod", "+x", "./app.sh"] + +CMD ["/bin/sh", "/run.sh"] diff --git a/examples/vault/README.md b/examples/vault/README.md new file mode 100644 index 0000000..0346483 --- /dev/null +++ b/examples/vault/README.md @@ -0,0 +1,124 @@ +# Vault Example + +## Requirements + +- vault CLI (see [here](https://developer.hashicorp.com/vault/docs/install) for installation instructions) + +## Deploy Vault + +``` +$ nitrogen setup vault-nitro ~/.ssh/id_rsa.pub +``` + +``` +$ nitrogen build examples/vault +``` + +``` +$ nitrogen deploy vault-nitro ~/.ssh/id_rsa +``` + +After the last command you should see something like: + +``` +... +2022-11-24T19:23:19.595376Z INFO nitrogen::commands::deploy: EIF is now running public_dns="ec2-54-159-199-57.compute-1.amazonaws.com" +2022-11-24T19:23:19.595512Z INFO nitrogen::commands::deploy: Check enclave status... +2022-11-24T19:23:20.159233Z INFO nitrogen::commands::deploy: Enclave up and running! +``` + +## Setup Vault + +Here we've deployed Vault in production mode so it takes a few more steps to fully set up Vault. + +First, set the address of the ec2 instance: + +``` +$ export VAULT_ADDR="ec2-54-159-199-57.compute-1.amazonaws.com" +``` + +Next initialize vault: + +``` +$ vault operator init +``` + +Should see some output like: + +``` +Unseal Key 1: J6jRd1lZ7eU+wmBmtE4OsmymVtWfPmpGnh7z/o4e68JG +Unseal Key 2: cLZnWKAt2KxSpsMxNAOZ05Pcv1XmxFugcrzh3fNp+GCw +Unseal Key 3: quW9NrKUdrLcpYCnHRqUORXH+VjX9O8QwHafQETtVxcU +Unseal Key 4: ZI9pTMq7KEy7e2zC+AJx5iSANTBQA4ts0UxJeh7EyEqS +Unseal Key 5: F5lI1mjyR7h24lDtY95uBLLRs+mupcTvJBRl+aDtSd4a + +Initial Root Token: hvs.FDnNk0XLP7jv2g18UnMkN3mi + +Vault initialized with 5 key shares and a key threshold of 3. Please securely +distribute the key shares printed above. When the Vault is re-sealed, +restarted, or stopped, you must supply at least 3 of these keys to unseal it +before it can start servicing requests. + +Vault does not store the generated root key. Without at least 3 keys to +reconstruct the root key, Vault will remain permanently sealed! + +It is possible to generate new unseal keys, provided you have a quorum of +existing unseal keys shares. See "vault operator rekey" for more information. +``` + +Then for 3 of the key shares call the following: + +``` +$ vault operator unseal +``` + +Finally log in with the root token: + +``` +$ vault login +``` + +## Store Keys + +To store keys in production mode we must first set up a secrets engine: + +``` +$ vault secrets enable kv +``` + +And then we can store and retrieve secrets: + +``` +$ vault kv put -mount=kv sec foo=bar +``` + +``` +$ vault kv get -mount=kev sec +``` + +## TLS + +**Note: reading the [nginx tls example](../nginx-tls/README.md) could be helpful for some more background** + +With some additional setup we can enable TLS. + +First, generate the certificate and key with `mkcert`: + +``` +$ mkcert -cert-file vault.pem -key-file vault.key +$ cp vault.pem vault.key examples/vault +``` + +``` +$ nitrogen build examples/vault -d Dockerfile.tls +``` + +``` +$ nitrogen deploy vault-nitro ~/.ssh/id_rsa +``` + +``` +$ export VAULT_ADDR="https://ec2-54-159-199-57.compute-1.amazonaws.com" +``` + +Then you can follow the rest of the sets from above but this is using TLS! diff --git a/examples/vault/app-tls.sh b/examples/vault/app-tls.sh new file mode 100644 index 0000000..f1b1ff5 --- /dev/null +++ b/examples/vault/app-tls.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +vault server -config=config-tls.hcl diff --git a/examples/vault/app.sh b/examples/vault/app.sh new file mode 100644 index 0000000..ecd854a --- /dev/null +++ b/examples/vault/app.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +vault server -config=config.hcl diff --git a/examples/vault/config-tls.hcl b/examples/vault/config-tls.hcl new file mode 100644 index 0000000..cde64d8 --- /dev/null +++ b/examples/vault/config-tls.hcl @@ -0,0 +1,14 @@ +backend "file" { + path = "/vault/file" + default_lease_ttl = "168h" + max_lease_ttl = "720h" +} + +listener "tcp" { + address = "0.0.0.0:8200" + tls_cert_file = "vault.pem" + tls_key_file = "vault.key" +} + +api_addr = "http://0.0.0.0:8200" +cluster_addr = "https://0.0.0.0:8201" diff --git a/examples/vault/config.hcl b/examples/vault/config.hcl new file mode 100644 index 0000000..559eeb2 --- /dev/null +++ b/examples/vault/config.hcl @@ -0,0 +1,13 @@ +backend "file" { + path = "/vault/file" + default_lease_ttl = "168h" + max_lease_ttl = "720h" +} + +listener "tcp" { + address = "0.0.0.0:8200" + tls_disable = "true" +} + +api_addr = "http://0.0.0.0:8200" +cluster_addr = "https://0.0.0.0:8201" diff --git a/examples/vault/run.sh b/examples/vault/run.sh new file mode 100644 index 0000000..2110aa5 --- /dev/null +++ b/examples/vault/run.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +ip addr add 127.0.0.1/32 dev lo +ip link set dev lo up + +socat vsock-listen:5000,reuseaddr,fork tcp-connect:127.0.0.1:8200 & + +sh ./app.sh diff --git a/src/bin/nitrogen.rs b/src/bin/nitrogen.rs index 0e7b91f..ecd4e93 100644 --- a/src/bin/nitrogen.rs +++ b/src/bin/nitrogen.rs @@ -52,8 +52,13 @@ enum Commands { Build { /// Dockerfile directory dockerfile_dir: String, + + /// Output EIF location + #[arg(short, long, default_value_t = String::from("Dockerfile"))] + dockerfile_name: String, + /// Output EIF location - #[arg(short, long, default_value_t = String::from("./nitrogen.eif"))] + #[arg(short, long, default_value_t = String::from("nitrogen.eif"))] eif: String, }, @@ -62,7 +67,7 @@ enum Commands { /// Name of a Nitrogen-generated CloudFormation stack name: String, /// Filepath to EIF - #[arg(short, long, default_value_t = String::from("./nitrogen.eif"))] + #[arg(short, long, default_value_t = String::from("nitrogen.eif"))] eif: String, /// Filepath to SSH key for the instance ssh_key: String, @@ -164,10 +169,14 @@ async fn main() -> Result<(), Error> { } Commands::Build { dockerfile_dir, + dockerfile_name, eif, } => { - info!(dockerfile_dir, "Building EIF from dockerfile."); - let out = build(&dockerfile_dir, &eif).await?; + info!( + dockerfile_dir, + dockerfile_name, "Building EIF from dockerfile." + ); + let out = build(&dockerfile_dir, &dockerfile_name, &eif).await?; debug!(docker_output=?out, "Docker output:"); Ok(()) } @@ -258,7 +267,12 @@ async fn main() -> Result<(), Error> { // TODO should save this somewhere else than their current directory let eif_path = &format!("{}.eif", service); - build(&proj_dir.to_str().unwrap().to_string(), eif_path).await?; + build( + &proj_dir.to_str().unwrap().to_string(), + &"Dockerfile".to_string(), + eif_path, + ) + .await?; info!("Sleeping for 20s to give ec2 instance a chance to boot..."); tokio::time::sleep(Duration::from_secs(20)).await; diff --git a/src/commands/build.rs b/src/commands/build.rs index 2297731..b085e57 100644 --- a/src/commands/build.rs +++ b/src/commands/build.rs @@ -7,10 +7,14 @@ use tokio::process::Command; use tracing::{info, instrument}; #[instrument(level = "debug")] -pub async fn build(dockerfile_dir: &String, eif_name: &String) -> Result { +pub async fn build( + dockerfile_dir: &String, + dockerfile_name: &String, + eif_name: &String, +) -> Result { let dockerdir = PathBuf::from(dockerfile_dir); let mut dockerfile_path = PathBuf::from(dockerfile_dir); - dockerfile_path.push("Dockerfile"); + dockerfile_path.push(dockerfile_name); let out = Command::new("docker") .args([ @@ -61,7 +65,8 @@ pub async fn build(dockerfile_dir: &String, eif_name: &String) -> Result Result } fn check_enclave_status(ssh_key: &str, url: &str) -> Result<(), Error> { - info!("Check enclave status..."); + info!("Checking enclave status..."); match describe_enclave(ssh_key, url)?.get("State") { // According to the docs, the state is either "running" or "terminating"