Skip to content

Commit f2d6c12

Browse files
authored
Merge pull request #2 from timoa/develop
2 parents 98e1b59 + 35208a4 commit f2d6c12

File tree

12 files changed

+261
-94
lines changed

12 files changed

+261
-94
lines changed

.terraform.lock.hcl

Lines changed: 19 additions & 17 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@ Terraform project that deploys VSCode Server on Oracle Cloud Infrastructure usin
1818
- [x] Create the instance on free tier (4 vCPU, 24GB memory)
1919
- [x] Configure the instance and install VSCode Server with Cloud Init
2020
- [x] Create automatically the SSH key pair
21-
- [ ] Mount and format the block volume on `/data` (WIP)
21+
- [x] Mount and format the block volume on `/data`
22+
- [x] Restrict SSH and VS Code port access
2223
- [ ] Encrypt the block volume with a KMS key
2324
- [ ] Configure backups of the block volume only (WIP)
2425
- [ ] Configure Cloudflare Zero Trust to secure the instance access
25-
- [ ] Write the documentation for the manual steps (Oracle Cloud Infrastructure, Cloudflare, etc.)
26+
- [ ] Write the documentation for the manual steps (Oracle Cloud Infrastructure & Cloudflare accounts, etc.)
27+
- [ ] Explain how to avoid the "Out of Host capacity" error
2628

2729
[github-badge]: https://github.com/timoa/terraform-oci-vscode-server/workflows/Terraform/badge.svg
2830
[github-url]: https://github.com/timoa/terraform-oci-vscode-server/actions?query=workflow%3ATerraform

