Merge pull request #22 from cmehay/v3

Add tor v3 addresses support
This commit is contained in:
Christophe Mehay 2019-04-29 22:41:28 +02:00 committed by GitHub
commit f104c78c7c
24 changed files with 1086 additions and 225 deletions

1
.gitignore vendored
View File

@ -105,3 +105,4 @@ ENV/
# more
key/
.vscode

View File

@ -1,5 +1,6 @@
repos:
- repo: git://github.com/pre-commit/pre-commit-hooks
sha: v0.9.1
rev: v2.2.1
hooks:
- id: check-added-large-files
- id: check-docstring-first
@ -10,12 +11,14 @@
args:
- --exclude=__init__.py
language_version: python3
- id: autopep8-wrapper
language_version: python3
- id: requirements-txt-fixer
- id: trailing-whitespace
- repo: https://github.com/pre-commit/mirrors-autopep8
rev: 'v1.4.4'
hooks:
- id: autopep8
- repo: git://github.com/asottile/reorder_python_imports
sha: v0.3.5
rev: v1.4.0
hooks:
- id: reorder-python-imports
language_version: python3

View File

@ -1,10 +1,10 @@
sudo: false
dist: xenial
language: python
python:
- "3.4"
- "3.5"
- "3.6"
- '3.6'
- '3.7'
install: pip install tox-travis pre-commit
script:
- pre-commit run --all-files
- tox
- pre-commit run --all-files
- tox

View File

@ -1,39 +1,43 @@
FROM alpine
FROM alpine:latest
ARG tor_version
ENV HOME /var/lib/tor
RUN apk add --no-cache git libevent-dev openssl-dev gcc make automake ca-certificates autoconf musl-dev coreutils && \
mkdir -p /usr/local/src/ && \
git clone https://git.torproject.org/tor.git /usr/local/src/tor && \
cd /usr/local/src/tor && \
git checkout $(git branch -a | grep 'release' | sort -V | tail -1) && \
./autogen.sh && \
./configure \
--disable-asciidoc \
--sysconfdir=/etc \
--disable-unittests && \
make && make install && \
cd .. && \
rm -rf tor && \
apk add --no-cache python3 python3-dev && \
python3 -m ensurepip && \
rm -r /usr/lib/python*/ensurepip && \
pip3 install --upgrade pip setuptools pycrypto && \
apk del git libevent-dev openssl-dev make automake python3-dev gcc autoconf musl-dev coreutils && \
apk add --no-cache libevent openssl
RUN apk add --no-cache git libevent-dev openssl-dev gcc make automake ca-certificates autoconf musl-dev coreutils zlib-dev && \
mkdir -p /usr/local/src/ && \
git clone https://git.torproject.org/tor.git /usr/local/src/tor && \
cd /usr/local/src/tor && \
git checkout tor-$tor_version && \
./autogen.sh && \
./configure \
--disable-asciidoc \
--sysconfdir=/etc \
--disable-unittests && \
make && make install && \
cd .. && \
rm -rf tor && \
apk add --no-cache python3 python3-dev && \
python3 -m ensurepip && \
rm -r /usr/lib/python*/ensurepip && \
pip3 install --upgrade pip setuptools && \
apk del git libevent-dev openssl-dev make automake python3-dev autoconf musl-dev coreutils && \
apk add --no-cache libevent openssl
RUN mkdir -p /etc/tor/
ADD assets/entrypoint-config.yml /
ADD assets/onions /usr/local/src/onions
ADD assets/torrc /var/local/tor/torrc.tpl
COPY assets/onions /usr/local/src/onions
COPY assets/torrc /var/local/tor/torrc.tpl
RUN cd /usr/local/src/onions && python3 setup.py install
RUN cd /usr/local/src/onions && apk add --no-cache openssl-dev libffi-dev gcc python3-dev libc-dev && \
python3 setup.py install && \
apk del libffi-dev gcc python3-dev libc-dev openssl-dev
RUN mkdir -p ${HOME}/.tor && \
addgroup -S -g 107 tor && \
adduser -S -G tor -u 104 -H -h ${HOME} tor
addgroup -S -g 107 tor && \
adduser -S -G tor -u 104 -H -h ${HOME} tor
COPY assets/entrypoint-config.yml /
VOLUME ["/var/lib/tor/hidden_service/"]

View File

@ -1,23 +1,28 @@
.EXPORT_ALL_VARIABLES:
TOR_VERSION = $(shell bash last_tor_version.sh)
test:
tox
tag:
git tag v$(TOR_VERSION)
check:
pre-commit run --all-files
build:
docker-compose build
- echo build with tor version $(TOR_VERSION)
docker-compose -f docker-compose.build.yml build
rebuild:
docker-compose -f docker-compose.build.yml build --no-cache
run: build
docker-compose up
docker-compose -f docker-compose-v1.yml up
build-v2:
docker-compose -f docker-compose.v2.yml build
run-v2: build-v2
run-v2: build
docker-compose -f docker-compose.v2.yml up
build-v3:
docker-compose -f docker-compose.v3.yml build
run-v3: build-v3
run-v3: build
docker-compose -f docker-compose.v3.yml up

15
Pipfile Normal file
View File

@ -0,0 +1,15 @@
[[source]]
url = "https://pypi.python.org/simple"
verify_ssl = true
name = "pypi"
[packages]
tox = "*"
[dev-packages]
tox = "*"
pre-commit = "*"
ptpython = "*"
cryptography = "*"
pylint = "*"
autopep8 = "*"

415
Pipfile.lock generated Normal file
View File

