├── .gitignore ├── doc └── ephemera.png ├── client ├── public │ ├── favicon.ico │ └── index.html ├── src │ ├── assets │ │ ├── logo.png │ │ ├── chevron-right.svg │ │ ├── eye.svg │ │ ├── lock.svg │ │ ├── info.svg │ │ ├── help-circle.svg │ │ ├── link.svg │ │ ├── clipboard.svg │ │ ├── alert-triangle.svg │ │ └── loader.svg │ ├── main.js │ ├── router │ │ └── index.js │ ├── crypto.js │ ├── App.vue │ └── components │ │ ├── view.vue │ │ └── save.vue ├── babel.config.js ├── example.env ├── .gitignore ├── README.md └── package.json ├── terraform ├── cloudflare │ ├── main.tf │ ├── README.md │ ├── page_rule.tf │ ├── vars.tf │ └── record.tf ├── main.tf ├── kms.tf ├── outputs.tf ├── dynamodb.tf ├── vars.tf ├── lambda.tf ├── s3.tf ├── README.md ├── iam.tf └── api_gateway.tf ├── go.mod ├── dynamo_test.go ├── LICENSE.txt ├── Makefile ├── init.go ├── kms_test.go ├── kms.go ├── lambda ├── save │ └── save.go └── view │ └── view.go ├── go.sum ├── README.md └── dynamo.go /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | build/* -------------------------------------------------------------------------------- /doc/ephemera.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockpane/ephemera/HEAD/doc/ephemera.png -------------------------------------------------------------------------------- /client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockpane/ephemera/HEAD/client/public/favicon.ico -------------------------------------------------------------------------------- /client/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockpane/ephemera/HEAD/client/src/assets/logo.png -------------------------------------------------------------------------------- /client/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /client/example.env: -------------------------------------------------------------------------------- 1 | # rename this file to '.env' 2 | VUE_APP_API_URL=https://api.your-lambda-endpoints.local 3 | 4 | -------------------------------------------------------------------------------- /terraform/cloudflare/main.tf: -------------------------------------------------------------------------------- 1 | provider "cloudflare" { 2 | 3 | api_token = var.CLOUDFLARE_API_TOKEN 4 | account_id = var.CLOUDFLARE_ACCOUNT_ID 5 | } 6 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/blockpane/ephemera 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/aws/aws-lambda-go v1.18.0 7 | github.com/aws/aws-sdk-go v1.33.17 8 | ) 9 | -------------------------------------------------------------------------------- /terraform/main.tf: -------------------------------------------------------------------------------- 1 | # export shell environments with credenials 2 | provider "aws" { 3 | region = var.region 4 | profile = var.aws_profile 5 | } 6 | 7 | # get account id 8 | data "aws_caller_identity" "current" { 9 | } 10 | -------------------------------------------------------------------------------- /terraform/kms.tf: -------------------------------------------------------------------------------- 1 | resource "aws_kms_key" "key" { 2 | description = "${var.app_name} key" 3 | } 4 | 5 | resource "aws_kms_alias" "alias" { 6 | name = "alias/${var.app_name}-key" 7 | target_key_id = aws_kms_key.key.key_id 8 | } -------------------------------------------------------------------------------- /client/src/assets/chevron-right.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /terraform/cloudflare/README.md: -------------------------------------------------------------------------------- 1 | ### Cloudflare record 2 | 3 | This will add a dns `proxied` record and a `page_rule` for your Ephermera bucket. You need to have a zone hosted at Cloudflare. 4 | 5 | ## How to guide 6 | 7 | - Run `terraform init`, `terraform plan` to see the plan 8 | - Run `terraform apply` -------------------------------------------------------------------------------- /client/src/assets/eye.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /client/src/assets/lock.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | pnpm-debug.log* 14 | 15 | # Editor directories and files 16 | .idea 17 | .vscode 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | *.sw? 23 | -------------------------------------------------------------------------------- /client/src/assets/info.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /client/src/assets/help-circle.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /client/src/assets/link.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /client/src/assets/clipboard.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /terraform/outputs.tf: -------------------------------------------------------------------------------- 1 | # API Gateway invoke URL to be passed to ephemera .env file 2 | output "base_url" { 3 | value = aws_api_gateway_deployment.deployment.invoke_url 4 | } 5 | 6 | # S3 website endpoint 7 | output "s3_url" { 8 | value = aws_s3_bucket.this.website_endpoint 9 | } 10 | 11 | # S3 bucket name to be picked by Cloudflare 12 | output "s3_bucket" { 13 | value = aws_s3_bucket.this.bucket 14 | } -------------------------------------------------------------------------------- /client/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import BootstrapVue from "bootstrap-vue" 3 | import App from './App.vue' 4 | import router from './router' 5 | import VueClipboard from 'vue-clipboard2' 6 | import './assets/bootstrap.css' 7 | 8 | Vue.use(BootstrapVue); 9 | Vue.use(VueClipboard); 10 | 11 | new Vue({ 12 | el: '#app', 13 | router, 14 | VueClipboard, 15 | render: h => h(App) 16 | }); 17 | 18 | -------------------------------------------------------------------------------- /dynamo_test.go: -------------------------------------------------------------------------------- 1 | package sharedpw 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestNewSecret(t *testing.T) { 9 | s := NewSecret() 10 | if s.Err != nil { 11 | t.Error(fmt.Sprintf("could not create new secret:\n%v\n", s.Err)) 12 | } 13 | j, err := s.ToJson() 14 | if err != nil || len(j) < 1 { 15 | t.Error(fmt.Sprintf("error marshalling to json got:\n%s\n%v\n", j, err)) 16 | } 17 | fmt.Println(j) 18 | } 19 | -------------------------------------------------------------------------------- /client/src/assets/alert-triangle.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | # my-project 2 | 3 | ## Project setup 4 | ``` 5 | npm install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | npm run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | npm run build 16 | ``` 17 | 18 | ### Lints and fixes files 19 | ``` 20 | npm run lint 21 | ``` 22 | 23 | ### Customize configuration 24 | See [Configuration Reference](https://cli.vuejs.org/config/). 25 | -------------------------------------------------------------------------------- /terraform/cloudflare/page_rule.tf: -------------------------------------------------------------------------------- 1 | data "terraform_remote_state" "that" { 2 | backend = "local" 3 | config = { 4 | path = "${path.module}/../terraform.tfstate" 5 | } 6 | } 7 | 8 | resource "cloudflare_page_rule" "this" { 9 | zone_id = var.CLOUDFLARE_ZONE_ID 10 | target = "${data.terraform_remote_state.that.outputs.s3_bucket}$/*" 11 | 12 | priority = 1 13 | status = "active" 14 | actions { 15 | ssl = "flexible" 16 | } 17 | 18 | 19 | } 20 | -------------------------------------------------------------------------------- /terraform/cloudflare/vars.tf: -------------------------------------------------------------------------------- 1 | # export TF_VAR_CLOUDFLARE_API_TOKEN= 2 | variable "CLOUDFLARE_API_TOKEN" { 3 | description = "Cloudflare API token value" 4 | } 5 | 6 | # export TF_VAR_CLOUDFLARE_ACCOUNT_ID= 7 | variable "CLOUDFLARE_ACCOUNT_ID" { 8 | description = "Cloudflare Account ID" 9 | } 10 | 11 | # export TF_VAR_CLOUDFLARE_ACCOUNT_ID= 12 | variable "CLOUDFLARE_ZONE_ID" { 13 | description = "Cloudflare Zone ID to create record for ephemera" 14 | } 15 | 16 | -------------------------------------------------------------------------------- /terraform/cloudflare/record.tf: -------------------------------------------------------------------------------- 1 | data "terraform_remote_state" "this" { 2 | backend = "local" 3 | config = { 4 | path = "${path.module}/../terraform.tfstate" 5 | } 6 | } 7 | 8 | resource "cloudflare_record" "this" { 9 | zone_id = var.CLOUDFLARE_ZONE_ID 10 | 11 | name = data.terraform_remote_state.this.outputs.s3_bucket 12 | type = "CNAME" 13 | ttl = "1" 14 | proxied = "true" 15 | 16 | value = data.terraform_remote_state.this.outputs.s3_url 17 | } 18 | -------------------------------------------------------------------------------- /terraform/dynamodb.tf: -------------------------------------------------------------------------------- 1 | resource "aws_dynamodb_table" "dynamodb_table" { 2 | name = var.app_name 3 | billing_mode = "PROVISIONED" 4 | read_capacity = 1 5 | write_capacity = 1 6 | hash_key = "secret" 7 | 8 | attribute { 9 | name = "secret" 10 | type = "S" 11 | } 12 | 13 | ttl { 14 | attribute_name = "expire" 15 | enabled = true 16 | } 17 | 18 | tags = { 19 | Name = "Managed by Terraform" 20 | Environment = "${var.environment}" 21 | } 22 | } -------------------------------------------------------------------------------- /client/src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | import save from '../components/save.vue' 4 | import view from '../components/view.vue' 5 | 6 | Vue.use(Router); 7 | 8 | export default new Router({ 9 | routes: [ 10 | { 11 | path: '/', 12 | name: 'save', 13 | component: save 14 | }, 15 | { 16 | path: '/view*', 17 | name: 'view', 18 | component: view 19 | } 20 | ] 21 | }) 22 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright [2020] [Todd Garrison] 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /client/src/assets/loader.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 |one-time secret sharing
11 |
19 | This is an application for sharing a secret, the secret can only be accessed once, and then it is gone
20 | forever.
21 |
24 | Type in the secret you want to share, and click "Get Link", provide this to the recipient and they can
25 | retrieve it.
26 |
28 | In the advanced settings you can control more of the behavior:
29 |
37 | This application was designed to offer a moderate level of security, slightly better than pasting a password 38 | in a chat window. But, 39 | there is no provable way to know what is being done behind the scenes, and the only way to be entirely sure 40 | is to get the source from 41 | Github, audit the code, and deploy it under your own 42 | control. 43 |
44 |45 | This application is designed to run as a serverless app in the AWS cloud, with the goal to be almost cost, 46 | and maintenance free. 47 | It uses KMS to encrypt the secrets in the database, which costs ~$1/month, specifically so that it could be 48 | easily deployed within 49 | an organization for exclusive use with little effort. 50 |
51 |
7 |
8 | Once the secret has been viewed, it will be irretrievable. The link is not valid, the secret has already been viewed, or it has been
48 | restricted to a different IP address.
10 |
11 |
12 |
Reveal Secret?
Someone has shared a secret with you!
14 |
17 | Reveal
18 |
Decrypted Secret
{{ passParams.passHint }}
33 |
34 | {{ Decipher }}
37 |
38 |
39 |
41 |
46 | That secret doesn't exist!
7 | {{ secretLen }} of {{ maxLen }} characters allowed.
>
Loading
{{ response }}
24 |
25 |
36 |