Skip to content

Commit

Permalink
Merge pull request #7 from cloud-gov/brokerpak-topic
Browse files Browse the repository at this point in the history
Catch up main with updates from brokerpak-topic
  • Loading branch information
jameshochadel authored Dec 10, 2024
2 parents f83edc5 + 9412e98 commit 4328653
Show file tree
Hide file tree
Showing 40 changed files with 502 additions and 49 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ clientconfig.yml
# Configuration for the CSB server.
secrets.env
zscaler.crt
# Sqlite database for local development.
.csb.db
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ RUN set -e; if [ "$BUILD_ENV" = "production" ] ; then echo "production env"; els
cp /app/zscaler.crt $CERT_DIR ; update-ca-certificates ; \
fi

RUN /app/csb pak build brokerpaks/cg-smtp
RUN /app/csb pak build brokerpaks/aws-ses

FROM ${base_image}

# Copy brokerpaks to final image
COPY --from=build /app/cg-smtp-0.1.0.brokerpak /app/
COPY --from=build /app/aws-ses-0.1.0.brokerpak /app/
11 changes: 9 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
build:
PAK_BUILD_CACHE_PATH=/tmp/pak-build-cache cloud-service-broker pak build brokerpaks/cg-smtp
PAK_BUILD_CACHE_PATH=/tmp/pak-build-cache cloud-service-broker pak build brokerpaks/aws-ses

run: build
# Load environment variables from secrets.env, scoped to this command only.
@bash -c 'env $$(cat secrets.env | xargs) cloud-service-broker serve'

watch:
find . | grep -E '\.env|\.tf' | entr -r make run
find . | grep -E '\.env|\.tf|\.yml' | entr -r make run

clean:
rm -r /tmp/pak-build-cache
rm *.brokerpak

run-docproxy:
find docproxy | entr -r go run ./docproxy/.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@ This repo contains configuration, including brokerpaks, for the cloud.gov deploy

- https://github.com/GSA-TTS/datagov-brokerpak-smtp
- https://github.com/GSA/ttsnotify-brokerpak-sms

## Credits

