├── .github
└── ISSUE_TEMPLATE
│ └── issue-report.md
├── LICENSE.md
├── README.md
├── UPDATING.md
├── coturn-server
├── README.md
└── example-turnserver.conf
├── signal-android
└── README.md
├── signal-desktop
└── README.md
├── signal-docker
├── .gitignore
├── README.md
├── coturn
│ └── turnserver.conf
├── docker-compose.yml
├── nginx
│ ├── certificate.crt
│ ├── default.conf
│ └── private.key
└── pg-init-scripts
│ └── create-multiple-postgresql-databases.sh
├── signal-ios
└── README.md
├── signal-minio
├── README.md
├── docker
│ ├── config
│ │ └── certs
│ │ │ ├── private.key
│ │ │ └── public.crt
│ └── docker-compose.yml
├── modified-files
│ ├── android
│ │ └── build.gradle
│ ├── nginx
│ │ └── minio-nginx.conf
│ └── server
│ │ ├── AttachmentControllerV1.java
│ │ ├── ProfileController.java
│ │ ├── UrlSigner.java
│ │ ├── UrlSignerTest.java
│ │ ├── pom.xml
│ │ └── signal-config.yml
└── scripts
│ └── minio.sh
├── signal-server-2.92
├── DEPENDENCIES.md
├── README.md
├── example-nginx.conf
└── example-signal.yml
├── signal-server-3.21
├── MINIO.md
├── README.md
└── config.yml
├── signal-server-4.xx
├── README.md
├── config.yml
└── nginx.conf
├── signal-server-5.xx
├── README.md
├── config.yml
└── docker-compose.yml
├── signal-server-autostart
├── README.md
└── signal-example.service
├── signal-server-aws-setup
└── README.md
├── signal-server-no-twilio
├── README.md
├── modified-files
│ └── TwilioSmsSender.java
└── twillo replacement with smsgateway.me
└── signal-server-self-signed-certificate
├── README.md
└── modified-files
├── PushServiceSocket.java
└── WebSocketConnection.java
/.github/ISSUE_TEMPLATE/issue-report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Help Wanted
3 | about: Ask for help regarding the guide
4 | title: What do you need help with?
5 | labels: help wanted
6 | assignees: madeindra
7 |
8 | ---
9 |
10 | **Server Version :** (Server v2.92 / Server v3.21)
11 |
12 | **Client Version :** (Android vX.XX.XX / iOS vX.XX.XX / Desktop vX.XX.XX)
13 |
14 | **Dependencies :** (Twilio / AWS / MinIO / Nginx / Apache / Self-Signed SSL Certificate / Docker / On Premise Redis / On Premise Postgresql / On Premise Minio / Turn / Localstack)
15 |
16 | ## Describe what are you trying to achieve
17 |
18 | A clear goal that you wanted
19 |
20 | ## Describe the issue that you face
21 |
22 | Tell me which guide that you follow & which step that you do or did not do
23 |
24 | Describe the step to reproduce the errors
25 |
26 | ## Logs
27 |
28 | ```
29 | Copy your log and paste it here
30 | ```
31 |
32 | ## Screenshots
33 |
34 | Attach screenshots of your error here
35 |
36 | ## Additional context
37 |
38 | If you have any additional info, please explain it here
39 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Made Indra
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Setup Guide for Signal
2 | On this repository, you will find guides about Signal Setup that might help people who want to run their own signal server.
3 |
4 | ## Disclaimer
5 | This repository is not an official Signal Guide and there is no relation to the team behind Signal. It's just a guide written outside my work-time to help people.
6 |
7 | These guides are provided as-is, please refer to Issue section of this readme file for more information regarding this guide.
8 |
9 | Please do not contact me through my personal line or social media.
10 |
11 | ## Requirements
12 | * SSL Certificate of your server's domain (For secure communication)
13 | * Google Recaptcha (For anti-spam in authentication)
14 | * Firebase (For push notification)
15 | * Twilio (For SMS OTP & Voice Call)
16 | * AWS S3 & Cloudfront (For Attachments & CDN. Can be subtituted with MinIO & LocalStack)
17 | * AWS SQS (For CDS queue)
18 | * AWS DynamoDB (For nosql database)
19 | * Micrometer (For monitoring)
20 | * Fixer (For payment)
21 |
22 | ## Content
23 | What's proven works
24 | * [Signal Server v2.92](./signal-server-2.92/)
25 | * [Signal Server v3.21](./signal-server-3.21/)
26 | * [Signal Server v4.xx](./signal-server-4.xx/)
27 | * [Signal Server v5.xx](./signal-server-5.xx/)
28 | * [Signal Android](./signal-android/)
29 | * [Signal IOS](./signal-ios/)
30 | * [Signal Desktop](./signal-desktop/)
31 | * [Signal Dependency on Docker (Postgres & Redis)](./signal-docker/)
32 | * [Voice/Video Call (CoTurn)](./coturn-server/)
33 | * [Port Forwarding (Nginx)](./signal-server-2.92/example-nginx.conf)
34 | * [Signal Server with AWS S3 CDN & SQS](./signal-server-aws-setup/)
35 | * [Signal Server v2.92 with MinIO CDN](./signal-minio/)
36 | * [Signal Server v3.21 with MinIO CDN (Improvement & Cleaner)](./signal-server-3.21/MINIO.md)
37 | * [Signal Server Autostart Script](./signal-server-autostart/)
38 | * [Signal Server With Self Signed Certificate for Localhost](./signal-server-self-signed-certificate/)
39 | * [Signal Server Without Twilio](./signal-server-no-twilio/)
40 | * [Signal Server OTP With smsgateway.me](./signal-server-no-twilio/twillo%20replacement%20with%20smsgateway.me)
41 |
42 | What's not proven work
43 | * [Contact Discovery Service (CDS)](./signal-server-2.92/example-cds.yml), you still can use your signal server without CDS. If you want to implement CDS, please refer to [ the guide written by @konglomerat-id here](https://github.com/secure-sign/securesign-setup-guide)
44 |
45 | ## Updating
46 | If you have do some modification to your code and want to get update from the official git repo, you can follow [UPDATING](./UPDATING.md)
47 |
48 | ## FAQ
49 | Q: Will I be able to do ... with Signal?
50 |
51 | A: My suggestion is first you need to setup normal signal server and check if it will fulfill your need.
52 |
53 | Q: My chat only goes one way or can't send chat even when the server already set properly.
54 |
55 | A: Probably caused by keystore haven't contain your SSL certificate / certificate did not match the url / you have not setup the Unidentified Delivery properly in the client. Will be explained in each guide of the server & clients.
56 |
57 | Q: How to setup Turn Server? I can't do voice/video call.
58 |
59 | A: Probably related to your port, either it is not allowed by the firewall or it is not properly set. See my example config on [Coturn Server](./coturn-server/example-turnserver.conf)
60 |
61 | Q: Can I subtitute AWS to MinIO?
62 |
63 | A: I tried it here [signal-minio](./signal-minio/) using modification created by kondal789rao
64 | . Someone also did a pull request here: Use MinIO instead of the AWS. Also you can try to look into Localstack for local development.
65 |
66 | Q: Can I disable / change Twilio? (by getting the generated OTP on the server)
67 |
68 | A: Yes, it is possible, you need to change TwilioSmsSender Class, see example on [Signal Server Without Twilio](./signal-server-no-twilio/).
69 |
70 | Q: Can I use Signal in localhost / internal IP / self-signed certificate (by trusting all certificate)
71 |
72 | A: Yes, it is possible but it is not secure, see example on [Signal Server Self Signed Certificate](./signal-server-self-signed-certificate/).
73 |
74 | Q: Can I remove Google Mobile Service (GMS) from Signal?
75 |
76 | A: Yes! A Signal user named "tx-hw" did it on their Github Fork of Signal-Android. You can read more about it on their Signal Community Post
77 |
78 | ## Contributor
79 | This guide has been written by the help of the developers from community.
80 |
81 | * [konglomerat-id](https://github.com/on-premise-signal/signal-setup-guide) wrote the configuration and steps to setup signal server 2.55
82 | * [LiteSpeedDev](https://github.com/LiteSpeedDev/SignalApp-Setup) wrote nginx configuration and turn/stun server
83 | * [kondal789rao](https://community.signalusers.org/t/amazon-s3-component-replacement-for-text-secure-server-local-installation/5375/18) modified AWS to MinIO.
84 | * [madeindra](https://github.com/madeindra/setup-guide) compiled the guide, improve the steps, and wrote more guide on uncovered topics
85 | * [sinholic](https://github.com/sinholic) wrote iOS guide
86 | * [amargarido](https://github.com/amargarido) helped to improve the readme
87 | * [jacob-pro ](https://github.com/jacob-pro) added v4.xx guide & helped improving v5.xx
88 | * [tronbiniyam](https://github.com/tronbiniyam) added sms otp subtitute to use to replace Twilio
89 |
90 | You are welcome to contribute on this guide, please fork the repository and create a Pull Request.
91 |
92 | ## Issue
93 | If you have any questions please create a discussion instead of issue.
94 |
95 | You are recommended to open a discussion thread here if you face a difficulties to let the communities help you too and contributing to the communities in the process.
96 |
--------------------------------------------------------------------------------
/UPDATING.md:
--------------------------------------------------------------------------------
1 | Signal just recently release ther v3.x server code. To pull update from their repo you can follow this guide.
2 | Warning: the previous version is v2.92, updating to v.3.x might break some functionalities.
3 |
4 | 1. Open https://github.com/signalapp/Signal-Server
5 |
6 | 2. Fork the repository to your own git Repo
7 |
8 | 3. Copy the ssh (e.g. git@github.com:yourusername/Signal-Server.git)
9 |
10 | 4. Open signal-server code in your editor (e.g. VSCode)
11 |
12 | 5. Run the command in terminal (with your own ssh)
13 |
14 | ```
15 | git remote add new_origin git@github.com:yourusername/Signal-Server.git
16 | git pull new_origin
17 | git rebase new_origin/master
18 | ```
19 |
20 | 6. There will be some conflict if you made any modification to the server, you need to solve it manually, there is no other way.
21 |
--------------------------------------------------------------------------------
/coturn-server/README.md:
--------------------------------------------------------------------------------
1 | ## Installing Coturn
2 |
3 | To enable voice & video call, you need to install Turn Server.
4 |
5 | 1. Install coturn using apt-get
6 | ```
7 | sudo apt-get -y update
8 | sudo apt-get -y install coturn
9 | ```
10 |
11 | 2. Enable coturn by editing `/etc/default/coturn`, remove “#” comment on `TURNSERVER_ENABLED` to enable turnserver.
12 |
13 | 3. Create your coturn config in `/etc/turnserver.conf`, you can check [the example config](./example-turnserver.conf), some config lines are commented out to disable SSL, you can enable it by removing the “#” and editing it to your own need.
14 |
15 | 4. Run the turnserver from command line
16 | ```
17 | turnserver -o
18 | ```
19 |
20 | In some cases, some mobile provider can’t be used to access port 3478 or port 5349, try to replace the port in config with 8000 or 8080 to check if it solve the problem.
21 |
--------------------------------------------------------------------------------
/coturn-server/example-turnserver.conf:
--------------------------------------------------------------------------------
1 | listening-port=3478
2 | lt-cred-mech
3 | verbose
4 | no-tls
5 | no-dtls
6 | realm=change-to-your-domain.com
7 | mobility
8 | static-auth-secret=change-to-your-secret
9 | no-loopback-peers
10 |
--------------------------------------------------------------------------------
/signal-android/README.md:
--------------------------------------------------------------------------------
1 | # Signal Android
2 | This guide is written by using Signal-Android branch Master version 4.53.6
3 |
4 | ## Requirement
5 | * Android Studio 3.5
6 | * JRE 1.8.0
7 | * JVM OpenJDK 64
8 | * SDK 28
9 | * NDK (Optional, only if you want to make custom package)
10 |
11 | ## How to
12 | 1. Convert your server ssl cert to pkcs#12. Change the `-in` and `-inkey` argument to your public and private key. If you generate using Let's Encrypt, the public key is the `fullchain.pem` and the private key is the `privkey.pem`
13 | ```
14 | openssl pkcs12 -export -out keystore.pkcs12 -in public_key_or_certificate -inkey private_key
15 | ```
16 |
17 | 2. Use 'Keystore Explorer’ (In MacOS, you can try using another software in other OS), edit `whisper.store` files (the password is "whisper" without quote), insert your pk12 certificate there. If you use AWS CDN Cloudfront, you also need to put Cloudfront's certificate there
18 |
19 | 3. Open the project in Android Studio (Open, not Import).
20 |
21 | 4. Update URL with own server in `app/build.gradle` (be sure to use https and don't include trailing slash). If you are having a hard time finding your `Cloudfront domain`, you can find it in CloudFront console formated as `random-id.cloudfront.net`.
22 | ```
23 | ...
24 |
25 | defaultConfig {
26 | versionCode canonicalVersionCode * postFixSize
27 | versionName canonicalVersionName
28 |
29 | minSdkVersion 19
30 | targetSdkVersion 28
31 | multiDexEnabled true
32 |
33 | vectorDrawables.useSupportLibrary = true
34 | project.ext.set("archivesBaseName", "Signal");
35 |
36 | buildConfigField "long", "BUILD_TIMESTAMP", getLastCommitTimestamp() + "L"
37 | buildConfigField "String", "SIGNAL_URL", "\"https://domain.com\""
38 | buildConfigField "String", "STORAGE_URL", "\"https://cloudfrontdomain.com\""
39 | buildConfigField "String", "SIGNAL_CDN_URL", "\"https://cloudfrontdomain.com\""
40 | buildConfigField "String", "SIGNAL_CONTACT_DISCOVERY_URL", "\"https://domain.com\""
41 | buildConfigField "String", "SIGNAL_SERVICE_STATUS_URL", "\"https://domain.com\""
42 | buildConfigField "String", "SIGNAL_KEY_BACKUP_URL", "\"https://domain.com\""
43 | buildConfigField "String", "CONTENT_PROXY_HOST", "\"https://domain.com\""
44 | buildConfigField "int", "CONTENT_PROXY_PORT", "443"
45 | buildConfigField "String", "USER_AGENT", "\"OWA\""
46 | buildConfigField "boolean", "DEV_BUILD", "false"
47 | buildConfigField "String", "MRENCLAVE", "\"cd6cfc342937b23b1bdd3bbf9721aa5615ac9ff50a75c5527d441cd3276826c9\""
48 | buildConfigField "String", "KEY_BACKUP_ENCLAVE_NAME", "\"f2e2a5004794a6c1bac5c4949eadbc243dd02e02d1a93f10fe24584fb70815d8\""
49 | buildConfigField "String", "KEY_BACKUP_MRENCLAVE", "\"f51f435802ada769e67aaf5744372bb7e7d519eecf996d335eb5b46b872b5789\""
50 | buildConfigField "String", "UNIDENTIFIED_SENDER_TRUST_ROOT", "\"public-key-generated-in-signal-server-step-3\""
51 | buildConfigField "String[]", "LANGUAGES", "new String[]{\"" + autoResConfig().collect { s -> s.replace('-r', '_') }.join('", "') + '"}'
52 | buildConfigField "int", "CANONICAL_VERSION_CODE", "$canonicalVersionCode"
53 |
54 | ...
55 | ```
56 |
57 | 5. update `UNIDENTIFIED_SENDER_TRUST_ROOT` (value is Public Key from when creating UnidentifiedDelivery of server’s config.yml)
58 |
59 |
60 | 6. Comment out `distributionSha256Sum` on `gradle/wrapper/gradle-wrapper.properties`
61 | ```
62 | distributionBase=GRADLE_USER_HOME
63 | distributionPath=wrapper/dists
64 | # distributionSha256Sum=027fdd265d277bae65a0d349b6b8da02135b0b8e14ba891e26281fa877fe37a2
65 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
66 | zipStoreBase=GRADLE_USER_HOME
67 | zipStorePath=wrapper/dists
68 | ```
69 | 7. Download `google-service.json` from Firebase, put it inside `app/`.
70 |
71 | 8. Update `app/src/main/res/values/firebase_messaging.xml` according to value from `google-service.json`
72 |
73 | 9. Update `ATTACHMENT_DOWNLOAD_PATH` and `ATTACHMENT_UPLOAD_PATH` in `libsignal/service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java` by deleting ‘attachments/‘ so attachment will be uploaded in root ( / ). If you don't want the attachments to be uploaded to root bucket, check the FAQ part of this guide.
74 |
75 | 10. Sync your project then build.
76 |
77 | ## Custom Server
78 | Change `app/build.gradle` to use your server. Always use https and without trailing slash on the url.
79 | ```
80 | // app/build.gradle
81 |
82 | buildConfigField "long", "BUILD_TIMESTAMP", getLastCommitTimestamp() + "L"
83 | buildConfigField "String", "SIGNAL_URL", "\"https://domain.com\""
84 | buildConfigField "String", "STORAGE_URL", "\"https://domain.com\""
85 | buildConfigField "String", "SIGNAL_CDN_URL", "\"https://your-own.cloudfrontnet\""
86 | buildConfigField "String", "SIGNAL_CONTACT_DISCOVERY_URL", "\"https://domain.com\""
87 | buildConfigField "String", "SIGNAL_SERVICE_STATUS_URL", "\"https://domain.com\""
88 | buildConfigField "String", "SIGNAL_KEY_BACKUP_URL", "\"https://domain.com\""
89 | buildConfigField "String", "CONTENT_PROXY_HOST", "\"https://domain.com\""
90 | buildConfigField "int", "CONTENT_PROXY_PORT", "443"
91 | buildConfigField "String", "USER_AGENT", "\"OWA\""
92 | buildConfigField "boolean", "DEV_BUILD", "false"
93 | buildConfigField "String", "MRENCLAVE", "\"cd6cfc342937b23b1bdd3bbf9721aa5615ac9ff50a75c5527d441cd3276826c9\""
94 | buildConfigField "String", "KEY_BACKUP_ENCLAVE_NAME", "\"f2e2a5004794a6c1bac5c4949eadbc243dd02e02d1a93f10fe24584fb70815d8\""
95 | buildConfigField "String", "KEY_BACKUP_MRENCLAVE", "\"f51f435802ada769e67aaf5744372bb7e7d519eecf996d335eb5b46b872b5789\""
96 | buildConfigField "String", "UNIDENTIFIED_SENDER_TRUST_ROOT", "\"CHANGE-TO-YOUR-UNIDENTIFIED-DELIVERY-PUBLIC-KEY\""
97 |
98 | ```
99 |
100 | ## Custom Package Name
101 | To create your own Signal-based Chat application, you need to rename the package. This is to prevent your app conflicting with signal on Play Store and on user's phone.
102 |
103 | 1. Open Signal-Android in Android Studio.
104 |
105 | Signal-Android package name is `org.thoughtcrime.securesms` and in the project it's shown as a structure like this
106 |
107 | `app/src/main/java/org/thoughtcrime/securesms/`
108 |
109 | It is recommended that your desired package name also consist of three part, for example `com.company.chatname`.
110 |
111 | 2. Right click on `securesms` directory, choose `Refactor`, then choose `Rename`. When prompted, choose `Rename Package`. Change it to your desired `chatname`. It is optional to choose `Search in comments and strings` and `Search for text occurance`, you can leave it blank.
112 |
113 | 3. After the search process is done, a window will appear on the bottom. Choose `Do Refactor`.
114 |
115 | 4. It will take some time, after it is done, repeat step 2-3 to `thoughtcrime` and `org`.
116 |
117 | 5. There's a chance some import could be left unchanged, to make sure, find `org.thoughtcrime.securesms`. If you found some, change it manually.
118 |
119 | If you have done those step, you can already build your own app and install it to android. But the camera will not work because you haven't change the package name in native code.
120 |
121 | 6. Change the package name in native code, find `org_thoughtcrime_securesms` and replace with your package name, **remember to use underscore (_) like how it is before**. It is located on:
122 |
123 | `app/jni/Android.mk`
124 | ```
125 | JNI_DIR := $(call my-dir)
126 |
127 | include $(CLEAR_VARS)
128 |
129 | LOCAL_MODULE := native-utils
130 | LOCAL_C_INCLUDES := $(JNI_DIR)/utils/
131 | LOCAL_CFLAGS += -Wall
132 |
133 | LOCAL_SRC_FILES := $(JNI_DIR)/utils/com_company_chatname_util_FileUtils.cpp
134 |
135 | include $(BUILD_SHARED_LIBRARY)
136 | ```
137 |
138 | `app/jni/utils/org_thoughtcrime_securesms_util_FileUtils.cpp`
139 | ```
140 | #include "com_company_chatname_util_FileUtils.h"
141 |
142 | ...
143 |
144 | jint JNICALL Java_com_company_chatname_util_FileUtils_getFileDescriptorOwner
145 |
146 | ...
147 |
148 | JNIEXPORT jint JNICALL Java_com_company_chatname_util_FileUtils_createMemoryFileDescriptor
149 |
150 | ...
151 | ```
152 |
153 | `app/jni/utils/org_thoughtcrime_securesms_util_FileUtils.h`
154 | ```
155 | /* DO NOT EDIT THIS FILE - it is machine generated */
156 | #include
157 | /* Header for class com_company_chatname_util_FileUtils */
158 |
159 | #ifndef _Included_com_company_chatname_util_FileUtils
160 | #define _Included_com_company_chatname_util_FileUtils
161 |
162 | ...
163 |
164 | /*
165 | * Class: com_company_chatname_util_FileUtils
166 | * Method: getFileDescriptorOwner
167 | * Signature: (Ljava/io/FileDescriptor;)I
168 | */
169 | JNIEXPORT jint JNICALL Java_com_company_chatname_util_FileUtils_getFileDescriptorOwner
170 | (JNIEnv *, jclass, jobject);
171 |
172 | /*
173 | * Class: com_company_chatname_util_FileUtils
174 | * Method: createMemoryFileDescriptor
175 | * Signature: (Ljava/lang/String;)I
176 | */
177 | JNIEXPORT jint JNICALL Java_com_company_chatname_util_FileUtils_createMemoryFileDescriptor
178 | (JNIEnv *, jclass, jstring);
179 |
180 | ...
181 | ```
182 |
183 | The last two files (.cpp and .h) also need to be renamed to follow your new package name, for example:
184 |
185 | * `app/jni/utils/com_company_chatname_util_FileUtils.cpp`
186 | * `app/jni/utils/com_company_chatname_util_FileUtils.h`
187 |
188 |
189 | 7. Install NDK, and run this command in your directory
190 |
191 | ```
192 | ndk-build
193 | ```
194 |
195 | If the command is not recognized, it means your NDK installation has not been added to Environment Variable, then run it with absolute path.
196 |
197 | 8. It will generate 4 `libnative-utils.so` inside `app/jni`. Use it to replace the originals that located in here:
198 | ```
199 | app
200 | └── src
201 | └── main
202 | └── jniLibs
203 | ├── arm64-v8a
204 | │ └── libnative-utils.so
205 | ├── armeabi-v7a
206 | │ └── libnative-utils.so
207 | ├── x86
208 | │ └── libnative-utils.so
209 | └── x86_64
210 | └── libnative-utils.so
211 | ```
212 |
213 | 9. Update the package name that formated as `org/thoughtcrime/securesms` and replace with your package name, **remember to use slash (/) like how it is before**. It is located on:
214 |
215 | `app/lint-baseline.xml`
216 | ```
217 | ...
218 |
219 | file="src/main/java/com/company/chatname/util/dualsim/SubscriptionManagerCompat.java"
220 |
221 | ...
222 |
223 | file="src/main/java/com/company/chatname/contacts/ContactSelectionListAdapter.java"
224 |
225 | ...
226 | ```
227 |
228 | `app/lint.xml`
229 | ```
230 | ...
231 |
232 |
233 |
234 |
235 | ...
236 | ```
237 |
238 | 10. Remember to update the application name in :
239 |
240 | `app/src/main/res/values/strings.xml`
241 |
242 | ## FAQ
243 | Q: How do i build an APK?
244 |
245 | A: You can just run `./gradlew clean assemblePlayRelease --exclude-task signProductionPlayRelease`. For more detailed instruction, follow Official Signal Android ReproducibleBuild Guide.
246 |
247 | Q: Why did I need to change the Attachment Path?
248 |
249 | A: For now, I have no idea why you can't upload to a path except root, I've tried modifying every permission in AWS but to no avail.
250 |
251 | Q: How did I make Maps works?
252 |
253 | A: On Android, search for `com.google.android.geo.API_KEY` and change the value below that line with your own Google Maps API Key. Remember to `Enable` `Maps SDK for Android`
254 |
255 | Q: How did I make Giphy/Sticker works?
256 |
257 | A: Don't change `CONTENT_PROXY_URL` on your `build.gradle` but you may change the Giphy API key on android.
258 |
259 | Q: What can I do so the attachments not uploaded to root bucket?
260 |
261 | A: You still need to do `Step 9`, but only remove the `attachments` part from `ATTACHMENT_UPLOAD_PATH`. Then modify your Signal-Server.
262 |
263 | First open `AttachmentControllerV2.java` located in `service/src/main/java/org/whispersystems/textsecuregcm/controllers/` and find this line:
264 | ```
265 | String objectName = String.valueOf(attachmentId);
266 | ```
267 |
268 | Modify it to:
269 | ```
270 | String objectName = "attachments/" + String.valueOf(attachmentId);
271 | ```
272 |
273 | Then open `AttachmentControllerTest.java` located in `service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/` and find this line:
274 | ```
275 | assertThat(descriptor.getKey()).isEqualTo(descriptor.getAttachmentIdString());
276 | ```
277 |
278 | Modify it to:
279 | ```
280 | assertThat(descriptor.getKey()).isEqualTo("attachments/" + descriptor.getAttachmentIdString());
281 | ```
282 |
283 | Then re-build the server and run it.
284 |
285 | Q: My app show `Your version of Signal has Expired`
286 |
287 | A: Please modify `getDaysTillBuildExpiry()` in `Util.java` to this:
288 |
289 | ```
290 | public static int getDaysTillBuildExpiry() {
291 | return Integer.MAX_VALUE;
292 | }
293 | ```
294 |
295 | Q: Is there any way to change package name in easier way?
296 |
297 | A: You can try adding `applicationId` under `defaultConfig`, you can find it inside `app/build.gradle`
298 |
299 | ```
300 | defaultConfig {
301 | ...
302 |
303 | applicationId "your.package.name"
304 |
305 | ...
306 | }
307 | ```
308 |
--------------------------------------------------------------------------------
/signal-desktop/README.md:
--------------------------------------------------------------------------------
1 | # Signal Desktop
2 | This guid is written by using Signal Desktop on branch Master version 1.30.0-beta.4
3 |
4 | ## Requirement
5 | * NVM 12.4.0
6 |
7 | ## How To
8 | 1. Install requirement
9 |
10 | on MacOS, Install Command Line Tools & git-lfs, to install run these commands.
11 | ```
12 | xcode-select --install
13 | sudo xcode-select --switch /Library/Developer/CommandLineTools
14 | brew install git-lfs
15 | ```
16 |
17 | On Win7, Install .Net 4.5.1 & Windows SDK 8.1 & Windows Build Tools. Then run this command.
18 | ```
19 | npm install --global --production --add-python-to-path windows-build-tools
20 | ```
21 |
22 | On Linux, Install python, gcc, g++, make
23 | ```
24 | sudo apt-get install python, gcc, g++, make
25 | ```
26 |
27 | 2. Clone signal-desktop
28 |
29 | 3. Mount signal desktop directory
30 | ```
31 | cd Signal-Desktop
32 | ```
33 |
34 | 4. Install yarn
35 | ```
36 | npm install --global yarn
37 | ```
38 |
39 | 5. Install & build with yarn
40 | ```
41 | yarn install --frozen-lockfile
42 | ```
43 |
44 | 6. Generate final JS & CSS
45 | ```
46 | yarn grunt
47 | ```
48 |
49 | 7. Generate full-set icon
50 | ```
51 | yarn icon-gen
52 | ```
53 |
54 | 8. Build with webpack
55 | ```
56 | yarn build:webpack
57 | ```
58 |
59 | 9. You can test with
60 | ```
61 | yarn test
62 | ```
63 |
64 | 10. Start the app
65 | ```
66 | yarn start
67 | ```
68 |
69 | 11. To connect to own production server, `create local-development.json`, the value is the same as `production.json` but without `updateEnabled`.
70 | ```
71 | {
72 | "serverUrl": "https://domain.com",
73 | "cdnUrl": "https://cdn-domain.com",
74 | "serverTrustRoot": "public-key-generated-in-signal-server-step-4",
75 | "updatesEnabled": true
76 | }
77 | ```
78 |
79 | ## Using own Server
80 | 1. Update `config/deafult.json`, set serverUrl & cdnUrl by using your Server URL & your CDN url, **don’t include trailing slash on serverUrl and cdnUrl**.
81 |
82 | 2. Update `config/default.json`, set certificateAuthority using CA’s SSL Certificate.
83 |
84 | 3. Update `config/default.json`, set serverTrustRoot using your CAPublicKey (Also used in android as UNIDENTIFIED SENDER TRUST ROOT).
85 |
86 | 4. Update `js/modules/web_api.js`, find functions called `getAttachment` and `putAttachment`, then replace `${cdnUrl}/attachments/${id}` with `${cdnUrl}/` **(Do this if you do the same in android client)**.
87 |
88 | 5. Still on the same functions, set a default value for `certificateAuthority` variable using your Certificate Authority (see below), **remember new line in your certificateAuthority needs to be formatted to “\n”**.
89 |
90 | **change this**
91 | ```
92 | ...
93 | certificateAuthority,
94 | ...
95 | ```
96 |
97 |
98 | **to this**
99 | ```
100 | ...
101 | certificateAuthority: "-----BEGIN CERTIFICATE-----\n change-to-your-certificate \n-----END CERTIFICATE-----\n",
102 | ...
103 | ```
104 |
105 |
106 | 6. Run the commands to rebuild the project
107 | ```
108 | yarn generate
109 | yarn build
110 | yarn start
111 | ```
112 |
113 | ## FAQ
114 | Q: I already have node but the version is not 12.4.0
115 |
116 | A: You can run `nvm use 12.4.0` to use the node 12.4.0 on the project.
117 |
118 | Q: How to create installation package?
119 |
120 | A: After running `yarn build` the package will be listed on `release` directory.
121 |
122 | Q: I did a change to the assets but the change did not reflect, why?
123 |
124 | A: You can run `yarn grunt dev` first.
125 |
126 | Q: How can I run the development server for Sticker Creator?
127 |
128 | A: Run `yarn dev`
129 |
130 | Q: How to enable request to development server?
131 |
132 | A: On Linux & Mac run this command to start the development
133 | ```
134 | SIGNAL_ENABLE_HTTP=1 yarn start
135 | ```
136 |
137 | Q: How could I get certificate authority?
138 |
139 | A: I called it the certificate of the certificate issuer. You can try browsing a https web using chrome, click on lock pad beside the URL address bar, then click on "Certificate (Valid)", you will see the top most & orange certificate, that is what you need, drag it to your desktop to save it.
140 |
141 | Q: Certificate Authority from browser is not in readable format, what should i do?
142 |
143 | A: If you got .cer format, then convert your certificate from DER to PEM by running this command. After this, you can open your pem certificate in any editor.
144 | ```
145 | openssl x509 -inform der -in certificate_name.cer -out certificate_name.pem
146 | ```
147 |
--------------------------------------------------------------------------------
/signal-docker/.gitignore:
--------------------------------------------------------------------------------
1 | postgres_database
2 | redis_main
3 | redis_replication
--------------------------------------------------------------------------------
/signal-docker/README.md:
--------------------------------------------------------------------------------
1 | # Postgres & Redis on Docker
2 |
3 | This is signal dependencies setup on Docker to make development easier.
4 |
5 | | container_name | image | port | info |
6 | |-------------------------|---------------------------------------|----------|---------------------------------------------------|
7 | | signal_database | postgres:11 | 5432 | |
8 | | redis_main | redis:5 | 6379 | |
9 | | redis_replication | redis:5 | 6380 | |
10 | | nginx | nginx:1.20 | 80,443 | optional, if you already have ssl cert |
11 | | coturn | coturn/coturn:4.5 | 3478 | optional, easier setup |
12 | | signal_adminer | adminer:latest | 8000 | optional, database management with web GUI |
13 | | redis_commander | rediscommander/redis-commander:latest | 8001 | optional, cache management with web GUI |
14 |
15 | ## Starting up
16 |
17 | ```
18 | docker-compose up -d
19 | ```
20 |
21 | ## Postgres Multiple Database
22 | If you encounter `bad permission` error during running it up, please modify the permission of the script
23 |
24 | ```
25 | sudo chmod +x create-multiple-postgresql-databases.sh
26 | ```
27 |
28 | ## Nginx
29 | If you already have SSL Certicicate & Private key, please put it inside `nginx` directory and rename it to `certificate.crt` and `private.key`, then modify `default.conf` to use your own domain name in it.
30 |
31 | If you prefer non docker containerized nginx, please refer to Signal-Server guide.
32 |
33 | ## CoTurn
34 | If you want easier setup, you can use coturn in container, be sure to update `coturn.conf` inside `coturn` directory to change domain name and secret used for STUN/TURN.
35 |
36 | If you prefer non docker containerized nginx, please refer to Coturn-Server guide.
37 |
38 | ## For macOS users
39 | If yor server is macOs you are limited to specific folders to mount docker volumes, so for example
40 | the volume on "./redis_master:/data" may not be mounted and produce an error since the relative path "./" is not likely to be included in the inclusion list.
41 | In order set up the volumes properly refer to:
42 | https://docs.docker.com/docker-for-mac/osxfs/#namespaces
43 |
44 | ## Known Issue
45 | Docker have issue with `firewalld` with `iptables`, I suggest you use `ufw` instead to prevent such issue.
46 |
--------------------------------------------------------------------------------
/signal-docker/coturn/turnserver.conf:
--------------------------------------------------------------------------------
1 | listening-port=3478
2 | verbose
3 | no-tls
4 | no-dtls
5 | realm=domain.com
6 | mobility
7 | static-auth-secret=secret
8 | no-loopback-peers
9 |
--------------------------------------------------------------------------------
/signal-docker/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '2.2'
2 | services:
3 | signal_database:
4 | image: postgres:11
5 | container_name: postgres_database
6 | restart: always
7 | environment:
8 | POSTGRES_USER: postgres
9 | POSTGRES_PASSWORD: postgres
10 | POSTGRES_MULTIPLE_DATABASES: abusedb, accountdb, keysdb, messagedb
11 | PGDATA: /var/lib/postgresql/data/pgdata
12 | ports:
13 | - '5432:5432'
14 | volumes:
15 | - ./postgres_database:/var/lib/postgresql/data
16 | - ./pg-init-scripts:/docker-entrypoint-initdb.d
17 | redis_main:
18 | image: redis:5
19 | container_name: redis_main
20 | restart: always
21 | ports:
22 | - '6379:6379'
23 | volumes:
24 | - ./redis_main:/data
25 | redis_replication:
26 | image: redis:5
27 | container_name: redis_replication
28 | restart: always
29 | command: redis-server --port 6380
30 | ports:
31 | - '6380:6380'
32 | volumes:
33 | - ./redis_replication:/data
34 |
35 | # Remove lines below if you don't plan to use NginX in container mode or don't have SSL certificate
36 | nginx:
37 | image: nginx:1.20
38 | container_name: nginx
39 | restart: always
40 | ports:
41 | - 80:80
42 | - 443:443
43 | volumes:
44 | - ./nginx/default.conf:/etc/nginx/conf.d/default.conf
45 | - ./nginx/private.key:/etc/nginx/cert/private.key
46 | - ./nginx/certificate.crt:/etc/nginx/cert/certificate.crt
47 |
48 | # Remove lines below if you don't plan to use CoTurn in container mode
49 | coturn:
50 | image: coturn/coturn:4.5
51 | container_name: coturn
52 | restart: always
53 | ports:
54 | - 3478:3478
55 | - 3478:3478/udp
56 | - 49160-65535:49160-65535/udp
57 | volumes:
58 | - ./coturn/turnserver.conf:/etc/coturn/turnserver.conf
59 |
60 | # Remove lines below if you don't want PostgresDB management GUI
61 | signal_adminer:
62 | container_name: signal_adminer
63 | image: adminer:latest
64 | ports:
65 | - 8000:8080
66 | links:
67 | - signal_database
68 | environment:
69 | ADMINER_DESIGN: nette
70 | networks:
71 | - default
72 |
73 | # Remove lines below if you don't want Redis management GUI
74 | redis_commander:
75 | container_name: redis_commander
76 | image: rediscommander/redis-commander:latest
77 | hostname: redis-commander
78 | ports:
79 | - 8001:8081
80 | links:
81 | - redis_main
82 | - redis_replication
83 | environment:
84 | - REDIS_HOSTS=main:redis_main:6379, replica:redis_replication:6380
85 |
--------------------------------------------------------------------------------
/signal-docker/nginx/certificate.crt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/madeindra/signal-setup-guide/c692873d1adea17dd6d71c9feed0ed5dcd7e4d63/signal-docker/nginx/certificate.crt
--------------------------------------------------------------------------------
/signal-docker/nginx/default.conf:
--------------------------------------------------------------------------------
1 | server {
2 | listen 443 ssl http2;
3 | listen [::]:443 ssl http2;
4 |
5 | server_name domain.com;
6 |
7 | # SSL
8 | ssl_certificate /etc/nginx/cert/certificate.crt;
9 | ssl_certificate_key /etc/nginx/cert/private.key;
10 |
11 | # security headers
12 | add_header X-Frame-Options "SAMEORIGIN" always;
13 | add_header X-XSS-Protection "1; mode=block" always;
14 | add_header X-Content-Type-Options "nosniff" always;
15 | add_header Referrer-Policy "no-referrer-when-downgrade" always;
16 | add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
17 | add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
18 |
19 | # . files
20 | location ~ /\.(?!well-known) {
21 | deny all;
22 | }
23 |
24 | # reverse proxy
25 | location / {
26 | proxy_pass http://host.docker.internal:8080;
27 | proxy_http_version 1.1;
28 | proxy_cache_bypass $http_upgrade;
29 |
30 | proxy_set_header Upgrade $http_upgrade;
31 | proxy_set_header Connection "upgrade";
32 | proxy_set_header Host $host;
33 | proxy_set_header X-Real-IP $remote_addr;
34 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
35 | proxy_set_header X-Forwarded-Proto $scheme;
36 | proxy_set_header X-Forwarded-Host $host;
37 | proxy_set_header X-Forwarded-Port $server_port;
38 | }
39 |
40 | location /v1/websocket {
41 | proxy_pass http://host.docker.internal:8080/v1/websocket;
42 | proxy_http_version 1.1;
43 | proxy_cache_bypass $http_upgrade;
44 |
45 | proxy_set_header Upgrade $http_upgrade;
46 | proxy_set_header Connection "upgrade";
47 | proxy_set_header Host $host;
48 | proxy_set_header X-Real-IP $remote_addr;
49 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
50 | proxy_set_header X-Forwarded-Proto $scheme;
51 | proxy_set_header X-Forwarded-Host $host;
52 | proxy_set_header X-Forwarded-Port $server_port;
53 | }
54 |
55 | # If you use Minio, please add minio port forwarding here
56 | }
57 |
58 | # HTTP redirect to HTTPS
59 | server {
60 | listen 80;
61 | listen [::]:80;
62 |
63 | server_name domain.com;
64 |
65 | location / {
66 | return 301 https://domain.com$request_uri;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/signal-docker/nginx/private.key:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/madeindra/signal-setup-guide/c692873d1adea17dd6d71c9feed0ed5dcd7e4d63/signal-docker/nginx/private.key
--------------------------------------------------------------------------------
/signal-docker/pg-init-scripts/create-multiple-postgresql-databases.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 | set -u
5 |
6 | function create_user_and_database() {
7 | local database=$1
8 | echo " Creating user and database '$database'"
9 | psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSQL
10 | CREATE USER $database;
11 | CREATE DATABASE $database;
12 | GRANT ALL PRIVILEGES ON DATABASE $database TO $database;
13 | EOSQL
14 | }
15 |
16 | if [ -n "$POSTGRES_MULTIPLE_DATABASES" ]; then
17 | echo "Multiple database creation requested: $POSTGRES_MULTIPLE_DATABASES"
18 | for db in $(echo $POSTGRES_MULTIPLE_DATABASES | tr ',' ' '); do
19 | create_user_and_database $db
20 | done
21 | echo "Multiple databases created"
22 | fi
--------------------------------------------------------------------------------
/signal-ios/README.md:
--------------------------------------------------------------------------------
1 | # Signal iOS
2 | This guide is written by using Signal-iOS branch Master version 3.13.2 build 3.13.2.6
3 |
4 | ## Requirement
5 | * XCode 11.x (Latest Stable)
6 | * Signal Server v3.21 (When I use 2.92 it always got error when profiling)
7 | * A named cdn domain with 1 CA SSL
8 |
9 |
10 | ## How to
11 | 1. Clone the signal iOS from github
12 | ```
13 | git clone --recurse-submodules https://github.com/signalapp/Signal-iOS
14 | ```
15 | 2. Install Dependency.
16 | ```
17 | cd Signal-iOS && make dependencies
18 | ```
19 | 3. Open `Signal.xcworkspace` XCode.
20 |
21 | 4. Open `SignalServiceKit/TSConstants.swift` to direct the app to your own server. Change the variables in `TSConstantsStaging` and `TSConstantsProduction`.
22 | ```
23 | public let textSecureWebSocketAPI = "wss://your-domain.com/v1/websocket/"
24 | public let textSecureServerURL = "https://your-domain.com/"
25 | public let textSecureCDN0ServerURL = "https://your-cdn-domain.com"
26 | public let textSecureCDN2ServerURL = "https://your-cdn-domain.com"
27 | public let contactDiscoveryURL = "https://your-domain.com"
28 | public let keyBackupURL = "https://your-domain.com"
29 | public let storageServiceURL = "https://your-cdn-domain.com"
30 | public let kUDTrustRoot = "public-key-generated-in-signal-server-step-3"
31 | ...
32 |
33 | public let serverPublicParamsBase64 = "change-to-zkparams-public-key"
34 | }
35 | ```
36 |
37 | 5. Open `OWSAttachmentDownloads.m` and `OWSUploadV2.m` to change your downloaded attachment so it match with the android version.
38 | ```diff
39 | OWSAttachmentDownloads.m
40 |
41 | - (void)downloadJob:(OWSAttachmentDownloadJob *)job
42 | attachmentPointer:(TSAttachmentPointer *)attachmentPointer
43 | success:(void (^)(NSString *encryptedDataPath))successHandler
44 | failure:(void (^)(NSURLSessionTask *_Nullable task, NSError *error))failureHandlerParam
45 | {
46 | OWSAssertDebug(job);
47 | OWSAssertDebug(attachmentPointer);
48 |
49 | AFHTTPSessionManager *manager = [self cdnSessionManagerForCdnNumber:attachmentPointer.cdnNumber];
50 | manager.completionQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
51 | NSString *urlPath;
52 | if (attachmentPointer.cdnKey.length > 0) {
53 | urlPath = [NSString
54 | - stringWithFormat:@"attachments/%@", -> this line
55 | [attachmentPointer.cdnKey
56 | stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLPathAllowedCharacterSet]];
57 | } else {
58 | - urlPath = [NSString stringWithFormat:@"attachments/%llu", attachmentPointer.serverId]; -> this line
59 | }
60 | NSURL *url = [[NSURL alloc] initWithString:urlPath relativeToURL:manager.baseURL];
61 | ```
62 |
63 | ```diff
64 | OWSUploadV2.m
65 |
66 | - (AnyPromise *)parseFormAndUpload:(nullable id)formResponseObject
67 | progressBlock:(UploadProgressBlock)progressBlock
68 | {
69 | OWSUploadForm *_Nullable form = [OWSUploadForm parseDictionary:formResponseObject];
70 | if (!form) {
71 | return [AnyPromise
72 | promiseWithValue:OWSErrorWithCodeDescription(OWSErrorCodeUploadFailed, @"Invalid upload form.")];
73 | }
74 | UInt64 serverId = form.attachmentId.unsignedLongLongValue;
75 | if (serverId < 1) {
76 | return [AnyPromise
77 | promiseWithValue:OWSErrorWithCodeDescription(OWSErrorCodeUploadFailed, @"Invalid upload form.")];
78 | }
79 |
80 | self.serverId = serverId;
81 |
82 | __weak OWSAttachmentUploadV2 *weakSelf = self;
83 | NSString *uploadUrlPath = @"attachments/"; -> this line
84 | return [OWSUploadV2 uploadObjcWithData:self.attachmentData
85 | uploadForm:form
86 | uploadUrlPath:uploadUrlPath
87 | progressBlock:progressBlock]
88 | .then(^{
89 | weakSelf.uploadTimestamp = NSDate.ows_millisecondTimeStamp;
90 | });
91 | }
92 |
93 | ```
94 |
95 | Remove the `attachments/` those line code
96 |
97 | 6. Convert your SSL certificate to DER format and change the `textsecure.cer` on `SignalServiceKit/Resources/Certificates/textsecure.cer`
98 |
99 | 7. Run your project with Simulator or iOS Phone
100 |
101 |
--------------------------------------------------------------------------------
/signal-minio/README.md:
--------------------------------------------------------------------------------
1 | ## Related Guide
2 |
3 | [New Cleaner Minio Modification in v3.21](../signal-server-3.21/MINIO.md)
4 | ## MinIO for AWS Replacement
5 |
6 | If you are interested to use MinIO instead of AWS S3 or Cloudfront, this is how you can do that. To simplify development, you can deploy minio using docker-compose.
7 |
8 | 1. The `docker-compose.yml` is already bundled here. To create persistent volume you can add `./data:/data` to `volumes` entry in minio. If you want to use https instead of http, add `./config:/root/.minio` to the entry too. **Remember to choose a name for your bucket.**
9 |
10 | 2. If you want https, create a dir called `config/certs`. Copy your SSL Certificate there and rename it `public.crt`, also copy your SSL Private Key there and rename it `private.key`.
11 |
12 | 3. Run the minio with `docker-compose` command.
13 | ```
14 | docker-compose up -d
15 | ```
16 |
17 | 4. Update your signal server `config.yml` to reflect minio’s configuration.
18 | ```
19 | ...
20 |
21 | attachments: # MinIO configuration
22 | accessKey: Q3AM3UQ867SPQQA43P2F # Default, change to your own access key
23 | accessSecret: zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG # Default, change to your own secret key
24 | bucket: change-to-your-bucket-name # use your bucket name here
25 | region: us-east-1 # Default region
26 |
27 | cdn: # MinIO configuration
28 | accessKey: Q3AM3UQ867SPQQA43P2F # Default, change to your own access key
29 | accessSecret: zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG # Default, change to your own secret key
30 | bucket: change-to-your-bucket-name # use your bucket name here
31 | region: us-east-1 # Default region
32 |
33 | ...
34 | ```
35 |
36 | 5. Update your nginx configuration to rediract to minio. Restart Nginx after updating this.
37 | ```
38 | ...
39 |
40 | server {
41 | listen 443;
42 | server_name domain.com;
43 |
44 | # To allow special characters in headers
45 | ignore_invalid_headers off;
46 |
47 | # Allow any size file to be uploaded.
48 | # Set to a value such as 1000m; to restrict file size to a specific value
49 | client_max_body_size 0;
50 |
51 | # To disable buffering
52 | proxy_buffering off;
53 |
54 | location /change-to-your-bucket-name {
55 | proxy_set_header X-Real-IP $remote_addr;
56 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
57 | proxy_set_header X-Forwarded-Proto $scheme;
58 | proxy_set_header Host $http_host;
59 |
60 | proxy_connect_timeout 300;
61 | # Default is HTTP/1, keepalive is only enabled in HTTP/1.1
62 | proxy_http_version 1.1;
63 | proxy_set_header Connection "";
64 | chunked_transfer_encoding off;
65 |
66 | proxy_pass http://127.0.0.1:9000/change-to-your-bucket-name;
67 | }
68 |
69 | location /minio {
70 | proxy_set_header X-Real-IP $remote_addr;
71 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
72 | proxy_set_header X-Forwarded-Proto $scheme;
73 | proxy_set_header Host $http_host;
74 |
75 | proxy_connect_timeout 300;
76 | # Default is HTTP/1, keepalive is only enabled in HTTP/1.1
77 | proxy_http_version 1.1;
78 | proxy_set_header Connection "";
79 | chunked_transfer_encoding off;
80 |
81 | proxy_pass http://127.0.0.1:9000;
82 | }
83 | }
84 |
85 |
86 | ...
87 | ```
88 |
89 | 6. Modify your Signal Server project file, first update dependency by adding these values to `service/pom.xml` inside tag.
90 | ```
91 | ...
92 |
93 |
94 | xmlpull
95 | xmlpull
96 | 1.1.3.1
97 |
98 |
99 |
100 | io.minio
101 | minio
102 | 6.0.11
103 |
104 |
105 | ...
106 | ```
107 |
108 | 7. Open `AttachmentControllerV1.java` located in `service/src/main/java/org/whispersystems/textsecuregcm/controllers/AttachmentControllerV1.java`. Add the import statements and modify existing function.
109 | ```
110 | import org.xmlpull.v1.XmlPullParserException;
111 | import java.security.InvalidKeyException;
112 | import java.security.NoSuchAlgorithmException;
113 | import io.minio.errors.MinioException;
114 |
115 |
116 |
117 | @Timed
118 | @GET
119 | @Produces(MediaType.APPLICATION_JSON)
120 | public AttachmentDescriptorV1 allocateAttachment(@Auth Account account)
121 | throws RateLimitExceededException, InvalidKeyException, NoSuchAlgorithmException, IOException, XmlPullParserException, MinioException
122 | {
123 | if (account.isRateLimited()) {
124 | rateLimiters.getAttachmentLimiter().validate(account.getNumber());
125 | }
126 |
127 | long attachmentId = generateAttachmentId();
128 | String url = urlSigner.getPreSignedUrl(attachmentId, HttpMethod.PUT);
129 |
130 | return new AttachmentDescriptorV1(attachmentId, url);
131 |
132 | }
133 |
134 |
135 |
136 | @Timed
137 | @GET
138 | @Produces(MediaType.APPLICATION_JSON)
139 | @Path("/{attachmentId}")
140 | public AttachmentUri redirectToAttachment(@Auth Account account,
141 | @PathParam("attachmentId") long attachmentId)
142 | throws IOException, InvalidKeyException, NoSuchAlgorithmException, XmlPullParserException, MinioException
143 | {
144 | return new AttachmentUri(new URL(urlSigner.getPreSignedUrl(attachmentId, HttpMethod.GET)));
145 | }
146 | ```
147 |
148 | 8. Open `ProfileController.java` located in `service/src/main/java/org/whispersystems/textsecuregcm/controllers/ProfileController.java`. Add the import statements and modify existing function.
149 | ```
150 | import com.amazonaws.ClientConfiguration;
151 | import com.amazonaws.client.builder.AwsClientBuilder;
152 | import com.amazonaws.services.s3.AmazonS3ClientBuilder;
153 |
154 |
155 |
156 | public ProfileController(RateLimiters rateLimiters,
157 | AccountsManager accountsManager,
158 | UsernamesManager usernamesManager,
159 | CdnConfiguration profilesConfiguration)
160 | {
161 | AWSCredentials credentials = new BasicAWSCredentials(profilesConfiguration.getAccessKey(), profilesConfiguration.getAccessSecret());
162 | AWSCredentialsProvider credentialsProvider = new AWSStaticCredentialsProvider(credentials);
163 |
164 | this.rateLimiters = rateLimiters;
165 | this.accountsManager = accountsManager;
166 | this.usernamesManager = usernamesManager;
167 | this.bucket = profilesConfiguration.getBucket();
168 |
169 | ClientConfiguration clientConfiguration = new ClientConfiguration();
170 | clientConfiguration.setSignerOverride("AWSS3V4SignerType");
171 |
172 | this.s3client = AmazonS3ClientBuilder
173 | .standard()
174 | .withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration("http://domain.com:9000", "your-bucket-region"))
175 | .withPathStyleAccessEnabled(true)
176 | .withClientConfiguration(clientConfiguration)
177 | .withCredentials(new AWSStaticCredentialsProvider(credentials))
178 | .build();
179 |
180 | this.policyGenerator = new PostPolicyGenerator(profilesConfiguration.getRegion(),
181 | profilesConfiguration.getBucket(),
182 | profilesConfiguration.getAccessKey());
183 |
184 | this.policySigner = new PolicySigner(profilesConfiguration.getAccessSecret(),
185 | profilesConfiguration.getRegion());
186 | }
187 | ```
188 |
189 | 9. Open `UrlSigner.java` located in `service/src/main/java/org/whispersystems/textsecuregcm/s3/UrlSigner.java`. Add the import statements and modify existing function, remember to change the URL and credential in minioClient variable. **Remember to change the `minioClient` value**
190 | ```
191 | import java.io.IOException;
192 | import java.security.InvalidKeyException;
193 | import java.security.NoSuchAlgorithmException;
194 |
195 | import org.xmlpull.v1.XmlPullParserException;
196 |
197 | import io.minio.MinioClient;
198 | import io.minio.errors.MinioException;
199 |
200 |
201 |
202 | public String getPreSignedUrl(long attachmentId, HttpMethod method) throws InvalidKeyException, NoSuchAlgorithmException, IOException, XmlPullParserException, MinioException {
203 | String request = geturl(bucket, String.valueOf(attachmentId), method);
204 | return request;
205 | }
206 |
207 | public String geturl( String bucketname, String attachmentId, HttpMethod method) throws NoSuchAlgorithmException,
208 | IOException, InvalidKeyException, XmlPullParserException, MinioException {
209 |
210 | String url = null;
211 |
212 |
213 | MinioClient minioClient = new MinioClient("http://domain.com:9000", "YOUR-MINIO-ACCESS-KEY", "YOUR-MINIO-ACCESS-SECRET");
214 |
215 | try {
216 | if(method==HttpMethod.PUT){
217 | url = minioClient.presignedPutObject(bucketname, attachmentId, 60 * 60 * 24);
218 | }
219 | if(method==HttpMethod.GET){
220 | url = minioClient.presignedGetObject(bucketname, attachmentId);
221 | }
222 | System.out.println(url);
223 | } catch(MinioException e) {
224 | System.out.println("Error occurred: " + e);
225 | } catch (java.security.InvalidKeyException e) {
226 | e.printStackTrace();
227 | }
228 |
229 | return url;
230 | }
231 | ```
232 |
233 | 10. Open `UrlSignerTest.java ` located in `service/src/test/java/org/whispersystems/textsecuregcm/tests/util/UrlSignerTest.Java`. Add double slash to comment code inside the test function.
234 | ```
235 | @Test
236 | public void testTransferAcceleration() {
237 | //UrlSigner signer = new UrlSigner("foo", "bar", "attachments-test");
238 | //URL url = signer.getPreSignedUrl(1234, HttpMethod.GET, false);
239 |
240 | //assertThat(url).hasHost("attachments-test.s3-accelerate.amazonaws.com");
241 | }
242 |
243 | @Test
244 | public void testTransferUnaccelerated() {
245 | //UrlSigner signer = new UrlSigner("foo", "bar", "attachments-test");
246 | //URL url = signer.getPreSignedUrl(1234, HttpMethod.GET, true);
247 |
248 | //assertThat(url).hasHost("s3.amazonaws.com");
249 | }
250 | ```
251 |
252 | 11. Recompile your signal server and re-run it. It could take some time since it needs time to download new dependencies added to pom.xml.
253 |
254 |
255 | 12. Update your CDN URL in android `build.gradle`. Sync your project and rebuild it.
256 | ```
257 | ...
258 |
259 | buildConfigField "String", "SIGNAL_CDN_URL", "\"https://domain.com/change-to-your-bucket-name\""
260 |
261 | ...
262 | ```
263 |
264 |
265 |
266 | Bucket policy 'public' allow read & write from anonymous users without authentication, to disable this, you can modify your docker-compose for minio and modify mc service by changing `public` to `download`.
267 | ```
268 | /usr/bin/mc policy set download s3/name-of-bucket;
269 | ```
270 |
271 | By modifying your bucket policy to `download` it will enable `read-only` access, anonymous users still able to see the files in your Web GUI.
272 |
273 | To disable access to minio through web browser, delete `MINIO_BROWSER=off` from the environment in docker-compose for minio.
274 |
275 | ## Checking MinIO Deployment
276 |
277 | To check if there are some error on your MinIO deployment, use `minio.sh` from this repository.
278 |
279 | Prepare a file to be uploaded and run this script
280 | ```
281 | ./minio.sh your-bucket-name your-file-to-be-uploaded
282 | ```
283 |
284 | If you cannot run the script, please give it an execute permission
285 | ```
286 | chmod +x ./minio.sh
287 | ```
288 |
289 | You will see Success Code `200` if you succeed in uploading the file, if not then there might be some error with your MinIO deployment. s
--------------------------------------------------------------------------------
/signal-minio/docker/config/certs/private.key:
--------------------------------------------------------------------------------
1 | Change This to your SSL Private Key
--------------------------------------------------------------------------------
/signal-minio/docker/config/certs/public.crt:
--------------------------------------------------------------------------------
1 | Change This to your SSL Certificate
--------------------------------------------------------------------------------
/signal-minio/docker/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '2'
2 | services:
3 | minio:
4 | image: minio/minio:latest
5 | container_name: minio
6 | restart: always
7 | ports:
8 | - 9000:9000
9 | environment:
10 | - MINIO_ACCESS_KEY=Q3AM3UQ867SPQQA43P2F
11 | - MINIO_SECRET_KEY=zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG
12 | - MINIO_REGION=us-east-1
13 | - MINIO_DOMAIN=localhost
14 | - MINIO_BROWSER=off
15 | command: server /export
16 | volumes:
17 | - ./config:/root/.minio
18 | - ./data:/data
19 | mc:
20 | image: minio/mc:latest
21 | depends_on:
22 | - minio
23 | container_name: mc
24 | environment:
25 | - MINIO_ACCESS_KEY=Q3AM3UQ867SPQQA43P2F
26 | - MINIO_SECRET_KEY=zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG
27 | entrypoint: >
28 | /bin/sh -c "
29 | until (/usr/bin/mc config host add s3 http://change-to-your-domain.com:9000 $${MINIO_ACCESS_KEY} $${MINIO_SECRET_KEY}) do echo '...waiting...' && sleep 1; done;
30 | /usr/bin/mc mb s3/change-to-bucket-name --region us-east-1;
31 | /usr/bin/mc policy set public s3/change-to-bucket-name;
32 | exit 0;
33 | "
34 |
--------------------------------------------------------------------------------
/signal-minio/modified-files/android/build.gradle:
--------------------------------------------------------------------------------
1 | // app/build.gradle
2 |
3 | buildConfigField "String", "SIGNAL_CDN_URL", "\"https://domain.com/change-to-your-bucket-name\""
4 |
--------------------------------------------------------------------------------
/signal-minio/modified-files/nginx/minio-nginx.conf:
--------------------------------------------------------------------------------
1 | server {
2 | listen 443;
3 | server_name domain.com;
4 |
5 | # To allow special characters in headers
6 | ignore_invalid_headers off;
7 |
8 | # Allow any size file to be uploaded.
9 | # Set to a value such as 1000m; to restrict file size to a specific value
10 | client_max_body_size 0;
11 |
12 | # To disable buffering
13 | proxy_buffering off;
14 |
15 | location /change-to-your-bucket-name {
16 | proxy_set_header X-Real-IP $remote_addr;
17 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
18 | proxy_set_header X-Forwarded-Proto $scheme;
19 | proxy_set_header Host $http_host;
20 |
21 | proxy_connect_timeout 300;
22 | # Default is HTTP/1, keepalive is only enabled in HTTP/1.1
23 | proxy_http_version 1.1;
24 | proxy_set_header Connection "";
25 | chunked_transfer_encoding off;
26 |
27 | proxy_pass http://127.0.0.1:9000/change-to-your-bucket-name;
28 | }
29 |
30 | location /minio {
31 | proxy_set_header X-Real-IP $remote_addr;
32 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
33 | proxy_set_header X-Forwarded-Proto $scheme;
34 | proxy_set_header Host $http_host;
35 |
36 | proxy_connect_timeout 300;
37 | # Default is HTTP/1, keepalive is only enabled in HTTP/1.1
38 | proxy_http_version 1.1;
39 | proxy_set_header Connection "";
40 | chunked_transfer_encoding off;
41 |
42 | proxy_pass http://127.0.0.1:9000;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/signal-minio/modified-files/server/AttachmentControllerV1.java:
--------------------------------------------------------------------------------
1 | // service/src/main/java/org/whispersystems/textsecuregcm/controllers/AttachmentControllerV1.java
2 |
3 | import org.xmlpull.v1.XmlPullParserException;
4 | import java.security.InvalidKeyException;
5 | import java.security.NoSuchAlgorithmException;
6 | import io.minio.errors.MinioException;
7 |
8 |
9 |
10 | @Timed
11 | @GET
12 | @Produces(MediaType.APPLICATION_JSON)
13 | public AttachmentDescriptorV1 allocateAttachment(@Auth Account account)
14 | throws RateLimitExceededException, InvalidKeyException, NoSuchAlgorithmException, IOException, XmlPullParserException, MinioException
15 | {
16 | if (account.isRateLimited()) {
17 | rateLimiters.getAttachmentLimiter().validate(account.getNumber());
18 | }
19 |
20 | long attachmentId = generateAttachmentId();
21 | String url = urlSigner.getPreSignedUrl(attachmentId, HttpMethod.PUT);
22 |
23 | return new AttachmentDescriptorV1(attachmentId, url);
24 |
25 | }
26 |
27 |
28 |
29 | @Timed
30 | @GET
31 | @Produces(MediaType.APPLICATION_JSON)
32 | @Path("/{attachmentId}")
33 | public AttachmentUri redirectToAttachment(@Auth Account account,
34 | @PathParam("attachmentId") long attachmentId)
35 | throws IOException, InvalidKeyException, NoSuchAlgorithmException, XmlPullParserException, MinioException
36 | {
37 | return new AttachmentUri(new URL(urlSigner.getPreSignedUrl(attachmentId, HttpMethod.GET)));
38 | }
--------------------------------------------------------------------------------
/signal-minio/modified-files/server/ProfileController.java:
--------------------------------------------------------------------------------
1 | // service/src/main/java/org/whispersystems/textsecuregcm/controllers/ProfileController.java
2 |
3 | import com.amazonaws.ClientConfiguration;
4 | import com.amazonaws.client.builder.AwsClientBuilder;
5 | import com.amazonaws.services.s3.AmazonS3ClientBuilder;
6 |
7 |
8 |
9 | public ProfileController(RateLimiters rateLimiters,
10 | AccountsManager accountsManager,
11 | UsernamesManager usernamesManager,
12 | CdnConfiguration profilesConfiguration)
13 | {
14 | AWSCredentials credentials = new BasicAWSCredentials(profilesConfiguration.getAccessKey(), profilesConfiguration.getAccessSecret());
15 | AWSCredentialsProvider credentialsProvider = new AWSStaticCredentialsProvider(credentials);
16 |
17 | this.rateLimiters = rateLimiters;
18 | this.accountsManager = accountsManager;
19 | this.usernamesManager = usernamesManager;
20 | this.bucket = profilesConfiguration.getBucket();
21 |
22 | ClientConfiguration clientConfiguration = new ClientConfiguration();
23 | clientConfiguration.setSignerOverride("AWSS3V4SignerType");
24 |
25 | this.s3client = AmazonS3ClientBuilder
26 | .standard()
27 | .withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration("http://your-domain.com:9000", "your-bucket-region"))
28 | .withPathStyleAccessEnabled(true)
29 | .withClientConfiguration(clientConfiguration)
30 | .withCredentials(new AWSStaticCredentialsProvider(credentials))
31 | .build();
32 |
33 | this.policyGenerator = new PostPolicyGenerator(profilesConfiguration.getRegion(),
34 | profilesConfiguration.getBucket(),
35 | profilesConfiguration.getAccessKey());
36 |
37 | this.policySigner = new PolicySigner(profilesConfiguration.getAccessSecret(),
38 | profilesConfiguration.getRegion());
39 | }
40 |
--------------------------------------------------------------------------------
/signal-minio/modified-files/server/UrlSigner.java:
--------------------------------------------------------------------------------
1 | // service/src/main/java/org/whispersystems/textsecuregcm/s3/UrlSigner.java
2 |
3 | import java.io.IOException;
4 | import java.security.InvalidKeyException;
5 | import java.security.NoSuchAlgorithmException;
6 |
7 | import org.xmlpull.v1.XmlPullParserException;
8 |
9 | import io.minio.MinioClient;
10 | import io.minio.errors.MinioException;
11 |
12 |
13 |
14 | public String getPreSignedUrl(long attachmentId, HttpMethod method) throws InvalidKeyException, NoSuchAlgorithmException, IOException, XmlPullParserException, MinioException {
15 | String request = geturl(bucket, String.valueOf(attachmentId), method);
16 | return request;
17 | }
18 |
19 | public String geturl( String bucketname, String attachemtnId, HttpMethod method) throws NoSuchAlgorithmException,
20 | IOException, InvalidKeyException, XmlPullParserException, MinioException {
21 |
22 | String url = null;
23 |
24 |
25 | MinioClient minioClient = new MinioClient("http://domain.com:9000", "YOUR-MINIO-ACCESS-KEY", "YOUR-MINIO-ACCESS-SECRET");
26 |
27 | try {
28 | if(method==HttpMethod.PUT){
29 | url = minioClient.presignedPutObject(bucketname, attachemtnId, 60 * 60 * 24);
30 | }
31 | if(method==HttpMethod.GET){
32 | url = minioClient.presignedGetObject(bucketname, attachemtnId);
33 | }
34 | System.out.println(url);
35 | } catch(MinioException e) {
36 | System.out.println("Error occurred: " + e);
37 | } catch (java.security.InvalidKeyException e) {
38 | e.printStackTrace();
39 | }
40 |
41 | return url;
42 | }
43 |
--------------------------------------------------------------------------------
/signal-minio/modified-files/server/UrlSignerTest.java:
--------------------------------------------------------------------------------
1 | // service/src/test/java/org/whispersystems/textsecuregcm/tests/util/UrlSignerTest.Java
2 |
3 | @Test
4 | public void testTransferAcceleration() {
5 | //UrlSigner signer = new UrlSigner("foo", "bar", "attachments-test");
6 | //URL url = signer.getPreSignedUrl(1234, HttpMethod.GET, false);
7 |
8 | //assertThat(url).hasHost("attachments-test.s3-accelerate.amazonaws.com");
9 | }
10 |
11 | @Test
12 | public void testTransferUnaccelerated() {
13 | //UrlSigner signer = new UrlSigner("foo", "bar", "attachments-test");
14 | //URL url = signer.getPreSignedUrl(1234, HttpMethod.GET, true);
15 |
16 | //assertThat(url).hasHost("s3.amazonaws.com");
17 | }
18 |
--------------------------------------------------------------------------------
/signal-minio/modified-files/server/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | xmlpull
5 | xmlpull
6 | 1.1.3.1
7 |
8 |
9 | io.minio
10 | minio
11 | 6.0.11
12 |
--------------------------------------------------------------------------------
/signal-minio/modified-files/server/signal-config.yml:
--------------------------------------------------------------------------------
1 | attachments: # MinIO configuration
2 | accessKey: Q3AM3UQ867SPQQA43P2F # Default, change to your own access key
3 | accessSecret: zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG # Default, change to your own secret key
4 | bucket: change-to-your-bucket-name # use your bucket name here
5 | region: us-east-1 # Default region
6 |
7 | cdn: # MinIO configuration
8 | accessKey: Q3AM3UQ867SPQQA43P2F # Default, change to your own access key
9 | accessSecret: zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG # Default, change to your own secret key
10 | bucket: change-to-your-bucket-name # use your bucket name here
11 | region: us-east-1 # Default region
12 |
--------------------------------------------------------------------------------
/signal-minio/scripts/minio.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # this script is used to test the minio docker
4 |
5 | # usage: chmod +x ./minio.sh
6 | # usage: ./minio.sh my-bucket my-file.zip
7 |
8 | bucket=$1
9 | file=$2
10 |
11 | host=domain.com:9000
12 | s3_key='Q3AM3UQ867SPQQA43P2F'
13 | s3_secret='zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG'
14 |
15 | base_file=`basename ${file}`
16 | resource="/${bucket}/${base_file}"
17 | content_type="application/octet-stream"
18 | date=`date -R`
19 | _signature="PUT\n\n${content_type}\n${date}\n${resource}"
20 | signature=`echo -en ${_signature} | openssl sha1 -hmac ${s3_secret} -binary | base64`
21 |
22 | curl -v -X PUT -T "${file}" \
23 | -H "Host: $host" \
24 | -H "Date: ${date}" \
25 | -H "Content-Type: ${content_type}" \
26 | -H "Authorization: AWS ${s3_key}:${signature}" \
27 | http://$host${resource}
28 |
--------------------------------------------------------------------------------
/signal-server-2.92/DEPENDENCIES.md:
--------------------------------------------------------------------------------
1 | # Signal Server Dependencies
2 | This guide is written in Ubuntu 18.04.
3 |
4 | ## Before you start
5 | Update package in linux by running this command:
6 |
7 | ```
8 | sudo apt update
9 | ```
10 |
11 | ## Git
12 | To clone repository / project files from github, install Git
13 |
14 | ```
15 | sudo apt install -y git
16 | ```
17 |
18 | ## Java JDK 11
19 | To run java application, install JDK 11
20 |
21 | ```
22 | sudo apt install -y default-jre
23 | sudo apt install -y default-jdk
24 | ```
25 |
26 | ## Maven
27 | To compile signal server, install Maven
28 |
29 | ```
30 | sudo apt install maven
31 | ```
32 |
33 | ## Nginx
34 | To forward signal port to http / https port, install Nginx
35 |
36 | ```
37 | sudo apt install nginx
38 | ```
39 |
40 | ## Certbot
41 | To generate let's encrypt free ssl certificate for https, install certbot
42 |
43 | ```
44 | sudo add-apt-repository ppa:certbot/certbot
45 | sudo apt update
46 | sudo apt install python-certbot-nginx
47 | ```
48 |
49 | ## Coturn
50 | To run a turn server for video/voice call, install coturn
51 |
52 | ```
53 | sudo apt install coturn
54 | ```
55 |
56 | ## Docker
57 | To run database, redis, and minio in docker, install docker
58 |
59 | ```
60 | sudo apt install apt-transport-https ca-certificates curl software-properties-common
61 | curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
62 | sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable"
63 | sudo apt update
64 | sudo apt install docker-ce
65 |
66 | sudo curl -L https://github.com/docker/compose/releases/download/1.28.5/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
67 | sudo chmod +x /usr/local/bin/docker-compose
68 | ```
69 |
70 | To allow non sudo user to use docker, run these commands :
71 |
72 | ```
73 | sudo usermod -aG docker ${USER}
74 | su - ${USER}
75 | id -nG
76 | ```
77 |
78 | ## Firewall
79 | To enable firewall for Nginx & OpenSSH run these commands
80 |
81 | ```
82 | sudo ufw enable
83 | sudo ufw app list
84 | sudo ufw allow 'Nginx Full'
85 | sudo ufw allow 'OpenSSH'
86 | ```
87 |
88 | **Why OpenSSH? Because if you forgot to allow it, you can't ssh to your server**
89 |
90 |
--------------------------------------------------------------------------------
/signal-server-2.92/README.md:
--------------------------------------------------------------------------------
1 | # Signal Server
2 | This guide is written by using Signal v2.92.
3 |
4 | (For Signal v3.21 please refer to [signal-new](../signal-server-3.21/) guide)
5 |
6 | ## Related Guide
7 | * Nginx Reverse Proxy (Scroll down on this guide)
8 | * [Step to install Dependencies](./DEPENDENCIES.md)
9 | * [Docker for PostgreSQL & Redis](../signal-docker)
10 | * [CoTurn Server](../coturn-server)
11 | * [Amazon Web Service / AWS](../signal-server-aws-setup)
12 | * [Remove Twilio](../signal-server-no-twilio)
13 | * [Using Own SSL Certificate (Self Signed)](../signal-server-self-signed-certificate)
14 | * [Use MinIO instead of AWS S3](../signal-minio)
15 | * [Autostart Signal on Startup](../signal-server-autostart)
16 |
17 | ## Requirement
18 | * JDK 11
19 | * SSL Certificate for your domain
20 | * Google Recaptcha
21 | * Firebase Cloud Messaging (It used to be GCM)
22 | * Twilio
23 | * AWS
24 |
25 | 1. Create your own `config.yml`, put it inside `signal-server/service/config/`. You can take a look at [the example here](./example-signal.yml).
26 |
27 | 2. Build the server (I suggest you keep the DskipTests if you do a modification)
28 | ```
29 | mvn clean install -DskipTests
30 | ```
31 |
32 | 3. Generate value for UnidentifiedDelivery
33 |
34 | You will get key pair using this command (keep the keypair, you will need it for Android and for the next step)
35 | ```
36 | java -jar service/target/TextSecureServer-2.92.jar certificate -ca
37 | ```
38 |
39 | Use the Private key to generate certificate (key id can be random, i use 1234)
40 | ```
41 | java -jar service/target/TextSecureServer-2.92.jar certificate --key --id
42 | ```
43 |
44 | 4. Run postgres, redis, coturn (I suggest you use docker-compose. If you don't use docker-compose from this project, please modify the entries for postgres, redis, and coturn accordingly in `config.yml`).
45 |
46 | 5. Migrate databases
47 | ```
48 | java -jar service/target/TextSecureServer-2.92.jar abusedb migrate service/config/config.yml
49 | java -jar service/target/TextSecureServer-2.92.jar accountdb migrate service/config/config.yml
50 | java -jar service/target/TextSecureServer-2.92.jar keysdb migrate service/config/config.yml
51 | java -jar service/target/TextSecureServer-2.92.jar messagedb migrate service/config/config.yml
52 | ```
53 |
54 | 6. Run the server (config.yml is from step 1)
55 | ```
56 | java -jar service/target/TextSecureServer-2.92.jar server service/config/config.yml
57 | ```
58 |
59 | 7. To run the server in the background (run continously), use nohup
60 | ```
61 | nohup java -jar service/target/TextSecureServer-2.92.jar server service/config/config.yml > /dev/null &
62 | ```
63 |
64 | ## Configuring Nginx & Generating SSL Certificate with Let's Encrypt
65 |
66 | If you already has your SSL Certificate, you can use [the example nginx config](./example-nginx.conf) on the `Step 4` and skip the `Step 6 - 9`.
67 |
68 | 1. Install nginx on your system
69 | ```
70 | sudo apt install nginx
71 | ```
72 |
73 | 2. Install Certbot for Let's Encrypt
74 | ```
75 | sudo add-apt-repository ppa:certbot/certbot
76 | sudo apt-get install python-certbot-nginx
77 | ```
78 |
79 | 3. Allow Nginx to be accessed from outside using Firewall. The `Nginx Full` argument will create a rule that allow port 80 and port 443, you can change it to `Nginx HTTP` to allow only port 80 or change it to `Nginx HTTPS` to allow only port 443
80 | ```
81 | sudo ufw allow 'Nginx Full'
82 | ```
83 |
84 | 4. Create your server configuration in `/etc/nginx/sites-enabled/domain.com`.
85 | ```
86 | server {
87 | listen 80;
88 | listen [::]:80;
89 |
90 | server_name domain.com;
91 | }
92 | ```
93 |
94 | 5. Reload your nginx to apply the new configuration
95 | ```
96 | sudo nginx -s reload
97 |
98 | ```
99 |
100 | 6. Run certbot to generate SSL Certificate
101 | ```
102 | certbot --nginx -d domain.com
103 | ```
104 |
105 | 8. When asked `Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.` You are recommended to choose `2: Redirect`. After the process is done your certificate will be located in
106 | ```
107 | etc
108 | └── letsencrypt
109 | └── live
110 | └── domain.com
111 | └── *.pem
112 | ```
113 |
114 | 9. Update your nginx config to suits your need, you can take a look at [the example here](./example-nginx.conf).
115 |
116 | 10. Check if your configuration is correct
117 | ```
118 | sudo nginx -t
119 | ```
120 |
121 | 11. If there's no error, you can reload your nginx to apply the new configuration
122 | ```
123 | sudo nginx -s reload
124 | ```
125 |
126 | If you are having a hard time configuring NginX manually, you can try generating configuration file by using nginxconfig.io.
127 |
128 | ## Creating a service to make server run automatically on reboot / restart
129 |
130 | 1. Go to `/etc/systemd/system` and create a file named `signal.service`
131 |
132 | 2. Open the file and paste this. **Change** `WorkingDirectory` to your directory, also change the path to java. To find the path to java, run `which java`.
133 |
134 | ```
135 | [Unit]
136 | Description=Signal Server
137 |
138 | [Service]
139 | User=root
140 | WorkingDirectory=/path/to/signal/directory
141 | ExecStart=/path/to/java -jar TextSecureServer-2.92.jar server service/config/config.yml
142 | Type=simple
143 | Restart=on-failure
144 | RestartSec=10
145 |
146 | [Install]
147 | WantedBy=multi-user.target
148 | ```
149 |
150 | 3. Run `sudo systemctl daemon-reload`
151 |
152 | 4. Run `sudo systemctl enable signal.service`
153 |
154 | ## FAQ
155 | Q: How do I get Recapthca?
156 |
157 | A: You register for Google Recaptcha v3, put your server's domain there.
158 |
159 | Q: How do I get GCM?
160 |
161 | A: Setup Firebase Cloud Messaging, you will get the key from there.
162 |
163 | Q: What AWS service do i need?
164 |
165 | A: CDN Cloudfront, S3 Bucket, SQS FIFO type, and IAM for the key.
166 |
167 | Q: How do I disable AccountCrawler Error?
168 |
169 | A: Disable accountDatabaseCrawler logging by commenting `environment.lifecycle().manage(accountDatabaseCrawler);` it is located in `service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java`. Rebuild the server then rerun it after you did the modification.
170 |
171 | ```
172 | ...
173 |
174 | apnSender.setApnFallbackManager(apnFallbackManager);
175 | environment.lifecycle().manage(apnFallbackManager);
176 | environment.lifecycle().manage(pubSubManager);
177 | environment.lifecycle().manage(pushSender);
178 | environment.lifecycle().manage(messagesCache);
179 | // environment.lifecycle().manage(accountDatabaseCrawler);
180 |
181 | ...
182 | ```
183 |
184 | Q: I got an error from updating profile name / avatar.
185 |
186 | A: Some error presumebly caused by constant change in client or server, starting fresh by reseting database & storage usually stopped this. Some people reported that modifying nginx configuration by adding `$uri` to the end of `proxy_pass`, but unfortunately I can't reproduce the desired result.
187 |
--------------------------------------------------------------------------------
/signal-server-2.92/example-nginx.conf:
--------------------------------------------------------------------------------
1 | server {
2 | listen 443 ssl http2;
3 | listen [::]:443 ssl http2;
4 |
5 | server_name domain.com;
6 |
7 | # SSL
8 | ssl_certificate /etc/letsencrypt/live/domain.com/fullchain.pem;
9 | ssl_certificate_key /etc/letsencrypt/live/domain.com/privkey.pem;
10 | ssl_trusted_certificate /etc/letsencrypt/live/domain.com/chain.pem;
11 |
12 | # security headers
13 | add_header X-Frame-Options "SAMEORIGIN" always;
14 | add_header X-XSS-Protection "1; mode=block" always;
15 | add_header X-Content-Type-Options "nosniff" always;
16 | add_header Referrer-Policy "no-referrer-when-downgrade" always;
17 | add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
18 | add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
19 |
20 | # . files
21 | location ~ /\.(?!well-known) {
22 | deny all;
23 | }
24 |
25 | # reverse proxy
26 | location / {
27 | proxy_pass http://127.0.0.1:8080;
28 | proxy_http_version 1.1;
29 | proxy_cache_bypass $http_upgrade;
30 |
31 | proxy_set_header Upgrade $http_upgrade;
32 | proxy_set_header Connection "upgrade";
33 | proxy_set_header Host $host;
34 | proxy_set_header X-Real-IP $remote_addr;
35 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
36 | proxy_set_header X-Forwarded-Proto $scheme;
37 | proxy_set_header X-Forwarded-Host $host;
38 | proxy_set_header X-Forwarded-Port $server_port;
39 | }
40 |
41 | location /v1/websocket {
42 | proxy_pass http://127.0.0.1:8080/v1/websocket;
43 | proxy_http_version 1.1;
44 | proxy_cache_bypass $http_upgrade;
45 |
46 | proxy_set_header Upgrade $http_upgrade;
47 | proxy_set_header Connection "upgrade";
48 | proxy_set_header Host $host;
49 | proxy_set_header X-Real-IP $remote_addr;
50 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
51 | proxy_set_header X-Forwarded-Proto $scheme;
52 | proxy_set_header X-Forwarded-Host $host;
53 | proxy_set_header X-Forwarded-Port $server_port;
54 | }
55 |
56 | }
57 |
58 | # HTTP redirect to HTTPS
59 | server {
60 | listen 80;
61 | listen [::]:80;
62 |
63 | server_name domain.com;
64 |
65 | location / {
66 | return 301 https://domain.com$request_uri;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/signal-server-2.92/example-signal.yml:
--------------------------------------------------------------------------------
1 | server: # Server configuration
2 | applicationConnectors:
3 | - type: http
4 | port: 8080
5 | adminConnectors:
6 | - type: http
7 | port: 8081
8 |
9 | backupService:
10 | userAuthenticationTokenSharedSecret: XXXXXXXXXXXXXXX # Generate it with `head -c 16 /dev/urandom | hexdump -ve '1/1 "%.2x"'`
11 |
12 | storageService:
13 | userAuthenticationTokenSharedSecret: XXXXXXXXXXXXXXX # Generate it with `head -c 16 /dev/urandom | hexdump -ve '1/1 "%.2x"'`
14 |
15 | cache: # Redis server configuration for cache cluster
16 | url: redis://127.0.0.1:6379/cache
17 | replicaUrls:
18 | - redis://127.0.0.1:6380/cacheReplica
19 |
20 | directory:
21 | redis: # Redis server configuration for directory cluster
22 | url: redis://127.0.0.1:6379/directory
23 | replicaUrls:
24 | - redis://127.0.0.1:6380/directoryReplica
25 | client: # Configuration for interfacing with Contact Discovery Service cluster
26 | userAuthenticationTokenSharedSecret: XXXXXXXXXXXXXXX # Generate it with `head -c 16 /dev/urandom | hexdump -ve '1/1 "%.2x"'`
27 | userAuthenticationTokenUserIdSecret: XXXXXXXXXXXXXXX # Generate it with `head -c 16 /dev/urandom | hexdump -ve '1/1 "%.2x"'`
28 | sqs:
29 | accessKey: XXXXXXXXXXXXXXX # Change this to your AWS IAM Access Key
30 | accessSecret: XXXXXXXXXXXXXXX # Change this to your AWS IAM Secret
31 | queueUrl: https://sqs.XXXXXXXXX.amazonaws.com/XXXXXXXXX/XXXXXXXXX.fifo # Change this to your AWS SQS (FIFO) URL
32 | server: # You can change the certificate with your own certificate
33 | replicationUrl: http://127.0.0.1:9090 # CDS replication endpoint base url
34 | replicationPassword: XXXXXXXXXXXXXXX # CDS replication endpoint password
35 | replicationCaCertificate: |
36 | -----BEGIN CERTIFICATE-----
37 | fill this with your ca certificate
38 | -----END CERTIFICATE-----
39 |
40 | messageCache: # Redis server configuration for message store cache
41 | redis:
42 | url: redis://127.0.0.1:6379/messageCache
43 | replicaUrls:
44 | - redis://127.0.0.1:6380/messageCacheReplica
45 |
46 | pushScheduler:
47 | url: redis://127.0.0.1:6379/pushScheduler
48 | replicaUrls:
49 | - redis://127.0.0.1:6380/pushSchedulerReplica
50 |
51 | push:
52 | queueSize: 200
53 |
54 | attachments: # AWS S3 configuration
55 | accessKey: XXXXXXXXXXXXXXX # Change this to your AWS IAM Access Key
56 | accessSecret: xxxxxxxxxxxxxxx # Change this to your AWS IAM Secret
57 | bucket: xxxxxxxxxx # Change this to your bucket's name
58 | region: xx-xxxxxxxxx-x # Change this to your bucket's region
59 |
60 | cdn: # AWS S3 configuration
61 | accessKey: XXXXXXXXXXXXXXX # Change this to your AWS IAM Access Key
62 | accessSecret: xxxxxxxxxxxxxxx # Change this to your AWS IAM Secret
63 | bucket: xxxxxxxxxx # Change this to your bucket's name
64 | region: xx-xxxxxxxxx-x # Change this to your bucket's region
65 |
66 | accountsDatabase:
67 | driverClass: org.postgresql.Driver
68 | user: postgres # change to your psql username
69 | password: postgres # change to your psql password
70 | url: jdbc:postgresql://127.0.0.1:5432/signal #change to your psql url, port, and database name
71 |
72 | keysDatabase:
73 | driverClass: org.postgresql.Driver
74 | user: postgres # change to your psql username
75 | password: postgres # change to your psql password
76 | url: jdbc:postgresql://127.0.0.1:5432/signal #change to your psql url, port, and database name
77 |
78 | messageStore:
79 | driverClass: org.postgresql.Driver
80 | user: postgres # change to your psql username
81 | password: postgres # change to your psql password
82 | url: jdbc:postgresql://127.0.0.1:5432/signal #change to your psql url, port, and database name
83 |
84 | abuseDatabase:
85 | driverClass: org.postgresql.Driver
86 | user: postgres # change to your psql username
87 | password: postgres # change to your psql password
88 | url: jdbc:postgresql://127.0.0.1:5432/signal #change to your psql url, port, and database name
89 |
90 | accountDatabaseCrawler:
91 | chunkSize: 1000
92 | chunkIntervalMs: 1000000
93 |
94 | gcm:
95 | senderId: 0000000000 # Change this to your FCM Sender ID
96 | apiKey: XXXXXXXXXXXXXXXX # Change this to your FCM API Key
97 |
98 | apn: # You can use these value if you don't need Apple Push Notification
99 | pushCertificate: "-----BEGIN CERTIFICATE-----\r\nMIIFjzCCBHegAwIBAgIIfkSIVVtC9UIwDQYJKoZIhvcNAQEFBQAwgZYxCzAJBgNV\r\nBAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3Js\r\nZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3\r\naWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw\r\nHhcNMTkwNTA5MTc0NzUyWhcNMjAwNTA4MTc0NzUyWjCBjjElMCMGCgmSJomT8ixk\r\nAQEMFWNvbS5xYW5kb3JhcHAucWFuZG9yMzFDMEEGA1UEAww6QXBwbGUgRGV2ZWxv\r\ncG1lbnQgSU9TIFB1c2ggU2VydmljZXM6IGNvbS5xYW5kb3JhcHAucWFuZG9yMzET\r\nMBEGA1UECwwKTlA2UE5GUUdBODELMAkGA1UEBhMCVVMwggEiMA0GCSqGSIb3DQEB\r\nAQUAA4IBDwAwggEKAoIBAQDIo+8GD6chbxCFDI7bTK6iTxQHnD/FiXJvEYBbO3ey\r\nQV8/XZm66VbO+RbA4UArA2bjdW5KZkSQ7hSYJcomQCmcmpqmrXjcaL0KfeWinSz4\r\nmbXxoXK9JsoUSjOfy7yXHGDLZSHGVvn5gO1FU9WPX3Iu7YYMT1hwnMgmZIuzn5K9\r\nAiyxvQzSlIlEsnpoqTDUz+1eXtPXAISPQT8+jM8JoZohTrODuUUj2rSPWi62kMBP\r\nsX4H4ncSjI56DG/E0lvUV7d0PEiUnoxGwzycmGJVgpZqv/E6p1eUNT7Utl4ktuiq\r\nEv8ft3+brJx+FokMi7AKjxLvlYS18LTG6JbQMcmXHsdlAgMBAAGjggHlMIIB4TAJ\r\nBgNVHRMEAjAAMB8GA1UdIwQYMBaAFIgnFwmpthhgi+zruvZHWcVSVKO3MIIBDwYD\r\nVR0gBIIBBjCCAQIwgf8GCSqGSIb3Y2QFATCB8TCBwwYIKwYBBQUHAgIwgbYMgbNS\r\nZWxpYW5jZSBvbiB0aGlzIGNlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVz\r\nIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFyZCB0ZXJt\r\ncyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRlIHBvbGljeSBhbmQg\r\nY2VydGlmaWNhdGlvbiBwcmFjdGljZSBzdGF0ZW1lbnRzLjApBggrBgEFBQcCARYd\r\naHR0cDovL3d3dy5hcHBsZS5jb20vYXBwbGVjYS8wEwYDVR0lBAwwCgYIKwYBBQUH\r\nAwIwTQYDVR0fBEYwRDBCoECgPoY8aHR0cDovL2RldmVsb3Blci5hcHBsZS5jb20v\r\nY2VydGlmaWNhdGlvbmF1dGhvcml0eS93d2RyY2EuY3JsMB0GA1UdDgQWBBR6BPca\r\ngaRwijNzbL4lcYrJkU2r7TALBgNVHQ8EBAMCB4AwEAYKKoZIhvdjZAYDAQQCBQAw\r\nDQYJKoZIhvcNAQEFBQADggEBAFcIWIc1T0PtgeaMgjwQcTmfJGy8MUdIO/hdElo/\r\nOZF4ts4c2xkddanZ9IzOCj/HzmRJEs6WVZhNxySc3Cxo6KejsLbGLJmMoEh72xyQ\r\nwgnMBiapMnRhCfd68NMnTUClNHvGsg+NipnAN63r+HZSgPsCMXHsEMyZ+qQendRc\r\nDZH6m5FN1TqdAVtChdDPItzYJuQpyeKJpiiQGeCd6YjCELkWVxHcTU67CWmkuVqx\r\n9BRoANbJXty3b9T5KHxJYcEMj3pvsgcTOR/nGKIT9+B2iqrt6i/YY2n4p5NXXFzZ\r\nHewPc93srGXfyrvW7SeQs+93vZ7WlntfihY7WCoUbEOnHso=\r\n-----END CERTIFICATE-----"
100 | pushKey: "-----BEGIN RSA PRIVATE KEY-----\r\nMIIEpQIBAAKCAQEAyKPvBg+nIW8QhQyO20yuok8UB5w/xYlybxGAWzt3skFfP12Z\r\nuulWzvkWwOFAKwNm43VuSmZEkO4UmCXKJkApnJqapq143Gi9Cn3lop0s+Jm18aFy\r\nvSbKFEozn8u8lxxgy2Uhxlb5+YDtRVPVj19yLu2GDE9YcJzIJmSLs5+SvQIssb0M\r\n0pSJRLJ6aKkw1M/tXl7T1wCEj0E/PozPCaGaIU6zg7lFI9q0j1outpDAT7F+B+J3\r\nEoyOegxvxNJb1Fe3dDxIlJ6MRsM8nJhiVYKWar/xOqdXlDU+1LZeJLboqhL/H7d/\r\nm6ycfhaJDIuwCo8S75WEtfC0xuiW0DHJlx7HZQIDAQABAoIBAFNo+1xMs5FNp9N4\r\nBgebGFp3f38ucMCBRGZyIydKUJd1X9Bq7BbtHF6M5O2odtGq52IWFpStcUHDCCK8\r\nSw6dy+7DwxkZss4GaNhswENbDjAHTsE1+goyjv3iXxXGUA+OB5tm3qSi0ebstzcE\r\nBBtHdaOWsQx7C+w88WQslntFEm6qNSqeM1s5eQ20wSnlAA43Pm+NdVNM+JYX4iqt\r\n/MFdaPINE5XAXyBRAYir0l1dkofeGsb4rCuZmXSmjRRcC9vdhzmjrDUxLspOI7du\r\nUT7vYy3/hWFdng5oHu7JoDVrxF8/5e11j04jTq8SiHYfxUdR/Pmzt4/Nnu8SVjDJ\r\nesZJhwECgYEA7ElQyw2fzF3CQ2x526SBvLfFSxuX8zDkPjFtsrpZf78MHz9AR3ak\r\nlgEjR9aOfeWC1nRRZ6kGG8AgbpIZEN5KoCwZM98D6oub0VIg8iuR1UeArjebI8QV\r\nq2q5BeR3v2nmHsDXeG57D7O7DRko1tazp+d3/19hmMBOa4os19SsEaUCgYEA2WFH\r\nPtVwRtsQNcuKBCKHGzEVDG6Gm86kye2AdfJB7USr0fvD3HRah/chGi+foz+CDwwA\r\nzuBcgL45rSZZCTA9AFzihVpAFJ+a3GDgT/hSjJmMX3vHmySaZABmKNsiXRlPW+Fr\r\n7jkXwk7JH6emgcBH9d+Gyyp6ybVZvv/tNrFCcsECgYEA1DhtNlLASZ+UUVZmhF3W\r\noJc1vmXELgqllS5z5mj05YXD73Sx2P24iXnwJB+Sz4SJ5O+IBeCLufTvrB/QH5Rn\r\n1kCFSk9thwVpJ7HqIVf8nWChNNiAoLkG9XTfRWmUG/mTU9/EJ0ijgtDcmcEVKxCf\r\nP5jn8BfM4pMmW/Q4nolHGnkCgYEAnnLT1a8KSft/k1arYVwxktZx+z/NCmDTqQRf\r\nMJnHCEWX4FVdbKG7I4Q1Mrsn53xxNrqPFDxh8M23iMh8+b+Zl1wdGQqxztaPsLdE\r\nicX9ldKOiULWOfWyO9Y2oO0p3SaHu/dSDrC66r02yMYRDl6zlTq7K/fozIJNynUN\r\n2WHXh4ECgYEAneclaDj/KMtedyaJcblyEjtpzW3V/Gotws2cAaHqVaWDJHRrp0bl\r\nZNg8Jjf/NoixxENZ5bdduAR3JEjTwScRpOluO45Huq2gIMS7jsBKoDAvF94DNUI5\r\nNDtK8x/8+SOdk5HAGNHDoMqVbgZ2NpeOIGxzoZUCCK4f1j1CZbeEIio=\r\n-----END RSA PRIVATE KEY-----"
101 | bundleId: com.notused
102 | sandbox: true
103 |
104 | recaptcha:
105 | secret: XXXXXXXXXXXXXXXX # Change this to your Google Recaptcha secret
106 |
107 | turn: # Coturn server configuration
108 | secret: secret # You will need the same config with the secret in turnserver.conf
109 | uris:
110 | - stun:signal.domain.com:3478 # If you change the port in turnserver.conf, change this too
111 | - stun:signal.domain.com:5349 # If you change the port in turnserver.conf, change this too
112 | - turn:signal.domain.com:5349?transport=udp # If you change the port in turnserver.conf, change this too
113 | - turn:signal.domain.com:3478?transport=udp # If you change the port in turnserver.conf, change this too
114 |
115 | twilio: # Twilio gateway configuration
116 | accountId: xxxxxxxxxx # Change this to your account id
117 | accountToken: xxxxxxxxxx # Change this to your token
118 | localDomain: signal.domain.com # Change this to your domain
119 | numbers:
120 | - "+000000000" # Change this to your number from Twilio
121 | - "+000000000" # You can use one or more numbers
122 |
123 | unidentifiedDelivery:
124 | certificate: XXXXXXXXXXXXXXXX # Change this to your generated certificate
125 | privateKey: XXXXXXXXXXXXXXXX= # Change this to you generated private key
126 | expiresDays: 365
127 |
128 | voiceVerification:
129 | url: https://cdn-ca.signal.org/verification
130 | locales:
131 | - en
132 |
--------------------------------------------------------------------------------
/signal-server-3.21/MINIO.md:
--------------------------------------------------------------------------------
1 | # MinIO Implementation
2 |
3 | To use MinIO instead of AWS in v3.21 you can implement this cleaner way rather than the old way
4 |
5 | ## Signal Server
6 |
7 | `service/pom.xml`
8 | ```diff
9 | +
10 | + xmlpull
11 | + xmlpull
12 | + 1.1.3.1
13 | +
14 | +
15 | +
16 | + io.minio
17 | + minio
18 | + 6.0.11
19 | +
20 | ```
21 |
22 | `service/src/main/java/org/whispersystems/textsecuregcm/s3/PostPolicyGenerator.java`
23 |
24 | ```diff
25 | " {\"bucket\": \"%s\"},\n" +
26 | " {\"key\": \"%s\"},\n" +
27 | " {\"acl\": \"private\"},\n" +
28 | + " {\"success_action_status\": \"200\"},\n" +
29 | " [\"starts-with\", \"$Content-Type\", \"\"],\n" +
30 | " [\"content-length-range\", 1, " + maxSizeInBytes + "],\n" +
31 | "\n" +
32 |
33 | ```
34 |
35 | `service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java`
36 |
37 | ```diff
38 | import org.whispersystems.websocket.WebSocketResourceProviderFactory;
39 | import org.whispersystems.websocket.setup.WebSocketEnvironment;
40 |
41 | + import com.amazonaws.ClientConfiguration;
42 | + import com.amazonaws.client.builder.AwsClientBuilder;
43 | + import com.amazonaws.services.s3.AmazonS3ClientBuilder;
44 |
45 | import javax.servlet.DispatcherType;
46 | import javax.servlet.FilterRegistration;
47 | import javax.servlet.ServletRegistration;
48 |
49 | ...
50 | environment.getObjectMapper().setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
51 | environment.getObjectMapper().setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
52 |
53 | + ClientConfiguration clientConfiguration = new ClientConfiguration();
54 | + clientConfiguration.setSignerOverride("AWSS3V4SignerType");
55 | +
56 | JdbiFactory jdbiFactory = new JdbiFactory(DefaultNameStrategy.CHECK_EMPTY);
57 | Jdbi accountJdbi = jdbiFactory.build(environment, config.getAccountsDatabaseConfiguration(), "accountdb");
58 | Jdbi messageJdbi = jdbiFactory.build(environment, config.getMessageStoreConfiguration(), "messagedb" );
59 | ...
60 |
61 | environment.lifecycle().manage(accountDatabaseCrawler);
62 | environment.lifecycle().manage(remoteConfigsManager);
63 |
64 | AWSCredentials credentials = new BasicAWSCredentials(config.getCdnConfiguration().getAccessKey(), config.getCdnConfiguration().getAccessSecret());
65 | AWSCredentialsProvider credentialsProvider = new AWSStaticCredentialsProvider(credentials);
66 | - AmazonS3 cdnS3Client = AmazonS3Client.builder().withCredentials(credentialsProvider).withRegion(config.getCdnConfiguration().getRegion()).build();
67 | + AmazonS3 cdnS3Client = AmazonS3ClientBuilder.standard().withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(config.getCdnConfiguration().getEndpoint(), config.getCdnConfiguration().getRegion())).withPathStyleAccessEnabled(true).withClientConfiguration(clientConfiguration).withCredentials(new AWSStaticCredentialsProvider(credentials)).build();
68 | PostPolicyGenerator profileCdnPolicyGenerator = new PostPolicyGenerator(config.getCdnConfiguration().getRegion(), config.getCdnConfiguration().getBucket(), config.getCdnConfiguration().getAccessKey());
69 | PolicySigner profileCdnPolicySigner = new PolicySigner(config.getCdnConfiguration().getAccessSecret(), config.getCdnConfiguration().getRegion());
70 |
71 | ServerSecretParams zkSecretParams = new ServerSecretParams(config.getZkConfig().getServerSecret());
72 | ServerZkProfileOperations zkProfileOperations = new ServerZkProfileOperations(zkSecretParams);
73 | ServerZkAuthOperations zkAuthOperations = new ServerZkAuthOperations(zkSecretParams);
74 | boolean isZkEnabled = config.getZkConfig().isEnabled();
75 |
76 | - AttachmentControllerV1 attachmentControllerV1 = new AttachmentControllerV1(rateLimiters, config.getAwsAttachmentsConfiguration().getAccessKey(), config.getAwsAttachmentsConfiguration().getAccessSecret(), config.getAwsAttachmentsConfiguration().getBucket());
77 | + AttachmentControllerV1 attachmentControllerV1 = new AttachmentControllerV1(rateLimiters, config.getAwsAttachmentsConfiguration().getEndpoint(), config.getAwsAttachmentsConfiguration().getAccessKey(), config.getAwsAttachmentsConfiguration().getAccessSecret(), config.getAwsAttachmentsConfiguration().getBucket());
78 | AttachmentControllerV2 attachmentControllerV2 = new AttachmentControllerV2(rateLimiters, config.getAwsAttachmentsConfiguration().getAccessKey(), config.getAwsAttachmentsConfiguration().getAccessSecret(), config.getAwsAttachmentsConfiguration().getRegion(), config.getAwsAttachmentsConfiguration().getBucket());
79 | AttachmentControllerV3 attachmentControllerV3 = new AttachmentControllerV3(rateLimiters, config.getGcpAttachmentsConfiguration().getDomain(), config.getGcpAttachmentsConfiguration().getEmail(), config.getGcpAttachmentsConfiguration().getMaxSizeInBytes(), config.getGcpAttachmentsConfiguration().getPathPrefix(), config.getGcpAttachmentsConfiguration().getRsaSigningKey());
80 | KeysController keysController = new KeysController(rateLimiters, keys, accountsManager, directoryQueue);
81 | ```
82 |
83 | `service/src/main/java/org/whispersystems/textsecuregcm/configuration/AwsAttachmentsConfiguration.java`
84 |
85 | ```diff
86 | @JsonProperty
87 | private String region;
88 |
89 | + @NotEmpty
90 | + @JsonProperty
91 | + private String endpoint;
92 |
93 | public String getAccessKey() {
94 | return accessKey;
95 | }
96 |
97 | public String getAccessSecret() {
98 | return accessSecret;
99 | }
100 |
101 | public String getBucket() {
102 | return bucket;
103 | }
104 |
105 | public String getRegion() {
106 | return region;
107 | }
108 | +
109 | + public String getEndpoint() {
110 | + return endpoint;
111 | + }
112 | }
113 | ```
114 |
115 | `service/src/main/java/org/whispersystems/textsecuregcm/configuration/CdnConfiguration.java`
116 |
117 | ```diff
118 | @JsonProperty
119 | private String region;
120 |
121 | + @NotEmpty
122 | + @JsonProperty
123 | + private String endpoint;
124 |
125 | public String getAccessKey() {
126 | return accessKey;
127 | }
128 |
129 | public String getAccessSecret() {
130 | return accessSecret;
131 | }
132 |
133 | public String getBucket() {
134 | return bucket;
135 | }
136 |
137 | public String getRegion() {
138 | return region;
139 | }
140 |
141 | + public String getEndpoint() {
142 | + return endpoint;
143 | + }
144 | +
145 | }
146 | ```
147 |
148 | `service/src/main/java/org/whispersystems/textsecuregcm/s3/UrlSigner.java`
149 |
150 | ```diff
151 | + import java.io.IOException;
152 | + import java.security.InvalidKeyException;
153 | + import java.security.NoSuchAlgorithmException;
154 | +
155 | + import org.xmlpull.v1.XmlPullParserException;
156 | +
157 | + import io.minio.MinioClient;
158 | + import io.minio.errors.MinioException;
159 |
160 | import java.net.URL;
161 | import java.util.Date;
162 | public class UrlSigner {
163 |
164 | private static final long DURATION = 60 * 60 * 1000;
165 |
166 | - private final AWSCredentials credentials;
167 | + private final String endpoint;
168 | + private final String accessKey;
169 | + private final String accessSecret;
170 | private final String bucket;
171 |
172 | - public UrlSigner(String accessKey, String accessSecret, String bucket) {
173 | - this.credentials = new BasicAWSCredentials(accessKey, accessSecret);
174 | + public UrlSigner(String endpoint, String accessKey, String accessSecret, String bucket) {
175 | + this.endpoint = endpoint;
176 | + this.accessKey = accessKey;
177 | + this.accessSecret = accessSecret;
178 | this.bucket = bucket;
179 | }
180 |
181 | - public URL getPreSignedUrl(long attachmentId, HttpMethod method, boolean unaccelerated) {
182 | - AmazonS3 client = new AmazonS3Client(credentials);
183 | - GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucket, String.valueOf(attachmentId), method);
184 | -
185 | - request.setExpiration(new Date(System.currentTimeMillis() + DURATION));
186 | - request.setContentType("application/octet-stream");
187 | - if (unaccelerated) {
188 | - client.setS3ClientOptions(S3ClientOptions.builder().setPathStyleAccess(true).build());
189 | - } else {
190 | - client.setS3ClientOptions(S3ClientOptions.builder().setAccelerateModeEnabled(true).build());
191 | - }
192 | - return client.generatePresignedUrl(request);
193 | - }
194 |
195 |
196 | + public String getPreSignedUrl(long attachmentId, HttpMethod method) throws InvalidKeyException, NoSuchAlgorithmException, IOException, XmlPullParserException, MinioException {
197 | + String request = geturl(bucket, String.valueOf(attachmentId), method);
198 | + return request;
199 | + }
200 |
201 | + public String geturl(String bucketname, String attachmentId, HttpMethod method) throws NoSuchAlgorithmException, IOException, InvalidKeyException, XmlPullParserException, MinioException {
202 | +
203 | + String url = null;
204 | +
205 | + MinioClient minioClient = new MinioClient(endpoint, accessKey, accessSecret);
206 | + try {
207 | + if(method==HttpMethod.PUT){
208 | + url = minioClient.presignedPutObject(bucketname, attachmentId, 60 * 60 * 24);
209 | + }
210 | + if(method==HttpMethod.GET){
211 | + url = minioClient.presignedGetObject(bucketname, attachmentId);
212 | + }
213 | + System.out.println(url);
214 | + } catch(MinioException e) {
215 | + System.out.println("Error occurred: " + e);
216 | + } catch (java.security.InvalidKeyException e) {
217 | + e.printStackTrace();
218 | + }
219 | +
220 | + return url;
221 | + }
222 | ```
223 |
224 | `service/src/test/java/org/whispersystems/textsecuregcm/tests/util/UrlSignerTest.java`
225 |
226 | ```diff
227 | package org.whispersystems.textsecuregcm.tests.util;
228 |
229 | import com.amazonaws.HttpMethod;
230 | import org.junit.Test;
231 | import org.whispersystems.textsecuregcm.s3.UrlSigner;
232 |
233 | import java.net.URL;
234 |
235 | import static org.assertj.core.api.Assertions.assertThat;
236 |
237 | public class UrlSignerTest {
238 |
239 | @Test
240 | public void testTransferAcceleration() {
241 | - UrlSigner signer = new UrlSigner("foo", "bar", "attachments-test");
242 | - URL url = signer.getPreSignedUrl(1234, HttpMethod.GET, false);
243 | + //UrlSigner signer = new UrlSigner("foo", "bar", "attachments-test");
244 | + //URL url = signer.getPreSignedUrl(1234, HttpMethod.GET, false);
245 |
246 | - assertThat(url).hasHost("attachments-test.s3-accelerate.amazonaws.com");
247 | + //assertThat(url).hasHost("attachments-test.s3-accelerate.amazonaws.com");
248 | }
249 |
250 | @Test
251 | public void testTransferUnaccelerated() {
252 | - UrlSigner signer = new UrlSigner("foo", "bar", "attachments-test");
253 | - URL url = signer.getPreSignedUrl(1234, HttpMethod.GET, true);
254 | + //UrlSigner signer = new UrlSigner("foo", "bar", "attachments-test");
255 | + //URL url = signer.getPreSignedUrl(1234, HttpMethod.GET, true);
256 |
257 | - assertThat(url).hasHost("s3.amazonaws.com");
258 | + //assertThat(url).hasHost("s3.amazonaws.com");
259 | }
260 |
261 | }
262 | ```
263 |
264 | `service/src/main/java/org/whispersystems/textsecuregcm/controllers/AttachmentControllerV1.java`
265 |
266 | ```diff
267 |
268 | + import org.xmlpull.v1.XmlPullParserException;
269 | + import java.security.InvalidKeyException;
270 | + import java.security.NoSuchAlgorithmException;
271 | + import io.minio.errors.MinioException;
272 |
273 | import io.dropwizard.auth.Auth;
274 |
275 | @Path("/v1/attachments")
276 | public class AttachmentControllerV1 extends AttachmentControllerBase {
277 |
278 | @SuppressWarnings("unused")
279 | private final Logger logger = LoggerFactory.getLogger(AttachmentControllerV1.class);
280 |
281 | private static final String[] UNACCELERATED_REGIONS = {"+20", "+971", "+968", "+974"};
282 |
283 | private final RateLimiters rateLimiters;
284 | private final UrlSigner urlSigner;
285 |
286 | - public AttachmentControllerV1(RateLimiters rateLimiters, String accessKey, String accessSecret, String bucket) {
287 | + public AttachmentControllerV1(RateLimiters rateLimiters, String endpoint, String accessKey, String accessSecret, String bucket) {
288 | this.rateLimiters = rateLimiters;
289 | - this.urlSigner = new UrlSigner(accessKey, accessSecret, bucket);
290 | + this.urlSigner = new UrlSigner(endpoint, accessKey, accessSecret, bucket);
291 | }
292 |
293 | @Timed
294 | @GET
295 | @Produces(MediaType.APPLICATION_JSON)
296 | public AttachmentDescriptorV1 allocateAttachment(@Auth Account account)
297 | - throws RateLimitExceededException
298 | + throws RateLimitExceededException, InvalidKeyException, NoSuchAlgorithmException, IOException, XmlPullParserException, MinioException
299 | {
300 | if (account.isRateLimited()) {
301 | rateLimiters.getAttachmentLimiter().validate(account.getNumber());
302 | }
303 |
304 | long attachmentId = generateAttachmentId();
305 | - URL url = urlSigner.getPreSignedUrl(attachmentId, HttpMethod.PUT, Stream.of(UNACCELERATED_REGIONS).anyMatch(region -> account.getNumber().startsWith(region)));
306 | + String url = urlSigner.getPreSignedUrl(attachmentId, HttpMethod.PUT);
307 |
308 | - return new AttachmentDescriptorV1(attachmentId, url.toExternalForm());
309 | + return new AttachmentDescriptorV1(attachmentId, url);
310 |
311 | }
312 |
313 | @Timed
314 | @GET
315 | @Produces(MediaType.APPLICATION_JSON)
316 | @Path("/{attachmentId}")
317 | public AttachmentUri redirectToAttachment(@Auth Account account,
318 | @PathParam("attachmentId") long attachmentId)
319 | - throws IOException
320 | + throws IOException, InvalidKeyException, NoSuchAlgorithmException, XmlPullParserException, MinioException
321 | {
322 | - return new AttachmentUri(urlSigner.getPreSignedUrl(attachmentId, HttpMethod.GET, Stream.of(UNACCELERATED_REGIONS).anyMatch(region -> account.getNumber().startsWith(region))));
323 | + return new AttachmentUri(new URL(urlSigner.getPreSignedUrl(attachmentId, HttpMethod.GET)));
324 | }
325 |
326 | }
327 |
328 | ```
329 |
330 | `service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/AttachmentControllerTest.java`
331 |
332 | ```diff
333 | .addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>(ImmutableSet.of(Account.class, DisabledPermittedAccount.class)))
334 | .setMapper(SystemMapper.getMapper())
335 | .setTestContainerFactory(new GrizzlyWebTestContainerFactory())
336 | - .addResource(new AttachmentControllerV1(rateLimiters, "accessKey", "accessSecret", "attachment-bucket"))
337 | + .addResource(new AttachmentControllerV1(rateLimiters, "http://example.com:9000", "accessKey", "accessSecret", "attachment-bucket"))
338 | .addResource(new AttachmentControllerV2(rateLimiters, "accessKey", "accessSecret", "us-east-1", "attachmentv2-bucket"))
339 | .addResource(new AttachmentControllerV3(rateLimiters, "some-cdn.signal.org", "signal@example.com", 1000, "/attach-here", RSA_PRIVATE_KEY_PEM))
340 | .build();
341 |
342 | ...
343 |
344 | - assertThat(descriptor.getKey()).isEqualTo(descriptor.getAttachmentIdString());
345 | + assertThat(descriptor.getKey()).isEqualTo("attachments/" + descriptor.getAttachmentIdString());
346 | assertThat(descriptor.getAcl()).isEqualTo("private");
347 | assertThat(descriptor.getAlgorithm()).isEqualTo("AWS4-HMAC-SHA256");
348 | assertThat(descriptor.getAttachmentId()).isGreaterThan(0);
349 | assertThat(String.valueOf(descriptor.getAttachmentId())).isEqualTo(descriptor.getAttachmentIdString());
350 | ```
351 |
352 | `service/config/signal.yml`
353 |
354 | ```diff
355 | attachments: # S3 configuration
356 | accessKey: Q3AM3UQ867SPQQA43P2F # change to your Minio Access Key
357 | accessSecret: zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG # change to your Minio Access Secret
358 | bucket: bucket-name # change to your Minio bucket name
359 | region: us-east-1 # change to your Minio region
360 | + endpoint: http://domain.com:9000 # add this entry, then change to your own domain & Minio port
361 |
362 | cdn: # S3 configuration
363 | accessKey: Q3AM3UQ867SPQQA43P2F # change to your Minio Access Key
364 | accessSecret: zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG # change to your Minio Access Secret
365 | bucket: bucket-name # change to your Minio bucket name
366 | region: us-east-1 # change to your Minio region
367 | + endpoint: http://domain.com.id:9000 # add this entry, then change to your own domain & Minio port
368 |
369 | ```
370 |
371 | ## Signal Android
372 |
373 | `libsignal/service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java`
374 |
375 | ```diff
376 | RequestBody requestBody = new MultipartBody.Builder()
377 | .setType(MultipartBody.FORM)
378 | .addFormDataPart("acl", acl)
379 | .addFormDataPart("key", key)
380 | .addFormDataPart("policy", policy)
381 | + .addFormDataPart("success_action_status", "200")
382 | .addFormDataPart("Content-Type", contentType)
383 | .addFormDataPart("x-amz-algorithm", algorithm)
384 | .addFormDataPart("x-amz-credential", credential)
385 | .addFormDataPart("x-amz-date", date)
386 | .addFormDataPart("x-amz-signature", signature)
387 | .addFormDataPart("file", "file", file)
388 | .build();
389 | ```
390 |
391 | `app/build.gradle`
392 |
393 | ```diff
394 | versionCode canonicalVersionCode * postFixSize
395 | versionName canonicalVersionName
396 |
397 | minSdkVersion 19
398 | targetSdkVersion 28
399 | multiDexEnabled true
400 |
401 | vectorDrawables.useSupportLibrary = true
402 | project.ext.set("archivesBaseName", "Signal");
403 |
404 |
405 | buildConfigField "long", "BUILD_TIMESTAMP", getLastCommitTimestamp() + "L"
406 | - buildConfigField "String", "SIGNAL_URL", "\"https://textsecure-service.whispersystems.org\""
407 | - buildConfigField "String", "STORAGE_URL", "\"https://storage.signal.org\""
408 | - buildConfigField "String", "SIGNAL_CDN_URL", "\"https://cdn.signal.org\""
409 | - buildConfigField "String", "SIGNAL_CDN2_URL", "\"https://cdn2.signal.org\""
410 | - buildConfigField "String", "SIGNAL_CONTACT_DISCOVERY_URL", "\"https://api.directory.signal.org\""
411 | + buildConfigField "String", "SIGNAL_URL", "\"https://your-domain.com\""
412 | + buildConfigField "String", "STORAGE_URL", "\"https://your-domain.com\""
413 | + buildConfigField "String", "SIGNAL_CDN_URL", "\"https://your-domain.com/your-bucket-name\""
414 | + buildConfigField "String", "SIGNAL_CDN2_URL", "\"https://your-domain.com/your-bucket-name\""
415 | + buildConfigField "String", "SIGNAL_CONTACT_DISCOVERY_URL", "\"https://your-domain.com\""
416 | buildConfigField "String", "SIGNAL_SERVICE_STATUS_URL", "\"uptime.signal.org\""
417 | buildConfigField "String", "SIGNAL_KEY_BACKUP_URL", "\"https://api.backup.signal.org\""
418 | buildConfigField "String", "CONTENT_PROXY_HOST", "\"contentproxy.signal.org\""
419 | buildConfigField "int", "CONTENT_PROXY_PORT", "443"
420 | buildConfigField "String", "SIGNAL_AGENT", "\"OWA\""
421 | buildConfigField "String", "CDS_MRENCLAVE", "\"cd6cfc342937b23b1bdd3bbf9721aa5615ac9ff50a75c5527d441cd3276826c9\""
422 | buildConfigField "String", "KBS_ENCLAVE_NAME", "\"fe7c1bfae98f9b073d220366ea31163ee82f6d04bead774f71ca8e5c40847bfe\""
423 | buildConfigField "String", "KBS_MRENCLAVE", "\"a3baab19ef6ce6f34ab9ebb25ba722725ae44a8872dc0ff08ad6d83a9489de87\""
424 | buildConfigField "String", "UNIDENTIFIED_SENDER_TRUST_ROOT", "\"BXu6QIKVz5MA8gstzfOgRQGqyLqOwNKHL6INkv3IHWMF\""
425 | buildConfigField "String", "ZKGROUP_SERVER_PUBLIC_PARAMS", "\"AMhf5ywVwITZMsff/eCyudZx9JDmkkkbV6PInzG4p8x3VqVJSFiMvnvlEKWuRob/1eaIetR31IYeAbm0NdOuHH8Qi+Rexi1wLlpzIo1gstHWBfZzy1+qHRV5A4TqPp15YzBPm0WSggW6PbSn+F4lf57VCnHF7p8SvzAA2ZZJPYJURt8X7bbg+H3i+PEjH9DXItNEqs2sNcug37xZQDLm7X0=\""
426 | buildConfigField "String[]", "LANGUAGES", "new String[]{\"" + autoResConfig().collect { s -> s.replace('-r', '_') }.join('", "') + '"}'
427 | buildConfigField "int", "CANONICAL_VERSION_CODE", "$canonicalVersionCode"
428 | ```
429 |
430 | ## Important!
431 |
432 | It seems that MinIO mc have a little problem in creating bucket. For this, follow this steps:
433 |
434 | 1. Enable `MINIO_BROWSER` in `docker-compose.yml`.
435 | 2. Open MinIO in your browser, it's default to `http://domain:9000`.
436 | 3. Create a bucket.
437 | 4. Edit bucket policy and add `*` with `Read & Write` policy (making it public).
438 | 5. Create a `attachments` directory in bucket
439 |
--------------------------------------------------------------------------------
/signal-server-3.21/README.md:
--------------------------------------------------------------------------------
1 | # Signal New Code
2 | This guide is written by using Signal Server v3.21 & Signal Android v4.66.1
3 |
4 | (For Signal v2.92 please refer to [signal-server](../signal-server-2.92/) guide)
5 |
6 | ## Related Guide
7 | * [Updated MinIO Guide](./MINIO.md)
8 | * [Same Requirement as Old Version](../signal-server-2.92/)
9 | * [Steps for Requirement](../signal-server-2.92/DEPENDENCIES.md)
10 |
11 | ## Requirement
12 | * Same requirement as old version
13 | * GCP is currenlty disabled
14 |
15 | ## Steps for Signal Server
16 |
17 | 1. Clone signal server
18 |
19 | ```
20 | git clone https://github.com/signalapp/signal-server-2.92/
21 | ```
22 |
23 | 2. Create your config using new `config.yml`
24 |
25 | 3. Update `zkgroup` dependency in `service/pom.xml` from `0.6.0` to `0.7.0`
26 |
27 | ```
28 | ...
29 |
30 |
31 | org.signal
32 | zkgroup-java
33 | 0.7.0
34 |
35 |
36 | ...
37 | ```
38 |
39 | 4. (Optional, only if you see error regarding this line) Add this `import` statement and fix the `final PrivateKey key` variable in `service/src/main/java/org/whispersystems/textsecuregcm/gcp/CanonicalRequestSigner.java`
40 |
41 | ```
42 | ...
43 |
44 | import java.security.KeyPair;
45 |
46 | ...
47 |
48 | final PrivateKey key = ((KeyPair) pemReader.readObject()).getPrivate();
49 |
50 | ...
51 | ```
52 |
53 | 5. (Optional, for attachment bucket) Change `objectName` in `service/src/main/java/org/whispersystems/textsecuregcm/controllers/AttachmentControllerV2.java` and change the variable.
54 |
55 | ```
56 | ...
57 |
58 | String objectName = "attachments/" + String.valueOf(attachmentId);
59 |
60 | ...
61 | ```
62 |
63 | 6. (Optional, if you follow the previous step) Update the test file in `service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/AttachmentControllerTest.java`
64 |
65 | ```
66 | ...
67 |
68 | assertThat(descriptor.getKey()).isEqualTo("attachments/" + descriptor.getAttachmentIdString());
69 |
70 | ...
71 | ```
72 |
73 | 7. Compile the server code
74 |
75 | ```
76 | mvn clean install -DskipTests
77 | ```
78 |
79 | 8. Generate `zkparams` value for the config.
80 |
81 | ```
82 | java -jar service/target/TextSecureServer-3.21.jar zkparams
83 | ```
84 |
85 | **Note**: The public key generated in this step may not suffice the length needed in the android, in that case, please add '=' to the end of the public key.
86 |
87 | 9. Generate `UnidentifiedDelivery` value for the config.
88 |
89 | ```
90 | java -jar service/target/TextSecureServer-3.21.jar certificate -ca
91 | ```
92 |
93 | Use the private key from previous command here. You can use any ID for the `key_ID`.
94 |
95 | ```
96 | java -jar service/target/TextSecureServer-3.21.jar certificate --key priv_key_from_step_above --id key_ID
97 | ```
98 |
99 | 10. Setup Nginx & coTurn, it has the same steps as the old code.
100 |
101 | 11. Run PostgreSQL & Redis, using the `docker-compose` is recommended.
102 |
103 | 12. Run database migration
104 |
105 | ```
106 | java -jar service/target/TextSecureServer-3.21.jar abusedb migrate service/config/config.yml
107 |
108 | java -jar service/target/TextSecureServer-3.21.jar accountdb migrate service/config/config.yml
109 |
110 | java -jar service/target/TextSecureServer-3.21.jar messagedb migrate service/config/config.yml
111 | ```
112 |
113 | 13. Run the server
114 |
115 | ```
116 | java -jar service/target/TextSecureServer-3.21.jar server service/config/config.yml
117 | ```
118 |
119 | ## Steps for Signal Android
120 |
121 | 1. Clone signal android
122 |
123 | ```
124 | git clone https://github.com/signalapp/Signal-Android/
125 | ```
126 |
127 | 2. Import your SSL certificate to `app/src/main/res/raw/whisper.store`. The password is "whisper" without quotation mark.
128 |
129 | 3. Remove `distributionSha256Sum` line from `gradle/wrapper/gradle-wrapper.properties`
130 |
131 | 4. Modify the server url in `app/build.gradle` under `defaultConfig`
132 |
133 | ```
134 | ...
135 |
136 | buildConfigField "String", "SIGNAL_URL", "\"https://domain.com\""
137 | buildConfigField "String", "STORAGE_URL", "\"https://domain.com\""
138 | buildConfigField "String", "SIGNAL_CDN_URL", "\"https://xxx.cloudfront.net\""
139 | buildConfigField "String", "SIGNAL_CDN2_URL", "\"https://xxx.cloudfront.net\""
140 | buildConfigField "String", "SIGNAL_CONTACT_DISCOVERY_URL", "\"https://domain.com\""
141 | buildConfigField "String", "SIGNAL_SERVICE_STATUS_URL", "\"https://domain.com\""
142 | buildConfigField "String", "SIGNAL_KEY_BACKUP_URL", "\"https://domain.com\""
143 |
144 | ...
145 |
146 | buildConfigField "String", "UNIDENTIFIED_SENDER_TRUST_ROOT", "\"change-to-unidentified-delivery-public-key\""
147 | buildConfigField "String", "ZKGROUP_SERVER_PUBLIC_PARAMS", "\"change-to-zkparams-public-key\""
148 |
149 | ...
150 | ```
151 |
152 | 5. Select `Sync Project` when prompted after saving the build.gradle change. **Note**: Please ignore when prompted to update the dependency.
153 |
154 | 6. (Optional, for attachment bucket) Change `ATTACHMENT_UPLOAD_PATH` in `libsignal/service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java`
155 |
156 | ```
157 | ...
158 |
159 | private static final String ATTACHMENT_UPLOAD_PATH = "";
160 |
161 | ...
162 | ```
163 |
164 | 7. Setup Firebase, it has the same steps as the old code.
165 |
166 | 8. Build the project.
167 |
168 | ## Known Issue
169 | 1. User can't create PIN in this server version, so skip it after registration.
170 | 2. User profile (name & display picture) not found because the API return 404.
171 | 3. Due to this server version being too old, the android client version that compatible to this server is too old too, so it give "Version Expired" error, for this please refer to the [Signal Android Guide](../signal-android/README.md).
--------------------------------------------------------------------------------
/signal-server-3.21/config.yml:
--------------------------------------------------------------------------------
1 | twilio: # twilio configuration for sms otp
2 | accountId: xxxxxxxxxx # your account id
3 | accountToken: xxxxxxxxxx # your account token
4 | localDomain: domain.com # your domain
5 | numbers:
6 | - "+000" # your twilio phone numbers
7 |
8 | backupService:
9 | userAuthenticationTokenSharedSecret: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # head -c 16 /dev/urandom | hexdump -ve '1/1 "%.2x"'
10 |
11 | storageService:
12 | userAuthenticationTokenSharedSecret: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # head -c 16 /dev/urandom | hexdump -ve '1/1 "%.2x"'
13 |
14 | push:
15 | queueSize: 200
16 |
17 | turn: # turn server configuration for voice & video call
18 | secret: xxxxx # your turn server's secret
19 | uris:
20 | - stun:domain.com:3478 # change to your turn server domain
21 | - stun:domain.com:5349 # change to your turn server domain
22 | - turn:domain.com:5349?transport=udp # change to your turn server domain
23 | - turn:domain.com:3478?transport=udp # change to your turn server domain
24 |
25 | cache: # redis server for cache cluster
26 | url: redis://127.0.0.1:6379/cache # change to your redis server
27 | replicaUrls:
28 | - redis://127.0.0.1:6379/cacheReplica # change to your redis server
29 |
30 | directory:
31 | redis: # redis server for directory cluster
32 | url: redis://127.0.0.1:6379/directory # change to your redis server
33 | replicaUrls:
34 | - redis://127.0.0.1:6379/directoryReplica # change to your redis server
35 | client: # for communication with cds
36 | userAuthenticationTokenSharedSecret: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # head -c 16 /dev/urandom | hexdump -ve '1/1 "%.2x"'
37 | userAuthenticationTokenUserIdSecret: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # head -c 16 /dev/urandom | hexdump -ve '1/1 "%.2x"'
38 | sqs: # sqs for queueing
39 | accessKey: XXXXXXXXXXXXXXX # your aws iam access key
40 | accessSecret: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # your aws iam access secret
41 | queueUrl: https://sqs.xxxxx.amazonaws.com/xxxxxxx/xxxxxx.fifo # your fifo queue url
42 | server:
43 | replicationUrl: http://127.0.0.1:9090 # cds replication endpoint base url
44 | replicationPassword: mypassword # cds replication endpoint password
45 | replicationCaCertificate: |
46 | -----BEGIN CERTIFICATE-----
47 | ..........................
48 | -----END CERTIFICATE-----
49 |
50 | messageCache: # redis server for message store cache
51 | redis:
52 | url: redis://127.0.0.1:6379/messageCache # change to your redis server
53 | replicaUrls:
54 | - redis://127.0.0.1:6379/messageCacheReplica # change to your redis server
55 |
56 | awsAttachments: # aws s3 configuration
57 | accessKey: XXXXXXXXXXXXXXX # your aws iam access key
58 | accessSecret: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # your aws iam access secret
59 | bucket: xxxxx # your s3 bucket name
60 | region: xx-xxxxx-x # your region
61 |
62 | cdn: # aws cdn configuration
63 | accessKey: XXXXXXXXXXXXXXX # your aws iam access key
64 | accessSecret: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # your aws iam access secret
65 | bucket: xxxxx # your s3 bucket name
66 | region: xx-xxxxx-x # your region
67 |
68 | gcpAttachments: # gcp storage configuration
69 | domain: domain.com
70 | email: dummy@dummy.com
71 | maxSizeInBytes: 1000000
72 | pathPrefix: /
73 | rsaSigningKey: "-----BEGIN RSA PRIVATE KEY-----\n ..... \n-----END RSA PRIVATE KEY-----\n"
74 |
75 | pubsub: # redis server for pubsub cache
76 | url: redis://127.0.0.1:6379/cache # change to your redis server
77 | replicaUrls:
78 | - redis://127.0.0.1:6379/cacheReplica # change to your redis server
79 |
80 | remoteConfig: # remote configuration token
81 | authorizedTokens:
82 | - dummy
83 |
84 | zkConfig: # zkgroup config, run using your build jar
85 | serverSecret: xxxxxxxxxx # java -jar TextSecure.jar zkparams
86 | serverPublic: xxxxxxxxxx # java -jar TextSecure.jar zkparams
87 | enabled: true
88 |
89 | accountsDatabase: # database for account
90 | driverClass: org.postgresql.Driver
91 | user: postgres # change to your psql username
92 | password: postgres # change to your psql password
93 | url: jdbc:postgresql://127.0.0.1:5432/signal #change to your psql url, port, and database name
94 |
95 | messageStore: # database for message
96 | driverClass: org.postgresql.Driver
97 | user: postgres # change to your psql username
98 | password: postgres # change to your psql password
99 | url: jdbc:postgresql://127.0.0.1:5432/signal #change to your psql url, port, and database name
100 |
101 | abuseDatabase: # database for abuse
102 | driverClass: org.postgresql.Driver
103 | user: postgres # change to your psql username
104 | password: postgres # change to your psql password
105 | url: jdbc:postgresql://127.0.0.1:5432/signal #change to your psql url, port, and database name
106 |
107 | gcm: # firebase messaging
108 | senderId: 000000000 # your sender id
109 | apiKey: xxxxxxxxxxx # your api key
110 |
111 | recaptcha: # google recaptcha v3
112 | secret: xxxxxxxxxxx # your recaptcha secret
113 |
114 | accountDatabaseCrawler: # crawler for account database
115 | chunkSize: 1000
116 | chunkIntervalMs: 1000000
117 |
118 | apn: # apple push nottification, just use this value if you don't plan to develop signal ios
119 | pushCertificate: "-----BEGIN CERTIFICATE-----\r\nMIIFjzCCBHegAwIBAgIIfkSIVVtC9UIwDQYJKoZIhvcNAQEFBQAwgZYxCzAJBgNV\r\nBAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3Js\r\nZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3\r\naWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw\r\nHhcNMTkwNTA5MTc0NzUyWhcNMjAwNTA4MTc0NzUyWjCBjjElMCMGCgmSJomT8ixk\r\nAQEMFWNvbS5xYW5kb3JhcHAucWFuZG9yMzFDMEEGA1UEAww6QXBwbGUgRGV2ZWxv\r\ncG1lbnQgSU9TIFB1c2ggU2VydmljZXM6IGNvbS5xYW5kb3JhcHAucWFuZG9yMzET\r\nMBEGA1UECwwKTlA2UE5GUUdBODELMAkGA1UEBhMCVVMwggEiMA0GCSqGSIb3DQEB\r\nAQUAA4IBDwAwggEKAoIBAQDIo+8GD6chbxCFDI7bTK6iTxQHnD/FiXJvEYBbO3ey\r\nQV8/XZm66VbO+RbA4UArA2bjdW5KZkSQ7hSYJcomQCmcmpqmrXjcaL0KfeWinSz4\r\nmbXxoXK9JsoUSjOfy7yXHGDLZSHGVvn5gO1FU9WPX3Iu7YYMT1hwnMgmZIuzn5K9\r\nAiyxvQzSlIlEsnpoqTDUz+1eXtPXAISPQT8+jM8JoZohTrODuUUj2rSPWi62kMBP\r\nsX4H4ncSjI56DG/E0lvUV7d0PEiUnoxGwzycmGJVgpZqv/E6p1eUNT7Utl4ktuiq\r\nEv8ft3+brJx+FokMi7AKjxLvlYS18LTG6JbQMcmXHsdlAgMBAAGjggHlMIIB4TAJ\r\nBgNVHRMEAjAAMB8GA1UdIwQYMBaAFIgnFwmpthhgi+zruvZHWcVSVKO3MIIBDwYD\r\nVR0gBIIBBjCCAQIwgf8GCSqGSIb3Y2QFATCB8TCBwwYIKwYBBQUHAgIwgbYMgbNS\r\nZWxpYW5jZSBvbiB0aGlzIGNlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVz\r\nIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFyZCB0ZXJt\r\ncyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRlIHBvbGljeSBhbmQg\r\nY2VydGlmaWNhdGlvbiBwcmFjdGljZSBzdGF0ZW1lbnRzLjApBggrBgEFBQcCARYd\r\naHR0cDovL3d3dy5hcHBsZS5jb20vYXBwbGVjYS8wEwYDVR0lBAwwCgYIKwYBBQUH\r\nAwIwTQYDVR0fBEYwRDBCoECgPoY8aHR0cDovL2RldmVsb3Blci5hcHBsZS5jb20v\r\nY2VydGlmaWNhdGlvbmF1dGhvcml0eS93d2RyY2EuY3JsMB0GA1UdDgQWBBR6BPca\r\ngaRwijNzbL4lcYrJkU2r7TALBgNVHQ8EBAMCB4AwEAYKKoZIhvdjZAYDAQQCBQAw\r\nDQYJKoZIhvcNAQEFBQADggEBAFcIWIc1T0PtgeaMgjwQcTmfJGy8MUdIO/hdElo/\r\nOZF4ts4c2xkddanZ9IzOCj/HzmRJEs6WVZhNxySc3Cxo6KejsLbGLJmMoEh72xyQ\r\nwgnMBiapMnRhCfd68NMnTUClNHvGsg+NipnAN63r+HZSgPsCMXHsEMyZ+qQendRc\r\nDZH6m5FN1TqdAVtChdDPItzYJuQpyeKJpiiQGeCd6YjCELkWVxHcTU67CWmkuVqx\r\n9BRoANbJXty3b9T5KHxJYcEMj3pvsgcTOR/nGKIT9+B2iqrt6i/YY2n4p5NXXFzZ\r\nHewPc93srGXfyrvW7SeQs+93vZ7WlntfihY7WCoUbEOnHso=\r\n-----END CERTIFICATE-----"
120 | pushKey: "-----BEGIN RSA PRIVATE KEY-----\r\nMIIEpQIBAAKCAQEAyKPvBg+nIW8QhQyO20yuok8UB5w/xYlybxGAWzt3skFfP12Z\r\nuulWzvkWwOFAKwNm43VuSmZEkO4UmCXKJkApnJqapq143Gi9Cn3lop0s+Jm18aFy\r\nvSbKFEozn8u8lxxgy2Uhxlb5+YDtRVPVj19yLu2GDE9YcJzIJmSLs5+SvQIssb0M\r\n0pSJRLJ6aKkw1M/tXl7T1wCEj0E/PozPCaGaIU6zg7lFI9q0j1outpDAT7F+B+J3\r\nEoyOegxvxNJb1Fe3dDxIlJ6MRsM8nJhiVYKWar/xOqdXlDU+1LZeJLboqhL/H7d/\r\nm6ycfhaJDIuwCo8S75WEtfC0xuiW0DHJlx7HZQIDAQABAoIBAFNo+1xMs5FNp9N4\r\nBgebGFp3f38ucMCBRGZyIydKUJd1X9Bq7BbtHF6M5O2odtGq52IWFpStcUHDCCK8\r\nSw6dy+7DwxkZss4GaNhswENbDjAHTsE1+goyjv3iXxXGUA+OB5tm3qSi0ebstzcE\r\nBBtHdaOWsQx7C+w88WQslntFEm6qNSqeM1s5eQ20wSnlAA43Pm+NdVNM+JYX4iqt\r\n/MFdaPINE5XAXyBRAYir0l1dkofeGsb4rCuZmXSmjRRcC9vdhzmjrDUxLspOI7du\r\nUT7vYy3/hWFdng5oHu7JoDVrxF8/5e11j04jTq8SiHYfxUdR/Pmzt4/Nnu8SVjDJ\r\nesZJhwECgYEA7ElQyw2fzF3CQ2x526SBvLfFSxuX8zDkPjFtsrpZf78MHz9AR3ak\r\nlgEjR9aOfeWC1nRRZ6kGG8AgbpIZEN5KoCwZM98D6oub0VIg8iuR1UeArjebI8QV\r\nq2q5BeR3v2nmHsDXeG57D7O7DRko1tazp+d3/19hmMBOa4os19SsEaUCgYEA2WFH\r\nPtVwRtsQNcuKBCKHGzEVDG6Gm86kye2AdfJB7USr0fvD3HRah/chGi+foz+CDwwA\r\nzuBcgL45rSZZCTA9AFzihVpAFJ+a3GDgT/hSjJmMX3vHmySaZABmKNsiXRlPW+Fr\r\n7jkXwk7JH6emgcBH9d+Gyyp6ybVZvv/tNrFCcsECgYEA1DhtNlLASZ+UUVZmhF3W\r\noJc1vmXELgqllS5z5mj05YXD73Sx2P24iXnwJB+Sz4SJ5O+IBeCLufTvrB/QH5Rn\r\n1kCFSk9thwVpJ7HqIVf8nWChNNiAoLkG9XTfRWmUG/mTU9/EJ0ijgtDcmcEVKxCf\r\nP5jn8BfM4pMmW/Q4nolHGnkCgYEAnnLT1a8KSft/k1arYVwxktZx+z/NCmDTqQRf\r\nMJnHCEWX4FVdbKG7I4Q1Mrsn53xxNrqPFDxh8M23iMh8+b+Zl1wdGQqxztaPsLdE\r\nicX9ldKOiULWOfWyO9Y2oO0p3SaHu/dSDrC66r02yMYRDl6zlTq7K/fozIJNynUN\r\n2WHXh4ECgYEAneclaDj/KMtedyaJcblyEjtpzW3V/Gotws2cAaHqVaWDJHRrp0bl\r\nZNg8Jjf/NoixxENZ5bdduAR3JEjTwScRpOluO45Huq2gIMS7jsBKoDAvF94DNUI5\r\nNDtK8x/8+SOdk5HAGNHDoMqVbgZ2NpeOIGxzoZUCCK4f1j1CZbeEIio=\r\n-----END RSA PRIVATE KEY-----"
121 | bundleId: com.notused
122 | sandbox: true
123 |
124 | unidentifiedDelivery: # unidentified delivery or untrusted sender root, run using your build jar & config
125 | certificate: xxxxxxx
126 | privateKey: xxxxxxxx
127 | expiresDays: 365
128 |
129 | voiceVerification: # voice otp verification, afaik it use vox service
130 | url: https://server.chatapp.net
131 | locales:
132 | - en
133 |
134 | pushScheduler: # redis server for push scheduler
135 | url: redis://127.0.0.1:6379/pushScheduler # change to your redis server
136 | replicaUrls:
137 | - redis://127.0.0.1:6379/pushSchedulerReplica # change to your redis server
138 |
139 | server: # server configuration
140 | applicationConnectors:
141 | - type: http
142 | port: 8080 # your signal server will run on this port
143 | adminConnectors:
144 | - type: http
145 | port: 8081 # your signal server admin will run on this port
146 |
--------------------------------------------------------------------------------
/signal-server-4.xx/README.md:
--------------------------------------------------------------------------------
1 | # Signal Server
2 | Written using Signal Server v4.97 in Ubuntu 20.04 x64
3 |
4 | This guide is a WIP! It will allow the server to launch and register devices, nothing
5 | more has been tested yet.
6 |
7 | ## Dependencies
8 |
9 | - https://docs.docker.com/engine/install/ubuntu/
10 | - `sudo apt install -y git maven`
11 |
12 | Download the server and build:
13 | ```
14 | git clone https://github.com/signalapp/Signal-Server.git
15 | cd Signal-Server
16 | git checkout v4.97
17 | mvn -DskipTests package
18 | ```
19 |
20 | ## Databases:
21 |
22 | ```
23 | docker run -d --restart unless-stopped --name accountdb -e "POSTGRES_PASSWORD=postgres" -e "POSTGRES_DB=accountdb" -p 5432:5432 postgres:11
24 | docker run -d --restart unless-stopped --name abusedb -e "POSTGRES_PASSWORD=postgres" -e "POSTGRES_DB=abusedb" -p 5433:5432 postgres:11
25 | docker run -d --restart unless-stopped --name messagedb -e "POSTGRES_PASSWORD=postgres" -e "POSTGRES_DB=messagedb" -p 5434:5432 postgres:11
26 | ```
27 | (Alternatively use https://github.com/mrts/docker-postgresql-multiple-databases to run all in one container/port)
28 |
29 | You must run the database migrations to initialise them:
30 |
31 | ```
32 | cd Signal-Server/service/target
33 | java -jar TextSecureServer-4.97.jar accountdb migrate config.yml
34 | java -jar TextSecureServer-4.97.jar abusedb migrate config.yml
35 | java -jar TextSecureServer-4.97.jar messagedb migrate config.yml
36 | ```
37 |
38 | ## Redis
39 |
40 | ```
41 | docker run -d --restart unless-stopped --name redis -e "IP=0.0.0.0" -p 7000-7005:7000-7005 grokzen/redis-cluster:latest
42 | ```
43 |
44 | ## Proxy
45 |
46 | Signal server expects the HTTP requests to arrive with the `X-Forwarded-For` header - i.e. via a proxy
47 |
48 | You can use Nginx with the [example config](./nginx.conf),
49 | (note the nginx config should be changed to add your certificates):
50 |
51 | ```
52 | docker run -d --restart unless-stopped --name nginx --net="host" -v nginx.conf:/etc/nginx/nginx.conf:ro nginx
53 | ```
54 |
55 | ## Signal Configuration
56 |
57 | An example config is [provided](./config.yml), however many fields must be initialised
58 | with valid keys and certificates for it to start - even if you are not using those particular
59 | services. Instructions to do so are included in the config file.
60 |
61 | ## Launch
62 |
63 | ```
64 | java -jar Signal-Server/service/target/TextSecureServer-4.97.jar server config.yml
65 | ```
66 |
67 | (See [signal-server-autostart](../signal-server-autostart/) to install as a service)
68 |
--------------------------------------------------------------------------------
/signal-server-4.xx/config.yml:
--------------------------------------------------------------------------------
1 | # Number must be in "" to include +
2 | # Code must be 6 digits long
3 | testDevices:
4 | - number: "+44..."
5 | code: 111111
6 |
7 | abuseDatabase:
8 | driverClass: org.postgresql.Driver
9 | user: postgres
10 | password: postgres
11 | url: jdbc:postgresql://127.0.0.1:5433/abusedb
12 |
13 | accountDatabaseCrawler:
14 | chunkSize: 1000
15 | chunkIntervalMs : 8000
16 |
17 | accountsDatabase:
18 | driverClass: org.postgresql.Driver
19 | user: postgres
20 | password: postgres
21 | url: jdbc:postgresql://127.0.0.1:5432/accountdb
22 |
23 | messageStore:
24 | driverClass: org.postgresql.Driver
25 | user: postgres
26 | password: postgres
27 | url: jdbc:postgresql://127.0.0.1:5434/messagedb
28 |
29 | apn:
30 | sandbox: true
31 | bundleId: test
32 | keyId: test
33 | teamId: test
34 | signingKey: | # openssl genpkey -out rsakey.pem -algorithm EC -pkeyopt ec_paramgen_curve:P-256
35 | -----BEGIN PRIVATE KEY-----
36 | -----END PRIVATE KEY-----
37 |
38 | awsAttachments:
39 | accessKey: test
40 | accessSecret: test
41 | bucket: test
42 | region: us-east-1
43 |
44 | backupService:
45 | userAuthenticationTokenSharedSecret: 6b9cf1432f4dc50f991bdab29ff44377
46 |
47 | cacheCluster:
48 | urls:
49 | - redis://127.0.0.1:7000/
50 |
51 | cdn:
52 | accessKey: test
53 | accessSecret: test
54 | bucket: signal
55 | region: us-east-1
56 |
57 | directory:
58 | redis:
59 | url: redis://127.0.0.1:7000/directory
60 | replicaUrls:
61 | - redis://127.0.0.1:7000/directoryReplica
62 | client:
63 | userAuthenticationTokenSharedSecret: # head -c 16 /dev/urandom | hexdump -ve '1/1 "%.2x"'
64 | userAuthenticationTokenUserIdSecret: # head -c 16 /dev/urandom | hexdump -ve '1/1 "%.2x"'
65 | sqs:
66 | accessKey: test
67 | accessSecret: test
68 | queueUrls:
69 | - https://sqs.region.amazonaws.com/id/name.fifo
70 | server:
71 | - replicationName: test
72 | replicationUrl: http://127.0.0.1:9090
73 | replicationPassword: password
74 | replicationCaCertificate: | # https://www.ibm.com/docs/en/runbook-automation?topic=certificate-generate-root-ca-key
75 | -----BEGIN CERTIFICATE-----
76 | -----END CERTIFICATE-----
77 | -----BEGIN RSA PRIVATE KEY-----
78 | -----END RSA PRIVATE KEY-----
79 |
80 | gcm:
81 | senderId: 12345
82 | apiKey: test
83 |
84 | gcpAttachments:
85 | domain: dummy.com
86 | email: dummy@dummy.com
87 | maxSizeInBytes: 1000000
88 | pathPrefix: /
89 | rsaSigningKey: | # openssl genpkey -out rsakey.pem -algorithm RSA -pkeyopt rsa_keygen_bits:2048
90 | -----BEGIN PRIVATE KEY-----
91 | -----END PRIVATE KEY-----
92 |
93 | messageCache:
94 | cluster:
95 | urls:
96 | - redis://127.0.0.1:7000/
97 |
98 | metricsCluster:
99 | urls:
100 | - redis://127.0.0.1:7000/
101 |
102 | micrometer:
103 | uri: proxy://localhost:2878
104 |
105 | paymentsService:
106 | userAuthenticationTokenSharedSecret: 6b9cf1432f4dc50f991bdab29ff44377
107 |
108 | pubsub:
109 | url: redis://127.0.0.1:7000/cache
110 | replicaUrls:
111 | - redis://127.0.0.1:7000/cacheReplica
112 |
113 | push:
114 | queueSize: 200
115 |
116 | pushScheduler:
117 | url: redis://127.0.0.1:7000/pushScheduler
118 | replicaUrls:
119 | - redis://127.0.0.1:7000/pushSchedulerReplica
120 |
121 | recaptcha:
122 | secret: test
123 |
124 | remoteConfig:
125 | authorizedTokens:
126 | - dummy
127 | globalConfig:
128 | dummy: dummy
129 |
130 | server:
131 | applicationConnectors:
132 | - type: http
133 | port: 8081
134 | adminConnectors:
135 | - type: http
136 | port: 8082
137 |
138 | storageService:
139 | userAuthenticationTokenSharedSecret: 6b9cf1432f4dc50f991bdab29ff44377
140 | uri: redis://127.0.0.1:7000/
141 |
142 | turn:
143 | secret: secret# TURN server secret
144 | uris:
145 | - stun:example.com:3478
146 | - stun:example.com:5349
147 | - turn:example.com:3478?transport=udp
148 | - turn:example.com:5349?transport=udp
149 |
150 | twilio:
151 | accountId: test
152 | accountToken: test
153 | nanpaMessagingServiceSid: test
154 | messagingServiceSid: test
155 | localDomain: example.com
156 | iosVerificationText: "verification code is - %s"
157 | androidNgVerificationText: "verification code is - %s"
158 | android202001VerificationText: "verification code is - %s"
159 | genericVerificationText: "verification code is - %s"
160 | numbers:
161 | - test
162 | senderId:
163 | defaultSenderId: test
164 |
165 | # Generate public and private with: java -jar TextSecureServer.jar certificate -ca
166 | # Generate certificate with: java -jar TextSecureServer-4.97.jar certificate --key PRIVATE_KEY = --id 1
167 | unidentifiedDelivery:
168 | certificate:
169 | privateKey:
170 | expiresDays: 365
171 |
172 | voiceVerification:
173 | url: https://example.com
174 | locales:
175 | - en
176 |
177 | zkConfig:
178 | serverSecret: # java -jar TextSecure.jar zkparams
179 | serverPublic: # java -jar TextSecure.jar zkparams
180 | enabled: true
181 |
182 | featureFlag:
183 | authorizedTokens:
184 | - test
185 |
--------------------------------------------------------------------------------
/signal-server-4.xx/nginx.conf:
--------------------------------------------------------------------------------
1 | events {
2 | }
3 | http {
4 | server {
5 | listen 8080;
6 | server_name frontend;
7 | location / {
8 | proxy_set_header X-Real-IP $remote_addr;
9 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
10 | proxy_pass http://localhost:8081;
11 | proxy_ssl_session_reuse off;
12 | proxy_redirect off;
13 | proxy_set_header Upgrade $http_upgrade;
14 | proxy_set_header Connection "Upgrade";
15 | proxy_set_header Host $host;
16 | proxy_http_version 1.1;
17 | }
18 | client_max_body_size 50M;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/signal-server-5.xx/README.md:
--------------------------------------------------------------------------------
1 | # Signal Server
2 | Written using Signal Server v5.51 in Ubuntu 18.04 x64
3 |
4 | This guide is a WIP!
5 |
6 | ## Background
7 |
8 | To run Signal server 5.xx out of the box / without substantial source modifications
9 | it requires it to be run in an AWS EC2 instance.
10 |
11 | The EC2 instance must be given an associated IAM role.
12 |
13 | ### Amazon AppConfig
14 |
15 | Signal v5 uses a [DynamicConfigurationManager](https://github.com/signalapp/Signal-Server/blob/10cd60738ac086407f01d68700717b7e544739b0/service/src/main/java/org/whispersystems/textsecuregcm/storage/DynamicConfigurationManager.java#L45)
16 | to fetch config changes from an AWS AppConfig.
17 |
18 | You must setup and deploy an [AppConfig environment](https://docs.aws.amazon.com/appconfig/latest/userguide/what-is-appconfig.html)
19 | with the same parameters as the config file:
20 | ```
21 | appConfig:
22 | application: ...
23 | environment:
24 | configuration:
25 | ```
26 | The EC2 IAM role must have a policy granting access to the AppConfig.
27 |
28 | ### Amazon DynamoDb
29 |
30 | Signal v5 uses a number of DynamoDb tables. You must create these and put them in the config
31 | file, e.g.:
32 |
33 | ```
34 | messageDynamoDb:
35 | region: us-east-1
36 | tableName: ...
37 | ```
38 |
39 | Once again the EC2 IAM role must have a policy granting access to the tables.
40 |
41 | ## Install
42 |
43 | - Git
44 | - Maven
45 |
46 | ```
47 | sudo apt update
48 | sudo apt install git maven -y
49 | ```
50 |
51 | ## Dependencies
52 | Note that in v5.xx, there are a lot of new dependecies that didn't exist in older version.
53 |
54 | - [coTurn Server](https://github.com/coturn/coturn)
55 | - [Redis](https://redis.io/)
56 | - [PostgreSQL](https://www.postgresql.org/)
57 | - [AWS DynamoDB](https://aws.amazon.com/dynamodb/)
58 | - [AWS SQS](https://aws.amazon.com/sqs/)
59 | - [AWS S3 Bucket](https://aws.amazon.com/s3/)
60 | - [AWS Cloudfront CDN](https://aws.amazon.com/cloudfront/)
61 | - [Google Cloud Platform](https://cloud.google.com/gcp)
62 | - [Twilio SMS & Voice](https://www.twilio.com/)
63 | - [Firebase](https://firebase.google.com/)
64 | - [Recaptcha v3](https://www.google.com/recaptcha/admin/create)
65 | - [Apple Push Notification](https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/establishing_a_certificate-based_connection_to_apns)
66 | - [Micrometer](http://micrometer.io/)
67 | - [Fixer](https://fixer.io/)
68 |
69 | ## Steps
70 | 1. Clone
71 | ```
72 | git clone https://github.com/signalapp/Signal-Server
73 | cd Signal-Server
74 | ```
75 |
76 | 2. Fetch dependencies
77 | ```
78 | mvn dependency:go-offline
79 | ```
80 |
81 | 3. Compile
82 | ```
83 | mvn -e -B package
84 | ```
85 |
86 | 4. Create config.yml, [see example](./config.yml)
87 |
88 | Steps to run the server will be updated
89 |
90 | ## Running with docker
91 | **This docker containers only support some dependency that can be run locally.**
92 |
93 | 1. Copy `docker-compose.yml`
94 |
95 | 2. Start the compose
96 | ```
97 | docker-compose start -D
98 | ```
99 |
100 | 3. Run the server with your config
101 |
--------------------------------------------------------------------------------
/signal-server-5.xx/config.yml:
--------------------------------------------------------------------------------
1 | abuseDatabase:
2 | driverClass: org.postgresql.Driver
3 | user: postgres
4 | password: postgres
5 | url: jdbc:postgresql://127.0.0.1:5432/signal
6 |
7 | accountDatabaseCrawler:
8 | chunkSize: 1000
9 | chunkIntervalMs : 8000
10 |
11 | accountsDatabase:
12 | driverClass: org.postgresql.Driver
13 | user: postgres
14 | password: postgres
15 | url: jdbc:postgresql://127.0.0.1:5432/signal
16 |
17 | apn:
18 | sandbox: true
19 | bundleId: test
20 | keyId: test
21 | teamId: test
22 | signingKey: test
23 |
24 | appConfig:
25 | application: test
26 | environment: test
27 | configuration: test
28 |
29 | awsAttachments:
30 | accessKey: test
31 | accessSecret: test
32 | bucket: test
33 | region: us-east-1
34 |
35 | backupService:
36 | userAuthenticationTokenSharedSecret: test
37 | uri: redis://127.0.0.1:6379/
38 | backupCaCertificate: test
39 |
40 | cacheCluster:
41 | urls:
42 | - redis://127.0.0.1:6379/
43 |
44 | cdn:
45 | accessKey: test
46 | accessSecret: test
47 | bucket: signal
48 | region: us-east-1
49 |
50 | clientPresenceCluster:
51 | urls:
52 | - redis://127.0.0.1:6379/
53 |
54 | directory:
55 | client:
56 | userAuthenticationTokenSharedSecret: # head -c 16 /dev/urandom | hexdump -ve '1/1 "%.2x"'
57 | userAuthenticationTokenUserIdSecret: # head -c 16 /dev/urandom | hexdump -ve '1/1 "%.2x"'
58 | sqs:
59 | accessKey: test
60 | accessSecret: test
61 | queueUrls:
62 | - https://sqs.region.amazonaws.com/id/name.fifo
63 | server:
64 | -
65 | replicationName: test
66 | replicationUrl: http://127.0.0.1:9090
67 | replicationPassword: password
68 | replicationCaCertificate: |
69 | -----BEGIN CERTIFICATE-----
70 | .....
71 | -----END CERTIFICATE-----
72 |
73 | gcm:
74 | senderId: 12345
75 | apiKey: test
76 |
77 | gcpAttachments:
78 | domain: dummy.com
79 | email: dummy@dummy.com
80 | maxSizeInBytes: 1000000
81 | pathPrefix: /
82 | rsaSigningKey: "-----BEGIN RSA PRIVATE KEY-----\n ..... \n-----END RSA PRIVATE KEY-----\n"
83 |
84 | keysDynamoDb:
85 | region: us-east-1
86 | tableName: test
87 |
88 | messageCache:
89 | cluster:
90 | urls:
91 | - redis://127.0.0.1:6379/
92 |
93 | messageDynamoDb:
94 | region: us-east-1
95 | tableName: test
96 |
97 | metricsCluster:
98 | urls:
99 | - test
100 |
101 | micrometer:
102 | uri: proxy://localhost:2878
103 |
104 | paymentsService:
105 | userAuthenticationTokenSharedSecret: test
106 | fixerApiKey: test
107 | paymentCurrencies:
108 | - USD
109 |
110 | pubsub:
111 | url: redis://127.0.0.1:6379/cache
112 | replicaUrls:
113 | - redis://127.0.0.1:6379/cacheReplica
114 |
115 | push:
116 | queueSize: 200
117 |
118 | pushSchedulerCluster:
119 | urls:
120 | - redis://127.0.0.1:6379/
121 |
122 | recaptcha:
123 | secret: test
124 |
125 | remoteConfig:
126 | authorizedTokens:
127 | - dummy
128 | globalConfig:
129 | dummy: dummy
130 |
131 | server:
132 | applicationConnectors:
133 | - type: http
134 | port: 8080
135 | adminConnectors:
136 | - type: http
137 | port: 8081
138 |
139 | storageService:
140 | userAuthenticationTokenSharedSecret: test
141 | uri: redis://127.0.0.1:6379/
142 | storageCaCertificate: test
143 |
144 | turn:
145 | secret: secret# TURN server secret
146 | uris:
147 | - stun:example.com:3478
148 | - stun:example.com:5349
149 | - turn:example.com:3478?transport=udp
150 | - turn:example.com:5349?transport=udp
151 |
152 | twilio:
153 | accountId: test
154 | accountToken: test
155 | nanpaMessagingServiceSid: test
156 | messagingServiceSid: test
157 | localDomain: example.com
158 | iosVerificationText: "verification code is - %s"
159 | androidNgVerificationText: "verification code is - %s"
160 | android202001VerificationText: "verification code is - %s"
161 | android202103VerificationText: "verification code is - %s"
162 | genericVerificationText: "verification code is - %s"
163 |
164 | unidentifiedDelivery:
165 | certificate: test # java -jar TextSecureServer.jar certificate -ca
166 | privateKey: test # java -jar TextSecureServer.jar certificate --key priv_key_from_step_above --id key_ID
167 | expiresDays: 365
168 |
169 | voiceVerification:
170 | url: https://example.com
171 | locales:
172 | - en
173 |
174 | zkConfig:
175 | serverSecret: test # java -jar TextSecure.jar zkparams
176 | serverPublic: test # java -jar TextSecure.jar zkparams
177 | enabled: true
178 |
--------------------------------------------------------------------------------
/signal-server-5.xx/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.3'
2 | services:
3 | signal_database:
4 | image: postgres:11
5 | container_name: postgres_database
6 | environment:
7 | - 'POSTGRES_USER=postgres'
8 | - 'POSTGRES_PASSWORD=postgres'
9 | - 'POSTGRES_MULTIPLE_DATABASES=signal'
10 | - 'PGDATA=/var/lib/postgresql/data/pgdata'
11 | ports:
12 | - '5432:5432'
13 | volumes:
14 | - ./postgres_database:/var/lib/postgresql/data
15 | dynamo_db:
16 | image: amazon/dynamodb-local:1.15.0
17 | container_name: dynamo_db
18 | ports:
19 | - '8000:8000'
20 | redis_cluster:
21 | image: grokzen/redis-cluster:latest
22 | container_name: redis_cluster
23 | environment:
24 | - 'IP=0.0.0.0'
25 | ports:
26 | - '7000-7050:7000-7050'
27 | - '5000-5010:5000-5010'
28 | sqs:
29 | image: roribio16/alpine-sqs:1.2.0
30 | container_name: sqs
31 | ports:
32 | - '9324:9324'
33 | - '9325:9325'
34 |
--------------------------------------------------------------------------------
/signal-server-autostart/README.md:
--------------------------------------------------------------------------------
1 | # Signal Systemd Service
2 |
3 | This guide is for those who want to autostart their signal server in Linux environment by using systemd.
4 |
5 | 1. Create a file named `signal.service` on `/etc/systemd/system/`
6 |
7 | 2. Fill the file with the content from example file [signal-example.service](signal-example.service)
8 |
9 | 3. In the example file, `java` is installed in `usr/bin/`, make sure you change it according to your path to java. (Try running `which java` in terminal to know the path).
10 |
11 | 4. Change the `/path/to/signal` in the example file to where you store the server & change the jar file name, I wrote `x.xx` as the version, you migh need to change it.
12 |
13 | 5. Now reload the systemd with this command
14 |
15 | ```
16 | sudo systemctl daemon-reload
17 | ```
18 |
19 | 6. Then you can enable autostart with this command
20 | ```
21 | sudo systemctl start signal
22 | ```
23 |
--------------------------------------------------------------------------------
/signal-server-autostart/signal-example.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=Signal Server
3 |
4 | [Service]
5 | ExecStart=/usr/bin/java -jar /path/to/signal/service/target/TextSecureServer-X.XX.jar server /path/to/signal/service/config/config.yml
6 | Type=simple
7 | WorkingDirectory=/path/to/signal
8 | TimeoutStopSec=10
9 | Restart=on-failure
10 | RestartSec=5
11 |
12 | [Install]
13 | WantedBy=multi-user.target
--------------------------------------------------------------------------------
/signal-server-aws-setup/README.md:
--------------------------------------------------------------------------------
1 | # Amazon Web Service (AWS) Setup Guide for Signal
2 | Currently, Signal use AWS for attachment and CDS queue. However you can skip this step if you don't want to use AWS, I wrote another guide on replacing S3 & CDN with Minio. But if you need AWS here you go.
3 |
4 | ## IAM for Access Key & Secret
5 | 1. Login to AWS Console and click on your name, select on `My Security Credentials`.
6 |
7 | 2. Expand the tab `Access keys (access key ID and secret access key)`.
8 |
9 | 3. Click on `Create New Access Key`.
10 |
11 | 4. Click on `Show Access Key` to show your Access Key & Secret.
12 |
13 | 5. Take note of it and keep it safe. You will need it for your Signal Server config.yml
14 |
15 | ## S3 for Attachments & Profile Picture
16 |
17 | 1. Login to AWS Console and search for `S3`.
18 |
19 | 2. Click on `Create Bucket`.
20 |
21 | 3. Name your `Bucket name` and select your `Region`, then select `Next`.
22 |
23 | 4. Scroll down to `Block Public Access Setting for bucket` remove the check on `Block all public access` and check on `I acknowledge that the current settings may result in this bucket and the objects within becoming public`.
24 |
25 | 5. Scroll down and click `Create Bucket`
26 |
27 | 6. Open your bucket by clicking on the name.
28 |
29 | 7. Go to `Permissions` tab and scroll down to `Bucket policy`, fill with this (change `your-bucket-name` to your bucket name).
30 |
31 | ```
32 | {
33 | "Version": "2008-10-17",
34 | "Statement": [
35 | {
36 | "Sid": "AllowPublicRead",
37 | "Effect": "Allow",
38 | "Principal": {
39 | "AWS": "*"
40 | },
41 | "Action": "s3:GetObject",
42 | "Resource": "arn:aws:s3:::your-bucket-name/*"
43 | }
44 | ]
45 | }
46 |
47 | ```
48 |
49 | 8. Scroll down to `Access control list (ACL)` and click on `Edit`.
50 |
51 | 9. Check on all List, Read, and Write and click `Save changes.
52 |
53 | 10. Go to `Access Points` tab and click `Create access point`.
54 |
55 | 11. Give `Access point name` and select `Internet` on `Network access type`, remove the check on `Block all public access`.
56 |
57 | 12. Scroll down and click `Create access point`
58 |
59 | ## Cloudfront CDN for Attachments & Profile Picture
60 |
61 | 1. Login to AWS Console and search for `CloudFront`.
62 |
63 | 2. Click on `Create Distribution`.
64 |
65 | 3. Under `Web` click `Get Started`.
66 |
67 | 4. On `Origin Domain Name` select your `Bucket`.
68 |
69 | 5. On `Viewer Protocol Policy` select `Redirect HTTP to HTTPS`.
70 |
71 | 6. On `Allowed HTTP Methods` select `GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE`.
72 |
73 | 7. Finish by selecting on `Create Distribution`.
74 |
75 | ## SQS for CDS Queue
76 |
77 | 1. Login to AWS Console and search for `SQS`.
78 |
79 | 2. Click on `Create new queue`.
80 |
81 | 3. Give a name in `Queue Name` with format `name.fifo`.
82 |
83 | 4. Select `FIFO Queue`, then select `Quick Create Queue`.
84 |
85 | 5. Select your queue and find the `URL`, you will need it for the Signal Server config and CDS config.
86 |
--------------------------------------------------------------------------------
/signal-server-no-twilio/README.md:
--------------------------------------------------------------------------------
1 | ## Removing Twilio
2 |
3 | Currently, the only way to remove twilio depends on project’s goal, you may want to substitute Twilio with other SMS service, you may want to send the OTP by mail, almost all goals require you to get the OTP before forwarding it with another means, here is how you can get the OTP value.
4 |
5 | Edit the TwilioSmsSender.java class and with this additional line of code you will get to print the OTP on screen.
6 |
7 | `TwilioSmsSender.java`
8 | ```
9 | public CompletableFuture deliverSmsVerification(String destination, Optional clientType, String verificationCode) {
10 | Map requestParameters = new HashMap<>();
11 | requestParameters.put("To", destination);
12 |
13 | // Print the verification code to Console, input it on login/registration
14 | logger.info("Your OTP is :" + verificationCode);
15 |
16 | ...
17 | ```
18 |
19 | ## Further Modification
20 |
21 | With modification above, it is possible to send the OTP via email.
22 |
23 | ## Using test Number
24 | Try adding `testDevices` to `config.yml`, number is the phone number and code is the OTP
25 |
26 | ```
27 | testDevices:
28 | - number: "+1234567890"
29 | code: 123456
30 | ```
--------------------------------------------------------------------------------
/signal-server-no-twilio/modified-files/TwilioSmsSender.java:
--------------------------------------------------------------------------------
1 | // TwilioSmsSender.java
2 |
3 | public CompletableFuture deliverSmsVerification(String destination, Optional clientType, String verificationCode) {
4 | Map requestParameters = new HashMap<>();
5 | requestParameters.put("To", destination);
6 |
7 | // Print the verification code to Console, input it on login/registration
8 | logger.info("Your OTP is :" + verificationCode);
9 |
10 | if (Util.isEmpty(messagingServicesId)) {
11 | requestParameters.put("From", getRandom(random, numbers));
12 | } else {
13 | requestParameters.put("MessagingServiceSid", messagingServicesId);
14 | }
15 |
16 | if ("ios".equals(clientType.orElse(null))) {
17 | requestParameters.put("Body", String.format(SmsSender.SMS_IOS_VERIFICATION_TEXT, verificationCode, verificationCode));
18 | } else if ("android-ng".equals(clientType.orElse(null))) {
19 | requestParameters.put("Body", String.format(SmsSender.SMS_ANDROID_NG_VERIFICATION_TEXT, verificationCode));
20 | } else {
21 | requestParameters.put("Body", String.format(SmsSender.SMS_VERIFICATION_TEXT, verificationCode));
22 | }
23 |
24 | HttpRequest request = HttpRequest.newBuilder()
25 | .uri(smsUri)
26 | .POST(FormDataBodyPublisher.of(requestParameters))
27 | .header("Content-Type", "application/x-www-form-urlencoded")
28 | .header("Authorization", "Basic " + Base64.encodeBytes((accountId + ":" + accountToken).getBytes()))
29 | .build();
30 |
31 | smsMeter.mark();
32 |
33 | return httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString())
34 | .thenApply(this::parseResponse)
35 | .handle(this::processResponse);
36 | }
37 |
--------------------------------------------------------------------------------
/signal-server-no-twilio/twillo replacement with smsgateway.me:
--------------------------------------------------------------------------------
1 | since twillo cost you higer price for testing and most of server are onpremise its optional and cost effective to use other server like smsgateway.me or local smpp service provide OTP services so there is quick way of fixing that issues
2 | # you need to add another class like in my case inside \service\src\main\java\org\whispersystems\textsecuregcm\sms
3 | EthioTeleSmsSender.java
4 |
5 |
6 | public class EthioTeleSmsSender {
7 | private final String deviceId;
8 | private final String apiKey;
9 | static final String SMS_VERIFICATION_TEXT = "your app verification code is :%s\";
10 |
11 | public EthioTeleSmsSender(EthioTeleConfiguration config) {
12 | this.deviceId = config.getDeviceId ();
13 | this.apiKey = config.getApiKey();
14 | }
15 | //String username, String password, String to, String message,
16 | public void deliverSmsVerification(String destination, Optional clientType, String verificationCode)
17 | {
18 |
19 | String message = String.format(SMS_VERIFICATION_TEXT, verificationCode);
20 | try {
21 | String[] curl = new String[]{
22 | "curl",
23 | "--header", String.format("Authorization: %s", this.apiKey)
24 | ,"--request",
25 | "POST",
26 | "--data",
27 | String.format("[{\"phone_number\": \"%s\",\"message\": \"%s\",\"device_id\":\"%s\"}]", destination, message, this.deviceId),
28 | "https://smsgateway.me/api/v4/message/send"
29 | };
30 |
31 | // System.out.println("Curl value : %s" + curl);
32 | Process proc = Runtime.getRuntime().exec(curl);
33 |
34 | InputStream stderr = proc.getErrorStream();
35 | InputStreamReader isr = new InputStreamReader(stderr);
36 | BufferedReader br = new BufferedReader(isr);
37 | String line = null;
38 | System.out.println("");
39 | while ( (line = br.readLine()) != null)
40 | System.out.println(line);
41 | System.out.println("");
42 | int exitVal = proc.waitFor();
43 | System.out.println("Process exitValue: " + exitVal);
44 | } catch (Throwable ex) {}
45 | System.out.println(" id: "+ this.deviceId);
46 | }
47 |
48 |
49 |
50 | public void deliverVoxVerification(String destination, Optional clientType, String verificationCode) {
51 | //ethioTeleSender.deliverVoxVerification(destination, verificationCode, locale);
52 | }
53 |
54 |
55 | }
56 | add the following class inside service\src\main\java\org\whispersystems\textsecuregcm\configuration
57 | public class EthioTeleConfiguration {
58 |
59 | //@NotNull
60 | @JsonProperty
61 | private String deviceId;
62 |
63 | //@NotNull
64 | @JsonProperty
65 | private String apiKey;
66 |
67 | public String getDeviceId() {
68 | return deviceId;
69 | }
70 |
71 | public String getApiKey() {
72 | return apiKey;
73 | }
74 | }
75 |
76 |
77 |
78 |
79 |
80 | you need to also modify the smssender.java
81 | add the following code
82 |
83 | private final EthioTeleSmsSender ethioTeleSender;
84 |
85 | public SmsSender(EthioTeleSmsSender ethioTeleSender)
86 | {
87 | this.ethioTeleSender = ethioTeleSender;
88 | }
89 | //String username, String password, String to, String message,
90 | public void deliverSmsVerification(String destination, Optional clientType, String verificationCode) {
91 | // Fix up mexico numbers to 'mobile' format just for SMS delivery.
92 | if (destination.startsWith("+52") && !destination.startsWith("+521")) {
93 | destination = "+521" + destination.substring(3);
94 | }
95 |
96 | //twilioSender.deliverSmsVerification(destination, clientType, verificationCode);
97 | ethioTeleSender.deliverSmsVerification(destination, clientType, verificationCode);
98 |
99 | }
100 | modify the WhisperServerService.java class
101 | EthioTeleSmsSender ethioTeleSmsSender = new EthioTeleSmsSender(config.getEthioTeleConfiguration());
102 | SmsSender smsSender = new SmsSender(ethioTeleSmsSender);
103 | modify the WhisperServerConfiguration.java class
104 | add the following
105 | @JsonProperty
106 | private EthioTeleConfiguration ethiotele;
107 |
108 |
109 | public EthioTeleConfiguration getEthioTeleConfiguration() {
110 | return ethiotele;
111 | }
112 | finaly we need to add deviceId and apiKey to config.yml get register from https://smsgateway.me/ get the android app from https://m.apkpure.com/sms-gateway-api/networked.solutions.sms.gateway.api
113 | ethiotele:
114 | deviceid: 123456
115 | apikey: eyJ0eXAiOiJKV1QiLCJhbGcfdfdNiJ9.eyJpc3MiOiJhZG1pbiIsImlhdCI6MTYyMzMxNTM0NywiZXhwIjo0MTAyNDQ0ODAwLCJ1aWQiOjg5MDU5LCJyb2xlcyI6WyJST0xFX1VTRVIiXX0.P6gQSwcV6VkxcP0kmB8vNZPVVJL5aZQz7k1KCR9R6Bo
116 |
117 |
--------------------------------------------------------------------------------
/signal-server-self-signed-certificate/README.md:
--------------------------------------------------------------------------------
1 | # Using Self-Signed Certificate
2 |
3 | ## Generating CA & Self Signed Certificate
4 |
5 | To create your own self-signed certificate, remember you also need to be your own Certificate Authorithy.
6 |
7 | Generate our CA private key (give password)
8 | ```
9 | openssl genrsa -des3 -out myCA.key 2048
10 | ```
11 |
12 | Generate CA root certificate
13 | ```
14 | openssl req -x509 -new -nodes -key myCA.key -sha256 -days 1825 -out myCA.pem
15 | ```
16 |
17 | Generate Your Private key
18 | ```
19 | openssl genrsa -out localhost.key 2048
20 | ```
21 |
22 | Generate Your Certificate Signing Request
23 | ```
24 | openssl req -new -key localhost.key -out localhost.csr
25 | ```
26 |
27 | Create a file named `localhost.ext` and add these lines to the file
28 | ```
29 | authorityKeyIdentifier=keyid,issuer
30 | basicConstraints=CA:FALSE
31 | keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
32 | subjectAltName = @alt_names
33 |
34 | [alt_names]
35 | DNS.1 = localhost
36 | DNS.2 = 127.0.0.1
37 | ```
38 |
39 | Run to create certificate (CA's Private Key Password needed)
40 | ```
41 | openssl x509 -req -in localhost.csr -CA myCA.pem -CAkey myCA.key -CAcreateserial \
42 | -out localhost.crt -days 1825 -sha256 -extfile localhost.ext
43 | ```
44 |
45 | ## Modifying Signal-Android to allow self-signed certificate
46 |
47 | 1. Open `PushServiceSocket.java` located in `libsignal/service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java`.
48 |
49 | Add these import statement and modify the existing okHttpClient methods called `createConnectionClient` and `createAttachmentClient`
50 | ```
51 | // PushServiceSocket.java
52 |
53 | import javax.net.ssl.HostnameVerifier;
54 | import javax.net.ssl.SSLSocketFactory;
55 | import javax.net.ssl.SSLSession;
56 | import javax.net.ssl.SSLContext;
57 | import java.security.cert.CertificateException;
58 |
59 | private OkHttpClient createConnectionClient(SignalUrl url) {
60 | try {
61 | final TrustManager[] trustAllCerts = new TrustManager[]{
62 | new X509TrustManager() {
63 |
64 | @Override
65 | public void checkClientTrusted(java.security.cert.X509Certificate[] chain,
66 | String authType) throws
67 | CertificateException {
68 | }
69 |
70 | @Override
71 | public void checkServerTrusted(java.security.cert.X509Certificate[] chain,
72 | String authType) throws
73 | CertificateException {
74 | }
75 | @Override
76 | public java.security.cert.X509Certificate[] getAcceptedIssuers() {
77 | return new java.security.cert.X509Certificate[]{};
78 | }
79 | }
80 | };
81 |
82 | SSLContext sslContext = SSLContext.getInstance("SSL");
83 | sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
84 |
85 | final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
86 | OkHttpClient.Builder builder = new OkHttpClient.Builder();
87 | builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]);
88 |
89 | builder.hostnameVerifier(new HostnameVerifier() {
90 | @Override
91 | public boolean verify(String hostname, SSLSession session) {
92 | return true;
93 | }
94 | });
95 | return builder.build();
96 | } catch (NoSuchAlgorithmException | KeyManagementException e) {
97 | throw new AssertionError(e);
98 | }
99 | }
100 |
101 | private OkHttpClient createAttachmentClient() {
102 | try {
103 | final TrustManager[] trustAllCerts = new TrustManager[]{
104 | new X509TrustManager() {
105 |
106 | @Override
107 | public void checkClientTrusted(java.security.cert.X509Certificate[] chain,
108 | String authType) throws
109 | CertificateException {
110 | }
111 |
112 | @Override
113 | public void checkServerTrusted(java.security.cert.X509Certificate[] chain,
114 | String authType) throws
115 | CertificateException {
116 | }
117 | @Override
118 | public java.security.cert.X509Certificate[] getAcceptedIssuers() {
119 | return new java.security.cert.X509Certificate[]{};
120 | }
121 | }
122 | };
123 |
124 | SSLContext sslContext = SSLContext.getInstance("SSL");
125 | sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
126 |
127 | final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
128 | OkHttpClient.Builder builder = new OkHttpClient.Builder();
129 | builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]);
130 |
131 | builder.hostnameVerifier(new HostnameVerifier() {
132 | @Override
133 | public boolean verify(String hostname, SSLSession session) {
134 | return true;
135 | }
136 | });
137 | return builder.build();
138 | } catch (NoSuchAlgorithmException | KeyManagementException e) {
139 | throw new AssertionError(e);
140 | }
141 | }
142 | ```
143 |
144 | 2. Open `WebSocketConnection.java` located in `libsignal/service/src/main/java/org/whispersystems/signalservice/internal/websocket/WebSocketConnection.java`.
145 |
146 | Add these import statement and modify the existing void methods called `connect()`
147 | ```
148 | // WebSocketConnection.java
149 |
150 | import javax.net.ssl.HostnameVerifier;
151 | import javax.net.ssl.SSLSocketFactory;
152 | import javax.net.ssl.SSLSession;
153 | import javax.net.ssl.SSLContext;
154 | import java.security.cert.CertificateException;
155 |
156 | public synchronized void connect() {
157 | Log.w(TAG, "WSC connect()...");
158 |
159 | if (client == null) {
160 | String filledUri;
161 |
162 | if (credentialsProvider.isPresent()) {
163 | String identifier = credentialsProvider.get().getUuid() != null ? credentialsProvider.get().getUuid().toString() : credentialsProvider.get().getE164();
164 | filledUri = String.format(wsUri, identifier, credentialsProvider.get().getPassword());
165 | } else {
166 | filledUri = wsUri;
167 | }
168 |
169 | final TrustManager[] trustAllCerts = new TrustManager[]{
170 | new X509TrustManager() {
171 |
172 | @Override
173 | public void checkClientTrusted(java.security.cert.X509Certificate[] chain,
174 | String authType) throws
175 | CertificateException {
176 | }
177 |
178 | @Override
179 | public void checkServerTrusted(java.security.cert.X509Certificate[] chain,
180 | String authType) throws
181 | CertificateException {
182 | }
183 | @Override
184 | public java.security.cert.X509Certificate[] getAcceptedIssuers() {
185 | return new java.security.cert.X509Certificate[]{};
186 | }
187 | }
188 | };
189 |
190 | try {
191 | SSLContext sslContext = SSLContext.getInstance("SSL");
192 | sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
193 |
194 | final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
195 |
196 | OkHttpClient.Builder builder = new OkHttpClient.Builder();
197 | builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]);
198 |
199 | builder.hostnameVerifier(new HostnameVerifier() {
200 | @Override
201 | public boolean verify(String hostname, SSLSession session) {
202 | return true;
203 | }
204 | });
205 |
206 | OkHttpClient okHttpClient = builder.build();
207 |
208 | Request.Builder requestBuilder = new Request.Builder().url(filledUri);
209 |
210 | if (userAgent != null) {
211 | requestBuilder.addHeader("X-Signal-Agent", userAgent);
212 | }
213 |
214 | if (listener != null) {
215 | listener.onConnecting();
216 | }
217 |
218 | this.connected = false;
219 | this.client = okHttpClient.newWebSocket(requestBuilder.build(), this);
220 | } catch (NoSuchAlgorithmException | KeyManagementException e) {
221 | throw new AssertionError(e);
222 | }
223 | }
224 | }
225 | ```
226 |
227 | 3. You're done. Remember to use `https://your.own.ip.address:port` when you change the signal server url in `app/build.gradle`.
--------------------------------------------------------------------------------
/signal-server-self-signed-certificate/modified-files/PushServiceSocket.java:
--------------------------------------------------------------------------------
1 | // PushServiceSocket.java
2 |
3 | import javax.net.ssl.HostnameVerifier;
4 | import javax.net.ssl.SSLSocketFactory;
5 | import javax.net.ssl.SSLSession;
6 | import javax.net.ssl.SSLContext;
7 | import java.security.cert.CertificateException;
8 |
9 | private OkHttpClient createConnectionClient(SignalUrl url) {
10 | try {
11 | final TrustManager[] trustAllCerts = new TrustManager[]{
12 | new X509TrustManager() {
13 |
14 | @Override
15 | public void checkClientTrusted(java.security.cert.X509Certificate[] chain,
16 | String authType) throws
17 | CertificateException {
18 | }
19 |
20 | @Override
21 | public void checkServerTrusted(java.security.cert.X509Certificate[] chain,
22 | String authType) throws
23 | CertificateException {
24 | }
25 | @Override
26 | public java.security.cert.X509Certificate[] getAcceptedIssuers() {
27 | return new java.security.cert.X509Certificate[]{};
28 | }
29 | }
30 | };
31 |
32 | SSLContext sslContext = SSLContext.getInstance("SSL");
33 | sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
34 |
35 | final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
36 | OkHttpClient.Builder builder = new OkHttpClient.Builder();
37 | builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]);
38 |
39 | builder.hostnameVerifier(new HostnameVerifier() {
40 | @Override
41 | public boolean verify(String hostname, SSLSession session) {
42 | return true;
43 | }
44 | });
45 | return builder.build();
46 | } catch (NoSuchAlgorithmException | KeyManagementException e) {
47 | throw new AssertionError(e);
48 | }
49 | }
50 |
51 | private OkHttpClient createAttachmentClient() {
52 | try {
53 | final TrustManager[] trustAllCerts = new TrustManager[]{
54 | new X509TrustManager() {
55 |
56 | @Override
57 | public void checkClientTrusted(java.security.cert.X509Certificate[] chain,
58 | String authType) throws
59 | CertificateException {
60 | }
61 |
62 | @Override
63 | public void checkServerTrusted(java.security.cert.X509Certificate[] chain,
64 | String authType) throws
65 | CertificateException {
66 | }
67 | @Override
68 | public java.security.cert.X509Certificate[] getAcceptedIssuers() {
69 | return new java.security.cert.X509Certificate[]{};
70 | }
71 | }
72 | };
73 |
74 | SSLContext sslContext = SSLContext.getInstance("SSL");
75 | sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
76 |
77 | final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
78 | OkHttpClient.Builder builder = new OkHttpClient.Builder();
79 | builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]);
80 |
81 | builder.hostnameVerifier(new HostnameVerifier() {
82 | @Override
83 | public boolean verify(String hostname, SSLSession session) {
84 | return true;
85 | }
86 | });
87 | return builder.build();
88 | } catch (NoSuchAlgorithmException | KeyManagementException e) {
89 | throw new AssertionError(e);
90 | }
91 | }
--------------------------------------------------------------------------------
/signal-server-self-signed-certificate/modified-files/WebSocketConnection.java:
--------------------------------------------------------------------------------
1 | // WebSocketConnection.java
2 |
3 | import javax.net.ssl.HostnameVerifier;
4 | import javax.net.ssl.SSLSocketFactory;
5 | import javax.net.ssl.SSLSession;
6 | import javax.net.ssl.SSLContext;
7 | import java.security.cert.CertificateException;
8 |
9 | public synchronized void connect() {
10 | Log.w(TAG, "WSC connect()...");
11 |
12 | if (client == null) {
13 | String filledUri;
14 |
15 | if (credentialsProvider.isPresent()) {
16 | String identifier = credentialsProvider.get().getUuid() != null ? credentialsProvider.get().getUuid().toString() : credentialsProvider.get().getE164();
17 | filledUri = String.format(wsUri, identifier, credentialsProvider.get().getPassword());
18 | } else {
19 | filledUri = wsUri;
20 | }
21 |
22 | final TrustManager[] trustAllCerts = new TrustManager[]{
23 | new X509TrustManager() {
24 |
25 | @Override
26 | public void checkClientTrusted(java.security.cert.X509Certificate[] chain,
27 | String authType) throws
28 | CertificateException {
29 | }
30 |
31 | @Override
32 | public void checkServerTrusted(java.security.cert.X509Certificate[] chain,
33 | String authType) throws
34 | CertificateException {
35 | }
36 | @Override
37 | public java.security.cert.X509Certificate[] getAcceptedIssuers() {
38 | return new java.security.cert.X509Certificate[]{};
39 | }
40 | }
41 | };
42 |
43 | try {
44 | SSLContext sslContext = SSLContext.getInstance("SSL");
45 | sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
46 |
47 | final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
48 |
49 | OkHttpClient.Builder builder = new OkHttpClient.Builder();
50 | builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]);
51 |
52 | builder.hostnameVerifier(new HostnameVerifier() {
53 | @Override
54 | public boolean verify(String hostname, SSLSession session) {
55 | return true;
56 | }
57 | });
58 |
59 | OkHttpClient okHttpClient = builder.build();
60 |
61 | Request.Builder requestBuilder = new Request.Builder().url(filledUri);
62 |
63 | if (userAgent != null) {
64 | requestBuilder.addHeader("X-Signal-Agent", userAgent);
65 | }
66 |
67 | if (listener != null) {
68 | listener.onConnecting();
69 | }
70 |
71 | this.connected = false;
72 | this.client = okHttpClient.newWebSocket(requestBuilder.build(), this);
73 | } catch (NoSuchAlgorithmException | KeyManagementException e) {
74 | throw new AssertionError(e);
75 | }
76 | }
77 | }
--------------------------------------------------------------------------------