nginx renewal fixes, oauth mastodon setup, secret restructure, etc

single-dockerfile
Trammell Hudson 2 years ago
parent 317d6b0e3a
commit 628f37fa37
  1. 1
      env.production
  2. 30
      hedgedoc/setup
  3. 38
      keycloak/client-delete
  4. 6
      keycloak/docker-compose.yaml
  5. 1
      keycloak/env.production
  6. 1
      keycloak/setup
  7. 4
      mastodon/docker-compose.yaml
  8. 6
      mastodon/env.production
  9. 47
      mastodon/setup
  10. 7
      nextcloud/docker-compose.yaml
  11. 2
      nextcloud/env.production
  12. 71
      nextcloud/setup
  13. 21
      nginx/certbot-renew
  14. 43
      nginx/setup

@ -5,3 +5,4 @@ KEYCLOAK_HOSTNAME=login.hackerspace.zone
HEDGEDOC_HOSTNAME=docs.hackerspace.zone HEDGEDOC_HOSTNAME=docs.hackerspace.zone
MASTODON_HOSTNAME=social.hackerspace.zone MASTODON_HOSTNAME=social.hackerspace.zone
NEXTCLOUD_HOSTNAME=cloud.hackerspace.zone NEXTCLOUD_HOSTNAME=cloud.hackerspace.zone
GRAFANA_HOSTNAME=dashboard.hackerspace.zone