USAGE.md

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
| Name | Version |
55
|------|---------|
66
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.0 |
7+
| <a name="requirement_cloudinit"></a> [cloudinit](#requirement\_cloudinit) | 2.2.0 |
78
| <a name="requirement_local"></a> [local](#requirement\_local) | 2.2.2 |
89
| <a name="requirement_null"></a> [null](#requirement\_null) | 3.1.1 |
910
| <a name="requirement_oci"></a> [oci](#requirement\_oci) | 4.72.0 |
10-
| <a name="requirement_template"></a> [template](#requirement\_template) | 2.2.0 |
1111
| <a name="requirement_tls"></a> [tls](#requirement\_tls) | 3.4.0 |
1212

1313
## Modules
@@ -22,20 +22,22 @@ No modules.
2222
| [local_file.public_key_openssh](https://registry.terraform.io/providers/hashicorp/local/2.2.2/docs/resources/file) | resource |
2323
| [null_resource.private_key_chmod](https://registry.terraform.io/providers/hashicorp/null/3.1.1/docs/resources/resource) | resource |
2424
| [null_resource.public_key_chmod](https://registry.terraform.io/providers/hashicorp/null/3.1.1/docs/resources/resource) | resource |
25+
| [null_resource.remote_exec](https://registry.terraform.io/providers/hashicorp/null/3.1.1/docs/resources/resource) | resource |
2526
| [oci_core_default_route_table.default_rt](https://registry.terraform.io/providers/oracle/oci/4.72.0/docs/resources/core_default_route_table) | resource |
2627
| [oci_core_instance.instance](https://registry.terraform.io/providers/oracle/oci/4.72.0/docs/resources/core_instance) | resource |
2728
| [oci_core_internet_gateway.igw](https://registry.terraform.io/providers/oracle/oci/4.72.0/docs/resources/core_internet_gateway) | resource |
29+
| [oci_core_security_list.security_list_ssh](https://registry.terraform.io/providers/oracle/oci/4.72.0/docs/resources/core_security_list) | resource |
30+
| [oci_core_security_list.security_list_vscode](https://registry.terraform.io/providers/oracle/oci/4.72.0/docs/resources/core_security_list) | resource |
2831
| [oci_core_subnet.subnet](https://registry.terraform.io/providers/oracle/oci/4.72.0/docs/resources/core_subnet) | resource |
2932
| [oci_core_vcn.vcn](https://registry.terraform.io/providers/oracle/oci/4.72.0/docs/resources/core_vcn) | resource |
3033
| [oci_core_volume.volume](https://registry.terraform.io/providers/oracle/oci/4.72.0/docs/resources/core_volume) | resource |
31-
| [oci_core_volume_attachment.volume_attach](https://registry.terraform.io/providers/oracle/oci/4.72.0/docs/resources/core_volume_attachment) | resource |
34+
| [oci_core_volume_attachment.volume_attachment](https://registry.terraform.io/providers/oracle/oci/4.72.0/docs/resources/core_volume_attachment) | resource |
3235
| [oci_core_volume_backup_policy_assignment.policy](https://registry.terraform.io/providers/oracle/oci/4.72.0/docs/resources/core_volume_backup_policy_assignment) | resource |
3336
| [tls_private_key.default](https://registry.terraform.io/providers/hashicorp/tls/3.4.0/docs/resources/private_key) | resource |
37+
| [cloudinit_config.cloudinit](https://registry.terraform.io/providers/hashicorp/cloudinit/2.2.0/docs/data-sources/config) | data source |
3438
| [oci_core_images.ubuntu_20_04_aarch64](https://registry.terraform.io/providers/oracle/oci/4.72.0/docs/data-sources/core_images) | data source |
3539
| [oci_core_volume_backup_policies.predefined_volume_backup_policies](https://registry.terraform.io/providers/oracle/oci/4.72.0/docs/data-sources/core_volume_backup_policies) | data source |
36-
| [oci_identity_availability_domain.ad](https://registry.terraform.io/providers/oracle/oci/4.72.0/docs/data-sources/identity_availability_domain) | data source |
37-
| [template_cloudinit_config.cloudinit](https://registry.terraform.io/providers/hashicorp/template/2.2.0/docs/data-sources/cloudinit_config) | data source |
38-
| [template_file.template](https://registry.terraform.io/providers/hashicorp/template/2.2.0/docs/data-sources/file) | data source |
40+
| [oci_identity_availability_domains.ads](https://registry.terraform.io/providers/oracle/oci/4.72.0/docs/data-sources/identity_availability_domains) | data source |
3941

4042
## Inputs
4143

@@ -49,12 +51,18 @@ No modules.
4951
| <a name="input_fingerprint"></a> [fingerprint](#input\_fingerprint) | Fingerprint | `string` | `null` | no |
5052
| <a name="input_private_key"></a> [private\_key](#input\_private\_key) | Private Key content | `string` | `null` | no |
5153
| <a name="input_region"></a> [region](#input\_region) | Default Region | `string` | `"uk-london-1"` | no |
54+
| <a name="input_allowed_ingress_ssh"></a> [allowed\_ingress\_ssh](#input\_allowed\_ingress\_ssh) | List of IPs allowed to SSH on the instance | `list(string)` | `[]` | no |
55+
| <a name="input_allowed_egress_ssh"></a> [allowed\_egress\_ssh](#input\_allowed\_egress\_ssh) | List of IPs the instance is allowed to connect | `list(string)` | <pre>[<br> "0.0.0.0/0"<br>]</pre> | no |
56+
| <a name="input_allowed_ingress_vscode"></a> [allowed\_ingress\_vscode](#input\_allowed\_ingress\_vscode) | List of IPs allowed to access to VS Code Server | `list(string)` | `[]` | no |
57+
| <a name="input_allowed_egress_vscode"></a> [allowed\_egress\_vscode](#input\_allowed\_egress\_vscode) | List of IPs the instance is allowed to connect | `list(string)` | <pre>[<br> "0.0.0.0/0"<br>]</pre> | no |
5258
| <a name="input_instance_shape"></a> [instance\_shape](#input\_instance\_shape) | Instance Shape | `string` | `"VM.Standard.A1.Flex"` | no |
5359
| <a name="input_instance_ocpus"></a> [instance\_ocpus](#input\_instance\_ocpus) | Number of OCPUS (CPU cores) | `string` | `4` | no |
5460
| <a name="input_instance_shape_config_memory_in_gbs"></a> [instance\_shape\_config\_memory\_in\_gbs](#input\_instance\_shape\_config\_memory\_in\_gbs) | Memory in GBs | `string` | `24` | no |
55-
| <a name="input_block_volume_size"></a> [block\_volume\_size](#input\_block\_volume\_size) | Block Volume size in GBs | `string` | `150` | no |
5661
| <a name="input_instance_os"></a> [instance\_os](#input\_instance\_os) | Instance OS | `string` | `"Canonical Ubuntu"` | no |
5762
| <a name="input_instance_os_version"></a> [instance\_os\_version](#input\_instance\_os\_version) | Instance OS Version | `string` | `"20.04"` | no |
63+
| <a name="input_instance_os_user"></a> [instance\_os\_user](#input\_instance\_os\_user) | Instance User | `string` | `"ubuntu"` | no |
64+
| <a name="input_block_volume_size"></a> [block\_volume\_size](#input\_block\_volume\_size) | Block Volume size in GBs (/data) | `string` | `100` | no |
65+
| <a name="input_block_volume_device_name"></a> [block\_volume\_device\_name](#input\_block\_volume\_device\_name) | Block Volume device name (/dev/oracleoci/oraclevdb) | `string` | `"/dev/oracleoci/oraclevdb"` | no |
5866
| <a name="input_vscode_version"></a> [vscode\_version](#input\_vscode\_version) | VS Code Server Version | `string` | `"4.4.0"` | no |
5967
| <a name="input_keypair_name"></a> [keypair\_name](#input\_keypair\_name) | Name of the Key Pair (instance or service for ex.) | `string` | `null` | no |
6068
| <a name="input_keypair_public_key"></a> [keypair\_public\_key](#input\_keypair\_public\_key) | A pregenerated OpenSSH-formatted public key. Changing this creates a new keypair. If a public key is not specified, then a public/private key pair will be automatically generated. If a pair is created, then destroying this resource means you will lose access to that keypair forever. | `string` | `null` | no |

cloudinit.tf

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,17 @@
1-
# Update the Cloud Init template
2-
data "template_file" "template" {
3-
template = file("./cloudinit/template.yml")
4-
5-
vars = {
1+
locals {
2+
cloudinit = templatefile("${path.root}/cloudinit/template.tftpl", {
3+
user = var.instance_os_user
64
vscode_version = var.vscode_version
7-
}
5+
})
86
}
97

108
# Generate the Cloud Init config file
11-
data "template_cloudinit_config" "cloudinit" {
12-
gzip = false
13-
base64_encode = false
9+
data "cloudinit_config" "cloudinit" {
1410

15-
# Configure the instance & Instance VSCode Server
11+
# Configure the instance + install VSCode Server
1612
part {
1713
filename = "init.cfg"
1814
content_type = "text/cloud-config"
19-
content = data.template_file.template.rendered
15+
content = local.cloudinit
2016
}
2117
}

cloudinit/template.yml renamed to cloudinit/template.tftpl

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,10 @@ package_upgrade: true
1010
# timezone: set the timezone
1111
timezone: UTC
1212

13-
# Test if Block volume is attached & format it if needed
14-
bootcmd:
15-
- mkdir -p /data
16-
1713
runcmd:
18-
- chown opc:opc /data
19-
- curl -fOL https://github.com/coder/code-server/releases/download/v${vscode_version}/code-server_${vscode_version}_arm64.deb
20-
- dpkg -i code-server_${vscode_version}_arm64.deb
21-
- systemctl enable --now code-server@opc
14+
- curl -fOL https://github.com/coder/code-server/releases/download/v${vscode_version}/code-server_${vscode_version}_arm64.deb # Download the code-server package
15+
- dpkg -i code-server_${vscode_version}_arm64.deb # Install the code-server package
16+
- systemctl enable --now code-server@${user}
2217

2318
final_message: "System boot (via cloud-init) is COMPLETE, after $UPTIME seconds. Finished at $TIMESTAMP"
2419

firewall.tf

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
locals {
2+
security_list_ssh = "${var.namespace}-seclist-ssh-${var.stage}"
3+
security_list_vscode = "${var.namespace}-seclist-vscode-${var.stage}"
4+
}
5+
6+
# Security List | SSH
7+
resource "oci_core_security_list" "security_list_ssh" {
8+
9+
# Global
10+
compartment_id = var.compartment_ocid
11+
vcn_id = oci_core_vcn.vcn.id
12+
display_name = local.security_list_ssh
13+
14+
# Ingress
15+
dynamic "ingress_security_rules" {
16+
for_each = var.allowed_ingress_ssh
17+
18+
content {
19+
description = "Allow traffic only from the SSH allowed IPs"
20+
source = ingress_security_rules.value
21+
protocol = "6" # TCP
22+
23+
tcp_options {
24+
min = 22 # SSH
25+
max = 22 # SSH
26+
}
27+
}
28+
}
29+
30+
# Egress
31+
dynamic "egress_security_rules" {
32+
for_each = var.allowed_egress_ssh
33+
34+
content {
35+
description = "Allow all outbound traffic from the SSH allowed IPs"
36+
destination = egress_security_rules.value
37+
protocol = "all"
38+
}
39+
}
40+
}
41+
42+
# Security List | VSCode
43+
resource "oci_core_security_list" "security_list_vscode" {
44+
45+
# Global
46+
compartment_id = var.compartment_ocid
47+
vcn_id = oci_core_vcn.vcn.id
48+
display_name = local.security_list_vscode
49+
50+
# Ingress
51+
dynamic "ingress_security_rules" {
52+
for_each = var.allowed_ingress_vscode
53+
54+
content {
55+
description = "Allow traffic only from the VSCode allowed IPs"
56+
source = ingress_security_rules.value
57+
protocol = "6" # TCP
58+
59+
tcp_options {
60+
min = 443 # HTTPS
61+
max = 443 # HTTPS
62+
}
63+
}
64+
}
65+
66+
# Egress
67+
dynamic "egress_security_rules" {
68+
for_each = var.allowed_egress_vscode
69+
70+
content {
71+
description = "Allow all outbound traffic from the VSCode allowed IPs"
72+
destination = egress_security_rules.value
73+
protocol = "all"
74+
}
75+
}
76+
}

instance.tf

Lines changed: 5 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ resource "oci_core_instance" "instance" {
2626

2727
# Global
2828
compartment_id = var.compartment_ocid
29-
availability_domain = data.oci_identity_availability_domain.ad.name
29+
availability_domain = local.availability_domain
3030
display_name = local.instance_name
3131

3232
# Instance
@@ -54,7 +54,9 @@ resource "oci_core_instance" "instance" {
5454

5555
# Launch Options
5656
launch_options {
57-
is_pv_encryption_in_transit_enabled = true
57+
#checkov:skip=CKV_OCI_4: Can't apply encryption in transit on preemptible instances
58+
# is_pv_encryption_in_transit_enabled = true
59+
network_type = "PARAVIRTUALIZED"
5860
}
5961

6062
# Instance Options
@@ -64,7 +66,7 @@ resource "oci_core_instance" "instance" {
6466

6567
metadata = {
6668
ssh_authorized_keys = tls_private_key.default[0].public_key_openssh
67-
user_data = base64encode(data.template_cloudinit_config.cloudinit.rendered)
69+
user_data = data.cloudinit_config.cloudinit.rendered
6870
}
6971

7072
preemptible_instance_config {
@@ -81,37 +83,3 @@ resource "oci_core_instance" "instance" {
8183
# Labels
8284
freeform_tags = local.common_labels
8385
}
84-
85-
# Block Volume
86-
resource "oci_core_volume" "volume" {
87-
88-
#checkov:skip=CKV_OCI_2: The 'backup_policy_id' field has been deprecated.
89-
90-
# Global
91-
compartment_id = var.compartment_ocid
92-
availability_domain = data.oci_identity_availability_domain.ad.name
93-
display_name = local.block_volume_name
94-
95-
# Volume
96-
size_in_gbs = var.block_volume_size
97-
98-
# Encryption
99-
#checkov:skip=CKV_OCI_3: Volume Encryption with KMS will come in the next release
100-
101-
# Labels
102-
freeform_tags = local.common_labels
103-
}
104-
105-
# Volume attachment
106-
resource "oci_core_volume_attachment" "volume_attach" {
107-
108-
# Global
109-
attachment_type = "iscsi"
110-
instance_id = oci_core_instance.instance.id
111-
volume_id = oci_core_volume.volume.id
112-
device = "/dev/oracleoci/oraclevdb"
113-
114-
# Set this to enable CHAP authentication for an ISCSI volume attachment. The oci_core_volume_attachment resource will
115-
# contain the CHAP authentication details via the "chap_secret" and "chap_username" attributes.
116-
use_chap = true
117-
}

instance_volume.tf

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# Create the Block Volume
2+
resource "oci_core_volume" "volume" {
3+
4+
#checkov:skip=CKV_OCI_2: The 'backup_policy_id' field has been deprecated.
5+
6+
# Global
7+
compartment_id = var.compartment_ocid
8+
availability_domain = local.availability_domain
9+
display_name = local.block_volume_name
10+
11+
# Volume
12+
size_in_gbs = var.block_volume_size
13+
14+
# Encryption
15+
#checkov:skip=CKV_OCI_3: Volume Encryption with KMS will come in the next release
16+
# is_pv_encryption_in_transit_enabled = true
17+
18+
# Labels
19+
freeform_tags = local.common_labels
20+
}
21+
22+
# Volume attachment
23+
resource "oci_core_volume_attachment" "volume_attachment" {
24+
25+
# Global
26+
attachment_type = "iscsi"
27+
instance_id = oci_core_instance.instance.id
28+
volume_id = oci_core_volume.volume.id
29+
device = var.block_volume_device_name
30+
31+
# Security
32+
use_chap = true
33+
}
34+
35+
# Mount the Block Volume
36+
resource "null_resource" "remote_exec" {
37+
depends_on = [
38+
oci_core_instance.instance,
39+
oci_core_volume_attachment.volume_attachment,
40+
]
41+
42+
triggers = {
43+
volume_attachment_id = oci_core_volume_attachment.volume_attachment.id # Trigger on volume attachment changes
44+
}
45+
46+
provisioner "remote-exec" {
47+
connection {
48+
agent = false
49+
timeout = "30m"
50+
host = oci_core_instance.instance.public_ip
51+
user = var.instance_os_user
52+
private_key = tls_private_key.default[0].private_key_openssh
53+
}
54+
55+
inline = [
56+
"sudo mkdir -p /data",
57+
"sudo chown ${var.instance_os_user}:${var.instance_os_user} /data", # Add user permission to /data
58+
"sudo iscsiadm -m node -o new -T ${oci_core_volume_attachment.volume_attachment.iqn} -p ${oci_core_volume_attachment.volume_attachment.ipv4}:${oci_core_volume_attachment.volume_attachment.port}",
59+
"sudo iscsiadm -m node -o update -T ${oci_core_volume_attachment.volume_attachment.iqn} -n node.startup -v automatic",
60+
"sudo iscsiadm -m node -T ${oci_core_volume_attachment.volume_attachment.iqn} -p ${oci_core_volume_attachment.volume_attachment.ipv4}:${oci_core_volume_attachment.volume_attachment.port} -o update -n node.session.auth.authmethod -v CHAP",
61+
"sudo iscsiadm -m node -T ${oci_core_volume_attachment.volume_attachment.iqn} -p ${oci_core_volume_attachment.volume_attachment.ipv4}:${oci_core_volume_attachment.volume_attachment.port} -o update -n node.session.auth.username -v ${oci_core_volume_attachment.volume_attachment.chap_username}",
62+
"sudo iscsiadm -m node -T ${oci_core_volume_attachment.volume_attachment.iqn} -p ${oci_core_volume_attachment.volume_attachment.ipv4}:${oci_core_volume_attachment.volume_attachment.port} -o update -n node.session.auth.password -v ${oci_core_volume_attachment.volume_attachment.chap_secret}",
63+
"sudo iscsiadm -m node -T ${oci_core_volume_attachment.volume_attachment.iqn} -p ${oci_core_volume_attachment.volume_attachment.ipv4}:${oci_core_volume_attachment.volume_attachment.port} -l",
64+
"sleep 5", # Wait for the volume to be mounted
65+
"sudo blkid $(readlink -f ${var.block_volume_device_name}) || sudo mkfs -t ext4 $(readlink -f ${var.block_volume_device_name})", # Format the volume if needed
66+
"sudo e2label $(readlink -f ${var.block_volume_device_name}) instance-data", # Set the label
67+
"sudo sed -i -e '/^[\\/][^ \t]*[ \t]*\\/data[ \t]/d' /etc/fstab", # Remove the old entry
68+
"sudo grep -q ^LABEL=instance-data /etc/fstab || echo 'LABEL=instance-data /data ext4 defaults' | sudo tee -a /etc/fstab >/dev/null", # Add the new entry
69+
"sudo grep -q \"^$(readlink -f ${var.block_volume_device_name}) /data \" /proc/mounts || sudo mount /data", # Mount the volume
70+
]
71+
}
72+
}

0 commit comments

Comments
 (0)