-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial commit: Set up project structure and added basic functionalit…
…y for HLS file encryption in AWS Lambda.
- Loading branch information
0 parents
commit b78afe7
Showing
11 changed files
with
395 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
ZIP_FILE_NAME="hls-enc-lambda" | ||
BIN="bootstrap" | ||
|
||
GOOS="linux" | ||
GOARCH="arm64" | ||
CGO_ENABLED=0 | ||
|
||
LAMBDA_FUNCTION_NAME="hls-enc" | ||
|
||
compile: | ||
@echo "Compiling..." | ||
GOOS=$(GOOS) GOARCH=$(GOARCH) go build -tags lambda.norpc -o $(BIN) main.go | ||
|
||
bundle: | ||
@echo "Bundling..." | ||
zip $(ZIP_FILE_NAME).zip $(BIN) | ||
|
||
push: | ||
@echo "Pushing..." | ||
aws lambda update-function-code --function-name $(LAMBDA_FUNCTION_NAME) --zip-file fileb://$(ZIP_FILE_NAME).zip | ||
|
||
clean: | ||
@echo "Cleaning..." | ||
rm -f $(BIN) $(ZIP_FILE_NAME).zip | ||
|
||
deploy: clean compile bundle push |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package config | ||
|
||
import ( | ||
"net/url" | ||
"os" | ||
"time" | ||
) | ||
|
||
type config struct { | ||
Origin string | ||
KeyID string | ||
PrivateKeyBase64 string | ||
ExpireTime time.Duration | ||
} | ||
|
||
var ConfigEnv *config | ||
|
||
// InitConfig initialize the configuration | ||
func InitConfig() { | ||
ConfigEnv = load() | ||
} | ||
|
||
// load configuration from environment variables | ||
func load() (c *config) { | ||
origin, _ := url.Parse(os.Getenv("CLOUDFRONT_ORIGIN")) | ||
expireTime, _ := time.ParseDuration(os.Getenv("EXPIRE_TIME")) | ||
c = &config{ | ||
Origin: origin.Host, | ||
KeyID: os.Getenv("CLOUDFRONT_ACCESS_KEY_ID"), | ||
PrivateKeyBase64: os.Getenv("CLOUDFRONT_PRIVATE_KEY_BASE64"), | ||
ExpireTime: expireTime, | ||
} | ||
return | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
module github.com/meanii/aws-lambda-hls-enc | ||
|
||
go 1.22.0 | ||
|
||
require ( | ||
github.com/aws/aws-lambda-go v1.46.0 | ||
github.com/aws/aws-sdk-go v1.50.27 | ||
) | ||
|
||
require ( | ||
github.com/awslabs/aws-lambda-go-api-proxy v0.16.1 // indirect | ||
github.com/grafov/m3u8 v0.12.0 // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
github.com/aws/aws-lambda-go v1.46.0 h1:UWVnvh2h2gecOlFhHQfIPQcD8pL/f7pVCutmFl+oXU8= | ||
github.com/aws/aws-lambda-go v1.46.0/go.mod h1:dpMpZgvWx5vuQJfBt0zqBha60q7Dd7RfgJv23DymV8A= | ||
github.com/aws/aws-sdk-go v1.50.27 h1:96ifhrSuja+AzdP3W/T2337igqVQ2FcSIJYkk+0rCeA= | ||
github.com/aws/aws-sdk-go v1.50.27/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= | ||
github.com/awslabs/aws-lambda-go-api-proxy v0.16.1 h1:x4F/VbWYt/f5K9+n3TAqbjFljDP52KWbYz/fNBvQdi8= | ||
github.com/awslabs/aws-lambda-go-api-proxy v0.16.1/go.mod h1:31WDgvTzVyra022CWzO6uEZFel9/y7QKaZpUQEqYLr0= | ||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/grafov/m3u8 v0.12.0 h1:T6iTwTsSEtMcwkayef+FJO8kj+Sglr4Lh81Zj8Ked/4= | ||
github.com/grafov/m3u8 v0.12.0/go.mod h1:nqzOkfBiZJENr52zTVd/Dcl03yzphIMbJqkXGu+u080= | ||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= | ||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= | ||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||
github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= | ||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= | ||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package handler | ||
|
||
import ( | ||
"fmt" | ||
"net/http" | ||
"net/url" | ||
|
||
"github.com/meanii/aws-lambda-hls-enc/config" | ||
"github.com/meanii/aws-lambda-hls-enc/helper" | ||
) | ||
|
||
// BinaryChunks is the handler for binary chunks | ||
// It will sign the URL and redirect to the signed URL | ||
func BinaryChunksRedirection(cfUrl url.URL, w http.ResponseWriter, r *http.Request) { | ||
signedUrlSchema := helper.URLSigner{ | ||
KeyID: config.ConfigEnv.KeyID, | ||
PrivKeyBase64: config.ConfigEnv.PrivateKeyBase64, | ||
Hour: config.ConfigEnv.ExpireTime, | ||
} | ||
|
||
signedChunkedUrl, err := signedUrlSchema.Signer(cfUrl.String()) | ||
if err != nil { | ||
fmt.Printf("Error signing URL: %s\n", err) | ||
w.WriteHeader(http.StatusInternalServerError) | ||
w.Write([]byte(fmt.Sprintf("Error signing URL: %s\n", err))) | ||
return | ||
} | ||
|
||
w.Header().Set("Access-Control-Allow-Origin", "*") | ||
w.Header().Set("Location", signedChunkedUrl) | ||
http.Redirect(w, r, signedChunkedUrl, http.StatusFound) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package handler | ||
|
||
import ( | ||
"net/http" | ||
) | ||
|
||
// HandlerMethodOptions is the handler for OPTIONS method | ||
func HandlerMethodOptions(w http.ResponseWriter, r *http.Request) { | ||
if r.Method == http.MethodOptions { | ||
w.Header().Set("Access-Control-Allow-Origin", "*") | ||
w.Header().Set("Access-Control-Allow-Methods", "GET, OPTIONS") | ||
w.WriteHeader(http.StatusOK) | ||
} | ||
} | ||
|
||
// HandlerMethodHead is the handler for HEAD method | ||
func HandlerMethodHead(w http.ResponseWriter, r *http.Request) { | ||
if r.Method == http.MethodHead { | ||
w.WriteHeader(http.StatusOK) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package handler | ||
|
||
import ( | ||
"fmt" | ||
"net/http" | ||
"net/url" | ||
"path" | ||
"strings" | ||
|
||
"github.com/grafov/m3u8" | ||
|
||
"github.com/meanii/aws-lambda-hls-enc/config" | ||
"github.com/meanii/aws-lambda-hls-enc/helper" | ||
) | ||
|
||
// M3u8Media is the handler for m3u8 media playlist | ||
// It will return the media playlist | ||
func M3u8Media(mediapl *m3u8.MediaPlaylist, w http.ResponseWriter, _ *http.Request) { | ||
mediaPlaylistString := mediapl.String() | ||
w.WriteHeader(http.StatusOK) | ||
w.Header().Set("Content-Type", "application/octet-stream") | ||
w.Header().Set("Access-Control-Allow-Origin", "*") | ||
w.Write([]byte(mediaPlaylistString)) | ||
} | ||
|
||
// M3u8Master is the handler for m3u8 master playlist | ||
// It will return the master playlist | ||
func M3u8Master( | ||
clurl url.URL, | ||
masterpl *m3u8.MasterPlaylist, | ||
w http.ResponseWriter, | ||
_ *http.Request, | ||
) { | ||
masterPlaylistString := masterpl.String() | ||
for _, variant := range masterpl.Variants { | ||
|
||
baseName := path.Base(clurl.String()) | ||
variantUrl := strings.Replace(clurl.String(), baseName, variant.URI, 1) | ||
|
||
// This is the URLSigner struct, it is used to sign the URL | ||
signedUrlSchema := helper.URLSigner{ | ||
KeyID: config.ConfigEnv.KeyID, | ||
PrivKeyBase64: config.ConfigEnv.PrivateKeyBase64, | ||
Hour: config.ConfigEnv.ExpireTime, | ||
} | ||
signedVariantUrl, err := signedUrlSchema.Signer(variantUrl) | ||
if err != nil { | ||
fmt.Printf("Error signing URL: %s\n", err) | ||
w.WriteHeader(http.StatusInternalServerError) | ||
w.Write([]byte(fmt.Sprintf("Error signing URL: %s\n", err))) | ||
return | ||
} | ||
|
||
// Replace the original variant URL with the signed variant URL | ||
signedVariantURL, _ := url.Parse(signedVariantUrl) | ||
signedChunkUrl := variant.URI + "?" + signedVariantURL.RawQuery // Add the query string | ||
masterPlaylistString = strings.Replace( | ||
masterPlaylistString, | ||
variant.URI, | ||
signedChunkUrl, | ||
-1, | ||
) | ||
|
||
} | ||
|
||
// Set the headers and write the response | ||
w.WriteHeader(http.StatusOK) | ||
w.Header().Set("Content-Type", "application/x-mpegURL") | ||
w.Header().Set("Access-Control-Allow-Origin", "*") | ||
w.Write([]byte(masterPlaylistString)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package helper | ||
|
||
import ( | ||
"net/http" | ||
) | ||
|
||
// Fetch fetches the content of the given URL | ||
func Fetch(url string) (*http.Response, error) { | ||
resp, err := http.Get(url) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return resp, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package helper | ||
|
||
import ( | ||
"crypto/rsa" | ||
"crypto/x509" | ||
"encoding/base64" | ||
"encoding/pem" | ||
"time" | ||
|
||
"github.com/aws/aws-sdk-go/service/cloudfront/sign" | ||
) | ||
|
||
// URLSigner is a struct that holds the key ID, private key, raw URL, and hour. | ||
type URLSigner struct { | ||
KeyID string // Credential Key Pair key ID | ||
PrivKeyBase64 string // private key in base64 | ||
Hour time.Duration // hour duration | ||
} | ||
|
||
// Signer signs a URL to be valid, using the private key and credential pair key ID. | ||
func (us *URLSigner) Signer(url string) (string, error) { | ||
// Load the private key, and return an error if it fails | ||
privKeyBytes, err := loadPrivateKey(us.PrivKeyBase64) | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
// Create a new URLSigner with the key ID and private key | ||
signer := sign.NewURLSigner(us.KeyID, privKeyBytes) | ||
signedURL, err := signer.Sign(url, time.Now().Add(us.Hour)) | ||
return signedURL, nil | ||
} | ||
|
||
// loadPrivateKey loads a RSA private key from the file path. | ||
func loadPrivateKey(privKeyBase64 string) (*rsa.PrivateKey, error) { | ||
// Decode the base64 string to bytes | ||
privKeyBytes, err := base64.StdEncoding.DecodeString(privKeyBase64) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// Decode the PEM block to get the private key | ||
block, _ := pem.Decode(privKeyBytes) | ||
|
||
// Parse the private key | ||
// #TODO: handle fallback to PKCS1 if PKCS8 fails to parse | ||
privKey, err := x509.ParsePKCS8PrivateKey(block.Bytes) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return privKey.(*rsa.PrivateKey), nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package main | ||
|
||
import ( | ||
"net/http" | ||
|
||
"github.com/aws/aws-lambda-go/lambda" | ||
"github.com/awslabs/aws-lambda-go-api-proxy/httpadapter" | ||
|
||
"github.com/meanii/aws-lambda-hls-enc/config" | ||
"github.com/meanii/aws-lambda-hls-enc/server" | ||
) | ||
|
||
func main() { | ||
config.InitConfig() | ||
http.HandleFunc("/", server.HandleRequest) | ||
|
||
// Start the Lambda proxy | ||
lambda.Start(httpadapter.New(http.DefaultServeMux).ProxyWithContext) | ||
} |
Oops, something went wrong.