- AWS Architecture icons are sourced from https://aws.amazon.com/architecture/icons/.
44 changes: 21 additions & 23 deletions brokerpaks/cg-smtp/cg-smtp.yml → brokerpaks/aws-ses/aws-ses.yml
Original file line number Diff line number Diff line change
@@ -1,43 +1,42 @@
version: 1
name: cg-smtp
name: aws-ses
id: 260f2ead-b9e9-48b5-9a01-6e3097208ad7
description: SMTP service provided by Amazon Simple Email Service (SES)
display_name: SMTP (using AWS SES)
image_url: https://example.com/icon.jpg
description: Send email from verified domains using Amazon Simple Email Service (SES). Supports SMTP and the SES HTTP API.
display_name: AWS Simple Email Service
image_url: https://services.cloud.gov/images/amazon-ses.svg
documentation_url: https://aws.amazon.com/ses/ # todo
provider_display_name: ""
provider_display_name: "Cloud.gov"
support_url: https://cloud.gov/contact/
tags: [aws, ses, smtp]
tags: [aws, csb]
plans:
- name: base
id: 35ffb84b-a898-442e-b5f9-0a6a5229827d
description: Provision SMTP credentials for sending email from any user at a domain, like 'agency.gov'.
description: Provision SMTP credentials for sending email from any user at a domain, like `agency.gov`.
display_name: Send-only service
provision:
plan_inputs:
user_inputs:
- field_name: domain
type: string
default: ""
details: Domain from mail will be sent. For example, agency.gov. If left empty, a temporary cloud.gov subdomain will be generated.
required: false
prohibit_update: true
details: Domain from which mail will be sent. For example, `agency.gov`. If left empty, a temporary cloud.gov subdomain will be generated.
- field_name: dmarc_report_uri_aggregate
type: string
required: true
default: ""
details: The mailto URI to which DMARC aggregate reports should be sent. For example, 'mailto:[email protected]'. Reports are automatically sent to [email protected].
details: The mailto URI to which DMARC aggregate reports should be sent. For example, `mailto:[email protected]`. Reports are automatically sent to `[email protected]`. If you specify a domain and later update this parameter, remember to update your DNS with the new records in the `required_records` output.
- field_name: dmarc_report_uri_failure
type: string
required: true
default: ""
details: The mailto URI to which DMARC individual message failure reports should be sent. For example, 'mailto:[email protected]'.
details: The mailto URI to which DMARC individual message failure reports should be sent. For example, `mailto:[email protected]`. If you specify a domain and later update this parameter, remember to update your DNS with the new records in the `required_records` output.
- field_name: enable_feedback_notifications
type: boolean
details: Flag to toggle creation of SNS topics for feedback notifications.
default: false
- field_name: mail_from_subdomain
type: string
default: ""
details: Subdomain to use as the sending email server.
required: false
details: The custom MAIL FROM domain that you want the verified identity to use. See the [SES v2 API reference](https://docs.aws.amazon.com/ses/latest/APIReference-V2/API_PutEmailIdentityMailFromAttributes.html) for requirements.
computed_inputs:
- name: aws_access_key_id_govcloud
type: string
Expand All @@ -60,7 +59,7 @@ provision:
- name: default_domain
overwrite: true
type: string
default: ${config("aws.zone")}
default: ${config("cg_smtp.aws.zone")}
- name: labels
default: ${json.marshal(request.default_labels)}
overwrite: true
Expand All @@ -73,7 +72,7 @@ provision:
default: ${request.context}
- name: service_offering_name
type: string
default: cg-smtp
default: aws-ses
- name: service_plan_name
type: string
default: base
Expand All @@ -89,10 +88,10 @@ provision:
details: If a domain was supplied, you must create these records in that zone in your DNS system.
- field_name: dmarc_report_uri_aggregate
type: string
details: The mailto URI to which DMARC aggregate reports should be sent. For example, 'mailto:[email protected]'. Reports are automatically sent to [email protected].
details: The mailto URI to which DMARC aggregate reports should be sent. For example, `mailto:[email protected]`. Reports are automatically sent to `[email protected]`.
- field_name: dmarc_report_uri_failure
type: string
details: The mailto URI to which DMARC individual message failure reports should be sent. For example, 'mailto:[email protected]'.
details: The mailto URI to which DMARC individual message failure reports should be sent. For example, `mailto:[email protected]`.
- field_name: instructions
type: string
details: Any further steps that you must take before using the service.
Expand All @@ -101,7 +100,7 @@ provision:
details: ARN of the SES Configuration Set associated with the identity. Used to create bindings.
- field_name: domain_arn
type: string
details: Instance SES domain identity (used when creating bindings)
details: Instance SES domain identity. Used to create bindings.
- field_name: bounce_topic_arn
type: string
details: ARN of the SNS topic receiving bounce feedback notifications.
Expand All @@ -125,11 +124,10 @@ bind:
- field_name: source_ips
type: array
default: []
details: IP Ranges that requests to SES must come from.
details: A list of IP ranges in CIDR format. If specified, requests made with this binding must originate from the specified ranges. By default, all requests are allowed.
prohibit_update: false
- field_name: notification_webhook
type: string
default: ""
details: HTTPS endpoint to subscribe to feedback notifications.
computed_inputs:
- name: aws_access_key_id_govcloud
Expand Down Expand Up @@ -177,7 +175,7 @@ bind:
default: ${request.context}
- name: service_offering_name
type: string
default: cg-smtp
default: aws-ses
- name: service_plan_name
type: string
default: base
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
type VCAPServices struct {
SMTPService []struct {
Credentials Credentials `json:"credentials"`
} `json:"cg-smtp"`
} `json:"aws-ses"`
}

