Skip to content

Commit 4e9ef78

Browse files
gbereniceGowiem
andauthored
feat: support AWS SSM tailscaled state (#41)
## what * This adds support to store `tailscaled` state in AWS SSM. This helps reusing device state names rather than deleting old devices which is not supported by Tailscale via Terraform ATM. * This adds recent Masterpoint's GH + CRabbit configs. * This sets trunk update to run less often to reduce noise. * This adds ability to confugure ASG min/max size and desired capacity. This is important in case of using an external state to avoid the [`Duplicate node key ` issue](https://tailscale.com/kb/1023/troubleshooting): > This can occur if you use a backup of one device to create another, or clone a file system from one device to another. The Tailscale configuration files are duplicated. The Tailscale files will need to be removed from one of the two. You can identify duplicated devices in the [Machines](https://login.tailscale.com/admin/machines) page of the admin console by looking for a Duplicate node key badge underneath the device name. On one of the systems, [uninstall and completely delete](https://tailscale.com/kb/1069/uninstall) the Tailscale app. It is especially important to remove the files listed for your platform, the goal is to make a new Tailscale IP address when it is installed again. Then, [reinstall the app](https://tailscale.com/kb/install). ## why * Ephemeral nodes behave glitchy during the rotation - we had to perform a manual instance restart to run Tailscale. It's hard to reproduce, so keeping a state in external storage in one more option to try to keep the device in order. ## references * https://tailscale.com/kb/1278/tailscaled#flags-to-tailscaled <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Summary by CodeRabbit - **New Features** - Introduced a new configuration file for CodeRabbit integration, enhancing review and feedback processes. - Added new modules for managing SSM state parameters and IAM policies in the Terraform setup. - Expanded configuration options with new variables for Auto Scaling Group and Tailscale state management. - Updated the workflow for trunk upgrades to run monthly, improving efficiency. - **Documentation** - Enhanced `README.md` with new module and variable details for better user guidance. - **Chores** - Updated `.gitignore` to manage ignored files more effectively. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Matt Gowie <[email protected]>
1 parent 67f76f8 commit 4e9ef78

File tree

8 files changed

+210
-24
lines changed

8 files changed

+210
-24
lines changed

.coderabbit.yaml

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# Docs: https://docs.coderabbit.ai/configure-coderabbit
2+
# Schema: https://coderabbit.ai/integrations/schema.v2.json
3+
# Support: https://discord.gg/GsXnASn26c
4+
5+
language: en
6+
7+
tone_instructions: |
8+
Provide feedback in a professional, friendly, constructive, and concise tone.
9+
Offer clear, specific suggestions and best practices to help enhance the code quality and promote learning.
10+
11+
early_access: true
12+
13+
knowledge_base:
14+
# The scope of learnings to use for the knowledge base.
15+
# `local` uses the repository's learnings,
16+
# `global` uses the organization's learnings,
17+
# `auto` uses repository's learnings for public repositories and organization's learnings for private repositories.
18+
# Default value: `auto`
19+
learnings:
20+
scope: global
21+
issues:
22+
scope: global
23+
pull_requests:
24+
scope: global
25+
26+
reviews:
27+
profile: chill
28+
auto_review:
29+
# Ignore reviewing if the title of the pull request contains any of these keywords (case-insensitive)
30+
ignore_title_keywords:
31+
- wip
32+
- draft
33+
- test
34+
# Set the commit status to 'pending' when the review is in progress and 'success' when it is complete.
35+
commit_status: false
36+
# Post review details on each review. Additionally, post a review status when a review is skipped in certain cases.
37+
review_status: false
38+
path_instructions:
39+
- path: "**/*.tf"
40+
instructions: |
41+
You're a Terraform expert who has thoroughly studied all the documentation from Hashicorp https://developer.hashicorp.com/terraform/docs and OpenTofu https://opentofu.org/docs/.
42+
You have a strong grasp of Terraform syntax and prioritize providing accurate and insightful code suggestions.
43+
As a fan of the Cloud Posse / SweetOps ecosystem, you incorporate many of their best practices https://docs.cloudposse.com/best-practices/terraform/ while balancing them with general Terraform guidelines.
44+
tools:
45+
# By default, all tools are enabled.
46+
# Masterpoint uses Trunk (https://trunk.io) so we do not need a lot of this feedback due to overlap.
47+
shellcheck:
48+
enabled: false
49+
ruff:
50+
enabled: false
51+
markdownlint:
52+
enabled: false
53+
github-checks:
54+
enabled: false
55+
languagetool:
56+
enabled: false
57+
biome:
58+
enabled: false
59+
hadolint:
60+
enabled: false
61+
swiftlint:
62+
enabled: false
63+
phpstan:
64+
enabled: false
65+
golangci-lint:
66+
enabled: false
67+
yamllint:
68+
enabled: false
69+
gitleaks:
70+
enabled: false
71+
checkov:
72+
enabled: false
73+
detekt:
74+
enabled: false
75+
eslint:
76+
enabled: false
77+
rubocop:
78+
enabled: false
79+
buf:
80+
enabled: false
81+
regal:
82+
enabled: false
83+
actionlint:
84+
enabled: false
85+
pmd:
86+
enabled: false
87+
cppcheck:
88+
enabled: false
89+
circleci:
90+
enabled: false

.github/workflows/trunk-upgrade.yaml

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
name: Weekly Trunk Upgrade
1+
name: Monthly Trunk Upgrade
22
on:
33
schedule:
4-
# Every Monday @ 5am
5-
- cron: 0 5 * * 1
4+
# On the first day of every month @ 8am
5+
- cron: 0 8 1 * *
66
# Allows us to manually run the workflow from Actions UI
77
workflow_dispatch: {}
88
permissions: read-all
@@ -18,7 +18,7 @@ jobs:
1818
uses: actions/checkout@v4
1919

2020
- name: Trunk Upgrade
21-
uses: trunk-io/trunk-action/upgrade@d5b1b61d0beee562512f530a278b6a2931fba857
21+
uses: trunk-io/trunk-action/upgrade@2eaee169140ec559bd556208f9f99cdfdf468da8 # v1.1.18
2222
with:
2323
base: main
2424
reviewers: "@masterpointio/masterpoint-internal"

.gitignore

+5-3
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@
77
.terraform
88
.terraform.tfstate.lock.info
99

10-
**/.idea
11-
**/*.iml
12-
1310
# Cloud Posse Build Harness https://github.com/cloudposse/build-harness
1411
**/.build-harness
1512
**/build-harness
1613

1714
# Crash log files
1815
crash.log
1916
test.log
17+
18+
# Random
19+
**/.idea
20+
**/*.iml
21+
.DS_Store

README.md

+18-10
Large diffs are not rendered by default.

examples/complete/fixtures.us-east-2.tfvars

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ region = "us-east-1"
88
availability_zones = ["us-east-1a", "us-east-1b"]
99
ipv4_primary_cidr_block = "172.16.0.0/16"
1010

11+
ssm_state_enabled = true
12+
1113
# Replace these values with your own
1214
tailnet = "orgname.org.github"
1315
oauth_client_id = "OAUTH_CLIENT_ID"

main.tf

+62-4
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,14 @@ locals {
44
prefixed_primary_tag = "tag:${local.primary_tag}"
55
prefixed_additional_tags = [for tag in var.additional_tags : "tag:${tag}"]
66

7+
ssm_state_param_name = var.ssm_state_enabled ? "/tailscale/${module.this.id}/state" : null
8+
ssm_state_flag = var.ssm_state_enabled ? "--state=${module.ssm_state[0].arn_map[local.ssm_state_param_name]}" : ""
9+
710
tailscale_tags = concat([local.prefixed_primary_tag], local.prefixed_additional_tags)
811

9-
tailscaled_extra_flags_enabled = length(var.tailscaled_extra_flags) > 0
12+
tailscaled_extra_flags = join(" ", compact(concat(var.tailscaled_extra_flags, [local.ssm_state_flag])))
13+
tailscaled_extra_flags_enabled = length(local.tailscaled_extra_flags) > 0
14+
1015
tailscale_up_extra_flags_enabled = length(var.tailscale_up_extra_flags) > 0
1116

1217
userdata = templatefile("${path.module}/userdata.sh.tmpl", {
@@ -18,7 +23,7 @@ locals {
1823
tags = join(",", local.tailscale_tags)
1924

2025
tailscaled_extra_flags_enabled = local.tailscaled_extra_flags_enabled
21-
tailscaled_extra_flags = join(" ", var.tailscaled_extra_flags)
26+
tailscaled_extra_flags = local.tailscaled_extra_flags
2227
tailscale_up_extra_flags_enabled = local.tailscale_up_extra_flags_enabled
2328
tailscale_up_extra_flags = join(" ", var.tailscale_up_extra_flags)
2429
})
@@ -45,8 +50,11 @@ module "tailscale_subnet_router" {
4550
session_logging_enabled = var.session_logging_enabled
4651
session_logging_ssm_document_name = var.session_logging_ssm_document_name
4752

48-
ami = var.ami
49-
instance_type = var.instance_type
53+
ami = var.ami
54+
instance_type = var.instance_type
55+
max_size = var.max_size
56+
min_size = var.min_size
57+
desired_capacity = var.desired_capacity
5058

5159
monitoring_enabled = var.monitoring_enabled
5260
associate_public_ip_address = var.associate_public_ip_address
@@ -63,3 +71,53 @@ resource "tailscale_tailnet_key" "default" {
6371
# A device is automatically tagged when it is authenticated with this key.
6472
tags = local.tailscale_tags
6573
}
74+
75+
module "ssm_state" {
76+
count = var.ssm_state_enabled ? 1 : 0
77+
source = "cloudposse/ssm-parameter-store/aws"
78+
version = "0.13.0"
79+
ignore_value_changes = true
80+
81+
parameter_write = [
82+
{
83+
name = local.ssm_state_param_name
84+
type = "SecureString"
85+
overwrite = "true"
86+
value = "{}"
87+
description = "Tailscaled state of ${module.this.id} subnet router."
88+
}
89+
]
90+
context = module.this.context
91+
tags = module.this.tags
92+
}
93+
94+
module "ssm_policy" {
95+
count = var.ssm_state_enabled ? 1 : 0
96+
source = "cloudposse/iam-policy/aws"
97+
version = "2.0.1"
98+
99+
name = "ssm"
100+
description = "Additional SSM access for SSM Agent"
101+
102+
iam_policy_enabled = true
103+
iam_policy = [{
104+
statements = [
105+
{
106+
sid = "SSMAgentPutParameter"
107+
effect = "Allow"
108+
actions = ["ssm:PutParameter"]
109+
resources = [
110+
module.ssm_state[0].arn_map[local.ssm_state_param_name],
111+
]
112+
},
113+
]
114+
}]
115+
context = module.this.context
116+
tags = module.this.tags
117+
}
118+
119+
resource "aws_iam_role_policy_attachment" "default" {
120+
count = var.ssm_state_enabled ? 1 : 0
121+
role = module.tailscale_subnet_router.role_id
122+
policy_arn = module.ssm_policy[0].policy_arn
123+
}

userdata.sh.tmpl

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ sudo yum-config-manager --add-repo https://pkgs.tailscale.com/stable/amazon-linu
1414
sudo yum install -y tailscale
1515

1616
%{ if tailscaled_extra_flags_enabled == true }
17-
echo "Exporting FLAGS to environment variable..."
18-
export FLAGS=${tailscaled_extra_flags}%
17+
echo "Exporting FLAGS to /etc/default/tailscaled..."
18+
sudo sed -i "s|^FLAGS=.*|FLAGS=\"${tailscaled_extra_flags}\"|" /etc/default/tailscaled
1919
%{ endif }
2020

2121
# Setup tailscale

variables.tf

+27-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ variable "session_logging_kms_key_alias" {
4343
EOF
4444
}
4545

46-
4746
variable "session_logging_ssm_document_name" {
4847
default = "SSM-SessionManagerRunShell-Tailscale"
4948
type = string
@@ -98,6 +97,24 @@ variable "associate_public_ip_address" {
9897
default = null
9998
}
10099

100+
variable "max_size" {
101+
description = "Maximum number of instances in the Auto Scaling Group. Must be >= desired_capacity."
102+
type = number
103+
default = 2
104+
}
105+
106+
variable "min_size" {
107+
description = "Minimum number of instances in the Auto Scaling Group"
108+
type = number
109+
default = 1
110+
}
111+
112+
variable "desired_capacity" {
113+
description = "Desired number of instances in the Auto Scaling Group"
114+
type = number
115+
default = 1
116+
}
117+
101118
################
102119
## Tailscale ##
103120
##############
@@ -180,3 +197,12 @@ variable "tailscale_up_extra_flags" {
180197
See more in the [docs](https://tailscale.com/kb/1241/tailscale-up).
181198
EOT
182199
}
200+
201+
variable "ssm_state_enabled" {
202+
default = false
203+
type = bool
204+
description = <<-EOT
205+
Control if tailscaled state is stored in AWS SSM (including preferences and keys). This tells the Tailscale daemon to write + read state from SSM, which unlocks important features like retaining the existing tailscale machine name.
206+
See more in the [docs](https://tailscale.com/kb/1278/tailscaled#flags-to-tailscaled).
207+
EOT
208+
}

0 commit comments

Comments
 (0)