Skip to content

Commit d8933fb

Browse files
authored
feat: (ESPSTUDIO-8123 ESPSTUDIO-8124)
feat: (ESPSTUDIO-8123 ESPSTUDIO-8124) Deployment script - support Viya Deployment script - support Keycloak Update the deployment script to set up oath on viya and keycloak put in separate code paths to set the appropriate oauth and URLs in the ini file add removal scripts these may come in handy for testing.
1 parent e4dd2da commit d8933fb

9 files changed

+497
-89
lines changed

install/config-map.yaml

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ metadata:
55
data:
66
grafana-uaa.ini: |
77
[server]
8-
domain = TEMPLATE_ESP_DOMAIN
9-
root_url = https://TEMPLATE_ESP_DOMAIN/grafana/
8+
domain = TEMPLATE_GRAFANA_DOMAIN
9+
root_url = https://TEMPLATE_GRAFANA_DOMAIN/grafana/
1010
serve_from_sub_path = true
1111
1212
[plugins]
@@ -16,6 +16,7 @@ data:
1616
accessTokenExpirationCheck = true
1717
1818
[security]
19+
disable_initial_admin_creation = true
1920
cookie_secure = true
2021
cookie_samesite = lax
2122
@@ -25,12 +26,12 @@ data:
2526
2627
[auth]
2728
disable_login_form = true
28-
signout_redirect_url = https://TEMPLATE_ESP_DOMAIN/oauth2/sign_out?rd=https://TEMPLATE_ESP_DOMAIN/uaa/logout.do?redirect=https://TEMPLATE_ESP_DOMAIN/uaa/login
29+
signout_redirect_url = TEMPLATE_SIGNOUT_REDIRECT_URL
2930
3031
[auth.generic_oauth]
3132
tls_skip_verify_insecure = true
3233
enabled = true
33-
name = UAA
34+
name = OAuth
3435
use_pkce = true
3536
auto_login = true
3637
client_id = TEMPLATE_OAUTH_CLIENT_ID
@@ -39,6 +40,9 @@ data:
3940
email_attribute_path = email
4041
name_attribute_path = user_name
4142
login_attribute_path = user_name
42-
auth_url = https://TEMPLATE_ESP_DOMAIN/uaa/oauth/authorize
43-
token_url = https://TEMPLATE_ESP_DOMAIN/uaa/oauth/token?token_format=jwt
44-
api_url = https://TEMPLATE_ESP_DOMAIN/uaa/userinfo
43+
auth_url = TEMPLATE_AUTH_URL
44+
token_url = TEMPLATE_TOKEN_URL
45+
api_url = TEMPLATE_API_URL
46+
allow_assign_grafana_admin = true
47+
role_attribute_path = contains(grafana_roles[*], 'grafana-admin') && 'GrafanaAdmin' || contains(grafana_roles[*], 'admin') && 'Admin' || contains(grafana_roles[*], 'editor') && 'Editor' || 'Viewer'
48+

install/configure-grafana.sh

Lines changed: 97 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -2,118 +2,134 @@
22

33
set -e -o pipefail -o nounset
44

5-
OAUTH_CLIENT_ID="${OAUTH_CLIENT_ID:-sv_client}"
6-
export OAUTH_CLIENT_ID
7-
8-
OAUTH_CLIENT_SECRET="${OAUTH_CLIENT_SECRET:-secret}"
9-
export OAUTH_CLIENT_SECRET
10-
11-
ESP_NAMESPACE="${1}"
12-
5+
#input variables
6+
ESP_NAMESPACE="${1}"; export ESP_NAMESPACE
137
ESP_PLUGIN_SOURCE="${2}"
14-
export ESP_PLUGIN_SOURCE
8+
OAUTH_TYPE="${3:-uaa}"
9+
10+
#optional environment variables - exported for use in other scripts
11+
OAUTH_CLIENT_ID="${OAUTH_CLIENT_ID:-sv_client}"; export OAUTH_CLIENT_ID
12+
OAUTH_CLIENT_SECRET="${OAUTH_CLIENT_SECRET:-secret}"; export OAUTH_CLIENT_SECRET
13+
KEYCLOAK_SUBPATH="${KEYCLOAK_SUBPATH:-auth}"; export KEYCLOAK_SUBPATH
1514

