First version that works, or at least gets all the way through ansible config

This commit is contained in:
Erik Stambaugh 2024-01-18 18:14:17 -08:00
parent 17d579a6eb
commit 31b13dc09b
15 changed files with 302 additions and 71 deletions

6
.gitignore vendored
View file

@ -9,3 +9,9 @@ terraform.tfstate*
config.tf config.tf
privkey privkey
pubkey pubkey
.toolcheck
.s3_iam_credentials
.s3_id
.s3_secret
ansible/credentials
ansible/mastodon_secrets.yaml

View file

@ -12,7 +12,10 @@ endef
default: terraform ansible default: terraform ansible
terraform ansible: config.mk ansible: config.mk .s3_iam_credentials
@$(MAKE) -C $@
terraform: config.mk
@$(MAKE) -C $@ @$(MAKE) -C $@
ssh: config.mk ssh: config.mk
@ -21,10 +24,16 @@ ssh: config.mk
#ansible: #ansible:
# @$(MAKE) -C ansible # @$(MAKE) -C ansible
config.mk: config.mk: config.mk.in
@ $(info $(CONFIG_MSG) ) @ $(info $(CONFIG_MSG) )
@ exit 1 @ exit 1
.s3_iam_credentials: terraform/.s3_id terraform/.s3_secret
cat $^ > $@
terraform/.s3_id terraform/.s3_secret: terraform
clean: clean:
rm -f config.mk rm -f config.mk
@$(MAKE) -C terraform clean @$(MAKE) -C terraform clean

View file

