CYBER-0 changes to secret management from stored config to individual variables
This commit is contained in:
@@ -18,6 +18,10 @@ GitLab runs this methodology when `RUN_ANSIBLE=true`. For local use from the rep
|
||||
ANSIBLE_VARS_FILE=/secure/vars.yml ./methodologies/ansible/run.sh --limit linux_vms
|
||||
```
|
||||
|
||||
In CI, `scripts/build-secrets.py` assembles individual GitLab variables
|
||||
(`ANSIBLE_USER`, `ANSIBLE_PASSWORD`, ...) into that vars file, so a committed
|
||||
secrets file is never required.
|
||||
|
||||
Build the image with:
|
||||
|
||||
```bash
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Assemble individual GitLab CI/CD variables into a temporary Ansible vars file.
|
||||
|
||||
Reads a fixed allowlist of environment variables (injected by GitLab from
|
||||
masked CI/CD variables), maps them to their Ansible names, and writes them to a
|
||||
YAML vars file for ``--extra-vars @<file>``. An optional private key is written
|
||||
to a separate 0600 file for ``--private-key``.
|
||||
|
||||
Values never appear on the process command line or in this script's output.
|
||||
Only the allowlisted variable names are consumed; everything else is ignored.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import stat
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
|
||||
# GitLab variable name -> Ansible variable name. Extend this mapping when a new
|
||||
# connection variable needs to be supplied per environment.
|
||||
VAR_MAP = {
|
||||
"ANSIBLE_USER": "ansible_user",
|
||||
"ANSIBLE_PASSWORD": "ansible_password",
|
||||
"ANSIBLE_BECOME_PASSWORD": "ansible_become_password",
|
||||
"VCENTER_HOSTNAME": "vcenter_hostname",
|
||||
"VCENTER_USERNAME": "vcenter_username",
|
||||
"VCENTER_PASSWORD": "vcenter_password",
|
||||
}
|
||||
|
||||
PRIVATE_KEY_ENV = "ANSIBLE_PRIVATE_KEY"
|
||||
VARS_FILENAME = "ansible-vars.yml"
|
||||
KEY_FILENAME = "ansible-private-key"
|
||||
|
||||
|
||||
def write_secret_file(path: Path, data: bytes) -> None:
|
||||
path.write_bytes(data)
|
||||
path.chmod(stat.S_IRUSR | stat.S_IWUSR)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"--out-dir",
|
||||
type=Path,
|
||||
required=True,
|
||||
help="Directory for the generated vars file and private key",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
args.out_dir.mkdir(parents=True, exist_ok=True)
|
||||
os.chmod(args.out_dir, stat.S_IRWXU)
|
||||
|
||||
variables = {
|
||||
ansible_name: os.environ[gitlab_name]
|
||||
for gitlab_name, ansible_name in VAR_MAP.items()
|
||||
if os.environ.get(gitlab_name)
|
||||
}
|
||||
|
||||
private_key = os.environ.get(PRIVATE_KEY_ENV)
|
||||
if not variables and not private_key:
|
||||
raise SystemExit(
|
||||
"no Ansible secrets were provided. Define at least one of "
|
||||
+ ", ".join(VAR_MAP)
|
||||
+ f" or {PRIVATE_KEY_ENV} as an environment-scoped CI/CD variable."
|
||||
)
|
||||
|
||||
vars_file = args.out_dir / VARS_FILENAME
|
||||
write_secret_file(
|
||||
vars_file,
|
||||
yaml.safe_dump(variables, sort_keys=True, default_flow_style=False).encode(
|
||||
"utf-8"
|
||||
),
|
||||
)
|
||||
print(f"Wrote Ansible variables to {vars_file}")
|
||||
|
||||
if private_key:
|
||||
key_data = (
|
||||
Path(private_key).read_bytes()
|
||||
if os.path.isfile(private_key)
|
||||
else private_key.encode("utf-8")
|
||||
)
|
||||
key_file = args.out_dir / KEY_FILENAME
|
||||
write_secret_file(key_file, key_data)
|
||||
print(f"Wrote private key to {key_file}")
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user