commit b16f2585c7f325e0f476fc33826827768b252b8b Author: Sonny Bakker Date: Sun Jan 31 23:32:33 2021 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ad2ea9f --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +*.retry +*.swp + +.venv +.env +env +venv + +node_modules/ + +.vault +.vaults/ +vault +vaults/ + +roles/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..d9ade6f --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,26 @@ +stages: + - lint + - test + +cache: + key: $CI_COMMIT_REF_SLUG + paths: + - .cache/pip + - node_modules/ + +lint: + stage: lint + image: node:12 + before_script: + - npm install prettier --no-save + script: + - npx prettier '**/*.yml' --check + +syntax-test: + stage: test + image: python:3.7 + before_script: + - pip install ansible --quiet + - ansible-galaxy install -r requirements.yml + script: + - ansible-playbook playbook.yml --syntax-check diff --git a/.prettierrc.yml b/.prettierrc.yml new file mode 100644 index 0000000..0cb31e6 --- /dev/null +++ b/.prettierrc.yml @@ -0,0 +1,5 @@ +singleQuote: true +printWidth: 90 +tabWidth: 2 +useTabs: false +bracketSpacing: true diff --git a/ansible.cfg b/ansible.cfg new file mode 100644 index 0000000..4009fad --- /dev/null +++ b/ansible.cfg @@ -0,0 +1,4 @@ +[defaults] +roles_path = ./roles +remote_user = ansible +inventory = ./inventory.yml diff --git a/handlers.yml b/handlers.yml new file mode 100644 index 0000000..1907c98 --- /dev/null +++ b/handlers.yml @@ -0,0 +1,5 @@ +- name: restart sentry + systemd: + name: sentry + state: restarted + enabled: true diff --git a/inventory.yml b/inventory.yml new file mode 100644 index 0000000..07c3fdb --- /dev/null +++ b/inventory.yml @@ -0,0 +1,3 @@ +sentry: + hosts: + 192.168.178.73: diff --git a/playbook.yml b/playbook.yml new file mode 100644 index 0000000..2bb3987 --- /dev/null +++ b/playbook.yml @@ -0,0 +1,33 @@ +- hosts: sentry + pre_tasks: + - include_role: + name: common + tasks_from: 'setup.yml' + - include_role: + name: common + tasks_from: 'network.yml' + - include_role: + name: common + tasks_from: 'host.yml' + - include_role: + name: common + tasks_from: 'sudoers.yml' + loop: + - { src: '../../templates/sudoers.j2', dest: '/etc/sudoers.d/30-ansible-extra' } + roles: + - common + tasks: + - include_role: + name: common + tasks_from: 'ssl.yml' + - include_role: + name: common + tasks_from: 'nginx.yml' + - import_tasks: 'tasks/docker.yml' + - import_tasks: 'tasks/main.yml' + handlers: + - import_tasks: 'handlers.yml' + vars_files: + - 'vars/main.yml' + - 'vars/network.yml' + - 'vars/postgres.yml' diff --git a/requirements.yml b/requirements.yml new file mode 100644 index 0000000..ba54c45 --- /dev/null +++ b/requirements.yml @@ -0,0 +1,4 @@ +- src: git+https://git.fudiggity.nl/ansible/common.git + name: common + version: master + scm: git diff --git a/tasks/docker.yml b/tasks/docker.yml new file mode 100644 index 0000000..25e600c --- /dev/null +++ b/tasks/docker.yml @@ -0,0 +1,42 @@ +- name: add docker gpg key + apt_key: + id: '0EBFCD88' + url: 'https://download.docker.com/linux/debian/gpg' + validate_certs: true + state: present + +- name: add docker repo + apt_repository: + repo: 'deb https://download.docker.com/linux/debian buster stable' + validate_certs: true + state: present + +- name: install docker + apt: + name: + - docker-ce + - docker-ce-cli + - containerd.io + state: present + +- name: check docker-compose existence + stat: + path: '/usr/local/bin/docker-compose' + register: docker_compose_stat + +- name: download docker-compose + get_url: + url: 'https://github.com/docker/compose/releases/download/1.26.0/docker-compose-Linux-x86_64' + dest: '/usr/local/bin/docker-compose' + mode: '0755' + when: docker_compose_stat.stat.isfile is not defined + +- name: add sentry user + user: + name: sentry + create_home: true + shell: '/bin/bash' + groups: + - sudo + - docker + append: true diff --git a/tasks/main.yml b/tasks/main.yml new file mode 100644 index 0000000..88e1681 --- /dev/null +++ b/tasks/main.yml @@ -0,0 +1,93 @@ +- name: copy firewall templates + template: + src: '{{ item.src }}' + dest: '{{ item.dest }}' + owner: root + group: root + mode: '0600' + loop: + - { src: 'templates/nftables.j2', dest: '/etc/nftables.conf' } + notify: restart nftables + +- name: create sites directory + file: + path: '/srv/sites' + state: directory + owner: root + group: root + mode: 0755 + +- name: create sentry dir + file: + path: '{{ app_dir }}' + state: directory + owner: '{{ app_user }}' + group: '{{ app_user }}' + mode: 0755 + +- name: clone project + become_user: '{{ app_user }}' + become: true + git: + repo: '{{ app_repository }}' + dest: '{{ app_dir }}' + version: '{{ app_branch }}' + update: true + +- name: copy over sentry configurations + template: + src: '{{ item.src }}' + dest: '{{ item.dest }}' + owner: '{{ app_user }}' + group: '{{ app_user }}' + mode: '0644' + loop: + - { src: 'templates/sentry.conf.j2', dest: '{{ app_dir }}/sentry/sentry.conf.py' } + - { src: 'templates/sentry.config.j2', dest: '{{ app_dir }}/sentry/config.yml' } + +- name: ensure sentry is stopped + systemd: + name: sentry + state: stopped + +# can be ran multiple times to upgrade sentry +- name: run sentry installer + command: './install.sh' # noqa 301 + args: + chdir: '{{ app_dir }}' + environment: + SENTRY_IMAGE: 'getsentry/sentry:{{ app_branch }}' + +- name: copy sentry systemd service + template: + src: '{{ item.src }}' + dest: '{{ item.dest }}' + owner: '{{ app_user }}' + group: '{{ app_user }}' + mode: '0644' + loop: + - { src: 'templates/sentry.systemd.j2', dest: '/etc/systemd/system/sentry.service' } + notify: restart sentry + +- name: copy nginx sentry config + template: + src: 'templates/nginx.j2' + dest: '/etc/nginx/sites-available/{{ app_name }}' + owner: root + group: root + mode: '0644' + +- name: link nginx config + file: + src: '/etc/nginx/sites-available/{{ app_name }}' + dest: '/etc/nginx/sites-enabled/{{ app_name }}' + owner: root + group: root + mode: '0777' + state: link + +- name: ensure nginx is restarted + systemd: + name: nginx + state: restarted + enabled: true diff --git a/templates/nftables.j2 b/templates/nftables.j2 new file mode 100644 index 0000000..6f597c8 --- /dev/null +++ b/templates/nftables.j2 @@ -0,0 +1,33 @@ +# {{ ansible_managed }} {{ ansible_date_time.time }} {{ ansible_date_time.date }} +# +# vim:set ts=2 sw=2 et: +# use uppercase table names for compatibility with docker + +flush ruleset + +table inet filter { + chain INPUT { + type filter hook input priority 0; policy drop; + + # accept any localhost traffic + iif lo accept + + # accept traffic originated from us + ct state { established, related } accept + + tcp dport { 22, 80, 443 } accept + } + + chain FORWARD { + type filter hook forward priority 0; policy drop; + ct state { established, related } accept; + + mark 1 accept + } +} + +table ip filter { + chain DOCKER-USER { + mark set 1 + } +} diff --git a/templates/nginx.j2 b/templates/nginx.j2 new file mode 100644 index 0000000..3167b94 --- /dev/null +++ b/templates/nginx.j2 @@ -0,0 +1,26 @@ +# {{ ansible_managed }} {{ ansible_date_time.time }} {{ ansible_date_time.date }} +# +server { + listen 80; + server_name {{ app_name }}.fudiggity.nl; + return 301 https://$server_name$request_uri; +} + +server { + listen 443 ssl; + server_name {{ app_name }}.fudiggity.nl; + + ssl_certificate /etc/ssl/{{ app_name }}/{{ app_name }}.crt; + ssl_certificate_key /etc/ssl/{{ app_name }}/local.pem; + + access_log /var/log/nginx/{{ app_name }}.log; + error_log /var/log/nginx/{{ app_name }}.log; + + location / { + include proxy_params; + + proxy_redirect off; + + proxy_pass http://localhost:9000; + } +} diff --git a/templates/sentry.conf.j2 b/templates/sentry.conf.j2 new file mode 100644 index 0000000..f0c0ff7 --- /dev/null +++ b/templates/sentry.conf.j2 @@ -0,0 +1,239 @@ +# {{ ansible_managed }} {{ ansible_date_time.time }} {{ ansible_date_time.date }} +# +# This file is just Python, with a touch of Django which means +# you can inherit and tweak settings to your hearts content. + +from sentry.conf.server import * # NOQA + +DATABASES = { + "default": { + "ENGINE": "sentry.db.postgres", + "NAME": "{{ postgres_db }}", + "USER": "{{ postgres_user }}", + "PASSWORD": "{{ postgres_password }}", + "HOST": "{{ postgres_host }}", + "PORT": "{{ postgres_port }}", + } +} + +# You should not change this setting after your database has been created +# unless you have altered all schemas first +SENTRY_USE_BIG_INTS = True + +# If you're expecting any kind of real traffic on Sentry, we highly recommend +# configuring the CACHES and Redis settings + +########### +# General # +########### + +# Instruct Sentry that this install intends to be run by a single organization +# and thus various UI optimizations should be enabled. +SENTRY_SINGLE_ORGANIZATION = True + +SENTRY_OPTIONS["system.event-retention-days"] = int( + env('SENTRY_EVENT_RETENTION_DAYS', '90') +) + +######### +# Redis # +######### + +# Generic Redis configuration used as defaults for various things including: +# Buffers, Quotas, TSDB + +SENTRY_OPTIONS["redis.clusters"] = { + "default": { + "hosts": {0: {"host": "redis", "password": "", "port": "6379", "db": "0"}} + } +} + +######### +# Queue # +######### + +# See https://docs.getsentry.com/on-premise/server/queue/ for more +# information on configuring your queue broker and workers. Sentry relies +# on a Python framework called Celery to manage queues. + +rabbitmq_host = None +if rabbitmq_host: + BROKER_URL = "amqp://{username}:{password}@{host}/{vhost}".format( + username="guest", password="guest", host=rabbitmq_host, vhost="/" + ) +else: + BROKER_URL = "redis://:{password}@{host}:{port}/{db}".format( + **SENTRY_OPTIONS["redis.clusters"]["default"]["hosts"][0] + ) + + +######### +# Cache # +######### + +# Sentry currently utilizes two separate mechanisms. While CACHES is not a +# requirement, it will optimize several high throughput patterns. + +CACHES = { + "default": { + "BACKEND": "django.core.cache.backends.memcached.MemcachedCache", + "LOCATION": ["memcached:11211"], + "TIMEOUT": 3600, + } +} + +# A primary cache is required for things such as processing events +SENTRY_CACHE = "sentry.cache.redis.RedisCache" + +DEFAULT_KAFKA_OPTIONS = { + "bootstrap.servers": "kafka:9092", + "message.max.bytes": 50000000, + "socket.timeout.ms": 1000, +} + +SENTRY_EVENTSTREAM = "sentry.eventstream.kafka.KafkaEventStream" +SENTRY_EVENTSTREAM_OPTIONS = {"producer_configuration": DEFAULT_KAFKA_OPTIONS} + +KAFKA_CLUSTERS["default"] = DEFAULT_KAFKA_OPTIONS + +############### +# Rate Limits # +############### + +# Rate limits apply to notification handlers and are enforced per-project +# automatically. + +SENTRY_RATELIMITER = "sentry.ratelimits.redis.RedisRateLimiter" + +################## +# Update Buffers # +################## + +# Buffers (combined with queueing) act as an intermediate layer between the +# database and the storage API. They will greatly improve efficiency on large +# numbers of the same events being sent to the API in a short amount of time. +# (read: if you send any kind of real data to Sentry, you should enable buffers) + +SENTRY_BUFFER = "sentry.buffer.redis.RedisBuffer" + +########## +# Quotas # +########## + +# Quotas allow you to rate limit individual projects or the Sentry install as +# a whole. + +SENTRY_QUOTAS = "sentry.quotas.redis.RedisQuota" + +######## +# TSDB # +######## + +# The TSDB is used for building charts as well as making things like per-rate +# alerts possible. + +SENTRY_TSDB = "sentry.tsdb.redissnuba.RedisSnubaTSDB" + +######### +# SNUBA # +######### + +SENTRY_SEARCH = "sentry.search.snuba.EventsDatasetSnubaSearchBackend" +SENTRY_SEARCH_OPTIONS = {} +SENTRY_TAGSTORE_OPTIONS = {} + +########### +# Digests # +########### + +# The digest backend powers notification summaries. + +SENTRY_DIGESTS = "sentry.digests.backends.redis.RedisBackend" + +############## +# Web Server # +############## + +SENTRY_WEB_HOST = "0.0.0.0" +SENTRY_WEB_PORT = 9000 +SENTRY_WEB_OPTIONS = { + # These ase for proper HTTP/1.1 support from uWSGI + # Without these it doesn't do keep-alives causing + # issues with Relay's direct requests. + "http-keepalive": True, + "http-chunked-input": True, + # the number of web workers + 'workers': 3, + # Turn off memory reporting + "memory-report": False, + # Some stuff so uwsgi will cycle workers sensibly + 'max-requests': 100000, + 'max-requests-delta': 500, + 'max-worker-lifetime': 86400, + # Duplicate options from sentry default just so we don't get + # bit by sentry changing a default value that we depend on. + 'thunder-lock': True, + 'log-x-forwarded-for': False, + 'buffer-size': 32768, + 'limit-post': 209715200, + 'disable-logging': True, + 'reload-on-rss': 600, + 'ignore-sigpipe': True, + 'ignore-write-errors': True, + 'disable-write-exception': True, +} + +########### +# SSL/TLS # +########### + +# If you're using a reverse SSL proxy, you should enable the X-Forwarded-Proto +# header and enable the settings below + +SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') +SESSION_COOKIE_SECURE = True +CSRF_COOKIE_SECURE = True +SOCIAL_AUTH_REDIRECT_IS_HTTPS = True + +# End of SSL/TLS settings + +############ +# Features # +############ + +SENTRY_FEATURES["projects:sample-events"] = False +SENTRY_FEATURES.update( + { + feature: True + for feature in ( + "organizations:discover", + "organizations:events", + "organizations:global-views", + "organizations:integrations-issue-basic", + "organizations:integrations-issue-sync", + "organizations:invite-members", + "organizations:sso-basic", + "organizations:sso-rippling", + "organizations:sso-saml2", + "projects:custom-inbound-filters", + "projects:data-forwarding", + "projects:discard-groups", + "projects:plugins", + "projects:rate-limits", + "projects:servicehooks", + ) + } +) + +###################### +# GitHub Integration # +###################### + +GITHUB_EXTENDED_PERMISSIONS = ['repo'] + +######################### +# Bitbucket Integration # +######################## + +# BITBUCKET_CONSUMER_KEY = 'YOUR_BITBUCKET_CONSUMER_KEY' +# BITBUCKET_CONSUMER_SECRET = 'YOUR_BITBUCKET_CONSUMER_SECRET' diff --git a/templates/sentry.config.j2 b/templates/sentry.config.j2 new file mode 100644 index 0000000..6cb2dfd --- /dev/null +++ b/templates/sentry.config.j2 @@ -0,0 +1,106 @@ +# {{ ansible_managed }} {{ ansible_date_time.time }} {{ ansible_date_time.date }} +# +# While a lot of configuration in Sentry can be changed via the UI, for all +# new-style config (as of 8.0) you can also declare values here in this file +# to enforce defaults or to ensure they cannot be changed via the UI. For more +# information see the Sentry documentation. + +############### +# Mail Server # +############### + +# mail.backend: 'smtp' # Use dummy if you want to disable email entirely +mail.host: 'smtp' +# mail.port: 25 +# mail.username: '' +# mail.password: '' +# mail.use-tls: false +# The email address to send on behalf of +# mail.from: 'root@localhost' + +# If you'd like to configure email replies, enable this. +# mail.enable-replies: true + +# When email-replies are enabled, this value is used in the Reply-To header +# mail.reply-hostname: '' + +# If you're using mailgun for inbound mail, set your API key and configure a +# route to forward to /api/hooks/mailgun/inbound/ +# Also don't forget to set `mail.enable-replies: true` above. +# mail.mailgun-api-key: '' + +################### +# System Settings # +################### +# The URL prefix in which Sentry is accessible. This will be used both for referencing +# URLs in the UI, as well as in outbound notifications. This only works for scheme, hostname and port. +system.url-prefix: "{{ app_url }}" + +# If this file ever becomes compromised, it's important to generate a new key. +# Changing this value will result in all current sessions being invalidated. +# A new key can be generated with `$ sentry config generate-secret-key` +system.secret-key: '{{ app_key }}' + +# The ``redis.clusters`` setting is used, unsurprisingly, to configure Redis +# clusters. These clusters can be then referred to by name when configuring +# backends such as the cache, digests, or TSDB backend. +# redis.clusters: +# default: +# hosts: +# 0: +# host: 127.0.0.1 +# port: 6379 + +################ +# File storage # +################ + +# Uploaded media uses these `filestore` settings. The available +# backends are either `filesystem` or `s3`. + +filestore.backend: 'filesystem' +filestore.options: + location: '/data/files' +dsym.cache-path: '/data/dsym-cache' +releasefile.cache-path: '/data/releasefile-cache' + +# filestore.backend: 's3' +# filestore.options: +# access_key: 'AKIXXXXXX' +# secret_key: 'XXXXXXX' +# bucket_name: 's3-bucket-name' + +system.internal-url-prefix: 'http://web:9000' +symbolicator.enabled: true +symbolicator.options: + url: "http://symbolicator:3021" + +transaction-events.force-disable-internal-project: true + +###################### +# GitHub Integration # +###################### + +# github-app.id: GITHUB_APP_ID +# github-app.name: 'GITHUB_APP_NAME' +# github-app.webhook-secret: 'GITHUB_WEBHOOK_SECRET' # Use only if configured in GitHub +# github-app.client-id: 'GITHUB_CLIENT_ID' +# github-app.client-secret: 'GITHUB_CLIENT_SECRET' +# github-app.private-key: | +# -----BEGIN RSA PRIVATE KEY----- +# privatekeyprivatekeyprivatekeyprivatekey +# privatekeyprivatekeyprivatekeyprivatekey +# privatekeyprivatekeyprivatekeyprivatekey +# privatekeyprivatekeyprivatekeyprivatekey +# privatekeyprivatekeyprivatekeyprivatekey +# -----END RSA PRIVATE KEY----- + +##################### +# Slack Integration # +##################### + +# Refer to https://forum.sentry.io/t/how-to-configure-slack-in-your-on-prem-sentry/3463 for setup instructions. + +# slack.client-id: <'client id'> +# slack.client-secret: +# slack.verification-token: diff --git a/templates/sentry.systemd.j2 b/templates/sentry.systemd.j2 new file mode 100644 index 0000000..973df02 --- /dev/null +++ b/templates/sentry.systemd.j2 @@ -0,0 +1,17 @@ +# {{ ansible_managed }} {{ ansible_date_time.time }} {{ ansible_date_time.date }} +# +[Unit] +Description=Sentry +Requires=docker.service +After=docker.service + +[Service] +Type=oneshot +RemainAfterExit=yes +User={{ app_user }} +ExecStart=/usr/local/bin/docker-compose up --detach +ExecStop=/usr/local/bin/docker-compose down +WorkingDirectory={{ app_dir }} + +[Install] +WantedBy=multi-user.target diff --git a/templates/sudoers.j2 b/templates/sudoers.j2 new file mode 100644 index 0000000..1b45772 --- /dev/null +++ b/templates/sudoers.j2 @@ -0,0 +1,3 @@ +# {{ ansible_managed }} {{ ansible_date_time.time }} {{ ansible_date_time.date }} +# +ansible ALL = (sentry:sentry) NOPASSWD: ALL diff --git a/vars/main.yml b/vars/main.yml new file mode 100644 index 0000000..6d11255 --- /dev/null +++ b/vars/main.yml @@ -0,0 +1,17 @@ +default_user: 'sonny' + +app_name: 'sentry' +app_dir: '/srv/sites/sentry' +app_url: 'https://sentry.fudiggity.nl' +app_repository: 'https://github.com/getsentry/onpremise.git' +app_branch: '20.7.2' +app_user: 'sentry' +app_key: !vault | + $ANSIBLE_VAULT;1.1;AES256 + 33666233326139613365306263323464666538303862666561313839646435643866663064356263 + 6338613833356164393234383834373433373461356535610a333137383364386132643630383163 + 64386239353139616165633537343933373730646337353830666335303165376366376532653566 + 6562656564613665300a646364383931343862626334393164323237333061393739643837343331 + 39646537356434303065633532353332653434316465656561343239616366323163633432646237 + 31643430363265616263333930373338343034626666313233333730333962313236366337333030 + 323730396534306339316161366564373962 diff --git a/vars/network.yml b/vars/network.yml new file mode 100644 index 0000000..e41809d --- /dev/null +++ b/vars/network.yml @@ -0,0 +1,6 @@ +hostname: 'sentry.fudiggity.nl' +host_interface: 'en*' +host_ip: '192.168.178.73' +host_subnet: '24' +host_gateway: '192.168.178.1' +host_dns: '192.168.178.1' diff --git a/vars/postgres.yml b/vars/postgres.yml new file mode 100644 index 0000000..594366b --- /dev/null +++ b/vars/postgres.yml @@ -0,0 +1,12 @@ +postgres_host: '192.168.178.165' +postgres_port: '5432' +postgres_db: 'sentry' +postgres_user: 'sentry' +postgres_password: !vault | + $ANSIBLE_VAULT;1.1;AES256 + 62306238313361643131646665646161336162626464393839353238363861376462663936666239 + 3166653966303132393563653832653635623131393536640a653037306539666261346232633930 + 34656431386531303234316137396436653635393061393934393839663032386638633264326133 + 6564366362326462640a323835633561363433393435376434306535636339646662343234356530 + 61656162663462616538313835343665333661303963653635346666323933663761376335373832 + 3761303331613539623761626535336330353836373838363963