diff --git a/config/hosts/rinnosuke/meta.nix b/config/hosts/rinnosuke/meta.nix new file mode 100644 index 00000000..9b760342 --- /dev/null +++ b/config/hosts/rinnosuke/meta.nix @@ -0,0 +1,6 @@ +{ config, lib, kw, ... }: { + network.nodes.rinnosuke = { + imports = kw.nodeImport "rinnosuke"; + }; +} + diff --git a/config/hosts/rinnosuke/nixos.nix b/config/hosts/rinnosuke/nixos.nix new file mode 100644 index 00000000..35d2c375 --- /dev/null +++ b/config/hosts/rinnosuke/nixos.nix @@ -0,0 +1,173 @@ +{ config, tf, meta, kw, pkgs, lib, sources, ... }: with lib; let + oci-root = meta.deploy.targets.oci-root.tf; + lustrateHost = "ubuntu"; +in { + imports = (with (import (sources.tf-nix + "/modules")); [ + nixos.ubuntu-linux + nixos.oracle + ]) ++ (with meta; [ services.nginx ]); + + services.nginx.virtualHosts = let + splashy = pkgs.host-splash-site config.networking.hostName; + in kw.virtualHostGen { + networkFilter = [ "public" ]; + block.locations."/" = { root = splashy; }; + }; + + networking = { + hostName = "rinnosuke"; + }; + + network = { + addresses.public.enable = true; + firewall.public.interfaces = singleton "ens3"; + tf = { + enable = true; + ipv4_attr = "public_ip"; + }; + }; + + deploy.tf = let + compartment_id = oci-root.resources.oci_kw_compartment.importAttr "id"; + inherit (tf.lib.tf) terraformExpr; + in { + deploy.systems.rinnosuke = { + lustrate = { + enable = true; + connection = tf.resources.rinnosuke.connection.set; + }; + connection = { + port = 62954; + }; + }; + providers.oci = { + inputs = { + tenancy_ocid = oci-root.outputs.oci_tenancy.import; + user_ocid = oci-root.resources.oci_kw_user.importAttr "id"; + fingerprint = oci-root.resources.oci_kw_apikey.importAttr "fingerprint"; + region = oci-root.outputs.oci_region.import; + private_key_path = oci-root.resources.oci_kw_key_file.importAttr "filename"; + }; + }; + resources = mkMerge [ { + cloudinit = { + provider = "cloudinit"; + type = "config"; + dataSource = true; + inputs = { + part = singleton { + content_type = "text/cloud-config"; + content = "#cloud-config\n" + builtins.toJSON { + disable_root = false; + }; + }; + }; + }; + availability_domain = { + provider = "oci"; + type = "identity_availability_domain"; + dataSource = true; + inputs = { + inherit compartment_id; + ad_number = 2; + }; + }; + generic_image = { + provider = "oci"; + type = "core_images"; + dataSource = true; + inputs = { + inherit compartment_id; + inherit (tf.resources.rinnosuke.inputs) shape; + operating_system = "Canonical Ubuntu"; # "Oracle Linux" + sort_by = "TIMECREATED"; + sort_order = "DESC"; + }; + }; + rinnosuke = { + provider = "oci"; + type = "core_instance"; + inputs = { + inherit compartment_id; + extended_metadata = { }; + metadata = { + ssh_authorized_keys = concatStringsSep "\n" config.users.users.root.openssh.authorizedKeys.keys; + user_data = tf.resources.cloudinit.refAttr "rendered"; + }; + shape = "VM.Standard.E2.1.Micro"; + shape_config = { + memory_in_gbs = 1; + ocpus = 1; + }; + source_details = { + source_type = "image"; + source_id = tf.resources.generic_image.refAttr "images[0].id"; + boot_volume_size_in_gbs = 50; # min 50GB, up to 200GB free + }; + create_vnic_details = [ + { + assign_public_ip = true; + subnet_id = oci-root.resources.oci_kw_subnet.importAttr "id"; + private_ip = terraformExpr ''cidrhost("${oci-root.resources.oci_kw_subnet.importAttr "cidr_block"}", 3)''; + nsg_ids = [ + (tf.resources.firewall_group.refAttr "id") + ]; + } + ]; + availability_domain = tf.resources.availability_domain.refAttr "name"; + }; + lifecycle.ignoreChanges = [ + "source_details[0].source_id" + ]; + connection = { + type = "ssh"; + user = "root"; + host = tf.lib.tf.terraformSelf "public_ip"; + timeout = "5m"; + }; + }; + firewall_group = { + provider = "oci"; + type = "core_network_security_group"; + inputs = { + display_name = "${config.networking.hostName} firewall group"; + inherit compartment_id; + vcn_id = oci-root.resources.oci_vcn.importAttr "id"; + }; + }; + } (let + protoValues = { + TCP = 6; + UDP = 17; + }; + inherit (config.networking) firewall; + ipv4 = "0.0.0.0/0"; + ipv6 = "::/0"; + mapPort = source: protocol: port: { + provider = "oci"; + type = "core_network_security_group_security_rule"; + inputs = { + network_security_group_id = tf.resources.firewall_group.refAttr "id"; + inherit protocol source; + direction = "INGRESS"; + ${if protocol == protoValues.TCP then "tcp_options" else "udp_options"} = { + destination_port_range = if isAttrs port then { + min = port.from; + max = port.to; + } else { + min = port; + max = port; + }; + }; + }; + }; + mapAll = protocol: port: [ (mapPort ipv4 protocol port) (mapPort ipv6 protocol port) ]; + mapAllForInterface = let + protos = [ "TCP" "UDP" ]; + types = [ "Ports" "PortRanges" ]; + in interface: concatMap (type: concatMap (proto: (concatMap (port: (mapAll protoValues.${proto}) port) interface."allowed${proto}${type}")) protos ) types; + rules = concatMap mapAllForInterface ([ firewall ] ++ map (interface: firewall.interfaces.${interface}) config.network.firewall.public.interfaces); + # TODO: use `count` and index into a fancy json or something? + in listToAttrs (imap0 (i: rule: nameValuePair "firewall${toString i}" rule) rules)) ]; + }; +} diff --git a/config/targets/oci-root/default.nix b/config/targets/oci-root/default.nix new file mode 100644 index 00000000..3c2f7282 --- /dev/null +++ b/config/targets/oci-root/default.nix @@ -0,0 +1,184 @@ +{ config, lib, ... }: with lib; { + deploy.targets.oci-root = { + tf = let + meta = config; + in { config, ... }: let + inherit (config.lib.tf) terraformExpr; + res = config.resources; + var = config.variables; + out = config.outputs; + in { + variables = let + apivar = { + type = "string"; + sensitive = true; + }; + in mkMerge [ + (genAttrs (map (value: "oci_root_${value}" ) [ "region" "tenancy" "user" "privkey" "fingerprint"] ) (attr: { + value.shellCommand = "pass secrets/oracle-${head (reverseList (splitString "_" attr))}"; + type = "string"; + })) + {"oci_root_privkey" = { sensitive = true; }; } + ]; + + providers.oci-root = { + type = "oci"; + inputs = with config.variables; { + tenancy_ocid = oci_root_tenancy.ref; + user_ocid = oci_root_user.ref; + private_key = oci_root_privkey.ref; + fingerprint = oci_root_fingerprint.ref; + region = oci_root_region.ref; + }; + }; + + resources = { + oci_kw_compartment = { + provider = "oci.oci-root"; + type = "identity_compartment"; + inputs = { + name = "kw"; + description = "kw"; + compartment_id = var.oci_root_tenancy.ref; + enable_delete = true; + }; + }; + oci_kw_user = { + provider = "oci.oci-root"; + type = "identity_user"; + inputs = { + name = "kw"; + description = "kw"; + compartment_id = var.oci_root_tenancy.ref; + }; + }; + oci_kw_group = { + provider = "oci.oci-root"; + type = "identity_group"; + inputs = { + name = "kw"; + description = "kw"; + compartment_id = var.oci_root_tenancy.ref; + }; + }; + oci_kw_usergroup = { + provider = "oci.oci-root"; + type = "identity_user_group_membership"; + inputs = { + group_id = res.oci_kw_group.refAttr "id"; + user_id = res.oci_kw_user.refAttr "id"; + }; + }; + oci_kw_key = { + provider = "tls"; + type = "private_key"; + inputs = { + algorithm = "RSA"; + rsa_bits = 2048; + }; + }; + oci_kw_key_file = { + provider = "local"; + type = "file"; + inputs = { + sensitive_content = res.oci_kw_key.refAttr "private_key_pem"; + filename = toString (config.terraform.dataDir + "/oci_kw_key"); + file_permission = "0600"; + }; + }; + oci_kw_apikey = { + provider = "oci.oci-root"; + type = "identity_api_key"; + inputs = { + key_value = res.oci_kw_key.refAttr "public_key_pem"; + user_id = res.oci_kw_user.refAttr "id"; + }; + }; + oci_kw_policy = { + provider = "oci.oci-root"; + type = "identity_policy"; + inputs = { + name = "kw-admin"; + description = "kw admin"; + compartment_id = var.oci_root_tenancy.ref; + statements = [ + "Allow group ${res.oci_kw_group.refAttr "name"} to manage all-resources in compartment id ${res.oci_kw_compartment.refAttr "id"}" + "Allow group ${res.oci_kw_group.refAttr "name"} to read virtual-network-family in compartment id ${var.oci_root_tenancy.ref}" + '' + Allow group ${res.oci_kw_group.refAttr "name"} to manage vcns in compartment id ${var.oci_root_tenancy.ref} where ALL { + ANY { request.operation = 'CreateNetworkSecurityGroup', request.operation = 'DeleteNetworkSecurityGroup' } + } + '' + ]; + }; + }; + oci_vcn = { + provider = "oci.oci-root"; + type = "core_vcn"; + inputs = { + display_name = "net"; + compartment_id = var.oci_root_tenancy.ref; + cidr_blocks = [ + "10.69.0.0/16" + ]; + is_ipv6enabled = true; + }; + }; + oci_internet = { + provider = "oci.oci-root"; + type = "core_internet_gateway"; + inputs = { + display_name = "net internet"; + compartment_id = var.oci_root_tenancy.ref; + vcn_id = res.oci_vcn.refAttr "id"; + }; + }; + oci_routes = { + provider = "oci.oci-root"; + type = "core_route_table"; + inputs = { + display_name = "net routes"; + route_rules = [ + { + description = "internet v4"; + destination_type = "CIDR_BLOCK"; + destination = "0.0.0.0/0"; + network_entity_id = res.oci_internet.refAttr "id"; + } + { + description = "internet v6"; + destination_type = "CIDR_BLOCK"; + destination = "::/0"; + network_entity_id = res.oci_internet.refAttr "id"; + } + ]; + compartment_id = var.oci_root_tenancy.ref; + vcn_id = res.oci_vcn.refAttr "id"; + }; + }; + oci_kw_subnet = { + provider = "oci.oci-root"; + type = "core_subnet"; + inputs = { + display_name = "kw"; + cidr_block = terraformExpr "cidrsubnet(${res.oci_vcn.namedRef}.cidr_blocks[0], 8, 8)"; # /24 + ipv6cidr_block = terraformExpr "cidrsubnet(${res.oci_vcn.namedRef}.ipv6cidr_blocks[0], 8, 0)"; # from a /56 block to /64 + compartment_id = res.oci_kw_compartment.refAttr "id"; + vcn_id = res.oci_vcn.refAttr "id"; + route_table_id = res.oci_routes.refAttr "id"; + }; + }; + }; + outputs = { + oci_region = { + value = var.oci_root_region.ref; + sensitive = true; + }; + oci_tenancy = { + value = var.oci_root_tenancy.ref; + sensitive = true; + }; + }; + }; + }; +} diff --git a/nix/sources.json b/nix/sources.json index 1c774917..b7721a78 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -159,10 +159,10 @@ "homepage": null, "owner": "arcnmx", "repo": "tf-nix", - "rev": "a1eb4c4203d13e644b8e7336777b354fe499c3fe", - "sha256": "00p2njqxa5wld3q1qqw80kxm5bprb3k7jhpqmv9rf5bbixgd4zdp", + "rev": "ab2c3268af12b1e5f75bf6bb37aa18c5f6b5eff1", + "sha256": "1szqwlipsxy1ys27wxdqm8s2aw3yffg50y2qxgg9wzm47fd58ys5", "type": "tarball", - "url": "https://github.com/arcnmx/tf-nix/archive/a1eb4c4203d13e644b8e7336777b354fe499c3fe.tar.gz", + "url": "https://github.com/arcnmx/tf-nix/archive/ab2c3268af12b1e5f75bf6bb37aa18c5f6b5eff1.tar.gz", "url_template": "https://github.com///archive/.tar.gz" } }