everything that happens here is nice and not crimes

Commit e6a94e8e authored by Alexandra Catalina's avatar Alexandra Catalina 🎱

remove capistrano

parent 2ecc5c4e
Pipeline #31 failed with stages
in 10 seconds
require 'capistrano/setup'
require 'capistrano/deploy'
require 'capistrano/npm'
require 'capistrano/passenger'
# https://github.com/capistrano/capistrano/blob/v3.7.0/UPGRADING-3.7.md#the-scm-variable-is-deprecated
require 'capistrano/scm/git'
install_plugin Capistrano::SCM::Git
.PHONY: deploy
deploy:
bundle exec cap production deploy
......@@ -12,41 +12,9 @@ npm install
node main.js # now running at localhost:3333
```
## running a production instance
The `provision` subdirectory contains an Ansible playbook for
provisioning a CentOS VM to run read-thing. Edit
`provision/inventory` to your taste, then run `ansible-playbook`
(requires Ansible 2.2+ with passlib):
```shell
cd provision && ansible-playbook -i inventory read-thing.yml
```
After provisioning, add your Twitter app’s client/consumer key and
secret token to `shared/config/oauth.json` in the project root on your
server (the project root defaults to `/var/www/read-thing`). If you
haven’t created a Twitter application, do so at
<https://apps.twitter.com>; check the box for “Allow this application
to be used to Sign in with Twitter” and ensure that the callback URL
is `https://your.domain/auth`. If you’re done testing locally (or are
using a different Twitter app for that), consider also checking the
box for “Enable Callback Locking”.
Finally, deploy using Capistrano:
```shell
bundle install
SERVER=your.instance.com REPO=https://gitlab.com/your/fork.git make deploy
```
If `SERVER` is not specified it defaults to `text.town`; `REPO`
defaults to this repository.
# license
Copyright 2020 Alex Dunn
Copyright 2021 Alex Dunn
read-thing is free software: you can redistribute it and/or modify it
under the terms of the GNU Affero General Public License as published
......
[default]
default ansible_ssh_host=text.town
---
- name: Provision a CentOS server for read-thing
remote_user: root
hosts: all
vars_prompt:
- name: keyfile
default: ~/.ssh/id_rsa.pub
prompt: Path to SSH public key
- name: deploy_user
default: deploy
prompt: Name of deploy user
- name: deploy_pass
confirm: yes
encrypt: sha512_crypt
private: yes
prompt: Password for deploy user
salt_size: 7
roles:
- { role: set-timezone }
- { role: create-users }
- { role: application-requirements }
- { role: configure-server }
- { role: security }
---
node_ver: 6.10.2
node_sha: 744f95bd205000226291dceb12cefa434b84825af961df86f01576cb2ea76755
{
"access_url": "https://api.twitter.com/oauth/access_token",
"callback_url": "http://localhost/auth",
"client_key": "",
"client_secret": "",
"method": "HMAC-SHA1",
"request_url": "https://api.twitter.com/oauth/request_token",
"version": "1.0A"
}
---
- name: create shared directories
file:
group: nginx
owner: "{{ deploy_user }}"
path: "{{ item }}"
state: directory
with_items:
- "{{ app_dir }}/shared/config"
- "{{ app_dir }}/shared/data"
- name: install config template
copy:
dest: "{{ app_dir }}/shared/config/{{ item }}"
group: nginx
owner: "{{ deploy_user }}"
src: "{{ item }}"
with_items:
- oauth.json
---
- name: update all yum packages
yum:
name: "*"
state: latest
update_cache: yes
- name: install tools
yum:
name: "{{ item }}"
with_items:
- nodejs
- openssl-devel
---
- include: repos.yml
- include: install.yml
- include: configure.yml
---
- name: add EPEL repo
yum:
name: https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
- name: download NodeSource repo
get_url:
url: https://rpm.nodesource.com/pub_6.x/el/7/x86_64/nodejs-{{ node_ver }}-1nodesource.el7.centos.x86_64.rpm
checksum: sha256:{{ node_sha }}
dest: /tmp/nodejs-devel-{{ node_ver }}-1nodesource.el7.centos.x86_64.rpm
- name: install NodeSource repo
yum:
name: /tmp/nodejs-devel-{{ node_ver }}-1nodesource.el7.centos.x86_64.rpm
---
app_dir: /var/www
passenger_sha: ee8df2cdb589da51c64d9e620feb96445f5c281cdec27e58eff6479a8a233e99
intermediate_sha: e446c5e9dbef9d09ac9f7027c034602492437a05ff6c40011d7235fca639c79a
passenger_root /usr/share/ruby/vendor_ruby/phusion_passenger/locations.ini;
passenger_ruby /usr/bin/ruby;
passenger_instance_registry_dir /var/run/passenger-instreg;
---
# https://letsencrypt.org/how-it-works/
# https://github.com/diafygi/acme-tiny#how-to-use-this-script
# https://github.com/diafygi/letsencrypt-nosudo#how-to-use-the-signing-script
- name: create ACME directories
file:
group: nginx
owner: "{{ deploy_user }}"
path: "{{ item }}"
state: directory
with_items:
- /home/{{ deploy_user }}/acme
- /usr/share/nginx/html/.well-known/acme-challenge
- name: create TLS user private key
shell: openssl genrsa 4096 > /home/{{ deploy_user }}/acme/user.key
args:
creates: /home/{{ deploy_user }}/acme/user.key
become: true
become_user: "{{ deploy_user }}"
- name: create TLS domain private key
shell: openssl genrsa 4096 > /etc/ssl/certs/{{ ansible_ssh_host }}.key
args:
creates: /etc/ssl/certs/{{ ansible_ssh_host }}.key
- name: set permissions on TLS domain private key
file:
group: nginx
owner: "{{ deploy_user }}"
path: /etc/ssl/certs/{{ ansible_ssh_host }}.key
- name: copy default OpenSSL config
copy:
dest: /home/{{ deploy_user }}/acme/{{ ansible_ssh_host }}.cnf
group: nginx
owner: "{{ deploy_user }}"
remote_src: yes
src: /etc/pki/tls/openssl.cnf
- name: add DNS info to local config
lineinfile:
dest: /home/{{ deploy_user }}/acme/{{ ansible_ssh_host }}.cnf
line: "[SAN]\nsubjectAltName=DNS:{{ ansible_ssh_host }},DNS:www.{{ ansible_ssh_host }}"
become: true
become_user: "{{ deploy_user }}"
- name: create Certificate Signing Request
shell: 'openssl req -new -sha256 -key /etc/ssl/certs/{{ ansible_ssh_host }}.key -subj "/" -reqexts SAN -config /home/{{ deploy_user }}/acme/{{ ansible_ssh_host }}.cnf > /home/{{ deploy_user }}/acme/{{ ansible_ssh_host }}.csr'
args:
creates: /home/{{ deploy_user }}/acme/{{ ansible_ssh_host }}.csr
become: true
become_user: "{{ deploy_user }}"
- name: check if /home/{{ deploy_user }}/acme/{{ ansible_ssh_host }}.crt exists
stat:
path: /home/{{ deploy_user }}/acme/{{ ansible_ssh_host }}.crt
register: certfile
- name: get certificate from Let’s Encrypt
shell: python3 /home/{{ deploy_user }}/acme-tiny/acme_tiny.py \
--account-key /home/{{ deploy_user }}/acme/user.key \
--csr /home/{{ deploy_user }}/acme/{{ ansible_ssh_host }}.csr \
--acme-dir /usr/share/nginx/html/.well-known/acme-challenge \
> /home/{{ deploy_user }}/acme/{{ ansible_ssh_host }}.crt
when: certfile.stat.exists == False or certfile.stat.size == 0
become: true
become_user: "{{ deploy_user }}"
# This is an intermediate certificate; it's not strictly required but
# not having it will cap your Qualys ranking at B
- name: download Let’s Encrypt X3 certificate
get_url:
url: https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem
checksum: sha256:{{ intermediate_sha }}
dest: /etc/ssl/certs/lets-encrypt-x3-cross-signed.pem
owner: "{{ deploy_user }}"
group: nginx
- name: combine certificates
shell: cat /home/{{ deploy_user }}/acme/{{ ansible_ssh_host }}.crt \
/etc/ssl/certs/lets-encrypt-x3-cross-signed.pem \
> /etc/ssl/certs/{{ ansible_ssh_host }}.pem
args:
creates: /etc/ssl/certs/{{ ansible_ssh_host }}.pem
- name: set permissions on certificate
file:
path: /etc/ssl//certs/{{ ansible_ssh_host }}.pem
owner: "{{ deploy_user }}"
group: nginx
- name: ensure /home/{{ deploy_user }}/bin exists
file:
state: directory
path: /home/{{ deploy_user }}/bin
owner: "{{ deploy_user }}"
group: nginx
- name: install renew.sh script
template:
src: renew.sh.j2
dest: /home/{{ deploy_user }}/bin/renew.sh
owner: "{{ deploy_user }}"
group: nginx
mode: 0755
- name: schedule certificate renewal
cron:
name: "ACME renewal"
special_time: monthly
job: /home/{{ deploy_user }}/bin/renew.sh \
2>> /var/log/acme_error.log
cron_file: acme_renewal
user: "{{ deploy_user }}"
- name: make ACME log writeable by {{ deploy_user }}
file:
path: /var/log/acme_error.log
state: touch
owner: "{{ deploy_user }}"
group: nginx
---
- name: add EPEL repo
yum:
name: https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
- name: install Phusion Passenger repo
get_url:
url: https://oss-binaries.phusionpassenger.com/yum/definitions/el-passenger.repo
checksum: sha256:{{ passenger_sha }}
dest: /etc/yum.repos.d/passenger.repo
- name: update all yum packages
yum:
name: "*"
state: latest
update_cache: yes
- name: install software
yum:
name: "{{ item }}"
state: present
with_items:
- curl-devel
- git
- nginx
- openssl-devel
- passenger
# for acme-tiny
- python34-devel
# https://www.phusionpassenger.com/library/walkthroughs/deploy/nodejs/ownserver/nginx/oss/el7/install_passenger.html
- pygpgme
- ruby
- name: remove app.conf
file:
path: /etc/nginx/conf.d/app.conf
state: absent
- name: start Nginx
systemd:
name: nginx
state: started
- name: clone acme-tiny repository
git:
repo: https://github.com/diafygi/acme-tiny.git
dest: /home/{{ deploy_user }}/acme-tiny
# This is the latest commit; update as necessary
version: daba51d37efd7c1f205f9da383b9b09968e30d29
become: true
become_user: "{{ deploy_user }}"
---
- include: install.yml
- include: acme.yml
- include: post-tls.yml
---
- name: add full Nginx configuration
template:
src: app.conf.j2
dest: /etc/nginx/conf.d/app.conf
force: yes
- name: ensure {{ app_dir }} exists
file:
path: "{{ app_dir }}"
state: directory
owner: "{{ deploy_user }}"
group: nginx
- name: install passenger.conf
copy:
dest: /etc/nginx/conf.d/passenger.conf
src: passenger.conf
- name: restart Nginx
systemd:
enabled: yes
name: nginx
state: restarted
- name: schedule Nginx to restart after certificate renewal
cron:
job: "systemctl restart nginx"
name: restart Nginx
special_time: monthly
# Based on https://mozilla.github.io/server-side-tls/ssl-config-generator/
# and https://letsecure.me/secure-web-deployment-with-lets-encrypt-and-nginx/
server {
listen 80;
listen 443 ssl http2;
server_name {{ ansible_ssh_host }};
ssl_protocols TLSv1.2;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
ssl_prefer_server_ciphers on;
ssl_certificate /etc/pki/tls/certs/{{ ansible_ssh_host }}.pem;
ssl_certificate_key /etc/pki/tls/certs/{{ ansible_ssh_host }}.key;
ssl_trusted_certificate /etc/pki/tls/certs/lets-encrypt-x3-cross-signed.pem;
ssl_session_cache shared:SSL:128m;
add_header Strict-Transport-Security "max-age=15768000; includeSubDomains";
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Xss-Protection "1";
ssl_stapling on;
ssl_stapling_verify on;
return 301 https://www.{{ ansible_ssh_host }}$request_uri;
}
server {
listen 80;
listen 443 ssl http2;
server_name {{ ansible_ssh_host }} www.{{ ansible_ssh_host }};
passenger_enabled on;
passenger_app_type node;
passenger_startup_file {{ app_dir }}/current/main.js;
ssl_protocols TLSv1.2;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
ssl_prefer_server_ciphers on;
ssl_certificate /etc/pki/tls/certs/{{ ansible_ssh_host }}.pem;
ssl_certificate_key /etc/pki/tls/certs/{{ ansible_ssh_host }}.key;
ssl_trusted_certificate /etc/pki/tls/certs/lets-encrypt-x3-cross-signed.pem;
ssl_session_cache shared:SSL:128m;
add_header Strict-Transport-Security "max-age=15768000; includeSubDomains";
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Xss-Protection "1";
ssl_stapling on;
ssl_stapling_verify on;
# Your favorite resolver may be used instead of the Google one below
resolver 8.8.8.8;
root {{ app_dir }}/current;
index index.html;
location / {
if ($scheme = http) {
return 301 https://$server_name$request_uri;
}
}
}
#!/bin/bash
set -e
python3 /home/{{ deploy_user }}/acme-tiny/acme_tiny.py --account-key /home/{{ deploy_user }}/acme/user.key --csr /home/{{ deploy_user }}/acme/{{ ansible_ssh_host }}.csr --acme-dir {{ app_dir }}/current/public/.well-known/acme-challenge > /home/{{ deploy_user }}/acme/{{ ansible_ssh_host }}.crt
cat /home/{{ deploy_user }}/acme/{{ ansible_ssh_host }}.crt /etc/ssl/certs/lets-encrypt-x3-cross-signed.pem > /etc/ssl/certs/{{ ansible_ssh_host }}.pem
---
- name: create nginx group
group:
name: nginx
- name: create “{{ deploy_user }}” user
user:
generate_ssh_key: yes
groups: nginx,wheel
home: /home/{{ deploy_user }}
name: "{{ deploy_user }}"
password: "{{ deploy_pass }}"
ssh_key_bits: 4096
update_password: on_create
- name: add key to deploy user’s authorized_keys
authorized_key:
key: "{{ lookup('file', keyfile) }}"
user: "{{ deploy_user }}"
---
- name: restart sshd
systemd:
enabled: yes
name: sshd
state: restarted
---
- name: enable fail2ban
systemd:
name: fail2ban
state: started
enabled: yes
---
- name: enable firewalld
systemd:
enabled: yes
name: firewalld
state: started
# https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/Security_Guide/sec-Using_Firewalls.html
- name: add services to firewalld allowed protocols
command: firewall-cmd --add-service={{ item }} --permanent --zone=public
with_items:
- http
- https
- name: reload firewalld
command: firewall-cmd --reload
---
- name: add EPEL repo
yum:
name: https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
- name: update all yum packages
yum:
name: "*"
state: latest
update_cache: yes
- name: install tools
yum:
name: "{{ item }}"
with_items:
- fail2ban
---
- include: install.yml
- include: updates.yml
- include: selinux.yml
- include: fail2ban.yml
- include: firewalld.yml
- include: ssh.yml
---
- selinux:
policy: targeted
state: enforcing
---
- name: update ssh config
template:
dest: /etc/ssh/sshd_config
group: root
mode: 0400
owner: root
src: sshd_config.j2
notify:
- restart sshd
- meta: flush_handlers
---
- name: automatically update yum packages
cron:
name: "yum update"
special_time: daily
job: "yum update -y >> /var/log/yum_update.log 2>&1"
cron_file: ansible_update
user: root
- name: clear yum cache once a week
cron:
name: "yum clean"
special_time: weekly
job: "yum clean all >> /var/log/yum_clean.log 2>&1"
cron_file: ansible_clean
user: root
# Configuration file generated by ansible
# What ports, IPs and protocols we listen for
Port 22
# Use these options to restrict which interfaces/protocols sshd will bind to
#ListenAddress ::
#ListenAddress 0.0.0.0
Protocol 2
# HostKeys for protocol version 2
HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_dsa_key
HostKey /etc/ssh/ssh_host_ecdsa_key
#Privilege Separation is turned on for security
UsePrivilegeSeparation yes
# Lifetime and size of ephemeral version 1 server key
KeyRegenerationInterval 3600
ServerKeyBits 768
# Logging
SyslogFacility AUTH
LogLevel INFO
# Authentication:
AllowUsers {{ deploy_user }}
PermitRootLogin no
LoginGraceTime 30