]> WPIA git - infra.git/blob - bootstrap-user
fix: add missing dependencies for init-vm if using small Debian install
[infra.git] / bootstrap-user
1 #!/bin/bash
2 #Get domain-name and gigi-ip from the puppet config to know how to communicate with gigi
3 hostname=$(sed -n "/\$systemDomain/ { s/\$systemDomain = '\([^']*\)'/\1/; p }" environments/production/manifests/ip.pp)
4 ip=$(grep -A10 -F '$ips' environments/production/manifests/ip.pp | grep -F 'gigi =>' | head -n 1 | sed "s/.*'\([^']*\)'.*/\1/")
5
6 folder=.bootstrap-user-data
7 mkdir -p $folder
8
9 # Curl gigi using correct host-header, faking https-access and using the cookies in "cookie-jar"
10 function mcurl {
11     local url="$1"
12     shift
13     curl -s --header "X-Real-Proto: https" --header "Host: www.$hostname" -b $folder/cookie-jar "http://$ip/$url" "$@"
14 }
15 function mccurl {
16     local url="$1"
17     shift
18     curl -s --header "$(cat ${folder}/certauth.txt)" --header "X-Real-Proto: https" --header "Host: secure.$hostname" -b $folder/cookie-jar "http://$ip/$url" "$@"
19 }
20
21 # get the csrf out of a webpage (arguments 1 and 2 can be used to select the correct csrf-token)
22 function csrf {
23     grep csrf | ${1:-cat}  | ${2:-cat} | sed "s/.*value='\([^']*\)'.*/\\1/"
24 }
25
26 # update the cookie-jar so that cookies received with https-only property are also sent over our fake-https-connection
27 function open-jar {
28     sed -i 's/TRUE/FALSE/g' "$1" # also use the cookie over insecure connection
29 }
30 function silent_read {
31     prompt="$1"
32     shift
33     read -rsp "$prompt" "$@"
34     printf '\n'
35 }
36 #execute a registration in gigi. If "$1" == "nopass" a password is not asked for but chosen at random.
37 function register {
38     csrf=$(mcurl register -c $folder/cookie-jar | csrf)
39     if ! [[ -f $folder/cookie-jar ]]; then
40         echo "error, could not start gigi"
41         exit 1
42     fi
43     open-jar $folder/cookie-jar
44     silent_read "First Name: " fname
45     silent_read "Last Name: " lname
46     silent_read "Year of birth: " year
47     silent_read "Month of birth: " month
48     silent_read "Day of birth: " day
49     silent_read "Email address: " email
50     while [[ "$email" == *"'"* || "$email" == *"\\"* ]]; do
51         silent_read "Email address was not valid, try again: " email
52     done
53     if [[ "$1" == "nopass" ]]; then
54         pw1="$(head -c 15 /dev/urandom | base64)"
55         pw2="$pw1"
56     else
57         silent_read "Password: " pw1
58         silent_read "Password (repeat): " pw2
59         while [[ "$pw1" != "$pw2" ]]; do
60             silent_read "Password: " pw1
61             silent_read "Password (repeat): " pw2
62         done
63     fi
64     mcurl register --data-urlencode "name-type=western" \
65           --data-urlencode "fname=$fname" \
66           --data-urlencode "lname=$lname" \
67           --data-urlencode "suffix" \
68           --data-urlencode "name" \
69           --data-urlencode "year=$year" \
70           --data-urlencode "month=$month" \
71           --data-urlencode "day=$day" \
72           --data-urlencode "residenceCountry=invalid" \
73           --data-urlencode "email=$email" \
74           --data-urlencode "pword1=$pw1" \
75           --data-urlencode "pword2=$pw2" \
76           --data-urlencode "general=1" \
77           --data-urlencode "country=1" \
78           --data-urlencode "regional=1" \
79           --data-urlencode "radius=1" \
80           --data-urlencode "tos_agree=1" \
81           --data-urlencode "dp_agree=1" \
82           --data-urlencode "process=Weiter" \
83           --data-urlencode "csrf=$csrf" > /dev/null
84 }
85 function check_error {
86     cat > $folder/page_output
87     cat $folder/page_output >> $folder/log
88     if grep -q "error-msgs" $folder/page_output; then
89         cat $folder/page_output
90         exit 1
91     fi
92 }
93
94 function issue {
95     curl=$1
96     shift
97     options=$1
98     shift
99     csrf=$($curl "account/certs/new" | csrf "head -n 1")
100
101     openssl req -newkey rsa:4096 -subj "/CN=blabla" -nodes -out $folder/req -keyout $folder/priv
102     encoded=$(tr '\n' '?' < $folder/req | sed "s/=/%3D/g;s/+/%2B/g;s/\?/%0A/g")
103
104     $curl account/certs/new -d "CSR=$encoded&process=Next&csrf=$csrf" | check_error
105
106     serial=$($curl account/certs/new "$@" -d "$options&OU=&hash_alg=SHA256&validFrom=now&validity=2y&login=1&description=&process=Issue+Certificate&csrf=$csrf" -v 2>&1 | tee $folder/certlog | grep "< Location: " | sed "s_.*/\([a-f0-9]*\)[^0-9]*_\1_")
107     echo "Certificate: $serial"
108     if [[ $serial != "" ]]; then
109         echo "installing"
110         $curl "account/certs/$serial.crt?chain&noAnchor" > $folder/cert.crt
111         $curl "account/certs/$serial.crt" > $folder/onlycert.crt
112         return 0;
113     else
114         return 1;
115     fi
116 }
117
118 if ! type curl > /dev/null; then
119     echo "requires curl" >&2
120     exit 1
121 fi
122 if ! [[ "$(sudo lxc-attach -n postgres-primary -- su -c "psql -At gigi" postgres <<< "\dt")" == "No relations found." ]]; then
123     echo "gigi already has a database" >&2
124     exit 1
125 fi
126 # Manually managing gigi + nginx for now
127 sudo lxc-attach -n front-nginx systemctl stop puppet.service
128 sudo lxc-attach -n gigi systemctl stop puppet.service
129
130 #Stopping nginx so no-one external can interfere with our init procedure
131 sudo lxc-attach -n front-nginx systemctl stop nginx.service
132 sudo lxc-attach -n gigi systemctl stop gigi-proxy.{socket,service}
133 sudo lxc-attach -n gigi systemctl stop cassiopeia-client.service
134 sudo lxc-attach -n gigi gigi reset-database
135 sudo lxc-attach -n gigi systemctl start cassiopeia-client.service
136 sudo lxc-attach -n gigi systemctl start gigi-proxy.socket
137
138 rm -f $folder/cookie-jar
139 echo "So... preliminary things done. Let's start with the setup"
140 echo "We need a first administrative user, this user will be one of the bootstrappers for the WoT and seed for support."
141 register
142 adminEmail=$email
143 adminPw=$pw1
144 echo "Ok, let's define the second bootstrapper"
145 register nopass
146 secondaryEmail=$email
147 echo "You should now have been sent an activation link to the email you entered previously"
148 read -rp "The activation link: " link
149 params=${link##*\?}
150 csrf=$(mcurl "verify?$params" -c $folder/cookie-jar | csrf)
151 open-jar $folder/cookie-jar
152 echo "doing verification with $params"
153 if ! mcurl verify -d "$params&csrf=$csrf" | grep -qF "<div class='alert alert-success'>"; then
154     echo "Your e-mail address did not verify." >&2
155     exit 1
156 fi
157
158 echo "granting initial bootstrapping-rights"
159 sudo lxc-attach -n postgres-primary -- su -c "psql -d gigi" postgres <<EOF
160 INSERT INTO user_groups("user","permission","grantedby") VALUES((SELECT "id" FROM "users" WHERE "email"='$adminEmail'),'supporter',(SELECT "id" FROM "users" WHERE "email"='$adminEmail'));
161 INSERT INTO user_groups("user","permission","grantedby") VALUES((SELECT "id" FROM "users" WHERE "email"='$adminEmail'),'org-agent',(SELECT "id" FROM "users" WHERE "email"='$adminEmail'));
162 INSERT INTO notary("from","to","points","location","when","date") VALUES((SELECT "id" FROM "users" WHERE "email"='$secondaryEmail'), (SELECT "preferredName" FROM "users" WHERE "email"='$adminEmail'), 100, 'initial', CURRENT_TIMESTAMP, '$(date +%Y-%m-%d)');
163 INSERT INTO notary("from","to","points","location","when","date") VALUES((SELECT "id" FROM "users" WHERE "email"='$adminEmail'), (SELECT "preferredName" FROM "users" WHERE "email"='$secondaryEmail'), 100, 'initial', CURRENT_TIMESTAMP, '$(date +%Y-%m-%d)');
164 INSERT INTO cats_passed("user_id", "variant_id") VALUES((SELECT "id" FROM "users" WHERE "email"='$adminEmail'),1);
165 INSERT INTO cats_passed("user_id", "variant_id") VALUES((SELECT "id" FROM "users" WHERE "email"='$adminEmail'),2);
166 INSERT INTO cats_passed("user_id", "variant_id") VALUES((SELECT "id" FROM "users" WHERE "email"='$adminEmail'),6);
167 EOF
168 sudo lxc-attach -n gigi -- systemctl stop gigi-proxy.service
169
170 csrf=$(mcurl login -c $folder/cookie-jar | csrf)
171 open-jar $folder/cookie-jar
172 mcurl login -c $folder/cookie-jar --data-urlencode "username=$adminEmail" --data-urlencode "password=$adminPw" --data-urlencode "csrf=$csrf" | check_error
173 open-jar $folder/cookie-jar
174
175 echo "Creating own cert"
176 if issue mcurl "profile=client&CN=SomeCA+User" --data-urlencode "SANs=email:$adminEmail"; then
177     printf "Got own cert!\n"
178     cat ${folder}/cert.crt ${folder}/priv > gigi-key.pem
179 else
180     printf "issuance failed\n" >&2
181     exit 1
182 fi
183 sed "s/^/\t/;s/^\t-----BEGIN/X-Client-Cert: -----BEGIN/;s/\r//g" < ${folder}/onlycert.crt > ${folder}/certauth.txt
184
185 mccurl login -c $folder/cookie-jar
186 open-jar $folder/cookie-jar
187
188 echo "Creating organisation"
189 csrf=$(mccurl "orga/new" | csrf)
190 mgmOid=$(mccurl "orga/new" -v -d "O=SomeCA&L=town&ST=state&C=AT&contact=ce%40email.org&comments=&action=new&csrf=$csrf" 2>&1 | grep "< Location: " | sed "s_.*/\([0-9]*\)[^0-9]*_\1_")
191 if ! grep -q '^[0-9]\+$' <<< $mgmOid; then
192     echo "Got an Organisation ID that is not a number: $mgmOid." >&2
193     exit 1
194 fi
195 printf "Management Organisation id is \"%s\"\n" "$mgmOid"
196
197 printf "adding org-domain for org %s: %s\n" "$mgmOid" "$hostname"
198 csrf=$(mccurl orga/$mgmOid | csrf "head -n 4" "tail -n 1")
199 domainName="$hostname"
200 mccurl orga/$mgmOid -d "domain=$domainName&addDomain=action&csrf=$csrf" | check_error
201
202 echo "using SQL to add self as orgadmin for organisation"
203 sudo lxc-attach -n postgres-primary -- su -c "psql -d gigi" postgres <<EOF
204 INSERT INTO org_admin("orgid", "memid", "creator", "master") VALUES('$mgmOid', (SELECT "id" FROM "users" WHERE "email"='$adminEmail'), (SELECT "id" FROM "users" WHERE "email"='$secondaryEmail'), 'y');
205 EOF
206
207 csrf=$(mccurl account/details -v | csrf "tail -1")
208 mccurl account/details -v -d "orgaForm=orga&org%3A$mgmOid&csrf=$csrf" | check_error
209
210 echo "Configuring pings for the domain"
211 domain=$(mccurl "account/domains" | grep "/account/domains/" | sed "s_.*/\([0-9]\+\)'.*_\1_")
212 if ! grep -q '^[0-9]\+$' <<< $domain; then
213     echo "Got a Domain ID that is not a number: $domain." >&2
214     exit 1
215 fi
216
217 csrf=$(mccurl "account/domains/$domain" | tee $folder/domain | csrf "tail -n 1")
218
219 token=$(grep pre $folder/domain | tail -n 1 | sed "s_.*>\([a-zA-Z0-9]*\)<.*_\1_")
220 name=$(grep "content available at" $folder/domain | sed "s_.*/\([a-zA-Z0-9]*\)\\.txt.*_\1_")
221
222 sudo mkdir -p /data/nginx/challenge/.well-known/someca-challenge
223 printf "%s" "$token" | sudo tee /data/nginx/challenge/.well-known/someca-challenge/$name.txt > /dev/null
224
225 openssl req -newkey rsa:4096 -subj "/CN=$domainName/OU=$token" -nodes -out $folder/self-req -keyout $folder/self-priv
226 openssl x509 -req -in $folder/self-req -signkey $folder/self-priv -out $folder/self-cert -extfile <(printf "extendedKeyUsage = clientAuth, serverAuth\n")
227
228 cp $folder/self-cert modules/gigi/files/gigi.crt
229 setfacl -m user:puppet:r $folder/self-priv
230 cp --preserve=all $folder/self-priv modules/gigi/files/gigi.key
231 sudo lxc-attach -n front-nginx -- puppet agent --test --verbose
232
233 mccurl "account/domains/$domain" -d "HTTPType=y&SSLType=y&ssl-type-0=direct&ssl-port-0=443&ssl-type-1=direct&ssl-port-1=&ssl-type-2=direct&ssl-port-2=&ssl-type-3=direct&ssl-port-3=&csrf=$csrf" | check_error
234
235 echo "Pings configured... waiting"
236 sleep 5
237 mccurl "account/domains/$domain" > $folder/domainStatus
238
239 echo "Issuing certificate for web"
240
241 if issue mccurl "profile=server-orga&CN=&SANs=dns%3Awww.$domainName%2Cdns%3Astatic.$domainName%2Cdns%3Aapi.$domainName%2Cdns%3Asecure.$domainName"; then
242     cp $folder/cert.crt modules/gigi/files/gigi.crt
243     setfacl -m user:puppet:r $folder/priv
244     cp --preserve=all $folder/priv modules/gigi/files/gigi.key
245     echo "reloading cert"
246     sudo lxc-attach -n front-nginx -- puppet agent --test --verbose
247 else
248     echo "refusing to update"
249 fi
250
251 if issue mccurl "profile=mail-orga&CN=Gigi+System&SANs=email%3Agigi@$domainName"; then
252     echo "great!"
253     keystorepw=$(head -c 15 /dev/urandom | base64)
254     openssl pkcs12 -export -name "mail" -in $folder/cert.crt -inkey $folder/priv -CAfile modules/nre/files/config/ca/root.crt -password file:<(printf '%s' "$keystorepw") | sudo tee modules/gigi/files/keystore.pkcs12 > /dev/null
255     printf '%s' "$keystorepw" | sudo tee modules/gigi/files/keystorepw > /dev/null
256 else
257     echo "refusing to update"
258 fi
259 echo "marking gigi ready"
260 echo yes | sudo lxc-attach -n gigi tee /gigi-ready > /dev/null
261 sudo lxc-attach -n gigi -- puppet agent --test --verbose
262
263 sudo lxc-attach -n front-nginx systemctl start puppet.service
264 sudo lxc-attach -n gigi systemctl start puppet.service