@ -2,8 +2,8 @@
include ../config.mk include ../config.mk
include ../terraform/terraform.mk include ../terraform/terraform.mk
# XXX parameterize # I don't remember why I had this at all:
AWS_REGION = us-west-2 #AWS_REGION = $(AWS_REGION)
SSH := ssh -o "StrictHostKeyChecking=no" -o UserKnownHostsFile=/dev/null -o ProxyCommand="sh -c \"aws --region $(AWS_REGION) ssm start-session --target %h --document-name AWS-StartSSHSession --parameters portNumber=%p\"" -i ../terraform/privkey -l ubuntu SSH := ssh -o "StrictHostKeyChecking=no" -o UserKnownHostsFile=/dev/null -o ProxyCommand="sh -c \"aws --region $(AWS_REGION) ssm start-session --target %h --document-name AWS-StartSSHSession --parameters portNumber=%p\"" -i ../terraform/privkey -l ubuntu
@ -21,7 +21,7 @@ inventory.yaml: inventory.tmpl.yaml sedline
SEDLINE = SEDLINE =
sedline: terraform_sedline config_sedline sedline: terraform_sedline config_sedline s3_sedline
config_sedline: $(addprefix __sed_,$(shell grep '^[0-9A-Z_]' ../config.mk | awk '{print $$1}')) config_sedline: $(addprefix __sed_,$(shell grep '^[0-9A-Z_]' ../config.mk | awk '{print $$1}'))
@ -30,13 +30,10 @@ terraform_sedline: $(addprefix __sed_,$(shell grep '^[0-9A-Z_]' ../terraform/ter
__sed_%: __sed_%:
$(eval SEDLINE := $$(SEDLINE) -e 's/{{$*}}/$($*)/') $(eval SEDLINE := $$(SEDLINE) -e 's/{{$*}}/$($*)/')
# FIXME: this is awful because it's all in the clear
s3_sedline:
# $(eval SEDLINE := $$(SEDLINE) -e 's/{{S3_IAM_ID}}/$(shell head -1 ../.s3_iam_credentials)/')
#TF_OUTPUTS = $(eval SEDLINE := $$(SEDLINE) -e 's/{{S3_IAM_SECRET}}/$(shell tail -1 ../.s3_iam_credentials)/')
#
#tf_outputs: ../terraform/terraform.tfstate
#
# FIXME: DRY this target # FIXME: DRY this target
@ -57,5 +54,6 @@ toolcheck:
fi fi
@echo @echo
mkdir -p credentials/mastodon

View file

@ -7,7 +7,24 @@ social:
hostname: social hostname: social
vars: vars:
ansible_ssh_common_args: -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ProxyCommand="sh -c \"aws --region {{AWS_REGION}} ssm start-session --target %h --document-name AWS-StartSSHSession --parameters portNumber=%p\"" ansible_ssh_common_args: -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ProxyCommand="sh -c \"aws --region {{AWS_REGION}} ssm start-session --target %h --document-name AWS-StartSSHSession --parameters portNumber=%p\""
public_ip: "{{PUBLIC_IP}}" alternate_domains: {{ALTERNATE_DOMAINS}}
aws_region: {{AWS_REGION}}
#db_password:
domain_name: {{DOMAIN_NAME}}
mastodon_sidekiq_count: {{MASTODON_SIDEKIQ_COUNT}} mastodon_sidekiq_count: {{MASTODON_SIDEKIQ_COUNT}}
mastodon_sidekiq_threads: {{MASTODON_SIDEKIQ_THREADS}} mastodon_sidekiq_threads: {{MASTODON_SIDEKIQ_THREADS}}
public_ip: "{{PUBLIC_IP}}"
s3_bucket_name: "{{S3_BUCKET_NAME}}"
#s3_endpoint:
s3_hostname: "s3.{{AWS_REGION}}.amazonaws.com"
s3_iam_id: {{S3_IAM_ID}}
s3_iam_secret: {{S3_IAM_SECRET}}
#smtp_server:
otp_secret:
secret_key_base:
vapid_secrets:
#vapid_private_key:
#vapid_public_key:

View file

@ -7,22 +7,97 @@
- docker-compose-v2 - docker-compose-v2
- git - git
- name: Mastodon path - name: base path
file: file:
path: "/srv/mastodon" path: "/srv/mastodon"
state: directory state: directory
recurse: true recurse: true
- name: mastodon source - name: source
git: git:
repo: "https://tea.entar.net/teh/mastodon.git" repo: "https://tea.entar.net/teh/mastodon.git"
dest: /srv/mastodon/src dest: /srv/mastodon/src
- name: mastodon docker-compose - name: docker-compose file
template: template:
src: templates/docker-compose.mastodon.yaml src: templates/docker-compose.mastodon.yaml
dest: /srv/mastodon/docker-compose.yaml dest: /srv/mastodon/docker-compose.yaml
register: compose
# add env file ## generate a secrets file if we need one
# FIXME: what's in the mastodon_secrets.yaml file should be in credential lookup like db_password is
- name: check mastodon secrets var file
delegate_to: localhost
become: false
stat:
path: mastodon_secrets.yaml
register: mastosecrets
- name: env file stub
template:
src: templates/env.production
dest: /srv/mastodon/.env.production
vars:
db_password: "{{ lookup('ansible.builtin.password', 'credentials/mastodon/postgres', length=15) }}"
alternate_domains: "mastodon_web"
when: mastosecrets.stat.exists != true
- name: get SECRET_KEY_BASE
shell: docker compose run --rm mastodon_web rake secret 2>/dev/null | tail -1
args:
chdir: /srv/mastodon
register: skb
when: mastosecrets.stat.exists != true
- name: get OTP_SECRET
shell: docker compose run --rm mastodon_web rake secret 2>/dev/null | tail -1
args:
chdir: /srv/mastodon
register: otp
when: mastosecrets.stat.exists != true
- name: get vapid secrets
command: docker compose run --rm mastodon_web rake mastodon:webpush:generate_vapid_key
args:
chdir: /srv/mastodon
register: vapid
when: mastosecrets.stat.exists != true
- name: create mastodon secrets file
delegate_to: localhost
become: false
template:
src: templates/mastodon_secrets.yaml
dest: mastodon_secrets.yaml
when: mastosecrets.stat.exists != true
## now that we have a secrets file, read it in and make the env file again
- name: read env secret vars
include_vars:
file: mastodon_secrets.yaml
- name: env file
template:
src: templates/env.production
dest: /srv/mastodon/.env.production
vars:
db_password: "{{ lookup('ansible.builtin.password', 'credentials/mastodon/postgres', length=15) }}"
alternate_domains: "mastodon_web"
register: envfile
## finally, let's launch mastodon
- name: launch mastodon
command: docker compose up -d
args:
chdir: /srv/mastodon
- name: restart mastodon
command: docker compose restart
args:
chdir: /srv/mastodon
when: envfile.changed or compose.changed

View file

@ -43,7 +43,7 @@ services:
# - "thread_pool.write.queue_size=1000" # - "thread_pool.write.queue_size=1000"
# networks: # networks:
# - mastodon # - mastodon
# - nginx # #- nginx
# healthcheck: # healthcheck:
# test: ["CMD-SHELL", "curl --silent --fail localhost:9200/_cluster/health || exit 1"] # test: ["CMD-SHELL", "curl --silent --fail localhost:9200/_cluster/health || exit 1"]
# volumes: # volumes:
@ -67,7 +67,7 @@ services:
command: bash -c "rm -f /mastodon/tmp/pids/server.pid; bundle exec rails s -p 3000" command: bash -c "rm -f /mastodon/tmp/pids/server.pid; bundle exec rails s -p 3000"
networks: networks:
- mastodon - mastodon
- nginx #- nginx
healthcheck: healthcheck:
# prettier-ignore # prettier-ignore
test: ['CMD-SHELL', 'wget -q --spider --proxy=off localhost:3000/health || exit 1'] test: ['CMD-SHELL', 'wget -q --spider --proxy=off localhost:3000/health || exit 1']
@ -78,7 +78,7 @@ services:
- mastodon_redis - mastodon_redis
# - es # - es
volumes: volumes:
- ./public/system:/mastodon/public/system - ./src/public/system:/mastodon/public/system
mastodon_streaming: mastodon_streaming:
container_name: mastodon_streaming container_name: mastodon_streaming
@ -91,7 +91,7 @@ services:
command: node ./streaming command: node ./streaming
networks: networks:
- mastodon - mastodon
- nginx #- nginx
healthcheck: healthcheck:
# prettier-ignore # prettier-ignore
test: ['CMD-SHELL', 'wget -q --spider --proxy=off localhost:4000/api/v1/streaming/health || exit 1'] test: ['CMD-SHELL', 'wget -q --spider --proxy=off localhost:4000/api/v1/streaming/health || exit 1']
@ -101,9 +101,9 @@ services:
- mastodon_db - mastodon_db
- mastodon_redis - mastodon_redis
{% for i in range(mastodon_sidekiq_count) %} {% for i in range(mastodon_sidekiq_count) %}
mastodon_sidekiq_{{i}}: mastodon_sidekiq_{{i}}:
container_name: mastodon_sidekiq container_name: mastodon_sidekiq_{{i}}
build: src build: src
image: ghcr.io/mastodon/mastodon:v4.2.1 image: ghcr.io/mastodon/mastodon:v4.2.1
restart: always restart: always
@ -116,13 +116,13 @@ services:
- mastodon_redis - mastodon_redis
networks: networks:
- mastodon - mastodon
- nginx #- nginx
volumes: volumes:
- ./public/system:/mastodon/public/system - ./src/public/system:/mastodon/public/system
healthcheck: healthcheck:
test: ['CMD-SHELL', "ps aux | grep '[s]idekiq\ 6' || false"] test: ['CMD-SHELL', "ps aux | grep '[s]idekiq\ 6' || false"]
{% endfor %} {% endfor %}
## Uncomment to enable federation with tor instances along with adding the following ENV variables ## Uncomment to enable federation with tor instances along with adding the following ENV variables
## http_proxy=http://privoxy:8118 ## http_proxy=http://privoxy:8118
@ -150,7 +150,7 @@ services:
command: command:
"--statsd.mapping-config=/statsd-mapping.yaml" "--statsd.mapping-config=/statsd-mapping.yaml"
volumes: volumes:
- ./statsd-mapping.yaml:/statsd-mapping.yaml - ./src/statsd-mapping.yaml:/statsd-mapping.yaml
networks: networks:
- mastodon - mastodon

View file

@ -0,0 +1,87 @@
# Note that this file accepts slightly different syntax depending on whether
# you are using `docker-compose` or not. In particular, if you use
# `docker-compose`, the value of each declared variable will be taken verbatim,
# including surrounding quotes.
# See: https://github.com/mastodon/mastodon/issues/16895
# Federation
# ----------
# This identifies your server and cannot be changed safely later
# ----------
LOCAL_DOMAIN={{domain_name}}
ALTERNATE_DOMAINS={{alternate_domains}}
# Redis
# -----
REDIS_HOST=mastodon_redis
REDIS_PORT=6379
# PostgreSQL
# ----------
DB_HOST=mastodon_db
DB_USER=postgres
DB_NAME=mastodon_production
DB_PASS={{db_password}}
DB_PORT=5432
POSTGRES_USER=postgres
POSTGRES_PASSWORD={{db_password}}
POSTGRES_DB=mastodon_production
## Elasticsearch (optional)
## ------------------------
#ES_ENABLED=true
#ES_HOST=localhost
#ES_PORT=9200
## Authentication for ES (optional)
#ES_USER=elastic
#ES_PASS=password
# Secrets
# -------
# Make sure to use `rake secret` to generate secrets
# -------
SECRET_KEY_BASE={{secret_key_base}}
OTP_SECRET={{otp_secret}}
{{vapid_secrets}}
# Sending mail
# ------------
#SMTP_SERVER=#{#{smtp_server#}#}
SMTP_PORT=25
SMTP_FROM_ADDRESS=Mastodon <notifications@{{domain_name}}>
SMTP_AUTH_METHOD=none
SMTP_OPENSSL_VERIFY_MODE=none
## File storage (optional)
## -----------------------
# teh-entar-net-mastodon-media.us-southeast-1.linodeobjects.com
S3_ENABLED=true
S3_BUCKET={{s3_bucket_name}}
AWS_ACCESS_KEY_ID={{s3_iam_id}}
AWS_SECRET_ACCESS_KEY={{s3_iam_secret}}
#S3_ALIAS_HOST=
S3_REGION={{aws_region}}
S3_PROTOCOL=https
#S3_HOSTNAME=teh-entar-net-mastodon-media.us-southeast-1.linodeobjects.com
S3_HOSTNAME={{s3_hostname}}
#S3_ENDPOINT=#{#{s3_endpoint#}#}
S3_OVERRIDE_PATH_STYLE=true
# IP and session retention
# -----------------------
# Make sure to modify the scheduling of ip_cleanup_scheduler in config/sidekiq.yml
# to be less than daily if you lower IP_RETENTION_PERIOD below two days (172800).
# -----------------------
IP_RETENTION_PERIOD=31556952
SESSION_RETENTION_PERIOD=31556952
STATSD_ADDR=statsd:9125
ACTIVITY_API_ENABLED=false
PEERS_API_ENABLED=false
RAILS_LOG_TO_STDOUT=enabled
RAILS_LOG_LEVEL=info

View file

@ -0,0 +1,5 @@
---
secret_key_base: {{skb.stdout}}
otp_secret: {{otp.stdout}}
vapid_secrets: |
{{vapid.stdout | indent(2)}}

View file

@ -21,11 +21,16 @@ AWS_SINGLE_INSTANCE_TYPE = t4g.small
# Paste (one-line!) the root SSH public key for the AWS instance, or leave blank to generate new private/public keys # Paste (one-line!) the root SSH public key for the AWS instance, or leave blank to generate new private/public keys
AWS_INSTANCE_PUBLIC_KEY = AWS_INSTANCE_PUBLIC_KEY =
# What is the DNS subdomain to be delegated for these services? Leave blank to skip.
AWS_ROUTE53_ZONE =
# What is the domain name that will be used for these services? Don't be silly and use the default in production
DOMAIN_NAME = burn.social
# What alternate domains will this use? Leave blank for none.
ALTERNATE_DOMAINS =
# TODO: more detailed sidekiq tuning per https://thomas-leister.de/en/scaling-up-mastodon/ # TODO: more detailed sidekiq tuning per https://thomas-leister.de/en/scaling-up-mastodon/
# How many sidekiq containers should Mastodon have? # How many sidekiq containers should Mastodon have?

View file

@ -5,7 +5,7 @@ default: terraform
# I hate sed too and I am so sorry for what I'm about to do # I hate sed too and I am so sorry for what I'm about to do
terraform: terraform-check *.tf terraform: terraform-check *.tf
terraform init terraform init || terraform init -upgrade
terraform apply terraform apply
terraform output | sed \ terraform output | sed \
-e 's/\(.*\) = /\U\1 = /' \ -e 's/\(.*\) = /\U\1 = /' \
@ -15,7 +15,7 @@ terraform: terraform-check *.tf
-e 's/ \+$$//' \ -e 's/ \+$$//' \
> terraform.mk > terraform.mk
terraform-check: toolcheck pubkey terraform-check: .toolcheck pubkey
$(eval AWS_INSTANCE_PUBLIC_KEY := $(shell sed -e 's/\//\\\//g' pubkey)) $(eval AWS_INSTANCE_PUBLIC_KEY := $(shell sed -e 's/\//\\\//g' pubkey))
@ -38,7 +38,8 @@ __sed_%:
CHECK_TOOLS = terraform aws CHECK_TOOLS = terraform aws
toolcheck: # this relies on your credentials file, so that if you edit it, it will run again
.toolcheck: ~/.aws/credentials
@echo @echo
@echo "Checking applications..." @echo "Checking applications..."
@ FAIL=""; \ @ FAIL=""; \
@ -55,6 +56,11 @@ toolcheck:
@echo @echo
@echo "Checking AWS configuration..." @echo "Checking AWS configuration..."
aws iam get-user aws iam get-user
touch .toolcheck
# ...but if you don't have a credentials file, that's ok, it just runs the check every time
~/.aws/credentials:
true
pubkey: pubkey:
if test -n "$(AWS_INSTANCE_PUBLIC_KEY)"; then \ if test -n "$(AWS_INSTANCE_PUBLIC_KEY)"; then \

View file

@ -2,7 +2,7 @@
locals { locals {
public_key = "{{AWS_INSTANCE_PUBLIC_KEY}}" public_key = "{{AWS_INSTANCE_PUBLIC_KEY}}"
instance_type = "{{AWS_SINGLE_INSTANCE_TYPE}}" instance_type = "{{AWS_SINGLE_INSTANCE_TYPE}}"
route53_zone = "{{AWS_ROUTE53_ZONE}}"
aws_region = "{{AWS_REGION}}" aws_region = "{{AWS_REGION}}"
domain_name = "{{DOMAIN_NAME}}"
} }

View file

@ -1,40 +1,10 @@
# [X] aws provider
# [/] random pet
# not needed w/o s3 bucket
# [/] s3 bucket
# [/] use pet name!
# n/a
# [X] vpc
# [/] tls private key
# [X] aws key pair
# [/] aws key local file
# [X] instance
# [ ] "myip"
# [X] sg
# [X] EIP
# [X] iam_instance_profile
# [X] iam_role
# [X] policydoc
# [X] policy
# [X] policy attachment
# [X] iam policy data
# [ ] route53 records
# [/] adminpass for nextcloud
# [ ] outputs:
# [ ] instance ID
# [ ] public IP
# [ ] name servers
# [ ] bucket
# [ ] myip
provider "aws" { provider "aws" {
region = local.aws_region region = local.aws_region
} }
#resource "random_pet" "name" () resource "random_pet" "name" {}
module "vpc" { module "vpc" {
source = "terraform-aws-modules/vpc/aws" source = "terraform-aws-modules/vpc/aws"

View file

@ -6,10 +6,20 @@ output "public_ip" {
value = aws_instance.social.public_ip value = aws_instance.social.public_ip
} }
output "nameservers" { output "nameservers" {
value = length(module.zone) == 0 ? "" : module.zone.0.route53_zone_name_servers #value = length(module.zone) == 0 ? "" : module.zone.0.route53_zone_name_servers
value = module.zone.route53_zone_name_servers
}
output "s3_bucket_name" {
value = module.s3_bucket.s3_bucket_id
} }
#output "bucket" {
#}
#output "myip" { #output "myip" {
#} #}
#output "aws_route53_zone" {
# value = local.route53_zone
#}
output "aws_route53_nameservers" {
value = module.zone.route53_zone_name_servers
}

View file

@ -1,21 +1,22 @@
module "zone" { module "zone" {
count = local.route53_zone == "" ? 0 : 1 # count = local.route53_zone == "" ? 0 : 1
source = "terraform-aws-modules/route53/aws//modules/zones" source = "terraform-aws-modules/route53/aws//modules/zones"
version = "~> 2.0" version = "~> 2.0"
zones = { zones = {
"${local.route53_zone}" = { comment = "${local.route53_zone}" } "${local.domain_name}" = { comment = "${local.domain_name}" }
} }
} }
module "records" { module "records" {
count = local.route53_zone == "" ? 0 : 1 # count = local.route53_zone == "" ? 0 : 1
source = "terraform-aws-modules/route53/aws//modules/records" source = "terraform-aws-modules/route53/aws//modules/records"
version = "~> 2.0" version = "~> 2.0"
zone_name = keys(module.zone.0.route53_zone_zone_id)[0] #zone_name = keys(module.zone.0.route53_zone_zone_id)[0]
zone_name = keys(module.zone.route53_zone_zone_id)[0]
records = [ records = [
{ {

42
terraform/s3.tf Normal file
View file

@ -0,0 +1,42 @@
module "s3_bucket" {
source = "terraform-aws-modules/s3-bucket/aws"
bucket = "mastodon-${random_pet.name.id}"
# acl = "private"
versioning = {
enabled = false
}
# server_side_encryption_configuration = {
# rule = {
# apply_server_side_encryption_by_default = {
# sse_algorithm = "AES256"
# }
#
# bucket_key_enabled = true
# }
# }
}
resource "aws_iam_access_key" "s3" {
user = aws_iam_user.s3.name
}
resource "aws_iam_user" "s3" {
name = "mastodon-s3-${random_pet.name.id}"
path = "/system/"
}
resource "local_file" "s3_secret" {
filename = ".s3_secret"
content = "${aws_iam_access_key.s3.secret}\n"
}
resource "local_file" "s3_id" {
filename = ".s3_id"
content = "${aws_iam_access_key.s3.id}\n"
}