├── .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 | } --------------------------------------------------------------------------------