Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add GCP connections for mirroring #22

Merged
merged 12 commits into from
Sep 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions backend/src/api/connections/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,18 @@ import {
import ApiResponseHandler from "api-response-handler"
import { decrypt } from "utils/encryption"
import { delete_connection as delete_connection_request } from "suricata_setup/"
import { ConnectionType } from "@common/enums"

const list_connections = async (req: Request, res: Response) => {
try {
const connections = (await list_connections_service()).map(v => {
delete v.aws.keypair
delete v.aws.access_id
delete v.aws.secret_access_key
if (v.connectionType === ConnectionType.AWS) {
delete v.aws.keypair
delete v.aws.access_id
delete v.aws.secret_access_key
} else if (v.connectionType === ConnectionType.GCP) {
delete v.gcp.key_file
}
return v
})

Expand Down
78 changes: 59 additions & 19 deletions backend/src/api/setup/index.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
import { Request, Response } from "express"
import ApiResponseHandler from "api-response-handler"
import { AWS_CONNECTION, STEP_RESPONSE } from "@common/types"
import { AWS_CONNECTION, SSH_INFO, STEP_RESPONSE } from "@common/types"
import { ConnectionType } from "@common/enums"
import { setup } from "suricata_setup"
import "express-session"
import { EC2_CONN } from "suricata_setup/aws-services/create-ec2-instance"
import { VirtualizationType } from "@aws-sdk/client-ec2"
import { save_connection } from "services/connections"
import { deleteKeyFromRedis, getFromRedis } from "suricata_setup/utils"
import {
list_images,
list_machines,
} from "suricata_setup/gcp-services/gcp_setup"

declare module "express-session" {
interface SessionData {
connection_config: Record<
string, // id
{
step?: STEP_RESPONSE["step_number"]
status?: STEP_RESPONSE["status"]
step?: STEP_RESPONSE<ConnectionType>["step_number"]
status?: STEP_RESPONSE<ConnectionType>["status"]
id?: string
type?: ConnectionType
data?: STEP_RESPONSE["data"]
Expand Down Expand Up @@ -53,17 +56,6 @@ export const setup_connection = async (
...resp,
}

if (resp.status === "COMPLETE") {
const {
params: { name },
} = req.body
await save_connection({
conn_meta: req.session.connection_config[id].data as AWS_CONNECTION,
id: id,
name: name,
})
}

delete resp.data

await ApiResponseHandler.success(res, resp)
Expand All @@ -77,23 +69,41 @@ export const aws_os_choices = async (
res: Response,
): Promise<void> => {
const { id } = req.body
const { access_id, secret_access_key, region } =
req.session.connection_config[id].data
const { access_id, secret_access_key, region } = req.session
.connection_config[id].data as STEP_RESPONSE<ConnectionType.AWS>["data"]
let conn = new EC2_CONN(access_id, secret_access_key, region)
let choices = await conn.get_latest_image()
await ApiResponseHandler.success(res, [
[choices.Description, choices.ImageId],
])
}

export const gcp_os_choices = async (
req: Request,
res: Response,
): Promise<void> => {
try {
const { id } = req.body
const { key_file, zone, project } = req.session.connection_config[id]
.data as STEP_RESPONSE<ConnectionType.GCP>["data"]

let choices = await list_images({ key_file, project, zone })
let resp = choices.map(v => [v.description, v.selfLink])
await ApiResponseHandler.success(res, resp)
} catch (err) {
await ApiResponseHandler.error(res, err)
}
}

export const aws_instance_choices = async (
req: Request,
res: Response,
): Promise<void> => {
try {
const { id, specs } = req.body
const { access_id, secret_access_key, virtualization_type, region } =
req.session.connection_config[id].data
const { access_id, secret_access_key, virtualization_type, region } = req
.session.connection_config[id]
.data as STEP_RESPONSE<ConnectionType.AWS>["data"]
let conn = new EC2_CONN(access_id, secret_access_key, region)
let choices = await conn.get_valid_types(
virtualization_type as VirtualizationType,
Expand All @@ -107,6 +117,32 @@ export const aws_instance_choices = async (
ApiResponseHandler.error(res, err)
}
}
export const gcp_instance_choices = async (
req: Request,
res: Response,
): Promise<void> => {
try {
const { id, specs } = req.body
const { key_file, zone, project } = req.session.connection_config[id]
.data as STEP_RESPONSE<ConnectionType.GCP>["data"]

let choices = await list_machines({
key_file,
zone,
project,
minCpu: specs.minCpu,
maxCpu: specs.maxCpu,
minMem: specs.minMem,
maxMem: specs.maxMem,
})
await ApiResponseHandler.success(
res,
choices.map(v => [v.name, v.selfLink]),
)
} catch (err) {
ApiResponseHandler.error(res, err)
}
}

export const get_setup_state = async (req: Request, res: Response) => {
const { uuid } = req.params
Expand All @@ -115,6 +151,10 @@ export const get_setup_state = async (req: Request, res: Response) => {
if (["OK", "FAIL"].includes(resp.success)) {
await deleteKeyFromRedis(uuid)
}
req.session.connection_config[resp.data.id] = {
...req.session.connection_config[resp.data.id],
...resp,
}
delete resp.data
await ApiResponseHandler.success(res, resp)
} catch (err) {
Expand Down
4 changes: 4 additions & 0 deletions backend/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import { MulterSource } from "multer-source"
import {
aws_instance_choices,
aws_os_choices,
gcp_instance_choices,
gcp_os_choices,
get_setup_state,
setup_connection,
} from "./api/setup"
Expand Down Expand Up @@ -99,6 +101,8 @@ app.post("/api/v1/setup_connection", setup_connection)
app.get("/api/v1/setup_connection/fetch/:uuid", get_setup_state)
app.post("/api/v1/setup_connection/aws/os", aws_os_choices)
app.post("/api/v1/setup_connection/aws/instances", aws_instance_choices)
app.post("/api/v1/setup_connection/gcp/os", gcp_os_choices)
app.post("/api/v1/setup_connection/gcp/instances", gcp_instance_choices)
app.get("/api/v1/list_connections", list_connections)
app.get("/api/v1/list_connections/:uuid", get_connection_for_uuid)
app.get(
Expand Down
31 changes: 29 additions & 2 deletions backend/src/models/connections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@ import {
BeforeInsert,
} from "typeorm"
import { ConnectionType } from "@common/enums"
import { AWS_CONNECTION, ENCRYPTED_AWS_CONNECTION__META } from "@common/types"
import {
AWS_CONNECTION,
ENCRYPTED_AWS_CONNECTION__META,
ENCRYPTED_GCP_CONNECTION__META,
GCP_CONNECTION,
SSH_INFO,
} from "@common/types"
import { encrypt, generate_iv } from "utils/encryption"

@Entity()
Expand All @@ -29,11 +35,17 @@ export class Connections extends BaseEntity {
name: string

@Column({ nullable: true, type: "jsonb" })
aws?: AWS_CONNECTION
aws?: AWS_CONNECTION & SSH_INFO

@Column({ nullable: true, type: "jsonb" })
aws_meta?: ENCRYPTED_AWS_CONNECTION__META

@Column({ nullable: true, type: "jsonb" })
gcp?: GCP_CONNECTION

@Column({ nullable: true, type: "jsonb" })
gcp_meta?: ENCRYPTED_GCP_CONNECTION__META

@BeforeInsert()
beforeInsert() {
let key = process.env.ENCRYPTION_KEY
Expand Down Expand Up @@ -75,6 +87,21 @@ export class Connections extends BaseEntity {
secret_access_key_iv: sa_key_iv.toString("base64"),
}
}
} else if (this.connectionType == ConnectionType.GCP && this.gcp) {
// Encrypt GCP Key File
let key_file_iv = generate_iv()
let { encrypted: encrypted_k, tag: tag_k } = encrypt(
this.gcp.key_file,
encryptionKey,
key_file_iv,
)
this.gcp.key_file = encrypted_k
if (!this.gcp_meta) {
this.gcp_meta = {
key_file_tag: tag_k.toString("base64"),
key_file_iv: key_file_iv.toString("base64"),
}
}
}
}
}
80 changes: 75 additions & 5 deletions backend/src/services/connections/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { ConnectionType } from "@common/enums"
import { AWS_CONNECTION } from "@common/types"
import { AWS_CONNECTION, GCP_CONNECTION, SSH_INFO } from "@common/types"
import { AppDataSource } from "data-source"
import Error500InternalServer from "errors/error-500-internal-server"
import { Connections } from "models"

const save_connection = async ({
const save_connection_aws = async ({
conn_meta,
name,
id,
}: {
conn_meta: AWS_CONNECTION
conn_meta: AWS_CONNECTION & SSH_INFO
name: string
id: string
}) => {
Expand Down Expand Up @@ -52,7 +52,7 @@ const save_connection = async ({
remote_machine_url,
keypair_name,
keypair_id,
} as AWS_CONNECTION
} as AWS_CONNECTION & SSH_INFO
conn.connectionType = ConnectionType.AWS
conn.uuid = id
conn.name = name
Expand All @@ -64,6 +64,74 @@ const save_connection = async ({
throw new Error500InternalServer(err)
}
}
const save_connection_gcp = async ({
conn_meta,
name,
id,
}: {
conn_meta: GCP_CONNECTION
name: string
id: string
}) => {
const {
key_file,
project,
zone,
network_url,
ip_range,
source_subnetwork_url,
firewall_rule_url,
destination_subnetwork_url,
router_url,
machine_type,
source_image,
image_template_url,
instance_url,
managed_group_url,
health_check_url,
backend_service_url,
forwarding_rule_url,
source_instance_url,
packet_mirror_url,
source_instance_name,
source_private_ip,
} = conn_meta
const conn = new Connections()

conn.gcp = {
key_file,
project,
zone,
network_url,
ip_range,
source_subnetwork_url,
firewall_rule_url,
destination_subnetwork_url,
router_url,
machine_type,
source_image,
image_template_url,
instance_url,
managed_group_url,
health_check_url,
backend_service_url,
forwarding_rule_url,
source_instance_url,
packet_mirror_url,
source_instance_name,
source_private_ip,
} as GCP_CONNECTION
conn.connectionType = ConnectionType.GCP
conn.uuid = id
conn.name = name
try {
const connectionRepository = AppDataSource.getRepository(Connections)
await connectionRepository.save(conn)
} catch (err) {
console.error(`Error in saving connection: ${err}`)
throw new Error500InternalServer(err)
}
}

const list_connections = async () => {
try {
Expand All @@ -77,6 +145,7 @@ const list_connections = async () => {
"conn.updatedAt",
"conn.connectionType",
"conn.aws",
"conn.gcp",
])
.getMany()
return resp
Expand Down Expand Up @@ -149,7 +218,8 @@ const delete_connection_for_uuid = async ({ uuid }) => {
}
}
export {
save_connection,
save_connection_aws,
save_connection_gcp,
list_connections,
get_connection_for_uuid,
update_connection_for_uuid,
Expand Down
Loading