diff --git a/.gitignore b/.gitignore index 46f0db6..e505984 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,4 @@ tags _build/ *.mo .vagrant/ +gnuviechadmin/assets/ diff --git a/Vagrantfile b/Vagrantfile index 1fc6ff5..8aec6d0 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -23,6 +23,7 @@ Vagrant.configure(2) do |config| # within the machine from a port on the host machine. In the example below, # accessing "localhost:8080" will access port 80 on the guest machine. config.vm.network "forwarded_port", guest: 443, host: 8443 + config.vm.network "forwarded_port", guest: 8000, host: 8000 # Create a private network, which allows host-only access to the machine # using a specific IP. diff --git a/salt/bootstrap.sh b/salt/bootstrap.sh index 98f077e..4e85da5 100755 --- a/salt/bootstrap.sh +++ b/salt/bootstrap.sh @@ -27,7 +27,6 @@ EOF cat >/etc/salt/grains < +''' + +from M2Crypto import X509 +from datetime import datetime +import os + + +def _error(ret, err_msg): + ret['result'] = False + ret['comment'] = err_msg + return ret + + +def valid_certificate( + name, mindays=14, keyfile=None, + checkchain=False, trustedcerts=None): + ''' + Checks whether the given certificate file is valid. + + name + The name of the certificate file to check + mindays + Mark the certificate as invalid if it is valid for less then this many + days + ''' + ret = { + 'name': name, + 'changes': {}, + 'result': None, + 'comment': ''} + if not os.path.isfile(name): + return _error( + ret, 'certificate file {0} does not exist'.format(name)) + try: + cert = X509.load_cert(name) + except Exception as e: + return _error( + ret, + 'error loading certificate {0}: {1}'.format(name, e)) + notafter = cert.get_not_after().get_datetime() + delta = notafter - datetime.now(notafter.tzinfo) + if delta.days < mindays: + return _error( + ret, + 'certificate {0} is only valid for {1} more day(s)'.format( + name, delta.days)) + # TODO: check keyfile match + # TODO: check trust chain + ret['comment'] = ( + 'certificate {0} is ok and still valid for {1} days'.format( + name, delta.days)) + ret['result'] = True + return ret diff --git a/salt/roots/gnuviechadmin/gnuviechadmin.nginx b/salt/roots/gnuviechadmin/gnuviechadmin.nginx new file mode 100644 index 0000000..d5768bd --- /dev/null +++ b/salt/roots/gnuviechadmin/gnuviechadmin.nginx @@ -0,0 +1,27 @@ +server { + server_name www.{{ domainname }}; + listen 443 ssl; + + ssl_certificate {{ ssl_certdir }}/{{ domainname }}.crt.pem; + ssl_certificate_key {{ ssl_keydir }}/{{ domainname }}.key.pem; + + if ( $host != '{{ domainname }}') { + return 301 https://{{ domainname }}$request_uri; + } + + client_max_body_size 1M; + gzip on; + gzip_types text/javascript application/x-javascript text/css; + + location /media { + alias /vagrant/gnuviechadmin/media; + } + + location /static { + alias /vagrant/gnuviechadmin/assets; + } + + location / { + proxy_pass http://localhost:8000; + } +} diff --git a/salt/roots/gnuviechadmin/gvasettings.sh b/salt/roots/gnuviechadmin/gvasettings.sh new file mode 100644 index 0000000..16edcd3 --- /dev/null +++ b/salt/roots/gnuviechadmin/gvasettings.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +export DJANGO_SETTINGS_MODULE="gnuviechadmin.settings.production" +export GVA_ADMIN_NAME="Jan Dittberner" +export GVA_ADMIN_EMAIL="{{ salt['pillar.get']('gnuviechadmin:adminemail') }}" +export GVA_PGSQL_DATABASE="{{ salt['pillar.get']('gnuviechadmin-database:database') }}" +export GVA_PGSQL_USER="{{ salt['pillar.get']('gnuviechadmin-database:owner:user') }}" +export GVA_PGSQL_PASSWORD="{{ salt['pillar.get']('gnuviechadmin-database:owner:password') }}" +export GVA_PGSQL_HOSTNAME="{{ salt['pillar.get']('gnuviechadmin-database:hostname') }}" +export GVA_PGSQL_PORT={{ salt['pillar.get']('gnuviechadmin-database:port') }} +export GVA_DOMAIN_NAME="{{ salt['pillar.get']('gnuviechadmin:domainname') }}" +export GVA_SITE_NAME="{{ salt['pillar.get']('gnuviechadmin:sitename') }}" +export GVA_SITE_SECRET="{{ salt['grains.get_or_set_hash']('gnuviechadmin:SECRET_KEY', 50) }}" +export GVA_SITE_ADMINMAIL="{{ salt['pillar.get']('gnuviechadmin:adminemail') }}" +export GVA_MIN_OS_UID={{ salt['pillar.get']('gnuviechadmin:minosuid') }} +export GVA_MIN_OS_GID={{ salt['pillar.get']('gnuviechadmin:minosgid') }} +export GVA_OSUSER_PREFIX="{{ salt['pillar.get']('gnuviechadmin:osuserprefix') }}" +export GVA_OSUSER_HOME_BASEPATH="{{ salt['pillar.get']('gnuviechadmin:osuserhomedirbase') }}" +export GVA_OSUSER_DEFAULT_SHELL="{{ salt['pillar.get']('gnuviechadmin:osuserdefaultshell') }}" +export GVA_BROKER_URL="{{ broker_url }}" +export GVA_OSUSER_UPLOADSERVER="{{ salt['pillar.get']('gnuviechadmin:uploadserver') }}" +export GVA_WEBMAIL_URL="{{ salt['pillar.get']('gnuviechadmin:webmail_url') }}" +export GVA_PHPMYADMIN_URL="{{ salt['pillar.get']('gnuviechadmin:phpmyadmin_url') }}" +export GVA_PHPPGADMIN_URL="{{ salt['pillar.get']('gnuviechadmin:phppgadmin_url') }}" diff --git a/salt/roots/gnuviechadmin/webinterface.sls b/salt/roots/gnuviechadmin/webinterface.sls index e69de29..90bddd4 100644 --- a/salt/roots/gnuviechadmin/webinterface.sls +++ b/salt/roots/gnuviechadmin/webinterface.sls @@ -0,0 +1,97 @@ +include: + - webserver + +gnuviechadmin-packages: + pkg.installed: + - names: + - libpq-dev + - libyaml-dev + - python-virtualenv + - python-dev + - python-pip + +{% import "webserver/sslcert.macros.sls" as sslcert %} + +{% set venv = salt['pillar.get']('gnuviechadmin:virtualenv') %} +{% set domainname = salt['pillar.get']('gnuviechadmin:domainname') %} +{{ sslcert.key_cert(domainname) }} + +{{ venv }}: + file.directory: + - user: vagrant + - group: vagrant + - require: + - cmd: gnuviechadmin-venv + +/home/vagrant/gvasettings.sh: + file.managed: + - user: vagrant + - group: vagrant + - mode: 0640 + - source: salt://gnuviechadmin/gvasettings.sh + - template: jinja + - context: + broker_url: amqp://{{ salt['pillar.get']('gnuviechadmin-queues:owner:user') }}:{{ salt['pillar.get']('gnuviechadmin-queues:owner:password') }}@mq/{{ salt['pillar.get']('gnuviechadmin-queues:vhost') }} + +gnuviechadmin-venv: + cmd.run: + - name: virtualenv {{ venv }} + - user: vagrant + - group: vagrant + - unless: test -f {{ venv }}/bin/pip + +gnuviechadmin-requires: + cmd.run: + - name: {{ venv }}/bin/pip install -U -r requirements/local.txt && touch {{ venv }}/lastinstall + - user: vagrant + - group: vagrant + - cwd: /vagrant + - require: + - file: {{ venv }} + - pkg: python-dev + - pkg: libpq-dev + - unless: test -e {{ venv }}/lastinstall && test /vagrant/requirements/local.txt -ot {{ venv }}/lastinstall && test /vagrant/requirements/base.txt -ot {{ venv }}/lastinstall + +gnuviechadmin-dbschema: + cmd.wait: + - name: . /home/vagrant/gvasettings.sh ; {{ venv }}/bin/python manage.py migrate --noinput + - user: vagrant + - group: vagrant + - cwd: /vagrant/gnuviechadmin + - watch: + - cmd: gnuviechadmin-requires + - file: /home/vagrant/gvasettings.sh + +gnuviechadmin-locale-data-compile: + cmd.wait: + - name: . /home/vagrant/gvasettings.sh ; find /vagrant/gnuviechadmin -type d -name 'locale' | while read dir; do cd $(dirname "$dir") ; {{ venv }}/bin/python /vagrant/gnuviechadmin/manage.py compilemessages ; done + - user: vagrant + - group: vagrant + - cwd: /vagrant/gnuviechadmin + - require: + - file: /home/vagrant/gvasettings.sh + - file: {{ venv }} + +/etc/nginx/sites-available/{{ domainname }}: + file.managed: + - user: root + - group: root + - mode: 0640 + - source: salt://gnuviechadmin/gnuviechadmin.nginx + - template: jinja + - context: + domainname: {{ domainname }} + ssl_keydir: {{ salt['pillar.get']('nginx:sslkeydir', '/etc/nginx/ssl/private') }} + ssl_certdir: {{ salt['pillar.get']('nginx:sslcertdir', '/etc/nginx/ssl/certs') }} + - require: + - pkg: nginx + - watch_in: + - service: nginx + +/etc/nginx/sites-enabled/{{ domainname }}: + file.symlink: + - target: /etc/nginx/sites-available/{{ domainname }} + - require: + - file: /etc/nginx/sites-available/{{ domainname }} + - watch_in: + - service: nginx diff --git a/salt/roots/webserver/nginx-ssl.conf b/salt/roots/webserver/nginx-ssl.conf index e0cb1ef..305f31d 100644 --- a/salt/roots/webserver/nginx-ssl.conf +++ b/salt/roots/webserver/nginx-ssl.conf @@ -4,7 +4,7 @@ ssl_ciphers kEECDH+AESGCM:kEECDH+AES:kEECDH:EDH+AESGCM:kEDH+AES:kEDH:AESGCM:ALL: ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; -ssl_dhparam {{ salt['pillar.get']('nginx:sslcertdir') }}/dhparams.pem; +ssl_dhparam {{ salt['pillar.get']('nginx:sslcertdir', '/etc/nginx/ssl/certs') }}/dhparams.pem; # OCSP stapling ssl_stapling on; diff --git a/salt/roots/webserver/sslcert.macros.sls b/salt/roots/webserver/sslcert.macros.sls new file mode 100644 index 0000000..528e65d --- /dev/null +++ b/salt/roots/webserver/sslcert.macros.sls @@ -0,0 +1,29 @@ +{%- macro key_cert(domain_name) %} +{% set nginx_ssl_keydir = salt['pillar.get']('nginx:sslkeydir', '/etc/nginx/ssl/private') %} +{% set nginx_ssl_certdir = salt['pillar.get']('nginx:sslcertdir', '/etc/nginx/ssl/certs') %} +{% set keyfile = nginx_ssl_keydir + '/' + domain_name + '.key.pem' %} +{% set certfile = nginx_ssl_certdir + '/' + domain_name + '.crt.pem' %} + +{{ keyfile }}: + rsa_key.valid_key: + - bits: {{ salt['pillar.get']('nginx:keylength:' + domain_name, 2048) }} + - require: + - file: {{ nginx_ssl_keydir }} + - require_in: + - file: /etc/nginx/sites-available/{{ domain_name }} + - service: nginx + +{{ certfile }}: + cmd.run: + - name: openssl req -new -x509 -key {{ keyfile }} -subj '/CN={{ domain_name }}' -days 730 -out {{ certfile }} + - require: + - rsa_key: {{ keyfile }} + - creates: {{ certfile }} + x509_certificate.valid_certificate: + - require: + - file: {{ nginx_ssl_certdir }} + - cmd: {{ certfile }} + - require_in: + - file: /etc/nginx/sites-available/{{ domain_name }} + - service: nginx +{% endmacro %}