Skip to content

Initial Wallet Auth #156

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 20 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -480,17 +480,32 @@ docker run -it --rm \

##### Using a wallet

For a wallet connection, you must first set up the wallet. If you are using Oracle Autonomous Database, for example, you can download the wallet from the Oracle Cloud Infrastructure (OCI) console.
For a **mTLS connection**, you must first set up the wallet. If you are using Oracle Autonomous Database with mTLS, for example, you can download the wallet from the Oracle Cloud Infrastructure (OCI) console.

1. Unzip the wallet into a new directory, e.g., called `wallet`.
1. Edit the `sqlnet.ora` file and set the `DIRECTORY` to `/wallet`. This is the path inside the exporter container where you will provide the wallet.
1. Take a note of the TNS name from the `tnsnames.ora` that will be used to connect to the database, e.g., `devdb_tp`.
1. Take a note of the TNS Alias name from the `tnsnames.ora` that will be used to connect to the database, e.g., `devdb_tp`.

To use a **wallet for authentication**, use `orapki` to create a wallet and secretstore. The `orapki` utility can be found in SQLcl or in full database techstack installations.
1. Create a wallet, for example:
```bash
orapki wallet create -wallet /wallet -auto_login -pwd <wallet_password>
```
1. Create a secretstore for the database user, specifying the TNS Alias name (`<tns_alias>`) from the `tnsnames.ora` file for example:
```bash
orapki secretstore create_credential -wallet /wallet -pwd <wallet_password> -connect_string <tns_alias> -username <db_user_name> -password <db_user_password>
```
1. Add the following lines to the `sqlnet.ora` file:
```text
WALLET_LOCATION = (SOURCE=(METHOD=FILE)(METHOD_DATA=(DIRECTORY=/wallet)))
SQLNET.WALLET_OVERRIDE = TRUE
```

Now, you provide the connection details using these variables:

- `DB_USERNAME` is the database username, e.g., `pdbadmin`
- `DB_PASSWORD` is the password for that user, e.g., `Welcome12345`
- `DB_CONNECT_STRING` is the connection string, e.g., `devdb_tp?TNS_ADMIN=/wallet`
- `DB_USERNAME` is the database username, e.g., `pdbadmin` **Note:** Do not set `DB_USERNAME` if using wallet authentication
- `DB_PASSWORD` is the password for that user, e.g., `Welcome12345` **Note:** Do not set `DB_PASSWORD` if using wallet authentication
- `DB_CONNECT_STRING` is the connection string, e.g., `devdb_tp`
- `DB_ROLE` (Optional) can be set to `SYSDBA` or `SYSOPER` if you want to connect with one of those roles, however Oracle recommends that you connect with the lowest possible privileges and roles necessary for the exporter to run.
- `ORACLE_HOME` is the location of the Oracle Instant Client, i.e., `/lib/oracle/21/client64/lib`. If you built your own container image, the path may be different.
- `TNS_ADMIN` is the location of your (unzipped) wallet. The `DIRECTORY` set in the `sqlnet.ora` file must match the path that it will be mounted on inside the container.
Expand Down
16 changes: 14 additions & 2 deletions collector/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type Exporter struct {
password string
connectString string
configDir string
externalAuth bool
duration, error prometheus.Gauge
totalScrapes prometheus.Counter
scrapeErrors *prometheus.CounterVec
Expand All @@ -55,6 +56,7 @@ type Config struct {
ConnectString string
DbRole string
ConfigDir string
ExternalAuth bool
MaxIdleConns int
MaxOpenConns int
CustomMetrics string
Expand Down Expand Up @@ -117,6 +119,7 @@ func NewExporter(logger log.Logger, cfg *Config) (*Exporter, error) {
password: cfg.Password,
connectString: cfg.ConnectString,
configDir: cfg.ConfigDir,
externalAuth: cfg.ExternalAuth,
duration: prometheus.NewGauge(prometheus.GaugeOpts{
Namespace: namespace,
Subsystem: exporterName,
Expand Down Expand Up @@ -375,7 +378,16 @@ func (e *Exporter) connect() error {
level.Debug(e.logger).Log("msg", "Launching connection to "+maskDsn(e.connectString))

var P godror.ConnectionParams
P.Username, P.Password, P.ConnectString = e.user, godror.NewPassword(e.password), e.connectString
// If password is not specified, externalAuth will be true and we'll ignore user input
e.externalAuth = e.password == ""
level.Debug(e.logger).Log("external authentication set to ", e.externalAuth)
msg := "Using Username/Password Authentication."
if e.externalAuth {
msg = "Database Password not specified; will attempt to use external authentication (ignoring user input)."
e.user = ""
}
level.Info(e.logger).Log("msg", msg)
P.Username, P.Password, P.ConnectString, P.ExternalAuth = e.user, godror.NewPassword(e.password), e.connectString, e.externalAuth

// if TNS_ADMIN env var is set, set ConfigDir to that location
P.ConfigDir = e.configDir
Expand All @@ -390,7 +402,7 @@ func (e *Exporter) connect() error {

level.Debug(e.logger).Log("msg", "connection properties: "+fmt.Sprint(P))

// note that this just configures the connection, it does not acutally connect until later
// note that this just configures the connection, it does not actually connect until later
// when we call db.Ping()
db := sql.OpenDB(godror.NewConnector(P))
level.Debug(e.logger).Log("set max idle connections to ", e.config.MaxIdleConns)
Expand Down
3 changes: 3 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ func main() {
connectString := os.Getenv("DB_CONNECT_STRING")
dbrole := os.Getenv("DB_ROLE")
tnsadmin := os.Getenv("TNS_ADMIN")
// externalAuth - Default to user/password but if no password is supplied then will automagically set to true
externalAuth := false

vaultID, useVault := os.LookupEnv("OCI_VAULT_ID")
if useVault {
Expand Down Expand Up @@ -87,6 +89,7 @@ func main() {
ConnectString: connectString,
DbRole: dbrole,
ConfigDir: tnsadmin,
ExternalAuth: externalAuth,
MaxOpenConns: *maxOpenConns,
MaxIdleConns: *maxIdleConns,
CustomMetrics: *customMetrics,
Expand Down