15+
#optional environment variables
1616
DRY_RUN="${DRY_RUN:-false}"
1717
INSTALL_GRAFANA="${INSTALL_GRAFANA:-false}"
18-
GRAFANA_VERSION="${GRAFANA_VERSION:-'9.5.13'}"
18+
GRAFANA_VERSION="${GRAFANA_VERSION:-9.5.13}"
19+
GRAFANA_NAMESPACE="${GRAFANA_NAMESPACE:-${ESP_NAMESPACE}}"
20+
21+
function check_requirements() {
22+
[ -z "$KUBECONFIG" ] && {
23+
echo "KUBECONFIG environment variable unset." >&2
24+
exit 1
25+
}
26+
27+
[ -z "${ESP_NAMESPACE}" ] && {
28+
echo "Usage: ${0} <namespace> <plugin-zip-url> <oauth-type>" >&2
29+
exit 1
30+
}
31+
32+
[ -z "${ESP_PLUGIN_SOURCE}" ] && {
33+
echo "Usage: ${0} <namespace> <plugin-zip-url> <oauth-type>" >&2
34+
exit 1
35+
}
36+
37+
if ! kubectl get namespace "${ESP_NAMESPACE}" 2>/dev/null 1>&2; then
38+
echo >&2 "ERROR: Namespace ${ESP_NAMESPACE} not found."
39+
exit 1
40+
fi
41+
}
1942

