go v1.20+, redis v7.x, docker compose v2.x
captcha code sequence generator, image render and verify code service with gRPC interfaces
based on lite (render, random) version of https://github.com/dchest/captcha
180x80px 4 digits png image
docker run --rm -it -p 1111:1111 -p 2222:2222 --env-file=.deploy/env/local.env -e CAPTCHA_REDIS_HOST=0.0.0.0 -e CAPTCHA_REDIS_PORT=6379 -e CAPTCHA_REDIS_DB=0 -e CAPTCHA_REDIS_PASS=YQ3dvPx3fVzv gtsh77workshop/grpc-captcha:v1.0.0
docker compose -f docker-compose.local.yml up
export $(grep -v '^#' ./deploy/env/local.env | xargs)
./bin/grpc-captcha_linux_amd64
grpcurl -H 'x-api-key: 1ace3bed-3aaf-4642-adb1-d63aef85895f' -plaintext -import-path pkg/proto/grpc-captcha -proto grpc-captcha.proto 0.0.0.0:2222 werkstatt.captcha.CaptchaService.Generate |jq
{
"id": "acf26399-0aa3-4fea-89ef-495476315998",
"data": "iVBORw0KGgoAAAANSUhEUgAAALQAAABQCAMAAACHxq+UAAAAP1BMVEUAAAA5QBDe5bWzuord5LSwt4fZ4LBzekq5wJDHzp7g57dzekp6gVEyOQlUWyuMk2NSWSmZoHDHzp6Ij1+xuIidZE4xAAAAAXRSTlMAQObYZgAAA29JREFUeJzsmt2yqyoMx5Npay9qp07f/2HPVBGSEL4R19ljerF3K4Qff0KIuuCyyy677LLLLrvsVMOzASoMERHwf4aOK/bfJ5+myf7f4h4Bbr293+9GV9MkqS3w+m8f21cQNuZWagWaKh2lfuThEikOgQbjOq60+fXxSFAzYB36+XxWUnvMbkwNxbZJMXNg64wz11HzgRIRARlRY1uyHaK26QKd3HsF+zMneXZSOk4U2J9oP17ThPVgTkpNlH6yTnSrZXnqafGxiNJWIwRtu41kjq/qekUojdJc26FKB7aPQWJXUPnAeKFZ7aExUx4hrvs2FBp47eFd0g4g5ZubyjIIO1DlIQGi85B5jk1sWZZxke2fIk56ssXkahBmE/3L0DjhSCTIvV95h31eoCXvUdAGlOhLlZablYS0euRk2dRGDUo20PRHQhrJ3ZnMUxs1OQJZjhBJjtAxfHvDUkjdxsyovmQe7CIQefdp2UNGS55Hm5Pr+/3an9TINfxKdh/MbAdFBK40okpt2opyb/jjCG8v2d3nItcGhGnKDs0TmH2lnJw8SvaL7tJ50DIDsLPSr+9MFy1ZVg3eAD3PM81hIn/4IaSf+lVjV/ecf8Y2HpA6lijOp4rYqnR+X3nbaqB5hgCub0jrltxR0lnZeEJpYPThEbAlSxdOmEchmpgGVen4GAOZ+Q0AWWT+nE/UFNp9pVuYGuii1MHuXOTuY0+xfUKFuUbu4iILQL4SkAOjWmWo5XUlN9ZlaOTjsVPObUa/i/jOFqto7Apmr4hTr/k9ZKnEe3Rhjnvx48J6DcY0qaA04XOwk+2SbnQPwewhg6qMJi/TpbJRwEUkT8d2XjK0Mw+jeBYNDrLJeLvd/Ed5YeYEtVmbrBAKNYy+llsX/vYzKS0J6ogMxOm7ghlsYKN/nx33sUL7FXVyk7AnxutbIwSXQ8vyIhXLHYkRH1Lp3NHIkYuIbzdUYW4mSttFTvv4Ca2+b8ka0hW1ez6oKlDc9ItnXWF6hVvrC0tuGNqtz1jYcD952b9q97MBKux+P5Y6+YcWVXY48yHUh9og6Lmrt2ro9djNtXnuTl3TbavKsq0vc60VQrdbj8UaDb09AGy18UJ33hcDTIM+5VVGkanMf57as0OgX70dCtugP119vl4DqOHz6Uzd1VvI2pj/CwAA//9L/w3JNJ6cGwAAAABJRU5ErkJggg=="
}
grpcurl -H 'x-api-key: 1ace3bed-3aaf-4642-adb1-d63aef85895f' -plaintext -import-path pkg/proto/grpc-captcha -proto grpc-captcha.proto -d '{"id":"acf26399-0aa3-4fea-89ef-495476315998", "otp": "5485"}' 0.0.0.0:2222 werkstatt.captcha.CaptchaService.Verify |jq
{}
paste data field value into https://onlinepngtools.com/convert-base64-to-png
docker run -it --rm --network host redis:7.2-alpine redis-cli -p 6379 -a YQ3dvPx3fVzv
For most cases we recommend simple use local proxy (eg nginx
) with TLS enabled which upstreams non-tls captcha service or even simpler you can use k8s
which already have its own TLS proxy/balancer
If in some cases there is no possibility to have a local proxy we prepared this full operable example for direct interaction with service's interfaces through TLS
-
load our test self-signed crt/keys data into env variables or use own (crt, key and ca crt) [using data instead of file path helps with k8s pod implementation, so you need to load them in advance]
export CAPTCHA_HTTP_TLS_CRT_DATA=`cat .deploy/crt/secure.nd.crt` CAPTCHA_GRPC_TLS_CRT_DATA=`cat .deploy/crt/secure.nd.crt` CAPTCHA_REDIS_TLS_CRT_DATA=`cat .deploy/crt/secure.nd.crt` CAPTCHA_HTTP_TLS_KEY_DATA=`cat .deploy/crt/secure.nd.key` CAPTCHA_GRPC_TLS_KEY_DATA=`cat .deploy/crt/secure.nd.key` CAPTCHA_REDIS_TLS_KEY_DATA=`cat .deploy/crt/secure.nd.key` CAPTCHA_HTTP_TLS_CRT_CA_DATA=`cat .deploy/crt/ca.crt` CAPTCHA_GRPC_TLS_CRT_CA_DATA=`cat .deploy/crt/ca.crt` CAPTCHA_REDIS_TLS_CRT_CA_DATA=`cat .deploy/crt/ca.crt`
-
start via docker compose (tls variant)
docker compose -f docker-compose.tls.yml up
-
add crt to trusted (only if self-signed) to be able to interact with grpc interface via tls
apt install ca-certificates cp .deploy/crt/secure.nd.crt /usr/local/share/ca-certificates/. update-ca-certificates
-
set local domain which corresponds to your crt's CN (opt) [below example for test self-signed crt/key provided within repo]
vi /etc/hosts captcha.secure.nd 127.0.0.1
grpcurl -H 'x-api-key: 1ace3bed-3aaf-4642-adb1-d63aef85895f' -import-path pkg/proto/grpc-captcha -proto grpc-captcha.proto captcha.secure.nd:2222 werkstatt.captcha.CaptchaService.Generate |jq
{
"id": "acf26399-0aa3-4fea-89ef-495476315998",
"data": "iVBORw0KGgoAAAANSUhEUgAAALQAAABQCAMAAACHxq+UAAAAP1BMVEUAAAA5QBDe5bWzuord5LSwt4fZ4LBzekq5wJDHzp7g57dzekp6gVEyOQlUWyuMk2NSWSmZoHDHzp6Ij1+xuIidZE4xAAAAAXRSTlMAQObYZgAAA29JREFUeJzsmt2yqyoMx5Npay9qp07f/2HPVBGSEL4R19ljerF3K4Qff0KIuuCyyy677LLLLrvsVMOzASoMERHwf4aOK/bfJ5+myf7f4h4Bbr293+9GV9MkqS3w+m8f21cQNuZWagWaKh2lfuThEikOgQbjOq60+fXxSFAzYB36+XxWUnvMbkwNxbZJMXNg64wz11HzgRIRARlRY1uyHaK26QKd3HsF+zMneXZSOk4U2J9oP17ThPVgTkpNlH6yTnSrZXnqafGxiNJWIwRtu41kjq/qekUojdJc26FKB7aPQWJXUPnAeKFZ7aExUx4hrvs2FBp47eFd0g4g5ZubyjIIO1DlIQGi85B5jk1sWZZxke2fIk56ssXkahBmE/3L0DjhSCTIvV95h31eoCXvUdAGlOhLlZablYS0euRk2dRGDUo20PRHQhrJ3ZnMUxs1OQJZjhBJjtAxfHvDUkjdxsyovmQe7CIQefdp2UNGS55Hm5Pr+/3an9TINfxKdh/MbAdFBK40okpt2opyb/jjCG8v2d3nItcGhGnKDs0TmH2lnJw8SvaL7tJ50DIDsLPSr+9MFy1ZVg3eAD3PM81hIn/4IaSf+lVjV/ecf8Y2HpA6lijOp4rYqnR+X3nbaqB5hgCub0jrltxR0lnZeEJpYPThEbAlSxdOmEchmpgGVen4GAOZ+Q0AWWT+nE/UFNp9pVuYGuii1MHuXOTuY0+xfUKFuUbu4iILQL4SkAOjWmWo5XUlN9ZlaOTjsVPObUa/i/jOFqto7Apmr4hTr/k9ZKnEe3Rhjnvx48J6DcY0qaA04XOwk+2SbnQPwewhg6qMJi/TpbJRwEUkT8d2XjK0Mw+jeBYNDrLJeLvd/Ed5YeYEtVmbrBAKNYy+llsX/vYzKS0J6ogMxOm7ghlsYKN/nx33sUL7FXVyk7AnxutbIwSXQ8vyIhXLHYkRH1Lp3NHIkYuIbzdUYW4mSttFTvv4Ca2+b8ka0hW1ez6oKlDc9ItnXWF6hVvrC0tuGNqtz1jYcD952b9q97MBKux+P5Y6+YcWVXY48yHUh9og6Lmrt2ro9djNtXnuTl3TbavKsq0vc60VQrdbj8UaDb09AGy18UJ33hcDTIM+5VVGkanMf57as0OgX70dCtugP119vl4DqOHz6Uzd1VvI2pj/CwAA//9L/w3JNJ6cGwAAAABJRU5ErkJggg=="
}
grpcurl -H 'x-api-key: 1ace3bed-3aaf-4642-adb1-d63aef85895f' -import-path pkg/proto/grpc-captcha -proto grpc-captcha.proto -d '{"id":"acf26399-0aa3-4fea-89ef-495476315998", "code": "5485"}' captcha.secure.nd:2222 werkstatt.captcha.CaptchaService.Verify |jq
{}
use curl -k https://host... instead of curl host...
docker run -it --rm --network host -v $(pwd)/.deploy/crt/ca.crt:/ca.crt -v $(pwd)/.deploy/crt/secure.nd.crt:/server.crt -v $(pwd)/.deploy/crt/secure.nd.key:/server.key redis:7.2-alpine redis-cli -p 6379 -a YQ3dvPx3fVzv --tls --cacert /ca.crt --cert /server.crt --key /server.key
paste data field value into https://onlinepngtools.com/convert-base64-to-png
Generate new sequence code of [0-9] digits in quantity of CAPTCHA_RENDER_DIG_CNT
, render png image CAPTCHA_RENDER_WIDTH
x CAPTCHA_RENDER_HEIGHT
px, store code sequence in redis db with TTL CAPTCHA_RENDER_TTL
and return base64 encoding of image and uniqique captcha id
to identify the sequence (uuidv4
)
compare code sequences in payload and value stored in redis by provided key (id
), if no such key ret NotFound
, if values are not equal ret FailedPrecondition
, if eq ret OK
and remove key from redis
make && make run
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.55.1
make lint
make test
make env
curl 0.0.0.0:1111/health/check
curl 0.0.0.0:1111/health/operable
curl 0.0.0.0:1111/metrics
define B =
go build -ldflags "-s -w -X main.name=$(APP) -X main.version=$(RELEASE) -X main.compiledAt=$(BUILD_TIME)" -o