type Credentials struct {
Expand Down
File renamed without changes.
20 changes: 10 additions & 10 deletions brokerpaks/cg-smtp/manifest.yml → brokerpaks/aws-ses/manifest.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
packversion: 1
name: cg-smtp
name: aws-ses
version: 0.1.0
metadata:
author: cloud.gov team
Expand All @@ -22,23 +22,23 @@ terraform_binaries:
version: 5.53.0
source: https://github.com/terraform-providers/terraform-provider-aws/archive/v5.53.0.zip
service_definitions:
- cg-smtp.yml
- aws-ses.yml
parameters: []
required_env_variables:
- AWS_ZONE
- AWS_ACCESS_KEY_ID_COMMERCIAL
- AWS_ACCESS_KEY_ID_GOVCLOUD
- AWS_SECRET_ACCESS_KEY_GOVCLOUD
- AWS_REGION_COMMERCIAL
- AWS_REGION_GOVCLOUD
- AWS_ACCESS_KEY_ID_COMMERCIAL
- AWS_SECRET_ACCESS_KEY_COMMERCIAL
- AWS_REGION_COMMERCIAL
- AWS_SECRET_ACCESS_KEY_GOVCLOUD
- CG_SMTP_AWS_ZONE
- CLOUD_GOV_ENVIRONMENT
env_config_mapping:
AWS_ACCESS_KEY_ID_COMMERCIAL: aws.commercial.access_key_id
AWS_ACCESS_KEY_ID_GOVCLOUD: aws.govcloud.access_key_id
AWS_SECRET_ACCESS_KEY_GOVCLOUD: aws.govcloud.secret_access_key
AWS_REGION_COMMERCIAL: aws.commercial.region
AWS_REGION_GOVCLOUD: aws.govcloud.region
AWS_ACCESS_KEY_ID_COMMERCIAL: aws.commercial.access_key_id
AWS_SECRET_ACCESS_KEY_COMMERCIAL: aws.commercial.secret_access_key
AWS_REGION_COMMERCIAL: aws.commercial.region
AWS_ZONE: aws.zone
AWS_SECRET_ACCESS_KEY_GOVCLOUD: aws.govcloud.secret_access_key
CG_SMTP_AWS_ZONE: cg_smtp.aws.zone
CLOUD_GOV_ENVIRONMENT: cloud_gov.environment
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ locals {
subscribed_webhook = ((local.subscribe_bounce_notification || local.subscribe_complaint_notification || local.subscribe_delivery_notification) ? var.notification_webhook : null)
}

# Trivy: It is best practice to manage access via groups intead of by directly attaching
# policies to users. However, each binding may specify separate source IP constraints
# on sending, so we cannot use a group with a single policy for all users.
#trivy:ignore:AVD-AWS-0143
resource "aws_iam_user" "user" {
name = local.user_name
path = "/cf/"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ locals {
resource "aws_sesv2_email_identity" "identity" {
configuration_set_name = aws_sesv2_configuration_set.config.configuration_set_name
email_identity = local.domain
# Should match https://github.com/cloud-gov/go-broker-tags/blob/main/tags.go#L10

lifecycle {
prevent_destroy = true
Expand Down
2 changes: 1 addition & 1 deletion docproxy/go.mod
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module github.com/cloud-gov/csb-docproxy
module github.com/cloud-gov/csb/docproxy

go 1.23.1

Expand Down
10 changes: 10 additions & 0 deletions docproxy/images/amazon-ses.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions docproxy/images/cloud-gov-logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
78 changes: 72 additions & 6 deletions docproxy/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@ package main

import (
"embed"
"fmt"
"log/slog"
"net/http"
"net/url"
"os"
"slices"
"strconv"
"strings"

"golang.org/x/net/html"
Expand Down Expand Up @@ -85,6 +89,19 @@ func modifyDocument(n *html.Node) {

return false
},
func(n *html.Node) bool {
if n.Type == html.TextNode && n.Parent.Parent.Type == html.ElementNode && n.Parent.Data == "a" {
cls := html.Attribute{
Key: "class",
Val: "navbar-brand",
}
if i := slices.Index(n.Parent.Attr, cls); i >= 0 {
// Change page title.
n.Data = "Services Reference"
}
}
return false
},
}
walk(n, func(n *html.Node) bool {
for _, m := range modifications {
Expand All @@ -106,10 +123,13 @@ var fonts embed.FS
//go:embed images/favicon.ico
var favicon []byte

// run registers routes and starts the server. It is separate from main so it
// can return errors conventionally and main can handle them all in one place.
func run() error {
slog.SetLogLoggerLevel(slog.LevelInfo)
//go:embed images/cloud-gov-logo.svg
var logo []byte

//go:embed images/amazon-ses.svg
var amazonSES []byte

func routes(c config) {
http.HandleFunc("/styles.css", func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "text/css; charset=utf-8")
w.Write(stylesheet)
Expand All @@ -127,8 +147,16 @@ func run() error {
w.Header().Add("Content-Type", "image/vnd.microsoft.icon")
w.Write(favicon)
})
http.HandleFunc("/images/cloud-gov-logo.svg", func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "image/svg+xml")
w.Write(logo)
})
http.HandleFunc("/images/amazon-ses.svg", func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "image/svg+xml")
w.Write(amazonSES)
})
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
resp, err := http.Get("https://csb.dev.us-gov-west-1.aws-us-gov.cloud.gov")
resp, err := http.Get(c.BrokerURL.String())
if err != nil {
slog.Error("Getting CSB site", "error", err)
w.WriteHeader(http.StatusBadGateway)
Expand All @@ -148,8 +176,46 @@ func run() error {
w.WriteHeader(http.StatusInternalServerError)
}
})
}

type config struct {
Port uint16
BrokerURL *url.URL
}

func loadConfig() (config, error) {
c := config{}

port := os.Getenv("PORT")
p, err := strconv.ParseUint(port, 10, 16)
if err != nil {
return config{}, fmt.Errorf("Invalid PORT: %w", err)
}
c.Port = uint16(p)

brokerURL := os.Getenv("BROKER_URL")
u, err := url.Parse(brokerURL)
if err != nil {
return config{}, fmt.Errorf("Invalid BROKER_URL: %w", err)
}
c.BrokerURL = u

return c, nil
}

// run registers routes and starts the server. It is separate from main so it
// can return errors conventionally and main can handle them all in one place.
func run() error {
slog.SetLogLoggerLevel(slog.LevelInfo)
config, err := loadConfig()
if err != nil {
return err
}

routes(config)
addr := fmt.Sprintf("localhost:%v", config.Port)
slog.Info("Starting server...")
return http.ListenAndServe("localhost:8080", nil)
return http.ListenAndServe(addr, nil)
}

func main() {
Expand Down
Loading

0 comments on commit 4328653

Please sign in to comment.