Merge branch '16393-keepproxy-timeout-with-slow-clients'
[arvados.git] / lib / controller / localdb / login_pam_docker_test.sh
1 #!/bin/bash
2
3 # This script demonstrates using LDAP for Arvados user authentication.
4 #
5 # It configures pam_ldap(5) and arvados controller in a docker
6 # container, with pam_ldap configured to authenticate against an
7 # OpenLDAP server in a second docker container.
8 #
9 # After adding a "foo" user entry, it uses curl to check that the
10 # Arvados controller's login endpoint accepts the "foo" account
11 # username/password and rejects invalid credentials.
12 #
13 # It is intended to be run inside .../build/run-tests.sh (in
14 # interactive mode: "test lib/controller/localdb -tags=docker
15 # -check.f=LDAP -check.vv"). It assumes ARVADOS_TEST_API_HOST points
16 # to a RailsAPI server and the desired version of arvados-server is
17 # installed in $GOPATH/bin.
18
19 set -e -o pipefail
20
21 debug=/dev/null
22 if [[ -n ${ARVADOS_DEBUG} ]]; then
23     debug=/dev/stderr
24     set -x
25 fi
26
27 hostname="$(hostname)"
28 tmpdir="$(mktemp -d)"
29 cleanup() {
30     trap - ERR
31     rm -r ${tmpdir}
32     for h in ${ldapctr} ${ctrlctr}; do
33         if [[ -n ${h} ]]; then
34             docker kill ${h}
35         fi
36     done
37 }
38 trap cleanup ERR
39
40 if [[ -z "$(docker image ls -q osixia/openldap:1.3.0)" ]]; then
41     echo >&2 "Pulling docker image for ldap server"
42     docker pull osixia/openldap:1.3.0
43 fi
44
45 ldapctr=ldap-${RANDOM}
46 echo >&2 "Starting ldap server in docker container ${ldapctr}"
47 docker run --rm --detach \
48        -p 389 -p 636 \
49        --name=${ldapctr} \
50        osixia/openldap:1.3.0
51 docker logs --follow ${ldapctr} 2>$debug >$debug &
52 ldaphostport=$(docker port ${ldapctr} 389/tcp)
53 ldapport=${ldaphostport##*:}
54 ldapurl="ldap://${hostname}:${ldapport}"
55 passwordhash="$(docker exec -i ${ldapctr} slappasswd -s "secret")"
56
57 # These are the default admin credentials for osixia/openldap:1.3.0
58 adminuser=admin
59 adminpassword=admin
60
61 cat >"${tmpdir}/zzzzz.yml" <<EOF
62 Clusters:
63   zzzzz:
64     PostgreSQL:
65       Connection:
66         client_encoding: utf8
67         host: ${hostname}
68         dbname: arvados_test
69         user: arvados
70         password: insecure_arvados_test
71     ManagementToken: e687950a23c3a9bceec28c6223a06c79
72     SystemRootToken: systemusertesttoken1234567890aoeuidhtnsqjkxbmwvzpy
73     API:
74       RequestTimeout: 30s
75     TLS:
76       Insecure: true
77     Collections:
78       BlobSigningKey: zfhgfenhffzltr9dixws36j1yhksjoll2grmku38mi7yxd66h5j4q9w4jzanezacp8s6q0ro3hxakfye02152hncy6zml2ed0uc
79       TrustAllContent: true
80       ForwardSlashNameSubstitution: /
81     Services:
82       RailsAPI:
83         InternalURLs:
84           "https://${hostname}:${ARVADOS_TEST_API_HOST##*:}/": {}
85       Controller:
86         ExternalURL: http://0.0.0.0:9999/
87         InternalURLs:
88           "http://0.0.0.0:9999/": {}
89     Login:
90       PAM: true
91       # Without this magic PAMDefaultEmailDomain, inserted users would
92       # prevent subsequent database/reset from working (see
93       # database_controller.rb).
94       PAMDefaultEmailDomain: example.com
95     SystemLogs:
96       LogLevel: debug
97 EOF
98
99 cat >"${tmpdir}/pam_ldap.conf" <<EOF
100 base dc=example,dc=org
101 ldap_version 3
102 uri ${ldapurl}
103 pam_password crypt
104 binddn cn=${adminuser},dc=example,dc=org
105 bindpw ${adminpassword}
106 EOF
107
108 cat >"${tmpdir}/add_example_user.ldif" <<EOF
109 dn: cn=bar,dc=example,dc=org
110 objectClass: posixGroup
111 objectClass: top
112 cn: bar
113 gidNumber: 11111
114 description: "Example group 'bar'"
115
116 dn: uid=foo,dc=example,dc=org
117 uid: foo
118 cn: foo
119 givenName: Foo
120 sn: Bar
121 mail: foobar@example.org
122 objectClass: inetOrgPerson
123 objectClass: posixAccount
124 objectClass: top
125 objectClass: shadowAccount
126 shadowMax: 180
127 shadowMin: 1
128 shadowWarning: 7
129 shadowLastChange: 10701
130 loginShell: /bin/bash
131 uidNumber: 11111
132 gidNumber: 11111
133 homeDirectory: /home/foo
134 userPassword: ${passwordhash}
135 EOF
136
137 echo >&2 "Adding example user entry user=foo pass=secret (retrying until server comes up)"
138 docker run --rm --entrypoint= \
139        -v "${tmpdir}/add_example_user.ldif":/add_example_user.ldif:ro \
140        osixia/openldap:1.3.0 \
141        bash -c "for f in \$(seq 1 5); do if ldapadd -H '${ldapurl}' -D 'cn=${adminuser},dc=example,dc=org' -w '${adminpassword}' -f /add_example_user.ldif; then exit 0; else sleep 2; fi; done; echo 'failed to add user entry'; exit 1"
142
143 echo >&2 "Building arvados controller binary to run in container"
144 go build -o "${tmpdir}" ../../../cmd/arvados-server
145
146 ctrlctr=ctrl-${RANDOM}
147 echo >&2 "Starting arvados controller in docker container ${ctrlctr}"
148 docker run --detach --rm --name=${ctrlctr} \
149        -p 9999 \
150        -v "${tmpdir}/pam_ldap.conf":/etc/pam_ldap.conf:ro \
151        -v "${tmpdir}/arvados-server":/bin/arvados-server:ro \
152        -v "${tmpdir}/zzzzz.yml":/etc/arvados/config.yml:ro \
153        -v $(realpath "${PWD}/../../.."):/arvados:ro \
154        debian:10 \
155        bash -c "apt update && DEBIAN_FRONTEND=noninteractive apt install -y ldap-utils libpam-ldap && pam-auth-update --package /usr/share/pam-configs/ldap && arvados-server controller"
156 docker logs --follow ${ctrlctr} 2>$debug >$debug &
157 ctrlhostport=$(docker port ${ctrlctr} 9999/tcp)
158
159 echo >&2 "Waiting for arvados controller to come up..."
160 for f in $(seq 1 20); do
161     if curl -s "http://${ctrlhostport}/arvados/v1/config" >/dev/null; then
162         break
163     else
164         sleep 1
165     fi
166     echo -n >&2 .
167 done
168 echo >&2
169 echo >&2 "Arvados controller is up at http://${ctrlhostport}"
170
171 check_contains() {
172     resp="${1}"
173     str="${2}"
174     if ! echo "${resp}" | fgrep -q "${str}"; then
175         echo >&2 "${resp}"
176         echo >&2 "FAIL: expected in response, but not found: ${str@Q}"
177         return 1
178     fi
179 }
180
181 echo >&2 "Testing authentication failure"
182 resp="$(curl -s --include -d username=foo -d password=nosecret "http://${ctrlhostport}/arvados/v1/users/authenticate" | tee $debug)"
183 check_contains "${resp}" "HTTP/1.1 401"
184 check_contains "${resp}" '{"errors":["PAM: Authentication failure (with username \"foo\" and password)"]}'
185
186 echo >&2 "Testing authentication success"
187 resp="$(curl -s --include -d username=foo -d password=secret "http://${ctrlhostport}/arvados/v1/users/authenticate" | tee $debug)"
188 check_contains "${resp}" "HTTP/1.1 200"
189 check_contains "${resp}" '"api_token":"'
190 check_contains "${resp}" '"scopes":["all"]'
191 check_contains "${resp}" '"uuid":"zzzzz-gj3su-'
192
193 cleanup