# Smithereen 1.0.3: a VKontakte-like network in Java. Walls (posts on someone else's wall carry a `target`, and the wall's # owner announces them with Add{Note}), friends as mutual follows (requests as Offer{Follow}), groups and events, photo # albums, threaded comments, polls, private messages. On the shared MySQL (database smithereen), with imgproxy for its # pictures and a file server for its uploads, both behind Caddy as smithereen.test (/i and /s). Its API is VKontakte's # (/api/method/?v=1.0) and takes a password grant for an Application its OAuth knows: the pasture makes a local # one in its database. Its HTTP client refuses private addresses, which the pasture's subnet is not. SMITHEREEN_IMAGE=${SMITHEREEN_IMAGE:-docker.io/grishkaa/smithereen@sha256:fcef096b78de21cc98936c2cebca5b5217d10976e59886be9e68d78f650581cf} # the commit that image was built from (its version.properties), whose schema.sql makes the database SMITHEREEN_COMMIT=d60c6e4c5abf2682d203f948770e6f63ce3bdeb5 SMITHEREEN_IMGPROXY_IMAGE=${SMITHEREEN_IMGPROXY_IMAGE:-docker.io/darthsim/imgproxy:v3.31.4} SMITHEREEN_PASSWORD=Smithereen-Pasture-Pass-1 # imgproxy's URL signing pair, shared with Smithereen's config: not a secret in the pasture SMITHEREEN_IMGPROXY_KEY=736d697468657265656e2d706173747572652d696d6770726f78792d6b65792d SMITHEREEN_IMGPROXY_SALT=736d697468657265656e2d706173747572652d696d6770726f78792d73616c74 . "$here/peers/shared.sh" smithereen_sql() { podman exec -i pasture-mysql mysql -uroot -ppasture -N smithereen "$@" 2>/dev/null; } smithereen_up() { shared_mysql_up # its schema updater makes stored functions, which MySQL refuses to a user without SUPER while binary logging is on podman exec pasture-mysql mysql -uroot -ppasture -e "set persist log_bin_trust_function_creators = 1" 2>/dev/null local st="$here/.state/smithereen" mkdir -p "$st" [ -s "$st/schema.sql" ] || curl -fsSL "https://raw.githubusercontent.com/grishka/Smithereen/$SMITHEREEN_COMMIT/schema.sql" -o "$st/schema.sql" \ || { echo "smithereen: no schema.sql" >&2; return 1; } if [ "$(echo "select count(*) from information_schema.tables where table_schema='smithereen' and table_name='users'" | smithereen_sql)" != "1" ]; then mysql_db smithereen smithereen_sql < "$st/schema.sql" # anyone may sign up, without a captcha or a confirmation mail: the pasture makes its accounts through the form. Its # NodeInfo fails (a NullPointerException) until the server has a short description echo "insert into config (\`key\`, value) values ('SignupMode', 'OPEN'), ('SignupFormUseCaptcha', '0'), ('SignupConfirmEmail', '0'), ('ServerDisplayName', 'Smithereen pasture'), ('ServerShortDescription', 'The pasture''s Smithereen') on duplicate key update value=values(value)" | smithereen_sql fi cat > "$st/config.properties" <<-EOF db.host=mysql db.name=smithereen db.user=pasture db.password=pasture server.ip=0.0.0.0 domain=smithereen.test upload.path=/uploads/uploads upload.url_path=/s/uploads media_cache.path=/uploads/media_cache media_cache.url_path=/s/media_cache media_cache.max_size=1G media_cache.file_size_limit=50M imgproxy.url_prefix=/i imgproxy.local_uploads=/uploads imgproxy.local_media_cache=/media_cache imgproxy.key=$SMITHEREEN_IMGPROXY_KEY imgproxy.salt=$SMITHEREEN_IMGPROXY_SALT EOF # Java trusts its own store only: the JDK's, with the pasture's CA added if [ ! -s "$st/cacerts" ]; then podman run --rm --entrypoint sh -v "$st:/st:Z" -v "$ca:/pasture/ca:z,ro" $SMITHEREEN_IMAGE -c \ 'cp /opt/java/openjdk/lib/security/cacerts /st/cacerts && chmod u+w /st/cacerts && /opt/java/openjdk/bin/keytool -importcert -noprompt -alias pasture -file /pasture/ca/root.crt -keystore /st/cacerts -storepass changeit' >/dev/null fi podman volume exists pasture-smithereen-uploads || podman volume create --label pasture=1 pasture-smithereen-uploads >/dev/null podman run -d --replace --name pasture-smithereen-imgproxy --label pasture=1 --network $net -v pasture-smithereen-uploads:/uploads \ -e IMGPROXY_PATH_PREFIX=/i -e IMGPROXY_ALLOWED_SOURCES=local:// -e IMGPROXY_LOCAL_FILESYSTEM_ROOT=/uploads \ -e IMGPROXY_KEY=$SMITHEREEN_IMGPROXY_KEY -e IMGPROXY_SALT=$SMITHEREEN_IMGPROXY_SALT $SMITHEREEN_IMGPROXY_IMAGE >/dev/null podman run -d --replace --name pasture-smithereen-files --label pasture=1 --network $net -v pasture-smithereen-uploads:/srv:ro \ docker.io/library/caddy:2 caddy file-server --root /srv --listen :80 >/dev/null podman run -d --replace --name pasture-smithereen --label pasture=1 --network $net -v pasture-smithereen-uploads:/uploads \ -v "$st/config.properties:/usr/local/etc/config.properties:Z,ro" -v "$st/cacerts:/pasture/cacerts:Z,ro" \ -e JAVA_TOOL_OPTIONS='-Djavax.net.ssl.trustStore=/pasture/cacerts -Djavax.net.ssl.trustStorePassword=changeit -Xmx512m' \ $SMITHEREEN_IMAGE >/dev/null podman exec pasture-smithereen mkdir -p /uploads/uploads /uploads/media_cache smithereen_wait smithereen_settle echo "smithereen: https://smithereen.test:6443" } smithereen_wait() { for _ in $(seq 1 90); do site smithereen.test -s -o /dev/null -w '%{http_code}' https://smithereen.test:6443/.well-known/nodeinfo 2>/dev/null | grep -q 200 && return 0 sleep 2 done echo "smithereen did not start" >&2; return 1 } # smithereen_account : an account made through the signup form, then renamed from its id to the # name (Smithereen leaves the name to its settings page) smithereen_account() { local name=$1 first=$2 [ "$(echo "select count(*) from users where username='$name' and domain=''" | smithereen_sql)" = "1" ] && return 0 site smithereen.test -s -o /dev/null -X POST https://smithereen.test:6443/account/register \ --data-urlencode "email=$name@smithereen.test" --data-urlencode "password=$SMITHEREEN_PASSWORD" \ --data-urlencode "password2=$SMITHEREEN_PASSWORD" --data-urlencode "first_name=$first" --data-urlencode "last_name=Pasture" echo "update users set username='$name' where id=(select user_id from accounts where email='$name@smithereen.test')" | smithereen_sql } # smuser and smfriend, a local client application for the password grant, and smuser's token smithereen_settle() { local st="$here/.state/smithereen" smithereen_account smuser Smitty smithereen_account smfriend Freddie if [ "$(echo "select count(*) from api_applications where name='pasture'" | smithereen_sql)" = "0" ]; then openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out "$st/app.pem" 2>/dev/null local pub priv pub=$(openssl pkey -in "$st/app.pem" -pubout -outform DER | python3 -c 'import sys; print(sys.stdin.buffer.read().hex())') priv=$(openssl pkcs8 -topk8 -nocrypt -in "$st/app.pem" -outform DER | python3 -c 'import sys; print(sys.stdin.buffer.read().hex())') echo "insert into api_applications (type, name, description, developer_id, public_key, private_key, extra) values (0, 'pasture', 'the pasture', 1, x'$pub', x'$priv', '{\"redirectURIs\":[\"https://smithereen.test/\"]}')" | smithereen_sql fi # the new names are read at start podman restart pasture-smithereen >/dev/null smithereen_wait local app app=$(echo "select id from api_applications where name='pasture'" | smithereen_sql) echo "https://smithereen.test/apps/$app" > "$st/client_id" # (its login flood control may refuse a second grant at once: tried again) for user in smuser smfriend; do for _ in 1 2 3 4 5; do site smithereen.test -s -X POST https://smithereen.test:6443/oauth/token --data-urlencode grant_type=password \ --data-urlencode "client_id=https://smithereen.test/apps/$app" --data-urlencode "username=$user@smithereen.test" \ --data-urlencode "password=$SMITHEREEN_PASSWORD" | python3 -c 'import json, sys; print(json.load(sys.stdin).get("access_token", ""))' > "$st/$user.token" [ -s "$st/$user.token" ] && [ "$(wc -c < "$st/$user.token")" -gt 2 ] && break sleep 3 done done }