@ -0,0 +1,415 @@
{
"_meta": {
"hash": {
"sha256": "98040ea608cfb470b37bd65b2b0092dc483e9d7781b613c1f526285eb992d3a0"
},
"pipfile-spec": 6,
"requires": {},
"sources": [
{
"name": "pypi",
"url": "https://pypi.python.org/simple",
"verify_ssl": true
}
]
},
"default": {
"filelock": {
"hashes": [
"sha256:b8d5ca5ca1c815e1574aee746650ea7301de63d87935b3463d26368b76e31633",
"sha256:d610c1bb404daf85976d7a82eb2ada120f04671007266b708606565dd03b5be6"
],
"version": "==3.0.10"
},
"pluggy": {
"hashes": [
"sha256:19ecf9ce9db2fce065a7a0586e07cfb4ac8614fe96edf628a264b1c70116cf8f",
"sha256:84d306a647cc805219916e62aab89caa97a33a1dd8c342e87a37f91073cd4746"
],
"version": "==0.9.0"
},
"py": {
"hashes": [
"sha256:64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa",
"sha256:dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53"
],
"version": "==1.8.0"
},
"six": {
"hashes": [
"sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
"sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
],
"version": "==1.12.0"
},
"toml": {
"hashes": [
"sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c",
"sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e"
],
"version": "==0.10.0"
},
"tox": {
"hashes": [
"sha256:69620e19de33a6b7ee8aeda5478791b3618ff58f0b869dbd0319fb71aa903deb",
"sha256:e5cdb1653aa27b3e46b5c390de6b6d51d31afcfdbd9d1222d82d76b82ad03d9b"
],
"index": "pypi",
"version": "==3.8.6"
},
"virtualenv": {
"hashes": [
"sha256:6aebaf4dd2568a0094225ebbca987859e369e3e5c22dc7d52e5406d504890417",
"sha256:984d7e607b0a5d1329425dd8845bd971b957424b5ba664729fab51ab8c11bc39"
],
"version": "==16.4.3"
}
},
"develop": {
"asn1crypto": {
"hashes": [
"sha256:2f1adbb7546ed199e3c90ef23ec95c5cf3585bac7d11fb7eb562a3fe89c64e87",
"sha256:9d5c20441baf0cb60a4ac34cc447c6c189024b6b4c6cd7877034f4965c464e49"
],
"version": "==0.24.0"
},
"aspy.yaml": {
"hashes": [
"sha256:ae249074803e8b957c83fdd82a99160d0d6d26dff9ba81ba608b42eebd7d8cd3",
"sha256:c7390d79f58eb9157406966201abf26da0d56c07e0ff0deadc39c8f4dbc13482"
],
"version": "==1.2.0"
},
"astroid": {
"hashes": [
"sha256:6560e1e1749f68c64a4b5dee4e091fce798d2f0d84ebe638cf0e0585a343acf4",
"sha256:b65db1bbaac9f9f4d190199bb8680af6f6f84fd3769a5ea883df8a91fe68b4c4"
],
"version": "==2.2.5"
},
"autopep8": {
"hashes": [
"sha256:33d2b5325b7e1afb4240814fe982eea3a92ebea712869bfd08b3c0393404248c"
],
"index": "pypi",
"version": "==1.4.3"
},
"cffi": {
"hashes": [
"sha256:00b97afa72c233495560a0793cdc86c2571721b4271c0667addc83c417f3d90f",
"sha256:0ba1b0c90f2124459f6966a10c03794082a2f3985cd699d7d63c4a8dae113e11",
"sha256:0bffb69da295a4fc3349f2ec7cbe16b8ba057b0a593a92cbe8396e535244ee9d",
"sha256:21469a2b1082088d11ccd79dd84157ba42d940064abbfa59cf5f024c19cf4891",
"sha256:2e4812f7fa984bf1ab253a40f1f4391b604f7fc424a3e21f7de542a7f8f7aedf",
"sha256:2eac2cdd07b9049dd4e68449b90d3ef1adc7c759463af5beb53a84f1db62e36c",
"sha256:2f9089979d7456c74d21303c7851f158833d48fb265876923edcb2d0194104ed",
"sha256:3dd13feff00bddb0bd2d650cdb7338f815c1789a91a6f68fdc00e5c5ed40329b",
"sha256:4065c32b52f4b142f417af6f33a5024edc1336aa845b9d5a8d86071f6fcaac5a",
"sha256:51a4ba1256e9003a3acf508e3b4f4661bebd015b8180cc31849da222426ef585",
"sha256:59888faac06403767c0cf8cfb3f4a777b2939b1fbd9f729299b5384f097f05ea",
"sha256:59c87886640574d8b14910840327f5cd15954e26ed0bbd4e7cef95fa5aef218f",
"sha256:610fc7d6db6c56a244c2701575f6851461753c60f73f2de89c79bbf1cc807f33",
"sha256:70aeadeecb281ea901bf4230c6222af0248c41044d6f57401a614ea59d96d145",
"sha256:71e1296d5e66c59cd2c0f2d72dc476d42afe02aeddc833d8e05630a0551dad7a",
"sha256:8fc7a49b440ea752cfdf1d51a586fd08d395ff7a5d555dc69e84b1939f7ddee3",
"sha256:9b5c2afd2d6e3771d516045a6cfa11a8da9a60e3d128746a7fe9ab36dfe7221f",
"sha256:9c759051ebcb244d9d55ee791259ddd158188d15adee3c152502d3b69005e6bd",
"sha256:b4d1011fec5ec12aa7cc10c05a2f2f12dfa0adfe958e56ae38dc140614035804",
"sha256:b4f1d6332339ecc61275bebd1f7b674098a66fea11a00c84d1c58851e618dc0d",
"sha256:c030cda3dc8e62b814831faa4eb93dd9a46498af8cd1d5c178c2de856972fd92",
"sha256:c2e1f2012e56d61390c0e668c20c4fb0ae667c44d6f6a2eeea5d7148dcd3df9f",
"sha256:c37c77d6562074452120fc6c02ad86ec928f5710fbc435a181d69334b4de1d84",
"sha256:c8149780c60f8fd02752d0429246088c6c04e234b895c4a42e1ea9b4de8d27fb",
"sha256:cbeeef1dc3c4299bd746b774f019de9e4672f7cc666c777cd5b409f0b746dac7",
"sha256:e113878a446c6228669144ae8a56e268c91b7f1fafae927adc4879d9849e0ea7",
"sha256:e21162bf941b85c0cda08224dade5def9360f53b09f9f259adb85fc7dd0e7b35",
"sha256:fb6934ef4744becbda3143d30c6604718871495a5e36c408431bf33d9c146889"
],
"version": "==1.12.2"
},
"cfgv": {
"hashes": [
"sha256:6e9f2feea5e84bc71e56abd703140d7a2c250fc5ba38b8702fd6a68ed4e3b2ef",
"sha256:e7f186d4a36c099a9e20b04ac3108bd8bb9b9257e692ce18c8c3764d5cb12172"
],
"version": "==1.6.0"
},
"cryptography": {
"hashes": [
"sha256:066f815f1fe46020877c5983a7e747ae140f517f1b09030ec098503575265ce1",
"sha256:210210d9df0afba9e000636e97810117dc55b7157c903a55716bb73e3ae07705",
"sha256:26c821cbeb683facb966045e2064303029d572a87ee69ca5a1bf54bf55f93ca6",
"sha256:2afb83308dc5c5255149ff7d3fb9964f7c9ee3d59b603ec18ccf5b0a8852e2b1",
"sha256:2db34e5c45988f36f7a08a7ab2b69638994a8923853dec2d4af121f689c66dc8",
"sha256:409c4653e0f719fa78febcb71ac417076ae5e20160aec7270c91d009837b9151",
"sha256:45a4f4cf4f4e6a55c8128f8b76b4c057027b27d4c67e3fe157fa02f27e37830d",
"sha256:48eab46ef38faf1031e58dfcc9c3e71756a1108f4c9c966150b605d4a1a7f659",
"sha256:6b9e0ae298ab20d371fc26e2129fd683cfc0cfde4d157c6341722de645146537",
"sha256:6c4778afe50f413707f604828c1ad1ff81fadf6c110cb669579dea7e2e98a75e",
"sha256:8c33fb99025d353c9520141f8bc989c2134a1f76bac6369cea060812f5b5c2bb",
"sha256:9873a1760a274b620a135054b756f9f218fa61ca030e42df31b409f0fb738b6c",
"sha256:9b069768c627f3f5623b1cbd3248c5e7e92aec62f4c98827059eed7053138cc9",
"sha256:9e4ce27a507e4886efbd3c32d120db5089b906979a4debf1d5939ec01b9dd6c5",
"sha256:acb424eaca214cb08735f1a744eceb97d014de6530c1ea23beb86d9c6f13c2ad",
"sha256:c8181c7d77388fe26ab8418bb088b1a1ef5fde058c6926790c8a0a3d94075a4a",
"sha256:d4afbb0840f489b60f5a580a41a1b9c3622e08ecb5eec8614d4fb4cd914c4460",
"sha256:d9ed28030797c00f4bc43c86bf819266c76a5ea61d006cd4078a93ebf7da6bfd",
"sha256:e603aa7bb52e4e8ed4119a58a03b60323918467ef209e6ff9db3ac382e5cf2c6"
],
"index": "pypi",
"version": "==2.6.1"
},
"docopt": {
"hashes": [
"sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491"
],
"version": "==0.6.2"
},
"filelock": {
"hashes": [
"sha256:b8d5ca5ca1c815e1574aee746650ea7301de63d87935b3463d26368b76e31633",
"sha256:d610c1bb404daf85976d7a82eb2ada120f04671007266b708606565dd03b5be6"
],
"version": "==3.0.10"
},
"identify": {
"hashes": [
"sha256:244e7864ef59f0c7c50c6db73f58564151d91345cd9b76ed793458953578cadd",
"sha256:8ff062f90ad4b09cfe79b5dfb7a12e40f19d2e68a5c9598a49be45f16aba7171"
],
"version": "==1.4.1"
},
"importlib-metadata": {
"hashes": [
"sha256:46fc60c34b6ed7547e2a723fc8de6dc2e3a1173f8423246b3ce497f064e9c3de",
"sha256:bc136180e961875af88b1ab85b4009f4f1278f8396a60526c0009f503a1a96ca"
],
"version": "==0.9"
},
"isort": {
"hashes": [
"sha256:01cb7e1ca5e6c5b3f235f0385057f70558b70d2f00320208825fa62887292f43",
"sha256:268067462aed7eb2a1e237fcb287852f22077de3fb07964e87e00f829eea2d1a"
],
"version": "==4.3.17"
},
"jedi": {
"hashes": [
"sha256:2bb0603e3506f708e792c7f4ad8fc2a7a9d9c2d292a358fbbd58da531695595b",
"sha256:2c6bcd9545c7d6440951b12b44d373479bf18123a401a52025cf98563fbd826c"
],
"version": "==0.13.3"
},
"lazy-object-proxy": {
"hashes": [
"sha256:0ce34342b419bd8f018e6666bfef729aec3edf62345a53b537a4dcc115746a33",
"sha256:1b668120716eb7ee21d8a38815e5eb3bb8211117d9a90b0f8e21722c0758cc39",
"sha256:209615b0fe4624d79e50220ce3310ca1a9445fd8e6d3572a896e7f9146bbf019",
"sha256:27bf62cb2b1a2068d443ff7097ee33393f8483b570b475db8ebf7e1cba64f088",
"sha256:27ea6fd1c02dcc78172a82fc37fcc0992a94e4cecf53cb6d73f11749825bd98b",
"sha256:2c1b21b44ac9beb0fc848d3993924147ba45c4ebc24be19825e57aabbe74a99e",
"sha256:2df72ab12046a3496a92476020a1a0abf78b2a7db9ff4dc2036b8dd980203ae6",
"sha256:320ffd3de9699d3892048baee45ebfbbf9388a7d65d832d7e580243ade426d2b",
"sha256:50e3b9a464d5d08cc5227413db0d1c4707b6172e4d4d915c1c70e4de0bbff1f5",
"sha256:5276db7ff62bb7b52f77f1f51ed58850e315154249aceb42e7f4c611f0f847ff",
"sha256:61a6cf00dcb1a7f0c773ed4acc509cb636af2d6337a08f362413c76b2b47a8dd",
"sha256:6ae6c4cb59f199d8827c5a07546b2ab7e85d262acaccaacd49b62f53f7c456f7",
"sha256:7661d401d60d8bf15bb5da39e4dd72f5d764c5aff5a86ef52a042506e3e970ff",
"sha256:7bd527f36a605c914efca5d3d014170b2cb184723e423d26b1fb2fd9108e264d",
"sha256:7cb54db3535c8686ea12e9535eb087d32421184eacc6939ef15ef50f83a5e7e2",
"sha256:7f3a2d740291f7f2c111d86a1c4851b70fb000a6c8883a59660d95ad57b9df35",
"sha256:81304b7d8e9c824d058087dcb89144842c8e0dea6d281c031f59f0acf66963d4",
"sha256:933947e8b4fbe617a51528b09851685138b49d511af0b6c0da2539115d6d4514",
"sha256:94223d7f060301b3a8c09c9b3bc3294b56b2188e7d8179c762a1cda72c979252",
"sha256:ab3ca49afcb47058393b0122428358d2fbe0408cf99f1b58b295cfeb4ed39109",
"sha256:bd6292f565ca46dee4e737ebcc20742e3b5be2b01556dafe169f6c65d088875f",
"sha256:cb924aa3e4a3fb644d0c463cad5bc2572649a6a3f68a7f8e4fbe44aaa6d77e4c",
"sha256:d0fc7a286feac9077ec52a927fc9fe8fe2fabab95426722be4c953c9a8bede92",
"sha256:ddc34786490a6e4ec0a855d401034cbd1242ef186c20d79d2166d6a4bd449577",
"sha256:e34b155e36fa9da7e1b7c738ed7767fc9491a62ec6af70fe9da4a057759edc2d",
"sha256:e5b9e8f6bda48460b7b143c3821b21b452cb3a835e6bbd5dd33aa0c8d3f5137d",
"sha256:e81ebf6c5ee9684be8f2c87563880f93eedd56dd2b6146d8a725b50b7e5adb0f",
"sha256:eb91be369f945f10d3a49f5f9be8b3d0b93a4c2be8f8a5b83b0571b8123e0a7a",
"sha256:f460d1ceb0e4a5dcb2a652db0904224f367c9b3c1470d5a7683c0480e582468b"
],
"version": "==1.3.1"
},
"mccabe": {
"hashes": [
"sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
"sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"
],
"version": "==0.6.1"
},
"nodeenv": {
"hashes": [
"sha256:ad8259494cf1c9034539f6cced78a1da4840a4b157e23640bc4a0c0546b0cb7a"
],
"version": "==1.3.3"
},
"parso": {
"hashes": [
"sha256:17cc2d7a945eb42c3569d4564cdf49bde221bc2b552af3eca9c1aad517dcdd33",
"sha256:2e9574cb12e7112a87253e14e2c380ce312060269d04bd018478a3c92ea9a376"
],
"version": "==0.4.0"
},
"pluggy": {
"hashes": [
"sha256:19ecf9ce9db2fce065a7a0586e07cfb4ac8614fe96edf628a264b1c70116cf8f",
"sha256:84d306a647cc805219916e62aab89caa97a33a1dd8c342e87a37f91073cd4746"
],
"version": "==0.9.0"
},
"pre-commit": {
"hashes": [
"sha256:75a9110eae00d009c913616c0fc8a6a02e7716c4a29a14cac9b313d2c7338ab0",
"sha256:f882c65316eb5b705fe4613e92a7c91055c1800102e4d291cfd18912ec9cf90e"
],
"index": "pypi",
"version": "==1.15.1"
},
"prompt-toolkit": {
"hashes": [
"sha256:11adf3389a996a6d45cc277580d0d53e8a5afd281d0c9ec71b28e6f121463780",
"sha256:2519ad1d8038fd5fc8e770362237ad0364d16a7650fb5724af6997ed5515e3c1",
"sha256:977c6583ae813a37dc1c2e1b715892461fcbdaa57f6fc62f33a528c4886c8f55"
],
"version": "==2.0.9"
},
"ptpython": {
"hashes": [
"sha256:51a74abe931f692360a32d650c2ba1ca329c08f3ed9b1de8abcd1164e0b0a6a7",
"sha256:938ee050e37d61c138dbbeb21383dfef8b9ed4ffb453a5f34041f42025bf5042",
"sha256:ebe9d68ea7532ec8ab306d4bdc7ec393701cd9bbd6eff0aa3067c821f99264d4"
],
"index": "pypi",
"version": "==2.0.4"
},
"py": {
"hashes": [
"sha256:64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa",
"sha256:dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53"
],
"version": "==1.8.0"
},
"pycodestyle": {
"hashes": [
"sha256:95a2219d12372f05704562a14ec30bc76b05a5b297b21a5dfe3f6fac3491ae56",
"sha256:e40a936c9a450ad81df37f549d676d127b1b66000a6c500caa2b085bc0ca976c"
],
"version": "==2.5.0"
},
"pycparser": {
"hashes": [
"sha256:a988718abfad80b6b157acce7bf130a30876d27603738ac39f140993246b25b3"
],
"version": "==2.19"
},
"pygments": {
"hashes": [
"sha256:5ffada19f6203563680669ee7f53b64dabbeb100eb51b61996085e99c03b284a",
"sha256:e8218dd399a61674745138520d0d4cf2621d7e032439341bc3f647bff125818d"
],
"version": "==2.3.1"
},
"pylint": {
"hashes": [
"sha256:5d77031694a5fb97ea95e828c8d10fc770a1df6eb3906067aaed42201a8a6a09",
"sha256:723e3db49555abaf9bf79dc474c6b9e2935ad82230b10c1138a71ea41ac0fff1"
],
"index": "pypi",
"version": "==2.3.1"
},
"pyyaml": {
"hashes": [
"sha256:1adecc22f88d38052fb787d959f003811ca858b799590a5eaa70e63dca50308c",
"sha256:436bc774ecf7c103814098159fbb84c2715d25980175292c648f2da143909f95",
"sha256:460a5a4248763f6f37ea225d19d5c205677d8d525f6a83357ca622ed541830c2",
"sha256:5a22a9c84653debfbf198d02fe592c176ea548cccce47553f35f466e15cf2fd4",
"sha256:7a5d3f26b89d688db27822343dfa25c599627bc92093e788956372285c6298ad",
"sha256:9372b04a02080752d9e6f990179a4ab840227c6e2ce15b95e1278456664cf2ba",
"sha256:a5dcbebee834eaddf3fa7366316b880ff4062e4bcc9787b78c7fbb4a26ff2dd1",
"sha256:aee5bab92a176e7cd034e57f46e9df9a9862a71f8f37cad167c6fc74c65f5b4e",
"sha256:c51f642898c0bacd335fc119da60baae0824f2cde95b0330b56c0553439f0673",
"sha256:c68ea4d3ba1705da1e0d85da6684ac657912679a649e8868bd850d2c299cce13",
"sha256:e23d0cc5299223dcc37885dae624f382297717e459ea24053709675a976a3e19"
],
"version": "==5.1"
},
"six": {
"hashes": [
"sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
"sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
],
"version": "==1.12.0"
},
"toml": {
"hashes": [
"sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c",
"sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e"
],
"version": "==0.10.0"
},
"tox": {
"hashes": [
"sha256:69620e19de33a6b7ee8aeda5478791b3618ff58f0b869dbd0319fb71aa903deb",
"sha256:e5cdb1653aa27b3e46b5c390de6b6d51d31afcfdbd9d1222d82d76b82ad03d9b"
],
"index": "pypi",
"version": "==3.8.6"
},
"typed-ast": {
"hashes": [
"sha256:035a54ede6ce1380599b2ce57844c6554666522e376bd111eb940fbc7c3dad23",
"sha256:037c35f2741ce3a9ac0d55abfcd119133cbd821fffa4461397718287092d9d15",
"sha256:049feae7e9f180b64efacbdc36b3af64a00393a47be22fa9cb6794e68d4e73d3",
"sha256:19228f7940beafc1ba21a6e8e070e0b0bfd1457902a3a81709762b8b9039b88d",
"sha256:2ea681e91e3550a30c2265d2916f40a5f5d89b59469a20f3bad7d07adee0f7a6",
"sha256:3a6b0a78af298d82323660df5497bcea0f0a4a25a0b003afd0ce5af049bd1f60",
"sha256:5385da8f3b801014504df0852bf83524599df890387a3c2b17b7caa3d78b1773",
"sha256:606d8afa07eef77280c2bf84335e24390055b478392e1975f96286d99d0cb424",
"sha256:69245b5b23bbf7fb242c9f8f08493e9ecd7711f063259aefffaeb90595d62287",
"sha256:6f6d839ab09830d59b7fa8fb6917023d8cb5498ee1f1dbd82d37db78eb76bc99",
"sha256:730888475f5ac0e37c1de4bd05eeb799fdb742697867f524dc8a4cd74bcecc23",
"sha256:9819b5162ffc121b9e334923c685b0d0826154e41dfe70b2ede2ce29034c71d8",
"sha256:9e60ef9426efab601dd9aa120e4ff560f4461cf8442e9c0a2b92548d52800699",
"sha256:af5fbdde0690c7da68e841d7fc2632345d570768ea7406a9434446d7b33b0ee1",
"sha256:b64efdbdf3bbb1377562c179f167f3bf301251411eb5ac77dec6b7d32bcda463",
"sha256:bac5f444c118aeb456fac1b0b5d14c6a71ea2a42069b09c176f75e9bd4c186f6",
"sha256:bda9068aafb73859491e13b99b682bd299c1b5fd50644d697533775828a28ee0",
"sha256:d659517ca116e6750101a1326107d3479028c5191f0ecee3c7203c50f5b915b0",
"sha256:eddd3fb1f3e0f82e5915a899285a39ee34ce18fd25d89582bc89fc9fb16cd2c6"
],
"markers": "implementation_name == 'cpython'",
"version": "==1.3.1"
},
"virtualenv": {
"hashes": [
"sha256:6aebaf4dd2568a0094225ebbca987859e369e3e5c22dc7d52e5406d504890417",
"sha256:984d7e607b0a5d1329425dd8845bd971b957424b5ba664729fab51ab8c11bc39"
],
"version": "==16.4.3"
},
"wcwidth": {
"hashes": [
"sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e",
"sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c"
],
"version": "==0.1.7"
},
"wrapt": {
"hashes": [
"sha256:4aea003270831cceb8a90ff27c4031da6ead7ec1886023b80ce0dfe0adf61533"
],
"version": "==1.11.1"
},
"zipp": {
"hashes": [
"sha256:55ca87266c38af6658b84db8cfb7343cdb0bf275f93c7afaea0d8e7a209c7478",
"sha256:682b3e1c62b7026afe24eadf6be579fb45fec54c07ea218bded8092af07a68c4"
],
"version": "==0.3.3"
}
}
}