20-
# Fetch access token to perform admin tasks:
21-
function fetch_uaa_admin_token() {
22-
_resp=$(curl "https://${ESP_DOMAIN}/uaa/oauth/token" -s -k -X POST \
23-
-H 'Content-Type: application/x-www-form-urlencoded' \
24-
-H 'Accept: application/json' \
25-
-d "client_id=${UAA_ADMIN}&client_secret=${UAA_SECRET}&grant_type=client_credentials&response_type=token")
43+
function generate_manifests() {
44+
if [ -d "./manifests" ]; then
45+
echo "Existing manifest directory found." >&2
46+
echo "Removing manifests..."
47+
rm -r ./manifests/
48+
fi
49+
50+
[ -d "./manifests" ] || mkdir "manifests"
51+
cp -r *.yaml manifests/
52+
53+
for file in `find ./manifests/ -name "*.y*ml"` ; do
54+
55+
sed -i 's|TEMPLATE_AUTH_URL|'$TEMPLATE_AUTH_URL'|g' $file
56+
sed -i 's|TEMPLATE_TOKEN_URL|'$TEMPLATE_TOKEN_URL'|g' $file
57+
sed -i 's|TEMPLATE_API_URL|'$TEMPLATE_API_URL'|g' $file
58+
sed -i 's|TEMPLATE_SIGNOUT_REDIRECT_URL|'$TEMPLATE_SIGNOUT_REDIRECT_URL'|g' $file
59+
60+
sed -i 's|TEMPLATE_GRAFANA_DOMAIN|'$GRAFANA_DOMAIN'|g' $file
61+
sed -i 's|TEMPLATE_ESP_DOMAIN|'$ESP_DOMAIN'|g' $file
62+
sed -i 's|TEMPLATE_OAUTH_CLIENT_ID|'$OAUTH_CLIENT_ID'|g' $file
63+
sed -i 's|TEMPLATE_OAUTH_CLIENT_SECRET|'$OAUTH_CLIENT_SECRET'|g' $file
64+
sed -i 's|TEMPLATE_ESP_PLUGIN_SOURCE|'$ESP_PLUGIN_SOURCE'|g' $file
65+
sed -i 's|TEMPLATE_GRAFANA_VERSION|'$GRAFANA_VERSION'|g' $file
66+
67+
if [[ "${DRY_RUN}" == true ]]; then
68+
echo $file
69+
cat $file
70+
fi
2671

27-
echo "${_resp}" | jq -r '.access_token'
72+
done
2873
}
2974

30-
# Add Grafana generic OAuth to allowed auth redirects:
31-
function add_grafana_auth_redirect() {
32-
_token="$(fetch_uaa_admin_token)"
33-
_redirect="https://${ESP_DOMAIN}/grafana/login/generic_oauth"
75+
check_requirements
3476

35-
_config=$(curl -s -k -X GET "https://${ESP_DOMAIN}/uaa/oauth/clients/${OAUTH_CLIENT_ID}" -H "Authorization: Bearer ${_token}")
77+
echo "Fetching required deployment information..."
78+
ESP_DOMAIN=$(kubectl -n "${ESP_NAMESPACE}" get ingress --output json | jq -r '.items[0].spec.rules[0].host')
79+
export ESP_DOMAIN
3680

37-
_update_body=$(echo "${_config}" | jq -c -r --arg redirect "${_redirect}" \
38-
'.redirect_uri += [$redirect] | {client_id: .client_id, redirect_uri: .redirect_uri}')
81+
GRAFANA_DOMAIN=$(kubectl -n "${GRAFANA_NAMESPACE}" get ingress --output json | jq -r '.items[0].spec.rules[0].host')
3982

40-
_resp=$(curl "https://${ESP_DOMAIN}/uaa/oauth/clients/${OAUTH_CLIENT_ID}" -s -k -X PUT \
41-
-o /dev/null -w "%{http_code}" \
42-
-H 'Content-Type: application/json' \
43-
-H "Authorization: Bearer ${_token}" \
44-
-H 'Accept: application/json' \
45-
-d "${_update_body}")
83+
echo "Adding Grafana to allowed OAuth client redirects..."
84+
if [ "${OAUTH_TYPE}" == "viya" ]; then
4685

47-
if [ "${_resp}" == '200' ]; then
48-
echo " Grafana OAuth redirect added."
49-
else
50-
echo >&2 "ERROR: OAuth client redirect update failed with status code ${_resp}."
51-
exit 1
52-
fi
53-
}
86+
if [[ "${DRY_RUN}" == false ]]; then
87+
bash register-oauth-client-viya.sh
88+
fi
5489

55-
[ -z "$KUBECONFIG" ] && {
56-
echo "KUBECONFIG environment variable unset." >&2
57-
exit 1
58-
}
90+
TEMPLATE_AUTH_URL="https://${ESP_DOMAIN}/SASLogon/oauth/authorize"
91+
TEMPLATE_TOKEN_URL="https://${ESP_DOMAIN}/SASLogon/oauth/token"
92+
TEMPLATE_API_URL="https://${ESP_DOMAIN}/SASLogon/userinfo"
93+
TEMPLATE_SIGNOUT_REDIRECT_URL="https://${ESP_DOMAIN}/SASLogon/logout.do"
5994

60-
[ -z "${ESP_NAMESPACE}" ] && {
61-
echo "Usage: ${0} <namespace> <plugin-zip-url>" >&2
62-
exit 1
63-
}
95+
elif [ "${OAUTH_TYPE}" == "keycloak" ]; then
6496

65-
[ -z "${ESP_PLUGIN_SOURCE}" ] && {
66-
echo "Usage: ${0} <namespace> <plugin-zip-url>" >&2
67-
exit 1
68-
}
97+
if [[ "${DRY_RUN}" == false ]]; then
98+
bash register-oauth-client-keycloak.sh
99+
fi
69100

70-
if [ -d "./manifests" ]; then
71-
echo "Existing manifest directory found." >&2
72-
echo "Removing manifests..."
73-
rm -r ./manifests/
74-
fi
101+
TEMPLATE_AUTH_URL="https://${ESP_DOMAIN}/${KEYCLOAK_SUBPATH}/realms/sas-esp/protocol/openid-connect/auth"
102+
TEMPLATE_TOKEN_URL="https://${ESP_DOMAIN}/${KEYCLOAK_SUBPATH}/realms/sas-esp/protocol/openid-connect/token"
103+
TEMPLATE_API_URL="https://${ESP_DOMAIN}/${KEYCLOAK_SUBPATH}/realms/sas-esp/protocol/openid-connect/userinfo"
104+
TEMPLATE_SIGNOUT_REDIRECT_URL="https://${ESP_DOMAIN}/${KEYCLOAK_SUBPATH}/realms/sas-esp/protocol/openid-connect/logout?client_id=${OAUTH_CLIENT_ID}\&post_logout_redirect_uri=https://${ESP_DOMAIN}/grafana/login"
75105

76-
echo "Fetching required deployment information..."
77-
ESP_DOMAIN=$(kubectl -n "${ESP_NAMESPACE}" get ingress --output json | jq -r '.items[0].spec.rules[0].host')
78-
export ESP_DOMAIN
106+
else
79107

80-
_uaa_secret_data=$(kubectl -n "${ESP_NAMESPACE}" get secret uaa-secret --output json)
81-
UAA_ADMIN=$(echo "${_uaa_secret_data}" | jq -r '.data.username | @base64d')
82-
export UAA_ADMIN
83-
UAA_SECRET=$(echo "${_uaa_secret_data}" | jq -r '.data.password | @base64d')
84-
export UAA_SECRET
108+
if [[ "${DRY_RUN}" == false ]]; then
109+
bash register-oauth-client-uaa.sh
110+
fi
111+
112+
TEMPLATE_AUTH_URL="https://${ESP_DOMAIN}/uaa/oauth/authorize"
113+
TEMPLATE_TOKEN_URL="https://${ESP_DOMAIN}/uaa/oauth/token?token_format=jwt"
114+
TEMPLATE_API_URL="https://${ESP_DOMAIN}/uaa/userinfo"
115+
TEMPLATE_SIGNOUT_REDIRECT_URL="https://${ESP_DOMAIN}/oauth2/sign_out?rd=https://${ESP_DOMAIN}/uaa/logout.do?redirect=https://${ESP_DOMAIN}/uaa/login"
116+
117+
fi
85118

86119
cat <<EOF
87120
Deployment details:
88121
ESP domain: ${ESP_DOMAIN}
89-
UAA admin user: ${UAA_ADMIN}
90-
UAA admin secret: ****
91122
OAuth client ID: ${OAUTH_CLIENT_ID}
92123
OAuth client secret: ****
93124
Deploying Grafana with values:
94125
ESP plugin source: ${ESP_PLUGIN_SOURCE}
95126
EOF
96127

97-
echo "Adding Grafana to allowed OAuth client redirects..."
98-
add_grafana_auth_redirect
99-
100128
echo "Generating manifests..."
101-
[ -d "./manifests" ] || mkdir "manifests"
102-
cp -r *.yaml manifests/
103-
104-
find ./manifests/ -type f -name "*.yaml" -exec perl -pi -e 's/\QTEMPLATE_ESP_DOMAIN/$ENV{"ESP_DOMAIN"}/g' '{}' +
105-
find ./manifests/ -type f -name "*.yaml" -exec perl -pi -e 's/\QTEMPLATE_OAUTH_CLIENT_ID/$ENV{"OAUTH_CLIENT_ID"}/g' '{}' +
106-
find ./manifests/ -type f -name "*.yaml" -exec perl -pi -e 's/\QTEMPLATE_OAUTH_CLIENT_SECRET/$ENV{"OAUTH_CLIENT_SECRET"}/g' '{}' +
107-
find ./manifests/ -type f -name "*.yaml" -exec perl -pi -e 's/\QTEMPLATE_ESP_PLUGIN_SOURCE/$ENV{"ESP_PLUGIN_SOURCE"}/g' '{}' +
108-
find ./manifests/ -type f -name "*.yaml" -exec perl -pi -e 's/\QTEMPLATE_GRAFANA_VERSION/$ENV{"GRAFANA_VERSION"}/g' '{}' +
129+
generate_manifests
109130

110131
if [[ "${DRY_RUN}" == true ]]; then
111-
echo "Dry run specified. Printing manifests to be applied:"
112-
echo "./manifests/config-map.yaml"
113-
cat ./manifests/config-map.yaml
114-
echo "./manifests/patch-grafana.yaml"
115-
cat ./manifests/patch-grafana.yaml
116-
exit 0
132+
exit 0
117133
fi
118134

119135
if [[ "${INSTALL_GRAFANA}" == true ]]; then

install/grafana.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,4 +106,4 @@ spec:
106106
tls:
107107
- hosts:
108108
- "TEMPLATE_ESP_DOMAIN"
109-
secretName: foo
109+
secretName: TEMPLATE_OAUTH_CLIENT_SECRET
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
#!/usr/bin/env bash
2+
3+
set -e -o pipefail -o nounset
4+
5+
function check_keycloak_deployment() {
6+
if ! kubectl -n "${ESP_NAMESPACE}" get deployment keycloak-deployment 2>/dev/null 1>&2; then
7+
echo >&2 "ERROR: No Keycloak deployment found under namespace ${ESP_NAMESPACE}."
8+
exit 1
9+
fi
10+
11+
_kc_pod=$(kubectl -n "${ESP_NAMESPACE}" get pods -o json |
12+
jq -r '.items[] | select(.metadata.name | test("^keycloak-deployment-")) | .metadata.name')
13+
[ -n "${_kc_pod}" ] || {
14+
echo >&2 "ERROR: No keycloak-deployment-* pod found under namespace ${ESP_NAMESPACE}."
15+
exit 1
16+
}
17+
18+
_kc_ready=$(kubectl -n "${ESP_NAMESPACE}" get pod "${_kc_pod}" -o json |
19+
jq -r '.status.conditions[] | select(.type == "Ready") | .status')
20+
[ "${_kc_ready}" == 'True' ] || {
21+
echo >&2 "ERROR: Keycloak deployment exists but is not ready. Try again later."
22+
exit 1
23+
}
24+
}
25+
26+
function check_requirements() {
27+
28+
if ! kubectl -n "${ESP_NAMESPACE}" get secret keycloak-admin-secret 2>/dev/null 1>&2; then
29+
echo >&2 "ERROR: No Keycloak admin secret found under namespace ${ESP_NAMESPACE}."
30+
exit 1
31+
fi
32+
33+
if ! kubectl -n "${ESP_NAMESPACE}" get secret oauth2-proxy-client-secret 2>/dev/null 1>&2; then
34+
echo >&2 "ERROR: No OAuth2 Proxy client secret found under namespace ${ESP_NAMESPACE}."
35+
exit 1
36+
fi
37+
38+
check_keycloak_deployment
39+
}
40+
41+
# Fetch access token to perform admin tasks:
42+
function fetch_keycloak_admin_token() {
43+
_resp=$(curl "https://${ESP_DOMAIN}/${KEYCLOAK_SUBPATH}/realms/master/protocol/openid-connect/token" -s -k -X POST \
44+
-H 'Content-Type: application/x-www-form-urlencoded' \
45+
-H 'Accept: application/json' \
46+
-d "client_id=admin-cli&grant_type=password&username=${KEYCLOAK_ADMIN}&password=${KEYCLOAK_SECRET}")
47+
48+
echo "${_resp}" | jq -r '.access_token'
49+
}
50+
51+
function create_role() {
52+
_role_name="${1}"
53+
_role_repr="{\"name\": \"${_role_name}\", \"clientRole\": true}"
54+
curl "https://${ESP_DOMAIN}/${KEYCLOAK_SUBPATH}/admin/realms/sas-esp/clients/${_client_id}/roles" -s -k -X POST \
55+
-H "Content-Type: application/json" \
56+
-H "Authorization: Bearer ${_token}" \
57+
-d "${_role_repr}"
58+
}
59+
60+
function add_protocol_mapper() {
61+
_mapper_repr=$(echo -e "
62+
{
63+
\"name\": \"GrafanaRoles\",
64+
\"protocol\": \"openid-connect\",
65+
\"protocolMapper\": \"oidc-usermodel-client-role-mapper\",
66+
\"consentRequired\": false,
67+
\"config\": {
68+
\"claim.name\": \"grafana_roles\",
69+
\"usermodel.clientRoleMapping.clientId\": \"${OAUTH_CLIENT_ID}\",
70+
\"jsonType.label\": \"String\",
71+
\"multivalued\": \"true\",
72+
\"access.token.claim\": \"true\",
73+
\"userinfo.token.claim\": \"false\",
74+
\"id.token.claim\": \"true\"
75+
}
76+
}")
77+
_mapper_body=$(echo "${_mapper_repr}" | jq -r -c)
78+
curl -s -k -X POST \
79+
"https://${ESP_DOMAIN}/${KEYCLOAK_SUBPATH}/admin/realms/sas-esp/clients/${_client_id}/protocol-mappers/models" \
80+
-H "Content-Type: application/json" \
81+
-H "Authorization: Bearer ${_token}" \
82+
-d "${_mapper_body}"
83+
}
84+
85+
function prepare_keycloak_roles() {
86+
_token="$(fetch_keycloak_admin_token)"
87+
# Get sas-esp realm clients:
88+
_kc_clients=$(curl -s -k -X GET "https://${ESP_DOMAIN}/${KEYCLOAK_SUBPATH}/admin/realms/sas-esp/clients" -H "Authorization: Bearer ${_token}")
89+
# Get OAuth2 Proxy client ID:
90+
_client_id=$(echo "${_kc_clients}" | jq -r --arg opid "${OAUTH_CLIENT_ID}" '.[] | select(.clientId == $opid) | .id')
91+
# Create Grafana roles:
92+
create_role "grafana-admin"
93+
create_role "admin"
94+
create_role "editor"
95+
# Create Grafana role protocol mapper:
96+
add_protocol_mapper
97+
}
98+
99+
_keycloak_admin_secret=$(kubectl -n "${ESP_NAMESPACE}" get secret keycloak-admin-secret --output json)
100+
KEYCLOAK_ADMIN=$(echo "${_keycloak_admin_secret}" | jq -r '.data.username | @base64d')
101+
KEYCLOAK_SECRET=$(echo "${_keycloak_admin_secret}" | jq -r '.data.password | @base64d')
102+
103+
_oauth2_proxy_secret=$(kubectl -n "${ESP_NAMESPACE}" get secret oauth2-proxy-client-secret --output json)
104+
OAUTH_CLIENT_ID=$(echo "${_oauth2_proxy_secret}" | jq -r '.data.OAUTH2_PROXY_CLIENT_ID | @base64d')
105+
export OAUTH_CLIENT_ID
106+
OAUTH_CLIENT_SECRET=$(echo "${_oauth2_proxy_secret}" | jq -r '.data.OAUTH2_PROXY_CLIENT_SECRET | @base64d')
107+
export OAUTH_CLIENT_SECRET
108+
109+
prepare_keycloak_roles

0 commit comments

Comments
 (0)