1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397
|
#!/bin/bash
# Creates a local PuppetDB sandbox directory
# Also creates a PostgreSQL sandbox inside of new PuppetDB sandbox in directory "pg"
# These sandboxes contain all PuppetDB and PostgreSQL configuration and data storage
# All flags are required
# Sandbox directory must not already exist
set -ueo pipefail
usage()
{
echo "Usage: $(basename $0) <-s|--sandbox> DIR --pgbin DIR --pgport PORT --bind-addr ADDR1 [--bind-addr ADDR2 ...]"
}
misuse()
{
usage 1>&2
exit 2
}
# Validate arguments
pdbbox=''
pgbin=''
pgport=''
bind_addr=()
while test $# -gt 0; do
case "$1" in
-s|--sandbox)
shift
test $# -gt 0 || misuse
pdbbox="$1"
shift
;;
--pgbin)
shift
test $# -gt 0 || misuse
pgbin="$1"
shift
;;
--pgport)
shift
test $# -gt 0 || misuse
pgport="$1"
shift
;;
--bind-addr)
shift
test $# -gt 0 || misuse
bind_addr+=("$1")
shift
;;
*)
misuse
esac
done
test "$pdbbox" || misuse
test "$pgbin" || misuse
test "$pgport" || misuse
test "$bind_addr" || misuse
bind_args=()
for addr in "${bind_addr[@]}"; do
bind_args+=(--bind-addr "$addr")
done
export PGBOX="$pdbbox/pg"
export PGUSER=postgres
mkdir "$pdbbox"
# Create PostgreSQL sandbox inside PuppetDB sandbox with pgbox
# Relies on the PGBOX environment variable to know where sandbox should go
pgbox init --pgbin "$pgbin" --port "$pgport" "${bind_args[@]}" -- -E UTF8 --locale=C
mkdir "$pdbbox/var"
tmp_dir="$(mktemp -d ./tmp-pdbbox-XXXXXX)"
trap "$(printf 'pgbox env pg_ctl stop; rm -rf %q' "$tmp_dir")" EXIT
# Start PostgreSQL server using new PostgreSQL sandbox
pgbox env pg_ctl start -w
# Create random PostgreSQL passwords
pg_passwd="$(cd "$PGBOX" && cat pass-admin)"
admin_passwd="$(dd if=/dev/urandom bs=1 count=32 | base64)"
passwd="$(dd if=/dev/urandom bs=1 count=32 | base64)"
migrator_passwd="$(dd if=/dev/urandom bs=1 count=32 | base64)"
read_passwd="$(dd if=/dev/urandom bs=1 count=32 | base64)"
# This code relies on echo being a function in bash, not a subprocess
# and "here documents" to prevent passwords from being visible in
# "ps", etc. We also assume that mktemp perms are always go-wrx,
# across all the relevant platforms.
# Save password to a file securely
save-passwd() {
test $# -eq 3
local passwd="$1" tmp_dir="$2" dest="$3"
# This code relies on echo being a function in bash, not a
# subprocess and "here documents" to prevent passwords from being
# visible in "ps", etc. We also assume that mktemp perms are
# always go-wrx, across all the relevant platforms.
tmp_pass="$(mktemp "$tmp_dir/tmp-save-pass-XXXXXX")"
chmod 0600 "$tmp_pass" # Not entirely safe, but expected to be redundant
echo -n "$passwd" > "$tmp_pass"
mv "$tmp_pass" "$dest"
}
# Set password for PostgreSQL user/role
set-passwd() {
test $# -eq 3
local role="$1" pwfile="$2" tmp_dir="$3" tmp_cmds
tmp_cmds="$(mktemp "$tmp_dir/set-pass-XXXXXX")"
chmod 0600 "$tmp_cmds" # Not entirely safe, but expected to be redundant
echo -n "alter role $role with password '" > "$tmp_cmds"
cat "$pwfile" >> "$tmp_cmds"
echo "';" >> "$tmp_cmds"
pgbox env psql -f "$tmp_cmds"
}
# Save newly created passwords to filesystem inside PuppetDB sandbox
save-passwd "$passwd" "$tmp_dir" "$pdbbox/test-pass"
save-passwd "$migrator_passwd" "$tmp_dir" "$pdbbox/test-pass-migrator"
save-passwd "$admin_passwd" "$tmp_dir" "$pdbbox/test-pass-admin"
save-passwd "$read_passwd" "$tmp_dir" "$pdbbox/test-pass-read"
# Create PostgreSQL password file
# https://www.postgresql.org/docs/14/libpq-pgpass.html
tmp_pass="$(mktemp "$tmp_dir/tmp-pass-XXXXXX")"
chmod 0600 "$tmp_pass" # Not entirely safe, but expected to be redundant
cat > "$tmp_pass" <<-EOF
# hostname:port:database:username:password
*:*:*:pdb_test:$passwd
*:*:*:pdb_test_migrator:$migrator_passwd
*:*:*:pdb_test_read:$read_passwd
*:*:*:pdb_test_admin:$admin_passwd
*:*:*:puppetdb:$passwd
*:*:*:puppetdb_read:$read_passwd
*:*:*:puppetdb_migrator:$migrator_passwd
*:*:*:postgres:$pg_passwd
EOF
mv "$tmp_pass" "$PGBOX/pgpass"
setup-roles()
{
local read_role="$1" write_role="$2" migrator_role="$3"
# The migrator needs the write role for migrations, so it can
# terminate any existing read and write role connections
# (transitively for the read role,via the write role's read role
# membership) after locking out new connections (by revoking the
# connect access that the migrator granted to the read and write
# roles).
pgbox env psql -c "grant $write_role to $migrator_role"
# The write role needs the pdb_test_read role for the partition gc
# bulldozer, so it can terminate any queries blocking the drop.
pgbox env psql -c "grant $read_role to $write_role"
}
# Create Postgres testing roles
pgbox env createuser -dERs pdb_test_admin
pgbox env createuser -DERS pdb_test
pgbox env createuser -DERS pdb_test_migrator
pgbox env createuser -DERS pdb_test_read
tmp_cmds="$(mktemp "$tmp_dir/tmp-cmds-XXXXXX")"
# Set passwords for Postgres testing roles
set-passwd pdb_test "$pdbbox/test-pass" "$tmp_dir"
set-passwd pdb_test_admin "$pdbbox/test-pass-admin" "$tmp_dir"
set-passwd pdb_test_migrator "$pdbbox/test-pass-migrator" "$tmp_dir"
set-passwd pdb_test_read "$pdbbox/test-pass-read" "$tmp_dir"
setup-roles pdb_test_read pdb_test pdb_test_migrator
# Creates a PuppetDB database. Loads necessary extensions, sets permissions
setup-pdb()
{
local dbname="$1"
pgbox env createdb -E UTF8 -O postgres "$dbname"
pgbox env psql "$dbname" <<ADMIN
create extension pg_trgm;
revoke create on schema public from public;
grant create on schema public to puppetdb;
-- Grant read to all future tables in public schema to the read-only user
alter default privileges for user puppetdb in schema public grant select on tables to puppetdb_read;
-- configure the migrator user
revoke connect on database $dbname from public;
grant connect on database $dbname to puppetdb_migrator with grant option;
ADMIN
pgbox env psql "$dbname" -U puppetdb_migrator <<MIGRATOR
grant connect on database $dbname to puppetdb;
grant connect on database $dbname to puppetdb_read;
MIGRATOR
}
# Create main PuppetDB postgres roles
pgbox env createuser -DRS puppetdb
pgbox env createuser -DRS puppetdb_read
pgbox env createuser -DRS puppetdb_migrator
setup-roles puppetdb_read puppetdb puppetdb_migrator
# Set passwords for main PuppetDB postgres roles
set-passwd puppetdb "$pdbbox/test-pass" "$tmp_dir"
set-passwd puppetdb_read "$pdbbox/test-pass-read" "$tmp_dir"
set-passwd puppetdb_migrator "$pdbbox/test-pass-migrator" "$tmp_dir"
setup-pdb puppetdb
setup-pdb puppetdb2
mkdir "$pdbbox/ssl"
(cd "$pdbbox/ssl"
openssl genrsa -out ca.key.pem 2048
openssl req -x509 -new -nodes -days 1000 -key ca.key.pem -out ca.crt.pem \
-subj "/C=US/ST=confusion/L=unknown/O=none/CN=pdbca.localhost"
openssl genrsa -out pdb.key.pem 2048
openssl req -new -key pdb.key.pem -out pdb.csr.pem \
-subj "/C=US/ST=confusion/L=unknown/O=none/CN=pdb.localhost"
openssl x509 -req -in pdb.csr.pem -days 1000 \
-CA ca.crt.pem -CAkey ca.key.pem -CAcreateserial \
-out pdb.crt.pem)
abs_pdbbox="$(cd "$pdbbox" && pwd)"
# Create logging config file
cat > "$pdbbox/logback.xml" <<EOF
<configuration scan="true">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d %-5p [%thread] [%c{2}] %m%n</pattern>
</encoder>
</appender>
<!-- Silence particularly noisy packages -->
<logger name="org.apache.activemq" level="warn"/>
<logger name="org.apache.activemq.store.kahadb.MessageDatabase"
level="info"/>
<logger name="org.springframework.jms.connection" level="warn"/>
<root level="info">
<appender-ref ref="STDOUT" />
</root>
</configuration>
EOF
host=${bind_addr[${#bind_addr[@]}-1]}
# Create PuppetDB main config file
confdir="$pdbbox/conf.d"
mkdir "$confdir"
cat > "$confdir/pdb.ini" <<EOF
[global]
vardir = $abs_pdbbox/var
logging-config = $abs_pdbbox/logback.xml
[database]
subname = //$host:$pgport/puppetdb
username = puppetdb
password = $passwd
migrator-username = puppetdb_migrator
migrator-password = $migrator_passwd
[read-database]
subname = //$host:$pgport/puppetdb
username = puppetdb_read
password = $read_passwd
[nrepl]
enabled = false
[jetty]
port = 8080
ssl-port = 8081
ssl-ca-cert = test-resources/ca.pem
ssl-cert = test-resources/localhost.pem
ssl-key = test-resources/localhost.key
client-auth = want
[puppetdb]
disable-update-checking = true
EOF
# Create PuppetDB auth config file
cat > "$confdir/auth.conf" <<EOF
authorization: {
version: 1
rules: [
{
# Allow unauthenticated access to the status service endpoint
match-request: {
path: "/status/v1/services"
type: path
method: get
}
allow-unauthenticated: true
sort-order: 500
name: "puppetlabs status service - full"
},
{
match-request: {
path: "/status/v1/simple"
type: path
method: get
}
allow-unauthenticated: true
sort-order: 500
name: "puppetlabs status service - simple"
},
{
# Allow nodes to access the metrics service
# for puppetdb, the metrics service is the only
# service using the authentication service
match-request: {
path: "/metrics"
type: path
method: [get, post]
}
allow: "*"
sort-order: 500
name: "puppetlabs puppetdb metrics"
},
{
# Deny everything else. This ACL is not strictly
# necessary, but illustrates the default policy
match-request: {
path: "/"
type: path
}
deny: "*"
sort-order: 999
name: "puppetlabs puppetdb deny all"
}
]
}
EOF
# do some tuning on PostgreSQL
# generally accepted configuration parameters:
# Set the shared_buffers to be 1/4 of the amount of RAM
case "$OSTYPE" in
darwin*)
total_ram_bytes=$(sysctl hw.memsize | awk '{ print $2 }')
total_ram_kb=$(($total_ram_bytes / 1024)) ;;
*)
total_ram_kb=$(grep MemTotal: /proc/meminfo | awk '{ print $2 }') ;;
esac
total_ram_mb=$(($total_ram_kb / 1024))
# these values come from pgtune and the postgresql tuning wiki
# shared buffers should max out at 1 GB of RAM
shared_buffers=$(($total_ram_mb / 4))
if (( $shared_buffers > 1024 )); then
shared_buffers=1024
fi
# do not let this be less than the default value
maintenance_work_mem=$(($total_ram_mb / 15))
if (( $maintenance_work_mem < 64 )); then
maintenance_work_mem=64
fi
# Append to PostgreSQL config
cat >> "$pdbbox/pg/data/postgresql.conf" <<EOF
# tuning parameters
shared_buffers = ${shared_buffers}MB
default_statistics_target = 50
maintenance_work_mem = ${maintenance_work_mem}MB
# aim to finish by the time 90% of the next checkpoint is here
# this will spread out writes
checkpoint_completion_target = 0.9
work_mem = 96MB
# Increasing wal_buffers from its tiny default of a small number of kilobytes
# is helpful for write-heavy systems, 16MB is the effective upper bound
wal_buffers = 8MB
EOF
# Make empty pdbbox file to signify that this directory is a PuppetDB sandbox
touch "$pdbbox/pdbbox"
|