From 2cd70a52bbff5d93cbe9130b25bcd67edcb9ef59 Mon Sep 17 00:00:00 2001 From: Daniel Berteaud Date: Fri, 5 Jan 2024 14:30:24 +0100 Subject: [PATCH] Add rendered example --- example/README.md | 2 + .../config/service-intentions/mariadb.hcl | 16 ++ example/images/mariadb-backup/Dockerfile | 7 + example/images/mariadb/Dockerfile | 20 ++ .../root/entrypoint.d/10-mariadb-conf.sh | 44 ++++ .../root/entrypoint.d/20-mariadb-init.sh | 36 ++++ example/images/mariadb/root/etc/my.cnf | 13 ++ example/init/vault-database | 19 ++ example/manage.nomad.hcl | 195 ++++++++++++++++++ example/mariadb.nomad.hcl | 191 +++++++++++++++++ example/prep.d/10-rand-pwd.sh | 16 ++ example/prep.d/mv_conf.sh | 19 ++ example/vault/policies/mariadb.hcl | 3 + 13 files changed, 581 insertions(+) create mode 100644 example/README.md create mode 100644 example/consul/config/service-intentions/mariadb.hcl create mode 100644 example/images/mariadb-backup/Dockerfile create mode 100644 example/images/mariadb/Dockerfile create mode 100755 example/images/mariadb/root/entrypoint.d/10-mariadb-conf.sh create mode 100755 example/images/mariadb/root/entrypoint.d/20-mariadb-init.sh create mode 100644 example/images/mariadb/root/etc/my.cnf create mode 100755 example/init/vault-database create mode 100644 example/manage.nomad.hcl create mode 100644 example/mariadb.nomad.hcl create mode 100755 example/prep.d/10-rand-pwd.sh create mode 100755 example/prep.d/mv_conf.sh create mode 100644 example/vault/policies/mariadb.hcl diff --git a/example/README.md b/example/README.md new file mode 100644 index 0000000..59ec6e0 --- /dev/null +++ b/example/README.md @@ -0,0 +1,2 @@ +# mariadb + diff --git a/example/consul/config/service-intentions/mariadb.hcl b/example/consul/config/service-intentions/mariadb.hcl new file mode 100644 index 0000000..3eaf43a --- /dev/null +++ b/example/consul/config/service-intentions/mariadb.hcl @@ -0,0 +1,16 @@ +Kind = "service-intentions" +Name = "mariadb" +Sources = [ + { + Name = "traefik" + Action = "allow" + }, + { + Name = "mariadb-manage" + Action = "allow" + }, + { + Name = "mariadb-backup" + Action = "allow" + } +] diff --git a/example/images/mariadb-backup/Dockerfile b/example/images/mariadb-backup/Dockerfile new file mode 100644 index 0000000..c33545a --- /dev/null +++ b/example/images/mariadb-backup/Dockerfile @@ -0,0 +1,7 @@ +FROM danielberteaud/mariadb-client:24.1-1 +MAINTAINER Daniel Berteaud + +RUN set -eux &&\ + apk --no-cache update &&\ + apk --no-cache add supercronic + diff --git a/example/images/mariadb/Dockerfile b/example/images/mariadb/Dockerfile new file mode 100644 index 0000000..5631915 --- /dev/null +++ b/example/images/mariadb/Dockerfile @@ -0,0 +1,20 @@ +FROM danielberteaud/mariadb-client:24.1-1 +MAINTAINER Daniel Berteaud + +ENV MYSQL_CONF_10_section=mysqld \ + MYSQL_CONF_11_innodb_buffer_pool_size=50% + +RUN set -eux &&\ + apk --no-cache upgrade &&\ + apk --no-cache add mariadb mariadb-server-utils &&\ + chown mysql:mysql /etc/my.cnf.d &&\ + rm -f /etc/my.cnf.d/* &&\ + mkdir /data /run/mysqld &&\ + chown mysql:mysql /data /run/mysqld &&\ + chmod 700 /data + +COPY root/ / + +EXPOSE 3306 +USER mysql +CMD ["mariadbd", "--console", "--skip-name-resolve"] diff --git a/example/images/mariadb/root/entrypoint.d/10-mariadb-conf.sh b/example/images/mariadb/root/entrypoint.d/10-mariadb-conf.sh new file mode 100755 index 0000000..ded85d9 --- /dev/null +++ b/example/images/mariadb/root/entrypoint.d/10-mariadb-conf.sh @@ -0,0 +1,44 @@ +#!/bin/sh + +set -eo pipefail + +get_max_mem(){ + if [ -e /sys/fs/cgroup/memory.max ]; then + # Read /sys/fs/cgroup/memory.max + MAX=$(cat /sys/fs/cgroup/memory.max) + # If it's "max", then the container has no limit, and we must detect the available RAM + if [ "${MAX}" = "max" ]; then + echo $(($(cat /proc/meminfo | grep MemTotal | sed -E 's/MemTotal:\s+([0-9]+)\s+kB/\1/')/1024)) + else + echo $(($(cat /sys/fs/cgroup/memory.max)/1024/1024)) + fi + else + echo $(($(cat /proc/meminfo | grep MemTotal | sed -E 's/MemTotal:\s+([0-9]+)\s+kB/\1/')/1024)) + fi +} + +if mount | grep -q ' /etc/my.cnf '; then + echo "/etc/my.cnf is mounted, skiping config from env vars" +else + echo "Configuring from env vars" + for VAR in $(printenv | grep -E '^MYSQL_CONF_' | sed -E 's/MYSQL_CONF_([^=]+)=.*/\1/' | sort -V); do + DIRECTIVE=$(echo ${VAR} | sed -E 's/^[0-9]+_//') + VALUE=$(printenv MYSQL_CONF_${VAR}) + + if [ "${DIRECTIVE}" = "section" ]; then + echo "[${VALUE}]" >> /etc/my.cnf.d/env.cnf + else + + # Allow some memory related settings to be expressed as a % + if echo ${DIRECTIVE} | grep -q -E "^(innodb_buffer_pool_size)$"; then + if echo ${VALUE} | grep -q -E "[0-9]+%$"; then + PERCENT=$(echo $VALUE | sed -E 's|%$||') + MAX_MEM=$(get_max_mem) + VALUE=$((${MAX_MEM}*${PERCENT}/100))MB + fi + fi + echo "Adding ${DIRECTIVE} = ${VALUE} in /etc/my.cnf.d/env.cnf" + echo "${DIRECTIVE} = ${VALUE}" >> /etc/my.cnf.d/env.cnf + fi + done +fi diff --git a/example/images/mariadb/root/entrypoint.d/20-mariadb-init.sh b/example/images/mariadb/root/entrypoint.d/20-mariadb-init.sh new file mode 100755 index 0000000..7a39527 --- /dev/null +++ b/example/images/mariadb/root/entrypoint.d/20-mariadb-init.sh @@ -0,0 +1,36 @@ +#!/bin/sh + +set -euo pipefail + +mkdir -p /data/db + +if [ -d /data/db/mysql ]; then + echo "MariaDB is already initialized" +else + echo "Bootstraping MariaDB" + + mysql_install_db + + MYSQL_DATABASE=${MYSQL_DATABASE:-""} + MYSQL_USER=${MYSQL_USER:-""} + MYSQL_PASSWORD=${MYSQL_PASSWORD:-""} + + cat << EOF > /tmp/mariainit.sql +USE mysql; +FLUSH PRIVILEGES; +GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' identified by '$MYSQL_ROOT_PASSWORD' WITH GRANT OPTION; +GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' identified by '$MYSQL_ROOT_PASSWORD' WITH GRANT OPTION; +DROP DATABASE test; +EOF + if [ "$MYSQL_DATABASE" != "" ]; then + echo "CREATE DATABASE IF NOT EXISTS \`$MYSQL_DATABASE\` CHARACTER SET utf8 COLLATE utf8_general_ci;" >> $tfile + + if [ "$MYSQL_USER" != "" ]; then + echo "GRANT ALL ON \`$MYSQL_DATABASE\`.* to '$MYSQL_USER'@'%' IDENTIFIED BY '$MYSQL_PASSWORD';" >> $tfile + fi + fi + + mariadbd --bootstrap --verbose=0 --skip-name-resolve < /tmp/mariainit.sql + rm -f /tmp/mariainit.sql + +fi diff --git a/example/images/mariadb/root/etc/my.cnf b/example/images/mariadb/root/etc/my.cnf new file mode 100644 index 0000000..b18587c --- /dev/null +++ b/example/images/mariadb/root/etc/my.cnf @@ -0,0 +1,13 @@ + +[client-server] +port = 3306 +socket = /run/mysqld/mysqld.sock + +[mysqld] +pid-file = /tmp/mysql.pid +symbolic-links = 0 +datadir = /data/db +default_storage_engine = InnoDB +innodb_log_file_size = 512M + +!includedir /etc/my.cnf.d diff --git a/example/init/vault-database b/example/init/vault-database new file mode 100755 index 0000000..3a26b7b --- /dev/null +++ b/example/init/vault-database @@ -0,0 +1,19 @@ +#!/bin/sh + +set -euo pipefail + +if [ "$(vault secrets list -format json | jq -r '.["database/"].type')" != "database" ]; then + vault secrets enable -path database database +fi + +if [ "$(vault list -format json database/config | jq '.[] | test("^mariadb$")')" = "false" ]; then + vault write database/config/mariadb \ + plugin_name="mysql-database-plugin" \ + connection_url="{{username}}:{{password}}@tcp(mariadb.example.org:3306)/" \ + allowed_roles="*" \ + username=vault \ + password="$(vault kv get -field vault_initial_pwd kv/service/mariadb)" \ + disable_escaping=true + vault write -force database/rotate-root/mariadb +fi + diff --git a/example/manage.nomad.hcl b/example/manage.nomad.hcl new file mode 100644 index 0000000..75cad1c --- /dev/null +++ b/example/manage.nomad.hcl @@ -0,0 +1,195 @@ +job "mariadb-manage" { + type = "batch" + + datacenters = ["dc1"] + + + meta { + # Force job to run each time + run = "${uuidv4()}" + } + + group "manage" { + network { + mode = "bridge" + } + + service { + name = "mariadb-manage" + connect { + sidecar_service { + proxy { + upstreams { + destination_name = "mariadb" + local_bind_port = 3306 + } + } + } + sidecar_task { + resources { + cpu = 50 + memory = 64 + } + + } + } + + } + + # wait for required services tp be ready before starting the main task + task "wait-for" { + + driver = "docker" + user = 1053 + + config { + image = "danielberteaud/wait-for:24.1-1" + readonly_rootfs = true + pids_limit = 20 + } + + lifecycle { + hook = "prestart" + } + + env { + SERVICE_0 = "mariadb.service.consul" + } + + resources { + cpu = 10 + memory = 10 + memory_max = 30 + } + } + + + + task "manage" { + driver = "docker" + + config { + image = "danielberteaud/mariadb-client:24.1-1" + pids_limit = 50 + readonly_rootfs = true + command = "/local/manage.sh" + volumes = [ + "secrets/my.cnf:/root/.my.cnf:ro" + ] + } + + vault { + policies = ["mariadb"] + } + + env { + + LANG = "fr_FR.utf8" + TZ = "Europe/Paris" + + } + + template { + data = <<_EOT +# Databases +# Users +_EOT + destination = "secrets/userdb.env" + uid = 100000 + gid = 100000 + perms = 0400 + env = true + } + + template { + data = <<_EOT +#!/bin/sh + +# vim: syntax=sh + +set -euo pipefail + +echo "Create vault user" +mysql <<_EOSQL +CREATE USER IF NOT EXISTS 'vault'@'%' IDENTIFIED BY '${VAULT_INITIAL_PASSWORD}'; +GRANT ALL PRIVILEGES ON *.* TO 'vault'@'%' WITH GRANT OPTION; +_EOSQL + +echo "Create databases" +for IDX in $(printenv | grep -E '^MY_DB_([0-9]+)=' | sed -E 's/^MY_DB_([0-9]+)=.*/\1/'); do + DB_NAME=$(printenv MY_DB_${IDX}) + echo "Found DB ${DB_NAME} to create" + DB_CHARSET=$(printenv MY_DB_${IDX}_CHARSET || echo "utf8mb4") + DB_COLLATE=$(printenv MY_DB_${IDX}_COLLATE || echo "utf8mb4_general_ci") + echo "Create database ${DB_NAME} (CHARACTER SET \"${DB_CHARSET}\" COLLATE \"${DB_COLLATE}\") if needed" + mysql <<_EOSQL + CREATE DATABASE IF NOT EXISTS ${DB_NAME} CHARACTER SET "${DB_CHARSET}" COLLATE "${DB_COLLATE}" +_EOSQL +done + +echo "Create users" +for IDX in $(printenv | grep -E '^MY_USER_([0-9]+)=' | sed -E 's/^MY_USER_([0-9]+)=.*/\1/'); do + DB_USER=$(printenv MY_USER_${IDX}) + echo "Found DB User ${DB_USER} to create" + DB_HOST=$(printenv MY_USER_${IDX}_HOST || echo '%') + DB_PASSWORD=$(printenv MY_USER_${IDX}_PASSWORD || echo '') + if [ "${DB_PASSWORD}" = "" ]; then + mysql <<_EOSQL +CREATE USER IF NOT EXISTS '${DB_USER}'@'${DB_HOST}'; +_EOSQL + else + mysql <<_EOSQL +CREATE USER IF NOT EXISTS '${DB_USER}'@'${DB_HOST}' IDENTIFIED BY '${DB_PASSWORD}'; +_EOSQL + fi + + echo "Applying grants for ${DB_USER}" + for GRANT in $(printenv | grep -E "^MY_USER_${IDX}_GRANT_([0-9]+)=)" | sed -E "s/^MY_USER_${IDX}_GRANT_([0-9]+)=.*/\1/"); do + mysql <<_EOSQL +GRANT $(printenv MY_USER_${IDX}_GRANT_${GRANT}); +_EOSQL + done +done + +_EOT + destination = "local/manage.sh" + uid = 100000 + gid = 100000 + perms = 755 + } + + template { + data = <<_EOT +[client] +host = 127.0.0.1 +user = root +password = {{ with secret "kv/service/mariadb" }}{{ .Data.data.root_pwd }}{{ end }} +_EOT + destination = "secrets/my.cnf" + uid = 100100 + gid = 100101 + perms = 640 + } + + template { + data = <<_EOT +{{ with secret "kv/service/mariadb" }} +VAULT_INITIAL_PASSWORD={{ .Data.data.vault_initial_pwd }} +BACKUP_PASSWORD={{ .Data.data.backup_pwd }} +{{ end }} +_EOT + destination = "secrets/manage.env" + uid = 100000 + gid = 100000 + perms = 400 + env = true + } + + resources { + cpu = 20 + memory = 64 + } + + } + } +} diff --git a/example/mariadb.nomad.hcl b/example/mariadb.nomad.hcl new file mode 100644 index 0000000..8c7b15f --- /dev/null +++ b/example/mariadb.nomad.hcl @@ -0,0 +1,191 @@ +job "mariadb" { + + + datacenters = ["dc1"] + + + group "server" { + + network { + mode = "bridge" + } + + volume "mariadb" { + type = "csi" + source = "mariadb-data" + access_mode = "single-node-writer" + attachment_mode = "file-system" + per_alloc = true + } + + service { + name = "mariadb" + port = 3306 + + connect { + sidecar_service { + disable_default_tcp_check = true + } + sidecar_task { + resources { + cpu = 50 + memory = 64 + } + + } + } + + + check { + name = "alive" + type = "script" + task = "mariadb" + command = "mysqladmin" + args = [ + "ping" + ] + timeout = "10s" + interval = "5s" + } + } + + # Run mysql_upgrade + task "upgrade" { + driver = "docker" + + lifecycle { + hook = "poststart" + } + + config { + image = "danielberteaud/mariadb:24.1-1" + pids_limit = 100 + command = "/local/mysql_upgrade.sh" + } + + vault { + policies = ["mariadb"] + env = false + disable_file = true + } + + template { + data = <<_EOT +[client] +user = root +host = 127.0.0.1 +password = {{ with secret "kv/service/mariadb" }}{{ .Data.data.root_pwd }}{{ end }} +_EOT + destination = "secrets/.my.cnf" + uid = 100100 + gid = 100101 + perms = 640 + } + + template { + data = <<_EOT +#!/bin/sh + +set -euo pipefail + +HOME=/secrets + +COUNT=0 +while true; do + if mysqladmin ping; then + echo "MariaDB is ready, running mysql_upgrade" + mysql_upgrade + exit 0 + fi + echo "MariaDB not ready yet, waiting a bit more" + COUNT=$((COUNT+1)) + sleep 1 +done + + +_EOT + destination = "local/mysql_upgrade.sh" + perms = 755 + } + + volume_mount { + volume = "mariadb" + destination = "/data" + } + + resources { + cpu = 10 + memory = 32 + memory_max = 64 + } + } + + task "mariadb" { + driver = "docker" + leader = true + + kill_timeout = "5m" + + config { + image = "danielberteaud/mariadb:24.1-1" + volumes = [ + "secrets/:/etc/my.cnf.d", + "secrets/my.conf:/var/lib/mysql/.my.cnf:ro", + ] + pids_limit = 300 + #readonly_rootfs = true + } + + vault { + policies = ["mariadb"] + env = false + disable_file = true + } + + env { + MYSQL_CONF_11_bind-address = "127.0.0.1" + + LANG = "fr_FR.utf8" + TZ = "Europe/Paris" + + } + + template { + data = <<_EOT +{{ with secret "kv/service/mariadb" }} +MYSQL_ROOT_PASSWORD={{ .Data.data.root_pwd }} +{{ end }} +_EOT + destination = "secrets/mariadb.env" + uid = 100000 + gid = 100000 + perms = 400 + env = true + } + + template { + data = <<_EOT +[client] +user = root +password = {{ with secret "kv/service/mariadb" }}{{ .Data.data.root_pwd }}{{ end }} +_EOT + destination = "secrets/my.conf" + uid = 100100 + gid = 100101 + perms = 640 + } + + volume_mount { + volume = "mariadb" + destination = "/data" + } + + resources { + cpu = 1000 + memory = 512 + } + + + } + } +} diff --git a/example/prep.d/10-rand-pwd.sh b/example/prep.d/10-rand-pwd.sh new file mode 100755 index 0000000..7cac0c3 --- /dev/null +++ b/example/prep.d/10-rand-pwd.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +set -euo pipefail + +if ! vault kv list kv/service 2>/dev/null | grep -q -E '^mariadb$'; then + vault kv put kv/service/mariadb \ + root_pwd=$(pwgen -s -n 50 1) \ + vault_initial_pwd=$(pwgen -s -n 50 1) +fi + +for PWD in root_pwd vault_initial_pwd; do + if ! vault kv get -field ${PWD} kv/service/mariadb >/dev/null 2>&1; then + vault kv patch kv/service/mariadb \ + ${PWD}=$(pwgen -s -n 50 1) + fi +done diff --git a/example/prep.d/mv_conf.sh b/example/prep.d/mv_conf.sh new file mode 100755 index 0000000..ad767d1 --- /dev/null +++ b/example/prep.d/mv_conf.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +set -eu + + + +if [ "mariadb" != "mariadb" ]; then + for DIR in vault consul nomad; do + if [ -d output/${DIR} ]; then + for FILE in $(find output/${DIR} -name "*mariadb*.hcl" -type f); do + NEW_FILE=$(echo "${FILE}" | sed -E "s/mariadb/mariadb/g") + mv "${FILE}" "${NEW_FILE}" + done + fi + done +fi + + + diff --git a/example/vault/policies/mariadb.hcl b/example/vault/policies/mariadb.hcl new file mode 100644 index 0000000..349544e --- /dev/null +++ b/example/vault/policies/mariadb.hcl @@ -0,0 +1,3 @@ +path "kv/data/service/mariadb" { + capabilities = ["read"] +}