@ -3,20 +3,24 @@ die() { echo >&2 "$@" ; exit 1 ; }
DIRNAME="$(dirname $0)" DIRNAME="$(dirname $0)"
cd "$DIRNAME" cd "$DIRNAME"
[ -r env.production ] && source env.production source ../env.production || die "no top levle env?"
[ -r ../env.production ] && source ../env.production source env.production || die "no local env?"
cd ../keycloak docker-compose down
source env.production # regenerate the client secrets
CLIENT_SECRET="$(openssl rand -hex 20)"
SESSION_SECRET="$(openssl rand -hex 20)"
sudo docker-compose exec -T keycloak \ rm -f env.secrets
/opt/keycloak/bin/kcadm.sh \ cat <<EOF > env.secrets
create clients \ CMD_OAUTH2_CLIENT_SECRET=$CLIENT_SECRET
--user admin \ CMD_SESSION_SECRET=$SESSION_SECRET
--password "$KEYCLOAK_ADMIN_PASSWORD" \ EOF
-r "$REALM" \
-f - <<EOF || die "unable to create hedgedoc client" ../keycloak/client-delete hedgedoc
../keycloak/client-create <<EOF || die "unable to create hedgedoc client"
{ {
"clientId": "hedgedoc", "clientId": "hedgedoc",
"rootUrl": "https://$HEDGEDOC_HOSTNAME", "rootUrl": "https://$HEDGEDOC_HOSTNAME",
@ -24,7 +28,7 @@ sudo docker-compose exec -T keycloak \
"redirectUris": [ "https://$HEDGEDOC_HOSTNAME/*" ], "redirectUris": [ "https://$HEDGEDOC_HOSTNAME/*" ],
"webOrigins": [ "https://$HEDGEDOC_HOSTNAME" ], "webOrigins": [ "https://$HEDGEDOC_HOSTNAME" ],
"clientAuthenticatorType": "client-secret", "clientAuthenticatorType": "client-secret",
"secret": "$CMD_OAUTH2_CLIENT_SECRET", "secret": "$CLIENT_SECRET",
"defaultClientScopes": [ "defaultClientScopes": [
"web-origins", "web-origins",
"acr", "acr",
@ -42,3 +46,5 @@ sudo docker-compose exec -T keycloak \
} }
EOF EOF
docker-compose up -d || die "unable to start container"

@ -0,0 +1,38 @@
#!/bin/bash
die() { echo >&2 "$@" ; exit 1 ; }
DIRNAME="$(dirname $0)"
cd "$DIRNAME"
source ../env.production || die "no top levle env?"
source env.production || die "no local env?"
source env.secrets || die "no local secrets?"
# try to get the clients by name
CLIENT_NAME="$1"
if [ -z "$CLIENT_NAME" ]; then
die "usage: $0 clientName"
fi
CLIENT_ID="$(docker-compose exec -T keycloak \
/opt/keycloak/bin/kcadm.sh \
get clients \
--server http://localhost:8080/ \
--user admin \
--password "$KEYCLOAK_ADMIN_PASSWORD" \
-r "$REALM" \
| jq -r ".[] | select( .clientId == \"$CLIENT_NAME\" ).id")"
if [ -z "$CLIENT_ID" ]; then
die "$CLIENT_NAME: no such client"
fi
echo "$0: $CLIENT_NAME = $CLIENT_ID"
docker-compose exec -T keycloak \
/opt/keycloak/bin/kcadm.sh \
delete "clients/$CLIENT_ID" \
--server http://localhost:8080/ \
--user admin \
--password "$KEYCLOAK_ADMIN_PASSWORD" \
-r "$REALM" \
|| die "$CLIENT_NAME($CLIENT_ID): unable to remove"

@ -17,10 +17,12 @@ services:
keycloak: keycloak:
image: quay.io/keycloak/keycloak:18.0.0 image: quay.io/keycloak/keycloak:18.0.0
entrypoint: /opt/keycloak/bin/kc.sh start-dev --proxy=edge entrypoint: /opt/keycloak/bin/kc.sh start --hostname="$${KEYCLOAK_HOSTNAME}" --proxy=edge
user: "0:0" # otherwise the persistent data directory is not writable
env_file: env_file:
- ../env.production - ../env.production
- env.production - env.production
- env.secrets
environment: environment:
DB_VENDOR: MYSQL DB_VENDOR: MYSQL
DB_ADDR: mysql DB_ADDR: mysql
@ -28,9 +30,11 @@ services:
DB_USER: keycloak DB_USER: keycloak
DB_PASSWORD: password DB_PASSWORD: password
KEYCLOAK_ADMIN: admin KEYCLOAK_ADMIN: admin
# KEYCLOAK_ADMIN_PASSWORD should be set in env.secrets
PROXY_ADDRESS_FORWARDING: 'true' PROXY_ADDRESS_FORWARDING: 'true'
volumes: volumes:
- ./data/certs:/etc/x509/https - ./data/certs:/etc/x509/https
- ./data/keycloak:/opt/keycloak/data
ports: ports:
- 8080:8080 - 8080:8080
depends_on: depends_on:

@ -1 +0,0 @@
KEYCLOAK_ADMIN_PASSWORD=abcd@1234!

@ -6,6 +6,7 @@ DIRNAME="$(dirname $0)"
cd "$DIRNAME" cd "$DIRNAME"
source ../env.production source ../env.production
source ./env.production source ./env.production
source ./env.secrets
info "logging into server" info "logging into server"
sudo docker-compose exec keycloak \ sudo docker-compose exec keycloak \

@ -46,7 +46,7 @@ services:
soft: -1 soft: -1
hard: -1 hard: -1
web: mastodon:
image: tootsuite/mastodon image: tootsuite/mastodon
restart: always restart: always
env_file: env_file:
@ -70,7 +70,6 @@ services:
- ./data/system:/mastodon/public/system - ./data/system:/mastodon/public/system
streaming: streaming:
build: .
image: tootsuite/mastodon image: tootsuite/mastodon
restart: always restart: always
env_file: env_file:
@ -91,7 +90,6 @@ services:
- redis - redis
sidekiq: sidekiq:
build: .
image: tootsuite/mastodon image: tootsuite/mastodon
restart: always restart: always
env_file: env_file:

@ -14,7 +14,7 @@
# ---------- # ----------
# This identifies your server and cannot be changed safely later # This identifies your server and cannot be changed safely later
# ---------- # ----------
LOCAL_DOMAIN=social.hackerspace.zone # LOCAL_DOMAIN is set in env.secrets
#WEB_DOMAIN=social.example.com #WEB_DOMAIN=social.example.com
# Redis # Redis
@ -77,12 +77,10 @@ OMNIAUTH_ONLY=true
# OIDC supported since https://github.com/mastodon/mastodon/pull/16221 # OIDC supported since https://github.com/mastodon/mastodon/pull/16221
OIDC_ENABLED=true OIDC_ENABLED=true
OIDC_PROMPT=Keycloak OIDC_PROMPT=Keycloak
OIDC_DISPLAY_NAME=hackerspace.zone
OIDC_ISSUER=https://login.hackerspace.zone/realms/hackerspace
OIDC_REDIRECT_URI=https://social.hackerspace.zone/auth/auth/openid_connect/callback
OIDC_DISCOVERY=true OIDC_DISCOVERY=true
OIDC_SCOPE=openid,profile OIDC_SCOPE=openid,profile
OIDC_UID_FIELD=preferred_username OIDC_UID_FIELD=preferred_username
OIDC_CLIENT_ID=mastodon OIDC_CLIENT_ID=mastodon
OIDC_SECURITY_ASSUME_EMAIL_IS_VERIFIED=true OIDC_SECURITY_ASSUME_EMAIL_IS_VERIFIED=true
# OIDC URLs are in env.secrets since they require env expansion
# OIDC_CLIENT_SECRET is in env.secrets # OIDC_CLIENT_SECRET is in env.secrets

@ -18,40 +18,39 @@ OTP_SECRET=000000
OIDC_CLIENT_SECRET=000000 OIDC_CLIENT_SECRET=000000
EOF EOF
# have to bring it all down before we touch the files
docker-compose down
if [ -z "$MASTODON_SKIP_DB_INIT" ]; then if [ -z "$MASTODON_SKIP_DB_INIT" ]; then
info "configuring mastodon" info "configuring mastodon"
sudo docker-compose run web \ sudo docker-compose run --rm mastodon \
rails db:setup \ rails db:setup \
|| die "unable to login" || die "unable to login"
fi fi
# now create the real secrets file OIDC_CLIENT_SECRET="$(openssl rand -hex 32)"
echo > env.secrets "# DO NOT CHECK IN"
# now create the real secrets file,
# along with some parameters that should be in the environment
cat <<EOF > env.secrets
# DO NOT CHECK IN
LOCAL_DOMAIN=$MASTODON_HOSTNAME
OIDC_DISPLAY_NAME=$REALM
OIDC_ISSUER=https://$KEYCLOAK_HOSTNAME/realms/$REALM
OIDC_REDIRECT_URI=https://$MASTODON_HOSTNAME/auth/auth/openid_connect/callback
OIDC_CLIENT_SECRET=$OIDC_CLIENT_SECRET
SECRET_KEY_BASE=$(openssl rand -hex 32)
OTP_SECRET=$(openssl rand -hex 32)
EOF
sudo docker-compose run web \ docker-compose run --rm mastodon \
rails mastodon:webpush:generate_vapid_key \ rails mastodon:webpush:generate_vapid_key \
>> env.secrets \ >> env.secrets \
|| die "unable to generate vapid key" || die "unable to generate vapid key"
echo "SECRET_KEY_BASE=$(openssl rand -hex 32)" >> env.secrets
echo "OTP_SECRET=$(openssl rand -hex 32)" >> env.secrets
CLIENT_SECRET="$(openssl rand -hex 32)" ../keycloak/client-delete mastodon
echo "OIDC_CLIENT_SECRET=$CLIENT_SECRET" >> env.secrets ../keycloak/client-create <<EOF || die "Unable to create keycloak client"
# create the keycloak side of the secret
cd ../keycloak
source env.production
sudo docker-compose exec -T keycloak \
/opt/keycloak/bin/kcadm.sh \
create clients \
--server http://localhost:8080/ \
--user admin \
--password "$KEYCLOAK_ADMIN_PASSWORD" \
--realm master \
-r "$REALM" \
-f - <<EOF || die "unable to create client id"
{ {
"clientId": "mastodon", "clientId": "mastodon",
"rootUrl": "https://$MASTODON_HOSTNAME/", "rootUrl": "https://$MASTODON_HOSTNAME/",
@ -59,6 +58,8 @@ sudo docker-compose exec -T keycloak \
"redirectUris": [ "https://$MASTODON_HOSTNAME/*" ], "redirectUris": [ "https://$MASTODON_HOSTNAME/*" ],
"webOrigins": [ "https://$MASTODON_HOSTNAME" ], "webOrigins": [ "https://$MASTODON_HOSTNAME" ],
"clientAuthenticatorType": "client-secret", "clientAuthenticatorType": "client-secret",
"secret": "$CLIENT_SECRET" "secret": "$OIDC_CLIENT_SECRET"
} }
EOF EOF
docker-compose up -d

@ -19,15 +19,16 @@ services:
env_file: env_file:
- ../env.production - ../env.production
- env.production - env.production
- env.secrets
environment: environment:
POSTGRES_HOST: database POSTGRES_HOST: database
POSTGRES_DB: nextcloud POSTGRES_DB: nextcloud
POSTGRES_USER: nextcloud POSTGRES_USER: nextcloud
POSTGRES_PASSWORD: nextcloud POSTGRES_PASSWORD: nextcloud
#NEXTCLOUD_TRUSTED_DOMAINS: "${NEXTCLOUD_HOSTNAME}"
NEXTCLOUD_TRUSTED_DOMAINS: cloud.hackerspace.zone
NEXTCLOUD_ADMIN_USER: admin
OVERWRITEPROTOCOL: https OVERWRITEPROTOCOL: https
NEXTCLOUD_ADMIN_USER: admin
# NEXTCLOUD_ADMIN_PASSWORD in env.secrets
# NEXTCLOUD_TRUSTED_DOMAINS also set in env.secrets
volumes: volumes:
- ./data/nextcloud:/var/www/html - ./data/nextcloud:/var/www/html
depends_on: depends_on:

@ -1 +1 @@
NEXTCLOUD_ADMIN_PASSWORD=admin # non-secret nextcloud config

@ -3,23 +3,25 @@ die() { echo >&2 "$@" ; exit 1 ; }
DIRNAME="$(dirname $0)" DIRNAME="$(dirname $0)"
cd "$DIRNAME" cd "$DIRNAME"
[ -r env.production ] && source env.production source ../env.production || die "no top level env?"
[ -r ../env.production ] && source ../env.production source env.production || die "no local env?"
sudo docker-compose exec -u www-data -T nextcloud \ if [ ! -r "env.secrets" ]; then
./occ app:install sociallogin \ NEXTCLOUD_CLIENT_SECRET="$(openssl rand -hex 32)"
|| die "unable to install sociallogin app" NEXTCLOUD_ADMIN_PASSWORD="$(openssl rand -hex 4)"
sudo docker-compose exec -u www-data -T nextcloud \ echo "Generating secrets: admin password $NEXTCLOUD_ADMIN_PASSWORD"
./occ config:app:set sociallogin prevent_create_email_exists --value=1 \ cat <<EOF > env.secrets
|| die "unable to config sociallogin" # Do not check in!
NEXTCLOUD_ADMIN_PASSWORD=$NEXTCLOUD_ADMIN_PASSWORD
sudo docker-compose exec -u www-data -T nextcloud \ NEXTCLOUD_TRUSTED_DOMAINS=$NEXTCLOUD_HOSTNAME
./occ config:app:set sociallogin update_profile_on_login --value=1 \ NEXTCLOUD_CLIENT_SECRET=$NEXTCLOUD_CLIENT_SECRET
|| die "unable to config sociallogin" EOF
else
source env.secrets || die "no secret env?"
fi
BASE="https://$KEYCLOAK_HOSTNAME/realms/$REALM/protocol/openid-connect" BASE="https://$KEYCLOAK_HOSTNAME/realms/$REALM/protocol/openid-connect"
SECRET="$(openssl rand -hex 20)"
PROVIDER="$(jq -c . <<EOF PROVIDER="$(jq -c . <<EOF
{ {
"custom_oidc": [ "custom_oidc": [
@ -27,15 +29,15 @@ PROVIDER="$(jq -c . <<EOF
"name": "keycloak", "name": "keycloak",
"title": "Keycloak", "title": "Keycloak",
"clientId": "nextcloud", "clientId": "nextcloud",
"clientSecret": "$SECRET", "clientSecret": "$NEXTCLOUD_CLIENT_SECRET",
"authorizeUrl": "$BASE/auth", "authorizeUrl": "$BASE/auth",
"tokenUrl": "$BASE/token", "tokenUrl": "$BASE/token",
"userInfoUrl": "$BASE/userinfo", "userInfoUrl": "$BASE/userinfo",
"logoutUrl": "", "logoutUrl": "$BASE/logout",
"displayNameClaim": "",
"scope": "openid", "scope": "openid",
"groupsClaim": "roles", "groupsClaim": "roles",
"style": "keycloak", "style": "keycloak",
"displayNameClaim": "",
"defaultGroup": "" "defaultGroup": ""
} }
] ]
@ -43,26 +45,27 @@ PROVIDER="$(jq -c . <<EOF
EOF EOF
)" )"
sudo docker-compose exec -u www-data -T nextcloud \
./occ config:app:set \
sociallogin custom_providers \
--value="$PROVIDER" \
|| die "unable to set keycloak parameters"
docker-compose up -d || die "unable to bring up docker"
# wait for the nextcloud instance to be responsive
# TODO: how to find out if it is ready?
echo "SLEEPING..."
sleep 30
docker-compose exec -u www-data -T nextcloud bash -x <<EOF || die "unable to configure sociallogin"
./occ app:remove sociallogin || echo "not yet installed"
./occ app:install sociallogin || exit 1
./occ config:app:set sociallogin prevent_create_email_exists --value=1 || exit 1
./occ config:app:set sociallogin update_profile_on_login --value=1 || exit 1
./occ config:app:set sociallogin custom_providers --value='$PROVIDER' || exit 1
EOF
# create the keycloak side of the secret ../keycloak/client-delete 'nextcloud' || echo "client did not exist?"
cd ../keycloak
source env.production
sudo docker-compose exec -T keycloak \ ../keycloak/client-create << EOF || die "unable to create client id"
/opt/keycloak/bin/kcadm.sh \
create clients \
--server http://localhost:8080/ \
--user admin \
--password "$KEYCLOAK_ADMIN_PASSWORD" \
--realm master \
-r "$REALM" \
-f - <<EOF || die "unable to create client id"
{ {
"clientId": "nextcloud", "clientId": "nextcloud",
"rootUrl": "https://$NEXTCLOUD_HOSTNAME/", "rootUrl": "https://$NEXTCLOUD_HOSTNAME/",
@ -70,6 +73,6 @@ sudo docker-compose exec -T keycloak \
"redirectUris": [ "https://$NEXTCLOUD_HOSTNAME/*" ], "redirectUris": [ "https://$NEXTCLOUD_HOSTNAME/*" ],
"webOrigins": [ "https://$NEXTCLOUD_HOSTNAME" ], "webOrigins": [ "https://$NEXTCLOUD_HOSTNAME" ],
"clientAuthenticatorType": "client-secret", "clientAuthenticatorType": "client-secret",
"secret": "$SECRET" "secret": "$NEXTCLOUD_CLIENT_SECRET"
} }
EOF EOF

@ -1,23 +1,30 @@
#!/bin/bash #!/bin/bash
die() { echo >&2 "$@" ; exit 1 ; } die() { echo >&2 "$@" ; exit 1 ; }
DIRNAME="$(dirname $0)"
cd "$DIRNAME"
source ../env.production source ../env.production
source ./env.production source ./env.production
domain_args="-d $DOMAIN_NAME,$KEYCLOAK_HOSTNAME,$HEDGEDOC_HOSTNAME,$MASTODON_HOSTNAME,$NEXTCLOUD_HOSTNAME" domain_args="-d $DOMAIN_NAME,$KEYCLOAK_HOSTNAME,$HEDGEDOC_HOSTNAME,$MASTODON_HOSTNAME,$NEXTCLOUD_HOSTNAME,$GRAFANA_HOSTNAME"
rsa_key_size=2048 rsa_key_size=2048
set -x set -x
# move the old live directory away # move the temp live directory away if
rm -rf data/certbot/conf/live.old # this is the first time we've run anything here
mv data/certbot/conf/live data/certbot/conf/live.old if [ ! -d "data/certbot/conf/accounts" ]; then
echo "deleting temp keys"
rm -rf data/certbot/conf/live
fi
docker-compose run --rm certbot \ docker-compose run --rm certbot \
certonly --webroot -w /var/www/certbot \ certonly \
$staging_arg \ --webroot \
--webroot-path /var/www/certbot \
--email "admin@$DOMAIN_NAME" \ --email "admin@$DOMAIN_NAME" \
--rsa-key-size $rsa_key_size \ --rsa-key-size "$rsa_key_size" \
--agree-tos \ --agree-tos \
--no-eff-email \ --no-eff-email \
--force-renewal \ --force-renewal \

@ -1,28 +1,39 @@
#!/bin/bash #!/bin/bash
die() { echo >&2 "$@" ; exit 1 ; } die() { echo >&2 "$@" ; exit 1 ; }
ENV=env.production DIRNAME="$(dirname $0)"
if [ ! -r "$ENV" ]; then cd "$DIRNAME"
die "$ENV: not found?"
fi
source ../env.production source ../env.production || die "no top level env"
source env.production source env.production || die "no local env"
if [ -z "${DOMAIN_NAME}" ]; then if [ -z "${DOMAIN_NAME}" ]; then
die "DOMAIN_NAME not set" die "DOMAIN_NAME not set"
fi fi
docker-compose down
certdir="data/certbot/conf/live/${DOMAIN_NAME}" certdir="data/certbot/conf/live/${DOMAIN_NAME}"
mkdir -p "$certdir" || die "$certdir: unable to make" mkdir -p "$certdir" || die "$certdir: unable to make"
openssl req \ if [ ! -r "$certdir/privkey.pem" ]; then
-x509 \ openssl req \
-newkey rsa:2048 \ -x509 \
-keyout "$certdir/privkey.pem" \ -newkey rsa:2048 \
-out "$certdir/fullchain.pem" \ -keyout "$certdir/privkey.pem" \
-sha256 \ -out "$certdir/fullchain.pem" \
-nodes \ -sha256 \
-days 365 \ -nodes \
-subj "/CN=${DOMAIN_NAME}'" \ -days 365 \
|| die "$certdir/privkey.pem: unable to create temp key" -subj "/CN=${DOMAIN_NAME}'" \
|| die "$certdir/privkey.pem: unable to create temp key"
fi
docker-compose up -d || die "unable to bring up nginx"
echo "SLEEPING..."
sleep 10
./certbot-renew || die "unable to create certs"

Loading…
Cancel
Save