Page MenuHomeDevCentral

D3068.diff
No OneTemporary

D3068.diff

diff --git a/pillar/credentials/vault.sls b/pillar/credentials/vault.sls
--- a/pillar/credentials/vault.sls
+++ b/pillar/credentials/vault.sls
@@ -205,9 +205,26 @@
- ops/secrets/dbserver/cluster-B/users/saas-mediawiki
- ops/secrets/nasqueron/mediawiki/secret_key
+ saas-wordpress:
+ - ops/secrets/dbserver/cluster-B/users/dereckson_blog
+
+ - ops/secrets/dereckson/wordpress/secrets
+
viperserv:
- ops/secrets/nasqueron.viperserv.vault
+ webserver-alkane:
+ - ops/secrets/dbserver/cluster-B/users/dereckson_www
+ - ops/secrets/dbserver/cluster-B/users/zed
+
+ - ops/secrets/zed/hypership/secret_key
+
+ #
+ # Wolfplex credentials
+ #
+
+ - ops/secrets/nasqueron.etherpad.api
+
webserver-legacy:
#
diff --git a/pillar/nodes/nodes.sls b/pillar/nodes/nodes.sls
--- a/pillar/nodes/nodes.sls
+++ b/pillar/nodes/nodes.sls
@@ -190,6 +190,8 @@
hostname: web-001.nasqueron.org
roles:
- webserver-alkane
+ - saas-mediawiki
+ - saas-wordpress
network:
ipv6_tunnel: False
diff --git a/pillar/saas/wordpress.sls b/pillar/saas/wordpress.sls
new file mode 100644
--- /dev/null
+++ b/pillar/saas/wordpress.sls
@@ -0,0 +1,18 @@
+# -------------------------------------------------------------
+# Salt — WordPress SaaS
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# License: Trivial work, not eligible to copyright
+# -------------------------------------------------------------
+
+wordpress_saas:
+ wordpress_directory: /srv/wordpress
+
+wordpress_sites:
+ dereckson:
+ user: web-be-dereckson-www
+ db:
+ service: db-B
+ credentials: dbserver/cluster-B/users/dereckson_blog
+ name: dereckson_blog
+ secrets: dereckson/wordpress/secrets
diff --git a/pillar/top.sls b/pillar/top.sls
--- a/pillar/top.sls
+++ b/pillar/top.sls
@@ -58,9 +58,12 @@
web-001:
- saas.mediawiki
+ - saas.wordpress
+ - webserver.credentials
windriver:
- devserver.ports
- devserver.repos
- webserver.labs
+ - webserver.credentials
- webserver.wwwroot51
diff --git a/pillar/webserver/credentials.sls b/pillar/webserver/credentials.sls
new file mode 100644
--- /dev/null
+++ b/pillar/webserver/credentials.sls
@@ -0,0 +1,62 @@
+# -------------------------------------------------------------
+# Salt — Sites to provision on the legacy web server
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# License: Trivial work, not eligible to copyright
+# -------------------------------------------------------------
+
+# -------------------------------------------------------------
+# Content of the .env files
+#
+# Those files allow site using DotEnv to read secrets.
+#
+# To ensure secrets can only be read by application user, use:
+#
+# ```
+# user: <php-fpm pool user>
+# ```
+# If your configuration can be read and stored in memory,
+# it's probably best to directly call Vault from the app
+# and only provision Vault AppRole credentials:
+#
+# ```
+# vault: <path to AppRole credential>
+# ```
+#
+# For PHP sites where the configuration file is read every
+# request, it's probably best to cache secrets in file
+# through this mechanism.
+#
+# If you need a database, you can use:
+#
+# ```
+# db:
+# service: entry in nasqueron_services table
+# credentials: path to Vault secret
+#
+# To provision a secret key or other credentials, use:
+#
+# extra_credentials:
+# key: path to vault secret
+#
+# If you need to pass extra plain values use:
+#
+# extra_values:
+# key: value
+# ```
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+webserver_content_dotenv:
+ /var/wwwroot/dereckson.be/www/.env:
+ user: web-be-dereckson-www
+ db:
+ service: db-B
+ credentials: dbserver/cluster-B/users/dereckson_www
+
+ /var/wwwroot/hypership.space/www/.env:
+ user: web-space-hypership-www
+ db:
+ service: db-B
+ credentials: dbserver/cluster-B/users/zed
+ extra_credentials:
+ ZED_SECRET_KEY: zed/hypership/secret_key
diff --git a/roles/saas-wordpress/init.sls b/roles/saas-wordpress/init.sls
new file mode 100644
--- /dev/null
+++ b/roles/saas-wordpress/init.sls
@@ -0,0 +1,9 @@
+# -------------------------------------------------------------
+# Salt — WordPress SaaS
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# License: Trivial work, not eligible to copyright
+# -------------------------------------------------------------
+
+include:
+ - .wordpress
diff --git a/roles/saas-wordpress/wordpress/config.sls b/roles/saas-wordpress/wordpress/config.sls
new file mode 100644
--- /dev/null
+++ b/roles/saas-wordpress/wordpress/config.sls
@@ -0,0 +1,40 @@
+# -------------------------------------------------------------
+# Salt — WordPress SaaS
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# License: Trivial work, not eligible to copyright
+# -------------------------------------------------------------
+
+# -------------------------------------------------------------
+# Experimental WordPress Saas installation
+#
+# The only goal of this stanza is to see how to populate
+# credentials through Vault.
+#
+# In a next step, wp-config.php will be set by an entry point
+# built on the top of nasqueron/saas-service, like we do for
+# Mediawiki.
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+{% set blog_args = pillar["wordpress_sites"]["dereckson"] %}
+{% set secrets = salt["vault.read_secret"]("ops/secrets/" + blog_args["secrets"]) %}
+
+/srv/wordpress/wp-config.php:
+ file.managed:
+ - source: salt://roles/saas-wordpress/wordpress/files/wp-config.php.jinja
+ - mode: 400
+ - show_changes: False
+ - user: {{ blog_args["user"] }}
+ - makedirs: True
+ - template: jinja
+ - context:
+ defines:
+ DB_HOST: {{ pillar["nasqueron_services"][blog_args["db"]["service"]] }}
+ DB_USER: {{ salt["credentials.get_username"](blog_args["db"]["credentials"]) }}
+ DB_PASSWORD: {{ salt["credentials.get_password"](blog_args["db"]["credentials"]) }}
+ DB_NAME: {{ blog_args["db"]["name"] }}
+
+ # Secrets
+ {% for key, value in secrets.items() %}
+ {{ key }}: {{ value | yaml_dquote }}
+ {% endfor %}
diff --git a/roles/saas-wordpress/wordpress/files/wp-config.php.jinja b/roles/saas-wordpress/wordpress/files/wp-config.php.jinja
new file mode 100644
--- /dev/null
+++ b/roles/saas-wordpress/wordpress/files/wp-config.php.jinja
@@ -0,0 +1,19 @@
+<?php
+
+# -------------------------------------------------------------
+# WordPress configuration
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# License: Trivial work, not eligible to copyright
+# Source file: roles/saas-wordpress/wordpress/files/wp-config.php
+# -------------------------------------------------------------
+#
+# <auto-generated>
+# This file is managed by our rOPS SaltStack repository.
+#
+# Changes to this file may cause incorrect behavior
+# and will be lost if the state is redeployed.
+# </auto-generated>
+{% for key, value in defines.items() %}
+define( "{{ key }}", {{ value | yaml_squote }} );
+{%- endfor -%}
diff --git a/roles/saas-wordpress/wordpress/init.sls b/roles/saas-wordpress/wordpress/init.sls
new file mode 100644
--- /dev/null
+++ b/roles/saas-wordpress/wordpress/init.sls
@@ -0,0 +1,9 @@
+# -------------------------------------------------------------
+# Salt — WordPress SaaS
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# License: Trivial work, not eligible to copyright
+# -------------------------------------------------------------
+
+include:
+ - .config
diff --git a/roles/webserver-content/README.md b/roles/webserver-content/README.md
--- a/roles/webserver-content/README.md
+++ b/roles/webserver-content/README.md
@@ -21,6 +21,9 @@
The bipbip.acme.tld site will be described in `tld/acme/bipbip.sls` file.
+The _generic folder offers common solutions to generic problems
+like provision a .env file with database credentials or secret key.
+
## Add a new domain
1. Create a new folder hierarchy for the domain
diff --git a/roles/webserver-content/_generic/files/dot.env b/roles/webserver-content/_generic/files/dot.env
new file mode 100644
--- /dev/null
+++ b/roles/webserver-content/_generic/files/dot.env
@@ -0,0 +1,17 @@
+# -------------------------------------------------------------
+# .env for DotEnv library
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# License: Trivial work, not eligible to copyright
+# Source file: roles/webserver-content/_generic/files/dot.env
+# -------------------------------------------------------------
+#
+# <auto-generated>
+# This file is managed by our rOPS SaltStack repository.
+#
+# Changes to this file may cause incorrect behavior
+# and will be lost if the state is redeployed.
+# </auto-generated>
+{% for key, value in environment.items() %}
+{{ key }}={{ value }}
+{%- endfor -%}
diff --git a/roles/webserver-content/_generic/init.sls b/roles/webserver-content/_generic/init.sls
new file mode 100644
--- /dev/null
+++ b/roles/webserver-content/_generic/init.sls
@@ -0,0 +1,42 @@
+# -------------------------------------------------------------
+# Salt — Webserver content
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# License: Trivial work, not eligible to copyright
+# -------------------------------------------------------------
+
+# -------------------------------------------------------------
+# .env
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+{% for env_path, env_args in pillar.get("webserver_content_dotenv", {}).items() %}
+
+{% set db_credentials = env_args["db"]["credentials"] %}
+
+{{ env_path }}:
+ file.managed:
+ - source: salt://roles/webserver-content/_generic/files/dot.env
+ - mode: 400
+ - show_changes: False
+ - template: jinja
+ - context:
+ environment:
+ {% if "db" in env_args %}
+ DB_HOST: {{ pillar["nasqueron_services"][env_args["db"]["service"]] }}
+ DB_USER: {{ salt["credentials.get_username"](db_credentials) }}
+ DB_PASSWORD: {{ salt["credentials.get_password"](db_credentials) }}
+ {% endif %}
+
+ {% if "vault" in env_args %}
+ VAULT_ROLE_ID: {{ salt["credentials.get_username"](env_args["vault"]) }}
+ VAULT_SECRET_ID: {{ salt["credentials.get_password"](env_args["vault"]) }}
+ {% endif %}
+
+ {% for key, value in env_args.get("extra_values", {}).items() %}
+ {{ key }}: {{ value }}
+ {% endfor %}
+
+ {% for key, vault_path in env_args.get("extra_credentials", {}).items() %}
+ {{ key }}: {{ salt["credentials.get_password"](vault_path) }}
+ {% endfor %}
+{% endfor %}
diff --git a/roles/webserver-content/init.sls b/roles/webserver-content/init.sls
--- a/roles/webserver-content/init.sls
+++ b/roles/webserver-content/init.sls
@@ -20,3 +20,5 @@
- .org/wolfplex/api
- .org/wolfplex/www
- .space/hypership
+
+ - ._generic
diff --git a/top.sls b/top.sls
--- a/top.sls
+++ b/top.sls
@@ -46,3 +46,4 @@
- roles/webserver-core
- roles/webserver-alkane
- roles/saas-mediawiki
+ - roles/saas-wordpress
diff --git a/utils/vault/wordpress-provision-secrets.py b/utils/vault/wordpress-provision-secrets.py
new file mode 100755
--- /dev/null
+++ b/utils/vault/wordpress-provision-secrets.py
@@ -0,0 +1,126 @@
+#!/usr/bin/env python3
+
+# -------------------------------------------------------------
+# SaaS :: WordPress :: Provision Vault secrets
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# Description: Write to Vault the secrets required by
+# WordPress to the specific secret path.
+# Dependencies: hvac
+# License: BSD-2-Clause
+# -------------------------------------------------------------
+
+
+import os
+import secrets
+import string
+import sys
+
+import hvac
+
+
+# -------------------------------------------------------------
+# WordPress secrets
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+
+KEYS = [
+ "AUTH_KEY",
+ "SECURE_AUTH_KEY",
+ "LOGGED_IN_KEY",
+ "NONCE_KEY",
+ "AUTH_SALT",
+ "SECURE_AUTH_SALT",
+ "LOGGED_IN_SALT",
+ "NONCE_SALT",
+]
+
+SPECIAL_CHARS = ">!@#$%^&*()_+|~-=`{}[]:;<>,.?/"
+SECRET_CHARS = string.ascii_letters + string.digits + SPECIAL_CHARS
+
+
+def generate_secret(length=64, min_digits=3, min_special_chars=3):
+ while True:
+ secret = "".join(secrets.choice(SECRET_CHARS) for _ in range(length))
+ if (
+ any(c.islower() for c in secret)
+ and any(c.isupper() for c in secret)
+ and sum(c.isdigit() for c in secret) >= min_digits
+ and sum(c in SPECIAL_CHARS for c in secret) >= min_special_chars
+ ):
+ break
+
+ return secret
+
+
+def generate_wordpress_secrets():
+ return {key: generate_secret() for key in KEYS}
+
+
+# -------------------------------------------------------------
+# Vault
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+
+VAULT_CA_CERTIFICATE = "roles/core/certificates/files/nasqueron-vault-ca.crt"
+
+
+def publish_secret(secret_path, url, token, mount_point="ops", path_prefix="secrets/"):
+ wordpress_secrets = generate_wordpress_secrets()
+
+ client = hvac.Client(url=url, token=token, verify=VAULT_CA_CERTIFICATE)
+ client.secrets.kv.v2.create_or_update_secret(
+ mount_point=mount_point,
+ path=path_prefix + secret_path,
+ secret=wordpress_secrets,
+ )
+
+
+def read_vault_token():
+ if "VAULT_TOKEN" in os.environ:
+ return True, os.environ["VAULT_TOKEN"]
+
+ if "HOME" in os.environ:
+ token_path = os.path.join(os.environ["HOME"], ".vault-token")
+ if os.path.isfile(token_path):
+ with open(token_path) as f:
+ return True, f.read().strip()
+
+ return False, None
+
+
+# -------------------------------------------------------------
+# Application entry-point
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+
+if __name__ == "__main__":
+ argc = len(sys.argv)
+
+ if argc < 2:
+ print(f"Usage: {sys.argv[0]} <secret path>", file=sys.stderr)
+ sys.exit(1)
+
+ if "VAULT_ADDR" not in os.environ:
+ print(
+ "Set VAULT_ADDR environment variable to point to your current Vault installation.",
+ file=sys.stderr,
+ )
+ print(
+ "For example, `export VAULT_ADDR=https://172.27.27.7:8200`", file=sys.stderr
+ )
+ sys.exit(2)
+
+ success, token = read_vault_token()
+ if not success:
+ print(
+ "Set VAULT_TOKEN environment variable to your Vault token to authenticate the request.",
+ file=sys.stderr,
+ )
+ print(
+ "Alternatively, you can also store your token in ~/.vault-token.",
+ file=sys.stderr,
+ )
+ sys.exit(2)
+
+ publish_secret(sys.argv[1], os.environ["VAULT_ADDR"], token)

File Metadata

Mime Type
text/plain
Expires
Mon, Nov 18, 00:29 (55 m, 47 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2249960
Default Alt Text
D3068.diff (15 KB)

Event Timeline