Page MenuHomeDevCentral

No OneTemporary

diff --git a/_modules/credentials.py b/_modules/credentials.py
index b259a0f..35ba308 100644
--- a/_modules/credentials.py
+++ b/_modules/credentials.py
@@ -1,134 +1,232 @@
# -*- coding: utf-8 -*-
# -------------------------------------------------------------
# Salt — Credentials
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Project: Nasqueron
# Description: Credentials-related execution module methods
# License: BSD-2-Clause
# -------------------------------------------------------------
+import os
+
from salt.utils.files import fopen
+VAULT_PREFIX = "ops/secrets/"
+
+
+# -------------------------------------------------------------
+# Configuration
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+
+def _are_credentials_hidden():
+ return "CONFIG_PUBLISHER" in os.environ or "state.show_sls" in os.environ.get(
+ "SUDO_COMMAND", ""
+ )
+
+
# -------------------------------------------------------------
# HOF utilities
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
def _filter_discard_empty_string_values(haystack):
if type(haystack) is dict:
return {k: v for k, v in haystack.items() if v != ""}
if type(haystack) is list:
return [v for v in haystack if v != ""]
raise ValueError("Argument isn't a list or a dict: " + str(type(haystack)))
def _join_document_fragments(fragments):
filtered = _filter_discard_empty_string_values(fragments)
return "\n\n".join(filtered)
+# -------------------------------------------------------------
+# Fetch credentials from Vault
+#
+# Methods signatures are compatible with Zemke-Rhyne module.
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+
+def _get_default_secret_path():
+ return VAULT_PREFIX
+
+
+def _read_secret(key, prefix=None):
+ if prefix is None:
+ prefix = _get_default_secret_path()
+
+ return __salt__["vault.read_secret"](f"{prefix}/{key}")
+
+
+def get_password(key, prefix=None):
+ """
+ A function to fetch credential on Vault
+
+ CLI Example:
+
+ salt docker-001 credentials.get_password nasqueron.foo.bar
+
+ :param key: The key in ops/secrets namespace
+ :param prefix: the prefix path for that key, by default "ops/secrets/"
+ :return: The username
+ """
+ if _are_credentials_hidden():
+ return "credential for " + key
+
+ return _read_secret(key, prefix)["password"]
+
+
+def get_username(key, prefix=None):
+ """
+ A function to fetch the username associated to a credential
+ through Vault
+
+ CLI Example:
+
+ salt docker-001 credentials.get_username nasqueron.foo.bar
+
+ :param key: The key in ops/secrets namespace
+ :param prefix: the prefix path for that key, by default "ops/secrets/"
+ :return: The secret value
+ """
+ return _read_secret(key, prefix)["username"]
+
+
+def get_token(key, prefix=None):
+ """
+ A function to fetch credential through Vault
+
+ CLI Example:
+
+ salt docker-001 credentials.get_token nasqueron.foo.bar
+
+ :param key: The key in ops/secrets namespace
+ :param prefix: the prefix path for that key, by default "ops/secrets/"
+ :return: The secret value
+
+ For Vault, this is actually an alias of the get_password method.
+ """
+ return get_password(key, prefix)
+
+
+def get_sentry_dsn(args):
+ if _are_credentials_hidden():
+ return "credential for " + args["credential"]
+
+ host = __pillar__["sentry_realms"][args["realm"]]["host"]
+ credential = _read_secret(args["credential"])
+
+ return (
+ f"https://{credential['username']}:{credential['password']}"
+ f"@{host}/{args['project_id']}"
+ )
+
+
# -------------------------------------------------------------
# Build Vault policies
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class VaultSaltRolePolicy:
def __init__(self, role):
self.role = role
def build_policy(self):
return _join_document_fragments(
[
self.build_read_secrets_policy(),
self.import_extra_policies(),
]
)
#
# Secrets from pillar entry vault_secrets_by_role
#
def build_read_secrets_policy(self):
vault_paths = __pillar__["vault_secrets_by_role"].get(self.role, [])
return _join_document_fragments(
[self.get_read_rule(vault_path) for vault_path in vault_paths]
)
def get_read_rule(self, vault_path):
resolved_vault_path = self.resolve_vault_path(vault_path)
return f"""path \"{resolved_vault_path}\" {{
capabilities = [ \"read\" ]
}}"""
@staticmethod
def resolve_vault_path(vault_path):
for pillar_path, mount_path in __pillar__.get("vault_mount_paths", {}).items():
if vault_path.startswith(pillar_path):
start_position = len(pillar_path)
return mount_path + vault_path[start_position:]
return vault_path
#
# Import policies from pillar entry vault_extra_policies_by_role
#
def import_extra_policies(self):
extra_policies = __pillar__["vault_extra_policies_by_role"].get(self.role, [])
return _join_document_fragments(
[self.import_policy(policy) for policy in extra_policies]
)
@staticmethod
def import_policy(policy):
policy_file = f"{__pillar__['vault_policies_source']}/{policy}.hcl"
if policy_file.startswith("salt://"):
policy_file = __salt__["cp.cache_file"](policy_file)
with fopen(policy_file) as fd:
return fd.read()
def _compile_roles_policies():
return {
role: VaultSaltRolePolicy(role).build_policy() for role in _get_relevant_roles()
}
def _get_relevant_roles():
return {
role
for pillar_entry in [
"vault_extra_policies_by_role",
"vault_secrets_by_role",
]
for role in __pillar__[pillar_entry].keys()
}
def _build_node_policy(node, roles_policies):
rules = [
roles_policies[role]
for role in __salt__["node.get"]("roles", node)
if role in roles_policies
]
return _join_document_fragments(rules)
def build_policies_by_node():
roles_policies = _compile_roles_policies()
policies = {
node: _build_node_policy(node, roles_policies)
for node in __pillar__["nodes"].keys()
}
return _filter_discard_empty_string_values(policies)
diff --git a/roles/paas-docker/containers/files/sentry/sentry.sh.jinja b/roles/paas-docker/containers/files/sentry/sentry.sh.jinja
index f6c24f0..0ea762d 100644
--- a/roles/paas-docker/containers/files/sentry/sentry.sh.jinja
+++ b/roles/paas-docker/containers/files/sentry/sentry.sh.jinja
@@ -1,26 +1,28 @@
#!/bin/sh
# -------------------------------------------------------------
# PaaS Docker
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Project: Nasqueron
# Created: 2018-11-10
# License: Trivial work, not eligible to copyright
# Description: Wrapper for sentry command (local instance)
# Source file: roles/paas-docker/containers/files/sentry/sentry.sh.jinja
# -------------------------------------------------------------
#
# <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>
-SECRET_KEY=$(zr getcredentials {{ credential_id }} token)
+set -e
+
+SECRET_KEY=$(credential {{ credential_key }})
docker run -it --rm \
-e SENTRY_SECRET_KEY=$SECRET_KEY \
--link {{ links.postgresql }}:postgres \
--link {{ links.redis }}:redis \
sentry "$@"
diff --git a/roles/paas-docker/containers/sentry.sls b/roles/paas-docker/containers/sentry.sls
index 376b808..96c9108 100644
--- a/roles/paas-docker/containers/sentry.sls
+++ b/roles/paas-docker/containers/sentry.sls
@@ -1,98 +1,98 @@
# -------------------------------------------------------------
# Salt — Provision Docker engine
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Project: Nasqueron
# Created: 2016-12-15
# License: Trivial work, not eligible to copyright
# -------------------------------------------------------------
{% set has_selinux = salt['grains.get']('selinux:enabled', False) %}
{% set containers = pillar['docker_containers'][grains['id']] %}
# -------------------------------------------------------------
# Data directory
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{% for realm, args in pillar['sentry_realms'].items() %}
/srv/sentry/{{ realm }}:
file.directory:
- user: 999
- group: 999
- makedirs: True
/srv/sentry/{{ realm }}/bin/sentry:
file.managed:
- source: salt://roles/paas-docker/containers/files/sentry/sentry.sh.jinja
- template: jinja
- mode: 755
- makedirs: True
- context:
links: {{ args['links'] }}
- credential_id: {{ salt['zr.get_credential_id'](args['credential']) }}
+ credential_key: args['credential']
{% if has_selinux %}
selinux_context_{{ realm }}_sentry_data:
selinux.fcontext_policy_present:
- name: /srv/sentry/{{ realm }}
- sel_type: container_file_t
selinux_context_{{ realm }}_sentry_data_applied:
selinux.fcontext_policy_applied:
- name: /srv/sentry/{{ realm }}
{% endif %}
{% endfor %}
# -------------------------------------------------------------
# Web application
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{% for instance, container in containers['sentry'].items() %}
{% set args = pillar['sentry_realms'][container['realm']] %}
{{ instance }}:
docker_container.running:
- detach: True
- interactive: True
- image: library/sentry
- binds: &binds /srv/sentry/{{ container['realm'] }}:/var/lib/sentry/files
- links: &links
- {{ args['links']['postgresql'] }}:postgres
- {{ args['links']['redis'] }}:redis
- {{ args['links']['smtp'] }}:smtp
- environment: &env
- SENTRY_SECRET_KEY: {{ salt['zr.get_token'](args['credential']) }}
- SENTRY_FILESTORE_DIR:
- SENTRY_USE_SSL: 1
- SENTRY_SERVER_EMAIL: {{ args['email_from'] }}
- SENTRY_FILESTORE_DIR: /var/lib/sentry/files
- ports:
- 80
- port_bindings:
- {{ container['app_port'] }}:9000
{% endfor %}
# -------------------------------------------------------------
# Services containers
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{% for service in ['worker', 'cron'] %}
{% for instance, container in containers['sentry_' + service].items() %}
{% set args = pillar['sentry_realms'][container['realm']] %}
{{ instance }}:
docker_container.running:
- detach: True
- interactive: True
- image: library/sentry
- binds: *binds
- links: *links
- environment: *env
- command: run {{ service }}
{% endfor %}
{% endfor %}
diff --git a/roles/paas-docker/containers/files/sentry/sentry.sh.jinja b/roles/paas-docker/salt/files/credential.sh
old mode 100644
new mode 100755
similarity index 57%
copy from roles/paas-docker/containers/files/sentry/sentry.sh.jinja
copy to roles/paas-docker/salt/files/credential.sh
index f6c24f0..2c7c51c
--- a/roles/paas-docker/containers/files/sentry/sentry.sh.jinja
+++ b/roles/paas-docker/salt/files/credential.sh
@@ -1,26 +1,22 @@
#!/bin/sh
-
# -------------------------------------------------------------
# PaaS Docker
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Project: Nasqueron
-# Created: 2018-11-10
# License: Trivial work, not eligible to copyright
-# Description: Wrapper for sentry command (local instance)
-# Source file: roles/paas-docker/containers/files/sentry/sentry.sh.jinja
+# Source file: roles/paas-docker/salt/files/credential.sh
# -------------------------------------------------------------
#
# <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>
-SECRET_KEY=$(zr getcredentials {{ credential_id }} token)
+if [ "$#" -eq 0 ]; then
+ echo "Usage: $0 <credential key>" 1>&2;
+ exit 1
+fi
-docker run -it --rm \
- -e SENTRY_SECRET_KEY=$SECRET_KEY \
- --link {{ links.postgresql }}:postgres \
- --link {{ links.redis }}:redis \
- sentry "$@"
+sudo salt-call credentials.get_password "$1" --out=json | jq .local
diff --git a/roles/paas-docker/salt/init.sls b/roles/paas-docker/salt/init.sls
index 2bd9723..009082c 100644
--- a/roles/paas-docker/salt/init.sls
+++ b/roles/paas-docker/salt/init.sls
@@ -1,22 +1,31 @@
# -------------------------------------------------------------
# Salt — Salt configuration
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Project: Nasqueron
# Created: 2018-03-10
# License: Trivial work, not eligible to copyright
# -------------------------------------------------------------
-{% from "map.jinja" import packages_prefixes with context %}
+{% from "map.jinja" import dirs, packages_prefixes with context %}
# -------------------------------------------------------------
# Dependencies for Docker Salt minions
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
required_python_packages_for_docker_and_salt:
pkg.installed:
- name: {{ packages_prefixes.python3 }}pip
pip.installed:
- name: docker
- bin_env: /usr/bin/pip3
- require:
- pkg: required_python_packages_for_docker_and_salt
+
+# -------------------------------------------------------------
+# Wrapper to fetch a credential
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+{{ dirs.bin }}/credential:
+ file.managed:
+ - source: salt://roles/paas-docker/salt/files/credential.sh
+ - mode: 755

File Metadata

Mime Type
text/x-diff
Expires
Sun, Nov 24, 19:43 (2 h, 47 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2254979
Default Alt Text
(14 KB)

Event Timeline