add rds_pg feature
This commit is contained in:
parent
12ef56ce68
commit
687862d34b
5 changed files with 136 additions and 5 deletions
40
README.md
40
README.md
|
@ -168,6 +168,46 @@ For bash based builds we support often used script-parts as predefined functions
|
||||||
|
|
||||||
A full working example: [doc/example/50_docker_module](doc/example/50_docker_module)
|
A full working example: [doc/example/50_docker_module](doc/example/50_docker_module)
|
||||||
|
|
||||||
|
## Feature AwsRdsPgMixin
|
||||||
|
|
||||||
|
The AwsRdsPgMixin provides
|
||||||
|
* execute_pg_rds_sql - function will optionally resolve dns-c-names for trusted ssl-handshakes
|
||||||
|
* alter_db_user_password
|
||||||
|
* add_new_user
|
||||||
|
* deactivate_user
|
||||||
|
|
||||||
|
the build.py file content:
|
||||||
|
```
|
||||||
|
class MyBuild(..., AwsRdsPgMixin):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@init
|
||||||
|
def initialize(project):
|
||||||
|
project.build_depends_on('ddadevops>=0.8.0')
|
||||||
|
|
||||||
|
...
|
||||||
|
config = add_aws_rds_pg_mixin_config(config,
|
||||||
|
stage + "-db.bcsimport.kauf." + account_name + ".breuni.de",
|
||||||
|
"kauf_bcsimport",
|
||||||
|
rds_resolve_dns=True,)
|
||||||
|
build = MyBuild(project, config)
|
||||||
|
build.initialize_build_dir()
|
||||||
|
|
||||||
|
@task
|
||||||
|
def rotate_credentials_in(project):
|
||||||
|
build = get_devops_build(project)
|
||||||
|
build.alter_db_user_password('/postgres/support')
|
||||||
|
build.alter_db_user_password('/postgres/superuser')
|
||||||
|
build.add_new_user('/postgres/superuser', '/postgres/app', 'pg_group_role')
|
||||||
|
|
||||||
|
|
||||||
|
@task
|
||||||
|
def rotate_credentials_out(project):
|
||||||
|
build = get_devops_build(project)
|
||||||
|
build.deactivate_user('/postgres/superuser', 'old_user_name')
|
||||||
|
```
|
||||||
|
|
||||||
# Releasing and updating
|
# Releasing and updating
|
||||||
## Publish snapshot
|
## Publish snapshot
|
||||||
|
|
||||||
|
|
2
build.py
2
build.py
|
@ -28,7 +28,7 @@ use_plugin("python.distutils")
|
||||||
default_task = "publish"
|
default_task = "publish"
|
||||||
|
|
||||||
name = "ddadevops"
|
name = "ddadevops"
|
||||||
version = "0.7.2.dev"
|
version = "0.8.0"
|
||||||
summary = "tools to support builds combining gopass, terraform, dda-pallet, aws & hetzner-cloud"
|
summary = "tools to support builds combining gopass, terraform, dda-pallet, aws & hetzner-cloud"
|
||||||
description = __doc__
|
description = __doc__
|
||||||
authors = [Author("meissa GmbH", "buero@meissa-gmbh.de")]
|
authors = [Author("meissa GmbH", "buero@meissa-gmbh.de")]
|
||||||
|
|
|
@ -11,6 +11,7 @@ from .devops_docker_build import DevopsDockerBuild, create_devops_docker_build_c
|
||||||
from .hetzner_mixin import HetznerMixin, add_hetzner_mixin_config
|
from .hetzner_mixin import HetznerMixin, add_hetzner_mixin_config
|
||||||
from .aws_backend_properties_mixin import AwsBackendPropertiesMixin, add_aws_backend_properties_mixin_config
|
from .aws_backend_properties_mixin import AwsBackendPropertiesMixin, add_aws_backend_properties_mixin_config
|
||||||
from .aws_mfa_mixin import AwsMfaMixin, add_aws_mfa_mixin_config
|
from .aws_mfa_mixin import AwsMfaMixin, add_aws_mfa_mixin_config
|
||||||
|
from .aws_rds_pg_mixin import AwsRdsPgMixin, add_aws_rds_pg_mixin_config
|
||||||
from .dda_tenant_mixin import DdaTenantMixin, add_dda_tenant_mixin_config
|
from .dda_tenant_mixin import DdaTenantMixin, add_dda_tenant_mixin_config
|
||||||
from .dda_simple_mixin import DdaSimpleMixin, add_dda_simple_mixin_config
|
from .dda_simple_mixin import DdaSimpleMixin, add_dda_simple_mixin_config
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
from python_terraform import *
|
from python_terraform import *
|
||||||
from boto3 import *
|
from boto3 import *
|
||||||
from .credential import gopass_credential_from_env_path
|
|
||||||
from .python_util import execute
|
from .python_util import execute
|
||||||
from .aws_backend_properties_mixin import AwsBackendPropertiesMixin
|
from .aws_backend_properties_mixin import AwsBackendPropertiesMixin
|
||||||
|
|
||||||
|
|
||||||
def add_aws_mfa_mixin_config(config, account_id, region,
|
def add_aws_mfa_mixin_config(config, account_id, region,
|
||||||
mfa_role='developer', mfa_account_prefix='', mfa_login_account_suffix='main'):
|
mfa_role='developer', mfa_account_prefix='',
|
||||||
|
mfa_login_account_suffix='main'):
|
||||||
config.update({'AwsMfaMixin':
|
config.update({'AwsMfaMixin':
|
||||||
{'account_id': account_id,
|
{'account_id': account_id,
|
||||||
'region': region,
|
'region': region,
|
||||||
'mfa_role': mfa_role,
|
'mfa_role': mfa_role,
|
||||||
'mfa_account_prefix': mfa_account_prefix,
|
'mfa_account_prefix': mfa_account_prefix,
|
||||||
'mfa_login_account_suffix': mfa_login_account_suffix}})
|
'mfa_login_account_suffix': mfa_login_account_suffix}})
|
||||||
|
@ -38,7 +38,7 @@ class AwsMfaMixin(AwsBackendPropertiesMixin):
|
||||||
'mfa_account_prefix': self.mfa_account_prefix,
|
'mfa_account_prefix': self.mfa_account_prefix,
|
||||||
'mfa_login_account_suffix': self.mfa_login_account_suffix})
|
'mfa_login_account_suffix': self.mfa_login_account_suffix})
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def get_username_from_account(self, p_account_name):
|
def get_username_from_account(self, p_account_name):
|
||||||
login_id = execute('cat ~/.aws/accounts | grep -A 2 "\[' + p_account_name +
|
login_id = execute('cat ~/.aws/accounts | grep -A 2 "\[' + p_account_name +
|
||||||
'\]" | grep username | awk -F= \'{print $2}\'', shell=True)
|
'\]" | grep username | awk -F= \'{print $2}\'', shell=True)
|
||||||
|
|
90
src/main/python/ddadevops/aws_rds_pg_mixin.py
Normal file
90
src/main/python/ddadevops/aws_rds_pg_mixin.py
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
from .python_util import execute
|
||||||
|
from .credential import gopass_password_from_path, gopass_field_from_path
|
||||||
|
from .devops_build import DevopsBuild
|
||||||
|
|
||||||
|
|
||||||
|
def add_aws_rds_pg_mixin_config(config, rds_host_name, db_name,
|
||||||
|
rds_resolve_dns=False,
|
||||||
|
db_port='5432'):
|
||||||
|
config.update({'AwsRdsPgMixin':
|
||||||
|
{'rds_host_name': rds_host_name,
|
||||||
|
'db_name': db_name,
|
||||||
|
'rds_resolve_dns': rds_resolve_dns,
|
||||||
|
'db_port': db_port,
|
||||||
|
}})
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
class AwsRdsPgMixin(DevopsBuild):
|
||||||
|
|
||||||
|
def __init__(self, project, config):
|
||||||
|
super().__init__(project, config)
|
||||||
|
aws_rds_pg_mixin_config = config['AwsRdsPgMixin']
|
||||||
|
self.rds_host_name = aws_rds_pg_mixin_config['rds_host_name']
|
||||||
|
self.rds_resolve_dns = aws_rds_pg_mixin_config['rds_resolve_dns']
|
||||||
|
self.db_name = aws_rds_pg_mixin_config['db_name']
|
||||||
|
self.db_port = aws_rds_pg_mixin_config['db_port']
|
||||||
|
|
||||||
|
def execute_pg_rds_sql(self, user, password, sql):
|
||||||
|
if self.rds_resolve_dns:
|
||||||
|
host_cmd = "dig " + self.rds_host_name + " +short | head -n1"
|
||||||
|
host = execute(host_cmd, shell=True)
|
||||||
|
else:
|
||||||
|
host = self.rds_host_name
|
||||||
|
|
||||||
|
cmd = "PGUSER=" + user + " PGPASSWORD=" + password + \
|
||||||
|
" psql --dbname=" + self.db_name + " --host=" + host + " --port=" + self.db_port + \
|
||||||
|
" --set=sslmode=require -Atc \"" + sql + "\""
|
||||||
|
result = execute(cmd, shell=True)
|
||||||
|
print("PSQL: ", host, result.rstrip())
|
||||||
|
return result
|
||||||
|
|
||||||
|
def alter_db_user_password(self, gopass_path):
|
||||||
|
user_name = gopass_field_from_path(
|
||||||
|
self.gopass_stage() + gopass_path, 'user')
|
||||||
|
user_old_password = gopass_field_from_path(
|
||||||
|
self.gopass_stage() + gopass_path, 'old-password')
|
||||||
|
user_new_password = gopass_password_from_path(
|
||||||
|
self.gopass_stage() + gopass_path)
|
||||||
|
|
||||||
|
self.execute_pg_rds_sql(user_name, user_old_password,
|
||||||
|
"ALTER ROLE " + user_name + " WITH PASSWORD '" + user_new_password + "';")
|
||||||
|
print("changed password:", self.gopass_stage(), ',', user_name)
|
||||||
|
|
||||||
|
def add_new_user(self, gopass_path_superuser, gopass_path_new_user, group_role):
|
||||||
|
superuser_name = gopass_field_from_path(
|
||||||
|
self.gopass_stage() + gopass_path_superuser, 'user')
|
||||||
|
superuser_password = gopass_password_from_path(
|
||||||
|
self.gopass_stage() + gopass_path_superuser)
|
||||||
|
new_user_name = gopass_field_from_path(
|
||||||
|
self.gopass_stage() + gopass_path_new_user, 'user')
|
||||||
|
new_user_password = gopass_password_from_path(
|
||||||
|
self.gopass_stage() + gopass_path_new_user)
|
||||||
|
|
||||||
|
self.execute_pg_rds_sql(superuser_name, superuser_password,
|
||||||
|
"CREATE ROLE " + new_user_name + " WITH LOGIN INHERIT PASSWORD '" + new_user_password + "';" +
|
||||||
|
"GRANT " + group_role + " TO " + new_user_name + ";")
|
||||||
|
print("created user:", self.gopass_stage(), ',', new_user_name)
|
||||||
|
|
||||||
|
def deactivate_user(self, gopass_path_superuser, to_remove_user_name):
|
||||||
|
superuser_name = gopass_field_from_path(
|
||||||
|
self.gopass_stage() + gopass_path_superuser, 'user')
|
||||||
|
superuser_password = gopass_password_from_path(
|
||||||
|
self.gopass_stage() + gopass_path_superuser)
|
||||||
|
|
||||||
|
owned_by_wrong_user = self.execute_pg_rds_sql(superuser_name, superuser_password,
|
||||||
|
"SELECT count(*) FROM pg_class c, pg_user u WHERE c.relowner = u.usesysid " +
|
||||||
|
"and u.usename='" + to_remove_user_name + "';")
|
||||||
|
if int(owned_by_wrong_user) > 0:
|
||||||
|
raise AssertionError(
|
||||||
|
"There are still objects owned by the user to be deleted.")
|
||||||
|
|
||||||
|
connections = self.execute_pg_rds_sql(superuser_name, superuser_password,
|
||||||
|
"SELECT count(*) FROM pg_stat_activity WHERE application_name = " +
|
||||||
|
"'PostgreSQL JDBC Driver' AND usename = '" + to_remove_user_name + "';")
|
||||||
|
if int(connections) > 0:
|
||||||
|
raise AssertionError("User is still connected.")
|
||||||
|
|
||||||
|
self.execute_pg_rds_sql(superuser_name, superuser_password,
|
||||||
|
"ALTER ROLE " + to_remove_user_name + " WITH NOLOGIN NOCREATEROLE;")
|
||||||
|
print('deactivated user:', to_remove_user_name)
|
Loading…
Reference in a new issue