Page Menu
Home
DevCentral
Search
Configure Global Search
Log In
Files
F3763046
D3068.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
15 KB
Referenced Files
None
Subscribers
None
D3068.diff
View Options
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
Details
Attached
Mime Type
text/plain
Expires
Fri, Nov 22, 19:15 (19 h, 8 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2256409
Default Alt Text
D3068.diff (15 KB)
Attached To
Mode
D3068: Generate .env or wp-config.php credentials files for Alkane PHP sites
Attached
Detach File
Event Timeline
Log In to Comment