190
README.md
View File

@ -2,7 +2,161 @@
[![Build Status](https://travis-ci.org/cmehay/docker-tor-hidden-service.svg?branch=master)](https://travis-ci.org/cmehay/docker-tor-hidden-service)
Create a tor hidden service with a link
## Setup
### Setup hosts
From 2019, new conf to handle tor v3 address has been added. Here an example with `docker-compose` v2+:
```yaml
version: "2"
services:
tor:
image: goldy/tor-hidden-service:0.3.5.8
links:
- hello
- world
- again
environment:
# Set mapping ports
HELLO_TOR_SERVICE_HOSTS: 80:hello:80,800:hello:80,8888:hello:80
# Set private key
HELLO_TOR_SERVIVE_KEY: |
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDR8TdQF9fDlGhy1SMgfhMBi9TaFeD12/FK27TZE/tYGhxXvs1C
NmFJy1hjVxspF5unmUsCk0yEsvEdcAdp17Vynz6W41VdinETU9yXHlUJ6NyI32AH
dnFnHEcsllSEqD1hPAAvMUWwSMJaNmBEFtl8DUMS9tPX5fWGX4w5Xx8dZwIDAQAB
AoGBAMb20jMHxaZHWg2qTRYYJa8LdHgS0BZxkWYefnBUbZn7dOz7mM+tddpX6raK
8OSqyQu3Tc1tB9GjPLtnVr9KfVwhUVM7YXC/wOZo+u72bv9+4OMrEK/R8xy30XWj
GePXEu95yArE4NucYphxBLWMMu2E4RodjyJpczsl0Lohcn4BAkEA+XPaEKnNA3AL
1DXRpSpaa0ukGUY/zM7HNUFMW3UP00nxNCpWLSBmrQ56Suy7iSy91oa6HWkDD/4C
k0HslnMW5wJBANdz4ehByMJZmJu/b5y8wnFSqep2jmJ1InMvd18BfVoBTQJwGMAr
+qwSwNXXK2YYl9VJmCPCfgN0o7h1AEzvdYECQAM5UxUqDKNBvHVmqKn4zShb1ugY
t1RfS8XNbT41WhoB96MT9P8qTwlniX8UZiwUrvNp1Ffy9n4raz8Z+APNwvsCQQC9
AuaOsReEmMFu8VTjNh2G+TQjgvqKmaQtVNjuOgpUKYv7tYehH3P7/T+62dcy7CRX
cwbLaFbQhUUUD2DCHdkBAkB6CbB+qhu67oE4nnBCXllI9EXktXgFyXv/cScNvM9Y
FDzzNAAfVc5Nmbmx28Nw+0w6pnpe/3m0Tudbq3nHdHfQ
-----END RSA PRIVATE KEY-----
# hello and again will share the same onion v3 address
FOO_TOR_SERVICE_HOSTS: 88:again:80,8000:world:80
FOO_TOR_SERVICE_VERSION: '3'
# tor v3 address private key base 64 encoded
FOO_TOR_SERVICE_KEY: |
PT0gZWQyNTUxOXYxLXNlY3JldDogdHlwZTAgPT0AAACArobDQYyZAWXei4QZwr++
j96H1X/gq14NwLRZ2O5DXuL0EzYKkdhZSILY85q+kfwZH8z4ceqe7u1F+0pQi/sM
hello:
image: tutum/hello-world
hostname: hello
world:
image: tutum/hello-world
hostname: world
again:
image: tutum/hello-world
hostname: again
```
This configuration will output:
```
foo: xwjtp3mj427zdp4tljiiivg2l5ijfvmt5lcsfaygtpp6cw254kykvpyd.onion:88, xwjtp3mj427zdp4tljiiivg2l5ijfvmt5lcsfaygtpp6cw254kykvpyd.onion:8000
hello: 5azvyr7dvvr4cldn.onion:80, 5azvyr7dvvr4cldn.onion:800, 5azvyr7dvvr4cldn.onion:8888
```
#### Environment variables
##### `{SERVICE}_TOR_SERVICE_HOSTS`
The config patern for this variable is: `{exposed_port}:{hostname}:{port}}`
For example `80:hello:8080` will expose a onion service on port 80 to the port 8080 of hello hostname.
You can concatenate services using comas.
##### `{SERVICE}_TOR_SERVICE_VERSION`
Can be `2` or `3`. Set the tor address type.
`2` gives short addresses `5azvyr7dvvr4cldn.onion` and `3` long addresses `xwjtp3mj427zdp4tljiiivg2l5ijfvmt5lcsfaygtpp6cw254kykvpyd.onion`
##### `{SERVICE}_TOR_SERVICE_KEY`
You can set the private key for the current service.
Tor v2 addresses uses RSA PEM keys like:
```
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDR8TdQF9fDlGhy1SMgfhMBi9TaFeD12/FK27TZE/tYGhxXvs1C
NmFJy1hjVxspF5unmUsCk0yEsvEdcAdp17Vynz6W41VdinETU9yXHlUJ6NyI32AH
dnFnHEcsllSEqD1hPAAvMUWwSMJaNmBEFtl8DUMS9tPX5fWGX4w5Xx8dZwIDAQAB
AoGBAMb20jMHxaZHWg2qTRYYJa8LdHgS0BZxkWYefnBUbZn7dOz7mM+tddpX6raK
8OSqyQu3Tc1tB9GjPLtnVr9KfVwhUVM7YXC/wOZo+u72bv9+4OMrEK/R8xy30XWj
GePXEu95yArE4NucYphxBLWMMu2E4RodjyJpczsl0Lohcn4BAkEA+XPaEKnNA3AL
1DXRpSpaa0ukGUY/zM7HNUFMW3UP00nxNCpWLSBmrQ56Suy7iSy91oa6HWkDD/4C
k0HslnMW5wJBANdz4ehByMJZmJu/b5y8wnFSqep2jmJ1InMvd18BfVoBTQJwGMAr
+qwSwNXXK2YYl9VJmCPCfgN0o7h1AEzvdYECQAM5UxUqDKNBvHVmqKn4zShb1ugY
t1RfS8XNbT41WhoB96MT9P8qTwlniX8UZiwUrvNp1Ffy9n4raz8Z+APNwvsCQQC9
AuaOsReEmMFu8VTjNh2G+TQjgvqKmaQtVNjuOgpUKYv7tYehH3P7/T+62dcy7CRX
cwbLaFbQhUUUD2DCHdkBAkB6CbB+qhu67oE4nnBCXllI9EXktXgFyXv/cScNvM9Y
FDzzNAAfVc5Nmbmx28Nw+0w6pnpe/3m0Tudbq3nHdHfQ
-----END RSA PRIVATE KEY-----
```
Tor v3 addresses uses ed25519 binary keys. It should be base64 encoded:
```
PT0gZWQyNTUxOXYxLXNlY3JldDogdHlwZTAgPT0AAACArobDQYyZAWXei4QZwr++j96H1X/gq14NwLRZ2O5DXuL0EzYKkdhZSILY85q+kfwZH8z4ceqe7u1F+0pQi/sM
```
#### Secrets
Secret key can be set through docker `secrets`, see `docker-compose.v3.yml` for example.
### Tools
A command line tool `onions` is available in container to get `.onion` url when container is running.
```sh
# Get services
$ docker exec -ti torhiddenproxy_tor_1 onions
hello: vegm3d7q64gutl75.onion:80
world: b2sflntvdne63amj.onion:80
# Get json
$ docker exec -ti torhiddenproxy_tor_1 onions --json
{"hello": ["b2sflntvdne63amj.onion:80"], "world": ["vegm3d7q64gutl75.onion:80"]}
```
### Auto reload
Changing `/etc/tor/torrc` file triggers a `SIGHUP` signal to `tor` to reload configuration.
To disable this behavior, add `ENTRYPOINT_DISABLE_RELOAD` in environment.
### Versions
Container version will follow tor release versions.
### pyentrypoint
This container uses [`pyentrypoint`](https://github.com/cmehay/pyentrypoint) to generate its setup.
If you need to use the legacy version, please checkout the `legacy` branch or pull `goldy/tor-hidden-service:legacy`.
### pytor
This containner uses [`pytor`](https://github.com/cmehay/pytor) to mannages tor cryptography, generate keys and compute onion urls.
# Legacy deprecated doc
ALL THE DOC BELLOW IS LEGACY, IT'S STILL WORKING BUT IT'S NOT RECOMMENDED ANYMORE AND COULD BE DROPPED IN FUTURE RELEASES.
### Create a tor hidden service with a link
```sh
# run a container with a network application
@ -22,8 +176,6 @@ $ docker run -ti --link something --volume /path/to/keys:/var/lib/tor/hidden_ser
Look at the `docker-compose.yml` file to see how to use it.
## Setup
### Set private key
Private key is settable by environment or by copying file in `hostname/private_key` in docket volume (`hostname` is the link name).
@ -127,35 +279,3 @@ Links setting are required when using docker-compose v2. See `docker-compose.v2.
### Copose v3 support and secrets
Links setting are required when using docker-compose v3. See `docker-compose.v3.yml` for example.
#### Secrets
Secret key can be set through docker `secrets`, see `docker-compose.v3.yml` for example.
### Tools
A command line tool `onions` is available in container to get `.onion` url when container is running.
```sh
# Get services
$ docker exec -ti torhiddenproxy_tor_1 onions
hello: vegm3d7q64gutl75.onion:80
world: b2sflntvdne63amj.onion:80
# Get json
$ docker exec -ti torhiddenproxy_tor_1 onions --json
{"hello": ["b2sflntvdne63amj.onion:80"], "world": ["vegm3d7q64gutl75.onion:80"]}
```
### Auto reload
Changing `/etc/tor/torrc` file trigger a `SIGHUP` signal to `tor` to reload configuration.
To disable this behavior, add `ENTRYPOINT_DISABLE_RELOAD` in environment.
### pyentrypoint
This container is using [`pyentrypoint`](https://github.com/cmehay/pyentrypoint) to generate its setup.
If you need to use the legacy version, please checkout the `legacy` branch or pull `goldy/tor-hidden-service:legacy`.

View File

@ -7,11 +7,13 @@ secret_env:
- '*_KEY'
- '*_PORTS'
- '*_SERVICE_NAME'
- '*_TOR_SERVICE_*'
pre_conf_commands:
- onions --setup-hosts
post_conf_commands:
- chmod -R 700 $HOME
- chown -R tor:tor $HOME
reload:

View File

@ -3,6 +3,7 @@ import argparse
import logging
import os
import sys
from base64 import b64decode
from json import dumps
from re import match
@ -46,6 +47,10 @@ class Setup(object):
assert len(key) > 800
self.setup[host]['key'] = key
def _load_keys_in_services(self):
for service in self.services:
service.load_key()
def _get_service(self, host, service):
self._add_host(host)
self.setup[host]['service'] = service
@ -66,30 +71,39 @@ class Setup(object):
if service:
return service
def add_empty_group(self, name):
def add_empty_group(self, name, version=None):
if self.find_group_by_name(name):
raise Exception('Group {name} already exists'.format(name=name))
group = ServicesGroup(name=name)
group = ServicesGroup(name=name, version=version)
self.services.append(group)
return group
def add_new_service(self, host, name=None, ports=None, key=None):
def add_new_service(self,
host,
name=None,
ports=None,
key=None):
group = self.find_group_by_name(name)
service = self.find_service_by_host(host)
if group:
service = group.get_service_by_host(host)
else:
service = self.find_service_by_host(host)
if not service:
service = Service(host=host)
if not group:
group = ServicesGroup(
service=service,
name=name,
hidden_service_dir=self.hidden_service_dir
hidden_service_dir=self.hidden_service_dir,
)
else:
group.add_service(service)
if group not in self.services:
self.services.append(group)
elif group and service not in group.services:
group.add_service(service)
else:
group = self.find_group_by_service(service)
self.find_group_by_service(service)
if key:
group.add_key(key)
if ports:
@ -108,22 +122,69 @@ class Setup(object):
self.add_new_service(host=host, ports=ports)
def _set_key(self, host, key):
self.add_new_service(host=host, key=key)
self.add_new_service(host=host, key=key.encode())
def _setup_from_env(self):
match_map = (
(r'([A-Z0-9]*)_PORTS', self._set_ports),
(r'([A-Z0-9]*)_KEY', self._set_key),
)
for key, val in os.environ.items():
for reg, call in match_map:
def _setup_from_env(self, match_map):
for reg, call in match_map:
for key, val in os.environ.items():
m = match(reg, key)
if m:
call(m.groups()[0].lower(), val)
def _setup_keys_and_ports_from_env(self):
self._setup_from_env(
(
(r'([A-Z0-9]+)_PORTS', self._set_ports),
(r'([A-Z0-9]+)_KEY', self._set_key),
)
)
def get_or_create_empty_group(self, name, version=None):
group = self.find_group_by_name(name)
if group:
if version:
group.set_version(version)
return group
return self.add_empty_group(name, version)
def _set_group_version(self, name, version):
'Setup groups with version'
group = self.get_or_create_empty_group(name, version=version)
group.set_version(version)
def _set_group_key(self, name, key):
'Set key for service group'
group = self.get_or_create_empty_group(name)
if group.version == 3:
group.add_key(b64decode(key))
else:
group.add_key(key)
def _set_group_hosts(self, name, hosts):
'Set services for service groups'
self.get_or_create_empty_group(name)
for host_map in hosts.split(','):
host_map = host_map.strip()
port_from, host, port_dest = host_map.split(':', 2)
if host == 'unix' and port_dest.startswith('/'):
self.add_new_service(host=name, name=name, ports=host_map)
else:
ports = '{frm}:{dst}'.format(frm=port_from, dst=port_dest)
self.add_new_service(host=host, name=name, ports=ports)
def _setup_services_from_env(self):
self._setup_from_env(
(
(r'([A-Z0-9]+)_TOR_SERVICE_VERSION', self._set_group_version),
(r'([A-Z0-9]+)_TOR_SERVICE_KEY', self._set_group_key),
(r'([A-Z0-9]+)_TOR_SERVICE_HOSTS', self._set_group_hosts),
)
)
def _get_setup_from_env(self):
self._set_service_names()
self._setup_from_env()
self._setup_keys_and_ports_from_env()
self._setup_services_from_env()
def _get_setup_from_links(self):
containers = DockerLinks().to_containers()
@ -162,6 +223,7 @@ class Setup(object):
self.setup = {}
self._get_setup_from_env()
self._get_setup_from_links()
self._load_keys_in_services()
self.check_services()
self.apply_conf()
@ -201,38 +263,65 @@ class Onions(Setup):
def torrc_parser(self):
self.torrc_dict = {}
def parse_dir(line):
_, path = line.split()
group_name = os.path.basename(path)
group = (self.find_group_by_name(group_name)
or self.add_empty_group(group_name))
return group
self.torrc_dict[group_name] = {
'services': [],
}
return group_name
def parse_port(line, service_group):
def parse_port(line, name):
_, port_from, dest = line.split()
service_host, port = dest.split(':')
ports_str = '{port_from}:{dest}'
name = service_host
ports_param = ports_str.format(port_from=port_from,
dest=port)
if port.startswith('/'):
name = service_group.name
service_host = name
ports_param = ports_str.format(port_from=port_from,
dest=dest)
service = (service_group.get_service_by_host(name)
or Service(name))
service.add_ports(ports_param)
if service not in service_group.services:
service_group.add_service(service)
self.torrc_dict[name]['services'].append({
'host': service_host,
'ports': ports_param,
})
def parse_version(line, name):
_, version = line.split()
self.torrc_dict[name]['version'] = int(version)
def setup_services():
for name, setup in self.torrc_dict.items():
version = setup.get('version', 2)
group = (self.find_group_by_name(name)
or self.add_empty_group(name, version=version))
for service_dict in setup.get('services', []):
host = service_dict['host']
service = (group.get_service_by_host(host)
or Service(host))
service.add_ports(service_dict['ports'])
if service not in group.services:
group.add_service(service)
self._load_keys_in_services()
if not os.path.exists(self.torrc):
return
with open(self.torrc, 'r') as f:
for line in f.readlines():
if line.startswith('HiddenServiceDir'):
service_group = parse_dir(line)
if line.startswith('HiddenServicePort'):
parse_port(line, service_group)
try:
with open(self.torrc, 'r') as f:
for line in f.readlines():
if line.startswith('HiddenServiceDir'):
name = parse_dir(line)
if line.startswith('HiddenServicePort'):
parse_port(line, name)
if line.startswith('HiddenServiceVersion'):
parse_version(line, name)
except BaseException:
raise Exception(
'Fail to parse torrc file. Please check the file'
)
setup_services()
def __str__(self):
if not self.services:
@ -257,6 +346,7 @@ def main():
help='Setup hosts')
args = parser.parse_args()
logging.getLogger().setLevel(logging.WARNING)
try:
onions = Onions()
if args.setup:
@ -264,6 +354,7 @@ def main():
else:
onions.torrc_parser()
except BaseException as e:
logging.exception(e)
error_msg = str(e)
else:
error_msg = None
@ -271,7 +362,6 @@ def main():
if error_msg:
print(dumps({'error': error_msg}))
sys.exit(1)
logging.getLogger().setLevel(logging.ERROR)
print(onions.to_json())
else:
if error_msg:

View File

@ -1,42 +1,67 @@
'This class define a service link'
import base64
import binascii
import logging
import os
import pathlib
import re
from base64 import b32encode
from hashlib import sha1
from Crypto.PublicKey import RSA
from pytor import OnionV2
from pytor import OnionV3
class ServicesGroup(object):
name = None
_priv_key = None
_key_in_secrets = False
version = None
imported_key = False
_default_version = 2
_imported_key = False
_onion = None
_hidden_service_dir = "/var/lib/tor/hidden_service/"
hidden_service_dir = "/var/lib/tor/hidden_service/"
def __init__(self, name=None, service=None, hidden_service_dir=None):
def __init__(self,
name=None,
service=None,
version=None,
hidden_service_dir=None):
name_regex = r'^[a-zA-Z0-9-_]+$'
self.hidden_service_dir = hidden_service_dir or self.hidden_service_dir
self.onion_map = {
2: OnionV2,
3: OnionV3,
}
if not name and not service:
raise Exception(
'Init service group with a name or service at least'
)
self.services = []
self.name = name or service.host
if hidden_service_dir:
self._hidden_service_dir = hidden_service_dir
if not re.match(name_regex, self.name):
raise Exception(
'Group {name} has invalid name'.format(name=self.name)
)
if service:
self.add_service(service)
self.set_version(version or self._default_version)
self.gen_key()
self.load_key()
if not self._priv_key:
self.gen_key()
def set_version(self, version):
version = int(version)
if version not in self.onion_map:
raise Exception(
'Url version {version} is not supported'.format(version)
)
self.version = version
self._onion = self.onion_map[version]()
@property
def hidden_service_dir(self):
return os.path.join(self._hidden_service_dir, self.name)
def add_service(self, service):
if service not in self.services:
@ -50,15 +75,22 @@ class ServicesGroup(object):
return service
def add_key(self, key):
if self._key_in_secrets:
if self._imported_key:
logging.warning('Secret key already set, overriding')
self._priv_key = key
self._key_in_secrets = False
# Try to decode key from base64 encoding
# import the raw data if the input cannot be decoded as base64
try:
key = base64.b64decode(key)
except binascii.Error:
pass
self._onion.set_private_key(key)
self._imported_key = True
def __iter__(self):
yield 'name', self.name
yield 'onion', self.onion_url
yield 'urls', list(self.urls)
yield 'version', self.version
def __str__(self):
return '{name}: {urls}'.format(name=self.name,
@ -66,16 +98,7 @@ class ServicesGroup(object):
@property
def onion_url(self):
"Get onion url from private key"
# Convert private RSA to public DER
priv = RSA.importKey(self._priv_key.strip())
der = priv.publickey().exportKey("DER")
# hash key, keep first half of sha1, base32 encode
onion = b32encode(sha1(der[22:]).digest()[:10])
return '{onion}.onion'.format(onion=onion.decode().lower())
return self._onion.onion_hostname
@property
def urls(self):
@ -88,30 +111,17 @@ class ServicesGroup(object):
'Write key on disk and set tor service'
if not hidden_service_dir:
hidden_service_dir = self.hidden_service_dir
serv_dir = os.path.join(hidden_service_dir, self.name)
os.makedirs(serv_dir, exist_ok=True)
os.chmod(serv_dir, 0o700)
with open(os.path.join(serv_dir, 'private_key'), 'w') as f:
f.write(self._priv_key)
os.fchmod(f.fileno(), 0o600)
with open(os.path.join(serv_dir, 'hostname'), 'w') as f:
f.write(self.onion_url)
if not os.path.isdir(hidden_service_dir):
pathlib.Path(hidden_service_dir).mkdir(parents=True)
self._onion.write_hidden_service(hidden_service_dir, force=True)
def _load_key(self, key_file):
if os.path.exists(key_file):
with open(key_file, 'r') as f:
key = f.read().encode()
if not len(key):
return
try:
rsa = RSA.importKey(key)
self._priv_key = rsa.exportKey("PEM").decode()
except BaseException:
raise('Fail to load key for {name} services'.format(
name=self.name
))
with open(key_file, 'rb') as f:
self._onion.set_private_key_from_file(f)
def load_key(self):
def load_key(self, override=False):
if self._imported_key and not override:
return
self.load_key_from_secrets()
self.load_key_from_conf()
@ -122,8 +132,9 @@ class ServicesGroup(object):
return
try:
self._load_key(secret_file)
self._key_in_secrets = True
except BaseException:
self._imported_key = True
except BaseException as e:
logging.exception(e)
logging.warning('Fail to load key from secret, '
'check the key or secret name collision')
@ -131,16 +142,17 @@ class ServicesGroup(object):
'Load key from disk if exists'
if not hidden_service_dir:
hidden_service_dir = self.hidden_service_dir
key_file = os.path.join(hidden_service_dir,
self.name,
'private_key')
self._load_key(key_file)
if not os.path.isdir(hidden_service_dir):
return
self._onion.load_hidden_service(hidden_service_dir)
def gen_key(self):
'Generate new 1024 bits RSA key for hidden service'
self._priv_key = RSA.generate(
bits=1024,
).exportKey("PEM").decode()
self.imported_key = False
return self._onion.gen_new_private_key()
@property
def _priv_key(self):
return self._onion.get_private_key()
class Ports:

View File

@ -6,7 +6,7 @@ from setuptools import setup
setup(
name='onions',
version='0.4.1',
version='0.5.0',
packages=find_packages(),
@ -31,10 +31,9 @@ setup(
"Topic :: System :: Installation/Setup",
],
install_requires=['pyentrypoint==0.5.1',
'Jinja2>=2.8',
'pycrypto', ],
install_requires=['pyentrypoint==0.5.2',
'Jinja2>=2.10',
'pytor>=0.1.2'],
entry_points={
'console_scripts': [
'onions = onions:main',

View File

@ -2,6 +2,7 @@ import json
import os
import re
from base64 import b32encode
from base64 import b64decode
from hashlib import sha1
import pytest
@ -9,8 +10,9 @@ from Crypto.PublicKey import RSA
from onions import Onions
def get_key_and_onion():
key = '''
def get_key_and_onion(version=2):
key = {}
key[2] = '''
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCsMP4gl6g1Q313miPhb1GnDr56ZxIWGsO2PwHM1infkbhlBakR
6DGQfpE31L1ZKTUxY0OexKbW088v8qCOfjD9Zk1i80JP4xzfWQcwFZ5yM/0fkhm3
@ -27,24 +29,40 @@ La/7Syrnobngsh/vX90CQB+PSSBqiPSsK2yPz6Gsd6OLCQ9sdy2oRwFTasH8sZyl
bhJ3M9WzP/EMkAzyW8mVs1moFp3hRcfQlZHl6g1U9D8=
-----END RSA PRIVATE KEY-----
'''
onion = b32encode(
onion = {}
pub = {}
onion[2] = b32encode(
sha1(
RSA.importKey(
key.strip()
key[2].strip()
).publickey().exportKey(
"DER"
)[22:]
).digest()[:10]
).decode().lower() + '.onion'
return key.strip(), onion
key[3] = '''
PT0gZWQyNTUxOXYxLXNlY3JldDogdHlwZTAgPT0AAACArobDQYyZAWXei4QZwr++j96H1X/gq14N
wLRZ2O5DXuL0EzYKkdhZSILY85q+kfwZH8z4ceqe7u1F+0pQi/sM
'''
pub[3] = '''
PT0gZWQyNTUxOXYxLXB1YmxpYzogdHlwZTAgPT0AAAC9kzftiea/kb+TWlCEVNpfUJLVk+rFIoMG
m9/hW13isA==
'''
onion[3] = 'xwjtp3mj427zdp4tljiiivg2l5ijfvmt5lcsfaygtpp6cw254kykvpyd.onion'
return key[version].strip(), onion[version]
def get_torrc_template():
return r'''
{% for service_group in services %}
HiddenServiceDir /var/lib/tor/hidden_service/{{service_group.name}}
HiddenServiceDir {{service_group.hidden_service_dir}}
{% if service_group.version == 3 %}
HiddenServiceVersion 3
{% endif %}
{% for service in service_group.services %}
{% for port in service.ports %}
{% if port.is_socket %}
@ -155,7 +173,7 @@ ff02::2 ip6-allrouters
172.17.0.2 compose_service1_1 bf447f22cdba
'''.strip()
fs.CreateFile('/etc/hosts', contents=etc_host)
fs.create_file('/etc/hosts', contents=etc_host)
monkeypatch.setattr(os, 'environ', env)
@ -190,34 +208,57 @@ def test_key(monkeypatch):
assert onion.services[0].onion_url == onion_url
def test_key_in_secret(fs, monkeypatch):
def test_key_v3(monkeypatch):
key, onion_url = get_key_and_onion(version=3)
env = {
'SERVICE1_SERVICE_NAME': 'group1',
'SERVICE2_SERVICE_NAME': 'group1',
'SERVICE3_SERVICE_NAME': 'group2',
'SERVICE1_PORTS': '80:80',
'SERVICE2_PORTS': '81:80,82:8000',
'SERVICE3_PORTS': '80:unix://unix.socket',
'GROUP1_TOR_SERVICE_HOSTS': '80:service1:80,81:service2:80',
'GROUP1_TOR_SERVICE_VERSION': '3',
'GROUP1_TOR_SERVICE_KEY': key,
}
monkeypatch.setattr(os, 'environ', env)
key, onion_url = get_key_and_onion()
onion = Onions()
onion._get_setup_from_env()
onion._load_keys_in_services()
fs.CreateFile('/run/secrets/group1', contents=key)
assert len(os.environ) == 3
assert len(onion.services) == 1
assert onion.services[0].onion_url == onion_url
def test_key_in_secret(fs, monkeypatch):
env = {
'GROUP1_TOR_SERVICE_HOSTS': '80:service1:80',
'GROUP2_TOR_SERVICE_HOSTS': '80:service2:80',
'GROUP3_TOR_SERVICE_HOSTS': '80:service3:80',
'GROUP3_TOR_SERVICE_VERSION': '3',
}
monkeypatch.setattr(os, 'environ', env)
key_v2, onion_url_v2 = get_key_and_onion()
key_v3, onion_url_v3 = get_key_and_onion(version=3)
fs.create_file('/run/secrets/group1', contents=key_v2)
fs.create_file('/run/secrets/group3', contents=b64decode(key_v3))
onion = Onions()
onion._get_setup_from_env()
onion._load_keys_in_services()
group1 = onion.find_group_by_name('group1')
group2 = onion.find_group_by_name('group2')
group3 = onion.find_group_by_name('group3')
# assert group._priv_key == key
assert group1.onion_url == onion_url
assert group2.onion_url != onion_url
assert group1.onion_url == onion_url_v2
assert group2.onion_url not in [onion_url_v2, onion_url_v3]
assert group3.onion_url == onion_url_v3
def test_configuration(fs, monkeypatch):
def test_configuration(fs, monkeypatch, tmpdir):
env = {
'SERVICE1_SERVICE_NAME': 'group1',
'SERVICE2_SERVICE_NAME': 'group1',
@ -225,44 +266,72 @@ def test_configuration(fs, monkeypatch):
'SERVICE1_PORTS': '80:80',
'SERVICE2_PORTS': '81:80,82:8000',
'SERVICE3_PORTS': '80:unix://unix.socket',
'GROUP3_TOR_SERVICE_VERSION': '2',
'GROUP3_TOR_SERVICE_HOSTS': '80:service4:888,81:service5:8080',
'GROUP4_TOR_SERVICE_VERSION': '3',
'GROUP4_TOR_SERVICE_HOSTS': '81:unix://unix2.sock',
'GROUP3V3_TOR_SERVICE_VERSION': '3',
'GROUP3V3_TOR_SERVICE_HOSTS': '80:service4:888,81:service5:8080',
'SERVICE5_TOR_SERVICE_HOSTS': '80:service5:80'
}
hidden_dir = '/var/lib/tor/hidden_service'
monkeypatch.setattr(os, 'environ', env)
monkeypatch.setattr(os, 'fchmod', lambda x, y: None)
key, onion_url = get_key_and_onion()
torrc_tpl = get_torrc_template()
fs.CreateFile('/var/local/tor/torrc.tpl', contents=torrc_tpl)
fs.CreateFile('/etc/tor/torrc')
fs.create_file('/var/local/tor/torrc.tpl', contents=torrc_tpl)
fs.create_file('/etc/tor/torrc')
fs.create_dir(hidden_dir)
onion = Onions()
onion._get_setup_from_env()
onion._load_keys_in_services()
onion.apply_conf()
onions_urls = {}
for dir in os.listdir(hidden_dir):
with open(os.path.join(hidden_dir, dir, 'hostname'), 'r') as f:
onions_urls[dir] = f.read().strip()
with open('/etc/tor/torrc', 'r') as f:
torrc = f.read()
print(torrc)
assert 'HiddenServiceDir /var/lib/tor/hidden_service/group1' in torrc
assert 'HiddenServicePort 80 service1:80' in torrc
assert 'HiddenServicePort 81 service2:80' in torrc
assert 'HiddenServicePort 82 service2:8000' in torrc
assert 'HiddenServiceDir /var/lib/tor/hidden_service/group2' in torrc
assert 'HiddenServicePort 80 unix://unix.socket' in torrc
assert 'HiddenServiceDir /var/lib/tor/hidden_service/group3' in torrc
assert 'HiddenServiceDir /var/lib/tor/hidden_service/group4' in torrc
assert 'HiddenServiceDir /var/lib/tor/hidden_service/group3v3' in torrc
assert 'HiddenServiceDir /var/lib/tor/hidden_service/service5' in torrc
assert torrc.count('HiddenServicePort 80 service4:888') == 2
assert torrc.count('HiddenServicePort 81 service5:8080') == 2
assert torrc.count('HiddenServicePort 80 service5:80') == 1
assert torrc.count('HiddenServicePort 81 unix://unix2.sock') == 1
assert torrc.count('HiddenServiceVersion 3') == 2
# Check parser
onion2 = Onions()
onion2.torrc_parser()
assert len(onion2.services) == 2
assert len(onion2.services) == 6
assert set(
group.name for group in onion2.services
) == set(['group1', 'group2'])
# ) == set(['group1', 'group2'])
) == set(['group1', 'group2', 'group3', 'group4', 'group3v3', 'service5'])
for group in onion2.services:
if group.name == 'group1':
assert len(group.services) == 2
assert group.version == 2
assert group.onion_url == onions_urls[group.name]
assert set(
service.host for service in group.services
) == set(['service1', 'service2'])
@ -279,6 +348,8 @@ def test_configuration(fs, monkeypatch):
) == set([(81, 80), (82, 8000)])
if group.name == 'group2':
assert len(group.services) == 1
assert group.version == 2
assert group.onion_url == onions_urls[group.name]
assert set(
service.host for service in group.services
) == set(['group2'])
@ -288,6 +359,53 @@ def test_configuration(fs, monkeypatch):
(port.port_from, port.dest) for port in service.ports
) == set([(80, 'unix://unix.socket')])
if group.name in ['group3', 'group3v3']:
assert len(group.services) == 2
assert group.version == 2 if group.name == 'group3' else 3
assert group.onion_url == onions_urls[group.name]
assert set(
service.host for service in group.services
) == set(['service4', 'service5'])
for service in group.services:
if service.host == 'service4':
assert len(service.ports) == 1
assert set(
(port.port_from, port.dest) for port in service.ports
) == set([(80, 888)])
if service.host == 'service5':
assert len(service.ports) == 1
assert set(
(port.port_from, port.dest) for port in service.ports
) == set([(81, 8080)])
if group.name == 'group4':
assert len(group.services) == 1
assert group.version == 3
assert group.onion_url == onions_urls[group.name]
assert set(
service.host for service in group.services
) == set(['group4'])
for service in group.services:
assert service.host == 'group4'
assert len(service.ports) == 1
assert set(
(port.port_from, port.dest) for port in service.ports
) == set([(81, 'unix://unix2.sock')])
if group.name == 'service5':
assert len(group.services) == 1
assert group.version == 2
assert group.onion_url == onions_urls[group.name]
assert set(
service.host for service in group.services
) == set(['service5'])
for service in group.services:
assert service.host == 'service5'
assert len(service.ports) == 1
assert set(
(port.port_from, port.dest) for port in service.ports
) == set([(80, 80)])
def test_groups(monkeypatch):
env = {

View File

@ -1,5 +1,8 @@
{% for service_group in services %}
HiddenServiceDir /var/lib/tor/hidden_service/{{service_group.name}}
HiddenServiceDir {{service_group.hidden_service_dir}}
{% if service_group.version == 3 %}
HiddenServiceVersion 3
{% endif %}
{% for service in service_group.services %}
{% for port in service.ports %}
{% if port.is_socket %}

11
docker-compose.build.yml Normal file
View File

@ -0,0 +1,11 @@
# docker version 3 builder
version: "3.1"
services:
tor:
image: goldy/tor-hidden-service:$TOR_VERSION
build:
context: .
args:
tor_version: $TOR_VERSION

View File

@ -1,7 +1,9 @@
# docker-compose.yml example
# LEGACY CONFIGURATION
# SEE README FOR INFORMATIONS
tor:
image: goldy/tor-hidden-service
image: goldy/tor-hidden-service:$TOR_VERSION
links:
- hello
- world

View File

@ -0,0 +1,59 @@
# docker version 2 example
version: "2"
services:
tor:
image: goldy/tor-hidden-service:$TOR_VERSION
links:
- hello
- world
- again
environment:
# Set mapping ports
HELLO_PORTS: 80:80,800:80,8888:80
# Set private key
HELLO_KEY: |
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDR8TdQF9fDlGhy1SMgfhMBi9TaFeD12/FK27TZE/tYGhxXvs1C
NmFJy1hjVxspF5unmUsCk0yEsvEdcAdp17Vynz6W41VdinETU9yXHlUJ6NyI32AH
dnFnHEcsllSEqD1hPAAvMUWwSMJaNmBEFtl8DUMS9tPX5fWGX4w5Xx8dZwIDAQAB
AoGBAMb20jMHxaZHWg2qTRYYJa8LdHgS0BZxkWYefnBUbZn7dOz7mM+tddpX6raK
8OSqyQu3Tc1tB9GjPLtnVr9KfVwhUVM7YXC/wOZo+u72bv9+4OMrEK/R8xy30XWj
GePXEu95yArE4NucYphxBLWMMu2E4RodjyJpczsl0Lohcn4BAkEA+XPaEKnNA3AL
1DXRpSpaa0ukGUY/zM7HNUFMW3UP00nxNCpWLSBmrQ56Suy7iSy91oa6HWkDD/4C
k0HslnMW5wJBANdz4ehByMJZmJu/b5y8wnFSqep2jmJ1InMvd18BfVoBTQJwGMAr
+qwSwNXXK2YYl9VJmCPCfgN0o7h1AEzvdYECQAM5UxUqDKNBvHVmqKn4zShb1ugY
t1RfS8XNbT41WhoB96MT9P8qTwlniX8UZiwUrvNp1Ffy9n4raz8Z+APNwvsCQQC9
AuaOsReEmMFu8VTjNh2G+TQjgvqKmaQtVNjuOgpUKYv7tYehH3P7/T+62dcy7CRX
cwbLaFbQhUUUD2DCHdkBAkB6CbB+qhu67oE4nnBCXllI9EXktXgFyXv/cScNvM9Y
FDzzNAAfVc5Nmbmx28Nw+0w6pnpe/3m0Tudbq3nHdHfQ
-----END RSA PRIVATE KEY-----
WORLD_PORTS: 8000:80
AGAIN_PORTS: 88:80
# hello and again will share the same onion_adress
AGAIN_SERVICE_NAME: foo
HELLO_SERVICE_NAME: foo
# Keep keys in volumes
volumes:
- tor-keys:/var/lib/tor/hidden_service/
hello:
image: tutum/hello-world
hostname: hello
world:
image: tutum/hello-world
hostname: world
again:
image: tutum/hello-world
hostname: again
volumes:
tor-keys:
driver: local

View File

@ -4,7 +4,7 @@ version: "2"
services:
tor:
image: goldy/tor-hidden-service
image: goldy/tor-hidden-service:$TOR_VERSION
build: .
links:
- world

View File

@ -4,17 +4,16 @@ version: "2"
services:
tor:
image: goldy/tor-hidden-service
build: .
image: goldy/tor-hidden-service:$TOR_VERSION
links:
- hello
- world
- again
environment:
# Set mapping ports
HELLO_PORTS: 80:80,800:80,8888:80
HELLO_TOR_SERVICE_HOSTS: 80:hello:80,800:hello:80,8888:hello:80
# Set private key
HELLO_KEY: |
HELLO_TOR_SERVIVE_KEY: |
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDR8TdQF9fDlGhy1SMgfhMBi9TaFeD12/FK27TZE/tYGhxXvs1C
NmFJy1hjVxspF5unmUsCk0yEsvEdcAdp17Vynz6W41VdinETU9yXHlUJ6NyI32AH
@ -31,13 +30,13 @@ services:
FDzzNAAfVc5Nmbmx28Nw+0w6pnpe/3m0Tudbq3nHdHfQ
-----END RSA PRIVATE KEY-----
WORLD_PORTS: 8000:80
AGAIN_PORTS: 88:80
# hello and again will share the same onion_adress
AGAIN_SERVICE_NAME: foo
HELLO_SERVICE_NAME: foo
FOO_TOR_SERVICE_HOSTS: 88:again:80,8000:world:80
FOO_TOR_SERVICE_VERSION: '3'
# tor v3 address private key base 64 encoded
FOO_TOR_SERVICE_KEY: |
PT0gZWQyNTUxOXYxLXNlY3JldDogdHlwZTAgPT0AAACArobDQYyZAWXei4QZwr++
j96H1X/gq14NwLRZ2O5DXuL0EzYKkdhZSILY85q+kfwZH8z4ceqe7u1F+0pQi/sM
# Keep keys in volumes
volumes:

View File

@ -4,23 +4,19 @@ version: "3.1"
services:
tor:
image: goldy/tor-hidden-service
build: .
image: goldy/tor-hidden-service:$TOR_VERSION
links:
- hello
- world
- again
environment:
# Set mapping ports
HELLO_PORTS: 80:80,800:80,8888:80
# Set version 3 on BAR group
BAR_TOR_SERVICE_HOSTS: '80:hello:80,88:world:80'
BAR_TOR_SERVICE_VERSION: '3'
WORLD_PORTS: 8000:80
# hello and again will share the same v2 onion_adress
FOO_TOR_SERVICE_HOSTS: '88:again:80,80:hello:80,800:hello:80,8888:hello:80'
AGAIN_PORTS: 88:80
# hello and again will share the same onion_adress
AGAIN_SERVICE_NAME: foo
HELLO_SERVICE_NAME: foo
# Keep keys in volumes
volumes:
@ -31,6 +27,9 @@ services:
- source: foo
target: foo
mode: 0400
- source: bar
target: bar
mode: 0400
hello:
image: tutum/hello-world
@ -50,4 +49,6 @@ volumes:
secrets:
foo:
file: ./foo_private_key
file: ./private_key_foo_v2
bar:
file: ./private_key_bar_v3

2
last_tor_version.sh Normal file
View File

@ -0,0 +1,2 @@
#!/bin/bash
git ls-remote --tags https://git.torproject.org/tor.git | grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -1

BIN
private_key_bar_v3 Normal file

Binary file not shown.

View File

@ -1,5 +1,5 @@
[tox]
envlist = py34, py35, py36
envlist = py34, py35, py36, py37
changedir=assets/onions/
setupdir=assets/onions/
skip_missing_interpreters = true