Page Menu
Home
DevCentral
Search
Configure Global Search
Log In
Files
F3766746
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
14 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
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)
Attached To
Mode
rOPS Nasqueron Operations
Attached
Detach File
Event Timeline
Log In to Comment