diff --git a/examples/command/portals/databases/mongodb/amazon_vpc/README.md b/examples/command/portals/databases/mongodb/amazon_vpc/README.md new file mode 100644 index 00000000000..201a6d1d44f --- /dev/null +++ b/examples/command/portals/databases/mongodb/amazon_vpc/README.md @@ -0,0 +1,9 @@ +# Amazon VPC + +This hands-on example uses Ockam to create an end-to-end encrypted portal to MongoDB. + +We connect a nodejs app in one Amazon VPC with a Amazon VPC managed Mongo database in another Amazon VPC. +The example uses AWS CLI to create these VPCs. + +You can read a detailed walkthough of this example at: +https://docs.ockam.io/portals/databases/mongodb/amazon_vpc diff --git a/examples/command/portals/databases/mongodb/amazon_vpc/analysis_corp/app.js b/examples/command/portals/databases/mongodb/amazon_vpc/analysis_corp/app.js new file mode 100644 index 00000000000..dd975c5f34b --- /dev/null +++ b/examples/command/portals/databases/mongodb/amazon_vpc/analysis_corp/app.js @@ -0,0 +1,51 @@ +const { MongoClient } = require("mongodb"); + +// Configuration to connect to the MongoDB database on - 127.0.0.1:17017 +// +// 127.0.0.1:17017 is the inlet address of the tcp portal to MongoDB. +// This inlet is local to analysis_corp, the MongoDB database is remote in bank_corp. +const uri = "mongodb://127.0.0.1:17017/mydb" + +// Attempt to connect to the database in a loop. +// +// Since all the containers may take a few seconds to start up. +// We'll attempt to connect in a loop once every 10 seconds by default. +async function connect(attempts = 100, waitTimeBetweenAttempts = 10000) { + while (attempts--) { + const client = new MongoClient(uri); + try { + await client.connect(); + console.log("Connected to the database.\n"); + return client; + } catch (err) { + console.log(`Couldn't connect to the database: ${err.message}, retrying ...`); + if (attempts == 0) throw err; + await new Promise((resolve) => setTimeout(resolve, waitTimeBetweenAttempts)); + } + } +} + +async function run() { + const client = await connect(); + try { + const database = client.db('sample'); + const users = database.collection('users'); + + console.log("Inserting some data into the users collections ..."); + const docs = ["Alice", "Bob", "Charlie"].map((name) => ({ name: name, score: Math.floor(Math.random() * 101) })); + await users.insertMany(docs); + + const result = await users.find().toArray() + console.log("USERS:", result); + console.log( + "\nThe example run was successful 🥳.\n" + + "\nThe app connected with the database through an encrypted portal." + + "\nInserted some data, and querried it back.\n", + ); + } finally { + await client.close(); + console.log("Disconnected from the database."); + } + +} +run() diff --git a/examples/command/portals/databases/mongodb/amazon_vpc/analysis_corp/run.sh b/examples/command/portals/databases/mongodb/amazon_vpc/analysis_corp/run.sh new file mode 100755 index 00000000000..eff5dc63a0b --- /dev/null +++ b/examples/command/portals/databases/mongodb/amazon_vpc/analysis_corp/run.sh @@ -0,0 +1,123 @@ +#!/usr/bin/env bash +set -ex + +run() { + enrollment_ticket="$1" + + # ---------------------------------------------------------------------------------------------------------------- + # CREATE NETWORK + + # Create a new VPC and tag it. + vpc_id=$(aws ec2 create-vpc --cidr-block 10.0.0.0/16 --query 'Vpc.VpcId') + aws ec2 create-tags --resources "$vpc_id" --tags "Key=Name,Value=${name}-vpc" + + # Create an Internet Gateway and attach it to the VPC. + gw_id=$(aws ec2 create-internet-gateway --query 'InternetGateway.InternetGatewayId') + aws ec2 attach-internet-gateway --vpc-id "$vpc_id" --internet-gateway-id "$gw_id" + + # Create a route table and a route to the Internet through the Gateway. + rtb_id=$(aws ec2 create-route-table --vpc-id "$vpc_id" --query 'RouteTable.RouteTableId') + aws ec2 create-route --route-table-id "$rtb_id" --destination-cidr-block 0.0.0.0/0 --gateway-id "$gw_id" + + # Create a subnet and associate the route table + az=$(aws ec2 describe-availability-zones --query "AvailabilityZones[0].ZoneName") + subnet_id=$(aws ec2 create-subnet --vpc-id "$vpc_id" --cidr-block 10.0.0.0/25 \ + --availability-zone "$az" --query 'Subnet.SubnetId') + aws ec2 modify-subnet-attribute --subnet-id "$subnet_id" --map-public-ip-on-launch + aws ec2 associate-route-table --subnet-id "$subnet_id" --route-table-id "$rtb_id" + + # Create a security group to allow: + # - TCP egress to the Internet + # - SSH ingress from the Internet + sg_id=$(aws ec2 create-security-group --group-name "${name}-sg" --vpc-id "$vpc_id" --query 'GroupId' \ + --description "Allow TCP egress and Postgres ingress") + aws ec2 authorize-security-group-egress --group-id "$sg_id" --cidr 0.0.0.0/0 --protocol tcp --port 0-65535 + aws ec2 authorize-security-group-ingress --group-id "$sg_id" --cidr 0.0.0.0/0 --protocol tcp --port 22 + + # ---------------------------------------------------------------------------------------------------------------- + # CREATE INSTANCE + + ami_id=$(aws ec2 describe-images --owners 137112412989 --query "Images | sort_by(@, &CreationDate) | [-1].ImageId" \ + --filters "Name=name,Values=al2023-ami-2023*" "Name=architecture,Values=x86_64" \ + "Name=virtualization-type,Values=hvm" "Name=root-device-type,Values=ebs" ) + + aws ec2 create-key-pair --key-name "${name}-key" --query 'KeyMaterial' > key.pem + chmod 400 key.pem + + sed "s/\$ENROLLMENT_TICKET/${enrollment_ticket}/g" run_ockam.sh > user_data.sh + instance_id=$(aws ec2 run-instances --image-id "$ami_id" --instance-type c5n.large \ + --subnet-id "$subnet_id" --security-group-ids "$sg_id" \ + --key-name "${name}-key" --user-data file://user_data.sh --query 'Instances[0].InstanceId') + aws ec2 create-tags --resources "$instance_id" --tags "Key=Name,Value=${name}-ec2-instance" + aws ec2 wait instance-running --instance-ids "$instance_id" + ip=$(aws ec2 describe-instances --instance-ids "$instance_id" --query 'Reservations[0].Instances[0].PublicIpAddress') + rm -f user_data.sh + + until scp -o StrictHostKeyChecking=no -i ./key.pem ./app.js "ec2-user@$ip:app.js"; do sleep 10; done + ssh -o StrictHostKeyChecking=no -i ./key.pem "ec2-user@$ip" \ + 'bash -s' << 'EOS' + sudo yum update -y && sudo yum install nodejs -y + npm install mongodb + node app.js +EOS +} + +cleanup() { + # ---------------------------------------------------------------------------------------------------------------- + # DELETE INSTANCE + + rm -f user_data.sh + instance_ids=$(aws ec2 describe-instances --filters "Name=tag:Name,Values=${name}-ec2-instance" \ + --query "Reservations[*].Instances[*].InstanceId") + for i in $instance_ids; do + aws ec2 terminate-instances --instance-ids "$i" + aws ec2 wait instance-terminated --instance-ids "$i" + done + + if aws ec2 describe-key-pairs --key-names "${name}-key" &>/dev/null; then + aws ec2 delete-key-pair --key-name "${name}-key" + fi + rm -f key.pem + + + # ---------------------------------------------------------------------------------------------------------------- + # DELETE NETWORK + + vpc_ids=$(aws ec2 describe-vpcs --query 'Vpcs[*].VpcId' --filters "Name=tag:Name,Values=${name}-vpc") + + for vpc_id in $vpc_ids; do + internet_gateways=$(aws ec2 describe-internet-gateways --query "InternetGateways[*].InternetGatewayId" \ + --filters Name=attachment.vpc-id,Values="$vpc_id") + for i in $internet_gateways; do + aws ec2 detach-internet-gateway --internet-gateway-id "$i" --vpc-id "$vpc_id" + aws ec2 delete-internet-gateway --internet-gateway-id "$i" + done + + subnet_ids=$(aws ec2 describe-subnets --query "Subnets[*].SubnetId" --filters Name=vpc-id,Values="$vpc_id") + for i in $subnet_ids; do aws ec2 delete-subnet --subnet-id "$i"; done + + route_tables=$(aws ec2 describe-route-tables --filters Name=vpc-id,Values="$vpc_id" \ + --query 'RouteTables[?length(Associations[?Main!=`true`]) > `0` || length(Associations) == `0`].RouteTableId') + for i in $route_tables; do aws ec2 delete-route-table --route-table-id "$i" || true; done + + security_groups=$(aws ec2 describe-security-groups --filters Name=vpc-id,Values="$vpc_id" \ + --query "SecurityGroups[?!contains(GroupName, 'default')].[GroupId]") + for i in $security_groups; do aws ec2 delete-security-group --group-id "$i"; done + + if aws ec2 describe-vpcs --vpc-ids "$vpc_id" &>/dev/null; then + aws ec2 delete-vpc --vpc-id "$vpc_id" + fi + done +} + +export AWS_PAGER=""; +export AWS_DEFAULT_OUTPUT="text"; + +user="" +command -v sha256sum &>/dev/null && user=$(aws sts get-caller-identity | sha256sum | cut -c 1-20) +command -v shasum &>/dev/null && user=$(aws sts get-caller-identity | shasum -a 256 | cut -c 1-20) +export name="ockam-ex-mongo-db-analysis-corp-$user" + +# Check if the first argument is "cleanup" +# If it is, call the cleanup function. If not, call the run function. +if [ "$1" = "cleanup" ]; then cleanup; else run "$1"; fi diff --git a/examples/command/portals/databases/mongodb/amazon_vpc/analysis_corp/run_ockam.sh b/examples/command/portals/databases/mongodb/amazon_vpc/analysis_corp/run_ockam.sh new file mode 100755 index 00000000000..c0e5fe87a4c --- /dev/null +++ b/examples/command/portals/databases/mongodb/amazon_vpc/analysis_corp/run_ockam.sh @@ -0,0 +1,39 @@ +#!/bin/bash +set -ex + +# Change into ec2-user's home directory and use sudo to run the commands as ec2-user +cd /home/ec2-user +sudo -u ec2-user bash << 'EOS' +set -ex + +# Install Ockam Command +curl --proto '=https' --tlsv1.2 -sSfL https://install.command.ockam.io | bash +source "$HOME/.ockam/env" + +# Run `ockam project enroll ...` +# +# The `project enroll` command creates a new vault and generates a cryptographic identity with +# private keys stored in that vault. +# +# The enrollment ticket includes routes and identifiers for the project membership authority +# and the project's node that offers the relay service. +# +# The enrollment ticket also includes an enrollment token. The project enroll command +# creates a secure channel with the project membership authority and presents this enrollment token. +# The authority enrolls presented identity and returns a project membership credential. +# +# The command, stores this credential for later use and exits. +ockam project enroll "$ENROLLMENT_TICKET" + +# Create an ockam node. +# +# Create an access control policy that only allows project members that possesses a credential with +# attribute mongodb-outlet="true" to connect to TCP Portal Inlets on this node. +# +# Create a TCP Portal Inlet to MongoDB. +# This makes the remote MongoDB available on all localhost IPs at - 0.0.0.0:17017 +ockam node create +ockam policy create --resource tcp-inlet --expression '(= subject.mongodb-outlet "true")' +ockam tcp-inlet create --from 0.0.0.0:17017 --via mongodb + +EOS diff --git a/examples/command/portals/databases/mongodb/amazon_vpc/bank_corp/run.sh b/examples/command/portals/databases/mongodb/amazon_vpc/bank_corp/run.sh new file mode 100755 index 00000000000..d050107d5f9 --- /dev/null +++ b/examples/command/portals/databases/mongodb/amazon_vpc/bank_corp/run.sh @@ -0,0 +1,105 @@ +#!/usr/bin/env bash +set -ex + +run() { + enrollment_ticket="$1" + + # ---------------------------------------------------------------------------------------------------------------- + # CREATE NETWORK + + # Create a new VPC and tag it. + vpc_id=$(aws ec2 create-vpc --cidr-block 10.0.0.0/16 --query 'Vpc.VpcId') + aws ec2 create-tags --resources "$vpc_id" --tags "Key=Name,Value=${name}-vpc" + + # Create and Internet Gateway and attach it to the VPC. + gw_id=$(aws ec2 create-internet-gateway --query 'InternetGateway.InternetGatewayId') + aws ec2 attach-internet-gateway --vpc-id "$vpc_id" --internet-gateway-id "$gw_id" + + # Create a route table and a route to the Internet through the Gateway. + rtb_id=$(aws ec2 create-route-table --vpc-id "$vpc_id" --query 'RouteTable.RouteTableId') + aws ec2 create-route --route-table-id "$rtb_id" --destination-cidr-block 0.0.0.0/0 --gateway-id "$gw_id" + + # Create a subnet and associate the route table + az=$(aws ec2 describe-availability-zones --query "AvailabilityZones[0].ZoneName") + subnet_id=$(aws ec2 create-subnet --vpc-id "$vpc_id" --cidr-block 10.0.0.0/25 \ + --availability-zone "$az" --query 'Subnet.SubnetId') + aws ec2 modify-subnet-attribute --subnet-id "$subnet_id" --map-public-ip-on-launch + aws ec2 associate-route-table --subnet-id "$subnet_id" --route-table-id "$rtb_id" + + # Create a security group to allow: + # - TCP egress to the Internet + # - No inbound connection + sg_id=$(aws ec2 create-security-group --group-name "${name}-sg" --vpc-id "$vpc_id" --query 'GroupId' \ + --description "Allow TCP egress and Postgres ingress") + aws ec2 authorize-security-group-egress --group-id "$sg_id" --cidr 0.0.0.0/0 --protocol tcp --port 0-65535 + + + # ---------------------------------------------------------------------------------------------------------------- + # CREATE INSTANCE + + ami_id=$(aws ec2 describe-images --owners 137112412989 --query "Images | sort_by(@, &CreationDate) | [-1].ImageId" \ + --filters "Name=name,Values=al2023-ami-2023*" "Name=architecture,Values=x86_64" \ + "Name=virtualization-type,Values=hvm" "Name=root-device-type,Values=ebs" ) + + sed "s/\$ENROLLMENT_TICKET/${enrollment_ticket}/g" run_ockam.sh > user_data.sh + instance_id=$(aws ec2 run-instances --image-id "$ami_id" --instance-type c5n.large \ + --subnet-id "$subnet_id" --security-group-ids "$sg_id" \ + --user-data file://user_data.sh --query 'Instances[0].InstanceId') + aws ec2 create-tags --resources "$instance_id" --tags "Key=Name,Value=${name}-ec2-instance" + aws ec2 wait instance-running --instance-ids "$instance_id" + rm -f user_data.sh +} + +cleanup() { + # ---------------------------------------------------------------------------------------------------------------- + # DELETE INSTANCE + + rm -f user_data.sh + instance_ids=$(aws ec2 describe-instances --filters "Name=tag:Name,Values=${name}-ec2-instance" \ + --query "Reservations[*].Instances[*].InstanceId") + for i in $instance_ids; do + aws ec2 terminate-instances --instance-ids "$i" + aws ec2 wait instance-terminated --instance-ids "$i" + done + + # ---------------------------------------------------------------------------------------------------------------- + # DELETE NETWORK + + vpc_ids=$(aws ec2 describe-vpcs --query 'Vpcs[*].VpcId' --filters "Name=tag:Name,Values=${name}-vpc") + + for vpc_id in $vpc_ids; do + internet_gateways=$(aws ec2 describe-internet-gateways --query "InternetGateways[*].InternetGatewayId" \ + --filters Name=attachment.vpc-id,Values="$vpc_id") + for i in $internet_gateways; do + aws ec2 detach-internet-gateway --internet-gateway-id "$i" --vpc-id "$vpc_id" + aws ec2 delete-internet-gateway --internet-gateway-id "$i" + done + + subnet_ids=$(aws ec2 describe-subnets --query "Subnets[*].SubnetId" --filters Name=vpc-id,Values="$vpc_id") + for i in $subnet_ids; do aws ec2 delete-subnet --subnet-id "$i"; done + + route_tables=$(aws ec2 describe-route-tables --filters Name=vpc-id,Values="$vpc_id" \ + --query 'RouteTables[?length(Associations[?Main!=`true`]) > `0` || length(Associations) == `0`].RouteTableId') + for i in $route_tables; do aws ec2 delete-route-table --route-table-id "$i" || true; done + + security_groups=$(aws ec2 describe-security-groups --filters Name=vpc-id,Values="$vpc_id" \ + --query "SecurityGroups[?!contains(GroupName, 'default')].[GroupId]") + for i in $security_groups; do aws ec2 delete-security-group --group-id "$i"; done + + if aws ec2 describe-vpcs --vpc-ids "$vpc_id" &>/dev/null; then + aws ec2 delete-vpc --vpc-id "$vpc_id" + fi + done +} + +export AWS_PAGER=""; +export AWS_DEFAULT_OUTPUT="text"; + +user="" +command -v sha256sum &>/dev/null && user=$(aws sts get-caller-identity | sha256sum | cut -c 1-20) +command -v shasum &>/dev/null && user=$(aws sts get-caller-identity | shasum -a 256 | cut -c 1-20) +export name="ockam-ex-mongo-db-bank-corp-$user" + +# Check if the first argument is "cleanup" +# If it is, call the cleanup function. If not, call the run function. +if [ "$1" = "cleanup" ]; then cleanup; else run "$1"; fi diff --git a/examples/command/portals/databases/mongodb/amazon_vpc/bank_corp/run_ockam.sh b/examples/command/portals/databases/mongodb/amazon_vpc/bank_corp/run_ockam.sh new file mode 100755 index 00000000000..31375844934 --- /dev/null +++ b/examples/command/portals/databases/mongodb/amazon_vpc/bank_corp/run_ockam.sh @@ -0,0 +1,54 @@ +#!/bin/bash +set -ex + +# Change into ec2-user's home directory and use sudo to run the commands as ec2-user +cd /home/ec2-user +sudo -u ec2-user bash << 'EOS' +set -ex + +# Install MongoDB https://www.mongodb.com/docs/manual/tutorial/install-mongodb-on-amazon/#install-mongodb-community-edition +sudo tee /etc/yum.repos.d/mongodb-org-7.1.repo > /dev/null </dev/null; then + curl --proto '=https' --tlsv1.2 -sSfL https://install.command.ockam.io | bash + source "$HOME/.ockam/env" +fi + +# Check if the first argument is "cleanup" +# If it is, call the cleanup function. If not, call the run function. +if [ "$1" = "cleanup" ]; then cleanup; else run; fi